From 8295f5f8fd28042e1a0a172d5afbba79178064c2 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 29 Dec 2018 12:15:53 -0600 Subject: Initial commit --- .gitattributes | 2 + .gitignore | 341 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ LICENSE.TXT | 28 +++++ README.md | 2 + 4 files changed, 373 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 LICENSE.TXT create mode 100644 README.md diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..dfe07704 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..3e8a1553 --- /dev/null +++ b/.gitignore @@ -0,0 +1,341 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ +# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true +**/wwwroot/lib/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb diff --git a/LICENSE.TXT b/LICENSE.TXT new file mode 100644 index 00000000..d4d316ef --- /dev/null +++ b/LICENSE.TXT @@ -0,0 +1,28 @@ +Copyright (c) .NET Foundation and contributors. +This software is released under the Microsoft Reciprocal License (MS-RL) (the "License"); you may not use the software except in compliance with the License. + +The text of the Microsoft Reciprocal License (MS-RL) can be found online at: + http://opensource.org/licenses/ms-rl + + +Microsoft Reciprocal License (MS-RL) + +This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. + +1. Definitions + The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. + A "contribution" is the original software, or any additions or changes to the software. + A "contributor" is any person that distributes its contribution under this license. + "Licensed patents" are a contributor's patent claims that read directly on its contribution. + +2. Grant of Rights + (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. + (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. + +3. Conditions and Limitations + (A) Reciprocal Grants- For any file you distribute that contains code from the software (in source code or binary format), you must provide recipients the source code to that file along with a copy of this license, which license will govern that file. You may license other files that are entirely your own work and do not contain code from the software under any terms you choose. + (B) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. + (C) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. + (D) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. + (E) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. + (F) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. diff --git a/README.md b/README.md new file mode 100644 index 00000000..a564d971 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# burn +burn.lib - Burn engine -- cgit v1.2.3-55-g6feb From 61847dddd4fd497057c780658e383c4627de19ec Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 29 Dec 2018 22:12:08 -0600 Subject: Import code from old v4 repo --- .../ManagedBundleRunner/BundleErrorEventArgs.cs | 33 + .../ManagedBundleRunner/BundleProgressEventArgs.cs | 23 + src/Samples/ManagedBundleRunner/BundleResult.cs | 24 + src/Samples/ManagedBundleRunner/BundleRunner.cs | 212 ++ src/Samples/runbundle/AssemblyInfo.cs | 12 + src/Samples/runbundle/Program.cs | 47 + src/engine/EngineForApplication.cpp | 894 ++++++ src/engine/EngineForApplication.h | 44 + src/engine/apply.cpp | 2516 ++++++++++++++++ src/engine/apply.h | 106 + src/engine/approvedexe.cpp | 262 ++ src/engine/approvedexe.h | 67 + src/engine/bitsengine.cpp | 505 ++++ src/engine/bitsengine.h | 23 + src/engine/cabextract.cpp | 974 ++++++ src/engine/cabextract.h | 40 + src/engine/cache.cpp | 2026 +++++++++++++ src/engine/cache.h | 150 + src/engine/catalog.cpp | 180 ++ src/engine/catalog.h | 56 + src/engine/condition.cpp | 1030 +++++++ src/engine/condition.h | 39 + src/engine/container.cpp | 386 +++ src/engine/container.h | 183 ++ src/engine/core.cpp | 1705 +++++++++++ src/engine/core.h | 211 ++ src/engine/dependency.cpp | 1203 ++++++++ src/engine/dependency.h | 176 ++ src/engine/detect.cpp | 431 +++ src/engine/detect.h | 44 + src/engine/elevation.cpp | 2814 +++++++++++++++++ src/engine/elevation.h | 178 ++ src/engine/embedded.cpp | 197 ++ src/engine/embedded.h | 27 + src/engine/engine.cpp | 889 ++++++ src/engine/engine.mc | 901 ++++++ src/engine/exeengine.cpp | 820 +++++ src/engine/exeengine.h | 48 + src/engine/inc/engine.h | 27 + src/engine/logging.cpp | 683 +++++ src/engine/logging.h | 144 + src/engine/manifest.cpp | 125 + src/engine/manifest.h | 23 + src/engine/msiengine.cpp | 1910 ++++++++++++ src/engine/msiengine.h | 73 + src/engine/mspengine.cpp | 980 ++++++ src/engine/mspengine.h | 67 + src/engine/msuengine.cpp | 513 ++++ src/engine/msuengine.h | 48 + src/engine/netfxchainer.cpp | 418 +++ src/engine/netfxchainer.h | 98 + src/engine/package.cpp | 678 +++++ src/engine/package.h | 336 +++ src/engine/payload.cpp | 367 +++ src/engine/payload.h | 93 + src/engine/pipe.cpp | 873 ++++++ src/engine/pipe.h | 113 + src/engine/plan.cpp | 3169 ++++++++++++++++++++ src/engine/plan.h | 543 ++++ src/engine/platform.cpp | 16 + src/engine/platform.h | 34 + src/engine/precomp.h | 100 + src/engine/pseudobundle.cpp | 271 ++ src/engine/pseudobundle.h | 39 + src/engine/registration.cpp | 1599 ++++++++++ src/engine/registration.h | 214 ++ src/engine/relatedbundle.cpp | 457 +++ src/engine/relatedbundle.h | 20 + src/engine/search.cpp | 1195 ++++++++ src/engine/search.h | 152 + src/engine/section.cpp | 399 +++ src/engine/section.h | 54 + src/engine/splashscreen.cpp | 316 ++ src/engine/splashscreen.h | 31 + src/engine/uithread.cpp | 220 ++ src/engine/uithread.h | 23 + src/engine/update.cpp | 44 + src/engine/update.h | 33 + src/engine/userexperience.cpp | 2122 +++++++++++++ src/engine/userexperience.h | 439 +++ src/engine/variable.cpp | 2345 +++++++++++++++ src/engine/variable.h | 190 ++ src/engine/variant.cpp | 601 ++++ src/engine/variant.h | 102 + src/stub/StubSection.cpp | 23 + src/stub/precomp.h | 13 + src/stub/stub.cpp | 64 + src/stub/stub.ico | Bin 0 -> 2238 bytes src/stub/stub.manifest | 18 + src/stub/stub.rc | 14 + 90 files changed, 41905 insertions(+) create mode 100644 src/Samples/ManagedBundleRunner/BundleErrorEventArgs.cs create mode 100644 src/Samples/ManagedBundleRunner/BundleProgressEventArgs.cs create mode 100644 src/Samples/ManagedBundleRunner/BundleResult.cs create mode 100644 src/Samples/ManagedBundleRunner/BundleRunner.cs create mode 100644 src/Samples/runbundle/AssemblyInfo.cs create mode 100644 src/Samples/runbundle/Program.cs create mode 100644 src/engine/EngineForApplication.cpp create mode 100644 src/engine/EngineForApplication.h create mode 100644 src/engine/apply.cpp create mode 100644 src/engine/apply.h create mode 100644 src/engine/approvedexe.cpp create mode 100644 src/engine/approvedexe.h create mode 100644 src/engine/bitsengine.cpp create mode 100644 src/engine/bitsengine.h create mode 100644 src/engine/cabextract.cpp create mode 100644 src/engine/cabextract.h create mode 100644 src/engine/cache.cpp create mode 100644 src/engine/cache.h create mode 100644 src/engine/catalog.cpp create mode 100644 src/engine/catalog.h create mode 100644 src/engine/condition.cpp create mode 100644 src/engine/condition.h create mode 100644 src/engine/container.cpp create mode 100644 src/engine/container.h create mode 100644 src/engine/core.cpp create mode 100644 src/engine/core.h create mode 100644 src/engine/dependency.cpp create mode 100644 src/engine/dependency.h create mode 100644 src/engine/detect.cpp create mode 100644 src/engine/detect.h create mode 100644 src/engine/elevation.cpp create mode 100644 src/engine/elevation.h create mode 100644 src/engine/embedded.cpp create mode 100644 src/engine/embedded.h create mode 100644 src/engine/engine.cpp create mode 100644 src/engine/engine.mc create mode 100644 src/engine/exeengine.cpp create mode 100644 src/engine/exeengine.h create mode 100644 src/engine/inc/engine.h create mode 100644 src/engine/logging.cpp create mode 100644 src/engine/logging.h create mode 100644 src/engine/manifest.cpp create mode 100644 src/engine/manifest.h create mode 100644 src/engine/msiengine.cpp create mode 100644 src/engine/msiengine.h create mode 100644 src/engine/mspengine.cpp create mode 100644 src/engine/mspengine.h create mode 100644 src/engine/msuengine.cpp create mode 100644 src/engine/msuengine.h create mode 100644 src/engine/netfxchainer.cpp create mode 100644 src/engine/netfxchainer.h create mode 100644 src/engine/package.cpp create mode 100644 src/engine/package.h create mode 100644 src/engine/payload.cpp create mode 100644 src/engine/payload.h create mode 100644 src/engine/pipe.cpp create mode 100644 src/engine/pipe.h create mode 100644 src/engine/plan.cpp create mode 100644 src/engine/plan.h create mode 100644 src/engine/platform.cpp create mode 100644 src/engine/platform.h create mode 100644 src/engine/precomp.h create mode 100644 src/engine/pseudobundle.cpp create mode 100644 src/engine/pseudobundle.h create mode 100644 src/engine/registration.cpp create mode 100644 src/engine/registration.h create mode 100644 src/engine/relatedbundle.cpp create mode 100644 src/engine/relatedbundle.h create mode 100644 src/engine/search.cpp create mode 100644 src/engine/search.h create mode 100644 src/engine/section.cpp create mode 100644 src/engine/section.h create mode 100644 src/engine/splashscreen.cpp create mode 100644 src/engine/splashscreen.h create mode 100644 src/engine/uithread.cpp create mode 100644 src/engine/uithread.h create mode 100644 src/engine/update.cpp create mode 100644 src/engine/update.h create mode 100644 src/engine/userexperience.cpp create mode 100644 src/engine/userexperience.h create mode 100644 src/engine/variable.cpp create mode 100644 src/engine/variable.h create mode 100644 src/engine/variant.cpp create mode 100644 src/engine/variant.h create mode 100644 src/stub/StubSection.cpp create mode 100644 src/stub/precomp.h create mode 100644 src/stub/stub.cpp create mode 100644 src/stub/stub.ico create mode 100644 src/stub/stub.manifest create mode 100644 src/stub/stub.rc diff --git a/src/Samples/ManagedBundleRunner/BundleErrorEventArgs.cs b/src/Samples/ManagedBundleRunner/BundleErrorEventArgs.cs new file mode 100644 index 00000000..2c377326 --- /dev/null +++ b/src/Samples/ManagedBundleRunner/BundleErrorEventArgs.cs @@ -0,0 +1,33 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace Wix.Samples +{ + using System; + + /// + /// Arguments provided when bundle encounters an error. + /// + [Serializable] + public class BundleErrorEventArgs : EventArgs + { + /// + /// Gets the error code. + /// + public int Code { get; set; } + + /// + /// Gets the error message. + /// + public string Message { get; set; } + + /// + /// Gets the recommended display flags for an error dialog. + /// + public int UIHint { get; set; } + + /// + /// Gets or sets the of the operation. This is passed back to the bundle. + /// + public BundleResult Result { get; set; } + } +} diff --git a/src/Samples/ManagedBundleRunner/BundleProgressEventArgs.cs b/src/Samples/ManagedBundleRunner/BundleProgressEventArgs.cs new file mode 100644 index 00000000..ed42b5b1 --- /dev/null +++ b/src/Samples/ManagedBundleRunner/BundleProgressEventArgs.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace Wix.Samples +{ + using System; + + /// + /// Arguments provided when bundle progress is updated. + /// + [Serializable] + public class BundleProgressEventArgs : EventArgs + { + /// + /// Gets the percentage from 0 to 100 completed for a bundle. + /// + public int Progress { get; set; } + + /// + /// Gets or sets the of the operation. This is passed back to the bundle. + /// + public BundleResult Result { get; set; } + } +} diff --git a/src/Samples/ManagedBundleRunner/BundleResult.cs b/src/Samples/ManagedBundleRunner/BundleResult.cs new file mode 100644 index 00000000..c32644f4 --- /dev/null +++ b/src/Samples/ManagedBundleRunner/BundleResult.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace Wix.Samples +{ + /// + /// Result codes. + /// + public enum BundleResult + { + Error = -1, + None, + Ok, + Cancel, + Abort, + Retry, + Ignore, + Yes, + No, + Close, + Help, + TryAgain, + Continue, + } +} diff --git a/src/Samples/ManagedBundleRunner/BundleRunner.cs b/src/Samples/ManagedBundleRunner/BundleRunner.cs new file mode 100644 index 00000000..e2089787 --- /dev/null +++ b/src/Samples/ManagedBundleRunner/BundleRunner.cs @@ -0,0 +1,212 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace Wix.Samples +{ + using System; + using System.Diagnostics; + using System.IO.Pipes; + using System.Text; + using System.Threading; + + /// + /// Runs a bundle with provided command-line. + /// + public class BundleRunner + { + /// + /// Creates a runner for the provided bundle. + /// + /// Path to the bundle to run. + public BundleRunner(string bundle) + { + this.Path = bundle; + } + + /// + /// Fired when the bundle encounters an error. + /// + public event EventHandler Error; + + /// + /// Fired when the bundle progress is udpated. + /// + public event EventHandler Progress; + + /// + /// Gets the path to the bundle to run. + /// + public string Path { get; private set; } + + /// + /// Runs the bundle with the provided command-line. + /// + /// Optional command-line to pass to the bundle. + /// Exit code from the bundle. + public int Run(string commandLine = null) + { + WaitHandle[] waits = new WaitHandle[] { new ManualResetEvent(false), new ManualResetEvent(false) }; + int returnCode = 0; + int pid = Process.GetCurrentProcess().Id; + string pipeName = String.Concat("bpe_", pid); + string pipeSecret = Guid.NewGuid().ToString("N"); + + using (NamedPipeServerStream pipe = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1)) + { + using (Process bundleProcess = new Process()) + { + bundleProcess.StartInfo.FileName = this.Path; + bundleProcess.StartInfo.Arguments = String.Format("{0} -burn.embedded {1} {2} {3}", commandLine ?? String.Empty, pipeName, pipeSecret, pid); + bundleProcess.StartInfo.UseShellExecute = false; + bundleProcess.StartInfo.CreateNoWindow = true; + bundleProcess.Start(); + + Connect(pipe, pipeSecret, pid, bundleProcess.Id); + + PumpMessages(pipe); + + bundleProcess.WaitForExit(); + returnCode = bundleProcess.ExitCode; + } + } + + return returnCode; + } + + /// + /// Called when bundle encounters an error. + /// + /// Additional arguments for this event. + protected virtual void OnError(BundleErrorEventArgs e) + { + EventHandler handler = this.Error; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// Called when bundle progress is updated. + /// + /// Additional arguments for this event. + protected virtual void OnProgress(BundleProgressEventArgs e) + { + EventHandler handler = this.Progress; + if (handler != null) + { + handler(this, e); + } + } + + private void Connect(NamedPipeServerStream pipe, string pipeSecret, int pid, int childPid) + { + pipe.WaitForConnection(); + + WriteSecretToPipe(pipe, pipeSecret); + + WriteNumberToPipe(pipe, (uint)pid); + + uint ack = ReadNumberFromPipe(pipe); + // This is not true when bundle is run under a debugger + //if (ack != childPid) + //{ + // throw new ApplicationException("Incorrect child process."); + //} + } + + private void PumpMessages(NamedPipeServerStream pipe) + { + uint messageId; + while (TryReadNumberFromPipe(pipe, out messageId)) + { + uint messageSize = ReadNumberFromPipe(pipe); + + BundleResult result = BundleResult.None; + switch (messageId) + { + case 1: //error + result = ProcessErrorMessage(pipe); + break; + + case 2: // progress + result = ProcessProgressMessage(pipe); + break; + + default: // unknown message, do nothing. + break; + } + + CompleteMessage(pipe, result); + } + } + + private BundleResult ProcessErrorMessage(NamedPipeServerStream pipe) + { + BundleErrorEventArgs e = new BundleErrorEventArgs(); + e.Code = (int)ReadNumberFromPipe(pipe); + e.Message = ReadStringFromPipe(pipe); + e.UIHint = (int)ReadNumberFromPipe(pipe); + + this.OnError(e); + + return e.Result; + } + + private BundleResult ProcessProgressMessage(NamedPipeServerStream pipe) + { + ReadNumberFromPipe(pipe); // eat the first progress number because it is always zero. + + BundleProgressEventArgs e = new BundleProgressEventArgs(); + e.Progress = (int)ReadNumberFromPipe(pipe); + + this.OnProgress(e); + + return e.Result; + } + + private void CompleteMessage(NamedPipeServerStream pipe, BundleResult result) + { + uint complete = 0xF0000002; + WriteNumberToPipe(pipe, complete); + WriteNumberToPipe(pipe, 4); // size of message data + WriteNumberToPipe(pipe, (uint)result); + } + + private uint ReadNumberFromPipe(NamedPipeServerStream pipe) + { + byte[] buffer = new byte[4]; + pipe.Read(buffer, 0, buffer.Length); + return BitConverter.ToUInt32(buffer, 0); + } + + private string ReadStringFromPipe(NamedPipeServerStream pipe) + { + uint length = ReadNumberFromPipe(pipe); + + byte[] buffer = new byte[length * 2]; + pipe.Read(buffer, 0, buffer.Length); + + return Encoding.Unicode.GetString(buffer); + } + + private bool TryReadNumberFromPipe(NamedPipeServerStream pipe, out uint value) + { + value = ReadNumberFromPipe(pipe); // reading will not block and return zero if pipe is not connected. + return pipe.IsConnected; + } + + private void WriteNumberToPipe(NamedPipeServerStream pipe, uint value) + { + byte[] buffer = BitConverter.GetBytes(value); + pipe.Write(buffer, 0, buffer.Length); + } + + private void WriteSecretToPipe(NamedPipeServerStream pipe, string secret) + { + byte[] buffer = Encoding.Unicode.GetBytes(secret); + + WriteNumberToPipe(pipe, (uint)buffer.Length); + pipe.Write(buffer, 0, buffer.Length); + } + } +} diff --git a/src/Samples/runbundle/AssemblyInfo.cs b/src/Samples/runbundle/AssemblyInfo.cs new file mode 100644 index 00000000..3a66d5e3 --- /dev/null +++ b/src/Samples/runbundle/AssemblyInfo.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("Executable to demonstrate Bundle Runner Sample")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyCulture("")] +[assembly: CLSCompliant(true)] +[assembly: ComVisible(false)] diff --git a/src/Samples/runbundle/Program.cs b/src/Samples/runbundle/Program.cs new file mode 100644 index 00000000..8edca5dc --- /dev/null +++ b/src/Samples/runbundle/Program.cs @@ -0,0 +1,47 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace Wix.Samples +{ + using System; + using System.Linq; + using Wix.Samples; + + /// + /// Example executable that installs then immediately uninstalls a bundle showing progress. + /// + class Program + { + static int Main(string[] args) + { + if (args.Length == 0) + { + Console.WriteLine("Must provide the path to the bundle to install then uninstall."); + return -1; + } + + BundleRunner runner = new BundleRunner(args[0]); + runner.Error += Program.OnError; + runner.Progress += Program.OnProgress; + + Console.WriteLine("Installing: {0}", runner.Path); + int exitCode = runner.Run(String.Join(" ", args.Skip(1).ToArray())); + if (0 == exitCode) + { + Console.WriteLine("\r\nUninstalling: {0}", runner.Path); + exitCode = runner.Run("-uninstall"); + } + + return exitCode; + } + + static void OnError(object sender, BundleErrorEventArgs e) + { + Console.WriteLine("error: {0}, uiHint: {1}, message: {2}", e.Code, e.UIHint, e.Message); + } + + static void OnProgress(object sender, BundleProgressEventArgs e) + { + Console.WriteLine("progresss: {0}%", e.Progress); + } + } +} diff --git a/src/engine/EngineForApplication.cpp b/src/engine/EngineForApplication.cpp new file mode 100644 index 00000000..eda5fc64 --- /dev/null +++ b/src/engine/EngineForApplication.cpp @@ -0,0 +1,894 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static HRESULT BAEngineGetPackageCount( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in BAENGINE_GETPACKAGECOUNT_ARGS* /*pArgs*/, + __in BAENGINE_GETPACKAGECOUNT_RESULTS* pResults + ) +{ + HRESULT hr = S_OK; + DWORD* pcPackages = &pResults->cPackages; + + *pcPackages = pContext->pEngineState->packages.cPackages; + + return hr; +} + +static HRESULT BAEngineGetVariableNumeric( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in BAENGINE_GETVARIABLENUMERIC_ARGS* pArgs, + __in BAENGINE_GETVARIABLENUMERIC_RESULTS* pResults + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzVariable = pArgs->wzVariable; + LONGLONG* pllValue = &pResults->llValue; + + if (wzVariable && *wzVariable) + { + hr = VariableGetNumeric(&pContext->pEngineState->variables, wzVariable, pllValue); + } + else + { + hr = E_INVALIDARG; + } + + return hr; +} + +static HRESULT BAEngineGetVariableString( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in BAENGINE_GETVARIABLESTRING_ARGS* pArgs, + __in BAENGINE_GETVARIABLESTRING_RESULTS* pResults + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + size_t cchRemaining = 0; + LPCWSTR wzVariable = pArgs->wzVariable; + LPWSTR wzValue = pResults->wzValue; + DWORD* pcchValue = &pResults->cchValue; + + if (wzVariable && *wzVariable) + { + hr = VariableGetString(&pContext->pEngineState->variables, wzVariable, &sczValue); + if (SUCCEEDED(hr)) + { + if (wzValue) + { + hr = ::StringCchCopyExW(wzValue, *pcchValue, sczValue, NULL, &cchRemaining, STRSAFE_FILL_BEHIND_NULL); + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + hr = E_MOREDATA; + + ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining); + *pcchValue = cchRemaining + 1; + } + } + else + { + hr = E_MOREDATA; + + ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining); + *pcchValue = cchRemaining + 1; + } + } + } + else + { + hr = E_INVALIDARG; + } + + StrSecureZeroFreeString(sczValue); + return hr; +} + +static HRESULT BAEngineGetVariableVersion( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in BAENGINE_GETVARIABLEVERSION_ARGS* pArgs, + __in BAENGINE_GETVARIABLEVERSION_RESULTS* pResults + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzVariable = pArgs->wzVariable; + DWORD64* pqwValue = &pResults->qwValue; + + if (wzVariable && *wzVariable) + { + hr = VariableGetVersion(&pContext->pEngineState->variables, wzVariable, pqwValue); + } + else + { + hr = E_INVALIDARG; + } + + return hr; +} + +static HRESULT BAEngineFormatString( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in BAENGINE_FORMATSTRING_ARGS* pArgs, + __in BAENGINE_FORMATSTRING_RESULTS* pResults + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + DWORD cchValue = 0; + LPCWSTR wzIn = pArgs->wzIn; + LPWSTR wzOut = pResults->wzOut; + DWORD* pcchOut = &pResults->cchOut; + + if (wzIn && *wzIn) + { + hr = VariableFormatString(&pContext->pEngineState->variables, wzIn, &sczValue, &cchValue); + if (SUCCEEDED(hr)) + { + if (wzOut) + { + hr = ::StringCchCopyExW(wzOut, *pcchOut, sczValue, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + if (FAILED(hr)) + { + *pcchOut = cchValue; + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + hr = E_MOREDATA; + } + } + } + else + { + hr = E_MOREDATA; + *pcchOut = cchValue; + } + } + } + else + { + hr = E_INVALIDARG; + } + + StrSecureZeroFreeString(sczValue); + return hr; +} + +static HRESULT BAEngineEscapeString( + __in BOOTSTRAPPER_ENGINE_CONTEXT* /*pContext*/, + __in BAENGINE_ESCAPESTRING_ARGS* pArgs, + __in BAENGINE_ESCAPESTRING_RESULTS* pResults + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + size_t cchRemaining = 0; + LPCWSTR wzIn = pArgs->wzIn; + LPWSTR wzOut = pResults->wzOut; + DWORD* pcchOut = &pResults->cchOut; + + if (wzIn && *wzIn) + { + hr = VariableEscapeString(wzIn, &sczValue); + if (SUCCEEDED(hr)) + { + if (wzOut) + { + hr = ::StringCchCopyExW(wzOut, *pcchOut, sczValue, NULL, &cchRemaining, STRSAFE_FILL_BEHIND_NULL); + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + hr = E_MOREDATA; + ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining); + *pcchOut = cchRemaining; + } + } + else + { + ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining); + *pcchOut = cchRemaining; + } + } + } + else + { + hr = E_INVALIDARG; + } + + StrSecureZeroFreeString(sczValue); + return hr; +} + +static HRESULT BAEngineEvaluateCondition( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in BAENGINE_EVALUATECONDITION_ARGS* pArgs, + __in BAENGINE_EVALUATECONDITION_RESULTS* pResults + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzCondition = pArgs->wzCondition; + BOOL* pf = &pResults->f; + + if (wzCondition && *wzCondition) + { + hr = ConditionEvaluate(&pContext->pEngineState->variables, wzCondition, pf); + } + else + { + hr = E_INVALIDARG; + } + + return hr; +} + +static HRESULT BAEngineLog( + __in BOOTSTRAPPER_ENGINE_CONTEXT* /*pContext*/, + __in BAENGINE_LOG_ARGS* pArgs, + __in BAENGINE_LOG_RESULTS* /*pResults*/ + ) +{ + HRESULT hr = S_OK; + REPORT_LEVEL rl = REPORT_NONE; + BOOTSTRAPPER_LOG_LEVEL level = pArgs->level; + LPCWSTR wzMessage = pArgs->wzMessage; + + switch (level) + { + case BOOTSTRAPPER_LOG_LEVEL_STANDARD: + rl = REPORT_STANDARD; + break; + + case BOOTSTRAPPER_LOG_LEVEL_VERBOSE: + rl = REPORT_VERBOSE; + break; + + case BOOTSTRAPPER_LOG_LEVEL_DEBUG: + rl = REPORT_DEBUG; + break; + + case BOOTSTRAPPER_LOG_LEVEL_ERROR: + rl = REPORT_ERROR; + break; + + default: + ExitFunction1(hr = E_INVALIDARG); + } + + hr = LogStringLine(rl, "%ls", wzMessage); + ExitOnFailure(hr, "Failed to log BA message."); + +LExit: + return hr; +} + +static HRESULT BAEngineSendEmbeddedError( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in BAENGINE_SENDEMBEDDEDERROR_ARGS* pArgs, + __in BAENGINE_SENDEMBEDDEDERROR_RESULTS* pResults + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + DWORD cbData = 0; + DWORD dwResult = 0; + DWORD dwErrorCode = pArgs->dwErrorCode; + LPCWSTR wzMessage = pArgs->wzMessage; + DWORD dwUIHint = pArgs->dwUIHint; + int* pnResult = &pResults->nResult; + + if (BURN_MODE_EMBEDDED != pContext->pEngineState->mode) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE); + ExitOnRootFailure(hr, "BA requested to send embedded message when not in embedded mode."); + } + + hr = BuffWriteNumber(&pbData, &cbData, dwErrorCode); + ExitOnFailure(hr, "Failed to write error code to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, wzMessage ? wzMessage : L""); + ExitOnFailure(hr, "Failed to write message string to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, dwUIHint); + ExitOnFailure(hr, "Failed to write UI hint to message buffer."); + + hr = PipeSendMessage(pContext->pEngineState->embeddedConnection.hPipe, BURN_EMBEDDED_MESSAGE_TYPE_ERROR, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send embedded message over pipe."); + + *pnResult = static_cast(dwResult); + +LExit: + ReleaseBuffer(pbData); + return hr; +} + +static HRESULT BAEngineSendEmbeddedProgress( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in BAENGINE_SENDEMBEDDEDPROGRESS_ARGS* pArgs, + __in BAENGINE_SENDEMBEDDEDPROGRESS_RESULTS* pResults + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + DWORD cbData = 0; + DWORD dwResult = 0; + DWORD dwProgressPercentage = pArgs->dwProgressPercentage; + DWORD dwOverallProgressPercentage = pArgs->dwOverallProgressPercentage; + int* pnResult = &pResults->nResult; + + if (BURN_MODE_EMBEDDED != pContext->pEngineState->mode) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE); + ExitOnRootFailure(hr, "BA requested to send embedded progress message when not in embedded mode."); + } + + hr = BuffWriteNumber(&pbData, &cbData, dwProgressPercentage); + ExitOnFailure(hr, "Failed to write progress percentage to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, dwOverallProgressPercentage); + ExitOnFailure(hr, "Failed to write overall progress percentage to message buffer."); + + hr = PipeSendMessage(pContext->pEngineState->embeddedConnection.hPipe, BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send embedded progress message over pipe."); + + *pnResult = static_cast(dwResult); + +LExit: + ReleaseBuffer(pbData); + return hr; +} + +static HRESULT BAEngineSetUpdate( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const BAENGINE_SETUPDATE_ARGS* pArgs, + __in BAENGINE_SETUPDATE_RESULTS* /*pResults*/ + ) +{ + HRESULT hr = S_OK; + LPCWSTR sczId = NULL; + LPWSTR sczLocalSource = NULL; + LPWSTR sczCommandline = NULL; + UUID guid = { }; + WCHAR wzGuid[39]; + RPC_STATUS rs = RPC_S_OK; + LPCWSTR wzLocalSource = pArgs->wzLocalSource; + LPCWSTR wzDownloadSource = pArgs->wzDownloadSource; + DWORD64 qwSize = pArgs->qwSize; + BOOTSTRAPPER_UPDATE_HASH_TYPE hashType = pArgs->hashType; + BYTE* rgbHash = pArgs->rgbHash; + DWORD cbHash = pArgs->cbHash; + + ::EnterCriticalSection(&pContext->pEngineState->csActive); + + if ((!wzLocalSource || !*wzLocalSource) && (!wzDownloadSource || !*wzDownloadSource)) + { + UpdateUninitialize(&pContext->pEngineState->update); + } + else if (BOOTSTRAPPER_UPDATE_HASH_TYPE_NONE == hashType && (0 != cbHash || rgbHash)) + { + hr = E_INVALIDARG; + } + else if (BOOTSTRAPPER_UPDATE_HASH_TYPE_SHA1 == hashType && (SHA1_HASH_LEN != cbHash || !rgbHash)) + { + hr = E_INVALIDARG; + } + else + { + UpdateUninitialize(&pContext->pEngineState->update); + + if (!wzLocalSource || !*wzLocalSource) + { + hr = StrAllocFormatted(&sczLocalSource, L"update\\%ls", pContext->pEngineState->registration.sczExecutableName); + ExitOnFailure(hr, "Failed to default local update source"); + } + + hr = CoreRecreateCommandLine(&sczCommandline, BOOTSTRAPPER_ACTION_INSTALL, pContext->pEngineState->command.display, pContext->pEngineState->command.restart, BOOTSTRAPPER_RELATION_NONE, FALSE, pContext->pEngineState->registration.sczActiveParent, pContext->pEngineState->registration.sczAncestors, NULL, pContext->pEngineState->command.wzCommandLine); + ExitOnFailure(hr, "Failed to recreate command-line for update bundle."); + + // Per-user bundles would fail to use the downloaded update bundle, as the existing install would already be cached + // at the registration id's location. Here I am generating a random guid, but in the future it would be nice if the + // feed would provide the ID of the update. + if (!pContext->pEngineState->registration.fPerMachine) + { + rs = ::UuidCreate(&guid); + hr = HRESULT_FROM_RPC(rs); + ExitOnFailure(hr, "Failed to create bundle update guid."); + + if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid))) + { + hr = E_OUTOFMEMORY; + ExitOnRootFailure(hr, "Failed to convert bundle update guid into string."); + } + + sczId = wzGuid; + } + else + { + sczId = pContext->pEngineState->registration.sczId; + } + + hr = PseudoBundleInitialize(FILEMAKEVERSION(rmj, rmm, rup, 0), &pContext->pEngineState->update.package, FALSE, sczId, BOOTSTRAPPER_RELATION_UPDATE, BOOTSTRAPPER_PACKAGE_STATE_ABSENT, pContext->pEngineState->registration.sczExecutableName, sczLocalSource ? sczLocalSource : wzLocalSource, wzDownloadSource, qwSize, TRUE, sczCommandline, NULL, NULL, NULL, rgbHash, cbHash); + ExitOnFailure(hr, "Failed to set update bundle."); + + pContext->pEngineState->update.fUpdateAvailable = TRUE; + } + +LExit: + ::LeaveCriticalSection(&pContext->pEngineState->csActive); + + ReleaseStr(sczCommandline); + ReleaseStr(sczLocalSource); + return hr; +} + +static HRESULT BAEngineSetLocalSource( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in BAENGINE_SETLOCALSOURCE_ARGS* pArgs, + __in BAENGINE_SETLOCALSOURCE_RESULTS* /*pResults*/ + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER* pContainer = NULL; + BURN_PAYLOAD* pPayload = NULL; + LPCWSTR wzPackageOrContainerId = pArgs->wzPackageOrContainerId; + LPCWSTR wzPayloadId = pArgs->wzPayloadId; + LPCWSTR wzPath = pArgs->wzPath; + + ::EnterCriticalSection(&pContext->pEngineState->csActive); + hr = UserExperienceEnsureEngineInactive(&pContext->pEngineState->userExperience); + ExitOnFailure(hr, "Engine is active, cannot change engine state."); + + if (!wzPath || !*wzPath) + { + hr = E_INVALIDARG; + } + else if (wzPayloadId && * wzPayloadId) + { + hr = PayloadFindById(&pContext->pEngineState->payloads, wzPayloadId, &pPayload); + ExitOnFailure(hr, "BA requested unknown payload with id: %ls", wzPayloadId); + + if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION); + ExitOnFailure(hr, "BA denied while trying to set source on embedded payload: %ls", wzPayloadId); + } + + hr = StrAllocString(&pPayload->sczSourcePath, wzPath, 0); + ExitOnFailure(hr, "Failed to set source path for payload."); + } + else if (wzPackageOrContainerId && *wzPackageOrContainerId) + { + hr = ContainerFindById(&pContext->pEngineState->containers, wzPackageOrContainerId, &pContainer); + ExitOnFailure(hr, "BA requested unknown container with id: %ls", wzPackageOrContainerId); + + hr = StrAllocString(&pContainer->sczSourcePath, wzPath, 0); + ExitOnFailure(hr, "Failed to set source path for container."); + } + else + { + hr = E_INVALIDARG; + } + +LExit: + ::LeaveCriticalSection(&pContext->pEngineState->csActive); + return hr; +} + +static HRESULT BAEngineSetDownloadSource( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in BAENGINE_SETDOWNLOADSOURCE_ARGS* pArgs, + __in BAENGINE_SETDOWNLOADSOURCE_RESULTS* /*pResults*/ + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER* pContainer = NULL; + BURN_PAYLOAD* pPayload = NULL; + DOWNLOAD_SOURCE* pDownloadSource = NULL; + LPCWSTR wzPackageOrContainerId = pArgs->wzPackageOrContainerId; + LPCWSTR wzPayloadId = pArgs->wzPayloadId; + LPCWSTR wzUrl = pArgs->wzUrl; + LPCWSTR wzUser = pArgs->wzUser; + LPCWSTR wzPassword = pArgs->wzPassword; + + ::EnterCriticalSection(&pContext->pEngineState->csActive); + hr = UserExperienceEnsureEngineInactive(&pContext->pEngineState->userExperience); + ExitOnFailure(hr, "Engine is active, cannot change engine state."); + + if (wzPayloadId && *wzPayloadId) + { + hr = PayloadFindById(&pContext->pEngineState->payloads, wzPayloadId, &pPayload); + ExitOnFailure(hr, "BA requested unknown payload with id: %ls", wzPayloadId); + + if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION); + ExitOnFailure(hr, "BA denied while trying to set download URL on embedded payload: %ls", wzPayloadId); + } + + pDownloadSource = &pPayload->downloadSource; + } + else if (wzPackageOrContainerId && *wzPackageOrContainerId) + { + hr = ContainerFindById(&pContext->pEngineState->containers, wzPackageOrContainerId, &pContainer); + ExitOnFailure(hr, "BA requested unknown container with id: %ls", wzPackageOrContainerId); + + pDownloadSource = &pContainer->downloadSource; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "BA did not provide container or payload id."); + } + + if (wzUrl && *wzUrl) + { + hr = StrAllocString(&pDownloadSource->sczUrl, wzUrl, 0); + ExitOnFailure(hr, "Failed to set download URL."); + + if (wzUser && *wzUser) + { + hr = StrAllocString(&pDownloadSource->sczUser, wzUser, 0); + ExitOnFailure(hr, "Failed to set download user."); + + if (wzPassword && *wzPassword) + { + hr = StrAllocString(&pDownloadSource->sczPassword, wzPassword, 0); + ExitOnFailure(hr, "Failed to set download password."); + } + else // no password. + { + ReleaseNullStr(pDownloadSource->sczPassword); + } + } + else // no user means no password either. + { + ReleaseNullStr(pDownloadSource->sczUser); + ReleaseNullStr(pDownloadSource->sczPassword); + } + } + else // no URL provided means clear out the whole download source. + { + ReleaseNullStr(pDownloadSource->sczUrl); + ReleaseNullStr(pDownloadSource->sczUser); + ReleaseNullStr(pDownloadSource->sczPassword); + } + +LExit: + ::LeaveCriticalSection(&pContext->pEngineState->csActive); + return hr; +} + +static HRESULT BAEngineSetVariableNumeric( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const BAENGINE_SETVARIABLENUMERIC_ARGS* pArgs, + __in BAENGINE_SETVARIABLENUMERIC_RESULTS* /*pResults*/ + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzVariable = pArgs->wzVariable; + LONGLONG llValue = pArgs->llValue; + + if (wzVariable && *wzVariable) + { + hr = VariableSetNumeric(&pContext->pEngineState->variables, wzVariable, llValue, FALSE); + ExitOnFailure(hr, "Failed to set numeric variable."); + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "BA did not provide variable name."); + } + +LExit: + return hr; +} + +static HRESULT BAEngineSetVariableString( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const BAENGINE_SETVARIABLESTRING_ARGS* pArgs, + __in BAENGINE_SETVARIABLESTRING_RESULTS* /*pResults*/ + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzVariable = pArgs->wzVariable; + LPCWSTR wzValue = pArgs->wzValue; + + if (wzVariable && *wzVariable) + { + hr = VariableSetString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE); + ExitOnFailure(hr, "Failed to set numeric variable."); + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "BA did not provide variable name."); + } + +LExit: + return hr; +} + +static HRESULT BAEngineSetVariableVersion( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const BAENGINE_SETVARIABLEVERSION_ARGS* pArgs, + __in BAENGINE_SETVARIABLEVERSION_RESULTS* /*pResults*/ + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzVariable = pArgs->wzVariable; + DWORD64 qwValue = pArgs->qwValue; + + if (wzVariable && *wzVariable) + { + hr = VariableSetVersion(&pContext->pEngineState->variables, wzVariable, qwValue, FALSE); + ExitOnFailure(hr, "Failed to set version variable."); + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "BA did not provide variable name."); + } + +LExit: + return hr; +} + +static HRESULT BAEngineCloseSplashScreen( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const BAENGINE_CLOSESPLASHSCREEN_ARGS* /*pArgs*/, + __in BAENGINE_CLOSESPLASHSCREEN_RESULTS* /*pResults*/ + ) +{ + // If the splash screen is still around, close it. + if (::IsWindow(pContext->pEngineState->command.hwndSplashScreen)) + { + ::PostMessageW(pContext->pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0); + } + + return S_OK; +} + +static HRESULT BAEngineDetect( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in BAENGINE_DETECT_ARGS* pArgs, + __in BAENGINE_DETECT_RESULTS* /*pResults*/ + ) +{ + HRESULT hr = S_OK; + + if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_DETECT, 0, reinterpret_cast(pArgs->hwndParent))) + { + ExitWithLastError(hr, "Failed to post detect message."); + } + +LExit: + return hr; +} + +static HRESULT BAEnginePlan( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const BAENGINE_PLAN_ARGS* pArgs, + __in BAENGINE_PLAN_RESULTS* /*pResults*/ + ) +{ + HRESULT hr = S_OK; + BOOTSTRAPPER_ACTION action = pArgs->action; + + if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_PLAN, 0, action)) + { + ExitWithLastError(hr, "Failed to post plan message."); + } + +LExit: + return hr; +} + +static HRESULT BAEngineElevate( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const BAENGINE_ELEVATE_ARGS* pArgs, + __in BAENGINE_ELEVATE_RESULTS* /*pResults*/ + ) +{ + HRESULT hr = S_OK; + + if (INVALID_HANDLE_VALUE != pContext->pEngineState->companionConnection.hPipe) + { + hr = HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED); + } + else if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_ELEVATE, 0, reinterpret_cast(pArgs->hwndParent))) + { + ExitWithLastError(hr, "Failed to post elevate message."); + } + +LExit: + return hr; +} + +static HRESULT BAEngineApply( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const BAENGINE_APPLY_ARGS* pArgs, + __in BAENGINE_APPLY_RESULTS* /*pResults*/ + ) +{ + HRESULT hr = S_OK; + + if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_APPLY, 0, reinterpret_cast(pArgs->hwndParent))) + { + ExitWithLastError(hr, "Failed to post apply message."); + } + +LExit: + return hr; +} + +static HRESULT BAEngineQuit( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const BAENGINE_QUIT_ARGS* pArgs, + __in BAENGINE_QUIT_RESULTS* /*pResults*/ + ) +{ + HRESULT hr = S_OK; + + if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_QUIT, static_cast(pArgs->dwExitCode), 0)) + { + ExitWithLastError(hr, "Failed to post shutdown message."); + } + +LExit: + return hr; +} + +static HRESULT BAEngineLaunchApprovedExe( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const BAENGINE_LAUNCHAPPROVEDEXE_ARGS* pArgs, + __in BAENGINE_LAUNCHAPPROVEDEXE_RESULTS* /*pResults*/ + ) +{ + HRESULT hr = S_OK; + BURN_APPROVED_EXE* pApprovedExe = NULL; + BOOL fLeaveCriticalSection = FALSE; + BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe = (BURN_LAUNCH_APPROVED_EXE*)MemAlloc(sizeof(BURN_LAUNCH_APPROVED_EXE), TRUE); + HWND hwndParent = pArgs->hwndParent; + LPCWSTR wzApprovedExeForElevationId = pArgs->wzApprovedExeForElevationId; + LPCWSTR wzArguments = pArgs->wzArguments; + DWORD dwWaitForInputIdleTimeout = pArgs->dwWaitForInputIdleTimeout; + + ::EnterCriticalSection(&pContext->pEngineState->csActive); + fLeaveCriticalSection = TRUE; + hr = UserExperienceEnsureEngineInactive(&pContext->pEngineState->userExperience); + ExitOnFailure(hr, "Engine is active, cannot change engine state."); + + if (!wzApprovedExeForElevationId || !*wzApprovedExeForElevationId) + { + ExitFunction1(hr = E_INVALIDARG); + } + + hr = ApprovedExesFindById(&pContext->pEngineState->approvedExes, wzApprovedExeForElevationId, &pApprovedExe); + ExitOnFailure(hr, "BA requested unknown approved exe with id: %ls", wzApprovedExeForElevationId); + + ::LeaveCriticalSection(&pContext->pEngineState->csActive); + fLeaveCriticalSection = FALSE; + + hr = StrAllocString(&pLaunchApprovedExe->sczId, wzApprovedExeForElevationId, NULL); + ExitOnFailure(hr, "Failed to copy the id."); + + if (wzArguments) + { + hr = StrAllocString(&pLaunchApprovedExe->sczArguments, wzArguments, NULL); + ExitOnFailure(hr, "Failed to copy the arguments."); + } + + pLaunchApprovedExe->dwWaitForInputIdleTimeout = dwWaitForInputIdleTimeout; + + pLaunchApprovedExe->hwndParent = hwndParent; + + if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_LAUNCH_APPROVED_EXE, 0, reinterpret_cast(pLaunchApprovedExe))) + { + ExitWithLastError(hr, "Failed to post launch approved exe message."); + } + +LExit: + if (fLeaveCriticalSection) + { + ::LeaveCriticalSection(&pContext->pEngineState->csActive); + } + + if (FAILED(hr)) + { + ApprovedExesUninitializeLaunch(pLaunchApprovedExe); + } + + return hr; +} + +HRESULT WINAPI EngineForApplicationProc( + __in BOOTSTRAPPER_ENGINE_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults, + __in_opt LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + BOOTSTRAPPER_ENGINE_CONTEXT* pContext = reinterpret_cast(pvContext); + + if (!pContext || !pvArgs || !pvResults) + { + ExitFunction1(hr = E_INVALIDARG); + } + + switch (message) + { + case BOOTSTRAPPER_ENGINE_MESSAGE_GETPACKAGECOUNT: + hr = BAEngineGetPackageCount(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLENUMERIC: + hr = BAEngineGetVariableNumeric(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLESTRING: + hr = BAEngineGetVariableString(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLEVERSION: + hr = BAEngineGetVariableVersion(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_FORMATSTRING: + hr = BAEngineFormatString(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_ESCAPESTRING: + hr = BAEngineEscapeString(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_EVALUATECONDITION: + hr = BAEngineEvaluateCondition(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_LOG: + hr = BAEngineLog(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_SENDEMBEDDEDERROR: + hr = BAEngineSendEmbeddedError(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_SENDEMBEDDEDPROGRESS: + hr = BAEngineSendEmbeddedProgress(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_SETUPDATE: + hr = BAEngineSetUpdate(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_SETLOCALSOURCE: + hr = BAEngineSetLocalSource(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_SETDOWNLOADSOURCE: + hr = BAEngineSetDownloadSource(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLENUMERIC: + hr = BAEngineSetVariableNumeric(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLESTRING: + hr = BAEngineSetVariableString(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLEVERSION: + hr = BAEngineSetVariableVersion(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_CLOSESPLASHSCREEN: + hr = BAEngineCloseSplashScreen(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_DETECT: + hr = BAEngineDetect(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_PLAN: + hr = BAEnginePlan(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_ELEVATE: + hr = BAEngineElevate(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_APPLY: + hr = BAEngineApply(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_QUIT: + hr = BAEngineQuit(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_LAUNCHAPPROVEDEXE: + hr = BAEngineLaunchApprovedExe(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + default: + hr = E_NOTIMPL; + break; + } + +LExit: + return hr; +} diff --git a/src/engine/EngineForApplication.h b/src/engine/EngineForApplication.h new file mode 100644 index 00000000..1b755acc --- /dev/null +++ b/src/engine/EngineForApplication.h @@ -0,0 +1,44 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +// constants + +enum WM_BURN +{ + WM_BURN_FIRST = WM_APP + 0xFFF, // this enum value must always be first. + + WM_BURN_DETECT, + WM_BURN_PLAN, + WM_BURN_ELEVATE, + WM_BURN_APPLY, + WM_BURN_LAUNCH_APPROVED_EXE, + WM_BURN_QUIT, + + WM_BURN_LAST, // this enum value must always be last. +}; + +// structs + +struct BOOTSTRAPPER_ENGINE_CONTEXT +{ + BURN_ENGINE_STATE* pEngineState; + DWORD dwThreadId; +}; + +// function declarations + +HRESULT WINAPI EngineForApplicationProc( + __in BOOTSTRAPPER_ENGINE_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults, + __in_opt LPVOID pvContext + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp new file mode 100644 index 00000000..0cef9ac8 --- /dev/null +++ b/src/engine/apply.cpp @@ -0,0 +1,2516 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +const DWORD BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS = 2; + +// structs + +struct BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT +{ + BURN_USER_EXPERIENCE* pUX; + BURN_CONTAINER* pContainer; + BURN_PACKAGE* pPackage; + BURN_PAYLOAD* pPayload; + DWORD64 qwCacheProgress; + DWORD64 qwTotalCacheSize; + + BOOL fCancel; + BOOL fError; +}; + +typedef struct _BURN_EXECUTE_CONTEXT +{ + BURN_USER_EXPERIENCE* pUX; + BOOL fRollback; + BURN_PACKAGE* pExecutingPackage; + DWORD cExecutedPackages; + DWORD cExecutePackagesTotal; + DWORD* pcOverallProgressTicks; +} BURN_EXECUTE_CONTEXT; + + +// internal function declarations +static HRESULT WINAPI AuthenticationRequired( + __in LPVOID pData, + __in HINTERNET hUrl, + __in long lHttpCode, + __out BOOL* pfRetrySend, + __out BOOL* pfRetry + ); + +static HRESULT ExecuteDependentRegistrationActions( + __in HANDLE hPipe, + __in const BURN_REGISTRATION* pRegistration, + __in_ecount(cActions) const BURN_DEPENDENT_REGISTRATION_ACTION* rgActions, + __in DWORD cActions + ); +static HRESULT ExtractContainer( + __in HANDLE hSourceEngineFile, + __in BURN_CONTAINER* pContainer, + __in_z LPCWSTR wzContainerPath, + __in_ecount(cExtractPayloads) BURN_EXTRACT_PAYLOAD* rgExtractPayloads, + __in DWORD cExtractPayloads + ); +static void UpdateCacheSuccessProgress( + __in BURN_PLAN* pPlan, + __in BURN_CACHE_ACTION* pCacheAction, + __inout DWORD64* pqwSuccessfulCachedProgress + ); +static HRESULT LayoutBundle( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_VARIABLES* pVariables, + __in HANDLE hPipe, + __in_z LPCWSTR wzExecutableName, + __in_z LPCWSTR wzLayoutDirectory, + __in_z LPCWSTR wzUnverifiedPath, + __in DWORD64 qwSuccessfulCacheProgress, + __in DWORD64 qwTotalCacheSize + ); +static HRESULT AcquireContainerOrPayload( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_VARIABLES* pVariables, + __in_opt BURN_CONTAINER* pContainer, + __in_opt BURN_PACKAGE* pPackage, + __in_opt BURN_PAYLOAD* pPayload, + __in LPCWSTR wzDestinationPath, + __in DWORD64 qwSuccessfulCacheProgress, + __in DWORD64 qwTotalCacheSize + ); +static HRESULT LayoutOrCacheContainerOrPayload( + __in BURN_USER_EXPERIENCE* pUX, + __in HANDLE hPipe, + __in_opt BURN_CONTAINER* pContainer, + __in_opt BURN_PACKAGE* pPackage, + __in_opt BURN_PAYLOAD* pPayload, + __in BOOL fAlreadyProvidedProgress, + __in DWORD64 qwSuccessfullyCacheProgress, + __in DWORD64 qwTotalCacheSize, + __in_z_opt LPCWSTR wzLayoutDirectory, + __in_z LPCWSTR wzUnverifiedPath, + __in BOOL fMove, + __in DWORD cTryAgainAttempts, + __out BOOL* pfRetry + ); +static HRESULT PromptForSource( + __in BURN_USER_EXPERIENCE* pUX, + __in_z LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in_z LPCWSTR wzLocalSource, + __in_z_opt LPCWSTR wzDownloadSource, + __out BOOL* pfRetry, + __out BOOL* pfDownload + ); +static HRESULT CopyPayload( + __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, + __in_z LPCWSTR wzSourcePath, + __in_z LPCWSTR wzDestinationPath + ); +static HRESULT DownloadPayload( + __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, + __in_z LPCWSTR wzDestinationPath + ); +static DWORD CALLBACK CacheProgressRoutine( + __in LARGE_INTEGER TotalFileSize, + __in LARGE_INTEGER TotalBytesTransferred, + __in LARGE_INTEGER StreamSize, + __in LARGE_INTEGER StreamBytesTransferred, + __in DWORD dwStreamNumber, + __in DWORD dwCallbackReason, + __in HANDLE hSourceFile, + __in HANDLE hDestinationFile, + __in_opt LPVOID lpData + ); +static void DoRollbackCache( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PLAN* pPlan, + __in HANDLE hPipe, + __in DWORD dwCheckpoint + ); +static HRESULT DoExecuteAction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in_opt HANDLE hCacheThread, + __in BURN_EXECUTE_CONTEXT* pContext, + __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, + __out DWORD* pdwCheckpoint, + __out BOOL* pfKeepRegistration, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +static HRESULT DoRollbackActions( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_CONTEXT* pContext, + __in DWORD dwCheckpoint, + __in BOOL fInTransaction, + __out BOOL* pfKeepRegistration, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +static HRESULT ExecuteExePackage( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_EXECUTE_CONTEXT* pContext, + __in BOOL fRollback, + __out BOOL* pfRetry, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +static HRESULT ExecuteMsiPackage( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_EXECUTE_CONTEXT* pContext, + __in BOOL fRollback, + __out BOOL* pfRetry, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +static HRESULT ExecuteMspPackage( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_EXECUTE_CONTEXT* pContext, + __in BOOL fRollback, + __out BOOL* pfRetry, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +static HRESULT ExecuteMsuPackage( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_EXECUTE_CONTEXT* pContext, + __in BOOL fRollback, + __in BOOL fStopWusaService, + __out BOOL* pfRetry, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +static HRESULT ExecutePackageProviderAction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pAction, + __in BURN_EXECUTE_CONTEXT* pContext + ); +static HRESULT ExecuteDependencyAction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pAction, + __in BURN_EXECUTE_CONTEXT* pContext + ); +static HRESULT ExecuteCompatiblePackageAction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pAction + ); +static HRESULT CleanPackage( + __in HANDLE hElevatedPipe, + __in BURN_PACKAGE* pPackage + ); +static int GenericExecuteMessageHandler( + __in GENERIC_EXECUTE_MESSAGE* pMessage, + __in LPVOID pvContext + ); +static int MsiExecuteMessageHandler( + __in WIU_MSI_EXECUTE_MESSAGE* pMessage, + __in_opt LPVOID pvContext + ); +static HRESULT ReportOverallProgressTicks( + __in BURN_USER_EXPERIENCE* pUX, + __in BOOL fRollback, + __in DWORD cOverallProgressTicksTotal, + __in DWORD cOverallProgressTicks + ); +static HRESULT ExecutePackageComplete( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_VARIABLES* pVariables, + __in BURN_PACKAGE* pPackage, + __in HRESULT hrOverall, + __in HRESULT hrExecute, + __in BOOL fRollback, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart, + __out BOOL* pfRetry, + __out BOOL* pfSuspend + ); + +static HRESULT DoMsiBeginTransaction( + __in BURN_EXECUTE_CONTEXT *context + , __in BURN_ENGINE_STATE* pEngineState +); +static HRESULT DoMsiCommitTransaction( + __in BURN_EXECUTE_CONTEXT *context + , __in BURN_ENGINE_STATE* pEngineState +); +static HRESULT DoMsiRollbackTransaction( + __in BURN_EXECUTE_CONTEXT *context + , __in BURN_ENGINE_STATE* pEngineState +); +static HRESULT ExecuteMsiBeginTransaction( + __in BURN_EXECUTE_CONTEXT* pContext + , __in BURN_ENGINE_STATE* pEngineState +); +static HRESULT ExecuteMsiCommitTransaction( + __in BURN_EXECUTE_CONTEXT* pContext + , __in BURN_ENGINE_STATE* pEngineState +); +static HRESULT ExecuteMsiRollbackTransaction( + __in BURN_EXECUTE_CONTEXT* pContext + , __in BURN_ENGINE_STATE* pEngineState +); + +// function definitions + +extern "C" void ApplyInitialize() +{ + // Prevent the system from sleeping. + ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED); +} + +extern "C" void ApplyUninitialize() +{ + ::SetThreadExecutionState(ES_CONTINUOUS); +} + +extern "C" HRESULT ApplySetVariables( + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + + hr = VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, NULL, TRUE); + ExitOnFailure(hr, "Failed to set the bundle forced restart package built-in variable."); + +LExit: + return hr; +} + +extern "C" void ApplyReset( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PACKAGES* pPackages + ) +{ + UserExperienceExecuteReset(pUX); + + for (DWORD i = 0; i < pPackages->cPackages; ++i) + { + BURN_PACKAGE* pPackage = pPackages->rgPackages + i; + pPackage->hrCacheResult = S_OK; + } +} + +extern "C" HRESULT ApplyLock( + __in BOOL /*fPerMachine*/, + __out HANDLE* /*phLock*/ + ) +{ + HRESULT hr = S_OK; +#if 0 // eventually figure out the correct way to support this. In its current form, embedded bundles (including related bundles) are hosed. + DWORD er = ERROR_SUCCESS; + HANDLE hLock = NULL; + + hLock = ::CreateMutexW(NULL, TRUE, fPerMachine ? L"Global\\WixBurnExecutionLock" : L"Local\\WixBurnExecutionLock"); + ExitOnNullWithLastError(hLock, hr, "Failed to create lock."); + + er = ::GetLastError(); + if (ERROR_ALREADY_EXISTS == er) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSTALL_ALREADY_RUNNING)); + } + + *phLock = hLock; + hLock = NULL; + +LExit: + ReleaseHandle(hLock); +#endif + return hr; +} + +extern "C" HRESULT ApplyRegister( + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + LPWSTR sczEngineWorkingPath = NULL; + + hr = UserExperienceOnRegisterBegin(&pEngineState->userExperience); + ExitOnRootFailure(hr, "BA aborted register begin."); + + // If we have a resume mode that suggests the bundle is on the machine. + if (BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING < pEngineState->command.resumeType) + { + // resume previous session + if (pEngineState->registration.fPerMachine) + { + hr = ElevationSessionResume(pEngineState->companionConnection.hPipe, pEngineState->registration.sczResumeCommandLine, pEngineState->registration.fDisableResume, &pEngineState->variables); + ExitOnFailure(hr, "Failed to resume registration session in per-machine process."); + } + else + { + hr = RegistrationSessionResume(&pEngineState->registration, &pEngineState->variables); + ExitOnFailure(hr, "Failed to resume registration session."); + } + } + else // need to complete registration on the machine. + { + hr = CacheCalculateBundleWorkingPath(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &sczEngineWorkingPath); + ExitOnFailure(hr, "Failed to calculate working path for engine."); + + // begin new session + if (pEngineState->registration.fPerMachine) + { + hr = ElevationSessionBegin(pEngineState->companionConnection.hPipe, sczEngineWorkingPath, pEngineState->registration.sczResumeCommandLine, pEngineState->registration.fDisableResume, &pEngineState->variables, pEngineState->plan.dwRegistrationOperations, pEngineState->plan.dependencyRegistrationAction, pEngineState->plan.qwEstimatedSize); + ExitOnFailure(hr, "Failed to begin registration session in per-machine process."); + } + else + { + hr = RegistrationSessionBegin(sczEngineWorkingPath, &pEngineState->registration, &pEngineState->variables, &pEngineState->userExperience, pEngineState->plan.dwRegistrationOperations, pEngineState->plan.dependencyRegistrationAction, pEngineState->plan.qwEstimatedSize); + ExitOnFailure(hr, "Failed to begin registration session."); + } + } + + // Apply any registration actions. + HRESULT hrExecuteRegistration = ExecuteDependentRegistrationActions(pEngineState->companionConnection.hPipe, &pEngineState->registration, pEngineState->plan.rgRegistrationActions, pEngineState->plan.cRegistrationActions); + UNREFERENCED_PARAMETER(hrExecuteRegistration); + + // Try to save engine state. + hr = CoreSaveEngineState(pEngineState); + if (FAILED(hr)) + { + LogErrorId(hr, MSG_STATE_NOT_SAVED, NULL, NULL, NULL); + hr = S_OK; + } + +LExit: + UserExperienceOnRegisterComplete(&pEngineState->userExperience, hr); + ReleaseStr(sczEngineWorkingPath); + + return hr; +} + +extern "C" HRESULT ApplyUnregister( + __in BURN_ENGINE_STATE* pEngineState, + __in BOOL fFailedOrRollback, + __in BOOL fKeepRegistration, + __in BOOL fSuspend, + __in BOOTSTRAPPER_APPLY_RESTART restart + ) +{ + HRESULT hr = S_OK; + BURN_RESUME_MODE resumeMode = BURN_RESUME_MODE_NONE; + + hr = UserExperienceOnUnregisterBegin(&pEngineState->userExperience); + ExitOnRootFailure(hr, "BA aborted unregister begin."); + + // Calculate the correct resume mode. If a restart has been initiated, that trumps all other + // modes. If the user chose to suspend the install then we'll use that as the resume mode. + // Barring those special cases, if it was determined that we should keep the registration then + // do that, otherwise the resume mode was initialized to none and registration will be removed. + if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart) + { + resumeMode = BURN_RESUME_MODE_REBOOT_PENDING; + } + else if (fSuspend) + { + resumeMode = BURN_RESUME_MODE_SUSPEND; + } + else if (fKeepRegistration) + { + resumeMode = BURN_RESUME_MODE_ARP; + } + + // If apply failed in any way and we're going to be keeping the bundle registered then + // execute any rollback dependency registration actions. + if (fFailedOrRollback && fKeepRegistration) + { + // Execute any rollback registration actions. + HRESULT hrRegistrationRollback = ExecuteDependentRegistrationActions(pEngineState->companionConnection.hPipe, &pEngineState->registration, pEngineState->plan.rgRollbackRegistrationActions, pEngineState->plan.cRollbackRegistrationActions); + UNREFERENCED_PARAMETER(hrRegistrationRollback); + } + + if (pEngineState->registration.fPerMachine) + { + hr = ElevationSessionEnd(pEngineState->companionConnection.hPipe, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction); + ExitOnFailure(hr, "Failed to end session in per-machine process."); + } + else + { + hr = RegistrationSessionEnd(&pEngineState->registration, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction); + ExitOnFailure(hr, "Failed to end session in per-user process."); + } + + pEngineState->resumeMode = resumeMode; + +LExit: + UserExperienceOnUnregisterComplete(&pEngineState->userExperience, hr); + + return hr; +} + +extern "C" HRESULT ApplyCache( + __in HANDLE hSourceEngineFile, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_VARIABLES* pVariables, + __in BURN_PLAN* pPlan, + __in HANDLE hPipe, + __inout DWORD* pcOverallProgressTicks, + __out BOOL* pfRollback + ) +{ + HRESULT hr = S_OK; + DWORD dwCheckpoint = 0; + BOOL fRetry = FALSE; + DWORD iRetryAction = BURN_PLAN_INVALID_ACTION_INDEX; + BURN_PACKAGE* pStartedPackage = NULL; + DWORD64 qwSuccessfulCachedProgress = 0; + + // Allow us to retry and skip packages. + DWORD iPackageStartAction = BURN_PLAN_INVALID_ACTION_INDEX; + DWORD iPackageCompleteAction = BURN_PLAN_INVALID_ACTION_INDEX; + + hr = UserExperienceOnCacheBegin(pUX); + ExitOnRootFailure(hr, "BA aborted cache."); + + do + { + hr = S_OK; + fRetry = FALSE; + + // Allow us to retry just a container or payload. + LPCWSTR wzRetryId = NULL; + DWORD iRetryContainerOrPayloadAction = BURN_PLAN_INVALID_ACTION_INDEX; + BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION cachePackageCompleteAction = BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE; + + // cache actions + for (DWORD i = (BURN_PLAN_INVALID_ACTION_INDEX == iRetryAction) ? 0 : iRetryAction; SUCCEEDED(hr) && i < pPlan->cCacheActions; ++i) + { + BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + i; + BOOL fRetryContainerOrPayload = FALSE; + cachePackageCompleteAction = BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE; + + if (pCacheAction->fSkipUntilRetried) + { + // If this action was retried, let's make sure it will not be skipped any longer. + if (iRetryAction == i) + { + pCacheAction->fSkipUntilRetried = FALSE; + } + else // skip the action. + { + continue; + } + } + + switch (pCacheAction->type) + { + case BURN_CACHE_ACTION_TYPE_CHECKPOINT: + dwCheckpoint = pCacheAction->checkpoint.dwId; + break; + + case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: + hr = LayoutBundle(pUX, pVariables, hPipe, pCacheAction->bundleLayout.sczExecutableName, pCacheAction->bundleLayout.sczLayoutDirectory, pCacheAction->bundleLayout.sczUnverifiedPath, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal); + if (SUCCEEDED(hr)) + { + UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); + ++(*pcOverallProgressTicks); + + hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks); + if (FAILED(hr)) + { + LogErrorId(hr, MSG_USER_CANCELED, L"layout bundle", NULL, NULL); + } + } + break; + + case BURN_CACHE_ACTION_TYPE_PACKAGE_START: + iPackageStartAction = i; // if we retry this package, we'll start here in the plan. + iPackageCompleteAction = pCacheAction->packageStart.iPackageCompleteAction; // if we ignore this package, we'll start after the complete action in the plan. + pStartedPackage = pCacheAction->packageStart.pPackage; + + hr = UserExperienceOnCachePackageBegin(pUX, pStartedPackage->sczId, pCacheAction->packageStart.cCachePayloads, pCacheAction->packageStart.qwCachePayloadSizeTotal); + if (FAILED(hr)) + { + LogErrorId(hr, MSG_USER_CANCELED, L"begin cache package", pStartedPackage->sczId, NULL); + } + break; + + case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER: + hr = AcquireContainerOrPayload(pUX, pVariables, pCacheAction->resolveContainer.pContainer, NULL, NULL, pCacheAction->resolveContainer.sczUnverifiedPath, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal); + if (SUCCEEDED(hr)) + { + UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); + } + else + { + LogErrorId(hr, MSG_FAILED_ACQUIRE_CONTAINER, pCacheAction->resolveContainer.pContainer->sczId, pCacheAction->resolveContainer.sczUnverifiedPath, NULL); + } + break; + + case BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER: + // If this action is to be skipped until the acquire action is not skipped and the other + // action is still being skipped then skip this action. + if (BURN_PLAN_INVALID_ACTION_INDEX != pCacheAction->extractContainer.iSkipUntilAcquiredByAction && pPlan->rgCacheActions[pCacheAction->extractContainer.iSkipUntilAcquiredByAction].fSkipUntilRetried) + { + break; + } + + hr = ExtractContainer(hSourceEngineFile, pCacheAction->extractContainer.pContainer, pCacheAction->extractContainer.sczContainerUnverifiedPath, pCacheAction->extractContainer.rgPayloads, pCacheAction->extractContainer.cPayloads); + if (FAILED(hr)) + { + LogErrorId(hr, MSG_FAILED_EXTRACT_CONTAINER, pCacheAction->extractContainer.pContainer->sczId, pCacheAction->extractContainer.sczContainerUnverifiedPath, NULL); + } + break; + + case BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER: + hr = LayoutOrCacheContainerOrPayload(pUX, hPipe, pCacheAction->layoutContainer.pContainer, pCacheAction->layoutContainer.pPackage, NULL, pPlan->rgContainerProgress[pCacheAction->layoutContainer.iProgress].fCachedDuringApply, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal, pCacheAction->layoutContainer.sczLayoutDirectory, pCacheAction->layoutContainer.sczUnverifiedPath, pCacheAction->layoutContainer.fMove, pCacheAction->layoutContainer.cTryAgainAttempts, &fRetryContainerOrPayload); + if (SUCCEEDED(hr)) + { + UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); + } + else + { + LogErrorId(hr, MSG_FAILED_LAYOUT_CONTAINER, pCacheAction->layoutContainer.pContainer->sczId, pCacheAction->layoutContainer.sczLayoutDirectory, pCacheAction->layoutContainer.sczUnverifiedPath); + + if (fRetryContainerOrPayload) + { + wzRetryId = pCacheAction->layoutContainer.pContainer->sczId; + iRetryContainerOrPayloadAction = pCacheAction->layoutContainer.iTryAgainAction; + + ++pCacheAction->layoutContainer.cTryAgainAttempts; + } + } + break; + + case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD: + hr = AcquireContainerOrPayload(pUX, pVariables, NULL, pCacheAction->resolvePayload.pPackage, pCacheAction->resolvePayload.pPayload, pCacheAction->resolvePayload.sczUnverifiedPath, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal); + if (SUCCEEDED(hr)) + { + UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); + } + else + { + LogErrorId(hr, MSG_FAILED_ACQUIRE_PAYLOAD, pCacheAction->resolvePayload.pPayload->sczKey, pCacheAction->resolvePayload.sczUnverifiedPath, NULL); + } + break; + + case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD: + hr = LayoutOrCacheContainerOrPayload(pUX, pCacheAction->cachePayload.pPackage->fPerMachine ? hPipe : INVALID_HANDLE_VALUE, NULL, pCacheAction->cachePayload.pPackage, pCacheAction->cachePayload.pPayload, pPlan->rgPayloadProgress[pCacheAction->cachePayload.iProgress].fCachedDuringApply, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal, NULL, pCacheAction->cachePayload.sczUnverifiedPath, pCacheAction->cachePayload.fMove, pCacheAction->cachePayload.cTryAgainAttempts, &fRetryContainerOrPayload); + if (SUCCEEDED(hr)) + { + UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); + } + else + { + LogErrorId(hr, MSG_FAILED_CACHE_PAYLOAD, pCacheAction->cachePayload.pPayload->sczKey, pCacheAction->cachePayload.sczUnverifiedPath, NULL); + + if (fRetryContainerOrPayload) + { + wzRetryId = pCacheAction->cachePayload.pPayload->sczKey; + iRetryContainerOrPayloadAction = pCacheAction->cachePayload.iTryAgainAction; + + ++pCacheAction->cachePayload.cTryAgainAttempts; + } + } + break; + + case BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD: + hr = LayoutOrCacheContainerOrPayload(pUX, hPipe, NULL, pCacheAction->layoutPayload.pPackage, pCacheAction->layoutPayload.pPayload, pPlan->rgPayloadProgress[pCacheAction->layoutPayload.iProgress].fCachedDuringApply, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal, pCacheAction->layoutPayload.sczLayoutDirectory, pCacheAction->layoutPayload.sczUnverifiedPath, pCacheAction->layoutPayload.fMove, pCacheAction->layoutPayload.cTryAgainAttempts, &fRetryContainerOrPayload); + if (SUCCEEDED(hr)) + { + UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); + } + else + { + LogErrorId(hr, MSG_FAILED_LAYOUT_PAYLOAD, pCacheAction->layoutPayload.pPayload->sczKey, pCacheAction->layoutPayload.sczLayoutDirectory, pCacheAction->layoutPayload.sczUnverifiedPath); + + if (fRetryContainerOrPayload) + { + wzRetryId = pCacheAction->layoutPayload.pPayload->sczKey; + iRetryContainerOrPayloadAction = pCacheAction->layoutPayload.iTryAgainAction; + + ++pCacheAction->layoutPayload.cTryAgainAttempts; + } + } + break; + + case BURN_CACHE_ACTION_TYPE_PACKAGE_STOP: + AssertSz(pStartedPackage == pCacheAction->packageStop.pPackage, "Expected package started cached to be the same as the package checkpointed."); + + hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks + 1); + if (FAILED(hr)) + { + LogErrorId(hr, MSG_USER_CANCELED, L"end cache package", NULL, NULL); + } + else + { + ++(*pcOverallProgressTicks); + + UserExperienceOnCachePackageComplete(pUX, pStartedPackage->sczId, hr, &cachePackageCompleteAction); + + pStartedPackage->hrCacheResult = hr; + + iPackageStartAction = BURN_PLAN_INVALID_ACTION_INDEX; + iPackageCompleteAction = BURN_PLAN_INVALID_ACTION_INDEX; + pStartedPackage = NULL; + } + break; + + case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT: + if (!::SetEvent(pCacheAction->syncpoint.hEvent)) + { + ExitWithLastError(hr, "Failed to set syncpoint event."); + } + break; + + default: + AssertSz(FALSE, "Unknown cache action."); + break; + } + } + + if (BURN_PLAN_INVALID_ACTION_INDEX != iRetryContainerOrPayloadAction) + { + Assert(wzRetryId); + + LogErrorId(hr, MSG_APPLY_RETRYING_PAYLOAD, wzRetryId, NULL, NULL); + + iRetryAction = iRetryContainerOrPayloadAction; + fRetry = TRUE; + } + else if (pStartedPackage) + { + Assert(BURN_PLAN_INVALID_ACTION_INDEX != iPackageStartAction); + Assert(BURN_PLAN_INVALID_ACTION_INDEX != iPackageCompleteAction); + + cachePackageCompleteAction = SUCCEEDED(hr) || pStartedPackage->fVital ? BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE; + UserExperienceOnCachePackageComplete(pUX, pStartedPackage->sczId, hr, &cachePackageCompleteAction); + if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_RETRY == cachePackageCompleteAction) + { + LogErrorId(hr, MSG_APPLY_RETRYING_PACKAGE, pStartedPackage->sczId, NULL, NULL); + + iRetryAction = iPackageStartAction; + fRetry = TRUE; + } + else if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE == cachePackageCompleteAction && !pStartedPackage->fVital) // ignore non-vital download failures. + { + LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, pStartedPackage->sczId, hr); + + ++(*pcOverallProgressTicks); // add progress even though we didn't fully cache the package. + + iRetryAction = iPackageCompleteAction + 1; + fRetry = TRUE; + } + + pStartedPackage->hrCacheResult = hr; + + iPackageStartAction = BURN_PLAN_INVALID_ACTION_INDEX; + iPackageCompleteAction = BURN_PLAN_INVALID_ACTION_INDEX; + pStartedPackage = NULL; + } + else + { + Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageStartAction); + Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageCompleteAction); + } + } while (fRetry); + +LExit: + Assert(NULL == pStartedPackage); + Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageStartAction); + Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageCompleteAction); + + if (FAILED(hr)) + { + DoRollbackCache(pUX, pPlan, hPipe, dwCheckpoint); + *pfRollback = TRUE; + } + + // Clean up any remanents in the cache. + if (INVALID_HANDLE_VALUE != hPipe) + { + ElevationCacheCleanup(hPipe); + } + + CacheCleanup(FALSE, pPlan->wzBundleId); + + UserExperienceOnCacheComplete(pUX, hr); + return hr; +} + +extern "C" HRESULT ApplyExecute( + __in BURN_ENGINE_STATE* pEngineState, + __in_opt HANDLE hCacheThread, + __inout DWORD* pcOverallProgressTicks, + __out BOOL* pfKeepRegistration, + __out BOOL* pfRollback, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + DWORD dwCheckpoint = 0; + BURN_EXECUTE_CONTEXT context = { }; + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; + BOOL fSeekNextRollbackBoundary = FALSE; + BOOL fInTransaction = FALSE; + + context.pUX = &pEngineState->userExperience; + context.cExecutePackagesTotal = pEngineState->plan.cExecutePackagesTotal; + context.pcOverallProgressTicks = pcOverallProgressTicks; + + // Send execute begin to BA. + hr = UserExperienceOnExecuteBegin(&pEngineState->userExperience, pEngineState->plan.cExecutePackagesTotal); + ExitOnRootFailure(hr, "BA aborted execute begin."); + + // Do execute actions. + for (DWORD i = 0; i < pEngineState->plan.cExecuteActions; ++i) + { + BURN_EXECUTE_ACTION* pExecuteAction = &pEngineState->plan.rgExecuteActions[i]; + if (pExecuteAction->fDeleted) + { + continue; + } + + // Transaction end/start + if (BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY == pExecuteAction->type) + { + // End previous transaction + if (fInTransaction) + { + LogString(REPORT_STANDARD, "Committing MSI transaction\n"); + hr = DoMsiCommitTransaction(&context, pEngineState); + ExitOnFailure(hr, "Failed committing an MSI transaction"); + fInTransaction = FALSE; + } + + // Start New transaction + if (!fInTransaction && pExecuteAction->rollbackBoundary.pRollbackBoundary && pExecuteAction->rollbackBoundary.pRollbackBoundary->fTransaction) + { + // Transactions don't go together with DisableRollback. + if (pEngineState->fDisableRollback) + { + LogString(REPORT_STANDARD, "Ignoring Transaction flag due to DisableRollback flag\n"); + } + else + { + LogString(REPORT_STANDARD, "Starting a new MSI transaction\n"); + hr = DoMsiBeginTransaction(&context, pEngineState); + ExitOnFailure(hr, "Failed beginning an MSI transaction"); + fInTransaction = TRUE; + } + } + } + + // If we are seeking the next rollback boundary, skip if this action wasn't it. + if (fSeekNextRollbackBoundary) + { + if (BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY == pExecuteAction->type) + { + continue; + } + else + { + fSeekNextRollbackBoundary = FALSE; + } + } + + // Execute the action. + hr = DoExecuteAction(pEngineState, pExecuteAction, hCacheThread, &context, &pRollbackBoundary, &dwCheckpoint, pfKeepRegistration, pfSuspend, pRestart); + + if (*pfSuspend || BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart) + { + if (fInTransaction) + { + hr = E_INVALIDSTATE; + LogString(REPORT_ERROR, "Ilegal state: Reboot requested within an MSI transaction. Transaction will rollback."); + } + else + { + ExitFunction(); + } + } + + if (FAILED(hr)) + { + // If we failed, but rollback is disabled just bail with our error code. + if (pEngineState->fDisableRollback) + { + *pfRollback = TRUE; + break; + } + else // the action failed, roll back to previous rollback boundary. + { + HRESULT hrRollback = DoRollbackActions(pEngineState, &context, dwCheckpoint, fInTransaction, pfKeepRegistration, pRestart); + UNREFERENCED_PARAMETER(hrRollback); + fInTransaction = FALSE; + + // If the rollback boundary is vital, end execution here. + if (pRollbackBoundary && pRollbackBoundary->fVital) + { + *pfRollback = TRUE; + break; + } + + // Move forward to next rollback boundary. + fSeekNextRollbackBoundary = TRUE; + } + } + } + + if (fInTransaction) + { + LogString(REPORT_STANDARD, "Committing an MSI transaction\n"); + hr = DoMsiCommitTransaction(&context, pEngineState); + ExitOnFailure(hr, "Failed committing an MSI transaction"); + fInTransaction = FALSE; + } + +LExit: + // Send execute complete to BA. + UserExperienceOnExecuteComplete(&pEngineState->userExperience, hr); + + return hr; +} + +extern "C" void ApplyClean( + __in BURN_USER_EXPERIENCE* /*pUX*/, + __in BURN_PLAN* pPlan, + __in HANDLE hPipe + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < pPlan->cCleanActions; ++i) + { + BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + i; + + hr = CleanPackage(hPipe, pCleanAction->pPackage); + } +} + + +// internal helper functions + +static HRESULT ExecuteDependentRegistrationActions( + __in HANDLE hPipe, + __in const BURN_REGISTRATION* pRegistration, + __in_ecount(cActions) const BURN_DEPENDENT_REGISTRATION_ACTION* rgActions, + __in DWORD cActions + ) +{ + HRESULT hr = S_OK; + + for (DWORD iAction = 0; iAction < cActions; ++iAction) + { + const BURN_DEPENDENT_REGISTRATION_ACTION* pAction = rgActions + iAction; + + if (pRegistration->fPerMachine) + { + hr = ElevationProcessDependentRegistration(hPipe, pAction); + ExitOnFailure(hr, "Failed to execute dependent registration action."); + } + else + { + hr = DependencyProcessDependentRegistration(pRegistration, pAction); + ExitOnFailure(hr, "Failed to process dependency registration action."); + } + } + +LExit: + return hr; +} + +static HRESULT ExtractContainer( + __in HANDLE hSourceEngineFile, + __in BURN_CONTAINER* pContainer, + __in_z LPCWSTR wzContainerPath, + __in_ecount(cExtractPayloads) BURN_EXTRACT_PAYLOAD* rgExtractPayloads, + __in DWORD cExtractPayloads + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER_CONTEXT context = { }; + HANDLE hContainerHandle = INVALID_HANDLE_VALUE; + LPWSTR sczExtractPayloadId = NULL; + + // If the container is actually attached, then it was planned to be acquired through hSourceEngineFile. + if (pContainer->fActuallyAttached) + { + hContainerHandle = hSourceEngineFile; + } + + hr = ContainerOpen(&context, pContainer, hContainerHandle, wzContainerPath); + ExitOnFailure(hr, "Failed to open container: %ls.", pContainer->sczId); + + while (S_OK == (hr = ContainerNextStream(&context, &sczExtractPayloadId))) + { + BOOL fExtracted = FALSE; + + for (DWORD iExtract = 0; iExtract < cExtractPayloads; ++iExtract) + { + BURN_EXTRACT_PAYLOAD* pExtract = rgExtractPayloads + iExtract; + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczExtractPayloadId, -1, pExtract->pPayload->sczSourcePath, -1)) + { + // TODO: Send progress when extracting stream to file. + hr = ContainerStreamToFile(&context, pExtract->sczUnverifiedPath); + ExitOnFailure(hr, "Failed to extract payload: %ls from container: %ls", sczExtractPayloadId, pContainer->sczId); + + fExtracted = TRUE; + break; + } + } + + if (!fExtracted) + { + hr = ContainerSkipStream(&context); + ExitOnFailure(hr, "Failed to skip the extraction of payload: %ls from container: %ls", sczExtractPayloadId, pContainer->sczId); + } + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to extract all payloads from container: %ls", pContainer->sczId); + +LExit: + ReleaseStr(sczExtractPayloadId); + ContainerClose(&context); + + return hr; +} + +static void UpdateCacheSuccessProgress( + __in BURN_PLAN* pPlan, + __in BURN_CACHE_ACTION* pCacheAction, + __inout DWORD64* pqwSuccessfulCachedProgress + ) +{ + switch (pCacheAction->type) + { + case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: + *pqwSuccessfulCachedProgress += pCacheAction->bundleLayout.qwBundleSize; + break; + + case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER: + if (!pPlan->rgContainerProgress[pCacheAction->resolveContainer.iProgress].fCachedDuringApply) + { + pPlan->rgContainerProgress[pCacheAction->resolveContainer.iProgress].fCachedDuringApply = TRUE; + *pqwSuccessfulCachedProgress += pCacheAction->resolveContainer.pContainer->qwFileSize; + } + break; + + case BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER: + if (!pPlan->rgContainerProgress[pCacheAction->layoutContainer.iProgress].fCachedDuringApply) + { + pPlan->rgContainerProgress[pCacheAction->layoutContainer.iProgress].fCachedDuringApply = TRUE; + *pqwSuccessfulCachedProgress += pCacheAction->layoutContainer.pContainer->qwFileSize; + } + break; + + case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD: + if (!pPlan->rgPayloadProgress[pCacheAction->resolvePayload.iProgress].fCachedDuringApply) + { + pPlan->rgPayloadProgress[pCacheAction->resolvePayload.iProgress].fCachedDuringApply = TRUE; + *pqwSuccessfulCachedProgress += pCacheAction->resolvePayload.pPayload->qwFileSize; + } + break; + + case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD: + if (!pPlan->rgPayloadProgress[pCacheAction->cachePayload.iProgress].fCachedDuringApply) + { + pPlan->rgPayloadProgress[pCacheAction->cachePayload.iProgress].fCachedDuringApply = TRUE; + *pqwSuccessfulCachedProgress += pCacheAction->cachePayload.pPayload->qwFileSize; + } + break; + + case BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD: + if (!pPlan->rgPayloadProgress[pCacheAction->layoutPayload.iProgress].fCachedDuringApply) + { + pPlan->rgPayloadProgress[pCacheAction->layoutPayload.iProgress].fCachedDuringApply = TRUE; + *pqwSuccessfulCachedProgress += pCacheAction->layoutPayload.pPayload->qwFileSize; + } + break; + + default: + AssertSz(FALSE, "Unexpected cache action type."); + break; + } +} + +static HRESULT LayoutBundle( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_VARIABLES* pVariables, + __in HANDLE hPipe, + __in_z LPCWSTR wzExecutableName, + __in_z LPCWSTR wzLayoutDirectory, + __in_z LPCWSTR wzUnverifiedPath, + __in DWORD64 qwSuccessfulCacheProgress, + __in DWORD64 qwTotalCacheSize + ) +{ + HRESULT hr = S_OK; + LPWSTR sczBundlePath = NULL; + LPWSTR sczDestinationPath = NULL; + int nEquivalentPaths = 0; + BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { }; + BOOL fRetry = FALSE; + BOOL fRetryAcquire = FALSE; + + hr = VariableGetString(pVariables, BURN_BUNDLE_SOURCE_PROCESS_PATH, &sczBundlePath); + if (FAILED(hr)) + { + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get path to bundle source process path to layout."); + } + + hr = PathForCurrentProcess(&sczBundlePath, NULL); + ExitOnFailure(hr, "Failed to get path to bundle to layout."); + } + + hr = PathConcat(wzLayoutDirectory, wzExecutableName, &sczDestinationPath); + ExitOnFailure(hr, "Failed to concat layout path for bundle."); + + // If the destination path is the currently running bundle, bail. + hr = PathCompare(sczBundlePath, sczDestinationPath, &nEquivalentPaths); + ExitOnFailure(hr, "Failed to determine if layout bundle path was equivalent with current process path."); + + if (CSTR_EQUAL == nEquivalentPaths) + { + ExitFunction1(hr = S_OK); + } + + progress.pUX = pUX; + progress.qwCacheProgress = qwSuccessfulCacheProgress; + progress.qwTotalCacheSize = qwTotalCacheSize; + + do + { + hr = S_OK; + fRetry = FALSE; + + for (;;) + { + fRetryAcquire = FALSE; + progress.fCancel = FALSE; + + hr = UserExperienceOnCacheAcquireBegin(pUX, NULL, NULL, BOOTSTRAPPER_CACHE_OPERATION_COPY, sczBundlePath); + ExitOnRootFailure(hr, "BA aborted cache acquire begin."); + + hr = CopyPayload(&progress, sczBundlePath, wzUnverifiedPath); + // Error handling happens after sending complete message to BA. + + UserExperienceOnCacheAcquireComplete(pUX, NULL, NULL, hr, &fRetryAcquire); + if (fRetryAcquire) + { + continue; + } + + ExitOnFailure(hr, "Failed to copy bundle from: '%ls' to: '%ls'", sczBundlePath, wzUnverifiedPath); + break; + } + + do + { + hr = UserExperienceOnCacheVerifyBegin(pUX, NULL, NULL); + ExitOnRootFailure(hr, "BA aborted cache verify begin."); + + if (INVALID_HANDLE_VALUE != hPipe) + { + hr = ElevationLayoutBundle(hPipe, wzLayoutDirectory, wzUnverifiedPath); + } + else + { + hr = CacheLayoutBundle(wzExecutableName, wzLayoutDirectory, wzUnverifiedPath); + } + + BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE; + UserExperienceOnCacheVerifyComplete(pUX, NULL, NULL, hr, &action); + if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action) + { + hr = S_FALSE; // retry verify. + } + else if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION == action) + { + fRetry = TRUE; // go back and retry acquire. + } + } while (S_FALSE == hr); + } while (fRetry); + LogExitOnFailure(hr, MSG_FAILED_LAYOUT_BUNDLE, "Failed to layout bundle: %ls to layout directory: %ls", sczBundlePath, wzLayoutDirectory); + +LExit: + ReleaseStr(sczDestinationPath); + ReleaseStr(sczBundlePath); + + return hr; +} + +static HRESULT AcquireContainerOrPayload( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_VARIABLES* pVariables, + __in_opt BURN_CONTAINER* pContainer, + __in_opt BURN_PACKAGE* pPackage, + __in_opt BURN_PAYLOAD* pPayload, + __in LPCWSTR wzDestinationPath, + __in DWORD64 qwSuccessfulCacheProgress, + __in DWORD64 qwTotalCacheSize + ) +{ + AssertSz(pContainer || pPayload, "Must provide a container or a payload."); + + HRESULT hr = S_OK; + int nEquivalentPaths = 0; + LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : NULL; + LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : NULL; + LPCWSTR wzRelativePath = pContainer ? pContainer->sczFilePath : pPayload->sczFilePath; + LPWSTR sczSourceFullPath = NULL; + BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { }; + BOOL fRetry = FALSE; + + progress.pContainer = pContainer; + progress.pPackage = pPackage; + progress.pPayload = pPayload; + progress.pUX = pUX; + progress.qwCacheProgress = qwSuccessfulCacheProgress; + progress.qwTotalCacheSize = qwTotalCacheSize; + + do + { + LPCWSTR wzDownloadUrl = pContainer ? pContainer->downloadSource.sczUrl : pPayload->downloadSource.sczUrl; + LPCWSTR wzSourcePath = pContainer ? pContainer->sczSourcePath : pPayload->sczSourcePath; + + BOOL fFoundLocal = FALSE; + BOOL fCopy = FALSE; + BOOL fDownload = FALSE; + + fRetry = FALSE; + progress.fCancel = FALSE; + + hr = CacheFindLocalSource(wzSourcePath, pVariables, &fFoundLocal, &sczSourceFullPath); + ExitOnFailure(hr, "Failed to search local source."); + + if (fFoundLocal) // the file exists locally, so copy it. + { + // If the source path and destination path are different, do the copy (otherwise there's no point). + hr = PathCompare(sczSourceFullPath, wzDestinationPath, &nEquivalentPaths); + ExitOnFailure(hr, "Failed to determine if payload source path was equivalent to the destination path."); + + fCopy = (CSTR_EQUAL != nEquivalentPaths); + } + else // can't find the file locally, so prompt for source. + { + DWORD dwLogId = pContainer ? (wzPayloadId ? MSG_PROMPT_CONTAINER_PAYLOAD_SOURCE : MSG_PROMPT_CONTAINER_SOURCE) : pPackage ? MSG_PROMPT_PACKAGE_PAYLOAD_SOURCE : MSG_PROMPT_BUNDLE_PAYLOAD_SOURCE; + LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId ? wzPackageOrContainerId : L"", wzPayloadId ? wzPayloadId : L"", sczSourceFullPath); + + hr = PromptForSource(pUX, wzPackageOrContainerId, wzPayloadId, sczSourceFullPath, wzDownloadUrl, &fRetry, &fDownload); + + // If the BA requested download then ensure a download url is available (it may have been set + // during PromptForSource so we need to check again). + if (fDownload) + { + wzDownloadUrl = pContainer ? pContainer->downloadSource.sczUrl : pPayload->downloadSource.sczUrl; + if (!wzDownloadUrl || !*wzDownloadUrl) + { + hr = E_INVALIDARG; + } + } + + // Log the error + LogExitOnFailure(hr, MSG_PAYLOAD_FILE_NOT_PRESENT, "Failed while prompting for source (original path '%ls').", sczSourceFullPath); + } + + if (fCopy) + { + hr = UserExperienceOnCacheAcquireBegin(pUX, wzPackageOrContainerId, wzPayloadId, BOOTSTRAPPER_CACHE_OPERATION_COPY, sczSourceFullPath); + ExitOnRootFailure(hr, "BA aborted cache acquire begin."); + + hr = CopyPayload(&progress, sczSourceFullPath, wzDestinationPath); + // Error handling happens after sending complete message to BA. + + // We successfully copied from a source location, set that as the last used source. + if (SUCCEEDED(hr)) + { + CacheSetLastUsedSource(pVariables, sczSourceFullPath, wzRelativePath); + } + } + else if (fDownload) + { + hr = UserExperienceOnCacheAcquireBegin(pUX, wzPackageOrContainerId, wzPayloadId, BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD, wzDownloadUrl); + ExitOnRootFailure(hr, "BA aborted cache download payload begin."); + + hr = DownloadPayload(&progress, wzDestinationPath); + // Error handling happens after sending complete message to BA. + } + + if (fCopy || fDownload) + { + UserExperienceOnCacheAcquireComplete(pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry); + if (fRetry) + { + hr = S_OK; + } + } + ExitOnFailure(hr, "Failed to acquire payload from: '%ls' to working path: '%ls'", fCopy ? sczSourceFullPath : wzDownloadUrl, wzDestinationPath); + } while (fRetry); + ExitOnFailure(hr, "Failed to find external payload to cache."); + +LExit: + ReleaseStr(sczSourceFullPath); + + return hr; +} + +static HRESULT LayoutOrCacheContainerOrPayload( + __in BURN_USER_EXPERIENCE* pUX, + __in HANDLE hPipe, + __in_opt BURN_CONTAINER* pContainer, + __in_opt BURN_PACKAGE* pPackage, + __in_opt BURN_PAYLOAD* pPayload, + __in BOOL fAlreadyProvidedProgress, + __in DWORD64 qwSuccessfulCachedProgress, + __in DWORD64 qwTotalCacheSize, + __in_z_opt LPCWSTR wzLayoutDirectory, + __in_z LPCWSTR wzUnverifiedPath, + __in BOOL fMove, + __in DWORD cTryAgainAttempts, + __out BOOL* pfRetry + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : L""; + LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : L""; + LARGE_INTEGER liContainerOrPayloadSize = { }; + LARGE_INTEGER liZero = { }; + BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { }; + + liContainerOrPayloadSize.QuadPart = pContainer ? pContainer->qwFileSize : pPayload->qwFileSize; + + progress.pContainer = pContainer; + progress.pPackage = pPackage; + progress.pPayload = pPayload; + progress.pUX = pUX; + progress.qwTotalCacheSize = qwTotalCacheSize; + if (fAlreadyProvidedProgress) + { + Assert(qwSuccessfulCachedProgress >= static_cast(liContainerOrPayloadSize.QuadPart)); + progress.qwCacheProgress = qwSuccessfulCachedProgress - liContainerOrPayloadSize.QuadPart; // remove the payload size, since it was marked successful thus included in the successful size already. + } + else + { + progress.qwCacheProgress = qwSuccessfulCachedProgress; + } + + *pfRetry = FALSE; + + do + { + hr = UserExperienceOnCacheVerifyBegin(pUX, wzPackageOrContainerId, wzPayloadId); + ExitOnRootFailure(hr, "BA aborted cache verify begin."); + + if (INVALID_HANDLE_VALUE != hPipe) // pass the decision off to the elevated process. + { + hr = ElevationCacheOrLayoutContainerOrPayload(hPipe, pContainer, pPackage, pPayload, wzLayoutDirectory, wzUnverifiedPath, fMove); + } + else if (wzLayoutDirectory) // layout the container or payload. + { + if (pContainer) + { + hr = CacheLayoutContainer(pContainer, wzLayoutDirectory, wzUnverifiedPath, fMove); + } + else + { + hr = CacheLayoutPayload(pPayload, wzLayoutDirectory, wzUnverifiedPath, fMove); + } + } + else // complete the payload. + { + Assert(!pContainer); + Assert(pPackage); + + hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, wzUnverifiedPath, fMove); + } + + // If succeeded, send 100% complete here. If the payload was already cached this is the first progress the BA + // will get. + if (SUCCEEDED(hr)) + { + CacheProgressRoutine(liContainerOrPayloadSize, liContainerOrPayloadSize, liZero, liZero, 0, 0, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, &progress); + if (progress.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + else if (progress.fError) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); + } + ExitOnRootFailure(hr, "BA aborted verify of %hs: %ls", pContainer ? "container" : "payload", pContainer ? wzPackageOrContainerId : wzPayloadId); + } + + BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = FAILED(hr) && cTryAgainAttempts < BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS ? BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION : BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE; + UserExperienceOnCacheVerifyComplete(pUX, wzPackageOrContainerId, wzPayloadId, hr, &action); + if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action) + { + hr = S_FALSE; // retry verify. + } + else if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION == action) + { + *pfRetry = TRUE; // go back and retry acquire. + } + } while (S_FALSE == hr); + +LExit: + return hr; +} + +static HRESULT PromptForSource( + __in BURN_USER_EXPERIENCE* pUX, + __in_z LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in_z LPCWSTR wzLocalSource, + __in_z_opt LPCWSTR wzDownloadSource, + __inout BOOL* pfRetry, + __inout BOOL* pfDownload + ) +{ + HRESULT hr = S_OK; + BOOTSTRAPPER_RESOLVESOURCE_ACTION action = BOOTSTRAPPER_RESOLVESOURCE_ACTION_NONE; + + UserExperienceDeactivateEngine(pUX); + + hr = UserExperienceOnResolveSource(pUX, wzPackageOrContainerId, wzPayloadId, wzLocalSource, wzDownloadSource, &action); + if (FAILED(hr)) + { + ExitFunction(); + } + + switch (action) + { + case BOOTSTRAPPER_RESOLVESOURCE_ACTION_NONE: + hr = E_FILENOTFOUND; + break; + + case BOOTSTRAPPER_RESOLVESOURCE_ACTION_RETRY: + *pfRetry = TRUE; + break; + + case BOOTSTRAPPER_RESOLVESOURCE_ACTION_DOWNLOAD: + *pfDownload = TRUE; + break; + + default: + hr = E_INVALIDARG; + break; + } + +LExit: + UserExperienceActivateEngine(pUX, NULL); + return hr; +} + +static HRESULT CopyPayload( + __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, + __in_z LPCWSTR wzSourcePath, + __in_z LPCWSTR wzDestinationPath + ) +{ + HRESULT hr = S_OK; + DWORD dwFileAttributes = 0; + LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L""; + LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : L""; + + DWORD dwLogId = pProgress->pContainer ? (pProgress->pPayload ? MSG_ACQUIRE_CONTAINER_PAYLOAD : MSG_ACQUIRE_CONTAINER) : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; + LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "copy", wzSourcePath); + + // If the destination file already exists, clear the readonly bit to avoid E_ACCESSDENIED. + if (FileExistsEx(wzDestinationPath, &dwFileAttributes)) + { + if (FILE_ATTRIBUTE_READONLY & dwFileAttributes) + { + dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY; + if (!::SetFileAttributes(wzDestinationPath, dwFileAttributes)) + { + ExitWithLastError(hr, "Failed to clear readonly bit on payload destination path: %ls", wzDestinationPath); + } + } + } + + if (!::CopyFileExW(wzSourcePath, wzDestinationPath, CacheProgressRoutine, pProgress, &pProgress->fCancel, 0)) + { + if (pProgress->fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + ExitOnRootFailure(hr, "BA aborted copy of payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); + } + else + { + ExitWithLastError(hr, "Failed attempt to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); + } + } + +LExit: + return hr; +} + +static HRESULT DownloadPayload( + __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, + __in_z LPCWSTR wzDestinationPath + ) +{ + HRESULT hr = S_OK; + DWORD dwFileAttributes = 0; + LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L""; + LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : L""; + DOWNLOAD_SOURCE* pDownloadSource = pProgress->pContainer ? &pProgress->pContainer->downloadSource : &pProgress->pPayload->downloadSource; + DWORD64 qwDownloadSize = pProgress->pContainer ? pProgress->pContainer->qwFileSize : pProgress->pPayload->qwFileSize; + DOWNLOAD_CACHE_CALLBACK cacheCallback = { }; + DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { }; + APPLY_AUTHENTICATION_REQUIRED_DATA authenticationData = { }; + + DWORD dwLogId = pProgress->pContainer ? (pProgress->pPayload ? MSG_ACQUIRE_CONTAINER_PAYLOAD : MSG_ACQUIRE_CONTAINER) : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; + LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "download", pDownloadSource->sczUrl); + + // If the destination file already exists, clear the readonly bit to avoid E_ACCESSDENIED. + if (FileExistsEx(wzDestinationPath, &dwFileAttributes)) + { + if (FILE_ATTRIBUTE_READONLY & dwFileAttributes) + { + dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY; + if (!::SetFileAttributes(wzDestinationPath, dwFileAttributes)) + { + ExitWithLastError(hr, "Failed to clear readonly bit on payload destination path: %ls", wzDestinationPath); + } + } + } + + cacheCallback.pfnProgress = CacheProgressRoutine; + cacheCallback.pfnCancel = NULL; // TODO: set this + cacheCallback.pv = pProgress; + + // If the protocol is specially marked, "bits" let's use that. + if (L'b' == pDownloadSource->sczUrl[0] && + L'i' == pDownloadSource->sczUrl[1] && + L't' == pDownloadSource->sczUrl[2] && + L's' == pDownloadSource->sczUrl[3] && + (L':' == pDownloadSource->sczUrl[4] || (L's' == pDownloadSource->sczUrl[4] && L':' == pDownloadSource->sczUrl[5])) + ) + { + hr = BitsDownloadUrl(&cacheCallback, pDownloadSource, wzDestinationPath); + } + else // wininet handles everything else. + { + authenticationData.pUX = pProgress->pUX; + authenticationData.wzPackageOrContainerId = wzPackageOrContainerId; + authenticationData.wzPayloadId = wzPayloadId; + authenticationCallback.pv = static_cast(&authenticationData); + authenticationCallback.pfnAuthenticate = &AuthenticationRequired; + + hr = DownloadUrl(pDownloadSource, qwDownloadSize, wzDestinationPath, &cacheCallback, &authenticationCallback); + } + ExitOnFailure(hr, "Failed attempt to download URL: '%ls' to: '%ls'", pDownloadSource->sczUrl, wzDestinationPath); + +LExit: + return hr; +} + +static HRESULT WINAPI AuthenticationRequired( + __in LPVOID pData, + __in HINTERNET hUrl, + __in long lHttpCode, + __out BOOL* pfRetrySend, + __out BOOL* pfRetry + ) +{ + Assert(401 == lHttpCode || 407 == lHttpCode); + + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + BOOTSTRAPPER_ERROR_TYPE errorType = (401 == lHttpCode) ? BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER : BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY; + LPWSTR sczError = NULL; + int nResult = IDNOACTION; + + *pfRetrySend = FALSE; + *pfRetry = FALSE; + + hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL); + ExitOnFailure(hr, "Failed to allocation error string."); + + APPLY_AUTHENTICATION_REQUIRED_DATA* authenticationData = reinterpret_cast(pData); + + UserExperienceOnError(authenticationData->pUX, errorType, authenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value; + nResult = UserExperienceCheckExecuteResult(authenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult); + if (IDTRYAGAIN == nResult && authenticationData->pUX->hwndApply) + { + er = ::InternetErrorDlg(authenticationData->pUX->hwndApply, hUrl, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL); + if (ERROR_SUCCESS == er || ERROR_CANCELLED == er) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + else if (ERROR_INTERNET_FORCE_RETRY == er) + { + *pfRetrySend = TRUE; + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); + } + } + else if (IDRETRY == nResult) + { + *pfRetry = TRUE; + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); + } + +LExit: + ReleaseStr(sczError); + + return hr; +} + +static DWORD CALLBACK CacheProgressRoutine( + __in LARGE_INTEGER TotalFileSize, + __in LARGE_INTEGER TotalBytesTransferred, + __in LARGE_INTEGER /*StreamSize*/, + __in LARGE_INTEGER /*StreamBytesTransferred*/, + __in DWORD /*dwStreamNumber*/, + __in DWORD /*dwCallbackReason*/, + __in HANDLE /*hSourceFile*/, + __in HANDLE /*hDestinationFile*/, + __in_opt LPVOID lpData + ) +{ + HRESULT hr = S_OK; + DWORD dwResult = PROGRESS_CONTINUE; + BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress = static_cast(lpData); + LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : NULL; + LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : NULL; + DWORD64 qwCacheProgress = pProgress->qwCacheProgress + TotalBytesTransferred.QuadPart; + if (qwCacheProgress > pProgress->qwTotalCacheSize) + { + AssertSz(FALSE, "Apply has cached more than Plan envisioned."); + qwCacheProgress = pProgress->qwTotalCacheSize; + } + DWORD dwOverallPercentage = pProgress->qwTotalCacheSize ? static_cast(qwCacheProgress * 100 / pProgress->qwTotalCacheSize) : 0; + + hr = UserExperienceOnCacheAcquireProgress(pProgress->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); + if (HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr) + { + dwResult = PROGRESS_CANCEL; + pProgress->fCancel = TRUE; + } + else if (FAILED(hr)) + { + dwResult = PROGRESS_CANCEL; + pProgress->fError = TRUE; + } + else + { + dwResult = PROGRESS_CONTINUE; + } + + return dwResult; +} + +static void DoRollbackCache( + __in BURN_USER_EXPERIENCE* /*pUX*/, + __in BURN_PLAN* pPlan, + __in HANDLE hPipe, + __in DWORD dwCheckpoint + ) +{ + HRESULT hr = S_OK; + DWORD iCheckpoint = 0; + + // Scan to last checkpoint. + for (DWORD i = 0; i < pPlan->cRollbackCacheActions; ++i) + { + BURN_CACHE_ACTION* pRollbackCacheAction = &pPlan->rgRollbackCacheActions[i]; + + if (BURN_CACHE_ACTION_TYPE_CHECKPOINT == pRollbackCacheAction->type && pRollbackCacheAction->checkpoint.dwId == dwCheckpoint) + { + iCheckpoint = i; + break; + } + } + + // Rollback cache actions. + if (iCheckpoint) + { + // i has to be a signed integer so it doesn't get decremented to 0xFFFFFFFF. + for (int i = iCheckpoint - 1; i >= 0; --i) + { + BURN_CACHE_ACTION* pRollbackCacheAction = &pPlan->rgRollbackCacheActions[i]; + + switch (pRollbackCacheAction->type) + { + case BURN_CACHE_ACTION_TYPE_CHECKPOINT: + break; + + case BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE: + hr = CleanPackage(hPipe, pRollbackCacheAction->rollbackPackage.pPackage); + break; + + default: + AssertSz(FALSE, "Invalid rollback cache action."); + break; + } + } + } +} + +/* MSI Transactions: + * All MSI/MSP/MSU packages wrapped in MsiBeginTranasaction-MsiEndTransaction pair are installed or uninstalled together. +*/ +static HRESULT ExecuteMsiBeginTransaction( + __in BURN_EXECUTE_CONTEXT* pContext + , __in BURN_ENGINE_STATE* pEngineState +) +{ + HRESULT hr = S_OK; + UINT uResult = ERROR_SUCCESS; + + // Per user/machine context + if (pEngineState->plan.fPerMachine) + { + hr = ElevationMsiBeginTransaction(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pContext); + ExitOnFailure(hr, "Failed to begin an MSI transaction."); + } + else + { + MSIHANDLE hMsiTrns = NULL; + HANDLE hMsiTrnsEvent = NULL; + uResult = MsiBeginTransaction(L"WiX", 0, &hMsiTrns, &hMsiTrnsEvent); + ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction"); + } + +LExit: + return hr; +} + +static HRESULT ExecuteMsiCommitTransaction( + __in BURN_EXECUTE_CONTEXT* pContext + , __in BURN_ENGINE_STATE* pEngineState +) +{ + HRESULT hr = S_OK; + UINT uResult = ERROR_SUCCESS; + + // Per user/machine context + if (pEngineState->plan.fPerMachine) + { + hr = ElevationMsiCommitTransaction(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pContext); + ExitOnFailure(hr, "Failed to commit an MSI transaction."); + } + else + { + uResult = MsiEndTransaction(MSITRANSACTIONSTATE_COMMIT); + ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction"); + } + +LExit: + return hr; +} + +static HRESULT ExecuteMsiRollbackTransaction( + __in BURN_EXECUTE_CONTEXT* pContext + , __in BURN_ENGINE_STATE* pEngineState +) +{ + HRESULT hr = S_OK; + UINT uResult = ERROR_SUCCESS; + + // Per user/machine context + if (pEngineState->plan.fPerMachine) + { + hr = ElevationMsiRollbackTransaction(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pContext); + ExitOnFailure(hr, "Failed to rollback an MSI transaction."); + } + else + { + uResult = MsiEndTransaction(MSITRANSACTIONSTATE_ROLLBACK); + ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction"); + } + +LExit: + return hr; +} + +// Currently, supporting only elevated transactions. +static HRESULT DoMsiBeginTransaction( + __in BURN_EXECUTE_CONTEXT *pContext + , __in BURN_ENGINE_STATE* pEngineState +) +{ + HRESULT hr = S_OK; + + hr = ExecuteMsiBeginTransaction(pContext, pEngineState); + ExitOnFailure(hr, "Failed to execute EXE package."); + +LExit: + return hr; +} + +static HRESULT DoMsiCommitTransaction( + __in BURN_EXECUTE_CONTEXT *pContext + , __in BURN_ENGINE_STATE* pEngineState +) +{ + HRESULT hr = S_OK; + + hr = ExecuteMsiCommitTransaction(pContext, pEngineState); + ExitOnFailure(hr, "Failed to execute EXE package."); + +LExit: + return hr; +} + +static HRESULT DoMsiRollbackTransaction( + __in BURN_EXECUTE_CONTEXT *pContext + , __in BURN_ENGINE_STATE* pEngineState +) +{ + HRESULT hr = S_OK; + + hr = ExecuteMsiRollbackTransaction(pContext, pEngineState); + ExitOnFailure(hr, "Failed to execute EXE package."); + +LExit: + return hr; +} + + +static HRESULT DoExecuteAction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in_opt HANDLE hCacheThread, + __in BURN_EXECUTE_CONTEXT* pContext, + __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, + __out DWORD* pdwCheckpoint, + __out BOOL* pfKeepRegistration, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + Assert(!pExecuteAction->fDeleted); + + HRESULT hr = S_OK; + HANDLE rghWait[2] = { }; + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; + BOOL fRetry = FALSE; + BOOL fStopWusaService = FALSE; + + pContext->fRollback = FALSE; + + do + { + switch (pExecuteAction->type) + { + case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT: + *pdwCheckpoint = pExecuteAction->checkpoint.dwId; + break; + + case BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT: + // wait for cache sync-point + rghWait[0] = pExecuteAction->syncpoint.hEvent; + rghWait[1] = hCacheThread; + switch (::WaitForMultipleObjects(rghWait[1] ? 2 : 1, rghWait, FALSE, INFINITE)) + { + case WAIT_OBJECT_0: + break; + + case WAIT_OBJECT_0 + 1: + if (!::GetExitCodeThread(hCacheThread, (DWORD*)&hr)) + { + ExitWithLastError(hr, "Failed to get cache thread exit code."); + } + + if (SUCCEEDED(hr)) + { + hr = E_UNEXPECTED; + } + ExitOnFailure(hr, "Cache thread exited unexpectedly."); + + case WAIT_FAILED: __fallthrough; + default: + ExitWithLastError(hr, "Failed to wait for cache check-point."); + } + break; + + case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: + hr = ExecuteExePackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart); + ExitOnFailure(hr, "Failed to execute EXE package."); + break; + + case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: + hr = ExecuteMsiPackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart); + ExitOnFailure(hr, "Failed to execute MSI package."); + break; + + case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: + hr = ExecuteMspPackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart); + ExitOnFailure(hr, "Failed to execute MSP package."); + break; + + case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: + hr = ExecuteMsuPackage(pEngineState, pExecuteAction, pContext, FALSE, fStopWusaService, &fRetry, pfSuspend, &restart); + fStopWusaService = fRetry; + ExitOnFailure(hr, "Failed to execute MSU package."); + break; + + case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER: + hr = ExecutePackageProviderAction(pEngineState, pExecuteAction, pContext); + ExitOnFailure(hr, "Failed to execute package provider registration action."); + break; + + case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: + hr = ExecuteDependencyAction(pEngineState, pExecuteAction, pContext); + ExitOnFailure(hr, "Failed to execute dependency action."); + break; + + case BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE: + hr = ExecuteCompatiblePackageAction(pEngineState, pExecuteAction); + ExitOnFailure(hr, "Failed to execute compatible package action."); + break; + + case BURN_EXECUTE_ACTION_TYPE_REGISTRATION: + *pfKeepRegistration = pExecuteAction->registration.fKeep; + break; + + case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY: + *ppRollbackBoundary = pExecuteAction->rollbackBoundary.pRollbackBoundary; + break; + + case BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP: __fallthrough; + case BURN_EXECUTE_ACTION_TYPE_SERVICE_START: __fallthrough; + default: + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Invalid execute action."); + } + + if (*pRestart < restart) + { + *pRestart = restart; + } + } while (fRetry && *pRestart < BOOTSTRAPPER_APPLY_RESTART_INITIATED); + +LExit: + return hr; +} + +static HRESULT DoRollbackActions( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_CONTEXT* pContext, + __in DWORD dwCheckpoint, + __in BOOL fInTransaction, + __out BOOL* pfKeepRegistration, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart +) +{ + HRESULT hr = S_OK; + DWORD iCheckpoint = 0; + BOOL fRetryIgnored = FALSE; + BOOL fSuspendIgnored = FALSE; + + pContext->fRollback = TRUE; + + // Rollback MSI transaction + if (fInTransaction) + { + hr = DoMsiRollbackTransaction(pContext, pEngineState); + ExitOnFailure(hr, "Failed rolling back transaction"); + } + + // scan to last checkpoint + for (DWORD i = 0; i < pEngineState->plan.cRollbackActions; ++i) + { + BURN_EXECUTE_ACTION* pRollbackAction = &pEngineState->plan.rgRollbackActions[i]; + if (pRollbackAction->fDeleted) + { + continue; + } + + if (BURN_EXECUTE_ACTION_TYPE_CHECKPOINT == pRollbackAction->type) + { + if (pRollbackAction->checkpoint.dwId == dwCheckpoint) + { + iCheckpoint = i; + break; + } + } + } + + // execute rollback actions + if (iCheckpoint) + { + // i has to be a signed integer so it doesn't get decremented to 0xFFFFFFFF. + for (int i = iCheckpoint - 1; i >= 0; --i) + { + BURN_EXECUTE_ACTION* pRollbackAction = &pEngineState->plan.rgRollbackActions[i]; + if (pRollbackAction->fDeleted) + { + continue; + } + + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; + switch (pRollbackAction->type) + { + case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT: + break; + + case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: + hr = ExecuteExePackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); + TraceError(hr, "Failed to rollback EXE package."); + hr = S_OK; + break; + + case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: + if (fInTransaction) + { + LogString(REPORT_STANDARD, "Skipping rolling back an MSI package- already done in transaction rollback\n"); + break; + } + hr = ExecuteMsiPackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); + TraceError(hr, "Failed to rollback MSI package."); + hr = S_OK; + break; + + case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: + if (fInTransaction) + { + LogString(REPORT_STANDARD, "Skipping rolling back an MSP package- already done in transaction rollback\n"); + break; + } + hr = ExecuteMspPackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); + TraceError(hr, "Failed to rollback MSP package."); + hr = S_OK; + break; + + case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: + if (fInTransaction) + { + LogString(REPORT_STANDARD, "Skipping rolling back an MSU package- already done in transaction rollback\n"); + break; + } + hr = ExecuteMsuPackage(pEngineState, pRollbackAction, pContext, TRUE, FALSE, &fRetryIgnored, &fSuspendIgnored, &restart); + TraceError(hr, "Failed to rollback MSU package."); + hr = S_OK; + break; + + case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER: + hr = ExecutePackageProviderAction(pEngineState, pRollbackAction, pContext); + TraceError(hr, "Failed to rollback package provider action."); + hr = S_OK; + break; + + case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: + hr = ExecuteDependencyAction(pEngineState, pRollbackAction, pContext); + TraceError(hr, "Failed to rollback dependency action."); + hr = S_OK; + break; + + case BURN_EXECUTE_ACTION_TYPE_REGISTRATION: + *pfKeepRegistration = pRollbackAction->registration.fKeep; + break; + + case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY: + ExitFunction1(hr = S_OK); + + case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE: + hr = CleanPackage(pEngineState->companionConnection.hPipe, pRollbackAction->uncachePackage.pPackage); + break; + + case BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP: __fallthrough; + case BURN_EXECUTE_ACTION_TYPE_SERVICE_START: __fallthrough; + default: + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Invalid rollback action: %d.", pRollbackAction->type); + } + + if (*pRestart < restart) + { + *pRestart = restart; + } + } + } + +LExit: + return hr; +} + +static HRESULT ExecuteExePackage( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_EXECUTE_CONTEXT* pContext, + __in BOOL fRollback, + __out BOOL* pfRetry, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + HRESULT hrExecute = S_OK; + GENERIC_EXECUTE_MESSAGE message = { }; + int nResult = 0; + BOOL fBeginCalled = FALSE; + + if (FAILED(pExecuteAction->exePackage.pPackage->hrCacheResult)) + { + LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->exePackage.pPackage->sczId, pExecuteAction->exePackage.pPackage->hrCacheResult); + ExitFunction1(hr = S_OK); + } + + Assert(pContext->fRollback == fRollback); + pContext->pExecutingPackage = pExecuteAction->exePackage.pPackage; + fBeginCalled = TRUE; + + // Send package execute begin to BA. + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->exePackage.pPackage->sczId, !fRollback); + ExitOnRootFailure(hr, "BA aborted execute EXE package begin."); + + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; + message.dwAllowedResults = MB_OKCANCEL; + message.progress.dwPercentage = fRollback ? 100 : 0; + nResult = GenericExecuteMessageHandler(&message, pContext); + hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); + ExitOnRootFailure(hr, "UX aborted EXE progress."); + + // Execute package. + if (pExecuteAction->exePackage.pPackage->fPerMachine) + { + hrExecute = ElevationExecuteExePackage(pEngineState->companionConnection.hPipe, pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart); + ExitOnFailure(hrExecute, "Failed to configure per-machine EXE package."); + } + else + { + hrExecute = ExeEngineExecutePackage(pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart); + ExitOnFailure(hrExecute, "Failed to configure per-user EXE package."); + } + + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; + message.dwAllowedResults = MB_OKCANCEL; + message.progress.dwPercentage = fRollback ? 0 : 100; + nResult = GenericExecuteMessageHandler(&message, pContext); + hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); + ExitOnRootFailure(hr, "UX aborted EXE progress."); + + pContext->cExecutedPackages += fRollback ? -1 : 1; + (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; + + hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); + ExitOnRootFailure(hr, "UX aborted EXE package execute progress."); + +LExit: + if (fBeginCalled) + { + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->exePackage.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); + } + + return hr; +} + +static HRESULT ExecuteMsiPackage( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_EXECUTE_CONTEXT* pContext, + __in BOOL fRollback, + __out BOOL* pfRetry, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + HRESULT hrExecute = S_OK; + BOOL fBeginCalled = FALSE; + + if (FAILED(pExecuteAction->msiPackage.pPackage->hrCacheResult)) + { + LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->msiPackage.pPackage->sczId, pExecuteAction->msiPackage.pPackage->hrCacheResult); + ExitFunction1(hr = S_OK); + } + + Assert(pContext->fRollback == fRollback); + pContext->pExecutingPackage = pExecuteAction->msiPackage.pPackage; + fBeginCalled = TRUE; + + // Send package execute begin to BA. + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msiPackage.pPackage->sczId, !fRollback); + ExitOnRootFailure(hr, "BA aborted execute MSI package begin."); + + // execute package + if (pExecuteAction->msiPackage.pPackage->fPerMachine) + { + hrExecute = ElevationExecuteMsiPackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); + ExitOnFailure(hrExecute, "Failed to configure per-machine MSI package."); + } + else + { + hrExecute = MsiEngineExecutePackage(pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); + ExitOnFailure(hrExecute, "Failed to configure per-user MSI package."); + } + + pContext->cExecutedPackages += fRollback ? -1 : 1; + (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; + + hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); + ExitOnRootFailure(hr, "UX aborted MSI package execute progress."); + +LExit: + if (fBeginCalled) + { + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->msiPackage.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); + } + + return hr; +} + +static HRESULT ExecuteMspPackage( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_EXECUTE_CONTEXT* pContext, + __in BOOL fRollback, + __out BOOL* pfRetry, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + HRESULT hrExecute = S_OK; + BOOL fBeginCalled = FALSE; + + if (FAILED(pExecuteAction->mspTarget.pPackage->hrCacheResult)) + { + LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->mspTarget.pPackage->sczId, pExecuteAction->mspTarget.pPackage->hrCacheResult); + ExitFunction1(hr = S_OK); + } + + Assert(pContext->fRollback == fRollback); + pContext->pExecutingPackage = pExecuteAction->mspTarget.pPackage; + fBeginCalled = TRUE; + + // Send package execute begin to BA. + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->mspTarget.pPackage->sczId, !fRollback); + ExitOnRootFailure(hr, "BA aborted execute MSP package begin."); + + // Now send all the patches that target this product code. + for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i) + { + BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage; + + hr = UserExperienceOnExecutePatchTarget(&pEngineState->userExperience, pMspPackage->sczId, pExecuteAction->mspTarget.sczTargetProductCode); + ExitOnRootFailure(hr, "BA aborted execute MSP target."); + } + + // execute package + if (pExecuteAction->mspTarget.fPerMachineTarget) + { + hrExecute = ElevationExecuteMspPackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); + ExitOnFailure(hrExecute, "Failed to configure per-machine MSP package."); + } + else + { + hrExecute = MspEngineExecutePackage(pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); + ExitOnFailure(hrExecute, "Failed to configure per-user MSP package."); + } + + pContext->cExecutedPackages += fRollback ? -1 : 1; + (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; + + hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); + ExitOnRootFailure(hr, "UX aborted MSP package execute progress."); + +LExit: + if (fBeginCalled) + { + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->mspTarget.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); + } + + return hr; +} + +static HRESULT ExecuteMsuPackage( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_EXECUTE_CONTEXT* pContext, + __in BOOL fRollback, + __in BOOL fStopWusaService, + __out BOOL* pfRetry, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + HRESULT hrExecute = S_OK; + GENERIC_EXECUTE_MESSAGE message = { }; + int nResult = 0; + BOOL fBeginCalled = FALSE; + + if (FAILED(pExecuteAction->msuPackage.pPackage->hrCacheResult)) + { + LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->msuPackage.pPackage->sczId, pExecuteAction->msuPackage.pPackage->hrCacheResult); + ExitFunction1(hr = S_OK); + } + + Assert(pContext->fRollback == fRollback); + pContext->pExecutingPackage = pExecuteAction->msuPackage.pPackage; + fBeginCalled = TRUE; + + // Send package execute begin to BA. + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msuPackage.pPackage->sczId, !fRollback); + ExitOnRootFailure(hr, "BA aborted execute MSU package begin."); + + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; + message.dwAllowedResults = MB_OKCANCEL; + message.progress.dwPercentage = fRollback ? 100 : 0; + nResult = GenericExecuteMessageHandler(&message, pContext); + hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); + ExitOnRootFailure(hr, "UX aborted MSU progress."); + + // execute package + if (pExecuteAction->msuPackage.pPackage->fPerMachine) + { + hrExecute = ElevationExecuteMsuPackage(pEngineState->companionConnection.hPipe, pExecuteAction, fRollback, fStopWusaService, GenericExecuteMessageHandler, pContext, pRestart); + ExitOnFailure(hrExecute, "Failed to configure per-machine MSU package."); + } + else + { + hrExecute = E_UNEXPECTED; + ExitOnFailure(hr, "MSU packages cannot be per-user."); + } + + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; + message.dwAllowedResults = MB_OKCANCEL; + message.progress.dwPercentage = fRollback ? 0 : 100; + nResult = GenericExecuteMessageHandler(&message, pContext); + hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); + ExitOnRootFailure(hr, "UX aborted MSU progress."); + + pContext->cExecutedPackages += fRollback ? -1 : 1; + (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; + + hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); + ExitOnRootFailure(hr, "UX aborted MSU package execute progress."); + +LExit: + if (fBeginCalled) + { + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->msuPackage.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); + } + + return hr; +} + +static HRESULT ExecutePackageProviderAction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pAction, + __in BURN_EXECUTE_CONTEXT* /*pContext*/ + ) +{ + HRESULT hr = S_OK; + + if (pAction->packageProvider.pPackage->fPerMachine) + { + hr = ElevationExecutePackageProviderAction(pEngineState->companionConnection.hPipe, pAction); + ExitOnFailure(hr, "Failed to register the package provider on per-machine package."); + } + else + { + hr = DependencyExecutePackageProviderAction(pAction); + ExitOnFailure(hr, "Failed to register the package provider on per-user package."); + } + +LExit: + return hr; +} + +static HRESULT ExecuteDependencyAction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pAction, + __in BURN_EXECUTE_CONTEXT* /*pContext*/ + ) +{ + HRESULT hr = S_OK; + + if (pAction->packageDependency.pPackage->fPerMachine) + { + hr = ElevationExecutePackageDependencyAction(pEngineState->companionConnection.hPipe, pAction); + ExitOnFailure(hr, "Failed to register the dependency on per-machine package."); + } + else + { + hr = DependencyExecutePackageDependencyAction(FALSE, pAction); + ExitOnFailure(hr, "Failed to register the dependency on per-user package."); + } + +LExit: + return hr; +} + +static HRESULT ExecuteCompatiblePackageAction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pAction + ) +{ + HRESULT hr = S_OK; + + if (pAction->compatiblePackage.pReferencePackage->fPerMachine) + { + hr = ElevationLoadCompatiblePackageAction(pEngineState->companionConnection.hPipe, pAction); + ExitOnFailure(hr, "Failed to load compatible package on per-machine package."); + } + + // Compatible package already loaded in this process. + +LExit: + return hr; +} + +static HRESULT CleanPackage( + __in HANDLE hElevatedPipe, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + + if (pPackage->fPerMachine) + { + hr = ElevationCleanPackage(hElevatedPipe, pPackage); + } + else + { + hr = CacheRemovePackage(FALSE, pPackage->sczId, pPackage->sczCacheId); + } + + return hr; +} + +static int GenericExecuteMessageHandler( + __in GENERIC_EXECUTE_MESSAGE* pMessage, + __in LPVOID pvContext + ) +{ + BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext; + int nResult = IDNOACTION; + + switch (pMessage->type) + { + case GENERIC_EXECUTE_MESSAGE_PROGRESS: + { + DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0; + UserExperienceOnExecuteProgress(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value. + } + break; + + case GENERIC_EXECUTE_MESSAGE_ERROR: + UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_EXE_PACKAGE, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwAllowedResults, 0, NULL, &nResult); // ignore return value. + break; + + case GENERIC_EXECUTE_MESSAGE_FILES_IN_USE: + UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->filesInUse.cFiles, pMessage->filesInUse.rgwzFiles, &nResult); // ignore return value. + break; + } + + nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult); + return nResult; +} + +static int MsiExecuteMessageHandler( + __in WIU_MSI_EXECUTE_MESSAGE* pMessage, + __in_opt LPVOID pvContext + ) +{ + BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext; + int nResult = IDNOACTION; + + switch (pMessage->type) + { + case WIU_MSI_EXECUTE_MESSAGE_PROGRESS: + { + DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0; + UserExperienceOnExecuteProgress(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value. + } + break; + + case WIU_MSI_EXECUTE_MESSAGE_ERROR: + nResult = pMessage->nResultRecommendation; + UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwAllowedResults, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value. + break; + + case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE: + nResult = pMessage->nResultRecommendation; + UserExperienceOnExecuteMsiMessage(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiMessage.mt, pMessage->dwAllowedResults, pMessage->msiMessage.wzMessage, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value. + break; + + case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE: + UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiFilesInUse.cFiles, pMessage->msiFilesInUse.rgwzFiles, &nResult); // ignore return value. + break; + } + + nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult); + return nResult; +} + +static HRESULT ReportOverallProgressTicks( + __in BURN_USER_EXPERIENCE* pUX, + __in BOOL fRollback, + __in DWORD cOverallProgressTicksTotal, + __in DWORD cOverallProgressTicks + ) +{ + HRESULT hr = S_OK; + DWORD dwProgress = cOverallProgressTicksTotal ? (cOverallProgressTicks * 100 / cOverallProgressTicksTotal) : 0; + + // TODO: consider sending different progress numbers in the future. + hr = UserExperienceOnProgress(pUX, fRollback, dwProgress, dwProgress); + + return hr; +} + +static HRESULT ExecutePackageComplete( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_VARIABLES* pVariables, + __in BURN_PACKAGE* pPackage, + __in HRESULT hrOverall, + __in HRESULT hrExecute, + __in BOOL fRollback, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart, + __out BOOL* pfRetry, + __out BOOL* pfSuspend + ) +{ + HRESULT hr = FAILED(hrOverall) ? hrOverall : hrExecute; // if the overall function failed use that otherwise use the execution result. + BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION executePackageCompleteAction = FAILED(hrOverall) || SUCCEEDED(hrExecute) || pPackage->fVital ? BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE; + + // Send package execute complete to BA. + UserExperienceOnExecutePackageComplete(pUX, pPackage->sczId, hr, *pRestart, &executePackageCompleteAction); + if (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RESTART == executePackageCompleteAction) + { + *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; + } + *pfRetry = (FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RETRY == executePackageCompleteAction); // allow retry only on failures. + *pfSuspend = (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_SUSPEND == executePackageCompleteAction); + + // Remember this package as the package that initiated the forced restart. + if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart) + { + // Best effort to set the forced restart package variable. + VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, pPackage->sczId, TRUE); + } + + // If we're retrying, leave a message in the log file and say everything is okay. + if (*pfRetry) + { + LogId(REPORT_STANDARD, MSG_APPLY_RETRYING_PACKAGE, pPackage->sczId, hrExecute); + hr = S_OK; + } + else if (SUCCEEDED(hrOverall) && FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE == executePackageCompleteAction && !pPackage->fVital) // If we *only* failed to execute and the BA ignored this *not-vital* package, say everything is okay. + { + LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, pPackage->sczId, hrExecute); + hr = S_OK; + } + else + { + LogId(REPORT_STANDARD, MSG_APPLY_COMPLETED_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, hr, LoggingRestartToString(*pRestart)); + } + + return hr; +} diff --git a/src/engine/apply.h b/src/engine/apply.h new file mode 100644 index 00000000..b717251e --- /dev/null +++ b/src/engine/apply.h @@ -0,0 +1,106 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + + +enum GENERIC_EXECUTE_MESSAGE_TYPE +{ + GENERIC_EXECUTE_MESSAGE_NONE, + GENERIC_EXECUTE_MESSAGE_ERROR, + GENERIC_EXECUTE_MESSAGE_PROGRESS, + GENERIC_EXECUTE_MESSAGE_FILES_IN_USE, +}; + +typedef struct _APPLY_AUTHENTICATION_REQUIRED_DATA +{ + BURN_USER_EXPERIENCE* pUX; + LPCWSTR wzPackageOrContainerId; + LPCWSTR wzPayloadId; +} APPLY_AUTHENTICATION_REQUIRED_DATA; + +typedef struct _GENERIC_EXECUTE_MESSAGE +{ + GENERIC_EXECUTE_MESSAGE_TYPE type; + DWORD dwAllowedResults; + + union + { + struct + { + DWORD dwErrorCode; + LPCWSTR wzMessage; + } error; + struct + { + DWORD dwPercentage; + } progress; + struct + { + DWORD cFiles; + LPCWSTR* rgwzFiles; + } filesInUse; + }; +} GENERIC_EXECUTE_MESSAGE; + + +typedef int (*PFN_GENERICMESSAGEHANDLER)( + __in GENERIC_EXECUTE_MESSAGE* pMessage, + __in LPVOID pvContext + ); + + +void ApplyInitialize(); +void ApplyUninitialize(); +HRESULT ApplySetVariables( + __in BURN_VARIABLES* pVariables + ); +void ApplyReset( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PACKAGES* pPackages + ); +HRESULT ApplyLock( + __in BOOL fPerMachine, + __out HANDLE* phLock + ); +HRESULT ApplyRegister( + __in BURN_ENGINE_STATE* pEngineState + ); +HRESULT ApplyUnregister( + __in BURN_ENGINE_STATE* pEngineState, + __in BOOL fFailedOrRollback, + __in BOOL fRollback, + __in BOOL fSuspend, + __in BOOTSTRAPPER_APPLY_RESTART restart + ); +HRESULT ApplyCache( + __in HANDLE hSourceEngineFile, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_VARIABLES* pVariables, + __in BURN_PLAN* pPlan, + __in HANDLE hPipe, + __inout DWORD* pcOverallProgressTicks, + __out BOOL* pfRollback + ); +HRESULT ApplyExecute( + __in BURN_ENGINE_STATE* pEngineState, + __in_opt HANDLE hCacheThread, + __inout DWORD* pcOverallProgressTicks, + __out BOOL* pfKeepRegistration, + __out BOOL* pfRollback, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +void ApplyClean( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PLAN* pPlan, + __in HANDLE hPipe + ); + + +#ifdef __cplusplus +} +#endif diff --git a/src/engine/approvedexe.cpp b/src/engine/approvedexe.cpp new file mode 100644 index 00000000..55518519 --- /dev/null +++ b/src/engine/approvedexe.cpp @@ -0,0 +1,262 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// function definitions + +extern "C" HRESULT ApprovedExesParseFromXml( + __in BURN_APPROVED_EXES* pApprovedExes, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + LPWSTR scz = NULL; + + // select approved exe nodes + hr = XmlSelectNodes(pixnBundle, L"ApprovedExeForElevation", &pixnNodes); + ExitOnFailure(hr, "Failed to select approved exe nodes."); + + // get approved exe node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get approved exe node count."); + + if (!cNodes) + { + ExitFunction(); + } + + // allocate memory for approved exes + pApprovedExes->rgApprovedExes = (BURN_APPROVED_EXE*)MemAlloc(sizeof(BURN_APPROVED_EXE) * cNodes, TRUE); + ExitOnNull(pApprovedExes->rgApprovedExes, hr, E_OUTOFMEMORY, "Failed to allocate memory for approved exe structs."); + + pApprovedExes->cApprovedExes = cNodes; + + // parse approved exe elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_APPROVED_EXE* pApprovedExe = &pApprovedExes->rgApprovedExes[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &pApprovedExe->sczId); + ExitOnFailure(hr, "Failed to get @Id."); + + // @Key + hr = XmlGetAttributeEx(pixnNode, L"Key", &pApprovedExe->sczKey); + ExitOnFailure(hr, "Failed to get @Key."); + + // @ValueName + hr = XmlGetAttributeEx(pixnNode, L"ValueName", &pApprovedExe->sczValueName); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @ValueName."); + } + + // @Win64 + hr = XmlGetYesNoAttribute(pixnNode, L"Win64", &pApprovedExe->fWin64); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Win64."); + } + + // prepare next iteration + ReleaseNullObject(pixnNode); + ReleaseNullStr(scz); + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseStr(scz); + return hr; +} + +extern "C" void ApprovedExesUninitialize( + __in BURN_APPROVED_EXES* pApprovedExes + ) +{ + if (pApprovedExes->rgApprovedExes) + { + for (DWORD i = 0; i < pApprovedExes->cApprovedExes; ++i) + { + BURN_APPROVED_EXE* pApprovedExe = &pApprovedExes->rgApprovedExes[i]; + + ReleaseStr(pApprovedExe->sczId); + ReleaseStr(pApprovedExe->sczKey); + ReleaseStr(pApprovedExe->sczValueName); + } + MemFree(pApprovedExes->rgApprovedExes); + } +} + +extern "C" void ApprovedExesUninitializeLaunch( + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe + ) +{ + if (pLaunchApprovedExe) + { + ReleaseStr(pLaunchApprovedExe->sczArguments); + ReleaseStr(pLaunchApprovedExe->sczExecutablePath); + ReleaseStr(pLaunchApprovedExe->sczId); + MemFree(pLaunchApprovedExe); + } +} + +extern "C" HRESULT ApprovedExesFindById( + __in BURN_APPROVED_EXES* pApprovedExes, + __in_z LPCWSTR wzId, + __out BURN_APPROVED_EXE** ppApprovedExe + ) +{ + HRESULT hr = S_OK; + BURN_APPROVED_EXE* pApprovedExe = NULL; + + for (DWORD i = 0; i < pApprovedExes->cApprovedExes; ++i) + { + pApprovedExe = &pApprovedExes->rgApprovedExes[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pApprovedExe->sczId, -1, wzId, -1)) + { + *ppApprovedExe = pApprovedExe; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + +extern "C" HRESULT ApprovedExesLaunch( + __in BURN_VARIABLES* pVariables, + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe, + __out DWORD* pdwProcessId + ) +{ + HRESULT hr = S_OK; + LPWSTR sczArgumentsFormatted = NULL; + LPWSTR sczArgumentsObfuscated = NULL; + LPWSTR sczCommand = NULL; + LPWSTR sczCommandObfuscated = NULL; + LPWSTR sczExecutableDirectory = NULL; + STARTUPINFOW si = { }; + PROCESS_INFORMATION pi = { }; + + // build command + if (pLaunchApprovedExe->sczArguments && *pLaunchApprovedExe->sczArguments) + { + hr = VariableFormatString(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsFormatted, NULL); + ExitOnFailure(hr, "Failed to format argument string."); + + hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsFormatted); + ExitOnFailure(hr, "Failed to create executable command."); + + hr = VariableFormatStringObfuscated(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsObfuscated, NULL); + ExitOnFailure(hr, "Failed to format obfuscated argument string."); + + hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsObfuscated); + } + else + { + hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath); + ExitOnFailure(hr, "Failed to create executable command."); + + hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath); + } + ExitOnFailure(hr, "Failed to create obfuscated executable command."); + + // Try to get the directory of the executable so we can set the current directory of the process to help those executables + // that expect stuff to be relative to them. Best effort only. + hr = PathGetDirectory(pLaunchApprovedExe->sczExecutablePath, &sczExecutableDirectory); + if (FAILED(hr)) + { + ReleaseNullStr(sczExecutableDirectory); + } + + LogId(REPORT_STANDARD, MSG_LAUNCHING_APPROVED_EXE, pLaunchApprovedExe->sczExecutablePath, sczCommandObfuscated); + + si.cb = sizeof(si); + if (!::CreateProcessW(pLaunchApprovedExe->sczExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NEW_PROCESS_GROUP, NULL, sczExecutableDirectory, &si, &pi)) + { + ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", pLaunchApprovedExe->sczExecutablePath); + } + + *pdwProcessId = pi.dwProcessId; + + if (pLaunchApprovedExe->dwWaitForInputIdleTimeout) + { + ::WaitForInputIdle(pi.hProcess, pLaunchApprovedExe->dwWaitForInputIdleTimeout); + } + +LExit: + StrSecureZeroFreeString(sczArgumentsFormatted); + ReleaseStr(sczArgumentsObfuscated); + StrSecureZeroFreeString(sczCommand); + ReleaseStr(sczCommandObfuscated); + ReleaseStr(sczExecutableDirectory); + + ReleaseHandle(pi.hThread); + ReleaseHandle(pi.hProcess); + + return hr; +} + +extern "C" HRESULT ApprovedExesVerifySecureLocation( + __in BURN_VARIABLES* pVariables, + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe + ) +{ + HRESULT hr = S_OK; + LPWSTR scz = NULL; + + const LPCWSTR vrgSecureFolderVariables[] = { + L"ProgramFiles64Folder", + L"ProgramFilesFolder", + }; + + for (DWORD i = 0; i < countof(vrgSecureFolderVariables); ++i) + { + LPCWSTR wzSecureFolderVariable = vrgSecureFolderVariables[i]; + + hr = VariableGetString(pVariables, wzSecureFolderVariable, &scz); + if (SUCCEEDED(hr)) + { + hr = PathDirectoryContainsPath(scz, pLaunchApprovedExe->sczExecutablePath); + if (S_OK == hr) + { + ExitFunction(); + } + } + else if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get the variable: %ls", wzSecureFolderVariable); + } + } + + // The problem with using a Variable for the root package cache folder is that it might not have been secured yet. + // Getting it through CacheGetRootCompletedPath makes sure it has been secured. + hr = CacheGetRootCompletedPath(TRUE, TRUE, &scz); + ExitOnFailure(hr, "Failed to get the root package cache folder."); + + hr = PathDirectoryContainsPath(scz, pLaunchApprovedExe->sczExecutablePath); + if (S_OK == hr) + { + ExitFunction(); + } + + hr = S_FALSE; + +LExit: + ReleaseStr(scz); + + return hr; +} diff --git a/src/engine/approvedexe.h b/src/engine/approvedexe.h new file mode 100644 index 00000000..23f3b1bb --- /dev/null +++ b/src/engine/approvedexe.h @@ -0,0 +1,67 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// structs + +typedef struct _BURN_APPROVED_EXE +{ + LPWSTR sczId; + LPWSTR sczKey; + LPWSTR sczValueName; + BOOL fWin64; +} BURN_APPROVED_EXE; + +typedef struct _BURN_APPROVED_EXES +{ + BURN_APPROVED_EXE* rgApprovedExes; + DWORD cApprovedExes; +} BURN_APPROVED_EXES; + +typedef struct _BURN_LAUNCH_APPROVED_EXE +{ + HWND hwndParent; + LPWSTR sczId; + LPWSTR sczExecutablePath; + LPWSTR sczArguments; + DWORD dwWaitForInputIdleTimeout; +} BURN_LAUNCH_APPROVED_EXE; + + +// function declarations + +HRESULT ApprovedExesParseFromXml( + __in BURN_APPROVED_EXES* pApprovedExes, + __in IXMLDOMNode* pixnBundle + ); + +void ApprovedExesUninitialize( + __in BURN_APPROVED_EXES* pApprovedExes + ); +void ApprovedExesUninitializeLaunch( + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe + ); +HRESULT ApprovedExesFindById( + __in BURN_APPROVED_EXES* pApprovedExes, + __in_z LPCWSTR wzId, + __out BURN_APPROVED_EXE** ppApprovedExe + ); +HRESULT ApprovedExesLaunch( + __in BURN_VARIABLES* pVariables, + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe, + __out DWORD* pdwProcessId + ); +HRESULT ApprovedExesVerifySecureLocation( + __in BURN_VARIABLES* pVariables, + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/bitsengine.cpp b/src/engine/bitsengine.cpp new file mode 100644 index 00000000..b8093b77 --- /dev/null +++ b/src/engine/bitsengine.cpp @@ -0,0 +1,505 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// const + +const DWORD BITSENGINE_NO_PROGRESS_TIMEOUT = 2 * 60; +const DWORD BITSENGINE_MSG_WAIT_TIMEOUT = 1; + +// functions + +static HRESULT CreateJob( + __out IBackgroundCopyJob** ppJob + ); +static HRESULT SetCredentials( + __in IBackgroundCopyJob* pJob, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword + ); +static void SendError( + __in DOWNLOAD_CACHE_CALLBACK* pCacheCallback, + __in IBackgroundCopyJob* pJob, + __in HRESULT hrError, + __in BG_ERROR_CONTEXT context, + __out_opt BOOL* pfRetry + ); + + +// class + +class CBurnBitsCallback : public IBackgroundCopyCallback +{ +public: // IUnknown + virtual STDMETHODIMP QueryInterface( + __in const IID& riid, + __out void** ppvObject + ) + { + HRESULT hr = S_OK; + + ExitOnNull(ppvObject, hr, E_INVALIDARG, "Invalid argument ppvObject"); + *ppvObject = NULL; + + if (::IsEqualIID(__uuidof(IBackgroundCopyCallback), riid)) + { + *ppvObject = static_cast(this); + } + else if (::IsEqualIID(IID_IUnknown, riid)) + { + *ppvObject = reinterpret_cast(this); + } + else // no interface for requested iid + { + ExitFunction1(hr = E_NOINTERFACE); + } + + AddRef(); + + LExit: + return hr; + } + + virtual STDMETHODIMP_(ULONG) AddRef() + { + return ::InterlockedIncrement(&this->m_cReferences); + } + + virtual STDMETHODIMP_(ULONG) Release() + { + long l = ::InterlockedDecrement(&this->m_cReferences); + if (0 < l) + { + return l; + } + + delete this; + return 0; + } + +public: // IBackgroundCopyCallback + virtual STDMETHODIMP JobTransferred( + __in IBackgroundCopyJob* pJob + ) + { + HRESULT hr = S_OK; + + hr = SendProgress(pJob); + ExitOnFailure(hr, "Failure while sending progress during BITS job transferred."); + + LExit: + if (FAILED(hr)) + { + ProcessResult(BG_ERROR_CONTEXT_NONE, hr); + } + else + { + ::SetEvent(m_hComplete); + } + + return S_OK; // must return S_OK otherwise BITS just keeps calling back. + } + + virtual STDMETHODIMP JobError( + __in IBackgroundCopyJob* /*pJob*/, + __in IBackgroundCopyError* pError + ) + { + HRESULT hr = S_OK; + BG_ERROR_CONTEXT context = BG_ERROR_CONTEXT_NONE; + HRESULT hrError = S_OK; + + hr = pError->GetError(&context, &hrError); + ExitOnFailure(hr, "Failed to get error context."); + + if (SUCCEEDED(hrError)) + { + hr = E_UNEXPECTED; + } + + LExit: + ProcessResult(context, FAILED(hrError) ? hrError : hr); + + return S_OK; // must return S_OK otherwise BITS just keeps calling back. + } + + virtual STDMETHODIMP JobModification( + __in IBackgroundCopyJob* pJob, + __in DWORD /*dwReserved*/ + ) + { + HRESULT hr = S_OK; + BG_JOB_STATE state = BG_JOB_STATE_ERROR; + + ::EnterCriticalSection(&m_cs); + + hr = pJob->GetState(&state); + ExitOnFailure(hr, "Failed to get state during job modification."); + + // If we're actually downloading stuff, let's send progress. + if (BG_JOB_STATE_TRANSFERRING == state) + { + hr = SendProgress(pJob); + ExitOnFailure(hr, "Failure while sending progress during BITS job modification."); + } + + LExit: + ::LeaveCriticalSection(&m_cs); + + ProcessResult(BG_ERROR_CONTEXT_NONE, hr); + + return S_OK; // documentation says to always return S_OK + } + +public: + void Reset() + { + m_hrError = S_OK; + m_contextError = BG_ERROR_CONTEXT_NONE; + + ::ResetEvent(m_hComplete); + } + + HRESULT WaitForCompletion( + __in IBackgroundCopyJob* pJob + ) + { + HRESULT hr = S_OK; + HANDLE rghEvents[1] = { m_hComplete }; + MSG msg = { }; + BOOL fMessageProcessed = FALSE; + + do + { + fMessageProcessed = FALSE; + + switch (::MsgWaitForMultipleObjects(countof(rghEvents), rghEvents, FALSE, BITSENGINE_MSG_WAIT_TIMEOUT * 1000, QS_ALLINPUT)) + { + case WAIT_OBJECT_0: + break; + + case WAIT_OBJECT_0 + 1: + ::PeekMessageW(&msg, NULL, 0, 0, PM_NOREMOVE); + fMessageProcessed = TRUE; + break; + + case WAIT_TIMEOUT: + // Call the progress callback periodically if we are not transferring to ensure that cancelling is responsive + // (progress callback is also handles cancelling). Note that if we are transferring, IBackgroundCopyCallback + // methods handle progress/cancelling. If we are not transferring, the IBackgroundCopyCallback methods may + // not be called until the job times out (minutes for a foreground job, weeks for a background job). + SendProgressIfNotTransferring(pJob); + fMessageProcessed = TRUE; + break; + + default: + ExitWithLastError(hr, "Failed while waiting for download."); + } + } while (fMessageProcessed); + + LExit: + return hr; + } + + void GetError( + __out HRESULT* pHR, + __out BG_ERROR_CONTEXT* pContext + ) + { + *pHR = m_hrError; + *pContext = m_contextError; + } + +private: + HRESULT SendProgress( + __in IBackgroundCopyJob* pJob + ) + { + HRESULT hr = S_OK; + BG_JOB_PROGRESS progress = { }; + + if (m_pCallback && m_pCallback->pfnProgress) + { + hr = pJob->GetProgress(&progress); + ExitOnFailure(hr, "Failed to get progress when BITS job was transferred."); + + hr = CacheSendProgressCallback(m_pCallback, progress.BytesTransferred, progress.BytesTotal, INVALID_HANDLE_VALUE); + ExitOnFailure(hr, "Failed to send progress from BITS job."); + } + + LExit: + return hr; + } + + void SendProgressIfNotTransferring( + __in IBackgroundCopyJob* pJob + ) + { + HRESULT hr = S_OK; + BG_JOB_STATE state = BG_JOB_STATE_ERROR; + + ::EnterCriticalSection(&m_cs); + + hr = pJob->GetState(&state); + ExitOnFailure(hr, "Failed to get BITS job state."); + + if (BG_JOB_STATE_TRANSFERRING != state) + { + hr = SendProgress(pJob); + ExitOnFailure(hr, "Failure while sending progress."); + } + + LExit: + ::LeaveCriticalSection(&m_cs); + + ProcessResult(BG_ERROR_CONTEXT_NONE, hr); + } + + void ProcessResult( + __in BG_ERROR_CONTEXT context, + __in HRESULT hr + ) + { + if (FAILED(hr)) + { + m_contextError = context; + m_hrError = hr; + + ::SetEvent(m_hComplete); + } + } + +public: + CBurnBitsCallback( + __in_opt DOWNLOAD_CACHE_CALLBACK* pCallback, + __out HRESULT* pHR + ) + { + HRESULT hr = S_OK; + + m_cReferences = 1; + ::InitializeCriticalSection(&m_cs); + + m_hComplete = ::CreateEventW(NULL, TRUE, FALSE, NULL); + ExitOnNullWithLastError(m_hComplete, hr, "Failed to create BITS job complete event."); + + m_contextError = BG_ERROR_CONTEXT_NONE; + m_hrError = S_OK; + + m_pCallback = pCallback; + + LExit: + *pHR = hr; + } + + ~CBurnBitsCallback() + { + m_pCallback = NULL; + ReleaseHandle(m_hComplete); + ::DeleteCriticalSection(&m_cs); + } + +private: + long m_cReferences; + CRITICAL_SECTION m_cs; + BG_ERROR_CONTEXT m_contextError; + HRESULT m_hrError; + + HANDLE m_hComplete; + DOWNLOAD_CACHE_CALLBACK* m_pCallback; +}; + + +extern "C" HRESULT BitsDownloadUrl( + __in DOWNLOAD_CACHE_CALLBACK* pCallback, + __in DOWNLOAD_SOURCE* pDownloadSource, + __in_z LPCWSTR wzDestinationPath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczDownloadUrl = NULL; + CBurnBitsCallback* pBitsCallback = NULL; + IBackgroundCopyJob* pJob = NULL; + BOOL fRetry = FALSE; + BG_ERROR_CONTEXT contextError = BG_ERROR_CONTEXT_NONE; + + // If the URL isn't at least 8 characters long (e.g.: "bits://X") then it + // isn't going to do us any good. + if (8 > lstrlenW(pDownloadSource->sczUrl)) + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid BITS engine URL: %ls", pDownloadSource->sczUrl); + } + + // Fix the URL to be "http" instead of "bits". + hr = StrAllocString(&sczDownloadUrl, pDownloadSource->sczUrl, 0); + ExitOnFailure(hr, "Failed to copy download URL."); + + sczDownloadUrl[0] = L'h'; + sczDownloadUrl[1] = L't'; + sczDownloadUrl[2] = L't'; + sczDownloadUrl[3] = L'p'; + + // Create and configure the BITS job. + hr = CreateJob(&pJob); + ExitOnFailure(hr, "Failed to create BITS job."); + + hr = SetCredentials(pJob, pDownloadSource->sczUser, pDownloadSource->sczPassword); + ExitOnFailure(hr, "Failed to set credentials for BITS job."); + + hr = pJob->AddFile(sczDownloadUrl, wzDestinationPath); + ExitOnFailure(hr, "Failed to add file to BITS job."); + + // Set the callback into the BITs job. + pBitsCallback = new CBurnBitsCallback(pCallback, &hr); + ExitOnNull(pBitsCallback, hr, E_OUTOFMEMORY, "Failed to create BITS job callback."); + ExitOnFailure(hr, "Failed to initialize BITS job callback."); + + hr = pJob->SetNotifyInterface(pBitsCallback); + ExitOnFailure(hr, "Failed to set callback interface for BITS job."); + + // Go into our retry download loop. + do + { + fRetry = FALSE; + + pBitsCallback->Reset(); // ensure we are ready for the download to start (again?). + + hr = pJob->Resume(); + ExitOnFailure(hr, "Falied to start BITS job."); + + hr = pBitsCallback->WaitForCompletion(pJob); + ExitOnFailure(hr, "Failed while waiting for BITS download."); + + // See if there are any errors. + pBitsCallback->GetError(&hr, &contextError); + if (HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr) + { + ExitFunction(); + } + else if (FAILED(hr)) + { + SendError(pCallback, pJob, hr, contextError, &fRetry); + } + } while (fRetry); + ExitOnFailure(hr, "Failed to download BITS job."); + + // After all that, we should have the file downloaded so complete the job to get + // the file copied to the destination. + hr = pJob->Complete(); + ExitOnFailure(hr, "Failed to complete BITS job."); + +LExit: + if (pJob) + { + pJob->SetNotifyInterface(NULL); + + // If we failed, kill the job. + if (FAILED(hr)) + { + pJob->Cancel(); // TODO: should we cancel if we're going to retry the package? Probably the right thing to do. + } + } + + ReleaseObject(pBitsCallback); + ReleaseObject(pJob); + ReleaseStr(sczDownloadUrl); + + return hr; +} + +static HRESULT CreateJob( + __out IBackgroundCopyJob** ppJob + ) +{ + HRESULT hr = S_OK; + IBackgroundCopyManager* pBitsManager = NULL; + IBackgroundCopyJob* pJob = NULL; + GUID guidJob = { }; + + hr = ::CoCreateInstance(__uuidof(BackgroundCopyManager), NULL, CLSCTX_ALL, __uuidof(IBackgroundCopyManager), reinterpret_cast(&pBitsManager)); + ExitOnFailure(hr, "Failed to create IBackgroundCopyManager."); + + hr = pBitsManager->CreateJob(L"WixBurn", BG_JOB_TYPE_DOWNLOAD, &guidJob, &pJob); + ExitOnFailure(hr, "Failed to create BITS job."); + + hr = pJob->SetNotifyFlags(BG_NOTIFY_JOB_TRANSFERRED | BG_NOTIFY_JOB_ERROR | BG_NOTIFY_JOB_MODIFICATION); + ExitOnFailure(hr, "Failed to set notification flags for BITS job."); + + hr = pJob->SetNoProgressTimeout(BITSENGINE_NO_PROGRESS_TIMEOUT); // use 2 minutes since default is 14 days. + ExitOnFailure(hr, "Failed to set progress timeout."); + + hr = pJob->SetPriority(BG_JOB_PRIORITY_FOREGROUND); + ExitOnFailure(hr, "Failed to set BITS job to foreground."); + + *ppJob = pJob; + pJob = NULL; + +LExit: + ReleaseObject(pJob); + ReleaseObject(pBitsManager); + + return hr; +} + +static HRESULT SetCredentials( + __in IBackgroundCopyJob* pJob, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword + ) +{ + HRESULT hr = S_OK; + IBackgroundCopyJob2* pJob2 = NULL; + BG_AUTH_CREDENTIALS ac = { }; + + // If IBackgroundCopyJob2::SetCredentials() is supported, set the username/password. + hr = pJob->QueryInterface(IID_PPV_ARGS(&pJob2)); + if (SUCCEEDED(hr)) + { + ac.Target = BG_AUTH_TARGET_PROXY; + ac.Credentials.Basic.UserName = const_cast(wzUser); + ac.Credentials.Basic.Password = const_cast(wzPassword); + + ac.Scheme = BG_AUTH_SCHEME_NTLM; + hr = pJob2->SetCredentials(&ac); + ExitOnFailure(hr, "Failed to set background copy NTLM credentials"); + + ac.Scheme = BG_AUTH_SCHEME_NEGOTIATE; + hr = pJob2->SetCredentials(&ac); + ExitOnFailure(hr, "Failed to set background copy negotiate credentials"); + } + + hr = S_OK; + +LExit: + ReleaseObject(pJob2); + + return hr; +} + +static void SendError( + __in DOWNLOAD_CACHE_CALLBACK* pCacheCallback, + __in IBackgroundCopyJob* pJob, + __in HRESULT hrError, + __in BG_ERROR_CONTEXT /*context*/, + __out_opt BOOL* pfRetry + ) +{ + HRESULT hr = S_OK; + IBackgroundCopyError* pError = NULL; + LPWSTR pszErrorDescription = NULL; + + hr = pJob->GetError(&pError); + if (SUCCEEDED(hr)) + { + pError->GetErrorDescription(LANGIDFROMLCID(::GetThreadLocale()), &pszErrorDescription); + } + + CacheSendErrorCallback(pCacheCallback, hrError, pszErrorDescription, pfRetry); + + if (pszErrorDescription) + { + ::CoTaskMemFree(pszErrorDescription); + } + ReleaseObject(pError); +} diff --git a/src/engine/bitsengine.h b/src/engine/bitsengine.h new file mode 100644 index 00000000..b1c1d805 --- /dev/null +++ b/src/engine/bitsengine.h @@ -0,0 +1,23 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +// structs + + +// functions + +HRESULT BitsDownloadUrl( + __in DOWNLOAD_CACHE_CALLBACK* pCallback, + __in DOWNLOAD_SOURCE* pDownloadSource, + __in LPCWSTR wzDestinationPath + ); + + +#ifdef __cplusplus +} +#endif diff --git a/src/engine/cabextract.cpp b/src/engine/cabextract.cpp new file mode 100644 index 00000000..04c2c6ec --- /dev/null +++ b/src/engine/cabextract.cpp @@ -0,0 +1,974 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#include + +#define ARRAY_GROWTH_SIZE 2 + +const LPSTR INVALID_CAB_NAME = ".cab"; + +// structs + +typedef struct _BURN_CAB_CONTEXT +{ + HANDLE hFile; + DWORD64 qwOffset; + DWORD64 qwSize; + + HANDLE hThread; + HANDLE hBeginOperationEvent; + HANDLE hOperationCompleteEvent; + + BURN_CAB_OPERATION operation; + HRESULT hrError; + + LPWSTR* psczStreamName; + LPCWSTR wzTargetFile; + HANDLE hTargetFile; + BYTE* pbTargetBuffer; + DWORD cbTargetBuffer; + DWORD iTargetBuffer; +} BURN_CAB_CONTEXT; + + +// internal function declarations + +static HRESULT BeginAndWaitForOperation( + __in BURN_CONTAINER_CONTEXT* pContext + ); +static HRESULT WaitForOperation( + __in BURN_CONTAINER_CONTEXT* pContext + ); +static DWORD WINAPI ExtractThreadProc( + __in LPVOID lpThreadParameter + ); +static INT_PTR DIAMONDAPI CabNotifyCallback( + __in FDINOTIFICATIONTYPE iNotification, + __inout FDINOTIFICATION *pFDINotify + ); +static INT_PTR CopyFileCallback( + __in BURN_CONTAINER_CONTEXT* pContext, + __inout FDINOTIFICATION *pFDINotify + ); +static INT_PTR CloseFileInfoCallback( + __in BURN_CONTAINER_CONTEXT* pContext, + __inout FDINOTIFICATION *pFDINotify + ); +static LPVOID DIAMONDAPI CabAlloc( + __in DWORD dwSize + ); +static void DIAMONDAPI CabFree( + __in LPVOID pvData + ); +static INT_PTR FAR DIAMONDAPI CabOpen( + __in char FAR *pszFile, + __in int /* oflag */, + __in int /* pmode */ + ); +static UINT FAR DIAMONDAPI CabRead( + __in INT_PTR hf, + __out void FAR *pv, + __in UINT cb + ); +static UINT FAR DIAMONDAPI CabWrite( + __in INT_PTR hf, + __in void FAR *pv, + __in UINT cb + ); +static long FAR DIAMONDAPI CabSeek( + __in INT_PTR hf, + __in long dist, + __in int seektype + ); +static int FAR DIAMONDAPI CabClose( + __in INT_PTR hf + ); +static HRESULT AddVirtualFilePointer( + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, + __in HANDLE hFile, + __in LONGLONG llInitialFilePointer + ); +static HRESULT ReadIfVirtualFilePointer( + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, + __in HANDLE hFile, + __in DWORD cbRead + ); +static BOOL SetIfVirtualFilePointer( + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, + __in HANDLE hFile, + __in LONGLONG llDistance, + __out LONGLONG* pllNewPostion, + __in DWORD dwSeekType + ); +static HRESULT CloseIfVirturalFilePointer( + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, + __in HANDLE hFile + ); +static BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* GetVirtualFilePointer( + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, + __in HANDLE hFile + ); + + +// internal variables + +__declspec(thread) static BURN_CONTAINER_CONTEXT* vpContext; + + +// function definitions + +extern "C" void CabExtractInitialize() +{ +} + +extern "C" HRESULT CabExtractOpen( + __in BURN_CONTAINER_CONTEXT* pContext, + __in LPCWSTR wzFilePath + ) +{ + HRESULT hr = S_OK; + + // initialize context + pContext->Cabinet.hTargetFile = INVALID_HANDLE_VALUE; + + hr = StrAllocString(&pContext->Cabinet.sczFile, wzFilePath, 0); + ExitOnFailure(hr, "Failed to copy file name."); + + // create events + pContext->Cabinet.hBeginOperationEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL); + ExitOnNullWithLastError(pContext->Cabinet.hBeginOperationEvent, hr, "Failed to create begin operation event."); + + pContext->Cabinet.hOperationCompleteEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL); + ExitOnNullWithLastError(pContext->Cabinet.hOperationCompleteEvent, hr, "Failed to create operation complete event."); + + // create extraction thread + pContext->Cabinet.hThread = ::CreateThread(NULL, 0, ExtractThreadProc, pContext, 0, NULL); + ExitOnNullWithLastError(pContext->Cabinet.hThread, hr, "Failed to create extraction thread."); + + // wait for operation to complete + hr = WaitForOperation(pContext); + ExitOnFailure(hr, "Failed to wait for operation complete."); + +LExit: + return hr; +} + +extern "C" HRESULT CabExtractNextStream( + __in BURN_CONTAINER_CONTEXT* pContext, + __inout_z LPWSTR* psczStreamName + ) +{ + HRESULT hr = S_OK; + + // set operation to move to next stream + pContext->Cabinet.operation = BURN_CAB_OPERATION_NEXT_STREAM; + pContext->Cabinet.psczStreamName = psczStreamName; + + // begin operation and wait + hr = BeginAndWaitForOperation(pContext); + if (E_ABORT != hr && E_NOMOREITEMS != hr) + { + ExitOnFailure(hr, "Failed to begin and wait for operation."); + } + +LExit: + return hr; +} + +extern "C" HRESULT CabExtractStreamToFile( + __in BURN_CONTAINER_CONTEXT* pContext, + __in_z LPCWSTR wzFileName + ) +{ + HRESULT hr = S_OK; + + // set operation to move to next stream + pContext->Cabinet.operation = BURN_CAB_OPERATION_STREAM_TO_FILE; + pContext->Cabinet.wzTargetFile = wzFileName; + + // begin operation and wait + hr = BeginAndWaitForOperation(pContext); + ExitOnFailure(hr, "Failed to begin and wait for operation."); + + // clear file name + pContext->Cabinet.wzTargetFile = NULL; + +LExit: + return hr; +} + +extern "C" HRESULT CabExtractStreamToBuffer( + __in BURN_CONTAINER_CONTEXT* pContext, + __out BYTE** ppbBuffer, + __out SIZE_T* pcbBuffer + ) +{ + HRESULT hr = S_OK; + + // set operation to move to next stream + pContext->Cabinet.operation = BURN_CAB_OPERATION_STREAM_TO_BUFFER; + + // begin operation and wait + hr = BeginAndWaitForOperation(pContext); + ExitOnFailure(hr, "Failed to begin and wait for operation."); + + // return values + *ppbBuffer = pContext->Cabinet.pbTargetBuffer; + *pcbBuffer = pContext->Cabinet.cbTargetBuffer; + + // clear buffer variables + pContext->Cabinet.pbTargetBuffer = NULL; + pContext->Cabinet.cbTargetBuffer = 0; + pContext->Cabinet.iTargetBuffer = 0; + +LExit: + return hr; +} + +extern "C" HRESULT CabExtractSkipStream( + __in BURN_CONTAINER_CONTEXT* pContext + ) +{ + HRESULT hr = S_OK; + + // set operation to move to next stream + pContext->Cabinet.operation = BURN_CAB_OPERATION_SKIP_STREAM; + + // begin operation and wait + hr = BeginAndWaitForOperation(pContext); + ExitOnFailure(hr, "Failed to begin and wait for operation."); + +LExit: + return hr; +} + +extern "C" HRESULT CabExtractClose( + __in BURN_CONTAINER_CONTEXT* pContext + ) +{ + HRESULT hr = S_OK; + + // terminate worker thread + if (pContext->Cabinet.hThread) + { + // set operation to move to close + pContext->Cabinet.operation = BURN_CAB_OPERATION_CLOSE; + + // set begin operation event + if (!::SetEvent(pContext->Cabinet.hBeginOperationEvent)) + { + ExitWithLastError(hr, "Failed to set begin operation event."); + } + + // wait for thread to terminate + if (WAIT_OBJECT_0 != ::WaitForSingleObject(pContext->Cabinet.hThread, INFINITE)) + { + ExitWithLastError(hr, "Failed to wait for thread to terminate."); + } + } + +LExit: + ReleaseHandle(pContext->Cabinet.hThread); + ReleaseHandle(pContext->Cabinet.hBeginOperationEvent); + ReleaseHandle(pContext->Cabinet.hOperationCompleteEvent); + ReleaseMem(pContext->Cabinet.rgVirtualFilePointers); + ReleaseStr(pContext->Cabinet.sczFile); + + return hr; +} + + +// internal helper functions + +static HRESULT BeginAndWaitForOperation( + __in BURN_CONTAINER_CONTEXT* pContext + ) +{ + HRESULT hr = S_OK; + + // set begin operation event + if (!::SetEvent(pContext->Cabinet.hBeginOperationEvent)) + { + ExitWithLastError(hr, "Failed to set begin operation event."); + } + + // wait for operation to complete + hr = WaitForOperation(pContext); + +LExit: + return hr; +} + +static HRESULT WaitForOperation( + __in BURN_CONTAINER_CONTEXT* pContext + ) +{ + HRESULT hr = S_OK; + HANDLE rghWait[2] = { }; + + // wait for operation complete event + rghWait[0] = pContext->Cabinet.hOperationCompleteEvent; + rghWait[1] = pContext->Cabinet.hThread; + switch (::WaitForMultipleObjects(countof(rghWait), rghWait, FALSE, INFINITE)) + { + case WAIT_OBJECT_0: + if (!::ResetEvent(pContext->Cabinet.hOperationCompleteEvent)) + { + ExitWithLastError(hr, "Failed to reset operation complete event."); + } + break; + + case WAIT_OBJECT_0 + 1: + if (!::GetExitCodeThread(pContext->Cabinet.hThread, (DWORD*)&hr)) + { + ExitWithLastError(hr, "Failed to get extraction thread exit code."); + } + ExitFunction(); + + case WAIT_FAILED: __fallthrough; + default: + ExitWithLastError(hr, "Failed to wait for operation complete event."); + } + + // clear operation + pContext->Cabinet.operation = BURN_CAB_OPERATION_NONE; + +LExit: + return hr; +} + +static DWORD WINAPI ExtractThreadProc( + __in LPVOID lpThreadParameter + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER_CONTEXT* pContext = (BURN_CONTAINER_CONTEXT*)lpThreadParameter; + BOOL fComInitialized = FALSE; + HFDI hfdi = NULL; + ERF erf = { }; + + // initialize COM + hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); + ExitOnFailure(hr, "Failed to initialize COM."); + fComInitialized = TRUE; + + // save context in TLS storage + vpContext = pContext; + + // create FDI context + hfdi = ::FDICreate(CabAlloc, CabFree, CabOpen, CabRead, CabWrite, CabClose, CabSeek, cpuUNKNOWN, &erf); + ExitOnNull(hfdi, hr, E_FAIL, "Failed to initialize cabinet.dll."); + + // begin CAB extraction + if (!::FDICopy(hfdi, INVALID_CAB_NAME, "", 0, CabNotifyCallback, NULL, NULL)) + { + hr = pContext->Cabinet.hrError; + if (E_ABORT == hr || E_NOMOREITEMS == hr) + { + ExitFunction(); + } + else if (SUCCEEDED(hr)) + { + if (ERROR_SUCCESS != erf.erfType) + { + hr = HRESULT_FROM_WIN32(erf.erfType); + } + else + { + switch (erf.erfOper) + { + case FDIERROR_NONE: + hr = E_UNEXPECTED; + break; + case FDIERROR_CABINET_NOT_FOUND: + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + break; + case FDIERROR_NOT_A_CABINET: + hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); + break; + case FDIERROR_UNKNOWN_CABINET_VERSION: + hr = HRESULT_FROM_WIN32(ERROR_VERSION_PARSE_ERROR); + break; + case FDIERROR_CORRUPT_CABINET: + hr = HRESULT_FROM_WIN32(ERROR_FILE_CORRUPT); + break; + case FDIERROR_ALLOC_FAIL: + hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); + break; + case FDIERROR_BAD_COMPR_TYPE: + hr = HRESULT_FROM_WIN32(ERROR_UNSUPPORTED_COMPRESSION); + break; + case FDIERROR_MDI_FAIL: + hr = HRESULT_FROM_WIN32(ERROR_BAD_COMPRESSION_BUFFER); + break; + case FDIERROR_TARGET_FILE: + hr = HRESULT_FROM_WIN32(ERROR_WRITE_FAULT); + break; + case FDIERROR_RESERVE_MISMATCH: + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + break; + case FDIERROR_WRONG_CABINET: + hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH); + break; + case FDIERROR_USER_ABORT: + hr = E_ABORT; + break; + default: + hr = E_FAIL; + break; + } + } + } + ExitOnFailure(hr, "Failed to extract all files from container, erf: %d:%X:%d", erf.fError, erf.erfOper, erf.erfType); + } + + // set operation complete event + if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent)) + { + ExitWithLastError(hr, "Failed to set operation complete event."); + } + + // wait for begin operation event + if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE)) + { + ExitWithLastError(hr, "Failed to wait for begin operation event."); + } + + if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent)) + { + ExitWithLastError(hr, "Failed to reset begin operation event."); + } + + // read operation + switch (pContext->Cabinet.operation) + { + case BURN_CAB_OPERATION_NEXT_STREAM: + ExitFunction1(hr = E_NOMOREITEMS); + break; + + case BURN_CAB_OPERATION_CLOSE: + ExitFunction1(hr = S_OK); + + default: + hr = E_INVALIDSTATE; + ExitOnRootFailure(hr, "Invalid operation for this state."); + } + +LExit: + if (hfdi) + { + ::FDIDestroy(hfdi); + } + if (fComInitialized) + { + ::CoUninitialize(); + } + + return (DWORD)hr; +} + +static INT_PTR DIAMONDAPI CabNotifyCallback( + __in FDINOTIFICATIONTYPE iNotification, + __inout FDINOTIFICATION *pFDINotify + ) +{ + BURN_CONTAINER_CONTEXT* pContext = vpContext; + INT_PTR ipResult = 0; // result to return on success + + switch (iNotification) + { + case fdintCOPY_FILE: + ipResult = CopyFileCallback(pContext, pFDINotify); + break; + + case fdintCLOSE_FILE_INFO: // resource extraction complete + ipResult = CloseFileInfoCallback(pContext, pFDINotify); + break; + + case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages + case fdintNEXT_CABINET: __fallthrough; + case fdintENUMERATE: __fallthrough; + case fdintCABINET_INFO: + break; + + default: + AssertSz(FALSE, "CabExtractCallback() - unknown FDI notification command"); + }; + +//LExit: + return ipResult; +} + +static INT_PTR CopyFileCallback( + __in BURN_CONTAINER_CONTEXT* pContext, + __inout FDINOTIFICATION* pFDINotify + ) +{ + HRESULT hr = S_OK; + INT_PTR ipResult = 1; // result to return on success + LPWSTR pwzPath = NULL; + LARGE_INTEGER li = { }; + + // set operation complete event + if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent)) + { + ExitWithLastError(hr, "Failed to set operation complete event."); + } + + // wait for begin operation event + if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE)) + { + ExitWithLastError(hr, "Failed to wait for begin operation event."); + } + + if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent)) + { + ExitWithLastError(hr, "Failed to reset begin operation event."); + } + + // read operation + switch (pContext->Cabinet.operation) + { + case BURN_CAB_OPERATION_NEXT_STREAM: + break; + + case BURN_CAB_OPERATION_CLOSE: + ExitFunction1(hr = E_ABORT); + + default: + hr = E_INVALIDSTATE; + ExitOnRootFailure(hr, "Invalid operation for this state."); + } + + // copy stream name + hr = StrAllocStringAnsi(pContext->Cabinet.psczStreamName, pFDINotify->psz1, 0, CP_UTF8); + ExitOnFailure(hr, "Failed to copy stream name: %ls", pFDINotify->psz1); + + // set operation complete event + if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent)) + { + ExitWithLastError(hr, "Failed to set operation complete event."); + } + + // wait for begin operation event + if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE)) + { + ExitWithLastError(hr, "Failed to wait for begin operation event."); + } + + if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent)) + { + ExitWithLastError(hr, "Failed to reset begin operation event."); + } + + // read operation + switch (pContext->Cabinet.operation) + { + case BURN_CAB_OPERATION_STREAM_TO_FILE: + // create file + pContext->Cabinet.hTargetFile = ::CreateFileW(pContext->Cabinet.wzTargetFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == pContext->Cabinet.hTargetFile) + { + ExitWithLastError(hr, "Failed to create file: %ls", pContext->Cabinet.wzTargetFile); + } + + // set file size + li.QuadPart = pFDINotify->cb; + if (!::SetFilePointerEx(pContext->Cabinet.hTargetFile, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to set file pointer to end of file."); + } + + if (!::SetEndOfFile(pContext->Cabinet.hTargetFile)) + { + ExitWithLastError(hr, "Failed to set end of file."); + } + + li.QuadPart = 0; + if (!::SetFilePointerEx(pContext->Cabinet.hTargetFile, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to set file pointer to beginning of file."); + } + + break; + + case BURN_CAB_OPERATION_STREAM_TO_BUFFER: + // allocate buffer for stream + pContext->Cabinet.pbTargetBuffer = (BYTE*)MemAlloc(pFDINotify->cb, TRUE); + ExitOnNull(pContext->Cabinet.pbTargetBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for stream."); + + // set buffer size and write position + pContext->Cabinet.cbTargetBuffer = pFDINotify->cb; + pContext->Cabinet.iTargetBuffer = 0; + + break; + + case BURN_CAB_OPERATION_SKIP_STREAM: + ipResult = 0; + break; + + case BURN_CAB_OPERATION_CLOSE: + ExitFunction1(hr = E_ABORT); + + default: + hr = E_INVALIDSTATE; + ExitOnRootFailure(hr, "Invalid operation for this state."); + } + +LExit: + ReleaseStr(pwzPath); + + pContext->Cabinet.hrError = hr; + return SUCCEEDED(hr) ? ipResult : -1; +} + +static INT_PTR CloseFileInfoCallback( + __in BURN_CONTAINER_CONTEXT* pContext, + __inout FDINOTIFICATION *pFDINotify + ) +{ + HRESULT hr = S_OK; + INT_PTR ipResult = 1; // result to return on success + FILETIME ftLocal = { }; + FILETIME ft = { }; + + // read operation + switch (pContext->Cabinet.operation) + { + case BURN_CAB_OPERATION_STREAM_TO_FILE: + // Make a best effort to set the time on the new file before + // we close it. + if (::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal)) + { + if (::LocalFileTimeToFileTime(&ftLocal, &ft)) + { + ::SetFileTime(pContext->Cabinet.hTargetFile, &ft, &ft, &ft); + } + } + + // close file + ReleaseFile(pContext->Cabinet.hTargetFile); + break; + + case BURN_CAB_OPERATION_STREAM_TO_BUFFER: + break; + + case BURN_CAB_OPERATION_CLOSE: + ExitFunction1(hr = E_ABORT); + + default: + hr = E_INVALIDSTATE; + ExitOnRootFailure(hr, "Invalid operation for this state."); + } + + //if (pContext->pfnProgress) + //{ + // hr = StrAllocFormatted(&pwzPath, L"%s%ls", pContext->wzRootPath, pFDINotify->psz1); + // ExitOnFailure(hr, "Failed to calculate file path from: %ls and %s", pContext->wzRootPath, pFDINotify->psz1); + // if (SUCCEEDED(hr)) + // { + // hr = pContext->pfnProgress(BOX_PROGRESS_DECOMPRESSION_END, pwzPath, 0, pContext->pvContext); + // if (S_OK != hr) + // { + // pContext->hrError = hr; + // ExitFunction(); + // } + // } + //} + +LExit: + pContext->Cabinet.hrError = hr; + return SUCCEEDED(hr) ? ipResult : -1; +} + +static LPVOID DIAMONDAPI CabAlloc( + __in DWORD dwSize + ) +{ + return MemAlloc(dwSize, FALSE); +} + +static void DIAMONDAPI CabFree( + __in LPVOID pvData + ) +{ + MemFree(pvData); +} + +static INT_PTR FAR DIAMONDAPI CabOpen( + __in char FAR * pszFile, + __in int /* oflag */, + __in int /* pmode */ + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER_CONTEXT* pContext = vpContext; + HANDLE hFile = INVALID_HANDLE_VALUE; + + // If this is the invalid cab name, use our file handle. + if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, INVALID_CAB_NAME, -1, pszFile, -1)) + { + if (!::DuplicateHandle(::GetCurrentProcess(), pContext->hFile, ::GetCurrentProcess(), &hFile, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + ExitWithLastError(hr, "Failed to duplicate handle to cab container."); + } + + // Use a virtual file pointer since duplicated file handles share their file pointer. Seek to container offset + // to start. + hr = AddVirtualFilePointer(&pContext->Cabinet, hFile, pContext->qwOffset); + ExitOnFailure(hr, "Failed to add virtual file pointer for cab container."); + } + else // open file requested. This is used in the rare cases where the CAB API wants to create a temp file. + { + hFile = ::CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open cabinet file: %hs", pszFile); + } + +LExit: + pContext->Cabinet.hrError = hr; + return FAILED(hr) ? -1 : (INT_PTR)hFile; +} + +static UINT FAR DIAMONDAPI CabRead( + __in INT_PTR hf, + __out void FAR *pv, + __in UINT cb + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER_CONTEXT* pContext = vpContext; + HANDLE hFile = (HANDLE)hf; + DWORD cbRead = 0; + + ReadIfVirtualFilePointer(&pContext->Cabinet, hFile, cb); + + if (!::ReadFile(hFile, pv, cb, &cbRead, NULL)) + { + ExitWithLastError(hr, "Failed to read during cabinet extraction."); + } + +LExit: + pContext->Cabinet.hrError = hr; + return FAILED(hr) ? -1 : cbRead; +} + +static UINT FAR DIAMONDAPI CabWrite( + __in INT_PTR /* hf */, + __in void FAR *pv, + __in UINT cb + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER_CONTEXT* pContext = vpContext; + DWORD cbWrite = 0; + + switch (pContext->Cabinet.operation) + { + case BURN_CAB_OPERATION_STREAM_TO_FILE: + // write file + if (!::WriteFile(pContext->Cabinet.hTargetFile, pv, cb, &cbWrite, NULL)) + { + ExitWithLastError(hr, "Failed to write during cabinet extraction."); + } + break; + + case BURN_CAB_OPERATION_STREAM_TO_BUFFER: + // copy to target buffer + memcpy_s(pContext->Cabinet.pbTargetBuffer + pContext->Cabinet.iTargetBuffer, pContext->Cabinet.cbTargetBuffer - pContext->Cabinet.iTargetBuffer, pv, cb); + pContext->Cabinet.iTargetBuffer += cb; + + cbWrite = cb; + break; + + default: + hr = E_INVALIDSTATE; + ExitOnFailure(hr, "Unexpected call to CabWrite()."); + } + +LExit: + pContext->Cabinet.hrError = hr; + return FAILED(hr) ? -1 : cbWrite; +} + +static long FAR DIAMONDAPI CabSeek( + __in INT_PTR hf, + __in long dist, + __in int seektype + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER_CONTEXT* pContext = vpContext; + HANDLE hFile = (HANDLE)hf; + LARGE_INTEGER liDistance = { }; + LARGE_INTEGER liNewPointer = { }; + DWORD dwSeekType = 0; + + // We assume that CabSeek() will only be called to seek the + // cabinet itself so we have to offset the seek operations to + // where the internal cabinet starts. + switch (seektype) + { + case FILE_BEGIN: + liDistance.QuadPart = pContext->qwOffset + dist; + dwSeekType = FILE_BEGIN; + break; + + case FILE_CURRENT: + liDistance.QuadPart = dist; + dwSeekType = FILE_CURRENT; + break; + + case FILE_END: + liDistance.QuadPart = pContext->qwOffset + pContext->qwSize + dist; + dwSeekType = FILE_BEGIN; + break; + + default: + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid seek type.");; + } + + if (SetIfVirtualFilePointer(&pContext->Cabinet, hFile, liDistance.QuadPart, &liNewPointer.QuadPart, seektype)) + { + // set file pointer + if (!::SetFilePointerEx(hFile, liDistance, &liNewPointer, seektype)) + { + ExitWithLastError(hr, "Failed to move file pointer 0x%x bytes.", dist); + } + } + + liNewPointer.QuadPart -= pContext->qwOffset; + +LExit: + pContext->Cabinet.hrError = hr; + return FAILED(hr) ? -1 : liNewPointer.LowPart; +} + +static int FAR DIAMONDAPI CabClose( + __in INT_PTR hf + ) +{ + BURN_CONTAINER_CONTEXT* pContext = vpContext; + HANDLE hFile = (HANDLE)hf; + + CloseIfVirturalFilePointer(&pContext->Cabinet, hFile); + ReleaseFileHandle(hFile); + + return 0; +} + +static HRESULT AddVirtualFilePointer( + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, + __in HANDLE hFile, + __in LONGLONG llInitialFilePointer + ) +{ + HRESULT hr = S_OK; + + hr = MemEnsureArraySize(reinterpret_cast(&pCabinetContext->rgVirtualFilePointers), pCabinetContext->cVirtualFilePointers, sizeof(BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER), ARRAY_GROWTH_SIZE); + ExitOnFailure(hr, "Failed to allocate memory for the virtual file pointer array."); + + pCabinetContext->rgVirtualFilePointers[pCabinetContext->cVirtualFilePointers].hFile = hFile; + pCabinetContext->rgVirtualFilePointers[pCabinetContext->cVirtualFilePointers].liPosition.QuadPart = llInitialFilePointer; + ++pCabinetContext->cVirtualFilePointers; + +LExit: + return hr; +} + +static HRESULT ReadIfVirtualFilePointer( + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, + __in HANDLE hFile, + __in DWORD cbRead + ) +{ + HRESULT hr = E_NOTFOUND; + + BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile); + if (pVfp) + { + // Set the file handle to the virtual file pointer. + if (!::SetFilePointerEx(hFile, pVfp->liPosition, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to move to virtual file pointer."); + } + + pVfp->liPosition.QuadPart += cbRead; // add the amount that will be read to advance the pointer. + hr = S_OK; + } + +LExit: + return hr; +} + +static BOOL SetIfVirtualFilePointer( + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, + __in HANDLE hFile, + __in LONGLONG llDistance, + __out LONGLONG* pllNewPostion, + __in DWORD dwSeekType + ) +{ + BOOL fFound = FALSE; + + BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile); + if (pVfp) + { + switch (dwSeekType) + { + case FILE_BEGIN: + pVfp->liPosition.QuadPart = llDistance; + break; + + case FILE_CURRENT: + pVfp->liPosition.QuadPart += llDistance; + break; + + case FILE_END: __fallthrough; + default: + AssertSz(FALSE, "Unsupported seek type."); + break; + } + + *pllNewPostion = pVfp->liPosition.QuadPart; + fFound = TRUE; + } + + return fFound; +} + +static HRESULT CloseIfVirturalFilePointer( + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, + __in HANDLE hFile + ) +{ + HRESULT hr = E_NOTFOUND; + + BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile); + if (pVfp) + { + pVfp->hFile = INVALID_HANDLE_VALUE; + pVfp->liPosition.QuadPart = 0; + hr = S_OK; + } + + return hr; +} + +static BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* GetVirtualFilePointer( + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, + __in HANDLE hFile + ) +{ + for (DWORD i = 0; i < pCabinetContext->cVirtualFilePointers; ++i) + { + BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = pCabinetContext->rgVirtualFilePointers + i; + if (pVfp->hFile == hFile) + { + return pVfp; + } + } + + return NULL; +} diff --git a/src/engine/cabextract.h b/src/engine/cabextract.h new file mode 100644 index 00000000..31667f2b --- /dev/null +++ b/src/engine/cabextract.h @@ -0,0 +1,40 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// function declarations + +void CabExtractInitialize(); +HRESULT CabExtractOpen( + __in BURN_CONTAINER_CONTEXT* pContext, + __in LPCWSTR wzFilePath + ); +HRESULT CabExtractNextStream( + __in BURN_CONTAINER_CONTEXT* pContext, + __inout_z LPWSTR* psczStreamName + ); +HRESULT CabExtractStreamToFile( + __in BURN_CONTAINER_CONTEXT* pContext, + __in_z LPCWSTR wzFileName + ); +HRESULT CabExtractStreamToBuffer( + __in BURN_CONTAINER_CONTEXT* pContext, + __out BYTE** ppbBuffer, + __out SIZE_T* pcbBuffer + ); +HRESULT CabExtractSkipStream( + __in BURN_CONTAINER_CONTEXT* pContext + ); +HRESULT CabExtractClose( + __in BURN_CONTAINER_CONTEXT* pContext + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/cache.cpp b/src/engine/cache.cpp new file mode 100644 index 00000000..9338426d --- /dev/null +++ b/src/engine/cache.cpp @@ -0,0 +1,2026 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static const LPCWSTR BUNDLE_CLEAN_ROOM_WORKING_FOLDER_NAME = L".cr"; +static const LPCWSTR BUNDLE_WORKING_FOLDER_NAME = L".be"; +static const LPCWSTR UNVERIFIED_CACHE_FOLDER_NAME = L".unverified"; +static const LPCWSTR PACKAGE_CACHE_FOLDER_NAME = L"Package Cache"; +static const DWORD FILE_OPERATION_RETRY_COUNT = 3; +static const DWORD FILE_OPERATION_RETRY_WAIT = 2000; + +static BOOL vfInitializedCache = FALSE; +static BOOL vfRunningFromCache = FALSE; +static LPWSTR vsczSourceProcessPath = NULL; +static LPWSTR vsczWorkingFolder = NULL; +static LPWSTR vsczDefaultUserPackageCache = NULL; +static LPWSTR vsczDefaultMachinePackageCache = NULL; +static LPWSTR vsczCurrentMachinePackageCache = NULL; + +static HRESULT CalculateWorkingFolder( + __in_z LPCWSTR wzBundleId, + __deref_out_z LPWSTR* psczWorkingFolder + ); +static HRESULT GetLastUsedSourceFolder( + __in BURN_VARIABLES* pVariables, + __out_z LPWSTR* psczLastSource + ); +static HRESULT CreateCompletedPath( + __in BOOL fPerMachine, + __in LPCWSTR wzCacheId, + __out LPWSTR* psczCacheDirectory + ); +static HRESULT CreateUnverifiedPath( + __in BOOL fPerMachine, + __in_z LPCWSTR wzPayloadId, + __out_z LPWSTR* psczUnverifiedPayloadPath + ); +static HRESULT GetRootPath( + __in BOOL fPerMachine, + __in BOOL fAllowRedirect, + __deref_out_z LPWSTR* psczRootPath + ); +static HRESULT VerifyThenTransferContainer( + __in BURN_CONTAINER* pContainer, + __in_z LPCWSTR wzCachedPath, + __in_z LPCWSTR wzUnverifiedContainerPath, + __in BOOL fMove + ); +static HRESULT VerifyThenTransferPayload( + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzCachedPath, + __in_z LPCWSTR wzUnverifiedPayloadPath, + __in BOOL fMove + ); +static HRESULT TransferWorkingPathToUnverifiedPath( + __in_z LPCWSTR wzWorkingPath, + __in_z LPCWSTR wzUnverifiedPayloadPath, + __in BOOL fMove + ); +static HRESULT VerifyFileAgainstPayload( + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzVerifyPath + ); +static HRESULT ResetPathPermissions( + __in BOOL fPerMachine, + __in_z LPCWSTR wzPath + ); +static HRESULT SecurePath( + __in LPCWSTR wzPath + ); +static HRESULT CopyEngineToWorkingFolder( + __in_z LPCWSTR wzSourcePath, + __in_z LPCWSTR wzWorkingFolderName, + __in_z LPCWSTR wzExecutableName, + __in BURN_PAYLOADS* pUxPayloads, + __in BURN_SECTION* pSection, + __deref_out_z_opt LPWSTR* psczEngineWorkingPath + ); +static HRESULT CopyEngineWithSignatureFixup( + __in HANDLE hEngineFile, + __in_z LPCWSTR wzEnginePath, + __in_z LPCWSTR wzTargetPath, + __in BURN_SECTION* pSection + ); +static HRESULT RemoveBundleOrPackage( + __in BOOL fBundle, + __in BOOL fPerMachine, + __in_z LPCWSTR wzBundleOrPackageId, + __in_z LPCWSTR wzCacheId + ); +static HRESULT VerifyHash( + __in BYTE* pbHash, + __in DWORD cbHash, + __in_z LPCWSTR wzUnverifiedPayloadPath, + __in HANDLE hFile + ); +static HRESULT VerifyPayloadWithCatalog( + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzUnverifiedPayloadPath, + __in HANDLE hFile + ); +static HRESULT VerifyPayloadAgainstChain( + __in BURN_PAYLOAD* pPayload, + __in PCCERT_CHAIN_CONTEXT pChainContext + ); + + +extern "C" HRESULT CacheInitialize( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in_z_opt LPCWSTR wzSourceProcessPath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCurrentPath = NULL; + LPWSTR sczCompletedFolder = NULL; + LPWSTR sczCompletedPath = NULL; + LPWSTR sczOriginalSource = NULL; + LPWSTR sczOriginalSourceFolder = NULL; + int nCompare = 0; + + if (!vfInitializedCache) + { + hr = PathForCurrentProcess(&sczCurrentPath, NULL); + ExitOnFailure(hr, "Failed to get current process path."); + + // Determine if we are running from the package cache or not. + hr = CacheGetCompletedPath(pRegistration->fPerMachine, pRegistration->sczId, &sczCompletedFolder); + ExitOnFailure(hr, "Failed to get completed path for bundle."); + + hr = PathConcat(sczCompletedFolder, pRegistration->sczExecutableName, &sczCompletedPath); + ExitOnFailure(hr, "Failed to combine working path with engine file name."); + + hr = PathCompare(sczCurrentPath, sczCompletedPath, &nCompare); + ExitOnFailure(hr, "Failed to compare current path for bundle: %ls", sczCurrentPath); + + vfRunningFromCache = (CSTR_EQUAL == nCompare); + + // If a source process path was not provided (e.g. we are not being + // run in a clean room) then use the current process path as the + // source process path. + if (!wzSourceProcessPath) + { + wzSourceProcessPath = sczCurrentPath; + } + + hr = StrAllocString(&vsczSourceProcessPath, wzSourceProcessPath, 0); + ExitOnFailure(hr, "Failed to initialize cache source path."); + + // If we're not running from the cache, ensure the original source is set. + if (!vfRunningFromCache) + { + // If the original source has not been set already then set it where the bundle is + // running from right now. This value will be persisted and we'll use it when launched + // from the clean room or package cache since none of our packages will be relative to + // those locations. + hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource); + if (E_NOTFOUND == hr) + { + hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, wzSourceProcessPath, FALSE); + ExitOnFailure(hr, "Failed to set original source variable."); + + hr = StrAllocString(&sczOriginalSource, wzSourceProcessPath, 0); + ExitOnFailure(hr, "Failed to copy current path to original source."); + } + + hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, &sczOriginalSourceFolder); + if (E_NOTFOUND == hr) + { + hr = PathGetDirectory(sczOriginalSource, &sczOriginalSourceFolder); + ExitOnFailure(hr, "Failed to get directory from original source path."); + + hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, sczOriginalSourceFolder, FALSE); + ExitOnFailure(hr, "Failed to set original source directory variable."); + } + } + + vfInitializedCache = TRUE; + } + +LExit: + ReleaseStr(sczCurrentPath); + ReleaseStr(sczCompletedFolder); + ReleaseStr(sczCompletedPath); + ReleaseStr(sczOriginalSource); + ReleaseStr(sczOriginalSourceFolder); + + return hr; +} + +extern "C" HRESULT CacheEnsureWorkingFolder( + __in_z LPCWSTR wzBundleId, + __deref_out_z_opt LPWSTR* psczWorkingFolder + ) +{ + HRESULT hr = S_OK; + LPWSTR sczWorkingFolder = NULL; + + hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder); + ExitOnFailure(hr, "Failed to calculate working folder to ensure it exists."); + + hr = DirEnsureExists(sczWorkingFolder, NULL); + ExitOnFailure(hr, "Failed create working folder."); + + // Best effort to ensure our working folder is not encrypted. + ::DecryptFileW(sczWorkingFolder, 0); + + if (psczWorkingFolder) + { + hr = StrAllocString(psczWorkingFolder, sczWorkingFolder, 0); + ExitOnFailure(hr, "Failed to copy working folder."); + } + +LExit: + ReleaseStr(sczWorkingFolder); + + return hr; +} + +extern "C" HRESULT CacheCalculateBundleWorkingPath( + __in_z LPCWSTR wzBundleId, + __in LPCWSTR wzExecutableName, + __deref_out_z LPWSTR* psczWorkingPath + ) +{ + Assert(vfInitializedCache); + + HRESULT hr = S_OK; + LPWSTR sczWorkingFolder = NULL; + + // If the bundle is running out of the package cache then we use that as the + // working folder since we feel safe in the package cache. + if (vfRunningFromCache) + { + hr = PathForCurrentProcess(psczWorkingPath, NULL); + ExitOnFailure(hr, "Failed to get current process path."); + } + else // Otherwise, use the real working folder. + { + hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder); + ExitOnFailure(hr, "Failed to get working folder for bundle."); + + hr = StrAllocFormatted(psczWorkingPath, L"%ls%ls\\%ls", sczWorkingFolder, BUNDLE_WORKING_FOLDER_NAME, wzExecutableName); + ExitOnFailure(hr, "Failed to calculate the bundle working path."); + } + +LExit: + ReleaseStr(sczWorkingFolder); + + return hr; +} + +extern "C" HRESULT CacheCalculateBundleLayoutWorkingPath( + __in_z LPCWSTR wzBundleId, + __deref_out_z LPWSTR* psczWorkingPath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczWorkingFolder = NULL; + + hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath); + ExitOnFailure(hr, "Failed to get working folder for bundle layout."); + + hr = StrAllocConcat(psczWorkingPath, wzBundleId, 0); + ExitOnFailure(hr, "Failed to append bundle id for bundle layout working path."); + +LExit: + ReleaseStr(sczWorkingFolder); + + return hr; +} + +extern "C" HRESULT CacheCalculatePayloadWorkingPath( + __in_z LPCWSTR wzBundleId, + __in BURN_PAYLOAD* pPayload, + __deref_out_z LPWSTR* psczWorkingPath + ) +{ + HRESULT hr = S_OK; + + hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath); + ExitOnFailure(hr, "Failed to get working folder for payload."); + + hr = StrAllocConcat(psczWorkingPath, pPayload->sczKey, 0); + ExitOnFailure(hr, "Failed to append SHA1 hash as payload unverified path."); + +LExit: + return hr; +} + +extern "C" HRESULT CacheCalculateContainerWorkingPath( + __in_z LPCWSTR wzBundleId, + __in BURN_CONTAINER* pContainer, + __deref_out_z LPWSTR* psczWorkingPath + ) +{ + HRESULT hr = S_OK; + + hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath); + ExitOnFailure(hr, "Failed to get working folder for container."); + + hr = StrAllocConcat(psczWorkingPath, pContainer->sczHash, 0); + ExitOnFailure(hr, "Failed to append SHA1 hash as container unverified path."); + +LExit: + return hr; +} + +extern "C" HRESULT CacheGetRootCompletedPath( + __in BOOL fPerMachine, + __in BOOL fForceInitialize, + __deref_out_z LPWSTR* psczRootCompletedPath + ) +{ + HRESULT hr = S_OK; + + if (fForceInitialize) + { + hr = CreateCompletedPath(fPerMachine, L"", psczRootCompletedPath); + } + else + { + hr = GetRootPath(fPerMachine, TRUE, psczRootCompletedPath); + } + + return hr; +} + +extern "C" HRESULT CacheGetCompletedPath( + __in BOOL fPerMachine, + __in_z LPCWSTR wzCacheId, + __deref_out_z LPWSTR* psczCompletedPath + ) +{ + HRESULT hr = S_OK; + BOOL fRedirected = FALSE; + LPWSTR sczRootPath = NULL; + LPWSTR sczCurrentCompletedPath = NULL; + LPWSTR sczDefaultCompletedPath = NULL; + + hr = GetRootPath(fPerMachine, TRUE, &sczRootPath); + ExitOnFailure(hr, "Failed to get %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user"); + + // GetRootPath returns S_FALSE if the package cache is redirected elsewhere. + fRedirected = S_FALSE == hr; + + hr = PathConcat(sczRootPath, wzCacheId, &sczCurrentCompletedPath); + ExitOnFailure(hr, "Failed to construct cache path."); + + hr = PathBackslashTerminate(&sczCurrentCompletedPath); + ExitOnFailure(hr, "Failed to ensure cache path was backslash terminated."); + + // Return the old package cache directory if the new directory does not exist but the old directory does. + // If neither package cache directory exists return the (possibly) redirected package cache directory. + if (fRedirected && !DirExists(sczCurrentCompletedPath, NULL)) + { + hr = GetRootPath(fPerMachine, FALSE, &sczRootPath); + ExitOnFailure(hr, "Failed to get old %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user"); + + hr = PathConcat(sczRootPath, wzCacheId, &sczDefaultCompletedPath); + ExitOnFailure(hr, "Failed to construct cache path."); + + hr = PathBackslashTerminate(&sczDefaultCompletedPath); + ExitOnFailure(hr, "Failed to ensure cache path was backslash terminated."); + + if (DirExists(sczDefaultCompletedPath, NULL)) + { + *psczCompletedPath = sczDefaultCompletedPath; + sczDefaultCompletedPath = NULL; + + ExitFunction(); + } + } + + *psczCompletedPath = sczCurrentCompletedPath; + sczCurrentCompletedPath = NULL; + +LExit: + ReleaseNullStr(sczDefaultCompletedPath); + ReleaseNullStr(sczCurrentCompletedPath); + ReleaseNullStr(sczRootPath); + + return hr; +} + +extern "C" HRESULT CacheGetResumePath( + __in_z LPCWSTR wzPayloadWorkingPath, + __deref_out_z LPWSTR* psczResumePath + ) +{ + HRESULT hr = S_OK; + + hr = StrAllocFormatted(psczResumePath, L"%ls.R", wzPayloadWorkingPath); + ExitOnFailure(hr, "Failed to create resume path."); + +LExit: + return hr; +} + +extern "C" HRESULT CacheFindLocalSource( + __in_z LPCWSTR wzSourcePath, + __in BURN_VARIABLES* pVariables, + __out BOOL* pfFound, + __out_z LPWSTR* psczSourceFullPath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczSourceProcessFolder = NULL; + LPWSTR sczCurrentPath = NULL; + LPWSTR sczLastSourcePath = NULL; + LPWSTR sczLastSourceFolder = NULL; + LPWSTR sczLayoutPath = NULL; + LPWSTR sczLayoutFolder = NULL; + LPCWSTR rgwzSearchPaths[3] = { }; + DWORD cSearchPaths = 0; + + // If the source path provided is a full path, obviously that is where we should be looking. + if (PathIsAbsolute(wzSourcePath)) + { + rgwzSearchPaths[0] = wzSourcePath; + cSearchPaths = 1; + } + else + { + // If we're not running from cache or we couldn't get the last source, use + // the source path location first. In the case where we are in the bundle's + // package cache and couldn't find a last used source we unfortunately will + // be picking the package cache path which isn't likely to have what we are + // looking for. + hr = GetLastUsedSourceFolder(pVariables, &sczLastSourceFolder); + if (!vfRunningFromCache || FAILED(hr)) + { + hr = PathGetDirectory(vsczSourceProcessPath, &sczSourceProcessFolder); + ExitOnFailure(hr, "Failed to get current process directory."); + + hr = PathConcat(sczSourceProcessFolder, wzSourcePath, &sczCurrentPath); + ExitOnFailure(hr, "Failed to combine last source with source."); + + rgwzSearchPaths[0] = sczCurrentPath; + cSearchPaths = 1; + } + + // If we have a last used source and it does not duplicate the existing search path, + // add the last used source to the search path second. + if (sczLastSourceFolder && *sczLastSourceFolder) + { + hr = PathConcat(sczLastSourceFolder, wzSourcePath, &sczLastSourcePath); + ExitOnFailure(hr, "Failed to combine last source with source."); + + if (0 == cSearchPaths || CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, rgwzSearchPaths[0], -1, sczLastSourcePath, -1)) + { + rgwzSearchPaths[cSearchPaths] = sczLastSourcePath; + ++cSearchPaths; + } + } + + // Also consider the layout directory if set on the command line or by the BA. + hr = VariableGetString(pVariables, BURN_BUNDLE_LAYOUT_DIRECTORY, &sczLayoutFolder); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get bundle layout directory property."); + + hr = PathConcat(sczLayoutFolder, wzSourcePath, &sczLayoutPath); + ExitOnFailure(hr, "Failed to combine layout source with source."); + + rgwzSearchPaths[cSearchPaths] = sczLayoutPath; + ++cSearchPaths; + } + } + + *pfFound = FALSE; // assume we won't find the file locally. + + for (DWORD i = 0; i < cSearchPaths; ++i) + { + // If the file exists locally, copy its path. + if (FileExistsEx(rgwzSearchPaths[i], NULL)) + { + hr = StrAllocString(psczSourceFullPath, rgwzSearchPaths[i], 0); + ExitOnFailure(hr, "Failed to copy source path."); + + *pfFound = TRUE; + break; + } + } + + // If nothing was found, return the first thing in our search path as the + // best path where we thought we should have found the file. + if (!*pfFound) + { + hr = StrAllocString(psczSourceFullPath, rgwzSearchPaths[0], 0); + ExitOnFailure(hr, "Failed to copy source path."); + } + +LExit: + ReleaseStr(sczCurrentPath); + ReleaseStr(sczSourceProcessFolder); + ReleaseStr(sczLastSourceFolder); + ReleaseStr(sczLastSourcePath); + ReleaseStr(sczLayoutFolder); + ReleaseStr(sczLayoutPath); + + return hr; +} + +extern "C" HRESULT CacheSetLastUsedSource( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzSourcePath, + __in_z LPCWSTR wzRelativePath + ) +{ + HRESULT hr = S_OK; + size_t cchSourcePath = 0; + size_t cchRelativePath = 0; + size_t iSourceRelativePath = 0; + LPWSTR sczSourceFolder = NULL; + LPWSTR sczLastSourceFolder = NULL; + int nCompare = 0; + + hr = ::StringCchLengthW(wzSourcePath, STRSAFE_MAX_CCH, &cchSourcePath); + ExitOnFailure(hr, "Failed to determine length of source path."); + + hr = ::StringCchLengthW(wzRelativePath, STRSAFE_MAX_CCH, &cchRelativePath); + ExitOnFailure(hr, "Failed to determine length of relative path."); + + // If the source path is smaller than the relative path (plus space for "X:\") then we know they + // are not relative to each other. + if (cchSourcePath < cchRelativePath + 3) + { + ExitFunction(); + } + + // If the source path ends with the relative path then this source could be a new path. + iSourceRelativePath = cchSourcePath - cchRelativePath; + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzSourcePath + iSourceRelativePath, -1, wzRelativePath, -1)) + { + hr = StrAllocString(&sczSourceFolder, wzSourcePath, iSourceRelativePath); + ExitOnFailure(hr, "Failed to trim source folder."); + + hr = VariableGetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, &sczLastSourceFolder); + if (SUCCEEDED(hr)) + { + nCompare = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczSourceFolder, -1, sczLastSourceFolder, -1); + } + else if (E_NOTFOUND == hr) + { + nCompare = CSTR_GREATER_THAN; + hr = S_OK; + } + + if (CSTR_EQUAL != nCompare) + { + hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, sczSourceFolder, FALSE); + ExitOnFailure(hr, "Failed to set last source."); + } + } + +LExit: + ReleaseStr(sczLastSourceFolder); + ReleaseStr(sczSourceFolder); + + return hr; +} + +extern "C" HRESULT CacheSendProgressCallback( + __in DOWNLOAD_CACHE_CALLBACK* pCallback, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in HANDLE hDestinationFile + ) +{ + static LARGE_INTEGER LARGE_INTEGER_ZERO = { }; + + HRESULT hr = S_OK; + DWORD dwResult = PROGRESS_CONTINUE; + LARGE_INTEGER liTotalSize = { }; + LARGE_INTEGER liTotalTransferred = { }; + + if (pCallback->pfnProgress) + { + liTotalSize.QuadPart = dw64Total; + liTotalTransferred.QuadPart = dw64Progress; + + dwResult = (*pCallback->pfnProgress)(liTotalSize, liTotalTransferred, LARGE_INTEGER_ZERO, LARGE_INTEGER_ZERO, 1, CALLBACK_CHUNK_FINISHED, INVALID_HANDLE_VALUE, hDestinationFile, pCallback->pv); + switch (dwResult) + { + case PROGRESS_CONTINUE: + hr = S_OK; + break; + + case PROGRESS_CANCEL: __fallthrough; // TODO: should cancel and stop be treated differently? + case PROGRESS_STOP: + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + ExitOnRootFailure(hr, "UX aborted on download progress."); + + case PROGRESS_QUIET: // Not actually an error, just an indication to the caller to stop requesting progress. + pCallback->pfnProgress = NULL; + hr = S_OK; + break; + + default: + hr = E_UNEXPECTED; + ExitOnRootFailure(hr, "Invalid return code from progress routine."); + } + } + +LExit: + return hr; +} + +extern "C" void CacheSendErrorCallback( + __in DOWNLOAD_CACHE_CALLBACK* pCallback, + __in HRESULT hrError, + __in_z_opt LPCWSTR wzError, + __out_opt BOOL* pfRetry + ) +{ + if (pfRetry) + { + *pfRetry = FALSE; + } + + if (pCallback->pfnCancel) + { + int nResult = (*pCallback->pfnCancel)(hrError, wzError, pfRetry != NULL, pCallback->pv); + if (pfRetry && IDRETRY == nResult) + { + *pfRetry = TRUE; + } + } +} + +extern "C" BOOL CacheBundleRunningFromCache() +{ + return vfRunningFromCache; +} + +extern "C" HRESULT CacheBundleToCleanRoom( + __in BURN_PAYLOADS* pUxPayloads, + __in BURN_SECTION* pSection, + __deref_out_z_opt LPWSTR* psczCleanRoomBundlePath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczSourcePath = NULL; + LPWSTR wzExecutableName = NULL; + + hr = PathForCurrentProcess(&sczSourcePath, NULL); + ExitOnFailure(hr, "Failed to get current path for process to cache to clean room."); + + wzExecutableName = PathFile(sczSourcePath); + + hr = CopyEngineToWorkingFolder(sczSourcePath, BUNDLE_CLEAN_ROOM_WORKING_FOLDER_NAME, wzExecutableName, pUxPayloads, pSection, psczCleanRoomBundlePath); + ExitOnFailure(hr, "Failed to cache bundle to clean room."); + +LExit: + ReleaseStr(sczSourcePath); + + return hr; +} + +extern "C" HRESULT CacheBundleToWorkingDirectory( + __in_z LPCWSTR /*wzBundleId*/, + __in_z LPCWSTR wzExecutableName, + __in BURN_PAYLOADS* pUxPayloads, + __in BURN_SECTION* pSection, + __deref_out_z_opt LPWSTR* psczEngineWorkingPath + ) +{ + Assert(vfInitializedCache); + + HRESULT hr = S_OK; + LPWSTR sczSourcePath = NULL; + + // Initialize the source. + hr = PathForCurrentProcess(&sczSourcePath, NULL); + ExitOnFailure(hr, "Failed to get current process path."); + + // If the bundle is running out of the package cache then we don't need to copy it to + // the working folder since we feel safe in the package cache and will run from there. + if (vfRunningFromCache) + { + hr = StrAllocString(psczEngineWorkingPath, sczSourcePath, 0); + ExitOnFailure(hr, "Failed to use current process path as target path."); + } + else // otherwise, carry on putting the bundle in the working folder. + { + hr = CopyEngineToWorkingFolder(sczSourcePath, BUNDLE_WORKING_FOLDER_NAME, wzExecutableName, pUxPayloads, pSection, psczEngineWorkingPath); + ExitOnFailure(hr, "Failed to copy engine to working folder."); + } + +LExit: + ReleaseStr(sczSourcePath); + + return hr; +} + +extern "C" HRESULT CacheLayoutBundle( + __in_z LPCWSTR wzExecutableName, + __in_z LPCWSTR wzLayoutDirectory, + __in_z LPCWSTR wzSourceBundlePath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczTargetPath = NULL; + + hr = PathConcat(wzLayoutDirectory, wzExecutableName, &sczTargetPath); + ExitOnFailure(hr, "Failed to combine completed path with engine file name for layout."); + + LogStringLine(REPORT_STANDARD, "Layout bundle from: '%ls' to: '%ls'", wzSourceBundlePath, sczTargetPath); + + hr = FileEnsureMoveWithRetry(wzSourceBundlePath, sczTargetPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); + ExitOnFailure(hr, "Failed to layout bundle from: '%ls' to '%ls'", wzSourceBundlePath, sczTargetPath); + +LExit: + ReleaseStr(sczTargetPath); + + return hr; +} + +extern "C" HRESULT CacheCompleteBundle( + __in BOOL fPerMachine, + __in_z LPCWSTR wzExecutableName, + __in_z LPCWSTR wzBundleId, + __in BURN_PAYLOADS* pUxPayloads, + __in_z LPCWSTR wzSourceBundlePath +#ifdef DEBUG + , __in_z LPCWSTR wzExecutablePath +#endif + ) +{ + HRESULT hr = S_OK; + int nCompare = 0; + LPWSTR sczTargetDirectory = NULL; + LPWSTR sczTargetPath = NULL; + LPWSTR sczSourceDirectory = NULL; + LPWSTR sczPayloadSourcePath = NULL; + + hr = CreateCompletedPath(fPerMachine, wzBundleId, &sczTargetDirectory); + ExitOnFailure(hr, "Failed to create completed cache path for bundle."); + + hr = PathConcat(sczTargetDirectory, wzExecutableName, &sczTargetPath); + ExitOnFailure(hr, "Failed to combine completed path with engine file name."); + + Assert(CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzExecutablePath, -1, sczTargetPath, -1)); + + // If the bundle is running out of the package cache then we don't need to copy it there + // (and don't want to since it'll be in use) so bail. + hr = PathCompare(wzSourceBundlePath, sczTargetPath, &nCompare); + ExitOnFailure(hr, "Failed to compare completed cache path for bundle: %ls", wzSourceBundlePath); + + if (CSTR_EQUAL == nCompare) + { + ExitFunction(); + } + + // Otherwise, carry on putting the bundle in the cache. + LogStringLine(REPORT_STANDARD, "Caching bundle from: '%ls' to: '%ls'", wzSourceBundlePath, sczTargetPath); + + FileRemoveFromPendingRename(sczTargetPath); // best effort to ensure bundle is not deleted from cache post restart. + + hr = FileEnsureCopyWithRetry(wzSourceBundlePath, sczTargetPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); + ExitOnFailure(hr, "Failed to cache bundle from: '%ls' to '%ls'", wzSourceBundlePath, sczTargetPath); + + // Reset the path permissions in the cache. + hr = ResetPathPermissions(fPerMachine, sczTargetPath); + ExitOnFailure(hr, "Failed to reset permissions on cached bundle: '%ls'", sczTargetPath); + + hr = PathGetDirectory(wzSourceBundlePath, &sczSourceDirectory); + ExitOnFailure(hr, "Failed to get directory from engine working path: %ls", wzSourceBundlePath); + + // Cache external UX payloads to completed path. + for (DWORD i = 0; i < pUxPayloads->cPayloads; ++i) + { + BURN_PAYLOAD* pPayload = &pUxPayloads->rgPayloads[i]; + + if (BURN_PAYLOAD_PACKAGING_EXTERNAL == pPayload->packaging) + { + hr = PathConcat(sczSourceDirectory, pPayload->sczSourcePath, &sczPayloadSourcePath); + ExitOnFailure(hr, "Failed to build payload source path."); + + hr = CacheCompletePayload(fPerMachine, pPayload, wzBundleId, sczPayloadSourcePath, FALSE); + ExitOnFailure(hr, "Failed to complete the cache of payload: %ls", pPayload->sczKey); + } + } + +LExit: + ReleaseStr(sczPayloadSourcePath); + ReleaseStr(sczSourceDirectory); + ReleaseStr(sczTargetPath); + ReleaseStr(sczTargetDirectory); + + return hr; +} + +extern "C" HRESULT CacheLayoutContainer( + __in BURN_CONTAINER* pContainer, + __in_z_opt LPCWSTR wzLayoutDirectory, + __in_z LPCWSTR wzUnverifiedContainerPath, + __in BOOL fMove + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCachedPath = NULL; + + hr = PathConcat(wzLayoutDirectory, pContainer->sczFilePath, &sczCachedPath); + ExitOnFailure(hr, "Failed to concat complete cached path."); + + hr = VerifyThenTransferContainer(pContainer, sczCachedPath, wzUnverifiedContainerPath, fMove); + ExitOnFailure(hr, "Failed to layout container from cached path: %ls", sczCachedPath); + +LExit: + ReleaseStr(sczCachedPath); + + return hr; +} + +extern "C" HRESULT CacheLayoutPayload( + __in BURN_PAYLOAD* pPayload, + __in_z_opt LPCWSTR wzLayoutDirectory, + __in_z LPCWSTR wzUnverifiedPayloadPath, + __in BOOL fMove + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCachedPath = NULL; + + hr = PathConcat(wzLayoutDirectory, pPayload->sczFilePath, &sczCachedPath); + ExitOnFailure(hr, "Failed to concat complete cached path."); + + hr = VerifyThenTransferPayload(pPayload, sczCachedPath, wzUnverifiedPayloadPath, fMove); + ExitOnFailure(hr, "Failed to layout payload from cached payload: %ls", sczCachedPath); + +LExit: + ReleaseStr(sczCachedPath); + + return hr; +} + +extern "C" HRESULT CacheCompletePayload( + __in BOOL fPerMachine, + __in BURN_PAYLOAD* pPayload, + __in_z_opt LPCWSTR wzCacheId, + __in_z LPCWSTR wzWorkingPayloadPath, + __in BOOL fMove + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCachedDirectory = NULL; + LPWSTR sczCachedPath = NULL; + LPWSTR sczUnverifiedPayloadPath = NULL; + + hr = CreateCompletedPath(fPerMachine, wzCacheId, &sczCachedDirectory); + ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", wzCacheId); + + hr = PathConcat(sczCachedDirectory, pPayload->sczFilePath, &sczCachedPath); + ExitOnFailure(hr, "Failed to concat complete cached path."); + + // If the cached file matches what we expected, we're good. + hr = VerifyFileAgainstPayload(pPayload, sczCachedPath); + if (SUCCEEDED(hr)) + { + ::DecryptFileW(sczCachedPath, 0); // Let's try to make sure it's not encrypted. + LogId(REPORT_STANDARD, MSG_VERIFIED_EXISTING_PAYLOAD, pPayload->sczKey, sczCachedPath); + ExitFunction(); + } + else if (E_PATHNOTFOUND != hr && E_FILENOTFOUND != hr) + { + LogErrorId(hr, MSG_FAILED_VERIFY_PAYLOAD, pPayload->sczKey, sczCachedPath, NULL); + + FileEnsureDelete(sczCachedPath); // if the file existed but did not verify correctly, make it go away. + } + + hr = CreateUnverifiedPath(fPerMachine, pPayload->sczKey, &sczUnverifiedPayloadPath); + ExitOnFailure(hr, "Failed to create unverified path."); + + // If the working path exists, let's get it into the unverified path so we can reset the ACLs and verify the file. + if (FileExistsEx(wzWorkingPayloadPath, NULL)) + { + hr = TransferWorkingPathToUnverifiedPath(wzWorkingPayloadPath, sczUnverifiedPayloadPath, fMove); + ExitOnFailure(hr, "Failed to transfer working path to unverified path for payload: %ls.", pPayload->sczKey); + } + else if (!FileExistsEx(sczUnverifiedPayloadPath, NULL)) // if the working path and unverified path do not exist, nothing we can do. + { + hr = E_FILENOTFOUND; + ExitOnFailure(hr, "Failed to find payload: %ls in working path: %ls and unverified path: %ls", pPayload->sczKey, wzWorkingPayloadPath, sczUnverifiedPayloadPath); + } + + hr = ResetPathPermissions(fPerMachine, sczUnverifiedPayloadPath); + ExitOnFailure(hr, "Failed to reset permissions on unverified cached payload: %ls", pPayload->sczKey); + + hr = VerifyFileAgainstPayload(pPayload, sczUnverifiedPayloadPath); + if (FAILED(hr)) + { + LogErrorId(hr, MSG_FAILED_VERIFY_PAYLOAD, pPayload->sczKey, sczUnverifiedPayloadPath, NULL); + + FileEnsureDelete(sczUnverifiedPayloadPath); // if the file did not verify correctly, make it go away. + ExitFunction(); + } + + LogId(REPORT_STANDARD, MSG_VERIFIED_ACQUIRED_PAYLOAD, pPayload->sczKey, sczUnverifiedPayloadPath, fMove ? "moving" : "copying", sczCachedPath); + + hr = FileEnsureMoveWithRetry(sczUnverifiedPayloadPath, sczCachedPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); + ExitOnFailure(hr, "Failed to move verified file to complete payload path: %ls", sczCachedPath); + + ::DecryptFileW(sczCachedPath, 0); // Let's try to make sure it's not encrypted. + +LExit: + ReleaseStr(sczUnverifiedPayloadPath); + ReleaseStr(sczCachedPath); + ReleaseStr(sczCachedDirectory); + + return hr; +} + +extern "C" HRESULT CacheRemoveWorkingFolder( + __in_z_opt LPCWSTR wzBundleId + ) +{ + HRESULT hr = S_OK; + LPWSTR sczWorkingFolder = NULL; + + if (vfInitializedCache) + { + hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder); + ExitOnFailure(hr, "Failed to calculate the working folder to remove it."); + + // Try to clean out everything in the working folder. + hr = DirEnsureDeleteEx(sczWorkingFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE); + TraceError(hr, "Could not delete bundle engine working folder."); + } + +LExit: + ReleaseStr(sczWorkingFolder); + + return hr; +} + +extern "C" HRESULT CacheRemoveBundle( + __in BOOL fPerMachine, + __in_z LPCWSTR wzBundleId + ) +{ + HRESULT hr = S_OK; + + hr = RemoveBundleOrPackage(TRUE, fPerMachine, wzBundleId, wzBundleId); + ExitOnFailure(hr, "Failed to remove bundle id: %ls.", wzBundleId); + +LExit: + return hr; +} + +extern "C" HRESULT CacheRemovePackage( + __in BOOL fPerMachine, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzCacheId + ) +{ + HRESULT hr = S_OK; + + hr = RemoveBundleOrPackage(FALSE, fPerMachine, wzPackageId, wzCacheId); + ExitOnFailure(hr, "Failed to remove package id: %ls.", wzPackageId); + +LExit: + return hr; +} + +extern "C" HRESULT CacheVerifyPayloadSignature( + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzUnverifiedPayloadPath, + __in HANDLE hFile + ) +{ + HRESULT hr = S_OK; + LONG er = ERROR_SUCCESS; + + GUID guidAuthenticode = WINTRUST_ACTION_GENERIC_VERIFY_V2; + WINTRUST_FILE_INFO wfi = { }; + WINTRUST_DATA wtd = { }; + CRYPT_PROVIDER_DATA* pProviderData = NULL; + CRYPT_PROVIDER_SGNR* pSigner = NULL; + + // Verify the payload assuming online. + wfi.cbStruct = sizeof(wfi); + wfi.pcwszFilePath = wzUnverifiedPayloadPath; + wfi.hFile = hFile; + + wtd.cbStruct = sizeof(wtd); + wtd.dwUnionChoice = WTD_CHOICE_FILE; + wtd.pFile = &wfi; + wtd.dwStateAction = WTD_STATEACTION_VERIFY; + wtd.dwProvFlags = WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT; + wtd.dwUIChoice = WTD_UI_NONE; + + er = ::WinVerifyTrust(static_cast(INVALID_HANDLE_VALUE), &guidAuthenticode, &wtd); + if (er) + { + // Verify the payload assuming offline. + wtd.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL; + + er = ::WinVerifyTrust(static_cast(INVALID_HANDLE_VALUE), &guidAuthenticode, &wtd); + ExitOnWin32Error(er, hr, "Failed authenticode verification of payload: %ls", wzUnverifiedPayloadPath); + } + + pProviderData = WTHelperProvDataFromStateData(wtd.hWVTStateData); + ExitOnNullWithLastError(pProviderData, hr, "Failed to get provider state from authenticode certificate."); + + pSigner = WTHelperGetProvSignerFromChain(pProviderData, 0, FALSE, 0); + ExitOnNullWithLastError(pSigner, hr, "Failed to get signer chain from authenticode certificate."); + + hr = VerifyPayloadAgainstChain(pPayload, pSigner->pChainContext); + ExitOnFailure(hr, "Failed to verify expected payload against actual certificate chain."); + +LExit: + return hr; +} + +extern "C" void CacheCleanup( + __in BOOL fPerMachine, + __in_z LPCWSTR wzBundleId + ) +{ + HRESULT hr = S_OK; + LPWSTR sczFolder = NULL; + LPWSTR sczFiles = NULL; + LPWSTR sczDelete = NULL; + HANDLE hFind = INVALID_HANDLE_VALUE; + WIN32_FIND_DATAW wfd = { }; + DWORD cFileName = 0; + + hr = CacheGetCompletedPath(fPerMachine, UNVERIFIED_CACHE_FOLDER_NAME, &sczFolder); + if (SUCCEEDED(hr)) + { + hr = DirEnsureDeleteEx(sczFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE); + } + + if (!fPerMachine) + { + hr = CalculateWorkingFolder(wzBundleId, &sczFolder); + if (SUCCEEDED(hr)) + { + hr = PathConcat(sczFolder, L"*.*", &sczFiles); + if (SUCCEEDED(hr)) + { + hFind = ::FindFirstFileW(sczFiles, &wfd); + if (INVALID_HANDLE_VALUE != hFind) + { + do + { + // Skip directories. + if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + continue; + } + + // For extra safety and to silence OACR. + wfd.cFileName[MAX_PATH - 1] = L'\0'; + + // Skip resume files (they end with ".R"). + cFileName = lstrlenW(wfd.cFileName); + if (2 < cFileName && L'.' == wfd.cFileName[cFileName - 2] && (L'R' == wfd.cFileName[cFileName - 1] || L'r' == wfd.cFileName[cFileName - 1])) + { + continue; + } + + hr = PathConcat(sczFolder, wfd.cFileName, &sczDelete); + if (SUCCEEDED(hr)) + { + hr = FileEnsureDelete(sczDelete); + } + } while (::FindNextFileW(hFind, &wfd)); + } + } + } + } + + if (INVALID_HANDLE_VALUE != hFind) + { + ::FindClose(hFind); + } + + ReleaseStr(sczDelete); + ReleaseStr(sczFiles); + ReleaseStr(sczFolder); +} + +extern "C" void CacheUninitialize() +{ + ReleaseNullStr(vsczCurrentMachinePackageCache); + ReleaseNullStr(vsczDefaultMachinePackageCache); + ReleaseNullStr(vsczDefaultUserPackageCache); + ReleaseNullStr(vsczWorkingFolder); + ReleaseNullStr(vsczSourceProcessPath); + + vfRunningFromCache = FALSE; + vfInitializedCache = FALSE; +} + +// Internal functions. + +static HRESULT CalculateWorkingFolder( + __in_z LPCWSTR /*wzBundleId*/, + __deref_out_z LPWSTR* psczWorkingFolder + ) +{ + HRESULT hr = S_OK; + RPC_STATUS rs = RPC_S_OK; + BOOL fElevated = FALSE; + WCHAR wzTempPath[MAX_PATH] = { }; + UUID guid = {}; + WCHAR wzGuid[39]; + + if (!vsczWorkingFolder) + { + ProcElevated(::GetCurrentProcess(), &fElevated); + + if (fElevated) + { + if (!::GetWindowsDirectoryW(wzTempPath, countof(wzTempPath))) + { + ExitWithLastError(hr, "Failed to get windows path for working folder."); + } + + hr = PathFixedBackslashTerminate(wzTempPath, countof(wzTempPath)); + ExitOnFailure(hr, "Failed to ensure windows path for working folder ended in backslash."); + + hr = ::StringCchCatW(wzTempPath, countof(wzTempPath), L"Temp\\"); + ExitOnFailure(hr, "Failed to concat Temp directory on windows path for working folder."); + } + else if (0 == ::GetTempPathW(countof(wzTempPath), wzTempPath)) + { + ExitWithLastError(hr, "Failed to get temp path for working folder."); + } + + rs = ::UuidCreate(&guid); + hr = HRESULT_FROM_RPC(rs); + ExitOnFailure(hr, "Failed to create working folder guid."); + + if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid))) + { + hr = E_OUTOFMEMORY; + ExitOnRootFailure(hr, "Failed to convert working folder guid into string."); + } + + hr = StrAllocFormatted(&vsczWorkingFolder, L"%ls%ls\\", wzTempPath, wzGuid); + ExitOnFailure(hr, "Failed to append bundle id on to temp path for working folder."); + } + + hr = StrAllocString(psczWorkingFolder, vsczWorkingFolder, 0); + ExitOnFailure(hr, "Failed to copy working folder path."); + +LExit: + return hr; +} + +static HRESULT GetRootPath( + __in BOOL fPerMachine, + __in BOOL fAllowRedirect, + __deref_out_z LPWSTR* psczRootPath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczAppData = NULL; + int nCompare = 0; + + // Cache paths are initialized once so they cannot be changed while the engine is caching payloads. + if (fPerMachine) + { + // Always construct the default machine package cache path so we can determine if we're redirected. + if (!vsczDefaultMachinePackageCache) + { + hr = PathGetKnownFolder(CSIDL_COMMON_APPDATA, &sczAppData); + ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-machine"); + + hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &vsczDefaultMachinePackageCache); + ExitOnFailure(hr, "Failed to construct %hs package cache directory name.", "per-machine"); + + hr = PathBackslashTerminate(&vsczDefaultMachinePackageCache); + ExitOnFailure(hr, "Failed to backslash terminate default %hs package cache directory name.", "per-machine"); + } + + if (!vsczCurrentMachinePackageCache) + { + hr = PolcReadString(POLICY_BURN_REGISTRY_PATH, L"PackageCache", NULL, &vsczCurrentMachinePackageCache); + ExitOnFailure(hr, "Failed to read PackageCache policy directory."); + + if (vsczCurrentMachinePackageCache) + { + hr = PathBackslashTerminate(&vsczCurrentMachinePackageCache); + ExitOnFailure(hr, "Failed to backslash terminate redirected per-machine package cache directory name."); + } + else + { + hr = StrAllocString(&vsczCurrentMachinePackageCache, vsczDefaultMachinePackageCache, 0); + ExitOnFailure(hr, "Failed to copy default package cache directory to current package cache directory."); + } + } + + hr = StrAllocString(psczRootPath, fAllowRedirect ? vsczCurrentMachinePackageCache : vsczDefaultMachinePackageCache, 0); + ExitOnFailure(hr, "Failed to copy %hs package cache root directory.", "per-machine"); + + hr = PathCompare(vsczDefaultMachinePackageCache, *psczRootPath, &nCompare); + ExitOnFailure(hr, "Failed to compare default and current package cache directories."); + + // Return S_FALSE if the current location is not the default location (redirected). + hr = CSTR_EQUAL == nCompare ? S_OK : S_FALSE; + } + else + { + if (!vsczDefaultUserPackageCache) + { + hr = PathGetKnownFolder(CSIDL_LOCAL_APPDATA, &sczAppData); + ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-user"); + + hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &vsczDefaultUserPackageCache); + ExitOnFailure(hr, "Failed to construct %hs package cache directory name.", "per-user"); + + hr = PathBackslashTerminate(&vsczDefaultUserPackageCache); + ExitOnFailure(hr, "Failed to backslash terminate default %hs package cache directory name.", "per-user"); + } + + hr = StrAllocString(psczRootPath, vsczDefaultUserPackageCache, 0); + ExitOnFailure(hr, "Failed to copy %hs package cache root directory.", "per-user"); + } + +LExit: + ReleaseStr(sczAppData); + + return hr; +} + +static HRESULT GetLastUsedSourceFolder( + __in BURN_VARIABLES* pVariables, + __out_z LPWSTR* psczLastSource + ) +{ + HRESULT hr = S_OK; + LPWSTR sczOriginalSource = NULL; + + hr = VariableGetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, psczLastSource); + if (E_NOTFOUND == hr) + { + // Try the original source folder. + hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource); + if (SUCCEEDED(hr)) + { + hr = PathGetDirectory(sczOriginalSource, psczLastSource); + } + } + + return hr; +} + +static HRESULT CreateCompletedPath( + __in BOOL fPerMachine, + __in LPCWSTR wzId, + __out LPWSTR* psczCacheDirectory + ) +{ + static BOOL fPerMachineCacheRootVerified = FALSE; + + HRESULT hr = S_OK; + LPWSTR sczCacheDirectory = NULL; + + // If we are doing a permachine install but have not yet verified that the root cache folder + // was created with the correct ACLs yet, do that now. + if (fPerMachine && !fPerMachineCacheRootVerified) + { + hr = GetRootPath(fPerMachine, TRUE, &sczCacheDirectory); + ExitOnFailure(hr, "Failed to get cache directory."); + + hr = DirEnsureExists(sczCacheDirectory, NULL); + ExitOnFailure(hr, "Failed to create cache directory: %ls", sczCacheDirectory); + + hr = SecurePath(sczCacheDirectory); + ExitOnFailure(hr, "Failed to secure cache directory: %ls", sczCacheDirectory); + + fPerMachineCacheRootVerified = TRUE; + } + + // Get the cache completed path, ensure it exists, and reset any permissions people + // might have tried to set on the directory so we inherit the (correct!) security + // permissions from the parent directory. + hr = CacheGetCompletedPath(fPerMachine, wzId, &sczCacheDirectory); + ExitOnFailure(hr, "Failed to get cache directory."); + + hr = DirEnsureExists(sczCacheDirectory, NULL); + ExitOnFailure(hr, "Failed to create cache directory: %ls", sczCacheDirectory); + + ResetPathPermissions(fPerMachine, sczCacheDirectory); + + *psczCacheDirectory = sczCacheDirectory; + sczCacheDirectory = NULL; + +LExit: + ReleaseStr(sczCacheDirectory); + return hr; +} + +static HRESULT CreateUnverifiedPath( + __in BOOL fPerMachine, + __in_z LPCWSTR wzPayloadId, + __out_z LPWSTR* psczUnverifiedPayloadPath + ) +{ + static BOOL fUnverifiedCacheFolderCreated = FALSE; + + HRESULT hr = S_OK; + LPWSTR sczUnverifiedCacheFolder = NULL; + + hr = CacheGetCompletedPath(fPerMachine, UNVERIFIED_CACHE_FOLDER_NAME, &sczUnverifiedCacheFolder); + ExitOnFailure(hr, "Failed to get cache directory."); + + if (!fUnverifiedCacheFolderCreated) + { + hr = DirEnsureExists(sczUnverifiedCacheFolder, NULL); + ExitOnFailure(hr, "Failed to create unverified cache directory: %ls", sczUnverifiedCacheFolder); + + ResetPathPermissions(fPerMachine, sczUnverifiedCacheFolder); + } + + hr = PathConcat(sczUnverifiedCacheFolder, wzPayloadId, psczUnverifiedPayloadPath); + ExitOnFailure(hr, "Failed to concat payload id to unverified folder path."); + +LExit: + ReleaseStr(sczUnverifiedCacheFolder); + + return hr; +} + +static HRESULT VerifyThenTransferContainer( + __in BURN_CONTAINER* pContainer, + __in_z LPCWSTR wzCachedPath, + __in_z LPCWSTR wzUnverifiedContainerPath, + __in BOOL fMove + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + + // Get the container on disk actual hash. + hFile = ::CreateFileW(wzUnverifiedContainerPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + ExitWithLastError(hr, "Failed to open container in working path: %ls", wzUnverifiedContainerPath); + } + + // Container should have a hash we can use to verify with. + if (pContainer->pbHash) + { + hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, wzUnverifiedContainerPath, hFile); + ExitOnFailure(hr, "Failed to verify container hash: %ls", wzCachedPath); + } + + LogStringLine(REPORT_STANDARD, "%ls container from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedContainerPath, wzCachedPath); + + if (fMove) + { + hr = FileEnsureMoveWithRetry(wzUnverifiedContainerPath, wzCachedPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); + ExitOnFailure(hr, "Failed to move %ls to %ls", wzUnverifiedContainerPath, wzCachedPath); + } + else + { + hr = FileEnsureCopyWithRetry(wzUnverifiedContainerPath, wzCachedPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); + ExitOnFailure(hr, "Failed to copy %ls to %ls", wzUnverifiedContainerPath, wzCachedPath); + } + +LExit: + ReleaseFileHandle(hFile); + + return hr; +} + +static HRESULT VerifyThenTransferPayload( + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzCachedPath, + __in_z LPCWSTR wzUnverifiedPayloadPath, + __in BOOL fMove + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + + // Get the payload on disk actual hash. + hFile = ::CreateFileW(wzUnverifiedPayloadPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + ExitWithLastError(hr, "Failed to open payload in working path: %ls", wzUnverifiedPayloadPath); + } + + // If the payload has a certificate root public key identifier provided, verify the certificate. + if (pPayload->pbCertificateRootPublicKeyIdentifier) + { + hr = CacheVerifyPayloadSignature(pPayload, wzUnverifiedPayloadPath, hFile); + ExitOnFailure(hr, "Failed to verify payload signature: %ls", wzCachedPath); + } + else if (pPayload->pCatalog) // If catalog files are specified, attempt to verify the file with a catalog file + { + hr = VerifyPayloadWithCatalog(pPayload, wzUnverifiedPayloadPath, hFile); + ExitOnFailure(hr, "Failed to verify payload signature: %ls", wzCachedPath); + } + else if (pPayload->pbHash) // the payload should have a hash we can use to verify it. + { + hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, wzUnverifiedPayloadPath, hFile); + ExitOnFailure(hr, "Failed to verify payload hash: %ls", wzCachedPath); + } + + LogStringLine(REPORT_STANDARD, "%ls payload from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedPayloadPath, wzCachedPath); + + if (fMove) + { + hr = FileEnsureMoveWithRetry(wzUnverifiedPayloadPath, wzCachedPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); + ExitOnFailure(hr, "Failed to move %ls to %ls", wzUnverifiedPayloadPath, wzCachedPath); + } + else + { + hr = FileEnsureCopyWithRetry(wzUnverifiedPayloadPath, wzCachedPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); + ExitOnFailure(hr, "Failed to copy %ls to %ls", wzUnverifiedPayloadPath, wzCachedPath); + } + +LExit: + ReleaseFileHandle(hFile); + + return hr; +} + +static HRESULT TransferWorkingPathToUnverifiedPath( + __in_z LPCWSTR wzWorkingPath, + __in_z LPCWSTR wzUnverifiedPayloadPath, + __in BOOL fMove + ) +{ + HRESULT hr = S_OK; + + if (fMove) + { + hr = FileEnsureMoveWithRetry(wzWorkingPath, wzUnverifiedPayloadPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); + ExitOnFailure(hr, "Failed to move %ls to %ls", wzWorkingPath, wzUnverifiedPayloadPath); + } + else + { + hr = FileEnsureCopyWithRetry(wzWorkingPath, wzUnverifiedPayloadPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); + ExitOnFailure(hr, "Failed to copy %ls to %ls", wzWorkingPath, wzUnverifiedPayloadPath); + } + +LExit: + return hr; +} + +static HRESULT VerifyFileAgainstPayload( + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzVerifyPath + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + + // Get the payload on disk actual hash. + hFile = ::CreateFileW(wzVerifyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (E_PATHNOTFOUND == hr || E_FILENOTFOUND == hr) + { + ExitFunction(); // do not log error when the file was not found. + } + ExitOnRootFailure(hr, "Failed to open payload at path: %ls", wzVerifyPath); + } + + // If the payload has a certificate root public key identifier provided, verify the certificate. + if (pPayload->pbCertificateRootPublicKeyIdentifier) + { + hr = CacheVerifyPayloadSignature(pPayload, wzVerifyPath, hFile); + ExitOnFailure(hr, "Failed to verify signature of payload: %ls", pPayload->sczKey); + } + else if (pPayload->pCatalog) // If catalog files are specified, attempt to verify the file with a catalog file + { + hr = VerifyPayloadWithCatalog(pPayload, wzVerifyPath, hFile); + ExitOnFailure(hr, "Failed to verify catalog signature of payload: %ls", pPayload->sczKey); + } + else if (pPayload->pbHash) // the payload should have a hash we can use to verify it. + { + hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, wzVerifyPath, hFile); + ExitOnFailure(hr, "Failed to verify hash of payload: %ls", pPayload->sczKey); + } + +LExit: + ReleaseFileHandle(hFile); + + return hr; +} + +static HRESULT AllocateSid( + __in WELL_KNOWN_SID_TYPE type, + __out PSID* ppSid + ) +{ + HRESULT hr = S_OK; + PSID pAllocSid = NULL; + DWORD cbSid = SECURITY_MAX_SID_SIZE; + + pAllocSid = static_cast(MemAlloc(cbSid, TRUE)); + ExitOnNull(pAllocSid, hr, E_OUTOFMEMORY, "Failed to allocate memory for well known SID."); + + if (!::CreateWellKnownSid(type, NULL, pAllocSid, &cbSid)) + { + ExitWithLastError(hr, "Failed to create well known SID."); + } + + *ppSid = pAllocSid; + pAllocSid = NULL; + +LExit: + ReleaseMem(pAllocSid); + return hr; +} + + +static HRESULT ResetPathPermissions( + __in BOOL fPerMachine, + __in LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD dwSetSecurity = DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION; + ACL acl = { }; + PSID pSid = NULL; + + if (fPerMachine) + { + hr = AllocateSid(WinBuiltinAdministratorsSid, &pSid); + ExitOnFailure(hr, "Failed to allocate administrator SID."); + + // Create an empty (not NULL!) ACL to reset the permissions on the file to purely inherit from parent. + if (!::InitializeAcl(&acl, sizeof(acl), ACL_REVISION)) + { + ExitWithLastError(hr, "Failed to initialize ACL."); + } + + dwSetSecurity |= OWNER_SECURITY_INFORMATION; + } + + hr = AclSetSecurityWithRetry(wzPath, SE_FILE_OBJECT, dwSetSecurity, pSid, NULL, &acl, NULL, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); + ExitOnWin32Error(er, hr, "Failed to reset the ACL on cached file: %ls", wzPath); + + ::SetFileAttributesW(wzPath, FILE_ATTRIBUTE_NORMAL); // Let's try to reset any possible read-only/system bits. + +LExit: + ReleaseMem(pSid); + return hr; +} + + +static HRESULT GrantAccessAndAllocateSid( + __in WELL_KNOWN_SID_TYPE type, + __in DWORD dwGrantAccess, + __in EXPLICIT_ACCESS* pAccess + ) +{ + HRESULT hr = S_OK; + + hr = AllocateSid(type, reinterpret_cast(&pAccess->Trustee.ptstrName)); + ExitOnFailure(hr, "Failed to allocate SID to grate access."); + + pAccess->grfAccessMode = GRANT_ACCESS; + pAccess->grfAccessPermissions = dwGrantAccess; + pAccess->grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; + pAccess->Trustee.TrusteeForm = TRUSTEE_IS_SID; + pAccess->Trustee.TrusteeType = TRUSTEE_IS_GROUP; + +LExit: + return hr; +} + + +static HRESULT SecurePath( + __in LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + EXPLICIT_ACCESSW access[4] = { }; + PACL pAcl = NULL; + + // Administrators must be the first one in the array so we can reuse the allocated SID below. + hr = GrantAccessAndAllocateSid(WinBuiltinAdministratorsSid, FILE_ALL_ACCESS, &access[0]); + ExitOnFailure(hr, "Failed to allocate access for Administrators group to path: %ls", wzPath); + + hr = GrantAccessAndAllocateSid(WinLocalSystemSid, FILE_ALL_ACCESS, &access[1]); + ExitOnFailure(hr, "Failed to allocate access for SYSTEM group to path: %ls", wzPath); + + hr = GrantAccessAndAllocateSid(WinWorldSid, GENERIC_READ | GENERIC_EXECUTE, &access[2]); + ExitOnFailure(hr, "Failed to allocate access for Everyone group to path: %ls", wzPath); + + hr = GrantAccessAndAllocateSid(WinBuiltinUsersSid, GENERIC_READ | GENERIC_EXECUTE, &access[3]); + ExitOnFailure(hr, "Failed to allocate access for Users group to path: %ls", wzPath); + + er = ::SetEntriesInAclW(countof(access), access, NULL, &pAcl); + ExitOnWin32Error(er, hr, "Failed to create ACL to secure cache path: %ls", wzPath); + + // Set the ACL and ensure the Administrators group ends up the owner + hr = AclSetSecurityWithRetry(wzPath, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION, + reinterpret_cast(access[0].Trustee.ptstrName), NULL, pAcl, NULL, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); + ExitOnFailure(hr, "Failed to secure cache path: %ls", wzPath); + +LExit: + if (pAcl) + { + ::LocalFree(pAcl); + } + + for (DWORD i = 0; i < countof(access); ++i) + { + ReleaseMem(access[i].Trustee.ptstrName); + } + + return hr; +} + + +static HRESULT CopyEngineToWorkingFolder( + __in_z LPCWSTR wzSourcePath, + __in_z LPCWSTR wzWorkingFolderName, + __in_z LPCWSTR wzExecutableName, + __in BURN_PAYLOADS* pUxPayloads, + __in BURN_SECTION* pSection, + __deref_out_z_opt LPWSTR* psczEngineWorkingPath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczWorkingFolder = NULL; + LPWSTR sczTargetDirectory = NULL; + LPWSTR sczTargetPath = NULL; + LPWSTR sczSourceDirectory = NULL; + LPWSTR sczPayloadSourcePath = NULL; + LPWSTR sczPayloadTargetPath = NULL; + + hr = CacheEnsureWorkingFolder(NULL, &sczWorkingFolder); + ExitOnFailure(hr, "Failed to create working path to copy engine."); + + hr = PathConcat(sczWorkingFolder, wzWorkingFolderName, &sczTargetDirectory); + ExitOnFailure(hr, "Failed to calculate the bundle working folder target name."); + + hr = DirEnsureExists(sczTargetDirectory, NULL); + ExitOnFailure(hr, "Failed create bundle working folder."); + + hr = PathConcat(sczTargetDirectory, wzExecutableName, &sczTargetPath); + ExitOnFailure(hr, "Failed to combine working path with engine file name."); + + // Copy the engine without any attached containers to the working path. + hr = CopyEngineWithSignatureFixup(pSection->hEngineFile, wzSourcePath, sczTargetPath, pSection); + ExitOnFailure(hr, "Failed to copy engine: '%ls' to working path: %ls", wzSourcePath, sczTargetPath); + + // Copy external UX payloads to working path. + for (DWORD i = 0; i < pUxPayloads->cPayloads; ++i) + { + BURN_PAYLOAD* pPayload = &pUxPayloads->rgPayloads[i]; + + if (BURN_PAYLOAD_PACKAGING_EXTERNAL == pPayload->packaging) + { + if (!sczSourceDirectory) + { + hr = PathGetDirectory(wzSourcePath, &sczSourceDirectory); + ExitOnFailure(hr, "Failed to get directory from engine path: %ls", wzSourcePath); + } + + hr = PathConcat(sczSourceDirectory, pPayload->sczSourcePath, &sczPayloadSourcePath); + ExitOnFailure(hr, "Failed to build payload source path for working copy."); + + hr = PathConcat(sczTargetDirectory, pPayload->sczFilePath, &sczPayloadTargetPath); + ExitOnFailure(hr, "Failed to build payload target path for working copy."); + + hr = FileEnsureCopyWithRetry(sczPayloadSourcePath, sczPayloadTargetPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); + ExitOnFailure(hr, "Failed to copy UX payload from: '%ls' to: '%ls'", sczPayloadSourcePath, sczPayloadTargetPath); + } + } + + if (psczEngineWorkingPath) + { + hr = StrAllocString(psczEngineWorkingPath, sczTargetPath, 0); + ExitOnFailure(hr, "Failed to copy target path for engine working path."); + } + +LExit: + ReleaseStr(sczPayloadTargetPath); + ReleaseStr(sczPayloadSourcePath); + ReleaseStr(sczSourceDirectory); + ReleaseStr(sczTargetPath); + ReleaseStr(sczTargetDirectory); + ReleaseStr(sczWorkingFolder); + + return hr; +} + + +static HRESULT CopyEngineWithSignatureFixup( + __in HANDLE hEngineFile, + __in_z LPCWSTR wzEnginePath, + __in_z LPCWSTR wzTargetPath, + __in BURN_SECTION* pSection + ) +{ + HRESULT hr = S_OK; + HANDLE hTarget = INVALID_HANDLE_VALUE; + LARGE_INTEGER li = { }; + DWORD dwZeroOriginals[3] = { }; + + hTarget = ::CreateFileW(wzTargetPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hTarget) + { + ExitWithLastError(hr, "Failed to create engine file at path: %ls", wzTargetPath); + } + + hr = FileSetPointer(hEngineFile, 0, NULL, FILE_BEGIN); + ExitOnFailure(hr, "Failed to seek to beginning of engine file: %ls", wzEnginePath); + + hr = FileCopyUsingHandles(hEngineFile, hTarget, pSection->cbEngineSize, NULL); + ExitOnFailure(hr, "Failed to copy engine from: %ls to: %ls", wzEnginePath, wzTargetPath); + + // If the original executable was signed, let's put back the checksum and signature. + if (pSection->dwOriginalSignatureOffset) + { + // Fix up the checksum. + li.QuadPart = pSection->dwChecksumOffset; + if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to seek to checksum in exe header."); + } + + hr = FileWriteHandle(hTarget, reinterpret_cast(&pSection->dwOriginalChecksum), sizeof(pSection->dwOriginalChecksum)); + ExitOnFailure(hr, "Failed to update signature offset."); + + // Fix up the signature information. + li.QuadPart = pSection->dwCertificateTableOffset; + if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to seek to signature table in exe header."); + } + + hr = FileWriteHandle(hTarget, reinterpret_cast(&pSection->dwOriginalSignatureOffset), sizeof(pSection->dwOriginalSignatureOffset)); + ExitOnFailure(hr, "Failed to update signature offset."); + + hr = FileWriteHandle(hTarget, reinterpret_cast(&pSection->dwOriginalSignatureSize), sizeof(pSection->dwOriginalSignatureSize)); + ExitOnFailure(hr, "Failed to update signature offset."); + + // Zero out the original information since that is how it was when the file was originally signed. + li.QuadPart = pSection->dwOriginalChecksumAndSignatureOffset; + if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to seek to original data in exe burn section header."); + } + + hr = FileWriteHandle(hTarget, reinterpret_cast(&dwZeroOriginals), sizeof(dwZeroOriginals)); + ExitOnFailure(hr, "Failed to zero out original data offset."); + } + +LExit: + ReleaseFileHandle(hTarget); + + return hr; +} + + +static HRESULT RemoveBundleOrPackage( + __in BOOL fBundle, + __in BOOL fPerMachine, + __in_z LPCWSTR wzBundleOrPackageId, + __in_z LPCWSTR wzCacheId + ) +{ + HRESULT hr = S_OK; + LPWSTR sczRootCacheDirectory = NULL; + LPWSTR sczDirectory = NULL; + + hr = CacheGetCompletedPath(fPerMachine, wzCacheId, &sczDirectory); + ExitOnFailure(hr, "Failed to calculate cache path."); + + LogId(REPORT_STANDARD, fBundle ? MSG_UNCACHE_BUNDLE : MSG_UNCACHE_PACKAGE, wzBundleOrPackageId, sczDirectory); + + // Try really hard to remove the cache directory. + hr = E_FAIL; + for (DWORD iRetry = 0; FAILED(hr) && iRetry < FILE_OPERATION_RETRY_COUNT; ++iRetry) + { + if (0 < iRetry) + { + ::Sleep(FILE_OPERATION_RETRY_WAIT); + } + + hr = DirEnsureDeleteEx(sczDirectory, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE); + if (E_PATHNOTFOUND == hr) + { + break; + } + } + + if (FAILED(hr)) + { + LogId(REPORT_STANDARD, fBundle ? MSG_UNABLE_UNCACHE_BUNDLE : MSG_UNABLE_UNCACHE_PACKAGE, wzBundleOrPackageId, sczDirectory, hr); + hr = S_OK; + } + else + { + // Try to remove root package cache in the off chance it is now empty. + hr = GetRootPath(fPerMachine, TRUE, &sczRootCacheDirectory); + ExitOnFailure(hr, "Failed to get %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user"); + DirEnsureDeleteEx(sczRootCacheDirectory, DIR_DELETE_SCHEDULE); + + // GetRootPath returns S_FALSE if the package cache is redirected elsewhere. + if (S_FALSE == hr) + { + hr = GetRootPath(fPerMachine, FALSE, &sczRootCacheDirectory); + ExitOnFailure(hr, "Failed to get old %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user"); + DirEnsureDeleteEx(sczRootCacheDirectory, DIR_DELETE_SCHEDULE); + } + } + +LExit: + ReleaseStr(sczDirectory); + ReleaseStr(sczRootCacheDirectory); + + return hr; +} + +static HRESULT VerifyHash( + __in BYTE* pbHash, + __in DWORD cbHash, + __in_z LPCWSTR wzUnverifiedPayloadPath, + __in HANDLE hFile + ) +{ + UNREFERENCED_PARAMETER(wzUnverifiedPayloadPath); + + HRESULT hr = S_OK; + BYTE rgbActualHash[SHA1_HASH_LEN] = { }; + DWORD64 qwHashedBytes; + LPWSTR pszExpected = NULL; + LPWSTR pszActual = NULL; + + // TODO: create a cryp hash file that sends progress. + hr = CrypHashFileHandle(hFile, PROV_RSA_FULL, CALG_SHA1, rgbActualHash, sizeof(rgbActualHash), &qwHashedBytes); + ExitOnFailure(hr, "Failed to calculate hash for path: %ls", wzUnverifiedPayloadPath); + + // Compare hashes. + if (cbHash != sizeof(rgbActualHash) || 0 != memcmp(pbHash, rgbActualHash, SHA1_HASH_LEN)) + { + hr = CRYPT_E_HASH_VALUE; + + // Best effort to log the expected and actual hash value strings. + if (SUCCEEDED(StrAllocHexEncode(pbHash, cbHash, &pszExpected)) && + SUCCEEDED(StrAllocHexEncode(rgbActualHash, SHA1_HASH_LEN, &pszActual))) + { + ExitOnFailure(hr, "Hash mismatch for path: %ls, expected: %ls, actual: %ls", wzUnverifiedPayloadPath, pszExpected, pszActual); + } + else + { + ExitOnFailure(hr, "Hash mismatch for path: %ls", wzUnverifiedPayloadPath); + } + } + +LExit: + ReleaseStr(pszActual); + ReleaseStr(pszExpected); + + return hr; +} + +static HRESULT VerifyPayloadWithCatalog( + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzUnverifiedPayloadPath, + __in HANDLE hFile + ) +{ + HRESULT hr = S_FALSE; + DWORD er = ERROR_SUCCESS; + WINTRUST_DATA WinTrustData = { }; + WINTRUST_CATALOG_INFO WinTrustCatalogInfo = { }; + GUID gSubSystemDriver = WINTRUST_ACTION_GENERIC_VERIFY_V2; + LPWSTR sczLowerCaseFile = NULL; + LPWSTR pCurrent = NULL; + LPWSTR sczName = NULL; + DWORD dwHashSize = 0; + DWORD dwTagSize; + LPBYTE pbHash = NULL; + + // Get lower case file name. Older operating systems need a lower case file + // to match in the catalog + hr = StrAllocString(&sczLowerCaseFile, wzUnverifiedPayloadPath, 0); + ExitOnFailure(hr, "Failed to allocate memory"); + + // Go through each character doing the lower case of each letter + pCurrent = sczLowerCaseFile; + while ('\0' != *pCurrent) + { + *pCurrent = (WCHAR)_tolower(*pCurrent); + pCurrent++; + } + + // Get file hash + CryptCATAdminCalcHashFromFileHandle(hFile, &dwHashSize, pbHash, 0); + er = ::GetLastError(); + if (ERROR_INSUFFICIENT_BUFFER == er) + { + pbHash = (LPBYTE)MemAlloc(dwHashSize, TRUE); + if (!CryptCATAdminCalcHashFromFileHandle(hFile, &dwHashSize, pbHash, 0)) + { + ExitWithLastError(hr, "Failed to get file hash."); + } + } + else + { + ExitOnWin32Error(er, hr, "Failed to get file hash."); + } + + // Make the hash into a string. This is the member tag for the catalog + dwTagSize = (dwHashSize * 2) + 1; + hr = StrAlloc(&sczName, dwTagSize); + ExitOnFailure(hr, "Failed to allocate string."); + hr = StrHexEncode(pbHash, dwHashSize, sczName, dwTagSize); + ExitOnFailure(hr, "Failed to encode file hash."); + + // Set up the WinVerifyTrust structures assuming online. + WinTrustData.cbStruct = sizeof(WINTRUST_DATA); + WinTrustData.dwUIChoice = WTD_UI_NONE; + WinTrustData.dwUnionChoice = WTD_CHOICE_CATALOG; + WinTrustData.dwStateAction = WTD_STATEACTION_VERIFY; + WinTrustData.dwProvFlags = WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT; + WinTrustData.pCatalog = &WinTrustCatalogInfo; + + WinTrustCatalogInfo.cbStruct = sizeof(WINTRUST_CATALOG_INFO); + WinTrustCatalogInfo.pbCalculatedFileHash = pbHash; + WinTrustCatalogInfo.cbCalculatedFileHash = dwHashSize; + WinTrustCatalogInfo.hMemberFile = hFile; + WinTrustCatalogInfo.pcwszMemberTag = sczName; + WinTrustCatalogInfo.pcwszMemberFilePath = sczLowerCaseFile; + WinTrustCatalogInfo.pcwszCatalogFilePath = pPayload->pCatalog->sczLocalFilePath; + + hr = ::WinVerifyTrust(static_cast(INVALID_HANDLE_VALUE), &gSubSystemDriver, &WinTrustData); + if (hr) + { + // Set up the WinVerifyTrust structures assuming online. + WinTrustData.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL; + + er = ::WinVerifyTrust(static_cast(INVALID_HANDLE_VALUE), &gSubSystemDriver, &WinTrustData); + + // WinVerifyTrust returns 0 for success, a few different Win32 error codes if it can't + // find the provider, and any other error code is provider specific, so may not + // be an actual Win32 error code + ExitOnWin32Error(er, hr, "Could not verify file %ls.", wzUnverifiedPayloadPath); + } + + // Need to close the WinVerifyTrust action + WinTrustData.dwStateAction = WTD_STATEACTION_CLOSE; + er = ::WinVerifyTrust(static_cast(INVALID_HANDLE_VALUE), &gSubSystemDriver, &WinTrustData); + ExitOnWin32Error(er, hr, "Could not close verify handle."); + +LExit: + ReleaseStr(sczLowerCaseFile); + ReleaseStr(sczName); + ReleaseMem(pbHash); + + return hr; +} + +static HRESULT VerifyPayloadAgainstChain( + __in BURN_PAYLOAD* pPayload, + __in PCCERT_CHAIN_CONTEXT pChainContext + ) +{ + HRESULT hr = S_OK; + PCCERT_CONTEXT pChainElementCertContext = NULL; + + BYTE rgbPublicKeyIdentifier[SHA1_HASH_LEN] = { }; + DWORD cbPublicKeyIdentifier = sizeof(rgbPublicKeyIdentifier); + BYTE* pbThumbprint = NULL; + DWORD cbThumbprint = 0; + + // Walk up the chain looking for a certificate in the chain that matches our expected public key identifier + // and thumbprint (if a thumbprint was provided). + HRESULT hrChainVerification = E_NOTFOUND; // assume we won't find a match. + for (DWORD i = 0; i < pChainContext->rgpChain[0]->cElement; ++i) + { + pChainElementCertContext = pChainContext->rgpChain[0]->rgpElement[i]->pCertContext; + + // Get the certificate's public key identifier. + if (!::CryptHashPublicKeyInfo(NULL, CALG_SHA1, 0, X509_ASN_ENCODING, &pChainElementCertContext->pCertInfo->SubjectPublicKeyInfo, rgbPublicKeyIdentifier, &cbPublicKeyIdentifier)) + { + ExitWithLastError(hr, "Failed to get certificate public key identifier."); + } + + // Compare the certificate's public key identifier with the payload's public key identifier. If they + // match, we're one step closer to the a positive result. + if (pPayload->cbCertificateRootPublicKeyIdentifier == cbPublicKeyIdentifier && + 0 == memcmp(pPayload->pbCertificateRootPublicKeyIdentifier, rgbPublicKeyIdentifier, cbPublicKeyIdentifier)) + { + // If the payload specified a thumbprint for the certificate, verify it. + if (pPayload->pbCertificateRootThumbprint) + { + hr = CertReadProperty(pChainElementCertContext, CERT_SHA1_HASH_PROP_ID, &pbThumbprint, &cbThumbprint); + ExitOnFailure(hr, "Failed to read certificate thumbprint."); + + if (pPayload->cbCertificateRootThumbprint == cbThumbprint && + 0 == memcmp(pPayload->pbCertificateRootThumbprint, pbThumbprint, cbThumbprint)) + { + // If we got here, we found that our payload public key identifier and thumbprint + // matched an element in the certficate chain. + hrChainVerification = S_OK; + break; + } + + ReleaseNullMem(pbThumbprint); + } + else // no thumbprint match necessary so we're good to go. + { + hrChainVerification = S_OK; + break; + } + } + } + hr = hrChainVerification; + ExitOnFailure(hr, "Failed to find expected public key in certificate chain."); + +LExit: + ReleaseMem(pbThumbprint); + + return hr; +} diff --git a/src/engine/cache.h b/src/engine/cache.h new file mode 100644 index 00000000..95e6cb90 --- /dev/null +++ b/src/engine/cache.h @@ -0,0 +1,150 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +// structs + +// functions + +HRESULT CacheInitialize( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in_z_opt LPCWSTR wzSourceProcessPath + ); +HRESULT CacheEnsureWorkingFolder( + __in LPCWSTR wzBundleId, + __deref_out_z_opt LPWSTR* psczWorkingFolder + ); +HRESULT CacheCalculateBundleWorkingPath( + __in_z LPCWSTR wzBundleId, + __in LPCWSTR wzExecutableName, + __deref_out_z LPWSTR* psczWorkingPath + ); +HRESULT CacheCalculateBundleLayoutWorkingPath( + __in_z LPCWSTR wzBundleId, + __deref_out_z LPWSTR* psczWorkingPath + ); +HRESULT CacheCalculatePayloadWorkingPath( + __in_z LPCWSTR wzBundleId, + __in BURN_PAYLOAD* pPayload, + __deref_out_z LPWSTR* psczWorkingPath + ); +HRESULT CacheCalculateContainerWorkingPath( + __in_z LPCWSTR wzBundleId, + __in BURN_CONTAINER* pContainer, + __deref_out_z LPWSTR* psczWorkingPath + ); +HRESULT CacheGetRootCompletedPath( + __in BOOL fPerMachine, + __in BOOL fForceInitialize, + __deref_out_z LPWSTR* psczRootCompletedPath + ); +HRESULT CacheGetCompletedPath( + __in BOOL fPerMachine, + __in_z LPCWSTR wzCacheId, + __deref_out_z LPWSTR* psczCompletedPath + ); +HRESULT CacheGetResumePath( + __in_z LPCWSTR wzPayloadWorkingPath, + __deref_out_z LPWSTR* psczResumePath + ); +HRESULT CacheFindLocalSource( + __in_z LPCWSTR wzSourcePath, + __in BURN_VARIABLES* pVariables, + __out BOOL* pfFound, + __out_z LPWSTR* psczSourceFullPath + ); +HRESULT CacheSetLastUsedSource( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzSourcePath, + __in_z LPCWSTR wzRelativePath + ); +HRESULT CacheSendProgressCallback( + __in DOWNLOAD_CACHE_CALLBACK* pCallback, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in HANDLE hDestinationFile + ); +void CacheSendErrorCallback( + __in DOWNLOAD_CACHE_CALLBACK* pCallback, + __in HRESULT hrError, + __in_z_opt LPCWSTR wzError, + __out_opt BOOL* pfRetry + ); +BOOL CacheBundleRunningFromCache(); +HRESULT CacheBundleToCleanRoom( + __in BURN_PAYLOADS* pUxPayloads, + __in BURN_SECTION* pSection, + __deref_out_z_opt LPWSTR* psczCleanRoomBundlePath + ); +HRESULT CacheBundleToWorkingDirectory( + __in_z LPCWSTR wzBundleId, + __in_z LPCWSTR wzExecutableName, + __in BURN_PAYLOADS* pUxPayloads, + __in BURN_SECTION* pSection, + __deref_out_z_opt LPWSTR* psczEngineWorkingPath + ); +HRESULT CacheLayoutBundle( + __in_z LPCWSTR wzExecutableName, + __in_z LPCWSTR wzLayoutDirectory, + __in_z LPCWSTR wzSourceBundlePath + ); +HRESULT CacheCompleteBundle( + __in BOOL fPerMachine, + __in_z LPCWSTR wzExecutableName, + __in_z LPCWSTR wzBundleId, + __in BURN_PAYLOADS* pUxPayloads, + __in_z LPCWSTR wzSourceBundlePath +#ifdef DEBUG + , __in_z LPCWSTR wzExecutablePath +#endif + ); +HRESULT CacheLayoutContainer( + __in BURN_CONTAINER* pContainer, + __in_z_opt LPCWSTR wzLayoutDirectory, + __in_z LPCWSTR wzUnverifiedContainerPath, + __in BOOL fMove + ); +HRESULT CacheLayoutPayload( + __in BURN_PAYLOAD* pPayload, + __in_z_opt LPCWSTR wzLayoutDirectory, + __in_z LPCWSTR wzUnverifiedPayloadPath, + __in BOOL fMove + ); +HRESULT CacheCompletePayload( + __in BOOL fPerMachine, + __in BURN_PAYLOAD* pPayload, + __in_z_opt LPCWSTR wzCacheId, + __in_z LPCWSTR wzUnverifiedPayloadPath, + __in BOOL fMove + ); +HRESULT CacheRemoveWorkingFolder( + __in_z_opt LPCWSTR wzBundleId + ); +HRESULT CacheRemoveBundle( + __in BOOL fPerMachine, + __in_z LPCWSTR wzPackageId + ); +HRESULT CacheRemovePackage( + __in BOOL fPerMachine, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzCacheId + ); +HRESULT CacheVerifyPayloadSignature( + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzUnverifiedPayloadPath, + __in HANDLE hFile + ); +void CacheCleanup( + __in BOOL fPerMachine, + __in_z LPCWSTR wzBundleId + ); +void CacheUninitialize(); + +#ifdef __cplusplus +} +#endif diff --git a/src/engine/catalog.cpp b/src/engine/catalog.cpp new file mode 100644 index 00000000..da086545 --- /dev/null +++ b/src/engine/catalog.cpp @@ -0,0 +1,180 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// function definitions + +extern "C" HRESULT CatalogsParseFromXml( + __in BURN_CATALOGS* pCatalogs, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + LPWSTR scz = NULL; + + // select catalog nodes + hr = XmlSelectNodes(pixnBundle, L"Catalog", &pixnNodes); + ExitOnFailure(hr, "Failed to select catalog nodes."); + + // get catalog node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get payload node count."); + if (!cNodes) + { + ExitFunction(); + } + + // allocate memory for catalogs + pCatalogs->rgCatalogs = (BURN_CATALOG*)MemAlloc(sizeof(BURN_CATALOG) * cNodes, TRUE); + ExitOnNull(pCatalogs->rgCatalogs, hr, E_OUTOFMEMORY, "Failed to allocate memory for payload structs."); + + pCatalogs->cCatalogs = cNodes; + + // parse catalog elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_CATALOG* pCatalog = &pCatalogs->rgCatalogs[i]; + pCatalog->hFile = INVALID_HANDLE_VALUE; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &pCatalog->sczKey); + ExitOnFailure(hr, "Failed to get @Id."); + + // @Payload + hr = XmlGetAttributeEx(pixnNode, L"Payload", &pCatalog->sczPayload); + ExitOnFailure(hr, "Failed to get @Payload."); + + // prepare next iteration + ReleaseNullObject(pixnNode); + } + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseStr(scz); + + return hr; +} + +extern "C" HRESULT CatalogFindById( + __in BURN_CATALOGS* pCatalogs, + __in_z LPCWSTR wzId, + __out BURN_CATALOG** ppCatalog + ) +{ + HRESULT hr = S_OK; + BURN_CATALOG* pCatalog = NULL; + + for (DWORD i = 0; i < pCatalogs->cCatalogs; ++i) + { + pCatalog = &pCatalogs->rgCatalogs[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pCatalog->sczKey, -1, wzId, -1)) + { + *ppCatalog = pCatalog; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + +extern "C" HRESULT CatalogLoadFromPayload( + __in BURN_CATALOGS* pCatalogs, + __in BURN_PAYLOADS* pPayloads + ) +{ + HRESULT hr = S_OK; + BURN_CATALOG* pCatalog = NULL; + BURN_PAYLOAD* pPayload = NULL; + + // go through each catalog file + for (DWORD i = 0; i < pCatalogs->cCatalogs; i++) + { + pCatalog = &pCatalogs->rgCatalogs[i]; + + // get the payload for this catalog file + hr = PayloadFindById(pPayloads, pCatalog->sczPayload, &pPayload); + ExitOnFailure(hr, "Failed to find payload for catalog file."); + + // Get the local file name + hr = StrAllocString(&pCatalog->sczLocalFilePath, pPayload->sczLocalFilePath, 0); + ExitOnFailure(hr, "Failed to get catalog local file path"); + + // Get a handle to the file + pCatalog->hFile = ::CreateFileW(pCatalog->sczLocalFilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == pCatalog->hFile) + { + ExitWithLastError(hr, "Failed to open catalog in working path: %ls", pCatalog->sczLocalFilePath); + } + + // Verify the catalog file + hr = CacheVerifyPayloadSignature(pPayload, pCatalog->sczLocalFilePath, pCatalog->hFile); + ExitOnFailure(hr, "Failed to verify catalog signature: %ls", pCatalog->sczLocalFilePath); + } + +LExit: + return hr; +} + +extern "C" HRESULT CatalogElevatedUpdateCatalogFile( + __in BURN_CATALOGS* pCatalogs, + __in_z LPCWSTR wzId, + __in_z LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + BURN_CATALOG* pCatalog = NULL; + + // Find the catalog + hr = CatalogFindById(pCatalogs, wzId, &pCatalog); + ExitOnFailure(hr, "Failed to locate catalog information."); + + if (NULL == pCatalog->sczLocalFilePath) + { + hr = StrAllocString(&pCatalog->sczLocalFilePath, wzPath, 0); + ExitOnFailure(hr, "Failed to allocated catalog path."); + + // Get a handle to the file + pCatalog->hFile = ::CreateFileW(pCatalog->sczLocalFilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == pCatalog->hFile) + { + ExitWithLastError(hr, "Failed to open catalog in working path: %ls", pCatalog->sczLocalFilePath); + } + } + +LExit: + return hr; +} + +extern "C" void CatalogUninitialize( + __in BURN_CATALOGS* pCatalogs + ) +{ + if (pCatalogs->rgCatalogs) + { + for (DWORD i = 0; i < pCatalogs->cCatalogs; ++i) + { + BURN_CATALOG* pCatalog = &pCatalogs->rgCatalogs[i]; + + ReleaseHandle(pCatalog->hFile); + ReleaseStr(pCatalog->sczKey); + ReleaseStr(pCatalog->sczLocalFilePath); + ReleaseStr(pCatalog->sczPayload); + } + MemFree(pCatalogs->rgCatalogs); + } + + // clear struct + memset(pCatalogs, 0, sizeof(BURN_CATALOGS)); +} diff --git a/src/engine/catalog.h b/src/engine/catalog.h new file mode 100644 index 00000000..3a87d0d2 --- /dev/null +++ b/src/engine/catalog.h @@ -0,0 +1,56 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +// structs + +typedef struct _BURN_CATALOG +{ + LPWSTR sczKey; + LPWSTR sczPayload; + + // mutable members + LPWSTR sczLocalFilePath; // location of extracted or downloaded copy + HANDLE hFile; +} BURN_CATALOG; + +typedef struct _BURN_CATALOGS +{ + BURN_CATALOG* rgCatalogs; + DWORD cCatalogs; +} BURN_CATALOGS; + +typedef struct _BURN_PAYLOADS BURN_PAYLOADS; + + +// functions + +HRESULT CatalogsParseFromXml( + __in BURN_CATALOGS* pCatalogs, + __in IXMLDOMNode* pixnBundle + ); +HRESULT CatalogFindById( + __in BURN_CATALOGS* pCatalogs, + __in_z LPCWSTR wzId, + __out BURN_CATALOG** ppCatalog + ); +HRESULT CatalogLoadFromPayload( + __in BURN_CATALOGS* pCatalogs, + __in BURN_PAYLOADS* pPayloads + ); +HRESULT CatalogElevatedUpdateCatalogFile( + __in BURN_CATALOGS* pCatalogs, + __in_z LPCWSTR wzId, + __in_z LPCWSTR wzPath + ); +void CatalogUninitialize( + __in BURN_CATALOGS* pCatalogs + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/condition.cpp b/src/engine/condition.cpp new file mode 100644 index 00000000..28391d2d --- /dev/null +++ b/src/engine/condition.cpp @@ -0,0 +1,1030 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// +// parse rules +// +// value variable | literal | integer | version +// comparison-operator < | > | <= | >= | = | <> | >< | << | >> +// term value | value comparison-operator value | ( expression ) +// boolean-factor term | NOT term +// boolean-term boolean-factor | boolean-factor AND boolean-term +// expression boolean-term | boolean-term OR expression +// + + +// constants + +#define COMPARISON 0x00010000 +#define INSENSITIVE 0x00020000 + +enum BURN_SYMBOL_TYPE +{ + // terminals + BURN_SYMBOL_TYPE_NONE = 0, + BURN_SYMBOL_TYPE_END = 1, + BURN_SYMBOL_TYPE_OR = 2, // OR + BURN_SYMBOL_TYPE_AND = 3, // AND + BURN_SYMBOL_TYPE_NOT = 4, // NOT + BURN_SYMBOL_TYPE_LT = 5 | COMPARISON, // < + BURN_SYMBOL_TYPE_GT = 6 | COMPARISON, // > + BURN_SYMBOL_TYPE_LE = 7 | COMPARISON, // <= + BURN_SYMBOL_TYPE_GE = 8 | COMPARISON, // >= + BURN_SYMBOL_TYPE_EQ = 9 | COMPARISON, // = + BURN_SYMBOL_TYPE_NE = 10 | COMPARISON, // <> + BURN_SYMBOL_TYPE_BAND = 11 | COMPARISON, // >< + BURN_SYMBOL_TYPE_HIEQ = 12 | COMPARISON, // << + BURN_SYMBOL_TYPE_LOEQ = 13 | COMPARISON, // >> + BURN_SYMBOL_TYPE_LT_I = 5 | COMPARISON | INSENSITIVE, // ~< + BURN_SYMBOL_TYPE_GT_I = 6 | COMPARISON | INSENSITIVE, // ~> + BURN_SYMBOL_TYPE_LE_I = 7 | COMPARISON | INSENSITIVE, // ~<= + BURN_SYMBOL_TYPE_GE_I = 8 | COMPARISON | INSENSITIVE, // ~>= + BURN_SYMBOL_TYPE_EQ_I = 9 | COMPARISON | INSENSITIVE, // ~= + BURN_SYMBOL_TYPE_NE_I = 10 | COMPARISON | INSENSITIVE, // ~<> + BURN_SYMBOL_TYPE_BAND_I = 11 | COMPARISON | INSENSITIVE, // ~>< + BURN_SYMBOL_TYPE_HIEQ_I = 12 | COMPARISON | INSENSITIVE, // ~<< + BURN_SYMBOL_TYPE_LOEQ_I = 13 | COMPARISON | INSENSITIVE, // ~>> + BURN_SYMBOL_TYPE_LPAREN = 14, // ( + BURN_SYMBOL_TYPE_RPAREN = 15, // ) + BURN_SYMBOL_TYPE_NUMBER = 16, + BURN_SYMBOL_TYPE_IDENTIFIER = 17, + BURN_SYMBOL_TYPE_LITERAL = 18, + BURN_SYMBOL_TYPE_VERSION = 19, +}; + + +// structs + +struct BURN_SYMBOL +{ + BURN_SYMBOL_TYPE Type; + DWORD iPosition; + BURN_VARIANT Value; +}; + +struct BURN_CONDITION_PARSE_CONTEXT +{ + BURN_VARIABLES* pVariables; + LPCWSTR wzCondition; + LPCWSTR wzRead; + BURN_SYMBOL NextSymbol; + BOOL fError; +}; + + +// internal function declarations + +static HRESULT ParseExpression( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __out BOOL* pf + ); +static HRESULT ParseBooleanTerm( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __out BOOL* pf + ); +static HRESULT ParseBooleanFactor( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __out BOOL* pf + ); +static HRESULT ParseTerm( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __out BOOL* pf + ); +static HRESULT ParseValue( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __out BURN_VARIANT* pValue + ); +static HRESULT Expect( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __in BURN_SYMBOL_TYPE symbolType + ); +static HRESULT NextSymbol( + __in BURN_CONDITION_PARSE_CONTEXT* pContext + ); +static HRESULT CompareValues( + __in BURN_SYMBOL_TYPE comparison, + __in BURN_VARIANT leftOperand, + __in BURN_VARIANT rightOperand, + __out BOOL* pfResult + ); +static HRESULT CompareStringValues( + __in BURN_SYMBOL_TYPE comparison, + __in_z LPCWSTR wzLeftOperand, + __in_z LPCWSTR wzRightOperand, + __out BOOL* pfResult + ); +static HRESULT CompareIntegerValues( + __in BURN_SYMBOL_TYPE comparison, + __in LONGLONG llLeftOperand, + __in LONGLONG llRightOperand, + __out BOOL* pfResult + ); +static HRESULT CompareVersionValues( + __in BURN_SYMBOL_TYPE comparison, + __in DWORD64 qwLeftOperand, + __in DWORD64 qwRightOperand, + __out BOOL* pfResult + ); + + +// function definitions + +extern "C" HRESULT ConditionEvaluate( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzCondition, + __out BOOL* pf + ) +{ + HRESULT hr = S_OK; + BURN_CONDITION_PARSE_CONTEXT context = { }; + BOOL f = FALSE; + + context.pVariables = pVariables; + context.wzCondition = wzCondition; + context.wzRead = wzCondition; + + hr = NextSymbol(&context); + ExitOnFailure(hr, "Failed to read next symbol."); + + hr = ParseExpression(&context, &f); + ExitOnFailure(hr, "Failed to parse expression."); + + hr = Expect(&context, BURN_SYMBOL_TYPE_END); + ExitOnFailure(hr, "Failed to expect end symbol."); + + LogId(REPORT_VERBOSE, MSG_CONDITION_RESULT, wzCondition, LoggingTrueFalseToString(f)); + + *pf = f; + hr = S_OK; + +LExit: + if (context.fError) + { + Assert(FAILED(hr)); + LogErrorId(hr, MSG_FAILED_PARSE_CONDITION, wzCondition, NULL, NULL); + } + + return hr; +} + +extern "C" HRESULT ConditionGlobalCheck( + __in BURN_VARIABLES* pVariables, + __in BURN_CONDITION* pCondition, + __in BOOTSTRAPPER_DISPLAY display, + __in_z LPCWSTR wzBundleName, + __out DWORD *pdwExitCode, + __out BOOL *pfContinueExecution + ) +{ + HRESULT hr = S_OK; + BOOL fSuccess = TRUE; + HRESULT hrError = HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION); + OS_VERSION osv = OS_VERSION_UNKNOWN; + DWORD dwServicePack = 0; + + OsGetVersion(&osv, &dwServicePack); + + // Always error on Windows 2000 or lower + if (OS_VERSION_WIN2000 >= osv) + { + fSuccess = FALSE; + } + else + { + if (NULL != pCondition->sczConditionString) + { + hr = ConditionEvaluate(pVariables, pCondition->sczConditionString, &fSuccess); + ExitOnFailure(hr, "Failed to evaluate condition: %ls", pCondition->sczConditionString); + } + } + + if (!fSuccess) + { + // Display the error messagebox, as long as we're in an appropriate display mode + hr = SplashScreenDisplayError(display, wzBundleName, hrError); + ExitOnFailure(hr, "Failed to display error dialog"); + + *pdwExitCode = static_cast(hrError); + *pfContinueExecution = FALSE; + } + +LExit: + return hr; +} + +HRESULT ConditionGlobalParseFromXml( + __in BURN_CONDITION* pCondition, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNode* pixnNode = NULL; + BSTR bstrExpression = NULL; + + // select variable nodes + hr = XmlSelectSingleNode(pixnBundle, L"Condition", &pixnNode); + if (S_FALSE == hr) + { + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to select condition node."); + + // @Condition + hr = XmlGetText(pixnNode, &bstrExpression); + ExitOnFailure(hr, "Failed to get Condition inner text."); + + hr = StrAllocString(&pCondition->sczConditionString, bstrExpression, 0); + ExitOnFailure(hr, "Failed to copy condition string from BSTR"); + +LExit: + ReleaseBSTR(bstrExpression); + ReleaseObject(pixnNode); + + return hr; +} + + +// internal function definitions + +static HRESULT ParseExpression( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __out BOOL* pf + ) +{ + HRESULT hr = S_OK; + BOOL fFirst = FALSE; + BOOL fSecond = FALSE; + + hr = ParseBooleanTerm(pContext, &fFirst); + ExitOnFailure(hr, "Failed to parse boolean-term."); + + if (BURN_SYMBOL_TYPE_OR == pContext->NextSymbol.Type) + { + hr = NextSymbol(pContext); + ExitOnFailure(hr, "Failed to read next symbol."); + + hr = ParseExpression(pContext, &fSecond); + ExitOnFailure(hr, "Failed to parse expression."); + + *pf = fFirst || fSecond; + } + else + { + *pf = fFirst; + } + +LExit: + return hr; +} + +static HRESULT ParseBooleanTerm( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __out BOOL* pf + ) +{ + HRESULT hr = S_OK; + BOOL fFirst = FALSE; + BOOL fSecond = FALSE; + + hr = ParseBooleanFactor(pContext, &fFirst); + ExitOnFailure(hr, "Failed to parse boolean-factor."); + + if (BURN_SYMBOL_TYPE_AND == pContext->NextSymbol.Type) + { + hr = NextSymbol(pContext); + ExitOnFailure(hr, "Failed to read next symbol."); + + hr = ParseBooleanTerm(pContext, &fSecond); + ExitOnFailure(hr, "Failed to parse boolean-term."); + + *pf = fFirst && fSecond; + } + else + { + *pf = fFirst; + } + +LExit: + return hr; +} + +static HRESULT ParseBooleanFactor( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __out BOOL* pf + ) +{ + HRESULT hr = S_OK; + BOOL fNot = FALSE; + BOOL f = FALSE; + + if (BURN_SYMBOL_TYPE_NOT == pContext->NextSymbol.Type) + { + hr = NextSymbol(pContext); + ExitOnFailure(hr, "Failed to read next symbol."); + + fNot = TRUE; + } + + hr = ParseTerm(pContext, &f); + ExitOnFailure(hr, "Failed to parse term."); + + *pf = fNot ? !f : f; + +LExit: + return hr; +} + +static HRESULT ParseTerm( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __out BOOL* pf + ) +{ + HRESULT hr = S_OK; + BURN_VARIANT firstValue = { }; + BURN_VARIANT secondValue = { }; + + if (BURN_SYMBOL_TYPE_LPAREN == pContext->NextSymbol.Type) + { + hr = NextSymbol(pContext); + ExitOnFailure(hr, "Failed to read next symbol."); + + hr = ParseExpression(pContext, pf); + ExitOnFailure(hr, "Failed to parse expression."); + + hr = Expect(pContext, BURN_SYMBOL_TYPE_RPAREN); + ExitOnFailure(hr, "Failed to expect right parenthesis."); + + ExitFunction1(hr = S_OK); + } + + hr = ParseValue(pContext, &firstValue); + ExitOnFailure(hr, "Failed to parse value."); + + if (COMPARISON & pContext->NextSymbol.Type) + { + BURN_SYMBOL_TYPE comparison = pContext->NextSymbol.Type; + + hr = NextSymbol(pContext); + ExitOnFailure(hr, "Failed to read next symbol."); + + hr = ParseValue(pContext, &secondValue); + ExitOnFailure(hr, "Failed to parse value."); + + hr = CompareValues(comparison, firstValue, secondValue, pf); + ExitOnFailure(hr, "Failed to compare value."); + } + else + { + LONGLONG llValue = 0; + LPWSTR sczValue = NULL; + DWORD64 qwValue = 0; + switch (firstValue.Type) + { + case BURN_VARIANT_TYPE_NONE: + *pf = FALSE; + break; + case BURN_VARIANT_TYPE_STRING: + hr = BVariantGetString(&firstValue, &sczValue); + if (SUCCEEDED(hr)) + { + *pf = sczValue && *sczValue; + } + StrSecureZeroFreeString(sczValue); + break; + case BURN_VARIANT_TYPE_NUMERIC: + hr = BVariantGetNumeric(&firstValue, &llValue); + if (SUCCEEDED(hr)) + { + *pf = 0 != llValue; + } + SecureZeroMemory(&llValue, sizeof(llValue)); + break; + case BURN_VARIANT_TYPE_VERSION: + hr = BVariantGetVersion(&firstValue, &qwValue); + if (SUCCEEDED(hr)) + { + *pf = 0 != qwValue; + } + SecureZeroMemory(&llValue, sizeof(qwValue)); + break; + default: + ExitFunction1(hr = E_UNEXPECTED); + } + } + +LExit: + BVariantUninitialize(&firstValue); + BVariantUninitialize(&secondValue); + return hr; +} + +static HRESULT ParseValue( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __out BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + + // Symbols don't encrypt their value, so can access the value directly. + switch (pContext->NextSymbol.Type) + { + case BURN_SYMBOL_TYPE_IDENTIFIER: + Assert(BURN_VARIANT_TYPE_STRING == pContext->NextSymbol.Value.Type); + + // find variable + hr = VariableGetVariant(pContext->pVariables, pContext->NextSymbol.Value.sczValue, pValue); + if (E_NOTFOUND != hr) + { + ExitOnRootFailure(hr, "Failed to find variable."); + } + break; + + case BURN_SYMBOL_TYPE_NUMBER: __fallthrough; + case BURN_SYMBOL_TYPE_LITERAL: __fallthrough; + case BURN_SYMBOL_TYPE_VERSION: + // steal value of symbol + memcpy_s(pValue, sizeof(BURN_VARIANT), &pContext->NextSymbol.Value, sizeof(BURN_VARIANT)); + memset(&pContext->NextSymbol.Value, 0, sizeof(BURN_VARIANT)); + break; + + default: + pContext->fError = TRUE; + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Failed to parse condition '%ls' at position: %u", pContext->wzCondition, pContext->NextSymbol.iPosition); + } + + // get next symbol + hr = NextSymbol(pContext); + ExitOnFailure(hr, "Failed to read next symbol."); + +LExit: + return hr; +} + +// +// Expect - expects a symbol. +// +static HRESULT Expect( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __in BURN_SYMBOL_TYPE symbolType + ) +{ + HRESULT hr = S_OK; + + if (pContext->NextSymbol.Type != symbolType) + { + pContext->fError = TRUE; + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Failed to parse condition '%ls' at position: %u", pContext->wzCondition, pContext->NextSymbol.iPosition); + } + + hr = NextSymbol(pContext); + ExitOnFailure(hr, "Failed to read next symbol."); + +LExit: + return hr; +} + +// +// NextSymbol - finds the next symbol in an expression string. +// +static HRESULT NextSymbol( + __in BURN_CONDITION_PARSE_CONTEXT* pContext + ) +{ + HRESULT hr = S_OK; + WORD charType = 0; + DWORD iPosition = 0; + DWORD n = 0; + + // free existing symbol + BVariantUninitialize(&pContext->NextSymbol.Value); + memset(&pContext->NextSymbol, 0, sizeof(BURN_SYMBOL)); + + // skip past blanks + while (L'\0' != pContext->wzRead[0]) + { + ::GetStringTypeW(CT_CTYPE1, pContext->wzRead, 1, &charType); + if (0 == (C1_BLANK & charType)) + { + break; // no blank, done + } + ++pContext->wzRead; + } + iPosition = (DWORD)(pContext->wzRead - pContext->wzCondition); + + // read depending on first character type + switch (pContext->wzRead[0]) + { + case L'\0': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_END; + break; + case L'~': + switch (pContext->wzRead[1]) + { + case L'=': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_EQ_I; + n = 2; + break; + case L'>': + switch (pContext->wzRead[2]) + { + case '=': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GE_I; + n = 3; + break; + case L'>': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LOEQ_I; + n = 3; + break; + case L'<': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_BAND_I; + n = 3; + break; + default: + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GT_I; + n = 2; + } + break; + case L'<': + switch (pContext->wzRead[2]) + { + case '=': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LE_I; + n = 3; + break; + case L'<': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_HIEQ_I; + n = 3; + break; + case '>': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NE_I; + n = 3; + break; + default: + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LT_I; + n = 2; + } + break; + default: + // error + pContext->fError = TRUE; + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unexpected '~' operator at position %d.", pContext->wzCondition, iPosition); + } + break; + case L'>': + switch (pContext->wzRead[1]) + { + case L'=': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GE; + n = 2; + break; + case L'>': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LOEQ; + n = 2; + break; + case L'<': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_BAND; + n = 2; + break; + default: + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GT; + n = 1; + } + break; + case L'<': + switch (pContext->wzRead[1]) + { + case L'=': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LE; + n = 2; + break; + case L'<': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_HIEQ; + n = 2; + break; + case L'>': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NE; + n = 2; + break; + default: + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LT; + n = 1; + } + break; + case L'=': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_EQ; + n = 1; + break; + case L'(': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LPAREN; + n = 1; + break; + case L')': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_RPAREN; + n = 1; + break; + case L'"': // literal + do + { + ++n; + if (L'\0' == pContext->wzRead[n]) + { + // error + pContext->fError = TRUE; + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unterminated literal at position %d.", pContext->wzCondition, iPosition); + } + } while (L'"' != pContext->wzRead[n]); + ++n; // terminating '"' + + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LITERAL; + hr = BVariantSetString(&pContext->NextSymbol.Value, &pContext->wzRead[1], n - 2); + ExitOnFailure(hr, "Failed to set symbol value."); + break; + default: + if (C1_DIGIT & charType || L'-' == pContext->wzRead[0]) + { + do + { + ++n; + ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType); + if (C1_ALPHA & charType || L'_' == pContext->wzRead[n]) + { + // error, identifier cannot start with a digit + pContext->fError = TRUE; + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Identifier cannot start at a digit, at position %d.", pContext->wzCondition, iPosition); + } + } while (C1_DIGIT & charType); + + // number + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NUMBER; + + LONGLONG ll = 0; + hr = StrStringToInt64(pContext->wzRead, n, &ll); + if (FAILED(hr)) + { + pContext->fError = TRUE; + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Constant too big, at position %d.", pContext->wzCondition, iPosition); + } + + hr = BVariantSetNumeric(&pContext->NextSymbol.Value, ll); + ExitOnFailure(hr, "Failed to set symbol value."); + } + else if (C1_ALPHA & charType || L'_' == pContext->wzRead[0]) + { + ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[1], 1, &charType); + if (L'v' == pContext->wzRead[0] && C1_DIGIT & charType) + { + // version + DWORD cParts = 1; + for (;;) + { + ++n; + if (L'.' == pContext->wzRead[n]) + { + ++cParts; + if (4 < cParts) + { + // error, too many parts in version + pContext->fError = TRUE; + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Version can have a maximum of 4 parts, at position %d.", pContext->wzCondition, iPosition); + } + } + else + { + ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType); + if (C1_DIGIT != (C1_DIGIT & charType)) + { + break; + } + } + } + + // Symbols don't encrypt their value, so can access the value directly. + hr = FileVersionFromStringEx(&pContext->wzRead[1], n - 1, &pContext->NextSymbol.Value.qwValue); + if (FAILED(hr)) + { + pContext->fError = TRUE; + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Invalid version format, at position %d.", pContext->wzCondition, iPosition); + } + + pContext->NextSymbol.Value.Type = BURN_VARIANT_TYPE_VERSION; + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_VERSION; + } + else + { + do + { + ++n; + ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType); + } while (C1_ALPHA & charType || C1_DIGIT & charType || L'_' == pContext->wzRead[n]); + + if (2 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 2, L"OR", 2)) + { + // OR + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_OR; + } + else if (3 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 3, L"AND", 3)) + { + // AND + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_AND; + } + else if (3 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 3, L"NOT", 3)) + { + // NOT + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NOT; + } + else + { + // identifier + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_IDENTIFIER; + hr = BVariantSetString(&pContext->NextSymbol.Value, pContext->wzRead, n); + ExitOnFailure(hr, "Failed to set symbol value."); + } + } + } + else + { + // error, unexpected character + pContext->fError = TRUE; + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unexpected character at position %d.", pContext->wzCondition, iPosition); + } + } + pContext->NextSymbol.iPosition = iPosition; + pContext->wzRead += n; + +LExit: + return hr; +} + +// +// CompareValues - compares two variant values using a given comparison. +// +static HRESULT CompareValues( + __in BURN_SYMBOL_TYPE comparison, + __in BURN_VARIANT leftOperand, + __in BURN_VARIANT rightOperand, + __out BOOL* pfResult + ) +{ + HRESULT hr = S_OK; + LONGLONG llLeft = 0; + DWORD64 qwLeft = 0; + LPWSTR sczLeft = NULL; + LONGLONG llRight = 0; + DWORD64 qwRight = 0; + LPWSTR sczRight = NULL; + + // get values to compare based on type + if (BURN_VARIANT_TYPE_STRING == leftOperand.Type && BURN_VARIANT_TYPE_STRING == rightOperand.Type) + { + hr = BVariantGetString(&leftOperand, &sczLeft); + ExitOnFailure(hr, "Failed to get the left string"); + hr = BVariantGetString(&rightOperand, &sczRight); + ExitOnFailure(hr, "Failed to get the right string"); + hr = CompareStringValues(comparison, sczLeft, sczRight, pfResult); + } + else if (BURN_VARIANT_TYPE_NUMERIC == leftOperand.Type && BURN_VARIANT_TYPE_NUMERIC == rightOperand.Type) + { + hr = BVariantGetNumeric(&leftOperand, &llLeft); + ExitOnFailure(hr, "Failed to get the left numeric"); + hr = BVariantGetNumeric(&rightOperand, &llRight); + ExitOnFailure(hr, "Failed to get the right numeric"); + hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult); + } + else if (BURN_VARIANT_TYPE_VERSION == leftOperand.Type && BURN_VARIANT_TYPE_VERSION == rightOperand.Type) + { + hr = BVariantGetVersion(&leftOperand, &qwLeft); + ExitOnFailure(hr, "Failed to get the left version"); + hr = BVariantGetVersion(&rightOperand, &qwRight); + ExitOnFailure(hr, "Failed to get the right version"); + hr = CompareVersionValues(comparison, qwLeft, qwRight, pfResult); + } + else if (BURN_VARIANT_TYPE_VERSION == leftOperand.Type && BURN_VARIANT_TYPE_STRING == rightOperand.Type) + { + hr = BVariantGetVersion(&leftOperand, &qwLeft); + ExitOnFailure(hr, "Failed to get the left version"); + hr = BVariantGetVersion(&rightOperand, &qwRight); + if (FAILED(hr)) + { + if (DISP_E_TYPEMISMATCH != hr) + { + ExitOnFailure(hr, "Failed to get the right version"); + } + *pfResult = (BURN_SYMBOL_TYPE_NE == comparison); + hr = S_OK; + } + else + { + hr = CompareVersionValues(comparison, qwLeft, qwRight, pfResult); + } + } + else if (BURN_VARIANT_TYPE_STRING == leftOperand.Type && BURN_VARIANT_TYPE_VERSION == rightOperand.Type) + { + hr = BVariantGetVersion(&rightOperand, &qwRight); + ExitOnFailure(hr, "Failed to get the right version"); + hr = BVariantGetVersion(&leftOperand, &qwLeft); + if (FAILED(hr)) + { + if (DISP_E_TYPEMISMATCH != hr) + { + ExitOnFailure(hr, "Failed to get the left version"); + } + *pfResult = (BURN_SYMBOL_TYPE_NE == comparison); + hr = S_OK; + } + else + { + hr = CompareVersionValues(comparison, qwLeft, qwRight, pfResult); + } + } + else if (BURN_VARIANT_TYPE_NUMERIC == leftOperand.Type && BURN_VARIANT_TYPE_STRING == rightOperand.Type) + { + hr = BVariantGetNumeric(&leftOperand, &llLeft); + ExitOnFailure(hr, "Failed to get the left numeric"); + hr = BVariantGetNumeric(&rightOperand, &llRight); + if (FAILED(hr)) + { + if (DISP_E_TYPEMISMATCH != hr) + { + ExitOnFailure(hr, "Failed to get the right numeric"); + } + *pfResult = (BURN_SYMBOL_TYPE_NE == comparison); + hr = S_OK; + } + else + { + hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult); + } + } + else if (BURN_VARIANT_TYPE_STRING == leftOperand.Type && BURN_VARIANT_TYPE_NUMERIC == rightOperand.Type) + { + hr = BVariantGetNumeric(&rightOperand, &llRight); + ExitOnFailure(hr, "Failed to get the right numeric"); + hr = BVariantGetNumeric(&leftOperand, &llLeft); + if (FAILED(hr)) + { + if (DISP_E_TYPEMISMATCH != hr) + { + ExitOnFailure(hr, "Failed to get the left numeric"); + } + *pfResult = (BURN_SYMBOL_TYPE_NE == comparison); + hr = S_OK; + } + else + { + hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult); + } + } + else + { + // not a combination that can be compared + *pfResult = (BURN_SYMBOL_TYPE_NE == comparison); + } + +LExit: + SecureZeroMemory(&qwLeft, sizeof(DWORD64)); + SecureZeroMemory(&llLeft, sizeof(LONGLONG)); + StrSecureZeroFreeString(sczLeft); + SecureZeroMemory(&qwRight, sizeof(DWORD64)); + SecureZeroMemory(&llRight, sizeof(LONGLONG)); + StrSecureZeroFreeString(sczRight); + + return hr; +} + +// +// CompareStringValues - compares two string values using a given comparison. +// +static HRESULT CompareStringValues( + __in BURN_SYMBOL_TYPE comparison, + __in_z LPCWSTR wzLeftOperand, + __in_z LPCWSTR wzRightOperand, + __out BOOL* pfResult + ) +{ + HRESULT hr = S_OK; + DWORD dwCompareString = (comparison & INSENSITIVE) ? NORM_IGNORECASE : 0; + int cchLeft = lstrlenW(wzLeftOperand); + int cchRight = lstrlenW(wzRightOperand); + + switch (comparison) + { + case BURN_SYMBOL_TYPE_LT: + case BURN_SYMBOL_TYPE_GT: + case BURN_SYMBOL_TYPE_LE: + case BURN_SYMBOL_TYPE_GE: + case BURN_SYMBOL_TYPE_EQ: + case BURN_SYMBOL_TYPE_NE: + case BURN_SYMBOL_TYPE_LT_I: + case BURN_SYMBOL_TYPE_GT_I: + case BURN_SYMBOL_TYPE_LE_I: + case BURN_SYMBOL_TYPE_GE_I: + case BURN_SYMBOL_TYPE_EQ_I: + case BURN_SYMBOL_TYPE_NE_I: + { + int i = ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand, cchLeft, wzRightOperand, cchRight); + hr = CompareIntegerValues(comparison, i, CSTR_EQUAL, pfResult); + } + break; + case BURN_SYMBOL_TYPE_BAND: + case BURN_SYMBOL_TYPE_BAND_I: + // test if left string contains right string + for (int i = 0; (i + cchRight) <= cchLeft; ++i) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand + i, cchRight, wzRightOperand, cchRight)) + { + *pfResult = TRUE; + ExitFunction(); + } + } + *pfResult = FALSE; + break; + case BURN_SYMBOL_TYPE_HIEQ: + case BURN_SYMBOL_TYPE_HIEQ_I: + // test if left string starts with right string + *pfResult = cchLeft >= cchRight && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand, cchRight, wzRightOperand, cchRight); + break; + case BURN_SYMBOL_TYPE_LOEQ: + case BURN_SYMBOL_TYPE_LOEQ_I: + // test if left string ends with right string + *pfResult = cchLeft >= cchRight && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand + (cchLeft - cchRight), cchRight, wzRightOperand, cchRight); + break; + default: + ExitFunction1(hr = E_INVALIDARG); + } + +LExit: + return hr; +} + +// +// CompareIntegerValues - compares two integer values using a given comparison. +// +static HRESULT CompareIntegerValues( + __in BURN_SYMBOL_TYPE comparison, + __in LONGLONG llLeftOperand, + __in LONGLONG llRightOperand, + __out BOOL* pfResult + ) +{ + HRESULT hr = S_OK; + + switch (comparison) + { + case BURN_SYMBOL_TYPE_LT: case BURN_SYMBOL_TYPE_LT_I: *pfResult = llLeftOperand < llRightOperand; break; + case BURN_SYMBOL_TYPE_GT: case BURN_SYMBOL_TYPE_GT_I: *pfResult = llLeftOperand > llRightOperand; break; + case BURN_SYMBOL_TYPE_LE: case BURN_SYMBOL_TYPE_LE_I: *pfResult = llLeftOperand <= llRightOperand; break; + case BURN_SYMBOL_TYPE_GE: case BURN_SYMBOL_TYPE_GE_I: *pfResult = llLeftOperand >= llRightOperand; break; + case BURN_SYMBOL_TYPE_EQ: case BURN_SYMBOL_TYPE_EQ_I: *pfResult = llLeftOperand == llRightOperand; break; + case BURN_SYMBOL_TYPE_NE: case BURN_SYMBOL_TYPE_NE_I: *pfResult = llLeftOperand != llRightOperand; break; + case BURN_SYMBOL_TYPE_BAND: case BURN_SYMBOL_TYPE_BAND_I: *pfResult = (llLeftOperand & llRightOperand) ? TRUE : FALSE; break; + case BURN_SYMBOL_TYPE_HIEQ: case BURN_SYMBOL_TYPE_HIEQ_I: *pfResult = ((llLeftOperand >> 16) & 0xFFFF) == llRightOperand; break; + case BURN_SYMBOL_TYPE_LOEQ: case BURN_SYMBOL_TYPE_LOEQ_I: *pfResult = (llLeftOperand & 0xFFFF) == llRightOperand; break; + default: + ExitFunction1(hr = E_INVALIDARG); + } + +LExit: + return hr; +} + +// +// CompareVersionValues - compares two quad-word version values using a given comparison. +// +static HRESULT CompareVersionValues( + __in BURN_SYMBOL_TYPE comparison, + __in DWORD64 qwLeftOperand, + __in DWORD64 qwRightOperand, + __out BOOL* pfResult + ) +{ + HRESULT hr = S_OK; + + switch (comparison) + { + case BURN_SYMBOL_TYPE_LT: *pfResult = qwLeftOperand < qwRightOperand; break; + case BURN_SYMBOL_TYPE_GT: *pfResult = qwLeftOperand > qwRightOperand; break; + case BURN_SYMBOL_TYPE_LE: *pfResult = qwLeftOperand <= qwRightOperand; break; + case BURN_SYMBOL_TYPE_GE: *pfResult = qwLeftOperand >= qwRightOperand; break; + case BURN_SYMBOL_TYPE_EQ: *pfResult = qwLeftOperand == qwRightOperand; break; + case BURN_SYMBOL_TYPE_NE: *pfResult = qwLeftOperand != qwRightOperand; break; + case BURN_SYMBOL_TYPE_BAND: *pfResult = (qwLeftOperand & qwRightOperand) ? TRUE : FALSE; break; + case BURN_SYMBOL_TYPE_HIEQ: *pfResult = ((qwLeftOperand >> 16) & 0xFFFF) == qwRightOperand; break; + case BURN_SYMBOL_TYPE_LOEQ: *pfResult = (qwLeftOperand & 0xFFFF) == qwRightOperand; break; + default: + ExitFunction1(hr = E_INVALIDARG); + } + +LExit: + return hr; +} diff --git a/src/engine/condition.h b/src/engine/condition.h new file mode 100644 index 00000000..91627f3c --- /dev/null +++ b/src/engine/condition.h @@ -0,0 +1,39 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +typedef struct _BURN_CONDITION +{ + // The is an expression a condition string to fire the built-in "need newer OS" message + LPWSTR sczConditionString; +} BURN_CONDITION; + + +// function declarations + +HRESULT ConditionEvaluate( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzCondition, + __out BOOL* pf + ); +HRESULT ConditionGlobalCheck( + __in BURN_VARIABLES* pVariables, + __in BURN_CONDITION* pBlock, + __in BOOTSTRAPPER_DISPLAY display, + __in_z LPCWSTR wzBundleName, + __out DWORD *pdwExitCode, + __out BOOL *pfContinueExecution + ); +HRESULT ConditionGlobalParseFromXml( + __in BURN_CONDITION* pBlock, + __in IXMLDOMNode* pixnBundle + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/container.cpp b/src/engine/container.cpp new file mode 100644 index 00000000..ada9025b --- /dev/null +++ b/src/engine/container.cpp @@ -0,0 +1,386 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// internal function declarations + +static HRESULT GetAttachedContainerInfo( + __in HANDLE hFile, + __in DWORD iContainerIndex, + __out DWORD* pdwFormat, + __out DWORD64* pqwOffset, + __out DWORD64* pqwSize + ); + + +// function definitions + +extern "C" HRESULT ContainersParseFromXml( + __in BURN_SECTION* pSection, + __in BURN_CONTAINERS* pContainers, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + LPWSTR scz = NULL; + + // select container nodes + hr = XmlSelectNodes(pixnBundle, L"Container", &pixnNodes); + ExitOnFailure(hr, "Failed to select container nodes."); + + // get container node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get container node count."); + + if (!cNodes) + { + ExitFunction(); + } + + // allocate memory for searches + pContainers->rgContainers = (BURN_CONTAINER*)MemAlloc(sizeof(BURN_CONTAINER) * cNodes, TRUE); + ExitOnNull(pContainers->rgContainers, hr, E_OUTOFMEMORY, "Failed to allocate memory for container structs."); + + pContainers->cContainers = cNodes; + + // parse search elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_CONTAINER* pContainer = &pContainers->rgContainers[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // TODO: Read type from manifest. Today only CABINET is supported. + pContainer->type = BURN_CONTAINER_TYPE_CABINET; + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &pContainer->sczId); + ExitOnFailure(hr, "Failed to get @Id."); + + // @Primary + hr = XmlGetYesNoAttribute(pixnNode, L"Primary", &pContainer->fPrimary); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Primary."); + } + + // @Attached + hr = XmlGetYesNoAttribute(pixnNode, L"Attached", &pContainer->fAttached); + if (E_NOTFOUND != hr || pContainer->fPrimary) // if it is a primary container, it has to be attached + { + ExitOnFailure(hr, "Failed to get @Attached."); + } + + // @AttachedIndex + hr = XmlGetAttributeNumber(pixnNode, L"AttachedIndex", &pContainer->dwAttachedIndex); + if (E_NOTFOUND != hr || pContainer->fAttached) // if it is an attached container it must have an index + { + ExitOnFailure(hr, "Failed to get @AttachedIndex."); + } + + // Attached containers are always found attached to the current process, so use the current proccess's + // name instead of what may be in the manifest. + if (pContainer->fAttached) + { + hr = PathForCurrentProcess(&scz, NULL); + ExitOnFailure(hr, "Failed to get path to current process for attached container."); + + LPCWSTR wzFileName = PathFile(scz); + + hr = StrAllocString(&pContainer->sczFilePath, wzFileName, 0); + ExitOnFailure(hr, "Failed to set attached container file path."); + } + else + { + // @FilePath + hr = XmlGetAttributeEx(pixnNode, L"FilePath", &pContainer->sczFilePath); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @FilePath."); + } + } + + // The source path starts as the file path. + hr = StrAllocString(&pContainer->sczSourcePath, pContainer->sczFilePath, 0); + ExitOnFailure(hr, "Failed to copy @FilePath"); + + // @DownloadUrl + hr = XmlGetAttributeEx(pixnNode, L"DownloadUrl", &pContainer->downloadSource.sczUrl); + if (E_NOTFOUND != hr || (!pContainer->fPrimary && !pContainer->sczSourcePath)) // if the package is not a primary package, it must have a source path or a download url + { + ExitOnFailure(hr, "Failed to get @DownloadUrl. Either @SourcePath or @DownloadUrl needs to be provided."); + } + + // @Hash + hr = XmlGetAttributeEx(pixnNode, L"Hash", &pContainer->sczHash); + if (SUCCEEDED(hr)) + { + hr = StrAllocHexDecode(pContainer->sczHash, &pContainer->pbHash, &pContainer->cbHash); + ExitOnFailure(hr, "Failed to hex decode the Container/@Hash."); + } + else if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Hash."); + } + + // If the container is attached, make sure the information in the section matches what the + // manifest contained and get the offset to the container. + if (pContainer->fAttached) + { + hr = SectionGetAttachedContainerInfo(pSection, pContainer->dwAttachedIndex, pContainer->type, &pContainer->qwAttachedOffset, &pContainer->qwFileSize, &pContainer->fActuallyAttached); + ExitOnFailure(hr, "Failed to get attached container information."); + } + + // prepare next iteration + ReleaseNullObject(pixnNode); + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseStr(scz); + + return hr; +} + +extern "C" void ContainersUninitialize( + __in BURN_CONTAINERS* pContainers + ) +{ + if (pContainers->rgContainers) + { + for (DWORD i = 0; i < pContainers->cContainers; ++i) + { + BURN_CONTAINER* pContainer = &pContainers->rgContainers[i]; + + ReleaseStr(pContainer->sczId); + ReleaseStr(pContainer->sczHash); + ReleaseStr(pContainer->sczSourcePath); + ReleaseStr(pContainer->sczFilePath); + ReleaseMem(pContainer->pbHash); + ReleaseStr(pContainer->downloadSource.sczUrl); + ReleaseStr(pContainer->downloadSource.sczUser); + ReleaseStr(pContainer->downloadSource.sczPassword); + } + MemFree(pContainers->rgContainers); + } + + // clear struct + memset(pContainers, 0, sizeof(BURN_CONTAINERS)); +} + +extern "C" HRESULT ContainerOpenUX( + __in BURN_SECTION* pSection, + __in BURN_CONTAINER_CONTEXT* pContext + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER container = { }; + LPWSTR sczExecutablePath = NULL; + + // open attached container + container.type = BURN_CONTAINER_TYPE_CABINET; + container.fPrimary = TRUE; + container.fAttached = TRUE; + container.dwAttachedIndex = 0; + + hr = SectionGetAttachedContainerInfo(pSection, container.dwAttachedIndex, container.type, &container.qwAttachedOffset, &container.qwFileSize, &container.fActuallyAttached); + ExitOnFailure(hr, "Failed to get container information for UX container."); + + AssertSz(container.fActuallyAttached, "The BA container must always be found attached."); + + hr = PathForCurrentProcess(&sczExecutablePath, NULL); + ExitOnFailure(hr, "Failed to get path for executing module."); + + hr = ContainerOpen(pContext, &container, pSection->hEngineFile, sczExecutablePath); + ExitOnFailure(hr, "Failed to open attached container."); + +LExit: + ReleaseStr(sczExecutablePath); + + return hr; +} + +extern "C" HRESULT ContainerOpen( + __in BURN_CONTAINER_CONTEXT* pContext, + __in BURN_CONTAINER* pContainer, + __in HANDLE hContainerFile, + __in_z LPCWSTR wzFilePath + ) +{ + HRESULT hr = S_OK; + LARGE_INTEGER li = { }; + + // initialize context + pContext->type = pContainer->type; + pContext->qwSize = pContainer->qwFileSize; + pContext->qwOffset = pContainer->qwAttachedOffset; + + // If the handle to the container is not open already, open container file + if (INVALID_HANDLE_VALUE == hContainerFile) + { + pContext->hFile = ::CreateFileW(wzFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + ExitOnInvalidHandleWithLastError(pContext->hFile, hr, "Failed to open file: %ls", wzFilePath); + } + else // use the container file handle. + { + if (!::DuplicateHandle(::GetCurrentProcess(), hContainerFile, ::GetCurrentProcess(), &pContext->hFile, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + ExitWithLastError(hr, "Failed to duplicate handle to container: %ls", wzFilePath); + } + } + + // If it is a container attached to an executable, seek to the container offset. + if (pContainer->fAttached) + { + li.QuadPart = (LONGLONG)pContext->qwOffset; + } + + if (!::SetFilePointerEx(pContext->hFile, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to move file pointer to container offset."); + } + + // open the archive + switch (pContext->type) + { + case BURN_CONTAINER_TYPE_CABINET: + hr = CabExtractOpen(pContext, wzFilePath); + break; + } + ExitOnFailure(hr, "Failed to open container."); + +LExit: + return hr; +} + +extern "C" HRESULT ContainerNextStream( + __in BURN_CONTAINER_CONTEXT* pContext, + __inout_z LPWSTR* psczStreamName + ) +{ + HRESULT hr = S_OK; + + switch (pContext->type) + { + case BURN_CONTAINER_TYPE_CABINET: + hr = CabExtractNextStream(pContext, psczStreamName); + break; + } + +//LExit: + return hr; +} + +extern "C" HRESULT ContainerStreamToFile( + __in BURN_CONTAINER_CONTEXT* pContext, + __in_z LPCWSTR wzFileName + ) +{ + HRESULT hr = S_OK; + + switch (pContext->type) + { + case BURN_CONTAINER_TYPE_CABINET: + hr = CabExtractStreamToFile(pContext, wzFileName); + break; + } + +//LExit: + return hr; +} + +extern "C" HRESULT ContainerStreamToBuffer( + __in BURN_CONTAINER_CONTEXT* pContext, + __out BYTE** ppbBuffer, + __out SIZE_T* pcbBuffer + ) +{ + HRESULT hr = S_OK; + + switch (pContext->type) + { + case BURN_CONTAINER_TYPE_CABINET: + hr = CabExtractStreamToBuffer(pContext, ppbBuffer, pcbBuffer); + break; + } + +//LExit: + return hr; +} + +extern "C" HRESULT ContainerSkipStream( + __in BURN_CONTAINER_CONTEXT* pContext + ) +{ + HRESULT hr = S_OK; + + switch (pContext->type) + { + case BURN_CONTAINER_TYPE_CABINET: + hr = CabExtractSkipStream(pContext); + break; + } + +//LExit: + return hr; +} + +extern "C" HRESULT ContainerClose( + __in BURN_CONTAINER_CONTEXT* pContext + ) +{ + HRESULT hr = S_OK; + + // close container + switch (pContext->type) + { + case BURN_CONTAINER_TYPE_CABINET: + hr = CabExtractClose(pContext); + ExitOnFailure(hr, "Failed to close cabinet."); + break; + } + +LExit: + ReleaseFile(pContext->hFile); + + if (SUCCEEDED(hr)) + { + memset(pContext, 0, sizeof(BURN_CONTAINER_CONTEXT)); + } + + return hr; +} + +extern "C" HRESULT ContainerFindById( + __in BURN_CONTAINERS* pContainers, + __in_z LPCWSTR wzId, + __out BURN_CONTAINER** ppContainer + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER* pContainer = NULL; + + for (DWORD i = 0; i < pContainers->cContainers; ++i) + { + pContainer = &pContainers->rgContainers[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pContainer->sczId, -1, wzId, -1)) + { + *ppContainer = pContainer; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} diff --git a/src/engine/container.h b/src/engine/container.h new file mode 100644 index 00000000..2ca3d7ad --- /dev/null +++ b/src/engine/container.h @@ -0,0 +1,183 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// typedefs + +//typedef HRESULT (*PFN_EXTRACTOPEN)( +// __in HANDLE hFile, +// __in DWORD64 qwOffset, +// __in DWORD64 qwSize, +// __out void** ppCookie +// ); +//typedef HRESULT (*PFN_EXTRACTNEXTSTREAM)( +// __in void* pCookie, +// __inout_z LPWSTR* psczStreamName +// ); +//typedef HRESULT (*PFN_EXTRACTSTREAMTOFILE)( +// __in void* pCookie, +// __in_z LPCWSTR wzFileName +// ); +//typedef HRESULT (*PFN_EXTRACTSTREAMTOBUFFER)( +// __in void* pCookie, +// __out BYTE** ppbBuffer, +// __out SIZE_T* pcbBuffer +// ); +//typedef HRESULT (*PFN_EXTRACTCLOSE)( +// __in void* pCookie +// ); + + +// constants + +enum BURN_CONTAINER_TYPE +{ + BURN_CONTAINER_TYPE_NONE, + BURN_CONTAINER_TYPE_CABINET, + BURN_CONTAINER_TYPE_SEVENZIP, +}; + +enum BURN_CAB_OPERATION +{ + BURN_CAB_OPERATION_NONE, + BURN_CAB_OPERATION_NEXT_STREAM, + BURN_CAB_OPERATION_STREAM_TO_FILE, + BURN_CAB_OPERATION_STREAM_TO_BUFFER, + BURN_CAB_OPERATION_SKIP_STREAM, + BURN_CAB_OPERATION_CLOSE, +}; + + +// structs + +typedef struct _BURN_CONTAINER +{ + LPWSTR sczId; + BURN_CONTAINER_TYPE type; + BOOL fPrimary; + BOOL fAttached; + DWORD dwAttachedIndex; + DWORD64 qwFileSize; + LPWSTR sczHash; + LPWSTR sczFilePath; // relative path to container. + LPWSTR sczSourcePath; + DOWNLOAD_SOURCE downloadSource; + + BYTE* pbHash; + DWORD cbHash; + DWORD64 qwAttachedOffset; + BOOL fActuallyAttached; // indicates whether an attached container is attached or missing. + + //LPWSTR* rgsczPayloads; + //DWORD cPayloads; +} BURN_CONTAINER; + +typedef struct _BURN_CONTAINERS +{ + BURN_CONTAINER* rgContainers; + DWORD cContainers; +} BURN_CONTAINERS; + +typedef struct _BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER +{ + HANDLE hFile; + LARGE_INTEGER liPosition; +} BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER; + +typedef struct _BURN_CONTAINER_CONTEXT_CABINET +{ + LPWSTR sczFile; + + HANDLE hThread; + HANDLE hBeginOperationEvent; + HANDLE hOperationCompleteEvent; + + BURN_CAB_OPERATION operation; + HRESULT hrError; + + LPWSTR* psczStreamName; + LPCWSTR wzTargetFile; + HANDLE hTargetFile; + BYTE* pbTargetBuffer; + DWORD cbTargetBuffer; + DWORD iTargetBuffer; + + BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* rgVirtualFilePointers; + DWORD cVirtualFilePointers; +} BURN_CONTAINER_CONTEXT_CABINET; + +typedef struct _BURN_CONTAINER_CONTEXT +{ + HANDLE hFile; + DWORD64 qwOffset; + DWORD64 qwSize; + + //PFN_EXTRACTOPEN pfnExtractOpen; + //PFN_EXTRACTNEXTSTREAM pfnExtractNextStream; + //PFN_EXTRACTSTREAMTOFILE pfnExtractStreamToFile; + //PFN_EXTRACTSTREAMTOBUFFER pfnExtractStreamToBuffer; + //PFN_EXTRACTCLOSE pfnExtractClose; + //void* pCookie; + BURN_CONTAINER_TYPE type; + union + { + BURN_CONTAINER_CONTEXT_CABINET Cabinet; + }; + +} BURN_CONTAINER_CONTEXT; + + +// functions + +HRESULT ContainersParseFromXml( + __in BURN_SECTION* pSection, + __in BURN_CONTAINERS* pContainers, + __in IXMLDOMNode* pixnBundle + ); +void ContainersUninitialize( + __in BURN_CONTAINERS* pContainers + ); +HRESULT ContainerOpenUX( + __in BURN_SECTION* pSection, + __in BURN_CONTAINER_CONTEXT* pContext + ); +HRESULT ContainerOpen( + __in BURN_CONTAINER_CONTEXT* pContext, + __in BURN_CONTAINER* pContainer, + __in HANDLE hContainerFile, + __in_z LPCWSTR wzFilePath + ); +HRESULT ContainerNextStream( + __inout BURN_CONTAINER_CONTEXT* pContext, + __inout_z LPWSTR* psczStreamName + ); +HRESULT ContainerStreamToFile( + __in BURN_CONTAINER_CONTEXT* pContext, + __in_z LPCWSTR wzFileName + ); +HRESULT ContainerStreamToBuffer( + __in BURN_CONTAINER_CONTEXT* pContext, + __out BYTE** ppbBuffer, + __out SIZE_T* pcbBuffer + ); +HRESULT ContainerSkipStream( + __in BURN_CONTAINER_CONTEXT* pContext + ); +HRESULT ContainerClose( + __in BURN_CONTAINER_CONTEXT* pContext + ); +HRESULT ContainerFindById( + __in BURN_CONTAINERS* pContainers, + __in_z LPCWSTR wzId, + __out BURN_CONTAINER** ppContainer + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/core.cpp b/src/engine/core.cpp new file mode 100644 index 00000000..519012e9 --- /dev/null +++ b/src/engine/core.cpp @@ -0,0 +1,1705 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// structs + +struct BURN_CACHE_THREAD_CONTEXT +{ + BURN_ENGINE_STATE* pEngineState; + DWORD* pcOverallProgressTicks; + BOOL* pfRollback; +}; + + +// internal function declarations + +static HRESULT ParseCommandLine( + __in int argc, + __in LPWSTR* argv, + __in BOOTSTRAPPER_COMMAND* pCommand, + __in BURN_PIPE_CONNECTION* pCompanionConnection, + __in BURN_PIPE_CONNECTION* pEmbeddedConnection, + __in BURN_VARIABLES* pVariables, + __out BURN_MODE* pMode, + __out BURN_AU_PAUSE_ACTION* pAutomaticUpdates, + __out BOOL* pfDisableSystemRestore, + __out_z LPWSTR* psczSourceProcessPath, + __out_z LPWSTR* psczOriginalSource, + __out BOOL* pfDisableUnelevate, + __out DWORD *pdwLoggingAttributes, + __out_z LPWSTR* psczLogFile, + __out_z LPWSTR* psczActiveParent, + __out_z LPWSTR* psczIgnoreDependencies, + __out_z LPWSTR* psczAncestors, + __out_z LPWSTR* psczSanitizedCommandLine + ); +static HRESULT ParsePipeConnection( + __in LPWSTR* rgArgs, + __in BURN_PIPE_CONNECTION* pConnection + ); +static HRESULT DetectPackage( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_PACKAGE* pPackage + ); +static HRESULT DetectPackagePayloadsCached( + __in BURN_PACKAGE* pPackage + ); +static DWORD WINAPI CacheThreadProc( + __in LPVOID lpThreadParameter + ); +static HRESULT WaitForCacheThread( + __in HANDLE hCacheThread + ); +static void LogPackages( + __in_opt const BURN_PACKAGE* pUpgradeBundlePackage, + __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage, + __in const BURN_PACKAGES* pPackages, + __in const BURN_RELATED_BUNDLES* pRelatedBundles, + __in const BOOTSTRAPPER_ACTION action + ); + + +// function definitions + +extern "C" HRESULT CoreInitialize( + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + LPWSTR sczSanitizedCommandLine = NULL; + LPWSTR sczStreamName = NULL; + BYTE* pbBuffer = NULL; + SIZE_T cbBuffer = 0; + BURN_CONTAINER_CONTEXT containerContext = { }; + BOOL fElevated = FALSE; + LPWSTR sczSourceProcessPath = NULL; + LPWSTR sczSourceProcessFolder = NULL; + LPWSTR sczOriginalSource = NULL; + + // Initialize variables. + hr = VariableInitialize(&pEngineState->variables); + ExitOnFailure(hr, "Failed to initialize variables."); + + // Open attached UX container. + hr = ContainerOpenUX(&pEngineState->section, &containerContext); + ExitOnFailure(hr, "Failed to open attached UX container."); + + // Load manifest. + hr = ContainerNextStream(&containerContext, &sczStreamName); + ExitOnFailure(hr, "Failed to open manifest stream."); + + hr = ContainerStreamToBuffer(&containerContext, &pbBuffer, &cbBuffer); + ExitOnFailure(hr, "Failed to get manifest stream from container."); + + hr = ManifestLoadXmlFromBuffer(pbBuffer, cbBuffer, pEngineState); + ExitOnFailure(hr, "Failed to load manifest."); + + // Parse command line. + hr = ParseCommandLine(pEngineState->argc, pEngineState->argv, &pEngineState->command, &pEngineState->companionConnection, &pEngineState->embeddedConnection, &pEngineState->variables, &pEngineState->mode, &pEngineState->automaticUpdates, &pEngineState->fDisableSystemRestore, &sczSourceProcessPath, &sczOriginalSource, &pEngineState->fDisableUnelevate, &pEngineState->log.dwAttributes, &pEngineState->log.sczPath, &pEngineState->registration.sczActiveParent, &pEngineState->sczIgnoreDependencies, &pEngineState->registration.sczAncestors, &sczSanitizedCommandLine); + ExitOnFailure(hr, "Failed to parse command line."); + + LogId(REPORT_STANDARD, MSG_BURN_COMMAND_LINE, sczSanitizedCommandLine ? sczSanitizedCommandLine : L""); + + // Retain whether bundle was initially run elevated. + ProcElevated(::GetCurrentProcess(), &fElevated); + + hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, fElevated, TRUE); + ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_ELEVATED); + + hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_UILEVEL, pEngineState->command.display, TRUE); + ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_UILEVEL); + + if (sczSourceProcessPath) + { + hr = VariableSetLiteralString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_PATH, sczSourceProcessPath, TRUE); + ExitOnFailure(hr, "Failed to set source process path variable."); + + hr = PathGetDirectory(sczSourceProcessPath, &sczSourceProcessFolder); + ExitOnFailure(hr, "Failed to get source process folder from path."); + + hr = VariableSetLiteralString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, sczSourceProcessFolder, TRUE); + ExitOnFailure(hr, "Failed to set source process folder variable."); + } + + // Set BURN_BUNDLE_ORIGINAL_SOURCE, if it was passed in on the command line. + // Needs to be done after ManifestLoadXmlFromBuffer. + if (sczOriginalSource) + { + hr = VariableSetLiteralString(&pEngineState->variables, BURN_BUNDLE_ORIGINAL_SOURCE, sczOriginalSource, FALSE); + ExitOnFailure(hr, "Failed to set original source variable."); + } + + if (BURN_MODE_UNTRUSTED == pEngineState->mode || BURN_MODE_NORMAL == pEngineState->mode || BURN_MODE_EMBEDDED == pEngineState->mode) + { + hr = CacheInitialize(&pEngineState->registration, &pEngineState->variables, sczSourceProcessPath); + ExitOnFailure(hr, "Failed to initialize internal cache functionality."); + } + + // If we're not elevated then we'll be loading the bootstrapper application, so extract + // the payloads from the BA container. + if (BURN_MODE_NORMAL == pEngineState->mode || BURN_MODE_EMBEDDED == pEngineState->mode) + { + // Extract all UX payloads to working folder. + hr = UserExperienceEnsureWorkingFolder(pEngineState->registration.sczId, &pEngineState->userExperience.sczTempDirectory); + ExitOnFailure(hr, "Failed to get unique temporary folder for bootstrapper application."); + + hr = PayloadExtractFromContainer(&pEngineState->userExperience.payloads, NULL, &containerContext, pEngineState->userExperience.sczTempDirectory); + ExitOnFailure(hr, "Failed to extract bootstrapper application payloads."); + + // Load the catalog files as soon as they are extracted. + hr = CatalogLoadFromPayload(&pEngineState->catalogs, &pEngineState->userExperience.payloads); + ExitOnFailure(hr, "Failed to load catalog files."); + } + +LExit: + ReleaseStr(sczOriginalSource); + ReleaseStr(sczSourceProcessFolder); + ReleaseStr(sczSourceProcessPath); + ContainerClose(&containerContext); + ReleaseStr(sczStreamName); + ReleaseStr(sczSanitizedCommandLine); + ReleaseMem(pbBuffer); + + return hr; +} + +extern "C" HRESULT CoreSerializeEngineState( + __in BURN_ENGINE_STATE* pEngineState, + __inout BYTE** ppbBuffer, + __inout SIZE_T* piBuffer + ) +{ + HRESULT hr = S_OK; + + hr = VariableSerialize(&pEngineState->variables, TRUE, ppbBuffer, piBuffer); + ExitOnFailure(hr, "Failed to serialize variables."); + +LExit: + return hr; +} + +extern "C" HRESULT CoreQueryRegistration( + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + BYTE* pbBuffer = NULL; + SIZE_T cbBuffer = 0; + SIZE_T iBuffer = 0; + + // Detect if bundle is already installed. + hr = RegistrationDetectInstalled(&pEngineState->registration, &pEngineState->registration.fInstalled); + ExitOnFailure(hr, "Failed to detect bundle install state."); + + // detect resume type + hr = RegistrationDetectResumeType(&pEngineState->registration, &pEngineState->command.resumeType); + ExitOnFailure(hr, "Failed to detect resume type."); + + // If we have a resume mode that suggests the bundle might already be present, try to load any + // previously stored state. + if (BOOTSTRAPPER_RESUME_TYPE_INVALID < pEngineState->command.resumeType) + { + // load resume state + hr = RegistrationLoadState(&pEngineState->registration, &pbBuffer, &cbBuffer); + if (SUCCEEDED(hr)) + { + hr = VariableDeserialize(&pEngineState->variables, TRUE, pbBuffer, cbBuffer, &iBuffer); + } + + // Log any failures and continue. + if (FAILED(hr)) + { + LogId(REPORT_STANDARD, MSG_CANNOT_LOAD_STATE_FILE, hr, pEngineState->registration.sczStateFile); + hr = S_OK; + } + } + +LExit: + ReleaseBuffer(pbBuffer); + + return hr; +} + +extern "C" HRESULT CoreDetect( + __in BURN_ENGINE_STATE* pEngineState, + __in_opt HWND hwndParent + ) +{ + HRESULT hr = S_OK; + BOOL fActivated = FALSE; + BOOL fDetectBegan = FALSE; + BURN_PACKAGE* pPackage = NULL; + HRESULT hrFirstPackageFailure = S_OK; + + LogId(REPORT_STANDARD, MSG_DETECT_BEGIN, pEngineState->packages.cPackages); + + hr = UserExperienceActivateEngine(&pEngineState->userExperience, &fActivated); + ExitOnFailure(hr, "Engine cannot start detect because it is busy with another action."); + + // Detect if bundle installed state has changed since start up. This + // only happens if Apply() changed the state of bundle (installed or + // uninstalled). In that case, Detect() can be used here to reset + // the installed state. + hr = RegistrationDetectInstalled(&pEngineState->registration, &pEngineState->registration.fInstalled); + ExitOnFailure(hr, "Failed to detect bundle install state."); + + if (pEngineState->registration.fInstalled) + { + hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_INSTALLED, 1, TRUE); + ExitOnFailure(hr, "Failed to set the bundle installed built-in variable."); + } + else + { + hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_INSTALLED, NULL, TRUE); + ExitOnFailure(hr, "Failed to unset the bundle installed built-in variable."); + } + + fDetectBegan = TRUE; + hr = UserExperienceOnDetectBegin(&pEngineState->userExperience, pEngineState->registration.fInstalled, pEngineState->packages.cPackages); + ExitOnRootFailure(hr, "UX aborted detect begin."); + + pEngineState->userExperience.hwndDetect = hwndParent; + + // Always reset the detect state which means the plan should be reset too. + DetectReset(&pEngineState->registration, &pEngineState->packages); + PlanReset(&pEngineState->plan, &pEngineState->packages); + + hr = SearchesExecute(&pEngineState->searches, &pEngineState->variables); + ExitOnFailure(hr, "Failed to execute searches."); + + // Load all of the related bundles. + hr = RegistrationDetectRelatedBundles(&pEngineState->registration); + ExitOnFailure(hr, "Failed to detect related bundles."); + + hr = DependencyDetectProviderKeyBundleId(&pEngineState->registration); + if (SUCCEEDED(hr)) + { + hr = DetectForwardCompatibleBundle(&pEngineState->userExperience, &pEngineState->command, &pEngineState->registration); + ExitOnFailure(hr, "Failed to detect forward compatible bundle."); + + // If a forward compatible bundle was detected, skip rest of bundle detection + // since we will passthrough. + if (pEngineState->registration.fEnabledForwardCompatibleBundle) + { + ExitFunction(); + } + } + else if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to detect provider key bundle id."); + + // Report the related bundles. + hr = DetectReportRelatedBundles(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, pEngineState->command.action); + ExitOnFailure(hr, "Failed to report detected related bundles."); + + // Do update detection. + hr = DetectUpdate(pEngineState->registration.sczId, &pEngineState->userExperience, &pEngineState->update); + ExitOnFailure(hr, "Failed to detect update."); + + // Detecting MSPs requires special initialization before processing each package but + // only do the detection if there are actually patch packages to detect because it + // can be expensive. + if (pEngineState->packages.cPatchInfo) + { + hr = MspEngineDetectInitialize(&pEngineState->packages); + ExitOnFailure(hr, "Failed to initialize MSP engine detection."); + } + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + pPackage = pEngineState->packages.rgPackages + i; + + hr = DetectPackage(pEngineState, pPackage); + + // If the package detection failed, ensure the package state is set to unknown. + if (FAILED(hr)) + { + if (SUCCEEDED(hrFirstPackageFailure)) + { + hrFirstPackageFailure = hr; + } + + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; + } + } + + // Log the detected states. + for (DWORD iPackage = 0; iPackage < pEngineState->packages.cPackages; ++iPackage) + { + pPackage = pEngineState->packages.rgPackages + iPackage; + + LogId(REPORT_STANDARD, MSG_DETECTED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingCacheStateToString(pPackage->cache)); + + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + { + for (DWORD iFeature = 0; iFeature < pPackage->Msi.cFeatures; ++iFeature) + { + const BURN_MSIFEATURE* pFeature = pPackage->Msi.rgFeatures + iFeature; + LogId(REPORT_STANDARD, MSG_DETECTED_MSI_FEATURE, pPackage->sczId, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState)); + } + } + else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + for (DWORD iTargetProduct = 0; iTargetProduct < pPackage->Msp.cTargetProductCodes; ++iTargetProduct) + { + const BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + iTargetProduct; + LogId(REPORT_STANDARD, MSG_DETECTED_MSP_TARGET, pPackage->sczId, pTargetProduct->wzTargetProductCode, LoggingPackageStateToString(pTargetProduct->patchPackageState)); + } + } + } + +LExit: + if (SUCCEEDED(hr)) + { + hr = hrFirstPackageFailure; + } + + if (fActivated) + { + UserExperienceDeactivateEngine(&pEngineState->userExperience); + } + + if (fDetectBegan) + { + UserExperienceOnDetectComplete(&pEngineState->userExperience, hr); + } + + pEngineState->userExperience.hwndDetect = NULL; + + LogId(REPORT_STANDARD, MSG_DETECT_COMPLETE, hr); + + return hr; +} + +extern "C" HRESULT CorePlan( + __in BURN_ENGINE_STATE* pEngineState, + __in BOOTSTRAPPER_ACTION action + ) +{ + HRESULT hr = S_OK; + BOOL fActivated = FALSE; + BOOL fPlanBegan = FALSE; + LPWSTR sczLayoutDirectory = NULL; + HANDLE hSyncpointEvent = NULL; + BURN_PACKAGE* pUpgradeBundlePackage = NULL; + BURN_PACKAGE* pForwardCompatibleBundlePackage = NULL; + + LogId(REPORT_STANDARD, MSG_PLAN_BEGIN, pEngineState->packages.cPackages, LoggingBurnActionToString(action)); + + hr = UserExperienceActivateEngine(&pEngineState->userExperience, &fActivated); + ExitOnFailure(hr, "Engine cannot start plan because it is busy with another action."); + + fPlanBegan = TRUE; + hr = UserExperienceOnPlanBegin(&pEngineState->userExperience, pEngineState->packages.cPackages); + ExitOnRootFailure(hr, "BA aborted plan begin."); + + // Always reset the plan. + PlanReset(&pEngineState->plan, &pEngineState->packages); + + // Remember the overall action state in the plan since it shapes the changes + // we make everywhere. + pEngineState->plan.action = action; + pEngineState->plan.wzBundleId = pEngineState->registration.sczId; + pEngineState->plan.wzBundleProviderKey = pEngineState->registration.sczId; + + hr = PlanSetVariables(action, &pEngineState->variables); + ExitOnFailure(hr, "Failed to update action."); + + // Set resume commandline + hr = PlanSetResumeCommand(&pEngineState->registration, action, &pEngineState->command, &pEngineState->log); + ExitOnFailure(hr, "Failed to set resume command"); + + hr = DependencyPlanInitialize(pEngineState, &pEngineState->plan); + ExitOnFailure(hr, "Failed to initialize the dependencies for the plan."); + + if (BOOTSTRAPPER_ACTION_LAYOUT == action) + { + Assert(!pEngineState->plan.fPerMachine); + + // Plan the bundle's layout. + hr = PlanLayoutBundle(&pEngineState->plan, pEngineState->registration.sczExecutableName, pEngineState->section.qwBundleSize, &pEngineState->variables, &pEngineState->payloads, &sczLayoutDirectory); + ExitOnFailure(hr, "Failed to plan the layout of the bundle."); + + // Plan the packages' layout. + hr = PlanPackages(&pEngineState->registration, &pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, FALSE, pEngineState->command.display, pEngineState->command.relationType, sczLayoutDirectory, &hSyncpointEvent); + ExitOnFailure(hr, "Failed to plan packages."); + } + else if (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == action || BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED == action) + { + Assert(!pEngineState->plan.fPerMachine); + + pUpgradeBundlePackage = &pEngineState->update.package; + + hr = PlanUpdateBundle(&pEngineState->userExperience, pUpgradeBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, &hSyncpointEvent); + ExitOnFailure(hr, "Failed to plan update."); + } + else if (pEngineState->registration.fEnabledForwardCompatibleBundle) + { + Assert(!pEngineState->plan.fPerMachine); + + pForwardCompatibleBundlePackage = &pEngineState->registration.forwardCompatibleBundle; + + hr = PlanPassThroughBundle(&pEngineState->userExperience, pForwardCompatibleBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, &hSyncpointEvent); + ExitOnFailure(hr, "Failed to plan passthrough."); + } + else // doing an action that modifies the machine state. + { + BOOL fContinuePlanning = TRUE; // assume we'll be able to keep planning after registration. + pEngineState->plan.fPerMachine = pEngineState->registration.fPerMachine; // default the scope of the plan to the per-machine state of the bundle. + + hr = PlanRegistration(&pEngineState->plan, &pEngineState->registration, pEngineState->command.resumeType, pEngineState->command.relationType, pEngineState->sczIgnoreDependencies, &fContinuePlanning); + ExitOnFailure(hr, "Failed to plan registration."); + + if (fContinuePlanning) + { + // Remember the early index, because we want to be able to insert some related bundles + // into the plan before other executed packages. This particularly occurs for uninstallation + // of addons and patches, which should be uninstalled before the main product. + DWORD dwExecuteActionEarlyIndex = pEngineState->plan.cExecuteActions; + + // Plan the related bundles first to support downgrades with ref-counting. + hr = PlanRelatedBundlesBegin(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->plan); + ExitOnFailure(hr, "Failed to plan related bundles."); + + hr = PlanPackages(&pEngineState->registration, &pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->registration.fInstalled, pEngineState->command.display, pEngineState->command.relationType, NULL, &hSyncpointEvent); + ExitOnFailure(hr, "Failed to plan packages."); + + // Schedule the update of related bundles last. + hr = PlanRelatedBundlesComplete(&pEngineState->registration, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, &hSyncpointEvent, dwExecuteActionEarlyIndex); + ExitOnFailure(hr, "Failed to schedule related bundles."); + } + } + + // Remove unnecessary actions. + hr = PlanFinalizeActions(&pEngineState->plan); + ExitOnFailure(hr, "Failed to remove unnecessary actions from plan."); + + // Finally, display all packages and related bundles in the log. + LogPackages(pUpgradeBundlePackage, pForwardCompatibleBundlePackage, &pEngineState->packages, &pEngineState->registration.relatedBundles, action); + +#ifdef DEBUG + PlanDump(&pEngineState->plan); +#endif + +LExit: + if (fActivated) + { + UserExperienceDeactivateEngine(&pEngineState->userExperience); + } + + if (fPlanBegan) + { + UserExperienceOnPlanComplete(&pEngineState->userExperience, hr); + } + + LogId(REPORT_STANDARD, MSG_PLAN_COMPLETE, hr); + ReleaseStr(sczLayoutDirectory); + + return hr; +} + +extern "C" HRESULT CoreElevate( + __in BURN_ENGINE_STATE* pEngineState, + __in_opt HWND hwndParent + ) +{ + HRESULT hr = S_OK; + + // If the elevated companion pipe isn't created yet, let's make that happen. + if (INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe) + { + if (!pEngineState->sczBundleEngineWorkingPath) + { + hr = CacheBundleToWorkingDirectory(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &pEngineState->userExperience.payloads, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath); + ExitOnFailure(hr, "Failed to cache engine to working directory."); + } + + hr = ElevationElevate(pEngineState, hwndParent); + ExitOnFailure(hr, "Failed to actually elevate."); + + hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, TRUE, TRUE); + ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_ELEVATED); + } + +LExit: + return hr; +} + +extern "C" HRESULT CoreApply( + __in BURN_ENGINE_STATE* pEngineState, + __in_opt HWND hwndParent + ) +{ + HRESULT hr = S_OK; + BOOL fActivated = FALSE; + HANDLE hLock = NULL; + DWORD cOverallProgressTicks = 0; + HANDLE hCacheThread = NULL; + BOOL fElevated = FALSE; + BOOL fRegistered = FALSE; + BOOL fKeepRegistration = pEngineState->plan.fKeepRegistrationDefault; + BOOL fRollback = FALSE; + BOOL fSuspend = FALSE; + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; + BURN_CACHE_THREAD_CONTEXT cacheThreadContext = { }; + DWORD dwPhaseCount = 0; + BOOTSTRAPPER_APPLYCOMPLETE_ACTION applyCompleteAction = BOOTSTRAPPER_APPLYCOMPLETE_ACTION_NONE; + + LogId(REPORT_STANDARD, MSG_APPLY_BEGIN); + + hr = UserExperienceActivateEngine(&pEngineState->userExperience, &fActivated); + ExitOnFailure(hr, "Engine cannot start apply because it is busy with another action."); + + // Ensure any previous attempts to execute are reset. + ApplyReset(&pEngineState->userExperience, &pEngineState->packages); + + if (pEngineState->plan.cCacheActions) + { + ++dwPhaseCount; + } + if (pEngineState->plan.cExecuteActions) + { + ++dwPhaseCount; + } + + hr = UserExperienceOnApplyBegin(&pEngineState->userExperience, dwPhaseCount); + ExitOnRootFailure(hr, "BA aborted apply begin."); + + // Abort if this bundle already requires a restart. + if (BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING == pEngineState->command.resumeType) + { + restart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; + + hr = HRESULT_FROM_WIN32(ERROR_FAIL_NOACTION_REBOOT); + UserExperienceSendError(&pEngineState->userExperience, BOOTSTRAPPER_ERROR_TYPE_APPLY, NULL, hr, NULL, MB_ICONERROR | MB_OK, IDNOACTION); // ignore return value. + ExitFunction(); + } + + hr = ApplyLock(FALSE, &hLock); + ExitOnFailure(hr, "Another per-user setup is already executing."); + + // Initialize only after getting a lock. + ApplyInitialize(); + + pEngineState->userExperience.hwndApply = hwndParent; + + hr = ApplySetVariables(&pEngineState->variables); + ExitOnFailure(hr, "Failed to set initial apply variables."); + + // If the plan is empty of work to do, skip everything. + if (!(pEngineState->plan.cRegistrationActions || pEngineState->plan.cCacheActions || pEngineState->plan.cExecuteActions || pEngineState->plan.cCleanActions)) + { + LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED); + ExitFunction(); + } + + // Ensure the engine is cached to the working path. + if (!pEngineState->sczBundleEngineWorkingPath) + { + hr = CacheBundleToWorkingDirectory(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &pEngineState->userExperience.payloads, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath); + ExitOnFailure(hr, "Failed to cache engine to working directory."); + } + + // Elevate. + if (pEngineState->plan.fPerMachine) + { + hr = CoreElevate(pEngineState, pEngineState->userExperience.hwndApply); + ExitOnFailure(hr, "Failed to elevate."); + + hr = ElevationApplyInitialize(pEngineState->companionConnection.hPipe, &pEngineState->variables, pEngineState->plan.action, pEngineState->automaticUpdates, !pEngineState->fDisableSystemRestore); + ExitOnFailure(hr, "Another per-machine setup is already executing."); + + fElevated = TRUE; + } + + // Register. + if (pEngineState->plan.fRegister) + { + hr = ApplyRegister(pEngineState); + ExitOnFailure(hr, "Failed to register bundle."); + fRegistered = TRUE; + } + + // Cache. + if (pEngineState->plan.cCacheActions) + { + // Launch the cache thread. + cacheThreadContext.pEngineState = pEngineState; + cacheThreadContext.pcOverallProgressTicks = &cOverallProgressTicks; + cacheThreadContext.pfRollback = &fRollback; + + hCacheThread = ::CreateThread(NULL, 0, CacheThreadProc, &cacheThreadContext, 0, NULL); + ExitOnNullWithLastError(hCacheThread, hr, "Failed to create cache thread."); + + // If we're not caching in parallel, wait for the cache thread to terminate. + if (!pEngineState->fParallelCacheAndExecute) + { + hr = WaitForCacheThread(hCacheThread); + ExitOnFailure(hr, "Failed while caching, aborting execution."); + + ReleaseHandle(hCacheThread); + } + } + + // Execute. + if (pEngineState->plan.cExecuteActions) + { + hr = ApplyExecute(pEngineState, hCacheThread, &cOverallProgressTicks, &fKeepRegistration, &fRollback, &fSuspend, &restart); + UserExperienceExecutePhaseComplete(&pEngineState->userExperience, hr); // signal that execute completed. + } + + // Wait for cache thread to terminate, this should return immediately unless we're waiting for layout to complete. + if (hCacheThread) + { + HRESULT hrCached = WaitForCacheThread(hCacheThread); + if (SUCCEEDED(hr)) + { + hr = hrCached; + } + } + + // If something went wrong or force restarted, skip cleaning. + if (FAILED(hr) || fRollback || fSuspend || BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart) + { + ExitFunction(); + } + + // Clean. + if (pEngineState->plan.cCleanActions) + { + ApplyClean(&pEngineState->userExperience, &pEngineState->plan, pEngineState->companionConnection.hPipe); + } + +LExit: + // Unregister. + if (fRegistered) + { + ApplyUnregister(pEngineState, FAILED(hr) || fRollback, fKeepRegistration || pEngineState->plan.fDisallowRemoval, fSuspend, restart); + } + + if (fElevated) + { + ElevationApplyUninitialize(pEngineState->companionConnection.hPipe); + } + + pEngineState->userExperience.hwndApply = NULL; + + ApplyUninitialize(); + + if (hLock) + { + ::ReleaseMutex(hLock); + ::CloseHandle(hLock); + } + + if (fActivated) + { + UserExperienceDeactivateEngine(&pEngineState->userExperience); + } + + ReleaseHandle(hCacheThread); + + UserExperienceOnApplyComplete(&pEngineState->userExperience, hr, restart, &applyCompleteAction); + if (BOOTSTRAPPER_APPLYCOMPLETE_ACTION_RESTART == applyCompleteAction) + { + pEngineState->fRestart = TRUE; + } + + LogId(REPORT_STANDARD, MSG_APPLY_COMPLETE, hr, LoggingRestartToString(restart), LoggingBoolToString(pEngineState->fRestart)); + + return hr; +} + +extern "C" HRESULT CoreLaunchApprovedExe( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe + ) +{ + HRESULT hr = S_OK; + BOOL fActivated = FALSE; + DWORD dwProcessId = 0; + + LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_BEGIN, pLaunchApprovedExe->sczId); + + hr = UserExperienceActivateEngine(&pEngineState->userExperience, &fActivated); + ExitOnFailure(hr, "Engine cannot start LaunchApprovedExe because it is busy with another action."); + + hr = UserExperienceOnLaunchApprovedExeBegin(&pEngineState->userExperience); + ExitOnRootFailure(hr, "BA aborted LaunchApprovedExe begin."); + + // Elevate. + hr = CoreElevate(pEngineState, pLaunchApprovedExe->hwndParent); + ExitOnFailure(hr, "Failed to elevate."); + + // Launch. + hr = ElevationLaunchApprovedExe(pEngineState->companionConnection.hPipe, pLaunchApprovedExe, &dwProcessId); + +LExit: + if (fActivated) + { + UserExperienceDeactivateEngine(&pEngineState->userExperience); + } + + UserExperienceOnLaunchApprovedExeComplete(&pEngineState->userExperience, hr, dwProcessId); + + LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_COMPLETE, hr, dwProcessId); + + ApprovedExesUninitializeLaunch(pLaunchApprovedExe); + + return hr; +} + +extern "C" HRESULT CoreQuit( + __in BURN_ENGINE_STATE* pEngineState, + __in int nExitCode + ) +{ + HRESULT hr = S_OK; + + // Save engine state if resume mode is unequal to "none". + if (BURN_RESUME_MODE_NONE != pEngineState->resumeMode) + { + hr = CoreSaveEngineState(pEngineState); + if (FAILED(hr)) + { + LogErrorId(hr, MSG_STATE_NOT_SAVED, NULL, NULL, NULL); + hr = S_OK; + } + } + + LogId(REPORT_STANDARD, MSG_QUIT, nExitCode); + + ::PostQuitMessage(nExitCode); // go bye-bye. + + return hr; +} + +extern "C" HRESULT CoreSaveEngineState( + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + BYTE* pbBuffer = NULL; + SIZE_T cbBuffer = 0; + + // serialize engine state + hr = CoreSerializeEngineState(pEngineState, &pbBuffer, &cbBuffer); + ExitOnFailure(hr, "Failed to serialize engine state."); + + // write to registration store + if (pEngineState->registration.fPerMachine) + { + hr = ElevationSaveState(pEngineState->companionConnection.hPipe, pbBuffer, cbBuffer); + ExitOnFailure(hr, "Failed to save engine state in per-machine process."); + } + else + { + hr = RegistrationSaveState(&pEngineState->registration, pbBuffer, cbBuffer); + ExitOnFailure(hr, "Failed to save engine state."); + } + +LExit: + ReleaseBuffer(pbBuffer); + + return hr; +} + +extern "C" LPCWSTR CoreRelationTypeToCommandLineString( + __in BOOTSTRAPPER_RELATION_TYPE relationType + ) +{ + LPCWSTR wzRelationTypeCommandLine = NULL; + switch (relationType) + { + case BOOTSTRAPPER_RELATION_DETECT: + wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_DETECT; + break; + case BOOTSTRAPPER_RELATION_UPGRADE: + wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE; + break; + case BOOTSTRAPPER_RELATION_ADDON: + wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_ADDON; + break; + case BOOTSTRAPPER_RELATION_PATCH: + wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_PATCH; + break; + case BOOTSTRAPPER_RELATION_UPDATE: + wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_UPDATE; + break; + case BOOTSTRAPPER_RELATION_DEPENDENT: + break; + case BOOTSTRAPPER_RELATION_NONE: __fallthrough; + default: + wzRelationTypeCommandLine = NULL; + break; + } + + return wzRelationTypeCommandLine; +} + +extern "C" HRESULT CoreRecreateCommandLine( + __deref_inout_z LPWSTR* psczCommandLine, + __in BOOTSTRAPPER_ACTION action, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RESTART restart, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in BOOL fPassthrough, + __in_z_opt LPCWSTR wzActiveParent, + __in_z_opt LPCWSTR wzAncestors, + __in_z_opt LPCWSTR wzAppendLogPath, + __in_z_opt LPCWSTR wzAdditionalCommandLineArguments + ) +{ + HRESULT hr = S_OK; + LPWSTR scz = NULL; + LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType); + + hr = StrAllocString(psczCommandLine, L"", 0); + ExitOnFailure(hr, "Failed to empty command line."); + + switch (display) + { + case BOOTSTRAPPER_DISPLAY_NONE: + hr = StrAllocConcat(psczCommandLine, L" /quiet", 0); + break; + case BOOTSTRAPPER_DISPLAY_PASSIVE: + hr = StrAllocConcat(psczCommandLine, L" /passive", 0); + break; + } + ExitOnFailure(hr, "Failed to append display state to command-line"); + + switch (action) + { + case BOOTSTRAPPER_ACTION_MODIFY: + hr = StrAllocConcat(psczCommandLine, L" /modify", 0); + break; + case BOOTSTRAPPER_ACTION_REPAIR: + hr = StrAllocConcat(psczCommandLine, L" /repair", 0); + break; + case BOOTSTRAPPER_ACTION_UNINSTALL: + hr = StrAllocConcat(psczCommandLine, L" /uninstall", 0); + break; + } + ExitOnFailure(hr, "Failed to append action state to command-line"); + + switch (restart) + { + case BOOTSTRAPPER_RESTART_ALWAYS: + hr = StrAllocConcat(psczCommandLine, L" /forcerestart", 0); + break; + case BOOTSTRAPPER_RESTART_NEVER: + hr = StrAllocConcat(psczCommandLine, L" /norestart", 0); + break; + } + ExitOnFailure(hr, "Failed to append restart state to command-line"); + + if (wzActiveParent) + { + if (*wzActiveParent) + { + hr = StrAllocFormatted(&scz, L" /%ls \"%ls\"", BURN_COMMANDLINE_SWITCH_PARENT, wzActiveParent); + ExitOnFailure(hr, "Failed to format active parent command-line for command-line."); + } + else + { + hr = StrAllocFormatted(&scz, L" /%ls", BURN_COMMANDLINE_SWITCH_PARENT_NONE); + ExitOnFailure(hr, "Failed to format parent:none command-line for command-line."); + } + + hr = StrAllocConcat(psczCommandLine, scz, 0); + ExitOnFailure(hr, "Failed to append active parent command-line to command-line."); + } + + if (wzAncestors) + { + hr = StrAllocFormatted(&scz, L" /%ls=%ls", BURN_COMMANDLINE_SWITCH_ANCESTORS, wzAncestors); + ExitOnFailure(hr, "Failed to format ancestors for command-line."); + + hr = StrAllocConcat(psczCommandLine, scz, 0); + ExitOnFailure(hr, "Failed to append ancestors to command-line."); + } + + if (wzRelationTypeCommandLine) + { + hr = StrAllocFormatted(&scz, L" /%ls", wzRelationTypeCommandLine); + ExitOnFailure(hr, "Failed to format relation type for command-line."); + + hr = StrAllocConcat(psczCommandLine, scz, 0); + ExitOnFailure(hr, "Failed to append relation type to command-line."); + } + + if (fPassthrough) + { + hr = StrAllocFormatted(&scz, L" /%ls", BURN_COMMANDLINE_SWITCH_PASSTHROUGH); + ExitOnFailure(hr, "Failed to format passthrough for command-line."); + + hr = StrAllocConcat(psczCommandLine, scz, 0); + ExitOnFailure(hr, "Failed to append passthrough to command-line."); + } + + if (wzAppendLogPath && *wzAppendLogPath) + { + hr = StrAllocFormatted(&scz, L" /%ls \"%ls\"", BURN_COMMANDLINE_SWITCH_LOG_APPEND, wzAppendLogPath); + ExitOnFailure(hr, "Failed to format append log command-line for command-line."); + + hr = StrAllocConcat(psczCommandLine, scz, 0); + ExitOnFailure(hr, "Failed to append log command-line to command-line"); + } + + if (wzAdditionalCommandLineArguments && *wzAdditionalCommandLineArguments) + { + hr = StrAllocConcat(psczCommandLine, L" ", 0); + ExitOnFailure(hr, "Failed to append space to command-line."); + + hr = StrAllocConcat(psczCommandLine, wzAdditionalCommandLineArguments, 0); + ExitOnFailure(hr, "Failed to append command-line to command-line."); + } + +LExit: + ReleaseStr(scz); + + return hr; +} + +extern "C" HRESULT CoreAppendFileHandleAttachedToCommandLine( + __in HANDLE hFileWithAttachedContainer, + __out HANDLE* phExecutableFile, + __deref_inout_z LPWSTR* psczCommandLine + ) +{ + HRESULT hr = S_OK; + HANDLE hExecutableFile = INVALID_HANDLE_VALUE; + + *phExecutableFile = INVALID_HANDLE_VALUE; + + if (!::DuplicateHandle(::GetCurrentProcess(), hFileWithAttachedContainer, ::GetCurrentProcess(), &hExecutableFile, 0, TRUE, DUPLICATE_SAME_ACCESS)) + { + ExitWithLastError(hr, "Failed to duplicate file handle for attached container."); + } + + hr = StrAllocFormattedSecure(psczCommandLine, L"%ls -%ls=%u", *psczCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, hExecutableFile); + ExitOnFailure(hr, "Failed to append the file handle to the command line."); + + *phExecutableFile = hExecutableFile; + hExecutableFile = INVALID_HANDLE_VALUE; + +LExit: + ReleaseFileHandle(hExecutableFile); + + return hr; +} + +extern "C" HRESULT CoreAppendFileHandleSelfToCommandLine( + __in LPCWSTR wzExecutablePath, + __out HANDLE* phExecutableFile, + __deref_inout_z LPWSTR* psczCommandLine, + __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine + ) +{ + HRESULT hr = S_OK; + HANDLE hExecutableFile = INVALID_HANDLE_VALUE; + SECURITY_ATTRIBUTES securityAttributes = { }; + securityAttributes.bInheritHandle = TRUE; + *phExecutableFile = INVALID_HANDLE_VALUE; + + hExecutableFile = ::CreateFileW(wzExecutablePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, &securityAttributes, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE != hExecutableFile) + { + hr = StrAllocFormattedSecure(psczCommandLine, L"%ls -%ls=%u", *psczCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, hExecutableFile); + ExitOnFailure(hr, "Failed to append the file handle to the command line."); + + if (psczObfuscatedCommandLine) + { + hr = StrAllocFormatted(psczObfuscatedCommandLine, L"%ls -%ls=%u", *psczObfuscatedCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, hExecutableFile); + ExitOnFailure(hr, "Failed to append the file handle to the obfuscated command line."); + } + + *phExecutableFile = hExecutableFile; + hExecutableFile = INVALID_HANDLE_VALUE; + } + +LExit: + ReleaseFileHandle(hExecutableFile); + + return hr; +} + +// internal helper functions + +static HRESULT ParseCommandLine( + __in int argc, + __in LPWSTR* argv, + __in BOOTSTRAPPER_COMMAND* pCommand, + __in BURN_PIPE_CONNECTION* pCompanionConnection, + __in BURN_PIPE_CONNECTION* pEmbeddedConnection, + __in BURN_VARIABLES* pVariables, + __out BURN_MODE* pMode, + __out BURN_AU_PAUSE_ACTION* pAutomaticUpdates, + __out BOOL* pfDisableSystemRestore, + __out_z LPWSTR* psczSourceProcessPath, + __out_z LPWSTR* psczOriginalSource, + __out BOOL* pfDisableUnelevate, + __out DWORD *pdwLoggingAttributes, + __out_z LPWSTR* psczLogFile, + __out_z LPWSTR* psczActiveParent, + __out_z LPWSTR* psczIgnoreDependencies, + __out_z LPWSTR* psczAncestors, + __out_z LPWSTR* psczSanitizedCommandLine + ) +{ + HRESULT hr = S_OK; + BOOL fUnknownArg = FALSE; + BOOL fHidden = FALSE; + LPWSTR sczCommandLine = NULL; + LPWSTR sczSanitizedArgument = NULL; + LPWSTR sczVariableName = NULL; + + for (int i = 0; i < argc; ++i) + { + fUnknownArg = FALSE; + int originalIndex = i; + ReleaseNullStr(sczSanitizedArgument); + + if (argv[i][0] == L'-' || argv[i][0] == L'/') + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"l", -1) || + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"log", -1)) + { + *pdwLoggingAttributes &= ~BURN_LOGGING_ATTRIBUTE_APPEND; + + if (i + 1 >= argc) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for log."); + } + + ++i; + + hr = StrAllocString(psczLogFile, argv[i], 0); + ExitOnFailure(hr, "Failed to copy log file path."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"?", -1) || + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"h", -1) || + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"help", -1)) + { + pCommand->action = BOOTSTRAPPER_ACTION_HELP; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"q", -1) || + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"quiet", -1) || + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"s", -1) || + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"silent", -1)) + { + pCommand->display = BOOTSTRAPPER_DISPLAY_NONE; + + if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart) + { + pCommand->restart = BOOTSTRAPPER_RESTART_AUTOMATIC; + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"passive", -1)) + { + pCommand->display = BOOTSTRAPPER_DISPLAY_PASSIVE; + + if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart) + { + pCommand->restart = BOOTSTRAPPER_RESTART_AUTOMATIC; + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"norestart", -1)) + { + pCommand->restart = BOOTSTRAPPER_RESTART_NEVER; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"forcerestart", -1)) + { + pCommand->restart = BOOTSTRAPPER_RESTART_ALWAYS; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"promptrestart", -1)) + { + pCommand->restart = BOOTSTRAPPER_RESTART_PROMPT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"layout", -1)) + { + if (BOOTSTRAPPER_ACTION_HELP != pCommand->action) + { + pCommand->action = BOOTSTRAPPER_ACTION_LAYOUT; + } + + // If there is another command line argument and it is not a switch, use that as the layout directory. + if (i + 1 < argc && argv[i + 1][0] != L'-' && argv[i + 1][0] != L'/') + { + ++i; + + hr = PathExpand(&pCommand->wzLayoutDirectory, argv[i], PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); + ExitOnFailure(hr, "Failed to copy path for layout directory."); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"uninstall", -1)) + { + if (BOOTSTRAPPER_ACTION_HELP != pCommand->action) + { + pCommand->action = BOOTSTRAPPER_ACTION_UNINSTALL; + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"repair", -1)) + { + if (BOOTSTRAPPER_ACTION_HELP != pCommand->action) + { + pCommand->action = BOOTSTRAPPER_ACTION_REPAIR; + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"modify", -1)) + { + if (BOOTSTRAPPER_ACTION_HELP != pCommand->action) + { + pCommand->action = BOOTSTRAPPER_ACTION_MODIFY; + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"package", -1) || + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"update", -1)) + { + if (BOOTSTRAPPER_ACTION_UNKNOWN == pCommand->action) + { + pCommand->action = BOOTSTRAPPER_ACTION_INSTALL; + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"noaupause", -1)) + { + *pAutomaticUpdates = BURN_AU_PAUSE_ACTION_NONE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"keepaupaused", -1)) + { + // Switch /noaupause takes precedence. + if (BURN_AU_PAUSE_ACTION_NONE != *pAutomaticUpdates) + { + *pAutomaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME; + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"disablesystemrestore", -1)) + { + *pfDisableSystemRestore = TRUE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"originalsource", -1)) + { + if (i + 1 >= argc) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for original source."); + } + + ++i; + hr = StrAllocString(psczOriginalSource, argv[i], 0); + ExitOnFailure(hr, "Failed to copy last used source."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PARENT, -1)) + { + if (i + 1 >= argc) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a value for parent."); + } + + ++i; + + hr = StrAllocString(psczActiveParent, argv[i], 0); + ExitOnFailure(hr, "Failed to copy parent."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PARENT_NONE, -1)) + { + hr = StrAllocString(psczActiveParent, L"", 0); + ExitOnFailure(hr, "Failed to initialize parent to none."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_LOG_APPEND, -1)) + { + if (i + 1 >= argc) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for append log."); + } + + ++i; + + hr = StrAllocString(psczLogFile, argv[i], 0); + ExitOnFailure(hr, "Failed to copy append log file path."); + + *pdwLoggingAttributes |= BURN_LOGGING_ATTRIBUTE_APPEND; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_ELEVATED, -1)) + { + if (i + 3 >= argc) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Must specify the elevated name, token and parent process id."); + } + + if (BURN_MODE_UNTRUSTED != *pMode) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided."); + } + + *pMode = BURN_MODE_ELEVATED; + + ++i; + + hr = ParsePipeConnection(argv + i, pCompanionConnection); + ExitOnFailure(hr, "Failed to parse elevated connection."); + + i += 2; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM), BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM))) + { + // Get a pointer to the next character after the switch. + LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM)]; + if (L'=' != wzParam[0] || L'\0' == wzParam[1]) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_CLEAN_ROOM); + } + + if (BURN_MODE_UNTRUSTED != *pMode) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided."); + } + + *pMode = BURN_MODE_NORMAL; + + hr = StrAllocString(psczSourceProcessPath, wzParam + 1, 0); + ExitOnFailure(hr, "Failed to copy source process path."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_EMBEDDED, -1)) + { + if (i + 3 >= argc) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Must specify the embedded name, token and parent process id."); + } + + switch (*pMode) + { + case BURN_MODE_UNTRUSTED: + // Leave mode as UNTRUSTED to launch the clean room process. + break; + case BURN_MODE_NORMAL: + // The initialization code already assumes that the + // clean room switch is at the beginning of the command line, + // so it's safe to assume that the mode is NORMAL in the clean room. + *pMode = BURN_MODE_EMBEDDED; + break; + default: + ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided."); + } + + ++i; + + hr = ParsePipeConnection(argv + i, pEmbeddedConnection); + ExitOnFailure(hr, "Failed to parse embedded connection."); + + i += 2; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_DETECT, -1)) + { + pCommand->relationType = BOOTSTRAPPER_RELATION_DETECT; + + LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType)); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE, -1)) + { + pCommand->relationType = BOOTSTRAPPER_RELATION_UPGRADE; + + LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType)); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_ADDON, -1)) + { + pCommand->relationType = BOOTSTRAPPER_RELATION_ADDON; + + LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType)); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_PATCH, -1)) + { + pCommand->relationType = BOOTSTRAPPER_RELATION_PATCH; + + LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType)); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_UPDATE, -1)) + { + pCommand->relationType = BOOTSTRAPPER_RELATION_UPDATE; + + LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType)); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PASSTHROUGH, -1)) + { + pCommand->fPassthrough = TRUE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_DISABLE_UNELEVATE, -1)) + { + *pfDisableUnelevate = TRUE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RUNONCE, -1)) + { + if (BURN_MODE_UNTRUSTED != *pMode) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided."); + } + + *pMode = BURN_MODE_RUNONCE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES), BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES))) + { + // Get a pointer to the next character after the switch. + LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES)]; + if (L'=' != wzParam[0] || L'\0' == wzParam[1]) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES); + } + + hr = StrAllocString(psczIgnoreDependencies, &wzParam[1], 0); + ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS), BURN_COMMANDLINE_SWITCH_ANCESTORS, lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS))) + { + // Get a pointer to the next character after the switch. + LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS)]; + if (L'=' != wzParam[0] || L'\0' == wzParam[1]) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_ANCESTORS); + } + + hr = StrAllocString(psczAncestors, &wzParam[1], 0); + ExitOnFailure(hr, "Failed to allocate the list of ancestors."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED), BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED))) + { + // Already processed in InitializeEngineState. + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF), BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF))) + { + // Already processed in InitializeEngineState. + } + else if (lstrlenW(&argv[i][1]) >= lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX) && + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX), BURN_COMMANDLINE_SWITCH_PREFIX, lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX))) + { + // Skip (but log) any other private burn switches we don't recognize, so that + // adding future private variables doesn't break old bundles + LogId(REPORT_STANDARD, MSG_BURN_UNKNOWN_PRIVATE_SWITCH, &argv[i][1]); + } + else + { + fUnknownArg = TRUE; + } + } + else + { + fUnknownArg = TRUE; + + const wchar_t* pwc = wcschr(argv[i], L'='); + if (pwc) + { + hr = StrAllocString(&sczVariableName, argv[i], pwc - argv[i]); + ExitOnFailure(hr, "Failed to copy variable name."); + + hr = VariableIsHidden(pVariables, sczVariableName, &fHidden); + ExitOnFailure(hr, "Failed to determine whether variable is hidden."); + + if (fHidden) + { + hr = StrAllocFormatted(&sczSanitizedArgument, L"%ls=*****", sczVariableName); + ExitOnFailure(hr, "Failed to copy sanitized argument."); + } + } + } + + // Remember command-line switch to pass off to UX. + if (fUnknownArg) + { + PathCommandLineAppend(&pCommand->wzCommandLine, argv[i]); + } + + if (sczSanitizedArgument) + { + PathCommandLineAppend(psczSanitizedCommandLine, sczSanitizedArgument); + } + else + { + for (; originalIndex <= i; ++originalIndex) + { + PathCommandLineAppend(psczSanitizedCommandLine, argv[originalIndex]); + } + } + } + + // If embedded, ensure the display goes embedded as well. + if (BURN_MODE_EMBEDDED == *pMode) + { + pCommand->display = BOOTSTRAPPER_DISPLAY_EMBEDDED; + } + + // Set the defaults if nothing was set above. + if (BOOTSTRAPPER_ACTION_UNKNOWN == pCommand->action) + { + pCommand->action = BOOTSTRAPPER_ACTION_INSTALL; + } + + if (BOOTSTRAPPER_DISPLAY_UNKNOWN == pCommand->display) + { + pCommand->display = BOOTSTRAPPER_DISPLAY_FULL; + } + + if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart) + { + pCommand->restart = BOOTSTRAPPER_RESTART_PROMPT; + } + +LExit: + ReleaseStr(sczVariableName); + ReleaseStr(sczSanitizedArgument); + ReleaseStr(sczCommandLine); + + return hr; +} + +static HRESULT ParsePipeConnection( + __in_ecount(3) LPWSTR* rgArgs, + __in BURN_PIPE_CONNECTION* pConnection + ) +{ + HRESULT hr = S_OK; + + hr = StrAllocString(&pConnection->sczName, rgArgs[0], 0); + ExitOnFailure(hr, "Failed to copy connection name from command line."); + + hr = StrAllocString(&pConnection->sczSecret, rgArgs[1], 0); + ExitOnFailure(hr, "Failed to copy connection secret from command line."); + + hr = StrStringToUInt32(rgArgs[2], 0, reinterpret_cast(&pConnection->dwProcessId)); + ExitOnFailure(hr, "Failed to copy parent process id from command line."); + +LExit: + return hr; +} + +static HRESULT DetectPackage( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + BOOL fBegan = FALSE; + + fBegan = TRUE; + hr = UserExperienceOnDetectPackageBegin(&pEngineState->userExperience, pPackage->sczId); + ExitOnRootFailure(hr, "BA aborted detect package begin."); + + // Detect the cache state of the package. + hr = DetectPackagePayloadsCached(pPackage); + ExitOnFailure(hr, "Failed to detect if payloads are all cached for package: %ls", pPackage->sczId); + + // Use the correct engine to detect the package. + switch (pPackage->type) + { + case BURN_PACKAGE_TYPE_EXE: + hr = ExeEngineDetectPackage(pPackage, &pEngineState->variables); + break; + + case BURN_PACKAGE_TYPE_MSI: + hr = MsiEngineDetectPackage(pPackage, &pEngineState->userExperience); + break; + + case BURN_PACKAGE_TYPE_MSP: + hr = MspEngineDetectPackage(pPackage, &pEngineState->userExperience); + break; + + case BURN_PACKAGE_TYPE_MSU: + hr = MsuEngineDetectPackage(pPackage, &pEngineState->variables); + break; + + default: + hr = E_NOTIMPL; + ExitOnRootFailure(hr, "Package type not supported by detect yet."); + } + + // TODO: consider how to notify the UX that a package is cached. + //else if (BOOTSTRAPPER_PACKAGE_STATE_CACHED > pPackage->currentState && pPackage->fCached) + //{ + // pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_CACHED; + //} + +LExit: + if (FAILED(hr)) + { + LogErrorId(hr, MSG_FAILED_DETECT_PACKAGE, pPackage->sczId, NULL, NULL); + } + + if (fBegan) + { + UserExperienceOnDetectPackageComplete(&pEngineState->userExperience, pPackage->sczId, hr, pPackage->currentState); + } + + return hr; +} + +static HRESULT DetectPackagePayloadsCached( + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCachePath = NULL; + BURN_CACHE_STATE cache = BURN_CACHE_STATE_NONE; // assume the package will not be cached. + LPWSTR sczPayloadCachePath = NULL; + LONGLONG llSize = 0; + + if (pPackage->sczCacheId && *pPackage->sczCacheId) + { + hr = CacheGetCompletedPath(pPackage->fPerMachine, pPackage->sczCacheId, &sczCachePath); + ExitOnFailure(hr, "Failed to get completed cache path."); + + // If the cached directory exists, we have something. + if (DirExists(sczCachePath, NULL)) + { + cache = BURN_CACHE_STATE_COMPLETE; // assume all payloads are cached. + + // Check all payloads to see if any are missing or not the right size. + for (DWORD i = 0; i < pPackage->cPayloads; ++i) + { + BURN_PACKAGE_PAYLOAD* pPackagePayload = pPackage->rgPayloads + i; + + hr = PathConcat(sczCachePath, pPackagePayload->pPayload->sczFilePath, &sczPayloadCachePath); + ExitOnFailure(hr, "Failed to concat payload cache path."); + + hr = FileSize(sczPayloadCachePath, &llSize); + if (SUCCEEDED(hr) && static_cast(llSize) != pPackagePayload->pPayload->qwFileSize) + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_CORRUPT); // size did not match expectations, so cache must have the wrong file. + } + + if (SUCCEEDED(hr)) + { + // TODO: should we do a full on hash verification on the file to ensure + // the exact right file is cached? + + pPackagePayload->fCached = TRUE; + } + else + { + LogId(REPORT_STANDARD, MSG_DETECT_PACKAGE_NOT_FULLY_CACHED, pPackage->sczId, pPackagePayload->pPayload->sczKey, hr); + + cache = BURN_CACHE_STATE_PARTIAL; // found a payload that was not cached so we are partial. + hr = S_OK; + } + } + } + } + + pPackage->cache = cache; + +LExit: + ReleaseStr(sczPayloadCachePath); + ReleaseStr(sczCachePath); + return hr; +} + +static DWORD WINAPI CacheThreadProc( + __in LPVOID lpThreadParameter + ) +{ + HRESULT hr = S_OK; + BURN_CACHE_THREAD_CONTEXT* pContext = reinterpret_cast(lpThreadParameter); + BURN_ENGINE_STATE* pEngineState = pContext->pEngineState; + DWORD* pcOverallProgressTicks = pContext->pcOverallProgressTicks; + BOOL* pfRollback = pContext->pfRollback; + BOOL fComInitialized = FALSE; + + // initialize COM + hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); + ExitOnFailure(hr, "Failed to initialize COM on cache thread."); + fComInitialized = TRUE; + + // cache packages + hr = ApplyCache(pEngineState->section.hSourceEngineFile, &pEngineState->userExperience, &pEngineState->variables, &pEngineState->plan, pEngineState->companionConnection.hCachePipe, pcOverallProgressTicks, pfRollback); + +LExit: + UserExperienceExecutePhaseComplete(&pEngineState->userExperience, hr); // signal that cache completed. + + if (fComInitialized) + { + ::CoUninitialize(); + } + + return (DWORD)hr; +} + +static HRESULT WaitForCacheThread( + __in HANDLE hCacheThread + ) +{ + HRESULT hr = S_OK; + + if (WAIT_OBJECT_0 != ::WaitForSingleObject(hCacheThread, INFINITE)) + { + ExitWithLastError(hr, "Failed to wait for cache thread to terminate."); + } + + if (!::GetExitCodeThread(hCacheThread, (DWORD*)&hr)) + { + ExitWithLastError(hr, "Failed to get cache thread exit code."); + } + +LExit: + return hr; +} + +static void LogPackages( + __in_opt const BURN_PACKAGE* pUpgradeBundlePackage, + __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage, + __in const BURN_PACKAGES* pPackages, + __in const BURN_RELATED_BUNDLES* pRelatedBundles, + __in const BOOTSTRAPPER_ACTION action + ) +{ + if (pUpgradeBundlePackage) + { + LogId(REPORT_STANDARD, MSG_PLANNED_UPGRADE_BUNDLE, pUpgradeBundlePackage->sczId, LoggingRequestStateToString(pUpgradeBundlePackage->defaultRequested), LoggingRequestStateToString(pUpgradeBundlePackage->requested), LoggingActionStateToString(pUpgradeBundlePackage->execute), LoggingActionStateToString(pUpgradeBundlePackage->rollback), LoggingDependencyActionToString(pUpgradeBundlePackage->dependencyExecute)); + } + else if (pForwardCompatibleBundlePackage) + { + LogId(REPORT_STANDARD, MSG_PLANNED_FORWARD_COMPATIBLE_BUNDLE, pForwardCompatibleBundlePackage->sczId, LoggingRequestStateToString(pForwardCompatibleBundlePackage->defaultRequested), LoggingRequestStateToString(pForwardCompatibleBundlePackage->requested), LoggingActionStateToString(pForwardCompatibleBundlePackage->execute), LoggingActionStateToString(pForwardCompatibleBundlePackage->rollback), LoggingDependencyActionToString(pForwardCompatibleBundlePackage->dependencyExecute)); + } + else + { + // Display related bundles first if uninstalling. + if (BOOTSTRAPPER_ACTION_UNINSTALL == action && 0 < pRelatedBundles->cRelatedBundles) + { + for (int i = pRelatedBundles->cRelatedBundles - 1; 0 <= i; --i) + { + const BURN_RELATED_BUNDLE* pRelatedBundle = &pRelatedBundles->rgRelatedBundles[i]; + const BURN_PACKAGE* pPackage = &pRelatedBundle->package; + + LogId(REPORT_STANDARD, MSG_PLANNED_RELATED_BUNDLE, pPackage->sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingDependencyActionToString(pPackage->dependencyExecute)); + } + } + + // Display all the packages in the log. + for (DWORD i = 0; i < pPackages->cPackages; ++i) + { + const DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? pPackages->cPackages - 1 - i : i; + const BURN_PACKAGE* pPackage = &pPackages->rgPackages[iPackage]; + + LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingBoolToString(pPackage->fAcquire), LoggingBoolToString(pPackage->fUncache), LoggingDependencyActionToString(pPackage->dependencyExecute)); + } + + for (DWORD i = 0; i < pPackages->cCompatiblePackages; ++i) + { + const DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? pPackages->cCompatiblePackages - 1 - i : i; + const BURN_PACKAGE* pPackage = &pPackages->rgCompatiblePackages[iPackage]; + + LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingBoolToString(pPackage->fAcquire), LoggingBoolToString(pPackage->fUncache), LoggingDependencyActionToString(pPackage->dependencyExecute)); + } + + // Display related bundles last if caching, installing, modifying, or repairing. + if (BOOTSTRAPPER_ACTION_UNINSTALL < action && 0 < pRelatedBundles->cRelatedBundles) + { + for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i) + { + const BURN_RELATED_BUNDLE* pRelatedBundle = &pRelatedBundles->rgRelatedBundles[i]; + const BURN_PACKAGE* pPackage = &pRelatedBundle->package; + + LogId(REPORT_STANDARD, MSG_PLANNED_RELATED_BUNDLE, pPackage->sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingDependencyActionToString(pPackage->dependencyExecute)); + } + } + } +} diff --git a/src/engine/core.h b/src/engine/core.h new file mode 100644 index 00000000..6a6da2b1 --- /dev/null +++ b/src/engine/core.h @@ -0,0 +1,211 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + +const LPCWSTR BURN_POLICY_REGISTRY_PATH = L"WiX\\Burn"; + +const LPCWSTR BURN_COMMANDLINE_SWITCH_PARENT = L"parent"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_PARENT_NONE = L"parent:none"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_CLEAN_ROOM = L"burn.clean.room"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_ELEVATED = L"burn.elevated"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_EMBEDDED = L"burn.embedded"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_RUNONCE = L"burn.runonce"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_LOG_APPEND = L"burn.log.append"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_DETECT = L"burn.related.detect"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE = L"burn.related.upgrade"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_ADDON = L"burn.related.addon"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_PATCH = L"burn.related.patch"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPDATE = L"burn.related.update"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_PASSTHROUGH = L"burn.passthrough"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_DISABLE_UNELEVATE = L"burn.disable.unelevate"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES = L"burn.ignoredependencies"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_ANCESTORS = L"burn.ancestors"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED = L"burn.filehandle.attached"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF = L"burn.filehandle.self"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_PREFIX = L"burn."; + +const LPCWSTR BURN_BUNDLE_LAYOUT_DIRECTORY = L"WixBundleLayoutDirectory"; +const LPCWSTR BURN_BUNDLE_ACTION = L"WixBundleAction"; +const LPCWSTR BURN_BUNDLE_ACTIVE_PARENT = L"WixBundleActiveParent"; +const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER = L"WixBundleExecutePackageCacheFolder"; +const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_ACTION = L"WixBundleExecutePackageAction"; +const LPCWSTR BURN_BUNDLE_FORCED_RESTART_PACKAGE = L"WixBundleForcedRestartPackage"; +const LPCWSTR BURN_BUNDLE_INSTALLED = L"WixBundleInstalled"; +const LPCWSTR BURN_BUNDLE_ELEVATED = L"WixBundleElevated"; +const LPCWSTR BURN_BUNDLE_PROVIDER_KEY = L"WixBundleProviderKey"; +const LPCWSTR BURN_BUNDLE_MANUFACTURER = L"WixBundleManufacturer"; +const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_PATH = L"WixBundleSourceProcessPath"; +const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_FOLDER = L"WixBundleSourceProcessFolder"; +const LPCWSTR BURN_BUNDLE_TAG = L"WixBundleTag"; +const LPCWSTR BURN_BUNDLE_UILEVEL = L"WixBundleUILevel"; +const LPCWSTR BURN_BUNDLE_VERSION = L"WixBundleVersion"; + +// The following constants must stay in sync with src\wix\Binder.cs +const LPCWSTR BURN_BUNDLE_NAME = L"WixBundleName"; +const LPCWSTR BURN_BUNDLE_ORIGINAL_SOURCE = L"WixBundleOriginalSource"; +const LPCWSTR BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER = L"WixBundleOriginalSourceFolder"; +const LPCWSTR BURN_BUNDLE_LAST_USED_SOURCE = L"WixBundleLastUsedSource"; + + +// enums + +enum BURN_MODE +{ + BURN_MODE_UNTRUSTED, + BURN_MODE_NORMAL, + BURN_MODE_ELEVATED, + BURN_MODE_EMBEDDED, + BURN_MODE_RUNONCE, +}; + +enum BURN_AU_PAUSE_ACTION +{ + BURN_AU_PAUSE_ACTION_NONE, + BURN_AU_PAUSE_ACTION_IFELEVATED, + BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME, +}; + + +// structs + +typedef struct _BURN_ENGINE_STATE +{ + // synchronization + CRITICAL_SECTION csActive; // Any call from the UX that reads or alters the engine state + // needs to be syncronized through this critical section. + // Note: The engine must never do a UX callback while in this critical section. + + // UX flow control + //BOOL fSuspend; // Is TRUE when UX made Suspend() call on core. + //BOOL fForcedReboot; // Is TRUE when UX made Reboot() call on core. + //BOOL fCancelled; // Is TRUE when UX return cancel on UX OnXXX() methods. + //BOOL fReboot; // Is TRUE when UX confirms OnRestartRequried(). + BOOL fRestart; // Set TRUE when UX returns IDRESTART during Apply(). + + // engine data + BOOTSTRAPPER_COMMAND command; + BURN_SECTION section; + BURN_VARIABLES variables; + BURN_CONDITION condition; + BURN_SEARCHES searches; + BURN_USER_EXPERIENCE userExperience; + BURN_REGISTRATION registration; + BURN_CONTAINERS containers; + BURN_CATALOGS catalogs; + BURN_PAYLOADS payloads; + BURN_PACKAGES packages; + BURN_UPDATE update; + BURN_APPROVED_EXES approvedExes; + + HWND hMessageWindow; + HANDLE hMessageWindowThread; + + BOOL fDisableRollback; + BOOL fDisableSystemRestore; + BOOL fParallelCacheAndExecute; + + BURN_LOGGING log; + + BURN_PLAN plan; + + BURN_MODE mode; + BURN_AU_PAUSE_ACTION automaticUpdates; + + DWORD dwElevatedLoggingTlsId; + + LPWSTR sczBundleEngineWorkingPath; + BURN_PIPE_CONNECTION companionConnection; + BURN_PIPE_CONNECTION embeddedConnection; + + BURN_RESUME_MODE resumeMode; + BOOL fDisableUnelevate; + + LPWSTR sczIgnoreDependencies; + + int argc; + LPWSTR* argv; +} BURN_ENGINE_STATE; + + +// function declarations + +HRESULT CoreInitialize( + __in BURN_ENGINE_STATE* pEngineState + ); +HRESULT CoreSerializeEngineState( + __in BURN_ENGINE_STATE* pEngineState, + __inout BYTE** ppbBuffer, + __inout SIZE_T* piBuffer + ); +HRESULT CoreQueryRegistration( + __in BURN_ENGINE_STATE* pEngineState + ); +//HRESULT CoreDeserializeEngineState( +// __in BURN_ENGINE_STATE* pEngineState, +// __in_bcount(cbBuffer) BYTE* pbBuffer, +// __in SIZE_T cbBuffer +// ); +HRESULT CoreDetect( + __in BURN_ENGINE_STATE* pEngineState, + __in_opt HWND hwndParent + ); +HRESULT CorePlan( + __in BURN_ENGINE_STATE* pEngineState, + __in BOOTSTRAPPER_ACTION action + ); +HRESULT CoreElevate( + __in BURN_ENGINE_STATE* pEngineState, + __in_opt HWND hwndParent + ); +HRESULT CoreApply( + __in BURN_ENGINE_STATE* pEngineState, + __in_opt HWND hwndParent + ); +HRESULT CoreLaunchApprovedExe( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe + ); +HRESULT CoreQuit( + __in BURN_ENGINE_STATE* pEngineState, + __in int nExitCode + ); +HRESULT CoreSaveEngineState( + __in BURN_ENGINE_STATE* pEngineState + ); +LPCWSTR CoreRelationTypeToCommandLineString( + __in BOOTSTRAPPER_RELATION_TYPE relationType + ); +HRESULT CoreRecreateCommandLine( + __deref_inout_z LPWSTR* psczCommandLine, + __in BOOTSTRAPPER_ACTION action, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RESTART restart, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in BOOL fPassthrough, + __in_z_opt LPCWSTR wzActiveParent, + __in_z_opt LPCWSTR wzAncestors, + __in_z_opt LPCWSTR wzAppendLogPath, + __in_z_opt LPCWSTR wzAdditionalCommandLineArguments + ); +HRESULT CoreAppendFileHandleAttachedToCommandLine( + __in HANDLE hFileWithAttachedContainer, + __out HANDLE* phExecutableFile, + __deref_inout_z LPWSTR* psczCommandLine + ); +HRESULT CoreAppendFileHandleSelfToCommandLine( + __in LPCWSTR wzExecutablePath, + __out HANDLE* phExecutableFile, + __deref_inout_z LPWSTR* psczCommandLine, + __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/dependency.cpp b/src/engine/dependency.cpp new file mode 100644 index 00000000..c7c6e024 --- /dev/null +++ b/src/engine/dependency.cpp @@ -0,0 +1,1203 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// constants + +#define INITIAL_STRINGDICT_SIZE 48 +const LPCWSTR vcszIgnoreDependenciesDelim = L";"; + + +// internal function declarations + +static HRESULT SplitIgnoreDependencies( + __in_z LPCWSTR wzIgnoreDependencies, + __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, + __inout LPUINT pcDependencies + ); + +static HRESULT JoinIgnoreDependencies( + __out_z LPWSTR* psczIgnoreDependencies, + __in_ecount(cDependencies) const DEPENDENCY* rgDependencies, + __in UINT cDependencies + ); + +static HRESULT GetIgnoredDependents( + __in const BURN_PACKAGE* pPackage, + __in const BURN_PLAN* pPlan, + __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents + ); + +static HRESULT GetProviderInformation( + __in HKEY hkRoot, + __in_z LPCWSTR wzProviderKey, + __deref_opt_out_z_opt LPWSTR* psczProviderKey, + __deref_opt_out_z_opt LPWSTR* psczId + ); + +static void CalculateDependencyActionStates( + __in const BURN_PACKAGE* pPackage, + __in const BOOTSTRAPPER_ACTION action, + __out BURN_DEPENDENCY_ACTION* pDependencyExecuteAction, + __out BURN_DEPENDENCY_ACTION* pDependencyRollbackAction + ); + +static HRESULT AddPackageDependencyActions( + __in_opt DWORD *pdwInsertSequence, + __in const BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in const BURN_DEPENDENCY_ACTION dependencyExecuteAction, + __in const BURN_DEPENDENCY_ACTION dependencyRollbackAction + ); + +static HRESULT RegisterPackageProvider( + __in const BURN_PACKAGE* pPackage + ); + +static void UnregisterPackageProvider( + __in const BURN_PACKAGE* pPackage + ); + +static HRESULT RegisterPackageDependency( + __in BOOL fPerMachine, + __in const BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzDependentProviderKey + ); + +static void UnregisterPackageDependency( + __in BOOL fPerMachine, + __in const BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzDependentProviderKey + ); + +static BOOL PackageProviderExists( + __in const BURN_PACKAGE* pPackage + ); + + +// functions + +extern "C" void DependencyUninitialize( + __in BURN_DEPENDENCY_PROVIDER* pProvider + ) +{ + ReleaseStr(pProvider->sczKey); + ReleaseStr(pProvider->sczVersion); + ReleaseStr(pProvider->sczDisplayName); + memset(pProvider, 0, sizeof(BURN_DEPENDENCY_PROVIDER)); +} + +extern "C" HRESULT DependencyParseProvidersFromXml( + __in BURN_PACKAGE* pPackage, + __in IXMLDOMNode* pixnPackage + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + DWORD cNodes = 0; + IXMLDOMNode* pixnNode = NULL; + + // Select dependency provider nodes. + hr = XmlSelectNodes(pixnPackage, L"Provides", &pixnNodes); + ExitOnFailure(hr, "Failed to select dependency provider nodes."); + + // Get dependency provider node count. + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get the dependency provider node count."); + + if (!cNodes) + { + ExitFunction1(hr = S_OK); + } + + // Allocate memory for dependency provider pointers. + pPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER) * cNodes, TRUE); + ExitOnNull(pPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers."); + + pPackage->cDependencyProviders = cNodes; + + // Parse dependency provider elements. + for (DWORD i = 0; i < cNodes; i++) + { + BURN_DEPENDENCY_PROVIDER* pDependencyProvider = &pPackage->rgDependencyProviders[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get the next dependency provider node."); + + // @Key + hr = XmlGetAttributeEx(pixnNode, L"Key", &pDependencyProvider->sczKey); + ExitOnFailure(hr, "Failed to get the Key attribute."); + + // @Version + hr = XmlGetAttributeEx(pixnNode, L"Version", &pDependencyProvider->sczVersion); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get the Version attribute."); + } + + // @DisplayName + hr = XmlGetAttributeEx(pixnNode, L"DisplayName", &pDependencyProvider->sczDisplayName); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get the DisplayName attribute."); + } + + // @Imported + hr = XmlGetYesNoAttribute(pixnNode, L"Imported", &pDependencyProvider->fImported); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get the Imported attribute."); + } + else + { + pDependencyProvider->fImported = FALSE; + hr = S_OK; + } + + // Prepare next iteration. + ReleaseNullObject(pixnNode); + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNode); + ReleaseObject(pixnNodes); + + return hr; +} + +extern "C" HRESULT DependencyDetectProviderKeyPackageId( + __in const BURN_PACKAGE* pPackage, + __deref_opt_out_z_opt LPWSTR* psczProviderKey, + __deref_opt_out_z_opt LPWSTR* psczId + ) +{ + HRESULT hr = E_NOTFOUND; + LPWSTR wzProviderKey = NULL; + HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + + // Find the first package id registered for the provider key. + hr = GetProviderInformation(hkRoot, pProvider->sczKey, psczProviderKey, psczId); + if (E_NOTFOUND == hr) + { + continue; + } + ExitOnFailure(hr, "Failed to get the package provider information."); + + ExitFunction(); + } + + // Older bundles may not have written the id so try the default. + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + { + wzProviderKey = pPackage->Msi.sczProductCode; + } + + if (wzProviderKey) + { + hr = GetProviderInformation(hkRoot, wzProviderKey, psczProviderKey, psczId); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get the package default provider information."); + } + +LExit: + return hr; +} + +extern "C" HRESULT DependencyDetectProviderKeyBundleId( + __in BURN_REGISTRATION* pRegistration + ) +{ + HRESULT hr = S_OK; + + hr = DepGetProviderInformation(pRegistration->hkRoot, pRegistration->sczProviderKey, &pRegistration->sczDetectedProviderKeyBundleId, NULL, NULL); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get provider key bundle id."); + + // If a bundle id was not explicitly set, default the provider key bundle id to this bundle's provider key. + if (!pRegistration->sczDetectedProviderKeyBundleId || !*pRegistration->sczDetectedProviderKeyBundleId) + { + hr = StrAllocString(&pRegistration->sczDetectedProviderKeyBundleId, pRegistration->sczProviderKey, 0); + ExitOnFailure(hr, "Failed to initialize provider key bundle id."); + } + +LExit: + return hr; +} + +extern "C" HRESULT DependencyPlanInitialize( + __in const BURN_ENGINE_STATE* pEngineState, + __in BURN_PLAN* pPlan + ) +{ + HRESULT hr = S_OK; + + // The current bundle provider key should always be ignored for dependency checks. + hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pEngineState->registration.sczProviderKey, NULL); + ExitOnFailure(hr, "Failed to add the bundle provider key to the list of dependencies to ignore."); + + // Add the list of dependencies to ignore to the plan. + if (pEngineState->sczIgnoreDependencies) + { + // TODO: After adding enumeration to STRINGDICT, a single STRINGDICT_HANDLE can be used everywhere. + hr = SplitIgnoreDependencies(pEngineState->sczIgnoreDependencies, &pPlan->rgPlannedProviders, &pPlan->cPlannedProviders); + ExitOnFailure(hr, "Failed to split the list of dependencies to ignore."); + } + +LExit: + return hr; +} + +extern "C" HRESULT DependencyAllocIgnoreDependencies( + __in const BURN_PLAN *pPlan, + __out_z LPWSTR* psczIgnoreDependencies + ) +{ + HRESULT hr = S_OK; + + // Join the list of dependencies to ignore for each related bundle. + if (0 < pPlan->cPlannedProviders) + { + hr = JoinIgnoreDependencies(psczIgnoreDependencies, pPlan->rgPlannedProviders, pPlan->cPlannedProviders); + ExitOnFailure(hr, "Failed to join the list of dependencies to ignore."); + } + +LExit: + return hr; +} + +extern "C" HRESULT DependencyAddIgnoreDependencies( + __in STRINGDICT_HANDLE sdIgnoreDependencies, + __in_z LPCWSTR wzAddIgnoreDependencies + ) +{ + HRESULT hr = S_OK; + LPWSTR wzContext = NULL; + + // Parse through the semicolon-delimited tokens and add to the array. + for (LPCWSTR wzToken = ::wcstok_s(const_cast(wzAddIgnoreDependencies), vcszIgnoreDependenciesDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, vcszIgnoreDependenciesDelim, &wzContext)) + { + hr = DictKeyExists(sdIgnoreDependencies, wzToken); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies."); + } + else + { + hr = DictAddKey(sdIgnoreDependencies, wzToken); + ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzToken); + } + } + +LExit: + return hr; +} + +extern "C" BOOL DependencyDependentExists( + __in const BURN_REGISTRATION* pRegistration, + __in_z LPCWSTR wzDependentProviderKey + ) +{ + HRESULT hr = S_OK; + + hr = DepDependentExists(pRegistration->hkRoot, pRegistration->sczProviderKey, wzDependentProviderKey); + return SUCCEEDED(hr); +} + +extern "C" HRESULT DependencyPlanPackageBegin( + __in BOOL fPerMachine, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan + ) +{ + HRESULT hr = S_OK; + STRINGDICT_HANDLE sdIgnoredDependents = NULL; + DEPENDENCY* rgDependents = NULL; + UINT cDependents = 0; + HKEY hkHive = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + BURN_DEPENDENCY_ACTION dependencyExecuteAction = BURN_DEPENDENCY_ACTION_NONE; + BURN_DEPENDENCY_ACTION dependencyRollbackAction = BURN_DEPENDENCY_ACTION_NONE; + + pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE; + pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE; + + // Make sure the package defines at least one provider. + if (0 == pPackage->cDependencyProviders) + { + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_SKIP_NOPROVIDERS, pPackage->sczId); + ExitFunction1(hr = S_OK); + } + + // Make sure the package is in the same scope as the bundle. + if (fPerMachine != pPackage->fPerMachine) + { + LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine)); + ExitFunction1(hr = S_OK); + } + + // If we're uninstalling the package, check if any dependents are registered. + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) + { + // Build up a list of dependents to ignore, including the current bundle. + hr = GetIgnoredDependents(pPackage, pPlan, &sdIgnoredDependents); + ExitOnFailure(hr, "Failed to build the list of ignored dependents."); + + // Skip the dependency check if "ALL" was authored for IGNOREDEPENDENCIES. + hr = DictKeyExists(sdIgnoredDependents, L"ALL"); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to check if \"ALL\" was set in IGNOREDEPENDENCIES."); + } + else + { + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + + hr = DepCheckDependents(hkHive, pProvider->sczKey, 0, sdIgnoredDependents, &rgDependents, &cDependents); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed dependents check on package provider: %ls", pProvider->sczKey); + } + else + { + hr = S_OK; + } + } + } + } + + // Calculate the dependency actions before the package itself is planned. + CalculateDependencyActionStates(pPackage, pPlan->action, &dependencyExecuteAction, &dependencyRollbackAction); + + // If dependents were found, change the action to not uninstall the package. + if (0 < cDependents) + { + LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS, pPackage->sczId, cDependents); + + for (DWORD i = 0; i < cDependents; ++i) + { + const DEPENDENCY* pDependency = &rgDependents[i]; + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_DEPENDENT, pDependency->sczKey, LoggingStringOrUnknownIfNull(pDependency->sczName)); + } + + pPackage->fDependencyManagerWasHere = TRUE; + pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; + pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + } + // Use the calculated dependency actions as the provider actions if there + // are any non-imported providers that need to be registered and the package + // is current (not obsolete). + else if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE != pPackage->currentState) + { + BOOL fAllImportedProviders = TRUE; // assume all providers were imported. + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + if (!pProvider->fImported) + { + fAllImportedProviders = FALSE; + break; + } + } + + if (!fAllImportedProviders) + { + pPackage->providerExecute = dependencyExecuteAction; + pPackage->providerRollback = dependencyRollbackAction; + } + } + + // If the package will be removed, add its providers to the growing list in the plan. + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) + { + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + + hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, NULL); + ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey); + } + } + + pPackage->dependencyExecute = dependencyExecuteAction; + pPackage->dependencyRollback = dependencyRollbackAction; + +LExit: + ReleaseDependencyArray(rgDependents, cDependents); + ReleaseDict(sdIgnoredDependents); + + return hr; +} + +extern "C" HRESULT DependencyPlanPackage( + __in_opt DWORD *pdwInsertSequence, + __in const BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* pAction = NULL; + + // If the dependency execution action is to unregister, add the dependency actions to the plan + // *before* the provider key is potentially removed. + if (BURN_DEPENDENCY_ACTION_UNREGISTER == pPackage->dependencyExecute) + { + hr = AddPackageDependencyActions(pdwInsertSequence, pPackage, pPlan, pPackage->dependencyExecute, pPackage->dependencyRollback); + ExitOnFailure(hr, "Failed to plan the dependency actions for package: %ls", pPackage->sczId); + } + + // Add the provider rollback plan. + if (BURN_DEPENDENCY_ACTION_NONE != pPackage->providerRollback) + { + hr = PlanAppendRollbackAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append provider rollback action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER; + pAction->packageProvider.pPackage = const_cast(pPackage); + pAction->packageProvider.action = pPackage->providerRollback; + + // Put a checkpoint before the execute action so that rollback happens + // if execute fails. + hr = PlanExecuteCheckpoint(pPlan); + ExitOnFailure(hr, "Failed to plan provider checkpoint action."); + } + + // Add the provider execute plan. This comes after rollback so if something goes wrong + // rollback will try to clean up after us. + if (BURN_DEPENDENCY_ACTION_NONE != pPackage->providerExecute) + { + if (NULL != pdwInsertSequence) + { + hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction); + ExitOnFailure(hr, "Failed to insert provider execute action."); + + // Always move the sequence after this dependency action so the provider registration + // stays in front of the inserted actions. + ++(*pdwInsertSequence); + } + else + { + hr = PlanAppendExecuteAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append provider execute action."); + } + + pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER; + pAction->packageProvider.pPackage = const_cast(pPackage); + pAction->packageProvider.action = pPackage->providerExecute; + } + +LExit: + return hr; +} + +extern "C" HRESULT DependencyPlanPackageComplete( + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan + ) +{ + HRESULT hr = S_OK; + + // Registration of dependencies happens here, after the package is planned to be + // installed and all that good stuff. + if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute) + { + // Recalculate the dependency actions in case other operations may have changed + // the package execution state. + CalculateDependencyActionStates(pPackage, pPlan->action, &pPackage->dependencyExecute, &pPackage->dependencyRollback); + + // If the dependency execution action is *still* to register, add the dependency actions to the plan. + if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute) + { + hr = AddPackageDependencyActions(NULL, pPackage, pPlan, pPackage->dependencyExecute, pPackage->dependencyRollback); + ExitOnFailure(hr, "Failed to plan the dependency actions for package: %ls", pPackage->sczId); + } + } + +LExit: + return hr; +} + +extern "C" HRESULT DependencyExecutePackageProviderAction( + __in const BURN_EXECUTE_ACTION* pAction + ) +{ + AssertSz(BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER == pAction->type, "Execute action type not supported by this function."); + + HRESULT hr = S_OK; + const BURN_PACKAGE* pPackage = pAction->packageProvider.pPackage; + + // Register or unregister the package provider(s). + if (BURN_DEPENDENCY_ACTION_REGISTER == pAction->packageProvider.action) + { + hr = RegisterPackageProvider(pPackage); + ExitOnFailure(hr, "Failed to register the package providers."); + } + else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pAction->packageProvider.action) + { + UnregisterPackageProvider(pPackage); + } + +LExit: + if (!pPackage->fVital) + { + hr = S_OK; + } + + return hr; +} + +extern "C" HRESULT DependencyExecutePackageDependencyAction( + __in BOOL fPerMachine, + __in const BURN_EXECUTE_ACTION* pAction + ) +{ + AssertSz(BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY == pAction->type, "Execute action type not supported by this function."); + + HRESULT hr = S_OK; + const BURN_PACKAGE* pPackage = pAction->packageDependency.pPackage; + + // Register or unregister the bundle as a dependent of each package dependency provider. + if (BURN_DEPENDENCY_ACTION_REGISTER == pAction->packageDependency.action) + { + hr = RegisterPackageDependency(fPerMachine, pPackage, pAction->packageDependency.sczBundleProviderKey); + ExitOnFailure(hr, "Failed to register the dependency on the package provider."); + } + else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pAction->packageDependency.action) + { + UnregisterPackageDependency(fPerMachine, pPackage, pAction->packageDependency.sczBundleProviderKey); + } + +LExit: + if (!pPackage->fVital) + { + hr = S_OK; + } + + return hr; +} + +extern "C" HRESULT DependencyRegisterBundle( + __in const BURN_REGISTRATION* pRegistration + ) +{ + HRESULT hr = S_OK; + LPWSTR sczVersion = NULL; + + hr = FileVersionToStringEx(pRegistration->qwVersion, &sczVersion); + ExitOnFailure(hr, "Failed to format the registration version string."); + + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_REGISTER, pRegistration->sczProviderKey, sczVersion); + + // Register the bundle provider key. + hr = DepRegisterDependency(pRegistration->hkRoot, pRegistration->sczProviderKey, sczVersion, pRegistration->sczDisplayName, pRegistration->sczId, 0); + ExitOnFailure(hr, "Failed to register the bundle dependency provider."); + +LExit: + ReleaseStr(sczVersion); + + return hr; +} + +extern "C" HRESULT DependencyProcessDependentRegistration( + __in const BURN_REGISTRATION* pRegistration, + __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction + ) +{ + HRESULT hr = S_OK; + + switch (pAction->type) + { + case BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER: + hr = DepRegisterDependent(pRegistration->hkRoot, pRegistration->sczProviderKey, pAction->sczDependentProviderKey, NULL, NULL, 0); + ExitOnFailure(hr, "Failed to register dependent: %ls", pAction->sczDependentProviderKey); + break; + + case BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER: + hr = DepUnregisterDependent(pRegistration->hkRoot, pRegistration->sczProviderKey, pAction->sczDependentProviderKey); + ExitOnFailure(hr, "Failed to unregister dependent: %ls", pAction->sczDependentProviderKey); + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Unrecognized registration action type: %d", pAction->type); + } + +LExit: + return hr; +} + +extern "C" void DependencyUnregisterBundle( + __in const BURN_REGISTRATION* pRegistration + ) +{ + HRESULT hr = S_OK; + + // Remove the bundle provider key. + hr = DepUnregisterDependency(pRegistration->hkRoot, pRegistration->sczProviderKey); + if (SUCCEEDED(hr)) + { + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_UNREGISTERED, pRegistration->sczProviderKey); + } + else if (FAILED(hr) && E_FILENOTFOUND != hr) + { + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_UNREGISTERED_FAILED, pRegistration->sczProviderKey, hr); + } +} + +// internal functions + +/******************************************************************** + SplitIgnoreDependencies - Splits a semicolon-delimited + string into a list of unique dependencies to ignore. + +*********************************************************************/ +static HRESULT SplitIgnoreDependencies( + __in_z LPCWSTR wzIgnoreDependencies, + __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, + __inout LPUINT pcDependencies + ) +{ + HRESULT hr = S_OK; + LPWSTR wzContext = NULL; + STRINGDICT_HANDLE sdIgnoreDependencies = NULL; + + // Create a dictionary to hold unique dependencies. + hr = DictCreateStringList(&sdIgnoreDependencies, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create the string dictionary."); + + // Parse through the semicolon-delimited tokens and add to the array. + for (LPCWSTR wzToken = ::wcstok_s(const_cast(wzIgnoreDependencies), vcszIgnoreDependenciesDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, vcszIgnoreDependenciesDelim, &wzContext)) + { + hr = DictKeyExists(sdIgnoreDependencies, wzToken); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies."); + } + else + { + hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzToken, NULL); + ExitOnFailure(hr, "Failed to add \"%ls\" to the list of dependencies to ignore.", wzToken); + + hr = DictAddKey(sdIgnoreDependencies, wzToken); + ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzToken); + } + } + +LExit: + ReleaseDict(sdIgnoreDependencies); + + return hr; +} + +/******************************************************************** + JoinIgnoreDependencies - Joins a list of dependencies + to ignore into a semicolon-delimited string of unique values. + +*********************************************************************/ +static HRESULT JoinIgnoreDependencies( + __out_z LPWSTR* psczIgnoreDependencies, + __in_ecount(cDependencies) const DEPENDENCY* rgDependencies, + __in UINT cDependencies + ) +{ + HRESULT hr = S_OK; + STRINGDICT_HANDLE sdIgnoreDependencies = NULL; + + // Make sure we pass back an empty string if there are no dependencies. + if (0 == cDependencies) + { + ExitFunction1(hr = S_OK); + } + + // Create a dictionary to hold unique dependencies. + hr = DictCreateStringList(&sdIgnoreDependencies, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create the string dictionary."); + + for (UINT i = 0; i < cDependencies; ++i) + { + const DEPENDENCY* pDependency = &rgDependencies[i]; + + hr = DictKeyExists(sdIgnoreDependencies, pDependency->sczKey); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies."); + } + else + { + if (0 < i) + { + hr = StrAllocConcat(psczIgnoreDependencies, vcszIgnoreDependenciesDelim, 1); + ExitOnFailure(hr, "Failed to append the string delimiter."); + } + + hr = StrAllocConcat(psczIgnoreDependencies, pDependency->sczKey, 0); + ExitOnFailure(hr, "Failed to append the key \"%ls\".", pDependency->sczKey); + + hr = DictAddKey(sdIgnoreDependencies, pDependency->sczKey); + ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", pDependency->sczKey); + } + } + +LExit: + ReleaseDict(sdIgnoreDependencies); + + return hr; +} + +/******************************************************************** + GetIgnoredDependents - Combines the current bundle's + provider key, packages' provider keys that are being uninstalled, + and any ignored dependencies authored for packages into a string + list to pass to deputil. + +*********************************************************************/ +static HRESULT GetIgnoredDependents( + __in const BURN_PACKAGE* pPackage, + __in const BURN_PLAN* pPlan, + __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents + ) +{ + HRESULT hr = S_OK; + LPWSTR sczIgnoreDependencies = NULL; + + // Create the dictionary and add the bundle provider key initially. + hr = DictCreateStringList(psdIgnoredDependents, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create the string dictionary."); + + hr = DictAddKey(*psdIgnoredDependents, pPlan->wzBundleProviderKey); + ExitOnFailure(hr, "Failed to add the bundle provider key \"%ls\" to the list of ignored dependencies.", pPlan->wzBundleProviderKey); + + // Add previously planned package providers to the dictionary. + for (DWORD i = 0; i < pPlan->cPlannedProviders; ++i) + { + const DEPENDENCY* pDependency = &pPlan->rgPlannedProviders[i]; + + hr = DictAddKey(*psdIgnoredDependents, pDependency->sczKey); + ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the list of ignored dependencies.", pDependency->sczKey); + } + + // Get the IGNOREDEPENDENCIES property if defined. + hr = PackageGetProperty(pPackage, DEPENDENCY_IGNOREDEPENDENCIES, &sczIgnoreDependencies); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get the package property: %ls", DEPENDENCY_IGNOREDEPENDENCIES); + + hr = DependencyAddIgnoreDependencies(*psdIgnoredDependents, sczIgnoreDependencies); + ExitOnFailure(hr, "Failed to add the authored ignored dependencies to the cumulative list of ignored dependencies."); + } + else + { + hr = S_OK; + } + +LExit: + ReleaseStr(sczIgnoreDependencies); + + return hr; +} + +/******************************************************************** + GetProviderId - Gets the ID of the package given the provider key. + +*********************************************************************/ +static HRESULT GetProviderInformation( + __in HKEY hkRoot, + __in_z LPCWSTR wzProviderKey, + __deref_opt_out_z_opt LPWSTR* psczProviderKey, + __deref_opt_out_z_opt LPWSTR* psczId + ) +{ + HRESULT hr = S_OK; + LPWSTR sczId = NULL; + + hr = DepGetProviderInformation(hkRoot, wzProviderKey, &sczId, NULL, NULL); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get the provider key package id."); + + // If the id was registered return it and exit. + if (sczId && *sczId) + { + if (psczProviderKey) + { + hr = StrAllocString(psczProviderKey, wzProviderKey, 0); + ExitOnFailure(hr, "Failed to copy the provider key."); + } + + if (psczId) + { + *psczId = sczId; + sczId = NULL; + } + + ExitFunction(); + } + else + { + hr = E_NOTFOUND; + } + +LExit: + ReleaseStr(sczId); + + return hr; +} + +/******************************************************************** + CalculateDependencyActionStates - Calculates the dependency execute and + rollback actions for a package. + +*********************************************************************/ +static void CalculateDependencyActionStates( + __in const BURN_PACKAGE* pPackage, + __in const BOOTSTRAPPER_ACTION action, + __out BURN_DEPENDENCY_ACTION* pDependencyExecuteAction, + __out BURN_DEPENDENCY_ACTION* pDependencyRollbackAction + ) +{ + switch (action) + { + case BOOTSTRAPPER_ACTION_UNINSTALL: + // Always remove the dependency when uninstalling a bundle even if the package is absent. + *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_UNREGISTER; + break; + case BOOTSTRAPPER_ACTION_INSTALL: __fallthrough; + case BOOTSTRAPPER_ACTION_CACHE: + // Always remove the dependency during rollback when installing a bundle. + *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_UNREGISTER; + __fallthrough; + case BOOTSTRAPPER_ACTION_MODIFY: __fallthrough; + case BOOTSTRAPPER_ACTION_REPAIR: + switch (pPackage->execute) + { + case BOOTSTRAPPER_ACTION_STATE_NONE: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_NONE: + // Register if a newer, compatible package is already installed. + switch (pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: + if (!PackageProviderExists(pPackage)) + { + break; + } + __fallthrough; + case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: + *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER; + break; + } + break; + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + // Register if the package is requested but already installed. + switch (pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: + if (!PackageProviderExists(pPackage)) + { + break; + } + __fallthrough; + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: + *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER; + break; + } + break; + } + break; + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: + *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_UNREGISTER; + break; + case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough; + case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough; + case BOOTSTRAPPER_ACTION_STATE_REPAIR: __fallthrough; + case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: __fallthrough; + case BOOTSTRAPPER_ACTION_STATE_MAJOR_UPGRADE: __fallthrough; + case BOOTSTRAPPER_ACTION_STATE_PATCH: + *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER; + break; + } + break; + } + + switch (*pDependencyExecuteAction) + { + case BURN_DEPENDENCY_ACTION_REGISTER: + switch (pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough; + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough; + case BOOTSTRAPPER_PACKAGE_STATE_CACHED: + *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_UNREGISTER; + break; + } + break; + case BURN_DEPENDENCY_ACTION_UNREGISTER: + switch (pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: + *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_REGISTER; + break; + } + break; + } +} + +/******************************************************************** + AddPackageDependencyActions - Adds the dependency execute and rollback + actions to the plan. + +*********************************************************************/ +static HRESULT AddPackageDependencyActions( + __in_opt DWORD *pdwInsertSequence, + __in const BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in const BURN_DEPENDENCY_ACTION dependencyExecuteAction, + __in const BURN_DEPENDENCY_ACTION dependencyRollbackAction + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* pAction = NULL; + + // Add the rollback plan. + if (BURN_DEPENDENCY_ACTION_NONE != dependencyRollbackAction) + { + hr = PlanAppendRollbackAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append rollback action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY; + pAction->packageDependency.pPackage = const_cast(pPackage); + pAction->packageDependency.action = dependencyRollbackAction; + + hr = StrAllocString(&pAction->packageDependency.sczBundleProviderKey, pPlan->wzBundleProviderKey, 0); + ExitOnFailure(hr, "Failed to copy the bundle dependency provider."); + + // Put a checkpoint before the execute action so that rollback happens + // if execute fails. + hr = PlanExecuteCheckpoint(pPlan); + ExitOnFailure(hr, "Failed to plan dependency checkpoint action."); + } + + // Add the execute plan. This comes after rollback so if something goes wrong + // rollback will try to clean up after us correctly. + if (BURN_DEPENDENCY_ACTION_NONE != dependencyExecuteAction) + { + if (NULL != pdwInsertSequence) + { + hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction); + ExitOnFailure(hr, "Failed to insert execute action."); + + // Always move the sequence after this dependency action so the dependency registration + // stays in front of the inserted actions. + ++(*pdwInsertSequence); + } + else + { + hr = PlanAppendExecuteAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append execute action."); + } + + pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY; + pAction->packageDependency.pPackage = const_cast(pPackage); + pAction->packageDependency.action = dependencyExecuteAction; + + hr = StrAllocString(&pAction->packageDependency.sczBundleProviderKey, pPlan->wzBundleProviderKey, 0); + ExitOnFailure(hr, "Failed to copy the bundle dependency provider."); + } + +LExit: + return hr; +} + +static HRESULT RegisterPackageProvider( + __in const BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + LPWSTR wzId = NULL; + HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + + if (pPackage->rgDependencyProviders) + { + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + { + wzId = pPackage->Msi.sczProductCode; + } + else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + wzId = pPackage->Msp.sczPatchCode; + } + + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + + if (!pProvider->fImported) + { + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_REGISTER, pProvider->sczKey, pProvider->sczVersion, pPackage->sczId); + + hr = DepRegisterDependency(hkRoot, pProvider->sczKey, pProvider->sczVersion, pProvider->sczDisplayName, wzId, 0); + ExitOnFailure(hr, "Failed to register the package dependency provider: %ls", pProvider->sczKey); + } + } + } + +LExit: + if (!pPackage->fVital) + { + hr = S_OK; + } + + return hr; +} + +/******************************************************************** + UnregisterPackageProvider - Removes each dependency provider + for the package (if not imported from the package itself). + + Note: Does not check for existing dependents before removing the key. +*********************************************************************/ +static void UnregisterPackageProvider( + __in const BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + + if (pPackage->rgDependencyProviders) + { + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + + if (!pProvider->fImported) + { + hr = DepUnregisterDependency(hkRoot, pProvider->sczKey); + if (SUCCEEDED(hr)) + { + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED, pProvider->sczKey, pPackage->sczId); + } + else if (FAILED(hr) && E_FILENOTFOUND != hr) + { + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_FAILED, pProvider->sczKey, pPackage->sczId, hr); + } + } + } + } +} + +/******************************************************************** + RegisterPackageDependency - Registers the provider key + as a dependent of a package. + +*********************************************************************/ +static HRESULT RegisterPackageDependency( + __in BOOL fPerMachine, + __in const BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzDependentProviderKey + ) +{ + HRESULT hr = S_OK; + HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + + // Do not register a dependency on a package in a different install context. + if (fPerMachine != pPackage->fPerMachine) + { + LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine)); + ExitFunction1(hr = S_OK); + } + + if (pPackage->rgDependencyProviders) + { + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_REGISTER_DEPENDENCY, wzDependentProviderKey, pProvider->sczKey, pPackage->sczId); + + hr = DepRegisterDependent(hkRoot, pProvider->sczKey, wzDependentProviderKey, NULL, NULL, 0); + if (E_FILENOTFOUND != hr || pPackage->fVital) + { + ExitOnFailure(hr, "Failed to register the dependency on package dependency provider: %ls", pProvider->sczKey); + } + else + { + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_SKIP_MISSING, pProvider->sczKey, pPackage->sczId); + hr = S_OK; + } + } + } + +LExit: + return hr; +} + +/******************************************************************** + UnregisterPackageDependency - Unregisters the provider key + as a dependent of a package. + +*********************************************************************/ +static void UnregisterPackageDependency( + __in BOOL fPerMachine, + __in const BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzDependentProviderKey + ) +{ + HRESULT hr = S_OK; + HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + + // Should be no registration to remove since we don't write keys across contexts. + if (fPerMachine != pPackage->fPerMachine) + { + LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine)); + return; + } + + // Loop through each package provider and remove the bundle dependency key. + if (pPackage->rgDependencyProviders) + { + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + + hr = DepUnregisterDependent(hkRoot, pProvider->sczKey, wzDependentProviderKey); + if (SUCCEEDED(hr)) + { + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY, wzDependentProviderKey, pProvider->sczKey, pPackage->sczId); + } + else if (FAILED(hr) && E_FILENOTFOUND != hr) + { + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY_FAILED, wzDependentProviderKey, pProvider->sczKey, pPackage->sczId, hr); + } + } + } +} + +/******************************************************************** + PackageProviderExists - Checks if a package provider is registered. + +*********************************************************************/ +static BOOL PackageProviderExists( + __in const BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = DependencyDetectProviderKeyPackageId(pPackage, NULL, NULL); + return SUCCEEDED(hr); +} diff --git a/src/engine/dependency.h b/src/engine/dependency.h new file mode 100644 index 00000000..905857e0 --- /dev/null +++ b/src/engine/dependency.h @@ -0,0 +1,176 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +// constants + +const LPCWSTR DEPENDENCY_IGNOREDEPENDENCIES = L"IGNOREDEPENDENCIES"; + + +// function declarations + +/******************************************************************** + DependencyUninitialize - Frees and zeros memory allocated in the + dependency. + +*********************************************************************/ +void DependencyUninitialize( + __in BURN_DEPENDENCY_PROVIDER* pProvider + ); + +/******************************************************************** + DependencyParseProvidersFromXml - Parses dependency information + from the manifest for the specified package. + +*********************************************************************/ +HRESULT DependencyParseProvidersFromXml( + __in BURN_PACKAGE* pPackage, + __in IXMLDOMNode* pixnPackage + ); + +/******************************************************************** + DependencyDetectProviderKeyPackageId - Detect if the provider key is + registered and if so what package code is registered. + + Note: Returns E_NOTFOUND if the provider key is not registered. +*********************************************************************/ +HRESULT DependencyDetectProviderKeyPackageId( + __in const BURN_PACKAGE* pPackage, + __deref_opt_out_z_opt LPWSTR* psczProviderKey, + __deref_opt_out_z_opt LPWSTR* psczId + ); + +/******************************************************************** + DependencyDetectProviderKeyBundleId - Detect if the provider key is + registered and if so what bundle is registered. + + Note: Returns E_NOTFOUND if the provider key is not registered. +*********************************************************************/ +HRESULT DependencyDetectProviderKeyBundleId( + __in BURN_REGISTRATION* pRegistration + ); + +/******************************************************************** + DependencyPlanInitialize - Initializes the plan. + +*********************************************************************/ +HRESULT DependencyPlanInitialize( + __in const BURN_ENGINE_STATE* pEngineState, + __in BURN_PLAN* pPlan + ); + +/******************************************************************** + DependencyAllocIgnoreDependencies - Allocates the dependencies to + ignore as a semicolon-delimited string. + +*********************************************************************/ +HRESULT DependencyAllocIgnoreDependencies( + __in const BURN_PLAN *pPlan, + __out_z LPWSTR* psczIgnoreDependencies + ); + +/******************************************************************** + DependencyAddIgnoreDependencies - Populates the ignore dependency + names. + +*********************************************************************/ +HRESULT DependencyAddIgnoreDependencies( + __in STRINGDICT_HANDLE sdIgnoreDependencies, + __in_z LPCWSTR wzAddIgnoreDependencies + ); + +/******************************************************************** + DependencyDependentExists - Checks to see if the provider key is + already dependent on this bundle. + +*********************************************************************/ +BOOL DependencyDependentExists( + __in const BURN_REGISTRATION* pRegistration, + __in_z LPCWSTR wzDependentProviderKey + ); + +/******************************************************************** + DependencyPlanPackageBegin - Updates the dependency registration + action depending on the calculated state for the package. + +*********************************************************************/ +HRESULT DependencyPlanPackageBegin( + __in BOOL fPerMachine, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan + ); + +/******************************************************************** + DependencyPlanPackage - adds dependency related actions to the plan + for this package. + +*********************************************************************/ +HRESULT DependencyPlanPackage( + __in_opt DWORD *pdwInsertSequence, + __in const BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan + ); + +/******************************************************************** + DependencyPlanPackageComplete - Updates the dependency registration + action depending on the planned action for the package. + +*********************************************************************/ +HRESULT DependencyPlanPackageComplete( + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan + ); + +/******************************************************************** + DependencyExecutePackageProviderAction - Registers or unregisters + provider information for the package contained within the action. + +*********************************************************************/ +HRESULT DependencyExecutePackageProviderAction( + __in const BURN_EXECUTE_ACTION* pAction + ); + +/******************************************************************** + DependencyExecutePackageDependencyAction - Registers or unregisters + dependency information for the package contained within the action. + +*********************************************************************/ +HRESULT DependencyExecutePackageDependencyAction( + __in BOOL fPerMachine, + __in const BURN_EXECUTE_ACTION* pAction + ); + +/******************************************************************** + DependencyRegisterBundle - Registers the bundle dependency provider. + +*********************************************************************/ +HRESULT DependencyRegisterBundle( + __in const BURN_REGISTRATION* pRegistration + ); + +/******************************************************************** + DependencyProcessDependentRegistration - Registers or unregisters dependents + on the bundle based on the action. + +*********************************************************************/ +HRESULT DependencyProcessDependentRegistration( + __in const BURN_REGISTRATION* pRegistration, + __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction + ); + +/******************************************************************** + DependencyUnregisterBundle - Removes the bundle dependency provider. + + Note: Does not check for existing dependents before removing the key. +*********************************************************************/ +void DependencyUnregisterBundle( + __in const BURN_REGISTRATION* pRegistration + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp new file mode 100644 index 00000000..7953daf5 --- /dev/null +++ b/src/engine/detect.cpp @@ -0,0 +1,431 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +typedef struct _DETECT_AUTHENTICATION_REQUIRED_DATA +{ + BURN_USER_EXPERIENCE* pUX; + LPCWSTR wzPackageOrContainerId; +} DETECT_AUTHENTICATION_REQUIRED_DATA; + +// internal function definitions +static HRESULT AuthenticationRequired( + __in LPVOID pData, + __in HINTERNET hUrl, + __in long lHttpCode, + __out BOOL* pfRetrySend, + __out BOOL* pfRetry + ); + +static HRESULT DetectAtomFeedUpdate( + __in_z LPCWSTR wzBundleId, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_UPDATE* pUpdate + ); + +static HRESULT DownloadUpdateFeed( + __in_z LPCWSTR wzBundleId, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_UPDATE* pUpdate, + __deref_inout_z LPWSTR* psczTempFile + ); + +// function definitions + +extern "C" void DetectReset( + __in BURN_REGISTRATION* pRegistration, + __in BURN_PACKAGES* pPackages + ) +{ + RelatedBundlesUninitialize(&pRegistration->relatedBundles); + ReleaseNullStr(pRegistration->sczDetectedProviderKeyBundleId); + pRegistration->fEnabledForwardCompatibleBundle = FALSE; + PackageUninitialize(&pRegistration->forwardCompatibleBundle); + + for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage) + { + BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage; + + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; + + pPackage->cache = BURN_CACHE_STATE_NONE; + for (DWORD iPayload = 0; iPayload < pPackage->cPayloads; ++iPayload) + { + BURN_PACKAGE_PAYLOAD* pPayload = pPackage->rgPayloads + iPayload; + pPayload->fCached = FALSE; + } + + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + { + for (DWORD iFeature = 0; iFeature < pPackage->Msi.cFeatures; ++iFeature) + { + BURN_MSIFEATURE* pFeature = pPackage->Msi.rgFeatures + iFeature; + + pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; + } + + pPackage->Msi.fCompatibleInstalled = FALSE; + } + else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + ReleaseNullMem(pPackage->Msp.rgTargetProducts); + pPackage->Msp.cTargetProductCodes = 0; + } + } + + for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo) + { + MSIPATCHSEQUENCEINFOW* pPatchInfo = pPackages->rgPatchInfo + iPatchInfo; + pPatchInfo->dwOrder = 0; + pPatchInfo->uStatus = 0; + } +} + +extern "C" HRESULT DetectForwardCompatibleBundle( + __in BURN_USER_EXPERIENCE* pUX, + __in BOOTSTRAPPER_COMMAND* pCommand, + __in BURN_REGISTRATION* pRegistration + ) +{ + HRESULT hr = S_OK; + BOOL fRecommendIgnore = TRUE; + BOOL fIgnoreBundle = FALSE; + + if (pRegistration->sczDetectedProviderKeyBundleId && + CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRegistration->sczId, -1)) + { + // Only change the recommendation if an active parent was provided. + if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent) + { + // On install, recommend running the forward compatible bundle because there is an active parent. This + // will essentially register the parent with the forward compatible bundle. + if (BOOTSTRAPPER_ACTION_INSTALL == pCommand->action) + { + fRecommendIgnore = FALSE; + } + else if (BOOTSTRAPPER_ACTION_UNINSTALL == pCommand->action || + BOOTSTRAPPER_ACTION_MODIFY == pCommand->action || + BOOTSTRAPPER_ACTION_REPAIR == pCommand->action) + { + // When modifying the bundle, only recommend running the forward compatible bundle if the parent + // is already registered as a dependent of the provider key. + if (DependencyDependentExists(pRegistration, pRegistration->sczActiveParent)) + { + fRecommendIgnore = FALSE; + } + } + } + + for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) + { + BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle; + fIgnoreBundle = fRecommendIgnore; + + if (BOOTSTRAPPER_RELATION_UPGRADE == pRelatedBundle->relationType && + pRegistration->qwVersion <= pRelatedBundle->qwVersion && + CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRelatedBundle->package.sczId, -1)) + { + hr = UserExperienceOnDetectForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->qwVersion, &fIgnoreBundle); + ExitOnRootFailure(hr, "BA aborted detect forward compatible bundle."); + + if (!fIgnoreBundle) + { + hr = PseudoBundleInitializePassthrough(&pRegistration->forwardCompatibleBundle, pCommand, NULL, pRegistration->sczActiveParent, pRegistration->sczAncestors, &pRelatedBundle->package); + ExitOnFailure(hr, "Failed to initialize update bundle."); + + pRegistration->fEnabledForwardCompatibleBundle = TRUE; + } + + LogId(REPORT_STANDARD, MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), LoggingVersionToString(pRelatedBundle->qwVersion), LoggingBoolToString(pRegistration->fEnabledForwardCompatibleBundle)); + break; + } + } + } + +LExit: + return hr; +} + +extern "C" HRESULT DetectReportRelatedBundles( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_REGISTRATION* pRegistration, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in BOOTSTRAPPER_ACTION action + ) +{ + HRESULT hr = S_OK; + + for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) + { + const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle; + BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE; + + switch (pRelatedBundle->relationType) + { + case BOOTSTRAPPER_RELATION_UPGRADE: + if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < action) + { + if (pRegistration->qwVersion > pRelatedBundle->qwVersion) + { + operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE; + } + else if (pRegistration->qwVersion < pRelatedBundle->qwVersion) + { + operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; + } + } + break; + + case BOOTSTRAPPER_RELATION_PATCH: __fallthrough; + case BOOTSTRAPPER_RELATION_ADDON: + if (BOOTSTRAPPER_ACTION_UNINSTALL == action) + { + operation = BOOTSTRAPPER_RELATED_OPERATION_REMOVE; + } + else if (BOOTSTRAPPER_ACTION_INSTALL == action || BOOTSTRAPPER_ACTION_MODIFY == action) + { + operation = BOOTSTRAPPER_RELATED_OPERATION_INSTALL; + } + else if (BOOTSTRAPPER_ACTION_REPAIR == action) + { + operation = BOOTSTRAPPER_RELATED_OPERATION_REPAIR; + } + break; + + case BOOTSTRAPPER_RELATION_DETECT: __fallthrough; + case BOOTSTRAPPER_RELATION_DEPENDENT: + break; + + default: + hr = E_FAIL; + ExitOnRootFailure(hr, "Unexpected relation type encountered: %d", pRelatedBundle->relationType); + break; + } + + LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), LoggingVersionToString(pRelatedBundle->qwVersion), LoggingRelatedOperationToString(operation)); + + hr = UserExperienceOnDetectRelatedBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->qwVersion, operation); + ExitOnRootFailure(hr, "BA aborted detect related bundle."); + } + +LExit: + return hr; +} + +extern "C" HRESULT DetectUpdate( + __in_z LPCWSTR wzBundleId, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_UPDATE* pUpdate + ) +{ + HRESULT hr = S_OK; + BOOL fBeginCalled = FALSE; + BOOL fSkip = TRUE; + BOOL fIgnoreError = FALSE; + + // If no update source was specified, skip update detection. + if (!pUpdate->sczUpdateSource || !*pUpdate->sczUpdateSource) + { + ExitFunction(); + } + + fBeginCalled = TRUE; + hr = UserExperienceOnDetectUpdateBegin(pUX, pUpdate->sczUpdateSource, &fSkip); + ExitOnRootFailure(hr, "BA aborted detect update begin."); + + if (!fSkip) + { + hr = DetectAtomFeedUpdate(wzBundleId, pUX, pUpdate); + ExitOnFailure(hr, "Failed to detect atom feed update."); + } + +LExit: + if (fBeginCalled) + { + UserExperienceOnDetectUpdateComplete(pUX, hr, &fIgnoreError); + if (fIgnoreError) + { + hr = S_OK; + } + } + + return hr; +} + +static HRESULT AuthenticationRequired( + __in LPVOID pData, + __in HINTERNET hUrl, + __in long lHttpCode, + __out BOOL* pfRetrySend, + __out BOOL* pfRetry + ) +{ + Assert(401 == lHttpCode || 407 == lHttpCode); + + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + BOOTSTRAPPER_ERROR_TYPE errorType = (401 == lHttpCode) ? BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER : BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY; + LPWSTR sczError = NULL; + DETECT_AUTHENTICATION_REQUIRED_DATA* pAuthenticationData = reinterpret_cast(pData); + int nResult = IDNOACTION; + + *pfRetrySend = FALSE; + *pfRetry = FALSE; + + hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL); + ExitOnFailure(hr, "Failed to allocation error string."); + + UserExperienceOnError(pAuthenticationData->pUX, errorType, pAuthenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value. + nResult = UserExperienceCheckExecuteResult(pAuthenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult); + if (IDTRYAGAIN == nResult && pAuthenticationData->pUX->hwndDetect) + { + er = ::InternetErrorDlg(pAuthenticationData->pUX->hwndDetect, hUrl, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL); + if (ERROR_SUCCESS == er || ERROR_CANCELLED == er) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + else if (ERROR_INTERNET_FORCE_RETRY == er) + { + *pfRetrySend = TRUE; + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); + } + } + else if (IDRETRY == nResult) + { + *pfRetry = TRUE; + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); + } + +LExit: + ReleaseStr(sczError); + + return hr; +} + +static HRESULT DownloadUpdateFeed( + __in_z LPCWSTR wzBundleId, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_UPDATE* pUpdate, + __deref_inout_z LPWSTR* psczTempFile + ) +{ + HRESULT hr = S_OK; + DOWNLOAD_SOURCE downloadSource = { }; + DOWNLOAD_CACHE_CALLBACK cacheCallback = { }; + DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { }; + DETECT_AUTHENTICATION_REQUIRED_DATA authenticationData = { }; + LPWSTR sczUpdateId = NULL; + LPWSTR sczError = NULL; + DWORD64 qwDownloadSize = 0; + + // Always do our work in the working folder, even if cached. + hr = PathCreateTimeBasedTempFile(NULL, L"UpdateFeed", NULL, L"xml", psczTempFile, NULL); + ExitOnFailure(hr, "Failed to create UpdateFeed based on current system time."); + + // Do we need a means of the BA to pass in a user name and password? If so, we should copy it to downloadSource here + hr = StrAllocString(&downloadSource.sczUrl, pUpdate->sczUpdateSource, 0); + ExitOnFailure(hr, "Failed to copy update url."); + + cacheCallback.pfnProgress = NULL; //UpdateProgressRoutine; + cacheCallback.pfnCancel = NULL; // TODO: set this + cacheCallback.pv = NULL; //pProgress; + + authenticationData.pUX = pUX; + authenticationData.wzPackageOrContainerId = wzBundleId; + + authenticationCallback.pv = static_cast(&authenticationData); + authenticationCallback.pfnAuthenticate = &AuthenticationRequired; + + hr = DownloadUrl(&downloadSource, qwDownloadSize, *psczTempFile, &cacheCallback, &authenticationCallback); + ExitOnFailure(hr, "Failed attempt to download update feed from URL: '%ls' to: '%ls'", downloadSource.sczUrl, *psczTempFile); + +LExit: + if (FAILED(hr)) + { + if (*psczTempFile) + { + FileEnsureDelete(*psczTempFile); + } + + ReleaseNullStr(*psczTempFile); + } + + ReleaseStr(downloadSource.sczUrl); + ReleaseStr(downloadSource.sczUser); + ReleaseStr(downloadSource.sczPassword); + ReleaseStr(sczUpdateId); + ReleaseStr(sczError); + return hr; +} + + +static HRESULT DetectAtomFeedUpdate( + __in_z LPCWSTR wzBundleId, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_UPDATE* pUpdate + ) +{ + Assert(pUpdate && pUpdate->sczUpdateSource && *pUpdate->sczUpdateSource); +#ifdef DEBUG + LogStringLine(REPORT_STANDARD, "DetectAtomFeedUpdate() - update location: %ls", pUpdate->sczUpdateSource); +#endif + + + HRESULT hr = S_OK; + LPWSTR sczUpdateFeedTempFile = NULL; + ATOM_FEED* pAtomFeed = NULL; + APPLICATION_UPDATE_CHAIN* pApupChain = NULL; + BOOL fStopProcessingUpdates = FALSE; + + hr = AtomInitialize(); + ExitOnFailure(hr, "Failed to initialize Atom."); + + hr = DownloadUpdateFeed(wzBundleId, pUX, pUpdate, &sczUpdateFeedTempFile); + ExitOnFailure(hr, "Failed to download update feed."); + + hr = AtomParseFromFile(sczUpdateFeedTempFile, &pAtomFeed); + ExitOnFailure(hr, "Failed to parse update atom feed: %ls.", sczUpdateFeedTempFile); + + hr = ApupAllocChainFromAtom(pAtomFeed, &pApupChain); + ExitOnFailure(hr, "Failed to allocate update chain from atom feed."); + + if (0 < pApupChain->cEntries) + { + for (DWORD i = 0; i < pApupChain->cEntries; ++i) + { + APPLICATION_UPDATE_ENTRY* pAppUpdateEntry = &pApupChain->rgEntries[i]; + + hr = UserExperienceOnDetectUpdate(pUX, pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->wzUrl : NULL, + pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->dw64Size : 0, + pAppUpdateEntry->dw64Version, pAppUpdateEntry->wzTitle, + pAppUpdateEntry->wzSummary, pAppUpdateEntry->wzContentType, pAppUpdateEntry->wzContent, &fStopProcessingUpdates); + ExitOnRootFailure(hr, "BA aborted detect update."); + + if (fStopProcessingUpdates) + { + break; + } + } + } + +LExit: + if (sczUpdateFeedTempFile && *sczUpdateFeedTempFile) + { + FileEnsureDelete(sczUpdateFeedTempFile); + } + + ApupFreeChain(pApupChain); + AtomFreeFeed(pAtomFeed); + ReleaseStr(sczUpdateFeedTempFile); + AtomUninitialize(); + + return hr; +} diff --git a/src/engine/detect.h b/src/engine/detect.h new file mode 100644 index 00000000..01488f1a --- /dev/null +++ b/src/engine/detect.h @@ -0,0 +1,44 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + + +// structs + + +// functions + +void DetectReset( + __in BURN_REGISTRATION* pRegistration, + __in BURN_PACKAGES* pPackages + ); + +HRESULT DetectForwardCompatibleBundle( + __in BURN_USER_EXPERIENCE* pUX, + __in BOOTSTRAPPER_COMMAND* pCommand, + __in BURN_REGISTRATION* pRegistration + ); + +HRESULT DetectReportRelatedBundles( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_REGISTRATION* pRegistration, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in BOOTSTRAPPER_ACTION action + ); + +HRESULT DetectUpdate( + __in_z LPCWSTR wzBundleId, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_UPDATE* pUpdate + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp new file mode 100644 index 00000000..1b9336e0 --- /dev/null +++ b/src/engine/elevation.cpp @@ -0,0 +1,2814 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +const DWORD BURN_TIMEOUT = 5 * 60 * 1000; // TODO: is 5 minutes good? + +typedef enum _BURN_ELEVATION_MESSAGE_TYPE +{ + BURN_ELEVATION_MESSAGE_TYPE_UNKNOWN, + BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE, + BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE, + BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN, + BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME, + BURN_ELEVATION_MESSAGE_TYPE_SESSION_END, + BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE, + BURN_ELEVATION_MESSAGE_TYPE_LAYOUT_BUNDLE, + BURN_ELEVATION_MESSAGE_TYPE_CACHE_OR_LAYOUT_CONTAINER_OR_PAYLOAD, + BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP, + BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION, + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE, + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE, + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE, + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE, + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER, + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY, + BURN_ELEVATION_MESSAGE_TYPE_LOAD_COMPATIBLE_PACKAGE, + BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_EMBEDDED_CHILD, + BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE, + BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE, + + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS, + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR, + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE, + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE, + BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID, + + BURN_ELEVATION_TRANSACTION_BEGIN, + BURN_ELEVATION_TRANSACTION_COMMIT, + BURN_ELEVATION_TRANSACTION_ROLLBACK + +} BURN_ELEVATION_MESSAGE_TYPE; + + +// struct + +typedef struct _BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT +{ + PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler; + LPVOID pvContext; +} BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT; + +typedef struct _BURN_ELEVATION_MSI_MESSAGE_CONTEXT +{ + PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler; + LPVOID pvContext; +} BURN_ELEVATION_MSI_MESSAGE_CONTEXT; + +typedef struct _BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT +{ + DWORD dwProcessId; +} BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT; + +typedef struct _BURN_ELEVATION_CHILD_MESSAGE_CONTEXT +{ + DWORD dwLoggingTlsId; + HANDLE hPipe; + HANDLE* phLock; + BOOL* pfDisabledAutomaticUpdates; + BURN_APPROVED_EXES* pApprovedExes; + BURN_CONTAINERS* pContainers; + BURN_PACKAGES* pPackages; + BURN_PAYLOADS* pPayloads; + BURN_VARIABLES* pVariables; + BURN_REGISTRATION* pRegistration; + BURN_USER_EXPERIENCE* pUserExperience; + + MSIHANDLE hMsiTrns; + HANDLE hMsiTrnsEvent; +} BURN_ELEVATION_CHILD_MESSAGE_CONTEXT; + + +// internal function declarations + +static HRESULT OnMsiBeginTransaction( + __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext +); +static HRESULT OnMsiCommitTransaction( + __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext +); +static HRESULT OnMsiRollbackTransaction( + __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext +); + +static DWORD WINAPI ElevatedChildCacheThreadProc( + __in LPVOID lpThreadParameter + ); +static HRESULT WaitForElevatedChildCacheThread( + __in HANDLE hCacheThread, + __in DWORD dwExpectedExitCode + ); +static HRESULT OnLoadCompatiblePackage( + __in BURN_PACKAGES* pPackages, + __in BYTE* pbData, + __in DWORD cbData + ); +static HRESULT ProcessGenericExecuteMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); +static HRESULT ProcessMsiPackageMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); +static HRESULT ProcessLaunchApprovedExeMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); +static HRESULT ProcessElevatedChildMessage( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); +static HRESULT ProcessElevatedChildCacheMessage( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); +static HRESULT ProcessResult( + __in DWORD dwResult, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +static HRESULT OnApplyInitialize( + __in BURN_VARIABLES* pVariables, + __in BURN_REGISTRATION* pRegistration, + __in HANDLE* phLock, + __in BOOL* pfDisabledWindowsUpdate, + __in BYTE* pbData, + __in DWORD cbData + ); +static HRESULT OnApplyUninitialize( + __in HANDLE* phLock + ); +static HRESULT OnSessionBegin( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BYTE* pbData, + __in DWORD cbData + ); +static HRESULT OnSessionResume( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in DWORD cbData + ); +static HRESULT OnSessionEnd( + __in BURN_REGISTRATION* pRegistration, + __in BYTE* pbData, + __in DWORD cbData + ); +static HRESULT OnSaveState( + __in BURN_REGISTRATION* pRegistration, + __in BYTE* pbData, + __in DWORD cbData + ); +static HRESULT OnLayoutBundle( + __in_z LPCWSTR wzExecutableName, + __in BYTE* pbData, + __in DWORD cbData + ); +static HRESULT OnCacheOrLayoutContainerOrPayload( + __in BURN_CONTAINERS* pContainers, + __in BURN_PACKAGES* pPackages, + __in BURN_PAYLOADS* pPayloads, + __in BYTE* pbData, + __in DWORD cbData + ); +static void OnCacheCleanup( + __in_z LPCWSTR wzBundleId + ); +static HRESULT OnProcessDependentRegistration( + __in const BURN_REGISTRATION* pRegistration, + __in BYTE* pbData, + __in DWORD cbData + ); +static HRESULT OnExecuteExePackage( + __in HANDLE hPipe, + __in BURN_PACKAGES* pPackages, + __in BURN_RELATED_BUNDLES* pRelatedBundles, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in DWORD cbData + ); +static HRESULT OnExecuteMsiPackage( + __in HANDLE hPipe, + __in BURN_PACKAGES* pPackages, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in DWORD cbData + ); +static HRESULT OnExecuteMspPackage( + __in HANDLE hPipe, + __in BURN_PACKAGES* pPackages, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in DWORD cbData + ); +static HRESULT OnExecuteMsuPackage( + __in HANDLE hPipe, + __in BURN_PACKAGES* pPackages, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in DWORD cbData + ); +static HRESULT OnExecutePackageProviderAction( + __in BURN_PACKAGES* pPackages, + __in BURN_RELATED_BUNDLES* pRelatedBundles, + __in BYTE* pbData, + __in DWORD cbData + ); +static HRESULT OnExecutePackageDependencyAction( + __in BURN_PACKAGES* pPackages, + __in BURN_RELATED_BUNDLES* pRelatedBundles, + __in BYTE* pbData, + __in DWORD cbData + ); +static int GenericExecuteMessageHandler( + __in GENERIC_EXECUTE_MESSAGE* pMessage, + __in LPVOID pvContext + ); +static int MsiExecuteMessageHandler( + __in WIU_MSI_EXECUTE_MESSAGE* pMessage, + __in_opt LPVOID pvContext + ); +static HRESULT OnCleanPackage( + __in BURN_PACKAGES* pPackages, + __in BYTE* pbData, + __in DWORD cbData + ); +static HRESULT OnLaunchApprovedExe( + __in HANDLE hPipe, + __in BURN_APPROVED_EXES* pApprovedExes, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in DWORD cbData + ); + + +// function definitions + +extern "C" HRESULT ElevationElevate( + __in BURN_ENGINE_STATE* pEngineState, + __in_opt HWND hwndParent + ) +{ + Assert(BURN_MODE_ELEVATED != pEngineState->mode); + Assert(!pEngineState->companionConnection.sczName); + Assert(!pEngineState->companionConnection.sczSecret); + Assert(!pEngineState->companionConnection.hProcess); + Assert(!pEngineState->companionConnection.dwProcessId); + Assert(INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe); + Assert(INVALID_HANDLE_VALUE == pEngineState->companionConnection.hCachePipe); + + HRESULT hr = S_OK; + int nResult = IDOK; + HANDLE hPipesCreatedEvent = INVALID_HANDLE_VALUE; + + hr = UserExperienceOnElevateBegin(&pEngineState->userExperience); + ExitOnRootFailure(hr, "BA aborted elevation requirement."); + + hr = PipeCreateNameAndSecret(&pEngineState->companionConnection.sczName, &pEngineState->companionConnection.sczSecret); + ExitOnFailure(hr, "Failed to create pipe name and client token."); + + hr = PipeCreatePipes(&pEngineState->companionConnection, TRUE, &hPipesCreatedEvent); + ExitOnFailure(hr, "Failed to create pipe and cache pipe."); + + LogId(REPORT_STANDARD, MSG_LAUNCH_ELEVATED_ENGINE_STARTING); + + do + { + nResult = IDOK; + + // Create the elevated process and if successful, wait for it to connect. + hr = PipeLaunchChildProcess(pEngineState->sczBundleEngineWorkingPath, &pEngineState->companionConnection, TRUE, hwndParent); + if (SUCCEEDED(hr)) + { + LogId(REPORT_STANDARD, MSG_LAUNCH_ELEVATED_ENGINE_SUCCESS); + + hr = PipeWaitForChildConnect(&pEngineState->companionConnection); + ExitOnFailure(hr, "Failed to connect to elevated child process."); + + LogId(REPORT_STANDARD, MSG_CONNECT_TO_ELEVATED_ENGINE_SUCCESS); + } + else if (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr) + { + // The user clicked "Cancel" on the elevation prompt or the elevation prompt timed out, provide the notification with the option to retry. + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + nResult = UserExperienceSendError(&pEngineState->userExperience, BOOTSTRAPPER_ERROR_TYPE_ELEVATE, NULL, hr, NULL, MB_ICONERROR | MB_RETRYCANCEL, IDNOACTION); + } + } while (IDRETRY == nResult); + ExitOnFailure(hr, "Failed to elevate."); + +LExit: + ReleaseHandle(hPipesCreatedEvent); + + if (FAILED(hr)) + { + PipeConnectionUninitialize(&pEngineState->companionConnection); + } + + UserExperienceOnElevateComplete(&pEngineState->userExperience, hr); + + return hr; +} + +extern "C" HRESULT ElevationApplyInitialize( + __in HANDLE hPipe, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_ACTION action, + __in BURN_AU_PAUSE_ACTION auAction, + __in BOOL fTakeSystemRestorePoint + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + + // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)action); + ExitOnFailure(hr, "Failed to write action to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)auAction); + ExitOnFailure(hr, "Failed to write update action to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fTakeSystemRestorePoint); + ExitOnFailure(hr, "Failed to write system restore point action to message buffer."); + + hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); + ExitOnFailure(hr, "Failed to write variables."); + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +extern "C" HRESULT ElevationApplyUninitialize( + __in HANDLE hPipe + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationSessionBegin - + +*******************************************************************/ +extern "C" HRESULT ElevationSessionBegin( + __in HANDLE hPipe, + __in_z LPCWSTR wzEngineWorkingPath, + __in_z LPCWSTR wzResumeCommandLine, + __in BOOL fDisableResume, + __in BURN_VARIABLES* pVariables, + __in DWORD dwRegistrationOperations, + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction, + __in DWORD64 qwEstimatedSize + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + + // serialize message data + hr = BuffWriteString(&pbData, &cbData, wzEngineWorkingPath); + ExitOnFailure(hr, "Failed to write engine working path to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, wzResumeCommandLine); + ExitOnFailure(hr, "Failed to write resume command line to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, fDisableResume); + ExitOnFailure(hr, "Failed to write resume flag."); + + hr = BuffWriteNumber(&pbData, &cbData, dwRegistrationOperations); + ExitOnFailure(hr, "Failed to write registration operations to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)dependencyRegistrationAction); + ExitOnFailure(hr, "Failed to write dependency registration action to message buffer."); + + hr = BuffWriteNumber64(&pbData, &cbData, qwEstimatedSize); + ExitOnFailure(hr, "Failed to write estimated size to message buffer."); + + hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); + ExitOnFailure(hr, "Failed to write variables."); + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationSessionResume - + +*******************************************************************/ +extern "C" HRESULT ElevationSessionResume( + __in HANDLE hPipe, + __in_z LPCWSTR wzResumeCommandLine, + __in BOOL fDisableResume, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + + // serialize message data + hr = BuffWriteString(&pbData, &cbData, wzResumeCommandLine); + ExitOnFailure(hr, "Failed to write resume command line to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, fDisableResume); + ExitOnFailure(hr, "Failed to write resume flag."); + + hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); + ExitOnFailure(hr, "Failed to write variables."); + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationSessionEnd - + +*******************************************************************/ +extern "C" HRESULT ElevationSessionEnd( + __in HANDLE hPipe, + __in BURN_RESUME_MODE resumeMode, + __in BOOTSTRAPPER_APPLY_RESTART restart, + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + + // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)resumeMode); + ExitOnFailure(hr, "Failed to write resume mode to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)restart); + ExitOnFailure(hr, "Failed to write restart enum to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)dependencyRegistrationAction); + ExitOnFailure(hr, "Failed to write dependency registration action to message buffer."); + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SESSION_END, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationSaveState - + +*******************************************************************/ +HRESULT ElevationSaveState( + __in HANDLE hPipe, + __in_bcount(cbBuffer) BYTE* pbBuffer, + __in SIZE_T cbBuffer + ) +{ + HRESULT hr = S_OK; + DWORD dwResult = 0; + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE, pbBuffer, (DWORD)cbBuffer, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + return hr; +} + +/******************************************************************* + ElevationLayoutBundle - + +*******************************************************************/ +extern "C" HRESULT ElevationLayoutBundle( + __in HANDLE hPipe, + __in_z LPCWSTR wzLayoutDirectory, + __in_z LPCWSTR wzUnverifiedPath + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + + // serialize message data + hr = BuffWriteString(&pbData, &cbData, wzLayoutDirectory); + ExitOnFailure(hr, "Failed to write layout directory to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, wzUnverifiedPath); + ExitOnFailure(hr, "Failed to write payload unverified path to message buffer."); + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LAYOUT_BUNDLE, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LAYOUT_BUNDLE message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationCacheOrLayoutPayload - + +*******************************************************************/ +extern "C" HRESULT ElevationCacheOrLayoutContainerOrPayload( + __in HANDLE hPipe, + __in_opt BURN_CONTAINER* pContainer, + __in_opt BURN_PACKAGE* pPackage, + __in_opt BURN_PAYLOAD* pPayload, + __in_z_opt LPCWSTR wzLayoutDirectory, + __in_z LPCWSTR wzUnverifiedPath, + __in BOOL fMove + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + + // serialize message data + hr = BuffWriteString(&pbData, &cbData, pContainer ? pContainer->sczId : NULL); + ExitOnFailure(hr, "Failed to write container id to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pPackage ? pPackage->sczId : NULL); + ExitOnFailure(hr, "Failed to write package id to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pPayload ? pPayload->sczKey : NULL); + ExitOnFailure(hr, "Failed to write payload id to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, wzLayoutDirectory); + ExitOnFailure(hr, "Failed to write layout directory to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, wzUnverifiedPath); + ExitOnFailure(hr, "Failed to write unverified path to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fMove); + ExitOnFailure(hr, "Failed to write move flag to message buffer."); + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_OR_LAYOUT_CONTAINER_OR_PAYLOAD, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_OR_LAYOUT_CONTAINER_OR_PAYLOAD message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationCacheCleanup - + +*******************************************************************/ +extern "C" HRESULT ElevationCacheCleanup( + __in HANDLE hPipe + ) +{ + HRESULT hr = S_OK; + DWORD dwResult = 0; + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP, NULL, 0, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + return hr; +} + +extern "C" HRESULT ElevationProcessDependentRegistration( + __in HANDLE hPipe, + __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + + // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, pAction->type); + ExitOnFailure(hr, "Failed to write action type to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pAction->sczBundleId); + ExitOnFailure(hr, "Failed to write bundle id to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pAction->sczDependentProviderKey); + ExitOnFailure(hr, "Failed to write dependent provider key to message buffer."); + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationExecuteExePackage - + +*******************************************************************/ +extern "C" HRESULT ElevationExecuteExePackage( + __in HANDLE hPipe, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT context = { }; + DWORD dwResult = 0; + + // serialize message data + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.pPackage->sczId); + ExitOnFailure(hr, "Failed to write package id to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->exePackage.action); + ExitOnFailure(hr, "Failed to write action to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, fRollback); + ExitOnFailure(hr, "Failed to write rollback."); + + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.sczIgnoreDependencies); + ExitOnFailure(hr, "Failed to write the list of dependencies to ignore to the message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.sczAncestors); + ExitOnFailure(hr, "Failed to write the list of ancestors to the message buffer."); + + hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); + ExitOnFailure(hr, "Failed to write variables."); + + // send message + context.pfnGenericMessageHandler = pfnGenericMessageHandler; + context.pvContext = pvContext; + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE, pbData, cbData, ProcessGenericExecuteMessages, &context, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE message to per-machine process."); + + hr = ProcessResult(dwResult, pRestart); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +extern "C" HRESULT ElevationMsiBeginTransaction( + __in HANDLE hPipe, + __in_opt HWND hwndParent, + __in LPVOID pvContext +) +{ + UNREFERENCED_PARAMETER(hwndParent); + HRESULT hr = S_OK; + BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = {}; + DWORD dwResult = ERROR_SUCCESS; + + context.pvContext = pvContext; + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_TRANSACTION_BEGIN, NULL, 0, NULL, &context, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process."); + ExitOnWin32Error(dwResult, hr, "Failed beginning an elevated MSI transaction"); + +LExit: + return hr; +} + +extern "C" HRESULT ElevationMsiCommitTransaction( + __in HANDLE hPipe, + __in_opt HWND hwndParent, + __in LPVOID pvContext +) +{ + UNREFERENCED_PARAMETER(hwndParent); + HRESULT hr = S_OK; + BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = {}; + DWORD dwResult = ERROR_SUCCESS; + + context.pvContext = pvContext; + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_TRANSACTION_COMMIT, NULL, 0, NULL, &context, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process."); + ExitOnWin32Error(dwResult, hr, "Failed committing an elevated MSI transaction"); + +LExit: + return hr; +} + +extern "C" HRESULT ElevationMsiRollbackTransaction( + __in HANDLE hPipe, + __in_opt HWND hwndParent, + __in LPVOID pvContext +) +{ + UNREFERENCED_PARAMETER(hwndParent); + HRESULT hr = S_OK; + BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = {}; + DWORD dwResult = ERROR_SUCCESS; + + context.pvContext = pvContext; + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_TRANSACTION_ROLLBACK, NULL, 0, NULL, &context, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process."); + ExitOnWin32Error(dwResult, hr, "Failed rolling back an elevated MSI transaction"); + +LExit: + return hr; +} + + + +/******************************************************************* + ElevationExecuteMsiPackage - + +*******************************************************************/ +extern "C" HRESULT ElevationExecuteMsiPackage( + __in HANDLE hPipe, + __in_opt HWND hwndParent, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = { }; + DWORD dwResult = 0; + + // serialize message data + // TODO: for patching we might not have a package + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msiPackage.pPackage->sczId); + ExitOnFailure(hr, "Failed to write package id to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)hwndParent); + ExitOnFailure(hr, "Failed to write parent hwnd to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msiPackage.sczLogPath); + ExitOnFailure(hr, "Failed to write package log to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.uiLevel); + ExitOnFailure(hr, "Failed to write UI level to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.action); + ExitOnFailure(hr, "Failed to write action to message buffer."); + + // Feature actions. + for (DWORD i = 0; i < pExecuteAction->msiPackage.pPackage->Msi.cFeatures; ++i) + { + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.rgFeatures[i]); + ExitOnFailure(hr, "Failed to write feature action to message buffer."); + } + + // Slipstream patches actions. + for (DWORD i = 0; i < pExecuteAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++i) + { + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.rgSlipstreamPatches[i]); + ExitOnFailure(hr, "Failed to write slipstream patch action to message buffer."); + } + + hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); + ExitOnFailure(hr, "Failed to write variables."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fRollback); + ExitOnFailure(hr, "Failed to write rollback flag to message buffer."); + + + // send message + context.pfnMessageHandler = pfnMessageHandler; + context.pvContext = pvContext; + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE, pbData, cbData, ProcessMsiPackageMessages, &context, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process."); + + hr = ProcessResult(dwResult, pRestart); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationExecuteMspPackage - + +*******************************************************************/ +extern "C" HRESULT ElevationExecuteMspPackage( + __in HANDLE hPipe, + __in_opt HWND hwndParent, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = { }; + DWORD dwResult = 0; + + // serialize message data + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.pPackage->sczId); + ExitOnFailure(hr, "Failed to write package id to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)hwndParent); + ExitOnFailure(hr, "Failed to write parent hwnd to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.sczTargetProductCode); + ExitOnFailure(hr, "Failed to write target product code to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.sczLogPath); + ExitOnFailure(hr, "Failed to write package log to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.uiLevel); + ExitOnFailure(hr, "Failed to write UI level to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.action); + ExitOnFailure(hr, "Failed to write action to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->mspTarget.cOrderedPatches); + ExitOnFailure(hr, "Failed to write count of ordered patches to message buffer."); + + for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i) + { + hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->mspTarget.rgOrderedPatches[i].dwOrder); + ExitOnFailure(hr, "Failed to write ordered patch order to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage->sczId); + ExitOnFailure(hr, "Failed to write ordered patch id to message buffer."); + } + + hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); + ExitOnFailure(hr, "Failed to write variables."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fRollback); + ExitOnFailure(hr, "Failed to write rollback flag to message buffer."); + + // send message + context.pfnMessageHandler = pfnMessageHandler; + context.pvContext = pvContext; + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE, pbData, cbData, ProcessMsiPackageMessages, &context, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE message to per-machine process."); + + hr = ProcessResult(dwResult, pRestart); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationExecuteMsuPackage - + +*******************************************************************/ +extern "C" HRESULT ElevationExecuteMsuPackage( + __in HANDLE hPipe, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BOOL fRollback, + __in BOOL fStopWusaService, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT context = { }; + DWORD dwResult = 0; + + // serialize message data + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msuPackage.pPackage->sczId); + ExitOnFailure(hr, "Failed to write package id to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msuPackage.sczLogPath); + ExitOnFailure(hr, "Failed to write package log to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msuPackage.action); + ExitOnFailure(hr, "Failed to write action to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, fRollback); + ExitOnFailure(hr, "Failed to write rollback."); + + hr = BuffWriteNumber(&pbData, &cbData, fStopWusaService); + ExitOnFailure(hr, "Failed to write StopWusaService."); + + // send message + context.pfnGenericMessageHandler = pfnGenericMessageHandler; + context.pvContext = pvContext; + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE, pbData, cbData, ProcessGenericExecuteMessages, &context, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE message to per-machine process."); + + hr = ProcessResult(dwResult, pRestart); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +extern "C" HRESULT ElevationExecutePackageProviderAction( + __in HANDLE hPipe, + __in BURN_EXECUTE_ACTION* pExecuteAction + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; + + // Serialize the message data. + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->packageProvider.pPackage->sczId); + ExitOnFailure(hr, "Failed to write package id to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->packageProvider.action); + ExitOnFailure(hr, "Failed to write action to message buffer."); + + // Send the message. + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER message to per-machine process."); + + // Ignore the restart since this action only results in registry writes. + hr = ProcessResult(dwResult, &restart); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +extern "C" HRESULT ElevationExecutePackageDependencyAction( + __in HANDLE hPipe, + __in BURN_EXECUTE_ACTION* pExecuteAction + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; + + // Serialize the message data. + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->packageDependency.pPackage->sczId); + ExitOnFailure(hr, "Failed to write package id to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->packageDependency.sczBundleProviderKey); + ExitOnFailure(hr, "Failed to write bundle dependency key to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->packageDependency.action); + ExitOnFailure(hr, "Failed to write action to message buffer."); + + // Send the message. + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY message to per-machine process."); + + // Ignore the restart since this action only results in registry writes. + hr = ProcessResult(dwResult, &restart); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationLoadCompatiblePackageAction - Load compatible package + information from the referenced package. + +*******************************************************************/ +extern "C" HRESULT ElevationLoadCompatiblePackageAction( + __in HANDLE hPipe, + __in BURN_EXECUTE_ACTION* pExecuteAction + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; + + // Serialize message data. + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->compatiblePackage.pReferencePackage->sczId); + ExitOnFailure(hr, "Failed to write package id to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->compatiblePackage.sczInstalledProductCode); + ExitOnFailure(hr, "Failed to write installed ProductCode to message buffer."); + + hr = BuffWriteNumber64(&pbData, &cbData, pExecuteAction->compatiblePackage.qwInstalledVersion); + ExitOnFailure(hr, "Failed to write installed version to message buffer."); + + // Send the message. + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LOAD_COMPATIBLE_PACKAGE, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LOAD_COMPATIBLE_PACKAGE message to per-machine process."); + + // Ignore the restart since this action only loads data into memory. + hr = ProcessResult(dwResult, &restart); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationCleanPackage - + +*******************************************************************/ +extern "C" HRESULT ElevationCleanPackage( + __in HANDLE hPipe, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + + // serialize message data + hr = BuffWriteString(&pbData, &cbData, pPackage->sczId); + ExitOnFailure(hr, "Failed to write clean package id to message buffer."); + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +extern "C" HRESULT ElevationLaunchApprovedExe( + __in HANDLE hPipe, + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe, + __out DWORD* pdwProcessId + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT context = { }; + + // Serialize message data. + hr = BuffWriteString(&pbData, &cbData, pLaunchApprovedExe->sczId); + ExitOnFailure(hr, "Failed to write approved exe id to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pLaunchApprovedExe->sczArguments); + ExitOnFailure(hr, "Failed to write approved exe arguments to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, pLaunchApprovedExe->dwWaitForInputIdleTimeout); + ExitOnFailure(hr, "Failed to write approved exe WaitForInputIdle timeout to message buffer."); + + // Send the message. + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE, pbData, cbData, ProcessLaunchApprovedExeMessages, &context, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE message to per-machine process."); + + hr = (HRESULT)dwResult; + *pdwProcessId = context.dwProcessId; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationChildPumpMessages - + +*******************************************************************/ +extern "C" HRESULT ElevationChildPumpMessages( + __in DWORD dwLoggingTlsId, + __in HANDLE hPipe, + __in HANDLE hCachePipe, + __in BURN_APPROVED_EXES* pApprovedExes, + __in BURN_CONTAINERS* pContainers, + __in BURN_PACKAGES* pPackages, + __in BURN_PAYLOADS* pPayloads, + __in BURN_VARIABLES* pVariables, + __in BURN_REGISTRATION* pRegistration, + __in BURN_USER_EXPERIENCE* pUserExperience, + __out HANDLE* phLock, + __out BOOL* pfDisabledAutomaticUpdates, + __out DWORD* pdwChildExitCode, + __out BOOL* pfRestart + ) +{ + HRESULT hr = S_OK; + BURN_ELEVATION_CHILD_MESSAGE_CONTEXT cacheContext = { }; + BURN_ELEVATION_CHILD_MESSAGE_CONTEXT context = { }; + HANDLE hCacheThread = NULL; + BURN_PIPE_RESULT result = { }; + + cacheContext.dwLoggingTlsId = dwLoggingTlsId; + cacheContext.hPipe = hCachePipe; + cacheContext.pContainers = pContainers; + cacheContext.pPackages = pPackages; + cacheContext.pPayloads = pPayloads; + cacheContext.pVariables = pVariables; + cacheContext.pRegistration = pRegistration; + cacheContext.pUserExperience = pUserExperience; + + context.dwLoggingTlsId = dwLoggingTlsId; + context.hPipe = hPipe; + context.phLock = phLock; + context.pfDisabledAutomaticUpdates = pfDisabledAutomaticUpdates; + context.pApprovedExes = pApprovedExes; + context.pContainers = pContainers; + context.pPackages = pPackages; + context.pPayloads = pPayloads; + context.pVariables = pVariables; + context.pRegistration = pRegistration; + context.pUserExperience = pUserExperience; + + hCacheThread = ::CreateThread(NULL, 0, ElevatedChildCacheThreadProc, &cacheContext, 0, NULL); + ExitOnNullWithLastError(hCacheThread, hr, "Failed to create elevated cache thread."); + + hr = PipePumpMessages(hPipe, ProcessElevatedChildMessage, &context, &result); + ExitOnFailure(hr, "Failed to pump messages in child process."); + + // Wait for the cache thread and verify it gets the right result but don't fail if things + // don't work out. + WaitForElevatedChildCacheThread(hCacheThread, result.dwResult); + + *pdwChildExitCode = result.dwResult; + *pfRestart = result.fRestart; + +LExit: + ReleaseHandle(hCacheThread); + + return hr; +} + +extern "C" HRESULT ElevationChildResumeAutomaticUpdates() +{ + HRESULT hr = S_OK; + + LogId(REPORT_STANDARD, MSG_RESUME_AU_STARTING); + + hr = WuaResumeAutomaticUpdates(); + ExitOnFailure(hr, "Failed to resume automatic updates after pausing them, continuing..."); + + LogId(REPORT_STANDARD, MSG_RESUME_AU_SUCCEEDED); + +LExit: + return hr; +} + +// internal function definitions + +static DWORD WINAPI ElevatedChildCacheThreadProc( + __in LPVOID lpThreadParameter + ) +{ + HRESULT hr = S_OK; + BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = reinterpret_cast(lpThreadParameter); + BOOL fComInitialized = FALSE; + BURN_PIPE_RESULT result = { }; + + if (!::TlsSetValue(pContext->dwLoggingTlsId, pContext->hPipe)) + { + ExitWithLastError(hr, "Failed to set elevated cache pipe into thread local storage for logging."); + } + + // initialize COM + hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); + ExitOnFailure(hr, "Failed to initialize COM."); + fComInitialized = TRUE; + + hr = PipePumpMessages(pContext->hPipe, ProcessElevatedChildCacheMessage, pContext, &result); + ExitOnFailure(hr, "Failed to pump messages in child process."); + + hr = (HRESULT)result.dwResult; + +LExit: + if (fComInitialized) + { + ::CoUninitialize(); + } + + return (DWORD)hr; +} + +static HRESULT WaitForElevatedChildCacheThread( + __in HANDLE hCacheThread, + __in DWORD dwExpectedExitCode + ) +{ + UNREFERENCED_PARAMETER(dwExpectedExitCode); + + HRESULT hr = S_OK; + DWORD dwExitCode = ERROR_SUCCESS; + + if (WAIT_OBJECT_0 != ::WaitForSingleObject(hCacheThread, BURN_TIMEOUT)) + { + ExitWithLastError(hr, "Failed to wait for cache thread to terminate."); + } + + if (!::GetExitCodeThread(hCacheThread, &dwExitCode)) + { + ExitWithLastError(hr, "Failed to get cache thread exit code."); + } + + AssertSz(dwExitCode == dwExpectedExitCode, "Cache thread should have exited with the expected exit code."); + +LExit: + return hr; +} + +static HRESULT ProcessGenericExecuteMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT* pContext = static_cast(pvContext); + LPWSTR sczMessage = NULL; + DWORD cFiles = 0; + LPWSTR* rgwzFiles = NULL; + GENERIC_EXECUTE_MESSAGE message = { }; + + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.dwAllowedResults); + ExitOnFailure(hr, "Failed to allowed results."); + + // Process the message. + switch (pMsg->dwMessage) + { + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS: + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; + + // read message parameters + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.progress.dwPercentage); + ExitOnFailure(hr, "Failed to progress."); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR: + message.type = GENERIC_EXECUTE_MESSAGE_ERROR; + + // read message parameters + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.error.dwErrorCode); + ExitOnFailure(hr, "Failed to read error code."); + + hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &sczMessage); + ExitOnFailure(hr, "Failed to read message."); + + message.error.wzMessage = sczMessage; + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE: + message.type = GENERIC_EXECUTE_MESSAGE_FILES_IN_USE; + + // read message parameters + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &cFiles); + ExitOnFailure(hr, "Failed to read file count."); + + rgwzFiles = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cFiles, TRUE); + ExitOnNull(rgwzFiles, hr, E_OUTOFMEMORY, "Failed to allocate buffer for files in use."); + + for (DWORD i = 0; i < cFiles; ++i) + { + hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &rgwzFiles[i]); + ExitOnFailure(hr, "Failed to read file name: %u", i); + } + + message.filesInUse.cFiles = cFiles; + message.filesInUse.rgwzFiles = (LPCWSTR*)rgwzFiles; + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid package message."); + break; + } + + // send message + *pdwResult = (DWORD)pContext->pfnGenericMessageHandler(&message, pContext->pvContext);; + +LExit: + ReleaseStr(sczMessage); + + if (rgwzFiles) + { + for (DWORD i = 0; i < cFiles; ++i) + { + ReleaseStr(rgwzFiles[i]); + } + MemFree(rgwzFiles); + } + return hr; +} + +static HRESULT ProcessMsiPackageMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + WIU_MSI_EXECUTE_MESSAGE message = { }; + DWORD cMsiData = 0; + LPWSTR* rgwzMsiData = NULL; + BURN_ELEVATION_MSI_MESSAGE_CONTEXT* pContext = static_cast(pvContext); + LPWSTR sczMessage = NULL; + + // Read MSI extended message data. + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &cMsiData); + ExitOnFailure(hr, "Failed to read MSI data count."); + + if (cMsiData) + { + rgwzMsiData = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cMsiData, TRUE); + ExitOnNull(rgwzMsiData, hr, E_OUTOFMEMORY, "Failed to allocate buffer to read MSI data."); + + for (DWORD i = 0; i < cMsiData; ++i) + { + hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &rgwzMsiData[i]); + ExitOnFailure(hr, "Failed to read MSI data: %u", i); + } + + message.cData = cMsiData; + message.rgwzData = (LPCWSTR*)rgwzMsiData; + } + + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, (DWORD*)&message.dwAllowedResults); + ExitOnFailure(hr, "Failed to read UI flags."); + + // Process the rest of the message. + switch (pMsg->dwMessage) + { + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS: + // read message parameters + message.type = WIU_MSI_EXECUTE_MESSAGE_PROGRESS; + + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.progress.dwPercentage); + ExitOnFailure(hr, "Failed to read progress."); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR: + // read message parameters + message.type = WIU_MSI_EXECUTE_MESSAGE_ERROR; + + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.error.dwErrorCode); + ExitOnFailure(hr, "Failed to read error code."); + + hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &sczMessage); + ExitOnFailure(hr, "Failed to read message."); + message.error.wzMessage = sczMessage; + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE: + // read message parameters + message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE; + + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, (DWORD*)&message.msiMessage.mt); + ExitOnFailure(hr, "Failed to read message type."); + + hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &sczMessage); + ExitOnFailure(hr, "Failed to read message."); + message.msiMessage.wzMessage = sczMessage; + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE: + message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE; + message.msiFilesInUse.cFiles = cMsiData; + message.msiFilesInUse.rgwzFiles = (LPCWSTR*)rgwzMsiData; + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid package message."); + break; + } + + // send message + *pdwResult = (DWORD)pContext->pfnMessageHandler(&message, pContext->pvContext); + +LExit: + ReleaseStr(sczMessage); + + if (rgwzMsiData) + { + for (DWORD i = 0; i < cMsiData; ++i) + { + ReleaseStr(rgwzMsiData[i]); + } + + MemFree(rgwzMsiData); + } + + return hr; +} + +static HRESULT ProcessLaunchApprovedExeMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT* pContext = static_cast(pvContext); + DWORD dwProcessId = 0; + + // Process the message. + switch (pMsg->dwMessage) + { + case BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID: + // read message parameters + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &dwProcessId); + ExitOnFailure(hr, "Failed to read approved exe process id."); + pContext->dwProcessId = dwProcessId; + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid launch approved exe message."); + break; + } + + *pdwResult = static_cast(hr); + +LExit: + return hr; +} + +static HRESULT ProcessElevatedChildMessage( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult +) +{ + HRESULT hr = S_OK; + BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = static_cast(pvContext); + HRESULT hrResult = S_OK; + DWORD dwPid = 0; + + switch (pMsg->dwMessage) + { + case BURN_ELEVATION_TRANSACTION_BEGIN: + hrResult = OnMsiBeginTransaction(pContext); + break; + + case BURN_ELEVATION_TRANSACTION_COMMIT: + hrResult = OnMsiCommitTransaction(pContext); + break; + + case BURN_ELEVATION_TRANSACTION_ROLLBACK: + hrResult = OnMsiRollbackTransaction(pContext); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE: + hrResult = OnApplyInitialize(pContext->pVariables, pContext->pRegistration, pContext->phLock, pContext->pfDisabledAutomaticUpdates, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE: + hrResult = OnApplyUninitialize(pContext->phLock); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN: + hrResult = OnSessionBegin(pContext->pRegistration, pContext->pVariables, pContext->pUserExperience, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME: + hrResult = OnSessionResume(pContext->pRegistration, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_SESSION_END: + hrResult = OnSessionEnd(pContext->pRegistration, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE: + hrResult = OnSaveState(pContext->pRegistration, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION: + hrResult = OnProcessDependentRegistration(pContext->pRegistration, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE: + hrResult = OnExecuteExePackage(pContext->hPipe, pContext->pPackages, &pContext->pRegistration->relatedBundles, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE: + hrResult = OnExecuteMsiPackage(pContext->hPipe, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE: + hrResult = OnExecuteMspPackage(pContext->hPipe, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE: + hrResult = OnExecuteMsuPackage(pContext->hPipe, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER: + hrResult = OnExecutePackageProviderAction(pContext->pPackages, &pContext->pRegistration->relatedBundles, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY: + hrResult = OnExecutePackageDependencyAction(pContext->pPackages, &pContext->pRegistration->relatedBundles, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_LOAD_COMPATIBLE_PACKAGE: + hrResult = OnLoadCompatiblePackage(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE: + hrResult = OnCleanPackage(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE: + hrResult = OnLaunchApprovedExe(pContext->hPipe, pContext->pApprovedExes, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Unexpected elevated message sent to child process, msg: %u", pMsg->dwMessage); + } + + *pdwResult = dwPid ? dwPid : (DWORD)hrResult; + +LExit: + return hr; +} + +static HRESULT ProcessElevatedChildCacheMessage( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = static_cast(pvContext); + HRESULT hrResult = S_OK; + + switch (pMsg->dwMessage) + { + case BURN_ELEVATION_MESSAGE_TYPE_LAYOUT_BUNDLE: + hrResult = OnLayoutBundle(pContext->pRegistration->sczExecutableName, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_CACHE_OR_LAYOUT_CONTAINER_OR_PAYLOAD: + hrResult = OnCacheOrLayoutContainerOrPayload(pContext->pContainers, pContext->pPackages, pContext->pPayloads, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP: + OnCacheCleanup(pContext->pRegistration->sczId); + hrResult = S_OK; + break; + + case BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE: + hrResult = OnCleanPackage(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Unexpected elevated cache message sent to child process, msg: %u", pMsg->dwMessage); + } + + *pdwResult = (DWORD)hrResult; + +LExit: + return hr; +} + +static HRESULT ProcessResult( + __in DWORD dwResult, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = static_cast(dwResult); + if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == hr) + { + *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; + hr = S_OK; + } + else if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED) == hr) + { + *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; + hr = S_OK; + } + + return hr; +} + +static HRESULT OnMsiBeginTransaction( + __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext +) +{ + UINT uResult = ERROR_SUCCESS; + HRESULT hr = S_OK; + + pContext->hMsiTrns = NULL; + pContext->hMsiTrnsEvent = NULL; + uResult = MsiBeginTransaction(L"WiX", 0, &pContext->hMsiTrns, &pContext->hMsiTrnsEvent); + ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction"); + +LExit: + return hr; +} + +static HRESULT OnMsiCommitTransaction( + __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext +) +{ + UINT uResult = ERROR_SUCCESS; + HRESULT hr = S_OK; + + uResult = MsiEndTransaction(MSITRANSACTIONSTATE_COMMIT); + ExitOnWin32Error(uResult, hr, "Failed committing an MSI transaction"); + +LExit: + pContext->hMsiTrns = NULL; + pContext->hMsiTrnsEvent = NULL; + return hr; +} + +static HRESULT OnMsiRollbackTransaction( + __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext +) { + UINT uResult = ERROR_SUCCESS; + HRESULT hr = S_OK; + + uResult = MsiEndTransaction(MSITRANSACTIONSTATE_ROLLBACK); + ExitOnWin32Error(uResult, hr, "Failed rolling back an MSI transaction"); + +LExit: + pContext->hMsiTrns = NULL; + pContext->hMsiTrnsEvent = NULL; + return hr; +} + +static HRESULT OnApplyInitialize( + __in BURN_VARIABLES* pVariables, + __in BURN_REGISTRATION* pRegistration, + __in HANDLE* phLock, + __in BOOL* pfDisabledWindowsUpdate, + __in BYTE* pbData, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + DWORD dwAction = 0; + DWORD dwAUAction = 0; + DWORD dwTakeSystemRestorePoint = 0; + LPWSTR sczBundleName = NULL; + + // Deserialize message data. + hr = BuffReadNumber(pbData, cbData, &iData, &dwAction); + ExitOnFailure(hr, "Failed to read action."); + + hr = BuffReadNumber(pbData, cbData, &iData, &dwAUAction); + ExitOnFailure(hr, "Failed to read update action."); + + hr = BuffReadNumber(pbData, cbData, &iData, &dwTakeSystemRestorePoint); + ExitOnFailure(hr, "Failed to read system restore point action."); + + hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); + ExitOnFailure(hr, "Failed to read variables."); + + // Initialize. + hr = ApplyLock(TRUE, phLock); + ExitOnFailure(hr, "Failed to acquire lock due to setup in other session."); + + // Reset and reload the related bundles. + RelatedBundlesUninitialize(&pRegistration->relatedBundles); + + hr = RelatedBundlesInitializeForScope(TRUE, pRegistration, &pRegistration->relatedBundles); + ExitOnFailure(hr, "Failed to initialize per-machine related bundles."); + + // Attempt to pause AU with best effort. + if (BURN_AU_PAUSE_ACTION_IFELEVATED == dwAUAction || BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME == dwAUAction) + { + LogId(REPORT_STANDARD, MSG_PAUSE_AU_STARTING); + + hr = WuaPauseAutomaticUpdates(); + if (FAILED(hr)) + { + LogId(REPORT_STANDARD, MSG_FAILED_PAUSE_AU, hr); + hr = S_OK; + } + else + { + LogId(REPORT_STANDARD, MSG_PAUSE_AU_SUCCEEDED); + if (BURN_AU_PAUSE_ACTION_IFELEVATED == dwAUAction) + { + *pfDisabledWindowsUpdate = TRUE; + } + } + } + + if (dwTakeSystemRestorePoint) + { + hr = VariableGetString(pVariables, BURN_BUNDLE_NAME, &sczBundleName); + if (FAILED(hr)) + { + hr = S_OK; + ExitFunction(); + } + + LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_STARTING); + + BOOTSTRAPPER_ACTION action = static_cast(dwAction); + SRP_ACTION restoreAction = (BOOTSTRAPPER_ACTION_INSTALL == action) ? SRP_ACTION_INSTALL : (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? SRP_ACTION_UNINSTALL : SRP_ACTION_MODIFY; + hr = SrpCreateRestorePoint(sczBundleName, restoreAction); + if (SUCCEEDED(hr)) + { + LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_SUCCEEDED); + } + else if (E_NOTIMPL == hr) + { + LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_DISABLED); + hr = S_OK; + } + else if (FAILED(hr)) + { + LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_FAILED, hr); + hr = S_OK; + } + } + +LExit: + ReleaseStr(sczBundleName); + return hr; +} + +static HRESULT OnApplyUninitialize( + __in HANDLE* phLock + ) +{ + Assert(phLock); + + // TODO: end system restore point. + + if (*phLock) + { + ::ReleaseMutex(*phLock); + ::CloseHandle(*phLock); + *phLock = NULL; + } + + return S_OK; +} + +static HRESULT OnSessionBegin( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BYTE* pbData, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczEngineWorkingPath = NULL; + DWORD dwRegistrationOperations = 0; + DWORD dwDependencyRegistrationAction = 0; + DWORD64 qwEstimatedSize = 0; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &sczEngineWorkingPath); + ExitOnFailure(hr, "Failed to read engine working path."); + + hr = BuffReadString(pbData, cbData, &iData, &pRegistration->sczResumeCommandLine); + ExitOnFailure(hr, "Failed to read resume command line."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&pRegistration->fDisableResume); + ExitOnFailure(hr, "Failed to read resume flag."); + + hr = BuffReadNumber(pbData, cbData, &iData, &dwRegistrationOperations); + ExitOnFailure(hr, "Failed to read registration operations."); + + hr = BuffReadNumber(pbData, cbData, &iData, &dwDependencyRegistrationAction); + ExitOnFailure(hr, "Failed to read dependency registration action."); + + hr = BuffReadNumber64(pbData, cbData, &iData, &qwEstimatedSize); + ExitOnFailure(hr, "Failed to read estimated size."); + + hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); + ExitOnFailure(hr, "Failed to read variables."); + + // Begin session in per-machine process. + hr = RegistrationSessionBegin(sczEngineWorkingPath, pRegistration, pVariables, pUserExperience, dwRegistrationOperations, (BURN_DEPENDENCY_REGISTRATION_ACTION)dwDependencyRegistrationAction, qwEstimatedSize); + ExitOnFailure(hr, "Failed to begin registration session."); + +LExit: + ReleaseStr(sczEngineWorkingPath); + + return hr; +} + +static HRESULT OnSessionResume( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &pRegistration->sczResumeCommandLine); + ExitOnFailure(hr, "Failed to read resume command line."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&pRegistration->fDisableResume); + ExitOnFailure(hr, "Failed to read resume flag."); + + hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); + ExitOnFailure(hr, "Failed to read variables."); + + // resume session in per-machine process + hr = RegistrationSessionResume(pRegistration, pVariables); + ExitOnFailure(hr, "Failed to resume registration session."); + +LExit: + return hr; +} + +static HRESULT OnSessionEnd( + __in BURN_REGISTRATION* pRegistration, + __in BYTE* pbData, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + DWORD dwResumeMode = 0; + DWORD dwRestart = 0; + DWORD dwDependencyRegistrationAction = 0; + + // Deserialize message data. + hr = BuffReadNumber(pbData, cbData, &iData, &dwResumeMode); + ExitOnFailure(hr, "Failed to read resume mode enum."); + + hr = BuffReadNumber(pbData, cbData, &iData, &dwRestart); + ExitOnFailure(hr, "Failed to read restart enum."); + + hr = BuffReadNumber(pbData, cbData, &iData, &dwDependencyRegistrationAction); + ExitOnFailure(hr, "Failed to read dependency registration action."); + + // suspend session in per-machine process + hr = RegistrationSessionEnd(pRegistration, (BURN_RESUME_MODE)dwResumeMode, (BOOTSTRAPPER_APPLY_RESTART)dwRestart, (BURN_DEPENDENCY_REGISTRATION_ACTION)dwDependencyRegistrationAction); + ExitOnFailure(hr, "Failed to suspend registration session."); + +LExit: + return hr; +} + +static HRESULT OnSaveState( + __in BURN_REGISTRATION* pRegistration, + __in BYTE* pbData, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + + // save state in per-machine process + hr = RegistrationSaveState(pRegistration, pbData, cbData); + ExitOnFailure(hr, "Failed to save state."); + +LExit: + return hr; +} + +static HRESULT OnLayoutBundle( + __in_z LPCWSTR wzExecutableName, + __in BYTE* pbData, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczLayoutDirectory = NULL; + LPWSTR sczUnverifiedPath = NULL; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &sczLayoutDirectory); + ExitOnFailure(hr, "Failed to read layout directory."); + + hr = BuffReadString(pbData, cbData, &iData, &sczUnverifiedPath); + ExitOnFailure(hr, "Failed to read unverified bundle path."); + + // Layout the bundle. + hr = CacheLayoutBundle(wzExecutableName, sczLayoutDirectory, sczUnverifiedPath); + ExitOnFailure(hr, "Failed to layout bundle from: %ls", sczUnverifiedPath); + +LExit: + ReleaseStr(sczUnverifiedPath); + ReleaseStr(sczLayoutDirectory); + + return hr; +} + +static HRESULT OnCacheOrLayoutContainerOrPayload( + __in BURN_CONTAINERS* pContainers, + __in BURN_PACKAGES* pPackages, + __in BURN_PAYLOADS* pPayloads, + __in BYTE* pbData, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR scz = NULL; + BURN_CONTAINER* pContainer = NULL; + BURN_PACKAGE* pPackage = NULL; + BURN_PAYLOAD* pPayload = NULL; + LPWSTR sczLayoutDirectory = NULL; + LPWSTR sczUnverifiedPath = NULL; + BOOL fMove = FALSE; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &scz); + ExitOnFailure(hr, "Failed to read package id."); + + if (scz && *scz) + { + hr = ContainerFindById(pContainers, scz, &pContainer); + ExitOnFailure(hr, "Failed to find container: %ls", scz); + } + + hr = BuffReadString(pbData, cbData, &iData, &scz); + ExitOnFailure(hr, "Failed to read package id."); + + if (scz && *scz) + { + hr = PackageFindById(pPackages, scz, &pPackage); + ExitOnFailure(hr, "Failed to find package: %ls", scz); + } + + hr = BuffReadString(pbData, cbData, &iData, &scz); + ExitOnFailure(hr, "Failed to read payload id."); + + if (scz && *scz) + { + hr = PayloadFindById(pPayloads, scz, &pPayload); + ExitOnFailure(hr, "Failed to find payload: %ls", scz); + } + + hr = BuffReadString(pbData, cbData, &iData, &sczLayoutDirectory); + ExitOnFailure(hr, "Failed to read layout directory."); + + hr = BuffReadString(pbData, cbData, &iData, &sczUnverifiedPath); + ExitOnFailure(hr, "Failed to read unverified path."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fMove); + ExitOnFailure(hr, "Failed to read move flag."); + + // Layout payload. + if (sczLayoutDirectory && *sczLayoutDirectory) + { + if (pContainer) + { + Assert(!pPackage); + Assert(!pPayload); + + hr = CacheLayoutContainer(pContainer, sczLayoutDirectory, sczUnverifiedPath, fMove); + ExitOnFailure(hr, "Failed to layout container from: %ls to %ls", sczUnverifiedPath, sczLayoutDirectory); + } + else + { + hr = CacheLayoutPayload(pPayload, sczLayoutDirectory, sczUnverifiedPath, fMove); + ExitOnFailure(hr, "Failed to layout payload from: %ls to %ls", sczUnverifiedPath, sczLayoutDirectory); + } + } + else if (pPackage) // complete payload. + { + Assert(!pContainer); + + hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, sczUnverifiedPath, fMove); + ExitOnFailure(hr, "Failed to cache payload: %ls", pPayload->sczKey); + } + else + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid data passed to cache or layout payload."); + } + +LExit: + ReleaseStr(sczUnverifiedPath); + ReleaseStr(sczLayoutDirectory); + ReleaseStr(scz); + + return hr; +} + +static void OnCacheCleanup( + __in_z LPCWSTR wzBundleId + ) +{ + CacheCleanup(TRUE, wzBundleId); +} + +static HRESULT OnProcessDependentRegistration( + __in const BURN_REGISTRATION* pRegistration, + __in BYTE* pbData, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + BURN_DEPENDENT_REGISTRATION_ACTION action = { }; + + // Deserialize message data. + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&action.type); + ExitOnFailure(hr, "Failed to read action type."); + + hr = BuffReadString(pbData, cbData, &iData, &action.sczBundleId); + ExitOnFailure(hr, "Failed to read bundle id."); + + hr = BuffReadString(pbData, cbData, &iData, &action.sczDependentProviderKey); + ExitOnFailure(hr, "Failed to read dependent provider key."); + + // Execute the registration action. + hr = DependencyProcessDependentRegistration(pRegistration, &action); + ExitOnFailure(hr, "Failed to execute dependent registration action for provider key: %ls", action.sczDependentProviderKey); + +LExit: + // TODO: do the right thing here. + //DependencyUninitializeRegistrationAction(&action); + ReleaseStr(action.sczDependentProviderKey); + ReleaseStr(action.sczBundleId) + + return hr; +} + +static HRESULT OnExecuteExePackage( + __in HANDLE hPipe, + __in BURN_PACKAGES* pPackages, + __in BURN_RELATED_BUNDLES* pRelatedBundles, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczPackage = NULL; + DWORD dwRollback = 0; + BURN_EXECUTE_ACTION executeAction = { }; + LPWSTR sczIgnoreDependencies = NULL; + LPWSTR sczAncestors = NULL; + BOOTSTRAPPER_APPLY_RESTART exeRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; + + executeAction.type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &sczPackage); + ExitOnFailure(hr, "Failed to read EXE package id."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.exePackage.action); + ExitOnFailure(hr, "Failed to read action."); + + hr = BuffReadNumber(pbData, cbData, &iData, &dwRollback); + ExitOnFailure(hr, "Failed to read rollback."); + + hr = BuffReadString(pbData, cbData, &iData, &sczIgnoreDependencies); + ExitOnFailure(hr, "Failed to read the list of dependencies to ignore."); + + hr = BuffReadString(pbData, cbData, &iData, &sczAncestors); + ExitOnFailure(hr, "Failed to read the list of ancestors."); + + hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); + ExitOnFailure(hr, "Failed to read variables."); + + hr = PackageFindById(pPackages, sczPackage, &executeAction.exePackage.pPackage); + if (E_NOTFOUND == hr) + { + hr = PackageFindRelatedById(pRelatedBundles, sczPackage, &executeAction.exePackage.pPackage); + } + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); + + // Pass the list of dependencies to ignore, if any, to the related bundle. + if (sczIgnoreDependencies && *sczIgnoreDependencies) + { + hr = StrAllocString(&executeAction.exePackage.sczIgnoreDependencies, sczIgnoreDependencies, 0); + ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore."); + } + + // Pass the list of ancestors, if any, to the related bundle. + if (sczAncestors && *sczAncestors) + { + hr = StrAllocString(&executeAction.exePackage.sczAncestors, sczAncestors, 0); + ExitOnFailure(hr, "Failed to allocate the list of ancestors."); + } + + // Execute EXE package. + hr = ExeEngineExecutePackage(&executeAction, pVariables, static_cast(dwRollback), GenericExecuteMessageHandler, hPipe, &exeRestart); + ExitOnFailure(hr, "Failed to execute EXE package."); + +LExit: + ReleaseStr(sczAncestors); + ReleaseStr(sczIgnoreDependencies); + ReleaseStr(sczPackage); + PlanUninitializeExecuteAction(&executeAction); + + if (SUCCEEDED(hr)) + { + if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == exeRestart) + { + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); + } + else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == exeRestart) + { + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED); + } + } + + return hr; +} + +static HRESULT OnExecuteMsiPackage( + __in HANDLE hPipe, + __in BURN_PACKAGES* pPackages, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczPackage = NULL; + HWND hwndParent = NULL; + BOOL fRollback = 0; + BURN_EXECUTE_ACTION executeAction = { }; + BOOTSTRAPPER_APPLY_RESTART msiRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; + + executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &sczPackage); + ExitOnFailure(hr, "Failed to read MSI package id."); + + hr = PackageFindById(pPackages, sczPackage, &executeAction.msiPackage.pPackage); + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&hwndParent); + ExitOnFailure(hr, "Failed to read parent hwnd."); + + hr = BuffReadString(pbData, cbData, &iData, &executeAction.msiPackage.sczLogPath); + ExitOnFailure(hr, "Failed to read package log."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.uiLevel); + ExitOnFailure(hr, "Failed to read UI level."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.action); + ExitOnFailure(hr, "Failed to read action."); + + // Read feature actions. + if (executeAction.msiPackage.pPackage->Msi.cFeatures) + { + executeAction.msiPackage.rgFeatures = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(executeAction.msiPackage.pPackage->Msi.cFeatures * sizeof(BOOTSTRAPPER_FEATURE_ACTION), TRUE); + ExitOnNull(executeAction.msiPackage.rgFeatures, hr, E_OUTOFMEMORY, "Failed to allocate memory for feature actions."); + + for (DWORD i = 0; i < executeAction.msiPackage.pPackage->Msi.cFeatures; ++i) + { + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.rgFeatures[i]); + ExitOnFailure(hr, "Failed to read feature action."); + } + } + + // Read slipstream patches actions. + if (executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages) + { + executeAction.msiPackage.rgSlipstreamPatches = (BOOTSTRAPPER_ACTION_STATE*)MemAlloc(executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages * sizeof(BOOTSTRAPPER_ACTION_STATE), TRUE); + ExitOnNull(executeAction.msiPackage.rgSlipstreamPatches, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream patch actions."); + + for (DWORD i = 0; i < executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++i) + { + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.rgSlipstreamPatches[i]); + ExitOnFailure(hr, "Failed to read slipstream action."); + } + } + + hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); + ExitOnFailure(hr, "Failed to read variables."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback); + ExitOnFailure(hr, "Failed to read rollback flag."); + + // Execute MSI package. + hr = MsiEngineExecutePackage(hwndParent, &executeAction, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &msiRestart); + ExitOnFailure(hr, "Failed to execute MSI package."); + +LExit: + ReleaseStr(sczPackage); + PlanUninitializeExecuteAction(&executeAction); + + if (SUCCEEDED(hr)) + { + if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == msiRestart) + { + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); + } + else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == msiRestart) + { + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED); + } + } + + return hr; +} + +static HRESULT OnExecuteMspPackage( + __in HANDLE hPipe, + __in BURN_PACKAGES* pPackages, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczPackage = NULL; + HWND hwndParent = NULL; + BOOL fRollback = 0; + BURN_EXECUTE_ACTION executeAction = { }; + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; + + executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSP_TARGET; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &sczPackage); + ExitOnFailure(hr, "Failed to read MSP package id."); + + hr = PackageFindById(pPackages, sczPackage, &executeAction.mspTarget.pPackage); + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&hwndParent); + ExitOnFailure(hr, "Failed to read parent hwnd."); + + executeAction.mspTarget.fPerMachineTarget = TRUE; // we're in the elevated process, clearly we're targeting a per-machine product. + + hr = BuffReadString(pbData, cbData, &iData, &executeAction.mspTarget.sczTargetProductCode); + ExitOnFailure(hr, "Failed to read target product code."); + + hr = BuffReadString(pbData, cbData, &iData, &executeAction.mspTarget.sczLogPath); + ExitOnFailure(hr, "Failed to read package log."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.uiLevel); + ExitOnFailure(hr, "Failed to read UI level."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.action); + ExitOnFailure(hr, "Failed to read action."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.cOrderedPatches); + ExitOnFailure(hr, "Failed to read count of ordered patches."); + + if (executeAction.mspTarget.cOrderedPatches) + { + executeAction.mspTarget.rgOrderedPatches = (BURN_ORDERED_PATCHES*)MemAlloc(executeAction.mspTarget.cOrderedPatches * sizeof(BURN_ORDERED_PATCHES), TRUE); + ExitOnNull(executeAction.mspTarget.rgOrderedPatches, hr, E_OUTOFMEMORY, "Failed to allocate memory for ordered patches."); + + for (DWORD i = 0; i < executeAction.mspTarget.cOrderedPatches; ++i) + { + hr = BuffReadNumber(pbData, cbData, &iData, &executeAction.mspTarget.rgOrderedPatches[i].dwOrder); + ExitOnFailure(hr, "Failed to read ordered patch order number."); + + hr = BuffReadString(pbData, cbData, &iData, &sczPackage); + ExitOnFailure(hr, "Failed to read ordered patch package id."); + + hr = PackageFindById(pPackages, sczPackage, &executeAction.mspTarget.rgOrderedPatches[i].pPackage); + ExitOnFailure(hr, "Failed to find ordered patch package: %ls", sczPackage); + } + } + + hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); + ExitOnFailure(hr, "Failed to read variables."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback); + ExitOnFailure(hr, "Failed to read rollback flag."); + + // Execute MSP package. + hr = MspEngineExecutePackage(hwndParent, &executeAction, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &restart); + ExitOnFailure(hr, "Failed to execute MSP package."); + +LExit: + ReleaseStr(sczPackage); + PlanUninitializeExecuteAction(&executeAction); + + if (SUCCEEDED(hr)) + { + if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == restart) + { + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); + } + else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart) + { + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED); + } + } + + return hr; +} + +static HRESULT OnExecuteMsuPackage( + __in HANDLE hPipe, + __in BURN_PACKAGES* pPackages, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczPackage = NULL; + DWORD dwRollback = 0; + DWORD dwStopWusaService = 0; + BURN_EXECUTE_ACTION executeAction = { }; + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; + + executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &sczPackage); + ExitOnFailure(hr, "Failed to read MSU package id."); + + hr = BuffReadString(pbData, cbData, &iData, &executeAction.msuPackage.sczLogPath); + ExitOnFailure(hr, "Failed to read package log."); + + hr = BuffReadNumber(pbData, cbData, &iData, reinterpret_cast(&executeAction.msuPackage.action)); + ExitOnFailure(hr, "Failed to read action."); + + hr = BuffReadNumber(pbData, cbData, &iData, &dwRollback); + ExitOnFailure(hr, "Failed to read rollback."); + + hr = BuffReadNumber(pbData, cbData, &iData, &dwStopWusaService); + ExitOnFailure(hr, "Failed to read StopWusaService."); + + hr = PackageFindById(pPackages, sczPackage, &executeAction.msuPackage.pPackage); + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); + + // execute MSU package + hr = MsuEngineExecutePackage(&executeAction, pVariables, static_cast(dwRollback), static_cast(dwStopWusaService), GenericExecuteMessageHandler, hPipe, &restart); + ExitOnFailure(hr, "Failed to execute MSU package."); + +LExit: + ReleaseStr(sczPackage); + PlanUninitializeExecuteAction(&executeAction); + + if (SUCCEEDED(hr)) + { + if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == restart) + { + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); + } + else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart) + { + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED); + } + } + + return hr; +} + +static HRESULT OnExecutePackageProviderAction( + __in BURN_PACKAGES* pPackages, + __in BURN_RELATED_BUNDLES* pRelatedBundles, + __in BYTE* pbData, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczPackage = NULL; + BURN_EXECUTE_ACTION executeAction = { }; + + executeAction.type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER; + + // Deserialize the message data. + hr = BuffReadString(pbData, cbData, &iData, &sczPackage); + ExitOnFailure(hr, "Failed to read package id from message buffer."); + + hr = BuffReadNumber(pbData, cbData, &iData, reinterpret_cast(&executeAction.packageProvider.action)); + ExitOnFailure(hr, "Failed to read action."); + + // Find the package again. + hr = PackageFindById(pPackages, sczPackage, &executeAction.packageProvider.pPackage); + if (E_NOTFOUND == hr) + { + hr = PackageFindRelatedById(pRelatedBundles, sczPackage, &executeAction.packageProvider.pPackage); + } + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); + + // Execute the package provider action. + hr = DependencyExecutePackageProviderAction(&executeAction); + ExitOnFailure(hr, "Failed to execute package provider action."); + +LExit: + ReleaseStr(sczPackage); + PlanUninitializeExecuteAction(&executeAction); + + return hr; +} + +static HRESULT OnExecutePackageDependencyAction( + __in BURN_PACKAGES* pPackages, + __in BURN_RELATED_BUNDLES* pRelatedBundles, + __in BYTE* pbData, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczPackage = NULL; + BURN_EXECUTE_ACTION executeAction = { }; + + executeAction.type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY; + + // Deserialize the message data. + hr = BuffReadString(pbData, cbData, &iData, &sczPackage); + ExitOnFailure(hr, "Failed to read package id from message buffer."); + + hr = BuffReadString(pbData, cbData, &iData, &executeAction.packageDependency.sczBundleProviderKey); + ExitOnFailure(hr, "Failed to read bundle dependency key from message buffer."); + + hr = BuffReadNumber(pbData, cbData, &iData, reinterpret_cast(&executeAction.packageDependency.action)); + ExitOnFailure(hr, "Failed to read action."); + + // Find the package again. + hr = PackageFindById(pPackages, sczPackage, &executeAction.packageDependency.pPackage); + if (E_NOTFOUND == hr) + { + hr = PackageFindRelatedById(pRelatedBundles, sczPackage, &executeAction.packageDependency.pPackage); + } + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); + + // Execute the package dependency action. + hr = DependencyExecutePackageDependencyAction(TRUE, &executeAction); + ExitOnFailure(hr, "Failed to execute package dependency action."); + +LExit: + ReleaseStr(sczPackage); + PlanUninitializeExecuteAction(&executeAction); + + return hr; +} + +static HRESULT OnLoadCompatiblePackage( + __in BURN_PACKAGES* pPackages, + __in BYTE* pbData, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczPackage = NULL; + BURN_EXECUTE_ACTION executeAction = { }; + + executeAction.type = BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE; + + // Deserialize the message data. + hr = BuffReadString(pbData, cbData, &iData, &sczPackage); + ExitOnFailure(hr, "Failed to read package id from message buffer."); + + // Find the reference package. + hr = PackageFindById(pPackages, sczPackage, &executeAction.compatiblePackage.pReferencePackage); + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); + + hr = BuffReadString(pbData, cbData, &iData, &executeAction.compatiblePackage.sczInstalledProductCode); + ExitOnFailure(hr, "Failed to read installed ProductCode from message buffer."); + + hr = BuffReadNumber64(pbData, cbData, &iData, &executeAction.compatiblePackage.qwInstalledVersion); + ExitOnFailure(hr, "Failed to read installed version from message buffer."); + + // Copy the installed data to the reference package. + hr = StrAllocString(&executeAction.compatiblePackage.pReferencePackage->Msi.sczInstalledProductCode, executeAction.compatiblePackage.sczInstalledProductCode, 0); + ExitOnFailure(hr, "Failed to copy installed ProductCode."); + + executeAction.compatiblePackage.pReferencePackage->Msi.qwInstalledVersion = executeAction.compatiblePackage.qwInstalledVersion; + + // Load the compatible package and add it to the list. + hr = MsiEngineAddCompatiblePackage(pPackages, executeAction.compatiblePackage.pReferencePackage, NULL); + ExitOnFailure(hr, "Failed to load compatible package."); + +LExit: + ReleaseStr(sczPackage); + PlanUninitializeExecuteAction(&executeAction); + + return hr; +} + +static int GenericExecuteMessageHandler( + __in GENERIC_EXECUTE_MESSAGE* pMessage, + __in LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + int nResult = IDOK; + HANDLE hPipe = (HANDLE)pvContext; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwMessage = 0; + + hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwAllowedResults); + ExitOnFailure(hr, "Failed to write UI flags."); + + switch(pMessage->type) + { + case GENERIC_EXECUTE_MESSAGE_PROGRESS: + // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, pMessage->progress.dwPercentage); + ExitOnFailure(hr, "Failed to write progress percentage to message buffer."); + + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS; + break; + + case GENERIC_EXECUTE_MESSAGE_ERROR: + // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, pMessage->error.dwErrorCode); + ExitOnFailure(hr, "Failed to write error code to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pMessage->error.wzMessage); + ExitOnFailure(hr, "Failed to write message to message buffer."); + + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR; + break; + + case GENERIC_EXECUTE_MESSAGE_FILES_IN_USE: + hr = BuffWriteNumber(&pbData, &cbData, pMessage->filesInUse.cFiles); + ExitOnFailure(hr, "Failed to count of files in use to message buffer."); + + for (DWORD i = 0; i < pMessage->filesInUse.cFiles; ++i) + { + hr = BuffWriteString(&pbData, &cbData, pMessage->filesInUse.rgwzFiles[i]); + ExitOnFailure(hr, "Failed to write file in use to message buffer."); + } + + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE; + break; + } + + // send message + hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, reinterpret_cast(&nResult)); + ExitOnFailure(hr, "Failed to send message to per-user process."); + +LExit: + ReleaseBuffer(pbData); + + return nResult; +} + +static int MsiExecuteMessageHandler( + __in WIU_MSI_EXECUTE_MESSAGE* pMessage, + __in_opt LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + int nResult = IDOK; + HANDLE hPipe = (HANDLE)pvContext; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwMessage = 0; + + // Always send any extra data via the struct first. + hr = BuffWriteNumber(&pbData, &cbData, pMessage->cData); + ExitOnFailure(hr, "Failed to write MSI data count to message buffer."); + + for (DWORD i = 0; i < pMessage->cData; ++i) + { + hr = BuffWriteString(&pbData, &cbData, pMessage->rgwzData[i]); + ExitOnFailure(hr, "Failed to write MSI data to message buffer."); + } + + hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwAllowedResults); + ExitOnFailure(hr, "Failed to write UI flags."); + + switch (pMessage->type) + { + case WIU_MSI_EXECUTE_MESSAGE_PROGRESS: + // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, pMessage->progress.dwPercentage); + ExitOnFailure(hr, "Failed to write progress percentage to message buffer."); + + // set message id + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS; + break; + + case WIU_MSI_EXECUTE_MESSAGE_ERROR: + // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, pMessage->error.dwErrorCode); + ExitOnFailure(hr, "Failed to write error code to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pMessage->error.wzMessage); + ExitOnFailure(hr, "Failed to write message to message buffer."); + + // set message id + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR; + break; + + case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE: + // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pMessage->msiMessage.mt); + ExitOnFailure(hr, "Failed to write MSI message type to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pMessage->msiMessage.wzMessage); + ExitOnFailure(hr, "Failed to write message to message buffer."); + + // set message id + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE; + break; + + case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE: + // NOTE: we do not serialize other message data here because all the "files in use" are in the data above. + + // set message id + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE; + break; + + default: + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Invalid message type: %d", pMessage->type); + } + + // send message + hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, (DWORD*)&nResult); + ExitOnFailure(hr, "Failed to send message to per-machine process."); + +LExit: + ReleaseBuffer(pbData); + + return nResult; +} + +static HRESULT OnCleanPackage( + __in BURN_PACKAGES* pPackages, + __in BYTE* pbData, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczPackage = NULL; + BURN_PACKAGE* pPackage = NULL; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &sczPackage); + ExitOnFailure(hr, "Failed to read package id."); + + hr = PackageFindById(pPackages, sczPackage, &pPackage); + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); + + // Remove the package from the cache. + hr = CacheRemovePackage(TRUE, pPackage->sczId, pPackage->sczCacheId); + ExitOnFailure(hr, "Failed to remove from cache package: %ls", pPackage->sczId); + +LExit: + ReleaseStr(sczPackage); + return hr; +} + +static HRESULT OnLaunchApprovedExe( + __in HANDLE hPipe, + __in BURN_APPROVED_EXES* pApprovedExes, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe = NULL; + BURN_APPROVED_EXE* pApprovedExe = NULL; + REGSAM samDesired = KEY_QUERY_VALUE; + HKEY hKey = NULL; + DWORD dwProcessId = 0; + BYTE* pbSendData = NULL; + SIZE_T cbSendData = 0; + DWORD dwResult = 0; + + pLaunchApprovedExe = (BURN_LAUNCH_APPROVED_EXE*)MemAlloc(sizeof(BURN_LAUNCH_APPROVED_EXE), TRUE); + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &pLaunchApprovedExe->sczId); + ExitOnFailure(hr, "Failed to read approved exe id."); + + hr = BuffReadString(pbData, cbData, &iData, &pLaunchApprovedExe->sczArguments); + ExitOnFailure(hr, "Failed to read approved exe arguments."); + + hr = BuffReadNumber(pbData, cbData, &iData, &pLaunchApprovedExe->dwWaitForInputIdleTimeout); + ExitOnFailure(hr, "Failed to read approved exe WaitForInputIdle timeout."); + + hr = ApprovedExesFindById(pApprovedExes, pLaunchApprovedExe->sczId, &pApprovedExe); + ExitOnFailure(hr, "The per-user process requested unknown approved exe with id: %ls", pLaunchApprovedExe->sczId); + + LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_SEARCH, pApprovedExe->sczKey, pApprovedExe->sczValueName ? pApprovedExe->sczValueName : L"", pApprovedExe->fWin64 ? L"yes" : L"no"); + + if (pApprovedExe->fWin64) + { + samDesired |= KEY_WOW64_64KEY; + } + + hr = RegOpen(HKEY_LOCAL_MACHINE, pApprovedExe->sczKey, samDesired, &hKey); + ExitOnFailure(hr, "Failed to open the registry key for the approved exe path."); + + hr = RegReadString(hKey, pApprovedExe->sczValueName, &pLaunchApprovedExe->sczExecutablePath); + ExitOnFailure(hr, "Failed to read the value for the approved exe path."); + + hr = ApprovedExesVerifySecureLocation(pVariables, pLaunchApprovedExe); + ExitOnFailure(hr, "Failed to verify the executable path is in a secure location: %ls", pLaunchApprovedExe->sczExecutablePath); + if (S_FALSE == hr) + { + LogStringLine(REPORT_STANDARD, "The executable path is not in a secure location: %ls", pLaunchApprovedExe->sczExecutablePath); + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)); + } + + hr = ApprovedExesLaunch(pVariables, pLaunchApprovedExe, &dwProcessId); + ExitOnFailure(hr, "Failed to launch approved exe: %ls", pLaunchApprovedExe->sczExecutablePath); + + //send process id over pipe + hr = BuffWriteNumber(&pbSendData, &cbSendData, dwProcessId); + ExitOnFailure(hr, "Failed to write the approved exe process id to message buffer."); + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID, pbSendData, cbSendData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID message to per-user process."); + +LExit: + ReleaseBuffer(pbSendData); + ApprovedExesUninitializeLaunch(pLaunchApprovedExe); + return hr; +} diff --git a/src/engine/elevation.h b/src/engine/elevation.h new file mode 100644 index 00000000..d82d9b1c --- /dev/null +++ b/src/engine/elevation.h @@ -0,0 +1,178 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + + +// Parent (per-user process) side functions. +HRESULT ElevationElevate( + __in BURN_ENGINE_STATE* pEngineState, + __in_opt HWND hwndParent + ); +HRESULT ElevationApplyInitialize( + __in HANDLE hPipe, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_ACTION action, + __in BURN_AU_PAUSE_ACTION auAction, + __in BOOL fTakeSystemRestorePoint + ); +HRESULT ElevationApplyUninitialize( + __in HANDLE hPipe + ); +HRESULT ElevationSessionBegin( + __in HANDLE hPipe, + __in_z LPCWSTR wzEngineWorkingPath, + __in_z LPCWSTR wzResumeCommandLine, + __in BOOL fDisableResume, + __in BURN_VARIABLES* pVariables, + __in DWORD dwRegistrationOperations, + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction, + __in DWORD64 qwEstimatedSize + ); +HRESULT ElevationSessionResume( + __in HANDLE hPipe, + __in_z LPCWSTR wzResumeCommandLine, + __in BOOL fDisableResume, + __in BURN_VARIABLES* pVariables + ); +HRESULT ElevationSessionEnd( + __in HANDLE hPipe, + __in BURN_RESUME_MODE resumeMode, + __in BOOTSTRAPPER_APPLY_RESTART restart, + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction + ); +HRESULT ElevationSaveState( + __in HANDLE hPipe, + __in_bcount(cbBuffer) BYTE* pbBuffer, + __in SIZE_T cbBuffer + ); +HRESULT ElevationLayoutBundle( + __in HANDLE hPipe, + __in_z LPCWSTR wzLayoutDirectory, + __in_z LPCWSTR wzUnverifiedPath + ); +HRESULT ElevationCacheOrLayoutContainerOrPayload( + __in HANDLE hPipe, + __in_opt BURN_CONTAINER* pContainer, + __in_opt BURN_PACKAGE* pPackage, + __in_opt BURN_PAYLOAD* pPayload, + __in_z_opt LPCWSTR wzLayoutDirectory, + __in_z LPCWSTR wzUnverifiedPath, + __in BOOL fMove + ); +HRESULT ElevationCacheCleanup( + __in HANDLE hPipe + ); +HRESULT ElevationProcessDependentRegistration( + __in HANDLE hPipe, + __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction + ); +HRESULT ElevationExecuteExePackage( + __in HANDLE hPipe, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +HRESULT ElevationExecuteMsiPackage( + __in HANDLE hPipe, + __in_opt HWND hwndParent, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +HRESULT ElevationExecuteMspPackage( + __in HANDLE hPipe, + __in_opt HWND hwndParent, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +HRESULT ElevationExecuteMsuPackage( + __in HANDLE hPipe, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BOOL fRollback, + __in BOOL fStopWusaService, + __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +HRESULT ElevationExecutePackageProviderAction( + __in HANDLE hPipe, + __in BURN_EXECUTE_ACTION* pExecuteAction + ); +HRESULT ElevationExecutePackageDependencyAction( + __in HANDLE hPipe, + __in BURN_EXECUTE_ACTION* pExecuteAction + ); +HRESULT ElevationLoadCompatiblePackageAction( + __in HANDLE hPipe, + __in BURN_EXECUTE_ACTION* pExecuteAction + ); +HRESULT ElevationLaunchElevatedChild( + __in HANDLE hPipe, + __in BURN_PACKAGE* pPackage, + __in LPCWSTR wzPipeName, + __in LPCWSTR wzPipeToken, + __out DWORD* pdwChildPid + ); +HRESULT ElevationCleanPackage( + __in HANDLE hPipe, + __in BURN_PACKAGE* pPackage + ); +HRESULT ElevationLaunchApprovedExe( + __in HANDLE hPipe, + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe, + __out DWORD* pdwProcessId + ); + +// Child (per-machine process) side functions. +HRESULT ElevationChildPumpMessages( + __in DWORD dwLoggingTlsId, + __in HANDLE hPipe, + __in HANDLE hCachePipe, + __in BURN_APPROVED_EXES* pApprovedExes, + __in BURN_CONTAINERS* pContainers, + __in BURN_PACKAGES* pPackages, + __in BURN_PAYLOADS* pPayloads, + __in BURN_VARIABLES* pVariables, + __in BURN_REGISTRATION* pRegistration, + __in BURN_USER_EXPERIENCE* pUserExperience, + __out HANDLE* phLock, + __out BOOL* pfDisabledAutomaticUpdates, + __out DWORD* pdwChildExitCode, + __out BOOL* pfRestart + ); +HRESULT ElevationChildResumeAutomaticUpdates(); + + +HRESULT ElevationMsiBeginTransaction( + __in HANDLE hPipe, + __in_opt HWND hwndParent, + __in LPVOID pvContext +); +HRESULT ElevationMsiCommitTransaction( + __in HANDLE hPipe, + __in_opt HWND hwndParent, + __in LPVOID pvContext +); +HRESULT ElevationMsiRollbackTransaction( + __in HANDLE hPipe, + __in_opt HWND hwndParent, + __in LPVOID pvContext +); + +#ifdef __cplusplus +} +#endif diff --git a/src/engine/embedded.cpp b/src/engine/embedded.cpp new file mode 100644 index 00000000..09666980 --- /dev/null +++ b/src/engine/embedded.cpp @@ -0,0 +1,197 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// struct + +struct BURN_EMBEDDED_CALLBACK_CONTEXT +{ + PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler; + LPVOID pvContext; +}; + +// internal function declarations + +static HRESULT ProcessEmbeddedMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); +static HRESULT OnEmbeddedErrorMessage( + __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __in_bcount(cbData) BYTE* pbData, + __in DWORD cbData, + __out DWORD* pdwResult + ); +static HRESULT OnEmbeddedProgress( + __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __in_bcount(cbData) BYTE* pbData, + __in DWORD cbData, + __out DWORD* pdwResult + ); + +// function definitions + +/******************************************************************* + EmbeddedLaunchChildProcess - + +*******************************************************************/ +extern "C" HRESULT EmbeddedRunBundle( + __in LPCWSTR wzExecutablePath, + __in LPCWSTR wzArguments, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __out DWORD* pdwExitCode + ) +{ + HRESULT hr = S_OK; + DWORD dwCurrentProcessId = ::GetCurrentProcessId(); + HANDLE hCreatedPipesEvent = NULL; + LPWSTR sczCommand = NULL; + STARTUPINFOW si = { }; + PROCESS_INFORMATION pi = { }; + BURN_PIPE_RESULT result = { }; + + BURN_PIPE_CONNECTION connection = { }; + PipeConnectionInitialize(&connection); + + BURN_EMBEDDED_CALLBACK_CONTEXT context = { }; + context.pfnGenericMessageHandler = pfnGenericMessageHandler; + context.pvContext = pvContext; + + hr = PipeCreateNameAndSecret(&connection.sczName, &connection.sczSecret); + ExitOnFailure(hr, "Failed to create embedded pipe name and client token."); + + hr = PipeCreatePipes(&connection, FALSE, &hCreatedPipesEvent); + ExitOnFailure(hr, "Failed to create embedded pipe."); + + hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls %ls %ls %u", wzArguments, BURN_COMMANDLINE_SWITCH_EMBEDDED, connection.sczName, connection.sczSecret, dwCurrentProcessId); + ExitOnFailure(hr, "Failed to allocate embedded command."); + + if (!::CreateProcessW(wzExecutablePath, sczCommand, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) + { + ExitWithLastError(hr, "Failed to create embedded process at path: %ls", wzExecutablePath); + } + + connection.dwProcessId = ::GetProcessId(pi.hProcess); + connection.hProcess = pi.hProcess; + pi.hProcess = NULL; + + hr = PipeWaitForChildConnect(&connection); + ExitOnFailure(hr, "Failed to wait for embedded process to connect to pipe."); + + hr = PipePumpMessages(connection.hPipe, ProcessEmbeddedMessages, &context, &result); + ExitOnFailure(hr, "Failed to process messages from embedded message."); + + // Get the return code from the embedded process. + hr = ProcWaitForCompletion(connection.hProcess, INFINITE, pdwExitCode); + ExitOnFailure(hr, "Failed to wait for embedded executable: %ls", wzExecutablePath); + +LExit: + ReleaseHandle(pi.hThread); + ReleaseHandle(pi.hProcess); + + StrSecureZeroFreeString(sczCommand); + ReleaseHandle(hCreatedPipesEvent); + PipeConnectionUninitialize(&connection); + + return hr; +} + + +// internal function definitions + +static HRESULT ProcessEmbeddedMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + BURN_EMBEDDED_CALLBACK_CONTEXT* pContext = static_cast(pvContext); + DWORD dwResult = 0; + + // Process the message. + switch (pMsg->dwMessage) + { + case BURN_EMBEDDED_MESSAGE_TYPE_ERROR: + hr = OnEmbeddedErrorMessage(pContext->pfnGenericMessageHandler, pContext->pvContext, static_cast(pMsg->pvData), pMsg->cbData, &dwResult); + ExitOnFailure(hr, "Failed to process embedded error message."); + break; + + case BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS: + hr = OnEmbeddedProgress(pContext->pfnGenericMessageHandler, pContext->pvContext, static_cast(pMsg->pvData), pMsg->cbData, &dwResult); + ExitOnFailure(hr, "Failed to process embedded progress message."); + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Unexpected embedded message sent to child process, msg: %u", pMsg->dwMessage); + } + + *pdwResult = dwResult; + +LExit: + return hr; +} + +static HRESULT OnEmbeddedErrorMessage( + __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __in_bcount(cbData) BYTE* pbData, + __in DWORD cbData, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + DWORD iData = 0; + GENERIC_EXECUTE_MESSAGE message = { }; + LPWSTR sczMessage = NULL; + + message.type = GENERIC_EXECUTE_MESSAGE_ERROR; + + hr = BuffReadNumber(pbData, cbData, &iData, &message.error.dwErrorCode); + ExitOnFailure(hr, "Failed to read error code from buffer."); + + hr = BuffReadString(pbData, cbData, &iData, &sczMessage); + ExitOnFailure(hr, "Failed to read error message from buffer."); + + message.error.wzMessage = sczMessage; + + hr = BuffReadNumber(pbData, cbData, &iData, &message.dwAllowedResults); + ExitOnFailure(hr, "Failed to read UI hint from buffer."); + + *pdwResult = (DWORD)pfnMessageHandler(&message, pvContext); + +LExit: + ReleaseStr(sczMessage); + + return hr; +} + +static HRESULT OnEmbeddedProgress( + __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __in_bcount(cbData) BYTE* pbData, + __in DWORD cbData, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + DWORD iData = 0; + GENERIC_EXECUTE_MESSAGE message = { }; + + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; + message.dwAllowedResults = MB_OKCANCEL; + + hr = BuffReadNumber(pbData, cbData, &iData, &message.progress.dwPercentage); + ExitOnFailure(hr, "Failed to read progress from buffer."); + + *pdwResult = (DWORD)pfnMessageHandler(&message, pvContext); + +LExit: + return hr; +} diff --git a/src/engine/embedded.h b/src/engine/embedded.h new file mode 100644 index 00000000..08adeae0 --- /dev/null +++ b/src/engine/embedded.h @@ -0,0 +1,27 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum _BURN_EMBEDDED_MESSAGE_TYPE +{ + BURN_EMBEDDED_MESSAGE_TYPE_UNKNOWN, + BURN_EMBEDDED_MESSAGE_TYPE_ERROR, + BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS, +} BURN_EMBEDDED_MESSAGE_TYPE; + + +HRESULT EmbeddedRunBundle( + __in LPCWSTR wzExecutablePath, + __in LPCWSTR wzArguments, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __out DWORD* pdwExitCode + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp new file mode 100644 index 00000000..3c0f09c9 --- /dev/null +++ b/src/engine/engine.cpp @@ -0,0 +1,889 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// constants + +const DWORD RESTART_RETRIES = 10; + +// internal function declarations + +static HRESULT InitializeEngineState( + __in BURN_ENGINE_STATE* pEngineState, + __in HANDLE hEngineFile + ); +static void UninitializeEngineState( + __in BURN_ENGINE_STATE* pEngineState + ); +static HRESULT RunUntrusted( + __in LPCWSTR wzCommandLine, + __in BURN_ENGINE_STATE* pEngineState + ); +static HRESULT RunNormal( + __in HINSTANCE hInstance, + __in BURN_ENGINE_STATE* pEngineState + ); +static HRESULT RunElevated( + __in HINSTANCE hInstance, + __in LPCWSTR wzCommandLine, + __in BURN_ENGINE_STATE* pEngineState + ); +static HRESULT RunEmbedded( + __in HINSTANCE hInstance, + __in BURN_ENGINE_STATE* pEngineState + ); +static HRESULT RunRunOnce( + __in const BURN_REGISTRATION* pRegistration, + __in int nCmdShow + ); +static HRESULT RunApplication( + __in BURN_ENGINE_STATE* pEngineState, + __out BOOL* pfReloadApp + ); +static HRESULT ProcessMessage( + __in BURN_ENGINE_STATE* pEngineState, + __in const MSG* pmsg + ); +static HRESULT DAPI RedirectLoggingOverPipe( + __in_z LPCSTR szString, + __in_opt LPVOID pvContext + ); +static HRESULT Restart(); + + +// function definitions + +extern "C" BOOL EngineInCleanRoom( + __in_z_opt LPCWSTR wzCommandLine + ) +{ + // Be very careful with the functions you call from here. + // This function will be called before ::SetDefaultDllDirectories() + // has been called so dependencies outside of kernel32.dll are + // very likely to introduce DLL hijacking opportunities. + + static DWORD cchCleanRoomSwitch = lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM); + + // This check is wholly dependent on the clean room command line switch being + // present at the beginning of the command line. Since Burn is the only thing + // that should be setting this command line option, that is in our control. + BOOL fInCleanRoom = (wzCommandLine && + (wzCommandLine[0] == L'-' || wzCommandLine[0] == L'/') && + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzCommandLine + 1, cchCleanRoomSwitch, BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, cchCleanRoomSwitch) && + wzCommandLine[1 + cchCleanRoomSwitch] == L'=' + ); + + return fInCleanRoom; +} + +extern "C" HRESULT EngineRun( + __in HINSTANCE hInstance, + __in HANDLE hEngineFile, + __in_z_opt LPCWSTR wzCommandLine, + __in int nCmdShow, + __out DWORD* pdwExitCode + ) +{ + HRESULT hr = S_OK; + BOOL fComInitialized = FALSE; + BOOL fLogInitialized = FALSE; + BOOL fCrypInitialized = FALSE; + BOOL fRegInitialized = FALSE; + BOOL fWiuInitialized = FALSE; + BOOL fXmlInitialized = FALSE; + OSVERSIONINFOEXW ovix = { }; + LPWSTR sczExePath = NULL; + BOOL fRunNormal = FALSE; + BOOL fRestart = FALSE; + + BURN_ENGINE_STATE engineState = { }; + + // Always initialize logging first + LogInitialize(::GetModuleHandleW(NULL)); + fLogInitialized = TRUE; + + // Ensure that log contains approriate level of information +#ifdef _DEBUG + LogSetLevel(REPORT_DEBUG, FALSE); +#else + LogSetLevel(REPORT_VERBOSE, FALSE); // FALSE means don't write an additional text line to the log saying the level changed +#endif + + hr = AppParseCommandLine(wzCommandLine, &engineState.argc, &engineState.argv); + ExitOnFailure(hr, "Failed to parse command line."); + + hr = InitializeEngineState(&engineState, hEngineFile); + ExitOnFailure(hr, "Failed to initialize engine state."); + + engineState.command.nCmdShow = nCmdShow; + + // initialize platform layer + PlatformInitialize(); + + // initialize COM + hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); + ExitOnFailure(hr, "Failed to initialize COM."); + fComInitialized = TRUE; + + // Initialize dutil. + hr = CrypInitialize(); + ExitOnFailure(hr, "Failed to initialize Cryputil."); + fCrypInitialized = TRUE; + + hr = RegInitialize(); + ExitOnFailure(hr, "Failed to initialize Regutil."); + fRegInitialized = TRUE; + + hr = WiuInitialize(); + ExitOnFailure(hr, "Failed to initialize Wiutil."); + fWiuInitialized = TRUE; + + hr = XmlInitialize(); + ExitOnFailure(hr, "Failed to initialize XML util."); + fXmlInitialized = TRUE; + + ovix.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); + if (!::GetVersionExW((LPOSVERSIONINFOW)&ovix)) + { + ExitWithLastError(hr, "Failed to get OS info."); + } + + PathForCurrentProcess(&sczExePath, NULL); // Ignore failure. + LogId(REPORT_STANDARD, MSG_BURN_INFO, szVerMajorMinorBuild, ovix.dwMajorVersion, ovix.dwMinorVersion, ovix.dwBuildNumber, ovix.wServicePackMajor, sczExePath); + ReleaseNullStr(sczExePath); + + // initialize core + hr = CoreInitialize(&engineState); + ExitOnFailure(hr, "Failed to initialize core."); + + // Select run mode. + switch (engineState.mode) + { + case BURN_MODE_UNTRUSTED: + hr = RunUntrusted(wzCommandLine, &engineState); + ExitOnFailure(hr, "Failed to run untrusted mode."); + break; + + case BURN_MODE_NORMAL: + fRunNormal = TRUE; + + hr = RunNormal(hInstance, &engineState); + ExitOnFailure(hr, "Failed to run per-user mode."); + break; + + case BURN_MODE_ELEVATED: + hr = RunElevated(hInstance, wzCommandLine, &engineState); + ExitOnFailure(hr, "Failed to run per-machine mode."); + break; + + case BURN_MODE_EMBEDDED: + fRunNormal = TRUE; + + hr = RunEmbedded(hInstance, &engineState); + ExitOnFailure(hr, "Failed to run embedded mode."); + break; + + case BURN_MODE_RUNONCE: + hr = RunRunOnce(&engineState.registration, nCmdShow); + ExitOnFailure(hr, "Failed to run RunOnce mode."); + break; + + default: + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Invalid run mode."); + } + + // set exit code and remember if we are supposed to restart. + *pdwExitCode = engineState.userExperience.dwExitCode; + fRestart = engineState.fRestart; + +LExit: + ReleaseStr(sczExePath); + + // If anything went wrong but the log was never open, try to open a "failure" log + // and that will dump anything captured in the log memory buffer to the log. + if (FAILED(hr) && BURN_LOGGING_STATE_CLOSED == engineState.log.state) + { + LoggingOpenFailed(); + } + + UserExperienceRemove(&engineState.userExperience); + + CacheRemoveWorkingFolder(engineState.registration.sczId); + CacheUninitialize(); + + // If this is a related bundle (but not an update) suppress restart and return the standard restart error code. + if (fRestart && BOOTSTRAPPER_RELATION_NONE != engineState.command.relationType && BOOTSTRAPPER_RELATION_UPDATE != engineState.command.relationType) + { + LogId(REPORT_STANDARD, MSG_RESTART_ABORTED, LoggingRelationTypeToString(engineState.command.relationType)); + + fRestart = FALSE; + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); + } + + UninitializeEngineState(&engineState); + + if (fXmlInitialized) + { + XmlUninitialize(); + } + + if (fWiuInitialized) + { + WiuUninitialize(); + } + + if (fRegInitialized) + { + RegUninitialize(); + } + + if (fCrypInitialized) + { + CrypUninitialize(); + } + + if (fComInitialized) + { + ::CoUninitialize(); + } + + if (fRunNormal) + { + LogId(REPORT_STANDARD, MSG_EXITING, FAILED(hr) ? (int)hr : *pdwExitCode, LoggingBoolToString(fRestart)); + + if (fRestart) + { + LogId(REPORT_STANDARD, MSG_RESTARTING); + } + } + + if (fLogInitialized) + { + LogClose(FALSE); + } + + if (fRestart) + { + Restart(); + } + + if (fLogInitialized) + { + LogUninitialize(FALSE); + } + + return hr; +} + + +// internal function definitions + +static HRESULT InitializeEngineState( + __in BURN_ENGINE_STATE* pEngineState, + __in HANDLE hEngineFile + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzParam = NULL; + HANDLE hSectionFile = hEngineFile; + HANDLE hSourceEngineFile = INVALID_HANDLE_VALUE; + + pEngineState->automaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED; + pEngineState->dwElevatedLoggingTlsId = TLS_OUT_OF_INDEXES; + ::InitializeCriticalSection(&pEngineState->csActive); + ::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive); + PipeConnectionInitialize(&pEngineState->companionConnection); + PipeConnectionInitialize(&pEngineState->embeddedConnection); + + for (int i = 0; i < pEngineState->argc; ++i) + { + if (pEngineState->argv[i][0] == L'-') + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &pEngineState->argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED), BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED))) + { + wzParam = &pEngineState->argv[i][2 + lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED)]; + if (L'=' != wzParam[-1] || L'\0' == wzParam[0]) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED); + } + + hr = StrStringToUInt32(wzParam, 0, reinterpret_cast(&hSourceEngineFile)); + ExitOnFailure(hr, "Failed to parse file handle: '%ls'", (wzParam)); + } + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &pEngineState->argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF), BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF))) + { + wzParam = &pEngineState->argv[i][2 + lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF)]; + if (L'=' != wzParam[-1] || L'\0' == wzParam[0]) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF); + } + + hr = StrStringToUInt32(wzParam, 0, reinterpret_cast(&hSectionFile)); + ExitOnFailure(hr, "Failed to parse file handle: '%ls'", (wzParam)); + } + } + } + + hr = SectionInitialize(&pEngineState->section, hSectionFile, hSourceEngineFile); + ExitOnFailure(hr, "Failed to initialize engine section."); + +LExit: + return hr; +} + +static void UninitializeEngineState( + __in BURN_ENGINE_STATE* pEngineState + ) +{ + if (pEngineState->argv) + { + AppFreeCommandLineArgs(pEngineState->argv); + } + + ReleaseStr(pEngineState->sczIgnoreDependencies); + + PipeConnectionUninitialize(&pEngineState->embeddedConnection); + PipeConnectionUninitialize(&pEngineState->companionConnection); + ReleaseStr(pEngineState->sczBundleEngineWorkingPath) + + ReleaseHandle(pEngineState->hMessageWindowThread); + + ::DeleteCriticalSection(&pEngineState->userExperience.csEngineActive); + UserExperienceUninitialize(&pEngineState->userExperience); + + ApprovedExesUninitialize(&pEngineState->approvedExes); + UpdateUninitialize(&pEngineState->update); + VariablesUninitialize(&pEngineState->variables); + SearchesUninitialize(&pEngineState->searches); + RegistrationUninitialize(&pEngineState->registration); + PayloadsUninitialize(&pEngineState->payloads); + PackagesUninitialize(&pEngineState->packages); + CatalogUninitialize(&pEngineState->catalogs); + SectionUninitialize(&pEngineState->section); + ContainersUninitialize(&pEngineState->containers); + + ReleaseStr(pEngineState->command.wzLayoutDirectory); + ReleaseStr(pEngineState->command.wzCommandLine); + + ReleaseStr(pEngineState->log.sczExtension); + ReleaseStr(pEngineState->log.sczPrefix); + ReleaseStr(pEngineState->log.sczPath); + ReleaseStr(pEngineState->log.sczPathVariable); + + if (TLS_OUT_OF_INDEXES != pEngineState->dwElevatedLoggingTlsId) + { + ::TlsFree(pEngineState->dwElevatedLoggingTlsId); + } + + ::DeleteCriticalSection(&pEngineState->csActive); + + // clear struct + memset(pEngineState, 0, sizeof(BURN_ENGINE_STATE)); +} + +static HRESULT RunUntrusted( + __in LPCWSTR wzCommandLine, + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCurrentProcessPath = NULL; + LPWSTR wzCleanRoomBundlePath = NULL; + LPWSTR sczCachedCleanRoomBundlePath = NULL; + LPWSTR sczParameters = NULL; + LPWSTR sczFullCommandLine = NULL; + STARTUPINFOW si = { }; + PROCESS_INFORMATION pi = { }; + HANDLE hFileAttached = NULL; + HANDLE hFileSelf = NULL; + HANDLE hProcess = NULL; + + hr = PathForCurrentProcess(&sczCurrentProcessPath, NULL); + ExitOnFailure(hr, "Failed to get path for current process."); + + BOOL fRunningFromCache = CacheBundleRunningFromCache(); + + // If we're running from the package cache, we're in a secure + // folder (DLLs cannot be inserted here for hijacking purposes) + // so just launch the current process's path as the clean room + // process. Technically speaking, we'd be able to skip creating + // a clean room process at all (since we're already running from + // a secure folder) but it makes the code that only wants to run + // in clean room more complicated if we don't launch an explicit + // clean room process. + if (fRunningFromCache) + { + wzCleanRoomBundlePath = sczCurrentProcessPath; + } + else + { + hr = CacheBundleToCleanRoom(&pEngineState->userExperience.payloads, &pEngineState->section, &sczCachedCleanRoomBundlePath); + ExitOnFailure(hr, "Failed to cache to clean room."); + + wzCleanRoomBundlePath = sczCachedCleanRoomBundlePath; + } + + // The clean room switch must always be at the front of the command line so + // the EngineInCleanRoom function will operate correctly. + hr = StrAllocFormatted(&sczParameters, L"-%ls=\"%ls\"", BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, sczCurrentProcessPath); + ExitOnFailure(hr, "Failed to allocate parameters for unelevated process."); + + // Send a file handle for the child Burn process to access the attached container. + hr = CoreAppendFileHandleAttachedToCommandLine(pEngineState->section.hEngineFile, &hFileAttached, &sczParameters); + ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED); + + // Grab a file handle for the child Burn process. + hr = CoreAppendFileHandleSelfToCommandLine(wzCleanRoomBundlePath, &hFileSelf, &sczParameters, NULL); + ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF); + + hr = StrAllocFormattedSecure(&sczParameters, L"%ls %ls", sczParameters, wzCommandLine); + ExitOnFailure(hr, "Failed to append original command line."); + +#ifdef ENABLE_UNELEVATE + // TODO: Pass file handle to unelevated process if this ever gets reenabled. + if (!pEngineState->fDisableUnelevate) + { + // Try to launch unelevated and if that fails for any reason, we'll launch our process normally (even though that may make it elevated). + hr = ProcExecuteAsInteractiveUser(wzCleanRoomBundlePath, sczParameters, &hProcess); + } +#endif + + if (!hProcess) + { + hr = StrAllocFormattedSecure(&sczFullCommandLine, L"\"%ls\" %ls", wzCleanRoomBundlePath, sczParameters); + ExitOnFailure(hr, "Failed to allocate full command-line."); + + si.cb = sizeof(si); + si.wShowWindow = static_cast(pEngineState->command.nCmdShow); + if (!::CreateProcessW(wzCleanRoomBundlePath, sczFullCommandLine, NULL, NULL, TRUE, 0, 0, NULL, &si, &pi)) + { + ExitWithLastError(hr, "Failed to launch clean room process: %ls", sczFullCommandLine); + } + + hProcess = pi.hProcess; + pi.hProcess = NULL; + } + + hr = ProcWaitForCompletion(hProcess, INFINITE, &pEngineState->userExperience.dwExitCode); + ExitOnFailure(hr, "Failed to wait for clean room process: %ls", wzCleanRoomBundlePath); + +LExit: + ReleaseHandle(pi.hThread); + ReleaseFileHandle(hFileSelf); + ReleaseFileHandle(hFileAttached); + ReleaseHandle(hProcess); + StrSecureZeroFreeString(sczFullCommandLine); + StrSecureZeroFreeString(sczParameters); + ReleaseStr(sczCachedCleanRoomBundlePath); + ReleaseStr(sczCurrentProcessPath); + + return hr; +} + +static HRESULT RunNormal( + __in HINSTANCE hInstance, + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + HANDLE hPipesCreatedEvent = NULL; + BOOL fContinueExecution = TRUE; + BOOL fReloadApp = FALSE; + + // Initialize logging. + hr = LoggingOpen(&pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->registration.sczDisplayName); + ExitOnFailure(hr, "Failed to open log."); + + // Ensure we're on a supported operating system. + hr = ConditionGlobalCheck(&pEngineState->variables, &pEngineState->condition, pEngineState->command.display, pEngineState->registration.sczDisplayName, &pEngineState->userExperience.dwExitCode, &fContinueExecution); + ExitOnFailure(hr, "Failed to check global conditions"); + + if (!fContinueExecution) + { + LogId(REPORT_STANDARD, MSG_FAILED_CONDITION_CHECK); + + // If the block told us to abort, abort! + ExitFunction1(hr = S_OK); + } + + if (pEngineState->userExperience.fSplashScreen && BOOTSTRAPPER_DISPLAY_NONE < pEngineState->command.display) + { + SplashScreenCreate(hInstance, NULL, &pEngineState->command.hwndSplashScreen); + } + + // Create a top-level window to handle system messages. + hr = UiCreateMessageWindow(hInstance, pEngineState); + ExitOnFailure(hr, "Failed to create the message window."); + + // Query registration state. + hr = CoreQueryRegistration(pEngineState); + ExitOnFailure(hr, "Failed to query registration."); + + // Set some built-in variables before loading the BA. + hr = PlanSetVariables(pEngineState->command.action, &pEngineState->variables); + ExitOnFailure(hr, "Failed to set action variables."); + + hr = RegistrationSetVariables(&pEngineState->registration, &pEngineState->variables); + ExitOnFailure(hr, "Failed to set registration variables."); + + // If a layout directory was specified on the command-line, set it as a well-known variable. + if (pEngineState->command.wzLayoutDirectory && *pEngineState->command.wzLayoutDirectory) + { + hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_LAYOUT_DIRECTORY, pEngineState->command.wzLayoutDirectory, FALSE); + ExitOnFailure(hr, "Failed to set layout directory variable to value provided from command-line."); + } + + do + { + fReloadApp = FALSE; + + hr = RunApplication(pEngineState, &fReloadApp); + ExitOnFailure(hr, "Failed while running "); + } while (fReloadApp); + +LExit: + // If the message window is still around, close it. + UiCloseMessageWindow(pEngineState); + + VariablesDump(&pEngineState->variables); + + // end per-machine process if running + if (INVALID_HANDLE_VALUE != pEngineState->companionConnection.hPipe) + { + PipeTerminateChildProcess(&pEngineState->companionConnection, pEngineState->userExperience.dwExitCode, FALSE); + } + + // If the splash screen is still around, close it. + if (::IsWindow(pEngineState->command.hwndSplashScreen)) + { + ::PostMessageW(pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0); + } + + ReleaseHandle(hPipesCreatedEvent); + + return hr; +} + +static HRESULT RunElevated( + __in HINSTANCE hInstance, + __in LPCWSTR /*wzCommandLine*/, + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + HANDLE hLock = NULL; + BOOL fDisabledAutomaticUpdates = FALSE; + + // connect to per-user process + hr = PipeChildConnect(&pEngineState->companionConnection, TRUE); + ExitOnFailure(hr, "Failed to connect to unelevated process."); + + // Set up the thread local storage to store the correct pipe to communicate logging then + // override logging to write over the pipe. + pEngineState->dwElevatedLoggingTlsId = ::TlsAlloc(); + if (TLS_OUT_OF_INDEXES == pEngineState->dwElevatedLoggingTlsId) + { + ExitWithLastError(hr, "Failed to allocate thread local storage for logging."); + } + + if (!::TlsSetValue(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe)) + { + ExitWithLastError(hr, "Failed to set elevated pipe into thread local storage for logging."); + } + + LogRedirect(RedirectLoggingOverPipe, pEngineState); + + // Create a top-level window to prevent shutting down the elevated process. + hr = UiCreateMessageWindow(hInstance, pEngineState); + ExitOnFailure(hr, "Failed to create the message window."); + + SrpInitialize(TRUE); + + // Pump messages from parent process. + hr = ElevationChildPumpMessages(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe, pEngineState->companionConnection.hCachePipe, &pEngineState->approvedExes, &pEngineState->containers, &pEngineState->packages, &pEngineState->payloads, &pEngineState->variables, &pEngineState->registration, &pEngineState->userExperience, &hLock, &fDisabledAutomaticUpdates, &pEngineState->userExperience.dwExitCode, &pEngineState->fRestart); + LogRedirect(NULL, NULL); // reset logging so the next failure gets written to "log buffer" for the failure log. + ExitOnFailure(hr, "Failed to pump messages from parent process."); + +LExit: + LogRedirect(NULL, NULL); // we're done talking to the child so always reset logging now. + + // If the message window is still around, close it. + UiCloseMessageWindow(pEngineState); + + if (fDisabledAutomaticUpdates) + { + ElevationChildResumeAutomaticUpdates(); + } + + if (hLock) + { + ::ReleaseMutex(hLock); + ::CloseHandle(hLock); + } + + return hr; +} + +static HRESULT RunEmbedded( + __in HINSTANCE hInstance, + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + + // Disable system restore since the parent bundle may have done it. + pEngineState->fDisableSystemRestore = TRUE; + + // Connect to parent process. + hr = PipeChildConnect(&pEngineState->embeddedConnection, FALSE); + ExitOnFailure(hr, "Failed to connect to parent of embedded process."); + + // Do not register the bundle to automatically restart if embedded. + if (BOOTSTRAPPER_DISPLAY_EMBEDDED == pEngineState->command.display) + { + pEngineState->registration.fDisableResume = TRUE; + } + + // Now run the application like normal. + hr = RunNormal(hInstance, pEngineState); + ExitOnFailure(hr, "Failed to run bootstrapper application embedded."); + +LExit: + return hr; +} + +static HRESULT RunRunOnce( + __in const BURN_REGISTRATION* pRegistration, + __in int nCmdShow + ) +{ + HRESULT hr = S_OK; + LPWSTR sczNewCommandLine = NULL; + LPWSTR sczBurnPath = NULL; + HANDLE hProcess = NULL; + + hr = RegistrationGetResumeCommandLine(pRegistration, &sczNewCommandLine); + ExitOnFailure(hr, "Unable to get resume command line from the registry"); + + // and re-launch + hr = PathForCurrentProcess(&sczBurnPath, NULL); + ExitOnFailure(hr, "Failed to get current process path."); + + hr = ProcExec(sczBurnPath, 0 < sczNewCommandLine ? sczNewCommandLine : L"", nCmdShow, &hProcess); + ExitOnFailure(hr, "Failed to re-launch bundle process after RunOnce: %ls", sczBurnPath); + +LExit: + ReleaseHandle(hProcess); + ReleaseStr(sczNewCommandLine); + ReleaseStr(sczBurnPath); + + return hr; +} + +static HRESULT RunApplication( + __in BURN_ENGINE_STATE* pEngineState, + __out BOOL* pfReloadApp + ) +{ + HRESULT hr = S_OK; + BOOTSTRAPPER_ENGINE_CONTEXT engineContext = { }; + BOOL fStartupCalled = FALSE; + BOOL fRet = FALSE; + MSG msg = { }; + BOOTSTRAPPER_SHUTDOWN_ACTION shutdownAction = BOOTSTRAPPER_SHUTDOWN_ACTION_NONE; + + ::PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + + // Setup the bootstrapper engine. + engineContext.dwThreadId = ::GetCurrentThreadId(); + engineContext.pEngineState = pEngineState; + + // Load the bootstrapper application. + hr = UserExperienceLoad(&pEngineState->userExperience, &engineContext, &pEngineState->command); + ExitOnFailure(hr, "Failed to load BA."); + + fStartupCalled = TRUE; + hr = UserExperienceOnStartup(&pEngineState->userExperience); + ExitOnFailure(hr, "Failed to start bootstrapper application."); + + // Enter the message pump. + while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) + { + if (-1 == fRet) + { + hr = E_UNEXPECTED; + ExitOnRootFailure(hr, "Unexpected return value from message pump."); + } + else + { + ProcessMessage(pEngineState, &msg); + } + } + + // Get exit code. + pEngineState->userExperience.dwExitCode = (DWORD)msg.wParam; + +LExit: + if (fStartupCalled) + { + UserExperienceOnShutdown(&pEngineState->userExperience, &shutdownAction); + if (BOOTSTRAPPER_SHUTDOWN_ACTION_RESTART == shutdownAction) + { + LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RESTART, LoggingBoolToString(pEngineState->fRestart)); + pEngineState->fRestart = TRUE; + } + else if (BOOTSTRAPPER_SHUTDOWN_ACTION_RELOAD_BOOTSTRAPPER == shutdownAction) + { + LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RELOAD); + *pfReloadApp = TRUE; + } + } + + // Unload BA. + UserExperienceUnload(&pEngineState->userExperience); + + return hr; +} + +static HRESULT ProcessMessage( + __in BURN_ENGINE_STATE* pEngineState, + __in const MSG* pmsg + ) +{ + HRESULT hr = S_OK; + + switch (pmsg->message) + { + case WM_BURN_DETECT: + hr = CoreDetect(pEngineState, reinterpret_cast(pmsg->lParam)); + break; + + case WM_BURN_PLAN: + hr = CorePlan(pEngineState, static_cast(pmsg->lParam)); + break; + + case WM_BURN_ELEVATE: + hr = CoreElevate(pEngineState, reinterpret_cast(pmsg->lParam)); + break; + + case WM_BURN_APPLY: + hr = CoreApply(pEngineState, reinterpret_cast(pmsg->lParam)); + break; + + case WM_BURN_LAUNCH_APPROVED_EXE: + hr = CoreLaunchApprovedExe(pEngineState, reinterpret_cast(pmsg->lParam)); + break; + + case WM_BURN_QUIT: + hr = CoreQuit(pEngineState, static_cast(pmsg->wParam)); + break; + } + + return hr; +} + +static HRESULT DAPI RedirectLoggingOverPipe( + __in_z LPCSTR szString, + __in_opt LPVOID pvContext + ) +{ + static BOOL s_fCurrentlyLoggingToPipe = FALSE; + + HRESULT hr = S_OK; + BURN_ENGINE_STATE* pEngineState = static_cast(pvContext); + BOOL fStartedLogging = FALSE; + HANDLE hPipe = INVALID_HANDLE_VALUE; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + + // Prevent this function from being called recursively. + if (s_fCurrentlyLoggingToPipe) + { + ExitFunction(); + } + + s_fCurrentlyLoggingToPipe = TRUE; + fStartedLogging = TRUE; + + // Make sure the current thread set the pipe in TLS. + hPipe = ::TlsGetValue(pEngineState->dwElevatedLoggingTlsId); + if (!hPipe || INVALID_HANDLE_VALUE == hPipe) + { + hr = HRESULT_FROM_WIN32(ERROR_PIPE_NOT_CONNECTED); + ExitFunction(); + } + + // Do not log or use ExitOnFailure() macro here because they will be discarded + // by the recursive block at the top of this function. + hr = BuffWriteStringAnsi(&pbData, &cbData, szString); + if (SUCCEEDED(hr)) + { + hr = PipeSendMessage(hPipe, static_cast(BURN_PIPE_MESSAGE_TYPE_LOG), pbData, cbData, NULL, NULL, &dwResult); + if (SUCCEEDED(hr)) + { + hr = (HRESULT)dwResult; + } + } + +LExit: + ReleaseBuffer(pbData); + + // We started logging so remember to say we are no longer logging. + if (fStartedLogging) + { + s_fCurrentlyLoggingToPipe = FALSE; + } + + return hr; +} + +static HRESULT Restart() +{ + HRESULT hr = S_OK; + HANDLE hProcessToken = NULL; + TOKEN_PRIVILEGES priv = { }; + DWORD dwRetries = 0; + + if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken)) + { + ExitWithLastError(hr, "Failed to get process token."); + } + + priv.PrivilegeCount = 1; + priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + if (!::LookupPrivilegeValueW(NULL, L"SeShutdownPrivilege", &priv.Privileges[0].Luid)) + { + ExitWithLastError(hr, "Failed to get shutdown privilege LUID."); + } + + if (!::AdjustTokenPrivileges(hProcessToken, FALSE, &priv, sizeof(TOKEN_PRIVILEGES), NULL, 0)) + { + ExitWithLastError(hr, "Failed to adjust token to add shutdown privileges."); + } + + do + { + hr = S_OK; + + // Wait a second to let the companion process (assuming we did an elevated install) to get to the + // point where it too is thinking about restarting the computer. Only one will schedule the restart + // but both will have their log files closed and otherwise be ready to exit. + // + // On retry, we'll also wait a second to let the OS try to get to a place where the restart can + // be initiated. + ::Sleep(1000); + + if (!vpfnInitiateSystemShutdownExW(NULL, NULL, 0, FALSE, TRUE, SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_INSTALLATION | SHTDN_REASON_FLAG_PLANNED)) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + } + } while (dwRetries++ < RESTART_RETRIES && (HRESULT_FROM_WIN32(ERROR_MACHINE_LOCKED) == hr || HRESULT_FROM_WIN32(ERROR_NOT_READY) == hr)); + ExitOnRootFailure(hr, "Failed to schedule restart."); + +LExit: + ReleaseHandle(hProcessToken); + return hr; +} diff --git a/src/engine/engine.mc b/src/engine/engine.mc new file mode 100644 index 00000000..fb2dd6e9 --- /dev/null +++ b/src/engine/engine.mc @@ -0,0 +1,901 @@ +; // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +MessageIdTypedef=DWORD + +LanguageNames=(English=0x409:MSG00409) + + +; // message definitions + +; // MessageId=# +; // Severity=Success +; // SymbolicName=MSG_SUCCESS +; // Language=English +; // Success %1. +; // . +; +; // MessageId=# +; // Severity=Warning +; // SymbolicName=MSG_WARNING +; // Language=English +; // Warning %1. +; // . +; +; // MessageId=# +; // Severity=Error +; // SymbolicName=MSG_ERROR +; // Language=English +; // Error %1. +; // . + +MessageId=1 +Severity=Success +SymbolicName=MSG_BURN_INFO +Language=English +Burn v%1!hs!, Windows v%2!d!.%3!d! (Build %4!d!: Service Pack %5!d!), path: %6!ls! +. + +MessageId=2 +Severity=Warning +SymbolicName=MSG_BURN_UNKNOWN_PRIVATE_SWITCH +Language=English +Unknown burn internal command-line switch encountered: '%1!ls!'. +. + +MessageId=3 +Severity=Success +SymbolicName=MSG_BURN_RUN_BY_RELATED_BUNDLE +Language=English +This bundle is being run by a related bundle as type '%1!hs!'. +. + +MessageId=4 +Severity=Success +SymbolicName=MSG_BA_REQUESTED_RESTART +Language=English +Bootstrapper application requested restart at shutdown. Planned to restart already: %1!hs!. +. + +MessageId=5 +Severity=Warning +SymbolicName=MSG_RESTARTING +Language=English +Restarting computer... +======================================= +. + +MessageId=6 +Severity=Success +SymbolicName=MSG_BA_REQUESTED_RELOAD +Language=English +Bootstrapper application requested to be reloaded. +. + +MessageId=7 +Severity=Success +SymbolicName=MSG_EXITING +Language=English +Exit code: 0x%1!x!, restarting: %2!hs! +. + +MessageId=8 +Severity=Warning +SymbolicName=MSG_RESTART_ABORTED +Language=English +Preventing requested restart because bundle is related: '%1!hs!'. Returning restart requested to parent bundle. +. + +MessageId=9 +Severity=Success +SymbolicName=MSG_BURN_COMMAND_LINE +Language=English +Command Line: '%1!ls!' +. + +MessageId=10 +Severity=Success +SymbolicName=MSG_LAUNCH_ELEVATED_ENGINE_STARTING +Language=English +Launching elevated engine process. +. + +MessageId=11 +Severity=Success +SymbolicName=MSG_LAUNCH_ELEVATED_ENGINE_SUCCESS +Language=English +Launched elevated engine process. +. + +MessageId=12 +Severity=Success +SymbolicName=MSG_CONNECT_TO_ELEVATED_ENGINE_SUCCESS +Language=English +Connected to elevated engine. +. + +MessageId=51 +Severity=Error +SymbolicName=MSG_FAILED_PARSE_CONDITION +Language=English +Error %1!hs!. Failed to parse condition %2!ls!. Unexpected symbol at position %3!hs! +. + +MessageId=52 +Severity=Success +SymbolicName=MSG_CONDITION_RESULT +Language=English +Condition '%1!ls!' evaluates to %2!hs!. +. + +MessageId=53 +Severity=Error +SymbolicName=MSG_FAILED_CONDITION_CHECK +Language=English +Bundle global condition check didn't succeed - aborting without loading application. +. + +MessageId=54 +Severity=Error +SymbolicName=MSG_PAYLOAD_FILE_NOT_PRESENT +Language=English +Failed to resolve source for file: %2!ls!, error: %1!ls!. +. + +MessageId=55 +Severity=Warning +SymbolicName=MSG_CANNOT_LOAD_STATE_FILE +Language=English +Could not load or read state file: %2!ls!, error: 0x%1!x!. +. + +MessageId=56 +Severity=Error +SymbolicName=MSG_USER_CANCELED +Language=English +Application canceled operation: %2!ls!, error: %1!ls! +. + +MessageId=100 +Severity=Success +SymbolicName=MSG_DETECT_BEGIN +Language=English +Detect begin, %1!u! packages +. + +MessageId=101 +Severity=Success +SymbolicName=MSG_DETECTED_PACKAGE +Language=English +Detected package: %1!ls!, state: %2!hs!, cached: %3!hs! +. + +MessageId=102 +Severity=Success +SymbolicName=MSG_DETECTED_RELATED_BUNDLE +Language=English +Detected related bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!hs!, operation: %5!hs! +. + +MessageId=103 +Severity=Success +SymbolicName=MSG_DETECTED_RELATED_PACKAGE +Language=English +Detected related package: %1!ls!, scope: %2!hs!, version: %3!hs!, language: %4!u! operation: %5!hs! +. + +MessageId=104 +Severity=Success +SymbolicName=MSG_DETECTED_MSI_FEATURE +Language=English +Detected package: %1!ls!, feature: %2!ls!, state: %3!hs! +. + +MessageId=105 +Severity=Success +SymbolicName=MSG_DETECTED_MSP_TARGET +Language=English +Detected package: %1!ls! target: %2!ls!, state: %3!hs! +. + +MessageId=106 +Severity=Success +SymbolicName=MSG_DETECT_CALCULATE_PATCH_APPLICABILITY +Language=English +Calculating patch applicability for target product code: %1!ls!, context: %2!hs! +. + +MessageId=107 +Severity=Success +SymbolicName=MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE +Language=English +Detected forward compatible bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!hs!, enabled: %5!hs! +. + +MessageId=108 +Severity=Success +SymbolicName=MSG_DETECTED_COMPATIBLE_PACKAGE_FROM_PROVIDER +Language=English +Detected compatible package: %1!ls!, provider: %2!ls!, installed: %3!ls!, version: %4!ls!, chained: %5!ls! +. + +MessageId=120 +Severity=Warning +SymbolicName=MSG_DETECT_PACKAGE_NOT_FULLY_CACHED +Language=English +Detected partially cached package: %1!ls!, invalid payload: %2!ls!, reason: 0x%3!x! +. + +MessageId=121 +Severity=Warning +SymbolicName=MSG_DETECT_FAILED_CALCULATE_PATCH_APPLICABILITY +Language=English +Could not calculate patch applicability for target product code: %1!ls!, context: %2!hs!, reason: 0x%3!x! +. + +MessageId=151 +Severity=Error +SymbolicName=MSG_FAILED_DETECT_PACKAGE +Language=English +Detect failed for package: %2!ls!, error: %1!ls! +. + +MessageId=152 +Severity=Error +SymbolicName=MSG_FAILED_READ_RELATED_PACKAGE_LANGUAGE +Language=English +Detected related package: %2!ls!, but failed to read language: %3!hs!, error: 0x%1!x! +. + +MessageId=170 +Severity=Warning +SymbolicName=MSG_DETECT_BAD_PRODUCT_CONFIGURATION +Language=English +Detected bad configuration for product: %1!ls! +. + +MessageId=199 +Severity=Success +SymbolicName=MSG_DETECT_COMPLETE +Language=English +Detect complete, result: 0x%1!x! +. + +MessageId=200 +Severity=Success +SymbolicName=MSG_PLAN_BEGIN +Language=English +Plan begin, %1!u! packages, action: %2!hs! +. + +MessageId=201 +Severity=Success +SymbolicName=MSG_PLANNED_PACKAGE +Language=English +Planned package: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, cache: %7!hs!, uncache: %8!hs!, dependency: %9!hs! +. + +MessageId=202 +Severity=Success +SymbolicName=MSG_PLANNED_BUNDLE_UX_CHANGED_REQUEST +Language=English +Planned bundle: %1!ls!, ba requested state: %2!hs! over default: %3!hs! +. + +MessageId=203 +Severity=Success +SymbolicName=MSG_PLANNED_MSI_FEATURE +Language=English +Planned feature: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute action: %5!hs!, rollback action: %6!hs! +. + +MessageId=204 +Severity=Success +SymbolicName=MSG_PLAN_MSI_FEATURES +Language=English +Plan %1!u! msi features for package: %2!ls! +. + +MessageId=205 +Severity=Warning +SymbolicName=MSG_PLAN_SKIP_PATCH_ACTION +Language=English +Plan %5!hs! skipped patch: %1!ls!, action: %2!hs! because chained target package: %3!ls! being uninstalled +. + +MessageId=206 +Severity=Warning +SymbolicName=MSG_PLAN_SKIP_SLIPSTREAM_ACTION +Language=English +Plan %5!hs! skipped patch: %1!ls!, action: %2!hs! because slipstreamed into chained target package: %3!ls!, action: %4!hs! +. + +MessageId=207 +Severity=Success +SymbolicName=MSG_PLANNED_RELATED_BUNDLE +Language=English +Planned related bundle: %1!ls!, type: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, dependency: %7!hs! +. + +MessageId=208 +Severity=Warning +SymbolicName=MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE +Language=English +Plan disabled rollback for package: %1!ls!, due to incomplete cache: %2!hs!, original rollback action: %3!hs! +. + +MessageId=209 +Severity=Warning +SymbolicName=MSG_PLAN_SKIPPED_PROVIDER_KEY_REMOVAL +Language=English +Plan skipped removal of provider key: %1!ls! because it is registered to a different bundle: %2!ls! +. + +MessageId=210 +Severity=Warning +SymbolicName=MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS +Language=English +Plan skipped due to %1!u! remaining dependents +. + +MessageId=211 +Severity=Success +SymbolicName=MSG_PLANNED_UPGRADE_BUNDLE +Language=English +Planned upgrade bundle: %1!ls!, default requested: %2!hs!, ba requested: %3!hs!, execute: %4!hs!, rollback: %5!hs!, dependency: %6!hs! +. + +MessageId=212 +Severity=Success +SymbolicName=MSG_PLANNED_FORWARD_COMPATIBLE_BUNDLE +Language=English +Planned forward compatible bundle: %1!ls!, default requested: %2!hs!, ba requested: %3!hs!, execute: %4!hs!, rollback: %5!hs!, dependency: %6!hs! +. + +MessageId=213 +Severity=Success +SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_DEPENDENT +Language=English +Plan skipped related bundle: %1!ls!, type: %2!hs!, because it was dependent and the current bundle is being executed as type: %3!hs!. +. + +MessageId=214 +Severity=Success +SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_SCHEDULED +Language=English +Plan skipped related bundle: %1!ls!, type: %2!hs!, because it was previously scheduled. +. + +MessageId=215 +Severity=Success +SymbolicName=MSG_PLANNED_ORPHAN_PACKAGE_FROM_PROVIDER +Language=English +Will remove orphan package: %1!ls!, installed: %2!ls!, chained: %3!ls! +. + +MessageId=216 +Severity=Success +SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER +Language=English +Plan skipped related bundle: %1!ls!, type: %2!hs!, provider key: %3!ls!, because an embedded bundle with the same provider key is being installed. +. + +MessageId=217 +Severity=Success +SymbolicName=MSG_PLAN_SKIPPED_DEPENDENT_BUNDLE_REPAIR +Language=English +Plan skipped dependent bundle repair: %1!ls!, type: %2!hs!, because no packages are being executed during this uninstall operation. +. + +MessageId=299 +Severity=Success +SymbolicName=MSG_PLAN_COMPLETE +Language=English +Plan complete, result: 0x%1!x! +. + +MessageId=300 +Severity=Success +SymbolicName=MSG_APPLY_BEGIN +Language=English +Apply begin +. + +MessageId=301 +Severity=Success +SymbolicName=MSG_APPLYING_PACKAGE +Language=English +Applying %1!hs! package: %2!ls!, action: %3!hs!, path: %4!ls!, arguments: '%5!ls!' +. + +MessageId=302 +Severity=Success +SymbolicName=MSG_ACQUIRED_PAYLOAD +Language=English +Acquired payload: %1!ls! to working path: %2!ls! from: %4!ls!. +. + +MessageId=304 +Severity=Success +SymbolicName=MSG_VERIFIED_EXISTING_PAYLOAD +Language=English +Verified existing payload: %1!ls! at path: %2!ls!. +. + +MessageId=305 +Severity=Success +SymbolicName=MSG_VERIFIED_ACQUIRED_PAYLOAD +Language=English +Verified acquired payload: %1!ls! at path: %2!ls!, %3!hs! to: %4!ls!. +. + +MessageId=306 +Severity=Success +SymbolicName=MSG_APPLYING_PATCH_PACKAGE +Language=English +Applying package: %1!ls!, target: %5!ls!, action: %2!hs!, path: %3!ls!, arguments: '%4!ls!' +. + +MessageId=307 +Severity=Warning +SymbolicName=MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE +Language=English +Attempted to uninstall absent package: %1!ls!. Continuing... +. + +MessageId=308 +Severity=Warning +SymbolicName=MSG_FAILED_PAUSE_AU +Language=English +Automatic updates could not be paused due to error: 0x%1!x!. Continuing... +. + +MessageId=309 +Severity=Warning +SymbolicName=MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE +Language=English +Skipping apply of package: %1!ls! due to cache error: 0x%2!x!. Continuing... +. + +MessageId=310 +Severity=Error +SymbolicName=MSG_FAILED_VERIFY_PAYLOAD +Language=English +Failed to verify payload: %2!ls! at path: %3!ls!, error: %1!ls!. Deleting file. +. + +MessageId=311 +Severity=Error +SymbolicName=MSG_FAILED_ACQUIRE_CONTAINER +Language=English +Failed to acquire container: %2!ls! to working path: %3!ls!, error: %1!ls!. +. + +MessageId=312 +Severity=Error +SymbolicName=MSG_FAILED_EXTRACT_CONTAINER +Language=English +Failed to extract payloads from container: %2!ls! to working path: %3!ls!, error: %1!ls!. +. + +MessageId=313 +Severity=Error +SymbolicName=MSG_FAILED_ACQUIRE_PAYLOAD +Language=English +Failed to acquire payload: %2!ls! to working path: %3!ls!, error: %1!ls!. +. + +MessageId=314 +Severity=Error +SymbolicName=MSG_FAILED_CACHE_PAYLOAD +Language=English +Failed to cache payload: %2!ls! from working path: %3!ls!, error: %1!ls!. +. + +MessageId=315 +Severity=Error +SymbolicName=MSG_FAILED_LAYOUT_BUNDLE +Language=English +Failed to layout bundle: %2!ls! to layout directory: %3!ls!, error: %1!ls!. +. + +MessageId=316 +Severity=Error +SymbolicName=MSG_FAILED_LAYOUT_CONTAINER +Language=English +Failed to layout container: %2!ls! to layout directory: %3!ls!, error: %1!ls!. +. + + +MessageId=317 +Severity=Error +SymbolicName=MSG_FAILED_LAYOUT_PAYLOAD +Language=English +Failed to layout payload: %2!ls! to layout directory: %3!ls!, error: %1!ls!. +. + +MessageId=318 +Severity=Success +SymbolicName=MSG_ROLLBACK_PACKAGE_SKIPPED +Language=English +Skipped rollback of package: %1!ls!, action: %2!hs!, already: %3!hs! +. + +MessageId=319 +Severity=Success +SymbolicName=MSG_APPLY_COMPLETED_PACKAGE +Language=English +Applied %1!hs! package: %2!ls!, result: 0x%3!x!, restart: %4!hs! +. + +MessageId=320 +Severity=Success +SymbolicName=MSG_DEPENDENCY_BUNDLE_REGISTER +Language=English +Registering bundle dependency provider: %1!ls!, version: %2!ls! +. + +MessageId=321 +Severity=Warning +SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_NOPROVIDERS +Language=English +Skipping dependency registration on package with no dependency providers: %1!ls! +. + +MessageId=322 +Severity=Warning +SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE +Language=English +Skipping cross-scope dependency registration on package: %1!ls!, bundle scope: %2!hs!, package scope: %3!hs! +. + +MessageId=323 +Severity=Success +SymbolicName=MSG_DEPENDENCY_PACKAGE_REGISTER +Language=English +Registering package dependency provider: %1!ls!, version: %2!ls!, package: %3!ls! +. + +MessageId=324 +Severity=Warning +SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_MISSING +Language=English +Skipping dependency registration on missing package provider: %1!ls!, package: %2!ls! +. + +MessageId=325 +Severity=Success +SymbolicName=MSG_DEPENDENCY_PACKAGE_REGISTER_DEPENDENCY +Language=English +Registering dependency: %1!ls! on package provider: %2!ls!, package: %3!ls! +. + +MessageId=326 +Severity=Success +SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY +Language=English +Removed dependency: %1!ls! on package provider: %2!ls!, package %3!ls! +. + +MessageId=327 +Severity=Warning +SymbolicName=MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS +Language=English +Will not uninstall package: %1!ls!, found dependents: %2!d! +. + +MessageId=328 +Severity=Warning +SymbolicName=MSG_DEPENDENCY_PACKAGE_DEPENDENT +Language=English +Found dependent: %1!ls!, name: %2!ls! +. + +MessageId=329 +Severity=Success +SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED +Language=English +Removed package dependency provider: %1!ls!, package: %2!ls! +. + +MessageId=330 +Severity=Success +SymbolicName=MSG_DEPENDENCY_BUNDLE_UNREGISTERED +Language=English +Removed bundle dependency provider: %1!ls! +. + +MessageId=331 +Severity=Warning +SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY_FAILED +Language=English +Could not remove dependency: %1!ls! on package provider: %2!ls!, package %3!ls!, error: 0x%4!x! +. + +MessageId=332 +Severity=Warning +SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_FAILED +Language=English +Could not remove package dependency provider: %1!ls!, package: %2!ls!, error: 0x%3!x! +. + +MessageId=333 +Severity=Warning +SymbolicName=MSG_DEPENDENCY_BUNDLE_UNREGISTERED_FAILED +Language=English +Could not remove bundle dependency provider: %1!ls!, error: 0x%2!x! +. + +MessageId=335 +Severity=Success +SymbolicName=MSG_ACQUIRE_BUNDLE_PAYLOAD +Language=English +Acquiring bundle payload: %2!ls!, %3!hs! from: %4!ls! +. + +MessageId=336 +Severity=Success +SymbolicName=MSG_ACQUIRE_CONTAINER +Language=English +Acquiring container: %1!ls!, %3!hs! from: %4!ls! +. + +MessageId=337 +Severity=Success +SymbolicName=MSG_ACQUIRE_CONTAINER_PAYLOAD +Language=English +Acquiring container: %1!ls!, payload: %2!ls!, %3!hs! from: %4!ls! +. + +MessageId=338 +Severity=Success +SymbolicName=MSG_ACQUIRE_PACKAGE_PAYLOAD +Language=English +Acquiring package: %1!ls!, payload: %2!ls!, %3!hs! from: %4!ls! +. + +MessageId=340 +Severity=Warning +SymbolicName=MSG_PROMPT_BUNDLE_PAYLOAD_SOURCE +Language=English +Prompt for source of bundle payload: %2!ls!, path: %3!ls! +. + +MessageId=341 +Severity=Warning +SymbolicName=MSG_PROMPT_CONTAINER_SOURCE +Language=English +Prompt for source of container: %1!ls!, path: %3!ls! +. + +MessageId=342 +Severity=Warning +SymbolicName=MSG_PROMPT_CONTAINER_PAYLOAD_SOURCE +Language=English +Prompt for source of container: %1!ls!, payload: %2!ls!, path: %3!ls! +. + +MessageId=343 +Severity=Warning +SymbolicName=MSG_PROMPT_PACKAGE_PAYLOAD_SOURCE +Language=English +Prompt for source of package: %1!ls!, payload: %2!ls!, path: %3!ls! +. + +MessageId=348 +Severity=Warning +SymbolicName=MSG_APPLY_RETRYING_PACKAGE +Language=English +Application requested retry of package: %1!ls!, encountered error: 0x%2!x!. Retrying... +. + +MessageId=349 +Severity=Warning +SymbolicName=MSG_APPLY_RETRYING_PAYLOAD +Language=English +Application requested retry of payload: %2!ls!, encountered error: %1!ls!. Retrying... +. + +MessageId=350 +Severity=Warning +SymbolicName=MSG_APPLY_CONTINUING_NONVITAL_PACKAGE +Language=English +Applied non-vital package: %1!ls!, encountered error: 0x%2!x!. Continuing... +. + +MessageId=351 +Severity=Success +SymbolicName=MSG_UNCACHE_PACKAGE +Language=English +Removing cached package: %1!ls!, from path: %2!ls! +. + +MessageId=352 +Severity=Success +SymbolicName=MSG_UNCACHE_BUNDLE +Language=English +Removing cached bundle: %1!ls!, from path: %2!ls! +. + +MessageId=353 +Severity=Warning +SymbolicName=MSG_UNABLE_UNCACHE_PACKAGE +Language=English +Unable to remove cached package: %1!ls!, from path: %2!ls!, reason: 0x%3!x!. Continuing... +. + +MessageId=354 +Severity=Warning +SymbolicName=MSG_UNABLE_UNCACHE_BUNDLE +Language=English +Unable to remove cached bundle: %1!ls!, from path: %2!ls!, reason: 0x%3!x!. Continuing... +. + +MessageId=355 +Severity=Warning +SymbolicName=MSG_SOURCELIST_REGISTER +Language=English +Unable to register source directory: %1!ls!, product: %2!ls!, reason: 0x%3!x!. Continuing... +. + +MessageId=358 +Severity=Success +SymbolicName=MSG_PAUSE_AU_STARTING +Language=English +Pausing automatic updates. +. + +MessageId=359 +Severity=Success +SymbolicName=MSG_PAUSE_AU_SUCCEEDED +Language=English +Paused automatic updates. +. + +MessageId=360 +Severity=Success +SymbolicName=MSG_SYSTEM_RESTORE_POINT_STARTING +Language=English +Creating a system restore point. +. + +MessageId=361 +Severity=Success +SymbolicName=MSG_SYSTEM_RESTORE_POINT_SUCCEEDED +Language=English +Created a system restore point. +. + +MessageId=362 +Severity=Success +SymbolicName=MSG_SYSTEM_RESTORE_POINT_DISABLED +Language=English +System restore disabled, system restore point not created. +. + +MessageId=363 +Severity=Warning +SymbolicName=MSG_SYSTEM_RESTORE_POINT_FAILED +Language=English +Could not create system restore point, error: 0x%1!x!. Continuing... +. + +MessageId=370 +Severity=Success +SymbolicName=MSG_SESSION_BEGIN +Language=English +Session begin, registration key: %1!ls!, options: 0x%2!x!, disable resume: %3!hs! +. + +MessageId=371 +Severity=Success +SymbolicName=MSG_SESSION_UPDATE +Language=English +Updating session, registration key: %1!ls!, resume: %2!hs!, restart initiated: %3!hs!, disable resume: %4!hs! +. + +MessageId=372 +Severity=Success +SymbolicName=MSG_SESSION_END +Language=English +Session end, registration key: %1!ls!, resume: %2!hs!, restart: %3!hs!, disable resume: %4!hs! +. + +MessageId=380 +Severity=Warning +SymbolicName=MSG_APPLY_SKIPPED +Language=English +Apply skipped, no planned actions +. + +MessageId=381 +Severity=Warning +SymbolicName=MSG_APPLY_CANCEL_IGNORED_DURING_ROLLBACK +Language=English +Ignoring application request to cancel from %1!ls! during rollback. +. + +MessageId=399 +Severity=Success +SymbolicName=MSG_APPLY_COMPLETE +Language=English +Apply complete, result: 0x%1!x!, restart: %2!hs!, ba requested restart: %3!hs! +. + +MessageId=400 +Severity=Success +SymbolicName=MSG_SYSTEM_SHUTDOWN +Language=English +Received system request to shut down the process: critical: %1!hs!, elevated: %2!hs!, allowed: %3!hs! +. + +MessageId=410 +Severity=Success +SymbolicName=MSG_VARIABLE_DUMP +Language=English +Variable: %1!ls! +. + +MessageId=420 +Severity=Success +SymbolicName=MSG_RESUME_AU_STARTING +Language=English +Resuming automatic updates. +. + +MessageId=421 +Severity=Success +SymbolicName=MSG_RESUME_AU_SUCCEEDED +Language=English +Resumed automatic updates. +. + +MessageId=500 +Severity=Success +SymbolicName=MSG_QUIT +Language=English +Shutting down, exit code: 0x%1!x! +. + +MessageId=501 +Severity=Warning +SymbolicName=MSG_STATE_NOT_SAVED +Language=English +The state file could not be saved, error: 0x%1!x!. Continuing... +. + +MessageId=600 +Severity=Success +SymbolicName=MSG_LAUNCH_APPROVED_EXE_BEGIN +Language=English +LaunchApprovedExe begin, id: %1!ls! +. + +MessageId=601 +Severity=Success +SymbolicName=MSG_LAUNCH_APPROVED_EXE_SEARCH +Language=English +Searching registry for approved exe path, key: %1!ls!, value: '%2!ls!', win64: %3!ls! +. + +MessageId=602 +Severity=Success +SymbolicName=MSG_LAUNCHING_APPROVED_EXE +Language=English +Launching approved exe, path: '%1!ls!', 'command: %2!ls!' +. + +MessageId=699 +Severity=Success +SymbolicName=MSG_LAUNCH_APPROVED_EXE_COMPLETE +Language=English +LaunchApprovedExe complete, result: 0x%1!x!, processId: %2!lu! +. + +MessageId=700 +Severity=Success +SymbolicName=MSG_MSI_PROPERTY_CONDITION_FAILED +Language=English +Skipping MSI property '%1!ls!' because condition '%2!ls!' evaluates to %3!hs!. +. + diff --git a/src/engine/exeengine.cpp b/src/engine/exeengine.cpp new file mode 100644 index 00000000..71540d5d --- /dev/null +++ b/src/engine/exeengine.cpp @@ -0,0 +1,820 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// internal function declarations + +static HRESULT HandleExitCode( + __in BURN_PACKAGE* pPackage, + __in DWORD dwExitCode, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +static HRESULT ParseCommandLineArgumentsFromXml( + __in IXMLDOMNode* pixnExePackage, + __in BURN_PACKAGE* pPackage + ); +static HRESULT ParseExitCodesFromXml( + __in IXMLDOMNode* pixnExePackage, + __in BURN_PACKAGE* pPackage + ); + + +// function definitions + +extern "C" HRESULT ExeEngineParsePackageFromXml( + __in IXMLDOMNode* pixnExePackage, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + LPWSTR scz = NULL; + + // @DetectCondition + hr = XmlGetAttributeEx(pixnExePackage, L"DetectCondition", &pPackage->Exe.sczDetectCondition); + ExitOnFailure(hr, "Failed to get @DetectCondition."); + + // @InstallArguments + hr = XmlGetAttributeEx(pixnExePackage, L"InstallArguments", &pPackage->Exe.sczInstallArguments); + ExitOnFailure(hr, "Failed to get @InstallArguments."); + + // @UninstallArguments + hr = XmlGetAttributeEx(pixnExePackage, L"UninstallArguments", &pPackage->Exe.sczUninstallArguments); + ExitOnFailure(hr, "Failed to get @UninstallArguments."); + + // @RepairArguments + hr = XmlGetAttributeEx(pixnExePackage, L"RepairArguments", &pPackage->Exe.sczRepairArguments); + ExitOnFailure(hr, "Failed to get @RepairArguments."); + + // @Repairable + hr = XmlGetYesNoAttribute(pixnExePackage, L"Repairable", &pPackage->Exe.fRepairable); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Repairable."); + } + + // @Protocol + hr = XmlGetAttributeEx(pixnExePackage, L"Protocol", &scz); + if (SUCCEEDED(hr)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"burn", -1)) + { + pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_BURN; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"netfx4", -1)) + { + pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_NETFX4; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"none", -1)) + { + pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_NONE; + } + else + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Invalid protocol type: %ls", scz); + } + } + else if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Protocol."); + } + + hr = ParseExitCodesFromXml(pixnExePackage, pPackage); + ExitOnFailure(hr, "Failed to parse exit codes."); + + hr = ParseCommandLineArgumentsFromXml(pixnExePackage, pPackage); + ExitOnFailure(hr, "Failed to parse command lines."); + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseStr(scz); + + return hr; +} + +extern "C" void ExeEnginePackageUninitialize( + __in BURN_PACKAGE* pPackage + ) +{ + ReleaseStr(pPackage->Exe.sczDetectCondition); + ReleaseStr(pPackage->Exe.sczInstallArguments); + ReleaseStr(pPackage->Exe.sczRepairArguments); + ReleaseStr(pPackage->Exe.sczUninstallArguments); + ReleaseStr(pPackage->Exe.sczIgnoreDependencies); + ReleaseStr(pPackage->Exe.sczAncestors); + //ReleaseStr(pPackage->Exe.sczProgressSwitch); + ReleaseMem(pPackage->Exe.rgExitCodes); + + // free command-line arguments + if (pPackage->Exe.rgCommandLineArguments) + { + for (DWORD i = 0; i < pPackage->Exe.cCommandLineArguments; ++i) + { + BURN_EXE_COMMAND_LINE_ARGUMENT* pCommandLineArgument = &pPackage->Exe.rgCommandLineArguments[i]; + ReleaseStr(pCommandLineArgument->sczInstallArgument); + ReleaseStr(pCommandLineArgument->sczUninstallArgument); + ReleaseStr(pCommandLineArgument->sczRepairArgument); + ReleaseStr(pCommandLineArgument->sczCondition); + } + MemFree(pPackage->Exe.rgCommandLineArguments); + } + + // clear struct + memset(&pPackage->Exe, 0, sizeof(pPackage->Exe)); +} + +extern "C" HRESULT ExeEngineDetectPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + BOOL fDetected = FALSE; + + // evaluate detect condition + if (pPackage->Exe.sczDetectCondition && *pPackage->Exe.sczDetectCondition) + { + hr = ConditionEvaluate(pVariables, pPackage->Exe.sczDetectCondition, &fDetected); + ExitOnFailure(hr, "Failed to evaluate executable package detect condition."); + } + + // update detect state + pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + +LExit: + return hr; +} + +// +// PlanCalculate - calculates the execute and rollback state for the requested package state. +// +extern "C" HRESULT ExeEnginePlanCalculatePackage( + __in BURN_PACKAGE* pPackage, + __out_opt BOOL* pfBARequestedCache + ) +{ + HRESULT hr = S_OK; + //BOOL fCondition = FALSE; + //BOOTSTRAPPER_PACKAGE_STATE expected = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; + BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; + BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + BOOL fBARequestedCache = FALSE; + + //// evaluate rollback install condition + //if (pPackage->sczRollbackInstallCondition) + //{ + // hr = ConditionEvaluate(pVariables, pPackage->sczRollbackInstallCondition, &fCondition); + // ExitOnFailure(hr, "Failed to evaluate rollback install condition."); + + // expected = fCondition ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + //} + + // execute action + switch (pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: + execute = pPackage->Exe.fPseudoBundle ? BOOTSTRAPPER_ACTION_STATE_INSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; + break; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + execute = pPackage->Exe.fRepairable ? BOOTSTRAPPER_ACTION_STATE_REPAIR : BOOTSTRAPPER_ACTION_STATE_NONE; + break; + case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_CACHE: + execute = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; + break; + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: + execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; + break; + default: + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; + break; + case BOOTSTRAPPER_REQUEST_STATE_CACHE: + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + fBARequestedCache = TRUE; + break; + default: + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid package current state: %d.", pPackage->currentState); + } + + // Calculate the rollback action if there is an execute action. + if (BOOTSTRAPPER_ACTION_STATE_NONE != execute) + { + switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_ABSENT: + rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL; + break; + default: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + rollback = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; + break; + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_ABSENT: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + default: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid package expected state."); + } + } + + // return values + pPackage->execute = execute; + pPackage->rollback = rollback; + + if (pfBARequestedCache) + { + *pfBARequestedCache = fBARequestedCache; + } + +LExit: + return hr; +} + +// +// PlanAdd - adds the calculated execute and rollback actions for the package. +// +extern "C" HRESULT ExeEnginePlanAddPackage( + __in_opt DWORD *pdwInsertSequence, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in_opt HANDLE hCacheEvent, + __in BOOL fPlanPackageCacheRollback + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* pAction = NULL; + + // add wait for cache + if (hCacheEvent) + { + hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent, fPlanPackageCacheRollback); + ExitOnFailure(hr, "Failed to plan package cache syncpoint"); + } + + hr = DependencyPlanPackage(pdwInsertSequence, pPackage, pPlan); + ExitOnFailure(hr, "Failed to plan package dependency actions."); + + // add execute action + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute) + { + if (NULL != pdwInsertSequence) + { + hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction); + ExitOnFailure(hr, "Failed to insert execute action."); + } + else + { + hr = PlanAppendExecuteAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append execute action."); + } + + pAction->type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE; + pAction->exePackage.pPackage = pPackage; + pAction->exePackage.fFireAndForget = (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == pPlan->action); + pAction->exePackage.action = pPackage->execute; + + if (pPackage->Exe.sczIgnoreDependencies) + { + hr = StrAllocString(&pAction->exePackage.sczIgnoreDependencies, pPackage->Exe.sczIgnoreDependencies, 0); + ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore."); + } + + if (pPackage->Exe.sczAncestors) + { + hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.sczAncestors, 0); + ExitOnFailure(hr, "Failed to allocate the list of ancestors."); + } + + LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, NULL); // ignore errors. + } + + // add rollback action + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback) + { + hr = PlanAppendRollbackAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append rollback action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE; + pAction->exePackage.pPackage = pPackage; + pAction->exePackage.action = pPackage->rollback; + + if (pPackage->Exe.sczIgnoreDependencies) + { + hr = StrAllocString(&pAction->exePackage.sczIgnoreDependencies, pPackage->Exe.sczIgnoreDependencies, 0); + ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore."); + } + + if (pPackage->Exe.sczAncestors) + { + hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.sczAncestors, 0); + ExitOnFailure(hr, "Failed to allocate the list of ancestors."); + } + + LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, NULL); // ignore errors. + } + +LExit: + return hr; +} + +extern "C" HRESULT ExeEngineExecutePackage( + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + WCHAR wzCurrentDirectory[MAX_PATH] = { }; + BOOL fChangedCurrentDirectory = FALSE; + int nResult = IDNOACTION; + LPCWSTR wzArguments = NULL; + LPWSTR sczArguments = NULL; + LPWSTR sczArgumentsFormatted = NULL; + LPWSTR sczArgumentsObfuscated = NULL; + LPWSTR sczCachedDirectory = NULL; + LPWSTR sczExecutablePath = NULL; + LPWSTR sczCommand = NULL; + LPWSTR sczCommandObfuscated = NULL; + HANDLE hExecutableFile = INVALID_HANDLE_VALUE; + STARTUPINFOW si = { }; + PROCESS_INFORMATION pi = { }; + DWORD dwExitCode = 0; + GENERIC_EXECUTE_MESSAGE message = { }; + + // get cached executable path + hr = CacheGetCompletedPath(pExecuteAction->exePackage.pPackage->fPerMachine, pExecuteAction->exePackage.pPackage->sczCacheId, &sczCachedDirectory); + ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->exePackage.pPackage->sczId); + + // Best effort to set the execute package cache folder and action variables. + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); + VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->exePackage.action, TRUE); + + hr = PathConcat(sczCachedDirectory, pExecuteAction->exePackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczExecutablePath); + ExitOnFailure(hr, "Failed to build executable path."); + + // pick arguments + switch (pExecuteAction->exePackage.action) + { + case BOOTSTRAPPER_ACTION_STATE_INSTALL: + wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczInstallArguments; + break; + + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: + wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczUninstallArguments; + break; + + case BOOTSTRAPPER_ACTION_STATE_REPAIR: + wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczRepairArguments; + break; + + default: + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action); + } + + // now add optional arguments + hr = StrAllocString(&sczArguments, wzArguments && *wzArguments ? wzArguments : L"", 0); + ExitOnFailure(hr, "Failed to copy package arguments."); + + for (DWORD i = 0; i < pExecuteAction->exePackage.pPackage->Exe.cCommandLineArguments; ++i) + { + BURN_EXE_COMMAND_LINE_ARGUMENT* commandLineArgument = &pExecuteAction->exePackage.pPackage->Exe.rgCommandLineArguments[i]; + BOOL fCondition = FALSE; + + hr = ConditionEvaluate(pVariables, commandLineArgument->sczCondition, &fCondition); + ExitOnFailure(hr, "Failed to evaluate executable package command-line condition."); + + if (fCondition) + { + hr = StrAllocConcat(&sczArguments, L" ", 0); + ExitOnFailure(hr, "Failed to separate command-line arguments."); + + switch (pExecuteAction->exePackage.action) + { + case BOOTSTRAPPER_ACTION_STATE_INSTALL: + hr = StrAllocConcat(&sczArguments, commandLineArgument->sczInstallArgument, 0); + ExitOnFailure(hr, "Failed to get command-line argument for install."); + break; + + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: + hr = StrAllocConcat(&sczArguments, commandLineArgument->sczUninstallArgument, 0); + ExitOnFailure(hr, "Failed to get command-line argument for uninstall."); + break; + + case BOOTSTRAPPER_ACTION_STATE_REPAIR: + hr = StrAllocConcat(&sczArguments, commandLineArgument->sczRepairArgument, 0); + ExitOnFailure(hr, "Failed to get command-line argument for repair."); + break; + + default: + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action); + } + } + } + + // build command + if (0 < lstrlenW(sczArguments)) + { + hr = VariableFormatString(pVariables, sczArguments, &sczArgumentsFormatted, NULL); + ExitOnFailure(hr, "Failed to format argument string."); + + hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", sczExecutablePath, sczArgumentsFormatted); + ExitOnFailure(hr, "Failed to create executable command."); + + hr = VariableFormatStringObfuscated(pVariables, sczArguments, &sczArgumentsObfuscated, NULL); + ExitOnFailure(hr, "Failed to format obfuscated argument string."); + + hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", sczExecutablePath, sczArgumentsObfuscated); + } + else + { + hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", sczExecutablePath); + ExitOnFailure(hr, "Failed to create executable command."); + + hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", sczExecutablePath); + } + ExitOnFailure(hr, "Failed to create obfuscated executable command."); + + if (pExecuteAction->exePackage.pPackage->Exe.fSupportsAncestors) + { + // Add the list of dependencies to ignore, if any, to the burn command line. + if (pExecuteAction->exePackage.sczIgnoreDependencies && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol) + { + hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies); + ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line."); + + hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies); + ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the obfuscated command line."); + } + + // Add the list of ancestors, if any, to the burn command line. + if (pExecuteAction->exePackage.sczAncestors) + { + hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors); + ExitOnFailure(hr, "Failed to append the list of ancestors to the command line."); + + hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors); + ExitOnFailure(hr, "Failed to append the list of ancestors to the obfuscated command line."); + } + } + + if (BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol) + { + hr = CoreAppendFileHandleSelfToCommandLine(sczExecutablePath, &hExecutableFile, &sczCommand, &sczCommandObfuscated); + ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF); + } + + // Log before we add the secret pipe name and client token for embedded processes. + LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->exePackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->exePackage.action), sczExecutablePath, sczCommandObfuscated); + + if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol) + { + hr = EmbeddedRunBundle(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode); + ExitOnFailure(hr, "Failed to run bundle as embedded from path: %ls", sczExecutablePath); + } + else if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_NETFX4 == pExecuteAction->exePackage.pPackage->Exe.protocol) + { + hr = NetFxRunChainer(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode); + ExitOnFailure(hr, "Failed to run netfx chainer: %ls", sczExecutablePath); + } + else // create and wait for the executable process while sending fake progress to allow cancel. + { + // Make the cache location of the executable the current directory to help those executables + // that expect stuff to be relative to them. + if (::GetCurrentDirectoryW(countof(wzCurrentDirectory), wzCurrentDirectory)) + { + fChangedCurrentDirectory = ::SetCurrentDirectoryW(sczCachedDirectory); + } + + si.cb = sizeof(si); // TODO: hookup the stdin/stdout/stderr pipes for logging purposes? + if (!::CreateProcessW(sczExecutablePath, sczCommand, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) + { + ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", sczExecutablePath); + } + + if (pExecuteAction->exePackage.fFireAndForget) + { + ::WaitForInputIdle(pi.hProcess, 5000); + ExitFunction(); + } + + do + { + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; + message.dwAllowedResults = MB_OKCANCEL; + message.progress.dwPercentage = 50; + nResult = pfnGenericMessageHandler(&message, pvContext); + hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); + ExitOnRootFailure(hr, "Bootstrapper application aborted during EXE progress."); + + hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode); + if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr) + { + ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", sczExecutablePath); + } + } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr); + } + + hr = HandleExitCode(pExecuteAction->exePackage.pPackage, dwExitCode, pRestart); + ExitOnRootFailure(hr, "Process returned error: 0x%x", dwExitCode); + +LExit: + if (fChangedCurrentDirectory) + { + ::SetCurrentDirectoryW(wzCurrentDirectory); + } + + StrSecureZeroFreeString(sczArguments); + StrSecureZeroFreeString(sczArgumentsFormatted); + ReleaseStr(sczArgumentsObfuscated); + ReleaseStr(sczCachedDirectory); + ReleaseStr(sczExecutablePath); + StrSecureZeroFreeString(sczCommand); + ReleaseStr(sczCommandObfuscated); + + ReleaseHandle(pi.hThread); + ReleaseHandle(pi.hProcess); + ReleaseFileHandle(hExecutableFile); + + // Best effort to clear the execute package cache folder and action variables. + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE); + + return hr; +} + + +// internal helper functions + +static HRESULT ParseExitCodesFromXml( + __in IXMLDOMNode* pixnExePackage, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + LPWSTR scz = NULL; + + // select exit code nodes + hr = XmlSelectNodes(pixnExePackage, L"ExitCode", &pixnNodes); + ExitOnFailure(hr, "Failed to select exit code nodes."); + + // get exit code node count + hr = pixnNodes->get_length((long*) &cNodes); + ExitOnFailure(hr, "Failed to get exit code node count."); + + if (cNodes) + { + // allocate memory for exit codes + pPackage->Exe.rgExitCodes = (BURN_EXE_EXIT_CODE*) MemAlloc(sizeof(BURN_EXE_EXIT_CODE) * cNodes, TRUE); + ExitOnNull(pPackage->Exe.rgExitCodes, hr, E_OUTOFMEMORY, "Failed to allocate memory for exit code structs."); + + pPackage->Exe.cExitCodes = cNodes; + + // parse package elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_EXE_EXIT_CODE* pExitCode = &pPackage->Exe.rgExitCodes[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // @Type + hr = XmlGetAttributeNumber(pixnNode, L"Type", (DWORD*)&pExitCode->type); + ExitOnFailure(hr, "Failed to get @Type."); + + // @Code + hr = XmlGetAttributeEx(pixnNode, L"Code", &scz); + ExitOnFailure(hr, "Failed to get @Code."); + + if (L'*' == scz[0]) + { + pExitCode->fWildcard = TRUE; + } + else + { + hr = StrStringToUInt32(scz, 0, (UINT*) &pExitCode->dwCode); + ExitOnFailure(hr, "Failed to parse @Code value: %ls", scz); + } + + // prepare next iteration + ReleaseNullObject(pixnNode); + } + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseStr(scz); + + return hr; +} + +static HRESULT ParseCommandLineArgumentsFromXml( + __in IXMLDOMNode* pixnExePackage, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + LPWSTR scz = NULL; + + // Select command-line argument nodes. + hr = XmlSelectNodes(pixnExePackage, L"CommandLine", &pixnNodes); + ExitOnFailure(hr, "Failed to select command-line argument nodes."); + + // Get command-line argument node count. + hr = pixnNodes->get_length((long*) &cNodes); + ExitOnFailure(hr, "Failed to get command-line argument count."); + + if (cNodes) + { + pPackage->Exe.rgCommandLineArguments = (BURN_EXE_COMMAND_LINE_ARGUMENT*) MemAlloc(sizeof(BURN_EXE_COMMAND_LINE_ARGUMENT) * cNodes, TRUE); + ExitOnNull(pPackage->Exe.rgCommandLineArguments, hr, E_OUTOFMEMORY, "Failed to allocate memory for command-line argument structs."); + + pPackage->Exe.cCommandLineArguments = cNodes; + + // Parse command-line argument elements. + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_EXE_COMMAND_LINE_ARGUMENT* pCommandLineArgument = &pPackage->Exe.rgCommandLineArguments[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next command-line argument node."); + + // @InstallArgument + hr = XmlGetAttributeEx(pixnNode, L"InstallArgument", &pCommandLineArgument->sczInstallArgument); + ExitOnFailure(hr, "Failed to get @InstallArgument."); + + // @UninstallArgument + hr = XmlGetAttributeEx(pixnNode, L"UninstallArgument", &pCommandLineArgument->sczUninstallArgument); + ExitOnFailure(hr, "Failed to get @UninstallArgument."); + + // @RepairArgument + hr = XmlGetAttributeEx(pixnNode, L"RepairArgument", &pCommandLineArgument->sczRepairArgument); + ExitOnFailure(hr, "Failed to get @RepairArgument."); + + // @Condition + hr = XmlGetAttributeEx(pixnNode, L"Condition", &pCommandLineArgument->sczCondition); + ExitOnFailure(hr, "Failed to get @Condition."); + + // Prepare next iteration. + ReleaseNullObject(pixnNode); + } + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseStr(scz); + + return hr; +} + +static HRESULT HandleExitCode( + __in BURN_PACKAGE* pPackage, + __in DWORD dwExitCode, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + BURN_EXE_EXIT_CODE_TYPE typeCode = BURN_EXE_EXIT_CODE_TYPE_NONE; + + for (DWORD i = 0; i < pPackage->Exe.cExitCodes; ++i) + { + BURN_EXE_EXIT_CODE* pExitCode = &pPackage->Exe.rgExitCodes[i]; + + // If this is a wildcard, use the last one we come across. + if (pExitCode->fWildcard) + { + typeCode = pExitCode->type; + } + else if (dwExitCode == pExitCode->dwCode) // If we have an exact match on the error code use that and stop looking. + { + typeCode = pExitCode->type; + break; + } + } + + // If we didn't find a matching code then treat 0 as success, the standard restarts codes as restarts + // and everything else as an error. + if (BURN_EXE_EXIT_CODE_TYPE_NONE == typeCode) + { + if (0 == dwExitCode) + { + typeCode = BURN_EXE_EXIT_CODE_TYPE_SUCCESS; + } + else if (ERROR_SUCCESS_REBOOT_REQUIRED == dwExitCode || + HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast(dwExitCode) || + ERROR_SUCCESS_RESTART_REQUIRED == dwExitCode || + HRESULT_FROM_WIN32(ERROR_SUCCESS_RESTART_REQUIRED) == static_cast(dwExitCode)) + { + typeCode = BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT; + } + else if (ERROR_SUCCESS_REBOOT_INITIATED == dwExitCode || + HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED) == static_cast(dwExitCode)) + { + typeCode = BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT; + } + else + { + typeCode = BURN_EXE_EXIT_CODE_TYPE_ERROR; + } + } + + switch (typeCode) + { + case BURN_EXE_EXIT_CODE_TYPE_SUCCESS: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; + hr = S_OK; + break; + + case BURN_EXE_EXIT_CODE_TYPE_ERROR: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; + hr = HRESULT_FROM_WIN32(dwExitCode); + if (SUCCEEDED(hr)) + { + hr = E_FAIL; + } + break; + + case BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; + hr = S_OK; + break; + + case BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; + hr = S_OK; + break; + + default: + hr = E_UNEXPECTED; + break; + } + +//LExit: + return hr; +} diff --git a/src/engine/exeengine.h b/src/engine/exeengine.h new file mode 100644 index 00000000..402e51ed --- /dev/null +++ b/src/engine/exeengine.h @@ -0,0 +1,48 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// function declarations + +HRESULT ExeEngineParsePackageFromXml( + __in IXMLDOMNode* pixnExePackage, + __in BURN_PACKAGE* pPackage + ); +void ExeEnginePackageUninitialize( + __in BURN_PACKAGE* pPackage + ); +HRESULT ExeEngineDetectPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_VARIABLES* pVariables + ); +HRESULT ExeEnginePlanCalculatePackage( + __in BURN_PACKAGE* pPackage, + __out_opt BOOL* pfBARequestedCache + ); +HRESULT ExeEnginePlanAddPackage( + __in_opt DWORD *pdwInsertSequence, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in_opt HANDLE hCacheEvent, + __in BOOL fPlanPackageCacheRollback + ); +HRESULT ExeEngineExecutePackage( + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/inc/engine.h b/src/engine/inc/engine.h new file mode 100644 index 00000000..808bb91a --- /dev/null +++ b/src/engine/inc/engine.h @@ -0,0 +1,27 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// function declarations + +BOOL EngineInCleanRoom( + __in_z_opt LPCWSTR wzCommandLine + ); + +HRESULT EngineRun( + __in HINSTANCE hInstance, + __in HANDLE hEngineFile, + __in_z_opt LPCWSTR wzCommandLine, + __in int nCmdShow, + __out DWORD* pdwExitCode + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/logging.cpp b/src/engine/logging.cpp new file mode 100644 index 00000000..818532ad --- /dev/null +++ b/src/engine/logging.cpp @@ -0,0 +1,683 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +static DWORD vdwPackageSequence = 0; +static const DWORD LOG_OPEN_RETRY_COUNT = 3; +static const DWORD LOG_OPEN_RETRY_WAIT = 2000; +static CONST LPWSTR LOG_FAILED_EVENT_LOG_MESSAGE = L"Burn Engine Fatal Error: failed to open log file."; + +// structs + + + +// internal function declarations + +static void CheckLoggingPolicy( + __out DWORD *pdwAttributes + ); +static HRESULT GetNonSessionSpecificTempFolder( + __deref_out_z LPWSTR* psczNonSessionTempFolder + ); + + +// function definitions + +extern "C" HRESULT LoggingOpen( + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __in_z LPCWSTR wzBundleName + ) +{ + HRESULT hr = S_OK; + LPWSTR sczLoggingBaseFolder = NULL; + + // Check if the logging policy is set and configure the logging appropriately. + CheckLoggingPolicy(&pLog->dwAttributes); + + if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_VERBOSE || pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG) + { + if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG) + { + LogSetLevel(REPORT_DEBUG, FALSE); + } + else if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_VERBOSE) + { + LogSetLevel(REPORT_VERBOSE, FALSE); + } + + if ((!pLog->sczPath || !*pLog->sczPath) && (!pLog->sczPrefix || !*pLog->sczPrefix)) + { + PathCreateTimeBasedTempFile(NULL, L"Setup", NULL, L"log", &pLog->sczPath, NULL); + } + } + + // Open the log approriately. + if (pLog->sczPath && *pLog->sczPath) + { + DWORD cRetry = 0; + + hr = DirGetCurrent(&sczLoggingBaseFolder); + ExitOnFailure(hr, "Failed to get current directory."); + + // Try pretty hard to open the log file when appending. + do + { + if (0 < cRetry) + { + ::Sleep(LOG_OPEN_RETRY_WAIT); + } + + hr = LogOpen(sczLoggingBaseFolder, pLog->sczPath, NULL, NULL, pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND, FALSE, &pLog->sczPath); + if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND && HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) == hr) + { + ++cRetry; + } + } while (cRetry > 0 && cRetry <= LOG_OPEN_RETRY_COUNT); + + if (FAILED(hr)) + { + // Log is not open, so note that. + LogDisable(); + pLog->state = BURN_LOGGING_STATE_DISABLED; + + if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND) + { + // If appending, ignore the failure and continue. + hr = S_OK; + } + else // specifically tried to create a log file so show an error if appropriate and bail. + { + HRESULT hrOriginal = hr; + + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_LOG_FAILURE); + SplashScreenDisplayError(display, wzBundleName, hr); + + ExitOnFailure(hrOriginal, "Failed to open log: %ls", pLog->sczPath); + } + } + else + { + pLog->state = BURN_LOGGING_STATE_OPEN; + } + } + else if (pLog->sczPrefix && *pLog->sczPrefix) + { + hr = GetNonSessionSpecificTempFolder(&sczLoggingBaseFolder); + ExitOnFailure(hr, "Failed to get non-session specific TEMP folder."); + + // Best effort to open default logging. + hr = LogOpen(sczLoggingBaseFolder, pLog->sczPrefix, NULL, pLog->sczExtension, FALSE, FALSE, &pLog->sczPath); + if (FAILED(hr)) + { + LogDisable(); + pLog->state = BURN_LOGGING_STATE_DISABLED; + + hr = S_OK; + } + else + { + pLog->state = BURN_LOGGING_STATE_OPEN; + } + } + else // no logging enabled. + { + LogDisable(); + pLog->state = BURN_LOGGING_STATE_DISABLED; + } + + // If the log was opened, write the header info and update the prefix and extension to match + // the log name so future logs are opened with the same pattern. + if (BURN_LOGGING_STATE_OPEN == pLog->state) + { + LPCWSTR wzExtension = PathExtension(pLog->sczPath); + if (wzExtension && *wzExtension) + { + hr = StrAllocString(&pLog->sczPrefix, pLog->sczPath, wzExtension - pLog->sczPath); + ExitOnFailure(hr, "Failed to copy log path to prefix."); + + hr = StrAllocString(&pLog->sczExtension, wzExtension + 1, 0); + ExitOnFailure(hr, "Failed to copy log extension to extension."); + } + else + { + hr = StrAllocString(&pLog->sczPrefix, pLog->sczPath, 0); + ExitOnFailure(hr, "Failed to copy full log path to prefix."); + } + + if (pLog->sczPathVariable && *pLog->sczPathVariable) + { + VariableSetString(pVariables, pLog->sczPathVariable, pLog->sczPath, FALSE); // Ignore failure. + } + } + +LExit: + ReleaseStr(sczLoggingBaseFolder); + + return hr; +} + +extern "C" void LoggingOpenFailed() +{ + HRESULT hr = S_OK; + HANDLE hEventLog = NULL; + LPCWSTR* lpStrings = const_cast(&LOG_FAILED_EVENT_LOG_MESSAGE); + WORD wNumStrings = 1; + + hr = LogOpen(NULL, L"Setup", L"_Failed", L"txt", FALSE, FALSE, NULL); + if (SUCCEEDED(hr)) + { + ExitFunction(); + } + + // If opening the "failure" log failed, then attempt to record that in the Application event log. + hEventLog = ::OpenEventLogW(NULL, L"Application"); + ExitOnNullWithLastError(hEventLog, hr, "Failed to open Application event log"); + + hr = ::ReportEventW(hEventLog, EVENTLOG_ERROR_TYPE, 1, 1, NULL, wNumStrings, 0, lpStrings, NULL); + ExitOnNullWithLastError(hEventLog, hr, "Failed to write event log entry"); + +LExit: + if (hEventLog) + { + ::CloseEventLog(hEventLog); + } +} + +extern "C" void LoggingIncrementPackageSequence() +{ + ++vdwPackageSequence; +} + +extern "C" HRESULT LoggingSetPackageVariable( + __in BURN_PACKAGE* pPackage, + __in_z_opt LPCWSTR wzSuffix, + __in BOOL fRollback, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __out_opt LPWSTR* psczLogPath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczLogPath = NULL; + + // Make sure that no package log files are created when logging has been disabled via Log element. + if (BURN_LOGGING_STATE_DISABLED == pLog->state) + { + if (psczLogPath) + { + *psczLogPath = NULL; + } + + ExitFunction(); + } + + if ((!fRollback && pPackage->sczLogPathVariable && *pPackage->sczLogPathVariable) || + (fRollback && pPackage->sczRollbackLogPathVariable && *pPackage->sczRollbackLogPathVariable)) + { + hr = StrAllocFormatted(&sczLogPath, L"%ls%hs%ls_%03u_%ls%ls.%ls", pLog->sczPrefix, wzSuffix && *wzSuffix ? "_" : "", wzSuffix && *wzSuffix ? wzSuffix : L"", vdwPackageSequence, pPackage->sczId, fRollback ? L"_rollback" : L"", pLog->sczExtension); + ExitOnFailure(hr, "Failed to allocate path for package log."); + + hr = VariableSetString(pVariables, fRollback ? pPackage->sczRollbackLogPathVariable : pPackage->sczLogPathVariable, sczLogPath, FALSE); + ExitOnFailure(hr, "Failed to set log path into variable."); + + if (psczLogPath) + { + hr = StrAllocString(psczLogPath, sczLogPath, 0); + ExitOnFailure(hr, "Failed to copy package log path."); + } + } + +LExit: + ReleaseStr(sczLogPath); + + return hr; +} + +extern "C" LPCSTR LoggingBurnActionToString( + __in BOOTSTRAPPER_ACTION action + ) +{ + switch (action) + { + case BOOTSTRAPPER_ACTION_UNKNOWN: + return "Unknown"; + case BOOTSTRAPPER_ACTION_HELP: + return "Help"; + case BOOTSTRAPPER_ACTION_LAYOUT: + return "Layout"; + case BOOTSTRAPPER_ACTION_CACHE: + return "Cache"; + case BOOTSTRAPPER_ACTION_UNINSTALL: + return "Uninstall"; + case BOOTSTRAPPER_ACTION_INSTALL: + return "Install"; + case BOOTSTRAPPER_ACTION_MODIFY: + return "Modify"; + case BOOTSTRAPPER_ACTION_REPAIR: + return "Repair"; + case BOOTSTRAPPER_ACTION_UPDATE_REPLACE: + return "UpdateReplace"; + case BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED: + return "UpdateReplaceEmbedded"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingActionStateToString( + __in BOOTSTRAPPER_ACTION_STATE actionState + ) +{ + switch (actionState) + { + case BOOTSTRAPPER_ACTION_STATE_NONE: + return "None"; + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: + return "Uninstall"; + case BOOTSTRAPPER_ACTION_STATE_INSTALL: + return "Install"; + case BOOTSTRAPPER_ACTION_STATE_ADMIN_INSTALL: + return "AdminInstall"; + case BOOTSTRAPPER_ACTION_STATE_MODIFY: + return "Modify"; + case BOOTSTRAPPER_ACTION_STATE_REPAIR: + return "Repair"; + case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: + return "MinorUpgrade"; + case BOOTSTRAPPER_ACTION_STATE_MAJOR_UPGRADE: + return "MajorUpgrade"; + case BOOTSTRAPPER_ACTION_STATE_PATCH: + return "Patch"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingDependencyActionToString( + BURN_DEPENDENCY_ACTION action + ) +{ + switch (action) + { + case BURN_DEPENDENCY_ACTION_NONE: + return "None"; + case BURN_DEPENDENCY_ACTION_REGISTER: + return "Register"; + case BURN_DEPENDENCY_ACTION_UNREGISTER: + return "Unregister"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingBoolToString( + __in BOOL f + ) +{ + if (f) + { + return "Yes"; + } + + return "No"; +} + +extern "C" LPCSTR LoggingTrueFalseToString( + __in BOOL f + ) +{ + if (f) + { + return "true"; + } + + return "false"; +} + +extern "C" LPCSTR LoggingPackageStateToString( + __in BOOTSTRAPPER_PACKAGE_STATE packageState + ) +{ + switch (packageState) + { + case BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN: + return "Unknown"; + case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: + return "Obsolete"; + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: + return "Absent"; + case BOOTSTRAPPER_PACKAGE_STATE_CACHED: + return "Cached"; + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: + return "Present"; + case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: + return "Superseded"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingCacheStateToString( + __in BURN_CACHE_STATE cacheState + ) +{ + switch (cacheState) + { + case BURN_CACHE_STATE_NONE: + return "None"; + case BURN_CACHE_STATE_PARTIAL: + return "Partial"; + case BURN_CACHE_STATE_COMPLETE: + return "Complete"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingMsiFeatureStateToString( + __in BOOTSTRAPPER_FEATURE_STATE featureState + ) +{ + switch (featureState) + { + case BOOTSTRAPPER_FEATURE_STATE_UNKNOWN: + return "Unknown"; + case BOOTSTRAPPER_FEATURE_STATE_ABSENT: + return "Absent"; + case BOOTSTRAPPER_FEATURE_STATE_ADVERTISED: + return "Advertised"; + case BOOTSTRAPPER_FEATURE_STATE_LOCAL: + return "Local"; + case BOOTSTRAPPER_FEATURE_STATE_SOURCE: + return "Source"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingMsiFeatureActionToString( + __in BOOTSTRAPPER_FEATURE_ACTION featureAction + ) +{ + switch (featureAction) + { + case BOOTSTRAPPER_FEATURE_ACTION_NONE: + return "None"; + case BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL: + return "AddLocal"; + case BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE: + return "AddSource"; + case BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT: + return "AddDefault"; + case BOOTSTRAPPER_FEATURE_ACTION_REINSTALL: + return "Reinstall"; + case BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE: + return "Advertise"; + case BOOTSTRAPPER_FEATURE_ACTION_REMOVE: + return "Remove"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingMsiInstallContext( + __in MSIINSTALLCONTEXT context + ) +{ + switch (context) + { + case MSIINSTALLCONTEXT_ALL: + return "All"; + case MSIINSTALLCONTEXT_ALLUSERMANAGED: + return "AllUserManaged"; + case MSIINSTALLCONTEXT_MACHINE: + return "Machine"; + case MSIINSTALLCONTEXT_NONE: + return "None"; + case MSIINSTALLCONTEXT_USERMANAGED: + return "UserManaged"; + case MSIINSTALLCONTEXT_USERUNMANAGED: + return "UserUnmanaged"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingPerMachineToString( + __in BOOL fPerMachine + ) +{ + if (fPerMachine) + { + return "PerMachine"; + } + + return "PerUser"; +} + +extern "C" LPCSTR LoggingRestartToString( + __in BOOTSTRAPPER_APPLY_RESTART restart + ) +{ + switch (restart) + { + case BOOTSTRAPPER_APPLY_RESTART_NONE: + return "None"; + case BOOTSTRAPPER_APPLY_RESTART_REQUIRED: + return "Required"; + case BOOTSTRAPPER_APPLY_RESTART_INITIATED: + return "Initiated"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingResumeModeToString( + __in BURN_RESUME_MODE resumeMode + ) +{ + switch (resumeMode) + { + case BURN_RESUME_MODE_NONE: + return "None"; + case BURN_RESUME_MODE_ACTIVE: + return "Active"; + case BURN_RESUME_MODE_SUSPEND: + return "Suspend"; + case BURN_RESUME_MODE_ARP: + return "ARP"; + case BURN_RESUME_MODE_REBOOT_PENDING: + return "Reboot Pending"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingRelationTypeToString( + __in BOOTSTRAPPER_RELATION_TYPE type + ) +{ + switch (type) + { + case BOOTSTRAPPER_RELATION_NONE: + return "None"; + case BOOTSTRAPPER_RELATION_DETECT: + return "Detect"; + case BOOTSTRAPPER_RELATION_UPGRADE: + return "Upgrade"; + case BOOTSTRAPPER_RELATION_ADDON: + return "Addon"; + case BOOTSTRAPPER_RELATION_PATCH: + return "Patch"; + case BOOTSTRAPPER_RELATION_DEPENDENT: + return "Dependent"; + case BOOTSTRAPPER_RELATION_UPDATE: + return "Update"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingRelatedOperationToString( + __in BOOTSTRAPPER_RELATED_OPERATION operation + ) +{ + switch (operation) + { + case BOOTSTRAPPER_RELATED_OPERATION_NONE: + return "None"; + case BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE: + return "Downgrade"; + case BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE: + return "MinorUpdate"; + case BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE: + return "MajorUpgrade"; + case BOOTSTRAPPER_RELATED_OPERATION_REMOVE: + return "Remove"; + case BOOTSTRAPPER_RELATED_OPERATION_INSTALL: + return "Install"; + case BOOTSTRAPPER_RELATED_OPERATION_REPAIR: + return "Repair"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingRequestStateToString( + __in BOOTSTRAPPER_REQUEST_STATE requestState + ) +{ + switch (requestState) + { + case BOOTSTRAPPER_REQUEST_STATE_NONE: + return "None"; + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: + return "ForceAbsent"; + case BOOTSTRAPPER_REQUEST_STATE_ABSENT: + return "Absent"; + case BOOTSTRAPPER_REQUEST_STATE_CACHE: + return "Cache"; + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: + return "Present"; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + return "Repair"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingRollbackOrExecute( + __in BOOL fRollback + ) +{ + return fRollback ? "rollback" : "execute"; +} + +extern "C" LPWSTR LoggingStringOrUnknownIfNull( + __in LPCWSTR wz + ) +{ + return wz ? wz : L"Unknown"; +} + +// Note: this function is not thread safe. +extern "C" LPCSTR LoggingVersionToString( + __in DWORD64 dw64Version + ) +{ + static CHAR szVersion[40] = { 0 }; + HRESULT hr = S_OK; + + hr = ::StringCchPrintfA(szVersion, countof(szVersion), "%I64u.%I64u.%I64u.%I64u", dw64Version >> 48 & 0xFFFF, dw64Version >> 32 & 0xFFFF, dw64Version >> 16 & 0xFFFF, dw64Version & 0xFFFF); + if (FAILED(hr)) + { + memset(szVersion, 0, sizeof(szVersion)); + } + + return szVersion; +} + + +// internal function declarations + +static void CheckLoggingPolicy( + __out DWORD *pdwAttributes + ) +{ + HRESULT hr = S_OK; + HKEY hk = NULL; + LPWSTR sczLoggingPolicy = NULL; + + hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Policies\\Microsoft\\Windows\\Installer", KEY_READ, &hk); + if (SUCCEEDED(hr)) + { + hr = RegReadString(hk, L"Logging", &sczLoggingPolicy); + if (SUCCEEDED(hr)) + { + LPCWSTR wz = sczLoggingPolicy; + while (*wz) + { + if (L'v' == *wz || L'V' == *wz) + { + *pdwAttributes |= BURN_LOGGING_ATTRIBUTE_VERBOSE; + } + else if (L'x' == *wz || L'X' == *wz) + { + *pdwAttributes |= BURN_LOGGING_ATTRIBUTE_EXTRADEBUG; + } + + ++wz; + } + } + } + + ReleaseStr(sczLoggingPolicy); + ReleaseRegKey(hk); +} + +static HRESULT GetNonSessionSpecificTempFolder( + __deref_out_z LPWSTR* psczNonSessionTempFolder + ) +{ + HRESULT hr = S_OK; + WCHAR wzTempFolder[MAX_PATH] = { }; + DWORD cchTempFolder = 0; + DWORD dwSessionId = 0; + LPWSTR sczSessionId = 0; + DWORD cchSessionId = 0; + + if (!::GetTempPathW(countof(wzTempFolder), wzTempFolder)) + { + ExitWithLastError(hr, "Failed to get temp folder."); + } + + hr = ::StringCchLengthW(wzTempFolder, countof(wzTempFolder), reinterpret_cast(&cchTempFolder)); + ExitOnFailure(hr, "Failed to get length of temp folder."); + + // If our session id is in the TEMP path then remove that part so we get the non-session + // specific temporary folder. + if (::ProcessIdToSessionId(::GetCurrentProcessId(), &dwSessionId)) + { + hr = StrAllocFormatted(&sczSessionId, L"%u\\", dwSessionId); + ExitOnFailure(hr, "Failed to format session id as a string."); + + hr = ::StringCchLengthW(sczSessionId, STRSAFE_MAX_CCH, reinterpret_cast(&cchSessionId)); + ExitOnFailure(hr, "Failed to get length of session id string."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzTempFolder + cchTempFolder - cchSessionId, cchSessionId, sczSessionId, cchSessionId)) + { + cchTempFolder -= cchSessionId; + } + } + + hr = StrAllocString(psczNonSessionTempFolder, wzTempFolder, cchTempFolder); + ExitOnFailure(hr, "Failed to copy temp folder."); + +LExit: + ReleaseStr(sczSessionId); + + return hr; +} diff --git a/src/engine/logging.h b/src/engine/logging.h new file mode 100644 index 00000000..cea4d31d --- /dev/null +++ b/src/engine/logging.h @@ -0,0 +1,144 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + +enum BURN_LOGGING_STATE +{ + BURN_LOGGING_STATE_CLOSED, + BURN_LOGGING_STATE_OPEN, + BURN_LOGGING_STATE_DISABLED, +}; + +enum BURN_LOGGING_ATTRIBUTE +{ + BURN_LOGGING_ATTRIBUTE_APPEND = 0x1, + BURN_LOGGING_ATTRIBUTE_VERBOSE = 0x2, + BURN_LOGGING_ATTRIBUTE_EXTRADEBUG = 0x4, +}; + + +// structs + +typedef struct _BURN_LOGGING +{ + BURN_LOGGING_STATE state; + LPWSTR sczPathVariable; + + DWORD dwAttributes; + LPWSTR sczPath; + LPWSTR sczPrefix; + LPWSTR sczExtension; +} BURN_LOGGING; + + + +// function declarations + +HRESULT LoggingOpen( + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __in_z LPCWSTR wzBundleName + ); + +void LoggingOpenFailed(); + +void LoggingIncrementPackageSequence(); + +HRESULT LoggingSetPackageVariable( + __in BURN_PACKAGE* pPackage, + __in_z_opt LPCWSTR wzSuffix, + __in BOOL fRollback, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __out_opt LPWSTR* psczLogPath + ); + +LPCSTR LoggingBurnActionToString( + __in BOOTSTRAPPER_ACTION action + ); + +LPCSTR LoggingActionStateToString( + __in BOOTSTRAPPER_ACTION_STATE actionState + ); + +LPCSTR LoggingDependencyActionToString( + BURN_DEPENDENCY_ACTION action + ); + +LPCSTR LoggingBoolToString( + __in BOOL f + ); + +LPCSTR LoggingTrueFalseToString( + __in BOOL f + ); + +LPCSTR LoggingPackageStateToString( + __in BOOTSTRAPPER_PACKAGE_STATE packageState + ); + +LPCSTR LoggingCacheStateToString( + __in BURN_CACHE_STATE cacheState + ); + +LPCSTR LoggingMsiFeatureStateToString( + __in BOOTSTRAPPER_FEATURE_STATE featureState + ); + +LPCSTR LoggingMsiFeatureActionToString( + __in BOOTSTRAPPER_FEATURE_ACTION featureAction + ); + +LPCSTR LoggingMsiInstallContext( + __in MSIINSTALLCONTEXT context + ); + +LPCSTR LoggingPerMachineToString( + __in BOOL fPerMachine + ); + +LPCSTR LoggingRestartToString( + __in BOOTSTRAPPER_APPLY_RESTART restart + ); + +LPCSTR LoggingResumeModeToString( + __in BURN_RESUME_MODE resumeMode + ); + +LPCSTR LoggingRelationTypeToString( + __in BOOTSTRAPPER_RELATION_TYPE type + ); + +LPCSTR LoggingRelatedOperationToString( + __in BOOTSTRAPPER_RELATED_OPERATION operation + ); + +LPCSTR LoggingRequestStateToString( + __in BOOTSTRAPPER_REQUEST_STATE requestState + ); + +LPCSTR LoggingRollbackOrExecute( + __in BOOL fRollback + ); + +LPWSTR LoggingStringOrUnknownIfNull( + __in LPCWSTR wz + ); + +// Note: this function is not thread safe. +LPCSTR LoggingVersionToString( + __in DWORD64 dw64Version + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/manifest.cpp b/src/engine/manifest.cpp new file mode 100644 index 00000000..c2214a89 --- /dev/null +++ b/src/engine/manifest.cpp @@ -0,0 +1,125 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// function definitions + +extern "C" HRESULT ManifestLoadXmlFromBuffer( + __in_bcount(cbBuffer) BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + IXMLDOMDocument* pixdDocument = NULL; + IXMLDOMElement* pixeBundle = NULL; + IXMLDOMNode* pixnLog = NULL; + IXMLDOMNode* pixnChain = NULL; + + // load xml document + hr = XmlLoadDocumentFromBuffer(pbBuffer, cbBuffer, &pixdDocument); + ExitOnFailure(hr, "Failed to load manifest as XML document."); + + // get bundle element + hr = pixdDocument->get_documentElement(&pixeBundle); + ExitOnFailure(hr, "Failed to get bundle element."); + + // parse the log element, if present. + hr = XmlSelectSingleNode(pixeBundle, L"Log", &pixnLog); + ExitOnFailure(hr, "Failed to get Log element."); + + if (S_OK == hr) + { + hr = XmlGetAttributeEx(pixnLog, L"PathVariable", &pEngineState->log.sczPathVariable); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get Log/@PathVariable."); + } + + hr = XmlGetAttributeEx(pixnLog, L"Prefix", &pEngineState->log.sczPrefix); + ExitOnFailure(hr, "Failed to get Log/@Prefix attribute."); + + hr = XmlGetAttributeEx(pixnLog, L"Extension", &pEngineState->log.sczExtension); + ExitOnFailure(hr, "Failed to get Log/@Extension attribute."); + } + + // get the chain element + hr = XmlSelectSingleNode(pixeBundle, L"Chain", &pixnChain); + ExitOnFailure(hr, "Failed to get chain element."); + + if (S_OK == hr) + { + // parse disable rollback + hr = XmlGetYesNoAttribute(pixnChain, L"DisableRollback", &pEngineState->fDisableRollback); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get Chain/@DisableRollback"); + } + + // parse disable system restore + hr = XmlGetYesNoAttribute(pixnChain, L"DisableSystemRestore", &pEngineState->fDisableSystemRestore); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get Chain/@DisableSystemRestore"); + } + + // parse parallel cache + hr = XmlGetYesNoAttribute(pixnChain, L"ParallelCache", &pEngineState->fParallelCacheAndExecute); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get Chain/@ParallelCache"); + } + } + + // parse built-in condition + hr = ConditionGlobalParseFromXml(&pEngineState->condition, pixeBundle); + ExitOnFailure(hr, "Failed to parse global condition."); + + // parse variables + hr = VariablesParseFromXml(&pEngineState->variables, pixeBundle); + ExitOnFailure(hr, "Failed to parse variables."); + + // parse searches + hr = SearchesParseFromXml(&pEngineState->searches, pixeBundle); // TODO: Modularization + ExitOnFailure(hr, "Failed to parse searches."); + + // parse user experience + hr = UserExperienceParseFromXml(&pEngineState->userExperience, pixeBundle); + ExitOnFailure(hr, "Failed to parse user experience."); + + // parse catalog files + hr = CatalogsParseFromXml(&pEngineState->catalogs, pixeBundle); + ExitOnFailure(hr, "Failed to parse catalog files."); + + // parse registration + hr = RegistrationParseFromXml(&pEngineState->registration, pixeBundle); + ExitOnFailure(hr, "Failed to parse registration."); + + // parse update + hr = UpdateParseFromXml(&pEngineState->update, pixeBundle); + ExitOnFailure(hr, "Failed to parse update."); + + // parse containers + hr = ContainersParseFromXml(&pEngineState->section, &pEngineState->containers, pixeBundle); + ExitOnFailure(hr, "Failed to parse containers."); + + // parse payloads + hr = PayloadsParseFromXml(&pEngineState->payloads, &pEngineState->containers, &pEngineState->catalogs, pixeBundle); + ExitOnFailure(hr, "Failed to parse payloads."); + + // parse packages + hr = PackagesParseFromXml(&pEngineState->packages, &pEngineState->payloads, pixeBundle); + ExitOnFailure(hr, "Failed to parse packages."); + + // parse approved exes for elevation + hr = ApprovedExesParseFromXml(&pEngineState->approvedExes, pixeBundle); + ExitOnFailure(hr, "Failed to parse approved exes."); + +LExit: + ReleaseObject(pixnChain); + ReleaseObject(pixnLog); + ReleaseObject(pixeBundle); + ReleaseObject(pixdDocument); + return hr; +} diff --git a/src/engine/manifest.h b/src/engine/manifest.h new file mode 100644 index 00000000..6e535d60 --- /dev/null +++ b/src/engine/manifest.h @@ -0,0 +1,23 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +interface IBurnPayload; // forward declare. + +#if defined(__cplusplus) +extern "C" { +#endif + + +// function declarations + +HRESULT ManifestLoadXmlFromBuffer( + __in_bcount(cbBuffer) BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __in BURN_ENGINE_STATE* pEngineState + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp new file mode 100644 index 00000000..17571ac5 --- /dev/null +++ b/src/engine/msiengine.cpp @@ -0,0 +1,1910 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// constants + + +// structs + + + +// internal function declarations + +static HRESULT ParseRelatedMsiFromXml( + __in IXMLDOMNode* pixnRelatedMsi, + __in BURN_RELATED_MSI* pRelatedMsi + ); +static HRESULT EvaluateActionStateConditions( + __in BURN_VARIABLES* pVariables, + __in_z_opt LPCWSTR sczAddLocalCondition, + __in_z_opt LPCWSTR sczAddSourceCondition, + __in_z_opt LPCWSTR sczAdvertiseCondition, + __out BOOTSTRAPPER_FEATURE_STATE* pState + ); +static HRESULT CalculateFeatureAction( + __in BOOTSTRAPPER_FEATURE_STATE currentState, + __in BOOTSTRAPPER_FEATURE_STATE requestedState, + __in BOOL fRepair, + __out BOOTSTRAPPER_FEATURE_ACTION* pFeatureAction, + __inout BOOL* pfDelta + ); +static HRESULT EscapePropertyArgumentString( + __in LPCWSTR wzProperty, + __inout_z LPWSTR* psczEscapedValue, + __in BOOL fZeroOnRealloc + ); +static HRESULT ConcatFeatureActionProperties( + __in BURN_PACKAGE* pPackage, + __in BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions, + __inout_z LPWSTR* psczArguments + ); +static HRESULT ConcatPatchProperty( + __in BURN_PACKAGE* pPackage, + __in_opt BOOTSTRAPPER_ACTION_STATE* rgSlipstreamPatchActions, + __inout_z LPWSTR* psczArguments + ); +static void RegisterSourceDirectory( + __in BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzCacheDirectory + ); + + +// function definitions + +extern "C" HRESULT MsiEngineParsePackageFromXml( + __in IXMLDOMNode* pixnMsiPackage, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + LPWSTR scz = NULL; + + // @ProductCode + hr = XmlGetAttributeEx(pixnMsiPackage, L"ProductCode", &pPackage->Msi.sczProductCode); + ExitOnFailure(hr, "Failed to get @ProductCode."); + + // @Language + hr = XmlGetAttributeNumber(pixnMsiPackage, L"Language", &pPackage->Msi.dwLanguage); + ExitOnFailure(hr, "Failed to get @Language."); + + // @Version + hr = XmlGetAttributeEx(pixnMsiPackage, L"Version", &scz); + ExitOnFailure(hr, "Failed to get @Version."); + + hr = FileVersionFromStringEx(scz, 0, &pPackage->Msi.qwVersion); + ExitOnFailure(hr, "Failed to parse @Version: %ls", scz); + + // @DisplayInternalUI + hr = XmlGetYesNoAttribute(pixnMsiPackage, L"DisplayInternalUI", &pPackage->Msi.fDisplayInternalUI); + ExitOnFailure(hr, "Failed to get @DisplayInternalUI."); + + // @UpgradeCode + hr = XmlGetAttributeEx(pixnMsiPackage, L"UpgradeCode", &pPackage->Msi.sczUpgradeCode); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @UpgradeCode."); + } + + // select feature nodes + hr = XmlSelectNodes(pixnMsiPackage, L"MsiFeature", &pixnNodes); + ExitOnFailure(hr, "Failed to select feature nodes."); + + // get feature node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get feature node count."); + + if (cNodes) + { + // allocate memory for features + pPackage->Msi.rgFeatures = (BURN_MSIFEATURE*)MemAlloc(sizeof(BURN_MSIFEATURE) * cNodes, TRUE); + ExitOnNull(pPackage->Msi.rgFeatures, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSI feature structs."); + + pPackage->Msi.cFeatures = cNodes; + + // parse feature elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &pFeature->sczId); + ExitOnFailure(hr, "Failed to get @Id."); + + // @AddLocalCondition + hr = XmlGetAttributeEx(pixnNode, L"AddLocalCondition", &pFeature->sczAddLocalCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @AddLocalCondition."); + } + + // @AddSourceCondition + hr = XmlGetAttributeEx(pixnNode, L"AddSourceCondition", &pFeature->sczAddSourceCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @AddSourceCondition."); + } + + // @AdvertiseCondition + hr = XmlGetAttributeEx(pixnNode, L"AdvertiseCondition", &pFeature->sczAdvertiseCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @AdvertiseCondition."); + } + + // @RollbackAddLocalCondition + hr = XmlGetAttributeEx(pixnNode, L"RollbackAddLocalCondition", &pFeature->sczRollbackAddLocalCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @RollbackAddLocalCondition."); + } + + // @RollbackAddSourceCondition + hr = XmlGetAttributeEx(pixnNode, L"RollbackAddSourceCondition", &pFeature->sczRollbackAddSourceCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @RollbackAddSourceCondition."); + } + + // @RollbackAdvertiseCondition + hr = XmlGetAttributeEx(pixnNode, L"RollbackAdvertiseCondition", &pFeature->sczRollbackAdvertiseCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @RollbackAdvertiseCondition."); + } + + // prepare next iteration + ReleaseNullObject(pixnNode); + } + } + + ReleaseNullObject(pixnNodes); // done with the MsiFeature elements. + + hr = MsiEngineParsePropertiesFromXml(pixnMsiPackage, &pPackage->Msi.rgProperties, &pPackage->Msi.cProperties); + ExitOnFailure(hr, "Failed to parse properties from XML."); + + // select related MSI nodes + hr = XmlSelectNodes(pixnMsiPackage, L"RelatedPackage", &pixnNodes); + ExitOnFailure(hr, "Failed to select related MSI nodes."); + + // get related MSI node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get related MSI node count."); + + if (cNodes) + { + // allocate memory for related MSIs + pPackage->Msi.rgRelatedMsis = (BURN_RELATED_MSI*)MemAlloc(sizeof(BURN_RELATED_MSI) * cNodes, TRUE); + ExitOnNull(pPackage->Msi.rgRelatedMsis, hr, E_OUTOFMEMORY, "Failed to allocate memory for related MSI structs."); + + pPackage->Msi.cRelatedMsis = cNodes; + + // parse related MSI elements + for (DWORD i = 0; i < cNodes; ++i) + { + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // parse related MSI element + hr = ParseRelatedMsiFromXml(pixnNode, &pPackage->Msi.rgRelatedMsis[i]); + ExitOnFailure(hr, "Failed to parse related MSI element."); + + // prepare next iteration + ReleaseNullObject(pixnNode); + } + } + + ReleaseNullObject(pixnNodes); // done with the RelatedPackage elements. + + // Select slipstream MSP nodes. + hr = XmlSelectNodes(pixnMsiPackage, L"SlipstreamMsp", &pixnNodes); + ExitOnFailure(hr, "Failed to select related MSI nodes."); + + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get related MSI node count."); + + if (cNodes) + { + pPackage->Msi.rgpSlipstreamMspPackages = reinterpret_cast(MemAlloc(sizeof(BURN_PACKAGE*) * cNodes, TRUE)); + ExitOnNull(pPackage->Msi.rgpSlipstreamMspPackages, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP packages."); + + pPackage->Msi.rgsczSlipstreamMspPackageIds = reinterpret_cast(MemAlloc(sizeof(LPWSTR*) * cNodes, TRUE)); + ExitOnNull(pPackage->Msi.rgsczSlipstreamMspPackageIds, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP ids."); + + pPackage->Msi.cSlipstreamMspPackages = cNodes; + + // Parse slipstream MSP Ids. + for (DWORD i = 0; i < cNodes; ++i) + { + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next slipstream MSP node."); + + hr = XmlGetAttributeEx(pixnNode, L"Id", pPackage->Msi.rgsczSlipstreamMspPackageIds + i); + ExitOnFailure(hr, "Failed to parse slipstream MSP ids."); + + ReleaseNullObject(pixnNode); + } + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseStr(scz); + + return hr; +} + +extern "C" HRESULT MsiEngineParsePropertiesFromXml( + __in IXMLDOMNode* pixnPackage, + __out BURN_MSIPROPERTY** prgProperties, + __out DWORD* pcProperties + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + + BURN_MSIPROPERTY* pProperties = NULL; + + // select property nodes + hr = XmlSelectNodes(pixnPackage, L"MsiProperty", &pixnNodes); + ExitOnFailure(hr, "Failed to select property nodes."); + + // get property node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get property node count."); + + if (cNodes) + { + // allocate memory for properties + pProperties = (BURN_MSIPROPERTY*)MemAlloc(sizeof(BURN_MSIPROPERTY) * cNodes, TRUE); + ExitOnNull(pProperties, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSI property structs."); + + // parse property elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_MSIPROPERTY* pProperty = &pProperties[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &pProperty->sczId); + ExitOnFailure(hr, "Failed to get @Id."); + + // @Value + hr = XmlGetAttributeEx(pixnNode, L"Value", &pProperty->sczValue); + ExitOnFailure(hr, "Failed to get @Value."); + + // @RollbackValue + hr = XmlGetAttributeEx(pixnNode, L"RollbackValue", &pProperty->sczRollbackValue); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @RollbackValue."); + } + + // @Condition + hr = XmlGetAttributeEx(pixnNode, L"Condition", &pProperty->sczCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Condition."); + } + + // prepare next iteration + ReleaseNullObject(pixnNode); + } + } + + *pcProperties = cNodes; + *prgProperties = pProperties; + pProperties = NULL; + + hr = S_OK; + +LExit: + ReleaseNullObject(pixnNodes); + ReleaseMem(pProperties); + + return hr; +} + +extern "C" void MsiEnginePackageUninitialize( + __in BURN_PACKAGE* pPackage + ) +{ + ReleaseStr(pPackage->Msi.sczProductCode); + ReleaseStr(pPackage->Msi.sczUpgradeCode); + ReleaseStr(pPackage->Msi.sczInstalledProductCode); + + // free features + if (pPackage->Msi.rgFeatures) + { + for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) + { + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; + + ReleaseStr(pFeature->sczId); + ReleaseStr(pFeature->sczAddLocalCondition); + ReleaseStr(pFeature->sczAddSourceCondition); + ReleaseStr(pFeature->sczAdvertiseCondition); + ReleaseStr(pFeature->sczRollbackAddLocalCondition); + ReleaseStr(pFeature->sczRollbackAddSourceCondition); + ReleaseStr(pFeature->sczRollbackAdvertiseCondition); + } + MemFree(pPackage->Msi.rgFeatures); + } + + // free properties + if (pPackage->Msi.rgProperties) + { + for (DWORD i = 0; i < pPackage->Msi.cProperties; ++i) + { + BURN_MSIPROPERTY* pProperty = &pPackage->Msi.rgProperties[i]; + + ReleaseStr(pProperty->sczId); + ReleaseStr(pProperty->sczValue); + ReleaseStr(pProperty->sczRollbackValue); + ReleaseStr(pProperty->sczCondition); + } + MemFree(pPackage->Msi.rgProperties); + } + + // free related MSIs + if (pPackage->Msi.rgRelatedMsis) + { + for (DWORD i = 0; i < pPackage->Msi.cRelatedMsis; ++i) + { + BURN_RELATED_MSI* pRelatedMsi = &pPackage->Msi.rgRelatedMsis[i]; + + ReleaseStr(pRelatedMsi->sczUpgradeCode); + ReleaseMem(pRelatedMsi->rgdwLanguages); + } + MemFree(pPackage->Msi.rgRelatedMsis); + } + + // free slipstream MSPs + if (pPackage->Msi.rgsczSlipstreamMspPackageIds) + { + for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) + { + ReleaseStr(pPackage->Msi.rgsczSlipstreamMspPackageIds[i]); + } + + MemFree(pPackage->Msi.rgsczSlipstreamMspPackageIds); + } + + if (pPackage->Msi.rgpSlipstreamMspPackages) + { + MemFree(pPackage->Msi.rgpSlipstreamMspPackages); + } + + // clear struct + memset(&pPackage->Msi, 0, sizeof(pPackage->Msi)); +} + +extern "C" HRESULT MsiEngineDetectPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + Trace(REPORT_STANDARD, "Detecting MSI package 0x%p", pPackage); + + HRESULT hr = S_OK; + LPWSTR sczInstalledVersion = NULL; + LPWSTR sczInstalledLanguage = NULL; + LPWSTR sczInstalledProductCode = NULL; + LPWSTR sczInstalledProviderKey = NULL; + INSTALLSTATE installState = INSTALLSTATE_UNKNOWN; + BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE; + BOOTSTRAPPER_RELATED_OPERATION relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE; + WCHAR wzProductCode[MAX_GUID_CHARS + 1] = { }; + DWORD64 qwVersion = 0; + UINT uLcid = 0; + BOOL fPerMachine = FALSE; + + // detect self by product code + // TODO: what to do about MSIINSTALLCONTEXT_USERMANAGED? + hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); + if (SUCCEEDED(hr)) + { + hr = FileVersionFromStringEx(sczInstalledVersion, 0, &pPackage->Msi.qwInstalledVersion); + ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for ProductCode: %ls", sczInstalledVersion, pPackage->Msi.sczProductCode); + + // compare versions + if (pPackage->Msi.qwVersion < pPackage->Msi.qwInstalledVersion) + { + operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; + } + else + { + if (pPackage->Msi.qwVersion > pPackage->Msi.qwInstalledVersion) + { + operation = BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE; + } + + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; + } + + // Report related MSI package to BA. + if (BOOTSTRAPPER_RELATED_OPERATION_NONE != operation) + { + LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, pPackage->Msi.sczProductCode, LoggingPerMachineToString(pPackage->fPerMachine), LoggingVersionToString(pPackage->Msi.qwInstalledVersion), pPackage->Msi.dwLanguage, LoggingRelatedOperationToString(operation)); + + hr = UserExperienceOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pPackage->Msi.sczUpgradeCode, pPackage->Msi.sczProductCode, pPackage->fPerMachine, pPackage->Msi.qwInstalledVersion, operation); + ExitOnRootFailure(hr, "BA aborted detect related MSI package."); + } + } + else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr || HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr) // package not present. + { + // Check for newer, compatible packages based on a fixed provider key. + hr = DependencyDetectProviderKeyPackageId(pPackage, &sczInstalledProviderKey, &sczInstalledProductCode); + if (SUCCEEDED(hr)) + { + hr = WiuGetProductInfoEx(sczInstalledProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); + if (SUCCEEDED(hr)) + { + hr = FileVersionFromStringEx(sczInstalledVersion, 0, &qwVersion); + ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for ProductCode: %ls", sczInstalledVersion, sczInstalledProductCode); + + if (pPackage->Msi.qwVersion < qwVersion) + { + LogId(REPORT_STANDARD, MSG_DETECTED_COMPATIBLE_PACKAGE_FROM_PROVIDER, pPackage->sczId, sczInstalledProviderKey, sczInstalledProductCode, sczInstalledVersion, pPackage->Msi.sczProductCode); + + hr = UserExperienceOnDetectCompatibleMsiPackage(pUserExperience, pPackage->sczId, sczInstalledProductCode, qwVersion); + ExitOnRootFailure(hr, "BA aborted detect compatible MSI package."); + + hr = StrAllocString(&pPackage->Msi.sczInstalledProductCode, sczInstalledProductCode, 0); + ExitOnFailure(hr, "Failed to copy the installed ProductCode to the package."); + + pPackage->Msi.qwInstalledVersion = qwVersion; + pPackage->Msi.fCompatibleInstalled = TRUE; + } + } + } + + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to get product information for ProductCode: %ls", pPackage->Msi.sczProductCode); + } + + // detect related packages by upgrade code + for (DWORD i = 0; i < pPackage->Msi.cRelatedMsis; ++i) + { + BURN_RELATED_MSI* pRelatedMsi = &pPackage->Msi.rgRelatedMsis[i]; + + for (DWORD iProduct = 0; ; ++iProduct) + { + // get product + hr = WiuEnumRelatedProducts(pRelatedMsi->sczUpgradeCode, iProduct, wzProductCode); + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + break; + } + ExitOnFailure(hr, "Failed to enum related products."); + + // If we found ourselves, skip because saying that a package is related to itself is nonsensical. + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pPackage->Msi.sczProductCode, -1, wzProductCode, -1)) + { + continue; + } + + // get product version + hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr) + { + ExitOnFailure(hr, "Failed to get version for product in user unmanaged context: %ls", wzProductCode); + fPerMachine = FALSE; + } + else + { + hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr) + { + ExitOnFailure(hr, "Failed to get version for product in machine context: %ls", wzProductCode); + fPerMachine = TRUE; + } + else + { + hr = S_OK; + continue; + } + } + + hr = FileVersionFromStringEx(sczInstalledVersion, 0, &qwVersion); + ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for ProductCode: %ls", sczInstalledVersion, wzProductCode); + + // compare versions + if (pRelatedMsi->fMinProvided && (pRelatedMsi->fMinInclusive ? (qwVersion < pRelatedMsi->qwMinVersion) : (qwVersion <= pRelatedMsi->qwMinVersion))) + { + continue; + } + + if (pRelatedMsi->fMaxProvided && (pRelatedMsi->fMaxInclusive ? (qwVersion > pRelatedMsi->qwMaxVersion) : (qwVersion >= pRelatedMsi->qwMaxVersion))) + { + continue; + } + + // Filter by language if necessary. + uLcid = 0; // always reset the found language. + if (pRelatedMsi->cLanguages) + { + // If there is a language to get, convert it into an LCID. + hr = WiuGetProductInfoEx(wzProductCode, NULL, fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_LANGUAGE, &sczInstalledLanguage); + if (SUCCEEDED(hr)) + { + hr = StrStringToUInt32(sczInstalledLanguage, 0, &uLcid); + } + + // Ignore related product where we can't read the language. + if (FAILED(hr)) + { + LogErrorId(hr, MSG_FAILED_READ_RELATED_PACKAGE_LANGUAGE, wzProductCode, sczInstalledLanguage, NULL); + + hr = S_OK; + continue; + } + + BOOL fMatchedLcid = FALSE; + for (DWORD iLanguage = 0; iLanguage < pRelatedMsi->cLanguages; ++iLanguage) + { + if (uLcid == pRelatedMsi->rgdwLanguages[iLanguage]) + { + fMatchedLcid = TRUE; + break; + } + } + + // Skip the product if the language did not meet the inclusive/exclusive criteria. + if ((pRelatedMsi->fLangInclusive && !fMatchedLcid) || (!pRelatedMsi->fLangInclusive && fMatchedLcid)) + { + continue; + } + } + + // If this is a detect-only related package and we're not installed yet, then we'll assume a downgrade + // would take place since that is the overwhelmingly common use of detect-only related packages. If + // not detect-only then it's easy; we're clearly doing a major upgrade. + if (pRelatedMsi->fOnlyDetect) + { + // If we've already detected a major upgrade that trumps any guesses that the detect is a downgrade + // or even something else. + if (BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE == operation) + { + relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE; + } + // It can't be a downgrade if the upgrade codes aren't the same. + else if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == pPackage->currentState && + pPackage->Msi.sczUpgradeCode && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pPackage->Msi.sczUpgradeCode, -1, pRelatedMsi->sczUpgradeCode, -1)) + { + relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; + operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE; + } + else // we're already on the machine so the detect-only *must* be for detection purposes only. + { + relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE; + } + } + else + { + relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE; + operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE; + } + + LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, wzProductCode, LoggingPerMachineToString(fPerMachine), LoggingVersionToString(qwVersion), uLcid, LoggingRelatedOperationToString(relatedMsiOperation)); + + // Pass to BA. + hr = UserExperienceOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pRelatedMsi->sczUpgradeCode, wzProductCode, fPerMachine, qwVersion, relatedMsiOperation); + ExitOnRootFailure(hr, "BA aborted detect related MSI package."); + } + } + + // detect features + if (pPackage->Msi.cFeatures) + { + for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) + { + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; + + // Try to detect features state if the product is present on the machine. + if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT <= pPackage->currentState) + { + hr = WiuQueryFeatureState(pPackage->Msi.sczProductCode, pFeature->sczId, &installState); + ExitOnFailure(hr, "Failed to query feature state."); + + if (INSTALLSTATE_UNKNOWN == installState) // in case of an upgrade a feature could be removed. + { + installState = INSTALLSTATE_ABSENT; + } + } + else // MSI not installed then the features can't be either. + { + installState = INSTALLSTATE_ABSENT; + } + + // set current state + switch (installState) + { + case INSTALLSTATE_ABSENT: + pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_ABSENT; + break; + case INSTALLSTATE_ADVERTISED: + pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_ADVERTISED; + break; + case INSTALLSTATE_LOCAL: + pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_LOCAL; + break; + case INSTALLSTATE_SOURCE: + pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_SOURCE; + break; + default: + hr = E_UNEXPECTED; + ExitOnRootFailure(hr, "Invalid state value."); + } + + // Pass to BA. + hr = UserExperienceOnDetectMsiFeature(pUserExperience, pPackage->sczId, pFeature->sczId, pFeature->currentState); + ExitOnRootFailure(hr, "BA aborted detect MSI feature."); + } + } + +LExit: + ReleaseStr(sczInstalledProviderKey); + ReleaseStr(sczInstalledProductCode); + ReleaseStr(sczInstalledLanguage); + ReleaseStr(sczInstalledVersion); + + return hr; +} + +// +// PlanCalculate - calculates the execute and rollback state for the requested package state. +// +extern "C" HRESULT MsiEnginePlanCalculatePackage( + __in BURN_PACKAGE* pPackage, + __in BURN_VARIABLES* pVariables, + __in BURN_USER_EXPERIENCE* pUserExperience, + __out BOOL* pfBARequestedCache + ) +{ + Trace(REPORT_STANDARD, "Planning MSI package 0x%p", pPackage); + + HRESULT hr = S_OK; + DWORD64 qwVersion = pPackage->Msi.qwVersion; + DWORD64 qwInstalledVersion = pPackage->Msi.qwInstalledVersion; + BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; + BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + BOOL fFeatureActionDelta = FALSE; + BOOL fRollbackFeatureActionDelta = FALSE; + BOOL fBARequestedCache = FALSE; + + if (pPackage->Msi.cFeatures) + { + // If the package is present and we're repairing it. + BOOL fRepairingPackage = (BOOTSTRAPPER_PACKAGE_STATE_CACHED < pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested); + + LogId(REPORT_STANDARD, MSG_PLAN_MSI_FEATURES, pPackage->Msi.cFeatures, pPackage->sczId); + + // plan features + for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) + { + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; + BOOTSTRAPPER_FEATURE_STATE defaultFeatureRequestedState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; + BOOTSTRAPPER_FEATURE_STATE featureRequestedState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; + BOOTSTRAPPER_FEATURE_STATE featureExpectedState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; + + // Evaluate feature conditions. + hr = EvaluateActionStateConditions(pVariables, pFeature->sczAddLocalCondition, pFeature->sczAddSourceCondition, pFeature->sczAdvertiseCondition, &defaultFeatureRequestedState); + ExitOnFailure(hr, "Failed to evaluate requested state conditions."); + + hr = EvaluateActionStateConditions(pVariables, pFeature->sczRollbackAddLocalCondition, pFeature->sczRollbackAddSourceCondition, pFeature->sczRollbackAdvertiseCondition, &featureExpectedState); + ExitOnFailure(hr, "Failed to evaluate expected state conditions."); + + // Remember the default feature requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it. + featureRequestedState = defaultFeatureRequestedState; + + // Send plan MSI feature message to BA. + hr = UserExperienceOnPlanMsiFeature(pUserExperience, pPackage->sczId, pFeature->sczId, &featureRequestedState); + ExitOnRootFailure(hr, "BA aborted plan MSI feature."); + + // Calculate feature actions. + hr = CalculateFeatureAction(pFeature->currentState, featureRequestedState, fRepairingPackage, &pFeature->execute, &fFeatureActionDelta); + ExitOnFailure(hr, "Failed to calculate execute feature state."); + + hr = CalculateFeatureAction(featureRequestedState, BOOTSTRAPPER_FEATURE_ACTION_NONE == pFeature->execute ? featureExpectedState : pFeature->currentState, FALSE, &pFeature->rollback, &fRollbackFeatureActionDelta); + ExitOnFailure(hr, "Failed to calculate rollback feature state."); + + LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURE, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState), LoggingMsiFeatureStateToString(defaultFeatureRequestedState), LoggingMsiFeatureStateToString(featureRequestedState), LoggingMsiFeatureActionToString(pFeature->execute), LoggingMsiFeatureActionToString(pFeature->rollback)); + } + } + + // execute action + switch (pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: + if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) + { + // Take a look at the version and determine if this is a potential + // minor upgrade (same ProductCode newer ProductVersion), otherwise, + // there is a newer version so no work necessary. + if (qwVersion > qwInstalledVersion) + { + execute = BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE; + } + else if (BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) + { + execute = BOOTSTRAPPER_ACTION_STATE_REPAIR; + } + else + { + execute = fFeatureActionDelta ? BOOTSTRAPPER_ACTION_STATE_MODIFY : BOOTSTRAPPER_ACTION_STATE_NONE; + } + } + else if ((BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_CACHE == pPackage->requested) && + pPackage->fUninstallable) // removing a package that can be removed. + { + execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; + } + else if (BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested) + { + execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; + } + else + { + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + } + break; + + case BOOTSTRAPPER_PACKAGE_STATE_CACHED: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; + break; + + default: + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough; + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; + break; + + case BOOTSTRAPPER_REQUEST_STATE_CACHE: + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + fBARequestedCache = TRUE; + break; + + default: + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid package current state result encountered during plan: %d", pPackage->currentState); + } + + // Calculate the rollback action if there is an execute action. + if (BOOTSTRAPPER_ACTION_STATE_NONE != execute) + { + switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: + rollback = fRollbackFeatureActionDelta ? BOOTSTRAPPER_ACTION_STATE_MODIFY : BOOTSTRAPPER_ACTION_STATE_NONE; + break; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_ABSENT: + rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL; + break; + default: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough; + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough; + case BOOTSTRAPPER_PACKAGE_STATE_CACHED: + // If we requested to put the package on the machine then remove the package during rollback + // if the package is uninstallable. + if ((BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) && + pPackage->fUninstallable) + { + rollback = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; + } + else + { + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + } + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid package detection result encountered."); + } + } + + // return values + pPackage->execute = execute; + pPackage->rollback = rollback; + + if (pfBARequestedCache) + { + *pfBARequestedCache = fBARequestedCache; + } + +LExit: + return hr; +} + +// +// PlanAdd - adds the calculated execute and rollback actions for the package. +// +extern "C" HRESULT MsiEnginePlanAddPackage( + __in BOOTSTRAPPER_DISPLAY display, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in_opt HANDLE hCacheEvent, + __in BOOL fPlanPackageCacheRollback + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* pAction = NULL; + BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions = NULL; + BOOTSTRAPPER_FEATURE_ACTION* rgRollbackFeatureActions = NULL; + + if (pPackage->Msi.cFeatures) + { + // Allocate and populate array for feature actions. + rgFeatureActions = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(sizeof(BOOTSTRAPPER_FEATURE_ACTION) * pPackage->Msi.cFeatures, TRUE); + ExitOnNull(rgFeatureActions, hr, E_OUTOFMEMORY, "Failed to allocate memory for feature actions."); + + rgRollbackFeatureActions = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(sizeof(BOOTSTRAPPER_FEATURE_ACTION) * pPackage->Msi.cFeatures, TRUE); + ExitOnNull(rgRollbackFeatureActions, hr, E_OUTOFMEMORY, "Failed to allocate memory for rollback feature actions."); + + for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) + { + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; + + // calculate feature actions + rgFeatureActions[i] = pFeature->execute; + rgRollbackFeatureActions[i] = pFeature->rollback; + } + } + + // add wait for cache + if (hCacheEvent) + { + hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent, fPlanPackageCacheRollback); + ExitOnFailure(hr, "Failed to plan package cache syncpoint"); + } + + hr = DependencyPlanPackage(NULL, pPackage, pPlan); + ExitOnFailure(hr, "Failed to plan package dependency actions."); + + // add rollback action + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback) + { + hr = PlanAppendRollbackAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append rollback action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE; + pAction->msiPackage.pPackage = pPackage; + pAction->msiPackage.action = pPackage->rollback; + pAction->msiPackage.uiLevel = MsiEngineCalculateInstallUiLevel(pPackage->Msi.fDisplayInternalUI, display, pAction->msiPackage.action); + pAction->msiPackage.rgFeatures = rgRollbackFeatureActions; + rgRollbackFeatureActions = NULL; + + LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors. + pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes; + + // Plan a checkpoint between rollback and execute so that we always attempt + // rollback in the case that the MSI was not able to rollback itself (e.g. + // user pushes cancel after InstallFinalize). + hr = PlanExecuteCheckpoint(pPlan); + ExitOnFailure(hr, "Failed to append execute checkpoint."); + } + + // add execute action + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute) + { + hr = PlanAppendExecuteAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append execute action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE; + pAction->msiPackage.pPackage = pPackage; + pAction->msiPackage.action = pPackage->execute; + pAction->msiPackage.uiLevel = MsiEngineCalculateInstallUiLevel(pPackage->Msi.fDisplayInternalUI, display, pAction->msiPackage.action); + pAction->msiPackage.rgFeatures = rgFeatureActions; + rgFeatureActions = NULL; + + LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors. + pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes; + } + + // Update any slipstream patches' state. + for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) + { + BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[i]; + AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches."); + + MspEngineSlipstreamUpdateState(pMspPackage, pPackage->execute, pPackage->rollback); + } + +LExit: + ReleaseMem(rgFeatureActions); + ReleaseMem(rgRollbackFeatureActions); + + return hr; +} + +extern "C" HRESULT MsiEngineAddCompatiblePackage( + __in BURN_PACKAGES* pPackages, + __in const BURN_PACKAGE* pPackage, + __out_opt BURN_PACKAGE** ppCompatiblePackage + ) +{ + Assert(BURN_PACKAGE_TYPE_MSI == pPackage->type); + + HRESULT hr = S_OK; + BURN_PACKAGE* pCompatiblePackage = NULL; + LPWSTR sczInstalledVersion = NULL; + + // Allocate enough memory all at once so pointers to packages within + // aren't invalidated if we otherwise reallocated. + hr = PackageEnsureCompatiblePackagesArray(pPackages); + ExitOnFailure(hr, "Failed to allocate memory for compatible MSI package."); + + pCompatiblePackage = pPackages->rgCompatiblePackages + pPackages->cCompatiblePackages; + ++pPackages->cCompatiblePackages; + + pCompatiblePackage->type = BURN_PACKAGE_TYPE_MSI; + + // Read in the compatible ProductCode if not already available. + if (pPackage->Msi.sczInstalledProductCode) + { + hr = StrAllocString(&pCompatiblePackage->Msi.sczProductCode, pPackage->Msi.sczInstalledProductCode, 0); + ExitOnFailure(hr, "Failed to copy installed ProductCode to compatible package."); + } + else + { + hr = DependencyDetectProviderKeyPackageId(pPackage, NULL, &pCompatiblePackage->Msi.sczProductCode); + ExitOnFailure(hr, "Failed to detect compatible package from provider key."); + } + + // Read in the compatible ProductVersion if not already available. + if (pPackage->Msi.qwInstalledVersion) + { + pCompatiblePackage->Msi.qwVersion = pPackage->Msi.qwInstalledVersion; + + hr = FileVersionToStringEx(pCompatiblePackage->Msi.qwVersion, &sczInstalledVersion); + ExitOnFailure(hr, "Failed to format version number string."); + } + else + { + hr = WiuGetProductInfoEx(pCompatiblePackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); + ExitOnFailure(hr, "Failed to read version from compatible package."); + + hr = FileVersionFromStringEx(sczInstalledVersion, 0, &pCompatiblePackage->Msi.qwVersion); + ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for ProductCode: %ls", sczInstalledVersion, pCompatiblePackage->Msi.sczProductCode); + } + + // For now, copy enough information to support uninstalling the newer, compatible package. + hr = StrAllocString(&pCompatiblePackage->sczId, pCompatiblePackage->Msi.sczProductCode, 0); + ExitOnFailure(hr, "Failed to copy installed ProductCode as compatible package ID."); + + pCompatiblePackage->fPerMachine = pPackage->fPerMachine; + pCompatiblePackage->fUninstallable = pPackage->fUninstallable; + pCompatiblePackage->cacheType = pPackage->cacheType; + + // Removing compatible packages is best effort. + pCompatiblePackage->fVital = FALSE; + + // Format a suitable log path variable from the original package. + hr = StrAllocFormatted(&pCompatiblePackage->sczLogPathVariable, L"%ls_Compatible", pPackage->sczLogPathVariable); + ExitOnFailure(hr, "Failed to format log path variable for compatible package."); + + // Use the default cache ID generation from the binder. + hr = StrAllocFormatted(&pCompatiblePackage->sczCacheId, L"%lsv%ls", pCompatiblePackage->sczId, sczInstalledVersion); + ExitOnFailure(hr, "Failed to format cache ID for compatible package."); + + pCompatiblePackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; + pCompatiblePackage->cache = BURN_CACHE_STATE_PARTIAL; // Cannot know if it's complete or not. + + // Copy all the providers to ensure no dependents. + if (pPackage->cDependencyProviders) + { + pCompatiblePackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER) * pPackage->cDependencyProviders, TRUE); + ExitOnNull(pCompatiblePackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate for compatible package providers."); + + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + i; + BURN_DEPENDENCY_PROVIDER* pCompatibleProvider = pCompatiblePackage->rgDependencyProviders + i; + + // Only need to copy the key for uninstall. + hr = StrAllocString(&pCompatibleProvider->sczKey, pProvider->sczKey, 0); + ExitOnFailure(hr, "Failed to copy the compatible provider key."); + + // Assume the package version is the same as the provider version. + hr = StrAllocString(&pCompatibleProvider->sczVersion, sczInstalledVersion, 0); + ExitOnFailure(hr, "Failed to copy the compatible provider version."); + + // Assume provider keys are similarly authored for this package. + pCompatibleProvider->fImported = pProvider->fImported; + } + + pCompatiblePackage->cDependencyProviders = pPackage->cDependencyProviders; + } + + pCompatiblePackage->type = BURN_PACKAGE_TYPE_MSI; + pCompatiblePackage->Msi.fDisplayInternalUI = pPackage->Msi.fDisplayInternalUI; + + if (ppCompatiblePackage) + { + *ppCompatiblePackage = pCompatiblePackage; + } + +LExit: + ReleaseStr(sczInstalledVersion); + + return hr; +} + +extern "C" HRESULT MsiEngineExecutePackage( + __in_opt HWND hwndParent, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + WIU_MSI_EXECUTE_CONTEXT context = { }; + WIU_RESTART restart = WIU_RESTART_NONE; + + LPWSTR sczInstalledVersion = NULL; + LPWSTR sczCachedDirectory = NULL; + LPWSTR sczMsiPath = NULL; + LPWSTR sczProperties = NULL; + LPWSTR sczObfuscatedProperties = NULL; + + // During rollback, if the package is already in the rollback state we expect don't + // touch it again. + if (fRollback) + { + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->msiPackage.action) + { + hr = WiuGetProductInfoEx(pExecuteAction->msiPackage.pPackage->Msi.sczProductCode, NULL, pExecuteAction->msiPackage.pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); + if (FAILED(hr)) // package not present. + { + LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pExecuteAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_ABSENT)); + + hr = S_OK; + ExitFunction(); + } + } + else if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->msiPackage.action) + { + hr = WiuGetProductInfoEx(pExecuteAction->msiPackage.pPackage->Msi.sczProductCode, NULL, pExecuteAction->msiPackage.pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); + if (SUCCEEDED(hr)) // package present. + { + LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pExecuteAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_PRESENT)); + + hr = S_OK; + ExitFunction(); + } + + hr = S_OK; + } + } + + // Default to "verbose" logging and set extra debug mode only if explicitly required. + DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE; + + if (pExecuteAction->msiPackage.dwLoggingAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG) + { + dwLogMode |= INSTALLLOGMODE_EXTRADEBUG; + } + + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL != pExecuteAction->msiPackage.action) + { + // get cached MSI path + hr = CacheGetCompletedPath(pExecuteAction->msiPackage.pPackage->fPerMachine, pExecuteAction->msiPackage.pPackage->sczCacheId, &sczCachedDirectory); + ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->msiPackage.pPackage->sczId); + + // Best effort to set the execute package cache folder variable. + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); + + hr = PathConcat(sczCachedDirectory, pExecuteAction->msiPackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczMsiPath); + ExitOnFailure(hr, "Failed to build MSI path."); + } + + // Best effort to set the execute package action variable. + VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->msiPackage.action, TRUE); + + // Wire up the external UI handler and logging. + hr = WiuInitializeExternalUI(pfnMessageHandler, pExecuteAction->msiPackage.uiLevel, hwndParent, pvContext, fRollback, &context); + ExitOnFailure(hr, "Failed to initialize external UI handler."); + + if (pExecuteAction->msiPackage.sczLogPath && *pExecuteAction->msiPackage.sczLogPath) + { + hr = WiuEnableLog(dwLogMode, pExecuteAction->msiPackage.sczLogPath, 0); + ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pExecuteAction->msiPackage.pPackage->sczId, pExecuteAction->msiPackage.sczLogPath); + } + + // set up properties + hr = MsiEngineConcatProperties(pExecuteAction->msiPackage.pPackage->Msi.rgProperties, pExecuteAction->msiPackage.pPackage->Msi.cProperties, pVariables, fRollback, &sczProperties, FALSE); + ExitOnFailure(hr, "Failed to add properties to argument string."); + + hr = MsiEngineConcatProperties(pExecuteAction->msiPackage.pPackage->Msi.rgProperties, pExecuteAction->msiPackage.pPackage->Msi.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE); + ExitOnFailure(hr, "Failed to add obfuscated properties to argument string."); + + // add feature action properties + hr = ConcatFeatureActionProperties(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgFeatures, &sczProperties); + ExitOnFailure(hr, "Failed to add feature action properties to argument string."); + + hr = ConcatFeatureActionProperties(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgFeatures, &sczObfuscatedProperties); + ExitOnFailure(hr, "Failed to add feature action properties to obfuscated argument string."); + + // add slipstream patch properties + hr = ConcatPatchProperty(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgSlipstreamPatches, &sczProperties); + ExitOnFailure(hr, "Failed to add patch properties to argument string."); + + hr = ConcatPatchProperty(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgSlipstreamPatches, &sczObfuscatedProperties); + ExitOnFailure(hr, "Failed to add patch properties to obfuscated argument string."); + + LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), sczMsiPath, sczObfuscatedProperties ? sczObfuscatedProperties : L""); + + // + // Do the actual action. + // + switch (pExecuteAction->msiPackage.action) + { + case BOOTSTRAPPER_ACTION_STATE_ADMIN_INSTALL: + hr = StrAllocConcatSecure(&sczProperties, L" ACTION=ADMIN", 0); + ExitOnFailure(hr, "Failed to add ADMIN property on admin install."); + __fallthrough; + + case BOOTSTRAPPER_ACTION_STATE_MAJOR_UPGRADE: __fallthrough; + case BOOTSTRAPPER_ACTION_STATE_INSTALL: + hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0); + ExitOnFailure(hr, "Failed to add reboot suppression property on install."); + + hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart); + ExitOnFailure(hr, "Failed to install MSI package."); + + RegisterSourceDirectory(pExecuteAction->msiPackage.pPackage, sczMsiPath); + break; + + case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: + // If feature selection is not enabled, then reinstall the existing features to ensure they get + // updated. + if (0 == pExecuteAction->msiPackage.pPackage->Msi.cFeatures) + { + hr = StrAllocConcatSecure(&sczProperties, L" REINSTALL=ALL", 0); + ExitOnFailure(hr, "Failed to add reinstall all property on minor upgrade."); + } + + hr = StrAllocConcatSecure(&sczProperties, L" REINSTALLMODE=\"vomus\" REBOOT=ReallySuppress", 0); + ExitOnFailure(hr, "Failed to add reinstall mode and reboot suppression properties on minor upgrade."); + + hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart); + ExitOnFailure(hr, "Failed to perform minor upgrade of MSI package."); + + RegisterSourceDirectory(pExecuteAction->msiPackage.pPackage, sczMsiPath); + break; + + case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough; + case BOOTSTRAPPER_ACTION_STATE_REPAIR: + { + LPCWSTR wzReinstallAll = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action || + pExecuteAction->msiPackage.pPackage->Msi.cFeatures) ? L"" : L" REINSTALL=ALL"; + LPCWSTR wzReinstallMode = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action) ? L"o" : L"e"; + + hr = StrAllocFormattedSecure(&sczProperties, L"%ls%ls REINSTALLMODE=\"cmus%ls\" REBOOT=ReallySuppress", sczProperties ? sczProperties : L"", wzReinstallAll, wzReinstallMode); + ExitOnFailure(hr, "Failed to add reinstall mode and reboot suppression properties on repair."); + } + + // Ignore all dependencies, since the Burn engine already performed the check. + hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES); + ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties."); + + hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart); + ExitOnFailure(hr, "Failed to run maintenance mode for MSI package."); + break; + + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: + hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0); + ExitOnFailure(hr, "Failed to add reboot suppression property on uninstall."); + + // Ignore all dependencies, since the Burn engine already performed the check. + hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES); + ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties."); + + hr = WiuConfigureProductEx(pExecuteAction->msiPackage.pPackage->Msi.sczProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, sczProperties, &restart); + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr) + { + LogId(REPORT_STANDARD, MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE, pExecuteAction->msiPackage.pPackage->sczId); + hr = S_OK; + } + ExitOnFailure(hr, "Failed to uninstall MSI package."); + break; + } + +LExit: + WiuUninitializeExternalUI(&context); + + StrSecureZeroFreeString(sczProperties); + ReleaseStr(sczObfuscatedProperties); + ReleaseStr(sczMsiPath); + ReleaseStr(sczCachedDirectory); + ReleaseStr(sczInstalledVersion); + + switch (restart) + { + case WIU_RESTART_NONE: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; + break; + + case WIU_RESTART_REQUIRED: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; + break; + + case WIU_RESTART_INITIATED: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; + break; + } + + // Best effort to clear the execute package cache folder and action variables. + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE); + + return hr; +} + +// The contents of psczProperties may be sensitive, should keep encrypted and SecureZeroFree. +extern "C" HRESULT MsiEngineConcatProperties( + __in_ecount(cProperties) BURN_MSIPROPERTY* rgProperties, + __in DWORD cProperties, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __deref_out_z LPWSTR* psczProperties, + __in BOOL fObfuscateHiddenVariables + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + LPWSTR sczEscapedValue = NULL; + LPWSTR sczProperty = NULL; + + for (DWORD i = 0; i < cProperties; ++i) + { + BURN_MSIPROPERTY* pProperty = &rgProperties[i]; + + if (pProperty->sczCondition && *pProperty->sczCondition) + { + BOOL fCondition = FALSE; + + hr = ConditionEvaluate(pVariables, pProperty->sczCondition, &fCondition); + if (FAILED(hr) || !fCondition) + { + LogId(REPORT_VERBOSE, MSG_MSI_PROPERTY_CONDITION_FAILED, pProperty->sczId, pProperty->sczCondition, LoggingTrueFalseToString(fCondition)); + continue; + } + } + + // format property value + if (fObfuscateHiddenVariables) + { + hr = VariableFormatStringObfuscated(pVariables, (fRollback && pProperty->sczRollbackValue) ? pProperty->sczRollbackValue : pProperty->sczValue, &sczValue, NULL); + } + else + { + hr = VariableFormatString(pVariables, (fRollback && pProperty->sczRollbackValue) ? pProperty->sczRollbackValue : pProperty->sczValue, &sczValue, NULL); + ExitOnFailure(hr, "Failed to format property value."); + } + ExitOnFailure(hr, "Failed to format property value."); + + // escape property value + hr = EscapePropertyArgumentString(sczValue, &sczEscapedValue, !fObfuscateHiddenVariables); + ExitOnFailure(hr, "Failed to escape string."); + + // build part + hr = VariableStrAllocFormatted(!fObfuscateHiddenVariables, &sczProperty, L" %s%=\"%s\"", pProperty->sczId, sczEscapedValue); + ExitOnFailure(hr, "Failed to format property string part."); + + // append to property string + hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, psczProperties, sczProperty, 0); + ExitOnFailure(hr, "Failed to append property string part."); + } + +LExit: + StrSecureZeroFreeString(sczValue); + StrSecureZeroFreeString(sczEscapedValue); + StrSecureZeroFreeString(sczProperty); + return hr; +} + +extern "C" INSTALLUILEVEL MsiEngineCalculateInstallUiLevel( + __in BOOL fDisplayInternalUI, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_ACTION_STATE actionState + ) +{ + // Assume there will be no internal UI displayed. + INSTALLUILEVEL uiLevel = static_cast(INSTALLUILEVEL_NONE | INSTALLUILEVEL_SOURCERESONLY); + + // suppress internal UI during uninstall to mimic ARP and "msiexec /x" behavior + if (fDisplayInternalUI && BOOTSTRAPPER_ACTION_STATE_UNINSTALL != actionState && BOOTSTRAPPER_ACTION_STATE_REPAIR != actionState) + { + switch (display) + { + case BOOTSTRAPPER_DISPLAY_FULL: + uiLevel = INSTALLUILEVEL_FULL; + break; + + case BOOTSTRAPPER_DISPLAY_PASSIVE: + uiLevel = INSTALLUILEVEL_REDUCED; + break; + } + } + + return uiLevel; +} + + +// internal helper functions + +static HRESULT ParseRelatedMsiFromXml( + __in IXMLDOMNode* pixnRelatedMsi, + __in BURN_RELATED_MSI* pRelatedMsi + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + LPWSTR scz = NULL; + + // @Id + hr = XmlGetAttributeEx(pixnRelatedMsi, L"Id", &pRelatedMsi->sczUpgradeCode); + ExitOnFailure(hr, "Failed to get @Id."); + + // @MinVersion + hr = XmlGetAttributeEx(pixnRelatedMsi, L"MinVersion", &scz); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @MinVersion."); + + hr = FileVersionFromStringEx(scz, 0, &pRelatedMsi->qwMinVersion); + ExitOnFailure(hr, "Failed to parse @MinVersion: %ls", scz); + + // flag that we have a min version + pRelatedMsi->fMinProvided = TRUE; + + // @MinInclusive + hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"MinInclusive", &pRelatedMsi->fMinInclusive); + ExitOnFailure(hr, "Failed to get @MinInclusive."); + } + + // @MaxVersion + hr = XmlGetAttributeEx(pixnRelatedMsi, L"MaxVersion", &scz); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @MaxVersion."); + + hr = FileVersionFromStringEx(scz, 0, &pRelatedMsi->qwMaxVersion); + ExitOnFailure(hr, "Failed to parse @MaxVersion: %ls", scz); + + // flag that we have a max version + pRelatedMsi->fMaxProvided = TRUE; + + // @MaxInclusive + hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"MaxInclusive", &pRelatedMsi->fMaxInclusive); + ExitOnFailure(hr, "Failed to get @MaxInclusive."); + } + + // @OnlyDetect + hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"OnlyDetect", &pRelatedMsi->fOnlyDetect); + ExitOnFailure(hr, "Failed to get @OnlyDetect."); + + // select language nodes + hr = XmlSelectNodes(pixnRelatedMsi, L"Language", &pixnNodes); + ExitOnFailure(hr, "Failed to select language nodes."); + + // get language node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get language node count."); + + if (cNodes) + { + // @LangInclusive + hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"LangInclusive", &pRelatedMsi->fLangInclusive); + ExitOnFailure(hr, "Failed to get @LangInclusive."); + + // allocate memory for language IDs + pRelatedMsi->rgdwLanguages = (DWORD*)MemAlloc(sizeof(DWORD) * cNodes, TRUE); + ExitOnNull(pRelatedMsi->rgdwLanguages, hr, E_OUTOFMEMORY, "Failed to allocate memory for language IDs."); + + pRelatedMsi->cLanguages = cNodes; + + // parse language elements + for (DWORD i = 0; i < cNodes; ++i) + { + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeNumber(pixnNode, L"Id", &pRelatedMsi->rgdwLanguages[i]); + ExitOnFailure(hr, "Failed to get Language/@Id."); + + // prepare next iteration + ReleaseNullObject(pixnNode); + } + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseStr(scz); + + return hr; +} + +static HRESULT EvaluateActionStateConditions( + __in BURN_VARIABLES* pVariables, + __in_z_opt LPCWSTR sczAddLocalCondition, + __in_z_opt LPCWSTR sczAddSourceCondition, + __in_z_opt LPCWSTR sczAdvertiseCondition, + __out BOOTSTRAPPER_FEATURE_STATE* pState + ) +{ + HRESULT hr = S_OK; + BOOL fCondition = FALSE; + + // if no condition was set, return no feature state + if (!sczAddLocalCondition && !sczAddSourceCondition && !sczAdvertiseCondition) + { + *pState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; + ExitFunction(); + } + + if (sczAddLocalCondition) + { + hr = ConditionEvaluate(pVariables, sczAddLocalCondition, &fCondition); + ExitOnFailure(hr, "Failed to evaluate add local condition."); + + if (fCondition) + { + *pState = BOOTSTRAPPER_FEATURE_STATE_LOCAL; + ExitFunction(); + } + } + + if (sczAddSourceCondition) + { + hr = ConditionEvaluate(pVariables, sczAddSourceCondition, &fCondition); + ExitOnFailure(hr, "Failed to evaluate add source condition."); + + if (fCondition) + { + *pState = BOOTSTRAPPER_FEATURE_STATE_SOURCE; + ExitFunction(); + } + } + + if (sczAdvertiseCondition) + { + hr = ConditionEvaluate(pVariables, sczAdvertiseCondition, &fCondition); + ExitOnFailure(hr, "Failed to evaluate advertise condition."); + + if (fCondition) + { + *pState = BOOTSTRAPPER_FEATURE_STATE_ADVERTISED; + ExitFunction(); + } + } + + // if no condition was true, set to absent + *pState = BOOTSTRAPPER_FEATURE_STATE_ABSENT; + +LExit: + return hr; +} + +static HRESULT CalculateFeatureAction( + __in BOOTSTRAPPER_FEATURE_STATE currentState, + __in BOOTSTRAPPER_FEATURE_STATE requestedState, + __in BOOL fRepair, + __out BOOTSTRAPPER_FEATURE_ACTION* pFeatureAction, + __inout BOOL* pfDelta + ) +{ + HRESULT hr = S_OK; + + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_NONE; + switch (requestedState) + { + case BOOTSTRAPPER_FEATURE_STATE_UNKNOWN: + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_NONE; + break; + + case BOOTSTRAPPER_FEATURE_STATE_ABSENT: + if (BOOTSTRAPPER_FEATURE_STATE_ABSENT != currentState) + { + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REMOVE; + } + break; + + case BOOTSTRAPPER_FEATURE_STATE_ADVERTISED: + if (BOOTSTRAPPER_FEATURE_STATE_ADVERTISED != currentState) + { + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE; + } + else if (fRepair) + { + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL; + } + break; + + case BOOTSTRAPPER_FEATURE_STATE_LOCAL: + if (BOOTSTRAPPER_FEATURE_STATE_LOCAL != currentState) + { + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL; + } + else if (fRepair) + { + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL; + } + break; + + case BOOTSTRAPPER_FEATURE_STATE_SOURCE: + if (BOOTSTRAPPER_FEATURE_STATE_SOURCE != currentState) + { + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE; + } + else if (fRepair) + { + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL; + } + break; + + default: + hr = E_UNEXPECTED; + ExitOnRootFailure(hr, "Invalid state value."); + } + + if (BOOTSTRAPPER_FEATURE_ACTION_NONE != *pFeatureAction) + { + *pfDelta = TRUE; + } + +LExit: + return hr; +} + +static HRESULT EscapePropertyArgumentString( + __in LPCWSTR wzProperty, + __inout_z LPWSTR* psczEscapedValue, + __in BOOL fZeroOnRealloc + ) +{ + HRESULT hr = S_OK; + DWORD cch = 0; + DWORD cchEscape = 0; + LPCWSTR wzSource = NULL; + LPWSTR wzTarget = NULL; + + // count characters to escape + wzSource = wzProperty; + while (*wzSource) + { + ++cch; + if (L'\"' == *wzSource) + { + ++cchEscape; + } + ++wzSource; + } + + // allocate target buffer + hr = VariableStrAlloc(fZeroOnRealloc, psczEscapedValue, cch + cchEscape + 1); // character count, plus escape character count, plus null terminator + ExitOnFailure(hr, "Failed to allocate string buffer."); + + // write to target buffer + wzSource = wzProperty; + wzTarget = *psczEscapedValue; + while (*wzSource) + { + *wzTarget = *wzSource; + if (L'\"' == *wzTarget) + { + ++wzTarget; + *wzTarget = L'\"'; + } + + ++wzSource; + ++wzTarget; + } + + *wzTarget = L'\0'; // add null terminator + +LExit: + return hr; +} + +static HRESULT ConcatFeatureActionProperties( + __in BURN_PACKAGE* pPackage, + __in BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions, + __inout_z LPWSTR* psczArguments + ) +{ + HRESULT hr = S_OK; + LPWSTR scz = NULL; + LPWSTR sczAddLocal = NULL; + LPWSTR sczAddSource = NULL; + LPWSTR sczAddDefault = NULL; + LPWSTR sczReinstall = NULL; + LPWSTR sczAdvertise = NULL; + LPWSTR sczRemove = NULL; + + // features + for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) + { + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; + + switch (rgFeatureActions[i]) + { + case BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL: + if (sczAddLocal) + { + hr = StrAllocConcat(&sczAddLocal, L",", 0); + ExitOnFailure(hr, "Failed to concat separator."); + } + hr = StrAllocConcat(&sczAddLocal, pFeature->sczId, 0); + ExitOnFailure(hr, "Failed to concat feature."); + break; + + case BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE: + if (sczAddSource) + { + hr = StrAllocConcat(&sczAddSource, L",", 0); + ExitOnFailure(hr, "Failed to concat separator."); + } + hr = StrAllocConcat(&sczAddSource, pFeature->sczId, 0); + ExitOnFailure(hr, "Failed to concat feature."); + break; + + case BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT: + if (sczAddDefault) + { + hr = StrAllocConcat(&sczAddDefault, L",", 0); + ExitOnFailure(hr, "Failed to concat separator."); + } + hr = StrAllocConcat(&sczAddDefault, pFeature->sczId, 0); + ExitOnFailure(hr, "Failed to concat feature."); + break; + + case BOOTSTRAPPER_FEATURE_ACTION_REINSTALL: + if (sczReinstall) + { + hr = StrAllocConcat(&sczReinstall, L",", 0); + ExitOnFailure(hr, "Failed to concat separator."); + } + hr = StrAllocConcat(&sczReinstall, pFeature->sczId, 0); + ExitOnFailure(hr, "Failed to concat feature."); + break; + + case BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE: + if (sczAdvertise) + { + hr = StrAllocConcat(&sczAdvertise, L",", 0); + ExitOnFailure(hr, "Failed to concat separator."); + } + hr = StrAllocConcat(&sczAdvertise, pFeature->sczId, 0); + ExitOnFailure(hr, "Failed to concat feature."); + break; + + case BOOTSTRAPPER_FEATURE_ACTION_REMOVE: + if (sczRemove) + { + hr = StrAllocConcat(&sczRemove, L",", 0); + ExitOnFailure(hr, "Failed to concat separator."); + } + hr = StrAllocConcat(&sczRemove, pFeature->sczId, 0); + ExitOnFailure(hr, "Failed to concat feature."); + break; + } + } + + if (sczAddLocal) + { + hr = StrAllocFormatted(&scz, L" ADDLOCAL=\"%s\"", sczAddLocal, 0); + ExitOnFailure(hr, "Failed to format ADDLOCAL string."); + + hr = StrAllocConcatSecure(psczArguments, scz, 0); + ExitOnFailure(hr, "Failed to concat argument string."); + } + + if (sczAddSource) + { + hr = StrAllocFormatted(&scz, L" ADDSOURCE=\"%s\"", sczAddSource, 0); + ExitOnFailure(hr, "Failed to format ADDSOURCE string."); + + hr = StrAllocConcatSecure(psczArguments, scz, 0); + ExitOnFailure(hr, "Failed to concat argument string."); + } + + if (sczAddDefault) + { + hr = StrAllocFormatted(&scz, L" ADDDEFAULT=\"%s\"", sczAddDefault, 0); + ExitOnFailure(hr, "Failed to format ADDDEFAULT string."); + + hr = StrAllocConcatSecure(psczArguments, scz, 0); + ExitOnFailure(hr, "Failed to concat argument string."); + } + + if (sczReinstall) + { + hr = StrAllocFormatted(&scz, L" REINSTALL=\"%s\"", sczReinstall, 0); + ExitOnFailure(hr, "Failed to format REINSTALL string."); + + hr = StrAllocConcatSecure(psczArguments, scz, 0); + ExitOnFailure(hr, "Failed to concat argument string."); + } + + if (sczAdvertise) + { + hr = StrAllocFormatted(&scz, L" ADVERTISE=\"%s\"", sczAdvertise, 0); + ExitOnFailure(hr, "Failed to format ADVERTISE string."); + + hr = StrAllocConcatSecure(psczArguments, scz, 0); + ExitOnFailure(hr, "Failed to concat argument string."); + } + + if (sczRemove) + { + hr = StrAllocFormatted(&scz, L" REMOVE=\"%s\"", sczRemove, 0); + ExitOnFailure(hr, "Failed to format REMOVE string."); + + hr = StrAllocConcatSecure(psczArguments, scz, 0); + ExitOnFailure(hr, "Failed to concat argument string."); + } + +LExit: + ReleaseStr(scz); + ReleaseStr(sczAddLocal); + ReleaseStr(sczAddSource); + ReleaseStr(sczAddDefault); + ReleaseStr(sczReinstall); + ReleaseStr(sczAdvertise); + ReleaseStr(sczRemove); + + return hr; +} + +static HRESULT ConcatPatchProperty( + __in BURN_PACKAGE* pPackage, + __in_opt BOOTSTRAPPER_ACTION_STATE* rgSlipstreamPatchActions, + __inout_z LPWSTR* psczArguments + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCachedDirectory = NULL; + LPWSTR sczMspPath = NULL; + LPWSTR sczPatches = NULL; + + // If there are slipstream patch actions, build up their patch action. + if (rgSlipstreamPatchActions) + { + for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) + { + BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[i]; + AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches."); + + BOOTSTRAPPER_ACTION_STATE patchExecuteAction = rgSlipstreamPatchActions[i]; + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < patchExecuteAction) + { + hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory); + ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId); + + hr = PathConcat(sczCachedDirectory, pMspPackage->rgPayloads[0].pPayload->sczFilePath, &sczMspPath); + ExitOnFailure(hr, "Failed to build MSP path."); + + if (!sczPatches) + { + hr = StrAllocConcat(&sczPatches, L" PATCH=\"", 0); + ExitOnFailure(hr, "Failed to prefix with PATCH property."); + } + else + { + hr = StrAllocConcat(&sczPatches, L";", 0); + ExitOnFailure(hr, "Failed to semi-colon delimit patches."); + } + + hr = StrAllocConcat(&sczPatches, sczMspPath, 0); + ExitOnFailure(hr, "Failed to append patch path."); + } + } + + if (sczPatches) + { + hr = StrAllocConcat(&sczPatches, L"\"", 0); + ExitOnFailure(hr, "Failed to close the quoted PATCH property."); + + hr = StrAllocConcatSecure(psczArguments, sczPatches, 0); + ExitOnFailure(hr, "Failed to append PATCH property."); + } + } + +LExit: + ReleaseStr(sczMspPath); + ReleaseStr(sczCachedDirectory); + ReleaseStr(sczPatches); + return hr; +} + +static void RegisterSourceDirectory( + __in BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzMsiPath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczMsiDirectory = NULL; + MSIINSTALLCONTEXT dwContext = pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED; + + hr = PathGetDirectory(wzMsiPath, &sczMsiDirectory); + ExitOnFailure(hr, "Failed to get directory for path: %ls", wzMsiPath); + + hr = WiuSourceListAddSourceEx(pPackage->Msi.sczProductCode, NULL, dwContext, MSICODE_PRODUCT, sczMsiDirectory, 1); + if (FAILED(hr)) + { + LogId(REPORT_VERBOSE, MSG_SOURCELIST_REGISTER, sczMsiDirectory, pPackage->Msi.sczProductCode, hr); + ExitFunction(); + } + +LExit: + ReleaseStr(sczMsiDirectory); + + return; +} diff --git a/src/engine/msiengine.h b/src/engine/msiengine.h new file mode 100644 index 00000000..2a8ebfd5 --- /dev/null +++ b/src/engine/msiengine.h @@ -0,0 +1,73 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// function declarations + +HRESULT MsiEngineParsePackageFromXml( + __in IXMLDOMNode* pixnBundle, + __in BURN_PACKAGE* pPackage + ); +HRESULT MsiEngineParsePropertiesFromXml( + __in IXMLDOMNode* pixnPackage, + __out BURN_MSIPROPERTY** prgProperties, + __out DWORD* pcProperties + ); +void MsiEnginePackageUninitialize( + __in BURN_PACKAGE* pPackage + ); +HRESULT MsiEngineDetectPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_USER_EXPERIENCE* pUserExperience + ); +HRESULT MsiEnginePlanCalculatePackage( + __in BURN_PACKAGE* pPackage, + __in BURN_VARIABLES* pVariables, + __in BURN_USER_EXPERIENCE* pUserExperience, + __out_opt BOOL* pfBARequestedCache + ); +HRESULT MsiEnginePlanAddPackage( + __in BOOTSTRAPPER_DISPLAY display, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in_opt HANDLE hCacheEvent, + __in BOOL fPlanPackageCacheRollback + ); +HRESULT MsiEngineAddCompatiblePackage( + __in BURN_PACKAGES* pPackages, + __in const BURN_PACKAGE* pPackage, + __out_opt BURN_PACKAGE** ppCompatiblePackage + ); +HRESULT MsiEngineExecutePackage( + __in_opt HWND hwndParent, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +HRESULT MsiEngineConcatProperties( + __in_ecount(cProperties) BURN_MSIPROPERTY* rgProperties, + __in DWORD cProperties, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __deref_out_z LPWSTR* psczProperties, + __in BOOL fObfuscateHiddenVariables + ); +INSTALLUILEVEL MsiEngineCalculateInstallUiLevel( + __in BOOL fDisplayInternalUI, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_ACTION_STATE actionState + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp new file mode 100644 index 00000000..463799e6 --- /dev/null +++ b/src/engine/mspengine.cpp @@ -0,0 +1,980 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// constants + + +// structs + +struct POSSIBLE_TARGETPRODUCT +{ + WCHAR wzProductCode[39]; + LPWSTR pszLocalPackage; + MSIINSTALLCONTEXT context; +}; + +// internal function declarations + +static HRESULT GetPossibleTargetProductCodes( + __in BURN_PACKAGES* pPackages, + __deref_inout_ecount_opt(*pcPossibleTargetProductCodes) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProductCodes, + __inout DWORD* pcPossibleTargetProductCodes + ); +static HRESULT AddPossibleTargetProduct( + __in STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes, + __in_z LPCWSTR wzPossibleTargetProductCode, + __in MSIINSTALLCONTEXT context, + __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts, + __inout DWORD* pcPossibleTargetProducts + ); +static HRESULT AddDetectedTargetProduct( + __in BURN_PACKAGES* pPackages, + __in BURN_PACKAGE* pPackage, + __in DWORD dwOrder, + __in_z LPCWSTR wzProductCode, + __in MSIINSTALLCONTEXT context + ); +static void DeterminePatchChainedTarget( + __in BURN_PACKAGES* pPackages, + __in BURN_PACKAGE* pMspPackage, + __in LPCWSTR wzTargetProductCode, + __out BURN_PACKAGE** ppChainedTargetPackage, + __out BOOL* pfSlipstreamed + ); +static HRESULT PlanTargetProduct( + __in BOOTSTRAPPER_DISPLAY display, + __in BOOL fRollback, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_ACTION_STATE actionState, + __in BURN_PACKAGE* pPackage, + __in BURN_MSPTARGETPRODUCT* pTargetProduct, + __in_opt HANDLE hCacheEvent + ); + + +// function definitions + +extern "C" HRESULT MspEngineParsePackageFromXml( + __in IXMLDOMNode* pixnMspPackage, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + + // @PatchCode + hr = XmlGetAttributeEx(pixnMspPackage, L"PatchCode", &pPackage->Msp.sczPatchCode); + ExitOnFailure(hr, "Failed to get @PatchCode."); + + // @PatchXml + hr = XmlGetAttributeEx(pixnMspPackage, L"PatchXml", &pPackage->Msp.sczApplicabilityXml); + ExitOnFailure(hr, "Failed to get @PatchXml."); + + // @DisplayInternalUI + hr = XmlGetYesNoAttribute(pixnMspPackage, L"DisplayInternalUI", &pPackage->Msp.fDisplayInternalUI); + ExitOnFailure(hr, "Failed to get @DisplayInternalUI."); + + // Read properties. + hr = MsiEngineParsePropertiesFromXml(pixnMspPackage, &pPackage->Msp.rgProperties, &pPackage->Msp.cProperties); + ExitOnFailure(hr, "Failed to parse properties from XML."); + +LExit: + + return hr; +} + +extern "C" void MspEnginePackageUninitialize( + __in BURN_PACKAGE* pPackage + ) +{ + ReleaseStr(pPackage->Msp.sczPatchCode); + ReleaseStr(pPackage->Msp.sczApplicabilityXml); + + // free properties + if (pPackage->Msp.rgProperties) + { + for (DWORD i = 0; i < pPackage->Msp.cProperties; ++i) + { + BURN_MSIPROPERTY* pProperty = &pPackage->Msp.rgProperties[i]; + + ReleaseStr(pProperty->sczId); + ReleaseStr(pProperty->sczValue); + ReleaseStr(pProperty->sczRollbackValue); + } + MemFree(pPackage->Msp.rgProperties); + } + + // free target products + ReleaseMem(pPackage->Msp.rgTargetProducts); + + // clear struct + memset(&pPackage->Msp, 0, sizeof(pPackage->Msp)); +} + +extern "C" HRESULT MspEngineDetectInitialize( + __in BURN_PACKAGES* pPackages + ) +{ + AssertSz(pPackages->cPatchInfo, "MspEngineDetectInitialize() should only be called if there are MSP packages."); + + HRESULT hr = S_OK; + POSSIBLE_TARGETPRODUCT* rgPossibleTargetProducts = NULL; + DWORD cPossibleTargetProducts = 0; + +#ifdef DEBUG + // All patch info should be initialized to zero. + for (DWORD i = 0; i < pPackages->cPatchInfo; ++i) + { + BURN_PACKAGE* pPackage = pPackages->rgPatchInfoToPackage[i]; + Assert(!pPackage->Msp.cTargetProductCodes); + Assert(!pPackage->Msp.rgTargetProducts); + } +#endif + + // Figure out which product codes to target on the machine. In the worst case all products on the machine + // will be returned. + hr = GetPossibleTargetProductCodes(pPackages, &rgPossibleTargetProducts, &cPossibleTargetProducts); + ExitOnFailure(hr, "Failed to get possible target product codes."); + + // Loop through possible target products, testing the collective patch applicability against each product in + // the appropriate context. Store the result with the appropriate patch package. + for (DWORD iSearch = 0; iSearch < cPossibleTargetProducts; ++iSearch) + { + const POSSIBLE_TARGETPRODUCT* pPossibleTargetProduct = rgPossibleTargetProducts + iSearch; + + LogId(REPORT_STANDARD, MSG_DETECT_CALCULATE_PATCH_APPLICABILITY, pPossibleTargetProduct->wzProductCode, LoggingMsiInstallContext(pPossibleTargetProduct->context)); + + if (pPossibleTargetProduct->pszLocalPackage) + { + // Ignores current machine state to determine just patch applicability. + // Superseded and obsolesced patches will be planned separately. + hr = WiuDetermineApplicablePatches(pPossibleTargetProduct->pszLocalPackage, pPackages->rgPatchInfo, pPackages->cPatchInfo); + } + else + { + hr = WiuDeterminePatchSequence(pPossibleTargetProduct->wzProductCode, NULL, pPossibleTargetProduct->context, pPackages->rgPatchInfo, pPackages->cPatchInfo); + } + + if (SUCCEEDED(hr)) + { + for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo) + { + if (ERROR_SUCCESS == pPackages->rgPatchInfo[iPatchInfo].uStatus) + { + BURN_PACKAGE* pMspPackage = pPackages->rgPatchInfoToPackage[iPatchInfo]; + Assert(BURN_PACKAGE_TYPE_MSP == pMspPackage->type); + + // Note that we do add superseded and obsolete MSP packages. Package Detect and Plan will sort them out later. + hr = AddDetectedTargetProduct(pPackages, pMspPackage, pPackages->rgPatchInfo[iPatchInfo].dwOrder, pPossibleTargetProduct->wzProductCode, pPossibleTargetProduct->context); + ExitOnFailure(hr, "Failed to add target product code to package: %ls", pMspPackage->sczId); + } + // TODO: should we log something for this error case? + } + } + else + { + LogId(REPORT_STANDARD, MSG_DETECT_FAILED_CALCULATE_PATCH_APPLICABILITY, pPossibleTargetProduct->wzProductCode, LoggingMsiInstallContext(pPossibleTargetProduct->context), hr); + } + + hr = S_OK; // always reset so we test all possible target products. + } + +LExit: + if (rgPossibleTargetProducts) + { + for (DWORD i = 0; i < cPossibleTargetProducts; ++i) + { + ReleaseStr(rgPossibleTargetProducts[i].pszLocalPackage); + } + MemFree(rgPossibleTargetProducts); + } + + return hr; +} + +extern "C" HRESULT MspEngineDetectPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + LPWSTR sczState = NULL; + + if (0 == pPackage->Msp.cTargetProductCodes) + { + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + } + else + { + // Start the package state at the the highest state then loop through all the + // target product codes and end up setting the current state to the lowest + // package state applied to the the target product codes. + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; + + for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; + + hr = WiuGetPatchInfoEx(pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode, NULL, pTargetProduct->context, INSTALLPROPERTY_PATCHSTATE, &sczState); + if (SUCCEEDED(hr)) + { + switch (*sczState) + { + case '1': + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; + break; + + case '2': + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; + break; + + case '4': + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE; + break; + + default: + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + break; + } + } + else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PATCH) == hr) + { + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get patch information for patch code: %ls, target product code: %ls", pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode); + + if (pPackage->currentState > pTargetProduct->patchPackageState) + { + pPackage->currentState = pTargetProduct->patchPackageState; + } + + hr = UserExperienceOnDetectTargetMsiPackage(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, pTargetProduct->patchPackageState); + ExitOnRootFailure(hr, "BA aborted detect target MSI package."); + } + } + +LExit: + ReleaseStr(sczState); + + return hr; +} + +// +// PlanCalculate - calculates the execute and rollback state for the requested package state. +// +extern "C" HRESULT MspEnginePlanCalculatePackage( + __in BURN_PACKAGE* pPackage, + __in BURN_USER_EXPERIENCE* pUserExperience, + __out BOOL* pfBARequestedCache + ) +{ + HRESULT hr = S_OK; + BOOL fBARequestedCache = FALSE; + + for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; + + BOOTSTRAPPER_REQUEST_STATE requested = pPackage->requested; + BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; + BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + + hr = UserExperienceOnPlanTargetMsiPackage(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, &requested); + ExitOnRootFailure(hr, "BA aborted plan target MSI package."); + + // Calculate the execute action. + switch (pTargetProduct->patchPackageState) + { + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: + switch (requested) + { + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + execute = BOOTSTRAPPER_ACTION_STATE_REPAIR; + break; + + case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_CACHE: + execute = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; + break; + + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: + execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; + break; + + default: + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: + switch (requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; + break; + + case BOOTSTRAPPER_REQUEST_STATE_CACHE: + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + fBARequestedCache = TRUE; + break; + + default: + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + } + + // Calculate the rollback action if there is an execute action. + if (BOOTSTRAPPER_ACTION_STATE_NONE != execute) + { + switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: + switch (requested) + { + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_ABSENT: + rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL; + break; + + default: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough; + case BOOTSTRAPPER_PACKAGE_STATE_CACHED: + switch (requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + rollback = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; + break; + + default: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + default: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + } + + pTargetProduct->execute = execute; + pTargetProduct->rollback = rollback; + + // The highest aggregate action state found will be returned. + if (pPackage->execute < execute) + { + pPackage->execute = execute; + } + + if (pPackage->rollback < rollback) + { + pPackage->rollback = rollback; + } + } + + if (pfBARequestedCache) + { + *pfBARequestedCache = fBARequestedCache; + } + +LExit: + + return hr; +} + +// +// PlanAdd - adds the calculated execute and rollback actions for the package. +// +extern "C" HRESULT MspEnginePlanAddPackage( + __in BOOTSTRAPPER_DISPLAY display, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in_opt HANDLE hCacheEvent, + __in BOOL fPlanPackageCacheRollback + ) +{ + HRESULT hr = S_OK; + + // TODO: need to handle the case where this patch adds itself to an earlier patch's list of target products. That would + // essentially bump this patch earlier in the plan and we need to make sure this patch is downloaded. + // add wait for cache + if (hCacheEvent) + { + hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent, fPlanPackageCacheRollback); + ExitOnFailure(hr, "Failed to plan package cache syncpoint"); + } + + hr = DependencyPlanPackage(NULL, pPackage, pPlan); + ExitOnFailure(hr, "Failed to plan package dependency actions."); + + // Plan the actions for each target product code. + for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; + + // If the dependency manager changed the action state for the patch, change the target product actions. + if (pPackage->fDependencyManagerWasHere) + { + pTargetProduct->execute = pPackage->execute; + pTargetProduct->rollback = pPackage->rollback; + } + + if (BOOTSTRAPPER_ACTION_STATE_NONE != pTargetProduct->execute) + { + hr = PlanTargetProduct(display, FALSE, pPlan, pLog, pVariables, pTargetProduct->execute, pPackage, pTargetProduct, hCacheEvent); + ExitOnFailure(hr, "Failed to plan target product."); + } + + if (BOOTSTRAPPER_ACTION_STATE_NONE != pTargetProduct->rollback) + { + hr = PlanTargetProduct(display, TRUE, pPlan, pLog, pVariables, pTargetProduct->rollback, pPackage, pTargetProduct, hCacheEvent); + ExitOnFailure(hr, "Failed to plan rollack target product."); + } + } + +LExit: + + return hr; +} + +extern "C" HRESULT MspEngineExecutePackage( + __in_opt HWND hwndParent, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + INSTALLUILEVEL uiLevel = pExecuteAction->mspTarget.pPackage->Msp.fDisplayInternalUI ? INSTALLUILEVEL_DEFAULT : static_cast(INSTALLUILEVEL_NONE | INSTALLUILEVEL_SOURCERESONLY); + WIU_MSI_EXECUTE_CONTEXT context = { }; + WIU_RESTART restart = WIU_RESTART_NONE; + + LPWSTR sczCachedDirectory = NULL; + LPWSTR sczMspPath = NULL; + LPWSTR sczPatches = NULL; + LPWSTR sczProperties = NULL; + LPWSTR sczObfuscatedProperties = NULL; + + // default to "verbose" logging + DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE; + + // get cached MSP paths + for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i) + { + LPCWSTR wzAppend = NULL; + BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage; + AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Invalid package type added to ordered patches."); + + if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->mspTarget.action) + { + hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory); + ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId); + + // TODO: Figure out if this makes sense -- the variable is set to the last patch's path only + // Best effort to set the execute package cache folder variable. + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); + + hr = PathConcat(sczCachedDirectory, pMspPackage->rgPayloads[0].pPayload->sczFilePath, &sczMspPath); + ExitOnFailure(hr, "Failed to build MSP path."); + + wzAppend = sczMspPath; + } + else // uninstall + { + wzAppend = pMspPackage->Msp.sczPatchCode; + } + + if (NULL != sczPatches) + { + hr = StrAllocConcat(&sczPatches, L";", 0); + ExitOnFailure(hr, "Failed to semi-colon delimit patches."); + } + + hr = StrAllocConcat(&sczPatches, wzAppend, 0); + ExitOnFailure(hr, "Failed to append patch."); + } + + // Best effort to set the execute package action variable. + VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->mspTarget.action, TRUE); + + // Wire up the external UI handler and logging. + hr = WiuInitializeExternalUI(pfnMessageHandler, uiLevel, hwndParent, pvContext, fRollback, &context); + ExitOnFailure(hr, "Failed to initialize external UI handler."); + + //if (BURN_LOGGING_LEVEL_DEBUG == logLevel) + //{ + // dwLogMode | INSTALLLOGMODE_EXTRADEBUG; + //} + + if (pExecuteAction->mspTarget.sczLogPath && *pExecuteAction->mspTarget.sczLogPath) + { + hr = WiuEnableLog(dwLogMode, pExecuteAction->mspTarget.sczLogPath, 0); + ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pExecuteAction->mspTarget.pPackage->sczId, pExecuteAction->mspTarget.sczLogPath); + } + + // set up properties + hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczProperties, FALSE); + ExitOnFailure(hr, "Failed to add properties to argument string."); + + hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE); + ExitOnFailure(hr, "Failed to add properties to obfuscated argument string."); + + LogId(REPORT_STANDARD, MSG_APPLYING_PATCH_PACKAGE, pExecuteAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pExecuteAction->mspTarget.action), sczPatches, sczObfuscatedProperties, pExecuteAction->mspTarget.sczTargetProductCode); + + // + // Do the actual action. + // + switch (pExecuteAction->mspTarget.action) + { + case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough; + case BOOTSTRAPPER_ACTION_STATE_REPAIR: + hr = StrAllocConcatSecure(&sczProperties, L" PATCH=\"", 0); + ExitOnFailure(hr, "Failed to add PATCH property on install."); + + hr = StrAllocConcatSecure(&sczProperties, sczPatches, 0); + ExitOnFailure(hr, "Failed to add patches to PATCH property on install."); + + hr = StrAllocConcatSecure(&sczProperties, L"\" REBOOT=ReallySuppress", 0); + ExitOnFailure(hr, "Failed to add reboot suppression property on install."); + + hr = WiuConfigureProductEx(pExecuteAction->mspTarget.sczTargetProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, sczProperties, &restart); + ExitOnFailure(hr, "Failed to install MSP package."); + break; + + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: + hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0); + ExitOnFailure(hr, "Failed to add reboot suppression property on uninstall."); + + // Ignore all dependencies, since the Burn engine already performed the check. + hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES); + ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties."); + + hr = WiuRemovePatches(sczPatches, pExecuteAction->mspTarget.sczTargetProductCode, sczProperties, &restart); + ExitOnFailure(hr, "Failed to uninstall MSP package."); + break; + } + +LExit: + WiuUninitializeExternalUI(&context); + + ReleaseStr(sczCachedDirectory); + ReleaseStr(sczMspPath); + StrSecureZeroFreeString(sczProperties); + ReleaseStr(sczObfuscatedProperties); + ReleaseStr(sczPatches); + + switch (restart) + { + case WIU_RESTART_NONE: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; + break; + + case WIU_RESTART_REQUIRED: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; + break; + + case WIU_RESTART_INITIATED: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; + break; + } + + // Best effort to clear the execute package cache folder and action variables. + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE); + + return hr; +} + +extern "C" void MspEngineSlipstreamUpdateState( + __in BURN_PACKAGE* pPackage, + __in BOOTSTRAPPER_ACTION_STATE execute, + __in BOOTSTRAPPER_ACTION_STATE rollback + ) +{ + Assert(BURN_PACKAGE_TYPE_MSP == pPackage->type); + + // If the dependency manager set our state then that means something else + // is dependent on our package. That trumps whatever the slipstream update + // state might set. + if (!pPackage->fDependencyManagerWasHere) + { + // The highest aggregate action state found will be returned. + if (pPackage->execute < execute) + { + pPackage->execute = execute; + } + + if (pPackage->rollback < rollback) + { + pPackage->rollback = rollback; + } + } +} + + +// internal helper functions + +static HRESULT GetPossibleTargetProductCodes( + __in BURN_PACKAGES* pPackages, + __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts, + __inout DWORD* pcPossibleTargetProducts + ) +{ + HRESULT hr = S_OK; + STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes = NULL; + BOOL fCheckAll = FALSE; + WCHAR wzPossibleTargetProductCode[MAX_GUID_CHARS + 1]; + + // Use a dictionary to ensure we capture unique product codes. Otherwise, we could end up + // doing patch applicability for the same product code multiple times and that would confuse + // everything down stream. + hr = DictCreateStringList(&sdUniquePossibleTargetProductCodes, 5, DICT_FLAG_NONE); + ExitOnFailure(hr, "Failed to create unique target product codes."); + + // If the patches target a specific set of product/upgrade codes, search only those. This + // should be much faster than searching all packages on the machine. + if (pPackages->rgPatchTargetCodes) + { + for (DWORD i = 0; i < pPackages->cPatchTargetCodes; ++i) + { + BURN_PATCH_TARGETCODE* pTargetCode = pPackages->rgPatchTargetCodes + i; + + // If targeting a product, add the unique product code to the list. + if (BURN_PATCH_TARGETCODE_TYPE_PRODUCT == pTargetCode->type) + { + hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, pTargetCode->sczTargetCode, MSIINSTALLCONTEXT_NONE, prgPossibleTargetProducts, pcPossibleTargetProducts); + ExitOnFailure(hr, "Failed to add product code to possible target product codes."); + } + else if (BURN_PATCH_TARGETCODE_TYPE_UPGRADE == pTargetCode->type) + { + // Enumerate all unique related products to the target upgrade code. + for (DWORD iProduct = 0; SUCCEEDED(hr); ++iProduct) + { + hr = WiuEnumRelatedProducts(pTargetCode->sczTargetCode, iProduct, wzPossibleTargetProductCode); + if (SUCCEEDED(hr)) + { + hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode, MSIINSTALLCONTEXT_NONE, prgPossibleTargetProducts, pcPossibleTargetProducts); + ExitOnFailure(hr, "Failed to add upgrade product code to possible target product codes."); + } + else if (E_BADCONFIGURATION == hr) + { + // Skip product's with bad configuration and continue. + LogId(REPORT_STANDARD, MSG_DETECT_BAD_PRODUCT_CONFIGURATION, wzPossibleTargetProductCode); + + hr = S_OK; + } + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to enumerate all products to patch related to upgrade code: %ls", pTargetCode->sczTargetCode); + } + else + { + // The element does not target a specific product. + fCheckAll = TRUE; + + break; + } + } + } + else + { + fCheckAll = TRUE; + } + + // One or more of the patches do not target a specific product so search everything on the machine. + if (fCheckAll) + { + for (DWORD iProduct = 0; SUCCEEDED(hr); ++iProduct) + { + MSIINSTALLCONTEXT context = MSIINSTALLCONTEXT_NONE; + + hr = WiuEnumProductsEx(NULL, NULL, MSIINSTALLCONTEXT_ALL, iProduct, wzPossibleTargetProductCode, &context, NULL, NULL); + if (SUCCEEDED(hr)) + { + hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode, context, prgPossibleTargetProducts, pcPossibleTargetProducts); + ExitOnFailure(hr, "Failed to add product code to search product codes."); + } + else if (E_BADCONFIGURATION == hr) + { + // Skip products with bad configuration and continue. + LogId(REPORT_STANDARD, MSG_DETECT_BAD_PRODUCT_CONFIGURATION, wzPossibleTargetProductCode); + + hr = S_OK; + } + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to enumerate all products on the machine for patches applicability."); + } + +LExit: + ReleaseDict(sdUniquePossibleTargetProductCodes); + + return hr; +} + +static HRESULT AddPossibleTargetProduct( + __in STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes, + __in_z LPCWSTR wzPossibleTargetProductCode, + __in MSIINSTALLCONTEXT context, + __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts, + __inout DWORD* pcPossibleTargetProducts + ) +{ + HRESULT hr = S_OK; + LPWSTR pszLocalPackage = NULL; + + // Only add this possible target code if we haven't queried for it already. + if (E_NOTFOUND == DictKeyExists(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode)) + { + // If the install context is not known, ask the Windows Installer for it. If we can't get the context + // then bail. + if (MSIINSTALLCONTEXT_NONE == context) + { + hr = WiuEnumProductsEx(wzPossibleTargetProductCode, NULL, MSIINSTALLCONTEXT_ALL, 0, NULL, &context, NULL, NULL); + if (FAILED(hr)) + { + ExitFunction1(hr = S_OK); + } + } + + hr = DictAddKey(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode); + ExitOnFailure(hr, "Failed to add possible target code to unique product codes."); + + hr = MemEnsureArraySize(reinterpret_cast(prgPossibleTargetProducts), *pcPossibleTargetProducts + 1, sizeof(POSSIBLE_TARGETPRODUCT), 3); + ExitOnFailure(hr, "Failed to grow array of possible target products."); + + POSSIBLE_TARGETPRODUCT *const pPossibleTargetProduct = *prgPossibleTargetProducts + *pcPossibleTargetProducts; + + hr = ::StringCchCopyW(pPossibleTargetProduct->wzProductCode, countof(pPossibleTargetProduct->wzProductCode), wzPossibleTargetProductCode); + ExitOnFailure(hr, "Failed to copy possible target product code."); + + // Attempt to get the local package path so we can more quickly determine patch applicability later. + hr = WiuGetProductInfoEx(wzPossibleTargetProductCode, NULL, context, INSTALLPROPERTY_LOCALPACKAGE, &pszLocalPackage); + if (SUCCEEDED(hr)) + { + pPossibleTargetProduct->pszLocalPackage = pszLocalPackage; + pszLocalPackage = NULL; + } + else + { + // Will instead call MsiDeterminePatchSequence later. + hr = S_OK; + } + + pPossibleTargetProduct->context = context; + + ++(*pcPossibleTargetProducts); + } + +LExit: + ReleaseStr(pszLocalPackage); + + return hr; +} + +static HRESULT AddDetectedTargetProduct( + __in BURN_PACKAGES* pPackages, + __in BURN_PACKAGE* pPackage, + __in DWORD dwOrder, + __in_z LPCWSTR wzProductCode, + __in MSIINSTALLCONTEXT context + ) +{ + HRESULT hr = S_OK; + + hr = MemEnsureArraySize(reinterpret_cast(&pPackage->Msp.rgTargetProducts), pPackage->Msp.cTargetProductCodes + 1, sizeof(BURN_MSPTARGETPRODUCT), 5); + ExitOnFailure(hr, "Failed to ensure enough target product codes were allocated."); + + hr = ::StringCchCopyW(pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].wzTargetProductCode, countof(pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].wzTargetProductCode), wzProductCode); + ExitOnFailure(hr, "Failed to copy target product code."); + + DeterminePatchChainedTarget(pPackages, pPackage, wzProductCode, + &pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].pChainedTargetPackage, + &pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].fSlipstream); + + pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].context = context; + pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].dwOrder = dwOrder; + ++pPackage->Msp.cTargetProductCodes; + +LExit: + return hr; +} + +static void DeterminePatchChainedTarget( + __in BURN_PACKAGES* pPackages, + __in BURN_PACKAGE* pMspPackage, + __in LPCWSTR wzTargetProductCode, + __out BURN_PACKAGE** ppChainedTargetPackage, + __out BOOL* pfSlipstreamed + ) +{ + BURN_PACKAGE* pTargetMsiPackage = NULL; + BOOL fSlipstreamed = FALSE; + + for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage) + { + BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage; + + if (BURN_PACKAGE_TYPE_MSI == pPackage->type && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzTargetProductCode, -1, pPackage->Msi.sczProductCode, -1)) + { + pTargetMsiPackage = pPackage; + + for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) + { + BURN_PACKAGE* pSlipstreamMsp = pPackage->Msi.rgpSlipstreamMspPackages[j]; + if (pSlipstreamMsp == pMspPackage) + { + AssertSz(!fSlipstreamed, "An MSP should only show up as a slipstreamed patch in an MSI once."); + fSlipstreamed = TRUE; + break; + } + } + + break; + } + } + + *ppChainedTargetPackage = pTargetMsiPackage; + *pfSlipstreamed = fSlipstreamed; + + return; +} + +static HRESULT PlanTargetProduct( + __in BOOTSTRAPPER_DISPLAY display, + __in BOOL fRollback, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_ACTION_STATE actionState, + __in BURN_PACKAGE* pPackage, + __in BURN_MSPTARGETPRODUCT* pTargetProduct, + __in_opt HANDLE hCacheEvent + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* rgActions = fRollback ? pPlan->rgRollbackActions : pPlan->rgExecuteActions; + DWORD cActions = fRollback ? pPlan->cRollbackActions : pPlan->cExecuteActions; + BURN_EXECUTE_ACTION* pAction = NULL; + DWORD dwInsertSequence = 0; + + // Try to find another MSP action with the exact same action (install or uninstall) targeting + // the same product in the same machine context (per-user or per-machine). + for (DWORD i = 0; i < cActions; ++i) + { + pAction = rgActions + i; + + if (BURN_EXECUTE_ACTION_TYPE_MSP_TARGET == pAction->type && + pAction->mspTarget.action == actionState && + pAction->mspTarget.fPerMachineTarget == (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context) && + CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pAction->mspTarget.sczTargetProductCode, -1, pTargetProduct->wzTargetProductCode, -1)) + { + dwInsertSequence = i; + break; + } + + pAction = NULL; + } + + // If we didn't find an MSP target action already updating the product, create a new action. + if (!pAction) + { + if (fRollback) + { + hr = PlanAppendRollbackAction(pPlan, &pAction); + } + else + { + hr = PlanAppendExecuteAction(pPlan, &pAction); + } + ExitOnFailure(hr, "Failed to plan action for target product."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_MSP_TARGET; + pAction->mspTarget.action = actionState; + pAction->mspTarget.pPackage = pPackage; + pAction->mspTarget.fPerMachineTarget = (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context); + pAction->mspTarget.uiLevel = MsiEngineCalculateInstallUiLevel(pPackage->Msp.fDisplayInternalUI, display, pAction->mspTarget.action); + pAction->mspTarget.pChainedTargetPackage = pTargetProduct->pChainedTargetPackage; + pAction->mspTarget.fSlipstream = pTargetProduct->fSlipstream; + hr = StrAllocString(&pAction->mspTarget.sczTargetProductCode, pTargetProduct->wzTargetProductCode, 0); + ExitOnFailure(hr, "Failed to copy target product code."); + + // If this is a per-machine target product, then the plan needs to be per-machine as well. + if (pAction->mspTarget.fPerMachineTarget) + { + pPlan->fPerMachine = TRUE; + } + + LoggingSetPackageVariable(pPackage, pAction->mspTarget.sczTargetProductCode, fRollback, pLog, pVariables, &pAction->mspTarget.sczLogPath); // ignore errors. + } + else + { + if (!fRollback && hCacheEvent) + { + // Since a previouse MSP target action is being updated with the new MSP, + // insert a wait syncpoint to before this action since we need to cache the current MSI before using it. + BURN_EXECUTE_ACTION* pWaitSyncPointAction = NULL; + hr = PlanInsertExecuteAction(dwInsertSequence, pPlan, &pWaitSyncPointAction); + ExitOnFailure(hr, "Failed to insert execute action."); + + pWaitSyncPointAction->type = BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT; + pWaitSyncPointAction->syncpoint.hEvent = hCacheEvent; + + // Since we inserted an action before the MSP target action that we will be updating, need to update the pointer. + pAction = pPlan->rgExecuteActions + (dwInsertSequence + 1); + } + } + + // Add our target product to the array and sort based on their order determined during detection. + hr = MemEnsureArraySize(reinterpret_cast(&pAction->mspTarget.rgOrderedPatches), pAction->mspTarget.cOrderedPatches + 1, sizeof(BURN_ORDERED_PATCHES), 2); + ExitOnFailure(hr, "Failed grow array of ordered patches."); + + pAction->mspTarget.rgOrderedPatches[pAction->mspTarget.cOrderedPatches].dwOrder = pTargetProduct->dwOrder; + pAction->mspTarget.rgOrderedPatches[pAction->mspTarget.cOrderedPatches].pPackage = pPackage; + ++pAction->mspTarget.cOrderedPatches; + + // Insertion sort to keep the patches ordered. + for (DWORD i = pAction->mspTarget.cOrderedPatches - 1; i > 0; --i) + { + if (pAction->mspTarget.rgOrderedPatches[i].dwOrder < pAction->mspTarget.rgOrderedPatches[i - 1].dwOrder) + { + BURN_ORDERED_PATCHES temp = pAction->mspTarget.rgOrderedPatches[i - 1]; + pAction->mspTarget.rgOrderedPatches[i - 1] = pAction->mspTarget.rgOrderedPatches[i]; + pAction->mspTarget.rgOrderedPatches[i] = temp; + } + else // no swap necessary, we're done. + { + break; + } + } + +LExit: + return hr; +} diff --git a/src/engine/mspengine.h b/src/engine/mspengine.h new file mode 100644 index 00000000..9f585720 --- /dev/null +++ b/src/engine/mspengine.h @@ -0,0 +1,67 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + + +// structures + + +// typedefs + + +// function declarations + +HRESULT MspEngineParsePackageFromXml( + __in IXMLDOMNode* pixnBundle, + __in BURN_PACKAGE* pPackage + ); +void MspEnginePackageUninitialize( + __in BURN_PACKAGE* pPackage + ); +HRESULT MspEngineDetectInitialize( + __in BURN_PACKAGES* pPackages + ); +HRESULT MspEngineDetectPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_USER_EXPERIENCE* pUserExperience + ); +HRESULT MspEnginePlanCalculatePackage( + __in BURN_PACKAGE* pPackage, + __in BURN_USER_EXPERIENCE* pUserExperience, + __out_opt BOOL* pfBARequestedCache + ); +HRESULT MspEnginePlanAddPackage( + __in BOOTSTRAPPER_DISPLAY display, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in_opt HANDLE hCacheEvent, + __in BOOL fPlanPackageCacheRollback + ); +HRESULT MspEngineExecutePackage( + __in_opt HWND hwndParent, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +void MspEngineSlipstreamUpdateState( + __in BURN_PACKAGE* pMspPackage, + __in BOOTSTRAPPER_ACTION_STATE execute, + __in BOOTSTRAPPER_ACTION_STATE rollback + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/msuengine.cpp b/src/engine/msuengine.cpp new file mode 100644 index 00000000..3818e932 --- /dev/null +++ b/src/engine/msuengine.cpp @@ -0,0 +1,513 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// constants + +#define WU_S_REBOOT_REQUIRED 0x00240005L +#define WU_S_ALREADY_INSTALLED 0x00240006L + + +// function definitions +static HRESULT EnsureWUServiceEnabled( + __in BOOL fStopWusaService, + __out SC_HANDLE* pschWu, + __out BOOL* pfPreviouslyDisabled + ); +static HRESULT SetServiceStartType( + __in SC_HANDLE sch, + __in DWORD stratType + ); +static HRESULT StopWUService( + __in SC_HANDLE schWu + ); + + +extern "C" HRESULT MsuEngineParsePackageFromXml( + __in IXMLDOMNode* pixnMsuPackage, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + + // @KB + hr = XmlGetAttributeEx(pixnMsuPackage, L"KB", &pPackage->Msu.sczKB); + ExitOnFailure(hr, "Failed to get @KB."); + + // @DetectCondition + hr = XmlGetAttributeEx(pixnMsuPackage, L"DetectCondition", &pPackage->Msu.sczDetectCondition); + ExitOnFailure(hr, "Failed to get @DetectCondition."); + +LExit: + return hr; +} + +extern "C" void MsuEnginePackageUninitialize( + __in BURN_PACKAGE* pPackage + ) +{ + ReleaseNullStr(pPackage->Msu.sczKB); + ReleaseNullStr(pPackage->Msu.sczDetectCondition); +} + +extern "C" HRESULT MsuEngineDetectPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + BOOL fDetected = FALSE; + + // evaluate detect condition + if (pPackage->Msu.sczDetectCondition && *pPackage->Msu.sczDetectCondition) + { + hr = ConditionEvaluate(pVariables, pPackage->Msu.sczDetectCondition, &fDetected); + ExitOnFailure(hr, "Failed to evaluate MSU package detect condition."); + } + + // update detect state + pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + +LExit: + return hr; +} + +// +// PlanCalculate - calculates the execute and rollback state for the requested package state. +// +extern "C" HRESULT MsuEnginePlanCalculatePackage( + __in BURN_PACKAGE* pPackage, + __out_opt BOOL* pfBARequestedCache + ) +{ + HRESULT hr = S_OK; + BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; + BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + BOOL fBARequestedCache = FALSE; + + BOOL fAllowUninstall = FALSE; + OS_VERSION osVersion = OS_VERSION_UNKNOWN; + DWORD dwServicePack = 0; + + // We can only uninstall MSU packages if they have a KB and we are on Win7 or newer. + OsGetVersion(&osVersion, &dwServicePack); + fAllowUninstall = (pPackage->Msu.sczKB && *pPackage->Msu.sczKB) && OS_VERSION_WIN7 <= osVersion; + + // execute action + switch (pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + + case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_CACHE: + execute = fAllowUninstall && pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; + break; + + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: + execute = fAllowUninstall ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; + break; + + default: + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; + break; + + case BOOTSTRAPPER_REQUEST_STATE_CACHE: + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + fBARequestedCache = TRUE; + + default: + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid package state."); + } + + // Calculate the rollback action if there is an execute action. + if (BOOTSTRAPPER_ACTION_STATE_NONE != execute) + { + switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_ABSENT: + rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL; + break; + + default: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + rollback = fAllowUninstall ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; + break; + + default: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid package expected state."); + } + } + + // return values + pPackage->execute = execute; + pPackage->rollback = rollback; + + if (pfBARequestedCache) + { + *pfBARequestedCache = fBARequestedCache; + } + +LExit: + return hr; +} + +// +// PlanAdd - adds the calculated execute and rollback actions for the package. +// +extern "C" HRESULT MsuEnginePlanAddPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in HANDLE hCacheEvent, + __in BOOL fPlanPackageCacheRollback + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* pAction = NULL; + + // add wait for cache + if (hCacheEvent) + { + hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent, fPlanPackageCacheRollback); + ExitOnFailure(hr, "Failed to plan package cache syncpoint"); + } + + hr = DependencyPlanPackage(NULL, pPackage, pPlan); + ExitOnFailure(hr, "Failed to plan package dependency actions."); + + // add execute action + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute) + { + hr = PlanAppendExecuteAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append execute action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE; + pAction->msuPackage.pPackage = pPackage; + pAction->msuPackage.action = pPackage->execute; + + LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors. + } + + // add rollback action + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback) + { + hr = PlanAppendRollbackAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append rollback action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE; + pAction->msuPackage.pPackage = pPackage; + pAction->msuPackage.action = pPackage->rollback; + + LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors. + } + +LExit: + return hr; +} + +extern "C" HRESULT MsuEngineExecutePackage( + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in BOOL fStopWusaService, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + int nResult = IDNOACTION; + LPWSTR sczCachedDirectory = NULL; + LPWSTR sczMsuPath = NULL; + LPWSTR sczWindowsPath = NULL; + LPWSTR sczSystemPath = NULL; + LPWSTR sczWusaPath = NULL; + LPWSTR sczCommand = NULL; + SC_HANDLE schWu = NULL; + BOOL fWuWasDisabled = FALSE; + STARTUPINFOW si = { }; + PROCESS_INFORMATION pi = { }; + GENERIC_EXECUTE_MESSAGE message = { }; + DWORD dwExitCode = 0; + BOOL fUseSysNativePath = FALSE; + +#if !defined(_WIN64) + hr = ProcWow64(::GetCurrentProcess(), &fUseSysNativePath); + ExitOnFailure(hr, "Failed to determine WOW64 status."); +#endif + + *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; + + // get wusa.exe path + if (fUseSysNativePath) + { + hr = PathGetKnownFolder(CSIDL_WINDOWS, &sczWindowsPath); + ExitOnFailure(hr, "Failed to find Windows directory."); + + hr = PathConcat(sczWindowsPath, L"SysNative\\", &sczSystemPath); + ExitOnFailure(hr, "Failed to append SysNative directory."); + } + else + { + hr = PathGetKnownFolder(CSIDL_SYSTEM, &sczSystemPath); + ExitOnFailure(hr, "Failed to find System32 directory."); + } + + hr = PathConcat(sczSystemPath, L"wusa.exe", &sczWusaPath); + ExitOnFailure(hr, "Failed to allocate WUSA.exe path."); + + // build command + switch (pExecuteAction->msuPackage.action) + { + case BOOTSTRAPPER_ACTION_STATE_INSTALL: + // get cached MSU path + hr = CacheGetCompletedPath(TRUE, pExecuteAction->msuPackage.pPackage->sczCacheId, &sczCachedDirectory); + ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->msuPackage.pPackage->sczId); + + // Best effort to set the execute package cache folder variable. + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); + + hr = PathConcat(sczCachedDirectory, pExecuteAction->msuPackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczMsuPath); + ExitOnFailure(hr, "Failed to build MSU path."); + + // format command + hr = StrAllocFormatted(&sczCommand, L"\"%ls\" \"%ls\" /quiet /norestart", sczWusaPath, sczMsuPath); + ExitOnFailure(hr, "Failed to format MSU install command."); + break; + + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: + // format command + hr = StrAllocFormatted(&sczCommand, L"\"%ls\" /uninstall /kb:%ls /quiet /norestart", sczWusaPath, pExecuteAction->msuPackage.pPackage->Msu.sczKB); + ExitOnFailure(hr, "Failed to format MSU uninstall command."); + break; + + default: + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Failed to get action arguments for MSU package."); + } + + if (pExecuteAction->msuPackage.sczLogPath && *pExecuteAction->msuPackage.sczLogPath) + { + hr = StrAllocConcat(&sczCommand, L" /log:", 0); + ExitOnFailure(hr, "Failed to append log switch to MSU command-line."); + + hr = StrAllocConcat(&sczCommand, pExecuteAction->msuPackage.sczLogPath, 0); + ExitOnFailure(hr, "Failed to append log path to MSU command-line."); + } + + LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->msuPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msuPackage.action), sczMsuPath ? sczMsuPath : pExecuteAction->msuPackage.pPackage->Msu.sczKB, sczCommand); + + hr = EnsureWUServiceEnabled(fStopWusaService, &schWu, &fWuWasDisabled); + ExitOnFailure(hr, "Failed to ensure WU service was enabled to install MSU package."); + + // create process + si.cb = sizeof(si); + if (!::CreateProcessW(sczWusaPath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) + { + ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", sczWusaPath); + } + + do + { + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; + message.dwAllowedResults = MB_OKCANCEL; + message.progress.dwPercentage = 50; + nResult = pfnGenericMessageHandler(&message, pvContext); + hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); + ExitOnRootFailure(hr, "Bootstrapper application aborted during MSU progress."); + + // wait for process to terminate + hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode); + if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr) + { + ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", sczWusaPath); + } + } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr); + + // get process exit code + if (!::GetExitCodeProcess(pi.hProcess, &dwExitCode)) + { + ExitWithLastError(hr, "Failed to get process exit code."); + } + + // We'll normalize the restart required error code from wusa.exe just in case. Most likely + // that on reboot we'll actually get WU_S_REBOOT_REQUIRED. + if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast(dwExitCode)) + { + dwExitCode = ERROR_SUCCESS_REBOOT_REQUIRED; + } + + // handle exit code + switch (dwExitCode) + { + case S_OK: __fallthrough; + case S_FALSE: __fallthrough; + case WU_S_ALREADY_INSTALLED: + hr = S_OK; + break; + + case ERROR_SUCCESS_REBOOT_REQUIRED: __fallthrough; + case WU_S_REBOOT_REQUIRED: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; + hr = S_OK; + break; + + default: + hr = static_cast(dwExitCode); + break; + } + +LExit: + ReleaseStr(sczCachedDirectory); + ReleaseStr(sczMsuPath); + ReleaseStr(sczSystemPath); + ReleaseStr(sczWindowsPath); + ReleaseStr(sczWusaPath); + ReleaseStr(sczCommand); + + ReleaseHandle(pi.hProcess); + ReleaseHandle(pi.hThread); + + if (fWuWasDisabled) + { + SetServiceStartType(schWu, SERVICE_DISABLED); + } + + // Best effort to clear the execute package cache folder variable. + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); + + return hr; +} + +static HRESULT EnsureWUServiceEnabled( + __in BOOL fStopWusaService, + __out SC_HANDLE* pschWu, + __out BOOL* pfPreviouslyDisabled + ) +{ + HRESULT hr = S_OK; + SC_HANDLE schSCM = NULL; + SC_HANDLE schWu = NULL; + SERVICE_STATUS serviceStatus = { }; + QUERY_SERVICE_CONFIGW* pConfig = NULL; + + schSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); + ExitOnNullWithLastError(schSCM, hr, "Failed to open service control manager."); + + schWu = ::OpenServiceW(schSCM, L"wuauserv", SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_STOP ); + ExitOnNullWithLastError(schWu, hr, "Failed to open WU service."); + + if (!::QueryServiceStatus(schWu, &serviceStatus) ) + { + ExitWithLastError(hr, "Failed to query status of WU service."); + } + + // Stop service if requested to. + if (SERVICE_STOPPED != serviceStatus.dwCurrentState && fStopWusaService) + { + hr = StopWUService(schWu); + } + + // If the service is not running then it might be disabled so let's check. + if (SERVICE_RUNNING != serviceStatus.dwCurrentState) + { + hr = SvcQueryConfig(schWu, &pConfig); + ExitOnFailure(hr, "Failed to read configuration for WU service."); + + // If WU is disabled, change it to a demand start service (but touch nothing else). + if (SERVICE_DISABLED == pConfig->dwStartType) + { + hr = SetServiceStartType(schWu, SERVICE_DEMAND_START); + ExitOnFailure(hr, "Failed to mark WU service to start on demand."); + + *pfPreviouslyDisabled = TRUE; + } + } + + *pschWu = schWu; + schWu = NULL; + +LExit: + ReleaseMem(pConfig); + ReleaseServiceHandle(schWu); + ReleaseServiceHandle(schSCM); + + return hr; +} + +static HRESULT SetServiceStartType( + __in SC_HANDLE sch, + __in DWORD startType + ) +{ + HRESULT hr = S_OK; + + if (!::ChangeServiceConfigW(sch, SERVICE_NO_CHANGE, startType, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) + { + ExitWithLastError(hr, "Failed to set service start type."); + } + +LExit: + return hr; +} + +static HRESULT StopWUService( + __in SC_HANDLE schWu + ) +{ + HRESULT hr = S_OK; + SERVICE_STATUS serviceStatus = { }; + + if(!::ControlService(schWu, SERVICE_CONTROL_STOP, &serviceStatus)) + { + ExitWithLastError(hr, "Failed to stop wusa service."); + } + +LExit: + return hr; +} diff --git a/src/engine/msuengine.h b/src/engine/msuengine.h new file mode 100644 index 00000000..d0323b06 --- /dev/null +++ b/src/engine/msuengine.h @@ -0,0 +1,48 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// function declarations + +HRESULT MsuEngineParsePackageFromXml( + __in IXMLDOMNode* pixnMsiPackage, + __in BURN_PACKAGE* pPackage + ); +void MsuEnginePackageUninitialize( + __in BURN_PACKAGE* pPackage + ); +HRESULT MsuEngineDetectPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_VARIABLES* pVariables + ); +HRESULT MsuEnginePlanCalculatePackage( + __in BURN_PACKAGE* pPackage, + __out_opt BOOL* pfBARequestedCache + ); +HRESULT MsuEnginePlanAddPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in HANDLE hCacheEvent, + __in BOOL fPlanPackageCacheRollback + ); +HRESULT MsuEngineExecutePackage( + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in BOOL fStopWusaService, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/netfxchainer.cpp b/src/engine/netfxchainer.cpp new file mode 100644 index 00000000..4e7a7720 --- /dev/null +++ b/src/engine/netfxchainer.cpp @@ -0,0 +1,418 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static VOID DestroyNetFxChainer( + __in NetFxChainer* pChainer + ) +{ + if (pChainer) + { + ReleaseHandle(pChainer->hSection); + ReleaseHandle(pChainer->hEventChaineeSend); + ReleaseHandle(pChainer->hEventChainerSend); + ReleaseHandle(pChainer->hMutex); + + if (pChainer->pData) + { + ::UnmapViewOfFile(pChainer->pData); + } + + MemFree(pChainer); + } +} + +static HRESULT CreateNetFxChainer( + __in LPCWSTR wzSectionName, + __in LPCWSTR wzEventName, + __out NetFxChainer** ppChainer + ) +{ + HRESULT hr = S_OK; + LPWSTR sczName = NULL; + NetFxChainer* pChainer = NULL; + + pChainer = (NetFxChainer*)MemAlloc(sizeof(NetFxChainer), TRUE); + ExitOnNull(pChainer, hr, E_OUTOFMEMORY, "Failed to allocate memory for NetFxChainer struct."); + + pChainer->hEventChaineeSend = ::CreateEvent(NULL, FALSE, FALSE, wzEventName); + ExitOnNullWithLastError(pChainer->hEventChaineeSend, hr, "Failed to create event: %ls", wzEventName); + + hr = StrAllocFormatted(&sczName, L"%ls_send", wzEventName); + ExitOnFailure(hr, "failed to allocate memory for event name"); + + pChainer->hEventChainerSend = ::CreateEvent(NULL, FALSE, FALSE, sczName); + ExitOnNullWithLastError(pChainer->hEventChainerSend, hr, "Failed to create event: %ls", sczName); + + hr = StrAllocFormatted(&sczName, L"%ls_mutex", wzEventName); + ExitOnFailure(hr, "failed to allocate memory for mutex name"); + + // Create the mutex, we initially own + pChainer->hMutex = ::CreateMutex(NULL, TRUE, sczName); + ExitOnNullWithLastError(pChainer->hMutex, hr, "Failed to create mutex: %ls", sczName); + + pChainer->hSection = ::CreateFileMapping(INVALID_HANDLE_VALUE, + NULL, // security attributes + PAGE_READWRITE, + 0, // high-order DWORD of maximum size + NETFXDATA_SIZE, // low-order DWORD of maximum size + wzSectionName); + ExitOnNullWithLastError(pChainer->hSection, hr, "Failed to memory map cabinet file: %ls", wzSectionName); + + pChainer->pData = reinterpret_cast(::MapViewOfFile(pChainer->hSection, + FILE_MAP_WRITE, + 0, 0, // offsets + 0 // map entire file + )); + ExitOnNullWithLastError(pChainer->pData, hr, "Failed to MapViewOfFile for %ls.", wzSectionName); + + // Initialize the shared memory + hr = ::StringCchCopyW(pChainer->pData->szEventName, countof(pChainer->pData->szEventName), wzEventName); + ExitOnFailure(hr, "failed to copy event name to shared memory structure."); + pChainer->pData->downloadFinished = false; + pChainer->pData->downloadSoFar = 0; + pChainer->pData->hrDownloadFinished = E_PENDING; + pChainer->pData->downloadAbort = false; + pChainer->pData->installFinished = false; + pChainer->pData->installSoFar = 0; + pChainer->pData->hrInstallFinished = E_PENDING; + pChainer->pData->installAbort = false; + pChainer->pData->hrInternalError = S_OK; + pChainer->pData->version = NETFXDATA_VERSION; + pChainer->pData->messageCode = 0; + pChainer->pData->messageResponse = 0; + pChainer->pData->messageDataLength = 0; + + // Done with initialization, allow others to access. + ::ReleaseMutex(pChainer->hMutex); + + *ppChainer = pChainer; + pChainer = NULL; + +LExit: + ReleaseStr(sczName); + + if (pChainer) + { + // Something failed, release the mutex and destroy the object + if (pChainer->hMutex) + { + ::ReleaseMutex(pChainer->hMutex); + } + + DestroyNetFxChainer(pChainer); + } + + return hr; +} + + +static VOID NetFxAbort( + __in NetFxChainer* pChainer + ) +{ + ::WaitForSingleObject(pChainer->hMutex, INFINITE); + + pChainer->pData->downloadAbort = true; + pChainer->pData->installAbort = true; + + ::ReleaseMutex(pChainer->hMutex); + + ::SetEvent(pChainer->hEventChainerSend); +} + +static BYTE NetFxGetProgress( + __in NetFxChainer* pChainer + ) +{ + BYTE bProgress = 0; + ::WaitForSingleObject(pChainer->hMutex, INFINITE); + + bProgress = (pChainer->pData->installSoFar + pChainer->pData->downloadSoFar) / 2; + + ::ReleaseMutex(pChainer->hMutex); + + return bProgress; +} + +static HRESULT NetFxGetMessage( + __in NetFxChainer* pChainer, + __out DWORD* pdwMessage, + __out LPVOID* ppBuffer, + __out DWORD* pdwBufferSize + ) +{ + HRESULT hr = S_OK; + ::WaitForSingleObject(pChainer->hMutex, INFINITE); + + *pdwMessage = pChainer->pData->messageCode; + *ppBuffer = NULL; + *pdwBufferSize = 0; + + if (NETFX_NO_MESSAGE != *pdwMessage) + { + *ppBuffer = MemAlloc(pChainer->pData->messageDataLength, TRUE); + ExitOnNull(*ppBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for message data"); + + memcpy(*ppBuffer, pChainer->pData->messageData, pChainer->pData->messageDataLength); + *pdwBufferSize = pChainer->pData->messageDataLength; + } + +LExit: + ::ReleaseMutex(pChainer->hMutex); + + return hr; +} + +static void NetFxRespond( + __in NetFxChainer* pChainer, + __in DWORD dwResponse + ) +{ + ::WaitForSingleObject(pChainer->hMutex, INFINITE); + + pChainer->pData->messageCode = NETFX_NO_MESSAGE; + pChainer->pData->messageResponse = dwResponse; + if (IDCANCEL == dwResponse) + { + pChainer->pData->downloadAbort = true; + pChainer->pData->installAbort = true; + } + + ::ReleaseMutex(pChainer->hMutex); + + ::SetEvent(pChainer->hEventChainerSend); +} + +static HRESULT NetFxGetResult( + __in NetFxChainer* pChainer, + __out HRESULT* phrInternalError + ) +{ + HRESULT hr = S_OK; + ::WaitForSingleObject(pChainer->hMutex, INFINITE); + + hr = pChainer->pData->hrInstallFinished; + + if (FAILED(pChainer->pData->hrDownloadFinished) && // Download failed + (S_OK == hr || E_ABORT == hr)) // Install succeeded or was aborted + { + hr = pChainer->pData->hrDownloadFinished; + } + + if (phrInternalError) + { + *phrInternalError = pChainer->pData->hrInternalError; + } + + ::ReleaseMutex(pChainer->hMutex); + + return hr; +} + +static HRESULT OnNetFxFilesInUse( + __in NetFxChainer* pNetfxChainer, + __in NetFxCloseApplications* pCloseApps, + __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + DWORD cFiles = 0; + LPWSTR* rgwzFiles = NULL; + GENERIC_EXECUTE_MESSAGE message = { }; + DWORD dwResponse = 0; + + cFiles = pCloseApps->dwApplicationsSize; + rgwzFiles = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cFiles, TRUE); + ExitOnNull(rgwzFiles, hr, E_OUTOFMEMORY, "Failed to allocate buffer."); + + for (DWORD i = 0; i < pCloseApps->dwApplicationsSize; ++i) + { + rgwzFiles[i] = pCloseApps->applications[i].szName; + } + + // send message + message.type = GENERIC_EXECUTE_MESSAGE_FILES_IN_USE; + message.dwAllowedResults = MB_ABORTRETRYIGNORE; + message.filesInUse.cFiles = cFiles; + message.filesInUse.rgwzFiles = (LPCWSTR*)rgwzFiles; + dwResponse = (DWORD)pfnMessageHandler(&message, pvContext); + + NetFxRespond(pNetfxChainer, dwResponse); + +LExit: + ReleaseMem(rgwzFiles); + + return hr; +} + +static HRESULT OnNetFxProgress( + __in NetFxChainer* pNetfxChainer, + __in BYTE bProgress, + __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext + ) +{ + GENERIC_EXECUTE_MESSAGE message = { }; + DWORD dwResponse = 0; + + // send message + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; + message.dwAllowedResults = MB_OKCANCEL; + message.progress.dwPercentage = 100 * (DWORD)bProgress / BYTE_MAX; + dwResponse = (DWORD)pfnMessageHandler(&message, pvContext); + + if (IDCANCEL == dwResponse) + { + NetFxAbort(pNetfxChainer); + } + + return S_OK; +} + +static HRESULT OnNetFxError( + __in NetFxChainer* /*pNetfxChainer*/, + __in HRESULT hrError, + __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext + ) +{ + GENERIC_EXECUTE_MESSAGE message = { }; + DWORD dwResponse = 0; + + // send message + message.type = GENERIC_EXECUTE_MESSAGE_ERROR; + message.dwAllowedResults = MB_OK; + message.error.dwErrorCode = hrError; + message.error.wzMessage = NULL; + dwResponse = (DWORD)pfnMessageHandler(&message, pvContext); + + return S_OK; +} + +static HRESULT ProcessNetFxMessage( + __in NetFxChainer* pNetfxChainer, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + DWORD dwMessage = NETFX_NO_MESSAGE; + DWORD dwBufferSize = 0; + LPVOID pBuffer = NULL; + + // send progress + hr = OnNetFxProgress(pNetfxChainer, NetFxGetProgress(pNetfxChainer), pfnGenericMessageHandler, pvContext); + ExitOnFailure(hr, "Failed to send progress from netfx chainer."); + + // Check for message + hr = NetFxGetMessage(pNetfxChainer, &dwMessage, &pBuffer, &dwBufferSize); + ExitOnFailure(hr, "Failed to get message from netfx chainer."); + + switch(dwMessage) + { + case NETFX_CLOSE_APPS: + hr = OnNetFxFilesInUse(pNetfxChainer, (NetFxCloseApplications*)pBuffer, pfnGenericMessageHandler, pvContext); + ExitOnFailure(hr, "Failed to send files in use message from netfx chainer."); + break; + + default: + // No message we understand. + break; + } + +LExit: + ReleaseMem(pBuffer); + + return hr; +} + +extern "C" HRESULT NetFxRunChainer( + __in LPCWSTR wzExecutablePath, + __in LPCWSTR wzArguments, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __out DWORD* pdwExitCode + ) +{ + HRESULT hr = S_OK; + DWORD er = 0; + WCHAR wzGuid[GUID_STRING_LENGTH]; + LPWSTR sczEventName = NULL; + LPWSTR sczSectionName = NULL; + LPWSTR sczCommand = NULL; + NetFxChainer* pNetfxChainer = NULL; + STARTUPINFOW si = { }; + PROCESS_INFORMATION pi = { }; + HRESULT hrInternalError = 0; + + // Create the unique name suffix. + hr = GuidFixedCreate(wzGuid); + ExitOnRootFailure(hr, "Failed to create netfx chainer guid."); + + hr = StrAllocFormatted(&sczSectionName, L"NetFxSection.%ls", wzGuid); + ExitOnFailure(hr, "Failed to allocate section name."); + + hr = StrAllocFormatted(&sczEventName, L"NetFxEvent.%ls", wzGuid); + ExitOnFailure(hr, "Failed to allocate event name."); + + hr = CreateNetFxChainer(sczSectionName, sczEventName, &pNetfxChainer); + ExitOnFailure(hr, "Failed to create netfx chainer."); + + hr = StrAllocFormattedSecure(&sczCommand, L"%ls /pipe %ls", wzArguments, sczSectionName); + ExitOnFailure(hr, "Failed to allocate netfx chainer arguments."); + + si.cb = sizeof(si); + if (!::CreateProcessW(wzExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) + { + ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", wzExecutablePath); + } + + HANDLE handles[2] = { pi.hProcess, pNetfxChainer->hEventChaineeSend }; + + for (;;) + { + er = ::WaitForMultipleObjects(2, handles, FALSE, 100); + if (WAIT_OBJECT_0 == er) + { + // Process has exited + *pdwExitCode = NetFxGetResult(pNetfxChainer, &hrInternalError); + if (E_PENDING == *pdwExitCode) + { + if (!::GetExitCodeProcess(pi.hProcess, pdwExitCode)) + { + ExitWithLastError(hr, "Failed to get netfx return code."); + } + } + else if (FAILED(hrInternalError)) + { + // push internal error message + OnNetFxError(pNetfxChainer, hrInternalError, pfnGenericMessageHandler, pvContext); + ExitOnFailure(hr, "Failed to send internal error message from netfx chainer."); + } + + break; + } + else if (WAIT_OBJECT_0 + 1 == er) + { + // Chainee has notified us of a change. + hr = ProcessNetFxMessage(pNetfxChainer, pfnGenericMessageHandler, pvContext); + ExitOnFailure(hr, "Failed to process netfx chainer message."); + } + else if (WAIT_FAILED == er) + { + ExitWithLastError(hr, "Failed to wait for netfx chainer process to complete"); + } + } + +LExit: + ReleaseStr(sczSectionName); + ReleaseStr(sczEventName); + StrSecureZeroFreeString(sczCommand); + DestroyNetFxChainer(pNetfxChainer); + ReleaseHandle(pi.hThread); + ReleaseHandle(pi.hProcess); + + return hr; +} diff --git a/src/engine/netfxchainer.h b/src/engine/netfxchainer.h new file mode 100644 index 00000000..7d3aff1c --- /dev/null +++ b/src/engine/netfxchainer.h @@ -0,0 +1,98 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +struct NetFxDataStructure +{ + bool downloadFinished; // download done yet? + bool installFinished; // install done yet? + bool downloadAbort; // set downloader to abort + bool installAbort; // set installer to abort + HRESULT hrDownloadFinished; // resultant HRESULT for download + HRESULT hrInstallFinished; // resultant HRESULT for install + HRESULT hrInternalError; + WCHAR szCurrentItemStep[MAX_PATH]; + BYTE downloadSoFar; // download progress 0 - 255 (0 to 100% done) + BYTE installSoFar; // install progress 0 - 255 (0 to 100% done) + WCHAR szEventName[MAX_PATH]; // event that chainer 'creates' and chainee 'opens'to sync communications + + BYTE version; // version of the data structure, set by chainer. + + DWORD messageCode; // current message being sent by the chainee, 0 if no message is active + DWORD messageResponse; // chainer's response to current message, 0 if not yet handled + DWORD messageDataLength; // length of the m_messageData field in bytes + BYTE messageData[1]; // variable length buffer, content depends on m_messageCode +}; + +struct NetFxChainer +{ + HANDLE hSection; + + HANDLE hEventChaineeSend; + HANDLE hEventChainerSend; + HANDLE hMutex; + + NetFxDataStructure* pData; + DWORD dwDataSize; +}; + +#define NETFXDATA_SIZE 65536 + +#define NETFXDATA_VERSION 1 + +#define NETFX_MESSAGE(version, defaultResponse, messageCode) \ + ((((DWORD)version & 0xFF) << 24) | (((DWORD)defaultResponse & 0xFF) << 16) | ((DWORD)messageCode & 0xFFFF)) +#define NETFX_MESSAGE_CODE(messageId) \ + (messageId & 0xFFFF) +#define NETFX_MESSAGE_DEFAULT_RESPONSE(messageId) \ + ((messageId >> 16) & 0xFF) +#define NETFX_MESSAGE_VERSION(messageId) \ + ((messageId >>24) & 0xFF) + +#define NETFX_NO_MESSAGE 0 + + +//------------------------------------------------------------------------------ +// NETFX_CLOSE_APPS +// +// Sent by the chainee when it detects that applications are holding files in +// use. Respond to this message in order to tell the chainee to close the +// applications to prevent a reboot. +// +// pData : NetFxCloseApplications : The list of applications +// Acceptable responses: +// IDYES : Indicates that the chainee should attempt to shutdown the apps. +// If all apps do not successfully close the message may be sent again. +// IDNO : Indicates that the chainee should not attempt to close apps. +// IDRETRY : Indicates that the chainee should refresh the list of apps. +// Another NETFX_CLOSE_APPS message will be sent asynchronously with +// the new list of apps. +//------------------------------------------------------------------------------ +#define NETFX_CLOSE_APPS NETFX_MESSAGE(NETFXDATA_VERSION, IDNO, 1) + +struct NetFxApplication +{ + WCHAR szName[MAX_PATH]; + DWORD dwPid; +}; + +struct NetFxCloseApplications +{ + DWORD dwApplicationsSize; + NetFxApplication applications[1]; +}; + +HRESULT NetFxRunChainer( + __in LPCWSTR wzExecutablePath, + __in LPCWSTR wzArguments, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __out DWORD* pdwExitCode + ); +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/package.cpp b/src/engine/package.cpp new file mode 100644 index 00000000..02958efd --- /dev/null +++ b/src/engine/package.cpp @@ -0,0 +1,678 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// internal function declarations + +static HRESULT ParsePayloadRefsFromXml( + __in BURN_PACKAGE* pPackage, + __in BURN_PAYLOADS* pPayloads, + __in IXMLDOMNode* pixnPackage + ); +static HRESULT ParsePatchTargetCode( + __in BURN_PACKAGES* pPackages, + __in IXMLDOMNode* pixnBundle + ); +static HRESULT FindRollbackBoundaryById( + __in BURN_PACKAGES* pPackages, + __in_z LPCWSTR wzId, + __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary + ); + + +// function definitions + +extern "C" HRESULT PackagesParseFromXml( + __in BURN_PACKAGES* pPackages, + __in BURN_PAYLOADS* pPayloads, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + BSTR bstrNodeName = NULL; + DWORD cMspPackages = 0; + LPWSTR scz = NULL; + + // select rollback boundary nodes + hr = XmlSelectNodes(pixnBundle, L"RollbackBoundary", &pixnNodes); + ExitOnFailure(hr, "Failed to select rollback boundary nodes."); + + // get rollback boundary node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get rollback bundary node count."); + + if (cNodes) + { + // allocate memory for rollback boundaries + pPackages->rgRollbackBoundaries = (BURN_ROLLBACK_BOUNDARY*)MemAlloc(sizeof(BURN_ROLLBACK_BOUNDARY) * cNodes, TRUE); + ExitOnNull(pPackages->rgRollbackBoundaries, hr, E_OUTOFMEMORY, "Failed to allocate memory for rollback boundary structs."); + + pPackages->cRollbackBoundaries = cNodes; + + // parse rollback boundary elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = &pPackages->rgRollbackBoundaries[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &pRollbackBoundary->sczId); + ExitOnFailure(hr, "Failed to get @Id."); + + // @Vital + hr = XmlGetYesNoAttribute(pixnNode, L"Vital", &pRollbackBoundary->fVital); + ExitOnFailure(hr, "Failed to get @Vital."); + + // @Transaction + hr = XmlGetYesNoAttribute(pixnNode, L"Transaction", &pRollbackBoundary->fTransaction); + ExitOnFailure(hr, "Failed to get @Transaction."); + + // prepare next iteration + ReleaseNullObject(pixnNode); + ReleaseNullBSTR(bstrNodeName); + } + } + + ReleaseNullObject(pixnNodes); // done with the RollbackBoundary elements. + + // select package nodes + hr = XmlSelectNodes(pixnBundle, L"Chain/ExePackage|Chain/MsiPackage|Chain/MspPackage|Chain/MsuPackage", &pixnNodes); + ExitOnFailure(hr, "Failed to select package nodes."); + + // get package node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get package node count."); + + if (!cNodes) + { + ExitFunction1(hr = S_OK); + } + + // allocate memory for packages + pPackages->rgPackages = (BURN_PACKAGE*)MemAlloc(sizeof(BURN_PACKAGE) * cNodes, TRUE); + ExitOnNull(pPackages->rgPackages, hr, E_OUTOFMEMORY, "Failed to allocate memory for package structs."); + + pPackages->cPackages = cNodes; + + // parse package elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_PACKAGE* pPackage = &pPackages->rgPackages[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &pPackage->sczId); + ExitOnFailure(hr, "Failed to get @Id."); + + // @Cache + hr = XmlGetAttributeEx(pixnNode, L"Cache", &scz); + if (SUCCEEDED(hr)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"no", -1)) + { + pPackage->cacheType = BURN_CACHE_TYPE_NO; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"yes", -1)) + { + pPackage->cacheType = BURN_CACHE_TYPE_YES; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"always", -1)) + { + pPackage->cacheType = BURN_CACHE_TYPE_ALWAYS; + } + else + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Invalid cache type: %ls", scz); + } + } + ExitOnFailure(hr, "Failed to get @Cache."); + + // @CacheId + hr = XmlGetAttributeEx(pixnNode, L"CacheId", &pPackage->sczCacheId); + ExitOnFailure(hr, "Failed to get @CacheId."); + + // @Size + hr = XmlGetAttributeLargeNumber(pixnNode, L"Size", &pPackage->qwSize); + ExitOnFailure(hr, "Failed to get @Size."); + + // @InstallSize + hr = XmlGetAttributeLargeNumber(pixnNode, L"InstallSize", &pPackage->qwInstallSize); + ExitOnFailure(hr, "Failed to get @InstallSize."); + + // @PerMachine + hr = XmlGetYesNoAttribute(pixnNode, L"PerMachine", &pPackage->fPerMachine); + ExitOnFailure(hr, "Failed to get @PerMachine."); + + // @Permanent + hr = XmlGetYesNoAttribute(pixnNode, L"Permanent", &pPackage->fUninstallable); + ExitOnFailure(hr, "Failed to get @Permanent."); + pPackage->fUninstallable = !pPackage->fUninstallable; // TODO: change "Uninstallable" variable name to permanent, until then Uninstallable is the opposite of Permanent so fix the variable. + + // @Vital + hr = XmlGetYesNoAttribute(pixnNode, L"Vital", &pPackage->fVital); + ExitOnFailure(hr, "Failed to get @Vital."); + + // @LogPathVariable + hr = XmlGetAttributeEx(pixnNode, L"LogPathVariable", &pPackage->sczLogPathVariable); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @LogPathVariable."); + } + + // @RollbackLogPathVariable + hr = XmlGetAttributeEx(pixnNode, L"RollbackLogPathVariable", &pPackage->sczRollbackLogPathVariable); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @RollbackLogPathVariable."); + } + + // @InstallCondition + hr = XmlGetAttributeEx(pixnNode, L"InstallCondition", &pPackage->sczInstallCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @InstallCondition."); + } + + // @RollbackBoundaryForward + hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryForward", &scz); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @RollbackBoundaryForward."); + + hr = FindRollbackBoundaryById(pPackages, scz, &pPackage->pRollbackBoundaryForward); + ExitOnFailure(hr, "Failed to find forward transaction boundary: %ls", scz); + } + + // @RollbackBoundaryBackward + hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryBackward", &scz); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @RollbackBoundaryBackward."); + + hr = FindRollbackBoundaryById(pPackages, scz, &pPackage->pRollbackBoundaryBackward); + ExitOnFailure(hr, "Failed to find backward transaction boundary: %ls", scz); + } + + // read type specific attributes + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"ExePackage", -1)) + { + pPackage->type = BURN_PACKAGE_TYPE_EXE; + + hr = ExeEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization + ExitOnFailure(hr, "Failed to parse EXE package."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiPackage", -1)) + { + pPackage->type = BURN_PACKAGE_TYPE_MSI; + + hr = MsiEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization + ExitOnFailure(hr, "Failed to parse MSI package."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MspPackage", -1)) + { + pPackage->type = BURN_PACKAGE_TYPE_MSP; + + hr = MspEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization + ExitOnFailure(hr, "Failed to parse MSP package."); + + ++cMspPackages; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsuPackage", -1)) + { + pPackage->type = BURN_PACKAGE_TYPE_MSU; + + hr = MsuEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization + ExitOnFailure(hr, "Failed to parse MSU package."); + } + else + { + // ignore other package types for now + } + + // parse payload references + hr = ParsePayloadRefsFromXml(pPackage, pPayloads, pixnNode); + ExitOnFailure(hr, "Failed to parse payload references."); + + // parse dependency providers + hr = DependencyParseProvidersFromXml(pPackage, pixnNode); + ExitOnFailure(hr, "Failed to parse dependency providers."); + + // prepare next iteration + ReleaseNullObject(pixnNode); + ReleaseNullBSTR(bstrNodeName); + } + + if (cMspPackages) + { + pPackages->rgPatchInfo = static_cast(MemAlloc(sizeof(MSIPATCHSEQUENCEINFOW) * cMspPackages, TRUE)); + ExitOnNull(pPackages->rgPatchInfo, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSP patch sequence information."); + + pPackages->rgPatchInfoToPackage = static_cast(MemAlloc(sizeof(BURN_PACKAGE*) * cMspPackages, TRUE)); + ExitOnNull(pPackages->rgPatchInfoToPackage, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch sequence information to package lookup."); + + for (DWORD i = 0; i < pPackages->cPackages; ++i) + { + BURN_PACKAGE* pPackage = &pPackages->rgPackages[i]; + + if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + pPackages->rgPatchInfo[pPackages->cPatchInfo].szPatchData = pPackage->Msp.sczApplicabilityXml; + pPackages->rgPatchInfo[pPackages->cPatchInfo].ePatchDataType = MSIPATCH_DATATYPE_XMLBLOB; + pPackages->rgPatchInfoToPackage[pPackages->cPatchInfo] = pPackage; + ++pPackages->cPatchInfo; + + // Loop through all MSI packages seeing if any of them slipstream this MSP. + for (DWORD j = 0; j < pPackages->cPackages; ++j) + { + BURN_PACKAGE* pMsiPackage = &pPackages->rgPackages[j]; + + if (BURN_PACKAGE_TYPE_MSI == pMsiPackage->type) + { + for (DWORD k = 0; k < pMsiPackage->Msi.cSlipstreamMspPackages; ++k) + { + if (pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k] && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k], -1)) + { + pMsiPackage->Msi.rgpSlipstreamMspPackages[k] = pPackage; + + ReleaseNullStr(pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k]); // we don't need the slipstream package id any longer so free it. + } + } + } + } + } + } + } + + AssertSz(pPackages->cPatchInfo == cMspPackages, "Count of packages patch info should be equal to the number of MSP packages."); + + hr = ParsePatchTargetCode(pPackages, pixnBundle); + ExitOnFailure(hr, "Failed to parse target product codes."); + + hr = S_OK; + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseBSTR(bstrNodeName); + ReleaseStr(scz); + + return hr; +} + +extern "C" void PackageUninitialize( + __in BURN_PACKAGE* pPackage + ) +{ + ReleaseStr(pPackage->sczId); + ReleaseStr(pPackage->sczLogPathVariable); + ReleaseStr(pPackage->sczRollbackLogPathVariable); + ReleaseStr(pPackage->sczInstallCondition); + ReleaseStr(pPackage->sczRollbackInstallCondition); + ReleaseStr(pPackage->sczCacheId); + + if (pPackage->rgDependencyProviders) + { + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + DependencyUninitialize(pPackage->rgDependencyProviders + i); + } + MemFree(pPackage->rgDependencyProviders); + } + + ReleaseMem(pPackage->rgPayloads); + + switch (pPackage->type) + { + case BURN_PACKAGE_TYPE_EXE: + ExeEnginePackageUninitialize(pPackage); // TODO: Modularization + break; + case BURN_PACKAGE_TYPE_MSI: + MsiEnginePackageUninitialize(pPackage); // TODO: Modularization + break; + case BURN_PACKAGE_TYPE_MSP: + MspEnginePackageUninitialize(pPackage); // TODO: Modularization + break; + case BURN_PACKAGE_TYPE_MSU: + MsuEnginePackageUninitialize(pPackage); // TODO: Modularization + break; + } +} + +extern "C" void PackagesUninitialize( + __in BURN_PACKAGES* pPackages + ) +{ + if (pPackages->rgRollbackBoundaries) + { + for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i) + { + ReleaseStr(pPackages->rgRollbackBoundaries[i].sczId); + } + MemFree(pPackages->rgRollbackBoundaries); + } + + if (pPackages->rgPackages) + { + for (DWORD i = 0; i < pPackages->cPackages; ++i) + { + PackageUninitialize(pPackages->rgPackages + i); + } + MemFree(pPackages->rgPackages); + } + + if (pPackages->rgCompatiblePackages) + { + for (DWORD i = 0; i < pPackages->cCompatiblePackages; ++i) + { + PackageUninitialize(pPackages->rgCompatiblePackages + i); + } + MemFree(pPackages->rgCompatiblePackages); + } + + if (pPackages->rgPatchTargetCodes) + { + for (DWORD i = 0; i < pPackages->cPatchTargetCodes; ++i) + { + ReleaseStr(pPackages->rgPatchTargetCodes[i].sczTargetCode); + } + MemFree(pPackages->rgPatchTargetCodes); + } + + ReleaseMem(pPackages->rgPatchInfo); + ReleaseMem(pPackages->rgPatchInfoToPackage); + + // clear struct + memset(pPackages, 0, sizeof(BURN_PACKAGES)); +} + +extern "C" HRESULT PackageFindById( + __in BURN_PACKAGES* pPackages, + __in_z LPCWSTR wzId, + __out BURN_PACKAGE** ppPackage + ) +{ + HRESULT hr = S_OK; + BURN_PACKAGE* pPackage = NULL; + + for (DWORD i = 0; i < pPackages->cPackages; ++i) + { + pPackage = &pPackages->rgPackages[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, wzId, -1)) + { + *ppPackage = pPackage; + ExitFunction1(hr = S_OK); + } + } + + for (DWORD i = 0; i < pPackages->cCompatiblePackages; ++i) + { + pPackage = &pPackages->rgCompatiblePackages[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, wzId, -1)) + { + *ppPackage = pPackage; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + + +extern "C" HRESULT PackageFindRelatedById( + __in BURN_RELATED_BUNDLES* pRelatedBundles, + __in_z LPCWSTR wzId, + __out BURN_PACKAGE** ppPackage + ) +{ + HRESULT hr = S_OK; + BURN_PACKAGE* pPackage = NULL; + + for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i) + { + pPackage = &pRelatedBundles->rgRelatedBundles[i].package; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, wzId, -1)) + { + *ppPackage = pPackage; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + +/******************************************************************** + PackageGetProperty - Determines if the property is defined + and optionally copies the property value. + + Note: The caller must free psczValue if requested. + + Note: Returns E_NOTFOUND if the property was not defined or if the + package does not support properties. + +*********************************************************************/ +extern "C" HRESULT PackageGetProperty( + __in const BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzProperty, + __out_z_opt LPWSTR* psczValue + ) +{ + HRESULT hr = E_NOTFOUND; + BURN_MSIPROPERTY* rgProperties = NULL; + DWORD cProperties = 0; + + // For MSIs and MSPs, enumerate the properties looking for wzProperty. + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + { + rgProperties = pPackage->Msi.rgProperties; + cProperties = pPackage->Msi.cProperties; + } + else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + rgProperties = pPackage->Msp.rgProperties; + cProperties = pPackage->Msp.cProperties; + } + + for (DWORD i = 0; i < cProperties; ++i) + { + const BURN_MSIPROPERTY* pProperty = &rgProperties[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pProperty->sczId, -1, wzProperty, -1)) + { + if (psczValue) + { + hr = StrAllocString(psczValue, pProperty->sczValue, 0); + ExitOnFailure(hr, "Failed to copy the property value."); + } + + ExitFunction1(hr = S_OK); + } + } + +LExit: + return hr; +} + +HRESULT PackageEnsureCompatiblePackagesArray( + __in BURN_PACKAGES* pPackages + ) +{ + HRESULT hr = S_OK; + + if (!pPackages->rgCompatiblePackages) + { + pPackages->rgCompatiblePackages = (BURN_PACKAGE*)MemAlloc(sizeof(BURN_PACKAGE) * pPackages->cPackages, TRUE); + ExitOnNull(pPackages->rgCompatiblePackages, hr, E_OUTOFMEMORY, "Failed to allocate memory for compatible packages."); + } + +LExit: + return hr; +} + + +// internal function declarations + +static HRESULT ParsePayloadRefsFromXml( + __in BURN_PACKAGE* pPackage, + __in BURN_PAYLOADS* pPayloads, + __in IXMLDOMNode* pixnPackage + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + LPWSTR sczId = NULL; + + // select package nodes + hr = XmlSelectNodes(pixnPackage, L"PayloadRef", &pixnNodes); + ExitOnFailure(hr, "Failed to select package nodes."); + + // get package node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get package node count."); + + if (!cNodes) + { + ExitFunction1(hr = S_OK); + } + + // allocate memory for payload pointers + pPackage->rgPayloads = (BURN_PACKAGE_PAYLOAD*)MemAlloc(sizeof(BURN_PACKAGE_PAYLOAD) * cNodes, TRUE); + ExitOnNull(pPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate memory for package payloads."); + + pPackage->cPayloads = cNodes; + + // parse package elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_PACKAGE_PAYLOAD* pPackagePayload = &pPackage->rgPayloads[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &sczId); + ExitOnFailure(hr, "Failed to get Id attribute."); + + // find payload + hr = PayloadFindById(pPayloads, sczId, &pPackagePayload->pPayload); + ExitOnFailure(hr, "Failed to find payload."); + + // prepare next iteration + ReleaseNullObject(pixnNode); + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseStr(sczId); + + return hr; +} + +static HRESULT ParsePatchTargetCode( + __in BURN_PACKAGES* pPackages, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + BSTR bstrNodeText = NULL; + BOOL fProduct; + + hr = XmlSelectNodes(pixnBundle, L"PatchTargetCode", &pixnNodes); + ExitOnFailure(hr, "Failed to select PatchTargetCode nodes."); + + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get PatchTargetCode node count."); + + if (!cNodes) + { + ExitFunction1(hr = S_OK); + } + + pPackages->rgPatchTargetCodes = (BURN_PATCH_TARGETCODE*)MemAlloc(sizeof(BURN_PATCH_TARGETCODE) * cNodes, TRUE); + ExitOnNull(pPackages->rgPatchTargetCodes, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch targetcodes."); + + pPackages->cPatchTargetCodes = cNodes; + + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_PATCH_TARGETCODE* pTargetCode = pPackages->rgPatchTargetCodes + i; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + hr = XmlGetAttributeEx(pixnNode, L"TargetCode", &pTargetCode->sczTargetCode); + ExitOnFailure(hr, "Failed to get @TargetCode attribute."); + + hr = XmlGetYesNoAttribute(pixnNode, L"Product", &fProduct); + if (E_NOTFOUND == hr) + { + fProduct = FALSE; + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get @Product."); + + pTargetCode->type = fProduct ? BURN_PATCH_TARGETCODE_TYPE_PRODUCT : BURN_PATCH_TARGETCODE_TYPE_UPGRADE; + + // prepare next iteration + ReleaseNullBSTR(bstrNodeText); + ReleaseNullObject(pixnNode); + } + +LExit: + ReleaseBSTR(bstrNodeText); + ReleaseObject(pixnNode); + ReleaseObject(pixnNodes); + + return hr; +} + +static HRESULT FindRollbackBoundaryById( + __in BURN_PACKAGES* pPackages, + __in_z LPCWSTR wzId, + __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary + ) +{ + HRESULT hr = S_OK; + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; + + for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i) + { + pRollbackBoundary = &pPackages->rgRollbackBoundaries[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pRollbackBoundary->sczId, -1, wzId, -1)) + { + *ppRollbackBoundary = pRollbackBoundary; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} diff --git a/src/engine/package.h b/src/engine/package.h new file mode 100644 index 00000000..c295378e --- /dev/null +++ b/src/engine/package.h @@ -0,0 +1,336 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +struct _BURN_RELATED_BUNDLES; +typedef _BURN_RELATED_BUNDLES BURN_RELATED_BUNDLES; + +struct _BURN_PACKAGE; +typedef _BURN_PACKAGE BURN_PACKAGE; + +// constants + +enum BURN_EXE_EXIT_CODE_TYPE +{ + BURN_EXE_EXIT_CODE_TYPE_NONE, + BURN_EXE_EXIT_CODE_TYPE_SUCCESS, + BURN_EXE_EXIT_CODE_TYPE_ERROR, + BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT, + BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT, +}; + +enum BURN_EXE_PROTOCOL_TYPE +{ + BURN_EXE_PROTOCOL_TYPE_NONE, + BURN_EXE_PROTOCOL_TYPE_BURN, + BURN_EXE_PROTOCOL_TYPE_NETFX4, +}; + +enum BURN_PACKAGE_TYPE +{ + BURN_PACKAGE_TYPE_NONE, + BURN_PACKAGE_TYPE_EXE, + BURN_PACKAGE_TYPE_MSI, + BURN_PACKAGE_TYPE_MSP, + BURN_PACKAGE_TYPE_MSU, +}; + +enum BURN_CACHE_STATE +{ + BURN_CACHE_STATE_NONE, + BURN_CACHE_STATE_PARTIAL, + BURN_CACHE_STATE_COMPLETE, +}; + +enum BURN_CACHE_TYPE +{ + BURN_CACHE_TYPE_NO, + BURN_CACHE_TYPE_YES, + BURN_CACHE_TYPE_ALWAYS, +}; + +enum BURN_DEPENDENCY_ACTION +{ + BURN_DEPENDENCY_ACTION_NONE, + BURN_DEPENDENCY_ACTION_REGISTER, + BURN_DEPENDENCY_ACTION_UNREGISTER, +}; + +enum BURN_PATCH_TARGETCODE_TYPE +{ + BURN_PATCH_TARGETCODE_TYPE_UNKNOWN, + BURN_PATCH_TARGETCODE_TYPE_PRODUCT, + BURN_PATCH_TARGETCODE_TYPE_UPGRADE, +}; + +// structs + +typedef struct _BURN_EXE_EXIT_CODE +{ + BURN_EXE_EXIT_CODE_TYPE type; + DWORD dwCode; + BOOL fWildcard; +} BURN_EXE_EXIT_CODE; + +typedef struct _BURN_EXE_COMMAND_LINE_ARGUMENT +{ + LPWSTR sczInstallArgument; + LPWSTR sczUninstallArgument; + LPWSTR sczRepairArgument; + LPWSTR sczCondition; +} BURN_EXE_COMMAND_LINE_ARGUMENT; + +typedef struct _BURN_MSPTARGETPRODUCT +{ + MSIINSTALLCONTEXT context; + DWORD dwOrder; + WCHAR wzTargetProductCode[39]; + BURN_PACKAGE* pChainedTargetPackage; + BOOL fSlipstream; + + BOOTSTRAPPER_PACKAGE_STATE patchPackageState; // only valid after Detect. + BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan. + BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan. +} BURN_MSPTARGETPRODUCT; + +typedef struct _BURN_MSIPROPERTY +{ + LPWSTR sczId; + LPWSTR sczValue; // used during forward execution + LPWSTR sczRollbackValue; // used during rollback + LPWSTR sczCondition; +} BURN_MSIPROPERTY; + +typedef struct _BURN_MSIFEATURE +{ + LPWSTR sczId; + LPWSTR sczAddLocalCondition; + LPWSTR sczAddSourceCondition; + LPWSTR sczAdvertiseCondition; + LPWSTR sczRollbackAddLocalCondition; + LPWSTR sczRollbackAddSourceCondition; + LPWSTR sczRollbackAdvertiseCondition; + + BOOTSTRAPPER_FEATURE_STATE currentState; // only valid after Detect. + BOOTSTRAPPER_FEATURE_ACTION execute; // only valid during Plan. + BOOTSTRAPPER_FEATURE_ACTION rollback; // only valid during Plan. +} BURN_MSIFEATURE; + +typedef struct _BURN_RELATED_MSI +{ + LPWSTR sczUpgradeCode; + DWORD64 qwMinVersion; + DWORD64 qwMaxVersion; + BOOL fMinProvided; + BOOL fMaxProvided; + BOOL fMinInclusive; + BOOL fMaxInclusive; + BOOL fOnlyDetect; + BOOL fLangInclusive; + + DWORD* rgdwLanguages; + DWORD cLanguages; +} BURN_RELATED_MSI; + +typedef struct _BURN_PACKAGE_PAYLOAD +{ + BURN_PAYLOAD* pPayload; + BOOL fCached; +} BURN_PACKAGE_PAYLOAD; + +typedef struct _BURN_DEPENDENCY_PROVIDER +{ + LPWSTR sczKey; + LPWSTR sczVersion; + LPWSTR sczDisplayName; + BOOL fImported; +} BURN_DEPENDENCY_PROVIDER; + +typedef struct _BURN_ROLLBACK_BOUNDARY +{ + LPWSTR sczId; + BOOL fVital; + BOOL fTransaction; +} BURN_ROLLBACK_BOUNDARY; + +typedef struct _BURN_PATCH_TARGETCODE +{ + LPWSTR sczTargetCode; + BURN_PATCH_TARGETCODE_TYPE type; +} BURN_PATCH_TARGETCODE; + +typedef struct _BURN_PACKAGE +{ + LPWSTR sczId; + + LPWSTR sczLogPathVariable; // name of the variable that will be set to the log path. + LPWSTR sczRollbackLogPathVariable; // name of the variable that will be set to the rollback path. + + LPWSTR sczInstallCondition; + LPWSTR sczRollbackInstallCondition; + BOOL fPerMachine; + BOOL fUninstallable; + BOOL fVital; + + BURN_CACHE_TYPE cacheType; + LPWSTR sczCacheId; + + DWORD64 qwInstallSize; + DWORD64 qwSize; + + BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryForward; // used during install and repair. + BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryBackward; // used during uninstall. + + BOOTSTRAPPER_PACKAGE_STATE currentState; // only valid after Detect. + BURN_CACHE_STATE cache; // only valid after Detect. + BOOTSTRAPPER_PACKAGE_STATE expected; // only valid during Plan. + BOOTSTRAPPER_REQUEST_STATE defaultRequested;// only valid during Plan. + BOOTSTRAPPER_REQUEST_STATE requested; // only valid during Plan. + BOOL fAcquire; // only valid during Plan. + BOOL fUncache; // only valid during Plan. + BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan. + BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan. + BURN_DEPENDENCY_ACTION providerExecute; // only valid during Plan. + BURN_DEPENDENCY_ACTION providerRollback; // only valid during Plan. + BURN_DEPENDENCY_ACTION dependencyExecute; // only valid during Plan. + BURN_DEPENDENCY_ACTION dependencyRollback; // only valid during Plan. + BOOL fDependencyManagerWasHere; // only valid during Plan. + HRESULT hrCacheResult; // only valid during Apply. + + BURN_PACKAGE_PAYLOAD* rgPayloads; + DWORD cPayloads; + + BURN_DEPENDENCY_PROVIDER* rgDependencyProviders; + DWORD cDependencyProviders; + + BURN_PACKAGE_TYPE type; + union + { + struct + { + LPWSTR sczDetectCondition; + LPWSTR sczInstallArguments; + LPWSTR sczRepairArguments; + LPWSTR sczUninstallArguments; + LPWSTR sczIgnoreDependencies; + LPWSTR sczAncestors; + + BOOL fPseudoBundle; + + BOOL fRepairable; + BURN_EXE_PROTOCOL_TYPE protocol; + + BOOL fSupportsAncestors; + + BURN_EXE_EXIT_CODE* rgExitCodes; + DWORD cExitCodes; + + BURN_EXE_COMMAND_LINE_ARGUMENT* rgCommandLineArguments; + DWORD cCommandLineArguments; + } Exe; + struct + { + LPWSTR sczProductCode; + DWORD dwLanguage; + DWORD64 qwVersion; + LPWSTR sczInstalledProductCode; + DWORD64 qwInstalledVersion; + BOOL fDisplayInternalUI; + LPWSTR sczUpgradeCode; + + BURN_MSIPROPERTY* rgProperties; + DWORD cProperties; + + BURN_MSIFEATURE* rgFeatures; + DWORD cFeatures; + + BURN_RELATED_MSI* rgRelatedMsis; + DWORD cRelatedMsis; + + _BURN_PACKAGE** rgpSlipstreamMspPackages; + LPWSTR* rgsczSlipstreamMspPackageIds; + DWORD cSlipstreamMspPackages; + + BOOL fCompatibleInstalled; + } Msi; + struct + { + LPWSTR sczPatchCode; + LPWSTR sczApplicabilityXml; + BOOL fDisplayInternalUI; + + BURN_MSIPROPERTY* rgProperties; + DWORD cProperties; + + BURN_MSPTARGETPRODUCT* rgTargetProducts; + DWORD cTargetProductCodes; + } Msp; + struct + { + LPWSTR sczDetectCondition; + LPWSTR sczKB; + } Msu; + }; +} BURN_PACKAGE; + +typedef struct _BURN_PACKAGES +{ + BURN_ROLLBACK_BOUNDARY* rgRollbackBoundaries; + DWORD cRollbackBoundaries; + + BURN_PACKAGE* rgPackages; + DWORD cPackages; + + BURN_PACKAGE* rgCompatiblePackages; + DWORD cCompatiblePackages; + + BURN_PATCH_TARGETCODE* rgPatchTargetCodes; + DWORD cPatchTargetCodes; + + MSIPATCHSEQUENCEINFOW* rgPatchInfo; + BURN_PACKAGE** rgPatchInfoToPackage; // direct lookup from patch information to the (MSP) package it describes. + // Thus this array is the exact same size as rgPatchInfo. + DWORD cPatchInfo; +} BURN_PACKAGES; + + +// function declarations + +HRESULT PackagesParseFromXml( + __in BURN_PACKAGES* pPackages, + __in BURN_PAYLOADS* pPayloads, + __in IXMLDOMNode* pixnBundle + ); +void PackageUninitialize( + __in BURN_PACKAGE* pPackage + ); +void PackagesUninitialize( + __in BURN_PACKAGES* pPackages + ); +HRESULT PackageFindById( + __in BURN_PACKAGES* pPackages, + __in_z LPCWSTR wzId, + __out BURN_PACKAGE** ppPackage + ); +HRESULT PackageFindRelatedById( + __in BURN_RELATED_BUNDLES* pRelatedBundles, + __in_z LPCWSTR wzId, + __out BURN_PACKAGE** ppPackage + ); +HRESULT PackageGetProperty( + __in const BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzProperty, + __out_z_opt LPWSTR* psczValue + ); +HRESULT PackageEnsureCompatiblePackagesArray( + __in BURN_PACKAGES* pPackages + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/payload.cpp b/src/engine/payload.cpp new file mode 100644 index 00000000..6833288f --- /dev/null +++ b/src/engine/payload.cpp @@ -0,0 +1,367 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// internal function declarations + +static HRESULT FindEmbeddedBySourcePath( + __in BURN_PAYLOADS* pPayloads, + __in_opt BURN_CONTAINER* pContainer, + __in_z LPCWSTR wzStreamName, + __out BURN_PAYLOAD** ppPayload + ); + + +// function definitions + +extern "C" HRESULT PayloadsParseFromXml( + __in BURN_PAYLOADS* pPayloads, + __in_opt BURN_CONTAINERS* pContainers, + __in_opt BURN_CATALOGS* pCatalogs, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + LPWSTR scz = NULL; + + // select payload nodes + hr = XmlSelectNodes(pixnBundle, L"Payload", &pixnNodes); + ExitOnFailure(hr, "Failed to select payload nodes."); + + // get payload node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get payload node count."); + + if (!cNodes) + { + ExitFunction(); + } + + // allocate memory for payloads + pPayloads->rgPayloads = (BURN_PAYLOAD*)MemAlloc(sizeof(BURN_PAYLOAD) * cNodes, TRUE); + ExitOnNull(pPayloads->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate memory for payload structs."); + + pPayloads->cPayloads = cNodes; + + // parse search elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_PAYLOAD* pPayload = &pPayloads->rgPayloads[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &pPayload->sczKey); + ExitOnFailure(hr, "Failed to get @Id."); + + // @FilePath + hr = XmlGetAttributeEx(pixnNode, L"FilePath", &pPayload->sczFilePath); + ExitOnFailure(hr, "Failed to get @FilePath."); + + // @Packaging + hr = XmlGetAttributeEx(pixnNode, L"Packaging", &scz); + ExitOnFailure(hr, "Failed to get @Packaging."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"download", -1)) + { + pPayload->packaging = BURN_PAYLOAD_PACKAGING_DOWNLOAD; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"embedded", -1)) + { + pPayload->packaging = BURN_PAYLOAD_PACKAGING_EMBEDDED; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"external", -1)) + { + pPayload->packaging = BURN_PAYLOAD_PACKAGING_EXTERNAL; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Packaging: %ls", scz); + } + + // @Container + if (pContainers) + { + hr = XmlGetAttributeEx(pixnNode, L"Container", &scz); + if (E_NOTFOUND != hr || BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging) + { + ExitOnFailure(hr, "Failed to get @Container."); + + // find container + hr = ContainerFindById(pContainers, scz, &pPayload->pContainer); + ExitOnFailure(hr, "Failed to to find container: %ls", scz); + } + } + + // @LayoutOnly + hr = XmlGetYesNoAttribute(pixnNode, L"LayoutOnly", &pPayload->fLayoutOnly); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @LayoutOnly."); + } + + // @SourcePath + hr = XmlGetAttributeEx(pixnNode, L"SourcePath", &pPayload->sczSourcePath); + if (E_NOTFOUND != hr || BURN_PAYLOAD_PACKAGING_DOWNLOAD != pPayload->packaging) + { + ExitOnFailure(hr, "Failed to get @SourcePath."); + } + + // @DownloadUrl + hr = XmlGetAttributeEx(pixnNode, L"DownloadUrl", &pPayload->downloadSource.sczUrl); + if (E_NOTFOUND != hr || BURN_PAYLOAD_PACKAGING_DOWNLOAD == pPayload->packaging) + { + ExitOnFailure(hr, "Failed to get @DownloadUrl."); + } + + // @FileSize + hr = XmlGetAttributeEx(pixnNode, L"FileSize", &scz); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @FileSize."); + + hr = StrStringToUInt64(scz, 0, &pPayload->qwFileSize); + ExitOnFailure(hr, "Failed to parse @FileSize."); + } + + // @CertificateAuthorityKeyIdentifier + hr = XmlGetAttributeEx(pixnNode, L"CertificateRootPublicKeyIdentifier", &scz); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @CertificateRootPublicKeyIdentifier."); + + hr = StrAllocHexDecode(scz, &pPayload->pbCertificateRootPublicKeyIdentifier, &pPayload->cbCertificateRootPublicKeyIdentifier); + ExitOnFailure(hr, "Failed to hex decode @CertificateRootPublicKeyIdentifier."); + } + + // @CertificateThumbprint + hr = XmlGetAttributeEx(pixnNode, L"CertificateRootThumbprint", &scz); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @CertificateRootThumbprint."); + + hr = StrAllocHexDecode(scz, &pPayload->pbCertificateRootThumbprint, &pPayload->cbCertificateRootThumbprint); + ExitOnFailure(hr, "Failed to hex decode @CertificateRootThumbprint."); + } + + // @Hash + hr = XmlGetAttributeEx(pixnNode, L"Hash", &scz); + ExitOnFailure(hr, "Failed to get @Hash."); + + hr = StrAllocHexDecode(scz, &pPayload->pbHash, &pPayload->cbHash); + ExitOnFailure(hr, "Failed to hex decode the Payload/@Hash."); + + // @Catalog + hr = XmlGetAttributeEx(pixnNode, L"Catalog", &scz); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Catalog."); + + hr = CatalogFindById(pCatalogs, scz, &pPayload->pCatalog); + ExitOnFailure(hr, "Failed to find catalog."); + } + + // prepare next iteration + ReleaseNullObject(pixnNode); + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseStr(scz); + + return hr; +} + +extern "C" void PayloadsUninitialize( + __in BURN_PAYLOADS* pPayloads + ) +{ + if (pPayloads->rgPayloads) + { + for (DWORD i = 0; i < pPayloads->cPayloads; ++i) + { + BURN_PAYLOAD* pPayload = &pPayloads->rgPayloads[i]; + + ReleaseStr(pPayload->sczKey); + ReleaseStr(pPayload->sczFilePath); + ReleaseMem(pPayload->pbHash); + ReleaseMem(pPayload->pbCertificateRootThumbprint); + ReleaseMem(pPayload->pbCertificateRootPublicKeyIdentifier); + ReleaseStr(pPayload->sczSourcePath); + ReleaseStr(pPayload->sczLocalFilePath); + ReleaseStr(pPayload->downloadSource.sczUrl); + ReleaseStr(pPayload->downloadSource.sczUser); + ReleaseStr(pPayload->downloadSource.sczPassword); + } + MemFree(pPayloads->rgPayloads); + } + + // clear struct + memset(pPayloads, 0, sizeof(BURN_PAYLOADS)); +} + +extern "C" HRESULT PayloadExtractFromContainer( + __in BURN_PAYLOADS* pPayloads, + __in_opt BURN_CONTAINER* pContainer, + __in BURN_CONTAINER_CONTEXT* pContainerContext, + __in_z LPCWSTR wzTargetDir + ) +{ + HRESULT hr = S_OK; + LPWSTR sczStreamName = NULL; + LPWSTR sczDirectory = NULL; + BURN_PAYLOAD* pPayload = NULL; + + // extract all payloads + for (;;) + { + // get next stream + hr = ContainerNextStream(pContainerContext, &sczStreamName); + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + break; + } + ExitOnFailure(hr, "Failed to get next stream."); + + // find payload by stream name + hr = FindEmbeddedBySourcePath(pPayloads, pContainer, sczStreamName, &pPayload); + ExitOnFailure(hr, "Failed to find embedded payload: %ls", sczStreamName); + + // make file path + hr = PathConcat(wzTargetDir, pPayload->sczFilePath, &pPayload->sczLocalFilePath); + ExitOnFailure(hr, "Failed to concat file paths."); + + // extract file + hr = PathGetDirectory(pPayload->sczLocalFilePath, &sczDirectory); + ExitOnFailure(hr, "Failed to get directory portion of local file path"); + + hr = DirEnsureExists(sczDirectory, NULL); + ExitOnFailure(hr, "Failed to ensure directory exists"); + + hr = ContainerStreamToFile(pContainerContext, pPayload->sczLocalFilePath); + ExitOnFailure(hr, "Failed to extract file."); + + // flag that the payload has been acquired + pPayload->state = BURN_PAYLOAD_STATE_ACQUIRED; + } + + // locate any payloads that were not extracted + for (DWORD i = 0; i < pPayloads->cPayloads; ++i) + { + pPayload = &pPayloads->rgPayloads[i]; + + // if the payload is part of the container + if (!pContainer || pPayload->pContainer == pContainer) + { + // if the payload has not been acquired + if (BURN_PAYLOAD_STATE_ACQUIRED > pPayload->state) + { + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Payload was not found in container: %ls", pPayload->sczKey); + } + } + } + +LExit: + ReleaseStr(sczStreamName); + ReleaseStr(sczDirectory); + + return hr; +} + +extern "C" HRESULT PayloadFindById( + __in BURN_PAYLOADS* pPayloads, + __in_z LPCWSTR wzId, + __out BURN_PAYLOAD** ppPayload + ) +{ + HRESULT hr = S_OK; + BURN_PAYLOAD* pPayload = NULL; + + for (DWORD i = 0; i < pPayloads->cPayloads; ++i) + { + pPayload = &pPayloads->rgPayloads[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPayload->sczKey, -1, wzId, -1)) + { + *ppPayload = pPayload; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + +extern "C" HRESULT PayloadFindEmbeddedBySourcePath( + __in BURN_PAYLOADS* pPayloads, + __in_z LPCWSTR wzStreamName, + __out BURN_PAYLOAD** ppPayload + ) +{ + HRESULT hr = S_OK; + BURN_PAYLOAD* pPayload = NULL; + + for (DWORD i = 0; i < pPayloads->cPayloads; ++i) + { + pPayload = &pPayloads->rgPayloads[i]; + + if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPayload->sczSourcePath, -1, wzStreamName, -1)) + { + *ppPayload = pPayload; + ExitFunction1(hr = S_OK); + } + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + + +// internal function definitions + +static HRESULT FindEmbeddedBySourcePath( + __in BURN_PAYLOADS* pPayloads, + __in_opt BURN_CONTAINER* pContainer, + __in_z LPCWSTR wzStreamName, + __out BURN_PAYLOAD** ppPayload + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < pPayloads->cPayloads; ++i) + { + BURN_PAYLOAD* pPayload = &pPayloads->rgPayloads[i]; + + if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging && (!pContainer || pPayload->pContainer == pContainer)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPayload->sczSourcePath, -1, wzStreamName, -1)) + { + *ppPayload = pPayload; + ExitFunction1(hr = S_OK); + } + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} diff --git a/src/engine/payload.h b/src/engine/payload.h new file mode 100644 index 00000000..0c7b68e4 --- /dev/null +++ b/src/engine/payload.h @@ -0,0 +1,93 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + +enum BURN_PAYLOAD_PACKAGING +{ + BURN_PAYLOAD_PACKAGING_NONE, + BURN_PAYLOAD_PACKAGING_DOWNLOAD, + BURN_PAYLOAD_PACKAGING_EMBEDDED, + BURN_PAYLOAD_PACKAGING_EXTERNAL, +}; + +enum BURN_PAYLOAD_STATE +{ + BURN_PAYLOAD_STATE_NONE, + BURN_PAYLOAD_STATE_ACQUIRED, + BURN_PAYLOAD_STATE_CACHED, +}; + + +// structs + +typedef struct _BURN_PAYLOAD +{ + LPWSTR sczKey; + BURN_PAYLOAD_PACKAGING packaging; + BOOL fLayoutOnly; + DWORD64 qwFileSize; + LPWSTR sczFilePath; // file path relative to the execute location + + BURN_CATALOG *pCatalog; // used to verify this payload + BYTE* pbCertificateRootPublicKeyIdentifier; + DWORD cbCertificateRootPublicKeyIdentifier; + BYTE* pbCertificateRootThumbprint; + DWORD cbCertificateRootThumbprint; + BYTE* pbHash; + DWORD cbHash; + + LPWSTR sczSourcePath; + BURN_CONTAINER* pContainer; + DOWNLOAD_SOURCE downloadSource; + + // mutable members + BURN_PAYLOAD_STATE state; + LPWSTR sczLocalFilePath; // location of extracted or downloaded copy +} BURN_PAYLOAD; + +typedef struct _BURN_PAYLOADS +{ + BURN_PAYLOAD* rgPayloads; + DWORD cPayloads; +} BURN_PAYLOADS; + + +// functions + +HRESULT PayloadsParseFromXml( + __in BURN_PAYLOADS* pPayloads, + __in_opt BURN_CONTAINERS* pContainers, + __in_opt BURN_CATALOGS* pCatalogs, + __in IXMLDOMNode* pixnBundle + ); +void PayloadsUninitialize( + __in BURN_PAYLOADS* pPayloads + ); +HRESULT PayloadExtractFromContainer( + __in BURN_PAYLOADS* pPayloads, + __in_opt BURN_CONTAINER* pContainer, + __in BURN_CONTAINER_CONTEXT* pContainerContext, + __in_z LPCWSTR wzTargetDir + ); +HRESULT PayloadFindById( + __in BURN_PAYLOADS* pPayloads, + __in_z LPCWSTR wzId, + __out BURN_PAYLOAD** ppPayload + ); +HRESULT PayloadFindEmbeddedBySourcePath( + __in BURN_PAYLOADS* pPayloads, + __in_z LPCWSTR wzStreamName, + __out BURN_PAYLOAD** ppPayload + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/pipe.cpp b/src/engine/pipe.cpp new file mode 100644 index 00000000..7ecc4859 --- /dev/null +++ b/src/engine/pipe.cpp @@ -0,0 +1,873 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static const DWORD PIPE_64KB = 64 * 1024; +static const DWORD PIPE_WAIT_FOR_CONNECTION = 100; // wait a 10th of a second, +static const DWORD PIPE_RETRY_FOR_CONNECTION = 1800; // for up to 3 minutes. + +static const LPCWSTR PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls"; +static const LPCWSTR CACHE_PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls.Cache"; + +static HRESULT AllocatePipeMessage( + __in DWORD dwMessage, + __in_bcount_opt(cbData) LPVOID pvData, + __in DWORD cbData, + __out_bcount(cb) LPVOID* ppvMessage, + __out DWORD* cbMessage + ); +static void FreePipeMessage( + __in BURN_PIPE_MESSAGE *pMsg + ); +static HRESULT WritePipeMessage( + __in HANDLE hPipe, + __in DWORD dwMessage, + __in_bcount_opt(cbData) LPVOID pvData, + __in DWORD cbData + ); +static HRESULT GetPipeMessage( + __in HANDLE hPipe, + __in BURN_PIPE_MESSAGE* pMsg + ); +static HRESULT ChildPipeConnected( + __in HANDLE hPipe, + __in_z LPCWSTR wzSecret, + __inout DWORD* pdwProcessId + ); + + + +/******************************************************************* + PipeConnectionInitialize - initialize pipe connection data. + +*******************************************************************/ +void PipeConnectionInitialize( + __in BURN_PIPE_CONNECTION* pConnection + ) +{ + memset(pConnection, 0, sizeof(BURN_PIPE_CONNECTION)); + pConnection->hPipe = INVALID_HANDLE_VALUE; + pConnection->hCachePipe = INVALID_HANDLE_VALUE; +} + +/******************************************************************* + PipeConnectionUninitialize - free data in a pipe connection. + +*******************************************************************/ +void PipeConnectionUninitialize( + __in BURN_PIPE_CONNECTION* pConnection + ) +{ + ReleaseFileHandle(pConnection->hCachePipe); + ReleaseFileHandle(pConnection->hPipe); + ReleaseHandle(pConnection->hProcess); + ReleaseStr(pConnection->sczSecret); + ReleaseStr(pConnection->sczName); + + memset(pConnection, 0, sizeof(BURN_PIPE_CONNECTION)); + pConnection->hPipe = INVALID_HANDLE_VALUE; + pConnection->hCachePipe = INVALID_HANDLE_VALUE; +} + +/******************************************************************* + PipeSendMessage - + +*******************************************************************/ +extern "C" HRESULT PipeSendMessage( + __in HANDLE hPipe, + __in DWORD dwMessage, + __in_bcount_opt(cbData) LPVOID pvData, + __in DWORD cbData, + __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + BURN_PIPE_RESULT result = { }; + + hr = WritePipeMessage(hPipe, dwMessage, pvData, cbData); + ExitOnFailure(hr, "Failed to write send message to pipe."); + + hr = PipePumpMessages(hPipe, pfnCallback, pvContext, &result); + ExitOnFailure(hr, "Failed to pump messages during send message to pipe."); + + *pdwResult = result.dwResult; + +LExit: + return hr; +} + +/******************************************************************* + PipePumpMessages - + +*******************************************************************/ +extern "C" HRESULT PipePumpMessages( + __in HANDLE hPipe, + __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, + __in_opt LPVOID pvContext, + __in BURN_PIPE_RESULT* pResult + ) +{ + HRESULT hr = S_OK; + BURN_PIPE_MESSAGE msg = { }; + SIZE_T iData = 0; + LPSTR sczMessage = NULL; + DWORD dwResult = 0; + + // Pump messages from child process. + while (S_OK == (hr = GetPipeMessage(hPipe, &msg))) + { + switch (msg.dwMessage) + { + case BURN_PIPE_MESSAGE_TYPE_LOG: + iData = 0; + + hr = BuffReadStringAnsi((BYTE*)msg.pvData, msg.cbData, &iData, &sczMessage); + ExitOnFailure(hr, "Failed to read log message."); + + hr = LogStringWorkRaw(sczMessage); + ExitOnFailure(hr, "Failed to write log message:'%hs'.", sczMessage); + + dwResult = static_cast(hr); + break; + + case BURN_PIPE_MESSAGE_TYPE_COMPLETE: + if (!msg.pvData || sizeof(DWORD) != msg.cbData) + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "No status returned to PipePumpMessages()"); + } + + pResult->dwResult = *static_cast(msg.pvData); + ExitFunction1(hr = S_OK); // exit loop. + + case BURN_PIPE_MESSAGE_TYPE_TERMINATE: + iData = 0; + + hr = BuffReadNumber(static_cast(msg.pvData), msg.cbData, &iData, &pResult->dwResult); + ExitOnFailure(hr, "Failed to read returned result to PipePumpMessages()"); + + if (sizeof(DWORD) * 2 == msg.cbData) + { + hr = BuffReadNumber(static_cast(msg.pvData), msg.cbData, &iData, (DWORD*)&pResult->fRestart); + ExitOnFailure(hr, "Failed to read returned restart to PipePumpMessages()"); + } + + ExitFunction1(hr = S_OK); // exit loop. + + default: + if (pfnCallback) + { + hr = pfnCallback(&msg, pvContext, &dwResult); + } + else + { + hr = E_INVALIDARG; + } + ExitOnFailure(hr, "Failed to process message: %u", msg.dwMessage); + break; + } + + // post result + hr = WritePipeMessage(hPipe, static_cast(BURN_PIPE_MESSAGE_TYPE_COMPLETE), &dwResult, sizeof(dwResult)); + ExitOnFailure(hr, "Failed to post result to child process."); + + FreePipeMessage(&msg); + } + ExitOnFailure(hr, "Failed to get message over pipe"); + + if (S_FALSE == hr) + { + hr = S_OK; + } + +LExit: + ReleaseStr(sczMessage); + FreePipeMessage(&msg); + + return hr; +} + +/******************************************************************* + PipeCreateNameAndSecret - + +*******************************************************************/ +extern "C" HRESULT PipeCreateNameAndSecret( + __out_z LPWSTR *psczConnectionName, + __out_z LPWSTR *psczSecret + ) +{ + HRESULT hr = S_OK; + WCHAR wzGuid[GUID_STRING_LENGTH]; + LPWSTR sczConnectionName = NULL; + LPWSTR sczSecret = NULL; + + // Create the unique pipe name. + hr = GuidFixedCreate(wzGuid); + ExitOnRootFailure(hr, "Failed to create pipe guid."); + + hr = StrAllocFormatted(&sczConnectionName, L"BurnPipe.%s", wzGuid); + ExitOnFailure(hr, "Failed to allocate pipe name."); + + // Create the unique client secret. + hr = GuidFixedCreate(wzGuid); + ExitOnRootFailure(hr, "Failed to create pipe secret."); + + hr = StrAllocString(&sczSecret, wzGuid, 0); + ExitOnFailure(hr, "Failed to allocate pipe secret."); + + *psczConnectionName = sczConnectionName; + sczConnectionName = NULL; + *psczSecret = sczSecret; + sczSecret = NULL; + +LExit: + ReleaseStr(sczSecret); + ReleaseStr(sczConnectionName); + + return hr; +} + +/******************************************************************* + PipeCreatePipes - create the pipes and event to signal child process. + +*******************************************************************/ +extern "C" HRESULT PipeCreatePipes( + __in BURN_PIPE_CONNECTION* pConnection, + __in BOOL fCreateCachePipe, + __out HANDLE* phEvent + ) +{ + Assert(pConnection->sczName); + Assert(INVALID_HANDLE_VALUE == pConnection->hPipe); + Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe); + + HRESULT hr = S_OK; + PSECURITY_DESCRIPTOR psd = NULL; + SECURITY_ATTRIBUTES sa = { }; + LPWSTR sczFullPipeName = NULL; + HANDLE hPipe = INVALID_HANDLE_VALUE; + HANDLE hCachePipe = INVALID_HANDLE_VALUE; + + // Only the grant special rights when the pipe is being used for "embedded" + // scenarios (aka: there is no cache pipe). + if (!fCreateCachePipe) + { + // Create the security descriptor that grants read/write/sync access to Everyone. + // TODO: consider locking down "WD" to LogonIds (logon session) + LPCWSTR wzSddl = L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW0x00100000;;;WD)"; + if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(wzSddl, SDDL_REVISION_1, &psd, NULL)) + { + ExitWithLastError(hr, "Failed to create the security descriptor for the connection event and pipe."); + } + + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = psd; + sa.bInheritHandle = FALSE; + } + + // Create the pipe. + hr = StrAllocFormatted(&sczFullPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName); + ExitOnFailure(hr, "Failed to allocate full name of pipe: %ls", pConnection->sczName); + + // TODO: consider using overlapped IO to do waits on the pipe and still be able to cancel and such. + hPipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, psd ? &sa : NULL); + if (INVALID_HANDLE_VALUE == hPipe) + { + ExitWithLastError(hr, "Failed to create pipe: %ls", sczFullPipeName); + } + + if (fCreateCachePipe) + { + // Create the cache pipe. + hr = StrAllocFormatted(&sczFullPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName); + ExitOnFailure(hr, "Failed to allocate full name of cache pipe: %ls", pConnection->sczName); + + hCachePipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, NULL); + if (INVALID_HANDLE_VALUE == hCachePipe) + { + ExitWithLastError(hr, "Failed to create pipe: %ls", sczFullPipeName); + } + } + + pConnection->hCachePipe = hCachePipe; + hCachePipe = INVALID_HANDLE_VALUE; + + pConnection->hPipe = hPipe; + hPipe = INVALID_HANDLE_VALUE; + + // TODO: remove the following + *phEvent = NULL; + +LExit: + ReleaseFileHandle(hCachePipe); + ReleaseFileHandle(hPipe); + ReleaseStr(sczFullPipeName); + + if (psd) + { + ::LocalFree(psd); + } + + return hr; +} + +/******************************************************************* + PipeLaunchParentProcess - Called from the per-machine process to create + a per-user process and set up the + communication pipe. + +*******************************************************************/ +const LPCWSTR BURN_COMMANDLINE_SWITCH_UNELEVATED = L"burn.unelevated"; +HRESULT PipeLaunchParentProcess( + __in_z LPCWSTR wzCommandLine, + __in int nCmdShow, + __in_z LPWSTR sczConnectionName, + __in_z LPWSTR sczSecret, + __in BOOL /*fDisableUnelevate*/ + ) +{ + HRESULT hr = S_OK; + DWORD dwProcessId = 0; + LPWSTR sczBurnPath = NULL; + LPWSTR sczParameters = NULL; + HANDLE hProcess = NULL; + + dwProcessId = ::GetCurrentProcessId(); + + hr = PathForCurrentProcess(&sczBurnPath, NULL); + ExitOnFailure(hr, "Failed to get current process path."); + + hr = StrAllocFormatted(&sczParameters, L"-%ls %ls %ls %u %ls", BURN_COMMANDLINE_SWITCH_UNELEVATED, sczConnectionName, sczSecret, dwProcessId, wzCommandLine); + ExitOnFailure(hr, "Failed to allocate parameters for unelevated process."); + +#ifdef ENABLE_UNELEVATE + if (fDisableUnelevate) + { + hr = ProcExec(sczBurnPath, sczParameters, nCmdShow, &hProcess); + ExitOnFailure(hr, "Failed to launch parent process with unelevate disabled: %ls", sczBurnPath); + } + else + { + // Try to launch unelevated and if that fails for any reason, try launch our process normally (even though that may make it elevated). + hr = ProcExecuteAsInteractiveUser(sczBurnPath, sczParameters, &hProcess); + if (FAILED(hr)) + { + hr = ShelExecUnelevated(sczBurnPath, sczParameters, L"open", NULL, nCmdShow); + if (FAILED(hr)) + { + hr = ShelExec(sczBurnPath, sczParameters, L"open", NULL, nCmdShow, NULL, NULL); + ExitOnFailure(hr, "Failed to launch parent process: %ls", sczBurnPath); + } + } + } +#else + hr = ProcExec(sczBurnPath, sczParameters, nCmdShow, &hProcess); + ExitOnFailure(hr, "Failed to launch parent process with unelevate disabled: %ls", sczBurnPath); +#endif + +LExit: + ReleaseHandle(hProcess); + ReleaseStr(sczParameters); + ReleaseStr(sczBurnPath); + + return hr; +} + +/******************************************************************* + PipeLaunchChildProcess - Called from the per-user process to create + the per-machine process and set up the + communication pipe. + +*******************************************************************/ +extern "C" HRESULT PipeLaunchChildProcess( + __in_z LPCWSTR wzExecutablePath, + __in BURN_PIPE_CONNECTION* pConnection, + __in BOOL fElevate, + __in_opt HWND hwndParent + ) +{ + HRESULT hr = S_OK; + DWORD dwCurrentProcessId = ::GetCurrentProcessId(); + LPWSTR sczParameters = NULL; + OS_VERSION osVersion = OS_VERSION_UNKNOWN; + DWORD dwServicePack = 0; + LPCWSTR wzVerb = NULL; + HANDLE hProcess = NULL; + + hr = StrAllocFormatted(&sczParameters, L"-q -%ls %ls %ls %u", BURN_COMMANDLINE_SWITCH_ELEVATED, pConnection->sczName, pConnection->sczSecret, dwCurrentProcessId); + ExitOnFailure(hr, "Failed to allocate parameters for elevated process."); + + OsGetVersion(&osVersion, &dwServicePack); + wzVerb = (OS_VERSION_VISTA > osVersion) || !fElevate ? L"open" : L"runas"; + + // Since ShellExecuteEx doesn't support passing inherited handles, don't bother with CoreAppendFileHandleSelfToCommandLine. + // We could fallback to using ::DuplicateHandle to inject the file handle later if necessary. + hr = ShelExec(wzExecutablePath, sczParameters, wzVerb, NULL, SW_HIDE, hwndParent, &hProcess); + ExitOnFailure(hr, "Failed to launch elevated child process: %ls", wzExecutablePath); + + pConnection->dwProcessId = ::GetProcessId(hProcess); + pConnection->hProcess = hProcess; + hProcess = NULL; + +LExit: + ReleaseHandle(hProcess); + ReleaseStr(sczParameters); + + return hr; +} + +/******************************************************************* + PipeWaitForChildConnect - + +*******************************************************************/ +extern "C" HRESULT PipeWaitForChildConnect( + __in BURN_PIPE_CONNECTION* pConnection + ) +{ + HRESULT hr = S_OK; + HANDLE hPipes[2] = { pConnection->hPipe, pConnection->hCachePipe}; + LPCWSTR wzSecret = pConnection->sczSecret; + DWORD cbSecret = lstrlenW(wzSecret) * sizeof(WCHAR); + DWORD dwCurrentProcessId = ::GetCurrentProcessId(); + DWORD dwAck = 0; + DWORD cb = 0; + + for (DWORD i = 0; i < countof(hPipes) && INVALID_HANDLE_VALUE != hPipes[i]; ++i) + { + HANDLE hPipe = hPipes[i]; + DWORD dwPipeState = PIPE_READMODE_BYTE | PIPE_NOWAIT; + + // Temporarily make the pipe non-blocking so we will not get stuck in ::ConnectNamedPipe() forever + // if the child decides not to show up. + if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL)) + { + ExitWithLastError(hr, "Failed to set pipe to non-blocking."); + } + + // Loop for a while waiting for a connection from child process. + DWORD cRetry = 0; + do + { + if (!::ConnectNamedPipe(hPipe, NULL)) + { + DWORD er = ::GetLastError(); + if (ERROR_PIPE_CONNECTED == er) + { + hr = S_OK; + break; + } + else if (ERROR_PIPE_LISTENING == er) + { + if (cRetry < PIPE_RETRY_FOR_CONNECTION) + { + hr = HRESULT_FROM_WIN32(er); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); + break; + } + + ++cRetry; + ::Sleep(PIPE_WAIT_FOR_CONNECTION); + } + else + { + hr = HRESULT_FROM_WIN32(er); + break; + } + } + } while (HRESULT_FROM_WIN32(ERROR_PIPE_LISTENING) == hr); + ExitOnRootFailure(hr, "Failed to wait for child to connect to pipe."); + + // Put the pipe back in blocking mode. + dwPipeState = PIPE_READMODE_BYTE | PIPE_WAIT; + if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL)) + { + ExitWithLastError(hr, "Failed to reset pipe to blocking."); + } + + // Prove we are the one that created the elevated process by passing the secret. + if (!::WriteFile(hPipe, &cbSecret, sizeof(cbSecret), &cb, NULL)) + { + ExitWithLastError(hr, "Failed to write secret length to pipe."); + } + + if (!::WriteFile(hPipe, wzSecret, cbSecret, &cb, NULL)) + { + ExitWithLastError(hr, "Failed to write secret to pipe."); + } + + if (!::WriteFile(hPipe, &dwCurrentProcessId, sizeof(dwCurrentProcessId), &cb, NULL)) + { + ExitWithLastError(hr, "Failed to write our process id to pipe."); + } + + // Wait until the elevated process responds that it is ready to go. + if (!::ReadFile(hPipe, &dwAck, sizeof(dwAck), &cb, NULL)) + { + ExitWithLastError(hr, "Failed to read ACK from pipe."); + } + + // The ACK should match out expected child process id. + //if (pConnection->dwProcessId != dwAck) + //{ + // hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + // ExitOnRootFailure(hr, "Incorrect ACK from elevated pipe: %u", dwAck); + //} + } + +LExit: + return hr; +} + +/******************************************************************* + PipeTerminateChildProcess - + +*******************************************************************/ +extern "C" HRESULT PipeTerminateChildProcess( + __in BURN_PIPE_CONNECTION* pConnection, + __in DWORD dwParentExitCode, + __in BOOL fRestart + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + + // Prepare the exit message. + hr = BuffWriteNumber(&pbData, &cbData, dwParentExitCode); + ExitOnFailure(hr, "Failed to write exit code to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, fRestart); + ExitOnFailure(hr, "Failed to write restart to message buffer."); + + // Send the messages. + if (INVALID_HANDLE_VALUE != pConnection->hCachePipe) + { + hr = WritePipeMessage(pConnection->hCachePipe, static_cast(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData); + ExitOnFailure(hr, "Failed to post terminate message to child process cache thread."); + } + + hr = WritePipeMessage(pConnection->hPipe, static_cast(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData); + ExitOnFailure(hr, "Failed to post terminate message to child process."); + + // If we were able to get a handle to the other process, wait for it to exit. + if (pConnection->hProcess) + { + if (WAIT_FAILED == ::WaitForSingleObject(pConnection->hProcess, PIPE_WAIT_FOR_CONNECTION * PIPE_RETRY_FOR_CONNECTION)) + { + ExitWithLastError(hr, "Failed to wait for child process exit."); + } + +#ifdef DEBUG + DWORD dwChildExitCode = 0; + DWORD dwErrorCode = ERROR_SUCCESS; + BOOL fReturnedExitCode = ::GetExitCodeProcess(pConnection->hProcess, &dwChildExitCode); + if (!fReturnedExitCode) + { + dwErrorCode = ::GetLastError(); // if the other process is elevated and we are not, then we'll get ERROR_ACCESS_DENIED. + + // The unit test use a thread instead of a process so try to get the exit code from + // the thread because we failed to get it from the process. + if (ERROR_INVALID_HANDLE == dwErrorCode) + { + fReturnedExitCode = ::GetExitCodeThread(pConnection->hProcess, &dwChildExitCode); + } + } + AssertSz((fReturnedExitCode && dwChildExitCode == dwParentExitCode) || + (!fReturnedExitCode && ERROR_ACCESS_DENIED == dwErrorCode), + "Child elevated process did not return matching exit code to parent process."); +#endif + } + +LExit: + return hr; +} + +/******************************************************************* + PipeChildConnect - Called from the child process to connect back + to the pipe provided by the parent process. + +*******************************************************************/ +extern "C" HRESULT PipeChildConnect( + __in BURN_PIPE_CONNECTION* pConnection, + __in BOOL fConnectCachePipe + ) +{ + Assert(pConnection->sczName); + Assert(pConnection->sczSecret); + Assert(!pConnection->hProcess); + Assert(INVALID_HANDLE_VALUE == pConnection->hPipe); + Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe); + + HRESULT hr = S_OK; + LPWSTR sczPipeName = NULL; + + // Try to connect to the parent. + hr = StrAllocFormatted(&sczPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName); + ExitOnFailure(hr, "Failed to allocate name of parent pipe."); + + hr = E_UNEXPECTED; + for (DWORD cRetry = 0; FAILED(hr) && cRetry < PIPE_RETRY_FOR_CONNECTION; ++cRetry) + { + pConnection->hPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (INVALID_HANDLE_VALUE == pConnection->hPipe) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (E_FILENOTFOUND == hr) // if the pipe isn't created, call it a timeout waiting on the parent. + { + hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); + } + + ::Sleep(PIPE_WAIT_FOR_CONNECTION); + } + else // we have a connection, go with it. + { + hr = S_OK; + } + } + ExitOnRootFailure(hr, "Failed to open parent pipe: %ls", sczPipeName) + + // Verify the parent and notify it that the child connected. + hr = ChildPipeConnected(pConnection->hPipe, pConnection->sczSecret, &pConnection->dwProcessId); + ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName); + + if (fConnectCachePipe) + { + // Connect to the parent for the cache pipe. + hr = StrAllocFormatted(&sczPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName); + ExitOnFailure(hr, "Failed to allocate name of parent cache pipe."); + + pConnection->hCachePipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (INVALID_HANDLE_VALUE == pConnection->hCachePipe) + { + ExitWithLastError(hr, "Failed to open parent pipe: %ls", sczPipeName) + } + + // Verify the parent and notify it that the child connected. + hr = ChildPipeConnected(pConnection->hCachePipe, pConnection->sczSecret, &pConnection->dwProcessId); + ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName); + } + + pConnection->hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, pConnection->dwProcessId); + ExitOnNullWithLastError(pConnection->hProcess, hr, "Failed to open companion process with PID: %u", pConnection->dwProcessId); + +LExit: + ReleaseStr(sczPipeName); + + return hr; +} + + +static HRESULT AllocatePipeMessage( + __in DWORD dwMessage, + __in_bcount_opt(cbData) LPVOID pvData, + __in DWORD cbData, + __out_bcount(cb) LPVOID* ppvMessage, + __out DWORD* cbMessage + ) +{ + HRESULT hr = S_OK; + LPVOID pv = NULL; + DWORD cb = 0; + + // If no data was provided, ensure the count of bytes is zero. + if (!pvData) + { + cbData = 0; + } + + // Allocate the message. + cb = sizeof(dwMessage) + sizeof(cbData) + cbData; + pv = MemAlloc(cb, FALSE); + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for message."); + + memcpy_s(pv, cb, &dwMessage, sizeof(dwMessage)); + memcpy_s(static_cast(pv) + sizeof(dwMessage), cb - sizeof(dwMessage), &cbData, sizeof(cbData)); + if (cbData) + { + memcpy_s(static_cast(pv) + sizeof(dwMessage) + sizeof(cbData), cb - sizeof(dwMessage) - sizeof(cbData), pvData, cbData); + } + + *cbMessage = cb; + *ppvMessage = pv; + pv = NULL; + +LExit: + ReleaseMem(pv); + return hr; +} + +static void FreePipeMessage( + __in BURN_PIPE_MESSAGE *pMsg + ) +{ + if (pMsg->fAllocatedData) + { + ReleaseNullMem(pMsg->pvData); + pMsg->fAllocatedData = FALSE; + } +} + +static HRESULT WritePipeMessage( + __in HANDLE hPipe, + __in DWORD dwMessage, + __in_bcount_opt(cbData) LPVOID pvData, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + LPVOID pv = NULL; + DWORD cb = 0; + + hr = AllocatePipeMessage(dwMessage, pvData, cbData, &pv, &cb); + ExitOnFailure(hr, "Failed to allocate message to write."); + + // Write the message. + DWORD cbWrote = 0; + DWORD cbTotalWritten = 0; + while (cbTotalWritten < cb) + { + if (!::WriteFile(hPipe, pv, cb - cbTotalWritten, &cbWrote, NULL)) + { + ExitWithLastError(hr, "Failed to write message type to pipe."); + } + + cbTotalWritten += cbWrote; + } + +LExit: + ReleaseMem(pv); + return hr; +} + +static HRESULT GetPipeMessage( + __in HANDLE hPipe, + __in BURN_PIPE_MESSAGE* pMsg + ) +{ + HRESULT hr = S_OK; + DWORD rgdwMessageAndByteCount[2] = { }; + DWORD cb = 0; + DWORD cbRead = 0; + + while (cbRead < sizeof(rgdwMessageAndByteCount)) + { + if (!::ReadFile(hPipe, reinterpret_cast(rgdwMessageAndByteCount) + cbRead, sizeof(rgdwMessageAndByteCount) - cbRead, &cb, NULL)) + { + DWORD er = ::GetLastError(); + if (ERROR_MORE_DATA == er) + { + hr = S_OK; + } + else if (ERROR_BROKEN_PIPE == er) // parent process shut down, time to exit. + { + memset(rgdwMessageAndByteCount, 0, sizeof(rgdwMessageAndByteCount)); + hr = S_FALSE; + break; + } + else + { + hr = HRESULT_FROM_WIN32(er); + } + ExitOnRootFailure(hr, "Failed to read message from pipe."); + } + + cbRead += cb; + } + + pMsg->dwMessage = rgdwMessageAndByteCount[0]; + pMsg->cbData = rgdwMessageAndByteCount[1]; + if (pMsg->cbData) + { + pMsg->pvData = MemAlloc(pMsg->cbData, FALSE); + ExitOnNull(pMsg->pvData, hr, E_OUTOFMEMORY, "Failed to allocate data for message."); + + if (!::ReadFile(hPipe, pMsg->pvData, pMsg->cbData, &cb, NULL)) + { + ExitWithLastError(hr, "Failed to read data for message."); + } + + pMsg->fAllocatedData = TRUE; + } + +LExit: + if (!pMsg->fAllocatedData && pMsg->pvData) + { + MemFree(pMsg->pvData); + } + + return hr; +} + +static HRESULT ChildPipeConnected( + __in HANDLE hPipe, + __in_z LPCWSTR wzSecret, + __inout DWORD* pdwProcessId + ) +{ + HRESULT hr = S_OK; + LPWSTR sczVerificationSecret = NULL; + DWORD cbVerificationSecret = 0; + DWORD dwVerificationProcessId = 0; + DWORD dwRead = 0; + DWORD dwAck = ::GetCurrentProcessId(); // send our process id as the ACK. + DWORD cb = 0; + + // Read the verification secret. + if (!::ReadFile(hPipe, &cbVerificationSecret, sizeof(cbVerificationSecret), &dwRead, NULL)) + { + ExitWithLastError(hr, "Failed to read size of verification secret from parent pipe."); + } + + if (255 < cbVerificationSecret / sizeof(WCHAR)) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Verification secret from parent is too big."); + } + + hr = StrAlloc(&sczVerificationSecret, cbVerificationSecret / sizeof(WCHAR) + 1); + ExitOnFailure(hr, "Failed to allocate buffer for verification secret."); + + if (!::ReadFile(hPipe, sczVerificationSecret, cbVerificationSecret, &dwRead, NULL)) + { + ExitWithLastError(hr, "Failed to read verification secret from parent pipe."); + } + + // Verify the secrets match. + if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, 0, sczVerificationSecret, -1, wzSecret, -1)) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Verification secret from parent does not match."); + } + + // Read the verification process id. + if (!::ReadFile(hPipe, &dwVerificationProcessId, sizeof(dwVerificationProcessId), &dwRead, NULL)) + { + ExitWithLastError(hr, "Failed to read verification process id from parent pipe."); + } + + // If a process id was not provided, we'll trust the process id from the parent. + if (*pdwProcessId == 0) + { + *pdwProcessId = dwVerificationProcessId; + } + else if (*pdwProcessId != dwVerificationProcessId) // verify the ids match. + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Verification process id from parent does not match."); + } + + // All is well, tell the parent process. + if (!::WriteFile(hPipe, &dwAck, sizeof(dwAck), &cb, NULL)) + { + ExitWithLastError(hr, "Failed to inform parent process that child is running."); + } + +LExit: + ReleaseStr(sczVerificationSecret); + return hr; +} diff --git a/src/engine/pipe.h b/src/engine/pipe.h new file mode 100644 index 00000000..b6a7742a --- /dev/null +++ b/src/engine/pipe.h @@ -0,0 +1,113 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _BURN_PIPE_CONNECTION +{ + LPWSTR sczName; + LPWSTR sczSecret; + DWORD dwProcessId; + + HANDLE hProcess; + HANDLE hPipe; + HANDLE hCachePipe; +} BURN_PIPE_CONNECTION; + +typedef enum _BURN_PIPE_MESSAGE_TYPE +{ + BURN_PIPE_MESSAGE_TYPE_LOG = 0xF0000001, + BURN_PIPE_MESSAGE_TYPE_COMPLETE = 0xF0000002, + BURN_PIPE_MESSAGE_TYPE_TERMINATE = 0xF0000003, +} BURN_PIPE_MESSAGE_TYPE; + +typedef struct _BURN_PIPE_MESSAGE +{ + DWORD dwMessage; + DWORD cbData; + + BOOL fAllocatedData; + LPVOID pvData; +} BURN_PIPE_MESSAGE; + +typedef struct _BURN_PIPE_RESULT +{ + DWORD dwResult; + BOOL fRestart; +} BURN_PIPE_RESULT; + + +typedef HRESULT (*PFN_PIPE_MESSAGE_CALLBACK)( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); + + +// Common functions. +void PipeConnectionInitialize( + __in BURN_PIPE_CONNECTION* pConnection + ); +void PipeConnectionUninitialize( + __in BURN_PIPE_CONNECTION* pConnection + ); +HRESULT PipeSendMessage( + __in HANDLE hPipe, + __in DWORD dwMessage, + __in_bcount_opt(cbData) LPVOID pvData, + __in DWORD cbData, + __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); +HRESULT PipePumpMessages( + __in HANDLE hPipe, + __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, + __in_opt LPVOID pvContext, + __in BURN_PIPE_RESULT* pResult + ); + +// Parent functions. +HRESULT PipeCreateNameAndSecret( + __out_z LPWSTR *psczConnectionName, + __out_z LPWSTR *psczSecret + ); +HRESULT PipeCreatePipes( + __in BURN_PIPE_CONNECTION* pConnection, + __in BOOL fCreateCachePipe, + __out HANDLE* phEvent + ); +HRESULT PipeLaunchParentProcess( + __in LPCWSTR wzCommandLine, + __in int nCmdShow, + __in_z LPWSTR sczConnectionName, + __in_z LPWSTR sczSecret, + __in BOOL fDisableUnelevate + ); +HRESULT PipeLaunchChildProcess( + __in_z LPCWSTR wzExecutablePath, + __in BURN_PIPE_CONNECTION* pConnection, + __in BOOL fElevate, + __in_opt HWND hwndParent + ); +HRESULT PipeWaitForChildConnect( + __in BURN_PIPE_CONNECTION* pConnection + ); +HRESULT PipeTerminateChildProcess( + __in BURN_PIPE_CONNECTION* pConnection, + __in DWORD dwParentExitCode, + __in BOOL fRestart + ); + +// Child functions. +HRESULT PipeChildConnect( + __in BURN_PIPE_CONNECTION* pConnection, + __in BOOL fConnectCachePipe + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp new file mode 100644 index 00000000..01c7a31d --- /dev/null +++ b/src/engine/plan.cpp @@ -0,0 +1,3169 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// internal struct definitions + +struct PLAN_NONPERMANENT_PACKAGE_INDICES +{ + DWORD iAfterExecuteFirstNonPermanentPackage; + DWORD iBeforeRollbackFirstNonPermanentPackage; + DWORD iAfterExecuteLastNonPermanentPackage; + DWORD iAfterRollbackLastNonPermanentPackage; +}; + +// internal function definitions + +static void UninitializeRegistrationAction( + __in BURN_DEPENDENT_REGISTRATION_ACTION* pAction + ); +static void UninitializeCacheAction( + __in BURN_CACHE_ACTION* pCacheAction + ); +static void ResetPlannedPackageState( + __in BURN_PACKAGE* pPackage + ); +static HRESULT ProcessPackage( + __in BOOL fBundlePerMachine, + __in BURN_PACKAGE* pCompatiblePackageParent, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in_z_opt LPCWSTR wzLayoutDirectory, + __inout HANDLE* phSyncpointEvent, + __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, + __in_opt PLAN_NONPERMANENT_PACKAGE_INDICES* pNonpermanentPackageIndices + ); +static HRESULT ProcessPackageRollbackBoundary( + __in BURN_PLAN* pPlan, + __in_opt BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary, + __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary + ); +static HRESULT GetActionDefaultRequestState( + __in BOOTSTRAPPER_ACTION action, + __in BOOL fPermanent, + __in BOOTSTRAPPER_PACKAGE_STATE currentState, + __out BOOTSTRAPPER_REQUEST_STATE* pRequestState + ); +static HRESULT AddRegistrationAction( + __in BURN_PLAN* pPlan, + __in BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type, + __in_z LPCWSTR wzDependentProviderKey, + __in_z LPCWSTR wzOwnerBundleId + ); +static HRESULT AddCachePackage( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __out HANDLE* phSyncpointEvent + ); +static HRESULT AddCachePackageHelper( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __out HANDLE* phSyncpointEvent + ); +static HRESULT AddCacheSlipstreamMsps( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage + ); +static BOOL AlreadyPlannedCachePackage( + __in BURN_PLAN* pPlan, + __in_z LPCWSTR wzPackageId, + __out HANDLE* phSyncpointEvent + ); +static DWORD GetNextCheckpointId(); +static HRESULT AppendCacheAction( + __in BURN_PLAN* pPlan, + __out BURN_CACHE_ACTION** ppCacheAction + ); +static HRESULT AppendRollbackCacheAction( + __in BURN_PLAN* pPlan, + __out BURN_CACHE_ACTION** ppCacheAction + ); +static HRESULT AppendLayoutContainerAction( + __in BURN_PLAN* pPlan, + __in_opt BURN_PACKAGE* pPackage, + __in DWORD iPackageStartAction, + __in BURN_CONTAINER* pContainer, + __in BOOL fContainerCached, + __in_z LPCWSTR wzLayoutDirectory + ); +static HRESULT AppendCacheOrLayoutPayloadAction( + __in BURN_PLAN* pPlan, + __in_opt BURN_PACKAGE* pPackage, + __in DWORD iPackageStartAction, + __in BURN_PAYLOAD* pPayload, + __in BOOL fPayloadCached, + __in_z_opt LPCWSTR wzLayoutDirectory + ); +static BOOL FindContainerCacheAction( + __in BURN_CACHE_ACTION_TYPE type, + __in BURN_PLAN* pPlan, + __in BURN_CONTAINER* pContainer, + __in DWORD iSearchStart, + __in DWORD iSearchEnd, + __out_opt BURN_CACHE_ACTION** ppCacheAction, + __out_opt DWORD* piCacheAction + ); +static HRESULT CreateContainerAcquireAndExtractAction( + __in BURN_PLAN* pPlan, + __in BURN_CONTAINER* pContainer, + __in DWORD iPackageStartAction, + __in BOOL fPayloadCached, + __out BURN_CACHE_ACTION** ppContainerExtractAction, + __out DWORD* piContainerTryAgainAction + ); +static HRESULT AddAcquireContainer( + __in BURN_PLAN* pPlan, + __in BURN_CONTAINER* pContainer, + __out_opt BURN_CACHE_ACTION** ppCacheAction, + __out_opt DWORD* piCacheAction + ); +static HRESULT AddExtractPayload( + __in BURN_CACHE_ACTION* pCacheAction, + __in_opt BURN_PACKAGE* pPackage, + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzPayloadWorkingPath + ); +static BURN_CACHE_ACTION* ProcessSharedPayload( + __in BURN_PLAN* pPlan, + __in BURN_PAYLOAD* pPayload + ); +static HRESULT RemoveUnnecessaryActions( + __in BOOL fExecute, + __in BURN_EXECUTE_ACTION* rgActions, + __in DWORD cActions + ); +static HRESULT FinalizeSlipstreamPatchActions( + __in BOOL fExecute, + __in BURN_EXECUTE_ACTION* rgActions, + __in DWORD cActions + ); +static HRESULT PlanDependencyActions( + __in BOOL fBundlePerMachine, + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage + ); +static HRESULT CalculateExecuteActions( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BURN_PACKAGE* pPackage, + __in BURN_VARIABLES* pVariables, + __out_opt BOOL* pfBARequestedCache + ); +static BOOL NeedsCache( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage + ); +static HRESULT CreateContainerProgress( + __in BURN_PLAN* pPlan, + __in BURN_CONTAINER* pContainer, + __out BURN_CACHE_CONTAINER_PROGRESS** ppContainerProgress + ); +static HRESULT CreatePayloadProgress( + __in BURN_PLAN* pPlan, + __in BURN_PAYLOAD* pPayload, + __out BURN_CACHE_PAYLOAD_PROGRESS** ppPayloadProgress + ); + +// function definitions + +extern "C" void PlanReset( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGES* pPackages + ) +{ + if (pPlan->rgRegistrationActions) + { + for (DWORD i = 0; i < pPlan->cRegistrationActions; ++i) + { + UninitializeRegistrationAction(&pPlan->rgRegistrationActions[i]); + } + MemFree(pPlan->rgRegistrationActions); + } + + if (pPlan->rgRollbackRegistrationActions) + { + for (DWORD i = 0; i < pPlan->cRollbackRegistrationActions; ++i) + { + UninitializeRegistrationAction(&pPlan->rgRollbackRegistrationActions[i]); + } + MemFree(pPlan->rgRollbackRegistrationActions); + } + + if (pPlan->rgCacheActions) + { + for (DWORD i = 0; i < pPlan->cCacheActions; ++i) + { + UninitializeCacheAction(&pPlan->rgCacheActions[i]); + } + MemFree(pPlan->rgCacheActions); + } + + if (pPlan->rgExecuteActions) + { + for (DWORD i = 0; i < pPlan->cExecuteActions; ++i) + { + PlanUninitializeExecuteAction(&pPlan->rgExecuteActions[i]); + } + MemFree(pPlan->rgExecuteActions); + } + + if (pPlan->rgRollbackActions) + { + for (DWORD i = 0; i < pPlan->cRollbackActions; ++i) + { + PlanUninitializeExecuteAction(&pPlan->rgRollbackActions[i]); + } + MemFree(pPlan->rgRollbackActions); + } + + if (pPlan->rgCleanActions) + { + // Nothing needs to be freed inside clean actions today. + MemFree(pPlan->rgCleanActions); + } + + if (pPlan->rgPlannedProviders) + { + ReleaseDependencyArray(pPlan->rgPlannedProviders, pPlan->cPlannedProviders); + } + + if (pPlan->rgContainerProgress) + { + MemFree(pPlan->rgContainerProgress); + } + + if (pPlan->shContainerProgress) + { + ReleaseDict(pPlan->shContainerProgress); + } + + if (pPlan->rgPayloadProgress) + { + MemFree(pPlan->rgPayloadProgress); + } + + if (pPlan->shPayloadProgress) + { + ReleaseDict(pPlan->shPayloadProgress); + } + + memset(pPlan, 0, sizeof(BURN_PLAN)); + + // Reset the planned actions for each package. + if (pPackages->rgPackages) + { + for (DWORD i = 0; i < pPackages->cPackages; ++i) + { + ResetPlannedPackageState(&pPackages->rgPackages[i]); + } + } +} + +extern "C" void PlanUninitializeExecuteAction( + __in BURN_EXECUTE_ACTION* pExecuteAction + ) +{ + switch (pExecuteAction->type) + { + case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: + ReleaseStr(pExecuteAction->exePackage.sczIgnoreDependencies); + ReleaseStr(pExecuteAction->exePackage.sczAncestors); + break; + + case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: + ReleaseStr(pExecuteAction->msiPackage.sczLogPath); + ReleaseMem(pExecuteAction->msiPackage.rgFeatures); + ReleaseMem(pExecuteAction->msiPackage.rgSlipstreamPatches); + ReleaseMem(pExecuteAction->msiPackage.rgOrderedPatches); + break; + + case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: + ReleaseStr(pExecuteAction->mspTarget.sczTargetProductCode); + ReleaseStr(pExecuteAction->mspTarget.sczLogPath); + ReleaseMem(pExecuteAction->mspTarget.rgOrderedPatches); + break; + + case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: + ReleaseStr(pExecuteAction->msuPackage.sczLogPath); + break; + + case BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP: __fallthrough; + case BURN_EXECUTE_ACTION_TYPE_SERVICE_START: + ReleaseStr(pExecuteAction->service.sczServiceName); + break; + + case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: + ReleaseStr(pExecuteAction->packageDependency.sczBundleProviderKey); + break; + + case BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE: + ReleaseStr(pExecuteAction->compatiblePackage.sczInstalledProductCode); + break; + } +} + +extern "C" HRESULT PlanSetVariables( + __in BOOTSTRAPPER_ACTION action, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + + hr = VariableSetNumeric(pVariables, BURN_BUNDLE_ACTION, action, TRUE); + ExitOnFailure(hr, "Failed to set the bundle action built-in variable."); + +LExit: + return hr; +} + +extern "C" HRESULT PlanDefaultPackageRequestState( + __in BURN_PACKAGE_TYPE packageType, + __in BOOTSTRAPPER_PACKAGE_STATE currentState, + __in BOOL fPermanent, + __in BOOTSTRAPPER_ACTION action, + __in BURN_VARIABLES* pVariables, + __in_z_opt LPCWSTR wzInstallCondition, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __out BOOTSTRAPPER_REQUEST_STATE* pRequestState + ) +{ + HRESULT hr = S_OK; + BOOTSTRAPPER_REQUEST_STATE defaultRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; + BOOL fCondition = FALSE; + + // If doing layout, then always default to requesting the file be cached. + if (BOOTSTRAPPER_ACTION_LAYOUT == action) + { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE; + } + else if (BOOTSTRAPPER_RELATION_PATCH == relationType && BURN_PACKAGE_TYPE_MSP == packageType) + { + // For patch related bundles, only install a patch if currently absent during install, modify, or repair. + if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == currentState && BOOTSTRAPPER_ACTION_INSTALL <= action) + { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; + } + else + { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; + } + } + else if (BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED == currentState && BOOTSTRAPPER_ACTION_UNINSTALL != action) + { + // Superseded means the package is on the machine but not active, so only uninstall operations are allowed. + // All other operations do nothing. + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; + } + else if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == currentState && !(BOOTSTRAPPER_ACTION_UNINSTALL == action && BURN_PACKAGE_TYPE_MSP == packageType)) + { + // Obsolete means the package is not on the machine and should not be installed, *except* patches can be obsolete + // and present so allow them to be removed during uninstall. Everyone else, gets nothing. + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; + } + else // pick the best option for the action state and install condition. + { + hr = GetActionDefaultRequestState(action, fPermanent, currentState, &defaultRequestState); + ExitOnFailure(hr, "Failed to get default request state for action."); + + // If there is an install condition (and we're doing an install) evaluate the condition + // to determine whether to use the default request state or make the package absent. + if (BOOTSTRAPPER_ACTION_UNINSTALL != action && wzInstallCondition && *wzInstallCondition) + { + hr = ConditionEvaluate(pVariables, wzInstallCondition, &fCondition); + ExitOnFailure(hr, "Failed to evaluate install condition."); + + *pRequestState = fCondition ? defaultRequestState : BOOTSTRAPPER_REQUEST_STATE_ABSENT; + } + else // just set the package to the default request state. + { + *pRequestState = defaultRequestState; + } + } + +LExit: + return hr; +} + +extern "C" HRESULT PlanLayoutBundle( + __in BURN_PLAN* pPlan, + __in_z LPCWSTR wzExecutableName, + __in DWORD64 qwBundleSize, + __in BURN_VARIABLES* pVariables, + __in BURN_PAYLOADS* pPayloads, + __out_z LPWSTR* psczLayoutDirectory + ) +{ + HRESULT hr = S_OK; + BURN_CACHE_ACTION* pCacheAction = NULL; + LPWSTR sczExecutablePath = NULL; + LPWSTR sczLayoutDirectory = NULL; + + // Get the layout directory. + hr = VariableGetString(pVariables, BURN_BUNDLE_LAYOUT_DIRECTORY, &sczLayoutDirectory); + if (E_NOTFOUND == hr) // if not set, use the current directory as the layout directory. + { + hr = VariableGetString(pVariables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, &sczLayoutDirectory); + if (E_NOTFOUND == hr) // if not set, use the current directory as the layout directory. + { + hr = PathForCurrentProcess(&sczExecutablePath, NULL); + ExitOnFailure(hr, "Failed to get path for current executing process as layout directory."); + + hr = PathGetDirectory(sczExecutablePath, &sczLayoutDirectory); + ExitOnFailure(hr, "Failed to get executing process as layout directory."); + } + } + ExitOnFailure(hr, "Failed to get bundle layout directory property."); + + hr = PathBackslashTerminate(&sczLayoutDirectory); + ExitOnFailure(hr, "Failed to ensure layout directory is backslash terminated."); + + // Plan the layout of the bundle engine itself. + hr = AppendCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to append bundle start action."); + + pCacheAction->type = BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE; + + hr = StrAllocString(&pCacheAction->bundleLayout.sczExecutableName, wzExecutableName, 0); + ExitOnFailure(hr, "Failed to to copy executable name for bundle."); + + hr = StrAllocString(&pCacheAction->bundleLayout.sczLayoutDirectory, sczLayoutDirectory, 0); + ExitOnFailure(hr, "Failed to to copy layout directory for bundle."); + + hr = CacheCalculateBundleLayoutWorkingPath(pPlan->wzBundleId, &pCacheAction->bundleLayout.sczUnverifiedPath); + ExitOnFailure(hr, "Failed to calculate bundle layout working path."); + + pCacheAction->bundleLayout.qwBundleSize = qwBundleSize; + + pPlan->qwCacheSizeTotal += qwBundleSize; + + ++pPlan->cOverallProgressTicksTotal; + + // Plan the layout of layout-only payloads. + for (DWORD i = 0; i < pPayloads->cPayloads; ++i) + { + BURN_PAYLOAD* pPayload = pPayloads->rgPayloads + i; + if (pPayload->fLayoutOnly) + { + // TODO: determine if a payload already exists in the layout and pass appropriate value fPayloadCached + // (instead of always FALSE). + hr = AppendCacheOrLayoutPayloadAction(pPlan, NULL, BURN_PLAN_INVALID_ACTION_INDEX, pPayload, FALSE, sczLayoutDirectory); + ExitOnFailure(hr, "Failed to plan layout payload."); + } + } + + *psczLayoutDirectory = sczLayoutDirectory; + sczLayoutDirectory = NULL; + +LExit: + ReleaseStr(sczLayoutDirectory); + ReleaseStr(sczExecutablePath); + + return hr; +} + +extern "C" HRESULT PlanPackages( + __in BURN_REGISTRATION* pRegistration, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PACKAGES* pPackages, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOL fBundleInstalled, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in_z_opt LPCWSTR wzLayoutDirectory, + __inout HANDLE* phSyncpointEvent + ) +{ + HRESULT hr = S_OK; + BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine. + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; + + PLAN_NONPERMANENT_PACKAGE_INDICES nonpermanentPackageIndices; + nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX; + nonpermanentPackageIndices.iBeforeRollbackFirstNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX; + nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX; + nonpermanentPackageIndices.iAfterRollbackLastNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX; + + // Plan the packages. + for (DWORD i = 0; i < pPackages->cPackages; ++i) + { + DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackages->cPackages - 1 - i : i; + BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage; + + // Support passing Ancestors to embedded burn bundles + if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) + { + // Pass along any ancestors and ourself to prevent infinite loops. + if (pRegistration->sczAncestors && *pRegistration->sczAncestors) + { + hr = StrAllocFormatted(&pPackage->Exe.sczAncestors, L"%ls;%ls", pRegistration->sczAncestors, pRegistration->sczId); + ExitOnFailure(hr, "Failed to copy ancestors and self to related bundle ancestors."); + } + else + { + hr = StrAllocString(&pPackage->Exe.sczAncestors, pRegistration->sczId, 0); + ExitOnFailure(hr, "Failed to copy self to related bundle ancestors."); + } + } + + hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices); + ExitOnFailure(hr, "Failed to process package."); + + // Attempt to remove orphaned packages during uninstall. Currently only MSI packages are supported and should not require source. + if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.fCompatibleInstalled) + { + BURN_PACKAGE* pCompatiblePackage = NULL; + BURN_EXECUTE_ACTION* pAction = NULL; + + // Add the compatible package to the list. + hr = MsiEngineAddCompatiblePackage(pPackages, pPackage, &pCompatiblePackage); + ExitOnFailure(hr, "Failed to add compatible package for package: %ls", pPackage->sczId); + + // Plan to load the compatible package into the elevated engine before its needed. + hr = PlanAppendExecuteAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append execute action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE; + pAction->compatiblePackage.pReferencePackage = pPackage; + pAction->compatiblePackage.qwInstalledVersion = pCompatiblePackage->Msi.qwVersion; + + hr = StrAllocString(&pAction->compatiblePackage.sczInstalledProductCode, pCompatiblePackage->Msi.sczProductCode, 0); + ExitOnFailure(hr, "Failed to copy installed ProductCode"); + + // Process the compatible MSI package like any other. + hr = ProcessPackage(fBundlePerMachine, pPackage, pUX, pPlan, pCompatiblePackage, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices); + ExitOnFailure(hr, "Failed to process compatible package."); + + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pCompatiblePackage->execute) + { + LogId(REPORT_STANDARD, MSG_PLANNED_ORPHAN_PACKAGE_FROM_PROVIDER, pPackage->sczId, pCompatiblePackage->Msi.sczProductCode, pPackage->Msi.sczProductCode); + } + } + } + + // Insert the "keep registration" and "remove registration" actions in the plan when installing the first time and anytime we are uninstalling respectively. + if (!fBundleInstalled && (BOOTSTRAPPER_ACTION_INSTALL == pPlan->action || BOOTSTRAPPER_ACTION_MODIFY == pPlan->action || BOOTSTRAPPER_ACTION_REPAIR == pPlan->action)) + { + if (BURN_PLAN_INVALID_ACTION_INDEX == nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage) + { + nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage = pPlan->cExecuteActions; + nonpermanentPackageIndices.iBeforeRollbackFirstNonPermanentPackage = pPlan->cRollbackActions; + } + + hr = PlanKeepRegistration(pPlan, nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage, nonpermanentPackageIndices.iBeforeRollbackFirstNonPermanentPackage); + ExitOnFailure(hr, "Failed to plan install keep registration."); + } + else if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) + { + if (BURN_PLAN_INVALID_ACTION_INDEX == nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage) + { + nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage = pPlan->cExecuteActions; + nonpermanentPackageIndices.iAfterRollbackLastNonPermanentPackage = pPlan->cRollbackActions; + } + + hr = PlanRemoveRegistration(pPlan, nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage, nonpermanentPackageIndices.iAfterRollbackLastNonPermanentPackage); + ExitOnFailure(hr, "Failed to plan uninstall remove registration."); + } + + // If we still have an open rollback boundary, complete it. + if (pRollbackBoundary) + { + hr = PlanRollbackBoundaryComplete(pPlan); + ExitOnFailure(hr, "Failed to plan rollback boundary begin."); + + pRollbackBoundary = NULL; + } + + // Plan clean up of packages. + for (DWORD i = 0; i < pPackages->cPackages; ++i) + { + DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackages->cPackages - 1 - i : i; + BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage; + + hr = PlanCleanPackage(pPlan, pPackage); + ExitOnFailure(hr, "Failed to plan clean package."); + } + + // Plan best-effort clean up of compatible packages. + for (DWORD i = 0; i < pPackages->cCompatiblePackages; ++i) + { + DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackages->cCompatiblePackages - 1 - i : i; + BURN_PACKAGE* pCompatiblePackage = pPackages->rgCompatiblePackages + iPackage; + + PlanCleanPackage(pPlan, pCompatiblePackage); + } + +LExit: + return hr; +} + +extern "C" HRESULT PlanRegistration( + __in BURN_PLAN* pPlan, + __in BURN_REGISTRATION* pRegistration, + __in BOOTSTRAPPER_RESUME_TYPE resumeType, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in_z_opt LPCWSTR wzIgnoreDependencies, + __out BOOL* pfContinuePlanning + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzSelfDependent = NULL; + STRINGDICT_HANDLE sdIgnoreDependents = NULL; + DEPENDENCY* rgDependencies = NULL; + UINT cDependencies = 0; + + pPlan->fRegister = TRUE; // register the bundle since we're modifying machine state. + + // Keep the registration if the bundle was already installed or we are planning after a restart. + pPlan->fKeepRegistrationDefault = (pRegistration->fInstalled || BOOTSTRAPPER_RESUME_TYPE_REBOOT == resumeType); + + pPlan->fDisallowRemoval = FALSE; // by default the bundle can be planned to be removed + + // If no parent was specified at all, use the bundle id as the self dependent. + if (!pRegistration->sczActiveParent) + { + wzSelfDependent = pRegistration->sczId; + } + else if (*pRegistration->sczActiveParent) // if parent was specified use that as the self dependent. + { + wzSelfDependent = pRegistration->sczActiveParent; + } + // else parent:none was used which means we should not register a dependency on ourself. + + if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) + { + // If our provider key was detected and it points to our current bundle then we can + // unregister the bundle dependency. + if (pRegistration->sczDetectedProviderKeyBundleId && + CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczId, -1, pRegistration->sczDetectedProviderKeyBundleId, -1)) + { + pPlan->dependencyRegistrationAction = BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER; + } + else // log that another bundle already owned our registration, hopefully this only happens when a newer version + { // of a bundle installed and is in the process of upgrading us. + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_PROVIDER_KEY_REMOVAL, pRegistration->sczProviderKey, pRegistration->sczDetectedProviderKeyBundleId); + } + + // Create the dictionary of dependents that should be ignored. + hr = DictCreateStringList(&sdIgnoreDependents, 5, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create the string dictionary."); + + // If the self-dependent dependent exists, plan its removal. If we did not do this, we + // would prevent self-removal. + if (wzSelfDependent && DependencyDependentExists(pRegistration, wzSelfDependent)) + { + hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER, wzSelfDependent, pRegistration->sczId); + ExitOnFailure(hr, "Failed to allocate registration action."); + + hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, wzSelfDependent); + ExitOnFailure(hr, "Failed to add self-dependent to ignore dependents."); + } + + // If we are not doing an upgrade, we check to see if there are still dependents on us and if so we skip planning. + // However, when being upgraded, we always execute our uninstall because a newer version of us is probably + // already on the machine and we need to clean up the stuff specific to this bundle. + if (BOOTSTRAPPER_RELATION_UPGRADE != relationType) + { + // If there were other dependencies to ignore, add them. + if (wzIgnoreDependencies && *wzIgnoreDependencies) + { + hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, wzIgnoreDependencies); + ExitOnFailure(hr, "Failed to add dependents ignored from command-line."); + } + + // For addon or patch bundles, dependent related bundles should be ignored. This allows + // that addon or patch to be removed even though bundles it targets still are registered. + for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) + { + const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i; + + if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType) + { + for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j; + + hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, pProvider->sczKey); + ExitOnFailure(hr, "Failed to add dependent bundle provider key to ignore dependents."); + } + } + } + + // If there are any (non-ignored and not-planned-to-be-removed) dependents left, uninstall. + hr = DepCheckDependents(pRegistration->hkRoot, pRegistration->sczProviderKey, 0, sdIgnoreDependents, &rgDependencies, &cDependencies); + if (E_FILENOTFOUND == hr) + { + hr = S_OK; + } + else if (SUCCEEDED(hr) && cDependencies) + { + // TODO: callback to the BA and let it have the option to ignore any of these dependents? + + pPlan->fDisallowRemoval = TRUE; // ensure the registration stays + *pfContinuePlanning = FALSE; // skip the rest of planning. + + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS, cDependencies); + } + ExitOnFailure(hr, "Failed to check for remaining dependents during planning."); + } + } + else + { + BOOL fAddonOrPatchBundle = (pRegistration->cAddonCodes || pRegistration->cPatchCodes); + + // If the bundle is not cached or will not be cached after restart, ensure the bundle is cached. + if (!FileExistsAfterRestart(pRegistration->sczCacheExecutablePath, NULL)) + { + pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE; + pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION; + } + else if (BOOTSTRAPPER_ACTION_REPAIR == pPlan->action && !CacheBundleRunningFromCache()) // repairing but not not running from the cache. + { + pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE; + pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION; + } + else if (BOOTSTRAPPER_ACTION_REPAIR == pPlan->action) // just repair, make sure the registration is "fixed up". + { + pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION; + } + + // Always update our estimated size registration when installing/modify/repair since things + // may have been added or removed or it just needs to be "fixed up". + pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE; + + // Always plan to write our provider key registration when installing/modify/repair to "fix it" + // if broken. + pPlan->dependencyRegistrationAction = BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER; + + // Register each dependent related bundle. The ensures that addons and patches are reference + // counted and stick around until the last targeted bundle is removed. + for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) + { + const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i; + + if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType) + { + for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j; + + if (!DependencyDependentExists(pRegistration, pProvider->sczKey)) + { + hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, pProvider->sczKey, pRelatedBundle->package.sczId); + ExitOnFailure(hr, "Failed to add registration action for dependent related bundle."); + } + } + } + } + + // Only do the following if we decided there was a dependent self to register. If so and and an explicit parent was + // provided, register dependent self. Otherwise, if this bundle is not an addon or patch bundle then self-regisiter + // as our own dependent. + if (wzSelfDependent && (pRegistration->sczActiveParent || !fAddonOrPatchBundle)) + { + if (!DependencyDependentExists(pRegistration, wzSelfDependent)) + { + hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, wzSelfDependent, pRegistration->sczId); + ExitOnFailure(hr, "Failed to add registration action for self dependent."); + } + } + } + +LExit: + ReleaseDict(sdIgnoreDependents); + ReleaseDependencyArray(rgDependencies, cDependencies); + + return hr; +} + +extern "C" HRESULT PlanPassThroughBundle( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __inout HANDLE* phSyncpointEvent + ) +{ + HRESULT hr = S_OK; + BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine. + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; + + // Plan passthrough package. + hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL); + ExitOnFailure(hr, "Failed to process passthrough package."); + + // If we still have an open rollback boundary, complete it. + if (pRollbackBoundary) + { + hr = PlanRollbackBoundaryComplete(pPlan); + ExitOnFailure(hr, "Failed to plan rollback boundary for passthrough package."); + } + + // Notice that the PlanCleanPackage() function is purposefully missing here. Passthrough packages + // are never cleaned up by the calling bundle (they delete themselves when appropriate) so we don't + // need to plan clean up. + +LExit: + return hr; +} + +extern "C" HRESULT PlanUpdateBundle( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __inout HANDLE* phSyncpointEvent + ) +{ + HRESULT hr = S_OK; + BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine. + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; + + // Plan update package. + hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL); + ExitOnFailure(hr, "Failed to process update package."); + + // If we still have an open rollback boundary, complete it. + if (pRollbackBoundary) + { + hr = PlanRollbackBoundaryComplete(pPlan); + ExitOnFailure(hr, "Failed to plan rollback boundary for update package."); + } + + // Plan clean up of update package. + hr = PlanCleanPackage(pPlan, pPackage); + ExitOnFailure(hr, "Failed to plan clean of update package."); + +LExit: + return hr; +} + +static HRESULT ProcessPackage( + __in BOOL fBundlePerMachine, + __in BURN_PACKAGE* pCompatiblePackageParent, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in_z_opt LPCWSTR wzLayoutDirectory, + __inout HANDLE* phSyncpointEvent, + __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, + __in_opt PLAN_NONPERMANENT_PACKAGE_INDICES* pNonpermanentPackageIndices + ) +{ + HRESULT hr = S_OK; + BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary = NULL; + BOOL fPlanPackageBegan = FALSE; + + // Remember the default requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it. + hr = PlanDefaultPackageRequestState(pPackage->type, pPackage->currentState, !pPackage->fUninstallable, pPlan->action, pVariables, pPackage->sczInstallCondition, relationType, &pPackage->defaultRequested); + ExitOnFailure(hr, "Failed to set default package state."); + + pPackage->requested = pPackage->defaultRequested; + fPlanPackageBegan = TRUE; + + if (pCompatiblePackageParent) + { + AssertSz(BURN_PACKAGE_TYPE_MSI == pPackage->type, "Currently only MSI packages have compatible packages."); + + hr = UserExperienceOnPlanCompatibleMsiPackageBegin(pUX, pCompatiblePackageParent->sczId, pPackage->sczId, pPackage->Msi.qwVersion, &pPackage->requested); + ExitOnRootFailure(hr, "BA aborted plan compatible MSI package begin."); + } + else + { + hr = UserExperienceOnPlanPackageBegin(pUX, pPackage->sczId, &pPackage->requested); + ExitOnRootFailure(hr, "BA aborted plan package begin."); + } + + pEffectiveRollbackBoundary = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackage->pRollbackBoundaryBackward : pPackage->pRollbackBoundaryForward; + hr = ProcessPackageRollbackBoundary(pPlan, pEffectiveRollbackBoundary, ppRollbackBoundary); + ExitOnFailure(hr, "Failed to process package rollback boundary."); + + // If the package is in a requested state, plan it. + if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested) + { + if (BOOTSTRAPPER_ACTION_LAYOUT == pPlan->action) + { + hr = PlanLayoutPackage(pPlan, pPackage, wzLayoutDirectory); + ExitOnFailure(hr, "Failed to plan layout package."); + } + else + { + if (pPackage->fUninstallable && pNonpermanentPackageIndices) + { + if (BURN_PLAN_INVALID_ACTION_INDEX == pNonpermanentPackageIndices->iBeforeRollbackFirstNonPermanentPackage) + { + pNonpermanentPackageIndices->iBeforeRollbackFirstNonPermanentPackage = pPlan->cRollbackActions; + } + } + + hr = PlanExecutePackage(fBundlePerMachine, display, pUX, pPlan, pPackage, pLog, pVariables, phSyncpointEvent); + ExitOnFailure(hr, "Failed to plan execute package."); + + if (pPackage->fUninstallable && pNonpermanentPackageIndices) + { + if (BURN_PLAN_INVALID_ACTION_INDEX == pNonpermanentPackageIndices->iAfterExecuteFirstNonPermanentPackage) + { + pNonpermanentPackageIndices->iAfterExecuteFirstNonPermanentPackage = pPlan->cExecuteActions - 1; + } + + pNonpermanentPackageIndices->iAfterExecuteLastNonPermanentPackage = pPlan->cExecuteActions; + pNonpermanentPackageIndices->iAfterRollbackLastNonPermanentPackage = pPlan->cRollbackActions; + } + } + } + else if (BOOTSTRAPPER_ACTION_LAYOUT != pPlan->action) + { + // All packages that have cacheType set to always should be cached if the bundle is going to be present. + if (BURN_CACHE_TYPE_ALWAYS == pPackage->cacheType && BOOTSTRAPPER_ACTION_INSTALL <= pPlan->action) + { + hr = PlanCachePackage(fBundlePerMachine, pUX, pPlan, pPackage, pVariables, phSyncpointEvent); + ExitOnFailure(hr, "Failed to plan cache package."); + } + else + { + // Make sure the package is properly ref-counted even if no plan is requested. + hr = PlanDependencyActions(fBundlePerMachine, pPlan, pPackage); + ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId); + } + } + + // Add the checkpoint after each package and dependency registration action. + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute || BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback || BURN_DEPENDENCY_ACTION_NONE != pPackage->dependencyExecute) + { + hr = PlanExecuteCheckpoint(pPlan); + ExitOnFailure(hr, "Failed to append execute checkpoint."); + } + +LExit: + if (fPlanPackageBegan) + { + if (pCompatiblePackageParent) + { + UserExperienceOnPlanCompatibleMsiPackageComplete(pUX, pCompatiblePackageParent->sczId, pPackage->sczId, hr, pPackage->currentState, pPackage->requested, pPackage->execute, pPackage->rollback); + } + else + { + UserExperienceOnPlanPackageComplete(pUX, pPackage->sczId, hr, pPackage->currentState, pPackage->requested, pPackage->execute, pPackage->rollback); + } + } + + return hr; +} + +static HRESULT ProcessPackageRollbackBoundary( + __in BURN_PLAN* pPlan, + __in_opt BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary, + __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary + ) +{ + HRESULT hr = S_OK; + + // If the package marks the start of a rollback boundary, start a new one. + if (pEffectiveRollbackBoundary) + { + // Complete previous rollback boundary. + if (*ppRollbackBoundary) + { + hr = PlanRollbackBoundaryComplete(pPlan); + ExitOnFailure(hr, "Failed to plan rollback boundary complete."); + } + + // Start new rollback boundary. + hr = PlanRollbackBoundaryBegin(pPlan, pEffectiveRollbackBoundary); + ExitOnFailure(hr, "Failed to plan rollback boundary begin."); + + *ppRollbackBoundary = pEffectiveRollbackBoundary; + } + +LExit: + return hr; +} + +extern "C" HRESULT PlanLayoutPackage( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __in_z_opt LPCWSTR wzLayoutDirectory + ) +{ + HRESULT hr = S_OK; + BURN_CACHE_ACTION* pCacheAction = NULL; + DWORD iPackageStartAction = 0; + + hr = AppendCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to append package start action."); + + pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_START; + pCacheAction->packageStart.pPackage = pPackage; + + // Remember the index for the package start action (which is now the last in the cache + // actions array) because the array may be resized later and move around in memory. + iPackageStartAction = pPlan->cCacheActions - 1; + + // If any of the package payloads are not cached, add them to the plan. + for (DWORD i = 0; i < pPackage->cPayloads; ++i) + { + BURN_PACKAGE_PAYLOAD* pPackagePayload = &pPackage->rgPayloads[i]; + + // If doing layout and the package is in a container. + if (wzLayoutDirectory && pPackagePayload->pPayload->pContainer) + { + // TODO: determine if a container already exists in the layout and pass appropriate value fPayloadCached (instead of always FALSE). + hr = AppendLayoutContainerAction(pPlan, pPackage, iPackageStartAction, pPackagePayload->pPayload->pContainer, FALSE, wzLayoutDirectory); + ExitOnFailure(hr, "Failed to append layout container action."); + } + else + { + // TODO: determine if a payload already exists in the layout and pass appropriate value fPayloadCached (instead of always FALSE). + hr = AppendCacheOrLayoutPayloadAction(pPlan, pPackage, iPackageStartAction, pPackagePayload->pPayload, FALSE, wzLayoutDirectory); + ExitOnFailure(hr, "Failed to append cache/layout payload action."); + } + + Assert(BURN_CACHE_ACTION_TYPE_PACKAGE_START == pPlan->rgCacheActions[iPackageStartAction].type); + ++pPlan->rgCacheActions[iPackageStartAction].packageStart.cCachePayloads; + pPlan->rgCacheActions[iPackageStartAction].packageStart.qwCachePayloadSizeTotal += pPackagePayload->pPayload->qwFileSize; + } + + // Create package stop action. + hr = AppendCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to append cache action."); + + pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_STOP; + pCacheAction->packageStop.pPackage = pPackage; + + // Update the start action with the location of the complete action. + pPlan->rgCacheActions[iPackageStartAction].packageStart.iPackageCompleteAction = pPlan->cCacheActions - 1; + + ++pPlan->cOverallProgressTicksTotal; + +LExit: + return hr; +} + +extern "C" HRESULT PlanCachePackage( + __in BOOL fPerMachine, + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __in BURN_VARIABLES* pVariables, + __out HANDLE* phSyncpointEvent + ) +{ + HRESULT hr = S_OK; + BOOL fBARequestedCache = FALSE; + + // Calculate the execute actions because we need them to decide whether the package should be cached. + hr = CalculateExecuteActions(pUserExperience, pPackage, pVariables, &fBARequestedCache); + ExitOnFailure(hr, "Failed to calculate execute actions for package: %ls", pPackage->sczId); + + if (fBARequestedCache || NeedsCache(pPlan, pPackage)) + { + hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent); + ExitOnFailure(hr, "Failed to plan cache package."); + + if (pPackage->fPerMachine) + { + pPlan->fPerMachine = TRUE; + } + } + + // Make sure the package is properly ref-counted. + hr = PlanDependencyActions(fPerMachine, pPlan, pPackage); + ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId); + +LExit: + return hr; +} + +extern "C" HRESULT PlanExecutePackage( + __in BOOL fPerMachine, + __in BOOTSTRAPPER_DISPLAY display, + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __inout HANDLE* phSyncpointEvent + ) +{ + HRESULT hr = S_OK; + BOOL fBARequestedCache = FALSE; + + hr = CalculateExecuteActions(pUserExperience, pPackage, pVariables, &fBARequestedCache); + ExitOnFailure(hr, "Failed to calculate plan actions for package: %ls", pPackage->sczId); + + // Calculate package states based on reference count and plan certain dependency actions prior to planning the package execute action. + hr = DependencyPlanPackageBegin(fPerMachine, pPackage, pPlan); + ExitOnFailure(hr, "Failed to begin plan dependency actions for package: %ls", pPackage->sczId); + + if (fBARequestedCache || NeedsCache(pPlan, pPackage)) + { + hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent); + ExitOnFailure(hr, "Failed to plan cache package."); + } + else if (BURN_CACHE_STATE_COMPLETE != pPackage->cache && // if the package is not in the cache, disable any rollback that would require the package from the cache. + (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pPackage->rollback || (BURN_PACKAGE_TYPE_EXE == pPackage->type && BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)) + ) + { + LogId(REPORT_STANDARD, MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE, pPackage->sczId, LoggingCacheStateToString(pPackage->cache), LoggingActionStateToString(pPackage->rollback)); + pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + } + + // Add the cache and install size to estimated size if it will be on the machine at the end of the install + if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || + (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_ABSENT < pPackage->requested) || + BURN_CACHE_TYPE_ALWAYS == pPackage->cacheType + ) + { + // If the package will remain in the cache, add the package size to the estimated size + if (BURN_CACHE_TYPE_YES <= pPackage->cacheType) + { + pPlan->qwEstimatedSize += pPackage->qwSize; + } + + // If the package will end up installed on the machine, add the install size to the estimated size. + if (BOOTSTRAPPER_REQUEST_STATE_CACHE < pPackage->requested) + { + // MSP packages get cached automatically by windows installer with any embedded cabs, so include that in the size as well + if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + pPlan->qwEstimatedSize += pPackage->qwSize; + } + + pPlan->qwEstimatedSize += pPackage->qwInstallSize; + } + } + + // Add execute actions. + switch (pPackage->type) + { + case BURN_PACKAGE_TYPE_EXE: + hr = ExeEnginePlanAddPackage(NULL, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire); + break; + + case BURN_PACKAGE_TYPE_MSI: + hr = MsiEnginePlanAddPackage(display, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire); + break; + + case BURN_PACKAGE_TYPE_MSP: + hr = MspEnginePlanAddPackage(display, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire); + break; + + case BURN_PACKAGE_TYPE_MSU: + hr = MsuEnginePlanAddPackage(pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire); + break; + + default: + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Invalid package type."); + } + ExitOnFailure(hr, "Failed to add plan actions for package: %ls", pPackage->sczId); + + // Plan certain dependency actions after planning the package execute action. + hr = DependencyPlanPackageComplete(pPackage, pPlan); + ExitOnFailure(hr, "Failed to complete plan dependency actions for package: %ls", pPackage->sczId); + + // If we are going to take any action on this package, add progress for it. + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute || BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback) + { + LoggingIncrementPackageSequence(); + + ++pPlan->cExecutePackagesTotal; + ++pPlan->cOverallProgressTicksTotal; + + // If package is per-machine and is being executed, flag the plan to be per-machine as well. + if (pPackage->fPerMachine) + { + pPlan->fPerMachine = TRUE; + } + } + +LExit: + return hr; +} + +extern "C" HRESULT PlanRelatedBundlesBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BURN_REGISTRATION* pRegistration, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in BURN_PLAN* pPlan + ) +{ + HRESULT hr = S_OK; + LPWSTR* rgsczAncestors = NULL; + UINT cAncestors = 0; + STRINGDICT_HANDLE sdAncestors = NULL; + + if (pRegistration->sczAncestors) + { + hr = StrSplitAllocArray(&rgsczAncestors, &cAncestors, pRegistration->sczAncestors, L";"); + ExitOnFailure(hr, "Failed to create string array from ancestors."); + + hr = DictCreateStringListFromArray(&sdAncestors, rgsczAncestors, cAncestors, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create dictionary from ancestors array."); + } + + for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) + { + BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i; + pRelatedBundle->package.defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE; + pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE; + + // Do not execute the same bundle twice. + if (sdAncestors) + { + hr = DictKeyExists(sdAncestors, pRelatedBundle->package.sczId); + if (SUCCEEDED(hr)) + { + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_SCHEDULED, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType)); + continue; + } + else if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to lookup the bundle ID in the ancestors dictionary."); + } + } + else if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_RELATION_NONE != relationType) + { + // Avoid repair loops for older bundles that do not handle ancestors. + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_DEPENDENT, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRelationTypeToString(relationType)); + continue; + } + + // Pass along any ancestors and ourself to prevent infinite loops. + if (pRegistration->sczAncestors && *pRegistration->sczAncestors) + { + hr = StrAllocFormatted(&pRelatedBundle->package.Exe.sczAncestors, L"%ls;%ls", pRegistration->sczAncestors, pRegistration->sczId); + ExitOnFailure(hr, "Failed to copy ancestors and self to related bundle ancestors."); + } + else + { + hr = StrAllocString(&pRelatedBundle->package.Exe.sczAncestors, pRegistration->sczId, 0); + ExitOnFailure(hr, "Failed to copy self to related bundle ancestors."); + } + + switch (pRelatedBundle->relationType) + { + case BOOTSTRAPPER_RELATION_UPGRADE: + if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < pPlan->action) + { + pRelatedBundle->package.requested = (pRegistration->qwVersion > pRelatedBundle->qwVersion) ? BOOTSTRAPPER_REQUEST_STATE_ABSENT : BOOTSTRAPPER_REQUEST_STATE_NONE; + } + break; + case BOOTSTRAPPER_RELATION_PATCH: __fallthrough; + case BOOTSTRAPPER_RELATION_ADDON: + if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) + { + pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_ABSENT; + } + else if (BOOTSTRAPPER_ACTION_INSTALL == pPlan->action || BOOTSTRAPPER_ACTION_MODIFY == pPlan->action) + { + pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_PRESENT; + } + else if (BOOTSTRAPPER_ACTION_REPAIR == pPlan->action) + { + pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_REPAIR; + } + break; + case BOOTSTRAPPER_RELATION_DEPENDENT: + // Automatically repair dependent bundles to restore missing + // packages after uninstall unless we're being upgraded with the + // assumption that upgrades are cumulative (as intended). + if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) + { + pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_REPAIR; + } + break; + case BOOTSTRAPPER_RELATION_DETECT: + break; + default: + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Unexpected relation type encountered during plan: %d", pRelatedBundle->relationType); + break; + } + + pRelatedBundle->package.defaultRequested = pRelatedBundle->package.requested; + + hr = UserExperienceOnPlanRelatedBundle(pUserExperience, pRelatedBundle->package.sczId, &pRelatedBundle->package.requested); + ExitOnRootFailure(hr, "BA aborted plan related bundle."); + + // Log when the BA changed the bundle state so the engine doesn't get blamed for planning the wrong thing. + if (pRelatedBundle->package.requested != pRelatedBundle->package.defaultRequested) + { + LogId(REPORT_STANDARD, MSG_PLANNED_BUNDLE_UX_CHANGED_REQUEST, pRelatedBundle->package.sczId, LoggingRequestStateToString(pRelatedBundle->package.requested), LoggingRequestStateToString(pRelatedBundle->package.defaultRequested)); + } + + // If uninstalling and the dependent related bundle may be executed, ignore its provider key to allow for downgrades with ref-counting. + if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested) + { + if (0 < pRelatedBundle->package.cDependencyProviders) + { + // Bundles only support a single provider key. + const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders; + + hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, pProvider->sczDisplayName); + ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey); + } + } + } + +LExit: + ReleaseDict(sdAncestors); + ReleaseStrArray(rgsczAncestors, cAncestors); + + return hr; +} + +extern "C" HRESULT PlanRelatedBundlesComplete( + __in BURN_REGISTRATION* pRegistration, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __inout HANDLE* phSyncpointEvent, + __in DWORD dwExecuteActionEarlyIndex + ) +{ + HRESULT hr = S_OK; + LPWSTR sczIgnoreDependencies = NULL; + STRINGDICT_HANDLE sdProviderKeys = NULL; + + // Get the list of dependencies to ignore to pass to related bundles. + hr = DependencyAllocIgnoreDependencies(pPlan, &sczIgnoreDependencies); + ExitOnFailure(hr, "Failed to get the list of dependencies to ignore."); + + hr = DictCreateStringList(&sdProviderKeys, pPlan->cExecuteActions, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create dictionary for planned packages."); + + BOOL fExecutingAnyPackage = FALSE; + + for (DWORD i = 0; i < pPlan->cExecuteActions; ++i) + { + if (BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE == pPlan->rgExecuteActions[i].type && BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].exePackage.action) + { + fExecutingAnyPackage = TRUE; + + BURN_PACKAGE* pPackage = pPlan->rgExecuteActions[i].packageProvider.pPackage; + if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) + { + if (0 < pPackage->cDependencyProviders) + { + // Bundles only support a single provider key. + const BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders; + DictAddKey(sdProviderKeys, pProvider->sczKey); + } + } + } + else + { + switch (pPlan->rgExecuteActions[i].type) + { + case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: + fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].msiPackage.action); + break; + + case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: + fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].mspTarget.action); + break; + + case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: + fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].msuPackage.action); + break; + } + } + } + + for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) + { + DWORD *pdwInsertIndex = NULL; + BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i; + + // Do not execute if a major upgrade to the related bundle is an embedded bundle (Provider keys are the same) + if (0 < pRelatedBundle->package.cDependencyProviders) + { + // Bundles only support a single provider key. + const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders; + hr = DictKeyExists(sdProviderKeys, pProvider->sczKey); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to check the dictionary for a related bundle provider key: \"%ls\".", pProvider->sczKey); + // Key found, so there is an embedded bundle with the same provider key that will be executed. So this related bundle should not be added to the plan + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), pProvider->sczKey); + continue; + } + else + { + hr = S_OK; + } + } + + // For an uninstall, there is no need to repair dependent bundles if no packages are executing. + if (!fExecutingAnyPackage && BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pRelatedBundle->package.requested && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) + { + pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE; + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DEPENDENT_BUNDLE_REPAIR, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType)); + } + + if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType) + { + // Addon and patch bundles will be passed a list of dependencies to ignore for planning. + hr = StrAllocString(&pRelatedBundle->package.Exe.sczIgnoreDependencies, sczIgnoreDependencies, 0); + ExitOnFailure(hr, "Failed to copy the list of dependencies to ignore."); + + // Uninstall addons and patches early in the chain, before other packages are uninstalled. + if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) + { + pdwInsertIndex = &dwExecuteActionEarlyIndex; + } + } + + if (BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested) + { + hr = ExeEnginePlanCalculatePackage(&pRelatedBundle->package, NULL); + ExitOnFailure(hr, "Failed to calcuate plan for related bundle: %ls", pRelatedBundle->package.sczId); + + // Calculate package states based on reference count for addon and patch related bundles. + if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType) + { + hr = DependencyPlanPackageBegin(pRegistration->fPerMachine, &pRelatedBundle->package, pPlan); + ExitOnFailure(hr, "Failed to begin plan dependency actions to package: %ls", pRelatedBundle->package.sczId); + + // If uninstalling a related bundle, make sure the bundle is uninstalled after removing registration. + if (pdwInsertIndex && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) + { + ++(*pdwInsertIndex); + } + } + + hr = ExeEnginePlanAddPackage(pdwInsertIndex, &pRelatedBundle->package, pPlan, pLog, pVariables, *phSyncpointEvent, FALSE); + ExitOnFailure(hr, "Failed to add to plan related bundle: %ls", pRelatedBundle->package.sczId); + + // Calculate package states based on reference count for addon and patch related bundles. + if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType) + { + hr = DependencyPlanPackageComplete(&pRelatedBundle->package, pPlan); + ExitOnFailure(hr, "Failed to complete plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId); + } + + // If we are going to take any action on this package, add progress for it. + if (BOOTSTRAPPER_ACTION_STATE_NONE != pRelatedBundle->package.execute || BOOTSTRAPPER_ACTION_STATE_NONE != pRelatedBundle->package.rollback) + { + LoggingIncrementPackageSequence(); + + ++pPlan->cExecutePackagesTotal; + ++pPlan->cOverallProgressTicksTotal; + } + + // If package is per-machine and is being executed, flag the plan to be per-machine as well. + if (pRelatedBundle->package.fPerMachine) + { + pPlan->fPerMachine = TRUE; + } + } + else if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType) + { + // Make sure the package is properly ref-counted even if no plan is requested. + hr = DependencyPlanPackageBegin(pRegistration->fPerMachine, &pRelatedBundle->package, pPlan); + ExitOnFailure(hr, "Failed to begin plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId); + + hr = DependencyPlanPackage(pdwInsertIndex, &pRelatedBundle->package, pPlan); + ExitOnFailure(hr, "Failed to plan related bundle package provider actions."); + + hr = DependencyPlanPackageComplete(&pRelatedBundle->package, pPlan); + ExitOnFailure(hr, "Failed to complete plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId); + } + } + +LExit: + ReleaseDict(sdProviderKeys); + ReleaseStr(sczIgnoreDependencies); + + return hr; +} + +extern "C" HRESULT PlanFinalizeActions( + __in BURN_PLAN* pPlan + ) +{ + HRESULT hr = S_OK; + + hr = RemoveUnnecessaryActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions); + ExitOnFailure(hr, "Failed to remove unnecessary execute actions."); + + hr = RemoveUnnecessaryActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions); + ExitOnFailure(hr, "Failed to remove unnecessary execute actions."); + + hr = FinalizeSlipstreamPatchActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions); + ExitOnFailure(hr, "Failed to finalize slipstream execute actions."); + + hr = FinalizeSlipstreamPatchActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions); + ExitOnFailure(hr, "Failed to finalize slipstream rollback actions."); + +LExit: + return hr; +} + +extern "C" HRESULT PlanCleanPackage( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + BOOL fPlanCleanPackage = FALSE; + BURN_CLEAN_ACTION* pCleanAction = NULL; + + // The following is a complex set of logic that determines when a package should be cleaned + // from the cache. Start by noting that we only clean if the package is being acquired or + // already cached and the package is not supposed to always be cached. + if ((pPackage->fAcquire || BURN_CACHE_STATE_PARTIAL == pPackage->cache || BURN_CACHE_STATE_COMPLETE == pPackage->cache) && + (BURN_CACHE_TYPE_ALWAYS > pPackage->cacheType || BOOTSTRAPPER_ACTION_INSTALL > pPlan->action)) + { + // The following are all different reasons why the package should be cleaned from the cache. + // The else-ifs are used to make the conditions easier to see (rather than have them combined + // in one huge condition). + if (BURN_CACHE_TYPE_YES > pPackage->cacheType) // easy, package is not supposed to stay cached. + { + fPlanCleanPackage = TRUE; + } + else if ((BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested || + BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested) && // requested to be removed and + BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) // actually being removed. + { + fPlanCleanPackage = TRUE; + } + else if ((BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested || + BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested) && // requested to be removed but + BOOTSTRAPPER_ACTION_STATE_NONE == pPackage->execute && // execute is do nothing and + !pPackage->fDependencyManagerWasHere && // dependency manager didn't change execute and + BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState) // currently not installed. + { + fPlanCleanPackage = TRUE; + } + else if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && // uninstalling and + BOOTSTRAPPER_REQUEST_STATE_NONE == pPackage->requested && // requested do nothing (aka: default) and + BOOTSTRAPPER_ACTION_STATE_NONE == pPackage->execute && // execute is still do nothing and + !pPackage->fDependencyManagerWasHere && // dependency manager didn't change execute and + BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState) // currently not installed. + { + fPlanCleanPackage = TRUE; + } + } + + if (fPlanCleanPackage) + { + hr = MemEnsureArraySize(reinterpret_cast(&pPlan->rgCleanActions), pPlan->cCleanActions + 1, sizeof(BURN_CLEAN_ACTION), 5); + ExitOnFailure(hr, "Failed to grow plan's array of clean actions."); + + pCleanAction = pPlan->rgCleanActions + pPlan->cCleanActions; + ++pPlan->cCleanActions; + + pCleanAction->pPackage = pPackage; + + pPackage->fUncache = TRUE; + } + +LExit: + return hr; +} + +extern "C" HRESULT PlanExecuteCacheSyncAndRollback( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __in HANDLE hCacheEvent, + __in BOOL fPlanPackageCacheRollback + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* pAction = NULL; + + hr = PlanAppendExecuteAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append wait action for caching."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT; + pAction->syncpoint.hEvent = hCacheEvent; + + if (fPlanPackageCacheRollback) + { + hr = PlanAppendRollbackAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append rollback action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE; + pAction->uncachePackage.pPackage = pPackage; + + hr = PlanExecuteCheckpoint(pPlan); + ExitOnFailure(hr, "Failed to append execute checkpoint for cache rollback."); + } + +LExit: + return hr; +} + +extern "C" HRESULT PlanExecuteCheckpoint( + __in BURN_PLAN* pPlan + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* pAction = NULL; + DWORD dwCheckpointId = GetNextCheckpointId(); + + // execute checkpoint + hr = PlanAppendExecuteAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append execute action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT; + pAction->checkpoint.dwId = dwCheckpointId; + + // rollback checkpoint + hr = PlanAppendRollbackAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append rollback action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT; + pAction->checkpoint.dwId = dwCheckpointId; + +LExit: + return hr; +} + +extern "C" HRESULT PlanInsertExecuteAction( + __in DWORD dwIndex, + __in BURN_PLAN* pPlan, + __out BURN_EXECUTE_ACTION** ppExecuteAction + ) +{ + HRESULT hr = S_OK; + + hr = MemInsertIntoArray((void**)&pPlan->rgExecuteActions, dwIndex, 1, pPlan->cExecuteActions + 1, sizeof(BURN_EXECUTE_ACTION), 5); + ExitOnFailure(hr, "Failed to grow plan's array of execute actions."); + + *ppExecuteAction = pPlan->rgExecuteActions + dwIndex; + ++pPlan->cExecuteActions; + +LExit: + return hr; +} + +extern "C" HRESULT PlanInsertRollbackAction( + __in DWORD dwIndex, + __in BURN_PLAN* pPlan, + __out BURN_EXECUTE_ACTION** ppRollbackAction + ) +{ + HRESULT hr = S_OK; + + hr = MemInsertIntoArray((void**)&pPlan->rgRollbackActions, dwIndex, 1, pPlan->cRollbackActions + 1, sizeof(BURN_EXECUTE_ACTION), 5); + ExitOnFailure(hr, "Failed to grow plan's array of rollback actions."); + + *ppRollbackAction = pPlan->rgRollbackActions + dwIndex; + ++pPlan->cRollbackActions; + +LExit: + return hr; +} + +extern "C" HRESULT PlanAppendExecuteAction( + __in BURN_PLAN* pPlan, + __out BURN_EXECUTE_ACTION** ppExecuteAction + ) +{ + HRESULT hr = S_OK; + + hr = MemEnsureArraySize((void**)&pPlan->rgExecuteActions, pPlan->cExecuteActions + 1, sizeof(BURN_EXECUTE_ACTION), 5); + ExitOnFailure(hr, "Failed to grow plan's array of execute actions."); + + *ppExecuteAction = pPlan->rgExecuteActions + pPlan->cExecuteActions; + ++pPlan->cExecuteActions; + +LExit: + return hr; +} + +extern "C" HRESULT PlanAppendRollbackAction( + __in BURN_PLAN* pPlan, + __out BURN_EXECUTE_ACTION** ppRollbackAction + ) +{ + HRESULT hr = S_OK; + + hr = MemEnsureArraySize((void**)&pPlan->rgRollbackActions, pPlan->cRollbackActions + 1, sizeof(BURN_EXECUTE_ACTION), 5); + ExitOnFailure(hr, "Failed to grow plan's array of rollback actions."); + + *ppRollbackAction = pPlan->rgRollbackActions + pPlan->cRollbackActions; + ++pPlan->cRollbackActions; + +LExit: + return hr; +} + +extern "C" HRESULT PlanKeepRegistration( + __in BURN_PLAN* pPlan, + __in DWORD iAfterExecutePackageAction, + __in DWORD iBeforeRollbackPackageAction + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* pAction = NULL; + + if (BURN_PLAN_INVALID_ACTION_INDEX != iAfterExecutePackageAction) + { + hr = PlanInsertExecuteAction(iAfterExecutePackageAction, pPlan, &pAction); + ExitOnFailure(hr, "Failed to insert keep registration execute action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION; + pAction->registration.fKeep = TRUE; + } + + if (BURN_PLAN_INVALID_ACTION_INDEX != iBeforeRollbackPackageAction) + { + hr = PlanInsertRollbackAction(iBeforeRollbackPackageAction, pPlan, &pAction); + ExitOnFailure(hr, "Failed to insert keep registration rollback action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION; + pAction->registration.fKeep = FALSE; + } + +LExit: + return hr; +} + +extern "C" HRESULT PlanRemoveRegistration( + __in BURN_PLAN* pPlan, + __in DWORD iAfterExecutePackageAction, + __in DWORD iAfterRollbackPackageAction + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* pAction = NULL; + + if (BURN_PLAN_INVALID_ACTION_INDEX != iAfterExecutePackageAction) + { + hr = PlanInsertExecuteAction(iAfterExecutePackageAction, pPlan, &pAction); + ExitOnFailure(hr, "Failed to insert remove registration execute action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION; + pAction->registration.fKeep = FALSE; + } + + if (BURN_PLAN_INVALID_ACTION_INDEX != iAfterRollbackPackageAction) + { + hr = PlanInsertRollbackAction(iAfterRollbackPackageAction, pPlan, &pAction); + ExitOnFailure(hr, "Failed to insert remove registration rollback action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION; + pAction->registration.fKeep = TRUE; + } + +LExit: + return hr; +} + +extern "C" HRESULT PlanRollbackBoundaryBegin( + __in BURN_PLAN* pPlan, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* pExecuteAction = NULL; + + // Add begin rollback boundary to execute plan. + hr = PlanAppendExecuteAction(pPlan, &pExecuteAction); + ExitOnFailure(hr, "Failed to append rollback boundary begin action."); + + pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY; + pExecuteAction->rollbackBoundary.pRollbackBoundary = pRollbackBoundary; + + // Add begin rollback boundary to rollback plan. + hr = PlanAppendRollbackAction(pPlan, &pExecuteAction); + ExitOnFailure(hr, "Failed to append rollback boundary begin action."); + + pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY; + pExecuteAction->rollbackBoundary.pRollbackBoundary = pRollbackBoundary; + +LExit: + return hr; +} + +extern "C" HRESULT PlanRollbackBoundaryComplete( + __in BURN_PLAN* pPlan + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* pExecuteAction = NULL; + DWORD dwCheckpointId = 0; + + // Add checkpoints. + dwCheckpointId = GetNextCheckpointId(); + + hr = PlanAppendExecuteAction(pPlan, &pExecuteAction); + ExitOnFailure(hr, "Failed to append execute action."); + + pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT; + pExecuteAction->checkpoint.dwId = dwCheckpointId; + + hr = PlanAppendRollbackAction(pPlan, &pExecuteAction); + ExitOnFailure(hr, "Failed to append rollback action."); + + pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT; + pExecuteAction->checkpoint.dwId = dwCheckpointId; + +LExit: + return hr; +} + +/******************************************************************* + PlanSetResumeCommand - Initializes resume command string + +*******************************************************************/ +extern "C" HRESULT PlanSetResumeCommand( + __in BURN_REGISTRATION* pRegistration, + __in BOOTSTRAPPER_ACTION action, + __in BOOTSTRAPPER_COMMAND* pCommand, + __in BURN_LOGGING* pLog + ) +{ + HRESULT hr = S_OK; + + // build the resume command-line. + hr = CoreRecreateCommandLine(&pRegistration->sczResumeCommandLine, action, pCommand->display, pCommand->restart, pCommand->relationType, pCommand->fPassthrough, pRegistration->sczActiveParent, pRegistration->sczAncestors, pLog->sczPath, pCommand->wzCommandLine); + ExitOnFailure(hr, "Failed to recreate resume command-line."); + +LExit: + return hr; +} + + +// internal function definitions + +static void UninitializeRegistrationAction( + __in BURN_DEPENDENT_REGISTRATION_ACTION* pAction + ) +{ + ReleaseStr(pAction->sczDependentProviderKey); + ReleaseStr(pAction->sczBundleId); + memset(pAction, 0, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION)); +} + +static void UninitializeCacheAction( + __in BURN_CACHE_ACTION* pCacheAction + ) +{ + switch (pCacheAction->type) + { + case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT: + ReleaseHandle(pCacheAction->syncpoint.hEvent); + break; + + case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: + ReleaseStr(pCacheAction->bundleLayout.sczExecutableName); + ReleaseStr(pCacheAction->bundleLayout.sczLayoutDirectory); + ReleaseStr(pCacheAction->bundleLayout.sczUnverifiedPath); + break; + + case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER: + ReleaseStr(pCacheAction->resolveContainer.sczUnverifiedPath); + break; + + case BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER: + ReleaseStr(pCacheAction->extractContainer.sczContainerUnverifiedPath); + ReleaseMem(pCacheAction->extractContainer.rgPayloads); + break; + + case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD: + ReleaseStr(pCacheAction->resolvePayload.sczUnverifiedPath); + break; + + case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD: + ReleaseStr(pCacheAction->cachePayload.sczUnverifiedPath); + break; + } +} + +static void ResetPlannedPackageState( + __in BURN_PACKAGE* pPackage + ) +{ + // Reset package state that is a result of planning. + pPackage->expected = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; + pPackage->defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE; + pPackage->requested = BOOTSTRAPPER_REQUEST_STATE_NONE; + pPackage->fAcquire = FALSE; + pPackage->fUncache = FALSE; + pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; + pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + pPackage->providerExecute = BURN_DEPENDENCY_ACTION_NONE; + pPackage->providerRollback = BURN_DEPENDENCY_ACTION_NONE; + pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE; + pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE; + pPackage->fDependencyManagerWasHere = FALSE; + + if (BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.rgFeatures) + { + for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) + { + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; + + pFeature->execute = BOOTSTRAPPER_FEATURE_ACTION_NONE; + pFeature->rollback = BOOTSTRAPPER_FEATURE_ACTION_NONE; + } + } + else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.rgTargetProducts) + { + for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = &pPackage->Msp.rgTargetProducts[i]; + + pTargetProduct->execute = BOOTSTRAPPER_ACTION_STATE_NONE; + pTargetProduct->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + } + } +} + +static HRESULT GetActionDefaultRequestState( + __in BOOTSTRAPPER_ACTION action, + __in BOOL fPermanent, + __in BOOTSTRAPPER_PACKAGE_STATE currentState, + __out BOOTSTRAPPER_REQUEST_STATE* pRequestState + ) +{ + HRESULT hr = S_OK; + + switch (action) + { + case BOOTSTRAPPER_ACTION_CACHE: + switch (currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; + break; + + case BOOTSTRAPPER_PACKAGE_STATE_CACHED: + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; + break; + + default: + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE; + break; + } + break; + + case BOOTSTRAPPER_ACTION_INSTALL: __fallthrough; + case BOOTSTRAPPER_ACTION_UPDATE_REPLACE: __fallthrough; + case BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED: + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; + break; + + case BOOTSTRAPPER_ACTION_REPAIR: + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_REPAIR; + break; + + case BOOTSTRAPPER_ACTION_UNINSTALL: + *pRequestState = fPermanent ? BOOTSTRAPPER_REQUEST_STATE_NONE : BOOTSTRAPPER_REQUEST_STATE_ABSENT; + break; + + case BOOTSTRAPPER_ACTION_MODIFY: + switch (currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT; + break; + + case BOOTSTRAPPER_PACKAGE_STATE_CACHED: + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE; + break; + + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; + break; + + default: + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; + break; + } + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid action state."); + } + +LExit: + return hr; +} + +static HRESULT AddRegistrationAction( + __in BURN_PLAN* pPlan, + __in BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type, + __in_z LPCWSTR wzDependentProviderKey, + __in_z LPCWSTR wzOwnerBundleId + ) +{ + HRESULT hr = S_OK; + BURN_DEPENDENT_REGISTRATION_ACTION_TYPE rollbackType = (BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER == type) ? BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER : BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER; + BURN_DEPENDENT_REGISTRATION_ACTION* pAction = NULL; + + // Create forward registration action. + hr = MemEnsureArraySize((void**)&pPlan->rgRegistrationActions, pPlan->cRegistrationActions + 1, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION), 5); + ExitOnFailure(hr, "Failed to grow plan's array of registration actions."); + + pAction = pPlan->rgRegistrationActions + pPlan->cRegistrationActions; + ++pPlan->cRegistrationActions; + + pAction->type = type; + + hr = StrAllocString(&pAction->sczBundleId, wzOwnerBundleId, 0); + ExitOnFailure(hr, "Failed to copy owner bundle to registration action."); + + hr = StrAllocString(&pAction->sczDependentProviderKey, wzDependentProviderKey, 0); + ExitOnFailure(hr, "Failed to copy dependent provider key to registration action."); + + // Create rollback registration action. + hr = MemEnsureArraySize((void**)&pPlan->rgRollbackRegistrationActions, pPlan->cRollbackRegistrationActions + 1, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION), 5); + ExitOnFailure(hr, "Failed to grow plan's array of rollback registration actions."); + + pAction = pPlan->rgRollbackRegistrationActions + pPlan->cRollbackRegistrationActions; + ++pPlan->cRollbackRegistrationActions; + + pAction->type = rollbackType; + + hr = StrAllocString(&pAction->sczBundleId, wzOwnerBundleId, 0); + ExitOnFailure(hr, "Failed to copy owner bundle to registration action."); + + hr = StrAllocString(&pAction->sczDependentProviderKey, wzDependentProviderKey, 0); + ExitOnFailure(hr, "Failed to copy dependent provider key to rollback registration action."); + +LExit: + return hr; +} + +static HRESULT AddCachePackage( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __out HANDLE* phSyncpointEvent + ) +{ + HRESULT hr = S_OK; + + // If this is an MSI package with slipstream MSPs, ensure the MSPs are cached first. + if (BURN_PACKAGE_TYPE_MSI == pPackage->type && 0 < pPackage->Msi.cSlipstreamMspPackages) + { + hr = AddCacheSlipstreamMsps(pPlan, pPackage); + ExitOnFailure(hr, "Failed to plan slipstream patches for package."); + } + + hr = AddCachePackageHelper(pPlan, pPackage, phSyncpointEvent); + ExitOnFailure(hr, "Failed to plan cache package."); + +LExit: + return hr; +} + +static HRESULT AddCachePackageHelper( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __out HANDLE* phSyncpointEvent + ) +{ + AssertSz(pPackage->sczCacheId && *pPackage->sczCacheId, "AddCachePackageHelper() expects the package to have a cache id."); + + HRESULT hr = S_OK; + BURN_CACHE_ACTION* pCacheAction = NULL; + DWORD dwCheckpoint = 0; + DWORD iPackageStartAction = 0; + + BOOL fPlanned = AlreadyPlannedCachePackage(pPlan, pPackage->sczId, phSyncpointEvent); + if (fPlanned) + { + ExitFunction(); + } + + // Cache checkpoints happen before the package is cached because downloading packages' + // payloads will not roll themselves back the way installation packages rollback on + // failure automatically. + dwCheckpoint = GetNextCheckpointId(); + + hr = AppendCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to append package start action."); + + pCacheAction->type = BURN_CACHE_ACTION_TYPE_CHECKPOINT; + pCacheAction->checkpoint.dwId = dwCheckpoint; + + // Only plan the cache rollback if the package is also going to be uninstalled; + // otherwise, future operations like repair will not be able to locate the cached package. + BOOL fPlanCacheRollback = (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->rollback); + + if (fPlanCacheRollback) + { + hr = AppendRollbackCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to append rollback cache action."); + + pCacheAction->type = BURN_CACHE_ACTION_TYPE_CHECKPOINT; + pCacheAction->checkpoint.dwId = dwCheckpoint; + } + + // Plan the package start. + hr = AppendCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to append package start action."); + + pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_START; + pCacheAction->packageStart.pPackage = pPackage; + + // Remember the index for the package start action (which is now the last in the cache + // actions array) because we have to update this action after processing all the payloads + // and the array may be resized later which would move a pointer around in memory. + iPackageStartAction = pPlan->cCacheActions - 1; + + if (fPlanCacheRollback) + { + // Create a package cache rollback action. + hr = AppendRollbackCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to append rollback cache action."); + + pCacheAction->type = BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE; + pCacheAction->rollbackPackage.pPackage = pPackage; + } + + // Add all the payload cache operations to the plan for this package. + for (DWORD i = 0; i < pPackage->cPayloads; ++i) + { + BURN_PACKAGE_PAYLOAD* pPackagePayload = &pPackage->rgPayloads[i]; + + hr = AppendCacheOrLayoutPayloadAction(pPlan, pPackage, iPackageStartAction, pPackagePayload->pPayload, pPackagePayload->fCached, NULL); + ExitOnFailure(hr, "Failed to append payload cache action."); + + Assert(BURN_CACHE_ACTION_TYPE_PACKAGE_START == pPlan->rgCacheActions[iPackageStartAction].type); + ++pPlan->rgCacheActions[iPackageStartAction].packageStart.cCachePayloads; + pPlan->rgCacheActions[iPackageStartAction].packageStart.qwCachePayloadSizeTotal += pPackagePayload->pPayload->qwFileSize; + } + + // Create package stop action. + hr = AppendCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to append cache action."); + + pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_STOP; + pCacheAction->packageStop.pPackage = pPackage; + + // Update the start action with the location of the complete action. + pPlan->rgCacheActions[iPackageStartAction].packageStart.iPackageCompleteAction = pPlan->cCacheActions - 1; + + // Create syncpoint action. + hr = AppendCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to append cache action."); + + pCacheAction->type = BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT; + pCacheAction->syncpoint.hEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL); + ExitOnNullWithLastError(pCacheAction->syncpoint.hEvent, hr, "Failed to create syncpoint event."); + + *phSyncpointEvent = pCacheAction->syncpoint.hEvent; + + ++pPlan->cOverallProgressTicksTotal; + + // If the package was not already fully cached then note that we planned the cache here. Otherwise, we only + // did cache operations to verify the cache is valid so we did not plan the acquisition of the package. + pPackage->fAcquire = (BURN_CACHE_STATE_COMPLETE != pPackage->cache); + +LExit: + return hr; +} + +static HRESULT AddCacheSlipstreamMsps( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + HANDLE hIgnored = NULL; + + AssertSz(BURN_PACKAGE_TYPE_MSI == pPackage->type, "Only MSI packages can have slipstream patches."); + + for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) + { + BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[i]; + AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches."); + + hr = AddCachePackageHelper(pPlan, pMspPackage, &hIgnored); + ExitOnFailure(hr, "Failed to plan slipstream MSP: %ls", pMspPackage->sczId); + } + +LExit: + return hr; +} + +static BOOL AlreadyPlannedCachePackage( + __in BURN_PLAN* pPlan, + __in_z LPCWSTR wzPackageId, + __out HANDLE* phSyncpointEvent + ) +{ + BOOL fPlanned = FALSE; + + for (DWORD iCacheAction = 0; iCacheAction < pPlan->cCacheActions; ++iCacheAction) + { + BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + iCacheAction; + + if (BURN_CACHE_ACTION_TYPE_PACKAGE_STOP == pCacheAction->type) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pCacheAction->packageStop.pPackage->sczId, -1, wzPackageId, -1)) + { + if (iCacheAction + 1 < pPlan->cCacheActions && BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT == pPlan->rgCacheActions[iCacheAction + 1].type) + { + *phSyncpointEvent = pPlan->rgCacheActions[iCacheAction + 1].syncpoint.hEvent; + } + + fPlanned = TRUE; + break; + } + } + } + + return fPlanned; +} + +static DWORD GetNextCheckpointId() +{ + static DWORD dwCounter = 0; + return ++dwCounter; +} + +static HRESULT AppendCacheAction( + __in BURN_PLAN* pPlan, + __out BURN_CACHE_ACTION** ppCacheAction + ) +{ + HRESULT hr = S_OK; + + hr = MemEnsureArraySize(reinterpret_cast(&pPlan->rgCacheActions), pPlan->cCacheActions + 1, sizeof(BURN_CACHE_ACTION), 5); + ExitOnFailure(hr, "Failed to grow plan's array of cache actions."); + + *ppCacheAction = pPlan->rgCacheActions + pPlan->cCacheActions; + ++pPlan->cCacheActions; + +LExit: + return hr; +} + +static HRESULT AppendRollbackCacheAction( + __in BURN_PLAN* pPlan, + __out BURN_CACHE_ACTION** ppCacheAction + ) +{ + HRESULT hr = S_OK; + + hr = MemEnsureArraySize(reinterpret_cast(&pPlan->rgRollbackCacheActions), pPlan->cRollbackCacheActions + 1, sizeof(BURN_CACHE_ACTION), 5); + ExitOnFailure(hr, "Failed to grow plan's array of rollback cache actions."); + + *ppCacheAction = pPlan->rgRollbackCacheActions + pPlan->cRollbackCacheActions; + ++pPlan->cRollbackCacheActions; + +LExit: + return hr; +} + +static HRESULT AppendLayoutContainerAction( + __in BURN_PLAN* pPlan, + __in_opt BURN_PACKAGE* pPackage, + __in DWORD iPackageStartAction, + __in BURN_CONTAINER* pContainer, + __in BOOL fContainerCached, + __in_z LPCWSTR wzLayoutDirectory + ) +{ + HRESULT hr = S_OK; + BURN_CACHE_ACTION* pAcquireAction = NULL; + DWORD iAcquireAction = BURN_PLAN_INVALID_ACTION_INDEX; + LPWSTR sczContainerWorkingPath = NULL; + BURN_CACHE_ACTION* pCacheAction = NULL; + BURN_CACHE_CONTAINER_PROGRESS* pContainerProgress = NULL; + + // No need to do anything if the container is already cached or is attached to the bundle (since the + // bundle itself will already have a layout action). + if (fContainerCached || pContainer->fAttached) + { + ExitFunction(); + } + + // Ensure the container is being acquired. If it is, then some earlier package already planned the layout of this container so + // don't do it again. Otherwise, plan away! + if (!FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pPlan, pContainer, 0, iPackageStartAction, NULL, NULL)) + { + hr = AddAcquireContainer(pPlan, pContainer, &pAcquireAction, &iAcquireAction); + ExitOnFailure(hr, "Failed to append acquire container action for layout to plan."); + + Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pAcquireAction->type); + + // Create the layout container action. + hr = StrAllocString(&sczContainerWorkingPath, pAcquireAction->resolveContainer.sczUnverifiedPath, 0); + ExitOnFailure(hr, "Failed to copy container working path for layout."); + + hr = AppendCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to append cache action to cache payload."); + + hr = CreateContainerProgress(pPlan, pContainer, &pContainerProgress); + ExitOnFailure(hr, "Failed to create container progress."); + + hr = StrAllocString(&pCacheAction->layoutContainer.sczLayoutDirectory, wzLayoutDirectory, 0); + ExitOnFailure(hr, "Failed to copy layout directory into plan."); + + pCacheAction->type = BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER; + pCacheAction->layoutContainer.pPackage = pPackage; + pCacheAction->layoutContainer.pContainer = pContainer; + pCacheAction->layoutContainer.iProgress = pContainerProgress->iIndex; + pCacheAction->layoutContainer.fMove = TRUE; + pCacheAction->layoutContainer.iTryAgainAction = iAcquireAction; + pCacheAction->layoutContainer.sczUnverifiedPath = sczContainerWorkingPath; + sczContainerWorkingPath = NULL; + } + +LExit: + ReleaseNullStr(sczContainerWorkingPath); + + return hr; +} + +static HRESULT AppendCacheOrLayoutPayloadAction( + __in BURN_PLAN* pPlan, + __in_opt BURN_PACKAGE* pPackage, + __in DWORD iPackageStartAction, + __in BURN_PAYLOAD* pPayload, + __in BOOL fPayloadCached, + __in_z_opt LPCWSTR wzLayoutDirectory + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPayloadWorkingPath = NULL; + BURN_CACHE_ACTION* pCacheAction = NULL; + DWORD iTryAgainAction = BURN_PLAN_INVALID_ACTION_INDEX; + BURN_CACHE_PAYLOAD_PROGRESS* pPayloadProgress = NULL; + + hr = CacheCalculatePayloadWorkingPath(pPlan->wzBundleId, pPayload, &sczPayloadWorkingPath); + ExitOnFailure(hr, "Failed to calculate unverified path for payload."); + + // If the payload is in a container, ensure the container is being acquired + // then add this payload to the list of payloads to extract already in the plan. + if (pPayload->pContainer) + { + BURN_CACHE_ACTION* pPreviousPackageExtractAction = NULL; + BURN_CACHE_ACTION* pThisPackageExtractAction = NULL; + + // If the payload is not already cached, then add it to the first extract container action in the plan. Extracting + // all the needed payloads from the container in a single pass is the most efficient way to extract files from + // containers. If there is not an extract container action before our package, that is okay because we'll create + // an extract container action for our package in a second anyway. + if (!fPayloadCached) + { + if (FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, pPlan, pPayload->pContainer, 0, iPackageStartAction, &pPreviousPackageExtractAction, NULL)) + { + hr = AddExtractPayload(pPreviousPackageExtractAction, pPackage, pPayload, sczPayloadWorkingPath); + ExitOnFailure(hr, "Failed to add extract payload action to previous package."); + } + } + + // If there is already an extract container action after our package start action then try to find an acquire action + // that is matched with it. If there is an acquire action then that is our "try again" action, otherwise we'll use the existing + // extract action as the "try again" action. + if (FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, pPlan, pPayload->pContainer, iPackageStartAction, BURN_PLAN_INVALID_ACTION_INDEX, &pThisPackageExtractAction, &iTryAgainAction)) + { + DWORD iAcquireAction = BURN_PLAN_INVALID_ACTION_INDEX; + if (FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pPlan, pPayload->pContainer, iPackageStartAction, iTryAgainAction, NULL, &iAcquireAction)) + { + iTryAgainAction = iAcquireAction; + } + } + else // did not find an extract container action for our package. + { + // Ensure there is an extract action (and maybe an acquire action) for every package that has payloads. The + // acquire and extract action will be skipped if the payload is already cached or was added to a previous + // package's extract action above. + // + // These actions always exist (even when they are likely to be skipped) so that "try again" will not + // jump so far back in the plan that you end up extracting payloads for other packages. With these actions + // "try again" will only retry the extraction for payloads in this package. + hr = CreateContainerAcquireAndExtractAction(pPlan, pPayload->pContainer, iPackageStartAction, pPreviousPackageExtractAction ? TRUE : fPayloadCached, &pThisPackageExtractAction, &iTryAgainAction); + ExitOnFailure(hr, "Failed to create container extract action."); + } + ExitOnFailure(hr, "Failed while searching for package's container extract action."); + + // We *always* add the payload to this package's extract action even though the extract action + // is probably being skipped until retry if there was a previous package extract action. + hr = AddExtractPayload(pThisPackageExtractAction, pPackage, pPayload, sczPayloadWorkingPath); + ExitOnFailure(hr, "Failed to add extract payload to current package."); + } + else // add a payload acquire action to the plan. + { + // Try to find an existing acquire action for this payload. If one is not found, + // we'll create it. At the same time we will change any cache/layout payload actions + // that would "MOVE" the file to "COPY" so that our new cache/layout action below + // can do the move. + pCacheAction = ProcessSharedPayload(pPlan, pPayload); + if (!pCacheAction) + { + hr = AppendCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to append cache action to acquire payload."); + + pCacheAction->type = BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD; + pCacheAction->fSkipUntilRetried = fPayloadCached; + pCacheAction->resolvePayload.pPackage = pPackage; + pCacheAction->resolvePayload.pPayload = pPayload; + hr = StrAllocString(&pCacheAction->resolvePayload.sczUnverifiedPath, sczPayloadWorkingPath, 0); + ExitOnFailure(hr, "Failed to copy unverified path for payload to acquire."); + } + + iTryAgainAction = static_cast(pCacheAction - pPlan->rgCacheActions); + pCacheAction = NULL; + } + + Assert(BURN_PLAN_INVALID_ACTION_INDEX != iTryAgainAction); + Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pPlan->rgCacheActions[iTryAgainAction].type || + BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pPlan->rgCacheActions[iTryAgainAction].type || + BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD == pPlan->rgCacheActions[iTryAgainAction].type); + + hr = AppendCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to append cache action to cache payload."); + + hr = CreatePayloadProgress(pPlan, pPayload, &pPayloadProgress); + ExitOnFailure(hr, "Failed to create payload progress."); + + if (!wzLayoutDirectory) + { + pCacheAction->type = BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD; + pCacheAction->cachePayload.pPackage = pPackage; + pCacheAction->cachePayload.pPayload = pPayload; + pCacheAction->cachePayload.iProgress = pPayloadProgress->iIndex; + pCacheAction->cachePayload.fMove = TRUE; + pCacheAction->cachePayload.iTryAgainAction = iTryAgainAction; + pCacheAction->cachePayload.sczUnverifiedPath = sczPayloadWorkingPath; + sczPayloadWorkingPath = NULL; + } + else + { + hr = StrAllocString(&pCacheAction->layoutPayload.sczLayoutDirectory, wzLayoutDirectory, 0); + ExitOnFailure(hr, "Failed to copy layout directory into plan."); + + pCacheAction->type = BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD; + pCacheAction->layoutPayload.pPackage = pPackage; + pCacheAction->layoutPayload.pPayload = pPayload; + pCacheAction->layoutPayload.iProgress = pPayloadProgress->iIndex; + pCacheAction->layoutPayload.fMove = TRUE; + pCacheAction->layoutPayload.iTryAgainAction = iTryAgainAction; + pCacheAction->layoutPayload.sczUnverifiedPath = sczPayloadWorkingPath; + sczPayloadWorkingPath = NULL; + } + + pCacheAction = NULL; + +LExit: + ReleaseStr(sczPayloadWorkingPath); + + return hr; +} + +static BOOL FindContainerCacheAction( + __in BURN_CACHE_ACTION_TYPE type, + __in BURN_PLAN* pPlan, + __in BURN_CONTAINER* pContainer, + __in DWORD iSearchStart, + __in DWORD iSearchEnd, + __out_opt BURN_CACHE_ACTION** ppCacheAction, + __out_opt DWORD* piCacheAction + ) +{ + BOOL fFound = FALSE; // assume we won't find what we are looking for. + + Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == type || BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == type); + + iSearchStart = (BURN_PLAN_INVALID_ACTION_INDEX == iSearchStart) ? 0 : iSearchStart; + iSearchEnd = (BURN_PLAN_INVALID_ACTION_INDEX == iSearchEnd) ? pPlan->cCacheActions : iSearchEnd; + + for (DWORD iSearch = iSearchStart; iSearch < iSearchEnd; ++iSearch) + { + BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + iSearch; + if (pCacheAction->type == type && + ((BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pCacheAction->type && pCacheAction->resolveContainer.pContainer == pContainer) || + (BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pCacheAction->type && pCacheAction->extractContainer.pContainer == pContainer))) + { + if (ppCacheAction) + { + *ppCacheAction = pCacheAction; + } + + if (piCacheAction) + { + *piCacheAction = iSearch; + } + + fFound = TRUE; + break; + } + } + + return fFound; +} + +static HRESULT CreateContainerAcquireAndExtractAction( + __in BURN_PLAN* pPlan, + __in BURN_CONTAINER* pContainer, + __in DWORD iPackageStartAction, + __in BOOL fPayloadCached, + __out BURN_CACHE_ACTION** ppContainerExtractAction, + __out DWORD* piContainerTryAgainAction + ) +{ + HRESULT hr = S_OK; + DWORD iAcquireAction = BURN_PLAN_INVALID_ACTION_INDEX; + BURN_CACHE_ACTION* pContainerExtractAction = NULL; + DWORD iExtractAction = BURN_PLAN_INVALID_ACTION_INDEX; + DWORD iTryAgainAction = BURN_PLAN_INVALID_ACTION_INDEX; + LPWSTR sczContainerWorkingPath = NULL; + + // If the container is actually attached to the executable then we will not need an acquire + // container action. + if (!pContainer->fActuallyAttached) + { + BURN_CACHE_ACTION* pAcquireContainerAction = NULL; + + // If there is no plan to acquire the container then add acquire action since we + // can't extract stuff out of a container until we acquire the container. + if (!FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pPlan, pContainer, iPackageStartAction, BURN_PLAN_INVALID_ACTION_INDEX, &pAcquireContainerAction, &iAcquireAction)) + { + hr = AddAcquireContainer(pPlan, pContainer, &pAcquireContainerAction, &iAcquireAction); + ExitOnFailure(hr, "Failed to append acquire container action to plan."); + + pAcquireContainerAction->fSkipUntilRetried = TRUE; // we'll start by assuming the acquire is not necessary and the fPayloadCached below will set us straight if wrong. + } + + Assert(BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction); + Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pAcquireContainerAction->type); + Assert(pContainer == pAcquireContainerAction->resolveContainer.pContainer); + } + + Assert((pContainer->fActuallyAttached && BURN_PLAN_INVALID_ACTION_INDEX == iAcquireAction) || + (!pContainer->fActuallyAttached && BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction)); + + // If we do not find an action for extracting payloads from this container, create it now. + if (!FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, pPlan, pContainer, (BURN_PLAN_INVALID_ACTION_INDEX == iAcquireAction) ? iPackageStartAction : iAcquireAction, BURN_PLAN_INVALID_ACTION_INDEX, &pContainerExtractAction, &iExtractAction)) + { + // Attached containers that are actually attached use the executable path for their working path. + if (pContainer->fActuallyAttached) + { + Assert(BURN_PLAN_INVALID_ACTION_INDEX == iAcquireAction); + + hr = PathForCurrentProcess(&sczContainerWorkingPath, NULL); + ExitOnFailure(hr, "Failed to get path for executing module as attached container working path."); + } + else // use the acquired working path as the location of the container. + { + Assert(BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction); + + hr = StrAllocString(&sczContainerWorkingPath, pPlan->rgCacheActions[iAcquireAction].resolveContainer.sczUnverifiedPath, 0); + ExitOnFailure(hr, "Failed to copy container unverified path for cache action to extract container."); + } + + hr = AppendCacheAction(pPlan, &pContainerExtractAction); + ExitOnFailure(hr, "Failed to append cache action to extract payloads from container."); + + iExtractAction = pPlan->cCacheActions - 1; + + pContainerExtractAction->type = BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER; + pContainerExtractAction->fSkipUntilRetried = pContainer->fActuallyAttached; // assume we can skip the extract engine when the container is already attached and the fPayloadCached below will set us straight if wrong. + pContainerExtractAction->extractContainer.pContainer = pContainer; + pContainerExtractAction->extractContainer.iSkipUntilAcquiredByAction = iAcquireAction; + pContainerExtractAction->extractContainer.sczContainerUnverifiedPath = sczContainerWorkingPath; + sczContainerWorkingPath = NULL; + } + + Assert(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pContainerExtractAction->type); + Assert(BURN_PLAN_INVALID_ACTION_INDEX != iExtractAction); + + // If there is an acquire action, that is our try again action. Otherwise, we'll use the extract action. + iTryAgainAction = (BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction) ? iAcquireAction : iExtractAction; + + // If the try again action thinks it can be skipped but the payload is not cached, + // ensure the action will not be skipped. + BURN_CACHE_ACTION* pTryAgainAction = pPlan->rgCacheActions + iTryAgainAction; + Assert((BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pTryAgainAction->type && pContainer == pTryAgainAction->resolveContainer.pContainer) || + (BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pTryAgainAction->type && pContainer == pTryAgainAction->extractContainer.pContainer)); + if (pTryAgainAction->fSkipUntilRetried && !fPayloadCached) + { + pTryAgainAction->fSkipUntilRetried = FALSE; + } + + *ppContainerExtractAction = pContainerExtractAction; + *piContainerTryAgainAction = iTryAgainAction; + +LExit: + ReleaseStr(sczContainerWorkingPath); + + return hr; +} + +static HRESULT AddAcquireContainer( + __in BURN_PLAN* pPlan, + __in BURN_CONTAINER* pContainer, + __out_opt BURN_CACHE_ACTION** ppCacheAction, + __out_opt DWORD* piCacheAction + ) +{ + HRESULT hr = S_OK; + LPWSTR sczContainerWorkingPath = NULL; + BURN_CACHE_ACTION* pAcquireContainerAction = NULL; + BURN_CACHE_CONTAINER_PROGRESS* pContainerProgress = NULL; + + hr = CacheCalculateContainerWorkingPath(pPlan->wzBundleId, pContainer, &sczContainerWorkingPath); + ExitOnFailure(hr, "Failed to calculate unverified path for container."); + + hr = AppendCacheAction(pPlan, &pAcquireContainerAction); + ExitOnFailure(hr, "Failed to append acquire container action to plan."); + + hr = CreateContainerProgress(pPlan, pContainer, &pContainerProgress); + ExitOnFailure(hr, "Failed to create container progress."); + + pAcquireContainerAction->type = BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER; + pAcquireContainerAction->resolveContainer.pContainer = pContainer; + pAcquireContainerAction->resolveContainer.iProgress = pContainerProgress->iIndex; + pAcquireContainerAction->resolveContainer.sczUnverifiedPath = sczContainerWorkingPath; + sczContainerWorkingPath = NULL; + + if (ppCacheAction) + { + *ppCacheAction = pAcquireContainerAction; + } + + if (piCacheAction) + { + *piCacheAction = pPlan->cCacheActions - 1; + } + +LExit: + ReleaseStr(sczContainerWorkingPath); + + return hr; +} + +static HRESULT AddExtractPayload( + __in BURN_CACHE_ACTION* pCacheAction, + __in_opt BURN_PACKAGE* pPackage, + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzPayloadWorkingPath + ) +{ + HRESULT hr = S_OK; + + Assert(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pCacheAction->type); + + hr = MemEnsureArraySize(reinterpret_cast(&pCacheAction->extractContainer.rgPayloads), pCacheAction->extractContainer.cPayloads + 1, sizeof(BURN_EXTRACT_PAYLOAD), 5); + ExitOnFailure(hr, "Failed to grow list of payloads to extract from container."); + + BURN_EXTRACT_PAYLOAD* pExtractPayload = pCacheAction->extractContainer.rgPayloads + pCacheAction->extractContainer.cPayloads; + pExtractPayload->pPackage = pPackage; + pExtractPayload->pPayload = pPayload; + hr = StrAllocString(&pExtractPayload->sczUnverifiedPath, wzPayloadWorkingPath, 0); + ExitOnFailure(hr, "Failed to copy unverified path for payload to extract."); + ++pCacheAction->extractContainer.cPayloads; + +LExit: + return hr; +} + +static BURN_CACHE_ACTION* ProcessSharedPayload( + __in BURN_PLAN* pPlan, + __in BURN_PAYLOAD* pPayload + ) +{ + BURN_CACHE_ACTION* pAcquireAction = NULL; +#ifdef DEBUG + DWORD cMove = 0; +#endif + + for (DWORD i = 0; i < pPlan->cCacheActions; ++i) + { + BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + i; + + if (BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD == pCacheAction->type && + pCacheAction->resolvePayload.pPayload == pPayload) + { + AssertSz(!pAcquireAction, "There should be at most one acquire cache action per payload."); + pAcquireAction = pCacheAction; + } + else if (BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD == pCacheAction->type && + pCacheAction->cachePayload.pPayload == pPayload && + pCacheAction->cachePayload.fMove) + { + // Since we found a shared payload, change its operation from MOVE to COPY. + pCacheAction->cachePayload.fMove = FALSE; + + AssertSz(1 == ++cMove, "Shared payload should be moved once and only once."); +#ifndef DEBUG + break; +#endif + } + else if (BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD == pCacheAction->type && + pCacheAction->layoutPayload.pPayload == pPayload && + pCacheAction->layoutPayload.fMove) + { + // Since we found a shared payload, change its operation from MOVE to COPY if necessary + pCacheAction->layoutPayload.fMove = FALSE; + + AssertSz(1 == ++cMove, "Shared payload should be moved once and only once."); +#ifndef DEBUG + break; +#endif + } + } + + return pAcquireAction; +} + +static HRESULT RemoveUnnecessaryActions( + __in BOOL fExecute, + __in BURN_EXECUTE_ACTION* rgActions, + __in DWORD cActions + ) +{ + HRESULT hr = S_OK; + LPCSTR szExecuteOrRollback = fExecute ? "execute" : "rollback"; + + for (DWORD i = 0; i < cActions; ++i) + { + BURN_EXECUTE_ACTION* pAction = rgActions + i; + + // If this MSP targets a package in the chain, check the target's execute state + // to see if this patch should be skipped. + if (BURN_EXECUTE_ACTION_TYPE_MSP_TARGET == pAction->type && pAction->mspTarget.pChainedTargetPackage) + { + BOOTSTRAPPER_ACTION_STATE chainedTargetPackageAction = fExecute ? pAction->mspTarget.pChainedTargetPackage->execute : pAction->mspTarget.pChainedTargetPackage->rollback; + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == chainedTargetPackageAction) + { + LogId(REPORT_STANDARD, MSG_PLAN_SKIP_PATCH_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback); + pAction->fDeleted = TRUE; + } + else if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < chainedTargetPackageAction && pAction->mspTarget.fSlipstream && BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pAction->mspTarget.action) + { + // If the slipstream target is being installed or upgraded (not uninstalled or repaired) then we will slipstream so skip + // this action to install the patch standalone. Also, if the slipstream target is being repaired and the patch is being + // repaired, skip this operation since it will be redundant. + // + // The primary goal here is to ensure that a slipstream patch that is yet not installed is installed even if the MSI + // is already on the machine. The slipstream must be installed standalone if the MSI is being repaired. + if (BOOTSTRAPPER_ACTION_STATE_REPAIR != chainedTargetPackageAction || BOOTSTRAPPER_ACTION_STATE_REPAIR == pAction->mspTarget.action) + { + LogId(REPORT_STANDARD, MSG_PLAN_SKIP_SLIPSTREAM_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback); + pAction->fDeleted = TRUE; + } + } + } + } + + return hr; +} + +static HRESULT FinalizeSlipstreamPatchActions( + __in BOOL fExecute, + __in BURN_EXECUTE_ACTION* rgActions, + __in DWORD cActions + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < cActions; ++i) + { + BURN_EXECUTE_ACTION* pAction = rgActions + i; + + // If this MSI package contains slipstream patches store the slipstream actions. + if (BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE == pAction->type && pAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages) + { + BURN_PACKAGE* pPackage = pAction->msiPackage.pPackage; + + // By default all slipstream actions will be initialized to "no action" (aka: 0). + pAction->msiPackage.rgSlipstreamPatches = (BOOTSTRAPPER_ACTION_STATE*)MemAlloc(sizeof(BOOTSTRAPPER_ACTION_STATE) * pPackage->Msi.cSlipstreamMspPackages, TRUE); + ExitOnNull(pAction->msiPackage.rgSlipstreamPatches, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch actions."); + + // If we are uninstalling or repairing the MSI, we must ignore all the slipstream patches because they cannot + // be applied right now. + if (BOOTSTRAPPER_ACTION_STATE_REPAIR != pAction->msiPackage.action && BOOTSTRAPPER_ACTION_STATE_UNINSTALL != pAction->msiPackage.action) + { + for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) + { + BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[j]; + AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches."); + + pAction->msiPackage.rgSlipstreamPatches[j] = fExecute ? pMspPackage->execute : pMspPackage->rollback; + for (DWORD k = 0; k < pMspPackage->Msp.cTargetProductCodes; ++k) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pMspPackage->Msp.rgTargetProducts + k; + if (pPackage == pTargetProduct->pChainedTargetPackage) + { + pAction->msiPackage.rgSlipstreamPatches[j] = fExecute ? pTargetProduct->execute : pTargetProduct->rollback; + break; + } + } + } + } + } + } + +LExit: + return hr; +} + +static HRESULT PlanDependencyActions( + __in BOOL fBundlePerMachine, + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + + hr = DependencyPlanPackageBegin(fBundlePerMachine, pPackage, pPlan); + ExitOnFailure(hr, "Failed to begin plan dependency actions for package: %ls", pPackage->sczId); + + hr = DependencyPlanPackage(NULL, pPackage, pPlan); + ExitOnFailure(hr, "Failed to plan package dependency actions."); + + hr = DependencyPlanPackageComplete(pPackage, pPlan); + ExitOnFailure(hr, "Failed to complete plan dependency actions for package: %ls", pPackage->sczId); + +LExit: + return hr; +} + +static HRESULT CalculateExecuteActions( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BURN_PACKAGE* pPackage, + __in BURN_VARIABLES* pVariables, + __out_opt BOOL* pfBARequestedCache + ) +{ + HRESULT hr = S_OK; + + // Calculate execute actions. + switch (pPackage->type) + { + case BURN_PACKAGE_TYPE_EXE: + hr = ExeEnginePlanCalculatePackage(pPackage, pfBARequestedCache); + break; + + case BURN_PACKAGE_TYPE_MSI: + hr = MsiEnginePlanCalculatePackage(pPackage, pVariables, pUserExperience, pfBARequestedCache); + break; + + case BURN_PACKAGE_TYPE_MSP: + hr = MspEnginePlanCalculatePackage(pPackage, pUserExperience, pfBARequestedCache); + break; + + case BURN_PACKAGE_TYPE_MSU: + hr = MsuEnginePlanCalculatePackage(pPackage, pfBARequestedCache); + break; + + default: + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Invalid package type."); + } + +LExit: + return hr; +} + +static BOOL NeedsCache( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage + ) +{ + // All packages that have cacheType set to always should be cached if the bundle is going to be present. + if (BURN_CACHE_TYPE_ALWAYS == pPackage->cacheType && BOOTSTRAPPER_ACTION_INSTALL <= pPlan->action) + { + return TRUE; + } + else if (BURN_PACKAGE_TYPE_EXE == pPackage->type) // Exe packages require the package for all operations (even uninstall). + { + return BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute; + } + else // The other package types can uninstall without the original package. + { + return BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pPackage->execute; + } +} + +static HRESULT CreateContainerProgress( + __in BURN_PLAN* pPlan, + __in BURN_CONTAINER* pContainer, + __out BURN_CACHE_CONTAINER_PROGRESS** ppContainerProgress + ) +{ + HRESULT hr = S_OK; + BURN_CACHE_CONTAINER_PROGRESS* pContainerProgress = NULL; + + hr = MemEnsureArraySize(reinterpret_cast(&pPlan->rgContainerProgress), pPlan->cContainerProgress + 1, sizeof(BURN_CACHE_CONTAINER_PROGRESS), 5); + ExitOnFailure(hr, "Failed to grow container progress list."); + + if (!pPlan->shContainerProgress) + { + hr = DictCreateWithEmbeddedKey(&pPlan->shContainerProgress, 5, reinterpret_cast(&pPlan->rgContainerProgress), offsetof(BURN_CACHE_CONTAINER_PROGRESS, wzId), DICT_FLAG_NONE); + ExitOnFailure(hr, "Failed to create container progress dictionary."); + } + + hr = DictGetValue(pPlan->shContainerProgress, pContainer->sczId, reinterpret_cast(&pContainerProgress)); + if (E_NOTFOUND == hr) + { + pContainerProgress = &pPlan->rgContainerProgress[pPlan->cContainerProgress]; + pContainerProgress->iIndex = pPlan->cContainerProgress; + pContainerProgress->pContainer = pContainer; + pContainerProgress->wzId = pContainer->sczId; + + hr = DictAddValue(pPlan->shContainerProgress, pContainerProgress); + ExitOnFailure(hr, "Failed to add \"%ls\" to the container progress dictionary.", pContainerProgress->wzId); + + ++pPlan->cContainerProgress; + pPlan->qwCacheSizeTotal += pContainer->qwFileSize; + } + ExitOnFailure(hr, "Failed to retrieve \"%ls\" from the container progress dictionary.", pContainer->sczId); + + *ppContainerProgress = pContainerProgress; + +LExit: + return hr; +} + +static HRESULT CreatePayloadProgress( + __in BURN_PLAN* pPlan, + __in BURN_PAYLOAD* pPayload, + __out BURN_CACHE_PAYLOAD_PROGRESS** ppPayloadProgress + ) +{ + HRESULT hr = S_OK; + BURN_CACHE_PAYLOAD_PROGRESS* pPayloadProgress = NULL; + + hr = MemEnsureArraySize(reinterpret_cast(&pPlan->rgPayloadProgress), pPlan->cPayloadProgress + 1, sizeof(BURN_CACHE_PAYLOAD_PROGRESS), 5); + ExitOnFailure(hr, "Failed to grow payload progress list."); + + if (!pPlan->shPayloadProgress) + { + hr = DictCreateWithEmbeddedKey(&pPlan->shPayloadProgress, 5, reinterpret_cast(&pPlan->rgPayloadProgress), offsetof(BURN_CACHE_PAYLOAD_PROGRESS, wzId), DICT_FLAG_NONE); + ExitOnFailure(hr, "Failed to create payload progress dictionary."); + } + + hr = DictGetValue(pPlan->shPayloadProgress, pPayload->sczKey, reinterpret_cast(&pPayloadProgress)); + if (E_NOTFOUND == hr) + { + pPayloadProgress = &pPlan->rgPayloadProgress[pPlan->cPayloadProgress]; + pPayloadProgress->iIndex = pPlan->cPayloadProgress; + pPayloadProgress->pPayload = pPayload; + pPayloadProgress->wzId = pPayload->sczKey; + + hr = DictAddValue(pPlan->shPayloadProgress, pPayloadProgress); + ExitOnFailure(hr, "Failed to add \"%ls\" to the payload progress dictionary.", pPayloadProgress->wzId); + + ++pPlan->cPayloadProgress; + pPlan->qwCacheSizeTotal += pPayload->qwFileSize; + } + ExitOnFailure(hr, "Failed to retrieve \"%ls\" from the payload progress dictionary.", pPayload->sczKey); + + *ppPayloadProgress = pPayloadProgress; + +LExit: + return hr; +} + + +#ifdef DEBUG + +static void CacheActionLog( + __in DWORD iAction, + __in BURN_CACHE_ACTION* pAction, + __in BOOL fRollback + ) +{ + LPCWSTR wzBase = fRollback ? L" Rollback cache" : L" Cache"; + switch (pAction->type) + { + case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: ACQUIRE_CONTAINER id: %ls, source path: %ls, working path: %ls, skip until retried: %hs", wzBase, iAction, pAction->resolveContainer.pContainer->sczId, pAction->resolveContainer.pContainer->sczSourcePath, pAction->resolveContainer.sczUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried)); + break; + + case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: ACQUIRE_PAYLOAD package id: %ls, payload id: %ls, source path: %ls, working path: %ls, skip until retried: %hs", wzBase, iAction, pAction->resolvePayload.pPackage ? pAction->resolvePayload.pPackage->sczId : L"", pAction->resolvePayload.pPayload->sczKey, pAction->resolvePayload.pPayload->sczSourcePath, pAction->resolvePayload.sczUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried)); + break; + + case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: CACHE_PAYLOAD package id: %ls, payload id: %ls, working path: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->cachePayload.pPackage->sczId, pAction->cachePayload.pPayload->sczKey, pAction->cachePayload.sczUnverifiedPath, pAction->cachePayload.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->cachePayload.iTryAgainAction); + break; + + case BURN_CACHE_ACTION_TYPE_CHECKPOINT: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: CHECKPOINT id: %u", wzBase, iAction, pAction->checkpoint.dwId); + break; + + case BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: EXTRACT_CONTAINER id: %ls, working path: %ls, skip until retried: %hs, skip until acquired by action: %u", wzBase, iAction, pAction->extractContainer.pContainer->sczId, pAction->extractContainer.sczContainerUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried), pAction->extractContainer.iSkipUntilAcquiredByAction); + for (DWORD j = 0; j < pAction->extractContainer.cPayloads; j++) + { + LogStringLine(REPORT_STANDARD, " extract package id: %ls, payload id: %ls, working path: %ls", pAction->extractContainer.rgPayloads[j].pPackage->sczId, pAction->extractContainer.rgPayloads[j].pPayload->sczKey, pAction->extractContainer.rgPayloads[j].sczUnverifiedPath); + } + break; + + case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: LAYOUT_BUNDLE working path: %ls, layout directory: %ls, exe name: %ls, skip until retried: %hs", wzBase, iAction, pAction->bundleLayout.sczUnverifiedPath, pAction->bundleLayout.sczLayoutDirectory, pAction->bundleLayout.sczExecutableName, LoggingBoolToString(pAction->fSkipUntilRetried)); + break; + + case BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: LAYOUT_CONTAINER package id: %ls, container id: %ls, working path: %ls, layout directory: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->layoutContainer.pPackage ? pAction->layoutContainer.pPackage->sczId : L"", pAction->layoutContainer.pContainer->sczId, pAction->layoutContainer.sczUnverifiedPath, pAction->layoutContainer.sczLayoutDirectory, pAction->layoutContainer.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->layoutContainer.iTryAgainAction); + break; + + case BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: LAYOUT_PAYLOAD package id: %ls, payload id: %ls, working path: %ls, layout directory: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->layoutPayload.pPackage ? pAction->layoutPayload.pPackage->sczId : L"", pAction->layoutPayload.pPayload->sczKey, pAction->layoutPayload.sczUnverifiedPath, pAction->layoutPayload.sczLayoutDirectory, pAction->layoutPayload.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->layoutPayload.iTryAgainAction); + break; + + case BURN_CACHE_ACTION_TYPE_PACKAGE_START: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_START id: %ls, plan index for skip: %u, payloads to cache: %u, bytes to cache: %llu, skip until retried: %hs", wzBase, iAction, pAction->packageStart.pPackage->sczId, pAction->packageStart.iPackageCompleteAction, pAction->packageStart.cCachePayloads, pAction->packageStart.qwCachePayloadSizeTotal, LoggingBoolToString(pAction->fSkipUntilRetried)); + break; + + case BURN_CACHE_ACTION_TYPE_PACKAGE_STOP: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_STOP id: %ls, skip until retried: %hs", wzBase, iAction, pAction->packageStop.pPackage->sczId, LoggingBoolToString(pAction->fSkipUntilRetried)); + break; + + case BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: ROLLBACK_PACKAGE id: %ls, skip until retried: %hs", wzBase, iAction, pAction->rollbackPackage.pPackage->sczId, LoggingBoolToString(pAction->fSkipUntilRetried)); + break; + + case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: SIGNAL_SYNCPOINT event handle: 0x%x, skip until retried: %hs", wzBase, iAction, pAction->syncpoint.hEvent, LoggingBoolToString(pAction->fSkipUntilRetried)); + break; + + case BURN_CACHE_ACTION_TYPE_TRANSACTION_BOUNDARY: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: TRANSACTION_BOUNDARY id: %ls, event handle: 0x%x, vital: %ls, transaction: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.hEvent, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no", pAction->rollbackBoundary.pRollbackBoundary->fTransaction ? L"yes" : L"no"); + break; + + default: + AssertSz(FALSE, "Unknown cache action type."); + break; + } +} + +static void ExecuteActionLog( + __in DWORD iAction, + __in BURN_EXECUTE_ACTION* pAction, + __in BOOL fRollback + ) +{ + LPCWSTR wzBase = fRollback ? L" Rollback" : L" Execute"; + switch (pAction->type) + { + case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: CHECKPOINT id: %u", wzBase, iAction, pAction->checkpoint.dwId); + break; + + case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_PROVIDER package id: %ls, action: %u", wzBase, iAction, pAction->packageProvider.pPackage->sczId, pAction->packageProvider.action); + break; + + case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_DEPENDENCY package id: %ls, bundle provider key: %ls, action: %u", wzBase, iAction, pAction->packageDependency.pPackage->sczId, pAction->packageDependency.sczBundleProviderKey, pAction->packageDependency.action); + break; + + case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: EXE_PACKAGE package id: %ls, action: %hs, ignore dependencies: %ls", wzBase, iAction, pAction->exePackage.pPackage->sczId, LoggingActionStateToString(pAction->exePackage.action), pAction->exePackage.sczIgnoreDependencies); + break; + + case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSI_PACKAGE package id: %ls, action: %hs, ui level: %u, log path: %ls, logging attrib: %u", wzBase, iAction, pAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pAction->msiPackage.action), pAction->msiPackage.uiLevel, pAction->msiPackage.sczLogPath, pAction->msiPackage.dwLoggingAttributes); + for (DWORD j = 0; j < pAction->msiPackage.cPatches; ++j) + { + LogStringLine(REPORT_STANDARD, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->msiPackage.rgOrderedPatches->dwOrder, pAction->msiPackage.rgOrderedPatches[j].dwOrder, pAction->msiPackage.rgOrderedPatches[j].pPackage->sczId); + } + break; + + case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSP_TARGET package id: %ls, action: %hs, target product code: %ls, target per-machine: %ls, ui level: %u, log path: %ls", wzBase, iAction, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.sczTargetProductCode, pAction->mspTarget.fPerMachineTarget ? L"yes" : L"no", pAction->mspTarget.uiLevel, pAction->mspTarget.sczLogPath); + for (DWORD j = 0; j < pAction->mspTarget.cOrderedPatches; ++j) + { + LogStringLine(REPORT_STANDARD, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->mspTarget.rgOrderedPatches[j].dwOrder, pAction->mspTarget.rgOrderedPatches[j].pPackage->sczId); + } + break; + + case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSU_PACKAGE package id: %ls, action: %hs, log path: %ls", wzBase, iAction, pAction->msuPackage.pPackage->sczId, LoggingActionStateToString(pAction->msuPackage.action), pAction->msuPackage.sczLogPath); + break; + + case BURN_EXECUTE_ACTION_TYPE_REGISTRATION: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: REGISTRATION keep: %ls", wzBase, iAction, pAction->registration.fKeep ? L"yes" : L"no"); + break; + + case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: ROLLBACK_BOUNDARY id: %ls, vital: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no"); + break; + + case BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: WAIT_SYNCPOINT event handle: 0x%x", wzBase, iAction, pAction->syncpoint.hEvent); + break; + + case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: UNCACHE_PACKAGE id: %ls", wzBase, iAction, pAction->uncachePackage.pPackage->sczId); + break; + + case BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: COMPATIBLE_PACKAGE reference id: %ls, installed ProductCode: %ls", wzBase, iAction, pAction->compatiblePackage.pReferencePackage->sczId, pAction->compatiblePackage.sczInstalledProductCode); + break; + + default: + AssertSz(FALSE, "Unknown execute action type."); + break; + } +} + +extern "C" void PlanDump( + __in BURN_PLAN* pPlan + ) +{ + LogStringLine(REPORT_STANDARD, "--- Begin plan dump ---"); + + LogStringLine(REPORT_STANDARD, "Plan action: %hs", LoggingBurnActionToString(pPlan->action)); + LogStringLine(REPORT_STANDARD, " per-machine: %hs", LoggingTrueFalseToString(pPlan->fPerMachine)); + LogStringLine(REPORT_STANDARD, " keep registration by default: %hs", LoggingTrueFalseToString(pPlan->fKeepRegistrationDefault)); + LogStringLine(REPORT_STANDARD, " estimated size: %llu", pPlan->qwEstimatedSize); + + LogStringLine(REPORT_STANDARD, "Plan cache size: %llu", pPlan->qwCacheSizeTotal); + for (DWORD i = 0; i < pPlan->cCacheActions; ++i) + { + CacheActionLog(i, pPlan->rgCacheActions + i, FALSE); + } + + for (DWORD i = 0; i < pPlan->cRollbackCacheActions; ++i) + { + CacheActionLog(i, pPlan->rgRollbackCacheActions + i, TRUE); + } + + LogStringLine(REPORT_STANDARD, "Plan execute package count: %u", pPlan->cExecutePackagesTotal); + LogStringLine(REPORT_STANDARD, " overall progress ticks: %u", pPlan->cOverallProgressTicksTotal); + for (DWORD i = 0; i < pPlan->cExecuteActions; ++i) + { + ExecuteActionLog(i, pPlan->rgExecuteActions + i, FALSE); + } + + for (DWORD i = 0; i < pPlan->cRollbackActions; ++i) + { + ExecuteActionLog(i, pPlan->rgRollbackActions + i, TRUE); + } + + for (DWORD i = 0; i < pPlan->cCleanActions; ++i) + { + LogStringLine(REPORT_STANDARD, " Clean action[%u]: CLEAN_PACKAGE package id: %ls", i, pPlan->rgCleanActions[i].pPackage->sczId); + } + + for (DWORD i = 0; i < pPlan->cPlannedProviders; ++i) + { + LogStringLine(REPORT_STANDARD, " Dependency action[%u]: PLANNED_PROVIDER key: %ls, name: %ls", i, pPlan->rgPlannedProviders[i].sczKey, pPlan->rgPlannedProviders[i].sczName); + } + + LogStringLine(REPORT_STANDARD, "--- End plan dump ---"); +} + +#endif diff --git a/src/engine/plan.h b/src/engine/plan.h new file mode 100644 index 00000000..54cfe59d --- /dev/null +++ b/src/engine/plan.h @@ -0,0 +1,543 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + +const DWORD BURN_PLAN_INVALID_ACTION_INDEX = 0x80000000; + +enum BURN_REGISTRATION_ACTION_OPERATIONS +{ + BURN_REGISTRATION_ACTION_OPERATIONS_NONE = 0x0, + BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE = 0x1, + BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION = 0x2, + BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE = 0x4, +}; + +enum BURN_DEPENDENCY_REGISTRATION_ACTION +{ + BURN_DEPENDENCY_REGISTRATION_ACTION_NONE, + BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, + BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, +}; + +enum BURN_DEPENDENT_REGISTRATION_ACTION_TYPE +{ + BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_NONE, + BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, + BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER, +}; + +enum BURN_CACHE_ACTION_TYPE +{ + BURN_CACHE_ACTION_TYPE_NONE, + BURN_CACHE_ACTION_TYPE_CHECKPOINT, + BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE, + BURN_CACHE_ACTION_TYPE_PACKAGE_START, + BURN_CACHE_ACTION_TYPE_PACKAGE_STOP, + BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE, + BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT, + BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, + BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, + BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER, + BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD, + BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD, + BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD, + BURN_CACHE_ACTION_TYPE_TRANSACTION_BOUNDARY, +}; + +enum BURN_EXECUTE_ACTION_TYPE +{ + BURN_EXECUTE_ACTION_TYPE_NONE, + BURN_EXECUTE_ACTION_TYPE_CHECKPOINT, + BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT, + BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE, + BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE, + BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE, + BURN_EXECUTE_ACTION_TYPE_MSP_TARGET, + BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE, + BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP, + BURN_EXECUTE_ACTION_TYPE_SERVICE_START, + BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER, + BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY, + BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY, + BURN_EXECUTE_ACTION_TYPE_REGISTRATION, + BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE, +}; + +enum BURN_CLEAN_ACTION_TYPE +{ + BURN_CLEAN_ACTION_TYPE_NONE, + BURN_CLEAN_ACTION_TYPE_BUNDLE, + BURN_CLEAN_ACTION_TYPE_PACKAGE, +}; + + +// structs + +typedef struct _BURN_EXTRACT_PAYLOAD +{ + BURN_PACKAGE* pPackage; + BURN_PAYLOAD* pPayload; + LPWSTR sczUnverifiedPath; +} BURN_EXTRACT_PAYLOAD; + +typedef struct _BURN_DEPENDENT_REGISTRATION_ACTION +{ + BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type; + LPWSTR sczBundleId; + LPWSTR sczDependentProviderKey; +} BURN_DEPENDENT_REGISTRATION_ACTION; + +typedef struct _BURN_CACHE_CONTAINER_PROGRESS +{ + LPWSTR wzId; + DWORD iIndex; + BOOL fCachedDuringApply; + BURN_CONTAINER* pContainer; +} BURN_CACHE_CONTAINER_PROGRESS; + +typedef struct _BURN_CACHE_PAYLOAD_PROGRESS +{ + LPWSTR wzId; + DWORD iIndex; + BOOL fCachedDuringApply; + BURN_PAYLOAD* pPayload; +} BURN_CACHE_PAYLOAD_PROGRESS; + +typedef struct _BURN_CACHE_ACTION +{ + BURN_CACHE_ACTION_TYPE type; + BOOL fSkipUntilRetried; + union + { + struct + { + DWORD dwId; + } checkpoint; + struct + { + LPWSTR sczExecutableName; + LPWSTR sczLayoutDirectory; + LPWSTR sczUnverifiedPath; + DWORD64 qwBundleSize; + } bundleLayout; + struct + { + BURN_PACKAGE* pPackage; + DWORD cCachePayloads; + DWORD64 qwCachePayloadSizeTotal; + DWORD iPackageCompleteAction; + } packageStart; + struct + { + BURN_PACKAGE* pPackage; + } packageStop; + struct + { + BURN_PACKAGE* pPackage; + } rollbackPackage; + struct + { + HANDLE hEvent; + } syncpoint; + struct + { + BURN_CONTAINER* pContainer; + DWORD iProgress; + LPWSTR sczUnverifiedPath; + } resolveContainer; + struct + { + BURN_CONTAINER* pContainer; + DWORD iSkipUntilAcquiredByAction; + LPWSTR sczContainerUnverifiedPath; + + BURN_EXTRACT_PAYLOAD* rgPayloads; + DWORD cPayloads; + } extractContainer; + struct + { + BURN_PACKAGE* pPackage; + BURN_CONTAINER* pContainer; + DWORD iProgress; + DWORD iTryAgainAction; + DWORD cTryAgainAttempts; + LPWSTR sczLayoutDirectory; + LPWSTR sczUnverifiedPath; + BOOL fMove; + } layoutContainer; + struct + { + BURN_PACKAGE* pPackage; + BURN_PAYLOAD* pPayload; + DWORD iProgress; + LPWSTR sczUnverifiedPath; + } resolvePayload; + struct + { + BURN_PACKAGE* pPackage; + BURN_PAYLOAD* pPayload; + DWORD iProgress; + DWORD iTryAgainAction; + DWORD cTryAgainAttempts; + LPWSTR sczUnverifiedPath; + BOOL fMove; + } cachePayload; + struct + { + BURN_PACKAGE* pPackage; + BURN_PAYLOAD* pPayload; + DWORD iProgress; + DWORD iTryAgainAction; + DWORD cTryAgainAttempts; + LPWSTR sczLayoutDirectory; + LPWSTR sczUnverifiedPath; + BOOL fMove; + } layoutPayload; + struct + { + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary; + HANDLE hEvent; + } rollbackBoundary; + }; +} BURN_CACHE_ACTION; + +typedef struct _BURN_ORDERED_PATCHES +{ + DWORD dwOrder; + BURN_PACKAGE* pPackage; +} BURN_ORDERED_PATCHES; + +typedef struct _BURN_EXECUTE_ACTION +{ + BURN_EXECUTE_ACTION_TYPE type; + BOOL fDeleted; // used to skip an action after it was planned since deleting actions out of the plan is too hard. + union + { + struct + { + DWORD dwId; + } checkpoint; + struct + { + HANDLE hEvent; + } syncpoint; + struct + { + BURN_PACKAGE* pPackage; + } uncachePackage; + struct + { + BURN_PACKAGE* pPackage; + BOOL fFireAndForget; + BOOTSTRAPPER_ACTION_STATE action; + LPWSTR sczIgnoreDependencies; + LPWSTR sczAncestors; + } exePackage; + struct + { + BURN_PACKAGE* pPackage; + LPWSTR sczLogPath; + DWORD dwLoggingAttributes; + INSTALLUILEVEL uiLevel; + BOOTSTRAPPER_ACTION_STATE action; + + BOOTSTRAPPER_FEATURE_ACTION* rgFeatures; + BOOTSTRAPPER_ACTION_STATE* rgSlipstreamPatches; + + BURN_ORDERED_PATCHES* rgOrderedPatches; + DWORD cPatches; + } msiPackage; + struct + { + BURN_PACKAGE* pPackage; + LPWSTR sczTargetProductCode; + BURN_PACKAGE* pChainedTargetPackage; + BOOL fSlipstream; + BOOL fPerMachineTarget; + LPWSTR sczLogPath; + INSTALLUILEVEL uiLevel; + BOOTSTRAPPER_ACTION_STATE action; + + BURN_ORDERED_PATCHES* rgOrderedPatches; + DWORD cOrderedPatches; + } mspTarget; + struct + { + BURN_PACKAGE* pPackage; + LPWSTR sczLogPath; + BOOTSTRAPPER_ACTION_STATE action; + } msuPackage; + struct + { + LPWSTR sczServiceName; + } service; + struct + { + BOOL fKeep; + } registration; + struct + { + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary; + } rollbackBoundary; + struct + { + BURN_PACKAGE* pPackage; + BURN_DEPENDENCY_ACTION action; + } packageProvider; + struct + { + BURN_PACKAGE* pPackage; + LPWSTR sczBundleProviderKey; + BURN_DEPENDENCY_ACTION action; + } packageDependency; + struct + { + BURN_PACKAGE* pReferencePackage; + LPWSTR sczInstalledProductCode; + DWORD64 qwInstalledVersion; + } compatiblePackage; + }; +} BURN_EXECUTE_ACTION; + +typedef struct _BURN_CLEAN_ACTION +{ + BURN_PACKAGE* pPackage; +} BURN_CLEAN_ACTION; + +typedef struct _BURN_PLAN +{ + BOOTSTRAPPER_ACTION action; + LPWSTR wzBundleId; // points directly into parent the ENGINE_STATE. + LPWSTR wzBundleProviderKey; // points directly into parent the ENGINE_STATE. + BOOL fPerMachine; + BOOL fRegister; + DWORD dwRegistrationOperations; + BOOL fKeepRegistrationDefault; + BOOL fDisallowRemoval; + + DWORD64 qwCacheSizeTotal; + + DWORD64 qwEstimatedSize; + + DWORD cExecutePackagesTotal; + DWORD cOverallProgressTicksTotal; + + BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction; + + BURN_DEPENDENT_REGISTRATION_ACTION* rgRegistrationActions; + DWORD cRegistrationActions; + + BURN_DEPENDENT_REGISTRATION_ACTION* rgRollbackRegistrationActions; + DWORD cRollbackRegistrationActions; + + BURN_CACHE_ACTION* rgCacheActions; + DWORD cCacheActions; + + BURN_CACHE_ACTION* rgRollbackCacheActions; + DWORD cRollbackCacheActions; + + BURN_EXECUTE_ACTION* rgExecuteActions; + DWORD cExecuteActions; + + BURN_EXECUTE_ACTION* rgRollbackActions; + DWORD cRollbackActions; + + BURN_CLEAN_ACTION* rgCleanActions; + DWORD cCleanActions; + + DEPENDENCY* rgPlannedProviders; + UINT cPlannedProviders; + + BURN_CACHE_CONTAINER_PROGRESS* rgContainerProgress; + DWORD cContainerProgress; + STRINGDICT_HANDLE shContainerProgress; + + BURN_CACHE_PAYLOAD_PROGRESS* rgPayloadProgress; + DWORD cPayloadProgress; + STRINGDICT_HANDLE shPayloadProgress; +} BURN_PLAN; + + +// functions + +void PlanReset( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGES* pPackages + ); +void PlanUninitializeExecuteAction( + __in BURN_EXECUTE_ACTION* pExecuteAction + ); +HRESULT PlanSetVariables( + __in BOOTSTRAPPER_ACTION action, + __in BURN_VARIABLES* pVariables + ); +HRESULT PlanDefaultPackageRequestState( + __in BURN_PACKAGE_TYPE packageType, + __in BOOTSTRAPPER_PACKAGE_STATE currentState, + __in BOOL fPermanent, + __in BOOTSTRAPPER_ACTION action, + __in BURN_VARIABLES* pVariables, + __in_z_opt LPCWSTR wzInstallCondition, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __out BOOTSTRAPPER_REQUEST_STATE* pRequestState + ); +HRESULT PlanLayoutBundle( + __in BURN_PLAN* pPlan, + __in_z LPCWSTR wzExecutableName, + __in DWORD64 qwBundleSize, + __in BURN_VARIABLES* pVariables, + __in BURN_PAYLOADS* pPayloads, + __out_z LPWSTR* psczLayoutDirectory + ); +HRESULT PlanPackages( + __in BURN_REGISTRATION* pRegistration, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PACKAGES* pPackages, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOL fBundleInstalled, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in_z_opt LPCWSTR wzLayoutDirectory, + __inout HANDLE* phSyncpointEvent + ); +HRESULT PlanRegistration( + __in BURN_PLAN* pPlan, + __in BURN_REGISTRATION* pRegistration, + __in BOOTSTRAPPER_RESUME_TYPE resumeType, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in_z_opt LPCWSTR wzIgnoreDependencies, + __out BOOL* pfContinuePlanning + ); +HRESULT PlanPassThroughBundle( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __inout HANDLE* phSyncpointEvent + ); +HRESULT PlanUpdateBundle( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __inout HANDLE* phSyncpointEvent + ); +HRESULT PlanLayoutPackage( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __in_z_opt LPCWSTR wzLayoutDirectory + ); +HRESULT PlanCachePackage( + __in BOOL fPerMachine, + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __in BURN_VARIABLES* pVariables, + __out HANDLE* phSyncpointEvent + ); +HRESULT PlanExecutePackage( + __in BOOL fPerMachine, + __in BOOTSTRAPPER_DISPLAY display, + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __inout HANDLE* phSyncpointEvent + ); +HRESULT PlanRelatedBundlesBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BURN_REGISTRATION* pRegistration, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in BURN_PLAN* pPlan + ); +HRESULT PlanRelatedBundlesComplete( + __in BURN_REGISTRATION* pRegistration, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __inout HANDLE* phSyncpointEvent, + __in DWORD dwExecuteActionEarlyIndex + ); +HRESULT PlanFinalizeActions( + __in BURN_PLAN* pPlan + ); +HRESULT PlanCleanPackage( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage + ); +HRESULT PlanExecuteCacheSyncAndRollback( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __in HANDLE hCacheEvent, + __in BOOL fPlanPackageCacheRollback + ); +HRESULT PlanExecuteCheckpoint( + __in BURN_PLAN* pPlan + ); +HRESULT PlanInsertExecuteAction( + __in DWORD dwIndex, + __in BURN_PLAN* pPlan, + __out BURN_EXECUTE_ACTION** ppExecuteAction + ); +HRESULT PlanInsertRollbackAction( + __in DWORD dwIndex, + __in BURN_PLAN* pPlan, + __out BURN_EXECUTE_ACTION** ppRollbackAction + ); +HRESULT PlanAppendExecuteAction( + __in BURN_PLAN* pPlan, + __out BURN_EXECUTE_ACTION** ppExecuteAction + ); +HRESULT PlanAppendRollbackAction( + __in BURN_PLAN* pPlan, + __out BURN_EXECUTE_ACTION** ppExecuteAction + ); +HRESULT PlanKeepRegistration( + __in BURN_PLAN* pPlan, + __in DWORD iAfterExecutePackageAction, + __in DWORD iBeforeRollbackPackageAction + ); +HRESULT PlanRemoveRegistration( + __in BURN_PLAN* pPlan, + __in DWORD iAfterExecutePackageAction, + __in DWORD iAfterRollbackPackageAction + ); +HRESULT PlanRollbackBoundaryBegin( + __in BURN_PLAN* pPlan, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary + ); +HRESULT PlanRollbackBoundaryComplete( + __in BURN_PLAN* pPlan + ); +HRESULT PlanSetResumeCommand( + __in BURN_REGISTRATION* pRegistration, + __in BOOTSTRAPPER_ACTION action, + __in BOOTSTRAPPER_COMMAND* pCommand, + __in BURN_LOGGING* pLog + ); + +#ifdef DEBUG +void PlanDump( + __in BURN_PLAN* pPlan + ); +#endif + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/platform.cpp b/src/engine/platform.cpp new file mode 100644 index 00000000..9469ff49 --- /dev/null +++ b/src/engine/platform.cpp @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// variables + +PFN_INITIATESYSTEMSHUTDOWNEXW vpfnInitiateSystemShutdownExW; + + +// function definitions + +extern "C" void PlatformInitialize() +{ + vpfnInitiateSystemShutdownExW = ::InitiateSystemShutdownExW; +} diff --git a/src/engine/platform.h b/src/engine/platform.h new file mode 100644 index 00000000..3681f248 --- /dev/null +++ b/src/engine/platform.h @@ -0,0 +1,34 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// typedefs + +typedef BOOL (WINAPI *PFN_INITIATESYSTEMSHUTDOWNEXW)( + __in_opt LPWSTR lpMachineName, + __in_opt LPWSTR lpMessage, + __in DWORD dwTimeout, + __in BOOL bForceAppsClosed, + __in BOOL bRebootAfterShutdown, + __in DWORD dwReason + ); + + +// variable declarations + +extern PFN_INITIATESYSTEMSHUTDOWNEXW vpfnInitiateSystemShutdownExW; + + +// function declarations + +void PlatformInitialize(); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/precomp.h b/src/engine/precomp.h new file mode 100644 index 00000000..d3ebe354 --- /dev/null +++ b/src/engine/precomp.h @@ -0,0 +1,100 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#define ExitTrace LogErrorString + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "BootstrapperEngine.h" +#include "BootstrapperApplication.h" + +#include "platform.h" +#include "variant.h" +#include "variable.h" +#include "condition.h" +#include "search.h" +#include "section.h" +#include "approvedexe.h" +#include "container.h" +#include "catalog.h" +#include "payload.h" +#include "cabextract.h" +#include "userexperience.h" +#include "package.h" +#include "update.h" +#include "pseudobundle.h" +#include "registration.h" +#include "relatedbundle.h" +#include "detect.h" +#include "plan.h" +#include "logging.h" +#include "pipe.h" +#include "core.h" +#include "cache.h" +#include "apply.h" +#include "exeengine.h" +#include "msiengine.h" +#include "mspengine.h" +#include "msuengine.h" +#include "dependency.h" +#include "elevation.h" +#include "embedded.h" +#include "manifest.h" +#include "splashscreen.h" +#include "uithread.h" +#include "bitsengine.h" +#include "netfxchainer.h" + +#include "EngineForApplication.h" +#include "engine.messages.h" diff --git a/src/engine/pseudobundle.cpp b/src/engine/pseudobundle.cpp new file mode 100644 index 00000000..ebdc040a --- /dev/null +++ b/src/engine/pseudobundle.cpp @@ -0,0 +1,271 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +extern "C" HRESULT PseudoBundleInitialize( + __in DWORD64 qwEngineVersion, + __in BURN_PACKAGE* pPackage, + __in BOOL fPerMachine, + __in_z LPCWSTR wzId, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in BOOTSTRAPPER_PACKAGE_STATE state, + __in_z LPCWSTR wzFilePath, + __in_z LPCWSTR wzLocalSource, + __in_z_opt LPCWSTR wzDownloadSource, + __in DWORD64 qwSize, + __in BOOL fVital, + __in_z_opt LPCWSTR wzInstallArguments, + __in_z_opt LPCWSTR wzRepairArguments, + __in_z_opt LPCWSTR wzUninstallArguments, + __in_opt BURN_DEPENDENCY_PROVIDER* pDependencyProvider, + __in_opt BYTE* pbHash, + __in DWORD cbHash + ) +{ + HRESULT hr = S_OK; + LPWSTR sczRelationTypeCommandLineSwitch = NULL; + + LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType); + if (wzRelationTypeCommandLine) + { + hr = StrAllocFormatted(&sczRelationTypeCommandLineSwitch, L" -%ls", wzRelationTypeCommandLine); + } + + // Initialize the single payload, and fill out all the necessary fields + pPackage->rgPayloads = (BURN_PACKAGE_PAYLOAD *)MemAlloc(sizeof(BURN_PACKAGE_PAYLOAD), TRUE); + ExitOnNull(pPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate space for burn package payload inside of related bundle struct"); + pPackage->cPayloads = 1; + + pPackage->rgPayloads->pPayload = (BURN_PAYLOAD *)MemAlloc(sizeof(BURN_PAYLOAD), TRUE); + ExitOnNull(pPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload inside of related bundle struct"); + pPackage->rgPayloads->pPayload->packaging = BURN_PAYLOAD_PACKAGING_EXTERNAL; + pPackage->rgPayloads->pPayload->qwFileSize = qwSize; + + hr = StrAllocString(&pPackage->rgPayloads->pPayload->sczKey, wzId, 0); + ExitOnFailure(hr, "Failed to copy key for pseudo bundle payload."); + + hr = StrAllocString(&pPackage->rgPayloads->pPayload->sczFilePath, wzFilePath, 0); + ExitOnFailure(hr, "Failed to copy filename for pseudo bundle."); + + hr = StrAllocString(&pPackage->rgPayloads->pPayload->sczSourcePath, wzLocalSource, 0); + ExitOnFailure(hr, "Failed to copy local source path for pseudo bundle."); + + if (wzDownloadSource && *wzDownloadSource) + { + hr = StrAllocString(&pPackage->rgPayloads->pPayload->downloadSource.sczUrl, wzDownloadSource, 0); + ExitOnFailure(hr, "Failed to copy download source for pseudo bundle."); + } + + if (pbHash) + { + pPackage->rgPayloads->pPayload->pbHash = static_cast(MemAlloc(cbHash, FALSE)); + ExitOnNull(pPackage->rgPayloads->pPayload->pbHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for pseudo bundle payload hash."); + + pPackage->rgPayloads->pPayload->cbHash = cbHash; + memcpy_s(pPackage->rgPayloads->pPayload->pbHash, pPackage->rgPayloads->pPayload->cbHash, pbHash, cbHash); + } + + pPackage->rgPayloads->fCached = (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_CACHED == state); + + pPackage->Exe.fPseudoBundle = TRUE; + + pPackage->type = BURN_PACKAGE_TYPE_EXE; + pPackage->fPerMachine = fPerMachine; + pPackage->currentState = state; + pPackage->cache = (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_CACHED == state) ? BURN_CACHE_STATE_COMPLETE : BURN_CACHE_STATE_NONE; + pPackage->qwInstallSize = qwSize; + pPackage->qwSize = qwSize; + pPackage->fVital = fVital; + + hr = StrAllocString(&pPackage->sczId, wzId, 0); + ExitOnFailure(hr, "Failed to copy key for pseudo bundle."); + + hr = StrAllocString(&pPackage->sczCacheId, wzId, 0); + ExitOnFailure(hr, "Failed to copy cache id for pseudo bundle."); + + // If we are a self updating bundle, we don't have to have Install arguments. + if (wzInstallArguments) + { + hr = StrAllocString(&pPackage->Exe.sczInstallArguments, wzInstallArguments, 0); + ExitOnFailure(hr, "Failed to copy install arguments for related bundle package"); + } + + if (sczRelationTypeCommandLineSwitch) + { + hr = StrAllocConcat(&pPackage->Exe.sczInstallArguments, sczRelationTypeCommandLineSwitch, 0); + ExitOnFailure(hr, "Failed to append relation type to install arguments for related bundle package"); + } + + if (wzRepairArguments) + { + hr = StrAllocString(&pPackage->Exe.sczRepairArguments, wzRepairArguments, 0); + ExitOnFailure(hr, "Failed to copy repair arguments for related bundle package"); + + if (sczRelationTypeCommandLineSwitch) + { + hr = StrAllocConcat(&pPackage->Exe.sczRepairArguments, sczRelationTypeCommandLineSwitch, 0); + ExitOnFailure(hr, "Failed to append relation type to repair arguments for related bundle package"); + } + + pPackage->Exe.fRepairable = TRUE; + } + + if (wzUninstallArguments) + { + hr = StrAllocString(&pPackage->Exe.sczUninstallArguments, wzUninstallArguments, 0); + ExitOnFailure(hr, "Failed to copy uninstall arguments for related bundle package"); + + if (sczRelationTypeCommandLineSwitch) + { + hr = StrAllocConcat(&pPackage->Exe.sczUninstallArguments, sczRelationTypeCommandLineSwitch, 0); + ExitOnFailure(hr, "Failed to append relation type to uninstall arguments for related bundle package"); + } + + pPackage->fUninstallable = TRUE; + } + + // Only support progress from engines that are compatible (aka: version greater than or equal to last protocol breaking change *and* versions that are older or the same as this engine). + pPackage->Exe.protocol = (FILEMAKEVERSION(3, 6, 2221, 0) <= qwEngineVersion && qwEngineVersion <= FILEMAKEVERSION(rmj, rmm, rup, 0)) ? BURN_EXE_PROTOCOL_TYPE_BURN : BURN_EXE_PROTOCOL_TYPE_NONE; + + // All versions of Burn past v3.9 RTM support suppressing ancestors. + pPackage->Exe.fSupportsAncestors = FILEMAKEVERSION(3, 9, 1006, 0) <= qwEngineVersion; + + if (pDependencyProvider) + { + pPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER), TRUE); + ExitOnNull(pPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers."); + pPackage->cDependencyProviders = 1; + + pPackage->rgDependencyProviders[0].fImported = pDependencyProvider->fImported; + + hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczKey, pDependencyProvider->sczKey, 0); + ExitOnFailure(hr, "Failed to copy key for pseudo bundle."); + + hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczVersion, pDependencyProvider->sczVersion, 0); + ExitOnFailure(hr, "Failed to copy version for pseudo bundle."); + + hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczDisplayName, pDependencyProvider->sczDisplayName, 0); + ExitOnFailure(hr, "Failed to copy display name for pseudo bundle."); + } + +LExit: + ReleaseStr(sczRelationTypeCommandLineSwitch); + + return hr; +} + +extern "C" HRESULT PseudoBundleInitializePassthrough( + __in BURN_PACKAGE* pPassthroughPackage, + __in BOOTSTRAPPER_COMMAND* pCommand, + __in_z_opt LPCWSTR wzAppendLogPath, + __in_z_opt LPWSTR wzActiveParent, + __in_z_opt LPWSTR wzAncestors, + __in BURN_PACKAGE* pPackage + ) +{ + Assert(BURN_PACKAGE_TYPE_EXE == pPackage->type); + + HRESULT hr = S_OK; + LPWSTR sczArguments = NULL; + + // Initialize the payloads, and copy the necessary fields. + pPassthroughPackage->rgPayloads = (BURN_PACKAGE_PAYLOAD *)MemAlloc(sizeof(BURN_PACKAGE_PAYLOAD) * pPackage->cPayloads, TRUE); + ExitOnNull(pPassthroughPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate space for burn package payload inside of passthrough bundle."); + pPassthroughPackage->cPayloads = pPackage->cPayloads; + + for (DWORD iPayload = 0; iPayload < pPackage->cPayloads; ++iPayload) + { + BURN_PACKAGE_PAYLOAD* pPayload = pPackage->rgPayloads + iPayload; + + pPassthroughPackage->rgPayloads[iPayload].pPayload = (BURN_PAYLOAD *)MemAlloc(sizeof(BURN_PAYLOAD), TRUE); + ExitOnNull(pPassthroughPackage->rgPayloads[iPayload].pPayload, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload inside of related bundle struct"); + pPassthroughPackage->rgPayloads[iPayload].pPayload->packaging = pPayload->pPayload->packaging; + pPassthroughPackage->rgPayloads[iPayload].pPayload->qwFileSize = pPayload->pPayload->qwFileSize; + + hr = StrAllocString(&pPassthroughPackage->rgPayloads[iPayload].pPayload->sczKey, pPayload->pPayload->sczKey, 0); + ExitOnFailure(hr, "Failed to copy key for passthrough pseudo bundle payload."); + + hr = StrAllocString(&pPassthroughPackage->rgPayloads[iPayload].pPayload->sczFilePath, pPayload->pPayload->sczFilePath, 0); + ExitOnFailure(hr, "Failed to copy filename for passthrough pseudo bundle."); + + hr = StrAllocString(&pPassthroughPackage->rgPayloads[iPayload].pPayload->sczSourcePath, pPayload->pPayload->sczSourcePath, 0); + ExitOnFailure(hr, "Failed to copy local source path for passthrough pseudo bundle."); + + if (pPayload->pPayload->downloadSource.sczUrl) + { + hr = StrAllocString(&pPassthroughPackage->rgPayloads[iPayload].pPayload->downloadSource.sczUrl, pPayload->pPayload->downloadSource.sczUrl, 0); + ExitOnFailure(hr, "Failed to copy download source for passthrough pseudo bundle."); + } + + if (pPayload->pPayload->pbHash) + { + pPassthroughPackage->rgPayloads[iPayload].pPayload->pbHash = static_cast(MemAlloc(pPayload->pPayload->cbHash, FALSE)); + ExitOnNull(pPassthroughPackage->rgPayloads[iPayload].pPayload->pbHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for pseudo bundle payload hash."); + + pPassthroughPackage->rgPayloads[iPayload].pPayload->cbHash = pPayload->pPayload->cbHash; + memcpy_s(pPassthroughPackage->rgPayloads[iPayload].pPayload->pbHash, pPassthroughPackage->rgPayloads[iPayload].pPayload->cbHash, pPayload->pPayload->pbHash, pPayload->pPayload->cbHash); + } + + pPassthroughPackage->rgPayloads[iPayload].fCached = pPayload->fCached; + } + + pPassthroughPackage->Exe.fPseudoBundle = TRUE; + + pPassthroughPackage->fPerMachine = FALSE; // passthrough bundles are always launched per-user. + pPassthroughPackage->type = pPackage->type; + pPassthroughPackage->currentState = pPackage->currentState; + pPassthroughPackage->cache = pPackage->cache; + pPassthroughPackage->qwInstallSize = pPackage->qwInstallSize; + pPassthroughPackage->qwSize = pPackage->qwSize; + pPassthroughPackage->fVital = pPackage->fVital; + + hr = StrAllocString(&pPassthroughPackage->sczId, pPackage->sczId, 0); + ExitOnFailure(hr, "Failed to copy key for passthrough pseudo bundle."); + + hr = StrAllocString(&pPassthroughPackage->sczCacheId, pPackage->sczCacheId, 0); + ExitOnFailure(hr, "Failed to copy cache id for passthrough pseudo bundle."); + + pPassthroughPackage->Exe.protocol = pPackage->Exe.protocol; + + // No matter the operation, we're passing the same command-line. That's what makes + // this a passthrough bundle. + hr = CoreRecreateCommandLine(&sczArguments, pCommand->action, pCommand->display, pCommand->restart, pCommand->relationType, TRUE, wzActiveParent, wzAncestors, wzAppendLogPath, pCommand->wzCommandLine); + ExitOnFailure(hr, "Failed to recreate command-line arguments."); + + hr = StrAllocString(&pPassthroughPackage->Exe.sczInstallArguments, sczArguments, 0); + ExitOnFailure(hr, "Failed to copy install arguments for passthrough bundle package"); + + hr = StrAllocString(&pPassthroughPackage->Exe.sczRepairArguments, sczArguments, 0); + ExitOnFailure(hr, "Failed to copy related arguments for passthrough bundle package"); + + pPassthroughPackage->Exe.fRepairable = TRUE; + + hr = StrAllocString(&pPassthroughPackage->Exe.sczUninstallArguments, sczArguments, 0); + ExitOnFailure(hr, "Failed to copy uninstall arguments for passthrough bundle package"); + + pPassthroughPackage->fUninstallable = TRUE; + + // TODO: consider bringing this back in the near future. + //if (pDependencyProvider) + //{ + // pPassthroughPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER), TRUE); + // ExitOnNull(pPassthroughPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers."); + // pPassthroughPackage->cDependencyProviders = 1; + + // pPassthroughPackage->rgDependencyProviders[0].fImported = pDependencyProvider->fImported; + + // hr = StrAllocString(&pPassthroughPackage->rgDependencyProviders[0].sczKey, pDependencyProvider->sczKey, 0); + // ExitOnFailure(hr, "Failed to copy key for pseudo bundle."); + + // hr = StrAllocString(&pPassthroughPackage->rgDependencyProviders[0].sczVersion, pDependencyProvider->sczVersion, 0); + // ExitOnFailure(hr, "Failed to copy version for pseudo bundle."); + + // hr = StrAllocString(&pPassthroughPackage->rgDependencyProviders[0].sczDisplayName, pDependencyProvider->sczDisplayName, 0); + // ExitOnFailure(hr, "Failed to copy display name for pseudo bundle."); + //} + +LExit: + ReleaseStr(sczArguments); + return hr; +} diff --git a/src/engine/pseudobundle.h b/src/engine/pseudobundle.h new file mode 100644 index 00000000..144f6880 --- /dev/null +++ b/src/engine/pseudobundle.h @@ -0,0 +1,39 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +HRESULT PseudoBundleInitialize( + __in DWORD64 qwEngineVersion, + __in BURN_PACKAGE* pPackage, + __in BOOL fPerMachine, + __in_z LPCWSTR wzId, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in BOOTSTRAPPER_PACKAGE_STATE state, + __in_z LPCWSTR wzFilePath, + __in_z LPCWSTR wzLocalSource, + __in_z_opt LPCWSTR wzDownloadSource, + __in DWORD64 qwSize, + __in BOOL fVital, + __in_z_opt LPCWSTR wzInstallArguments, + __in_z_opt LPCWSTR wzRepairArguments, + __in_z_opt LPCWSTR wzUninstallArguments, + __in_opt BURN_DEPENDENCY_PROVIDER* pDependencyProvider, + __in_opt BYTE* pbHash, + __in DWORD cbHash + ); +HRESULT PseudoBundleInitializePassthrough( + __in BURN_PACKAGE* pPassthroughPackage, + __in BOOTSTRAPPER_COMMAND* pCommand, + __in_z_opt LPCWSTR wzAppendLogPath, + __in_z_opt LPWSTR wzActiveParent, + __in_z_opt LPWSTR wzAncestors, + __in BURN_PACKAGE* pPackage + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/registration.cpp b/src/engine/registration.cpp new file mode 100644 index 00000000..93c990f5 --- /dev/null +++ b/src/engine/registration.cpp @@ -0,0 +1,1599 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// constants + +const LPCWSTR REGISTRY_RUN_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"; +const LPCWSTR REGISTRY_RUN_ONCE_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"; +const LPCWSTR REGISTRY_REBOOT_PENDING_FORMAT = L"%ls.RebootRequired"; +const LPCWSTR REGISTRY_BUNDLE_INSTALLED = L"Installed"; +const LPCWSTR REGISTRY_BUNDLE_DISPLAY_ICON = L"DisplayIcon"; +const LPCWSTR REGISTRY_BUNDLE_DISPLAY_VERSION = L"DisplayVersion"; +const LPCWSTR REGISTRY_BUNDLE_ESTIMATED_SIZE = L"EstimatedSize"; +const LPCWSTR REGISTRY_BUNDLE_PUBLISHER = L"Publisher"; +const LPCWSTR REGISTRY_BUNDLE_HELP_LINK = L"HelpLink"; +const LPCWSTR REGISTRY_BUNDLE_HELP_TELEPHONE = L"HelpTelephone"; +const LPCWSTR REGISTRY_BUNDLE_URL_INFO_ABOUT = L"URLInfoAbout"; +const LPCWSTR REGISTRY_BUNDLE_URL_UPDATE_INFO = L"URLUpdateInfo"; +const LPCWSTR REGISTRY_BUNDLE_PARENT_DISPLAY_NAME = L"ParentDisplayName"; +const LPCWSTR REGISTRY_BUNDLE_PARENT_KEY_NAME = L"ParentKeyName"; +const LPCWSTR REGISTRY_BUNDLE_COMMENTS = L"Comments"; +const LPCWSTR REGISTRY_BUNDLE_CONTACT = L"Contact"; +const LPCWSTR REGISTRY_BUNDLE_NO_MODIFY = L"NoModify"; +const LPCWSTR REGISTRY_BUNDLE_MODIFY_PATH = L"ModifyPath"; +const LPCWSTR REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY = L"NoElevateOnModify"; +const LPCWSTR REGISTRY_BUNDLE_NO_REMOVE = L"NoRemove"; +const LPCWSTR REGISTRY_BUNDLE_SYSTEM_COMPONENT = L"SystemComponent"; +const LPCWSTR REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING = L"QuietUninstallString"; +const LPCWSTR REGISTRY_BUNDLE_UNINSTALL_STRING = L"UninstallString"; +const LPCWSTR REGISTRY_BUNDLE_RESUME_COMMAND_LINE = L"BundleResumeCommandLine"; +const LPCWSTR REGISTRY_BUNDLE_VERSION_MAJOR = L"VersionMajor"; +const LPCWSTR REGISTRY_BUNDLE_VERSION_MINOR = L"VersionMinor"; + +// internal function declarations + +static HRESULT ParseSoftwareTagsFromXml( + __in IXMLDOMNode* pixnRegistrationNode, + __out BURN_SOFTWARE_TAG** prgSoftwareTags, + __out DWORD* pcSoftwareTags + ); +static HRESULT SetPaths( + __in BURN_REGISTRATION* pRegistration + ); +static HRESULT GetBundleManufacturer( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __out LPWSTR* psczBundleManufacturer + ); +static HRESULT GetBundleName( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __out LPWSTR* psczBundleName + ); +static HRESULT UpdateResumeMode( + __in BURN_REGISTRATION* pRegistration, + __in HKEY hkRegistration, + __in BURN_RESUME_MODE resumeMode, + __in BOOL fRestartInitiated + ); +static HRESULT ParseRelatedCodes( + __in BURN_REGISTRATION* pRegistration, + __in IXMLDOMNode* pixnBundle + ); +static HRESULT FormatUpdateRegistrationKey( + __in BURN_REGISTRATION* pRegistration, + __out_z LPWSTR* psczKey + ); +static HRESULT WriteSoftwareTags( + __in BOOL fPerMachine, + __in BURN_SOFTWARE_TAGS* pSoftwareTags + ); +static HRESULT RemoveSoftwareTags( + __in BOOL fPerMachine, + __in BURN_SOFTWARE_TAGS* pSoftwareTags + ); +static HRESULT WriteUpdateRegistration( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables + ); +static HRESULT RemoveUpdateRegistration( + __in BURN_REGISTRATION* pRegistration + ); +static HRESULT RegWriteStringVariable( + __in HKEY hkKey, + __in BURN_VARIABLES* pVariables, + __in LPCWSTR wzVariable, + __in LPCWSTR wzName + ); +static HRESULT UpdateBundleNameRegistration( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in HKEY hkRegistration + ); + +// function definitions + +/******************************************************************* + RegistrationParseFromXml - Parses registration information from manifest. + +*******************************************************************/ +extern "C" HRESULT RegistrationParseFromXml( + __in BURN_REGISTRATION* pRegistration, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNode* pixnRegistrationNode = NULL; + IXMLDOMNode* pixnArpNode = NULL; + IXMLDOMNode* pixnUpdateNode = NULL; + LPWSTR scz = NULL; + + // select registration node + hr = XmlSelectSingleNode(pixnBundle, L"Registration", &pixnRegistrationNode); + if (S_FALSE == hr) + { + hr = E_NOTFOUND; + } + ExitOnFailure(hr, "Failed to select registration node."); + + // @Id + hr = XmlGetAttributeEx(pixnRegistrationNode, L"Id", &pRegistration->sczId); + ExitOnFailure(hr, "Failed to get @Id."); + + // @Tag + hr = XmlGetAttributeEx(pixnRegistrationNode, L"Tag", &pRegistration->sczTag); + ExitOnFailure(hr, "Failed to get @Tag."); + + hr = ParseRelatedCodes(pRegistration, pixnBundle); + ExitOnFailure(hr, "Failed to parse related bundles"); + + // @Version + hr = XmlGetAttributeEx(pixnRegistrationNode, L"Version", &scz); + ExitOnFailure(hr, "Failed to get @Version."); + + hr = FileVersionFromStringEx(scz, 0, &pRegistration->qwVersion); + ExitOnFailure(hr, "Failed to parse @Version: %ls", scz); + + // @ProviderKey + hr = XmlGetAttributeEx(pixnRegistrationNode, L"ProviderKey", &pRegistration->sczProviderKey); + ExitOnFailure(hr, "Failed to get @ProviderKey."); + + // @ExecutableName + hr = XmlGetAttributeEx(pixnRegistrationNode, L"ExecutableName", &pRegistration->sczExecutableName); + ExitOnFailure(hr, "Failed to get @ExecutableName."); + + // @PerMachine + hr = XmlGetYesNoAttribute(pixnRegistrationNode, L"PerMachine", &pRegistration->fPerMachine); + ExitOnFailure(hr, "Failed to get @PerMachine."); + + // select ARP node + hr = XmlSelectSingleNode(pixnRegistrationNode, L"Arp", &pixnArpNode); + if (S_FALSE != hr) + { + ExitOnFailure(hr, "Failed to select ARP node."); + + // @Register + hr = XmlGetYesNoAttribute(pixnArpNode, L"Register", &pRegistration->fRegisterArp); + ExitOnFailure(hr, "Failed to get @Register."); + + // @DisplayName + hr = XmlGetAttributeEx(pixnArpNode, L"DisplayName", &pRegistration->sczDisplayName); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @DisplayName."); + } + + // @DisplayVersion + hr = XmlGetAttributeEx(pixnArpNode, L"DisplayVersion", &pRegistration->sczDisplayVersion); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @DisplayVersion."); + } + + // @Publisher + hr = XmlGetAttributeEx(pixnArpNode, L"Publisher", &pRegistration->sczPublisher); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Publisher."); + } + + // @HelpLink + hr = XmlGetAttributeEx(pixnArpNode, L"HelpLink", &pRegistration->sczHelpLink); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @HelpLink."); + } + + // @HelpTelephone + hr = XmlGetAttributeEx(pixnArpNode, L"HelpTelephone", &pRegistration->sczHelpTelephone); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @HelpTelephone."); + } + + // @AboutUrl + hr = XmlGetAttributeEx(pixnArpNode, L"AboutUrl", &pRegistration->sczAboutUrl); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @AboutUrl."); + } + + // @UpdateUrl + hr = XmlGetAttributeEx(pixnArpNode, L"UpdateUrl", &pRegistration->sczUpdateUrl); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @UpdateUrl."); + } + + // @ParentDisplayName + hr = XmlGetAttributeEx(pixnArpNode, L"ParentDisplayName", &pRegistration->sczParentDisplayName); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @ParentDisplayName."); + } + + // @Comments + hr = XmlGetAttributeEx(pixnArpNode, L"Comments", &pRegistration->sczComments); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Comments."); + } + + // @Contact + hr = XmlGetAttributeEx(pixnArpNode, L"Contact", &pRegistration->sczContact); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Contact."); + } + + // @DisableModify + hr = XmlGetAttributeEx(pixnArpNode, L"DisableModify", &scz); + if (SUCCEEDED(hr)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"button", -1)) + { + pRegistration->modify = BURN_REGISTRATION_MODIFY_DISABLE_BUTTON; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"yes", -1)) + { + pRegistration->modify = BURN_REGISTRATION_MODIFY_DISABLE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"no", -1)) + { + pRegistration->modify = BURN_REGISTRATION_MODIFY_ENABLED; + } + else + { + hr = E_UNEXPECTED; + ExitOnRootFailure(hr, "Invalid modify disabled type: %ls", scz); + } + } + else if (E_NOTFOUND == hr) + { + pRegistration->modify = BURN_REGISTRATION_MODIFY_ENABLED; + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get @DisableModify."); + + // @DisableRemove + hr = XmlGetYesNoAttribute(pixnArpNode, L"DisableRemove", &pRegistration->fNoRemove); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @DisableRemove."); + pRegistration->fNoRemoveDefined = TRUE; + } + } + + hr = ParseSoftwareTagsFromXml(pixnRegistrationNode, &pRegistration->softwareTags.rgSoftwareTags, &pRegistration->softwareTags.cSoftwareTags); + ExitOnFailure(hr, "Failed to parse software tag."); + + // select Update node + hr = XmlSelectSingleNode(pixnRegistrationNode, L"Update", &pixnUpdateNode); + if (S_FALSE != hr) + { + ExitOnFailure(hr, "Failed to select Update node."); + + pRegistration->update.fRegisterUpdate = TRUE; + + // @Manufacturer + hr = XmlGetAttributeEx(pixnUpdateNode, L"Manufacturer", &pRegistration->update.sczManufacturer); + ExitOnFailure(hr, "Failed to get @Manufacturer."); + + // @Department + hr = XmlGetAttributeEx(pixnUpdateNode, L"Department", &pRegistration->update.sczDepartment); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Department."); + } + + // @ProductFamily + hr = XmlGetAttributeEx(pixnUpdateNode, L"ProductFamily", &pRegistration->update.sczProductFamily); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @ProductFamily."); + } + + // @Name + hr = XmlGetAttributeEx(pixnUpdateNode, L"Name", &pRegistration->update.sczName); + ExitOnFailure(hr, "Failed to get @Name."); + + // @Classification + hr = XmlGetAttributeEx(pixnUpdateNode, L"Classification", &pRegistration->update.sczClassification); + ExitOnFailure(hr, "Failed to get @Classification."); + } + + hr = SetPaths(pRegistration); + ExitOnFailure(hr, "Failed to set registration paths."); + +LExit: + ReleaseObject(pixnRegistrationNode); + ReleaseObject(pixnArpNode); + ReleaseObject(pixnUpdateNode); + ReleaseStr(scz); + + return hr; +} + +/******************************************************************* + RegistrationUninitialize - + +*******************************************************************/ +extern "C" void RegistrationUninitialize( + __in BURN_REGISTRATION* pRegistration + ) +{ + ReleaseStr(pRegistration->sczId); + ReleaseStr(pRegistration->sczTag); + + for (DWORD i = 0; i < pRegistration->cDetectCodes; ++i) + { + ReleaseStr(pRegistration->rgsczDetectCodes[i]); + } + ReleaseMem(pRegistration->rgsczDetectCodes); + + for (DWORD i = 0; i < pRegistration->cUpgradeCodes; ++i) + { + ReleaseStr(pRegistration->rgsczUpgradeCodes[i]); + } + ReleaseMem(pRegistration->rgsczUpgradeCodes); + + for (DWORD i = 0; i < pRegistration->cAddonCodes; ++i) + { + ReleaseStr(pRegistration->rgsczAddonCodes[i]); + } + ReleaseMem(pRegistration->rgsczAddonCodes); + + for (DWORD i = 0; i < pRegistration->cPatchCodes; ++i) + { + ReleaseStr(pRegistration->rgsczPatchCodes[i]); + } + ReleaseMem(pRegistration->rgsczPatchCodes); + + ReleaseStr(pRegistration->sczProviderKey); + ReleaseStr(pRegistration->sczActiveParent); + ReleaseStr(pRegistration->sczExecutableName); + + ReleaseStr(pRegistration->sczRegistrationKey); + ReleaseStr(pRegistration->sczCacheExecutablePath); + ReleaseStr(pRegistration->sczResumeCommandLine); + ReleaseStr(pRegistration->sczStateFile); + + ReleaseStr(pRegistration->sczDisplayName); + ReleaseStr(pRegistration->sczDisplayVersion); + ReleaseStr(pRegistration->sczPublisher); + ReleaseStr(pRegistration->sczHelpLink); + ReleaseStr(pRegistration->sczHelpTelephone); + ReleaseStr(pRegistration->sczAboutUrl); + ReleaseStr(pRegistration->sczUpdateUrl); + ReleaseStr(pRegistration->sczParentDisplayName); + ReleaseStr(pRegistration->sczComments); + ReleaseStr(pRegistration->sczContact); + + ReleaseStr(pRegistration->update.sczManufacturer); + ReleaseStr(pRegistration->update.sczDepartment); + ReleaseStr(pRegistration->update.sczProductFamily); + ReleaseStr(pRegistration->update.sczName); + ReleaseStr(pRegistration->update.sczClassification); + + if (pRegistration->softwareTags.rgSoftwareTags) + { + for (DWORD i = 0; i < pRegistration->softwareTags.cSoftwareTags; ++i) + { + ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczFilename); + ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczRegid); + ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczTag); + } + + MemFree(pRegistration->softwareTags.rgSoftwareTags); + } + + ReleaseStr(pRegistration->sczDetectedProviderKeyBundleId); + ReleaseStr(pRegistration->sczAncestors); + RelatedBundlesUninitialize(&pRegistration->relatedBundles); + + // clear struct + memset(pRegistration, 0, sizeof(BURN_REGISTRATION)); +} + +/******************************************************************* + RegistrationSetVariables - Initializes bundle variables that map to + registration entities. + +*******************************************************************/ +extern "C" HRESULT RegistrationSetVariables( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + LPWSTR sczBundleManufacturer = NULL; + LPWSTR sczBundleName = NULL; + + if (pRegistration->fInstalled) + { + hr = VariableSetNumeric(pVariables, BURN_BUNDLE_INSTALLED, 1, TRUE); + ExitOnFailure(hr, "Failed to set the bundle installed built-in variable."); + } + + // Ensure the registration bundle name is updated. + hr = GetBundleName(pRegistration, pVariables, &sczBundleName); + ExitOnFailure(hr, "Failed to initialize bundle name."); + + hr = GetBundleManufacturer(pRegistration, pVariables, &sczBundleName); + ExitOnFailure(hr, "Failed to initialize bundle manufacturer."); + + if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent) + { + hr = VariableSetString(pVariables, BURN_BUNDLE_ACTIVE_PARENT, pRegistration->sczActiveParent, TRUE); + ExitOnFailure(hr, "Failed to overwrite the bundle active parent built-in variable."); + } + + hr = VariableSetString(pVariables, BURN_BUNDLE_PROVIDER_KEY, pRegistration->sczProviderKey, TRUE); + ExitOnFailure(hr, "Failed to overwrite the bundle provider key built-in variable."); + + hr = VariableSetString(pVariables, BURN_BUNDLE_TAG, pRegistration->sczTag, TRUE); + ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable."); + + hr = VariableSetVersion(pVariables, BURN_BUNDLE_VERSION, pRegistration->qwVersion, TRUE); + ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable."); + +LExit: + ReleaseStr(sczBundleManufacturer); + ReleaseStr(sczBundleName); + + return hr; +} + +extern "C" HRESULT RegistrationDetectInstalled( + __in BURN_REGISTRATION* pRegistration, + __out BOOL* pfInstalled + ) +{ + HRESULT hr = S_OK; + HKEY hkRegistration = NULL; + DWORD dwInstalled = 0; + + // open registration key + hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration); + if (SUCCEEDED(hr)) + { + hr = RegReadNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, &dwInstalled); + } + + // Not finding the key or value is okay. + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + hr = S_OK; + } + + *pfInstalled = (1 == dwInstalled); + + ReleaseRegKey(hkRegistration); + return hr; +} + +/******************************************************************* + RegistrationDetectResumeMode - Detects registration information on the system + to determine if a resume is taking place. + +*******************************************************************/ +extern "C" HRESULT RegistrationDetectResumeType( + __in BURN_REGISTRATION* pRegistration, + __out BOOTSTRAPPER_RESUME_TYPE* pResumeType + ) +{ + HRESULT hr = S_OK; + LPWSTR sczRebootRequiredKey = NULL; + HKEY hkRebootRequired = NULL; + HKEY hkRegistration = NULL; + DWORD dwResume = 0; + + // Check to see if a restart is pending for this bundle. + hr = StrAllocFormatted(&sczRebootRequiredKey, REGISTRY_REBOOT_PENDING_FORMAT, pRegistration->sczRegistrationKey); + ExitOnFailure(hr, "Failed to format pending restart registry key to read."); + + hr = RegOpen(pRegistration->hkRoot, sczRebootRequiredKey, KEY_QUERY_VALUE, &hkRebootRequired); + if (SUCCEEDED(hr)) + { + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING; + ExitFunction1(hr = S_OK); + } + + // open registration key + hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_NONE; + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to open registration key."); + + // read Resume value + hr = RegReadNumber(hkRegistration, L"Resume", &dwResume); + if (E_FILENOTFOUND == hr) + { + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INVALID; + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to read Resume value."); + + switch (dwResume) + { + case BURN_RESUME_MODE_ACTIVE: + // a previous run was interrupted + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INTERRUPTED; + break; + + case BURN_RESUME_MODE_SUSPEND: + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_SUSPEND; + break; + + case BURN_RESUME_MODE_ARP: + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_ARP; + break; + + case BURN_RESUME_MODE_REBOOT_PENDING: + // The volatile pending registry doesn't exist (checked above) which means + // the system was successfully restarted. + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_REBOOT; + break; + + default: + // the value stored in the registry is not valid + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INVALID; + break; + } + +LExit: + ReleaseRegKey(hkRegistration); + ReleaseRegKey(hkRebootRequired); + ReleaseStr(sczRebootRequiredKey); + + return hr; +} + +/******************************************************************* + RegistrationDetectRelatedBundles - finds the bundles with same + upgrade/detect/addon/patch codes. + +*******************************************************************/ +extern "C" HRESULT RegistrationDetectRelatedBundles( + __in BURN_REGISTRATION* pRegistration + ) +{ + HRESULT hr = S_OK; + + hr = RelatedBundlesInitializeForScope(TRUE, pRegistration, &pRegistration->relatedBundles); + ExitOnFailure(hr, "Failed to initialize per-machine related bundles."); + + hr = RelatedBundlesInitializeForScope(FALSE, pRegistration, &pRegistration->relatedBundles); + ExitOnFailure(hr, "Failed to initialize per-user related bundles."); + +LExit: + return hr; +} + +/******************************************************************* + RegistrationSessionBegin - Registers a run session on the system. + +*******************************************************************/ +extern "C" HRESULT RegistrationSessionBegin( + __in_z LPCWSTR wzEngineWorkingPath, + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in BURN_USER_EXPERIENCE* pUserExperience, + __in DWORD dwRegistrationOptions, + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction, + __in DWORD64 qwEstimatedSize + ) +{ + HRESULT hr = S_OK; + DWORD dwSize = 0; + HKEY hkRegistration = NULL; + LPWSTR sczPublisher = NULL; + + LogId(REPORT_VERBOSE, MSG_SESSION_BEGIN, pRegistration->sczRegistrationKey, dwRegistrationOptions, LoggingBoolToString(pRegistration->fDisableResume)); + + // Cache bundle executable. + if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE) + { + hr = CacheCompleteBundle(pRegistration->fPerMachine, pRegistration->sczExecutableName, pRegistration->sczId, &pUserExperience->payloads, wzEngineWorkingPath +#ifdef DEBUG + , pRegistration->sczCacheExecutablePath +#endif + ); + ExitOnFailure(hr, "Failed to cache bundle from path: %ls", wzEngineWorkingPath); + } + + // create registration key + hr = RegCreate(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration); + ExitOnFailure(hr, "Failed to create registration key."); + + // Write any ARP values and software tags. + if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION) + { + // Upgrade information + hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, pRegistration->sczCacheExecutablePath); + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH); + + hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, pRegistration->rgsczUpgradeCodes, pRegistration->cUpgradeCodes); + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE); + + hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, pRegistration->rgsczAddonCodes, pRegistration->cAddonCodes); + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE); + + hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, pRegistration->rgsczDetectCodes, pRegistration->cDetectCodes); + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE); + + hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, pRegistration->rgsczPatchCodes, pRegistration->cPatchCodes); + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE); + + hr = RegWriteStringFormatted(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, L"%hu.%hu.%hu.%hu", + static_cast(pRegistration->qwVersion >> 48), static_cast(pRegistration->qwVersion >> 32), + static_cast(pRegistration->qwVersion >> 16), static_cast(pRegistration->qwVersion)); + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION); + + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_VERSION_MAJOR, static_cast(pRegistration->qwVersion >> 48)); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_VERSION_MAJOR); + + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_VERSION_MINOR, static_cast(pRegistration->qwVersion >> 32)); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_VERSION_MINOR); + + if (pRegistration->sczProviderKey) + { + hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY, pRegistration->sczProviderKey); + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY); + } + + if (pRegistration->sczTag) + { + hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_TAG, pRegistration->sczTag); + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_TAG); + } + + hr = RegWriteStringFormatted(hkRegistration, BURN_REGISTRATION_REGISTRY_ENGINE_VERSION, L"%hs", szVerMajorMinorBuild); + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_ENGINE_VERSION); + + // DisplayIcon: [path to exe] and ",0" to refer to the first icon in the executable. + hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_DISPLAY_ICON, L"%s,0", pRegistration->sczCacheExecutablePath); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_DISPLAY_ICON); + + // update display name + hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration); + ExitOnFailure(hr, "Failed to update name and publisher."); + + // DisplayVersion: provided by UI + if (pRegistration->sczDisplayVersion) + { + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_DISPLAY_VERSION, pRegistration->sczDisplayVersion); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_DISPLAY_VERSION); + } + + // Publisher: provided by UI + hr = GetBundleManufacturer(pRegistration, pVariables, &sczPublisher); + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PUBLISHER, SUCCEEDED(hr) ? sczPublisher : pRegistration->sczPublisher); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PUBLISHER); + + // HelpLink: provided by UI + if (pRegistration->sczHelpLink) + { + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_HELP_LINK, pRegistration->sczHelpLink); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_HELP_LINK); + } + + // HelpTelephone: provided by UI + if (pRegistration->sczHelpTelephone) + { + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_HELP_TELEPHONE, pRegistration->sczHelpTelephone); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_HELP_TELEPHONE); + } + + // URLInfoAbout, provided by UI + if (pRegistration->sczAboutUrl) + { + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_URL_INFO_ABOUT, pRegistration->sczAboutUrl); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_URL_INFO_ABOUT); + } + + // URLUpdateInfo, provided by UI + if (pRegistration->sczUpdateUrl) + { + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_URL_UPDATE_INFO, pRegistration->sczUpdateUrl); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_URL_UPDATE_INFO); + } + + // ParentDisplayName + if (pRegistration->sczParentDisplayName) + { + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PARENT_DISPLAY_NAME, pRegistration->sczParentDisplayName); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PARENT_DISPLAY_NAME); + + // Need to write the ParentKeyName but can be set to anything. + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PARENT_KEY_NAME, pRegistration->sczParentDisplayName); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PARENT_KEY_NAME); + } + + // Comments, provided by UI + if (pRegistration->sczComments) + { + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_COMMENTS, pRegistration->sczComments); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_COMMENTS); + } + + // Contact, provided by UI + if (pRegistration->sczContact) + { + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_CONTACT, pRegistration->sczContact); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_CONTACT); + } + + // InstallLocation: provided by UI + // TODO: need to figure out what "InstallLocation" means in a chainer. + + // NoModify + if (BURN_REGISTRATION_MODIFY_DISABLE == pRegistration->modify) + { + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_MODIFY, 1); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_MODIFY); + } + else if (BURN_REGISTRATION_MODIFY_DISABLE_BUTTON != pRegistration->modify) // if support modify (aka: did not disable anything) + { + // ModifyPath: [path to exe] /modify + hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_MODIFY_PATH, L"\"%ls\" /modify", pRegistration->sczCacheExecutablePath); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_MODIFY_PATH); + + // NoElevateOnModify: 1 + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY, 1); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY); + } + + // NoRemove: should this be allowed? + if (pRegistration->fNoRemoveDefined) + { + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_REMOVE, (DWORD)pRegistration->fNoRemove); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_REMOVE); + } + + // Conditionally hide the ARP entry. + if (!pRegistration->fRegisterArp) + { + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_SYSTEM_COMPONENT, 1); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_SYSTEM_COMPONENT); + } + + // QuietUninstallString: [path to exe] /uninstall /quiet + hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING, L"\"%ls\" /uninstall /quiet", pRegistration->sczCacheExecutablePath); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING); + + // UninstallString, [path to exe] + // If the modify button is to be disabled, we'll add "/modify" to the uninstall string because the button is "Uninstall/Change". Otherwise, + // it's just the "Uninstall" button so we add "/uninstall" to make the program just go away. + LPCWSTR wzUninstallParameters = (BURN_REGISTRATION_MODIFY_DISABLE_BUTTON == pRegistration->modify) ? L"/modify" : L" /uninstall"; + hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_UNINSTALL_STRING, L"\"%ls\" %ls", pRegistration->sczCacheExecutablePath, wzUninstallParameters); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_UNINSTALL_STRING); + + if (pRegistration->softwareTags.cSoftwareTags) + { + hr = WriteSoftwareTags(pRegistration->fPerMachine, &pRegistration->softwareTags); + ExitOnFailure(hr, "Failed to write software tags."); + } + + // Update registration. + if (pRegistration->update.fRegisterUpdate) + { + hr = WriteUpdateRegistration(pRegistration, pVariables); + ExitOnFailure(hr, "Failed to write update registration."); + } + } + + // Update estimated size. + if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE) + { + qwEstimatedSize /= 1024; // Convert bytes to KB + if (0 < qwEstimatedSize) + { + if (DWORD_MAX < qwEstimatedSize) + { + // ARP doesn't support QWORDs here + dwSize = DWORD_MAX; + } + else + { + dwSize = static_cast(qwEstimatedSize); + } + + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_ESTIMATED_SIZE, dwSize); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_ESTIMATED_SIZE); + } + } + + // Register the bundle dependency key. + if (BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER == dependencyRegistrationAction) + { + hr = DependencyRegisterBundle(pRegistration); + ExitOnFailure(hr, "Failed to register the bundle dependency key."); + } + + // update resume mode + hr = UpdateResumeMode(pRegistration, hkRegistration, BURN_RESUME_MODE_ACTIVE, FALSE); + ExitOnFailure(hr, "Failed to update resume mode."); + +LExit: + ReleaseStr(sczPublisher); + ReleaseRegKey(hkRegistration); + + return hr; +} + + +/******************************************************************* + RegistrationSessionResume - Resumes a previous run session. + +*******************************************************************/ +extern "C" HRESULT RegistrationSessionResume( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + HKEY hkRegistration = NULL; + + // open registration key + hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration); + ExitOnFailure(hr, "Failed to open registration key."); + + // update resume mode + hr = UpdateResumeMode(pRegistration, hkRegistration, BURN_RESUME_MODE_ACTIVE, FALSE); + ExitOnFailure(hr, "Failed to update resume mode."); + + // update display name + hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration); + ExitOnFailure(hr, "Failed to update name and publisher."); + +LExit: + ReleaseRegKey(hkRegistration); + + return hr; +} + + +/******************************************************************* + RegistrationSessionEnd - Unregisters a run session from the system. + + *******************************************************************/ +extern "C" HRESULT RegistrationSessionEnd( + __in BURN_REGISTRATION* pRegistration, + __in BURN_RESUME_MODE resumeMode, + __in BOOTSTRAPPER_APPLY_RESTART restart, + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction + ) +{ + HRESULT hr = S_OK; + LPWSTR sczRebootRequiredKey = NULL; + HKEY hkRebootRequired = NULL; + HKEY hkRegistration = NULL; + + LogId(REPORT_STANDARD, MSG_SESSION_END, pRegistration->sczRegistrationKey, LoggingResumeModeToString(resumeMode), LoggingRestartToString(restart), LoggingBoolToString(pRegistration->fDisableResume)); + + // If a restart is required for any reason, write a volatile registry key to track of + // of that fact until the reboot has taken place. + if (BOOTSTRAPPER_APPLY_RESTART_NONE != restart) + { + // We'll write the volatile registry key right next to the bundle ARP registry key + // because that's easy. This is all best effort since the worst case just means in + // the rare case the user launches the same install again before taking the restart + // the BA won't know a restart was still required. + hr = StrAllocFormatted(&sczRebootRequiredKey, REGISTRY_REBOOT_PENDING_FORMAT, pRegistration->sczRegistrationKey); + if (SUCCEEDED(hr)) + { + hr = RegCreateEx(pRegistration->hkRoot, sczRebootRequiredKey, KEY_WRITE, TRUE, NULL, &hkRebootRequired, NULL); + } + + if (FAILED(hr)) + { + ExitTrace(hr, "Failed to write volatile reboot required registry key."); + hr = S_OK; + } + } + + // If no resume mode, then remove the bundle registration. + if (BURN_RESUME_MODE_NONE == resumeMode) + { + // If we just registered the bundle dependency but something went wrong and caused us to not + // keep the bundle registration (like rollback) or we are supposed to unregister the bundle + // dependency when unregistering the bundle, do so. + if (BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER == dependencyRegistrationAction || + BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER == dependencyRegistrationAction) + { + // Remove the bundle dependency key. + DependencyUnregisterBundle(pRegistration); + } + + // Delete update registration key. + if (pRegistration->update.fRegisterUpdate) + { + RemoveUpdateRegistration(pRegistration); + } + + RemoveSoftwareTags(pRegistration->fPerMachine, &pRegistration->softwareTags); + + // Delete registration key. + hr = RegDelete(pRegistration->hkRoot, pRegistration->sczRegistrationKey, REG_KEY_DEFAULT, FALSE); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to delete registration key: %ls", pRegistration->sczRegistrationKey); + } + + CacheRemoveBundle(pRegistration->fPerMachine, pRegistration->sczId); + } + else // the mode needs to be updated so open the registration key. + { + // Open registration key. + hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration); + ExitOnFailure(hr, "Failed to open registration key."); + } + + // Update resume mode. + hr = UpdateResumeMode(pRegistration, hkRegistration, resumeMode, BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart); + ExitOnFailure(hr, "Failed to update resume mode."); + +LExit: + ReleaseRegKey(hkRegistration); + ReleaseRegKey(hkRebootRequired); + ReleaseStr(sczRebootRequiredKey); + + return hr; +} + +/******************************************************************* + RegistrationSaveState - Saves an engine state BLOB for retreval after a resume. + +*******************************************************************/ +extern "C" HRESULT RegistrationSaveState( + __in BURN_REGISTRATION* pRegistration, + __in_bcount(cbBuffer) BYTE* pbBuffer, + __in SIZE_T cbBuffer + ) +{ + HRESULT hr = S_OK; + + // write data to file + hr = FileWrite(pRegistration->sczStateFile, FILE_ATTRIBUTE_NORMAL, pbBuffer, cbBuffer, NULL); + if (E_PATHNOTFOUND == hr) + { + // TODO: should we log that the bundle's cache folder was not present so the state file wasn't created either? + hr = S_OK; + } + ExitOnFailure(hr, "Failed to write state to file: %ls", pRegistration->sczStateFile); + +LExit: + return hr; +} + +/******************************************************************* + RegistrationLoadState - Loads a previously stored engine state BLOB. + +*******************************************************************/ +extern "C" HRESULT RegistrationLoadState( + __in BURN_REGISTRATION* pRegistration, + __out_bcount(*pcbBuffer) BYTE** ppbBuffer, + __out DWORD* pcbBuffer + ) +{ + // read data from file + HRESULT hr = FileRead(ppbBuffer, pcbBuffer, pRegistration->sczStateFile); + return hr; +} + +/******************************************************************* +RegistrationGetResumeCommandLine - Gets the resume command line from the registry + +*******************************************************************/ +extern "C" HRESULT RegistrationGetResumeCommandLine( + __in const BURN_REGISTRATION* pRegistration, + __deref_out_z LPWSTR* psczResumeCommandLine + ) +{ + HRESULT hr = S_OK; + HKEY hkRegistration = NULL; + + // open registration key + hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration); + if (SUCCEEDED(hr)) + { + hr = RegReadString(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE, psczResumeCommandLine); + } + + // Not finding the key or value is okay. + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + hr = S_OK; + } + + ReleaseRegKey(hkRegistration); + + return hr; +} + + +// internal helper functions + +static HRESULT ParseSoftwareTagsFromXml( + __in IXMLDOMNode* pixnRegistrationNode, + __out BURN_SOFTWARE_TAG** prgSoftwareTags, + __out DWORD* pcSoftwareTags + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + + BURN_SOFTWARE_TAG* pSoftwareTags = NULL; + BSTR bstrTagXml = NULL; + + // select tag nodes + hr = XmlSelectNodes(pixnRegistrationNode, L"SoftwareTag", &pixnNodes); + ExitOnFailure(hr, "Failed to select software tag nodes."); + + // get tag node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get software tag count."); + + if (cNodes) + { + pSoftwareTags = (BURN_SOFTWARE_TAG*)MemAlloc(sizeof(BURN_SOFTWARE_TAG) * cNodes, TRUE); + ExitOnNull(pSoftwareTags, hr, E_OUTOFMEMORY, "Failed to allocate memory for software tag structs."); + + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_SOFTWARE_TAG* pSoftwareTag = &pSoftwareTags[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + hr = XmlGetAttributeEx(pixnNode, L"Filename", &pSoftwareTag->sczFilename); + ExitOnFailure(hr, "Failed to get @Filename."); + + hr = XmlGetAttributeEx(pixnNode, L"Regid", &pSoftwareTag->sczRegid); + ExitOnFailure(hr, "Failed to get @Regid."); + + hr = XmlGetText(pixnNode, &bstrTagXml); + ExitOnFailure(hr, "Failed to get SoftwareTag text."); + + hr = StrAnsiAllocString(&pSoftwareTag->sczTag, bstrTagXml, 0, CP_UTF8); + ExitOnFailure(hr, "Failed to convert SoftwareTag text to UTF-8"); + + // prepare next iteration + ReleaseNullBSTR(bstrTagXml); + ReleaseNullObject(pixnNode); + } + } + + *pcSoftwareTags = cNodes; + *prgSoftwareTags = pSoftwareTags; + pSoftwareTags = NULL; + + hr = S_OK; + +LExit: + ReleaseBSTR(bstrTagXml); + ReleaseObject(pixnNode); + ReleaseObject(pixnNodes); + ReleaseMem(pSoftwareTags); + + return hr; +} + +static HRESULT SetPaths( + __in BURN_REGISTRATION* pRegistration + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCacheDirectory = NULL; + + // save registration key root + pRegistration->hkRoot = pRegistration->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + + // build uninstall registry key path + hr = StrAllocFormatted(&pRegistration->sczRegistrationKey, L"%s\\%s", BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, pRegistration->sczId); + ExitOnFailure(hr, "Failed to build uninstall registry key path."); + + // build cache directory + hr = CacheGetCompletedPath(pRegistration->fPerMachine, pRegistration->sczId, &sczCacheDirectory); + ExitOnFailure(hr, "Failed to build cache directory."); + + // build cached executable path + hr = PathConcat(sczCacheDirectory, pRegistration->sczExecutableName, &pRegistration->sczCacheExecutablePath); + ExitOnFailure(hr, "Failed to build cached executable path."); + + // build state file path + hr = StrAllocFormatted(&pRegistration->sczStateFile, L"%s\\state.rsm", sczCacheDirectory); + ExitOnFailure(hr, "Failed to build state file path."); + +LExit: + ReleaseStr(sczCacheDirectory); + return hr; +} + +static HRESULT GetBundleManufacturer( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __out LPWSTR* psczBundleManufacturer + ) +{ + HRESULT hr = S_OK; + + hr = VariableGetString(pVariables, BURN_BUNDLE_MANUFACTURER, psczBundleManufacturer); + if (E_NOTFOUND == hr) + { + hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_MANUFACTURER, pRegistration->sczPublisher, FALSE); + ExitOnFailure(hr, "Failed to set bundle manufacturer."); + + hr = StrAllocString(psczBundleManufacturer, pRegistration->sczPublisher, 0); + } + ExitOnFailure(hr, "Failed to get bundle manufacturer."); + +LExit: + return hr; +} + +static HRESULT GetBundleName( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __out LPWSTR* psczBundleName + ) +{ + HRESULT hr = S_OK; + + hr = VariableGetString(pVariables, BURN_BUNDLE_NAME, psczBundleName); + if (E_NOTFOUND == hr) + { + hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_NAME, pRegistration->sczDisplayName, FALSE); + ExitOnFailure(hr, "Failed to set bundle name."); + + hr = StrAllocString(psczBundleName, pRegistration->sczDisplayName, 0); + } + ExitOnFailure(hr, "Failed to get bundle name."); + +LExit: + return hr; +} + +static HRESULT UpdateResumeMode( + __in BURN_REGISTRATION* pRegistration, + __in HKEY hkRegistration, + __in BURN_RESUME_MODE resumeMode, + __in BOOL fRestartInitiated + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + HKEY hkRebootRequired = NULL; + HKEY hkRun = NULL; + LPWSTR sczResumeCommandLine = NULL; + LPCWSTR sczResumeKey = REGISTRY_RUN_ONCE_KEY; + OS_VERSION osv = OS_VERSION_UNKNOWN; + DWORD dwServicePack = 0; + + LogId(REPORT_STANDARD, MSG_SESSION_UPDATE, pRegistration->sczRegistrationKey, LoggingResumeModeToString(resumeMode), LoggingBoolToString(fRestartInitiated), LoggingBoolToString(pRegistration->fDisableResume)); + + // On Windows XP and Server 2003, write the resume information to the Run key + // instead of RunOnce. That avoids the problem that driver installation might + // trigger RunOnce commands to be executed before the reboot. + OsGetVersion(&osv, &dwServicePack); + if (osv < OS_VERSION_VISTA) + { + sczResumeKey = REGISTRY_RUN_KEY; + } + + // write resume information + if (hkRegistration) + { + // write Resume value + hr = RegWriteNumber(hkRegistration, L"Resume", (DWORD)resumeMode); + ExitOnFailure(hr, "Failed to write Resume value."); + + // Write the Installed value *only* when the mode is ARP. This will tell us + // that the bundle considers itself "installed" on the machine. Note that we + // never change the value to "0" after that. The bundle will be considered + // "uninstalled" when all of the registration is removed. + if (BURN_RESUME_MODE_ARP == resumeMode) + { + // Write Installed value. + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, 1); + ExitOnFailure(hr, "Failed to write Installed value."); + } + } + + // If the engine is active write the run key so we resume if there is an unexpected + // power loss. Also, if a restart was initiated in the middle of the chain then + // ensure the run key exists (it should since going active would have written it). + // Do not write the run key when embedded since the containing bundle + // is expected to detect for and restart the embedded bundle. + if ((BURN_RESUME_MODE_ACTIVE == resumeMode || fRestartInitiated) && !pRegistration->fDisableResume) + { + // append RunOnce switch + hr = StrAllocFormatted(&sczResumeCommandLine, L"\"%ls\" /%ls", pRegistration->sczCacheExecutablePath, BURN_COMMANDLINE_SWITCH_RUNONCE); + ExitOnFailure(hr, "Failed to format resume command line for RunOnce."); + + // write run key + hr = RegCreate(pRegistration->hkRoot, sczResumeKey, KEY_WRITE, &hkRun); + ExitOnFailure(hr, "Failed to create run key."); + + hr = RegWriteString(hkRun, pRegistration->sczId, sczResumeCommandLine); + ExitOnFailure(hr, "Failed to write run key value."); + + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE, pRegistration->sczResumeCommandLine); + ExitOnFailure(hr, "Failed to write resume command line value."); + } + else // delete run key value + { + hr = RegOpen(pRegistration->hkRoot, sczResumeKey, KEY_WRITE, &hkRun); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + hr = S_OK; + } + else + { + ExitOnWin32Error(er, hr, "Failed to open run key."); + + er = ::RegDeleteValueW(hkRun, pRegistration->sczId); + if (ERROR_FILE_NOT_FOUND == er) + { + er = ERROR_SUCCESS; + } + ExitOnWin32Error(er, hr, "Failed to delete run key value."); + } + + if (hkRegistration) + { + er = ::RegDeleteValueW(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE); + if (ERROR_FILE_NOT_FOUND == er) + { + er = ERROR_SUCCESS; + } + ExitOnWin32Error(er, hr, "Failed to delete resume command line value."); + } + } + +LExit: + ReleaseStr(sczResumeCommandLine); + ReleaseRegKey(hkRebootRequired); + ReleaseRegKey(hkRun); + + return hr; +} + +static HRESULT ParseRelatedCodes( + __in BURN_REGISTRATION* pRegistration, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnElement = NULL; + LPWSTR sczAction = NULL; + LPWSTR sczId = NULL; + DWORD cElements = 0; + + hr = XmlSelectNodes(pixnBundle, L"RelatedBundle", &pixnNodes); + ExitOnFailure(hr, "Failed to get RelatedBundle nodes"); + + hr = pixnNodes->get_length((long*)&cElements); + ExitOnFailure(hr, "Failed to get RelatedBundle element count."); + + for (DWORD i = 0; i < cElements; ++i) + { + hr = XmlNextElement(pixnNodes, &pixnElement, NULL); + ExitOnFailure(hr, "Failed to get next RelatedBundle element."); + + hr = XmlGetAttributeEx(pixnElement, L"Action", &sczAction); + ExitOnFailure(hr, "Failed to get @Action."); + + hr = XmlGetAttributeEx(pixnElement, L"Id", &sczId); + ExitOnFailure(hr, "Failed to get @Id."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Detect", -1)) + { + hr = MemEnsureArraySize(reinterpret_cast(&pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes + 1, sizeof(LPWSTR), 5); + ExitOnFailure(hr, "Failed to resize Detect code array in registration"); + + pRegistration->rgsczDetectCodes[pRegistration->cDetectCodes] = sczId; + sczId = NULL; + ++pRegistration->cDetectCodes; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Upgrade", -1)) + { + hr = MemEnsureArraySize(reinterpret_cast(&pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes + 1, sizeof(LPWSTR), 5); + ExitOnFailure(hr, "Failed to resize Upgrade code array in registration"); + + pRegistration->rgsczUpgradeCodes[pRegistration->cUpgradeCodes] = sczId; + sczId = NULL; + ++pRegistration->cUpgradeCodes; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Addon", -1)) + { + hr = MemEnsureArraySize(reinterpret_cast(&pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes + 1, sizeof(LPWSTR), 5); + ExitOnFailure(hr, "Failed to resize Addon code array in registration"); + + pRegistration->rgsczAddonCodes[pRegistration->cAddonCodes] = sczId; + sczId = NULL; + ++pRegistration->cAddonCodes; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Patch", -1)) + { + hr = MemEnsureArraySize(reinterpret_cast(&pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes + 1, sizeof(LPWSTR), 5); + ExitOnFailure(hr, "Failed to resize Patch code array in registration"); + + pRegistration->rgsczPatchCodes[pRegistration->cPatchCodes] = sczId; + sczId = NULL; + ++pRegistration->cPatchCodes; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Action: %ls", sczAction); + } + } + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnElement); + ReleaseStr(sczAction); + ReleaseStr(sczId); + + return hr; +} + +static HRESULT FormatUpdateRegistrationKey( + __in BURN_REGISTRATION* pRegistration, + __out_z LPWSTR* psczKey + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + + hr = StrAllocFormatted(&sczKey, L"SOFTWARE\\%ls\\Updates\\", pRegistration->update.sczManufacturer); + ExitOnFailure(hr, "Failed to format the key path for update registration."); + + if (pRegistration->update.sczProductFamily) + { + hr = StrAllocFormatted(&sczKey, L"%ls%ls\\", sczKey, pRegistration->update.sczProductFamily); + ExitOnFailure(hr, "Failed to format the key path for update registration."); + } + + hr = StrAllocConcat(&sczKey, pRegistration->update.sczName, 0); + ExitOnFailure(hr, "Failed to format the key path for update registration."); + + *psczKey = sczKey; + sczKey = NULL; + +LExit: + ReleaseStr(sczKey); + + return hr; +} + +static HRESULT WriteSoftwareTags( + __in BOOL fPerMachine, + __in BURN_SOFTWARE_TAGS* pSoftwareTags + ) +{ + HRESULT hr = S_OK; + LPWSTR sczRootFolder = NULL; + LPWSTR sczRegidFolder = NULL; + LPWSTR sczPath = NULL; + + hr = PathGetKnownFolder(fPerMachine ? CSIDL_COMMON_APPDATA : CSIDL_LOCAL_APPDATA, &sczRootFolder); + ExitOnFailure(hr, "Failed to find local %hs appdata directory.", fPerMachine ? "per-machine" : "per-user"); + + for (DWORD iTag = 0; iTag < pSoftwareTags->cSoftwareTags; ++iTag) + { + BURN_SOFTWARE_TAG* pSoftwareTag = pSoftwareTags->rgSoftwareTags + iTag; + + hr = PathConcat(sczRootFolder, pSoftwareTag->sczRegid, &sczRegidFolder); + ExitOnFailure(hr, "Failed to allocate regid folder path."); + + hr = PathConcat(sczRegidFolder, pSoftwareTag->sczFilename, &sczPath); + ExitOnFailure(hr, "Failed to allocate regid folder path."); + + hr = DirEnsureExists(sczRegidFolder, NULL); + ExitOnFailure(hr, "Failed to create regid folder: %ls", sczRegidFolder); + + hr = FileWrite(sczPath, FILE_ATTRIBUTE_NORMAL, reinterpret_cast(pSoftwareTag->sczTag), lstrlenA(pSoftwareTag->sczTag), NULL); + ExitOnFailure(hr, "Failed to write tag xml to file: %ls", sczPath); + } + +LExit: + ReleaseStr(sczPath); + ReleaseStr(sczRegidFolder); + ReleaseStr(sczRootFolder); + + return hr; +} + +static HRESULT RemoveSoftwareTags( + __in BOOL fPerMachine, + __in BURN_SOFTWARE_TAGS* pSoftwareTags + ) +{ + HRESULT hr = S_OK; + LPWSTR sczRootFolder = NULL; + LPWSTR sczRegidFolder = NULL; + LPWSTR sczPath = NULL; + + hr = PathGetKnownFolder(fPerMachine ? CSIDL_COMMON_APPDATA : CSIDL_LOCAL_APPDATA, &sczRootFolder); + ExitOnFailure(hr, "Failed to find local %hs appdata directory.", fPerMachine ? "per-machine" : "per-user"); + + for (DWORD iTag = 0; iTag < pSoftwareTags->cSoftwareTags; ++iTag) + { + BURN_SOFTWARE_TAG* pSoftwareTag = pSoftwareTags->rgSoftwareTags + iTag; + + hr = PathConcat(sczRootFolder, pSoftwareTag->sczRegid, &sczRegidFolder); + ExitOnFailure(hr, "Failed to allocate regid folder path."); + + hr = PathConcat(sczRegidFolder, pSoftwareTag->sczFilename, &sczPath); + ExitOnFailure(hr, "Failed to allocate regid folder path."); + + // Best effort to delete the software tag file and the regid folder. + FileEnsureDelete(sczPath); + + ::RemoveDirectoryW(sczRegidFolder); + } + +LExit: + ReleaseStr(sczPath); + ReleaseStr(sczRegidFolder); + ReleaseStr(sczRootFolder); + + return hr; +} + +static HRESULT WriteUpdateRegistration( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + HKEY hkKey = NULL; + + hr = FormatUpdateRegistrationKey(pRegistration, &sczKey); + ExitOnFailure(hr, "Failed to get the formatted key path for update registration."); + + hr = RegCreate(pRegistration->hkRoot, sczKey, KEY_WRITE, &hkKey); + ExitOnFailure(hr, "Failed to create the key for update registration."); + + hr = RegWriteString(hkKey, L"ThisVersionInstalled", L"Y"); + ExitOnFailure(hr, "Failed to write %ls value.", L"ThisVersionInstalled"); + + hr = RegWriteString(hkKey, L"PackageName", pRegistration->sczDisplayName); + ExitOnFailure(hr, "Failed to write %ls value.", L"PackageName"); + + hr = RegWriteString(hkKey, L"PackageVersion", pRegistration->sczDisplayVersion); + ExitOnFailure(hr, "Failed to write %ls value.", L"PackageVersion"); + + hr = RegWriteString(hkKey, L"Publisher", pRegistration->sczPublisher); + ExitOnFailure(hr, "Failed to write %ls value.", L"Publisher"); + + if (pRegistration->update.sczDepartment) + { + hr = RegWriteString(hkKey, L"PublishingGroup", pRegistration->update.sczDepartment); + ExitOnFailure(hr, "Failed to write %ls value.", L"PublishingGroup"); + } + + hr = RegWriteString(hkKey, L"ReleaseType", pRegistration->update.sczClassification); + ExitOnFailure(hr, "Failed to write %ls value.", L"ReleaseType"); + + hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_LOGONUSER, L"InstalledBy"); + ExitOnFailure(hr, "Failed to write %ls value.", L"InstalledBy"); + + hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_DATE, L"InstalledDate"); + ExitOnFailure(hr, "Failed to write %ls value.", L"InstalledDate"); + + hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_INSTALLERNAME, L"InstallerName"); + ExitOnFailure(hr, "Failed to write %ls value.", L"InstallerName"); + + hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_INSTALLERVERSION, L"InstallerVersion"); + ExitOnFailure(hr, "Failed to write %ls value.", L"InstallerVersion"); + +LExit: + ReleaseRegKey(hkKey); + ReleaseStr(sczKey); + + return hr; +} + +static HRESULT RemoveUpdateRegistration( + __in BURN_REGISTRATION* pRegistration + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + LPWSTR sczPackageVersion = NULL; + HKEY hkKey = NULL; + BOOL fDeleteRegKey = TRUE; + + hr = FormatUpdateRegistrationKey(pRegistration, &sczKey); + ExitOnFailure(hr, "Failed to format key for update registration."); + + // Only delete if the uninstalling bundle's PackageVersion is the same as the + // PackageVersion in the update registration key. + // This is to support build to build upgrades + hr = RegOpen(pRegistration->hkRoot, sczKey, KEY_QUERY_VALUE, &hkKey); + if (SUCCEEDED(hr)) + { + hr = RegReadString(hkKey, L"PackageVersion", &sczPackageVersion); + if (SUCCEEDED(hr)) + { + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczPackageVersion, -1, pRegistration->sczDisplayVersion, -1)) + { + fDeleteRegKey = FALSE; + } + } + ReleaseRegKey(hkKey); + } + + // Unable to open the key or read the value is okay. + hr = S_OK; + + if (fDeleteRegKey) + { + hr = RegDelete(pRegistration->hkRoot, sczKey, REG_KEY_DEFAULT, FALSE); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to remove update registration key: %ls", sczKey); + } + } + +LExit: + ReleaseStr(sczPackageVersion); + ReleaseStr(sczKey); + + return hr; +} + +static HRESULT RegWriteStringVariable( + __in HKEY hk, + __in BURN_VARIABLES* pVariables, + __in LPCWSTR wzVariable, + __in LPCWSTR wzName + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + hr = VariableGetString(pVariables, wzVariable, &sczValue); + ExitOnFailure(hr, "Failed to get the %ls variable.", wzVariable); + + hr = RegWriteString(hk, wzName, sczValue); + ExitOnFailure(hr, "Failed to write %ls value.", wzName); + +LExit: + StrSecureZeroFreeString(sczValue); + + return hr; +} + +static HRESULT UpdateBundleNameRegistration( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in HKEY hkRegistration + ) +{ + HRESULT hr = S_OK; + LPWSTR sczDisplayName = NULL; + + // DisplayName: provided by UI + hr = GetBundleName(pRegistration, pVariables, &sczDisplayName); + hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME, SUCCEEDED(hr) ? sczDisplayName : pRegistration->sczDisplayName); + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME); + +LExit: + ReleaseStr(sczDisplayName); + + return hr; +} diff --git a/src/engine/registration.h b/src/engine/registration.h new file mode 100644 index 00000000..c830a06d --- /dev/null +++ b/src/engine/registration.h @@ -0,0 +1,214 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +enum BURN_MODE; +enum BURN_DEPENDENCY_REGISTRATION_ACTION; +struct _BURN_LOGGING; +typedef _BURN_LOGGING BURN_LOGGING; + +// constants + +const LPCWSTR BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH = L"BundleCachePath"; +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE = L"BundleAddonCode"; +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE = L"BundleDetectCode"; +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE = L"BundlePatchCode"; +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode"; +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME = L"DisplayName"; +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION = L"BundleVersion"; +const LPCWSTR BURN_REGISTRATION_REGISTRY_ENGINE_VERSION = L"EngineVersion"; +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey"; +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_TAG = L"BundleTag"; + +enum BURN_RESUME_MODE +{ + BURN_RESUME_MODE_NONE, + BURN_RESUME_MODE_ACTIVE, + BURN_RESUME_MODE_SUSPEND, + BURN_RESUME_MODE_ARP, + BURN_RESUME_MODE_REBOOT_PENDING, +}; + +enum BURN_REGISTRATION_MODIFY_TYPE +{ + BURN_REGISTRATION_MODIFY_ENABLED, + BURN_REGISTRATION_MODIFY_DISABLE, + BURN_REGISTRATION_MODIFY_DISABLE_BUTTON, +}; + + +// structs + +typedef struct _BURN_UPDATE_REGISTRATION +{ + BOOL fRegisterUpdate; + LPWSTR sczManufacturer; + LPWSTR sczDepartment; + LPWSTR sczProductFamily; + LPWSTR sczName; + LPWSTR sczClassification; +} BURN_UPDATE_REGISTRATION; + +typedef struct _BURN_RELATED_BUNDLE +{ + BOOTSTRAPPER_RELATION_TYPE relationType; + + DWORD64 qwVersion; + LPWSTR sczTag; + + BURN_PACKAGE package; +} BURN_RELATED_BUNDLE; + +typedef struct _BURN_RELATED_BUNDLES +{ + BURN_RELATED_BUNDLE* rgRelatedBundles; + DWORD cRelatedBundles; +} BURN_RELATED_BUNDLES; + +typedef struct _BURN_SOFTWARE_TAG +{ + LPWSTR sczFilename; + LPWSTR sczRegid; + LPSTR sczTag; +} BURN_SOFTWARE_TAG; + +typedef struct _BURN_SOFTWARE_TAGS +{ + BURN_SOFTWARE_TAG* rgSoftwareTags; + DWORD cSoftwareTags; +} BURN_SOFTWARE_TAGS; + +typedef struct _BURN_REGISTRATION +{ + BOOL fPerMachine; + BOOL fRegisterArp; + BOOL fDisableResume; + BOOL fInstalled; + LPWSTR sczId; + LPWSTR sczTag; + + LPWSTR *rgsczDetectCodes; + DWORD cDetectCodes; + + LPWSTR *rgsczUpgradeCodes; + DWORD cUpgradeCodes; + + LPWSTR *rgsczAddonCodes; + DWORD cAddonCodes; + + LPWSTR *rgsczPatchCodes; + DWORD cPatchCodes; + + DWORD64 qwVersion; + LPWSTR sczActiveParent; + LPWSTR sczProviderKey; + LPWSTR sczExecutableName; + + // paths + HKEY hkRoot; + LPWSTR sczRegistrationKey; + LPWSTR sczCacheExecutablePath; + LPWSTR sczResumeCommandLine; + LPWSTR sczStateFile; + + // ARP registration + LPWSTR sczDisplayName; + LPWSTR sczDisplayVersion; + LPWSTR sczPublisher; + LPWSTR sczHelpLink; + LPWSTR sczHelpTelephone; + LPWSTR sczAboutUrl; + LPWSTR sczUpdateUrl; + LPWSTR sczParentDisplayName; + LPWSTR sczComments; + //LPWSTR sczReadme; // TODO: this would be a file path + LPWSTR sczContact; + //DWORD64 qwEstimatedSize; // TODO: size should come from disk cost calculation + BURN_REGISTRATION_MODIFY_TYPE modify; + BOOL fNoRemoveDefined; + BOOL fNoRemove; + + BURN_SOFTWARE_TAGS softwareTags; + + // Update registration + BURN_UPDATE_REGISTRATION update; + + // Only valid after detect. + BURN_RELATED_BUNDLES relatedBundles; + + LPWSTR sczDetectedProviderKeyBundleId; + LPWSTR sczAncestors; + + BOOL fEnabledForwardCompatibleBundle; + BURN_PACKAGE forwardCompatibleBundle; +} BURN_REGISTRATION; + + +// functions + +HRESULT RegistrationParseFromXml( + __in BURN_REGISTRATION* pRegistration, + __in IXMLDOMNode* pixnBundle + ); +void RegistrationUninitialize( + __in BURN_REGISTRATION* pRegistration + ); +HRESULT RegistrationSetVariables( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables + ); +HRESULT RegistrationDetectInstalled( + __in BURN_REGISTRATION* pRegistration, + __out BOOL* pfInstalled + ); +HRESULT RegistrationDetectResumeType( + __in BURN_REGISTRATION* pRegistration, + __out BOOTSTRAPPER_RESUME_TYPE* pResumeType + ); +HRESULT RegistrationDetectRelatedBundles( + __in BURN_REGISTRATION* pRegistration + ); +HRESULT RegistrationSessionBegin( + __in_z LPCWSTR wzEngineWorkingPath, + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in BURN_USER_EXPERIENCE* pUserExperience, + __in DWORD dwRegistrationOptions, + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction, + __in DWORD64 qwEstimatedSize + ); +HRESULT RegistrationSessionResume( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables + ); +HRESULT RegistrationSessionEnd( + __in BURN_REGISTRATION* pRegistration, + __in BURN_RESUME_MODE resumeMode, + __in BOOTSTRAPPER_APPLY_RESTART restart, + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction + ); +HRESULT RegistrationSaveState( + __in BURN_REGISTRATION* pRegistration, + __in_bcount_opt(cbBuffer) BYTE* pbBuffer, + __in_opt DWORD cbBuffer + ); +HRESULT RegistrationLoadState( + __in BURN_REGISTRATION* pRegistration, + __out_bcount(*pcbBuffer) BYTE** ppbBuffer, + __out DWORD* pcbBuffer + ); +HRESULT RegistrationGetResumeCommandLine( + __in const BURN_REGISTRATION* pRegistration, + __deref_out_z LPWSTR* psczResumeCommandLine + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/relatedbundle.cpp b/src/engine/relatedbundle.cpp new file mode 100644 index 00000000..87794177 --- /dev/null +++ b/src/engine/relatedbundle.cpp @@ -0,0 +1,457 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// internal function declarations + +static HRESULT LoadIfRelatedBundle( + __in BOOL fPerMachine, + __in HKEY hkUninstallKey, + __in_z LPCWSTR sczRelatedBundleId, + __in BURN_REGISTRATION* pRegistration, + __in BURN_RELATED_BUNDLES* pRelatedBundles + ); +static HRESULT DetermineRelationType( + __in HKEY hkBundleId, + __in BURN_REGISTRATION* pRegistration, + __out BOOTSTRAPPER_RELATION_TYPE* pRelationType + ); +static HRESULT LoadRelatedBundleFromKey( + __in_z LPCWSTR wzRelatedBundleId, + __in HKEY hkBundleId, + __in BOOL fPerMachine, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __inout BURN_RELATED_BUNDLE *pRelatedBundle + ); + + +// function definitions + +extern "C" HRESULT RelatedBundlesInitializeForScope( + __in BOOL fPerMachine, + __in BURN_REGISTRATION* pRegistration, + __in BURN_RELATED_BUNDLES* pRelatedBundles + ) +{ + HRESULT hr = S_OK; + HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + HKEY hkUninstallKey = NULL; + LPWSTR sczRelatedBundleId = NULL; + + hr = RegOpen(hkRoot, BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, &hkUninstallKey); + if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) + { + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to open uninstall registry key."); + + for (DWORD dwIndex = 0; /* exit via break below */; ++dwIndex) + { + hr = RegKeyEnum(hkUninstallKey, dwIndex, &sczRelatedBundleId); + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + break; + } + ExitOnFailure(hr, "Failed to enumerate uninstall key for related bundles."); + + // If we did not find our bundle id, try to load the subkey as a related bundle. + if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczRelatedBundleId, -1, pRegistration->sczId, -1)) + { + // Ignore failures here since we'll often find products that aren't actually + // related bundles (or even bundles at all). + HRESULT hrRelatedBundle = LoadIfRelatedBundle(fPerMachine, hkUninstallKey, sczRelatedBundleId, pRegistration, pRelatedBundles); + UNREFERENCED_PARAMETER(hrRelatedBundle); + } + } + +LExit: + ReleaseStr(sczRelatedBundleId); + ReleaseRegKey(hkUninstallKey); + + return hr; +} + +extern "C" void RelatedBundlesUninitialize( + __in BURN_RELATED_BUNDLES* pRelatedBundles + ) +{ + if (pRelatedBundles->rgRelatedBundles) + { + for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i) + { + PackageUninitialize(&pRelatedBundles->rgRelatedBundles[i].package); + ReleaseStr(pRelatedBundles->rgRelatedBundles[i].sczTag); + } + + MemFree(pRelatedBundles->rgRelatedBundles); + } + + memset(pRelatedBundles, 0, sizeof(BURN_RELATED_BUNDLES)); +} + + +// internal helper functions + +static HRESULT LoadIfRelatedBundle( + __in BOOL fPerMachine, + __in HKEY hkUninstallKey, + __in_z LPCWSTR sczRelatedBundleId, + __in BURN_REGISTRATION* pRegistration, + __in BURN_RELATED_BUNDLES* pRelatedBundles + ) +{ + HRESULT hr = S_OK; + HKEY hkBundleId = NULL; + BOOTSTRAPPER_RELATION_TYPE relationType = BOOTSTRAPPER_RELATION_NONE; + + hr = RegOpen(hkUninstallKey, sczRelatedBundleId, KEY_READ, &hkBundleId); + ExitOnFailure(hr, "Failed to open uninstall key for potential related bundle: %ls", sczRelatedBundleId); + + hr = DetermineRelationType(hkBundleId, pRegistration, &relationType); + if (FAILED(hr) || BOOTSTRAPPER_RELATION_NONE == relationType) + { + // Must not be a related bundle. + hr = E_NOTFOUND; + } + else // load the related bundle. + { + hr = MemEnsureArraySize(reinterpret_cast(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5); + ExitOnFailure(hr, "Failed to ensure there is space for related bundles."); + + BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles; + + hr = LoadRelatedBundleFromKey(sczRelatedBundleId, hkBundleId, fPerMachine, relationType, pRelatedBundle); + ExitOnFailure(hr, "Failed to initialize package from related bundle id: %ls", sczRelatedBundleId); + + ++pRelatedBundles->cRelatedBundles; + } + +LExit: + ReleaseRegKey(hkBundleId); + + return hr; +} + +static HRESULT DetermineRelationType( + __in HKEY hkBundleId, + __in BURN_REGISTRATION* pRegistration, + __out BOOTSTRAPPER_RELATION_TYPE* pRelationType + ) +{ + HRESULT hr = S_OK; + LPWSTR* rgsczUpgradeCodes = NULL; + DWORD cUpgradeCodes = 0; + STRINGDICT_HANDLE sdUpgradeCodes = NULL; + LPWSTR* rgsczAddonCodes = NULL; + DWORD cAddonCodes = 0; + STRINGDICT_HANDLE sdAddonCodes = NULL; + LPWSTR* rgsczDetectCodes = NULL; + DWORD cDetectCodes = 0; + STRINGDICT_HANDLE sdDetectCodes = NULL; + LPWSTR* rgsczPatchCodes = NULL; + DWORD cPatchCodes = 0; + STRINGDICT_HANDLE sdPatchCodes = NULL; + + *pRelationType = BOOTSTRAPPER_RELATION_NONE; + + // All remaining operations should treat all related bundles as non-vital. + hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes, &cUpgradeCodes); + if (HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE) == hr) + { + TraceError(hr, "Failed to read upgrade codes as REG_MULTI_SZ. Trying again as REG_SZ in case of older bundles."); + + rgsczUpgradeCodes = reinterpret_cast(MemAlloc(sizeof(LPWSTR), TRUE)); + ExitOnNull(rgsczUpgradeCodes, hr, E_OUTOFMEMORY, "Failed to allocate list for a single upgrade code from older bundle."); + + hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes[0]); + if (SUCCEEDED(hr)) + { + cUpgradeCodes = 1; + } + } + + // Compare upgrade codes. + if (SUCCEEDED(hr)) + { + hr = DictCreateStringListFromArray(&sdUpgradeCodes, rgsczUpgradeCodes, cUpgradeCodes, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "upgrade codes"); + + // Upgrade relationship: when their upgrade codes match our upgrade codes. + hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for upgrade code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_UPGRADE; + ExitFunction(); + } + + // Detect relationship: when their upgrade codes match our detect codes. + hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for detect code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_DETECT; + ExitFunction(); + } + + // Dependent relationship: when their upgrade codes match our addon codes. + hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for addon code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT; + ExitFunction(); + } + + // Dependent relationship: when their upgrade codes match our patch codes. + hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for addon code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT; + ExitFunction(); + } + + ReleaseNullDict(sdUpgradeCodes); + ReleaseNullStrArray(rgsczUpgradeCodes, cUpgradeCodes); + } + + // Compare addon codes. + hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, &rgsczAddonCodes, &cAddonCodes); + if (SUCCEEDED(hr)) + { + hr = DictCreateStringListFromArray(&sdAddonCodes, rgsczAddonCodes, cAddonCodes, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "addon codes"); + + // Addon relationship: when their addon codes match our detect codes. + hr = DictCompareStringListToArray(sdAddonCodes, const_cast(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for addon code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_ADDON; + ExitFunction(); + } + + // Addon relationship: when their addon codes match our upgrade codes. + hr = DictCompareStringListToArray(sdAddonCodes, const_cast(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for addon code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_ADDON; + ExitFunction(); + } + + ReleaseNullDict(sdAddonCodes); + ReleaseNullStrArray(rgsczAddonCodes, cAddonCodes); + } + + // Compare patch codes. + hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, &rgsczPatchCodes, &cPatchCodes); + if (SUCCEEDED(hr)) + { + hr = DictCreateStringListFromArray(&sdPatchCodes, rgsczPatchCodes, cPatchCodes, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "patch codes"); + + // Patch relationship: when their patch codes match our detect codes. + hr = DictCompareStringListToArray(sdPatchCodes, const_cast(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for patch code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_PATCH; + ExitFunction(); + } + + // Patch relationship: when their patch codes match our upgrade codes. + hr = DictCompareStringListToArray(sdPatchCodes, const_cast(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for patch code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_PATCH; + ExitFunction(); + } + + ReleaseNullDict(sdPatchCodes); + ReleaseNullStrArray(rgsczPatchCodes, cPatchCodes); + } + + // Compare detect codes. + hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, &rgsczDetectCodes, &cDetectCodes); + if (SUCCEEDED(hr)) + { + hr = DictCreateStringListFromArray(&sdDetectCodes, rgsczDetectCodes, cDetectCodes, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "detect codes"); + + // Detect relationship: when their detect codes match our detect codes. + hr = DictCompareStringListToArray(sdDetectCodes, const_cast(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for detect code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_DETECT; + ExitFunction(); + } + + // Dependent relationship: when their detect codes match our addon codes. + hr = DictCompareStringListToArray(sdDetectCodes, const_cast(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for addon code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT; + ExitFunction(); + } + + // Dependent relationship: when their detect codes match our patch codes. + hr = DictCompareStringListToArray(sdDetectCodes, const_cast(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for addon code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT; + ExitFunction(); + } + + ReleaseNullDict(sdDetectCodes); + ReleaseNullStrArray(rgsczDetectCodes, cDetectCodes); + } + +LExit: + if (SUCCEEDED(hr) && BOOTSTRAPPER_RELATION_NONE == *pRelationType) + { + hr = E_NOTFOUND; + } + + ReleaseDict(sdUpgradeCodes); + ReleaseStrArray(rgsczUpgradeCodes, cUpgradeCodes); + ReleaseDict(sdAddonCodes); + ReleaseStrArray(rgsczAddonCodes, cAddonCodes); + ReleaseDict(sdDetectCodes); + ReleaseStrArray(rgsczDetectCodes, cDetectCodes); + ReleaseDict(sdPatchCodes); + ReleaseStrArray(rgsczPatchCodes, cPatchCodes); + + return hr; +} + +static HRESULT LoadRelatedBundleFromKey( + __in_z LPCWSTR wzRelatedBundleId, + __in HKEY hkBundleId, + __in BOOL fPerMachine, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __inout BURN_RELATED_BUNDLE *pRelatedBundle + ) +{ + HRESULT hr = S_OK; + DWORD64 qwEngineVersion = 0; + LPWSTR sczCachePath = NULL; + DWORD64 qwFileSize = 0; + BURN_DEPENDENCY_PROVIDER dependencyProvider = { }; + + hr = RegReadVersion(hkBundleId, BURN_REGISTRATION_REGISTRY_ENGINE_VERSION, &qwEngineVersion); + if (FAILED(hr)) + { + qwEngineVersion = 0; + hr = S_OK; + } + + hr = RegReadVersion(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, &pRelatedBundle->qwVersion); + ExitOnFailure(hr, "Failed to read version from registry for bundle: %ls", wzRelatedBundleId); + + hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, &sczCachePath); + ExitOnFailure(hr, "Failed to read cache path from registry for bundle: %ls", wzRelatedBundleId); + + hr = FileSize(sczCachePath, reinterpret_cast(&qwFileSize)); + ExitOnFailure(hr, "Failed to get size of pseudo bundle: %ls", sczCachePath); + + hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY, &dependencyProvider.sczKey); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to read provider key from registry for bundle: %ls", wzRelatedBundleId); + + dependencyProvider.fImported = TRUE; + + hr = FileVersionToStringEx(pRelatedBundle->qwVersion, &dependencyProvider.sczVersion); + ExitOnFailure(hr, "Failed to copy version for bundle: %ls", wzRelatedBundleId); + + hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME, &dependencyProvider.sczDisplayName); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to copy display name for bundle: %ls", wzRelatedBundleId); + } + } + + hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_TAG, &pRelatedBundle->sczTag); + if (E_FILENOTFOUND == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to read tag from registry for bundle: %ls", wzRelatedBundleId); + + pRelatedBundle->relationType = relationType; + + hr = PseudoBundleInitialize(qwEngineVersion, &pRelatedBundle->package, fPerMachine, wzRelatedBundleId, pRelatedBundle->relationType, + BOOTSTRAPPER_PACKAGE_STATE_PRESENT, sczCachePath, sczCachePath, NULL, qwFileSize, FALSE, + L"-quiet", L"-repair -quiet", L"-uninstall -quiet", + (dependencyProvider.sczKey && *dependencyProvider.sczKey) ? &dependencyProvider : NULL, + NULL, 0); + ExitOnFailure(hr, "Failed to initialize related bundle to represent bundle: %ls", wzRelatedBundleId); + +LExit: + DependencyUninitialize(&dependencyProvider); + ReleaseStr(sczCachePath); + + return hr; +} diff --git a/src/engine/relatedbundle.h b/src/engine/relatedbundle.h new file mode 100644 index 00000000..01691c25 --- /dev/null +++ b/src/engine/relatedbundle.h @@ -0,0 +1,20 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +HRESULT RelatedBundlesInitializeForScope( + __in BOOL fPerMachine, + __in BURN_REGISTRATION* pRegistration, + __in BURN_RELATED_BUNDLES* pRelatedBundles + ); +void RelatedBundlesUninitialize( + __in BURN_RELATED_BUNDLES* pRelatedBundles + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/search.cpp b/src/engine/search.cpp new file mode 100644 index 00000000..c50790fd --- /dev/null +++ b/src/engine/search.cpp @@ -0,0 +1,1195 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// internal function declarations + +static HRESULT DirectorySearchExists( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT DirectorySearchPath( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT FileSearchExists( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT FileSearchVersion( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT FileSearchPath( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT RegistrySearchExists( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT RegistrySearchValue( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT MsiComponentSearch( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT MsiProductSearch( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT MsiFeatureSearch( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); + + +// function definitions + +extern "C" HRESULT SearchesParseFromXml( + __in BURN_SEARCHES* pSearches, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + BSTR bstrNodeName = NULL; + LPWSTR scz = NULL; + + // select search nodes + hr = XmlSelectNodes(pixnBundle, L"DirectorySearch|FileSearch|RegistrySearch|MsiComponentSearch|MsiProductSearch|MsiFeatureSearch", &pixnNodes); + ExitOnFailure(hr, "Failed to select search nodes."); + + // get search node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get search node count."); + + if (!cNodes) + { + ExitFunction(); + } + + // allocate memory for searches + pSearches->rgSearches = (BURN_SEARCH*)MemAlloc(sizeof(BURN_SEARCH) * cNodes, TRUE); + ExitOnNull(pSearches->rgSearches, hr, E_OUTOFMEMORY, "Failed to allocate memory for search structs."); + + pSearches->cSearches = cNodes; + + // parse search elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_SEARCH* pSearch = &pSearches->rgSearches[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &pSearch->sczKey); + ExitOnFailure(hr, "Failed to get @Id."); + + // @Variable + hr = XmlGetAttributeEx(pixnNode, L"Variable", &pSearch->sczVariable); + ExitOnFailure(hr, "Failed to get @Variable."); + + // @Condition + hr = XmlGetAttributeEx(pixnNode, L"Condition", &pSearch->sczCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Condition."); + } + + // read type specific attributes + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"DirectorySearch", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_DIRECTORY; + + // @Path + hr = XmlGetAttributeEx(pixnNode, L"Path", &pSearch->DirectorySearch.sczPath); + ExitOnFailure(hr, "Failed to get @Path."); + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1)) + { + pSearch->DirectorySearch.Type = BURN_DIRECTORY_SEARCH_TYPE_EXISTS; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"path", -1)) + { + pSearch->DirectorySearch.Type = BURN_DIRECTORY_SEARCH_TYPE_PATH; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"FileSearch", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_FILE; + + // @Path + hr = XmlGetAttributeEx(pixnNode, L"Path", &pSearch->FileSearch.sczPath); + ExitOnFailure(hr, "Failed to get @Path."); + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1)) + { + pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_EXISTS; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) + { + pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_VERSION; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"path", -1)) + { + pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_PATH; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"RegistrySearch", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_REGISTRY; + + // @Root + hr = XmlGetAttributeEx(pixnNode, L"Root", &scz); + ExitOnFailure(hr, "Failed to get @Root."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKCR", -1)) + { + pSearch->RegistrySearch.hRoot = HKEY_CLASSES_ROOT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKCU", -1)) + { + pSearch->RegistrySearch.hRoot = HKEY_CURRENT_USER; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKLM", -1)) + { + pSearch->RegistrySearch.hRoot = HKEY_LOCAL_MACHINE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKU", -1)) + { + pSearch->RegistrySearch.hRoot = HKEY_USERS; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Root: %ls", scz); + } + + // @Key + hr = XmlGetAttributeEx(pixnNode, L"Key", &pSearch->RegistrySearch.sczKey); + ExitOnFailure(hr, "Failed to get Key attribute."); + + // @Value + hr = XmlGetAttributeEx(pixnNode, L"Value", &pSearch->RegistrySearch.sczValue); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get Value attribute."); + } + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + hr = XmlGetYesNoAttribute(pixnNode, L"Win64", &pSearch->RegistrySearch.fWin64); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get Win64 attribute."); + } + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1)) + { + pSearch->RegistrySearch.Type = BURN_REGISTRY_SEARCH_TYPE_EXISTS; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"value", -1)) + { + pSearch->RegistrySearch.Type = BURN_REGISTRY_SEARCH_TYPE_VALUE; + + // @ExpandEnvironment + hr = XmlGetYesNoAttribute(pixnNode, L"ExpandEnvironment", &pSearch->RegistrySearch.fExpandEnvironment); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @ExpandEnvironment."); + } + + // @VariableType + hr = XmlGetAttributeEx(pixnNode, L"VariableType", &scz); + ExitOnFailure(hr, "Failed to get @VariableType."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) + { + pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_NUMERIC; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1)) + { + pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_STRING; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) + { + pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_VERSION; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @VariableType: %ls", scz); + } + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiComponentSearch", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_MSI_COMPONENT; + + // @ProductCode + hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiComponentSearch.sczProductCode); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @ProductCode."); + } + + // @ComponentId + hr = XmlGetAttributeEx(pixnNode, L"ComponentId", &pSearch->MsiComponentSearch.sczComponentId); + ExitOnFailure(hr, "Failed to get @ComponentId."); + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"keyPath", -1)) + { + pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1)) + { + pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_STATE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"directory", -1)) + { + pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiProductSearch", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_MSI_PRODUCT; + pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE; + + // @ProductCode (if we don't find a product code then look for an upgrade code) + hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiProductSearch.sczGuid); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @ProductCode."); + pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_PRODUCTCODE; + } + else + { + // @UpgradeCode + hr = XmlGetAttributeEx(pixnNode, L"UpgradeCode", &pSearch->MsiProductSearch.sczGuid); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @UpgradeCode."); + pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE; + } + } + + // make sure we found either a product or upgrade code + if (BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE == pSearch->MsiProductSearch.GuidType) + { + hr = E_NOTFOUND; + ExitOnFailure(hr, "Failed to get @ProductCode or @UpgradeCode."); + } + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) + { + pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"language", -1)) + { + pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1)) + { + pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_STATE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"assignment", -1)) + { + pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiFeatureSearch", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_MSI_FEATURE; + + // @ProductCode + hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiFeatureSearch.sczProductCode); + ExitOnFailure(hr, "Failed to get @ProductCode."); + + // @FeatureId + hr = XmlGetAttributeEx(pixnNode, L"FeatureId", &pSearch->MsiFeatureSearch.sczFeatureId); + ExitOnFailure(hr, "Failed to get @FeatureId."); + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1)) + { + pSearch->MsiFeatureSearch.Type = BURN_MSI_FEATURE_SEARCH_TYPE_STATE; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Unexpected element name: %ls", bstrNodeName); + } + + // prepare next iteration + ReleaseNullObject(pixnNode); + ReleaseNullBSTR(bstrNodeName); + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseBSTR(bstrNodeName); + ReleaseStr(scz); + return hr; +} + +extern "C" HRESULT SearchesExecute( + __in BURN_SEARCHES* pSearches, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + BOOL f = FALSE; + + for (DWORD i = 0; i < pSearches->cSearches; ++i) + { + BURN_SEARCH* pSearch = &pSearches->rgSearches[i]; + + // evaluate condition + if (pSearch->sczCondition && *pSearch->sczCondition) + { + hr = ConditionEvaluate(pVariables, pSearch->sczCondition, &f); + if (E_INVALIDDATA == hr) + { + TraceError(hr, "Failed to parse search condition. Id = '%ls', Condition = '%ls'", pSearch->sczKey, pSearch->sczCondition); + hr = S_OK; + continue; + } + ExitOnFailure(hr, "Failed to evaluate search condition. Id = '%ls', Condition = '%ls'", pSearch->sczKey, pSearch->sczCondition); + + if (!f) + { + continue; // condition evaluated to false, skip + } + } + + switch (pSearch->Type) + { + case BURN_SEARCH_TYPE_DIRECTORY: + switch (pSearch->DirectorySearch.Type) + { + case BURN_DIRECTORY_SEARCH_TYPE_EXISTS: + hr = DirectorySearchExists(pSearch, pVariables); + break; + case BURN_DIRECTORY_SEARCH_TYPE_PATH: + hr = DirectorySearchPath(pSearch, pVariables); + break; + default: + hr = E_UNEXPECTED; + } + break; + case BURN_SEARCH_TYPE_FILE: + switch (pSearch->FileSearch.Type) + { + case BURN_FILE_SEARCH_TYPE_EXISTS: + hr = FileSearchExists(pSearch, pVariables); + break; + case BURN_FILE_SEARCH_TYPE_VERSION: + hr = FileSearchVersion(pSearch, pVariables); + break; + case BURN_FILE_SEARCH_TYPE_PATH: + hr = FileSearchPath(pSearch, pVariables); + break; + default: + hr = E_UNEXPECTED; + } + break; + case BURN_SEARCH_TYPE_REGISTRY: + switch (pSearch->RegistrySearch.Type) + { + case BURN_REGISTRY_SEARCH_TYPE_EXISTS: + hr = RegistrySearchExists(pSearch, pVariables); + break; + case BURN_REGISTRY_SEARCH_TYPE_VALUE: + hr = RegistrySearchValue(pSearch, pVariables); + break; + default: + hr = E_UNEXPECTED; + } + break; + case BURN_SEARCH_TYPE_MSI_COMPONENT: + hr = MsiComponentSearch(pSearch, pVariables); + break; + case BURN_SEARCH_TYPE_MSI_PRODUCT: + hr = MsiProductSearch(pSearch, pVariables); + break; + case BURN_SEARCH_TYPE_MSI_FEATURE: + hr = MsiFeatureSearch(pSearch, pVariables); + break; + default: + hr = E_UNEXPECTED; + } + + if (FAILED(hr)) + { + TraceError(hr, "Search failed. Id = '%ls'", pSearch->sczKey); + continue; + } + } + + hr = S_OK; + +LExit: + return hr; +} + +extern "C" void SearchesUninitialize( + __in BURN_SEARCHES* pSearches + ) +{ + if (pSearches->rgSearches) + { + for (DWORD i = 0; i < pSearches->cSearches; ++i) + { + BURN_SEARCH* pSearch = &pSearches->rgSearches[i]; + + ReleaseStr(pSearch->sczKey); + ReleaseStr(pSearch->sczVariable); + ReleaseStr(pSearch->sczCondition); + + switch (pSearch->Type) + { + case BURN_SEARCH_TYPE_DIRECTORY: + ReleaseStr(pSearch->DirectorySearch.sczPath); + break; + case BURN_SEARCH_TYPE_FILE: + ReleaseStr(pSearch->FileSearch.sczPath); + break; + case BURN_SEARCH_TYPE_REGISTRY: + ReleaseStr(pSearch->RegistrySearch.sczKey); + ReleaseStr(pSearch->RegistrySearch.sczValue); + break; + case BURN_SEARCH_TYPE_MSI_COMPONENT: + ReleaseStr(pSearch->MsiComponentSearch.sczProductCode); + ReleaseStr(pSearch->MsiComponentSearch.sczComponentId); + break; + case BURN_SEARCH_TYPE_MSI_PRODUCT: + ReleaseStr(pSearch->MsiProductSearch.sczGuid); + break; + case BURN_SEARCH_TYPE_MSI_FEATURE: + ReleaseStr(pSearch->MsiFeatureSearch.sczProductCode); + ReleaseStr(pSearch->MsiFeatureSearch.sczFeatureId); + break; + } + } + MemFree(pSearches->rgSearches); + } +} + + +// internal function definitions + +static HRESULT DirectorySearchExists( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + BOOL fExists = FALSE; + + // format path + hr = VariableFormatString(pVariables, pSearch->DirectorySearch.sczPath, &sczPath, NULL); + ExitOnFailure(hr, "Failed to format variable string."); + + DWORD dwAttributes = ::GetFileAttributesW(sczPath); + if (INVALID_FILE_ATTRIBUTES == dwAttributes) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + hr = S_OK; // didn't find file, fExists still is false. + } + } + else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + fExists = TRUE; + } + + // else must have found a file. + // What if there is a hidden variable in sczPath? + ExitOnFailure(hr, "Failed while searching directory search: %ls, for path: %ls", pSearch->sczKey, sczPath); + + // set variable + hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE); + ExitOnFailure(hr, "Failed to set variable."); + +LExit: + StrSecureZeroFreeString(sczPath); + + return hr; +} + +static HRESULT DirectorySearchPath( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + + // format path + hr = VariableFormatString(pVariables, pSearch->DirectorySearch.sczPath, &sczPath, NULL); + ExitOnFailure(hr, "Failed to format variable string."); + + DWORD dwAttributes = ::GetFileAttributesW(sczPath); + if (INVALID_FILE_ATTRIBUTES == dwAttributes) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + } + else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE); + ExitOnFailure(hr, "Failed to set directory search path variable."); + } + else // must have found a file. + { + hr = E_PATHNOTFOUND; + } + + // What if there is a hidden variable in sczPath? + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + LogStringLine(REPORT_STANDARD, "Directory search: %ls, did not find path: %ls, reason: 0x%x", pSearch->sczKey, sczPath, hr); + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed while searching directory search: %ls, for path: %ls", pSearch->sczKey, sczPath); + +LExit: + StrSecureZeroFreeString(sczPath); + + return hr; +} + +static HRESULT FileSearchExists( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + LPWSTR sczPath = NULL; + BOOL fExists = FALSE; + + // format path + hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL); + ExitOnFailure(hr, "Failed to format variable string."); + + // find file + DWORD dwAttributes = ::GetFileAttributesW(sczPath); + if (INVALID_FILE_ATTRIBUTES == dwAttributes) + { + er = ::GetLastError(); + if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er) + { + // What if there is a hidden variable in sczPath? + LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath); + } + else + { + ExitOnWin32Error(er, hr, "Failed get to file attributes. '%ls'", pSearch->DirectorySearch.sczPath); + } + } + else if (FILE_ATTRIBUTE_DIRECTORY != (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + fExists = TRUE; + } + + // set variable + hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE); + ExitOnFailure(hr, "Failed to set variable."); + +LExit: + StrSecureZeroFreeString(sczPath); + return hr; +} + +static HRESULT FileSearchVersion( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + ULARGE_INTEGER uliVersion = { }; + LPWSTR sczPath = NULL; + + // format path + hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL); + ExitOnFailure(hr, "Failed to format path string."); + + // get file version + hr = FileVersion(sczPath, &uliVersion.HighPart, &uliVersion.LowPart); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + // What if there is a hidden variable in sczPath? + LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath); + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed get file version."); + + // set variable + hr = VariableSetVersion(pVariables, pSearch->sczVariable, uliVersion.QuadPart, FALSE); + ExitOnFailure(hr, "Failed to set variable."); + +LExit: + StrSecureZeroFreeString(sczPath); + return hr; +} + +static HRESULT FileSearchPath( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + + // format path + hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL); + ExitOnFailure(hr, "Failed to format variable string."); + + DWORD dwAttributes = ::GetFileAttributesW(sczPath); + if (INVALID_FILE_ATTRIBUTES == dwAttributes) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + } + else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) // found a directory. + { + hr = E_FILENOTFOUND; + } + else // found our file. + { + hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE); + ExitOnFailure(hr, "Failed to set variable to file search path."); + } + + // What if there is a hidden variable in sczPath? + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath); + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed while searching file search: %ls, for path: %ls", pSearch->sczKey, sczPath); + +LExit: + StrSecureZeroFreeString(sczPath); + + return hr; +} + +static HRESULT RegistrySearchExists( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + LPWSTR sczKey = NULL; + LPWSTR sczValue = NULL; + HKEY hKey = NULL; + DWORD dwType = 0; + BOOL fExists = FALSE; + REGSAM samDesired = KEY_QUERY_VALUE; + + if (pSearch->RegistrySearch.fWin64) + { + samDesired = samDesired | KEY_WOW64_64KEY; + } + + // format key string + hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczKey, &sczKey, NULL); + ExitOnFailure(hr, "Failed to format key string."); + + // open key + hr = RegOpen(pSearch->RegistrySearch.hRoot, sczKey, samDesired, &hKey); + if (SUCCEEDED(hr)) + { + fExists = TRUE; + } + else if (E_FILENOTFOUND == hr) + { + // What if there is a hidden variable in sczKey? + LogStringLine(REPORT_STANDARD, "Registry key not found. Key = '%ls'", sczKey); + fExists = FALSE; + hr = S_OK; + } + else + { + // What if there is a hidden variable in sczKey? + ExitOnFailure(hr, "Failed to open registry key. Key = '%ls'", sczKey); + } + + if (fExists && pSearch->RegistrySearch.sczValue) + { + // format value string + hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczValue, &sczValue, NULL); + ExitOnFailure(hr, "Failed to format value string."); + + // query value + er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, NULL, NULL); + switch (er) + { + case ERROR_SUCCESS: + fExists = TRUE; + break; + case ERROR_FILE_NOT_FOUND: + // What if there is a hidden variable in sczKey or sczValue? + LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue); + fExists = FALSE; + break; + default: + ExitOnWin32Error(er, hr, "Failed to query registry key value."); + } + } + + // set variable + hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE); + ExitOnFailure(hr, "Failed to set variable."); + +LExit: + if (FAILED(hr)) + { + // What if there is a hidden variable in sczKey? + LogStringLine(REPORT_STANDARD, "RegistrySearchExists failed: ID '%ls', HRESULT 0x%x", sczKey, hr); + } + + StrSecureZeroFreeString(sczKey); + StrSecureZeroFreeString(sczValue); + ReleaseRegKey(hKey); + + return hr; +} + +static HRESULT RegistrySearchValue( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + LPWSTR sczKey = NULL; + LPWSTR sczValue = NULL; + HKEY hKey = NULL; + DWORD dwType = 0; + DWORD cbData = 0; + LPBYTE pData = NULL; + DWORD cch = 0; + BURN_VARIANT value = { }; + REGSAM samDesired = KEY_QUERY_VALUE; + + if (pSearch->RegistrySearch.fWin64) + { + samDesired = samDesired | KEY_WOW64_64KEY; + } + + // format key string + hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczKey, &sczKey, NULL); + ExitOnFailure(hr, "Failed to format key string."); + + // format value string + if (pSearch->RegistrySearch.sczValue) + { + hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczValue, &sczValue, NULL); + ExitOnFailure(hr, "Failed to format value string."); + } + + // open key + hr = RegOpen(pSearch->RegistrySearch.hRoot, sczKey, samDesired, &hKey); + if (E_FILENOTFOUND == hr) + { + // What if there is a hidden variable in sczKey? + LogStringLine(REPORT_STANDARD, "Registry key not found. Key = '%ls'", sczKey); + hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value); + ExitOnFailure(hr, "Failed to clear variable."); + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to open registry key."); + + // get value + er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, NULL, &cbData); + if (ERROR_FILE_NOT_FOUND == er) + { + // What if there is a hidden variable in sczKey or sczValue? + LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue); + hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value); + ExitOnFailure(hr, "Failed to clear variable."); + ExitFunction1(hr = S_OK); + } + ExitOnWin32Error(er, hr, "Failed to query registry key value size."); + + pData = (LPBYTE)MemAlloc(cbData + sizeof(WCHAR), TRUE); // + sizeof(WCHAR) here to ensure that we always have a null terminator for REG_SZ + ExitOnNull(pData, hr, E_OUTOFMEMORY, "Failed to allocate memory registry value."); + + er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, pData, &cbData); + ExitOnWin32Error(er, hr, "Failed to query registry key value."); + + switch (dwType) + { + case REG_DWORD: + if (sizeof(LONG) != cbData) + { + ExitFunction1(hr = E_UNEXPECTED); + } + hr = BVariantSetNumeric(&value, *((LONG*)pData)); + break; + case REG_QWORD: + if (sizeof(LONGLONG) != cbData) + { + ExitFunction1(hr = E_UNEXPECTED); + } + hr = BVariantSetNumeric(&value, *((LONGLONG*)pData)); + break; + case REG_EXPAND_SZ: + if (pSearch->RegistrySearch.fExpandEnvironment) + { + hr = StrAlloc(&value.sczValue, cbData); + ExitOnFailure(hr, "Failed to allocate string buffer."); + value.Type = BURN_VARIANT_TYPE_STRING; + + cch = ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cbData); + if (cch > cbData) + { + hr = StrAlloc(&value.sczValue, cch); + ExitOnFailure(hr, "Failed to allocate string buffer."); + + if (cch != ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cch)) + { + ExitWithLastError(hr, "Failed to get expand environment string."); + } + } + break; + } + __fallthrough; + case REG_SZ: + hr = BVariantSetString(&value, (LPCWSTR)pData, 0); + break; + default: + ExitOnFailure(hr = E_NOTIMPL, "Unsupported registry key value type. Type = '%u'", dwType); + } + ExitOnFailure(hr, "Failed to read registry value."); + + // change value to requested type + hr = BVariantChangeType(&value, pSearch->RegistrySearch.VariableType); + ExitOnFailure(hr, "Failed to change value type."); + + // Set variable as a literal. + hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value); + ExitOnFailure(hr, "Failed to set variable."); + +LExit: + if (FAILED(hr)) + { + // What if there is a hidden variable in sczKey? + LogStringLine(REPORT_STANDARD, "RegistrySearchValue failed: ID '%ls', HRESULT 0x%x", sczKey, hr); + } + + StrSecureZeroFreeString(sczKey); + StrSecureZeroFreeString(sczValue); + ReleaseRegKey(hKey); + ReleaseMem(pData); + BVariantUninitialize(&value); + + return hr; +} + +static HRESULT MsiComponentSearch( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + INSTALLSTATE is = INSTALLSTATE_BROKEN; + LPWSTR sczComponentId = NULL; + LPWSTR sczProductCode = NULL; + LPWSTR sczPath = NULL; + + // format component id string + hr = VariableFormatString(pVariables, pSearch->MsiComponentSearch.sczComponentId, &sczComponentId, NULL); + ExitOnFailure(hr, "Failed to format component id string."); + + if (pSearch->MsiComponentSearch.sczProductCode) + { + // format product code string + hr = VariableFormatString(pVariables, pSearch->MsiComponentSearch.sczProductCode, &sczProductCode, NULL); + ExitOnFailure(hr, "Failed to format product code string."); + } + + if (sczProductCode) + { + hr = WiuGetComponentPath(sczProductCode, sczComponentId, &is, &sczPath); + } + else + { + hr = WiuLocateComponent(sczComponentId, &is, &sczPath); + } + + if (INSTALLSTATE_SOURCEABSENT == is) + { + is = INSTALLSTATE_SOURCE; + } + else if (INSTALLSTATE_UNKNOWN == is || INSTALLSTATE_NOTUSED == is) + { + is = INSTALLSTATE_ABSENT; + } + else if (INSTALLSTATE_ABSENT != is && INSTALLSTATE_LOCAL != is && INSTALLSTATE_SOURCE != is) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Failed to get component path: %d", is); + } + + // set variable + switch (pSearch->MsiComponentSearch.Type) + { + case BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH: + if (INSTALLSTATE_ABSENT == is || INSTALLSTATE_LOCAL == is || INSTALLSTATE_SOURCE == is) + { + hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE); + } + break; + case BURN_MSI_COMPONENT_SEARCH_TYPE_STATE: + hr = VariableSetNumeric(pVariables, pSearch->sczVariable, is, FALSE); + break; + case BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY: + if (INSTALLSTATE_ABSENT == is || INSTALLSTATE_LOCAL == is || INSTALLSTATE_SOURCE == is) + { + // remove file part from path, if any + LPWSTR wz = wcsrchr(sczPath, L'\\'); + if (wz) + { + wz[1] = L'\0'; + } + + hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE); + } + break; + } + ExitOnFailure(hr, "Failed to set variable."); + +LExit: + if (FAILED(hr)) + { + LogStringLine(REPORT_STANDARD, "MsiComponentSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr); + } + + StrSecureZeroFreeString(sczComponentId); + StrSecureZeroFreeString(sczProductCode); + ReleaseStr(sczPath); + return hr; +} + +static HRESULT MsiProductSearch( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + LPWSTR sczGuid = NULL; + LPCWSTR wzProperty = NULL; + LPWSTR *rgsczRelatedProductCodes = NULL; + DWORD dwRelatedProducts = 0; + BURN_VARIANT_TYPE type = BURN_VARIANT_TYPE_NONE; + BURN_VARIANT value = { }; + // We're not going to encrypt this value, so can access the value directly. + + switch (pSearch->MsiProductSearch.Type) + { + case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION: + wzProperty = INSTALLPROPERTY_VERSIONSTRING; + break; + case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE: + wzProperty = INSTALLPROPERTY_LANGUAGE; + break; + case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE: + wzProperty = INSTALLPROPERTY_PRODUCTSTATE; + break; + case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT: + wzProperty = INSTALLPROPERTY_ASSIGNMENTTYPE; + break; + default: + ExitOnFailure(hr = E_NOTIMPL, "Unsupported product search type: %u", pSearch->MsiProductSearch.Type); + } + + // format guid string + hr = VariableFormatString(pVariables, pSearch->MsiProductSearch.sczGuid, &sczGuid, NULL); + ExitOnFailure(hr, "Failed to format GUID string."); + + // get product info + value.Type = BURN_VARIANT_TYPE_STRING; + + // if this is an upgrade code then get the product code of the highest versioned related product + if (BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE == pSearch->MsiProductSearch.GuidType) + { + // WiuEnumRelatedProductCodes will log sczGuid on errors, what if there's a hidden variable in there? + hr = WiuEnumRelatedProductCodes(sczGuid, &rgsczRelatedProductCodes, &dwRelatedProducts, TRUE); + ExitOnFailure(hr, "Failed to enumerate related products for upgrade code."); + + // if we actually found a related product then use its upgrade code for the rest of the search + if (1 == dwRelatedProducts) + { + hr = StrAllocStringSecure(&sczGuid, rgsczRelatedProductCodes[0], 0); + ExitOnFailure(hr, "Failed to copy upgrade code."); + } + else + { + // set this here so we have a way of knowing that we don't need to bother + // querying for the product information below + hr = HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT); + } + } + + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr) + { + hr = WiuGetProductInfo(sczGuid, wzProperty, &value.sczValue); + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr) + { + // product state is available only through MsiGetProductInfoEx + // What if there is a hidden variable in sczGuid? + LogStringLine(REPORT_VERBOSE, "Trying per-machine extended info for property '%ls' for product: %ls", wzProperty, sczGuid); + hr = WiuGetProductInfoEx(sczGuid, NULL, MSIINSTALLCONTEXT_MACHINE, wzProperty, &value.sczValue); + + // if not in per-machine context, try per-user (unmanaged) + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr) + { + // What if there is a hidden variable in sczGuid? + LogStringLine(REPORT_STANDARD, "Trying per-user extended info for property '%ls' for product: %ls", wzProperty, sczGuid); + hr = WiuGetProductInfoEx(sczGuid, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, wzProperty, &value.sczValue); + } + } + } + + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr) + { + // What if there is a hidden variable in sczGuid? + LogStringLine(REPORT_STANDARD, "Product or related product not found: %ls", sczGuid); + + // set value to indicate absent + switch (pSearch->MsiProductSearch.Type) + { + case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT: __fallthrough; + case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION: + value.Type = BURN_VARIANT_TYPE_NUMERIC; + value.llValue = 0; + break; + case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE: + // is supposed to remain empty + break; + case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE: + value.Type = BURN_VARIANT_TYPE_NUMERIC; + value.llValue = INSTALLSTATE_ABSENT; + break; + } + + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get product info."); + + // change value type + switch (pSearch->MsiProductSearch.Type) + { + case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION: + type = BURN_VARIANT_TYPE_VERSION; + break; + case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE: + type = BURN_VARIANT_TYPE_STRING; + break; + case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE: __fallthrough; + case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT: + type = BURN_VARIANT_TYPE_NUMERIC; + break; + } + hr = BVariantChangeType(&value, type); + ExitOnFailure(hr, "Failed to change value type."); + + // Set variable as a literal. + hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value); + ExitOnFailure(hr, "Failed to set variable."); + +LExit: + if (FAILED(hr)) + { + LogStringLine(REPORT_STANDARD, "MsiProductSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr); + } + + StrSecureZeroFreeString(sczGuid); + ReleaseStrArray(rgsczRelatedProductCodes, dwRelatedProducts); + BVariantUninitialize(&value); + + return hr; +} + +static HRESULT MsiFeatureSearch( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* /*pVariables*/ + ) +{ + HRESULT hr = E_NOTIMPL; + +//LExit: + if (FAILED(hr)) + { + LogStringLine(REPORT_STANDARD, "MsiFeatureSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr); + } + + return hr; +} diff --git a/src/engine/search.h b/src/engine/search.h new file mode 100644 index 00000000..65dfb18f --- /dev/null +++ b/src/engine/search.h @@ -0,0 +1,152 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + +enum BURN_SEARCH_TYPE +{ + BURN_SEARCH_TYPE_NONE, + BURN_SEARCH_TYPE_DIRECTORY, + BURN_SEARCH_TYPE_FILE, + BURN_SEARCH_TYPE_REGISTRY, + BURN_SEARCH_TYPE_MSI_COMPONENT, + BURN_SEARCH_TYPE_MSI_PRODUCT, + BURN_SEARCH_TYPE_MSI_FEATURE, +}; + +enum BURN_DIRECTORY_SEARCH_TYPE +{ + BURN_DIRECTORY_SEARCH_TYPE_NONE, + BURN_DIRECTORY_SEARCH_TYPE_EXISTS, + BURN_DIRECTORY_SEARCH_TYPE_PATH, +}; + +enum BURN_FILE_SEARCH_TYPE +{ + BURN_FILE_SEARCH_TYPE_NONE, + BURN_FILE_SEARCH_TYPE_EXISTS, + BURN_FILE_SEARCH_TYPE_VERSION, + BURN_FILE_SEARCH_TYPE_PATH, +}; + +enum BURN_REGISTRY_SEARCH_TYPE +{ + BURN_REGISTRY_SEARCH_TYPE_NONE, + BURN_REGISTRY_SEARCH_TYPE_EXISTS, + BURN_REGISTRY_SEARCH_TYPE_VALUE, +}; + +enum BURN_MSI_COMPONENT_SEARCH_TYPE +{ + BURN_MSI_COMPONENT_SEARCH_TYPE_NONE, + BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH, + BURN_MSI_COMPONENT_SEARCH_TYPE_STATE, + BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY, +}; + +enum BURN_MSI_PRODUCT_SEARCH_TYPE +{ + BURN_MSI_PRODUCT_SEARCH_TYPE_NONE, + BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION, + BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE, + BURN_MSI_PRODUCT_SEARCH_TYPE_STATE, + BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT, +}; + +enum BURN_MSI_PRODUCT_SEARCH_GUID_TYPE +{ + BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE, + BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_PRODUCTCODE, + BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE +}; + +enum BURN_MSI_FEATURE_SEARCH_TYPE +{ + BURN_MSI_FEATURE_SEARCH_TYPE_NONE, + BURN_MSI_FEATURE_SEARCH_TYPE_STATE, +}; + + +// structs + +typedef struct _BURN_SEARCH +{ + LPWSTR sczKey; + LPWSTR sczVariable; + LPWSTR sczCondition; + + BURN_SEARCH_TYPE Type; + union + { + struct + { + BURN_DIRECTORY_SEARCH_TYPE Type; + LPWSTR sczPath; + } DirectorySearch; + struct + { + BURN_FILE_SEARCH_TYPE Type; + LPWSTR sczPath; + } FileSearch; + struct + { + BURN_REGISTRY_SEARCH_TYPE Type; + BURN_VARIANT_TYPE VariableType; + HKEY hRoot; + LPWSTR sczKey; + LPWSTR sczValue; + BOOL fWin64; + BOOL fExpandEnvironment; + } RegistrySearch; + struct + { + BURN_MSI_COMPONENT_SEARCH_TYPE Type; + LPWSTR sczProductCode; + LPWSTR sczComponentId; + } MsiComponentSearch; + struct + { + BURN_MSI_PRODUCT_SEARCH_TYPE Type; + BURN_MSI_PRODUCT_SEARCH_GUID_TYPE GuidType; + LPWSTR sczGuid; + } MsiProductSearch; + struct + { + BURN_MSI_FEATURE_SEARCH_TYPE Type; + LPWSTR sczProductCode; + LPWSTR sczFeatureId; + } MsiFeatureSearch; + }; +} BURN_SEARCH; + +typedef struct _BURN_SEARCHES +{ + BURN_SEARCH* rgSearches; + DWORD cSearches; +} BURN_SEARCHES; + + +// function declarations + +HRESULT SearchesParseFromXml( + __in BURN_SEARCHES* pSearches, + __in IXMLDOMNode* pixnBundle + ); +HRESULT SearchesExecute( + __in BURN_SEARCHES* pSearches, + __in BURN_VARIABLES* pVariables + ); +void SearchesUninitialize( + __in BURN_SEARCHES* pSearches + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/section.cpp b/src/engine/section.cpp new file mode 100644 index 00000000..3adcdd14 --- /dev/null +++ b/src/engine/section.cpp @@ -0,0 +1,399 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// constants + +// If these defaults ever change, be sure to update constants in burn\stub\StubSection.cpp as well. +#define BURN_SECTION_NAME ".wixburn" +#define BURN_SECTION_MAGIC 0x00f14300 +#define BURN_SECTION_VERSION 0x00000002 +#define MANIFEST_CABINET_TOKEN L"0" + +// structs +typedef struct _BURN_SECTION_HEADER +{ + DWORD dwMagic; + DWORD dwVersion; + + GUID guidBundleId; + + DWORD dwStubSize; + DWORD dwOriginalChecksum; + DWORD dwOriginalSignatureOffset; + DWORD dwOriginalSignatureSize; + + DWORD dwFormat; + DWORD cContainers; + DWORD rgcbContainers[1]; +} BURN_SECTION_HEADER; + +static HRESULT VerifySectionMatchesMemoryPEHeader( + __in REFGUID pSection + ); + + +extern "C" HRESULT SectionInitialize( + __in BURN_SECTION* pSection, + __in HANDLE hEngineFile, + __in HANDLE hSourceEngineFile + ) +{ + HRESULT hr = S_OK; + DWORD cbRead = 0; + LARGE_INTEGER li = { }; + LONGLONG llSize = 0; + IMAGE_DOS_HEADER dosHeader = { }; + IMAGE_NT_HEADERS ntHeader = { }; + DWORD dwChecksumOffset = 0; + DWORD dwCertificateTableOffset = 0; + DWORD dwSignatureOffset = 0; + DWORD cbSignature = 0; + IMAGE_SECTION_HEADER sectionHeader = { }; + DWORD dwOriginalChecksumAndSignatureOffset = 0; + BURN_SECTION_HEADER* pBurnSectionHeader = NULL; + + pSection->hEngineFile = hEngineFile; + ExitOnInvalidHandleWithLastError(pSection->hEngineFile, hr, "Failed to open handle to engine process path."); + + pSection->hSourceEngineFile = INVALID_HANDLE_VALUE == hSourceEngineFile ? hEngineFile : hSourceEngineFile; + + // + // First, make sure we have a valid DOS signature. + // + if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to seek to start of file."); + } + + // read DOS header + if (!::ReadFile(pSection->hEngineFile, &dosHeader, sizeof(IMAGE_DOS_HEADER), &cbRead, NULL)) + { + ExitWithLastError(hr, "Failed to read DOS header."); + } + else if (sizeof(IMAGE_DOS_HEADER) > cbRead || IMAGE_DOS_SIGNATURE != dosHeader.e_magic) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find valid DOS image header in buffer."); + } + + // + // Now, make sure we have a valid NT signature. + // + + // seek to new header + li.QuadPart = dosHeader.e_lfanew; + if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to seek to NT header."); + } + + // read NT header + if (!::ReadFile(pSection->hEngineFile, &ntHeader, sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER), &cbRead, NULL)) + { + ExitWithLastError(hr, "Failed to read NT header."); + } + else if ((sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER)) > cbRead || IMAGE_NT_SIGNATURE != ntHeader.Signature) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find valid NT image header in buffer."); + } + + // Get the table offsets. + dwChecksumOffset = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + (sizeof(DWORD) * 16); + dwCertificateTableOffset = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - (sizeof(IMAGE_DATA_DIRECTORY) * (IMAGE_NUMBEROF_DIRECTORY_ENTRIES - IMAGE_DIRECTORY_ENTRY_SECURITY)); + + // Seek into the certificate table to get the signature size. + li.QuadPart = dwCertificateTableOffset; + if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to seek to section info."); + } + + if (!::ReadFile(pSection->hEngineFile, &dwSignatureOffset, sizeof(dwSignatureOffset), &cbRead, NULL)) + { + ExitWithLastError(hr, "Failed to read signature offset."); + } + + if (!::ReadFile(pSection->hEngineFile, &cbSignature, sizeof(cbSignature), &cbRead, NULL)) + { + ExitWithLastError(hr, "Failed to read signature size."); + } + + // + // Finally, get into the section table and look for the Burn section info. + // + + // seek past optional headers + li.QuadPart = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + ntHeader.FileHeader.SizeOfOptionalHeader; + if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to seek past optional headers."); + } + + // read sections one by one until we find our section + for (DWORD i = 0; ; ++i) + { + // read section + if (!::ReadFile(pSection->hEngineFile, §ionHeader, sizeof(IMAGE_SECTION_HEADER), &cbRead, NULL)) + { + ExitWithLastError(hr, "Failed to read image section header, index: %u", i); + } + if (sizeof(IMAGE_SECTION_HEADER) > cbRead) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to read complete image section header, index: %u", i); + } + + // compare header name + C_ASSERT(sizeof(sectionHeader.Name) == sizeof(BURN_SECTION_NAME) - 1); + if (0 == memcmp(sectionHeader.Name, BURN_SECTION_NAME, sizeof(sectionHeader.Name))) + { + break; + } + + // fail if we hit the end + if (i + 1 >= ntHeader.FileHeader.NumberOfSections) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find Burn section."); + } + } + + // + // We've arrived at the section info. + // + + // check size of section + if (sizeof(BURN_SECTION_HEADER) > sectionHeader.SizeOfRawData) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to read section info, data to short: %u", sectionHeader.SizeOfRawData); + } + + // allocate buffer for section info + pBurnSectionHeader = (BURN_SECTION_HEADER*)MemAlloc(sectionHeader.SizeOfRawData, TRUE); + ExitOnNull(pBurnSectionHeader, hr, E_OUTOFMEMORY, "Failed to allocate buffer for section info."); + + // seek to section info + li.QuadPart = sectionHeader.PointerToRawData; + if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to seek to section info."); + } + + // Note the location of original checksum and signature information in the burn section header. + dwOriginalChecksumAndSignatureOffset = sectionHeader.PointerToRawData + (reinterpret_cast(&pBurnSectionHeader->dwOriginalChecksum) - reinterpret_cast(pBurnSectionHeader)); + + // read section info + if (!::ReadFile(pSection->hEngineFile, pBurnSectionHeader, sectionHeader.SizeOfRawData, &cbRead, NULL)) + { + ExitWithLastError(hr, "Failed to read section info."); + } + else if (sectionHeader.SizeOfRawData > cbRead) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to read complete section info."); + } + + // validate version of section info + if (BURN_SECTION_VERSION != pBurnSectionHeader->dwVersion) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to read section info, unsupported version: %08x", pBurnSectionHeader->dwVersion); + } + + hr = FileSizeByHandle(pSection->hSourceEngineFile, &llSize); + ExitOnFailure(hr, "Failed to get total size of bundle."); + + pSection->cbStub = pBurnSectionHeader->dwStubSize; + + // If there is an original signature use that to determine the engine size. + if (pBurnSectionHeader->dwOriginalSignatureOffset) + { + pSection->cbEngineSize = pBurnSectionHeader->dwOriginalSignatureOffset + pBurnSectionHeader->dwOriginalSignatureSize; + } + else if (dwSignatureOffset) // if there is a signature, use it. + { + pSection->cbEngineSize = dwSignatureOffset + cbSignature; + } + else // just use the stub and UX container as the size of the engine. + { + pSection->cbEngineSize = pSection->cbStub + pBurnSectionHeader->rgcbContainers[0]; + } + + pSection->qwBundleSize = static_cast(llSize); + + pSection->dwChecksumOffset = dwChecksumOffset; + pSection->dwCertificateTableOffset = dwCertificateTableOffset; + pSection->dwOriginalChecksumAndSignatureOffset = dwOriginalChecksumAndSignatureOffset; + + pSection->dwOriginalChecksum = pBurnSectionHeader->dwOriginalChecksum; + pSection->dwOriginalSignatureOffset = pBurnSectionHeader->dwOriginalSignatureOffset; + pSection->dwOriginalSignatureSize = pBurnSectionHeader->dwOriginalSignatureSize; + + pSection->dwFormat = pBurnSectionHeader->dwFormat; + pSection->cContainers = pBurnSectionHeader->cContainers; + pSection->rgcbContainers = (DWORD*)MemAlloc(sizeof(DWORD) * pSection->cContainers, TRUE); + ExitOnNull(pSection->rgcbContainers, hr, E_OUTOFMEMORY, "Failed to allocate memory for container sizes."); + + memcpy(pSection->rgcbContainers, pBurnSectionHeader->rgcbContainers, sizeof(DWORD) * pSection->cContainers); + + // TODO: verify more than just the GUID. + hr = VerifySectionMatchesMemoryPEHeader(pBurnSectionHeader->guidBundleId); + ExitOnRootFailure(hr, "PE Header from file didn't match PE Header in memory."); + +LExit: + ReleaseMem(pBurnSectionHeader); + + return hr; +} + +extern "C" void SectionUninitialize( + __out BURN_SECTION* pSection + ) +{ + ReleaseMem(pSection->rgcbContainers); + memset(pSection, 0, sizeof(BURN_SECTION)); +} + +extern "C" HRESULT SectionGetAttachedContainerInfo( + __in BURN_SECTION* pSection, + __in DWORD iContainerIndex, + __in DWORD dwExpectedType, + __out DWORD64* pqwOffset, + __out DWORD64* pqwSize, + __out BOOL* pfPresent + ) +{ + HRESULT hr = S_OK; + + // validate container info + if (iContainerIndex >= pSection->cContainers) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find container info, too few elements: %u", pSection->cContainers); + } + else if (dwExpectedType != pSection->dwFormat) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Unexpected container format."); + } + + // If we are asking for the UX container, find it right after the stub. + if (0 == iContainerIndex) + { + *pqwOffset = pSection->cbStub; + } + else // attached containers start after the whole engine. + { + *pqwOffset = pSection->cbEngineSize; + for (DWORD i = 1; i < iContainerIndex; ++i) + { + *pqwOffset += pSection->rgcbContainers[i]; + } + } + + *pqwSize = pSection->rgcbContainers[iContainerIndex]; + *pfPresent = (*pqwOffset + *pqwSize) <= pSection->qwBundleSize; + + AssertSz(*pfPresent || pSection->qwBundleSize <= *pqwOffset, "An attached container should either be present or completely absent from the bundle. Found a case where the attached container is partially present which is wrong."); + +LExit: + return hr; +} + +HRESULT VerifySectionMatchesMemoryPEHeader( + __in REFGUID pBundleId + ) +{ + HRESULT hr = S_OK; + BYTE* pbPEHeader = NULL; + PIMAGE_DOS_HEADER pDosHeader = NULL; + PIMAGE_NT_HEADERS pNtHeader = NULL; + PIMAGE_SECTION_HEADER pSections = NULL; + PIMAGE_SECTION_HEADER pSectionHeader = NULL; + BURN_SECTION_HEADER* pBurnSectionHeader = NULL; + + pbPEHeader = reinterpret_cast(::GetModuleHandleW(NULL)); + ExitOnNullWithLastError(pbPEHeader, hr, "Failed to get module handle to process."); + + // + // First, make sure we have a valid DOS signature. + // + + pDosHeader = reinterpret_cast(pbPEHeader); + if (IMAGE_DOS_SIGNATURE != pDosHeader->e_magic) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find valid DOS image header in buffer."); + } + + // + // Now, make sure we have a valid NT signature. + // + + pNtHeader = reinterpret_cast(pbPEHeader + pDosHeader->e_lfanew); + if (IMAGE_NT_SIGNATURE != pNtHeader->Signature) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find valid NT image header in buffer."); + } + + // + // Finally, get into the section table and look for the Burn section info. + // + + pSections = reinterpret_cast(pbPEHeader + pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + pNtHeader->FileHeader.SizeOfOptionalHeader); + + // Read sections one by one until we find our section. + for (DWORD i = 0; ; ++i) + { + pSectionHeader = pSections + i; + + // Compare header name. + C_ASSERT(sizeof(pSectionHeader->Name) == sizeof(BURN_SECTION_NAME) - 1); + if (0 == memcmp(pSectionHeader->Name, BURN_SECTION_NAME, sizeof(pSectionHeader->Name))) + { + break; + } + + // Fail if we hit the end. + if (i + 1 >= pNtHeader->FileHeader.NumberOfSections) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find Burn section."); + } + } + + // + // We've arrived at the section info. + // + + // Check size of section. + if (sizeof(BURN_SECTION_HEADER) > pSectionHeader->SizeOfRawData) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to read section info, data to short: %u", pSectionHeader->SizeOfRawData); + } + + // Get Burn section info. + pBurnSectionHeader = reinterpret_cast(pbPEHeader + pSectionHeader->VirtualAddress); + + // Validate version of section info. + if (BURN_SECTION_VERSION != pBurnSectionHeader->dwVersion) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to read section info, unsupported version: %08x", pBurnSectionHeader->dwVersion); + } + + if (!::IsEqualGUID(pBundleId, pBurnSectionHeader->guidBundleId)) + { + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Bundle guid didn't match the guid in the PE Header in memory."); + } + +LExit: + return hr; +} diff --git a/src/engine/section.h b/src/engine/section.h new file mode 100644 index 00000000..78331469 --- /dev/null +++ b/src/engine/section.h @@ -0,0 +1,54 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// structs + +typedef struct _BURN_SECTION +{ + HANDLE hEngineFile; + HANDLE hSourceEngineFile; + + DWORD cbStub; + DWORD cbEngineSize; // stub + UX container + original certficiate + DWORD64 qwBundleSize; // stub + UX container + original certificate [+ attached containers* + final certificate] + + DWORD dwChecksumOffset; + DWORD dwCertificateTableOffset; + DWORD dwOriginalChecksumAndSignatureOffset; + + DWORD dwOriginalChecksum; + DWORD dwOriginalSignatureOffset; + DWORD dwOriginalSignatureSize; + + DWORD dwFormat; + DWORD cContainers; + DWORD* rgcbContainers; +} BURN_SECTION; + + +HRESULT SectionInitialize( + __in BURN_SECTION* pSection, + __in HANDLE hEngineFile, + __in HANDLE hSourceEngineFile + ); +void SectionUninitialize( + __in BURN_SECTION* pSection + ); +HRESULT SectionGetAttachedContainerInfo( + __in BURN_SECTION* pSection, + __in DWORD iContainerIndex, + __in DWORD dwExpectedType, + __out DWORD64* pqwOffset, + __out DWORD64* pqwSize, + __out BOOL* pfPresent + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/splashscreen.cpp b/src/engine/splashscreen.cpp new file mode 100644 index 00000000..1f95886a --- /dev/null +++ b/src/engine/splashscreen.cpp @@ -0,0 +1,316 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace Gdiplus; + +#define BURN_SPLASHSCREEN_CLASS_WINDOW L"WixBurnSplashScreen" +#define IDB_SPLASHSCREEN 1 + +// struct + +struct SPLASHSCREEN_INFO +{ + Bitmap* pBitmap; + Point pt; + Size size; +}; + +struct SPLASHSCREEN_CONTEXT +{ + HANDLE hIntializedEvent; + HINSTANCE hInstance; + LPCWSTR wzCaption; + + HWND* pHwnd; +}; + +// internal function definitions + +static DWORD WINAPI ThreadProc( + __in LPVOID pvContext + ); +static LRESULT CALLBACK WndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ); +static void OnPaint( + __in HDC hdc, + __in SPLASHSCREEN_INFO* pSplashScreen + ); +static HRESULT LoadSplashScreen( + __in HMODULE hInstance, + __in SPLASHSCREEN_INFO* pSplashScreen + ); + + +// function definitions + +extern "C" void SplashScreenCreate( + __in HINSTANCE hInstance, + __in_z_opt LPCWSTR wzCaption, + __out HWND* pHwnd + ) +{ + HRESULT hr = S_OK; + SPLASHSCREEN_CONTEXT context = { }; + HANDLE rgSplashScreenEvents[2] = { }; + DWORD dwSplashScreenThreadId = 0; + + rgSplashScreenEvents[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL); + ExitOnNullWithLastError(rgSplashScreenEvents[0], hr, "Failed to create modal event."); + + // create splash screen thread. + context.hIntializedEvent = rgSplashScreenEvents[0]; + context.hInstance = hInstance; + context.wzCaption = wzCaption; + context.pHwnd = pHwnd; + + rgSplashScreenEvents[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, &dwSplashScreenThreadId); + ExitOnNullWithLastError(rgSplashScreenEvents[1], hr, "Failed to create UI thread."); + + // It doesn't really matter if the thread gets initialized (WAIT_OBJECT_0) or fails and exits + // prematurely (WAIT_OBJECT_0 + 1), we just want to wait long enough for one of those two + // events to happen. + ::WaitForMultipleObjects(countof(rgSplashScreenEvents), rgSplashScreenEvents, FALSE, INFINITE); + +LExit: + ReleaseHandle(rgSplashScreenEvents[1]); + ReleaseHandle(rgSplashScreenEvents[0]); +} + +extern "C" HRESULT SplashScreenDisplayError( + __in BOOTSTRAPPER_DISPLAY display, + __in_z LPCWSTR wzBundleName, + __in HRESULT hrError + ) +{ + HRESULT hr = S_OK; + LPWSTR sczDisplayString = NULL; + + hr = StrAllocFromError(&sczDisplayString, hrError, NULL); + ExitOnFailure(hr, "Failed to allocate string to display error message"); + + Trace(REPORT_STANDARD, "Error message displayed because: %ls", sczDisplayString); + + if (BOOTSTRAPPER_DISPLAY_NONE == display || BOOTSTRAPPER_DISPLAY_PASSIVE == display || BOOTSTRAPPER_DISPLAY_EMBEDDED == display) + { + // Don't display the error dialog in these modes + ExitFunction1(hr = S_OK); + } + + ::MessageBoxW(NULL, sczDisplayString, wzBundleName, MB_OK | MB_ICONERROR | MB_SYSTEMMODAL); + +LExit: + ReleaseStr(sczDisplayString); + + return hr; +} + + +static DWORD WINAPI ThreadProc( + __in LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + + ULONG_PTR token = 0; + GdiplusStartupInput input; + GdiplusStartupOutput output = { }; + + SPLASHSCREEN_CONTEXT* pContext = static_cast(pvContext); + SPLASHSCREEN_INFO splashScreen = { }; + + WNDCLASSW wc = { }; + BOOL fRegistered = TRUE; + HWND hWnd = NULL; + + BOOL fRet = FALSE; + MSG msg = { }; + + input.GdiplusVersion = 1; + + hr = GdipInitialize(&input, &token, &output); + ExitOnFailure(hr, "Failed to initialize GDI+."); + + hr = LoadSplashScreen(pContext->hInstance, &splashScreen); + ExitOnFailure(hr, "Failed to load splash screen."); + + // Register the window class and create the window. + wc.lpfnWndProc = WndProc; + wc.hInstance = pContext->hInstance; + wc.hCursor = ::LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); + wc.lpszClassName = BURN_SPLASHSCREEN_CLASS_WINDOW; + if (!::RegisterClassW(&wc)) + { + ExitWithLastError(hr, "Failed to register window."); + } + + fRegistered = TRUE; + + hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, wc.lpszClassName, pContext->wzCaption, WS_POPUP | WS_VISIBLE, splashScreen.pt.X, splashScreen.pt.Y, splashScreen.size.Width, splashScreen.size.Height, HWND_DESKTOP, NULL, pContext->hInstance, &splashScreen); + ExitOnNullWithLastError(hWnd, hr, "Failed to create window."); + + // Return the splash screen window and free the main thread waiting for us to be initialized. + *pContext->pHwnd = hWnd; + ::SetEvent(pContext->hIntializedEvent); + + // Pump messages until the bootstrapper application destroys the window. + while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) + { + if (-1 == fRet) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Unexpected return value from message pump."); + } + else if (!::IsDialogMessageW(hWnd, &msg)) + { + ::TranslateMessage(&msg); + ::DispatchMessageW(&msg); + } + } + +LExit: + if (fRegistered) + { + ::UnregisterClassW(BURN_SPLASHSCREEN_CLASS_WINDOW, pContext->hInstance); + } + + if (splashScreen.pBitmap) + { + delete splashScreen.pBitmap; + } + + if (token) + { + GdipUninitialize(token); + } + + return hr; +} + +static LRESULT CALLBACK WndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + LRESULT lres = 0; + SPLASHSCREEN_INFO* pSplashScreen = reinterpret_cast(::GetWindowLongW(hWnd, GWLP_USERDATA)); + + switch (uMsg) + { + case WM_NCCREATE: + { + LPCREATESTRUCTW lpcs = reinterpret_cast(lParam); + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(lpcs->lpCreateParams)); + } + break; + + case WM_NCDESTROY: + lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam); + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); + return lres; + + case WM_NCHITTEST: + return HTCAPTION; // allow window to be moved by grabbing any pixel. + + case WM_DESTROY: + ::PostQuitMessage(0); + return 0; + + case WM_ERASEBKGND: + // The splash screen image will be repainted in its entirety. + return 1; + + case WM_PAINT: + { + PAINTSTRUCT ps = { }; + + HDC hdc = BeginPaint(hWnd, &ps); + OnPaint(hdc, pSplashScreen); + EndPaint(hWnd, &ps); + } + return 0; + } + + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + +static void OnPaint( + __in HDC hdc, + __in SPLASHSCREEN_INFO* pSplashScreen + ) +{ + // Use high-quality bicubuc stretching from GDI+ which looks better than GDI. + Graphics graphics(hdc); + graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic); + + Rect dst(0, 0, pSplashScreen->size.Width, pSplashScreen->size.Height); + Status status = graphics.DrawImage(pSplashScreen->pBitmap, dst); + +#if DEBUG + HRESULT hr = GdipHresultFromStatus(status); + TraceError(hr, "Failed to draw splash screen bitmap."); +#else + UNREFERENCED_PARAMETER(status); +#endif +} + +static HRESULT LoadSplashScreen( + __in HMODULE hInstance, + __in SPLASHSCREEN_INFO* pSplashScreen + ) +{ + HRESULT hr = S_OK; + POINT ptCursor = { }; + HMONITOR hMonitor = NULL; + MONITORINFOEXW mi; + HDC hdc = NULL; + UINT dpiX = 0; + UINT dpiY = 0; + + pSplashScreen->pBitmap = Bitmap::FromResource(hInstance, MAKEINTRESOURCEW(IDB_SPLASHSCREEN)); + ExitOnNull(pSplashScreen->pBitmap, hr, E_INVALIDDATA, "Failed to find the splash screen bitmap."); + ExitOnGdipFailure(pSplashScreen->pBitmap->GetLastStatus(), hr, "Failed to load the splash screen bitmap."); + + pSplashScreen->pt.X = CW_USEDEFAULT; + pSplashScreen->pt.Y = CW_USEDEFAULT; + pSplashScreen->size.Width = pSplashScreen->pBitmap->GetWidth(); + pSplashScreen->size.Height = pSplashScreen->pBitmap->GetHeight(); + + // Stretch and center the window on the monitor with the mouse. + if (::GetCursorPos(&ptCursor)) + { + hMonitor = ::MonitorFromPoint(ptCursor, MONITOR_DEFAULTTONEAREST); + if (hMonitor) + { + ZeroMemory(&mi, sizeof(mi)); + mi.cbSize = sizeof(mi); + + if (::GetMonitorInfoW(hMonitor, &mi)) + { + hdc = ::CreateDCW(L"DISPLAY", mi.szDevice, NULL, NULL); + if (hdc) + { + dpiX = ::GetDeviceCaps(hdc, LOGPIXELSX); + dpiY = ::GetDeviceCaps(hdc, LOGPIXELSY); + + pSplashScreen->size.Width = pSplashScreen->size.Width * dpiX / 96; + pSplashScreen->size.Height = pSplashScreen->size.Height * dpiY / 96; + + ::ReleaseDC(NULL, hdc); + } + + pSplashScreen->pt.X = mi.rcWork.left + (mi.rcWork.right - mi.rcWork.left - pSplashScreen->size.Width) / 2; + pSplashScreen->pt.Y = mi.rcWork.top + (mi.rcWork.bottom - mi.rcWork.top - pSplashScreen->size.Height) / 2; + } + } + } + +LExit: + return hr; +} diff --git a/src/engine/splashscreen.h b/src/engine/splashscreen.h new file mode 100644 index 00000000..8f8817c7 --- /dev/null +++ b/src/engine/splashscreen.h @@ -0,0 +1,31 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + + +// structs + + +// functions + +void SplashScreenCreate( + __in HINSTANCE hInstance, + __in_z_opt LPCWSTR wzCaption, + __out HWND* pHwnd + ); +HRESULT SplashScreenDisplayError( + __in BOOTSTRAPPER_DISPLAY display, + __in_z LPCWSTR wzBundleName, + __in HRESULT hrError + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/uithread.cpp b/src/engine/uithread.cpp new file mode 100644 index 00000000..cf111745 --- /dev/null +++ b/src/engine/uithread.cpp @@ -0,0 +1,220 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#define BURN_UITHREAD_CLASS_WINDOW L"WixBurnMessageWindow" + + +// structs + +struct UITHREAD_CONTEXT +{ + HANDLE hInitializedEvent; + HINSTANCE hInstance; + BURN_ENGINE_STATE* pEngineState; +}; + +struct UITHREAD_INFO +{ + BOOL fElevated; + BURN_USER_EXPERIENCE* pUserExperience; +}; + + +// internal function declarations + +static DWORD WINAPI ThreadProc( + __in LPVOID pvContext + ); + +static LRESULT CALLBACK WndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ); + + +// function definitions + +HRESULT UiCreateMessageWindow( + __in HINSTANCE hInstance, + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + HANDLE rgWaitHandles[2] = { }; + UITHREAD_CONTEXT context = { }; + + // Create event to signal after the UI thread / window is initialized. + rgWaitHandles[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL); + ExitOnNullWithLastError(rgWaitHandles[0], hr, "Failed to create initialization event."); + + // Pass necessary information to create the window. + context.hInitializedEvent = rgWaitHandles[0]; + context.hInstance = hInstance; + context.pEngineState = pEngineState; + + // Create our separate UI thread. + rgWaitHandles[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, NULL); + ExitOnNullWithLastError(rgWaitHandles[1], hr, "Failed to create the UI thread."); + + // Wait for either the thread to be initialized or the window to exit / fail prematurely. + ::WaitForMultipleObjects(countof(rgWaitHandles), rgWaitHandles, FALSE, INFINITE); + + pEngineState->hMessageWindowThread = rgWaitHandles[1]; + rgWaitHandles[1] = NULL; + +LExit: + ReleaseHandle(rgWaitHandles[1]); + ReleaseHandle(rgWaitHandles[0]); + + return hr; +} + +void UiCloseMessageWindow( + __in BURN_ENGINE_STATE* pEngineState + ) +{ + if (::IsWindow(pEngineState->hMessageWindow)) + { + ::PostMessageW(pEngineState->hMessageWindow, WM_CLOSE, 0, 0); + + // Give the window 15 seconds to close because if it stays open it can prevent + // the engine from starting a reboot (should a reboot actually be necessary). + ::WaitForSingleObject(pEngineState->hMessageWindowThread, 15 * 1000); + } +} + + +// internal function definitions + +static DWORD WINAPI ThreadProc( + __in LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + UITHREAD_CONTEXT* pContext = static_cast(pvContext); + UITHREAD_INFO info = { }; + + WNDCLASSW wc = { }; + BOOL fRegistered = TRUE; + HWND hWnd = NULL; + + BOOL fRet = FALSE; + MSG msg = { }; + + BURN_ENGINE_STATE* pEngineState = pContext->pEngineState; + BOOL fElevated = BURN_MODE_ELEVATED == pContext->pEngineState->mode; + + // If elevated, set up the thread local storage to store the correct pipe to communicate logging. + if (fElevated) + { + Assert(TLS_OUT_OF_INDEXES != pEngineState->dwElevatedLoggingTlsId); + + if (!::TlsSetValue(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe)) + { + // If the function failed we cannot write to the pipe so just terminate. + ExitFunction1(hr = E_INVALIDSTATE); + } + } + + wc.lpfnWndProc = WndProc; + wc.hInstance = pContext->hInstance; + wc.lpszClassName = BURN_UITHREAD_CLASS_WINDOW; + + if (!::RegisterClassW(&wc)) + { + ExitWithLastError(hr, "Failed to register window."); + } + + fRegistered = TRUE; + + info.fElevated = fElevated; + info.pUserExperience = &pEngineState->userExperience; + + // Create the window to handle reboots without activating it. + hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, wc.lpszClassName, NULL, WS_POPUP | WS_VISIBLE, CW_USEDEFAULT, SW_SHOWNA, 0, 0, HWND_DESKTOP, NULL, pContext->hInstance, &info); + ExitOnNullWithLastError(hWnd, hr, "Failed to create window."); + + // Persist the window handle and let the caller know we've initialized. + pEngineState->hMessageWindow = hWnd; + ::SetEvent(pContext->hInitializedEvent); + + // Pump messages until the window is closed. + while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) + { + if (-1 == fRet) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Unexpected return value from message pump."); + } + else if (!::IsDialogMessageW(msg.hwnd, &msg)) + { + ::TranslateMessage(&msg); + ::DispatchMessageW(&msg); + } + } + +LExit: + if (fRegistered) + { + ::UnregisterClassW(BURN_UITHREAD_CLASS_WINDOW, pContext->hInstance); + } + + return hr; +} + +static LRESULT CALLBACK WndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_NCCREATE: + { + LPCREATESTRUCTW lpcs = reinterpret_cast(lParam); + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(lpcs->lpCreateParams)); + break; + } + + case WM_NCDESTROY: + { + LRESULT lRes = ::DefWindowProcW(hWnd, uMsg, wParam, lParam); + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); + return lRes; + } + + case WM_QUERYENDSESSION: + { + DWORD dwEndSession = static_cast(lParam); + BOOL fCritical = ENDSESSION_CRITICAL & dwEndSession; + BOOL fCancel = TRUE; + BOOL fRet = FALSE; + + // Always block shutdown in the elevated process, but ask the BA in the non-elevated. + UITHREAD_INFO* pInfo = reinterpret_cast(::GetWindowLongW(hWnd, GWLP_USERDATA)); + if (!pInfo->fElevated) + { + // TODO: instead of recommending canceling all non-critical shutdowns, maybe we should only recommend cancel + // when the engine is doing work? + fCancel = !fCritical; + // TODO: There's a race condition here where the BA may not have been loaded, or already was unloaded. + UserExperienceOnSystemShutdown(pInfo->pUserExperience, dwEndSession, &fCancel); + } + + fRet = !fCancel; + LogId(REPORT_STANDARD, MSG_SYSTEM_SHUTDOWN, LoggingBoolToString(fCritical), LoggingBoolToString(pInfo->fElevated), LoggingBoolToString(fRet)); + return fRet; + } + + case WM_DESTROY: + ::PostQuitMessage(0); + return 0; + } + + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); +} diff --git a/src/engine/uithread.h b/src/engine/uithread.h new file mode 100644 index 00000000..41168d52 --- /dev/null +++ b/src/engine/uithread.h @@ -0,0 +1,23 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// functions + +HRESULT UiCreateMessageWindow( + __in HINSTANCE hInstance, + __in BURN_ENGINE_STATE* pEngineState + ); + +void UiCloseMessageWindow( + __in BURN_ENGINE_STATE* pEngineState + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/update.cpp b/src/engine/update.cpp new file mode 100644 index 00000000..b04fa9a4 --- /dev/null +++ b/src/engine/update.cpp @@ -0,0 +1,44 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// internal function declarations + + +// function definitions + +extern "C" HRESULT UpdateParseFromXml( + __in BURN_UPDATE* pUpdate, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNode* pixnUpdateNode = NULL; + + hr = XmlSelectSingleNode(pixnBundle, L"Update", &pixnUpdateNode); + if (S_FALSE == hr) + { + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to select Bundle/Update node."); + + // @Location + hr = XmlGetAttributeEx(pixnUpdateNode, L"Location", &pUpdate->sczUpdateSource); + ExitOnFailure(hr, "Failed to get Update@Location."); + +LExit: + ReleaseObject(pixnUpdateNode); + + return hr; +} + +extern "C" void UpdateUninitialize( + __in BURN_UPDATE* pUpdate + ) +{ + PackageUninitialize(&pUpdate->package); + + ReleaseStr(pUpdate->sczUpdateSource); + memset(pUpdate, 0, sizeof(BURN_UPDATE)); +} diff --git a/src/engine/update.h b/src/engine/update.h new file mode 100644 index 00000000..67d40481 --- /dev/null +++ b/src/engine/update.h @@ -0,0 +1,33 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// structs + +typedef struct _BURN_UPDATE +{ + BOOL fUpdateAvailable; + LPWSTR sczUpdateSource; + + BURN_PACKAGE package; +} BURN_UPDATE; + + +// function declarations + +HRESULT UpdateParseFromXml( + __in BURN_UPDATE* pUpdate, + __in IXMLDOMNode* pixnBundle + ); +void UpdateUninitialize( + __in BURN_UPDATE* pUpdate + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp new file mode 100644 index 00000000..8d5271aa --- /dev/null +++ b/src/engine/userexperience.cpp @@ -0,0 +1,2122 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// internal function declarations + +static int FilterResult( + __in DWORD dwAllowedResults, + __in int nResult + ); + +static HRESULT FilterExecuteResult( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus, + __in BOOL fRollback, + __in BOOL fCancel, + __in LPCWSTR sczEventName + ); + + +// function definitions + +/******************************************************************* + UserExperienceParseFromXml - + +*******************************************************************/ +extern "C" HRESULT UserExperienceParseFromXml( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNode* pixnUserExperienceNode = NULL; + + // select UX node + hr = XmlSelectSingleNode(pixnBundle, L"UX", &pixnUserExperienceNode); + if (S_FALSE == hr) + { + hr = E_NOTFOUND; + } + ExitOnFailure(hr, "Failed to select user experience node."); + + // parse splash screen + hr = XmlGetYesNoAttribute(pixnUserExperienceNode, L"SplashScreen", &pUserExperience->fSplashScreen); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to to get UX/@SplashScreen"); + } + + // parse payloads + hr = PayloadsParseFromXml(&pUserExperience->payloads, NULL, NULL, pixnUserExperienceNode); + ExitOnFailure(hr, "Failed to parse user experience payloads."); + + // make sure we have at least one payload + if (0 == pUserExperience->payloads.cPayloads) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Too few UX payloads."); + } + +LExit: + ReleaseObject(pixnUserExperienceNode); + + return hr; +} + +/******************************************************************* + UserExperienceUninitialize - + +*******************************************************************/ +extern "C" void UserExperienceUninitialize( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + ReleaseStr(pUserExperience->sczTempDirectory); + PayloadsUninitialize(&pUserExperience->payloads); + + // clear struct + memset(pUserExperience, 0, sizeof(BURN_USER_EXPERIENCE)); +} + +/******************************************************************* + UserExperienceLoad - + +*******************************************************************/ +extern "C" HRESULT UserExperienceLoad( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOTSTRAPPER_ENGINE_CONTEXT* pEngineContext, + __in BOOTSTRAPPER_COMMAND* pCommand + ) +{ + HRESULT hr = S_OK; + BOOTSTRAPPER_CREATE_ARGS args = { }; + BOOTSTRAPPER_CREATE_RESULTS results = { }; + + args.cbSize = sizeof(BOOTSTRAPPER_CREATE_ARGS); + args.pCommand = pCommand; + args.pfnBootstrapperEngineProc = EngineForApplicationProc; + args.pvBootstrapperEngineProcContext = pEngineContext; + args.qwEngineAPIVersion = MAKEQWORDVERSION(0, 0, 0, 5); // TODO: need to decide whether to keep this, and if so when to update it. + + results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS); + + // Load BA DLL. + pUserExperience->hUXModule = ::LoadLibraryExW(pUserExperience->payloads.rgPayloads[0].sczLocalFilePath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + ExitOnNullWithLastError(pUserExperience->hUXModule, hr, "Failed to load UX DLL."); + + // Get BootstrapperApplicationCreate entry-point. + PFN_BOOTSTRAPPER_APPLICATION_CREATE pfnCreate = (PFN_BOOTSTRAPPER_APPLICATION_CREATE)::GetProcAddress(pUserExperience->hUXModule, "BootstrapperApplicationCreate"); + ExitOnNullWithLastError(pfnCreate, hr, "Failed to get BootstrapperApplicationCreate entry-point"); + + // Create BA. + hr = pfnCreate(&args, &results); + ExitOnFailure(hr, "Failed to create BA."); + + pUserExperience->pfnBAProc = results.pfnBootstrapperApplicationProc; + pUserExperience->pvBAProcContext = results.pvBootstrapperApplicationProcContext; + +LExit: + return hr; +} + +/******************************************************************* + UserExperienceUnload - + +*******************************************************************/ +extern "C" HRESULT UserExperienceUnload( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + + if (pUserExperience->hUXModule) + { + // Get BootstrapperApplicationDestroy entry-point and call it if it exists. + PFN_BOOTSTRAPPER_APPLICATION_DESTROY pfnDestroy = (PFN_BOOTSTRAPPER_APPLICATION_DESTROY)::GetProcAddress(pUserExperience->hUXModule, "BootstrapperApplicationDestroy"); + if (pfnDestroy) + { + pfnDestroy(); + } + + // Free BA DLL. + if (!::FreeLibrary(pUserExperience->hUXModule)) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + TraceError(hr, "Failed to unload BA DLL."); + } + pUserExperience->hUXModule = NULL; + } + +//LExit: + return hr; +} + +extern "C" HRESULT UserExperienceEnsureWorkingFolder( + __in LPCWSTR wzBundleId, + __deref_out_z LPWSTR* psczUserExperienceWorkingFolder + ) +{ + HRESULT hr = S_OK; + LPWSTR sczWorkingFolder = NULL; + + hr = CacheEnsureWorkingFolder(wzBundleId, &sczWorkingFolder); + ExitOnFailure(hr, "Failed to create working folder."); + + hr = StrAllocFormatted(psczUserExperienceWorkingFolder, L"%ls%ls\\", sczWorkingFolder, L".ba"); + ExitOnFailure(hr, "Failed to calculate the bootstrapper application working path."); + + hr = DirEnsureExists(*psczUserExperienceWorkingFolder, NULL); + ExitOnFailure(hr, "Failed create bootstrapper application working folder."); + +LExit: + ReleaseStr(sczWorkingFolder); + + return hr; +} + + +extern "C" HRESULT UserExperienceRemove( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + + // Remove temporary UX directory + if (pUserExperience->sczTempDirectory) + { + hr = DirEnsureDeleteEx(pUserExperience->sczTempDirectory, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE); + TraceError(hr, "Could not delete bootstrapper application folder. Some files will be left in the temp folder."); + } + +//LExit: + return hr; +} + +extern "C" int UserExperienceSendError( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOTSTRAPPER_ERROR_TYPE errorType, + __in_z_opt LPCWSTR wzPackageId, + __in HRESULT hrCode, + __in_z_opt LPCWSTR wzError, + __in DWORD uiFlags, + __in int nRecommendation + ) +{ + int nResult = nRecommendation; + DWORD dwCode = HRESULT_CODE(hrCode); + LPWSTR sczError = NULL; + + // If no error string was provided, try to get the error string from the HRESULT. + if (!wzError) + { + if (SUCCEEDED(StrAllocFromError(&sczError, hrCode, NULL))) + { + wzError = sczError; + } + } + + UserExperienceOnError(pUserExperience, errorType, wzPackageId, dwCode, wzError, uiFlags, 0, NULL, &nResult); // ignore return value. + + ReleaseStr(sczError); + return nResult; +} + +extern "C" HRESULT UserExperienceActivateEngine( + __in BURN_USER_EXPERIENCE* pUserExperience, + __out_opt BOOL* pfActivated + ) +{ + HRESULT hr = S_OK; + BOOL fActivated; + + ::EnterCriticalSection(&pUserExperience->csEngineActive); + if (InterlockedCompareExchange(reinterpret_cast(&pUserExperience->fEngineActive), TRUE, FALSE)) + { + AssertSz(FALSE, "Engine should have been deactivated before activating it."); + + fActivated = FALSE; + hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE); + } + else + { + fActivated = TRUE; + } + ::LeaveCriticalSection(&pUserExperience->csEngineActive); + + if (pfActivated) + { + *pfActivated = fActivated; + } + ExitOnRootFailure(hr, "Engine active cannot be changed because it was already in that state."); + +LExit: + return hr; +} + +extern "C" void UserExperienceDeactivateEngine( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + BOOL fActive = InterlockedExchange(reinterpret_cast(&pUserExperience->fEngineActive), FALSE); + fActive = fActive; // prevents warning in "ship" build. + AssertSz(fActive, "Engine should have be active before deactivating it."); +} + +extern "C" HRESULT UserExperienceEnsureEngineInactive( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = pUserExperience->fEngineActive ? HRESULT_FROM_WIN32(ERROR_BUSY) : S_OK; + ExitOnRootFailure(hr, "Engine is active, cannot proceed."); + +LExit: + return hr; +} + +extern "C" void UserExperienceExecuteReset( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + pUserExperience->hrApplyError = S_OK; + pUserExperience->hwndApply = NULL; +} + +extern "C" void UserExperienceExecutePhaseComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrResult + ) +{ + if (FAILED(hrResult)) + { + pUserExperience->hrApplyError = hrResult; + } +} + +EXTERN_C BAAPI UserExperienceOnApplyBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in DWORD dwPhaseCount + ) +{ + HRESULT hr = S_OK; + BA_ONAPPLYBEGIN_ARGS args = { }; + BA_ONAPPLYBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.dwPhaseCount = dwPhaseCount; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnApplyBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnApplyComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus, + __in BOOTSTRAPPER_APPLY_RESTART restart, + __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction + ) +{ + HRESULT hr = S_OK; + BA_ONAPPLYCOMPLETE_ARGS args = { }; + BA_ONAPPLYCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + args.restart = restart; + args.recommendation = *pAction; + + results.cbSize = sizeof(results); + results.action = *pAction; + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnApplyComplete failed."); + + *pAction = results.action; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCacheAcquireBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in BOOTSTRAPPER_CACHE_OPERATION operation, + __in_z LPCWSTR wzSource + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEACQUIREBEGIN_ARGS args = { }; + BA_ONCACHEACQUIREBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageOrContainerId = wzPackageOrContainerId; + args.wzPayloadId = wzPayloadId; + args.operation = operation; + args.wzSource = wzSource; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnCacheAcquireBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCacheAcquireComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in HRESULT hrStatus, + __inout BOOL* pfRetry + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEACQUIRECOMPLETE_ARGS args = { }; + BA_ONCACHEACQUIRECOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageOrContainerId = wzPackageOrContainerId; + args.wzPayloadId = wzPayloadId; + args.hrStatus = hrStatus; + args.recommendation = *pfRetry ? BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_RETRY : BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_NONE; + + results.cbSize = sizeof(results); + results.action = args.recommendation; + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnCacheAcquireComplete failed."); + + if (FAILED(hrStatus)) + { + *pfRetry = BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_RETRY == results.action; + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCacheAcquireProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in DWORD dwOverallPercentage + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEACQUIREPROGRESS_ARGS args = { }; + BA_ONCACHEACQUIREPROGRESS_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageOrContainerId = wzPackageOrContainerId; + args.wzPayloadId = wzPayloadId; + args.dw64Progress = dw64Progress; + args.dw64Total = dw64Total; + args.dwOverallPercentage = dwOverallPercentage; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREPROGRESS, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnCacheAcquireProgress failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCacheBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEBEGIN_ARGS args = { }; + BA_ONCACHEBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnCacheBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCacheComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONCACHECOMPLETE_ARGS args = { }; + BA_ONCACHECOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnCacheComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCachePackageBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in DWORD cCachePayloads, + __in DWORD64 dw64PackageCacheSize + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEPACKAGEBEGIN_ARGS args = { }; + BA_ONCACHEPACKAGEBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.cCachePayloads = cCachePayloads; + args.dw64PackageCacheSize = dw64PackageCacheSize; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnCachePackageBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCachePackageComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in HRESULT hrStatus, + __inout BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION* pAction + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEPACKAGECOMPLETE_ARGS args = { }; + BA_ONCACHEPACKAGECOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.hrStatus = hrStatus; + args.recommendation = *pAction; + + results.cbSize = sizeof(results); + results.action = *pAction; + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnCachePackageComplete failed."); + + if (FAILED(hrStatus)) + { + *pAction = results.action; + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCacheVerifyBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEVERIFYBEGIN_ARGS args = { }; + BA_ONCACHEVERIFYBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageOrContainerId = wzPackageOrContainerId; + args.wzPayloadId = wzPayloadId; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnCacheVerifyBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCacheVerifyComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in HRESULT hrStatus, + __inout BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION* pAction + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEVERIFYCOMPLETE_ARGS args = { }; + BA_ONCACHEVERIFYCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageOrContainerId = wzPackageOrContainerId; + args.wzPayloadId = wzPayloadId; + args.hrStatus = hrStatus; + args.recommendation = *pAction; + + results.cbSize = sizeof(results); + results.action = *pAction; + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnCacheVerifyComplete failed."); + + if (FAILED(hrStatus)) + { + *pAction = results.action; + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fInstalled, + __in DWORD cPackages + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTBEGIN_ARGS args = { }; + BA_ONDETECTBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.cPackages = cPackages; + args.fInstalled = fInstalled; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnDetectBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectCompatibleMsiPackage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzCompatiblePackageId, + __in DWORD64 dw64CompatiblePackageVersion + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTCOMPATIBLEMSIPACKAGE_ARGS args = { }; + BA_ONDETECTCOMPATIBLEMSIPACKAGE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.wzCompatiblePackageId = wzCompatiblePackageId; + args.dw64CompatiblePackageVersion = dw64CompatiblePackageVersion; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPATIBLEMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnDetectCompatibleMsiPackage failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTCOMPLETE_ARGS args = { }; + BA_ONDETECTCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnDetectComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectForwardCompatibleBundle( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzBundleId, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in_z LPCWSTR wzBundleTag, + __in BOOL fPerMachine, + __in DWORD64 dw64Version, + __inout BOOL* pfIgnoreBundle + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_ARGS args = { }; + BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzBundleId = wzBundleId; + args.relationType = relationType; + args.wzBundleTag = wzBundleTag; + args.fPerMachine = fPerMachine; + args.dw64Version = dw64Version; + + results.cbSize = sizeof(results); + results.fIgnoreBundle = *pfIgnoreBundle; + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTFORWARDCOMPATIBLEBUNDLE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnDetectForwardCompatibleBundle failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + *pfIgnoreBundle = results.fIgnoreBundle; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectMsiFeature( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzFeatureId, + __in BOOTSTRAPPER_FEATURE_STATE state + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTMSIFEATURE_ARGS args = { }; + BA_ONDETECTMSIFEATURE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.wzFeatureId = wzFeatureId; + args.state = state; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTMSIFEATURE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnDetectMsiFeature failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectPackageBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTPACKAGEBEGIN_ARGS args = { }; + BA_ONDETECTPACKAGEBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnDetectPackageBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectPackageComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in HRESULT hrStatus, + __in BOOTSTRAPPER_PACKAGE_STATE state + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTPACKAGECOMPLETE_ARGS args = { }; + BA_ONDETECTPACKAGECOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.hrStatus = hrStatus; + args.state = state; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnDetectPackageComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectRelatedBundle( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzBundleId, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in_z LPCWSTR wzBundleTag, + __in BOOL fPerMachine, + __in DWORD64 dw64Version, + __in BOOTSTRAPPER_RELATED_OPERATION operation + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTRELATEDBUNDLE_ARGS args = { }; + BA_ONDETECTRELATEDBUNDLE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzBundleId = wzBundleId; + args.relationType = relationType; + args.wzBundleTag = wzBundleTag; + args.fPerMachine = fPerMachine; + args.dw64Version = dw64Version; + args.operation = operation; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnDetectRelatedBundle failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectRelatedMsiPackage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzUpgradeCode, + __in_z LPCWSTR wzProductCode, + __in BOOL fPerMachine, + __in DWORD64 dw64Version, + __in BOOTSTRAPPER_RELATED_OPERATION operation + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTRELATEDMSIPACKAGE_ARGS args = { }; + BA_ONDETECTRELATEDMSIPACKAGE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.wzUpgradeCode = wzUpgradeCode; + args.wzProductCode = wzProductCode; + args.fPerMachine = fPerMachine; + args.dw64Version = dw64Version; + args.operation = operation; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnDetectRelatedMsiPackage failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectTargetMsiPackage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzProductCode, + __in BOOTSTRAPPER_PACKAGE_STATE patchState + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTTARGETMSIPACKAGE_ARGS args = { }; + BA_ONDETECTTARGETMSIPACKAGE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.wzProductCode = wzProductCode; + args.patchState = patchState; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTTARGETMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnDetectTargetMsiPackage failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectUpdate( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzUpdateLocation, + __in DWORD64 dw64Size, + __in DWORD64 dw64Version, + __in_z_opt LPCWSTR wzTitle, + __in_z_opt LPCWSTR wzSummary, + __in_z_opt LPCWSTR wzContentType, + __in_z_opt LPCWSTR wzContent, + __inout BOOL* pfStopProcessingUpdates + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTUPDATE_ARGS args = { }; + BA_ONDETECTUPDATE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzUpdateLocation = wzUpdateLocation; + args.dw64Size = dw64Size; + args.dw64Version = dw64Version; + args.wzTitle = wzTitle; + args.wzSummary = wzSummary; + args.wzContentType = wzContentType; + args.wzContent = wzContent; + + results.cbSize = sizeof(results); + results.fStopProcessingUpdates = *pfStopProcessingUpdates; + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnDetectUpdate failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + *pfStopProcessingUpdates = results.fStopProcessingUpdates; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectUpdateBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzUpdateLocation, + __inout BOOL* pfSkip + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTUPDATEBEGIN_ARGS args = { }; + BA_ONDETECTUPDATEBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzUpdateLocation = wzUpdateLocation; + + results.cbSize = sizeof(results); + results.fSkip = *pfSkip; + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATEBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnDetectUpdateBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + *pfSkip = results.fSkip; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectUpdateComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus, + __inout BOOL* pfIgnoreError + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTUPDATECOMPLETE_ARGS args = { }; + BA_ONDETECTUPDATECOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + results.fIgnoreError = *pfIgnoreError; + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnDetectUpdateComplete failed."); + + if (FAILED(hrStatus)) + { + *pfIgnoreError = results.fIgnoreError; + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnElevateBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + BA_ONELEVATEBEGIN_ARGS args = { }; + BA_ONELEVATEBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATEBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnElevateBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnElevateComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONELEVATECOMPLETE_ARGS args = { }; + BA_ONELEVATECOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnElevateComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnError( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOTSTRAPPER_ERROR_TYPE errorType, + __in_z_opt LPCWSTR wzPackageId, + __in DWORD dwCode, + __in_z_opt LPCWSTR wzError, + __in DWORD dwUIHint, + __in DWORD cData, + __in_ecount_z_opt(cData) LPCWSTR* rgwzData, + __inout int* pnResult + ) +{ + HRESULT hr = S_OK; + BA_ONERROR_ARGS args = { }; + BA_ONERROR_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.errorType = errorType; + args.wzPackageId = wzPackageId; + args.dwCode = dwCode; + args.wzError = wzError; + args.dwUIHint = dwUIHint; + args.cData = cData; + args.rgwzData = rgwzData; + args.nRecommendation = *pnResult; + + results.cbSize = sizeof(results); + results.nResult = *pnResult; + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONERROR, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnError failed."); + + *pnResult = results.nResult; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnExecuteBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in DWORD cExecutingPackages + ) +{ + HRESULT hr = S_OK; + BA_ONEXECUTEBEGIN_ARGS args = { }; + BA_ONEXECUTEBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.cExecutingPackages = cExecutingPackages; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnExecuteBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnExecuteComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONEXECUTECOMPLETE_ARGS args = { }; + BA_ONEXECUTECOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnExecuteComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnExecuteFilesInUse( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in DWORD cFiles, + __in_ecount_z_opt(cFiles) LPCWSTR* rgwzFiles, + __inout int* pnResult + ) +{ + HRESULT hr = S_OK; + BA_ONEXECUTEFILESINUSE_ARGS args = { }; + BA_ONEXECUTEFILESINUSE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.cFiles = cFiles; + args.rgwzFiles = rgwzFiles; + args.nRecommendation = *pnResult; + + results.cbSize = sizeof(results); + results.nResult = *pnResult; + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEFILESINUSE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnExecuteFilesInUse failed."); + + *pnResult = results.nResult; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnExecuteMsiMessage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in INSTALLMESSAGE messageType, + __in DWORD dwUIHint, + __in_z LPCWSTR wzMessage, + __in DWORD cData, + __in_ecount_z_opt(cData) LPCWSTR* rgwzData, + __inout int* pnResult + ) +{ + HRESULT hr = S_OK; + BA_ONEXECUTEMSIMESSAGE_ARGS args = { }; + BA_ONEXECUTEMSIMESSAGE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.messageType = messageType; + args.dwUIHint = dwUIHint; + args.wzMessage = wzMessage; + args.cData = cData; + args.rgwzData = rgwzData; + args.nRecommendation = *pnResult; + + results.cbSize = sizeof(results); + results.nResult = *pnResult; + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEMSIMESSAGE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnExecuteMsiMessage failed."); + + *pnResult = results.nResult; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnExecutePackageBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in BOOL fExecute + ) +{ + HRESULT hr = S_OK; + BA_ONEXECUTEPACKAGEBEGIN_ARGS args = { }; + BA_ONEXECUTEPACKAGEBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.fExecute = fExecute; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnExecutePackageBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnExecutePackageComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in HRESULT hrStatus, + __in BOOTSTRAPPER_APPLY_RESTART restart, + __inout BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION* pAction + ) +{ + HRESULT hr = S_OK; + BA_ONEXECUTEPACKAGECOMPLETE_ARGS args = { }; + BA_ONEXECUTEPACKAGECOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.hrStatus = hrStatus; + args.restart = restart; + args.recommendation = *pAction; + + results.cbSize = sizeof(results); + results.action = *pAction; + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnExecutePackageComplete failed."); + + *pAction = results.action; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnExecutePatchTarget( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzTargetProductCode + ) +{ + HRESULT hr = S_OK; + BA_ONEXECUTEPATCHTARGET_ARGS args = { }; + BA_ONEXECUTEPATCHTARGET_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.wzTargetProductCode = wzTargetProductCode; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPATCHTARGET, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnExecutePatchTarget failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnExecuteProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in DWORD dwProgressPercentage, + __in DWORD dwOverallPercentage, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + BA_ONEXECUTEPROGRESS_ARGS args = { }; + BA_ONEXECUTEPROGRESS_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.dwProgressPercentage = dwProgressPercentage; + args.dwOverallPercentage = dwOverallPercentage; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROGRESS, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnExecuteProgress failed."); + +LExit: + if (FAILED(hr)) + { + *pnResult = IDERROR; + } + else if (results.fCancel) + { + *pnResult = IDCANCEL; + } + else + { + *pnResult = IDNOACTION; + } + return hr; +} + +EXTERN_C BAAPI UserExperienceOnLaunchApprovedExeBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + BA_ONLAUNCHAPPROVEDEXEBEGIN_ARGS args = { }; + BA_ONLAUNCHAPPROVEDEXEBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXEBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnLaunchApprovedExeBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnLaunchApprovedExeComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus, + __in DWORD dwProcessId + ) +{ + HRESULT hr = S_OK; + BA_ONLAUNCHAPPROVEDEXECOMPLETE_ARGS args = { }; + BA_ONLAUNCHAPPROVEDEXECOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + args.dwProcessId = dwProcessId; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnLaunchApprovedExeComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPlanBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in DWORD cPackages + ) +{ + HRESULT hr = S_OK; + BA_ONPLANBEGIN_ARGS args = { }; + BA_ONPLANBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.cPackages = cPackages; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnPlanBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPlanCompatibleMsiPackageBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzCompatiblePackageId, + __in DWORD64 dw64CompatiblePackageVersion, + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState + ) +{ + HRESULT hr = S_OK; + BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_ARGS args = { }; + BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.wzCompatiblePackageId = wzCompatiblePackageId; + args.dw64CompatiblePackageVersion = dw64CompatiblePackageVersion; + args.recommendedState = *pRequestedState; + + results.cbSize = sizeof(results); + results.requestedState = *pRequestedState; + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnPlanCompatibleMsiPackageBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + *pRequestedState = results.requestedState; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPlanCompatibleMsiPackageComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzCompatiblePackageId, + __in HRESULT hrStatus, + __in BOOTSTRAPPER_PACKAGE_STATE state, + __in BOOTSTRAPPER_REQUEST_STATE requested, + __in BOOTSTRAPPER_ACTION_STATE execute, + __in BOOTSTRAPPER_ACTION_STATE rollback + ) +{ + HRESULT hr = S_OK; + BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_ARGS args = { }; + BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.wzCompatiblePackageId = wzCompatiblePackageId; + args.hrStatus = hrStatus; + args.state = state; + args.requested = requested; + args.execute = execute; + args.rollback = rollback; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnPlanCompatibleMsiPackageComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPlanMsiFeature( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzFeatureId, + __inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState + ) +{ + HRESULT hr = S_OK; + BA_ONPLANMSIFEATURE_ARGS args = { }; + BA_ONPLANMSIFEATURE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.wzFeatureId = wzFeatureId; + args.recommendedState = *pRequestedState; + + results.cbSize = sizeof(results); + results.requestedState = *pRequestedState; + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIFEATURE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnPlanMsiFeature failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + *pRequestedState = results.requestedState; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPlanComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONPLANCOMPLETE_ARGS args = { }; + BA_ONPLANCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnPlanComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPlanPackageBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState + ) +{ + HRESULT hr = S_OK; + BA_ONPLANPACKAGEBEGIN_ARGS args = { }; + BA_ONPLANPACKAGEBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.recommendedState = *pRequestedState; + + results.cbSize = sizeof(results); + results.requestedState = *pRequestedState; + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnPlanPackageBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + *pRequestedState = results.requestedState; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPlanPackageComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in HRESULT hrStatus, + __in BOOTSTRAPPER_PACKAGE_STATE state, + __in BOOTSTRAPPER_REQUEST_STATE requested, + __in BOOTSTRAPPER_ACTION_STATE execute, + __in BOOTSTRAPPER_ACTION_STATE rollback + ) +{ + HRESULT hr = S_OK; + BA_ONPLANPACKAGECOMPLETE_ARGS args = { }; + BA_ONPLANPACKAGECOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.hrStatus = hrStatus; + args.state = state; + args.requested = requested; + args.execute = execute; + args.rollback = rollback; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnPlanPackageComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPlanRelatedBundle( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzBundleId, + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState + ) +{ + HRESULT hr = S_OK; + BA_ONPLANRELATEDBUNDLE_ARGS args = { }; + BA_ONPLANRELATEDBUNDLE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzBundleId = wzBundleId; + args.recommendedState = *pRequestedState; + + results.cbSize = sizeof(results); + results.requestedState = *pRequestedState; + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnPlanRelatedBundle failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + *pRequestedState = results.requestedState; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPlanTargetMsiPackage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzProductCode, + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState + ) +{ + HRESULT hr = S_OK; + BA_ONPLANTARGETMSIPACKAGE_ARGS args = { }; + BA_ONPLANTARGETMSIPACKAGE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.wzProductCode = wzProductCode; + args.recommendedState = *pRequestedState; + + results.cbSize = sizeof(results); + results.requestedState = *pRequestedState; + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANTARGETMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnPlanTargetMsiPackage failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + *pRequestedState = results.requestedState; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fRollback, + __in DWORD dwProgressPercentage, + __in DWORD dwOverallPercentage + ) +{ + HRESULT hr = S_OK; + BA_ONPROGRESS_ARGS args = { }; + BA_ONPROGRESS_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.dwProgressPercentage = dwProgressPercentage; + args.dwOverallPercentage = dwOverallPercentage; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPROGRESS, &args, &results, pUserExperience->pvBAProcContext); + hr = FilterExecuteResult(pUserExperience, hr, fRollback, results.fCancel, L"OnProgress"); + + return hr; +} + +EXTERN_C BAAPI UserExperienceOnRegisterBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + BA_ONREGISTERBEGIN_ARGS args = { }; + BA_ONREGISTERBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnRegisterBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnRegisterComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONREGISTERCOMPLETE_ARGS args = { }; + BA_ONREGISTERCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnRegisterComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnResolveSource( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in_z LPCWSTR wzLocalSource, + __in_z_opt LPCWSTR wzDownloadSource, + __inout BOOTSTRAPPER_RESOLVESOURCE_ACTION* pAction + ) +{ + HRESULT hr = S_OK; + BA_ONRESOLVESOURCE_ARGS args = { }; + BA_ONRESOLVESOURCE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageOrContainerId = wzPackageOrContainerId; + args.wzPayloadId = wzPayloadId; + args.wzLocalSource = wzLocalSource; + args.wzDownloadSource = wzDownloadSource; + args.recommendation = *pAction; + + results.cbSize = sizeof(results); + results.action = *pAction; + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONRESOLVESOURCE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnResolveSource failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + else + { + *pAction = results.action; + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnShutdown( + __in BURN_USER_EXPERIENCE* pUserExperience, + __inout BOOTSTRAPPER_SHUTDOWN_ACTION* pAction + ) +{ + HRESULT hr = S_OK; + BA_ONSHUTDOWN_ARGS args = { }; + BA_ONSHUTDOWN_RESULTS results = { }; + + args.cbSize = sizeof(args); + + results.cbSize = sizeof(results); + results.action = *pAction; + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONSHUTDOWN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnShutdown failed."); + + *pAction = results.action; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnStartup( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + BA_ONSTARTUP_ARGS args = { }; + BA_ONSTARTUP_RESULTS results = { }; + + args.cbSize = sizeof(args); + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONSTARTUP, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnStartup failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnSystemShutdown( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in DWORD dwEndSession, + __inout BOOL* pfCancel + ) +{ + HRESULT hr = S_OK; + BA_ONSYSTEMSHUTDOWN_ARGS args = { }; + BA_ONSYSTEMSHUTDOWN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.dwEndSession = dwEndSession; + + results.cbSize = sizeof(results); + results.fCancel = *pfCancel; + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMSHUTDOWN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnSystemShutdown failed."); + + *pfCancel = results.fCancel; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnUnregisterBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + BA_ONUNREGISTERBEGIN_ARGS args = { }; + BA_ONUNREGISTERBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnUnregisterBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnUnregisterComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONUNREGISTERCOMPLETE_ARGS args = { }; + BA_ONUNREGISTERCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnUnregisterComplete failed."); + +LExit: + return hr; +} + +extern "C" int UserExperienceCheckExecuteResult( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fRollback, + __in DWORD dwAllowedResults, + __in int nResult + ) +{ + // Do not allow canceling while rolling back. + if (fRollback && (IDCANCEL == nResult || IDABORT == nResult)) + { + nResult = IDNOACTION; + } + else if (FAILED(pUserExperience->hrApplyError) && !fRollback) // if we failed cancel except not during rollback. + { + nResult = IDCANCEL; + } + + nResult = FilterResult(dwAllowedResults, nResult); + return nResult; +} + +extern "C" HRESULT UserExperienceInterpretResult( + __in BURN_USER_EXPERIENCE* /*pUserExperience*/, + __in DWORD dwAllowedResults, + __in int nResult + ) +{ + int nFilteredResult = FilterResult(dwAllowedResults, nResult); + return IDOK == nFilteredResult || IDNOACTION == nFilteredResult ? S_OK : IDCANCEL == nFilteredResult || IDABORT == nFilteredResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); +} + +extern "C" HRESULT UserExperienceInterpretExecuteResult( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fRollback, + __in DWORD dwAllowedResults, + __in int nResult + ) +{ + HRESULT hr = S_OK; + + // If we failed return that error unless this is rollback which should roll on. + if (FAILED(pUserExperience->hrApplyError) && !fRollback) + { + hr = pUserExperience->hrApplyError; + } + else + { + int nCheckedResult = UserExperienceCheckExecuteResult(pUserExperience, fRollback, dwAllowedResults, nResult); + hr = IDOK == nCheckedResult || IDNOACTION == nCheckedResult ? S_OK : IDCANCEL == nCheckedResult || IDABORT == nCheckedResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); + } + + return hr; +} + + +// internal functions + +static int FilterResult( + __in DWORD dwAllowedResults, + __in int nResult + ) +{ + if (IDNOACTION == nResult || IDERROR == nResult) // do nothing and errors pass through. + { + } + else + { + switch (dwAllowedResults) + { + case MB_OK: + nResult = IDOK; + break; + + case MB_OKCANCEL: + if (IDOK == nResult || IDYES == nResult) + { + nResult = IDOK; + } + else if (IDCANCEL == nResult || IDABORT == nResult || IDNO == nResult) + { + nResult = IDCANCEL; + } + else + { + nResult = IDNOACTION; + } + break; + + case MB_ABORTRETRYIGNORE: + if (IDCANCEL == nResult || IDABORT == nResult) + { + nResult = IDABORT; + } + else if (IDRETRY == nResult || IDTRYAGAIN == nResult) + { + nResult = IDRETRY; + } + else if (IDIGNORE == nResult) + { + nResult = IDIGNORE; + } + else + { + nResult = IDNOACTION; + } + break; + + case MB_YESNO: + if (IDOK == nResult || IDYES == nResult) + { + nResult = IDYES; + } + else if (IDCANCEL == nResult || IDABORT == nResult || IDNO == nResult) + { + nResult = IDNO; + } + else + { + nResult = IDNOACTION; + } + break; + + case MB_YESNOCANCEL: + if (IDOK == nResult || IDYES == nResult) + { + nResult = IDYES; + } + else if (IDNO == nResult) + { + nResult = IDNO; + } + else if (IDCANCEL == nResult || IDABORT == nResult) + { + nResult = IDCANCEL; + } + else + { + nResult = IDNOACTION; + } + break; + + case MB_RETRYCANCEL: + if (IDRETRY == nResult || IDTRYAGAIN == nResult) + { + nResult = IDRETRY; + } + else if (IDCANCEL == nResult || IDABORT == nResult) + { + nResult = IDABORT; + } + else + { + nResult = IDNOACTION; + } + break; + + case MB_CANCELTRYCONTINUE: + if (IDCANCEL == nResult || IDABORT == nResult) + { + nResult = IDABORT; + } + else if (IDRETRY == nResult || IDTRYAGAIN == nResult) + { + nResult = IDRETRY; + } + else if (IDCONTINUE == nResult || IDIGNORE == nResult) + { + nResult = IDCONTINUE; + } + else + { + nResult = IDNOACTION; + } + break; + + case WIU_MB_OKIGNORECANCELRETRY: // custom Windows Installer utility return code. + if (IDOK == nResult || IDYES == nResult) + { + nResult = IDOK; + } + else if (IDCONTINUE == nResult || IDIGNORE == nResult) + { + nResult = IDIGNORE; + } + else if (IDCANCEL == nResult || IDABORT == nResult) + { + nResult = IDCANCEL; + } + else if (IDRETRY == nResult || IDTRYAGAIN == nResult || IDNO == nResult) + { + nResult = IDRETRY; + } + else + { + nResult = IDNOACTION; + } + break; + + case MB_RETRYTRYAGAIN: // custom return code. + if (IDRETRY != nResult && IDTRYAGAIN != nResult) + { + nResult = IDNOACTION; + } + break; + + default: + AssertSz(FALSE, "Unknown allowed results."); + break; + } + } + + return nResult; +} + +// This filters the BA's responses to events during apply. +// If an apply thread failed, then return its error so this thread will bail out. +// During rollback, the BA can't cancel. +static HRESULT FilterExecuteResult( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus, + __in BOOL fRollback, + __in BOOL fCancel, + __in LPCWSTR sczEventName + ) +{ + HRESULT hr = hrStatus; + HRESULT hrApplyError = pUserExperience->hrApplyError; // make sure to use the same value for the whole method, since it can be changed in other threads. + + // If we failed return that error unless this is rollback which should roll on. + if (FAILED(hrApplyError) && !fRollback) + { + hr = hrApplyError; + } + else if (fRollback) + { + if (fCancel) + { + LogId(REPORT_STANDARD, MSG_APPLY_CANCEL_IGNORED_DURING_ROLLBACK, sczEventName); + } + // TODO: since cancel isn't allowed, should the BA's HRESULT be ignored as well? + // In the previous code, they could still alter rollback by returning IDERROR. + } + else + { + ExitOnFailure(hr, "BA %ls failed.", sczEventName); + + if (fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + } + +LExit: + return hr; +} diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h new file mode 100644 index 00000000..27a94115 --- /dev/null +++ b/src/engine/userexperience.h @@ -0,0 +1,439 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#define BAAPI HRESULT __stdcall + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + +const DWORD MB_RETRYTRYAGAIN = 0xF; + + +// structs + +struct BOOTSTRAPPER_ENGINE_CONTEXT; + +typedef struct _BURN_USER_EXPERIENCE +{ + BOOL fSplashScreen; + BURN_PAYLOADS payloads; + + HMODULE hUXModule; + PFN_BOOTSTRAPPER_APPLICATION_PROC pfnBAProc; + LPVOID pvBAProcContext; + LPWSTR sczTempDirectory; + + CRITICAL_SECTION csEngineActive; // Changing the engine active state in the user experience must be + // syncronized through this critical section. + // Note: The engine must never do a UX callback while in this critical section. + + BOOL fEngineActive; // Indicates that the engine is currently active with one of the execution + // steps (detect, plan, apply), and cannot accept requests from the UX. + // This flag should be cleared by the engine prior to UX callbacks that + // allows altering of the engine state. + + HRESULT hrApplyError; // Tracks is an error occurs during apply that requires the cache or + // execute threads to bail. + + HWND hwndApply; // The window handle provided at the beginning of Apply(). Only valid + // during apply. + + HWND hwndDetect; // The window handle provided at the beginning of Detect(). Only valid + // during Detect. + + DWORD dwExitCode; // Exit code returned by the user experience for the engine overall. +} BURN_USER_EXPERIENCE; + +// functions + +HRESULT UserExperienceParseFromXml( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in IXMLDOMNode* pixnBundle + ); +void UserExperienceUninitialize( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +HRESULT UserExperienceLoad( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOTSTRAPPER_ENGINE_CONTEXT* pEngineContext, + __in BOOTSTRAPPER_COMMAND* pCommand + ); +HRESULT UserExperienceUnload( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +HRESULT UserExperienceEnsureWorkingFolder( + __in LPCWSTR wzBundleId, + __deref_out_z LPWSTR* psczUserExperienceWorkingFolder + ); +HRESULT UserExperienceRemove( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +int UserExperienceSendError( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOTSTRAPPER_ERROR_TYPE errorType, + __in_z_opt LPCWSTR wzPackageId, + __in HRESULT hrCode, + __in_z_opt LPCWSTR wzError, + __in DWORD uiFlags, + __in int nRecommendation + ); +HRESULT UserExperienceActivateEngine( + __in BURN_USER_EXPERIENCE* pUserExperience, + __out_opt BOOL* pfActivated + ); +void UserExperienceDeactivateEngine( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +HRESULT UserExperienceEnsureEngineInactive( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +void UserExperienceExecuteReset( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +void UserExperienceExecutePhaseComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrResult + ); +BAAPI UserExperienceOnApplyBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in DWORD dwPhaseCount + ); +BAAPI UserExperienceOnApplyComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus, + __in BOOTSTRAPPER_APPLY_RESTART restart, + __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction +); +BAAPI UserExperienceOnCacheAcquireBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in BOOTSTRAPPER_CACHE_OPERATION operation, + __in_z LPCWSTR wzSource + ); +BAAPI UserExperienceOnCacheAcquireComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in HRESULT hrStatus, + __inout BOOL* pfRetry + ); +BAAPI UserExperienceOnCacheAcquireProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in DWORD dwOverallPercentage + ); +BAAPI UserExperienceOnCacheBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +BAAPI UserExperienceOnCacheComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ); +BAAPI UserExperienceOnCachePackageBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in DWORD cCachePayloads, + __in DWORD64 dw64PackageCacheSize + ); +BAAPI UserExperienceOnCachePackageComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in HRESULT hrStatus, + __inout BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION* pAction + ); +BAAPI UserExperienceOnCacheVerifyBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId + ); +BAAPI UserExperienceOnCacheVerifyComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in HRESULT hrStatus, + __inout BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION* pAction + ); +BAAPI UserExperienceOnDetectBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fInstalled, + __in DWORD cPackages + ); +BAAPI UserExperienceOnDetectCompatibleMsiPackage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzCompatiblePackageId, + __in DWORD64 dw64CompatiblePackageVersion + ); +BAAPI UserExperienceOnDetectComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ); +BAAPI UserExperienceOnDetectForwardCompatibleBundle( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzBundleId, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in_z LPCWSTR wzBundleTag, + __in BOOL fPerMachine, + __in DWORD64 dw64Version, + __inout BOOL* pfIgnoreBundle + ); +BAAPI UserExperienceOnDetectMsiFeature( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzFeatureId, + __in BOOTSTRAPPER_FEATURE_STATE state + ); +BAAPI UserExperienceOnDetectPackageBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId + ); +BAAPI UserExperienceOnDetectPackageComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in HRESULT hrStatus, + __in BOOTSTRAPPER_PACKAGE_STATE state + ); +BAAPI UserExperienceOnDetectRelatedBundle( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzBundleId, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in_z LPCWSTR wzBundleTag, + __in BOOL fPerMachine, + __in DWORD64 dw64Version, + __in BOOTSTRAPPER_RELATED_OPERATION operation + ); +BAAPI UserExperienceOnDetectRelatedMsiPackage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzUpgradeCode, + __in_z LPCWSTR wzProductCode, + __in BOOL fPerMachine, + __in DWORD64 dw64Version, + __in BOOTSTRAPPER_RELATED_OPERATION operation + ); +BAAPI UserExperienceOnDetectTargetMsiPackage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzProductCode, + __in BOOTSTRAPPER_PACKAGE_STATE patchState + ); +BAAPI UserExperienceOnDetectUpdate( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzUpdateLocation, + __in DWORD64 dw64Size, + __in DWORD64 dw64Version, + __in_z_opt LPCWSTR wzTitle, + __in_z_opt LPCWSTR wzSummary, + __in_z_opt LPCWSTR wzContentType, + __in_z_opt LPCWSTR wzContent, + __inout BOOL* pfStopProcessingUpdates + ); +BAAPI UserExperienceOnDetectUpdateBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzUpdateLocation, + __inout BOOL* pfSkip + ); +BAAPI UserExperienceOnDetectUpdateComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus, + __inout BOOL* pfIgnoreError + ); +BAAPI UserExperienceOnElevateBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +BAAPI UserExperienceOnElevateComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ); +BAAPI UserExperienceOnError( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOTSTRAPPER_ERROR_TYPE errorType, + __in_z_opt LPCWSTR wzPackageId, + __in DWORD dwCode, + __in_z_opt LPCWSTR wzError, + __in DWORD dwUIHint, + __in DWORD cData, + __in_ecount_z_opt(cData) LPCWSTR* rgwzData, + __inout int* pnResult + ); +BAAPI UserExperienceOnExecuteBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in DWORD cExecutingPackages + ); +BAAPI UserExperienceOnExecuteComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus +); +BAAPI UserExperienceOnExecuteFilesInUse( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in DWORD cFiles, + __in_ecount_z_opt(cFiles) LPCWSTR* rgwzFiles, + __inout int* pnResult + ); +BAAPI UserExperienceOnExecuteMsiMessage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in INSTALLMESSAGE messageType, + __in DWORD dwUIHint, + __in_z LPCWSTR wzMessage, + __in DWORD cData, + __in_ecount_z_opt(cData) LPCWSTR* rgwzData, + __inout int* pnResult + ); +BAAPI UserExperienceOnExecutePackageBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in BOOL fExecute + ); +BAAPI UserExperienceOnExecutePackageComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in HRESULT hrStatus, + __in BOOTSTRAPPER_APPLY_RESTART restart, + __inout BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION* pAction + ); +BAAPI UserExperienceOnExecutePatchTarget( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzTargetProductCode + ); +BAAPI UserExperienceOnExecuteProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in DWORD dwProgressPercentage, + __in DWORD dwOverallPercentage, + __inout int* pnResult + ); +BAAPI UserExperienceOnLaunchApprovedExeBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +BAAPI UserExperienceOnLaunchApprovedExeComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus, + __in DWORD dwProcessId + ); +BAAPI UserExperienceOnPlanBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in DWORD cPackages + ); +BAAPI UserExperienceOnPlanCompatibleMsiPackageBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzCompatiblePackageId, + __in DWORD64 dw64CompatiblePackageVersion, + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState + ); +BAAPI UserExperienceOnPlanCompatibleMsiPackageComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzCompatiblePackageId, + __in HRESULT hrStatus, + __in BOOTSTRAPPER_PACKAGE_STATE state, + __in BOOTSTRAPPER_REQUEST_STATE requested, + __in BOOTSTRAPPER_ACTION_STATE execute, + __in BOOTSTRAPPER_ACTION_STATE rollback + ); +BAAPI UserExperienceOnPlanComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ); +BAAPI UserExperienceOnPlanMsiFeature( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzFeatureId, + __inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState + ); +BAAPI UserExperienceOnPlanPackageBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState + ); +BAAPI UserExperienceOnPlanPackageComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in HRESULT hrStatus, + __in BOOTSTRAPPER_PACKAGE_STATE state, + __in BOOTSTRAPPER_REQUEST_STATE requested, + __in BOOTSTRAPPER_ACTION_STATE execute, + __in BOOTSTRAPPER_ACTION_STATE rollback + ); +BAAPI UserExperienceOnPlanRelatedBundle( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzBundleId, + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState + ); +BAAPI UserExperienceOnPlanTargetMsiPackage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzProductCode, + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState + ); +BAAPI UserExperienceOnProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fRollback, + __in DWORD dwProgressPercentage, + __in DWORD dwOverallPercentage + ); +BAAPI UserExperienceOnRegisterBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +BAAPI UserExperienceOnRegisterComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ); +BAAPI UserExperienceOnResolveSource( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in_z LPCWSTR wzLocalSource, + __in_z_opt LPCWSTR wzDownloadSource, + __inout BOOTSTRAPPER_RESOLVESOURCE_ACTION* pAction + ); +BAAPI UserExperienceOnShutdown( + __in BURN_USER_EXPERIENCE* pUserExperience, + __inout BOOTSTRAPPER_SHUTDOWN_ACTION* pAction + ); +BAAPI UserExperienceOnStartup( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +BAAPI UserExperienceOnSystemShutdown( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in DWORD dwEndSession, + __inout BOOL* pfCancel + ); +BAAPI UserExperienceOnUnregisterBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +BAAPI UserExperienceOnUnregisterComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ); +HRESULT UserExperienceInterpretResult( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in DWORD dwAllowedResults, + __in int nResult + ); +int UserExperienceCheckExecuteResult( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fRollback, + __in DWORD dwAllowedResults, + __in int nResult + ); +HRESULT UserExperienceInterpretExecuteResult( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fRollback, + __in DWORD dwAllowedResults, + __in int nResult + ); +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp new file mode 100644 index 00000000..ed4abea2 --- /dev/null +++ b/src/engine/variable.cpp @@ -0,0 +1,2345 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// structs + +typedef const struct _BUILT_IN_VARIABLE_DECLARATION +{ + LPCWSTR wzVariable; + PFN_INITIALIZEVARIABLE pfnInitialize; + DWORD_PTR dwpInitializeData; + BOOL fPersist; + BOOL fOverridable; +} BUILT_IN_VARIABLE_DECLARATION; + + +// constants + +const DWORD GROW_VARIABLE_ARRAY = 3; + +enum OS_INFO_VARIABLE +{ + OS_INFO_VARIABLE_NONE, + OS_INFO_VARIABLE_VersionNT, + OS_INFO_VARIABLE_VersionNT64, + OS_INFO_VARIABLE_ServicePackLevel, + OS_INFO_VARIABLE_NTProductType, + OS_INFO_VARIABLE_NTSuiteBackOffice, + OS_INFO_VARIABLE_NTSuiteDataCenter, + OS_INFO_VARIABLE_NTSuiteEnterprise, + OS_INFO_VARIABLE_NTSuitePersonal, + OS_INFO_VARIABLE_NTSuiteSmallBusiness, + OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted, + OS_INFO_VARIABLE_NTSuiteWebServer, + OS_INFO_VARIABLE_CompatibilityMode, + OS_INFO_VARIABLE_TerminalServer, + OS_INFO_VARIABLE_ProcessorArchitecture, +}; + +enum SET_VARIABLE +{ + SET_VARIABLE_NOT_BUILTIN, + SET_VARIABLE_OVERRIDE_BUILTIN, + SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS, + SET_VARIABLE_ANY, +}; + +// internal function declarations + +static HRESULT FormatString( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzIn, + __out_z_opt LPWSTR* psczOut, + __out_opt DWORD* pcchOut, + __in BOOL fObfuscateHiddenVariables + ); +static HRESULT AddBuiltInVariable( + __in BURN_VARIABLES* pVariables, + __in LPCWSTR wzVariable, + __in PFN_INITIALIZEVARIABLE pfnInitialize, + __in DWORD_PTR dwpInitializeData, + __in BOOL fPersist, + __in BOOL fOverridable + ); +static HRESULT GetVariable( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out BURN_VARIABLE** ppVariable + ); +static HRESULT FindVariableIndexByName( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out DWORD* piVariable + ); +static HRESULT InsertVariable( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in DWORD iPosition + ); +static HRESULT SetVariableValue( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in BURN_VARIANT* pVariant, + __in BOOL fLiteral, + __in SET_VARIABLE setBuiltin, + __in BOOL fLog + ); +static HRESULT InitializeVariableVersionNT( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableOsInfo( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableSystemInfo( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableComputerName( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableVersionMsi( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableCsidlFolder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableWindowsVolumeFolder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableTempFolder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableSystemFolder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariablePrivileged( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableRebootPending( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeSystemLanguageID( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeUserUILanguageID( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeUserLanguageID( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableString( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableNumeric( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableRegistryFolder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariable6432Folder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableDate( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableInstallerName( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableInstallerVersion( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableVersion( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableLogonUser( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT Get64bitFolderFromRegistry( + __in int nFolder, + __deref_out_z LPWSTR* psczPath + ); + + +// function definitions + +extern "C" HRESULT VariableInitialize( + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + + ::InitializeCriticalSection(&pVariables->csAccess); + + const BUILT_IN_VARIABLE_DECLARATION vrgBuiltInVariables[] = { + {L"AdminToolsFolder", InitializeVariableCsidlFolder, CSIDL_ADMINTOOLS}, + {L"AppDataFolder", InitializeVariableCsidlFolder, CSIDL_APPDATA}, + {L"CommonAppDataFolder", InitializeVariableCsidlFolder, CSIDL_COMMON_APPDATA}, +#if defined(_WIN64) + {L"CommonFiles64Folder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMON}, + {L"CommonFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMONX86}, +#else + {L"CommonFiles64Folder", InitializeVariableRegistryFolder, CSIDL_PROGRAM_FILES_COMMON}, + {L"CommonFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMON}, +#endif + {L"CommonFiles6432Folder", InitializeVariable6432Folder, CSIDL_PROGRAM_FILES_COMMON}, + {L"CompatibilityMode", InitializeVariableOsInfo, OS_INFO_VARIABLE_CompatibilityMode}, + {VARIABLE_DATE, InitializeVariableDate, 0}, + {L"ComputerName", InitializeVariableComputerName, 0}, + {L"DesktopFolder", InitializeVariableCsidlFolder, CSIDL_DESKTOP}, + {L"FavoritesFolder", InitializeVariableCsidlFolder, CSIDL_FAVORITES}, + {L"FontsFolder", InitializeVariableCsidlFolder, CSIDL_FONTS}, + {VARIABLE_INSTALLERNAME, InitializeVariableInstallerName, 0}, + {VARIABLE_INSTALLERVERSION, InitializeVariableInstallerVersion, 0}, + {L"LocalAppDataFolder", InitializeVariableCsidlFolder, CSIDL_LOCAL_APPDATA}, + {VARIABLE_LOGONUSER, InitializeVariableLogonUser, 0}, + {L"MyPicturesFolder", InitializeVariableCsidlFolder, CSIDL_MYPICTURES}, + {L"NTProductType", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTProductType}, + {L"NTSuiteBackOffice", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteBackOffice}, + {L"NTSuiteDataCenter", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteDataCenter}, + {L"NTSuiteEnterprise", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteEnterprise}, + {L"NTSuitePersonal", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuitePersonal}, + {L"NTSuiteSmallBusiness", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteSmallBusiness}, + {L"NTSuiteSmallBusinessRestricted", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted}, + {L"NTSuiteWebServer", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteWebServer}, + {L"PersonalFolder", InitializeVariableCsidlFolder, CSIDL_PERSONAL}, + {L"Privileged", InitializeVariablePrivileged, 0}, + {L"ProcessorArchitecture", InitializeVariableSystemInfo, OS_INFO_VARIABLE_ProcessorArchitecture}, +#if defined(_WIN64) + {L"ProgramFiles64Folder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES}, + {L"ProgramFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILESX86}, +#else + {L"ProgramFiles64Folder", InitializeVariableRegistryFolder, CSIDL_PROGRAM_FILES}, + {L"ProgramFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES}, +#endif + {L"ProgramFiles6432Folder", InitializeVariable6432Folder, CSIDL_PROGRAM_FILES}, + {L"ProgramMenuFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAMS}, + {L"RebootPending", InitializeVariableRebootPending, 0}, + {L"SendToFolder", InitializeVariableCsidlFolder, CSIDL_SENDTO}, + {L"ServicePackLevel", InitializeVariableVersionNT, OS_INFO_VARIABLE_ServicePackLevel}, + {L"StartMenuFolder", InitializeVariableCsidlFolder, CSIDL_STARTMENU}, + {L"StartupFolder", InitializeVariableCsidlFolder, CSIDL_STARTUP}, + {L"SystemFolder", InitializeVariableSystemFolder, FALSE}, + {L"System64Folder", InitializeVariableSystemFolder, TRUE}, + {L"SystemLanguageID", InitializeSystemLanguageID, 0}, + {L"TempFolder", InitializeVariableTempFolder, 0}, + {L"TemplateFolder", InitializeVariableCsidlFolder, CSIDL_TEMPLATES}, + {L"TerminalServer", InitializeVariableOsInfo, OS_INFO_VARIABLE_TerminalServer}, + {L"UserUILanguageID", InitializeUserUILanguageID, 0}, + {L"UserLanguageID", InitializeUserLanguageID, 0}, + {L"VersionMsi", InitializeVariableVersionMsi, 0}, + {L"VersionNT", InitializeVariableVersionNT, OS_INFO_VARIABLE_VersionNT}, + {L"VersionNT64", InitializeVariableVersionNT, OS_INFO_VARIABLE_VersionNT64}, + {L"WindowsFolder", InitializeVariableCsidlFolder, CSIDL_WINDOWS}, + {L"WindowsVolume", InitializeVariableWindowsVolumeFolder, 0}, + {BURN_BUNDLE_ACTION, InitializeVariableNumeric, 0, FALSE, TRUE}, + {BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, InitializeVariableString, NULL, FALSE, TRUE}, + {BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, InitializeVariableString, NULL, FALSE, TRUE}, + {BURN_BUNDLE_FORCED_RESTART_PACKAGE, InitializeVariableString, NULL, TRUE, TRUE}, + {BURN_BUNDLE_INSTALLED, InitializeVariableNumeric, 0, FALSE, TRUE}, + {BURN_BUNDLE_ELEVATED, InitializeVariableNumeric, 0, FALSE, TRUE}, + {BURN_BUNDLE_ACTIVE_PARENT, InitializeVariableString, NULL, FALSE, TRUE}, + {BURN_BUNDLE_PROVIDER_KEY, InitializeVariableString, (DWORD_PTR)L"", FALSE, TRUE}, + {BURN_BUNDLE_SOURCE_PROCESS_PATH, InitializeVariableString, NULL, FALSE, TRUE}, + {BURN_BUNDLE_SOURCE_PROCESS_FOLDER, InitializeVariableString, NULL, FALSE, TRUE}, + {BURN_BUNDLE_TAG, InitializeVariableString, (DWORD_PTR)L"", FALSE, TRUE}, + {BURN_BUNDLE_UILEVEL, InitializeVariableNumeric, 0, FALSE, TRUE}, + {BURN_BUNDLE_VERSION, InitializeVariableVersion, 0, FALSE, TRUE}, + }; + + for (DWORD i = 0; i < countof(vrgBuiltInVariables); ++i) + { + BUILT_IN_VARIABLE_DECLARATION* pBuiltInVariable = &vrgBuiltInVariables[i]; + + hr = AddBuiltInVariable(pVariables, pBuiltInVariable->wzVariable, pBuiltInVariable->pfnInitialize, pBuiltInVariable->dwpInitializeData, pBuiltInVariable->fPersist, pBuiltInVariable->fOverridable); + ExitOnFailure(hr, "Failed to add built-in variable: %ls.", pBuiltInVariable->wzVariable); + } + +LExit: + return hr; +} + +extern "C" HRESULT VariablesParseFromXml( + __in BURN_VARIABLES* pVariables, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + LPWSTR sczId = NULL; + LPWSTR scz = NULL; + BURN_VARIANT value = { }; + BURN_VARIANT_TYPE valueType = BURN_VARIANT_TYPE_NONE; + BOOL fHidden = FALSE; + BOOL fPersisted = FALSE; + DWORD iVariable = 0; + + ::EnterCriticalSection(&pVariables->csAccess); + + // select variable nodes + hr = XmlSelectNodes(pixnBundle, L"Variable", &pixnNodes); + ExitOnFailure(hr, "Failed to select variable nodes."); + + // get variable node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get variable node count."); + + // parse package elements + for (DWORD i = 0; i < cNodes; ++i) + { + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &sczId); + ExitOnFailure(hr, "Failed to get @Id."); + + // @Hidden + hr = XmlGetYesNoAttribute(pixnNode, L"Hidden", &fHidden); + ExitOnFailure(hr, "Failed to get @Hidden."); + + // @Persisted + hr = XmlGetYesNoAttribute(pixnNode, L"Persisted", &fPersisted); + ExitOnFailure(hr, "Failed to get @Persisted."); + + // @Value + hr = XmlGetAttributeEx(pixnNode, L"Value", &scz); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Value."); + + hr = BVariantSetString(&value, scz, 0); + ExitOnFailure(hr, "Failed to set variant value."); + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) + { + if (!fHidden) + { + LogStringLine(REPORT_STANDARD, "Initializing numeric variable '%ls' to value '%ls'", sczId, value.sczValue); + } + valueType = BURN_VARIANT_TYPE_NUMERIC; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1)) + { + if (!fHidden) + { + LogStringLine(REPORT_STANDARD, "Initializing string variable '%ls' to value '%ls'", sczId, value.sczValue); + } + valueType = BURN_VARIANT_TYPE_STRING; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) + { + if (!fHidden) + { + LogStringLine(REPORT_STANDARD, "Initializing version variable '%ls' to value '%ls'", sczId, value.sczValue); + } + valueType = BURN_VARIANT_TYPE_VERSION; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else + { + valueType = BURN_VARIANT_TYPE_NONE; + } + + if (fHidden) + { + LogStringLine(REPORT_STANDARD, "Initializing hidden variable '%ls'", sczId); + } + + // change value variant to correct type + hr = BVariantChangeType(&value, valueType); + ExitOnFailure(hr, "Failed to change variant type."); + + // find existing variable + hr = FindVariableIndexByName(pVariables, sczId, &iVariable); + ExitOnFailure(hr, "Failed to find variable value '%ls'.", sczId); + + // insert element if not found + if (S_FALSE == hr) + { + hr = InsertVariable(pVariables, sczId, iVariable); + ExitOnFailure(hr, "Failed to insert variable '%ls'.", sczId); + } + else if (BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariables->rgVariables[iVariable].internalType) + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Attempt to set built-in variable value: %ls", sczId); + } + pVariables->rgVariables[iVariable].fHidden = fHidden; + pVariables->rgVariables[iVariable].fPersisted = fPersisted; + + // update variable value + hr = BVariantSetValue(&pVariables->rgVariables[iVariable].Value, &value); + ExitOnFailure(hr, "Failed to set value of variable: %ls", sczId); + + hr = BVariantSetEncryption(&pVariables->rgVariables[iVariable].Value, fHidden); + ExitOnFailure(hr, "Failed to set variant encryption"); + + // prepare next iteration + ReleaseNullObject(pixnNode); + BVariantUninitialize(&value); + ReleaseNullStrSecure(scz); + } + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseStr(scz); + ReleaseStr(sczId); + BVariantUninitialize(&value); + + return hr; +} + +extern "C" void VariablesUninitialize( + __in BURN_VARIABLES* pVariables + ) +{ + ::DeleteCriticalSection(&pVariables->csAccess); + + if (pVariables->rgVariables) + { + for (DWORD i = 0; i < pVariables->cVariables; ++i) + { + BURN_VARIABLE* pVariable = &pVariables->rgVariables[i]; + if (pVariable) + { + ReleaseStr(pVariable->sczName); + BVariantUninitialize(&pVariable->Value); + } + } + MemFree(pVariables->rgVariables); + } +} + +extern "C" void VariablesDump( + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + for (DWORD i = 0; i < pVariables->cVariables; ++i) + { + BURN_VARIABLE* pVariable = &pVariables->rgVariables[i]; + if (pVariable && BURN_VARIANT_TYPE_NONE != pVariable->Value.Type) + { + hr = StrAllocFormatted(&sczValue, L"%ls = [%ls]", pVariable->sczName, pVariable->sczName); + if (SUCCEEDED(hr)) + { + if (pVariable->fHidden) + { + hr = VariableFormatStringObfuscated(pVariables, sczValue, &sczValue, NULL); + } + else + { + hr = VariableFormatString(pVariables, sczValue, &sczValue, NULL); + } + } + + if (FAILED(hr)) + { + // already logged; best-effort to dump the rest on our way out the door + continue; + } + + LogId(REPORT_VERBOSE, MSG_VARIABLE_DUMP, sczValue); + + ReleaseNullStrSecure(sczValue); + } + } + + StrSecureZeroFreeString(sczValue); +} + +// The contents of pllValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroMemory. +extern "C" HRESULT VariableGetNumeric( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out LONGLONG* pllValue + ) +{ + HRESULT hr = S_OK; + BURN_VARIABLE* pVariable = NULL; + + ::EnterCriticalSection(&pVariables->csAccess); + + hr = GetVariable(pVariables, wzVariable, &pVariable); + if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) + { + ExitFunction1(hr = E_NOTFOUND); + } + else if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); + + hr = BVariantGetNumeric(&pVariable->Value, pllValue); + ExitOnFailure(hr, "Failed to get value as numeric for variable: %ls", wzVariable); + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + + return hr; +} + +// The contents of psczValue may be sensitive, if variable is hidden should keep encrypted and SecureZeroFree. +extern "C" HRESULT VariableGetString( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out_z LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + BURN_VARIABLE* pVariable = NULL; + + ::EnterCriticalSection(&pVariables->csAccess); + + hr = GetVariable(pVariables, wzVariable, &pVariable); + if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) + { + ExitFunction1(hr = E_NOTFOUND); + } + else if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); + + hr = BVariantGetString(&pVariable->Value, psczValue); + ExitOnFailure(hr, "Failed to get value as string for variable: %ls", wzVariable); + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + + return hr; +} + +// The contents of pqwValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroMemory. +extern "C" HRESULT VariableGetVersion( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in DWORD64* pqwValue + ) +{ + HRESULT hr = S_OK; + BURN_VARIABLE* pVariable = NULL; + + ::EnterCriticalSection(&pVariables->csAccess); + + hr = GetVariable(pVariables, wzVariable, &pVariable); + if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) + { + ExitFunction1(hr = E_NOTFOUND); + } + else if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); + + hr = BVariantGetVersion(&pVariable->Value, pqwValue); + ExitOnFailure(hr, "Failed to get value as version for variable: %ls", wzVariable); + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + + return hr; +} + +extern "C" HRESULT VariableGetVariant( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + BURN_VARIABLE* pVariable = NULL; + + ::EnterCriticalSection(&pVariables->csAccess); + + hr = GetVariable(pVariables, wzVariable, &pVariable); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); + + hr = BVariantCopy(&pVariable->Value, pValue); + ExitOnFailure(hr, "Failed to copy value of variable: %ls", wzVariable); + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + + return hr; +} + +// The contents of psczValue may be sensitive, should keep encrypted and SecureZeroFree. +extern "C" HRESULT VariableGetFormatted( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out_z LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + BURN_VARIABLE* pVariable = NULL; + LPWSTR scz = NULL; + + ::EnterCriticalSection(&pVariables->csAccess); + + hr = GetVariable(pVariables, wzVariable, &pVariable); + if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) + { + ExitFunction1(hr = E_NOTFOUND); + } + else if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get variable: %ls", wzVariable); + + // Strings need to get expanded unless they're built-in or literal because they're guaranteed not to have embedded variables. + if (BURN_VARIANT_TYPE_STRING == pVariable->Value.Type && + BURN_VARIABLE_INTERNAL_TYPE_NORMAL == pVariable->internalType && + !pVariable->fLiteral) + { + hr = BVariantGetString(&pVariable->Value, &scz); + ExitOnFailure(hr, "Failed to get unformatted string."); + + hr = VariableFormatString(pVariables, scz, psczValue, NULL); + ExitOnFailure(hr, "Failed to format value '%ls' of variable: %ls", pVariable->fHidden ? L"*****" : pVariable->Value.sczValue, wzVariable); + } + else + { + hr = BVariantGetString(&pVariable->Value, psczValue); + ExitOnFailure(hr, "Failed to get value as string for variable: %ls", wzVariable); + } + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + StrSecureZeroFreeString(scz); + + return hr; +} + +extern "C" HRESULT VariableSetNumeric( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in LONGLONG llValue, + __in BOOL fOverwriteBuiltIn + ) +{ + BURN_VARIANT variant = { }; + + // We're not going to encrypt this value, so can access the value directly. + variant.llValue = llValue; + variant.Type = BURN_VARIANT_TYPE_NUMERIC; + + return SetVariableValue(pVariables, wzVariable, &variant, FALSE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); +} + +extern "C" HRESULT VariableSetLiteralString( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in_z_opt LPCWSTR wzValue, + __in BOOL fOverwriteBuiltIn + ) +{ + BURN_VARIANT variant = { }; + + // We're not going to encrypt this value, so can access the value directly. + variant.sczValue = (LPWSTR)wzValue; + variant.Type = BURN_VARIANT_TYPE_STRING; + + return SetVariableValue(pVariables, wzVariable, &variant, TRUE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); +} + +extern "C" HRESULT VariableSetString( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in_z_opt LPCWSTR wzValue, + __in BOOL fOverwriteBuiltIn + ) +{ + BURN_VARIANT variant = { }; + + // We're not going to encrypt this value, so can access the value directly. + variant.sczValue = (LPWSTR)wzValue; + variant.Type = BURN_VARIANT_TYPE_STRING; + + return SetVariableValue(pVariables, wzVariable, &variant, FALSE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); +} + +extern "C" HRESULT VariableSetVersion( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in DWORD64 qwValue, + __in BOOL fOverwriteBuiltIn + ) +{ + BURN_VARIANT variant = { }; + + // We're not going to encrypt this value, so can access the value directly. + variant.qwValue = qwValue; + variant.Type = BURN_VARIANT_TYPE_VERSION; + + return SetVariableValue(pVariables, wzVariable, &variant, FALSE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); +} + +extern "C" HRESULT VariableSetLiteralVariant( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in BURN_VARIANT* pVariant + ) +{ + return SetVariableValue(pVariables, wzVariable, pVariant, TRUE, SET_VARIABLE_NOT_BUILTIN, TRUE); +} + +// The contents of psczOut may be sensitive, should keep encrypted and SecureZeroFree +extern "C" HRESULT VariableFormatString( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzIn, + __out_z_opt LPWSTR* psczOut, + __out_opt DWORD* pcchOut + ) +{ + return FormatString(pVariables, wzIn, psczOut, pcchOut, FALSE); +} + +extern "C" HRESULT VariableFormatStringObfuscated( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzIn, + __out_z_opt LPWSTR* psczOut, + __out_opt DWORD* pcchOut + ) +{ + return FormatString(pVariables, wzIn, psczOut, pcchOut, TRUE); +} + +extern "C" HRESULT VariableEscapeString( + __in_z LPCWSTR wzIn, + __out_z LPWSTR* psczOut + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzRead = NULL; + LPWSTR pwzEscaped = NULL; + LPWSTR pwz = NULL; + SIZE_T i = 0; + + // allocate buffer for escaped string + hr = StrAlloc(&pwzEscaped, lstrlenW(wzIn) + 1); + ExitOnFailure(hr, "Failed to allocate buffer for escaped string."); + + // read through string and move characters, inserting escapes as needed + wzRead = wzIn; + for (;;) + { + // find next character needing escaping + i = wcscspn(wzRead, L"[]{}"); + + // copy skipped characters + if (0 < i) + { + hr = StrAllocConcat(&pwzEscaped, wzRead, i); + ExitOnFailure(hr, "Failed to append characters."); + } + + if (L'\0' == wzRead[i]) + { + break; // end reached + } + + // escape character + hr = StrAllocFormatted(&pwz, L"[\\%c]", wzRead[i]); + ExitOnFailure(hr, "Failed to format escape sequence."); + + hr = StrAllocConcat(&pwzEscaped, pwz, 0); + ExitOnFailure(hr, "Failed to append escape sequence."); + + // update read pointer + wzRead += i + 1; + } + + // return value + hr = StrAllocString(psczOut, pwzEscaped, 0); + ExitOnFailure(hr, "Failed to copy string."); + +LExit: + ReleaseStr(pwzEscaped); + ReleaseStr(pwz); + return hr; +} + +extern "C" HRESULT VariableSerialize( + __in BURN_VARIABLES* pVariables, + __in BOOL fPersisting, + __inout BYTE** ppbBuffer, + __inout SIZE_T* piBuffer + ) +{ + HRESULT hr = S_OK; + BOOL fIncluded = FALSE; + LONGLONG ll = 0; + LPWSTR scz = NULL; + DWORD64 qw = 0; + + ::EnterCriticalSection(&pVariables->csAccess); + + // Write variable count. + hr = BuffWriteNumber(ppbBuffer, piBuffer, pVariables->cVariables); + ExitOnFailure(hr, "Failed to write variable count."); + + // Write variables. + for (DWORD i = 0; i < pVariables->cVariables; ++i) + { + BURN_VARIABLE* pVariable = &pVariables->rgVariables[i]; + + // If we aren't persisting, include only variables that aren't rejected by the elevated process. + // If we are persisting, include only variables that should be persisted. + fIncluded = (!fPersisting && BURN_VARIABLE_INTERNAL_TYPE_BUILTIN != pVariable->internalType) || + (fPersisting && pVariable->fPersisted); + + // Write included flag. + hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)fIncluded); + ExitOnFailure(hr, "Failed to write included flag."); + + if (!fIncluded) + { + continue; + } + + // Write variable name. + hr = BuffWriteString(ppbBuffer, piBuffer, pVariable->sczName); + ExitOnFailure(hr, "Failed to write variable name."); + + // Write variable value type. + hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)pVariable->Value.Type); + ExitOnFailure(hr, "Failed to write variable value type."); + + // Write variable value. + switch (pVariable->Value.Type) + { + case BURN_VARIANT_TYPE_NONE: + break; + case BURN_VARIANT_TYPE_NUMERIC: + hr = BVariantGetNumeric(&pVariable->Value, &ll); + ExitOnFailure(hr, "Failed to get numeric."); + + hr = BuffWriteNumber64(ppbBuffer, piBuffer, static_cast(ll)); + ExitOnFailure(hr, "Failed to write variable value as number."); + + SecureZeroMemory(&ll, sizeof(ll)); + break; + case BURN_VARIANT_TYPE_VERSION: + hr = BVariantGetVersion(&pVariable->Value, &qw); + ExitOnFailure(hr, "Failed to get version."); + + hr = BuffWriteNumber64(ppbBuffer, piBuffer, qw); + ExitOnFailure(hr, "Failed to write variable value as number."); + + SecureZeroMemory(&qw, sizeof(qw)); + break; + case BURN_VARIANT_TYPE_STRING: + hr = BVariantGetString(&pVariable->Value, &scz); + ExitOnFailure(hr, "Failed to get string."); + + hr = BuffWriteString(ppbBuffer, piBuffer, scz); + ExitOnFailure(hr, "Failed to write variable value as string."); + + ReleaseNullStrSecure(scz); + break; + default: + hr = E_INVALIDARG; + ExitOnFailure(hr, "Unsupported variable type."); + } + + // Write literal flag. + hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)pVariable->fLiteral); + ExitOnFailure(hr, "Failed to write literal flag."); + } + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + SecureZeroMemory(&ll, sizeof(ll)); + SecureZeroMemory(&qw, sizeof(qw)); + StrSecureZeroFreeString(scz); + + return hr; +} + +extern "C" HRESULT VariableDeserialize( + __in BURN_VARIABLES* pVariables, + __in BOOL fWasPersisted, + __in_bcount(cbBuffer) BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer + ) +{ + HRESULT hr = S_OK; + DWORD cVariables = 0; + LPWSTR sczName = NULL; + BOOL fIncluded = FALSE; + BOOL fLiteral = FALSE; + BURN_VARIANT value = { }; + LPWSTR scz = NULL; + DWORD64 qw = 0; + + ::EnterCriticalSection(&pVariables->csAccess); + + // Read variable count. + hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, &cVariables); + ExitOnFailure(hr, "Failed to read variable count."); + + // Read variables. + for (DWORD i = 0; i < cVariables; ++i) + { + // Read variable included flag. + hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&fIncluded); + ExitOnFailure(hr, "Failed to read variable included flag."); + + if (!fIncluded) + { + continue; // if variable is not included, skip. + } + + // Read variable name. + hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &sczName); + ExitOnFailure(hr, "Failed to read variable name."); + + // Read variable value type. + hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&value.Type); + ExitOnFailure(hr, "Failed to read variable value type."); + + // Read variable value. + switch (value.Type) + { + case BURN_VARIANT_TYPE_NONE: + break; + case BURN_VARIANT_TYPE_NUMERIC: + hr = BuffReadNumber64(pbBuffer, cbBuffer, piBuffer, &qw); + ExitOnFailure(hr, "Failed to read variable value as number."); + + hr = BVariantSetNumeric(&value, static_cast(qw)); + ExitOnFailure(hr, "Failed to set variable value."); + + SecureZeroMemory(&qw, sizeof(qw)); + break; + case BURN_VARIANT_TYPE_VERSION: + hr = BuffReadNumber64(pbBuffer, cbBuffer, piBuffer, &qw); + ExitOnFailure(hr, "Failed to read variable value as number."); + + hr = BVariantSetVersion(&value, qw); + ExitOnFailure(hr, "Failed to set variable value."); + + SecureZeroMemory(&qw, sizeof(qw)); + break; + case BURN_VARIANT_TYPE_STRING: + hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &scz); + ExitOnFailure(hr, "Failed to read variable value as string."); + + hr = BVariantSetString(&value, scz, NULL); + ExitOnFailure(hr, "Failed to set variable value."); + + ReleaseNullStrSecure(scz); + break; + default: + hr = E_INVALIDARG; + ExitOnFailure(hr, "Unsupported variable type."); + } + + // Read variable literal flag. + hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&fLiteral); + ExitOnFailure(hr, "Failed to read variable literal flag."); + + // Set variable. + hr = SetVariableValue(pVariables, sczName, &value, fLiteral, fWasPersisted ? SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS : SET_VARIABLE_ANY, FALSE); + ExitOnFailure(hr, "Failed to set variable."); + + // Clean up. + BVariantUninitialize(&value); + } + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + + ReleaseStr(sczName); + BVariantUninitialize(&value); + SecureZeroMemory(&qw, sizeof(qw)); + StrSecureZeroFreeString(scz); + + return hr; +} + +extern "C" HRESULT VariableStrAlloc( + __in BOOL fZeroOnRealloc, + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in DWORD_PTR cch + ) +{ + HRESULT hr = S_OK; + + if (fZeroOnRealloc) + { + hr = StrAllocSecure(ppwz, cch); + } + else + { + hr = StrAlloc(ppwz, cch); + } + + return hr; +} + +extern "C" HRESULT VariableStrAllocString( + __in BOOL fZeroOnRealloc, + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource + ) +{ + HRESULT hr = S_OK; + + if (fZeroOnRealloc) + { + hr = StrAllocStringSecure(ppwz, wzSource, cchSource); + } + else + { + hr = StrAllocString(ppwz, wzSource, cchSource); + } + + return hr; +} + +extern "C" HRESULT VariableStrAllocConcat( + __in BOOL fZeroOnRealloc, + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource + ) +{ + HRESULT hr = S_OK; + + if (fZeroOnRealloc) + { + hr = StrAllocConcatSecure(ppwz, wzSource, cchSource); + } + else + { + hr = StrAllocConcat(ppwz, wzSource, cchSource); + } + + return hr; +} + +extern "C" HRESULT __cdecl VariableStrAllocFormatted( + __in BOOL fZeroOnRealloc, + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ) +{ + HRESULT hr = S_OK; + va_list args; + + va_start(args, wzFormat); + if (fZeroOnRealloc) + { + hr = StrAllocFormattedArgsSecure(ppwz, wzFormat, args); + } + else + { + hr = StrAllocFormattedArgs(ppwz, wzFormat, args); + } + va_end(args); + + return hr; +} + +extern "C" HRESULT VariableIsHidden( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out BOOL* pfHidden + ) +{ + HRESULT hr = S_OK; + BURN_VARIABLE* pVariable = NULL; + + ::EnterCriticalSection(&pVariables->csAccess); + + hr = GetVariable(pVariables, wzVariable, &pVariable); + if (E_NOTFOUND == hr) + { + // A missing variable does not need its data hidden. + *pfHidden = FALSE; + + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to get visibility of variable: %ls", wzVariable); + + *pfHidden = pVariable->fHidden; + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + + return hr; +} + + +// internal function definitions + +// The contents of psczOut may be sensitive, should keep encrypted and SecureZeroFree. +static HRESULT FormatString( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzIn, + __out_z_opt LPWSTR* psczOut, + __out_opt DWORD* pcchOut, + __in BOOL fObfuscateHiddenVariables + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + LPWSTR sczUnformatted = NULL; + LPWSTR sczFormat = NULL; + LPCWSTR wzRead = NULL; + LPCWSTR wzOpen = NULL; + LPCWSTR wzClose = NULL; + LPWSTR scz = NULL; + LPWSTR* rgVariables = NULL; + DWORD cVariables = 0; + DWORD cch = 0; + BOOL fHidden = FALSE; + MSIHANDLE hRecord = NULL; + + ::EnterCriticalSection(&pVariables->csAccess); + + // allocate buffer for format string + hr = StrAlloc(&sczFormat, lstrlenW(wzIn) + 1); + ExitOnFailure(hr, "Failed to allocate buffer for format string."); + + // read out variables from the unformatted string and build a format string + wzRead = wzIn; + for (;;) + { + // scan for opening '[' + wzOpen = wcschr(wzRead, L'['); + if (!wzOpen) + { + // end reached, append the remainder of the string and end loop + hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, 0); + ExitOnFailure(hr, "Failed to append string."); + break; + } + + // scan for closing ']' + wzClose = wcschr(wzOpen + 1, L']'); + if (!wzClose) + { + // end reached, treat unterminated expander as literal + hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, 0); + ExitOnFailure(hr, "Failed to append string."); + break; + } + cch = wzClose - wzOpen - 1; + + if (0 == cch) + { + // blank, copy all text including the terminator + hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, (DWORD_PTR)(wzClose - wzRead) + 1); + ExitOnFailure(hr, "Failed to append string."); + } + else + { + // append text preceding expander + if (wzOpen > wzRead) + { + hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, (DWORD_PTR)(wzOpen - wzRead)); + ExitOnFailure(hr, "Failed to append string."); + } + + // get variable name + hr = VariableStrAllocString(!fObfuscateHiddenVariables, &scz, wzOpen + 1, cch); + ExitOnFailure(hr, "Failed to get variable name."); + + // allocate space in variable array + if (rgVariables) + { + LPVOID pv = MemReAlloc(rgVariables, sizeof(LPWSTR) * (cVariables + 1), TRUE); + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate variable array."); + rgVariables = (LPWSTR*)pv; + } + else + { + rgVariables = (LPWSTR*)MemAlloc(sizeof(LPWSTR) * (cVariables + 1), TRUE); + ExitOnNull(rgVariables, hr, E_OUTOFMEMORY, "Failed to allocate variable array."); + } + + // set variable value + if (2 <= cch && L'\\' == wzOpen[1]) + { + // escape sequence, copy character + hr = VariableStrAllocString(!fObfuscateHiddenVariables, &rgVariables[cVariables], &wzOpen[2], 1); + } + else + { + if (fObfuscateHiddenVariables) + { + hr = VariableIsHidden(pVariables, scz, &fHidden); + ExitOnFailure(hr, "Failed to determine variable visibility: '%ls'.", scz); + } + + if (fHidden) + { + hr = StrAllocString(&rgVariables[cVariables], L"*****", 0); + } + else + { + // get formatted variable value + hr = VariableGetFormatted(pVariables, scz, &rgVariables[cVariables]); + if (E_NOTFOUND == hr) // variable not found + { + hr = StrAllocStringSecure(&rgVariables[cVariables], L"", 0); + } + } + } + ExitOnFailure(hr, "Failed to set variable value."); + ++cVariables; + + // append placeholder to format string + hr = VariableStrAllocFormatted(!fObfuscateHiddenVariables, &scz, L"[%d]", cVariables); + ExitOnFailure(hr, "Failed to format placeholder string."); + + hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, scz, 0); + ExitOnFailure(hr, "Failed to append placeholder."); + } + + // update read pointer + wzRead = wzClose + 1; + } + + // create record + hRecord = ::MsiCreateRecord(cVariables); + ExitOnNull(hRecord, hr, E_OUTOFMEMORY, "Failed to allocate record."); + + // set format string + er = ::MsiRecordSetStringW(hRecord, 0, sczFormat); + ExitOnWin32Error(er, hr, "Failed to set record format string."); + + // copy record fields + for (DWORD i = 0; i < cVariables; ++i) + { + if (*rgVariables[i]) // not setting if blank + { + er = ::MsiRecordSetStringW(hRecord, i + 1, rgVariables[i]); + ExitOnWin32Error(er, hr, "Failed to set record string."); + } + } + + // get formatted character count + cch = 0; +#pragma prefast(push) +#pragma prefast(disable:6298) + er = ::MsiFormatRecordW(NULL, hRecord, L"", &cch); +#pragma prefast(pop) + if (ERROR_MORE_DATA != er) + { + ExitOnWin32Error(er, hr, "Failed to get formatted length."); + } + + // return formatted string + if (psczOut) + { + hr = VariableStrAlloc(!fObfuscateHiddenVariables, &scz, ++cch); + ExitOnFailure(hr, "Failed to allocate string."); + + er = ::MsiFormatRecordW(NULL, hRecord, scz, &cch); + ExitOnWin32Error(er, hr, "Failed to format record."); + + hr = VariableStrAllocString(!fObfuscateHiddenVariables, psczOut, scz, 0); + ExitOnFailure(hr, "Failed to copy string."); + } + + // return character count + if (pcchOut) + { + *pcchOut = cch; + } + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + + if (rgVariables) + { + for (DWORD i = 0; i < cVariables; ++i) + { + if (fObfuscateHiddenVariables) + { + ReleaseStr(rgVariables[i]); + } + else + { + StrSecureZeroFreeString(rgVariables[i]); + } + } + MemFree(rgVariables); + } + + if (hRecord) + { + ::MsiCloseHandle(hRecord); + } + + if (fObfuscateHiddenVariables) + { + ReleaseStr(sczUnformatted); + ReleaseStr(sczFormat); + ReleaseStr(scz); + } + else + { + StrSecureZeroFreeString(sczUnformatted); + StrSecureZeroFreeString(sczFormat); + StrSecureZeroFreeString(scz); + } + + return hr; +} + +static HRESULT AddBuiltInVariable( + __in BURN_VARIABLES* pVariables, + __in LPCWSTR wzVariable, + __in PFN_INITIALIZEVARIABLE pfnInitialize, + __in DWORD_PTR dwpInitializeData, + __in BOOL fPersist, + __in BOOL fOverridable + ) +{ + HRESULT hr = S_OK; + DWORD iVariable = 0; + BURN_VARIABLE* pVariable = NULL; + + hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable); + ExitOnFailure(hr, "Failed to find variable value."); + + // insert element if not found + if (S_FALSE == hr) + { + hr = InsertVariable(pVariables, wzVariable, iVariable); + ExitOnFailure(hr, "Failed to insert variable."); + } + + // set variable values + pVariable = &pVariables->rgVariables[iVariable]; + pVariable->fPersisted = fPersist; + pVariable->internalType = fOverridable ? BURN_VARIABLE_INTERNAL_TYPE_OVERRIDABLE_BUILTIN : BURN_VARIABLE_INTERNAL_TYPE_BUILTIN; + pVariable->pfnInitialize = pfnInitialize; + pVariable->dwpInitializeData = dwpInitializeData; + +LExit: + return hr; +} + +static HRESULT GetVariable( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out BURN_VARIABLE** ppVariable + ) +{ + HRESULT hr = S_OK; + DWORD iVariable = 0; + BURN_VARIABLE* pVariable = NULL; + + hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable); + ExitOnFailure(hr, "Failed to find variable value '%ls'.", wzVariable); + + if (S_FALSE == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + + pVariable = &pVariables->rgVariables[iVariable]; + + // initialize built-in variable + if (BURN_VARIANT_TYPE_NONE == pVariable->Value.Type && BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariable->internalType) + { + hr = pVariable->pfnInitialize(pVariable->dwpInitializeData, &pVariable->Value); + ExitOnFailure(hr, "Failed to initialize built-in variable value '%ls'.", wzVariable); + } + + *ppVariable = pVariable; + +LExit: + return hr; +} + +static HRESULT FindVariableIndexByName( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out DWORD* piVariable + ) +{ + HRESULT hr = S_OK; + DWORD iRangeFirst = 0; + DWORD cRangeLength = pVariables->cVariables; + + while (cRangeLength) + { + // get variable in middle of range + DWORD iPosition = cRangeLength / 2; + BURN_VARIABLE* pVariable = &pVariables->rgVariables[iRangeFirst + iPosition]; + + switch (::CompareStringW(LOCALE_INVARIANT, SORT_STRINGSORT, wzVariable, -1, pVariable->sczName, -1)) + { + case CSTR_LESS_THAN: + // restrict range to elements before the current + cRangeLength = iPosition; + break; + case CSTR_EQUAL: + // variable found + *piVariable = iRangeFirst + iPosition; + ExitFunction1(hr = S_OK); + case CSTR_GREATER_THAN: + // restrict range to elements after the current + iRangeFirst += iPosition + 1; + cRangeLength -= iPosition + 1; + break; + default: + ExitWithLastError(hr, "Failed to compare strings."); + } + } + + *piVariable = iRangeFirst; + hr = S_FALSE; // variable not found + +LExit: + return hr; +} + +static HRESULT InsertVariable( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in DWORD iPosition + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + + // ensure there is room in the variable array + if (pVariables->cVariables == pVariables->dwMaxVariables) + { + hr = ::DWordAdd(pVariables->dwMaxVariables, GROW_VARIABLE_ARRAY, &(pVariables->dwMaxVariables)); + ExitOnRootFailure(hr, "Overflow while growing variable array size"); + + if (pVariables->rgVariables) + { + hr = ::SizeTMult(sizeof(BURN_VARIABLE), pVariables->dwMaxVariables, &cbAllocSize); + ExitOnRootFailure(hr, "Overflow while calculating size of variable array buffer"); + + LPVOID pv = MemReAlloc(pVariables->rgVariables, cbAllocSize, FALSE); + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate room for more variables."); + + // Prefast claims it's possible to hit this. Putting the check in just in case. + if (pVariables->dwMaxVariables < pVariables->cVariables) + { + hr = INTSAFE_E_ARITHMETIC_OVERFLOW; + ExitOnRootFailure(hr, "Overflow while dealing with variable array buffer allocation"); + } + + pVariables->rgVariables = (BURN_VARIABLE*)pv; + memset(&pVariables->rgVariables[pVariables->cVariables], 0, sizeof(BURN_VARIABLE) * (pVariables->dwMaxVariables - pVariables->cVariables)); + } + else + { + pVariables->rgVariables = (BURN_VARIABLE*)MemAlloc(sizeof(BURN_VARIABLE) * pVariables->dwMaxVariables, TRUE); + ExitOnNull(pVariables->rgVariables, hr, E_OUTOFMEMORY, "Failed to allocate room for variables."); + } + } + + // move variables + if (0 < pVariables->cVariables - iPosition) + { + memmove(&pVariables->rgVariables[iPosition + 1], &pVariables->rgVariables[iPosition], sizeof(BURN_VARIABLE) * (pVariables->cVariables - iPosition)); + memset(&pVariables->rgVariables[iPosition], 0, sizeof(BURN_VARIABLE)); + } + + ++pVariables->cVariables; + + // allocate name + hr = StrAllocString(&pVariables->rgVariables[iPosition].sczName, wzVariable, 0); + ExitOnFailure(hr, "Failed to copy variable name."); + +LExit: + return hr; +} + +static HRESULT SetVariableValue( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in BURN_VARIANT* pVariant, + __in BOOL fLiteral, + __in SET_VARIABLE setBuiltin, + __in BOOL fLog + ) +{ + HRESULT hr = S_OK; + DWORD iVariable = 0; + + ::EnterCriticalSection(&pVariables->csAccess); + + hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable); + ExitOnFailure(hr, "Failed to find variable value '%ls'.", wzVariable); + + // Insert element if not found. + if (S_FALSE == hr) + { + hr = InsertVariable(pVariables, wzVariable, iVariable); + ExitOnFailure(hr, "Failed to insert variable '%ls'.", wzVariable); + } + else if (BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariables->rgVariables[iVariable].internalType) // built-in variables must be overridden. + { + if (SET_VARIABLE_OVERRIDE_BUILTIN == setBuiltin || + (SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS == setBuiltin && pVariables->rgVariables[iVariable].fPersisted) || + SET_VARIABLE_ANY == setBuiltin && BURN_VARIABLE_INTERNAL_TYPE_BUILTIN != pVariables->rgVariables[iVariable].internalType) + { + hr = S_OK; + } + else + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Attempt to set built-in variable value: %ls", wzVariable); + } + } + else // must *not* be a built-in variable so caller should not have tried to override it as a built-in. + { + // Not possible from external callers so just assert. + AssertSz(SET_VARIABLE_OVERRIDE_BUILTIN != setBuiltin, "Intent to overwrite non-built-in variable."); + } + + // Log value when not overwriting a built-in variable. + if (fLog && BURN_VARIABLE_INTERNAL_TYPE_NORMAL == pVariables->rgVariables[iVariable].internalType) + { + if (pVariables->rgVariables[iVariable].fHidden) + { + LogStringLine(REPORT_STANDARD, "Setting hidden variable '%ls'", wzVariable); + } + else + { + // Assume value isn't encrypted since it's not hidden. + switch (pVariant->Type) + { + case BURN_VARIANT_TYPE_NONE: + if (BURN_VARIANT_TYPE_NONE != pVariables->rgVariables[iVariable].Value.Type) + { + LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable, pVariant->sczValue); + } + break; + + case BURN_VARIANT_TYPE_NUMERIC: + LogStringLine(REPORT_STANDARD, "Setting numeric variable '%ls' to value %lld", wzVariable, pVariant->llValue); + break; + + case BURN_VARIANT_TYPE_STRING: + if (!pVariant->sczValue) + { + LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable, pVariant->sczValue); + } + else + { + LogStringLine(REPORT_STANDARD, "Setting string variable '%ls' to value '%ls'", wzVariable, pVariant->sczValue); + } + break; + + case BURN_VARIANT_TYPE_VERSION: + LogStringLine(REPORT_STANDARD, "Setting version variable '%ls' to value '%hu.%hu.%hu.%hu'", wzVariable, (WORD)(pVariant->qwValue >> 48), (WORD)(pVariant->qwValue >> 32), (WORD)(pVariant->qwValue >> 16), (WORD)(pVariant->qwValue)); + break; + + default: + AssertSz(FALSE, "Unknown variant type."); + break; + } + } + } + + // Update variable value. + hr = BVariantSetValue(&pVariables->rgVariables[iVariable].Value, pVariant); + ExitOnFailure(hr, "Failed to set value of variable: %ls", wzVariable); + + // Update variable literal flag. + pVariables->rgVariables[iVariable].fLiteral = fLiteral; + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + + if (FAILED(hr) && fLog) + { + LogStringLine(REPORT_STANDARD, "Setting variable failed: ID '%ls', HRESULT 0x%x", wzVariable, hr); + } + + return hr; +} + +extern "C" typedef NTSTATUS (NTAPI *RTL_GET_VERSION)(_Out_ PRTL_OSVERSIONINFOEXW lpVersionInformation); + +static HRESULT InitializeVariableVersionNT( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + HMODULE ntdll = NULL; + RTL_GET_VERSION rtlGetVersion = NULL; + RTL_OSVERSIONINFOEXW ovix = { }; + BURN_VARIANT value = { }; + + if (!::GetModuleHandleExW(0, L"ntdll", &ntdll)) + { + ExitWithLastError(hr, "Failed to locate NTDLL."); + } + + rtlGetVersion = reinterpret_cast(::GetProcAddress(ntdll, "RtlGetVersion")); + if (NULL == rtlGetVersion) + { + ExitWithLastError(hr, "Failed to locate RtlGetVersion."); + } + + ovix.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); + hr = static_cast(rtlGetVersion(&ovix)); + ExitOnFailure(hr, "Failed to get OS info."); + + switch ((OS_INFO_VARIABLE)dwpData) + { + case OS_INFO_VARIABLE_ServicePackLevel: + if (0 != ovix.wServicePackMajor) + { + value.qwValue = static_cast(ovix.wServicePackMajor); + value.Type = BURN_VARIANT_TYPE_NUMERIC; + } + break; + case OS_INFO_VARIABLE_VersionNT: + value.qwValue = MAKEQWORDVERSION(ovix.dwMajorVersion, ovix.dwMinorVersion, 0, 0); + value.Type = BURN_VARIANT_TYPE_VERSION; + break; + case OS_INFO_VARIABLE_VersionNT64: + { +#if !defined(_WIN64) + BOOL fIsWow64 = FALSE; + + ProcWow64(::GetCurrentProcess(), &fIsWow64); + if (fIsWow64) +#endif + { + value.qwValue = MAKEQWORDVERSION(ovix.dwMajorVersion, ovix.dwMinorVersion, 0, 0); + value.Type = BURN_VARIANT_TYPE_VERSION; + } + } + break; + default: + AssertSz(FALSE, "Unknown OS info type."); + break; + } + + hr = BVariantCopy(&value, pValue); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + if (NULL != ntdll) + { + FreeLibrary(ntdll); + } + + return hr; +} + +static HRESULT InitializeVariableOsInfo( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + OSVERSIONINFOEXW ovix = { }; + BURN_VARIANT value = { }; + + ovix.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); + if (!::GetVersionExW((LPOSVERSIONINFOW)&ovix)) + { + ExitWithLastError(hr, "Failed to get OS info."); + } + + switch ((OS_INFO_VARIABLE)dwpData) + { + case OS_INFO_VARIABLE_NTProductType: + value.llValue = ovix.wProductType; + value.Type = BURN_VARIANT_TYPE_NUMERIC; + break; + case OS_INFO_VARIABLE_NTSuiteBackOffice: + value.llValue = VER_SUITE_BACKOFFICE & ovix.wSuiteMask ? 1 : 0; + value.Type = BURN_VARIANT_TYPE_NUMERIC; + break; + case OS_INFO_VARIABLE_NTSuiteDataCenter: + value.llValue = VER_SUITE_DATACENTER & ovix.wSuiteMask ? 1 : 0; + value.Type = BURN_VARIANT_TYPE_NUMERIC; + break; + case OS_INFO_VARIABLE_NTSuiteEnterprise: + value.llValue = VER_SUITE_ENTERPRISE & ovix.wSuiteMask ? 1 : 0; + value.Type = BURN_VARIANT_TYPE_NUMERIC; + break; + case OS_INFO_VARIABLE_NTSuitePersonal: + value.llValue = VER_SUITE_PERSONAL & ovix.wSuiteMask ? 1 : 0; + value.Type = BURN_VARIANT_TYPE_NUMERIC; + break; + case OS_INFO_VARIABLE_NTSuiteSmallBusiness: + value.llValue = VER_SUITE_SMALLBUSINESS & ovix.wSuiteMask ? 1 : 0; + value.Type = BURN_VARIANT_TYPE_NUMERIC; + break; + case OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted: + value.llValue = VER_SUITE_SMALLBUSINESS_RESTRICTED & ovix.wSuiteMask ? 1 : 0; + value.Type = BURN_VARIANT_TYPE_NUMERIC; + break; + case OS_INFO_VARIABLE_NTSuiteWebServer: + value.llValue = VER_SUITE_BLADE & ovix.wSuiteMask ? 1 : 0; + value.Type = BURN_VARIANT_TYPE_NUMERIC; + break; + case OS_INFO_VARIABLE_CompatibilityMode: + { + DWORDLONG dwlConditionMask = 0; + VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_EQUAL); + VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_EQUAL); + VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMAJOR, VER_EQUAL); + VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMINOR, VER_EQUAL); + + value.llValue = ::VerifyVersionInfoW(&ovix, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, dwlConditionMask); + value.Type = BURN_VARIANT_TYPE_NUMERIC; + } + break; + case OS_INFO_VARIABLE_TerminalServer: + value.llValue = (VER_SUITE_TERMINAL == (ovix.wSuiteMask & VER_SUITE_TERMINAL)) && (VER_SUITE_SINGLEUSERTS != (ovix.wSuiteMask & VER_SUITE_SINGLEUSERTS)) ? 1 : 0; + value.Type = BURN_VARIANT_TYPE_NUMERIC; + break; + default: + AssertSz(FALSE, "Unknown OS info type."); + break; + } + + hr = BVariantCopy(&value, pValue); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeVariableSystemInfo( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + SYSTEM_INFO si = { }; + BURN_VARIANT value = { }; + + ::GetNativeSystemInfo(&si); + + switch ((OS_INFO_VARIABLE)dwpData) + { + case OS_INFO_VARIABLE_ProcessorArchitecture: + value.llValue = si.wProcessorArchitecture; + value.Type = BURN_VARIANT_TYPE_NUMERIC; + break; + default: + AssertSz(FALSE, "Unknown OS info type."); + break; + } + + hr = BVariantCopy(&value, pValue); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeVariableComputerName( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + UNREFERENCED_PARAMETER(dwpData); + + HRESULT hr = S_OK; + WCHAR wzComputerName[MAX_COMPUTERNAME_LENGTH + 1] = { }; + DWORD cchComputerName = countof(wzComputerName); + + // get computer name + if (!::GetComputerNameW(wzComputerName, &cchComputerName)) + { + ExitWithLastError(hr, "Failed to get computer name."); + } + + // set value + hr = BVariantSetString(pValue, wzComputerName, 0); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeVariableVersionMsi( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + UNREFERENCED_PARAMETER(dwpData); + + HRESULT hr = S_OK; + DLLGETVERSIONPROC pfnMsiDllGetVersion = NULL; + DLLVERSIONINFO msiVersionInfo = { }; + + // get DllGetVersion proc address + pfnMsiDllGetVersion = (DLLGETVERSIONPROC)::GetProcAddress(::GetModuleHandleW(L"msi"), "DllGetVersion"); + ExitOnNullWithLastError(pfnMsiDllGetVersion, hr, "Failed to find DllGetVersion entry point in msi.dll."); + + // get msi.dll version info + msiVersionInfo.cbSize = sizeof(DLLVERSIONINFO); + hr = pfnMsiDllGetVersion(&msiVersionInfo); + ExitOnFailure(hr, "Failed to get msi.dll version info."); + + hr = BVariantSetVersion(pValue, MAKEQWORDVERSION(msiVersionInfo.dwMajorVersion, msiVersionInfo.dwMinorVersion, 0, 0)); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeVariableCsidlFolder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + int nFolder = (int)dwpData; + + // get folder path + hr = ShelGetFolder(&sczPath, nFolder); + ExitOnRootFailure(hr, "Failed to get shell folder."); + + // set value + hr = BVariantSetString(pValue, sczPath, 0); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + ReleaseStr(sczPath); + + return hr; +} + +static HRESULT InitializeVariableTempFolder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + UNREFERENCED_PARAMETER(dwpData); + + HRESULT hr = S_OK; + WCHAR wzPath[MAX_PATH] = { }; + + // get volume path name + if (!::GetTempPathW(MAX_PATH, wzPath)) + { + ExitWithLastError(hr, "Failed to get temp path."); + } + + // set value + hr = BVariantSetString(pValue, wzPath, 0); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeVariableSystemFolder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + BOOL f64 = (BOOL)dwpData; + WCHAR wzSystemFolder[MAX_PATH] = { }; + +#if !defined(_WIN64) + BOOL fIsWow64 = FALSE; + ProcWow64(::GetCurrentProcess(), &fIsWow64); + + if (fIsWow64) + { + if (f64) + { + if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder))) + { + ExitWithLastError(hr, "Failed to get 64-bit system folder."); + } + } + else + { + if (!::GetSystemWow64DirectoryW(wzSystemFolder, countof(wzSystemFolder))) + { + ExitWithLastError(hr, "Failed to get 32-bit system folder."); + } + } + } + else + { + if (!f64) + { + if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder))) + { + ExitWithLastError(hr, "Failed to get 32-bit system folder."); + } + } + } +#else + if (f64) + { + if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder))) + { + ExitWithLastError(hr, "Failed to get 64-bit system folder."); + } + } + else + { + if (!::GetSystemWow64DirectoryW(wzSystemFolder, countof(wzSystemFolder))) + { + ExitWithLastError(hr, "Failed to get 32-bit system folder."); + } + } +#endif + + if (*wzSystemFolder) + { + hr = PathFixedBackslashTerminate(wzSystemFolder, countof(wzSystemFolder)); + ExitOnFailure(hr, "Failed to backslash terminate system folder."); + } + + // set value + hr = BVariantSetString(pValue, wzSystemFolder, 0); + ExitOnFailure(hr, "Failed to set system folder variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeVariableWindowsVolumeFolder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + UNREFERENCED_PARAMETER(dwpData); + + HRESULT hr = S_OK; + WCHAR wzWindowsPath[MAX_PATH] = { }; + WCHAR wzVolumePath[MAX_PATH] = { }; + + // get windows directory + if (!::GetWindowsDirectoryW(wzWindowsPath, countof(wzWindowsPath))) + { + ExitWithLastError(hr, "Failed to get windows directory."); + } + + // get volume path name + if (!::GetVolumePathNameW(wzWindowsPath, wzVolumePath, MAX_PATH)) + { + ExitWithLastError(hr, "Failed to get volume path name."); + } + + // set value + hr = BVariantSetString(pValue, wzVolumePath, 0); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeVariablePrivileged( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + UNREFERENCED_PARAMETER(dwpData); + + HRESULT hr = S_OK; + BOOL fPrivileged = FALSE; + + // check if process could run privileged. + hr = OsCouldRunPrivileged(&fPrivileged); + ExitOnFailure(hr, "Failed to check if process could run privileged."); + + // set value + hr = BVariantSetNumeric(pValue, fPrivileged); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeVariableRebootPending( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + UNREFERENCED_PARAMETER(dwpData); + + HRESULT hr = S_OK; + BOOL fRebootPending = FALSE; + BOOL fComInitialized = FALSE; + + // Do a best effort to ask WU if a reboot is required. If anything goes + // wrong then let's pretend a reboot is not required. + hr = ::CoInitialize(NULL); + if (SUCCEEDED(hr) || RPC_E_CHANGED_MODE == hr) + { + fComInitialized = TRUE; + + hr = WuaRestartRequired(&fRebootPending); + if (FAILED(hr)) + { + fRebootPending = FALSE; + hr = S_OK; + } + } + + hr = BVariantSetNumeric(pValue, fRebootPending); + ExitOnFailure(hr, "Failed to set reboot pending variant value."); + +LExit: + if (fComInitialized) + { + ::CoUninitialize(); + } + + return hr; +} + +static HRESULT InitializeSystemLanguageID( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + UNREFERENCED_PARAMETER(dwpData); + + HRESULT hr = S_OK; + LANGID langid = ::GetSystemDefaultLangID(); + + hr = BVariantSetNumeric(pValue, langid); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeUserUILanguageID( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + UNREFERENCED_PARAMETER(dwpData); + + HRESULT hr = S_OK; + LANGID langid = ::GetUserDefaultUILanguage(); + + hr = BVariantSetNumeric(pValue, langid); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeUserLanguageID( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + UNREFERENCED_PARAMETER(dwpData); + + HRESULT hr = S_OK; + LANGID langid = ::GetUserDefaultLangID(); + + hr = BVariantSetNumeric(pValue, langid); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeVariableString( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzValue = (LPCWSTR)dwpData; + + // set value + hr = BVariantSetString(pValue, wzValue, 0); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeVariableNumeric( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + LONGLONG llValue = (LONGLONG)dwpData; + + // set value + hr = BVariantSetNumeric(pValue, llValue); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeVariableRegistryFolder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + int nFolder = (int)dwpData; + LPWSTR sczPath = NULL; + +#if !defined(_WIN64) + BOOL fIsWow64 = FALSE; + + ProcWow64(::GetCurrentProcess(), &fIsWow64); + if (!fIsWow64) // on 32-bit machines, variables aren't set + { + ExitFunction(); + } +#endif + + hr = Get64bitFolderFromRegistry(nFolder, &sczPath); + ExitOnFailure(hr, "Failed to get 64-bit folder."); + + // set value + hr = BVariantSetString(pValue, sczPath, 0); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + ReleaseStr(sczPath); + + return hr; +} + +static HRESULT InitializeVariable6432Folder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + int nFolder = (int)dwpData; + LPWSTR sczPath = NULL; + +#if !defined(_WIN64) + BOOL fIsWow64 = FALSE; + + // If 32-bit use shell-folder. + ProcWow64(::GetCurrentProcess(), &fIsWow64); + if (!fIsWow64) + { + hr = ShelGetFolder(&sczPath, nFolder); + ExitOnRootFailure(hr, "Failed to get shell folder."); + } + else +#endif + { + hr = Get64bitFolderFromRegistry(nFolder, &sczPath); + ExitOnFailure(hr, "Failed to get 64-bit folder."); + } + + // set value + hr = BVariantSetString(pValue, sczPath, 0); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + ReleaseStr(sczPath); + + return hr; +} + +// Get the date in the same format as Windows Installer. +static HRESULT InitializeVariableDate( + __in DWORD_PTR /*dwpData*/, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + SYSTEMTIME systime = { }; + LPWSTR sczDate = NULL; + int cchDate = 0; + + ::GetSystemTime(&systime); + + cchDate = ::GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, NULL, NULL, cchDate); + if (!cchDate) + { + ExitOnLastError(hr, "Failed to get the required buffer length for the Date."); + } + + hr = StrAlloc(&sczDate, cchDate); + ExitOnFailure(hr, "Failed to allocate the buffer for the Date."); + + if (!::GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, NULL, sczDate, cchDate)) + { + ExitOnLastError(hr, "Failed to get the Date."); + } + + // set value + hr = BVariantSetString(pValue, sczDate, cchDate); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + ReleaseStr(sczDate); + + return hr; +} + +static HRESULT InitializeVariableInstallerName( + __in DWORD_PTR /*dwpData*/, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + + // set value + hr = BVariantSetString(pValue, L"WiX Burn", 0); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeVariableInstallerVersion( + __in DWORD_PTR /*dwpData*/, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + LPWSTR sczVersion = NULL; + + hr = StrAllocStringAnsi(&sczVersion, szVerMajorMinorBuild, 0, CP_ACP); + ExitOnFailure(hr, "Failed to copy the engine version."); + + // set value + hr = BVariantSetString(pValue, sczVersion, 0); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + ReleaseStr(sczVersion); + + return hr; +} + +static HRESULT InitializeVariableVersion( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + + // set value + hr = BVariantSetVersion(pValue, static_cast(dwpData)); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +// Get the current user the same as Windows Installer. +static HRESULT InitializeVariableLogonUser( + __in DWORD_PTR /*dwpData*/, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + WCHAR wzUserName[UNLEN + 1]; + DWORD cchUserName = countof(wzUserName); + + if (!::GetUserNameW(wzUserName, &cchUserName)) + { + ExitOnLastError(hr, "Failed to get the user name."); + } + + // set value + hr = BVariantSetString(pValue, wzUserName, 0); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT Get64bitFolderFromRegistry( + __in int nFolder, + __deref_out_z LPWSTR* psczPath + ) +{ + HRESULT hr = S_OK; + HKEY hkFolders = NULL; + + AssertSz(CSIDL_PROGRAM_FILES == nFolder || CSIDL_PROGRAM_FILES_COMMON == nFolder, "Unknown folder CSIDL."); + LPCWSTR wzFolderValue = CSIDL_PROGRAM_FILES_COMMON == nFolder ? L"CommonFilesDir" : L"ProgramFilesDir"; + + hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion", KEY_READ | KEY_WOW64_64KEY, &hkFolders); + ExitOnFailure(hr, "Failed to open Windows folder key."); + + hr = RegReadString(hkFolders, wzFolderValue, psczPath); + ExitOnFailure(hr, "Failed to read folder path for '%ls'.", wzFolderValue); + + hr = PathBackslashTerminate(psczPath); + ExitOnFailure(hr, "Failed to ensure path was backslash terminated."); + +LExit: + ReleaseRegKey(hkFolders); + + return hr; +} + diff --git a/src/engine/variable.h b/src/engine/variable.h new file mode 100644 index 00000000..c48e0160 --- /dev/null +++ b/src/engine/variable.h @@ -0,0 +1,190 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + +const LPCWSTR VARIABLE_DATE = L"Date"; +const LPCWSTR VARIABLE_LOGONUSER = L"LogonUser"; +const LPCWSTR VARIABLE_INSTALLERNAME = L"InstallerName"; +const LPCWSTR VARIABLE_INSTALLERVERSION = L"InstallerVersion"; + + +// typedefs + +typedef HRESULT (*PFN_INITIALIZEVARIABLE)( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); + + +// constants + +enum BURN_VARIABLE_INTERNAL_TYPE +{ + BURN_VARIABLE_INTERNAL_TYPE_NORMAL, // the BA can set this variable. + BURN_VARIABLE_INTERNAL_TYPE_OVERRIDABLE_BUILTIN, // the BA can't set this variable, but the unelevated process can serialize it to the elevated process. + BURN_VARIABLE_INTERNAL_TYPE_BUILTIN, // the BA can't set this variable, and the unelevated process can't serialize it to the elevated process. +}; + + +// structs + +typedef struct _BURN_VARIABLE +{ + LPWSTR sczName; + BURN_VARIANT Value; + BOOL fHidden; + BOOL fLiteral; // if fLiteral, then when formatting this variable its value should be used as is (don't continue recursively formatting). + BOOL fPersisted; + + // used for late initialization of built-in variables + BURN_VARIABLE_INTERNAL_TYPE internalType; + PFN_INITIALIZEVARIABLE pfnInitialize; + DWORD_PTR dwpInitializeData; +} BURN_VARIABLE; + +typedef struct _BURN_VARIABLES +{ + CRITICAL_SECTION csAccess; + DWORD dwMaxVariables; + DWORD cVariables; + BURN_VARIABLE* rgVariables; +} BURN_VARIABLES; + + +// function declarations + +HRESULT VariableInitialize( + __in BURN_VARIABLES* pVariables + ); +HRESULT VariablesParseFromXml( + __in BURN_VARIABLES* pVariables, + __in IXMLDOMNode* pixnBundle + ); +void VariablesUninitialize( + __in BURN_VARIABLES* pVariables + ); +void VariablesDump( + __in BURN_VARIABLES* pVariables + ); +HRESULT VariableGetNumeric( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out LONGLONG* pllValue + ); +HRESULT VariableGetString( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out_z LPWSTR* psczValue + ); +HRESULT VariableGetVersion( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in DWORD64* pqwValue + ); +HRESULT VariableGetVariant( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in BURN_VARIANT* pValue + ); +HRESULT VariableGetFormatted( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out_z LPWSTR* psczValue + ); +HRESULT VariableSetNumeric( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in LONGLONG llValue, + __in BOOL fOverwriteBuiltIn + ); +HRESULT VariableSetLiteralString( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in_z_opt LPCWSTR wzValue, + __in BOOL fOverwriteBuiltIn + ); +HRESULT VariableSetString( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in_z_opt LPCWSTR wzValue, + __in BOOL fOverwriteBuiltIn + ); +HRESULT VariableSetVersion( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in DWORD64 qwValue, + __in BOOL fOverwriteBuiltIn + ); +HRESULT VariableSetLiteralVariant( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in BURN_VARIANT* pVariant + ); +HRESULT VariableFormatString( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzIn, + __out_z_opt LPWSTR* psczOut, + __out_opt DWORD* pcchOut + ); +HRESULT VariableFormatStringObfuscated( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzIn, + __out_z_opt LPWSTR* psczOut, + __out_opt DWORD* pcchOut + ); +HRESULT VariableEscapeString( + __in_z LPCWSTR wzIn, + __out_z LPWSTR* psczOut + ); +HRESULT VariableSerialize( + __in BURN_VARIABLES* pVariables, + __in BOOL fPersisting, + __inout BYTE** ppbBuffer, + __inout SIZE_T* piBuffer + ); +HRESULT VariableDeserialize( + __in BURN_VARIABLES* pVariables, + __in BOOL fWasPersisted, + __in_bcount(cbBuffer) BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer + ); +HRESULT VariableStrAlloc( + __in BOOL fZeroOnRealloc, + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in DWORD_PTR cch + ); +HRESULT VariableStrAllocString( + __in BOOL fZeroOnRealloc, + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource + ); +HRESULT VariableStrAllocConcat( + __in BOOL fZeroOnRealloc, + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource + ); +HRESULT __cdecl VariableStrAllocFormatted( + __in BOOL fZeroOnRealloc, + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ); +HRESULT VariableIsHidden( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out BOOL* pfHidden + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/variant.cpp b/src/engine/variant.cpp new file mode 100644 index 00000000..2a9f08ed --- /dev/null +++ b/src/engine/variant.cpp @@ -0,0 +1,601 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#define VARIANT_ENCRYPTION_SCOPE CRYPTPROTECTMEMORY_SAME_PROCESS + +// internal function declarations + +static HRESULT BVariantEncryptNumeric( + __in BURN_VARIANT* pVariant, + __in BOOL fEncrypt + ); + +static HRESULT BVariantEncryptString( + __in BURN_VARIANT* pVariant, + __in BOOL fEncrypt + ); + +static HRESULT BVariantEncryptVersion( + __in BURN_VARIANT* pVariant, + __in BOOL fEncrypt + ); + +static HRESULT BVariantRetrieveDecryptedNumeric( + __in BURN_VARIANT* pVariant, + __out LONGLONG* pllValue + ); + +static HRESULT BVariantRetrieveDecryptedString( + __in BURN_VARIANT* pVariant, + __out LPWSTR* psczValue + ); + +static HRESULT BVariantRetrieveDecryptedVersion( + __in BURN_VARIANT* pVariant, + __out DWORD64* pqwValue + ); + +// function definitions + +extern "C" void BVariantUninitialize( + __in BURN_VARIANT* pVariant + ) +{ + if (BURN_VARIANT_TYPE_STRING == pVariant->Type) + { + StrSecureZeroFreeString(pVariant->sczValue); + } + SecureZeroMemory(pVariant, sizeof(BURN_VARIANT)); +} + +// The contents of pllValue may be sensitive, should keep encrypted and SecureZeroMemory. +extern "C" HRESULT BVariantGetNumeric( + __in BURN_VARIANT* pVariant, + __out LONGLONG* pllValue + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + switch (pVariant->Type) + { + case BURN_VARIANT_TYPE_NUMERIC: + BVariantRetrieveDecryptedNumeric(pVariant, pllValue); + break; + case BURN_VARIANT_TYPE_STRING: + hr = BVariantRetrieveDecryptedString(pVariant, &sczValue); + if (SUCCEEDED(hr)) + { + hr = StrStringToInt64(sczValue, 0, pllValue); + if (FAILED(hr)) + { + hr = DISP_E_TYPEMISMATCH; + } + } + StrSecureZeroFreeString(sczValue); + break; + case BURN_VARIANT_TYPE_VERSION: + BVariantRetrieveDecryptedVersion(pVariant, (DWORD64*)pllValue); + break; + default: + hr = E_INVALIDARG; + break; + } + + return hr; +} + +// The contents of psczValue may be sensitive, should keep encrypted and SecureZeroFree. +extern "C" HRESULT BVariantGetString( + __in BURN_VARIANT* pVariant, + __out_z LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + LONGLONG llValue = 0; + DWORD64 qwValue = 0; + + switch (pVariant->Type) + { + case BURN_VARIANT_TYPE_NUMERIC: + hr = BVariantRetrieveDecryptedNumeric(pVariant, &llValue); + if (SUCCEEDED(hr)) + { + hr = StrAllocFormattedSecure(psczValue, L"%I64d", llValue); + ExitOnFailure(hr, "Failed to convert int64 to string."); + } + SecureZeroMemory(&llValue, sizeof(llValue)); + break; + case BURN_VARIANT_TYPE_STRING: + hr = BVariantRetrieveDecryptedString(pVariant, psczValue); + break; + case BURN_VARIANT_TYPE_VERSION: + hr = BVariantRetrieveDecryptedVersion(pVariant, &qwValue); + if (SUCCEEDED(hr)) + { + hr = StrAllocFormattedSecure(psczValue, L"%hu.%hu.%hu.%hu", + (WORD)(qwValue >> 48), + (WORD)(qwValue >> 32), + (WORD)(qwValue >> 16), + (WORD)qwValue); + ExitOnFailure(hr, "Failed to convert version to string."); + } + SecureZeroMemory(&qwValue, sizeof(qwValue)); + break; + default: + hr = E_INVALIDARG; + break; + } + +LExit: + return hr; +} + +// The contents of pqwValue may be sensitive, should keep encrypted and SecureZeroMemory. +extern "C" HRESULT BVariantGetVersion( + __in BURN_VARIANT* pVariant, + __out DWORD64* pqwValue + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + switch (pVariant->Type) + { + case BURN_VARIANT_TYPE_NUMERIC: + BVariantRetrieveDecryptedNumeric(pVariant, (LONGLONG*)pqwValue); + break; + case BURN_VARIANT_TYPE_STRING: + hr = BVariantRetrieveDecryptedString(pVariant, &sczValue); + if (SUCCEEDED(hr)) + { + hr = FileVersionFromStringEx(sczValue, 0, pqwValue); + if (FAILED(hr)) + { + hr = DISP_E_TYPEMISMATCH; + } + } + StrSecureZeroFreeString(sczValue); + break; + case BURN_VARIANT_TYPE_VERSION: + BVariantRetrieveDecryptedVersion(pVariant, pqwValue); + break; + default: + hr = E_INVALIDARG; + break; + } + + return hr; +} + +extern "C" HRESULT BVariantSetNumeric( + __in BURN_VARIANT* pVariant, + __in LONGLONG llValue + ) +{ + HRESULT hr = S_OK; + BOOL fEncryptValue = pVariant->fEncryptValue; + + if (BURN_VARIANT_TYPE_STRING == pVariant->Type) + { + StrSecureZeroFreeString(pVariant->sczValue); + } + memset(pVariant, 0, sizeof(BURN_VARIANT)); + pVariant->llValue = llValue; + pVariant->Type = BURN_VARIANT_TYPE_NUMERIC; + BVariantSetEncryption(pVariant, fEncryptValue); + + return hr; +} + +extern "C" HRESULT BVariantSetString( + __in BURN_VARIANT* pVariant, + __in_z_opt LPCWSTR wzValue, + __in DWORD_PTR cchValue + ) +{ + HRESULT hr = S_OK; + BOOL fEncryptValue = pVariant->fEncryptValue; + + if (!wzValue) // if we're nulling out the string, make the variable NONE. + { + BVariantUninitialize(pVariant); + } + else // assign the value. + { + if (BURN_VARIANT_TYPE_STRING != pVariant->Type) + { + memset(pVariant, 0, sizeof(BURN_VARIANT)); + } + else + { + // We're about to copy an unencrypted value. + pVariant->fEncryptValue = FALSE; + } + + hr = StrAllocStringSecure(&pVariant->sczValue, wzValue, cchValue); + ExitOnFailure(hr, "Failed to copy string."); + + pVariant->Type = BURN_VARIANT_TYPE_STRING; + } + +LExit: + BVariantSetEncryption(pVariant, fEncryptValue); + return hr; +} + +extern "C" HRESULT BVariantSetVersion( + __in BURN_VARIANT* pVariant, + __in DWORD64 qwValue + ) +{ + HRESULT hr = S_OK; + BOOL fEncryptValue = pVariant->fEncryptValue; + + if (BURN_VARIANT_TYPE_STRING == pVariant->Type) + { + StrSecureZeroFreeString(pVariant->sczValue); + } + memset(pVariant, 0, sizeof(BURN_VARIANT)); + pVariant->qwValue = qwValue; + pVariant->Type = BURN_VARIANT_TYPE_VERSION; + BVariantSetEncryption(pVariant, fEncryptValue); + + return hr; +} + +extern "C" HRESULT BVariantSetValue( + __in BURN_VARIANT* pVariant, + __in BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + LONGLONG llValue = 0; + LPWSTR sczValue = NULL; + DWORD64 qwValue = 0; + BOOL fEncrypt = pVariant->fEncryptValue; + + switch (pValue->Type) + { + case BURN_VARIANT_TYPE_NONE: + BVariantUninitialize(pVariant); + break; + case BURN_VARIANT_TYPE_NUMERIC: + hr = BVariantGetNumeric(pValue, &llValue); + if (SUCCEEDED(hr)) + { + hr = BVariantSetNumeric(pVariant, llValue); + } + SecureZeroMemory(&llValue, sizeof(llValue)); + break; + case BURN_VARIANT_TYPE_STRING: + hr = BVariantGetString(pValue, &sczValue); + if (SUCCEEDED(hr)) + { + hr = BVariantSetString(pVariant, sczValue, 0); + } + StrSecureZeroFreeString(sczValue); + break; + case BURN_VARIANT_TYPE_VERSION: + hr = BVariantGetVersion(pValue, &qwValue); + if (SUCCEEDED(hr)) + { + hr = BVariantSetVersion(pVariant, qwValue); + } + SecureZeroMemory(&qwValue, sizeof(qwValue)); + break; + default: + hr = E_INVALIDARG; + } + ExitOnFailure(hr, "Failed to copy variant."); + + hr = BVariantSetEncryption(pVariant, fEncrypt); + +LExit: + return hr; +} + +extern "C" HRESULT BVariantCopy( + __in BURN_VARIANT* pSource, + __out BURN_VARIANT* pTarget + ) +{ + HRESULT hr = S_OK; + LONGLONG llValue = 0; + LPWSTR sczValue = NULL; + DWORD64 qwValue = 0; + + BVariantUninitialize(pTarget); + + switch (pSource->Type) + { + case BURN_VARIANT_TYPE_NONE: + break; + case BURN_VARIANT_TYPE_NUMERIC: + hr = BVariantGetNumeric(pSource, &llValue); + if (SUCCEEDED(hr)) + { + hr = BVariantSetNumeric(pTarget, llValue); + } + SecureZeroMemory(&llValue, sizeof(llValue)); + break; + case BURN_VARIANT_TYPE_STRING: + hr = BVariantGetString(pSource, &sczValue); + if (SUCCEEDED(hr)) + { + hr = BVariantSetString(pTarget, sczValue, 0); + } + StrSecureZeroFreeString(sczValue); + break; + case BURN_VARIANT_TYPE_VERSION: + hr = BVariantGetVersion(pSource, &qwValue); + if (SUCCEEDED(hr)) + { + hr = BVariantSetVersion(pTarget, qwValue); + } + SecureZeroMemory(&qwValue, sizeof(qwValue)); + break; + default: + hr = E_INVALIDARG; + } + ExitOnFailure(hr, "Failed to copy variant."); + + hr = BVariantSetEncryption(pTarget, pSource->fEncryptValue); + +LExit: + return hr; +} + +extern "C" HRESULT BVariantChangeType( + __in BURN_VARIANT* pVariant, + __in BURN_VARIANT_TYPE type + ) +{ + HRESULT hr = S_OK; + BURN_VARIANT variant = { }; + BOOL fEncryptValue = pVariant->fEncryptValue; + + if (pVariant->Type == type) + { + ExitFunction(); // variant already is of the requested type + } + + switch (type) + { + case BURN_VARIANT_TYPE_NONE: + hr = S_OK; + break; + case BURN_VARIANT_TYPE_NUMERIC: + hr = BVariantGetNumeric(pVariant, &variant.llValue); + break; + case BURN_VARIANT_TYPE_STRING: + hr = BVariantGetString(pVariant, &variant.sczValue); + break; + case BURN_VARIANT_TYPE_VERSION: + hr = BVariantGetVersion(pVariant, &variant.qwValue); + break; + default: + ExitFunction1(hr = E_INVALIDARG); + } + ExitOnFailure(hr, "Failed to copy variant value."); + variant.Type = type; + + BVariantUninitialize(pVariant); + memcpy_s(pVariant, sizeof(BURN_VARIANT), &variant, sizeof(BURN_VARIANT)); + SecureZeroMemory(&variant, sizeof(BURN_VARIANT)); + BVariantSetEncryption(pVariant, fEncryptValue); + +LExit: + return hr; +} + +extern "C" HRESULT BVariantSetEncryption( + __in BURN_VARIANT* pVariant, + __in BOOL fEncrypt + ) +{ + HRESULT hr = S_OK; + + if (pVariant->fEncryptValue == fEncrypt) + { + // The requested encryption state is already applied. + ExitFunction(); + } + + switch (pVariant->Type) + { + case BURN_VARIANT_TYPE_NONE: + hr = S_OK; + break; + case BURN_VARIANT_TYPE_NUMERIC: + hr = BVariantEncryptNumeric(pVariant, fEncrypt); + break; + case BURN_VARIANT_TYPE_STRING: + hr = BVariantEncryptString(pVariant, fEncrypt); + break; + case BURN_VARIANT_TYPE_VERSION: + hr = BVariantEncryptVersion(pVariant, fEncrypt); + break; + default: + hr = E_INVALIDARG; + } + ExitOnFailure(hr, "Failed to set the variant's encryption state"); + pVariant->fEncryptValue = fEncrypt; + +LExit: + return hr; +} + +static HRESULT BVariantEncryptNumeric( + __in BURN_VARIANT* pVariant, + __in BOOL fEncrypt + ) +{ + HRESULT hr = S_OK; + + if (fEncrypt) + { + hr = CrypEncryptMemory(&pVariant->llValue, sizeof(pVariant->encryptionPadding), VARIANT_ENCRYPTION_SCOPE); + } + else + { + hr = CrypDecryptMemory(&pVariant->llValue, sizeof(pVariant->encryptionPadding), VARIANT_ENCRYPTION_SCOPE); + } + +//LExit: + return hr; +} + +static HRESULT BVariantEncryptString( + __in BURN_VARIANT* pVariant, + __in BOOL fEncrypt + ) +{ + HRESULT hr = S_OK; + SIZE_T cbData = 0; + + if (NULL == pVariant->sczValue) + { + ExitFunction(); + } + + cbData = MemSize(pVariant->sczValue); + if (-1 == cbData) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Failed to get the size of the string"); + } + + DWORD remainder = fEncrypt ? cbData % CRYP_ENCRYPT_MEMORY_SIZE : 0; + DWORD extraNeeded = 0 < remainder ? CRYP_ENCRYPT_MEMORY_SIZE - remainder : 0; + if ((MAXDWORD - extraNeeded) < cbData) + { + hr = E_INVALIDDATA; + ExitOnFailure(hr, "The string is too big: size %u", cbData); + } + else if (0 < extraNeeded) + { + cbData += extraNeeded; + LPVOID pvNew = NULL; + hr = MemReAllocSecure(static_cast(pVariant->sczValue), cbData, TRUE, &pvNew); + ExitOnFailure(hr, "Failed to resize the string so it could be encrypted"); + pVariant->sczValue = static_cast(pvNew); + } + + if (fEncrypt) + { + hr = CrypEncryptMemory(pVariant->sczValue, static_cast(cbData), VARIANT_ENCRYPTION_SCOPE); + } + else + { + hr = CrypDecryptMemory(pVariant->sczValue, static_cast(cbData), VARIANT_ENCRYPTION_SCOPE); + } + +LExit: + return hr; +} + +static HRESULT BVariantEncryptVersion( + __in BURN_VARIANT* pVariant, + __in BOOL fEncrypt + ) +{ + HRESULT hr = S_OK; + + if (fEncrypt) + { + hr = CrypEncryptMemory(&pVariant->qwValue, sizeof(pVariant->encryptionPadding), VARIANT_ENCRYPTION_SCOPE); + } + else + { + hr = CrypDecryptMemory(&pVariant->qwValue, sizeof(pVariant->encryptionPadding), VARIANT_ENCRYPTION_SCOPE); + } + +//LExit: + return hr; +} + +// The contents of pllValue may be sensitive, should keep encrypted and SecureZeroMemory. +static HRESULT BVariantRetrieveDecryptedNumeric( + __in BURN_VARIANT* pVariant, + __out LONGLONG* pllValue + ) +{ + HRESULT hr = S_OK; + + Assert(NULL != pllValue); + if (pVariant->fEncryptValue) + { + hr = BVariantEncryptNumeric(pVariant, FALSE); + ExitOnFailure(hr, "Failed to decrypt numeric"); + } + + *pllValue = pVariant->llValue; + + if (pVariant->fEncryptValue) + { + hr = BVariantEncryptNumeric(pVariant, TRUE); + } + +LExit: + return hr; +} + +// The contents of psczValue may be sensitive, should keep encrypted and SecureZeroFree. +static HRESULT BVariantRetrieveDecryptedString( + __in BURN_VARIANT* pVariant, + __out LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + + if (NULL == pVariant->sczValue) + { + *psczValue = NULL; + ExitFunction(); + } + + if (pVariant->fEncryptValue) + { + hr = BVariantEncryptString(pVariant, FALSE); + ExitOnFailure(hr, "Failed to decrypt string"); + } + + hr = StrAllocStringSecure(psczValue, pVariant->sczValue, 0); + ExitOnFailure(hr, "Failed to copy value."); + + if (pVariant->fEncryptValue) + { + hr = BVariantEncryptString(pVariant, TRUE); + } + +LExit: + return hr; +} + +// The contents of pqwValue may be sensitive, should keep encrypted and SecureZeroMemory. +static HRESULT BVariantRetrieveDecryptedVersion( + __in BURN_VARIANT* pVariant, + __out DWORD64* pqwValue + ) +{ + HRESULT hr = S_OK; + + Assert(NULL != pqwValue); + if (pVariant->fEncryptValue) + { + hr = BVariantEncryptVersion(pVariant, FALSE); + ExitOnFailure(hr, "Failed to decrypt version"); + } + + *pqwValue = pVariant->qwValue; + + if (pVariant->fEncryptValue) + { + hr = BVariantEncryptVersion(pVariant, TRUE); + } + +LExit: + return hr; +} diff --git a/src/engine/variant.h b/src/engine/variant.h new file mode 100644 index 00000000..9259f05a --- /dev/null +++ b/src/engine/variant.h @@ -0,0 +1,102 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + +enum BURN_VARIANT_TYPE +{ + BURN_VARIANT_TYPE_NONE, + BURN_VARIANT_TYPE_NUMERIC, + BURN_VARIANT_TYPE_STRING, + BURN_VARIANT_TYPE_VERSION, +}; + + +// struct + +typedef struct _BURN_VARIANT +{ + union + { + LONGLONG llValue; + DWORD64 qwValue; + LPWSTR sczValue; + BYTE encryptionPadding[CRYP_ENCRYPT_MEMORY_SIZE]; + }; + BURN_VARIANT_TYPE Type; + BOOL fEncryptValue; +} BURN_VARIANT; + + +// function declarations + +void BVariantUninitialize( + __in BURN_VARIANT* pVariant + ); +HRESULT BVariantGetNumeric( + __in BURN_VARIANT* pVariant, + __out LONGLONG* pllValue + ); +HRESULT BVariantGetString( + __in BURN_VARIANT* pVariant, + __out_z LPWSTR* psczValue + ); +HRESULT BVariantGetVersion( + __in BURN_VARIANT* pVariant, + __out DWORD64* pqwValue + ); +HRESULT BVariantSetNumeric( + __in BURN_VARIANT* pVariant, + __in LONGLONG llValue + ); +HRESULT BVariantSetString( + __in BURN_VARIANT* pVariant, + __in_z_opt LPCWSTR wzValue, + __in DWORD_PTR cchValue + ); +HRESULT BVariantSetVersion( + __in BURN_VARIANT* pVariant, + __in DWORD64 qwValue + ); +/******************************************************************** +BVariantSetValue - Convenience function that calls BVariantUninitialize, + BVariantSetNumeric, BVariantSetString, or + BVariantSetVersion based on the type of pValue. + The encryption state of pVariant is preserved. +********************************************************************/ +HRESULT BVariantSetValue( + __in BURN_VARIANT* pVariant, + __in BURN_VARIANT* pValue + ); +/******************************************************************** +BVariantCopy - creates a copy of pSource. + The encryption state of pTarget is set to + the encryption state of pSource. +********************************************************************/ +HRESULT BVariantCopy( + __in BURN_VARIANT* pSource, + __out BURN_VARIANT* pTarget + ); +HRESULT BVariantChangeType( + __in BURN_VARIANT* pVariant, + __in BURN_VARIANT_TYPE type + ); +/******************************************************************** +BVariantSetEncryption - sets the encryption state of pVariant. + If the encryption state matches the requested + state, this function does nothing. +********************************************************************/ +HRESULT BVariantSetEncryption( + __in BURN_VARIANT* pVariant, + __in BOOL fEncrypt + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/stub/StubSection.cpp b/src/stub/StubSection.cpp new file mode 100644 index 00000000..962bb3cf --- /dev/null +++ b/src/stub/StubSection.cpp @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#pragma section(".wixburn",read) + +// If these defaults ever change, be sure to update constants in burn\engine\section.cpp as well. +#pragma data_seg(push, ".wixburn") +static DWORD dwMagic = 0x00f14300; +static DWORD dwVersion = 0x00000002; + +static GUID guidBundleId = { }; + +static DWORD dwStubSize = 0; +static DWORD dwOriginalChecksum = 0; +static DWORD dwOriginalSignatureOffset = 0; +static DWORD dwOriginalSignatureSize = 0; + +static DWORD dwContainerFormat = 1; +static DWORD dwContainerCount = 0; +static DWORD qwBootstrapperApplicationContainerSize = 0; +static DWORD qwAttachedContainerSize = 0; +#pragma data_seg(pop) diff --git a/src/stub/precomp.h b/src/stub/precomp.h new file mode 100644 index 00000000..387d4f0f --- /dev/null +++ b/src/stub/precomp.h @@ -0,0 +1,13 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include + +#include +#include +#include +#include +#include + +#include "engine.h" diff --git a/src/stub/stub.cpp b/src/stub/stub.cpp new file mode 100644 index 00000000..2f09eede --- /dev/null +++ b/src/stub/stub.cpp @@ -0,0 +1,64 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +int WINAPI wWinMain( + __in HINSTANCE hInstance, + __in_opt HINSTANCE /* hPrevInstance */, + __in_z_opt LPWSTR lpCmdLine, + __in int nCmdShow + ) +{ + HRESULT hr = S_OK; + DWORD dwExitCode = 0; + LPWSTR sczPath = NULL; + HANDLE hEngineFile = INVALID_HANDLE_VALUE; + + LPCWSTR rgsczSafelyLoadSystemDlls[] = + { + L"cabinet.dll", // required by Burn. + L"msi.dll", // required by Burn. + L"version.dll", // required by Burn. + L"wininet.dll", // required by Burn. + + L"comres.dll", // required by CLSIDFromProgID() when loading clbcatq.dll. + L"clbcatq.dll", // required by CLSIDFromProgID() when loading msxml?.dll. + + L"msasn1.dll", // required by DecryptFile() when loading crypt32.dll. + L"crypt32.dll", // required by DecryptFile() when loading feclient.dll. + L"feclient.dll", // unsafely loaded by DecryptFile(). + }; + + // Best effort attempt to get our file handle as soon as possible. + hr = PathForCurrentProcess(&sczPath, NULL); + if (SUCCEEDED(hr)) + { + hEngineFile = ::CreateFileW(sczPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + } + + // If the engine is in the clean room, we'll do the unsafe initialization + // because some systems in Windows (namely GDI+) will fail when run in + // a process that protects against DLL hijacking. Since we know the clean + // room is in a clean folder and not subject to DLL hijacking we won't + // make ourselves perfectly secure so that we can load BAs that still + // depend on those parts of Windows that are insecure to DLL hijacking. + if (EngineInCleanRoom(lpCmdLine)) + { + AppInitializeUnsafe(); + } + else + { + AppInitialize(rgsczSafelyLoadSystemDlls, countof(rgsczSafelyLoadSystemDlls)); + } + + // call run + hr = EngineRun(hInstance, hEngineFile, lpCmdLine, nCmdShow, &dwExitCode); + ExitOnFailure(hr, "Failed to run application."); + +LExit: + ReleaseFileHandle(hEngineFile); + ReleaseStr(sczPath); + + return FAILED(hr) ? (int)hr : (int)dwExitCode; +} diff --git a/src/stub/stub.ico b/src/stub/stub.ico new file mode 100644 index 00000000..c2e2717c Binary files /dev/null and b/src/stub/stub.ico differ diff --git a/src/stub/stub.manifest b/src/stub/stub.manifest new file mode 100644 index 00000000..d5767cdb --- /dev/null +++ b/src/stub/stub.manifest @@ -0,0 +1,18 @@ + + + + + + + WiX Toolset Bootstrapper + true + + + + + + + + + + diff --git a/src/stub/stub.rc b/src/stub/stub.rc new file mode 100644 index 00000000..5601703d --- /dev/null +++ b/src/stub/stub.rc @@ -0,0 +1,14 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#define VER_APP +#define VER_ORIGINAL_FILENAME "setup.exe" +#define VER_INTERNAL_NAME "setup" +#define VER_FILE_DESCRIPTION "WiX Toolset Bootstrapper" +#include "wix.rc" + +1 ICON "stub.ico" + +//#define MANIFEST_RESOURCE_ID 1 +#ifndef ARM // the ARM manifest is automatically injected but other platforms need it done manually. +//MANIFEST_RESOURCE_ID RT_MANIFEST "stub.manifest" +#endif -- cgit v1.2.3-55-g6feb From 3480ec56611d6c8784b7610dcac818133318f675 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 29 Dec 2018 22:18:45 -0600 Subject: Integrate into latest v4 --- .editorconfig | 37 ++++++ appveyor.cmd | 11 ++ appveyor.yml | 35 ++++++ burn.sln | 41 +++++++ nuget.config | 8 ++ src/Cpp.Build.props | 100 ++++++++++++++++ src/Directory.Build.props | 26 +++++ src/Directory.Build.targets | 48 ++++++++ src/engine/engine.cpp | 1 + src/engine/engine.vcxproj | 174 ++++++++++++++++++++++++++++ src/engine/packages.config | 6 + src/engine/precomp.cpp | 3 + src/engine/precomp.h | 7 +- src/engine/variable.cpp | 1 + src/stub/packages.config | 5 + src/stub/precomp.cpp | 3 + src/stub/runtime.win.WixToolset.Burn.nuspec | 20 ++++ src/stub/stub.rc | 11 -- src/stub/stub.vcxproj | 104 +++++++++++++++++ version.json | 11 ++ 20 files changed, 639 insertions(+), 13 deletions(-) create mode 100644 .editorconfig create mode 100644 appveyor.cmd create mode 100644 appveyor.yml create mode 100644 burn.sln create mode 100644 nuget.config create mode 100644 src/Cpp.Build.props create mode 100644 src/Directory.Build.props create mode 100644 src/Directory.Build.targets create mode 100644 src/engine/engine.vcxproj create mode 100644 src/engine/packages.config create mode 100644 src/engine/precomp.cpp create mode 100644 src/stub/packages.config create mode 100644 src/stub/precomp.cpp create mode 100644 src/stub/runtime.win.WixToolset.Burn.nuspec create mode 100644 src/stub/stub.vcxproj create mode 100644 version.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..1d72e683 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,37 @@ +# Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. +# +# Do NOT modify this file. Update the canonical version in Home\repo-template\src\.editorconfig +# then update all of the repos. + +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.{cs,vb}] +dotnet_sort_system_directives_first = true + +[*.cs] +csharp_indent_case_contents = true : error +csharp_indent_switch_labels = true : error +csharp_new_line_before_open_brace = all +csharp_prefer_braces = true : error +csharp_style_expression_bodied_methods = when_on_single_line : suggestion +csharp_style_expression_bodied_constructors = when_on_single_line : suggestion +csharp_style_expression_bodied_operators = when_on_single_line : suggestion +csharp_style_expression_bodied_properties = when_on_single_line : suggestion +csharp_style_expression_bodied_indexers = when_on_single_line : suggestion +csharp_style_expression_bodied_accessors = when_on_single_line : suggestion +csharp_style_var_elsewhere = true : suggestion +csharp_style_var_for_built_in_types = true : suggestion +csharp_style_var_when_type_is_apparent = true : suggestion +dotnet_style_qualification_for_event = true : error +dotnet_style_qualification_for_field = true : error +dotnet_style_qualification_for_method = true : error +dotnet_style_qualification_for_property = true : error + +[*.targets] +indent_size = 2 diff --git a/appveyor.cmd b/appveyor.cmd new file mode 100644 index 00000000..dbe8e248 --- /dev/null +++ b/appveyor.cmd @@ -0,0 +1,11 @@ +@setlocal +@pushd %~dp0 + +nuget restore + +msbuild -p:Configuration=Release;Platform=x86 + +msbuild -p:Configuration=Release -t:Pack src\stub\stub.vcxproj + +@popd +@endlocal \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..d55322da --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,35 @@ +# Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. +# +# Do NOT modify this file. Update the canonical version in Home\repo-template\src\appveyor.yml +# then update all of the repos. + +image: Visual Studio 2017 + +version: 0.0.0.{build} +configuration: Release + +environment: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + NUGET_XMLDOC_MODE: skip + +build_script: + - appveyor.cmd + +pull_requests: + do_not_increment_build_number: true + +nuget: + disable_publish_on_pr: true + +skip_branch_with_pr: true +skip_tags: true + +artifacts: +- path: build\Release\**\*.nupkg + name: nuget + +notifications: +- provider: Slack + incoming_webhook: + secure: p5xuu+4x2JHfwGDMDe5KcG1k7gZxqYc4jWVwvyNZv5cvkubPD2waJs5yXMAXZNN7Z63/3PWHb7q4KoY/99AjauYa1nZ4c5qYqRPFRBKTHfA= diff --git a/burn.sln b/burn.sln new file mode 100644 index 00000000..0bdee49f --- /dev/null +++ b/burn.sln @@ -0,0 +1,41 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.12 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "engine", "src\engine\engine.vcxproj", "{8119537D-E1D9-6591-D51A-49768A2F9C37}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stub", "src\stub\stub.vcxproj", "{C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|x64.ActiveCfg = Debug|x64 + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|x64.Build.0 = Debug|x64 + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|x86.ActiveCfg = Debug|Win32 + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|x86.Build.0 = Debug|Win32 + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|x64.ActiveCfg = Release|x64 + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|x64.Build.0 = Release|x64 + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|x86.ActiveCfg = Release|Win32 + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|x86.Build.0 = Release|Win32 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|x64.ActiveCfg = Debug|x64 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|x64.Build.0 = Debug|x64 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|x86.ActiveCfg = Debug|Win32 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|x86.Build.0 = Debug|Win32 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x64.ActiveCfg = Release|x64 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x64.Build.0 = Release|x64 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x86.ActiveCfg = Release|Win32 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A35910C5-8A89-473E-9578-E084172DD3C9} + EndGlobalSection +EndGlobal diff --git a/nuget.config b/nuget.config new file mode 100644 index 00000000..790be2b0 --- /dev/null +++ b/nuget.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/Cpp.Build.props b/src/Cpp.Build.props new file mode 100644 index 00000000..296b36ca --- /dev/null +++ b/src/Cpp.Build.props @@ -0,0 +1,100 @@ + + + + + + Win32 + $(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\ + $(OutputPath)$(Platform)\ + + + + + $(DisableSpecificCompilerWarnings) + Level4 + $(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) + Use + precomp.h + StdCall + true + false + -YlprecompDefine + /Zc:threadSafeInit- %(AdditionalOptions) + true + + + $(ArmPreprocessorDefinitions);%(PreprocessorDefinitions) + $(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories) + + + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories) + + + $(ProjectSubSystem) + $(ProjectModuleDefinitionFile) + $(ResourceOnlyDll) + true + $(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies) + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories) + /IGNORE:4099 %(AdditionalOptions) + + + + + + NoExtensions + + + + + CDecl + + + + + OldStyle + true + true + + + + + Disabled + EnableFastChecks + _DEBUG;DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebug + + + + + + MultiThreadedDebugDll + + + + + MinSpace + NDEBUG;%(PreprocessorDefinitions) + true + true + MultiThreaded + + + true + true + + + + + + MultiThreadedDll + + + + + $(LinkKeyFile) + $(LinkDelaySign) + + + diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 00000000..e853e22d --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,26 @@ + + + + + + Debug + false + + $(MSBuildProjectName) + $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\)) + $(BaseOutputPath)obj\$(ProjectName)\ + $(BaseOutputPath)$(Configuration)\ + + WiX Toolset Team + WiX Toolset + Copyright (c) .NET Foundation and contributors. All rights reserved. + MS-RL + WiX Toolset + + + + + diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets new file mode 100644 index 00000000..dac7452a --- /dev/null +++ b/src/Directory.Build.targets @@ -0,0 +1,48 @@ + + + + + + + true + $(SolutionPath) + $(NCrunchOriginalSolutionPath) + + + + + + + $([System.IO.File]::ReadAllText($(TheSolutionPath))) + $([System.IO.Path]::GetDirectoryName( $(TheSolutionPath) )) + (?<="[PackageName]", ")(.*)(?=", ") + + + + + + %(Identity) + $(SolutionFileContent.Contains('\%(Identity).csproj')) + + + + + $(RegexPattern.Replace('[PackageName]','%(PackageName)') ) + $([System.Text.RegularExpressions.Regex]::Match('$(SolutionFileContent)', '%(Pattern)')) + + + + + + + + + + + diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 3c0f09c9..190c327f 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -144,6 +144,7 @@ extern "C" HRESULT EngineRun( fXmlInitialized = TRUE; ovix.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); + #pragma warning(suppress: 4996) if (!::GetVersionExW((LPOSVERSIONINFOW)&ovix)) { ExitWithLastError(hr, "Failed to get OS info."); diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj new file mode 100644 index 00000000..169c47fa --- /dev/null +++ b/src/engine/engine.vcxproj @@ -0,0 +1,174 @@ + + + + + + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + {8119537D-E1D9-6591-D51A-49768A2F9C37} + StaticLibrary + engine + v141 + Unicode + Native component of WixToolset.Burn + + + + + + + + + + + + + $(ProjectDir)..\inc + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Compiling message file... + mc.exe -h "$(IntDir)." -r "$(IntDir)." -A -c -z engine.messages "$(InputDir)engine.mc" +rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" + $(IntDir)engine.messages.h;$(OutDir)engine.messages.rc + + + + + + + + rmj=4;rmm=0;rup=$(BuildNumber);szVerMajorMinorBuild="$(BuildVersion)";%(PreprocessorDefinitions) + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + diff --git a/src/engine/packages.config b/src/engine/packages.config new file mode 100644 index 00000000..52c84cf0 --- /dev/null +++ b/src/engine/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/engine/precomp.cpp b/src/engine/precomp.cpp new file mode 100644 index 00000000..37664a1c --- /dev/null +++ b/src/engine/precomp.cpp @@ -0,0 +1,3 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" diff --git a/src/engine/precomp.h b/src/engine/precomp.h index d3ebe354..477dc310 100644 --- a/src/engine/precomp.h +++ b/src/engine/precomp.h @@ -4,12 +4,15 @@ #define ExitTrace LogErrorString -#include - #include #include #include + +#pragma warning(push) +#pragma warning(disable:4458) // declaration of 'xxx' hides class member #include +#pragma warning(pop) + #include #include #include diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp index ed4abea2..7377b116 100644 --- a/src/engine/variable.cpp +++ b/src/engine/variable.cpp @@ -1698,6 +1698,7 @@ static HRESULT InitializeVariableOsInfo( BURN_VARIANT value = { }; ovix.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); + #pragma warning(suppress: 4996) if (!::GetVersionExW((LPOSVERSIONINFOW)&ovix)) { ExitWithLastError(hr, "Failed to get OS info."); diff --git a/src/stub/packages.config b/src/stub/packages.config new file mode 100644 index 00000000..883e8d91 --- /dev/null +++ b/src/stub/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/stub/precomp.cpp b/src/stub/precomp.cpp new file mode 100644 index 00000000..37664a1c --- /dev/null +++ b/src/stub/precomp.cpp @@ -0,0 +1,3 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" diff --git a/src/stub/runtime.win.WixToolset.Burn.nuspec b/src/stub/runtime.win.WixToolset.Burn.nuspec new file mode 100644 index 00000000..e296bf8f --- /dev/null +++ b/src/stub/runtime.win.WixToolset.Burn.nuspec @@ -0,0 +1,20 @@ + + + + $id$ + $version$ + $authors$ + $authors$ + + https://licenses.nuget.org/MS-RL + https://github.com/wixtoolset/burn + false + $description$ + $copyright$ + + + + + + + diff --git a/src/stub/stub.rc b/src/stub/stub.rc index 5601703d..80e1aac4 100644 --- a/src/stub/stub.rc +++ b/src/stub/stub.rc @@ -1,14 +1,3 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -#define VER_APP -#define VER_ORIGINAL_FILENAME "setup.exe" -#define VER_INTERNAL_NAME "setup" -#define VER_FILE_DESCRIPTION "WiX Toolset Bootstrapper" -#include "wix.rc" - 1 ICON "stub.ico" - -//#define MANIFEST_RESOURCE_ID 1 -#ifndef ARM // the ARM manifest is automatically injected but other platforms need it done manually. -//MANIFEST_RESOURCE_ID RT_MANIFEST "stub.manifest" -#endif diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj new file mode 100644 index 00000000..89b811ca --- /dev/null +++ b/src/stub/stub.vcxproj @@ -0,0 +1,104 @@ + + + + + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + x86 + amd64 + + + + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1} + Application + Windows + burn.$(NameSuffix) + v141 + Unicode + Native component of WixToolset.Burn + + + + + + + + + + + + + + $(ProjectDir)..\engine\inc + cabinet.lib;crypt32.lib;gdiplus.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;wintrust.lib;wuguid.lib;engine.lib;engine.res + + + + + true + true + cabinet.dll;crypt32.dll;gdiplus.dll;msi.dll;shlwapi.dll;version.dll;wininet.dll;wintrust.dll + + + + + + + + + Create + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}. + + + + + diff --git a/version.json b/version.json new file mode 100644 index 00000000..5f857771 --- /dev/null +++ b/version.json @@ -0,0 +1,11 @@ +{ + "version": "4.0", + "publicReleaseRefSpec": [ + "^refs/heads/master$" + ], + "cloudBuild": { + "buildNumber": { + "enabled": true + } + } +} -- cgit v1.2.3-55-g6feb From a56c1ce571ea08e5b2a248428185112db3a2e23d Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 30 Dec 2018 10:50:54 -0600 Subject: Add BootstrapperCore nuget feed. --- nuget.config | 1 + 1 file changed, 1 insertion(+) diff --git a/nuget.config b/nuget.config index 790be2b0..b6266ac2 100644 --- a/nuget.config +++ b/nuget.config @@ -2,6 +2,7 @@ + -- cgit v1.2.3-55-g6feb From d894373d6caac674dea3dbd438c55dd03479c0dd Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sun, 30 Dec 2018 09:03:05 -0800 Subject: Reduce extra appveyor builds by targeting only master and develop branches --- appveyor.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index d55322da..c1df03cc 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,6 +3,11 @@ # Do NOT modify this file. Update the canonical version in Home\repo-template\src\appveyor.yml # then update all of the repos. +branches: + only: + - master + - develop + image: Visual Studio 2017 version: 0.0.0.{build} -- cgit v1.2.3-55-g6feb From 407f635beec463d5dfb52de50619039f64c90ab6 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 13 Jan 2019 19:25:18 -0600 Subject: Update to latest Cpp.Build.props for locating latest Win10 SDK. --- src/Cpp.Build.props | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Cpp.Build.props b/src/Cpp.Build.props index 296b36ca..0e00132b 100644 --- a/src/Cpp.Build.props +++ b/src/Cpp.Build.props @@ -8,6 +8,10 @@ $(OutputPath)$(Platform)\ + + $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) + + $(DisableSpecificCompilerWarnings) @@ -16,7 +20,7 @@ WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) Use precomp.h - StdCall + StdCall true false -YlprecompDefine -- cgit v1.2.3-55-g6feb From 68dc212737ccbecc94d17b8abc00ff87191fe3fa Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 11 Dec 2019 11:29:15 +1100 Subject: Update to latest repo-template. --- src/Cpp.Build.props | 2 +- src/Directory.Build.props | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Cpp.Build.props b/src/Cpp.Build.props index 0e00132b..44a042c7 100644 --- a/src/Cpp.Build.props +++ b/src/Cpp.Build.props @@ -8,7 +8,7 @@ $(OutputPath)$(Platform)\ - + $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index e853e22d..a22f4470 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -8,6 +8,7 @@ Debug false + MSB3246 $(MSBuildProjectName) $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\)) @@ -21,6 +22,7 @@ WiX Toolset - + + -- cgit v1.2.3-55-g6feb From 8b15ced27a3006d25550b36ef1486704ff7e2fbe Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 11 Dec 2019 18:35:26 +1100 Subject: Update dependencies. --- src/engine/engine.vcxproj | 8 ++++---- src/engine/packages.config | 4 ++-- src/stub/packages.config | 2 +- src/stub/stub.vcxproj | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index 169c47fa..0d7afa88 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -2,8 +2,8 @@ - - + + @@ -167,8 +167,8 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + diff --git a/src/engine/packages.config b/src/engine/packages.config index 52c84cf0..769c5a6e 100644 --- a/src/engine/packages.config +++ b/src/engine/packages.config @@ -1,6 +1,6 @@  - - + + \ No newline at end of file diff --git a/src/stub/packages.config b/src/stub/packages.config index 883e8d91..478af2c6 100644 --- a/src/stub/packages.config +++ b/src/stub/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index 89b811ca..02c61295 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -2,7 +2,7 @@ - + @@ -99,6 +99,6 @@ This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}. - + -- cgit v1.2.3-55-g6feb From 91cc7ff5c0f387d39c5999032b4472cbb00e1cd6 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 21 Dec 2019 18:56:14 +1100 Subject: Add cbSize parameter to BOOTSTRAPPER_COMMAND. --- src/engine/engine.cpp | 1 + src/engine/engine.vcxproj | 4 ++-- src/engine/packages.config | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 190c327f..341fd471 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -98,6 +98,7 @@ extern "C" HRESULT EngineRun( BOOL fRestart = FALSE; BURN_ENGINE_STATE engineState = { }; + engineState.command.cbSize = sizeof(BOOTSTRAPPER_COMMAND); // Always initialize logging first LogInitialize(::GetModuleHandleW(NULL)); diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index 0d7afa88..fecef29e 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -2,7 +2,7 @@ - + @@ -167,7 +167,7 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + diff --git a/src/engine/packages.config b/src/engine/packages.config index 769c5a6e..f7638ec7 100644 --- a/src/engine/packages.config +++ b/src/engine/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file -- cgit v1.2.3-55-g6feb From 6ce359752afac0d3d70c2cf5fabd7d92859564ee Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Thu, 12 Mar 2020 16:22:04 +1100 Subject: Retry ElevationElevate once if we think it failed due to antivirus interference. --- src/engine/core.cpp | 10 ++++++++-- src/engine/elevation.cpp | 4 ++++ src/engine/engine.vcxproj | 4 ++-- src/engine/packages.config | 2 +- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 519012e9..10750be8 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -508,10 +508,11 @@ extern "C" HRESULT CoreElevate( ) { HRESULT hr = S_OK; + DWORD cAVRetryAttempts = 0; - // If the elevated companion pipe isn't created yet, let's make that happen. - if (INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe) + while (INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe) { + // If the elevated companion pipe isn't created yet, let's make that happen. if (!pEngineState->sczBundleEngineWorkingPath) { hr = CacheBundleToWorkingDirectory(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &pEngineState->userExperience.payloads, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath); @@ -519,6 +520,11 @@ extern "C" HRESULT CoreElevate( } hr = ElevationElevate(pEngineState, hwndParent); + if (E_SUSPECTED_AV_INTERFERENCE == hr && 1 > cAVRetryAttempts) + { + ++cAVRetryAttempts; + continue; + } ExitOnFailure(hr, "Failed to actually elevate."); hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, TRUE, TRUE); diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp index 1b9336e0..0b96c300 100644 --- a/src/engine/elevation.cpp +++ b/src/engine/elevation.cpp @@ -291,6 +291,10 @@ extern "C" HRESULT ElevationElevate( LogId(REPORT_STANDARD, MSG_LAUNCH_ELEVATED_ENGINE_SUCCESS); hr = PipeWaitForChildConnect(&pEngineState->companionConnection); + if (HRESULT_FROM_WIN32(ERROR_NO_DATA) == hr) + { + hr = E_SUSPECTED_AV_INTERFERENCE; + } ExitOnFailure(hr, "Failed to connect to elevated child process."); LogId(REPORT_STANDARD, MSG_CONNECT_TO_ELEVATED_ENGINE_SUCCESS); diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index fecef29e..499fcd4d 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -2,7 +2,7 @@ - + @@ -167,7 +167,7 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + diff --git a/src/engine/packages.config b/src/engine/packages.config index f7638ec7..01a9390c 100644 --- a/src/engine/packages.config +++ b/src/engine/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file -- cgit v1.2.3-55-g6feb From 0354a00e74492ad8d930c5bf499bc8606e48b1c9 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 29 Mar 2020 19:14:06 +1000 Subject: Add support for BundleExtensions. --- src/engine/EngineForApplication.cpp | 2 +- src/engine/EngineForApplication.h | 4 +- src/engine/EngineForExtension.cpp | 405 ++++++++++++++++++++++++++++++++++++ src/engine/EngineForExtension.h | 27 +++ src/engine/burnextension.cpp | 184 ++++++++++++++++ src/engine/burnextension.h | 51 +++++ src/engine/core.h | 1 + src/engine/engine.cpp | 12 ++ src/engine/engine.vcxproj | 13 +- src/engine/manifest.cpp | 4 + src/engine/packages.config | 2 +- src/engine/precomp.h | 4 + src/engine/userexperience.cpp | 2 +- src/engine/userexperience.h | 2 +- src/engine/variable.cpp | 2 +- 15 files changed, 701 insertions(+), 14 deletions(-) create mode 100644 src/engine/EngineForExtension.cpp create mode 100644 src/engine/EngineForExtension.h create mode 100644 src/engine/burnextension.cpp create mode 100644 src/engine/burnextension.h diff --git a/src/engine/EngineForApplication.cpp b/src/engine/EngineForApplication.cpp index eda5fc64..c3600c7b 100644 --- a/src/engine/EngineForApplication.cpp +++ b/src/engine/EngineForApplication.cpp @@ -593,7 +593,7 @@ static HRESULT BAEngineSetVariableString( if (wzVariable && *wzVariable) { hr = VariableSetString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE); - ExitOnFailure(hr, "Failed to set numeric variable."); + ExitOnFailure(hr, "Failed to set string variable."); } else { diff --git a/src/engine/EngineForApplication.h b/src/engine/EngineForApplication.h index 1b755acc..e5e8f6d7 100644 --- a/src/engine/EngineForApplication.h +++ b/src/engine/EngineForApplication.h @@ -24,11 +24,11 @@ enum WM_BURN // structs -struct BOOTSTRAPPER_ENGINE_CONTEXT +typedef struct _BOOTSTRAPPER_ENGINE_CONTEXT { BURN_ENGINE_STATE* pEngineState; DWORD dwThreadId; -}; +} BOOTSTRAPPER_ENGINE_CONTEXT; // function declarations diff --git a/src/engine/EngineForExtension.cpp b/src/engine/EngineForExtension.cpp new file mode 100644 index 00000000..9667dd18 --- /dev/null +++ b/src/engine/EngineForExtension.cpp @@ -0,0 +1,405 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static HRESULT BEEngineEscapeString( + __in BURN_EXTENSION_ENGINE_CONTEXT* /*pContext*/, + __in BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_ARGS* pArgs, + __in BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_RESULTS* pResults + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + size_t cchRemaining = 0; + LPCWSTR wzIn = pArgs->wzIn; + LPWSTR wzOut = pResults->wzOut; + DWORD* pcchOut = &pResults->cchOut; + + if (wzIn && *wzIn) + { + hr = VariableEscapeString(wzIn, &sczValue); + if (SUCCEEDED(hr)) + { + if (wzOut) + { + hr = ::StringCchCopyExW(wzOut, *pcchOut, sczValue, NULL, &cchRemaining, STRSAFE_FILL_BEHIND_NULL); + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + hr = E_MOREDATA; + ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining); + *pcchOut = cchRemaining; + } + } + else + { + ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining); + *pcchOut = cchRemaining; + } + } + } + else + { + hr = E_INVALIDARG; + } + + StrSecureZeroFreeString(sczValue); + return hr; +} + +static HRESULT BEEngineEvaluateCondition( + __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, + __in BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_ARGS* pArgs, + __in BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_RESULTS* pResults + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzCondition = pArgs->wzCondition; + BOOL* pf = &pResults->f; + + if (wzCondition && *wzCondition) + { + hr = ConditionEvaluate(&pContext->pEngineState->variables, wzCondition, pf); + } + else + { + hr = E_INVALIDARG; + } + + return hr; +} + +static HRESULT BEEngineFormatString( + __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, + __in BUNDLE_EXTENSION_ENGINE_FORMATSTRING_ARGS* pArgs, + __in BUNDLE_EXTENSION_ENGINE_FORMATSTRING_RESULTS* pResults + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + DWORD cchValue = 0; + LPCWSTR wzIn = pArgs->wzIn; + LPWSTR wzOut = pResults->wzOut; + DWORD* pcchOut = &pResults->cchOut; + + if (wzIn && *wzIn) + { + hr = VariableFormatString(&pContext->pEngineState->variables, wzIn, &sczValue, &cchValue); + if (SUCCEEDED(hr)) + { + if (wzOut) + { + hr = ::StringCchCopyExW(wzOut, *pcchOut, sczValue, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + if (FAILED(hr)) + { + *pcchOut = cchValue; + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + hr = E_MOREDATA; + } + } + } + else + { + hr = E_MOREDATA; + *pcchOut = cchValue; + } + } + } + else + { + hr = E_INVALIDARG; + } + + StrSecureZeroFreeString(sczValue); + return hr; +} + +static HRESULT BEEngineGetVariableNumeric( + __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, + __in BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_ARGS* pArgs, + __in BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_RESULTS* pResults + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzVariable = pArgs->wzVariable; + LONGLONG* pllValue = &pResults->llValue; + + if (wzVariable && *wzVariable) + { + hr = VariableGetNumeric(&pContext->pEngineState->variables, wzVariable, pllValue); + } + else + { + hr = E_INVALIDARG; + } + + return hr; +} + +static HRESULT BEEngineGetVariableString( + __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, + __in BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_ARGS* pArgs, + __in BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_RESULTS* pResults + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + size_t cchRemaining = 0; + LPCWSTR wzVariable = pArgs->wzVariable; + LPWSTR wzValue = pResults->wzValue; + DWORD* pcchValue = &pResults->cchValue; + + if (wzVariable && *wzVariable) + { + hr = VariableGetString(&pContext->pEngineState->variables, wzVariable, &sczValue); + if (SUCCEEDED(hr)) + { + if (wzValue) + { + hr = ::StringCchCopyExW(wzValue, *pcchValue, sczValue, NULL, &cchRemaining, STRSAFE_FILL_BEHIND_NULL); + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + hr = E_MOREDATA; + + ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining); + *pcchValue = cchRemaining + 1; + } + } + else + { + hr = E_MOREDATA; + + ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining); + *pcchValue = cchRemaining + 1; + } + } + } + else + { + hr = E_INVALIDARG; + } + + StrSecureZeroFreeString(sczValue); + return hr; +} + +static HRESULT BEEngineGetVariableVersion( + __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, + __in BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_ARGS* pArgs, + __in BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_RESULTS* pResults + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzVariable = pArgs->wzVariable; + DWORD64* pqwValue = &pResults->qwValue; + + if (wzVariable && *wzVariable) + { + hr = VariableGetVersion(&pContext->pEngineState->variables, wzVariable, pqwValue); + } + else + { + hr = E_INVALIDARG; + } + + return hr; +} + +static HRESULT BEEngineLog( + __in BURN_EXTENSION_ENGINE_CONTEXT* /*pContext*/, + __in BUNDLE_EXTENSION_ENGINE_LOG_ARGS* pArgs, + __in BUNDLE_EXTENSION_ENGINE_LOG_RESULTS* /*pResults*/ + ) +{ + HRESULT hr = S_OK; + REPORT_LEVEL rl = REPORT_NONE; + BUNDLE_EXTENSION_LOG_LEVEL level = pArgs->level; + LPCWSTR wzMessage = pArgs->wzMessage; + + switch (level) + { + case BUNDLE_EXTENSION_LOG_LEVEL_STANDARD: + rl = REPORT_STANDARD; + break; + + case BUNDLE_EXTENSION_LOG_LEVEL_VERBOSE: + rl = REPORT_VERBOSE; + break; + + case BUNDLE_EXTENSION_LOG_LEVEL_DEBUG: + rl = REPORT_DEBUG; + break; + + case BUNDLE_EXTENSION_LOG_LEVEL_ERROR: + rl = REPORT_ERROR; + break; + + default: + ExitFunction1(hr = E_INVALIDARG); + } + + hr = LogStringLine(rl, "%ls", wzMessage); + ExitOnFailure(hr, "Failed to log Bundle Extension message."); + +LExit: + return hr; +} + +static HRESULT BEEngineSetVariableLiteralString( + __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, + __in const BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_ARGS* pArgs, + __in BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_RESULTS* /*pResults*/ + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzVariable = pArgs->wzVariable; + LPCWSTR wzValue = pArgs->wzValue; + + if (wzVariable && *wzVariable) + { + hr = VariableSetLiteralString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE); + ExitOnFailure(hr, "Failed to set literal string variable."); + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Bundle Extension did not provide variable name."); + } + +LExit: + return hr; +} + +static HRESULT BEEngineSetVariableNumeric( + __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, + __in const BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_ARGS* pArgs, + __in BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_RESULTS* /*pResults*/ + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzVariable = pArgs->wzVariable; + LONGLONG llValue = pArgs->llValue; + + if (wzVariable && *wzVariable) + { + hr = VariableSetNumeric(&pContext->pEngineState->variables, wzVariable, llValue, FALSE); + ExitOnFailure(hr, "Failed to set numeric variable."); + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Bundle Extension did not provide variable name."); + } + +LExit: + return hr; +} + +static HRESULT BEEngineSetVariableString( + __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, + __in const BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_ARGS* pArgs, + __in BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_RESULTS* /*pResults*/ + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzVariable = pArgs->wzVariable; + LPCWSTR wzValue = pArgs->wzValue; + + if (wzVariable && *wzVariable) + { + hr = VariableSetString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE); + ExitOnFailure(hr, "Failed to set string variable."); + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Bundle Extension did not provide variable name."); + } + +LExit: + return hr; +} + +static HRESULT BEEngineSetVariableVersion( + __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, + __in const BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_ARGS* pArgs, + __in BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_RESULTS* /*pResults*/ + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzVariable = pArgs->wzVariable; + DWORD64 qwValue = pArgs->qwValue; + + if (wzVariable && *wzVariable) + { + hr = VariableSetVersion(&pContext->pEngineState->variables, wzVariable, qwValue, FALSE); + ExitOnFailure(hr, "Failed to set version variable."); + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Bundle Extension did not provide variable name."); + } + +LExit: + return hr; +} + +HRESULT WINAPI EngineForExtensionProc( + __in BUNDLE_EXTENSION_ENGINE_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults, + __in_opt LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + BURN_EXTENSION_ENGINE_CONTEXT* pContext = reinterpret_cast(pvContext); + + if (!pContext || !pvArgs || !pvResults) + { + ExitFunction1(hr = E_INVALIDARG); + } + + switch (message) + { + case BUNDLE_EXTENSION_ENGINE_MESSAGE_ESCAPESTRING: + hr = BEEngineEscapeString(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BUNDLE_EXTENSION_ENGINE_MESSAGE_EVALUATECONDITION: + hr = BEEngineEvaluateCondition(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BUNDLE_EXTENSION_ENGINE_MESSAGE_FORMATSTRING: + hr = BEEngineFormatString(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLENUMERIC: + hr = BEEngineGetVariableNumeric(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLESTRING: + hr = BEEngineGetVariableString(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLEVERSION: + hr = BEEngineGetVariableVersion(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BUNDLE_EXTENSION_ENGINE_MESSAGE_LOG: + hr = BEEngineLog(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLELITERALSTRING: + hr = BEEngineSetVariableLiteralString(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLENUMERIC: + hr = BEEngineSetVariableNumeric(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLESTRING: + hr = BEEngineSetVariableString(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLEVERSION: + hr = BEEngineSetVariableVersion(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; + default: + hr = E_NOTIMPL; + break; + } + +LExit: + return hr; +} diff --git a/src/engine/EngineForExtension.h b/src/engine/EngineForExtension.h new file mode 100644 index 00000000..bad5f08a --- /dev/null +++ b/src/engine/EngineForExtension.h @@ -0,0 +1,27 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +// structs + +typedef struct _BURN_EXTENSION_ENGINE_CONTEXT +{ + BURN_ENGINE_STATE* pEngineState; +} BURN_EXTENSION_ENGINE_CONTEXT; + +// function declarations + +HRESULT WINAPI EngineForExtensionProc( + __in BUNDLE_EXTENSION_ENGINE_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults, + __in_opt LPVOID pvContext + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/burnextension.cpp b/src/engine/burnextension.cpp new file mode 100644 index 00000000..99673cd9 --- /dev/null +++ b/src/engine/burnextension.cpp @@ -0,0 +1,184 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// function definitions + +/******************************************************************* + BurnExtensionParseFromXml - + +*******************************************************************/ +EXTERN_C HRESULT BurnExtensionParseFromXml( + __in BURN_EXTENSIONS* pBurnExtensions, + __in BURN_PAYLOADS* pBaPayloads, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + + // Select BundleExtension nodes. + hr = XmlSelectNodes(pixnBundle, L"BundleExtension", &pixnNodes); + ExitOnFailure(hr, "Failed to select BundleExtension nodes."); + + // Get BundleExtension node count. + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get BundleExtension node count."); + + if (!cNodes) + { + ExitFunction(); + } + + // Allocate memory for BundleExtensions. + pBurnExtensions->rgExtensions = (BURN_EXTENSION*)MemAlloc(sizeof(BURN_EXTENSION) * cNodes, TRUE); + ExitOnNull(pBurnExtensions->rgExtensions, hr, E_OUTOFMEMORY, "Failed to allocate memory for BundleExtension structs."); + + pBurnExtensions->cExtensions = cNodes; + + // parse search elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_EXTENSION* pExtension = &pBurnExtensions->rgExtensions[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &pExtension->sczId); + ExitOnFailure(hr, "Failed to get @Id."); + + // @EntryPayloadId + hr = XmlGetAttributeEx(pixnNode, L"EntryPayloadId", &pExtension->sczEntryPayloadId); + ExitOnFailure(hr, "Failed to get @EntryPayloadId."); + + hr = PayloadFindById(pBaPayloads, pExtension->sczEntryPayloadId, &pExtension->pEntryPayload); + ExitOnFailure(hr, "Failed to find BundleExtension EntryPayload '%ls'.", pExtension->sczEntryPayloadId); + + // prepare next iteration + ReleaseNullObject(pixnNode); + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNode); + ReleaseObject(pixnNodes); + + return hr; +} + +/******************************************************************* + BurnExtensionUninitialize - + +*******************************************************************/ +EXTERN_C void BurnExtensionUninitialize( + __in BURN_EXTENSIONS* pBurnExtensions + ) +{ + if (pBurnExtensions->rgExtensions) + { + for (DWORD i = 0; i < pBurnExtensions->cExtensions; ++i) + { + BURN_EXTENSION* pExtension = &pBurnExtensions->rgExtensions[i]; + + ReleaseStr(pExtension->sczEntryPayloadId); + ReleaseStr(pExtension->sczId); + } + MemFree(pBurnExtensions->rgExtensions); + } + + // clear struct + memset(pBurnExtensions, 0, sizeof(BURN_EXTENSIONS)); +} + +/******************************************************************* + BurnExtensionLoad - + +*******************************************************************/ +EXTERN_C HRESULT BurnExtensionLoad( + __in BURN_EXTENSIONS * pBurnExtensions, + __in BURN_EXTENSION_ENGINE_CONTEXT* pEngineContext + ) +{ + HRESULT hr = S_OK; + BUNDLE_EXTENSION_CREATE_ARGS args = { }; + BUNDLE_EXTENSION_CREATE_RESULTS results = { }; + + if (!pBurnExtensions->rgExtensions || !pBurnExtensions->cExtensions) + { + ExitFunction(); + } + + for (DWORD i = 0; i < pBurnExtensions->cExtensions; ++i) + { + BURN_EXTENSION* pExtension = &pBurnExtensions->rgExtensions[i]; + + memset(&args, 0, sizeof(BUNDLE_EXTENSION_CREATE_ARGS)); + memset(&results, 0, sizeof(BUNDLE_EXTENSION_CREATE_RESULTS)); + + args.cbSize = sizeof(BUNDLE_EXTENSION_CREATE_ARGS); + args.pfnBundleExtensionEngineProc = EngineForExtensionProc; + args.pvBundleExtensionEngineProcContext = pEngineContext; + args.qwEngineAPIVersion = MAKEQWORDVERSION(0, 0, 0, 1); // TODO: need to decide whether to keep this, and if so when to update it. + + results.cbSize = sizeof(BUNDLE_EXTENSION_CREATE_RESULTS); + + // Load BundleExtension DLL. + pExtension->hBextModule = ::LoadLibraryExW(pExtension->pEntryPayload->sczLocalFilePath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + ExitOnNullWithLastError(pExtension->hBextModule, hr, "Failed to load BundleExtension DLL '%ls': '%ls'.", pExtension->sczId, pExtension->pEntryPayload->sczLocalFilePath); + + // Get BundleExtensionCreate entry-point. + PFN_BUNDLE_EXTENSION_CREATE pfnCreate = (PFN_BUNDLE_EXTENSION_CREATE)::GetProcAddress(pExtension->hBextModule, "BundleExtensionCreate"); + ExitOnNullWithLastError(pfnCreate, hr, "Failed to get BundleExtensionCreate entry-point '%ls'.", pExtension->sczId); + + // Create BundleExtension. + hr = pfnCreate(&args, &results); + ExitOnFailure(hr, "Failed to create BundleExtension '%ls'.", pExtension->sczId); + + pExtension->pfnBurnExtensionProc = results.pfnBundleExtensionProc; + pExtension->pvBurnExtensionProcContext = results.pvBundleExtensionProcContext; + } + +LExit: + return hr; +} + +/******************************************************************* + BurnExtensionUnload - + +*******************************************************************/ +EXTERN_C void BurnExtensionUnload( + __in BURN_EXTENSIONS * pBurnExtensions + ) +{ + HRESULT hr = S_OK; + + if (pBurnExtensions->rgExtensions) + { + for (DWORD i = 0; i < pBurnExtensions->cExtensions; ++i) + { + BURN_EXTENSION* pExtension = &pBurnExtensions->rgExtensions[i]; + + if (pExtension->hBextModule) + { + // Get BundleExtensionDestroy entry-point and call it if it exists. + PFN_BUNDLE_EXTENSION_DESTROY pfnDestroy = (PFN_BUNDLE_EXTENSION_DESTROY)::GetProcAddress(pExtension->hBextModule, "BundleExtensionDestroy"); + if (pfnDestroy) + { + pfnDestroy(); + } + + // Free BundleExtension DLL. + if (!::FreeLibrary(pExtension->hBextModule)) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + TraceError(hr, "Failed to unload BundleExtension DLL."); + } + pExtension->hBextModule = NULL; + } + } + } +} diff --git a/src/engine/burnextension.h b/src/engine/burnextension.h new file mode 100644 index 00000000..43c8afe6 --- /dev/null +++ b/src/engine/burnextension.h @@ -0,0 +1,51 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#define BEEAPI HRESULT __stdcall + +#if defined(__cplusplus) +extern "C" { +#endif + +// structs + +typedef struct _BURN_EXTENSION_ENGINE_CONTEXT BURN_EXTENSION_ENGINE_CONTEXT; + +typedef struct _BURN_EXTENSION +{ + LPWSTR sczEntryPayloadId; + LPWSTR sczId; + + BURN_PAYLOAD* pEntryPayload; + + HMODULE hBextModule; + PFN_BUNDLE_EXTENSION_PROC pfnBurnExtensionProc; + LPVOID pvBurnExtensionProcContext; +} BURN_EXTENSION; + +typedef struct _BURN_EXTENSIONS +{ + BURN_EXTENSION* rgExtensions; + DWORD cExtensions; +} BURN_EXTENSIONS; + +// functions + +HRESULT BurnExtensionParseFromXml( + __in BURN_EXTENSIONS* pBurnExtensions, + __in BURN_PAYLOADS* pBaPayloads, + __in IXMLDOMNode* pixnBundle + ); +void BurnExtensionUninitialize( + __in BURN_EXTENSIONS* pBurnExtensions + ); +HRESULT BurnExtensionLoad( + __in BURN_EXTENSIONS* pBurnExtensions, + __in BURN_EXTENSION_ENGINE_CONTEXT* pEngineContext + ); +void BurnExtensionUnload( + __in BURN_EXTENSIONS* pBurnExtensions + ); +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/core.h b/src/engine/core.h index 6a6da2b1..544c1786 100644 --- a/src/engine/core.h +++ b/src/engine/core.h @@ -103,6 +103,7 @@ typedef struct _BURN_ENGINE_STATE BURN_PACKAGES packages; BURN_UPDATE update; BURN_APPROVED_EXES approvedExes; + BURN_EXTENSIONS extensions; HWND hMessageWindow; HANDLE hMessageWindowThread; diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 341fd471..488dbfe8 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -352,6 +352,8 @@ static void UninitializeEngineState( ReleaseHandle(pEngineState->hMessageWindowThread); + BurnExtensionUninitialize(&pEngineState->extensions); + ::DeleteCriticalSection(&pEngineState->userExperience.csEngineActive); UserExperienceUninitialize(&pEngineState->userExperience); @@ -493,6 +495,7 @@ static HRESULT RunNormal( HANDLE hPipesCreatedEvent = NULL; BOOL fContinueExecution = TRUE; BOOL fReloadApp = FALSE; + BURN_EXTENSION_ENGINE_CONTEXT extensionEngineContext = { }; // Initialize logging. hr = LoggingOpen(&pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->registration.sczDisplayName); @@ -537,6 +540,13 @@ static HRESULT RunNormal( ExitOnFailure(hr, "Failed to set layout directory variable to value provided from command-line."); } + // Setup the extension engine. + extensionEngineContext.pEngineState = pEngineState; + + // Load the extensions. + hr = BurnExtensionLoad(&pEngineState->extensions, &extensionEngineContext); + ExitOnFailure(hr, "Failed to load BundleExtensions."); + do { fReloadApp = FALSE; @@ -546,6 +556,8 @@ static HRESULT RunNormal( } while (fReloadApp); LExit: + BurnExtensionUnload(&pEngineState->extensions); + // If the message window is still around, close it. UiCloseMessageWindow(pEngineState); diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index 499fcd4d..c2e8f34c 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -2,7 +2,7 @@ - + @@ -50,10 +50,12 @@ + + @@ -90,15 +92,11 @@ - - - - - + @@ -110,6 +108,7 @@ + @@ -167,7 +166,7 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + diff --git a/src/engine/manifest.cpp b/src/engine/manifest.cpp index c2214a89..a20f1980 100644 --- a/src/engine/manifest.cpp +++ b/src/engine/manifest.cpp @@ -116,6 +116,10 @@ extern "C" HRESULT ManifestLoadXmlFromBuffer( hr = ApprovedExesParseFromXml(&pEngineState->approvedExes, pixeBundle); ExitOnFailure(hr, "Failed to parse approved exes."); + // parse extensions + hr = BurnExtensionParseFromXml(&pEngineState->extensions, &pEngineState->userExperience.payloads, pixeBundle); + ExitOnFailure(hr, "Failed to parse extensions."); + LExit: ReleaseObject(pixnChain); ReleaseObject(pixnLog); diff --git a/src/engine/packages.config b/src/engine/packages.config index 01a9390c..75a6476b 100644 --- a/src/engine/packages.config +++ b/src/engine/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/engine/precomp.h b/src/engine/precomp.h index 477dc310..780822a1 100644 --- a/src/engine/precomp.h +++ b/src/engine/precomp.h @@ -61,6 +61,8 @@ #include "BootstrapperEngine.h" #include "BootstrapperApplication.h" +#include "BundleExtensionEngine.h" +#include "BundleExtension.h" #include "platform.h" #include "variant.h" @@ -73,6 +75,7 @@ #include "catalog.h" #include "payload.h" #include "cabextract.h" +#include "burnextension.h" #include "userexperience.h" #include "package.h" #include "update.h" @@ -100,4 +103,5 @@ #include "netfxchainer.h" #include "EngineForApplication.h" +#include "EngineForExtension.h" #include "engine.messages.h" diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index 8d5271aa..26b20f39 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -103,7 +103,7 @@ extern "C" HRESULT UserExperienceLoad( // Load BA DLL. pUserExperience->hUXModule = ::LoadLibraryExW(pUserExperience->payloads.rgPayloads[0].sczLocalFilePath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); - ExitOnNullWithLastError(pUserExperience->hUXModule, hr, "Failed to load UX DLL."); + ExitOnNullWithLastError(pUserExperience->hUXModule, hr, "Failed to load BA DLL."); // Get BootstrapperApplicationCreate entry-point. PFN_BOOTSTRAPPER_APPLICATION_CREATE pfnCreate = (PFN_BOOTSTRAPPER_APPLICATION_CREATE)::GetProcAddress(pUserExperience->hUXModule, "BootstrapperApplicationCreate"); diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index 27a94115..bec6d292 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -15,7 +15,7 @@ const DWORD MB_RETRYTRYAGAIN = 0xF; // structs -struct BOOTSTRAPPER_ENGINE_CONTEXT; +typedef struct _BOOTSTRAPPER_ENGINE_CONTEXT BOOTSTRAPPER_ENGINE_CONTEXT; typedef struct _BURN_USER_EXPERIENCE { diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp index 7377b116..8b1fd279 100644 --- a/src/engine/variable.cpp +++ b/src/engine/variable.cpp @@ -309,7 +309,7 @@ extern "C" HRESULT VariablesParseFromXml( hr = pixnNodes->get_length((long*)&cNodes); ExitOnFailure(hr, "Failed to get variable node count."); - // parse package elements + // parse variable elements for (DWORD i = 0; i < cNodes; ++i) { hr = XmlNextElement(pixnNodes, &pixnNode, NULL); -- cgit v1.2.3-55-g6feb From 1a0190bd31953a5ffb68cac75866328dccbf03f2 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 29 Mar 2020 19:16:30 +1000 Subject: Add support for ExtensionSearches. --- src/engine/burnextension.cpp | 49 ++++++++++++++++++++++++++++++++++++++++++++ src/engine/burnextension.h | 10 +++++++++ src/engine/manifest.cpp | 16 +++++++-------- src/engine/precomp.h | 2 +- src/engine/search.cpp | 31 +++++++++++++++++++++++++++- src/engine/search.h | 6 ++++++ 6 files changed, 104 insertions(+), 10 deletions(-) diff --git a/src/engine/burnextension.cpp b/src/engine/burnextension.cpp index 99673cd9..68d34123 100644 --- a/src/engine/burnextension.cpp +++ b/src/engine/burnextension.cpp @@ -182,3 +182,52 @@ EXTERN_C void BurnExtensionUnload( } } } + +EXTERN_C HRESULT BurnExtensionFindById( + __in BURN_EXTENSIONS* pBurnExtensions, + __in_z LPCWSTR wzId, + __out BURN_EXTENSION** ppExtension + ) +{ + HRESULT hr = S_OK; + BURN_EXTENSION* pExtension = NULL; + + for (DWORD i = 0; i < pBurnExtensions->cExtensions; ++i) + { + pExtension = &pBurnExtensions->rgExtensions[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pExtension->sczId, -1, wzId, -1)) + { + *ppExtension = pExtension; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + +EXTERN_C BEEAPI BurnExtensionPerformSearch( + __in BURN_EXTENSION* pExtension, + __in LPWSTR wzSearchId, + __in LPWSTR wzVariable + ) +{ + HRESULT hr = S_OK; + BUNDLE_EXTENSION_SEARCH_ARGS args = { }; + BUNDLE_EXTENSION_SEARCH_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzId = wzSearchId; + args.wzVariable = wzVariable; + + results.cbSize = sizeof(results); + + hr = pExtension->pfnBurnExtensionProc(BUNDLE_EXTENSION_MESSAGE_SEARCH, &args, &results, pExtension->pvBurnExtensionProcContext); + ExitOnFailure(hr, "BundleExtension '%ls' Search '%ls' failed.", pExtension->sczId, wzSearchId); + +LExit: + return hr; +} diff --git a/src/engine/burnextension.h b/src/engine/burnextension.h index 43c8afe6..370ddd2d 100644 --- a/src/engine/burnextension.h +++ b/src/engine/burnextension.h @@ -46,6 +46,16 @@ HRESULT BurnExtensionLoad( void BurnExtensionUnload( __in BURN_EXTENSIONS* pBurnExtensions ); +HRESULT BurnExtensionFindById( + __in BURN_EXTENSIONS* pBurnExtensions, + __in_z LPCWSTR wzId, + __out BURN_EXTENSION** ppExtension + ); +BEEAPI BurnExtensionPerformSearch( + __in BURN_EXTENSION* pExtension, + __in LPWSTR wzSearchId, + __in LPWSTR wzVariable + ); #if defined(__cplusplus) } #endif diff --git a/src/engine/manifest.cpp b/src/engine/manifest.cpp index a20f1980..8783b15e 100644 --- a/src/engine/manifest.cpp +++ b/src/engine/manifest.cpp @@ -80,14 +80,18 @@ extern "C" HRESULT ManifestLoadXmlFromBuffer( hr = VariablesParseFromXml(&pEngineState->variables, pixeBundle); ExitOnFailure(hr, "Failed to parse variables."); - // parse searches - hr = SearchesParseFromXml(&pEngineState->searches, pixeBundle); // TODO: Modularization - ExitOnFailure(hr, "Failed to parse searches."); - // parse user experience hr = UserExperienceParseFromXml(&pEngineState->userExperience, pixeBundle); ExitOnFailure(hr, "Failed to parse user experience."); + // parse extensions + hr = BurnExtensionParseFromXml(&pEngineState->extensions, &pEngineState->userExperience.payloads, pixeBundle); + ExitOnFailure(hr, "Failed to parse extensions."); + + // parse searches + hr = SearchesParseFromXml(&pEngineState->searches, &pEngineState->extensions, pixeBundle); + ExitOnFailure(hr, "Failed to parse searches."); + // parse catalog files hr = CatalogsParseFromXml(&pEngineState->catalogs, pixeBundle); ExitOnFailure(hr, "Failed to parse catalog files."); @@ -116,10 +120,6 @@ extern "C" HRESULT ManifestLoadXmlFromBuffer( hr = ApprovedExesParseFromXml(&pEngineState->approvedExes, pixeBundle); ExitOnFailure(hr, "Failed to parse approved exes."); - // parse extensions - hr = BurnExtensionParseFromXml(&pEngineState->extensions, &pEngineState->userExperience.payloads, pixeBundle); - ExitOnFailure(hr, "Failed to parse extensions."); - LExit: ReleaseObject(pixnChain); ReleaseObject(pixnLog); diff --git a/src/engine/precomp.h b/src/engine/precomp.h index 780822a1..7aa7dafa 100644 --- a/src/engine/precomp.h +++ b/src/engine/precomp.h @@ -68,7 +68,6 @@ #include "variant.h" #include "variable.h" #include "condition.h" -#include "search.h" #include "section.h" #include "approvedexe.h" #include "container.h" @@ -76,6 +75,7 @@ #include "payload.h" #include "cabextract.h" #include "burnextension.h" +#include "search.h" #include "userexperience.h" #include "package.h" #include "update.h" diff --git a/src/engine/search.cpp b/src/engine/search.cpp index c50790fd..763286fd 100644 --- a/src/engine/search.cpp +++ b/src/engine/search.cpp @@ -45,12 +45,16 @@ static HRESULT MsiFeatureSearch( __in BURN_SEARCH* pSearch, __in BURN_VARIABLES* pVariables ); +static HRESULT PerformExtensionSearch( + __in BURN_SEARCH* pSearch + ); // function definitions extern "C" HRESULT SearchesParseFromXml( __in BURN_SEARCHES* pSearches, + __in BURN_EXTENSIONS* pBurnExtensions, __in IXMLDOMNode* pixnBundle ) { @@ -62,7 +66,7 @@ extern "C" HRESULT SearchesParseFromXml( LPWSTR scz = NULL; // select search nodes - hr = XmlSelectNodes(pixnBundle, L"DirectorySearch|FileSearch|RegistrySearch|MsiComponentSearch|MsiProductSearch|MsiFeatureSearch", &pixnNodes); + hr = XmlSelectNodes(pixnBundle, L"DirectorySearch|FileSearch|RegistrySearch|MsiComponentSearch|MsiProductSearch|MsiFeatureSearch|ExtensionSearch", &pixnNodes); ExitOnFailure(hr, "Failed to select search nodes."); // get search node count @@ -373,6 +377,17 @@ extern "C" HRESULT SearchesParseFromXml( ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); } } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"ExtensionSearch", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_EXTENSION; + + // @ExtensionId + hr = XmlGetAttributeEx(pixnNode, L"ExtensionId", &scz); + ExitOnFailure(hr, "Failed to get @ExtensionId."); + + hr = BurnExtensionFindById(pBurnExtensions, scz, &pSearch->ExtensionSearch.pExtension); + ExitOnFailure(hr, "Failed to find extension '%ls' for search '%ls'", scz, pSearch->sczKey); + } else { hr = E_UNEXPECTED; @@ -477,6 +492,9 @@ extern "C" HRESULT SearchesExecute( case BURN_SEARCH_TYPE_MSI_FEATURE: hr = MsiFeatureSearch(pSearch, pVariables); break; + case BURN_SEARCH_TYPE_EXTENSION: + hr = PerformExtensionSearch(pSearch); + break; default: hr = E_UNEXPECTED; } @@ -1193,3 +1211,14 @@ static HRESULT MsiFeatureSearch( return hr; } + +static HRESULT PerformExtensionSearch( + __in BURN_SEARCH* pSearch + ) +{ + HRESULT hr = S_OK; + + hr = BurnExtensionPerformSearch(pSearch->ExtensionSearch.pExtension, pSearch->sczKey, pSearch->sczVariable); + + return hr; +} diff --git a/src/engine/search.h b/src/engine/search.h index 65dfb18f..d6b2586e 100644 --- a/src/engine/search.h +++ b/src/engine/search.h @@ -18,6 +18,7 @@ enum BURN_SEARCH_TYPE BURN_SEARCH_TYPE_MSI_COMPONENT, BURN_SEARCH_TYPE_MSI_PRODUCT, BURN_SEARCH_TYPE_MSI_FEATURE, + BURN_SEARCH_TYPE_EXTENSION, }; enum BURN_DIRECTORY_SEARCH_TYPE @@ -122,6 +123,10 @@ typedef struct _BURN_SEARCH LPWSTR sczProductCode; LPWSTR sczFeatureId; } MsiFeatureSearch; + struct + { + BURN_EXTENSION* pExtension; + } ExtensionSearch; }; } BURN_SEARCH; @@ -136,6 +141,7 @@ typedef struct _BURN_SEARCHES HRESULT SearchesParseFromXml( __in BURN_SEARCHES* pSearches, + __in BURN_EXTENSIONS* pBurnExtensions, __in IXMLDOMNode* pixnBundle ); HRESULT SearchesExecute( -- cgit v1.2.3-55-g6feb From 8af9c39dceb6a6ccb55b5a4d76bb71c7c4df133a Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 29 Mar 2020 19:27:25 +1000 Subject: Help BAs and BundleExtensions find their data file. --- src/engine/burnextension.cpp | 8 ++++++++ src/engine/core.cpp | 6 ++++++ src/engine/engine.cpp | 2 ++ src/engine/userexperience.cpp | 2 +- 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/engine/burnextension.cpp b/src/engine/burnextension.cpp index 68d34123..59f84eca 100644 --- a/src/engine/burnextension.cpp +++ b/src/engine/burnextension.cpp @@ -104,6 +104,7 @@ EXTERN_C HRESULT BurnExtensionLoad( ) { HRESULT hr = S_OK; + LPWSTR sczBundleExtensionDataPath = NULL; BUNDLE_EXTENSION_CREATE_ARGS args = { }; BUNDLE_EXTENSION_CREATE_RESULTS results = { }; @@ -112,6 +113,9 @@ EXTERN_C HRESULT BurnExtensionLoad( ExitFunction(); } + hr = PathConcat(pEngineContext->pEngineState->userExperience.sczTempDirectory, L"BundleExtensionData.xml", &sczBundleExtensionDataPath); + ExitOnFailure(hr, "Failed to get BundleExtensionDataPath."); + for (DWORD i = 0; i < pBurnExtensions->cExtensions; ++i) { BURN_EXTENSION* pExtension = &pBurnExtensions->rgExtensions[i]; @@ -123,6 +127,8 @@ EXTERN_C HRESULT BurnExtensionLoad( args.pfnBundleExtensionEngineProc = EngineForExtensionProc; args.pvBundleExtensionEngineProcContext = pEngineContext; args.qwEngineAPIVersion = MAKEQWORDVERSION(0, 0, 0, 1); // TODO: need to decide whether to keep this, and if so when to update it. + args.wzBootstrapperWorkingFolder = pEngineContext->pEngineState->userExperience.sczTempDirectory; + args.wzBundleExtensionDataPath = sczBundleExtensionDataPath; results.cbSize = sizeof(BUNDLE_EXTENSION_CREATE_RESULTS); @@ -143,6 +149,8 @@ EXTERN_C HRESULT BurnExtensionLoad( } LExit: + ReleaseStr(sczBundleExtensionDataPath); + return hr; } diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 10750be8..26e74588 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -151,6 +151,12 @@ extern "C" HRESULT CoreInitialize( // Load the catalog files as soon as they are extracted. hr = CatalogLoadFromPayload(&pEngineState->catalogs, &pEngineState->userExperience.payloads); ExitOnFailure(hr, "Failed to load catalog files."); + + hr = PathConcat(pEngineState->userExperience.sczTempDirectory, L"BootstrapperApplicationData.xml", &pEngineState->command.wzBootstrapperApplicationDataPath); + ExitOnFailure(hr, "Failed to get BootstrapperApplicationDataPath."); + + hr = StrAllocString(&pEngineState->command.wzBootstrapperWorkingFolder, pEngineState->userExperience.sczTempDirectory, 0); + ExitOnFailure(hr, "Failed to copy sczBootstrapperWorkingFolder."); } LExit: diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 488dbfe8..36c58b49 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -368,6 +368,8 @@ static void UninitializeEngineState( SectionUninitialize(&pEngineState->section); ContainersUninitialize(&pEngineState->containers); + ReleaseStr(pEngineState->command.wzBootstrapperApplicationDataPath); + ReleaseStr(pEngineState->command.wzBootstrapperWorkingFolder); ReleaseStr(pEngineState->command.wzLayoutDirectory); ReleaseStr(pEngineState->command.wzCommandLine); diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index 26b20f39..566597a2 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -97,7 +97,7 @@ extern "C" HRESULT UserExperienceLoad( args.pCommand = pCommand; args.pfnBootstrapperEngineProc = EngineForApplicationProc; args.pvBootstrapperEngineProcContext = pEngineContext; - args.qwEngineAPIVersion = MAKEQWORDVERSION(0, 0, 0, 5); // TODO: need to decide whether to keep this, and if so when to update it. + args.qwEngineAPIVersion = MAKEQWORDVERSION(0, 0, 0, 6); // TODO: need to decide whether to keep this, and if so when to update it. results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS); -- cgit v1.2.3-55-g6feb From 7e79c6be038f3703e53fa5ff04c4e2ad865541c1 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 30 Mar 2020 21:05:11 +1000 Subject: Implement SetVariable. --- src/engine/search.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++++++++- src/engine/search.h | 5 ++++ src/engine/variable.cpp | 9 +++++++ src/engine/variable.h | 5 ++++ 4 files changed, 89 insertions(+), 1 deletion(-) diff --git a/src/engine/search.cpp b/src/engine/search.cpp index 763286fd..16f8e459 100644 --- a/src/engine/search.cpp +++ b/src/engine/search.cpp @@ -48,6 +48,10 @@ static HRESULT MsiFeatureSearch( static HRESULT PerformExtensionSearch( __in BURN_SEARCH* pSearch ); +static HRESULT PerformSetVariable( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables +); // function definitions @@ -64,9 +68,10 @@ extern "C" HRESULT SearchesParseFromXml( DWORD cNodes = 0; BSTR bstrNodeName = NULL; LPWSTR scz = NULL; + BURN_VARIANT_TYPE valueType = BURN_VARIANT_TYPE_NONE; // select search nodes - hr = XmlSelectNodes(pixnBundle, L"DirectorySearch|FileSearch|RegistrySearch|MsiComponentSearch|MsiProductSearch|MsiFeatureSearch|ExtensionSearch", &pixnNodes); + hr = XmlSelectNodes(pixnBundle, L"DirectorySearch|FileSearch|RegistrySearch|MsiComponentSearch|MsiProductSearch|MsiFeatureSearch|ExtensionSearch|SetVariable", &pixnNodes); ExitOnFailure(hr, "Failed to select search nodes."); // get search node count @@ -388,6 +393,50 @@ extern "C" HRESULT SearchesParseFromXml( hr = BurnExtensionFindById(pBurnExtensions, scz, &pSearch->ExtensionSearch.pExtension); ExitOnFailure(hr, "Failed to find extension '%ls' for search '%ls'", scz, pSearch->sczKey); } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"SetVariable", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_SET_VARIABLE; + + // @Value + hr = XmlGetAttributeEx(pixnNode, L"Value", &scz); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Value."); + + hr = BVariantSetString(&pSearch->SetVariable.value, scz, 0); + ExitOnFailure(hr, "Failed to set variant value."); + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) + { + valueType = BURN_VARIANT_TYPE_NUMERIC; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1)) + { + valueType = BURN_VARIANT_TYPE_STRING; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) + { + valueType = BURN_VARIANT_TYPE_VERSION; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else + { + valueType = BURN_VARIANT_TYPE_NONE; + } + + // change value variant to correct type + hr = BVariantChangeType(&pSearch->SetVariable.value, valueType); + ExitOnFailure(hr, "Failed to change variant type."); + } else { hr = E_UNEXPECTED; @@ -495,6 +544,9 @@ extern "C" HRESULT SearchesExecute( case BURN_SEARCH_TYPE_EXTENSION: hr = PerformExtensionSearch(pSearch); break; + case BURN_SEARCH_TYPE_SET_VARIABLE: + hr = PerformSetVariable(pSearch, pVariables); + break; default: hr = E_UNEXPECTED; } @@ -549,6 +601,9 @@ extern "C" void SearchesUninitialize( ReleaseStr(pSearch->MsiFeatureSearch.sczProductCode); ReleaseStr(pSearch->MsiFeatureSearch.sczFeatureId); break; + case BURN_SEARCH_TYPE_SET_VARIABLE: + BVariantUninitialize(&pSearch->SetVariable.value); + break; } } MemFree(pSearches->rgSearches); @@ -1222,3 +1277,17 @@ static HRESULT PerformExtensionSearch( return hr; } + +static HRESULT PerformSetVariable( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + + hr = VariableSetVariant(pVariables, pSearch->sczVariable, &pSearch->SetVariable.value); + ExitOnFailure(hr, "Failed to set variable: %ls", pSearch->sczVariable); + +LExit: + return hr; +} diff --git a/src/engine/search.h b/src/engine/search.h index d6b2586e..c699c97c 100644 --- a/src/engine/search.h +++ b/src/engine/search.h @@ -19,6 +19,7 @@ enum BURN_SEARCH_TYPE BURN_SEARCH_TYPE_MSI_PRODUCT, BURN_SEARCH_TYPE_MSI_FEATURE, BURN_SEARCH_TYPE_EXTENSION, + BURN_SEARCH_TYPE_SET_VARIABLE, }; enum BURN_DIRECTORY_SEARCH_TYPE @@ -127,6 +128,10 @@ typedef struct _BURN_SEARCH { BURN_EXTENSION* pExtension; } ExtensionSearch; + struct + { + BURN_VARIANT value; + } SetVariable; }; } BURN_SEARCH; diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp index 8b1fd279..dc5a569a 100644 --- a/src/engine/variable.cpp +++ b/src/engine/variable.cpp @@ -732,6 +732,15 @@ extern "C" HRESULT VariableSetLiteralVariant( return SetVariableValue(pVariables, wzVariable, pVariant, TRUE, SET_VARIABLE_NOT_BUILTIN, TRUE); } +extern "C" HRESULT VariableSetVariant( + __in BURN_VARIABLES * pVariables, + __in_z LPCWSTR wzVariable, + __in BURN_VARIANT * pVariant + ) +{ + return SetVariableValue(pVariables, wzVariable, pVariant, FALSE, SET_VARIABLE_NOT_BUILTIN, TRUE); +} + // The contents of psczOut may be sensitive, should keep encrypted and SecureZeroFree extern "C" HRESULT VariableFormatString( __in BURN_VARIABLES* pVariables, diff --git a/src/engine/variable.h b/src/engine/variable.h index c48e0160..63f12cdf 100644 --- a/src/engine/variable.h +++ b/src/engine/variable.h @@ -127,6 +127,11 @@ HRESULT VariableSetLiteralVariant( __in_z LPCWSTR wzVariable, __in BURN_VARIANT* pVariant ); +HRESULT VariableSetVariant( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in BURN_VARIANT* pVariant + ); HRESULT VariableFormatString( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzIn, -- cgit v1.2.3-55-g6feb From 7670257bba2a8dc11c01664bc5f102a8dec17b93 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 21 Apr 2020 17:28:17 +1000 Subject: Add action to OnExecutePackageBegin. --- src/engine/apply.cpp | 24 ++++++++++++------------ src/engine/engine.vcxproj | 4 ++-- src/engine/packages.config | 2 +- src/engine/userexperience.cpp | 4 +++- src/engine/userexperience.h | 3 ++- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 0cef9ac8..ee11cdee 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -2064,7 +2064,7 @@ static HRESULT ExecuteExePackage( fBeginCalled = TRUE; // Send package execute begin to BA. - hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->exePackage.pPackage->sczId, !fRollback); + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->exePackage.pPackage->sczId, !fRollback, pExecuteAction->exePackage.action); ExitOnRootFailure(hr, "BA aborted execute EXE package begin."); message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; @@ -2072,7 +2072,7 @@ static HRESULT ExecuteExePackage( message.progress.dwPercentage = fRollback ? 100 : 0; nResult = GenericExecuteMessageHandler(&message, pContext); hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); - ExitOnRootFailure(hr, "UX aborted EXE progress."); + ExitOnRootFailure(hr, "BA aborted EXE progress."); // Execute package. if (pExecuteAction->exePackage.pPackage->fPerMachine) @@ -2091,13 +2091,13 @@ static HRESULT ExecuteExePackage( message.progress.dwPercentage = fRollback ? 0 : 100; nResult = GenericExecuteMessageHandler(&message, pContext); hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); - ExitOnRootFailure(hr, "UX aborted EXE progress."); + ExitOnRootFailure(hr, "BA aborted EXE progress."); pContext->cExecutedPackages += fRollback ? -1 : 1; (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); - ExitOnRootFailure(hr, "UX aborted EXE package execute progress."); + ExitOnRootFailure(hr, "BA aborted EXE package execute progress."); LExit: if (fBeginCalled) @@ -2133,7 +2133,7 @@ static HRESULT ExecuteMsiPackage( fBeginCalled = TRUE; // Send package execute begin to BA. - hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msiPackage.pPackage->sczId, !fRollback); + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msiPackage.pPackage->sczId, !fRollback, pExecuteAction->msiPackage.action); ExitOnRootFailure(hr, "BA aborted execute MSI package begin."); // execute package @@ -2152,7 +2152,7 @@ static HRESULT ExecuteMsiPackage( (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); - ExitOnRootFailure(hr, "UX aborted MSI package execute progress."); + ExitOnRootFailure(hr, "BA aborted MSI package execute progress."); LExit: if (fBeginCalled) @@ -2188,7 +2188,7 @@ static HRESULT ExecuteMspPackage( fBeginCalled = TRUE; // Send package execute begin to BA. - hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->mspTarget.pPackage->sczId, !fRollback); + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->mspTarget.pPackage->sczId, !fRollback, pExecuteAction->mspTarget.action); ExitOnRootFailure(hr, "BA aborted execute MSP package begin."); // Now send all the patches that target this product code. @@ -2216,7 +2216,7 @@ static HRESULT ExecuteMspPackage( (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); - ExitOnRootFailure(hr, "UX aborted MSP package execute progress."); + ExitOnRootFailure(hr, "BA aborted MSP package execute progress."); LExit: if (fBeginCalled) @@ -2255,7 +2255,7 @@ static HRESULT ExecuteMsuPackage( fBeginCalled = TRUE; // Send package execute begin to BA. - hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msuPackage.pPackage->sczId, !fRollback); + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msuPackage.pPackage->sczId, !fRollback, pExecuteAction->msuPackage.action); ExitOnRootFailure(hr, "BA aborted execute MSU package begin."); message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; @@ -2263,7 +2263,7 @@ static HRESULT ExecuteMsuPackage( message.progress.dwPercentage = fRollback ? 100 : 0; nResult = GenericExecuteMessageHandler(&message, pContext); hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); - ExitOnRootFailure(hr, "UX aborted MSU progress."); + ExitOnRootFailure(hr, "BA aborted MSU progress."); // execute package if (pExecuteAction->msuPackage.pPackage->fPerMachine) @@ -2282,13 +2282,13 @@ static HRESULT ExecuteMsuPackage( message.progress.dwPercentage = fRollback ? 0 : 100; nResult = GenericExecuteMessageHandler(&message, pContext); hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); - ExitOnRootFailure(hr, "UX aborted MSU progress."); + ExitOnRootFailure(hr, "BA aborted MSU progress."); pContext->cExecutedPackages += fRollback ? -1 : 1; (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); - ExitOnRootFailure(hr, "UX aborted MSU package execute progress."); + ExitOnRootFailure(hr, "BA aborted MSU package execute progress."); LExit: if (fBeginCalled) diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index c2e8f34c..b0c3e295 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -2,7 +2,7 @@ - + @@ -166,7 +166,7 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + diff --git a/src/engine/packages.config b/src/engine/packages.config index 75a6476b..38569fda 100644 --- a/src/engine/packages.config +++ b/src/engine/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index 566597a2..4d149e9a 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -1209,7 +1209,8 @@ LExit: EXTERN_C BAAPI UserExperienceOnExecutePackageBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, - __in BOOL fExecute + __in BOOL fExecute, + __in BOOTSTRAPPER_ACTION_STATE action ) { HRESULT hr = S_OK; @@ -1219,6 +1220,7 @@ EXTERN_C BAAPI UserExperienceOnExecutePackageBegin( args.cbSize = sizeof(args); args.wzPackageId = wzPackageId; args.fExecute = fExecute; + args.action = action; results.cbSize = sizeof(results); diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index bec6d292..5aade0f2 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -292,7 +292,8 @@ BAAPI UserExperienceOnExecuteMsiMessage( BAAPI UserExperienceOnExecutePackageBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, - __in BOOL fExecute + __in BOOL fExecute, + __in BOOTSTRAPPER_ACTION_STATE action ); BAAPI UserExperienceOnExecutePackageComplete( __in BURN_USER_EXPERIENCE* pUserExperience, -- cgit v1.2.3-55-g6feb From 04eff6d8290ea8f3be0c7e8447b73451a2263bb8 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 29 Apr 2020 18:57:50 +1000 Subject: Implement fDisableUnloading for the BA. --- src/engine/engine.vcxproj | 4 ++-- src/engine/packages.config | 2 +- src/engine/userexperience.cpp | 7 ++++--- src/engine/userexperience.h | 1 + 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index b0c3e295..6fd9d64a 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -2,7 +2,7 @@ - + @@ -166,7 +166,7 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + diff --git a/src/engine/packages.config b/src/engine/packages.config index 38569fda..e9b2d190 100644 --- a/src/engine/packages.config +++ b/src/engine/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index 4d149e9a..24fc1ec7 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -97,7 +97,7 @@ extern "C" HRESULT UserExperienceLoad( args.pCommand = pCommand; args.pfnBootstrapperEngineProc = EngineForApplicationProc; args.pvBootstrapperEngineProcContext = pEngineContext; - args.qwEngineAPIVersion = MAKEQWORDVERSION(0, 0, 0, 6); // TODO: need to decide whether to keep this, and if so when to update it. + args.qwEngineAPIVersion = MAKEQWORDVERSION(0, 0, 0, 7); // TODO: need to decide whether to keep this, and if so when to update it. results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS); @@ -115,6 +115,7 @@ extern "C" HRESULT UserExperienceLoad( pUserExperience->pfnBAProc = results.pfnBootstrapperApplicationProc; pUserExperience->pvBAProcContext = results.pvBootstrapperApplicationProcContext; + pUserExperience->fDisableUnloading = results.fDisableUnloading; LExit: return hr; @@ -139,8 +140,8 @@ extern "C" HRESULT UserExperienceUnload( pfnDestroy(); } - // Free BA DLL. - if (!::FreeLibrary(pUserExperience->hUXModule)) + // Free BA DLL if it supports it. + if (!pUserExperience->fDisableUnloading && !::FreeLibrary(pUserExperience->hUXModule)) { hr = HRESULT_FROM_WIN32(::GetLastError()); TraceError(hr, "Failed to unload BA DLL."); diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index 5aade0f2..7d2b743d 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -25,6 +25,7 @@ typedef struct _BURN_USER_EXPERIENCE HMODULE hUXModule; PFN_BOOTSTRAPPER_APPLICATION_PROC pfnBAProc; LPVOID pvBAProcContext; + BOOL fDisableUnloading; LPWSTR sczTempDirectory; CRITICAL_SECTION csEngineActive; // Changing the engine active state in the user experience must be -- cgit v1.2.3-55-g6feb From d232c5621fe336b1f563b69be7637c93e795e151 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 10 May 2020 12:04:47 +1000 Subject: Redo nuget package so that burn.exe's get put where Core expects them to be --- src/stub/WixToolset.Burn.nuspec | 21 +++++++++++++++++++++ src/stub/WixToolset.Burn.props | 12 ++++++++++++ src/stub/runtime.win.WixToolset.Burn.nuspec | 20 -------------------- src/stub/stub.vcxproj | 9 ++------- 4 files changed, 35 insertions(+), 27 deletions(-) create mode 100644 src/stub/WixToolset.Burn.nuspec create mode 100644 src/stub/WixToolset.Burn.props delete mode 100644 src/stub/runtime.win.WixToolset.Burn.nuspec diff --git a/src/stub/WixToolset.Burn.nuspec b/src/stub/WixToolset.Burn.nuspec new file mode 100644 index 00000000..e71f64e4 --- /dev/null +++ b/src/stub/WixToolset.Burn.nuspec @@ -0,0 +1,21 @@ + + + + $id$ + $version$ + $authors$ + $authors$ + + https://licenses.nuget.org/MS-RL + https://github.com/wixtoolset/burn + false + $description$ + $copyright$ + + + + + + + + diff --git a/src/stub/WixToolset.Burn.props b/src/stub/WixToolset.Burn.props new file mode 100644 index 00000000..b83d0b1c --- /dev/null +++ b/src/stub/WixToolset.Burn.props @@ -0,0 +1,12 @@ + + + + + + + + %(RecursiveDir)%(FileName)%(Extension) + PreserveNewest + + + diff --git a/src/stub/runtime.win.WixToolset.Burn.nuspec b/src/stub/runtime.win.WixToolset.Burn.nuspec deleted file mode 100644 index e296bf8f..00000000 --- a/src/stub/runtime.win.WixToolset.Burn.nuspec +++ /dev/null @@ -1,20 +0,0 @@ - - - - $id$ - $version$ - $authors$ - $authors$ - - https://licenses.nuget.org/MS-RL - https://github.com/wixtoolset/burn - false - $description$ - $copyright$ - - - - - - - diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index 02c61295..7af42fa9 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -23,16 +23,11 @@ - - x86 - amd64 - - {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1} Application Windows - burn.$(NameSuffix) + burn v141 Unicode Native component of WixToolset.Burn @@ -90,7 +85,7 @@ - + -- cgit v1.2.3-55-g6feb From 50cb451bcee148afd9768086c1bb5ed4f75562df Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Thu, 14 May 2020 19:59:06 +1000 Subject: WIXFEAT:6164 Give BA control over UI level and handler. --- src/engine/apply.cpp | 8 +-- src/engine/core.cpp | 8 +-- src/engine/elevation.cpp | 24 +++++++++ src/engine/engine.vcxproj | 8 +-- src/engine/msiengine.cpp | 117 +++++++++++++++++++++++++++++++----------- src/engine/msiengine.h | 18 +++++-- src/engine/mspengine.cpp | 38 +++++++++----- src/engine/mspengine.h | 2 +- src/engine/package.h | 2 - src/engine/packages.config | 4 +- src/engine/plan.cpp | 24 ++++----- src/engine/plan.h | 8 +-- src/engine/userexperience.cpp | 47 ++++++++++++++++- src/engine/userexperience.h | 13 ++++- src/stub/packages.config | 2 +- src/stub/stub.vcxproj | 4 +- 16 files changed, 237 insertions(+), 90 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index ee11cdee..eae7c681 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -2064,7 +2064,7 @@ static HRESULT ExecuteExePackage( fBeginCalled = TRUE; // Send package execute begin to BA. - hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->exePackage.pPackage->sczId, !fRollback, pExecuteAction->exePackage.action); + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->exePackage.pPackage->sczId, !fRollback, pExecuteAction->exePackage.action, INSTALLUILEVEL_NOCHANGE, false); ExitOnRootFailure(hr, "BA aborted execute EXE package begin."); message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; @@ -2133,7 +2133,7 @@ static HRESULT ExecuteMsiPackage( fBeginCalled = TRUE; // Send package execute begin to BA. - hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msiPackage.pPackage->sczId, !fRollback, pExecuteAction->msiPackage.action); + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msiPackage.pPackage->sczId, !fRollback, pExecuteAction->msiPackage.action, pExecuteAction->msiPackage.uiLevel, pExecuteAction->msiPackage.fDisableExternalUiHandler); ExitOnRootFailure(hr, "BA aborted execute MSI package begin."); // execute package @@ -2188,7 +2188,7 @@ static HRESULT ExecuteMspPackage( fBeginCalled = TRUE; // Send package execute begin to BA. - hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->mspTarget.pPackage->sczId, !fRollback, pExecuteAction->mspTarget.action); + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->mspTarget.pPackage->sczId, !fRollback, pExecuteAction->mspTarget.action, pExecuteAction->mspTarget.uiLevel, pExecuteAction->mspTarget.fDisableExternalUiHandler); ExitOnRootFailure(hr, "BA aborted execute MSP package begin."); // Now send all the patches that target this product code. @@ -2255,7 +2255,7 @@ static HRESULT ExecuteMsuPackage( fBeginCalled = TRUE; // Send package execute begin to BA. - hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msuPackage.pPackage->sczId, !fRollback, pExecuteAction->msuPackage.action); + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msuPackage.pPackage->sczId, !fRollback, pExecuteAction->msuPackage.action, INSTALLUILEVEL_NOCHANGE, false); ExitOnRootFailure(hr, "BA aborted execute MSU package begin."); message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 26e74588..c153f162 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -431,7 +431,7 @@ extern "C" HRESULT CorePlan( ExitOnFailure(hr, "Failed to plan the layout of the bundle."); // Plan the packages' layout. - hr = PlanPackages(&pEngineState->registration, &pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, FALSE, pEngineState->command.display, pEngineState->command.relationType, sczLayoutDirectory, &hSyncpointEvent); + hr = PlanPackages(&pEngineState->registration, &pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, FALSE, pEngineState->command.relationType, sczLayoutDirectory, &hSyncpointEvent); ExitOnFailure(hr, "Failed to plan packages."); } else if (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == action || BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED == action) @@ -440,7 +440,7 @@ extern "C" HRESULT CorePlan( pUpgradeBundlePackage = &pEngineState->update.package; - hr = PlanUpdateBundle(&pEngineState->userExperience, pUpgradeBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, &hSyncpointEvent); + hr = PlanUpdateBundle(&pEngineState->userExperience, pUpgradeBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.relationType, &hSyncpointEvent); ExitOnFailure(hr, "Failed to plan update."); } else if (pEngineState->registration.fEnabledForwardCompatibleBundle) @@ -449,7 +449,7 @@ extern "C" HRESULT CorePlan( pForwardCompatibleBundlePackage = &pEngineState->registration.forwardCompatibleBundle; - hr = PlanPassThroughBundle(&pEngineState->userExperience, pForwardCompatibleBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, &hSyncpointEvent); + hr = PlanPassThroughBundle(&pEngineState->userExperience, pForwardCompatibleBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.relationType, &hSyncpointEvent); ExitOnFailure(hr, "Failed to plan passthrough."); } else // doing an action that modifies the machine state. @@ -471,7 +471,7 @@ extern "C" HRESULT CorePlan( hr = PlanRelatedBundlesBegin(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->plan); ExitOnFailure(hr, "Failed to plan related bundles."); - hr = PlanPackages(&pEngineState->registration, &pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->registration.fInstalled, pEngineState->command.display, pEngineState->command.relationType, NULL, &hSyncpointEvent); + hr = PlanPackages(&pEngineState->registration, &pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->registration.fInstalled, pEngineState->command.relationType, NULL, &hSyncpointEvent); ExitOnFailure(hr, "Failed to plan packages."); // Schedule the update of related bundles last. diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp index 0b96c300..d0652270 100644 --- a/src/engine/elevation.cpp +++ b/src/engine/elevation.cpp @@ -821,9 +821,15 @@ extern "C" HRESULT ElevationExecuteMsiPackage( hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msiPackage.sczLogPath); ExitOnFailure(hr, "Failed to write package log to message buffer."); + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.actionMsiProperty); + ExitOnFailure(hr, "Failed to write actionMsiProperty to message buffer."); + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.uiLevel); ExitOnFailure(hr, "Failed to write UI level to message buffer."); + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.fDisableExternalUiHandler); + ExitOnFailure(hr, "Failed to write fDisableExternalUiHandler to message buffer."); + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.action); ExitOnFailure(hr, "Failed to write action to message buffer."); @@ -897,9 +903,15 @@ extern "C" HRESULT ElevationExecuteMspPackage( hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.sczLogPath); ExitOnFailure(hr, "Failed to write package log to message buffer."); + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.actionMsiProperty); + ExitOnFailure(hr, "Failed to write actionMsiProperty to message buffer."); + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.uiLevel); ExitOnFailure(hr, "Failed to write UI level to message buffer."); + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.fDisableExternalUiHandler); + ExitOnFailure(hr, "Failed to write fDisableExternalUiHandler to message buffer."); + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.action); ExitOnFailure(hr, "Failed to write action to message buffer."); @@ -2237,9 +2249,15 @@ static HRESULT OnExecuteMsiPackage( hr = BuffReadString(pbData, cbData, &iData, &executeAction.msiPackage.sczLogPath); ExitOnFailure(hr, "Failed to read package log."); + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.actionMsiProperty); + ExitOnFailure(hr, "Failed to read actionMsiProperty."); + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.uiLevel); ExitOnFailure(hr, "Failed to read UI level."); + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.fDisableExternalUiHandler); + ExitOnFailure(hr, "Failed to read fDisableExternalUiHandler."); + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.action); ExitOnFailure(hr, "Failed to read action."); @@ -2334,9 +2352,15 @@ static HRESULT OnExecuteMspPackage( hr = BuffReadString(pbData, cbData, &iData, &executeAction.mspTarget.sczLogPath); ExitOnFailure(hr, "Failed to read package log."); + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.actionMsiProperty); + ExitOnFailure(hr, "Failed to read actionMsiProperty."); + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.uiLevel); ExitOnFailure(hr, "Failed to read UI level."); + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.fDisableExternalUiHandler); + ExitOnFailure(hr, "Failed to read fDisableExternalUiHandler."); + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.action); ExitOnFailure(hr, "Failed to read action."); diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index 6fd9d64a..80b67b7d 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -2,8 +2,8 @@ - - + + @@ -166,8 +166,8 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index 17571ac5..3f89dde9 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -4,7 +4,10 @@ // constants - +#define BURNMSIINSTALL_PROPERTY_NAME L"BURNMSIINSTALL" +#define BURNMSIMODIFY_PROPERTY_NAME L"BURNMSIMODIFY" +#define BURNMSIREPAIR_PROPERTY_NAME L"BURNMSIREPAIR" +#define BURNMSIUNINSTALL_PROPERTY_NAME L"BURNMSIUNINSTALL" // structs @@ -79,10 +82,6 @@ extern "C" HRESULT MsiEngineParsePackageFromXml( hr = FileVersionFromStringEx(scz, 0, &pPackage->Msi.qwVersion); ExitOnFailure(hr, "Failed to parse @Version: %ls", scz); - // @DisplayInternalUI - hr = XmlGetYesNoAttribute(pixnMsiPackage, L"DisplayInternalUI", &pPackage->Msi.fDisplayInternalUI); - ExitOnFailure(hr, "Failed to get @DisplayInternalUI."); - // @UpgradeCode hr = XmlGetAttributeEx(pixnMsiPackage, L"UpgradeCode", &pPackage->Msi.sczUpgradeCode); if (E_NOTFOUND != hr) @@ -874,7 +873,7 @@ LExit: // PlanAdd - adds the calculated execute and rollback actions for the package. // extern "C" HRESULT MsiEnginePlanAddPackage( - __in BOOTSTRAPPER_DISPLAY display, + __in BURN_USER_EXPERIENCE* pUserExperience, __in BURN_PACKAGE* pPackage, __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, @@ -926,10 +925,13 @@ extern "C" HRESULT MsiEnginePlanAddPackage( pAction->type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE; pAction->msiPackage.pPackage = pPackage; pAction->msiPackage.action = pPackage->rollback; - pAction->msiPackage.uiLevel = MsiEngineCalculateInstallUiLevel(pPackage->Msi.fDisplayInternalUI, display, pAction->msiPackage.action); pAction->msiPackage.rgFeatures = rgRollbackFeatureActions; rgRollbackFeatureActions = NULL; + hr = MsiEngineCalculateInstallUiLevel(pUserExperience, pPackage->sczId, FALSE, pAction->msiPackage.action, + &pAction->msiPackage.actionMsiProperty, &pAction->msiPackage.uiLevel, &pAction->msiPackage.fDisableExternalUiHandler); + ExitOnFailure(hr, "Failed to get msi ui options."); + LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors. pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes; @@ -949,10 +951,13 @@ extern "C" HRESULT MsiEnginePlanAddPackage( pAction->type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE; pAction->msiPackage.pPackage = pPackage; pAction->msiPackage.action = pPackage->execute; - pAction->msiPackage.uiLevel = MsiEngineCalculateInstallUiLevel(pPackage->Msi.fDisplayInternalUI, display, pAction->msiPackage.action); pAction->msiPackage.rgFeatures = rgFeatureActions; rgFeatureActions = NULL; + hr = MsiEngineCalculateInstallUiLevel(pUserExperience, pPackage->sczId, TRUE, pAction->msiPackage.action, + &pAction->msiPackage.actionMsiProperty, &pAction->msiPackage.uiLevel, &pAction->msiPackage.fDisableExternalUiHandler); + ExitOnFailure(hr, "Failed to get msi ui options."); + LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors. pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes; } @@ -1073,7 +1078,6 @@ extern "C" HRESULT MsiEngineAddCompatiblePackage( } pCompatiblePackage->type = BURN_PACKAGE_TYPE_MSI; - pCompatiblePackage->Msi.fDisplayInternalUI = pPackage->Msi.fDisplayInternalUI; if (ppCompatiblePackage) { @@ -1161,8 +1165,16 @@ extern "C" HRESULT MsiEngineExecutePackage( VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->msiPackage.action, TRUE); // Wire up the external UI handler and logging. - hr = WiuInitializeExternalUI(pfnMessageHandler, pExecuteAction->msiPackage.uiLevel, hwndParent, pvContext, fRollback, &context); - ExitOnFailure(hr, "Failed to initialize external UI handler."); + if (pExecuteAction->msiPackage.fDisableExternalUiHandler) + { + hr = WiuInitializeInternalUI(pExecuteAction->msiPackage.uiLevel, hwndParent, &context); + ExitOnFailure(hr, "Failed to initialize internal UI for MSI package."); + } + else + { + hr = WiuInitializeExternalUI(pfnMessageHandler, pExecuteAction->msiPackage.uiLevel, hwndParent, pvContext, fRollback, &context); + ExitOnFailure(hr, "Failed to initialize external UI handler."); + } if (pExecuteAction->msiPackage.sczLogPath && *pExecuteAction->msiPackage.sczLogPath) { @@ -1191,6 +1203,12 @@ extern "C" HRESULT MsiEngineExecutePackage( hr = ConcatPatchProperty(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgSlipstreamPatches, &sczObfuscatedProperties); ExitOnFailure(hr, "Failed to add patch properties to obfuscated argument string."); + hr = MsiEngineConcatActionProperty(pExecuteAction->msiPackage.actionMsiProperty, &sczProperties); + ExitOnFailure(hr, "Failed to add action property to argument string."); + + hr = MsiEngineConcatActionProperty(pExecuteAction->msiPackage.actionMsiProperty, &sczObfuscatedProperties); + ExitOnFailure(hr, "Failed to add action property to obfuscated argument string."); + LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), sczMsiPath, sczObfuscatedProperties ? sczObfuscatedProperties : L""); // @@ -1300,6 +1318,40 @@ LExit: return hr; } +extern "C" HRESULT MsiEngineConcatActionProperty( + __in BURN_MSI_PROPERTY actionMsiProperty, + __deref_out_z LPWSTR* psczProperties + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzPropertyName = NULL; + + switch (actionMsiProperty) + { + case BURN_MSI_PROPERTY_INSTALL: + wzPropertyName = BURNMSIINSTALL_PROPERTY_NAME; + break; + case BURN_MSI_PROPERTY_MODIFY: + wzPropertyName = BURNMSIMODIFY_PROPERTY_NAME; + break; + case BURN_MSI_PROPERTY_REPAIR: + wzPropertyName = BURNMSIREPAIR_PROPERTY_NAME; + break; + case BURN_MSI_PROPERTY_UNINSTALL: + wzPropertyName = BURNMSIUNINSTALL_PROPERTY_NAME; + break; + } + + if (wzPropertyName) + { + hr = StrAllocConcatFormattedSecure(psczProperties, L" %ls=1", wzPropertyName); + ExitOnFailure(hr, "Failed to add burn action property."); + } + +LExit: + return hr; +} + // The contents of psczProperties may be sensitive, should keep encrypted and SecureZeroFree. extern "C" HRESULT MsiEngineConcatProperties( __in_ecount(cProperties) BURN_MSIPROPERTY* rgProperties, @@ -1363,31 +1415,36 @@ LExit: return hr; } -extern "C" INSTALLUILEVEL MsiEngineCalculateInstallUiLevel( - __in BOOL fDisplayInternalUI, - __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_ACTION_STATE actionState +extern "C" HRESULT MsiEngineCalculateInstallUiLevel( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzPackageId, + __in BOOL fExecute, + __in BOOTSTRAPPER_ACTION_STATE actionState, + __out BURN_MSI_PROPERTY* pActionMsiProperty, + __out INSTALLUILEVEL* pUiLevel, + __out BOOL* pfDisableExternalUiHandler ) { - // Assume there will be no internal UI displayed. - INSTALLUILEVEL uiLevel = static_cast(INSTALLUILEVEL_NONE | INSTALLUILEVEL_SOURCERESONLY); + *pUiLevel = static_cast(INSTALLUILEVEL_NONE | INSTALLUILEVEL_SOURCERESONLY); + *pfDisableExternalUiHandler = FALSE; - // suppress internal UI during uninstall to mimic ARP and "msiexec /x" behavior - if (fDisplayInternalUI && BOOTSTRAPPER_ACTION_STATE_UNINSTALL != actionState && BOOTSTRAPPER_ACTION_STATE_REPAIR != actionState) + switch (actionState) { - switch (display) - { - case BOOTSTRAPPER_DISPLAY_FULL: - uiLevel = INSTALLUILEVEL_FULL; - break; - - case BOOTSTRAPPER_DISPLAY_PASSIVE: - uiLevel = INSTALLUILEVEL_REDUCED; - break; - } + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: + *pActionMsiProperty = BURN_MSI_PROPERTY_UNINSTALL; + break; + case BOOTSTRAPPER_ACTION_STATE_REPAIR: + *pActionMsiProperty = BURN_MSI_PROPERTY_REPAIR; + break; + case BOOTSTRAPPER_ACTION_STATE_MODIFY: + *pActionMsiProperty = BURN_MSI_PROPERTY_MODIFY; + break; + default: + *pActionMsiProperty = BURN_MSI_PROPERTY_INSTALL; + break; } - return uiLevel; + return UserExperienceOnPlanMsiPackage(pUserExperience, wzPackageId, fExecute, actionState, pActionMsiProperty, pUiLevel, pfDisableExternalUiHandler); } diff --git a/src/engine/msiengine.h b/src/engine/msiengine.h index 2a8ebfd5..cbe80262 100644 --- a/src/engine/msiengine.h +++ b/src/engine/msiengine.h @@ -32,7 +32,7 @@ HRESULT MsiEnginePlanCalculatePackage( __out_opt BOOL* pfBARequestedCache ); HRESULT MsiEnginePlanAddPackage( - __in BOOTSTRAPPER_DISPLAY display, + __in BURN_USER_EXPERIENCE* pUserExperience, __in BURN_PACKAGE* pPackage, __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, @@ -54,6 +54,10 @@ HRESULT MsiEngineExecutePackage( __in LPVOID pvContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ); +HRESULT MsiEngineConcatActionProperty( + __in BURN_MSI_PROPERTY actionMsiProperty, + __deref_out_z LPWSTR* psczProperties + ); HRESULT MsiEngineConcatProperties( __in_ecount(cProperties) BURN_MSIPROPERTY* rgProperties, __in DWORD cProperties, @@ -62,10 +66,14 @@ HRESULT MsiEngineConcatProperties( __deref_out_z LPWSTR* psczProperties, __in BOOL fObfuscateHiddenVariables ); -INSTALLUILEVEL MsiEngineCalculateInstallUiLevel( - __in BOOL fDisplayInternalUI, - __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_ACTION_STATE actionState +HRESULT MsiEngineCalculateInstallUiLevel( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzPackageId, + __in BOOL fExecute, + __in BOOTSTRAPPER_ACTION_STATE actionState, + __out BURN_MSI_PROPERTY* pActionMsiProperty, + __out INSTALLUILEVEL* pUiLevel, + __out BOOL* pfDisableExternalUiHandler ); #if defined(__cplusplus) diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp index 463799e6..0dddb366 100644 --- a/src/engine/mspengine.cpp +++ b/src/engine/mspengine.cpp @@ -44,7 +44,7 @@ static void DeterminePatchChainedTarget( __out BOOL* pfSlipstreamed ); static HRESULT PlanTargetProduct( - __in BOOTSTRAPPER_DISPLAY display, + __in BURN_USER_EXPERIENCE* pUserExperience, __in BOOL fRollback, __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, @@ -73,10 +73,6 @@ extern "C" HRESULT MspEngineParsePackageFromXml( hr = XmlGetAttributeEx(pixnMspPackage, L"PatchXml", &pPackage->Msp.sczApplicabilityXml); ExitOnFailure(hr, "Failed to get @PatchXml."); - // @DisplayInternalUI - hr = XmlGetYesNoAttribute(pixnMspPackage, L"DisplayInternalUI", &pPackage->Msp.fDisplayInternalUI); - ExitOnFailure(hr, "Failed to get @DisplayInternalUI."); - // Read properties. hr = MsiEngineParsePropertiesFromXml(pixnMspPackage, &pPackage->Msp.rgProperties, &pPackage->Msp.cProperties); ExitOnFailure(hr, "Failed to parse properties from XML."); @@ -400,7 +396,7 @@ LExit: // PlanAdd - adds the calculated execute and rollback actions for the package. // extern "C" HRESULT MspEnginePlanAddPackage( - __in BOOTSTRAPPER_DISPLAY display, + __in BURN_USER_EXPERIENCE* pUserExperience, __in BURN_PACKAGE* pPackage, __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, @@ -437,13 +433,13 @@ extern "C" HRESULT MspEnginePlanAddPackage( if (BOOTSTRAPPER_ACTION_STATE_NONE != pTargetProduct->execute) { - hr = PlanTargetProduct(display, FALSE, pPlan, pLog, pVariables, pTargetProduct->execute, pPackage, pTargetProduct, hCacheEvent); + hr = PlanTargetProduct(pUserExperience, FALSE, pPlan, pLog, pVariables, pTargetProduct->execute, pPackage, pTargetProduct, hCacheEvent); ExitOnFailure(hr, "Failed to plan target product."); } if (BOOTSTRAPPER_ACTION_STATE_NONE != pTargetProduct->rollback) { - hr = PlanTargetProduct(display, TRUE, pPlan, pLog, pVariables, pTargetProduct->rollback, pPackage, pTargetProduct, hCacheEvent); + hr = PlanTargetProduct(pUserExperience, TRUE, pPlan, pLog, pVariables, pTargetProduct->rollback, pPackage, pTargetProduct, hCacheEvent); ExitOnFailure(hr, "Failed to plan rollack target product."); } } @@ -464,7 +460,6 @@ extern "C" HRESULT MspEngineExecutePackage( ) { HRESULT hr = S_OK; - INSTALLUILEVEL uiLevel = pExecuteAction->mspTarget.pPackage->Msp.fDisplayInternalUI ? INSTALLUILEVEL_DEFAULT : static_cast(INSTALLUILEVEL_NONE | INSTALLUILEVEL_SOURCERESONLY); WIU_MSI_EXECUTE_CONTEXT context = { }; WIU_RESTART restart = WIU_RESTART_NONE; @@ -517,8 +512,16 @@ extern "C" HRESULT MspEngineExecutePackage( VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->mspTarget.action, TRUE); // Wire up the external UI handler and logging. - hr = WiuInitializeExternalUI(pfnMessageHandler, uiLevel, hwndParent, pvContext, fRollback, &context); - ExitOnFailure(hr, "Failed to initialize external UI handler."); + if (pExecuteAction->mspTarget.fDisableExternalUiHandler) + { + hr = WiuInitializeInternalUI(pExecuteAction->mspTarget.uiLevel, hwndParent, &context); + ExitOnFailure(hr, "Failed to initialize internal UI for MSP package."); + } + else + { + hr = WiuInitializeExternalUI(pfnMessageHandler, pExecuteAction->mspTarget.uiLevel, hwndParent, pvContext, fRollback, &context); + ExitOnFailure(hr, "Failed to initialize external UI handler."); + } //if (BURN_LOGGING_LEVEL_DEBUG == logLevel) //{ @@ -538,6 +541,12 @@ extern "C" HRESULT MspEngineExecutePackage( hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE); ExitOnFailure(hr, "Failed to add properties to obfuscated argument string."); + hr = MsiEngineConcatActionProperty(pExecuteAction->mspTarget.actionMsiProperty, &sczProperties); + ExitOnFailure(hr, "Failed to add action property to argument string."); + + hr = MsiEngineConcatActionProperty(pExecuteAction->mspTarget.actionMsiProperty, &sczObfuscatedProperties); + ExitOnFailure(hr, "Failed to add action property to obfuscated argument string."); + LogId(REPORT_STANDARD, MSG_APPLYING_PATCH_PACKAGE, pExecuteAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pExecuteAction->mspTarget.action), sczPatches, sczObfuscatedProperties, pExecuteAction->mspTarget.sczTargetProductCode); // @@ -868,7 +877,7 @@ static void DeterminePatchChainedTarget( } static HRESULT PlanTargetProduct( - __in BOOTSTRAPPER_DISPLAY display, + __in BURN_USER_EXPERIENCE* pUserExperience, __in BOOL fRollback, __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, @@ -920,12 +929,15 @@ static HRESULT PlanTargetProduct( pAction->mspTarget.action = actionState; pAction->mspTarget.pPackage = pPackage; pAction->mspTarget.fPerMachineTarget = (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context); - pAction->mspTarget.uiLevel = MsiEngineCalculateInstallUiLevel(pPackage->Msp.fDisplayInternalUI, display, pAction->mspTarget.action); pAction->mspTarget.pChainedTargetPackage = pTargetProduct->pChainedTargetPackage; pAction->mspTarget.fSlipstream = pTargetProduct->fSlipstream; hr = StrAllocString(&pAction->mspTarget.sczTargetProductCode, pTargetProduct->wzTargetProductCode, 0); ExitOnFailure(hr, "Failed to copy target product code."); + hr = MsiEngineCalculateInstallUiLevel(pUserExperience, pPackage->sczId, !fRollback, pAction->mspTarget.action, + &pAction->mspTarget.actionMsiProperty, &pAction->mspTarget.uiLevel, &pAction->mspTarget.fDisableExternalUiHandler); + ExitOnFailure(hr, "Failed to get msp ui options."); + // If this is a per-machine target product, then the plan needs to be per-machine as well. if (pAction->mspTarget.fPerMachineTarget) { diff --git a/src/engine/mspengine.h b/src/engine/mspengine.h index 9f585720..0f323e57 100644 --- a/src/engine/mspengine.h +++ b/src/engine/mspengine.h @@ -38,7 +38,7 @@ HRESULT MspEnginePlanCalculatePackage( __out_opt BOOL* pfBARequestedCache ); HRESULT MspEnginePlanAddPackage( - __in BOOTSTRAPPER_DISPLAY display, + __in BURN_USER_EXPERIENCE* pUserExperience, __in BURN_PACKAGE* pPackage, __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, diff --git a/src/engine/package.h b/src/engine/package.h index c295378e..05965a16 100644 --- a/src/engine/package.h +++ b/src/engine/package.h @@ -239,7 +239,6 @@ typedef struct _BURN_PACKAGE DWORD64 qwVersion; LPWSTR sczInstalledProductCode; DWORD64 qwInstalledVersion; - BOOL fDisplayInternalUI; LPWSTR sczUpgradeCode; BURN_MSIPROPERTY* rgProperties; @@ -261,7 +260,6 @@ typedef struct _BURN_PACKAGE { LPWSTR sczPatchCode; LPWSTR sczApplicabilityXml; - BOOL fDisplayInternalUI; BURN_MSIPROPERTY* rgProperties; DWORD cProperties; diff --git a/src/engine/packages.config b/src/engine/packages.config index e9b2d190..251df9d0 100644 --- a/src/engine/packages.config +++ b/src/engine/packages.config @@ -1,6 +1,6 @@  - - + + \ No newline at end of file diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index 01c7a31d..02f5be23 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -31,7 +31,6 @@ static HRESULT ProcessPackage( __in BURN_PACKAGE* pPackage, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z_opt LPCWSTR wzLayoutDirectory, __inout HANDLE* phSyncpointEvent, @@ -473,7 +472,6 @@ extern "C" HRESULT PlanPackages( __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, __in BOOL fBundleInstalled, - __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z_opt LPCWSTR wzLayoutDirectory, __inout HANDLE* phSyncpointEvent @@ -511,7 +509,7 @@ extern "C" HRESULT PlanPackages( } } - hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices); + hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices); ExitOnFailure(hr, "Failed to process package."); // Attempt to remove orphaned packages during uninstall. Currently only MSI packages are supported and should not require source. @@ -536,7 +534,7 @@ extern "C" HRESULT PlanPackages( ExitOnFailure(hr, "Failed to copy installed ProductCode"); // Process the compatible MSI package like any other. - hr = ProcessPackage(fBundlePerMachine, pPackage, pUX, pPlan, pCompatiblePackage, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices); + hr = ProcessPackage(fBundlePerMachine, pPackage, pUX, pPlan, pCompatiblePackage, pLog, pVariables, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices); ExitOnFailure(hr, "Failed to process compatible package."); if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pCompatiblePackage->execute) @@ -787,7 +785,6 @@ extern "C" HRESULT PlanPassThroughBundle( __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, __inout HANDLE* phSyncpointEvent ) @@ -797,7 +794,7 @@ extern "C" HRESULT PlanPassThroughBundle( BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; // Plan passthrough package. - hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL); + hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL); ExitOnFailure(hr, "Failed to process passthrough package."); // If we still have an open rollback boundary, complete it. @@ -821,7 +818,6 @@ extern "C" HRESULT PlanUpdateBundle( __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, __inout HANDLE* phSyncpointEvent ) @@ -831,7 +827,7 @@ extern "C" HRESULT PlanUpdateBundle( BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; // Plan update package. - hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL); + hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL); ExitOnFailure(hr, "Failed to process update package."); // If we still have an open rollback boundary, complete it. @@ -857,7 +853,6 @@ static HRESULT ProcessPackage( __in BURN_PACKAGE* pPackage, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z_opt LPCWSTR wzLayoutDirectory, __inout HANDLE* phSyncpointEvent, @@ -911,7 +906,7 @@ static HRESULT ProcessPackage( } } - hr = PlanExecutePackage(fBundlePerMachine, display, pUX, pPlan, pPackage, pLog, pVariables, phSyncpointEvent); + hr = PlanExecutePackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, phSyncpointEvent); ExitOnFailure(hr, "Failed to plan execute package."); if (pPackage->fUninstallable && pNonpermanentPackageIndices) @@ -1091,7 +1086,6 @@ LExit: extern "C" HRESULT PlanExecutePackage( __in BOOL fPerMachine, - __in BOOTSTRAPPER_DISPLAY display, __in BURN_USER_EXPERIENCE* pUserExperience, __in BURN_PLAN* pPlan, __in BURN_PACKAGE* pPackage, @@ -1156,11 +1150,11 @@ extern "C" HRESULT PlanExecutePackage( break; case BURN_PACKAGE_TYPE_MSI: - hr = MsiEnginePlanAddPackage(display, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire); + hr = MsiEnginePlanAddPackage(pUserExperience, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire); break; case BURN_PACKAGE_TYPE_MSP: - hr = MspEnginePlanAddPackage(display, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire); + hr = MspEnginePlanAddPackage(pUserExperience, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire); break; case BURN_PACKAGE_TYPE_MSU: @@ -3074,7 +3068,7 @@ static void ExecuteActionLog( break; case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSI_PACKAGE package id: %ls, action: %hs, ui level: %u, log path: %ls, logging attrib: %u", wzBase, iAction, pAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pAction->msiPackage.action), pAction->msiPackage.uiLevel, pAction->msiPackage.sczLogPath, pAction->msiPackage.dwLoggingAttributes); + LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSI_PACKAGE package id: %ls, action: %hs, action msi property: %u, ui level: %u, disable externaluihandler: %ls, log path: %ls, logging attrib: %u", wzBase, iAction, pAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pAction->msiPackage.action), pAction->msiPackage.actionMsiProperty, pAction->msiPackage.uiLevel, pAction->msiPackage.fDisableExternalUiHandler ? L"yes" : L"no", pAction->msiPackage.sczLogPath, pAction->msiPackage.dwLoggingAttributes); for (DWORD j = 0; j < pAction->msiPackage.cPatches; ++j) { LogStringLine(REPORT_STANDARD, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->msiPackage.rgOrderedPatches->dwOrder, pAction->msiPackage.rgOrderedPatches[j].dwOrder, pAction->msiPackage.rgOrderedPatches[j].pPackage->sczId); @@ -3082,7 +3076,7 @@ static void ExecuteActionLog( break; case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSP_TARGET package id: %ls, action: %hs, target product code: %ls, target per-machine: %ls, ui level: %u, log path: %ls", wzBase, iAction, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.sczTargetProductCode, pAction->mspTarget.fPerMachineTarget ? L"yes" : L"no", pAction->mspTarget.uiLevel, pAction->mspTarget.sczLogPath); + LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSP_TARGET package id: %ls, action: %hs, target product code: %ls, target per-machine: %ls, action msi property: %u, ui level: %u, disable externaluihandler: %ls, log path: %ls", wzBase, iAction, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.sczTargetProductCode, pAction->mspTarget.fPerMachineTarget ? L"yes" : L"no", pAction->mspTarget.actionMsiProperty, pAction->mspTarget.uiLevel, pAction->mspTarget.fDisableExternalUiHandler ? L"yes" : L"no", pAction->mspTarget.sczLogPath); for (DWORD j = 0; j < pAction->mspTarget.cOrderedPatches; ++j) { LogStringLine(REPORT_STANDARD, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->mspTarget.rgOrderedPatches[j].dwOrder, pAction->mspTarget.rgOrderedPatches[j].pPackage->sczId); diff --git a/src/engine/plan.h b/src/engine/plan.h index 54cfe59d..89f4b4bf 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -245,7 +245,9 @@ typedef struct _BURN_EXECUTE_ACTION BURN_PACKAGE* pPackage; LPWSTR sczLogPath; DWORD dwLoggingAttributes; + BURN_MSI_PROPERTY actionMsiProperty; INSTALLUILEVEL uiLevel; + BOOL fDisableExternalUiHandler; BOOTSTRAPPER_ACTION_STATE action; BOOTSTRAPPER_FEATURE_ACTION* rgFeatures; @@ -262,7 +264,9 @@ typedef struct _BURN_EXECUTE_ACTION BOOL fSlipstream; BOOL fPerMachineTarget; LPWSTR sczLogPath; + BURN_MSI_PROPERTY actionMsiProperty; INSTALLUILEVEL uiLevel; + BOOL fDisableExternalUiHandler; BOOTSTRAPPER_ACTION_STATE action; BURN_ORDERED_PATCHES* rgOrderedPatches; @@ -404,7 +408,6 @@ HRESULT PlanPackages( __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, __in BOOL fBundleInstalled, - __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z_opt LPCWSTR wzLayoutDirectory, __inout HANDLE* phSyncpointEvent @@ -423,7 +426,6 @@ HRESULT PlanPassThroughBundle( __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, __inout HANDLE* phSyncpointEvent ); @@ -433,7 +435,6 @@ HRESULT PlanUpdateBundle( __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, __inout HANDLE* phSyncpointEvent ); @@ -452,7 +453,6 @@ HRESULT PlanCachePackage( ); HRESULT PlanExecutePackage( __in BOOL fPerMachine, - __in BOOTSTRAPPER_DISPLAY display, __in BURN_USER_EXPERIENCE* pUserExperience, __in BURN_PLAN* pPlan, __in BURN_PACKAGE* pPackage, diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index 24fc1ec7..dd59a431 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -97,7 +97,7 @@ extern "C" HRESULT UserExperienceLoad( args.pCommand = pCommand; args.pfnBootstrapperEngineProc = EngineForApplicationProc; args.pvBootstrapperEngineProcContext = pEngineContext; - args.qwEngineAPIVersion = MAKEQWORDVERSION(0, 0, 0, 7); // TODO: need to decide whether to keep this, and if so when to update it. + args.qwEngineAPIVersion = MAKEQWORDVERSION(2020, 5, 14, 0); results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS); @@ -1211,7 +1211,9 @@ EXTERN_C BAAPI UserExperienceOnExecutePackageBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, __in BOOL fExecute, - __in BOOTSTRAPPER_ACTION_STATE action + __in BOOTSTRAPPER_ACTION_STATE action, + __in INSTALLUILEVEL uiLevel, + __in BOOL fDisableExternalUiHandler ) { HRESULT hr = S_OK; @@ -1222,6 +1224,8 @@ EXTERN_C BAAPI UserExperienceOnExecutePackageBegin( args.wzPackageId = wzPackageId; args.fExecute = fExecute; args.action = action; + args.uiLevel = uiLevel; + args.fDisableExternalUiHandler = fDisableExternalUiHandler; results.cbSize = sizeof(results); @@ -1526,6 +1530,45 @@ LExit: return hr; } +EXTERN_C BAAPI UserExperienceOnPlanMsiPackage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in BOOL fExecute, + __in BOOTSTRAPPER_ACTION_STATE action, + __inout BURN_MSI_PROPERTY* pActionMsiProperty, + __inout INSTALLUILEVEL* pUiLevel, + __inout BOOL* pfDisableExternalUiHandler + ) +{ + HRESULT hr = S_OK; + BA_ONPLANMSIPACKAGE_ARGS args = { }; + BA_ONPLANMSIPACKAGE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.fExecute = fExecute; + args.action = action; + + results.cbSize = sizeof(results); + results.actionMsiProperty = *pActionMsiProperty; + results.uiLevel = *pUiLevel; + results.fDisableExternalUiHandler = *pfDisableExternalUiHandler; + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnPlanMsiPackage failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + *pActionMsiProperty = results.actionMsiProperty; + *pUiLevel = results.uiLevel; + *pfDisableExternalUiHandler = results.fDisableExternalUiHandler; + +LExit: + return hr; +} + EXTERN_C BAAPI UserExperienceOnPlanPackageBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index 7d2b743d..a5a97ffa 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -294,7 +294,9 @@ BAAPI UserExperienceOnExecutePackageBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, __in BOOL fExecute, - __in BOOTSTRAPPER_ACTION_STATE action + __in BOOTSTRAPPER_ACTION_STATE action, + __in INSTALLUILEVEL uiLevel, + __in BOOL fDisableExternalUiHandler ); BAAPI UserExperienceOnExecutePackageComplete( __in BURN_USER_EXPERIENCE* pUserExperience, @@ -354,6 +356,15 @@ BAAPI UserExperienceOnPlanMsiFeature( __in_z LPCWSTR wzFeatureId, __inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState ); +BAAPI UserExperienceOnPlanMsiPackage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in BOOL fExecute, + __in BOOTSTRAPPER_ACTION_STATE action, + __inout BURN_MSI_PROPERTY* pActionMsiProperty, + __inout INSTALLUILEVEL* pUiLevel, + __inout BOOL* pfDisableExternalUiHandler + ); BAAPI UserExperienceOnPlanPackageBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, diff --git a/src/stub/packages.config b/src/stub/packages.config index 478af2c6..e8115bfa 100644 --- a/src/stub/packages.config +++ b/src/stub/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index 7af42fa9..ce43e0e6 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -2,7 +2,7 @@ - + @@ -94,6 +94,6 @@ This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}. - + -- cgit v1.2.3-55-g6feb From c903a96bd8d61a375448e1a6ad7b40bab8cb24c4 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Thu, 21 May 2020 13:15:58 +1000 Subject: WIXBUG:3869 FilterResult needs to apply MB_TYPEMASK to dwAllowedResults. --- src/engine/userexperience.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index dd59a431..6b0e3bf5 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -1973,12 +1973,13 @@ static int FilterResult( __in int nResult ) { + DWORD dwFilteredAllowedResults = dwAllowedResults & MB_TYPEMASK; if (IDNOACTION == nResult || IDERROR == nResult) // do nothing and errors pass through. { } else { - switch (dwAllowedResults) + switch (dwFilteredAllowedResults) { case MB_OK: nResult = IDOK; -- cgit v1.2.3-55-g6feb From 513286d4798572cc82a78554ef15ef2fe79f407e Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Thu, 21 May 2020 14:13:16 +1000 Subject: WIXBUG:5980 Fix bugs around INSTALLUILEVEL_SOURCERESONLY. Apply requires a valid hWnd since otherwise a source resolution prompt could hang the bundle. Burn now defaults to INSTALLUILEVEL_NONE by itself if the bundle is not showing UI. --- src/engine/EngineForApplication.cpp | 6 ++++++ src/engine/core.cpp | 8 ++++---- src/engine/msiengine.cpp | 14 +++++++++++--- src/engine/msiengine.h | 2 ++ src/engine/mspengine.cpp | 9 ++++++--- src/engine/mspengine.h | 1 + src/engine/plan.cpp | 20 +++++++++++++------- src/engine/plan.h | 4 ++++ 8 files changed, 47 insertions(+), 17 deletions(-) diff --git a/src/engine/EngineForApplication.cpp b/src/engine/EngineForApplication.cpp index c3600c7b..e559b438 100644 --- a/src/engine/EngineForApplication.cpp +++ b/src/engine/EngineForApplication.cpp @@ -709,6 +709,12 @@ static HRESULT BAEngineApply( { HRESULT hr = S_OK; + ExitOnNull(pArgs->hwndParent, hr, E_INVALIDARG, "BA passed NULL hwndParent to Apply."); + if (!::IsWindow(pArgs->hwndParent)) + { + ExitOnFailure(hr = E_INVALIDARG, "BA passed invalid hwndParent to Apply."); + } + if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_APPLY, 0, reinterpret_cast(pArgs->hwndParent))) { ExitWithLastError(hr, "Failed to post apply message."); diff --git a/src/engine/core.cpp b/src/engine/core.cpp index c153f162..26e74588 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -431,7 +431,7 @@ extern "C" HRESULT CorePlan( ExitOnFailure(hr, "Failed to plan the layout of the bundle."); // Plan the packages' layout. - hr = PlanPackages(&pEngineState->registration, &pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, FALSE, pEngineState->command.relationType, sczLayoutDirectory, &hSyncpointEvent); + hr = PlanPackages(&pEngineState->registration, &pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, FALSE, pEngineState->command.display, pEngineState->command.relationType, sczLayoutDirectory, &hSyncpointEvent); ExitOnFailure(hr, "Failed to plan packages."); } else if (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == action || BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED == action) @@ -440,7 +440,7 @@ extern "C" HRESULT CorePlan( pUpgradeBundlePackage = &pEngineState->update.package; - hr = PlanUpdateBundle(&pEngineState->userExperience, pUpgradeBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.relationType, &hSyncpointEvent); + hr = PlanUpdateBundle(&pEngineState->userExperience, pUpgradeBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, &hSyncpointEvent); ExitOnFailure(hr, "Failed to plan update."); } else if (pEngineState->registration.fEnabledForwardCompatibleBundle) @@ -449,7 +449,7 @@ extern "C" HRESULT CorePlan( pForwardCompatibleBundlePackage = &pEngineState->registration.forwardCompatibleBundle; - hr = PlanPassThroughBundle(&pEngineState->userExperience, pForwardCompatibleBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.relationType, &hSyncpointEvent); + hr = PlanPassThroughBundle(&pEngineState->userExperience, pForwardCompatibleBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, &hSyncpointEvent); ExitOnFailure(hr, "Failed to plan passthrough."); } else // doing an action that modifies the machine state. @@ -471,7 +471,7 @@ extern "C" HRESULT CorePlan( hr = PlanRelatedBundlesBegin(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->plan); ExitOnFailure(hr, "Failed to plan related bundles."); - hr = PlanPackages(&pEngineState->registration, &pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->registration.fInstalled, pEngineState->command.relationType, NULL, &hSyncpointEvent); + hr = PlanPackages(&pEngineState->registration, &pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->registration.fInstalled, pEngineState->command.display, pEngineState->command.relationType, NULL, &hSyncpointEvent); ExitOnFailure(hr, "Failed to plan packages."); // Schedule the update of related bundles last. diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index 3f89dde9..8b8121c1 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -873,6 +873,7 @@ LExit: // PlanAdd - adds the calculated execute and rollback actions for the package. // extern "C" HRESULT MsiEnginePlanAddPackage( + __in BOOTSTRAPPER_DISPLAY display, __in BURN_USER_EXPERIENCE* pUserExperience, __in BURN_PACKAGE* pPackage, __in BURN_PLAN* pPlan, @@ -928,7 +929,7 @@ extern "C" HRESULT MsiEnginePlanAddPackage( pAction->msiPackage.rgFeatures = rgRollbackFeatureActions; rgRollbackFeatureActions = NULL; - hr = MsiEngineCalculateInstallUiLevel(pUserExperience, pPackage->sczId, FALSE, pAction->msiPackage.action, + hr = MsiEngineCalculateInstallUiLevel(display, pUserExperience, pPackage->sczId, FALSE, pAction->msiPackage.action, &pAction->msiPackage.actionMsiProperty, &pAction->msiPackage.uiLevel, &pAction->msiPackage.fDisableExternalUiHandler); ExitOnFailure(hr, "Failed to get msi ui options."); @@ -954,7 +955,7 @@ extern "C" HRESULT MsiEnginePlanAddPackage( pAction->msiPackage.rgFeatures = rgFeatureActions; rgFeatureActions = NULL; - hr = MsiEngineCalculateInstallUiLevel(pUserExperience, pPackage->sczId, TRUE, pAction->msiPackage.action, + hr = MsiEngineCalculateInstallUiLevel(display, pUserExperience, pPackage->sczId, TRUE, pAction->msiPackage.action, &pAction->msiPackage.actionMsiProperty, &pAction->msiPackage.uiLevel, &pAction->msiPackage.fDisableExternalUiHandler); ExitOnFailure(hr, "Failed to get msi ui options."); @@ -1416,6 +1417,7 @@ LExit: } extern "C" HRESULT MsiEngineCalculateInstallUiLevel( + __in BOOTSTRAPPER_DISPLAY display, __in BURN_USER_EXPERIENCE* pUserExperience, __in LPCWSTR wzPackageId, __in BOOL fExecute, @@ -1425,9 +1427,15 @@ extern "C" HRESULT MsiEngineCalculateInstallUiLevel( __out BOOL* pfDisableExternalUiHandler ) { - *pUiLevel = static_cast(INSTALLUILEVEL_NONE | INSTALLUILEVEL_SOURCERESONLY); + *pUiLevel = INSTALLUILEVEL_NONE; *pfDisableExternalUiHandler = FALSE; + if (BOOTSTRAPPER_DISPLAY_FULL == display || + BOOTSTRAPPER_DISPLAY_PASSIVE == display) + { + *pUiLevel = static_cast(*pUiLevel | INSTALLUILEVEL_SOURCERESONLY); + } + switch (actionState) { case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: diff --git a/src/engine/msiengine.h b/src/engine/msiengine.h index cbe80262..04c375c2 100644 --- a/src/engine/msiengine.h +++ b/src/engine/msiengine.h @@ -32,6 +32,7 @@ HRESULT MsiEnginePlanCalculatePackage( __out_opt BOOL* pfBARequestedCache ); HRESULT MsiEnginePlanAddPackage( + __in BOOTSTRAPPER_DISPLAY display, __in BURN_USER_EXPERIENCE* pUserExperience, __in BURN_PACKAGE* pPackage, __in BURN_PLAN* pPlan, @@ -67,6 +68,7 @@ HRESULT MsiEngineConcatProperties( __in BOOL fObfuscateHiddenVariables ); HRESULT MsiEngineCalculateInstallUiLevel( + __in BOOTSTRAPPER_DISPLAY display, __in BURN_USER_EXPERIENCE* pUserExperience, __in LPCWSTR wzPackageId, __in BOOL fExecute, diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp index 0dddb366..57321d75 100644 --- a/src/engine/mspengine.cpp +++ b/src/engine/mspengine.cpp @@ -44,6 +44,7 @@ static void DeterminePatchChainedTarget( __out BOOL* pfSlipstreamed ); static HRESULT PlanTargetProduct( + __in BOOTSTRAPPER_DISPLAY display, __in BURN_USER_EXPERIENCE* pUserExperience, __in BOOL fRollback, __in BURN_PLAN* pPlan, @@ -396,6 +397,7 @@ LExit: // PlanAdd - adds the calculated execute and rollback actions for the package. // extern "C" HRESULT MspEnginePlanAddPackage( + __in BOOTSTRAPPER_DISPLAY display, __in BURN_USER_EXPERIENCE* pUserExperience, __in BURN_PACKAGE* pPackage, __in BURN_PLAN* pPlan, @@ -433,13 +435,13 @@ extern "C" HRESULT MspEnginePlanAddPackage( if (BOOTSTRAPPER_ACTION_STATE_NONE != pTargetProduct->execute) { - hr = PlanTargetProduct(pUserExperience, FALSE, pPlan, pLog, pVariables, pTargetProduct->execute, pPackage, pTargetProduct, hCacheEvent); + hr = PlanTargetProduct(display, pUserExperience, FALSE, pPlan, pLog, pVariables, pTargetProduct->execute, pPackage, pTargetProduct, hCacheEvent); ExitOnFailure(hr, "Failed to plan target product."); } if (BOOTSTRAPPER_ACTION_STATE_NONE != pTargetProduct->rollback) { - hr = PlanTargetProduct(pUserExperience, TRUE, pPlan, pLog, pVariables, pTargetProduct->rollback, pPackage, pTargetProduct, hCacheEvent); + hr = PlanTargetProduct(display, pUserExperience, TRUE, pPlan, pLog, pVariables, pTargetProduct->rollback, pPackage, pTargetProduct, hCacheEvent); ExitOnFailure(hr, "Failed to plan rollack target product."); } } @@ -877,6 +879,7 @@ static void DeterminePatchChainedTarget( } static HRESULT PlanTargetProduct( + __in BOOTSTRAPPER_DISPLAY display, __in BURN_USER_EXPERIENCE* pUserExperience, __in BOOL fRollback, __in BURN_PLAN* pPlan, @@ -934,7 +937,7 @@ static HRESULT PlanTargetProduct( hr = StrAllocString(&pAction->mspTarget.sczTargetProductCode, pTargetProduct->wzTargetProductCode, 0); ExitOnFailure(hr, "Failed to copy target product code."); - hr = MsiEngineCalculateInstallUiLevel(pUserExperience, pPackage->sczId, !fRollback, pAction->mspTarget.action, + hr = MsiEngineCalculateInstallUiLevel(display, pUserExperience, pPackage->sczId, !fRollback, pAction->mspTarget.action, &pAction->mspTarget.actionMsiProperty, &pAction->mspTarget.uiLevel, &pAction->mspTarget.fDisableExternalUiHandler); ExitOnFailure(hr, "Failed to get msp ui options."); diff --git a/src/engine/mspengine.h b/src/engine/mspengine.h index 0f323e57..1f0c31df 100644 --- a/src/engine/mspengine.h +++ b/src/engine/mspengine.h @@ -38,6 +38,7 @@ HRESULT MspEnginePlanCalculatePackage( __out_opt BOOL* pfBARequestedCache ); HRESULT MspEnginePlanAddPackage( + __in BOOTSTRAPPER_DISPLAY display, __in BURN_USER_EXPERIENCE* pUserExperience, __in BURN_PACKAGE* pPackage, __in BURN_PLAN* pPlan, diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index 02f5be23..0b040bf8 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -31,6 +31,7 @@ static HRESULT ProcessPackage( __in BURN_PACKAGE* pPackage, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z_opt LPCWSTR wzLayoutDirectory, __inout HANDLE* phSyncpointEvent, @@ -472,6 +473,7 @@ extern "C" HRESULT PlanPackages( __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, __in BOOL fBundleInstalled, + __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z_opt LPCWSTR wzLayoutDirectory, __inout HANDLE* phSyncpointEvent @@ -509,7 +511,7 @@ extern "C" HRESULT PlanPackages( } } - hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices); + hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices); ExitOnFailure(hr, "Failed to process package."); // Attempt to remove orphaned packages during uninstall. Currently only MSI packages are supported and should not require source. @@ -534,7 +536,7 @@ extern "C" HRESULT PlanPackages( ExitOnFailure(hr, "Failed to copy installed ProductCode"); // Process the compatible MSI package like any other. - hr = ProcessPackage(fBundlePerMachine, pPackage, pUX, pPlan, pCompatiblePackage, pLog, pVariables, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices); + hr = ProcessPackage(fBundlePerMachine, pPackage, pUX, pPlan, pCompatiblePackage, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices); ExitOnFailure(hr, "Failed to process compatible package."); if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pCompatiblePackage->execute) @@ -785,6 +787,7 @@ extern "C" HRESULT PlanPassThroughBundle( __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, __inout HANDLE* phSyncpointEvent ) @@ -794,7 +797,7 @@ extern "C" HRESULT PlanPassThroughBundle( BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; // Plan passthrough package. - hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL); + hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL); ExitOnFailure(hr, "Failed to process passthrough package."); // If we still have an open rollback boundary, complete it. @@ -818,6 +821,7 @@ extern "C" HRESULT PlanUpdateBundle( __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, __inout HANDLE* phSyncpointEvent ) @@ -827,7 +831,7 @@ extern "C" HRESULT PlanUpdateBundle( BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; // Plan update package. - hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL); + hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL); ExitOnFailure(hr, "Failed to process update package."); // If we still have an open rollback boundary, complete it. @@ -853,6 +857,7 @@ static HRESULT ProcessPackage( __in BURN_PACKAGE* pPackage, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z_opt LPCWSTR wzLayoutDirectory, __inout HANDLE* phSyncpointEvent, @@ -906,7 +911,7 @@ static HRESULT ProcessPackage( } } - hr = PlanExecutePackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, phSyncpointEvent); + hr = PlanExecutePackage(fBundlePerMachine, display, pUX, pPlan, pPackage, pLog, pVariables, phSyncpointEvent); ExitOnFailure(hr, "Failed to plan execute package."); if (pPackage->fUninstallable && pNonpermanentPackageIndices) @@ -1086,6 +1091,7 @@ LExit: extern "C" HRESULT PlanExecutePackage( __in BOOL fPerMachine, + __in BOOTSTRAPPER_DISPLAY display, __in BURN_USER_EXPERIENCE* pUserExperience, __in BURN_PLAN* pPlan, __in BURN_PACKAGE* pPackage, @@ -1150,11 +1156,11 @@ extern "C" HRESULT PlanExecutePackage( break; case BURN_PACKAGE_TYPE_MSI: - hr = MsiEnginePlanAddPackage(pUserExperience, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire); + hr = MsiEnginePlanAddPackage(display, pUserExperience, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire); break; case BURN_PACKAGE_TYPE_MSP: - hr = MspEnginePlanAddPackage(pUserExperience, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire); + hr = MspEnginePlanAddPackage(display, pUserExperience, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire); break; case BURN_PACKAGE_TYPE_MSU: diff --git a/src/engine/plan.h b/src/engine/plan.h index 89f4b4bf..db9745e9 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -408,6 +408,7 @@ HRESULT PlanPackages( __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, __in BOOL fBundleInstalled, + __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z_opt LPCWSTR wzLayoutDirectory, __inout HANDLE* phSyncpointEvent @@ -426,6 +427,7 @@ HRESULT PlanPassThroughBundle( __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, __inout HANDLE* phSyncpointEvent ); @@ -435,6 +437,7 @@ HRESULT PlanUpdateBundle( __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, __inout HANDLE* phSyncpointEvent ); @@ -453,6 +456,7 @@ HRESULT PlanCachePackage( ); HRESULT PlanExecutePackage( __in BOOL fPerMachine, + __in BOOTSTRAPPER_DISPLAY display, __in BURN_USER_EXPERIENCE* pUserExperience, __in BURN_PLAN* pPlan, __in BURN_PACKAGE* pPackage, -- cgit v1.2.3-55-g6feb From 259c6b7420c5e95258f563eca14859a182ffe76a Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 23 May 2020 16:22:44 +1000 Subject: Add WindowsBuildNumber variable which is set to dwBuildNumber from RtlGetVersion() --- src/engine/variable.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp index dc5a569a..6322942e 100644 --- a/src/engine/variable.cpp +++ b/src/engine/variable.cpp @@ -36,6 +36,7 @@ enum OS_INFO_VARIABLE OS_INFO_VARIABLE_CompatibilityMode, OS_INFO_VARIABLE_TerminalServer, OS_INFO_VARIABLE_ProcessorArchitecture, + OS_INFO_VARIABLE_WindowsBuildNumber, }; enum SET_VARIABLE @@ -253,6 +254,7 @@ extern "C" HRESULT VariableInitialize( {L"VersionMsi", InitializeVariableVersionMsi, 0}, {L"VersionNT", InitializeVariableVersionNT, OS_INFO_VARIABLE_VersionNT}, {L"VersionNT64", InitializeVariableVersionNT, OS_INFO_VARIABLE_VersionNT64}, + {L"WindowsBuildNumber", InitializeVariableVersionNT, OS_INFO_VARIABLE_WindowsBuildNumber}, {L"WindowsFolder", InitializeVariableCsidlFolder, CSIDL_WINDOWS}, {L"WindowsVolume", InitializeVariableWindowsVolumeFolder, 0}, {BURN_BUNDLE_ACTION, InitializeVariableNumeric, 0, FALSE, TRUE}, @@ -1680,6 +1682,9 @@ static HRESULT InitializeVariableVersionNT( } } break; + case OS_INFO_VARIABLE_WindowsBuildNumber: + value.qwValue = static_cast(ovix.dwBuildNumber); + value.Type = BURN_VARIANT_TYPE_NUMERIC; default: AssertSz(FALSE, "Unknown OS info type."); break; -- cgit v1.2.3-55-g6feb From 04f279ab8659c9e4c6c6c796548da01eca1e7615 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 15 Jun 2020 22:02:33 +1000 Subject: Set Visible to false for the contents of WixToolset.Burn. --- appveyor.cmd | 6 +++--- src/stub/WixToolset.Burn.props | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/appveyor.cmd b/appveyor.cmd index dbe8e248..cc3f798a 100644 --- a/appveyor.cmd +++ b/appveyor.cmd @@ -1,11 +1,11 @@ @setlocal @pushd %~dp0 -nuget restore +nuget restore || exit /b -msbuild -p:Configuration=Release;Platform=x86 +msbuild -p:Configuration=Release;Platform=x86 || exit /b -msbuild -p:Configuration=Release -t:Pack src\stub\stub.vcxproj +msbuild -p:Configuration=Release -t:Pack src\stub\stub.vcxproj || exit /b @popd @endlocal \ No newline at end of file diff --git a/src/stub/WixToolset.Burn.props b/src/stub/WixToolset.Burn.props index b83d0b1c..38cd333e 100644 --- a/src/stub/WixToolset.Burn.props +++ b/src/stub/WixToolset.Burn.props @@ -6,6 +6,7 @@ %(RecursiveDir)%(FileName)%(Extension) + False PreserveNewest -- cgit v1.2.3-55-g6feb From 453bec104676c7dd808a20eaf30db9c918bed1e7 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 24 Jun 2020 09:20:15 +1000 Subject: Update to latest dutil. --- src/engine/engine.vcxproj | 4 ++-- src/engine/packages.config | 2 +- src/engine/precomp.h | 6 ++++-- src/engine/registration.cpp | 2 +- src/stub/WixToolset.Burn.nuspec | 3 +-- src/stub/packages.config | 2 +- src/stub/precomp.h | 1 + src/stub/stub.cpp | 30 ++++++++++++++++++++++++++++++ src/stub/stub.vcxproj | 8 ++++---- 9 files changed, 45 insertions(+), 13 deletions(-) diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index 80b67b7d..2fe68c2a 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -3,7 +3,7 @@ - + @@ -167,7 +167,7 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + diff --git a/src/engine/packages.config b/src/engine/packages.config index 251df9d0..52ccddb4 100644 --- a/src/engine/packages.config +++ b/src/engine/packages.config @@ -2,5 +2,5 @@ - + \ No newline at end of file diff --git a/src/engine/precomp.h b/src/engine/precomp.h index 7aa7dafa..04dfa50e 100644 --- a/src/engine/precomp.h +++ b/src/engine/precomp.h @@ -2,8 +2,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -#define ExitTrace LogErrorString - #include #include #include @@ -26,6 +24,10 @@ #include #include +#include + +#define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_EXTERNAL + #include #include #include diff --git a/src/engine/registration.cpp b/src/engine/registration.cpp index 93c990f5..bbde92b3 100644 --- a/src/engine/registration.cpp +++ b/src/engine/registration.cpp @@ -893,7 +893,7 @@ extern "C" HRESULT RegistrationSessionEnd( if (FAILED(hr)) { - ExitTrace(hr, "Failed to write volatile reboot required registry key."); + ExitTraceSource(DUTIL_SOURCE_DEFAULT, hr, "Failed to write volatile reboot required registry key."); hr = S_OK; } } diff --git a/src/stub/WixToolset.Burn.nuspec b/src/stub/WixToolset.Burn.nuspec index e71f64e4..35392523 100644 --- a/src/stub/WixToolset.Burn.nuspec +++ b/src/stub/WixToolset.Burn.nuspec @@ -5,8 +5,7 @@ $version$ $authors$ $authors$ - - https://licenses.nuget.org/MS-RL + MS-RL https://github.com/wixtoolset/burn false $description$ diff --git a/src/stub/packages.config b/src/stub/packages.config index e8115bfa..38ce9597 100644 --- a/src/stub/packages.config +++ b/src/stub/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/stub/precomp.h b/src/stub/precomp.h index 387d4f0f..c8301a0f 100644 --- a/src/stub/precomp.h +++ b/src/stub/precomp.h @@ -9,5 +9,6 @@ #include #include #include +#include #include "engine.h" diff --git a/src/stub/stub.cpp b/src/stub/stub.cpp index 2f09eede..d3ace1f3 100644 --- a/src/stub/stub.cpp +++ b/src/stub/stub.cpp @@ -3,6 +3,16 @@ #include "precomp.h" +static void CALLBACK BurnTraceError( + __in_z LPCSTR szFile, + __in int iLine, + __in REPORT_LEVEL rl, + __in UINT source, + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ); + int WINAPI wWinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE /* hPrevInstance */, @@ -30,6 +40,8 @@ int WINAPI wWinMain( L"feclient.dll", // unsafely loaded by DecryptFile(). }; + DutilInitialize(&BurnTraceError); + // Best effort attempt to get our file handle as soon as possible. hr = PathForCurrentProcess(&sczPath, NULL); if (SUCCEEDED(hr)) @@ -60,5 +72,23 @@ LExit: ReleaseFileHandle(hEngineFile); ReleaseStr(sczPath); + DutilUninitialize(); + return FAILED(hr) ? (int)hr : (int)dwExitCode; } + +static void CALLBACK BurnTraceError( + __in_z LPCSTR /*szFile*/, + __in int /*iLine*/, + __in REPORT_LEVEL /*rl*/, + __in UINT source, + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ) +{ + if (DUTIL_SOURCE_DEFAULT == source) + { + LogErrorStringArgs(hrError, szFormat, args); + } +} diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index ce43e0e6..a3db2044 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -2,7 +2,7 @@ - + @@ -28,7 +28,7 @@ Application Windows burn - v141 + v142 Unicode Native component of WixToolset.Burn @@ -85,7 +85,7 @@ - + @@ -94,6 +94,6 @@ This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}. - + -- cgit v1.2.3-55-g6feb From f15c71a28063a21d73bcbc61a712ebe2c702740c Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 24 Jun 2020 09:20:42 +1000 Subject: Move native pdbs into symbols package. --- src/stub/stub.vcxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index a3db2044..416deffd 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -85,7 +85,7 @@ - + -- cgit v1.2.3-55-g6feb From 172b71f2fb75735d59f0c750f442b7d5fb88d248 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 24 Jun 2020 09:25:46 +1000 Subject: Add SourceLink to C++ projects. --- src/stub/packages.config | 3 +++ src/stub/stub.vcxproj | 14 +++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/stub/packages.config b/src/stub/packages.config index 38ce9597..f3abcd35 100644 --- a/src/stub/packages.config +++ b/src/stub/packages.config @@ -1,5 +1,8 @@  + + + \ No newline at end of file diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index 416deffd..91366cad 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -2,6 +2,9 @@ + + + @@ -40,6 +43,9 @@ + + + @@ -93,7 +99,13 @@ This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}. + + + + + + - + \ No newline at end of file -- cgit v1.2.3-55-g6feb From a49441dfa8f9f796d98ce240ffb839b325908498 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 24 Jun 2020 20:37:01 +1000 Subject: Upgrade to VS2019 appveyor image. --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index c1df03cc..7c686b04 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,7 +8,7 @@ branches: - master - develop -image: Visual Studio 2017 +image: Visual Studio 2019 version: 0.0.0.{build} configuration: Release -- cgit v1.2.3-55-g6feb From 8bcb8c013a258691d4004c50af4689b90dd05c45 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 15 Jul 2020 21:41:47 +1000 Subject: Import BurnUnitTest from old wix4 repo. --- src/test/BurnUnitTest/AssemblyInfo.cpp | 12 + src/test/BurnUnitTest/BurnTestException.h | 93 +++ src/test/BurnUnitTest/BurnTestFixture.h | 41 ++ src/test/BurnUnitTest/BurnUnitTest.h | 55 ++ src/test/BurnUnitTest/BurnUnitTest.rc | 7 + src/test/BurnUnitTest/BurnUnitTest.vcxproj | 60 ++ src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters | 74 +++ src/test/BurnUnitTest/CacheTest.cpp | 72 ++ src/test/BurnUnitTest/ElevationTest.cpp | 218 +++++++ src/test/BurnUnitTest/ManifestHelpers.cpp | 41 ++ src/test/BurnUnitTest/ManifestHelpers.h | 24 + src/test/BurnUnitTest/ManifestTest.cpp | 59 ++ src/test/BurnUnitTest/RegistrationTest.cpp | 655 +++++++++++++++++++ src/test/BurnUnitTest/SearchTest.cpp | 721 +++++++++++++++++++++ src/test/BurnUnitTest/VariableHelpers.cpp | 199 ++++++ src/test/BurnUnitTest/VariableHelpers.h | 36 + src/test/BurnUnitTest/VariableTest.cpp | 480 ++++++++++++++ src/test/BurnUnitTest/precomp.cpp | 3 + src/test/BurnUnitTest/precomp.h | 78 +++ 19 files changed, 2928 insertions(+) create mode 100644 src/test/BurnUnitTest/AssemblyInfo.cpp create mode 100644 src/test/BurnUnitTest/BurnTestException.h create mode 100644 src/test/BurnUnitTest/BurnTestFixture.h create mode 100644 src/test/BurnUnitTest/BurnUnitTest.h create mode 100644 src/test/BurnUnitTest/BurnUnitTest.rc create mode 100644 src/test/BurnUnitTest/BurnUnitTest.vcxproj create mode 100644 src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters create mode 100644 src/test/BurnUnitTest/CacheTest.cpp create mode 100644 src/test/BurnUnitTest/ElevationTest.cpp create mode 100644 src/test/BurnUnitTest/ManifestHelpers.cpp create mode 100644 src/test/BurnUnitTest/ManifestHelpers.h create mode 100644 src/test/BurnUnitTest/ManifestTest.cpp create mode 100644 src/test/BurnUnitTest/RegistrationTest.cpp create mode 100644 src/test/BurnUnitTest/SearchTest.cpp create mode 100644 src/test/BurnUnitTest/VariableHelpers.cpp create mode 100644 src/test/BurnUnitTest/VariableHelpers.h create mode 100644 src/test/BurnUnitTest/VariableTest.cpp create mode 100644 src/test/BurnUnitTest/precomp.cpp create mode 100644 src/test/BurnUnitTest/precomp.h diff --git a/src/test/BurnUnitTest/AssemblyInfo.cpp b/src/test/BurnUnitTest/AssemblyInfo.cpp new file mode 100644 index 00000000..0282b1b7 --- /dev/null +++ b/src/test/BurnUnitTest/AssemblyInfo.cpp @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System::Reflection; +using namespace System::Runtime::CompilerServices; +using namespace System::Runtime::InteropServices; + +[assembly: AssemblyTitleAttribute("Windows Installer XML Burn unit tests")]; +[assembly: AssemblyDescriptionAttribute("Burn unit tests")]; +[assembly: AssemblyCultureAttribute("")]; +[assembly: ComVisible(false)]; diff --git a/src/test/BurnUnitTest/BurnTestException.h b/src/test/BurnUnitTest/BurnTestException.h new file mode 100644 index 00000000..bd94b4fc --- /dev/null +++ b/src/test/BurnUnitTest/BurnTestException.h @@ -0,0 +1,93 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + + public ref struct BurnTestException : public System::Exception + { + public: + BurnTestException(HRESULT error) + { + this->HResult = error; + } + + BurnTestException(HRESULT error, String^ message) + : Exception(message) + { + this->HResult = error; + } + + property Int32 ErrorCode + { + Int32 get() + { + return this->HResult; + } + } + + }; +} +} +} +} +} + +// this class is used by __TestThrowOnFailure_Format() below to deallocate +// the string created after the function call has returned +class __TestThrowOnFailure_StringFree +{ + LPWSTR m_scz; + +public: + __TestThrowOnFailure_StringFree(LPWSTR scz) + { + m_scz = scz; + } + + ~__TestThrowOnFailure_StringFree() + { + ReleaseStr(m_scz); + } + + operator LPCWSTR() + { + return m_scz; + } +}; + +// used by the TestThrowOnFailure macros to format the error string and +// return an LPCWSTR that can be used to initialize a System::String +#pragma warning (push) +#pragma warning (disable : 4793) +inline __TestThrowOnFailure_StringFree __TestThrowOnFailure_Format(LPCWSTR wzFormat, ...) +{ + Assert(wzFormat && *wzFormat); + + HRESULT hr = S_OK; + LPWSTR scz = NULL; + va_list args; + + va_start(args, wzFormat); + hr = StrAllocFormattedArgs(&scz, wzFormat, args); + va_end(args); + ExitOnFailure(hr, "Failed to format message string."); + +LExit: + return scz; +} +#pragma warning (pop) + +#define TestThrowOnFailure(hr, s) if (FAILED(hr)) { throw gcnew Microsoft::Tools::WindowsInstallerXml::Test::Bootstrapper::BurnTestException(hr, gcnew System::String(s)); } +#define TestThrowOnFailure1(hr, s, p) if (FAILED(hr)) { throw gcnew Microsoft::Tools::WindowsInstallerXml::Test::Bootstrapper::BurnTestException(hr, gcnew System::String(__TestThrowOnFailure_Format(s, p))); } +#define TestThrowOnFailure2(hr, s, p1, p2) if (FAILED(hr)) { throw gcnew Microsoft::Tools::WindowsInstallerXml::Test::Bootstrapper::BurnTestException(hr, gcnew System::String(__TestThrowOnFailure_Format(s, p1, p2))); } diff --git a/src/test/BurnUnitTest/BurnTestFixture.h b/src/test/BurnUnitTest/BurnTestFixture.h new file mode 100644 index 00000000..b89fe6fa --- /dev/null +++ b/src/test/BurnUnitTest/BurnTestFixture.h @@ -0,0 +1,41 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + + public ref class BurnTestFixture + { + public: + BurnTestFixture() + { + HRESULT hr = XmlInitialize(); + TestThrowOnFailure(hr, L"Failed to initialize XML support."); + + hr = RegInitialize(); + TestThrowOnFailure(hr, L"Failed to initialize Regutil."); + + PlatformInitialize(); + } + + ~BurnTestFixture() + { + XmlUninitialize(); + RegUninitialize(); + } + }; +} +} +} +} +} diff --git a/src/test/BurnUnitTest/BurnUnitTest.h b/src/test/BurnUnitTest/BurnUnitTest.h new file mode 100644 index 00000000..a4ca2707 --- /dev/null +++ b/src/test/BurnUnitTest/BurnUnitTest.h @@ -0,0 +1,55 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace WixTest; + using namespace Xunit; + + public ref class BurnUnitTest : WixTestBase, IUseFixture + { + public: + BurnUnitTest() + { + } + + virtual void TestInitialize() override + { + WixTestBase::TestInitialize(); + + HRESULT hr = S_OK; + + LogInitialize(::GetModuleHandleW(NULL)); + + hr = LogOpen(NULL, L"BurnUnitTest", NULL, L"txt", FALSE, FALSE, NULL); + TestThrowOnFailure(hr, L"Failed to open log."); + } + + virtual void TestUninitialize() override + { + LogUninitialize(FALSE); + + WixTestBase::TestUninitialize(); + } + + virtual void SetFixture(BurnTestFixture^ fixture) + { + // Don't care about the fixture, just need it to be created and disposed. + UNREFERENCED_PARAMETER(fixture); + } + }; +} +} +} +} +} diff --git a/src/test/BurnUnitTest/BurnUnitTest.rc b/src/test/BurnUnitTest/BurnUnitTest.rc new file mode 100644 index 00000000..159b0b42 --- /dev/null +++ b/src/test/BurnUnitTest/BurnUnitTest.rc @@ -0,0 +1,7 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#define VER_APP +#define VER_ORIGINAL_FILENAME "BurnUnitTest.dll" +#define VER_INTERNAL_NAME "setup" +#define VER_FILE_DESCRIPTION "WiX Toolset Bootstrapper unit tests" +#include "wix.rc" diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj new file mode 100644 index 00000000..5157d0d6 --- /dev/null +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -0,0 +1,60 @@ + + + + + + + + Debug + Win32 + + + Release + Win32 + + + + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942} + {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67} + UnitTest + ManagedCProj + DynamicLibrary + Unicode + true + + + + $(WixRoot)src\libs\dutil\inc;$(WixRoot)src\burn\inc;$(WixRoot)src\burn\engine;$(WixRoot)src\libs\deputil\inc + cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;wintrust.lib;dutil.lib;deputil.lib;engine.lib;gdiplus.lib + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(XunitPath)\xunit.dll + + + + diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters b/src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters new file mode 100644 index 00000000..bee5d15e --- /dev/null +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters @@ -0,0 +1,74 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/test/BurnUnitTest/CacheTest.cpp b/src/test/BurnUnitTest/CacheTest.cpp new file mode 100644 index 00000000..9b3c6e64 --- /dev/null +++ b/src/test/BurnUnitTest/CacheTest.cpp @@ -0,0 +1,72 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace System::IO; + using namespace WixTest; + using namespace Xunit; + + public ref class CacheTest : BurnUnitTest + { + public: + [NamedFact] + void CacheSignatureTest() + { + HRESULT hr = S_OK; + BURN_PACKAGE package = { }; + BURN_PAYLOAD payload = { }; + LPWSTR sczPayloadPath = NULL; + BYTE* pb = NULL; + DWORD cb = NULL; + + try + { + pin_ptr dataDirectory = PtrToStringChars(TestContext->DataDirectory); + hr = PathConcat(dataDirectory, L"BurnTestPayloads\\Products\\TestExe\\TestExe.exe", &sczPayloadPath); + Assert::True(S_OK == hr, "Failed to get path to test file."); + Assert::True(FileExistsEx(sczPayloadPath, NULL), "Test file does not exist."); + + hr = StrAllocHexDecode(L"232BD16B78C1926F95D637731E1EE5379A3C4222", &pb, &cb); + Assert::Equal(S_OK, hr); + + package.fPerMachine = FALSE; + package.sczCacheId = L"Bootstrapper.CacheTest.CacheSignatureTest"; + payload.sczKey = L"CacheSignatureTest.PayloadKey"; + payload.sczFilePath = L"CacheSignatureTest.File"; + payload.pbHash = pb; + payload.cbHash = cb; + + hr = CacheCompletePayload(package.fPerMachine, &payload, package.sczCacheId, sczPayloadPath, FALSE); + Assert::Equal(S_OK, hr); + } + finally + { + ReleaseMem(pb); + ReleaseStr(sczPayloadPath); + + String^ filePath = Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), "Package Cache\\Bootstrapper.CacheTest.CacheSignatureTest\\CacheSignatureTest.File"); + if (File::Exists(filePath)) + { + File::SetAttributes(filePath, FileAttributes::Normal); + File::Delete(filePath); + } + } + } + }; +} +} +} +} +} diff --git a/src/test/BurnUnitTest/ElevationTest.cpp b/src/test/BurnUnitTest/ElevationTest.cpp new file mode 100644 index 00000000..bb10ce43 --- /dev/null +++ b/src/test/BurnUnitTest/ElevationTest.cpp @@ -0,0 +1,218 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +const DWORD TEST_CHILD_SENT_MESSAGE_ID = 0xFFFE; +const DWORD TEST_PARENT_SENT_MESSAGE_ID = 0xFFFF; +const HRESULT S_TEST_SUCCEEDED = 0x3133; +const char TEST_MESSAGE_DATA[] = "{94949868-7EAE-4ac5-BEAC-AFCA2821DE01}"; + + +static BOOL STDAPICALLTYPE ElevateTest_ShellExecuteExW( + __inout LPSHELLEXECUTEINFOW lpExecInfo + ); +static DWORD CALLBACK ElevateTest_ThreadProc( + __in LPVOID lpThreadParameter + ); +static HRESULT ProcessParentMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); +static HRESULT ProcessChildMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace System::IO; + using namespace System::Threading; + using namespace WixTest; + using namespace Xunit; + + public ref class ElevationTest : BurnUnitTest + { + public: + [NamedFact] + void ElevateTest() + { + HRESULT hr = S_OK; + BURN_PIPE_CONNECTION connection = { }; + HANDLE hEvent = NULL; + DWORD dwResult = S_OK; + try + { + ShelFunctionOverride(ElevateTest_ShellExecuteExW); + + PipeConnectionInitialize(&connection); + + // + // per-user side setup + // + hr = PipeCreateNameAndSecret(&connection.sczName, &connection.sczSecret); + TestThrowOnFailure(hr, L"Failed to create connection name and secret."); + + hr = PipeCreatePipes(&connection, TRUE, &hEvent); + TestThrowOnFailure(hr, L"Failed to create pipes."); + + hr = PipeLaunchChildProcess(L"tests\\ignore\\this\\path\\to\\burn.exe", &connection, TRUE, NULL); + TestThrowOnFailure(hr, L"Failed to create elevated process."); + + hr = PipeWaitForChildConnect(&connection); + TestThrowOnFailure(hr, L"Failed to wait for child process to connect."); + + // post execute message + hr = PipeSendMessage(connection.hPipe, TEST_PARENT_SENT_MESSAGE_ID, NULL, 0, ProcessParentMessages, NULL, &dwResult); + TestThrowOnFailure(hr, "Failed to post execute message to per-machine process."); + + // + // initiate termination + // + hr = PipeTerminateChildProcess(&connection, 666, FALSE); + TestThrowOnFailure(hr, L"Failed to terminate elevated process."); + + // check flags + Assert::Equal(S_TEST_SUCCEEDED, (HRESULT)dwResult); + } + finally + { + PipeConnectionUninitialize(&connection); + ReleaseHandle(hEvent); + } + } + }; +} +} +} +} +} + + +static BOOL STDAPICALLTYPE ElevateTest_ShellExecuteExW( + __inout LPSHELLEXECUTEINFOW lpExecInfo + ) +{ + HRESULT hr = S_OK; + LPWSTR scz = NULL; + + hr = StrAllocString(&scz, lpExecInfo->lpParameters, 0); + ExitOnFailure(hr, "Failed to copy arguments."); + + // Pretend this thread is the elevated process. + lpExecInfo->hProcess = ::CreateThread(NULL, 0, ElevateTest_ThreadProc, scz, 0, NULL); + ExitOnNullWithLastError(lpExecInfo->hProcess, hr, "Failed to create thread."); + scz = NULL; + +LExit: + ReleaseStr(scz); + + return SUCCEEDED(hr); +} + +static DWORD CALLBACK ElevateTest_ThreadProc( + __in LPVOID lpThreadParameter + ) +{ + HRESULT hr = S_OK; + LPWSTR sczArguments = (LPWSTR)lpThreadParameter; + BURN_PIPE_CONNECTION connection = { }; + BURN_PIPE_RESULT result = { }; + + PipeConnectionInitialize(&connection); + + StrAlloc(&connection.sczName, MAX_PATH); + StrAlloc(&connection.sczSecret, MAX_PATH); + + // parse command line arguments + if (3 != swscanf_s(sczArguments, L"-q -burn.elevated %s %s %u", connection.sczName, MAX_PATH, connection.sczSecret, MAX_PATH, &connection.dwProcessId)) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Failed to parse argument string."); + } + + // set up connection with per-user process + hr = PipeChildConnect(&connection, TRUE); + ExitOnFailure(hr, "Failed to connect to per-user process."); + + // pump messages + hr = PipePumpMessages(connection.hPipe, ProcessChildMessages, static_cast(connection.hPipe), &result); + ExitOnFailure(hr, "Failed while pumping messages in child 'process'."); + +LExit: + PipeConnectionUninitialize(&connection); + ReleaseStr(sczArguments); + + return FAILED(hr) ? (DWORD)hr : result.dwResult; +} + +static HRESULT ProcessParentMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID /*pvContext*/, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + HRESULT hrResult = E_INVALIDDATA; + + // Process the message. + switch (pMsg->dwMessage) + { + case TEST_CHILD_SENT_MESSAGE_ID: + if (sizeof(TEST_MESSAGE_DATA) == pMsg->cbData && 0 == memcmp(TEST_MESSAGE_DATA, pMsg->pvData, sizeof(TEST_MESSAGE_DATA))) + { + hrResult = S_TEST_SUCCEEDED; + } + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Unexpected elevated message sent to parent process, msg: %u", pMsg->dwMessage); + } + + *pdwResult = static_cast(hrResult); + +LExit: + return hr; +} + +static HRESULT ProcessChildMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + HANDLE hPipe = static_cast(pvContext); + DWORD dwResult = 0; + + // Process the message. + switch (pMsg->dwMessage) + { + case TEST_PARENT_SENT_MESSAGE_ID: + // send test message + hr = PipeSendMessage(hPipe, TEST_CHILD_SENT_MESSAGE_ID, (LPVOID)TEST_MESSAGE_DATA, sizeof(TEST_MESSAGE_DATA), NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send message to per-machine process."); + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Unexpected elevated message sent to child process, msg: %u", pMsg->dwMessage); + } + + *pdwResult = dwResult; + +LExit: + return hr; +} diff --git a/src/test/BurnUnitTest/ManifestHelpers.cpp b/src/test/BurnUnitTest/ManifestHelpers.cpp new file mode 100644 index 00000000..96d5fab4 --- /dev/null +++ b/src/test/BurnUnitTest/ManifestHelpers.cpp @@ -0,0 +1,41 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +using namespace System; +using namespace Xunit; + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + void LoadBundleXmlHelper(LPCWSTR wzDocument, IXMLDOMElement** ppixeBundle) + { + HRESULT hr = S_OK; + IXMLDOMDocument* pixdDocument = NULL; + try + { + hr = XmlLoadDocument(wzDocument, &pixdDocument); + TestThrowOnFailure(hr, L"Failed to load XML document."); + + hr = pixdDocument->get_documentElement(ppixeBundle); + TestThrowOnFailure(hr, L"Failed to get bundle element."); + } + finally + { + ReleaseObject(pixdDocument); + } + } +} +} +} +} +} diff --git a/src/test/BurnUnitTest/ManifestHelpers.h b/src/test/BurnUnitTest/ManifestHelpers.h new file mode 100644 index 00000000..e3e57555 --- /dev/null +++ b/src/test/BurnUnitTest/ManifestHelpers.h @@ -0,0 +1,24 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + + +void LoadBundleXmlHelper(LPCWSTR wzDocument, IXMLDOMElement** ppixeBundle); + + +} +} +} +} +} diff --git a/src/test/BurnUnitTest/ManifestTest.cpp b/src/test/BurnUnitTest/ManifestTest.cpp new file mode 100644 index 00000000..14ead82e --- /dev/null +++ b/src/test/BurnUnitTest/ManifestTest.cpp @@ -0,0 +1,59 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace WixTest; + using namespace Xunit; + + public ref class ManifestTest : BurnUnitTest + { + public: + [NamedFact] + void ManifestLoadXmlTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + try + { + LPCSTR szDocument = + "" + " " + " " + " " + " " + " "; + + hr = VariableInitialize(&engineState.variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // load manifest from XML + hr = ManifestLoadXmlFromBuffer((BYTE*)szDocument, lstrlenA(szDocument), &engineState); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // check variable values + Assert::True(VariableExistsHelper(&engineState.variables, L"Variable1")); + } + finally + { + //CoreUninitialize(&engineState); + } + } + }; +} +} +} +} +} diff --git a/src/test/BurnUnitTest/RegistrationTest.cpp b/src/test/BurnUnitTest/RegistrationTest.cpp new file mode 100644 index 00000000..1ab6b8e9 --- /dev/null +++ b/src/test/BurnUnitTest/RegistrationTest.cpp @@ -0,0 +1,655 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +#define ROOT_PATH L"SOFTWARE\\WiX_Burn_UnitTest" +#define HKLM_PATH L"SOFTWARE\\WiX_Burn_UnitTest\\HKLM" +#define HKCU_PATH L"SOFTWARE\\WiX_Burn_UnitTest\\HKCU" +#define REGISTRY_UNINSTALL_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall" +#define REGISTRY_RUN_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce" + +#define TEST_UNINSTALL_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_UNINSTALL_KEY L"\\{D54F896D-1952-43e6-9C67-B5652240618C}" +#define TEST_RUN_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_RUN_KEY + + +static LSTATUS APIENTRY RegistrationTest_RegCreateKeyExW( + __in HKEY hKey, + __in LPCWSTR lpSubKey, + __reserved DWORD Reserved, + __in_opt LPWSTR lpClass, + __in DWORD dwOptions, + __in REGSAM samDesired, + __in_opt CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes, + __out PHKEY phkResult, + __out_opt LPDWORD lpdwDisposition + ); +static LSTATUS APIENTRY RegistrationTest_RegOpenKeyExW( + __in HKEY hKey, + __in_opt LPCWSTR lpSubKey, + __reserved DWORD ulOptions, + __in REGSAM samDesired, + __out PHKEY phkResult + ); +static LSTATUS APIENTRY RegistrationTest_RegDeleteKeyExW( + __in HKEY hKey, + __in LPCWSTR lpSubKey, + __in REGSAM samDesired, + __reserved DWORD Reserved + ); + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace Microsoft::Win32; + using namespace System; + using namespace System::IO; + using namespace WixTest; + using namespace Xunit; + + public ref class RegistrationTest : BurnUnitTest + { + public: + [NamedFact] + void RegisterBasicTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + LPWSTR sczCurrentProcess = NULL; + BURN_VARIABLES variables = { }; + BURN_USER_EXPERIENCE userExperience = { }; + BOOTSTRAPPER_COMMAND command = { }; + BURN_REGISTRATION registration = { }; + BURN_LOGGING logging = { }; + String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); + try + { + // set mock API's + RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); + + Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH)); + + logging.sczPath = L"BurnUnitTest.txt"; + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + hr = UserExperienceParseFromXml(&userExperience, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse UX from XML."); + + hr = RegistrationParseFromXml(®istration, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse registration from XML."); + + hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging); + TestThrowOnFailure(hr, L"Failed to set registration resume command."); + + hr = PathForCurrentProcess(&sczCurrentProcess, NULL); + TestThrowOnFailure(hr, L"Failed to get current process path."); + + // write registration + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, &userExperience, BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE | BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + // verify that registration was created + Assert::True(Directory::Exists(cacheDirectory)); + Assert::True(File::Exists(Path::Combine(cacheDirectory, gcnew String(L"setup.exe")))); + + Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)(Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr))); + + // end session + hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + TestThrowOnFailure(hr, L"Failed to unregister bundle."); + + // verify that registration was removed + Assert::False(Directory::Exists(cacheDirectory)); + + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + } + finally + { + ReleaseStr(sczCurrentProcess); + ReleaseObject(pixeBundle); + UserExperienceUninitialize(&userExperience); + RegistrationUninitialize(®istration); + VariablesUninitialize(&variables); + + Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH)); + if (Directory::Exists(cacheDirectory)) + { + Directory::Delete(cacheDirectory, true); + } + + RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + } + } + + [NamedFact] + void RegisterArpMinimumTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + LPWSTR sczCurrentProcess = NULL; + BURN_VARIABLES variables = { }; + BURN_USER_EXPERIENCE userExperience = { }; + BOOTSTRAPPER_COMMAND command = { }; + BURN_REGISTRATION registration = { }; + BURN_LOGGING logging = { }; + String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); + try + { + // set mock API's + RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); + + Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH)); + + logging.sczPath = L"BurnUnitTest.txt"; + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + hr = UserExperienceParseFromXml(&userExperience, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse UX from XML."); + + hr = RegistrationParseFromXml(®istration, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse registration from XML."); + + hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging); + TestThrowOnFailure(hr, L"Failed to set registration resume command."); + + hr = PathForCurrentProcess(&sczCurrentProcess, NULL); + TestThrowOnFailure(hr, L"Failed to get current process path."); + + // + // install + // + + // write registration + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, &userExperience, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + // verify that registration was created + Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // complete registration + hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); + TestThrowOnFailure(hr, L"Failed to unregister bundle."); + + // verify that registration was updated + Assert::Equal(Int32(BURN_RESUME_MODE_ARP), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // + // uninstall + // + + // write registration + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, &userExperience, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, 0); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + // verify that registration was updated + Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // delete registration + hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + TestThrowOnFailure(hr, L"Failed to unregister bundle."); + + // verify that registration was removed + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + } + finally + { + ReleaseStr(sczCurrentProcess); + ReleaseObject(pixeBundle); + UserExperienceUninitialize(&userExperience); + RegistrationUninitialize(®istration); + VariablesUninitialize(&variables); + + Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH)); + if (Directory::Exists(cacheDirectory)) + { + Directory::Delete(cacheDirectory, true); + } + + RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + } + } + + [NamedFact] + void RegisterArpFullTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + LPWSTR sczCurrentProcess = NULL; + BURN_VARIABLES variables = { }; + BURN_USER_EXPERIENCE userExperience = { }; + BOOTSTRAPPER_COMMAND command = { }; + BURN_REGISTRATION registration = { }; + BURN_LOGGING logging = { }; + String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); + try + { + // set mock API's + RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); + + Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH)); + + logging.sczPath = L"BurnUnitTest.txt"; + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + hr = UserExperienceParseFromXml(&userExperience, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse UX from XML."); + + hr = RegistrationParseFromXml(®istration, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse registration from XML."); + + hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging); + TestThrowOnFailure(hr, L"Failed to set registration resume command."); + + hr = PathForCurrentProcess(&sczCurrentProcess, NULL); + TestThrowOnFailure(hr, L"Failed to get current process path."); + + // + // install + // + + // write registration + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, &userExperience, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + // verify that registration was created + Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // finish registration + hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + // verify that registration was updated + Assert::Equal(Int32(BURN_RESUME_MODE_ARP), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + Assert::Equal(gcnew String(L"DisplayName1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"DisplayName"), nullptr)); + Assert::Equal(gcnew String(L"1.2.3.4"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"DisplayVersion"), nullptr)); + Assert::Equal(gcnew String(L"Publisher1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Publisher"), nullptr)); + Assert::Equal(gcnew String(L"http://www.microsoft.com/help"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"HelpLink"), nullptr)); + Assert::Equal(gcnew String(L"555-555-5555"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"HelpTelephone"), nullptr)); + Assert::Equal(gcnew String(L"http://www.microsoft.com/about"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"URLInfoAbout"), nullptr)); + Assert::Equal(gcnew String(L"http://www.microsoft.com/update"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"URLUpdateInfo"), nullptr)); + Assert::Equal(gcnew String(L"Comments1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Comments"), nullptr)); + Assert::Equal(gcnew String(L"Contact1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Contact"), nullptr)); + Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"NoModify"), nullptr)); + Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"NoRemove"), nullptr)); + + // + // uninstall + // + + // write registration + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, &userExperience, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, 0); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + // verify that registration was updated + Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // delete registration + hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + TestThrowOnFailure(hr, L"Failed to unregister bundle."); + + // verify that registration was removed + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + } + finally + { + ReleaseStr(sczCurrentProcess); + ReleaseObject(pixeBundle); + UserExperienceUninitialize(&userExperience); + RegistrationUninitialize(®istration); + VariablesUninitialize(&variables); + + Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH)); + if (Directory::Exists(cacheDirectory)) + { + Directory::Delete(cacheDirectory, true); + } + + RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + } + } + + [NamedFact] + void ResumeTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + LPWSTR sczCurrentProcess = NULL; + BURN_VARIABLES variables = { }; + BURN_USER_EXPERIENCE userExperience = { }; + BOOTSTRAPPER_COMMAND command = { }; + BURN_REGISTRATION registration = { }; + BURN_LOGGING logging = { }; + BYTE rgbData[256] = { }; + BOOTSTRAPPER_RESUME_TYPE resumeType = BOOTSTRAPPER_RESUME_TYPE_NONE; + BYTE* pbBuffer = NULL; + DWORD cbBuffer = 0; + String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); + try + { + for (DWORD i = 0; i < 256; ++i) + { + rgbData[i] = (BYTE)i; + } + + // set mock API's + RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); + + Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH)); + + logging.sczPath = L"BurnUnitTest.txt"; + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + hr = UserExperienceParseFromXml(&userExperience, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse UX from XML."); + + hr = RegistrationParseFromXml(®istration, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse registration from XML."); + + hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging); + TestThrowOnFailure(hr, L"Failed to set registration resume command."); + + hr = PathForCurrentProcess(&sczCurrentProcess, NULL); + TestThrowOnFailure(hr, L"Failed to get current process path."); + + // read resume type before session + hr = RegistrationDetectResumeType(®istration, &resumeType); + TestThrowOnFailure(hr, L"Failed to read resume type."); + + Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_NONE, (int)resumeType); + + // begin session + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, &userExperience, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + hr = RegistrationSaveState(®istration, rgbData, sizeof(rgbData)); + TestThrowOnFailure(hr, L"Failed to save state."); + + // read interrupted resume type + hr = RegistrationDetectResumeType(®istration, &resumeType); + TestThrowOnFailure(hr, L"Failed to read interrupted resume type."); + + Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_INTERRUPTED, (int)resumeType); + + // suspend session + hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_SUSPEND, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); + TestThrowOnFailure(hr, L"Failed to suspend session."); + + // verify that run key was removed + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // read suspend resume type + hr = RegistrationDetectResumeType(®istration, &resumeType); + TestThrowOnFailure(hr, L"Failed to read suspend resume type."); + + Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_SUSPEND, (int)resumeType); + + // read state back + hr = RegistrationLoadState(®istration, &pbBuffer, &cbBuffer); + TestThrowOnFailure(hr, L"Failed to load state."); + + Assert::Equal((DWORD)sizeof(rgbData), cbBuffer); + Assert::True(0 == memcmp(pbBuffer, rgbData, sizeof(rgbData))); + + // write active resume mode + hr = RegistrationSessionResume(®istration, &variables); + TestThrowOnFailure(hr, L"Failed to write active resume mode."); + + // verify that run key was put back + Assert::NotEqual((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // end session + hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + TestThrowOnFailure(hr, L"Failed to unregister bundle."); + + // read resume type after session + hr = RegistrationDetectResumeType(®istration, &resumeType); + TestThrowOnFailure(hr, L"Failed to read resume type."); + + Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_NONE, (int)resumeType); + } + finally + { + ReleaseStr(sczCurrentProcess); + ReleaseObject(pixeBundle); + UserExperienceUninitialize(&userExperience); + RegistrationUninitialize(®istration); + VariablesUninitialize(&variables); + + Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH)); + if (Directory::Exists(cacheDirectory)) + { + Directory::Delete(cacheDirectory, true); + } + + RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + } + } + + //BOOTSTRAPPER_RESUME_TYPE_NONE, + //BOOTSTRAPPER_RESUME_TYPE_INVALID, // resume information is present but invalid + //BOOTSTRAPPER_RESUME_TYPE_UNEXPECTED, // relaunched after an unexpected interruption + //BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING, // reboot has not taken place yet + //BOOTSTRAPPER_RESUME_TYPE_REBOOT, // relaunched after reboot + //BOOTSTRAPPER_RESUME_TYPE_SUSPEND, // relaunched after suspend + //BOOTSTRAPPER_RESUME_TYPE_ARP, // launched from ARP + }; +} +} +} +} +} + + +static LSTATUS APIENTRY RegistrationTest_RegCreateKeyExW( + __in HKEY hKey, + __in LPCWSTR lpSubKey, + __reserved DWORD Reserved, + __in_opt LPWSTR lpClass, + __in DWORD dwOptions, + __in REGSAM samDesired, + __in_opt CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes, + __out PHKEY phkResult, + __out_opt LPDWORD lpdwDisposition + ) +{ + LSTATUS ls = ERROR_SUCCESS; + LPCWSTR wzRoot = NULL; + HKEY hkRoot = NULL; + + if (HKEY_LOCAL_MACHINE == hKey) + { + wzRoot = HKLM_PATH; + } + else if (HKEY_CURRENT_USER == hKey) + { + wzRoot = HKCU_PATH; + } + else + { + hkRoot = hKey; + } + + if (wzRoot) + { + ls = ::RegOpenKeyExW(HKEY_CURRENT_USER, wzRoot, 0, KEY_WRITE, &hkRoot); + if (ERROR_SUCCESS != ls) + { + ExitFunction(); + } + } + + ls = ::RegCreateKeyExW(hkRoot, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition); + +LExit: + ReleaseRegKey(hkRoot); + + return ls; +} + +static LSTATUS APIENTRY RegistrationTest_RegOpenKeyExW( + __in HKEY hKey, + __in_opt LPCWSTR lpSubKey, + __reserved DWORD ulOptions, + __in REGSAM samDesired, + __out PHKEY phkResult + ) +{ + LSTATUS ls = ERROR_SUCCESS; + LPCWSTR wzRoot = NULL; + HKEY hkRoot = NULL; + + if (HKEY_LOCAL_MACHINE == hKey) + { + wzRoot = HKLM_PATH; + } + else if (HKEY_CURRENT_USER == hKey) + { + wzRoot = HKCU_PATH; + } + else + { + hkRoot = hKey; + } + + if (wzRoot) + { + ls = ::RegOpenKeyExW(HKEY_CURRENT_USER, wzRoot, 0, KEY_WRITE, &hkRoot); + if (ERROR_SUCCESS != ls) + { + ExitFunction(); + } + } + + ls = ::RegOpenKeyExW(hkRoot, lpSubKey, ulOptions, samDesired, phkResult); + +LExit: + ReleaseRegKey(hkRoot); + + return ls; +} + +static LSTATUS APIENTRY RegistrationTest_RegDeleteKeyExW( + __in HKEY hKey, + __in LPCWSTR lpSubKey, + __in REGSAM samDesired, + __reserved DWORD Reserved + ) +{ + LSTATUS ls = ERROR_SUCCESS; + LPCWSTR wzRoot = NULL; + HKEY hkRoot = NULL; + + if (HKEY_LOCAL_MACHINE == hKey) + { + wzRoot = HKLM_PATH; + } + else if (HKEY_CURRENT_USER == hKey) + { + wzRoot = HKCU_PATH; + } + else + { + hkRoot = hKey; + } + + if (wzRoot) + { + ls = ::RegOpenKeyExW(HKEY_CURRENT_USER, wzRoot, 0, KEY_WRITE | samDesired, &hkRoot); + if (ERROR_SUCCESS != ls) + { + ExitFunction(); + } + } + + ls = ::RegDeleteKeyExW(hkRoot, lpSubKey, samDesired, Reserved); + +LExit: + ReleaseRegKey(hkRoot); + + return ls; +} diff --git a/src/test/BurnUnitTest/SearchTest.cpp b/src/test/BurnUnitTest/SearchTest.cpp new file mode 100644 index 00000000..d03db84c --- /dev/null +++ b/src/test/BurnUnitTest/SearchTest.cpp @@ -0,0 +1,721 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiGetComponentPathW( + __in LPCWSTR szProduct, + __in LPCWSTR szComponent, + __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, + __inout_opt LPDWORD pcchBuf + ); +static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiLocateComponentW( + __in LPCWSTR szComponent, + __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, + __inout_opt LPDWORD pcchBuf + ); +static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoW( + __in LPCWSTR szProductCode, + __in LPCWSTR szProperty, + __out_ecount_opt(*pcchValue) LPWSTR szValue, + __inout_opt LPDWORD pcchValue + ); +static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoExW( + __in LPCWSTR szProductCode, + __in_opt LPCWSTR szUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in LPCWSTR szProperty, + __out_ecount_opt(*pcchValue) LPWSTR szValue, + __inout_opt LPDWORD pcchValue + ); + +using namespace System; +using namespace Xunit; +using namespace Microsoft::Win32; + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + public ref class SearchTest : BurnUnitTest + { + public: + [NamedFact] + void DirectorySearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + pin_ptr wzDirectory1 = PtrToStringChars(this->TestContext->TestDirectory); + pin_ptr wzDirectory2 = PtrToStringChars(System::IO::Path::Combine(this->TestContext->TestDirectory, gcnew String(L"none"))); + + VariableSetStringHelper(&variables, L"Directory1", wzDirectory1); + VariableSetStringHelper(&variables, L"Directory2", wzDirectory2); + + LPCWSTR wzDocument = + L"" + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + + // check variable values + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable1")); + Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable2")); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + + [NamedFact] + void FileSearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + ULARGE_INTEGER uliVersion = { }; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + pin_ptr wzFile1 = PtrToStringChars(System::IO::Path::Combine(this->TestContext->TestDirectory, gcnew String(L"none.txt"))); + pin_ptr wzFile2 = PtrToStringChars(System::Reflection::Assembly::GetExecutingAssembly()->Location); + + hr = FileVersion(wzFile2, &uliVersion.HighPart, &uliVersion.LowPart); + TestThrowOnFailure(hr, L"Failed to get DLL version."); + + VariableSetStringHelper(&variables, L"File1", wzFile1); + VariableSetStringHelper(&variables, L"File2", wzFile2); + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + + // check variable values + Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable1")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable2")); + Assert::Equal(uliVersion.QuadPart, VariableGetVersionHelper(&variables, L"Variable3")); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + + [NamedFact] + void RegistrySearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + HKEY hkey32 = NULL; + HKEY hkey64 = NULL; + BOOL f64bitMachine = (nullptr != Environment::GetEnvironmentVariable("ProgramFiles(x86)")); + + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"String"), gcnew String(L"String1 %TEMP%"), RegistryValueKind::String); + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"StringExpand"), gcnew String(L"String1 %TEMP%"), RegistryValueKind::ExpandString); + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"DWord"), 1, RegistryValueKind::DWord); + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"QWord"), 1ll, RegistryValueKind::QWord); + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"VersionString"), gcnew String(L"1.1.1.1"), RegistryValueKind::String); + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"VersionQWord"), MAKEQWORDVERSION(1,1,1,1), RegistryValueKind::QWord); + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\String"), nullptr, gcnew String(L"String1"), RegistryValueKind::String); + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Numeric"), nullptr, 1ll, RegistryValueKind::DWord); + + if (f64bitMachine) + { + hr = RegCreate(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness\\", KEY_WRITE | KEY_WOW64_32KEY, &hkey32); + Assert::True(SUCCEEDED(hr)); + + hr = RegCreate(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness\\", KEY_WRITE | KEY_WOW64_64KEY, &hkey64); + Assert::True(SUCCEEDED(hr)); + + hr = RegWriteString(hkey64, L"TestStringSpecificToBitness", L"64-bit"); + Assert::True(SUCCEEDED(hr)); + + hr = RegWriteString(hkey32, L"TestStringSpecificToBitness", L"32-bit"); + Assert::True(SUCCEEDED(hr)); + } + + VariableSetStringHelper(&variables, L"MyKey", L"SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"); + VariableSetStringHelper(&variables, L"MyValue", L"String"); + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + + // check variable values + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable1")); + Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable2")); + Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable3")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable4")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable5")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable6")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable7")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable8")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable9")); + Assert::NotEqual(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable10")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable11")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable12")); + Assert::Equal(MAKEQWORDVERSION(1,1,1,1), VariableGetVersionHelper(&variables, L"Variable13")); + Assert::Equal(MAKEQWORDVERSION(1,1,1,1), VariableGetVersionHelper(&variables, L"Variable14")); + Assert::Equal(gcnew String(L"String1"), VariableGetStringHelper(&variables, L"Variable15")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable16")); + Assert::False(VariableExistsHelper(&variables, L"Variable17")); + Assert::False(VariableExistsHelper(&variables, L"Variable18")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable19")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable20")); + if (f64bitMachine) + { + Assert::Equal(gcnew String(L"32-bit"), VariableGetStringHelper(&variables, L"Variable21")); + Assert::Equal(gcnew String(L"64-bit"), VariableGetStringHelper(&variables, L"Variable22")); + } + + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable23")); + Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable24")); + Assert::Equal(gcnew String(L"Msi.Package"), VariableGetStringHelper(&variables, L"Variable25")); + } + finally + { + ReleaseRegKey(hkey32); + ReleaseRegKey(hkey64); + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + + Registry::CurrentUser->DeleteSubKeyTree(gcnew String(L"SOFTWARE\\Microsoft\\WiX_Burn_UnitTest")); + if (f64bitMachine) + { + RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness", REG_KEY_32BIT, FALSE); + RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest", REG_KEY_32BIT, FALSE); + RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness", REG_KEY_64BIT, FALSE); + RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest", REG_KEY_64BIT, FALSE); + } + } + } + + [NamedFact] + void MsiComponentSearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // set mock API's + WiuFunctionOverride(NULL, MsiComponentSearchTest_MsiGetComponentPathW, MsiComponentSearchTest_MsiLocateComponentW, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " // todo: value key path + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + + // check variable values + Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable1")); + Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable2")); + Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable3")); + Assert::Equal(gcnew String(L"C:\\directory\\file1.txt"), VariableGetStringHelper(&variables, L"Variable4")); + Assert::Equal(gcnew String(L"C:\\directory\\file2.txt"), VariableGetStringHelper(&variables, L"Variable5")); + Assert::Equal(gcnew String(L"C:\\directory\\file3.txt"), VariableGetStringHelper(&variables, L"Variable6")); + Assert::Equal(gcnew String(L"C:\\directory\\file4.txt"), VariableGetStringHelper(&variables, L"Variable7")); + Assert::Equal(gcnew String(L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\"), VariableGetStringHelper(&variables, L"Variable8")); + Assert::Equal(gcnew String(L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), VariableGetStringHelper(&variables, L"Variable9")); + Assert::Equal(3ll, VariableGetNumericHelper(&variables, L"Variable10")); + Assert::Equal(3ll, VariableGetNumericHelper(&variables, L"Variable11")); + Assert::Equal(4ll, VariableGetNumericHelper(&variables, L"Variable12")); + Assert::Equal(4ll, VariableGetNumericHelper(&variables, L"Variable13")); + Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable14")); + Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable15")); + Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable16")); + Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable17")); + Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable18")); + Assert::Equal(gcnew String(L"C:\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\file5.txt"), VariableGetStringHelper(&variables, L"Variable19")); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + + [NamedFact] + void MsiProductSearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // set mock API's + WiuFunctionOverride(NULL, NULL, NULL, NULL, MsiProductSearchTest_MsiGetProductInfoW, MsiProductSearchTest_MsiGetProductInfoExW, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + + // check variable values + Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable1")); + Assert::Equal(MAKEQWORDVERSION(1,0,0,0), VariableGetVersionHelper(&variables, L"Variable2")); + Assert::Equal(1033ll, VariableGetNumericHelper(&variables, L"Variable3")); + Assert::Equal(5ll, VariableGetNumericHelper(&variables, L"Variable4")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable5")); + Assert::Equal(MAKEQWORDVERSION(1,0,0,0), VariableGetVersionHelper(&variables, L"Variable6")); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + + [NamedFact] + void MsiFeatureSearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + try + { + LPCWSTR wzDocument = + L"" + L" " + L""; + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + + [NamedFact] + void ConditionalSearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + try + { + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L""; + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + + // check variable values + Assert::False(VariableExistsHelper(&variables, L"Variable1")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable2")); + Assert::False(VariableExistsHelper(&variables, L"Variable3")); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + [NamedFact] + void NoSearchesTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + try + { + LPCWSTR wzDocument = + L"" + L""; + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + }; +} +} +} +} +} + + +static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiGetComponentPathW( + __in LPCWSTR szProduct, + __in LPCWSTR szComponent, + __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, + __inout_opt LPDWORD pcchBuf + ) +{ + INSTALLSTATE is = INSTALLSTATE_INVALIDARG; + String^ product = gcnew String(szProduct); + + if (String::Equals(product, gcnew String(L"{BAD00000-0000-0000-0000-000000000000}"))) + { + is = INSTALLSTATE_UNKNOWN; + } + else if (String::Equals(product, gcnew String(L"{600D0000-0000-0000-0000-000000000000}"))) + { + is = MsiComponentSearchTest_MsiLocateComponentW(szComponent, lpPathBuf, pcchBuf); + } + + return is; +} + +static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiLocateComponentW( + __in LPCWSTR szComponent, + __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, + __inout_opt LPDWORD pcchBuf + ) +{ + HRESULT hr = S_OK; + INSTALLSTATE is = INSTALLSTATE_INVALIDARG; + String^ component = gcnew String(szComponent); + LPCWSTR wzValue = NULL; + + if (String::Equals(component, gcnew String(L"{BAD00000-1000-0000-0000-000000000000}"))) + { + is = INSTALLSTATE_UNKNOWN; + } + else if (String::Equals(component, gcnew String(L"{600D0000-1000-1000-0000-000000000000}"))) + { + wzValue = L"C:\\directory\\file1.txt"; + is = INSTALLSTATE_LOCAL; + } + else if (String::Equals(component, gcnew String(L"{600D0000-1000-2000-0000-000000000000}"))) + { + wzValue = L"C:\\directory\\file2.txt"; + is = INSTALLSTATE_SOURCE; + } + else if (String::Equals(component, gcnew String(L"{600D0000-1000-3000-0000-000000000000}"))) + { + wzValue = L"C:\\directory\\file3.txt"; + is = INSTALLSTATE_SOURCEABSENT; + } + else if (String::Equals(component, gcnew String(L"{600D0000-1000-4000-0000-000000000000}"))) + { + wzValue = L"C:\\directory\\file4.txt"; + is = INSTALLSTATE_ABSENT; + } + else if (String::Equals(component, gcnew String(L"{600D0000-1000-5000-0000-000000000000}"))) + { + wzValue = L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\"; + is = INSTALLSTATE_LOCAL; + } + else if (String::Equals(component, gcnew String(L"{600D0000-1000-6000-0000-000000000000}"))) + { + wzValue = L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"; + is = INSTALLSTATE_LOCAL; + } + else if (String::Equals(component, gcnew String(L"{600D0000-1000-7000-0000-000000000000}"))) + { + wzValue = L"C:\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\file5.txt"; + is = INSTALLSTATE_ABSENT; + } + + if (wzValue && lpPathBuf) + { + hr = ::StringCchCopyW(lpPathBuf, *pcchBuf, wzValue); + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + *pcchBuf = lstrlenW(wzValue); + is = INSTALLSTATE_MOREDATA; + } + else if (FAILED(hr)) + { + is = INSTALLSTATE_INVALIDARG; + } + } + + return is; +} + +static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoW( + __in LPCWSTR szProductCode, + __in LPCWSTR szProperty, + __out_ecount_opt(*pcchValue) LPWSTR szValue, + __inout_opt LPDWORD pcchValue + ) +{ + if (String::Equals(gcnew String(szProductCode), gcnew String(L"{600D0000-0000-0000-0000-000000000000}")) && + String::Equals(gcnew String(szProperty), gcnew String(INSTALLPROPERTY_PRODUCTSTATE))) + { + // force call to WiuGetProductInfoEx + return ERROR_UNKNOWN_PROPERTY; + } + + UINT er = MsiProductSearchTest_MsiGetProductInfoExW(szProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, szProperty, szValue, pcchValue); + return er; +} + +static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoExW( + __in LPCWSTR szProductCode, + __in_opt LPCWSTR /*szUserSid*/, + __in MSIINSTALLCONTEXT dwContext, + __in LPCWSTR szProperty, + __out_ecount_opt(*pcchValue) LPWSTR szValue, + __inout_opt LPDWORD pcchValue + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_FUNCTION_FAILED; + LPCWSTR wzValue = NULL; + + String^ productCode = gcnew String(szProductCode); + String^ _property = gcnew String(szProperty); + switch (dwContext) + { + case MSIINSTALLCONTEXT_USERMANAGED: + er = ERROR_UNKNOWN_PRODUCT; + break; + case MSIINSTALLCONTEXT_USERUNMANAGED: + if (String::Equals(productCode, gcnew String(L"{600D0000-0000-0000-0000-000000000000}"))) + { + if (String::Equals(_property, gcnew String(INSTALLPROPERTY_PRODUCTSTATE))) + { + wzValue = L"5"; + } + } + break; + case MSIINSTALLCONTEXT_MACHINE: + if (String::Equals(productCode, gcnew String(L"{BAD00000-0000-0000-0000-000000000000}"))) + { + er = ERROR_UNKNOWN_PRODUCT; + } + else if (String::Equals(productCode, gcnew String(L"{600D0000-0000-0000-0000-000000000000}"))) + { + if (String::Equals(_property, gcnew String(INSTALLPROPERTY_VERSIONSTRING))) + { + wzValue = L"1.0.0.0"; + } + else if (String::Equals(_property, gcnew String(INSTALLPROPERTY_LANGUAGE))) + { + wzValue = L"1033"; + } + else if (String::Equals(_property, gcnew String(INSTALLPROPERTY_ASSIGNMENTTYPE))) + { + wzValue = L"1"; + } + else if (String::Equals(_property, gcnew String(INSTALLPROPERTY_PRODUCTSTATE))) + { + // try again in per-user context + er = ERROR_UNKNOWN_PRODUCT; + } + } + else if (String::Equals(productCode, gcnew String(L"{600D0000-1000-0000-0000-000000000000}"))) + { + static BOOL fFlipp = FALSE; + if (fFlipp) + { + if (String::Equals(_property, gcnew String(INSTALLPROPERTY_VERSIONSTRING))) + { + wzValue = L"1.0.0.0"; + } + } + else + { + *pcchValue = MAX_PATH * 2; + er = ERROR_MORE_DATA; + } + fFlipp = !fFlipp; + } + break; + } + + if (wzValue) + { + hr = ::StringCchCopyW(szValue, *pcchValue, wzValue); + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + *pcchValue = lstrlenW(wzValue); + er = ERROR_MORE_DATA; + } + else if (SUCCEEDED(hr)) + { + er = ERROR_SUCCESS; + } + } + + return er; +} diff --git a/src/test/BurnUnitTest/VariableHelpers.cpp b/src/test/BurnUnitTest/VariableHelpers.cpp new file mode 100644 index 00000000..9ce46a76 --- /dev/null +++ b/src/test/BurnUnitTest/VariableHelpers.cpp @@ -0,0 +1,199 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +using namespace System; +using namespace Xunit; + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue) + { + HRESULT hr = S_OK; + + hr = VariableSetString(pVariables, wzVariable, wzValue, FALSE); + TestThrowOnFailure2(hr, L"Failed to set %s to: %s", wzVariable, wzValue); + } + + void VariableSetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LONGLONG llValue) + { + HRESULT hr = S_OK; + + hr = VariableSetNumeric(pVariables, wzVariable, llValue, FALSE); + TestThrowOnFailure2(hr, L"Failed to set %s to: %I64d", wzVariable, llValue); + } + + void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, DWORD64 qwValue) + { + HRESULT hr = S_OK; + + hr = VariableSetVersion(pVariables, wzVariable, qwValue, FALSE); + TestThrowOnFailure2(hr, L"Failed to set %s to: 0x%016I64x", wzVariable, qwValue); + } + + String^ VariableGetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) + { + HRESULT hr = S_OK; + LPWSTR scz = NULL; + try + { + hr = VariableGetString(pVariables, wzVariable, &scz); + TestThrowOnFailure1(hr, L"Failed to get: %s", wzVariable); + + return gcnew String(scz); + } + finally + { + ReleaseStr(scz); + } + } + + __int64 VariableGetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) + { + HRESULT hr = S_OK; + LONGLONG llValue = 0; + + hr = VariableGetNumeric(pVariables, wzVariable, &llValue); + TestThrowOnFailure1(hr, L"Failed to get: %s", wzVariable); + + return llValue; + } + + unsigned __int64 VariableGetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) + { + HRESULT hr = S_OK; + DWORD64 qwValue = 0; + + hr = VariableGetVersion(pVariables, wzVariable, &qwValue); + TestThrowOnFailure1(hr, L"Failed to get: %s", wzVariable); + + return qwValue; + } + + String^ VariableGetFormattedHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) + { + HRESULT hr = S_OK; + LPWSTR scz = NULL; + try + { + hr = VariableGetFormatted(pVariables, wzVariable, &scz); + TestThrowOnFailure1(hr, L"Failed to get formatted: %s", wzVariable); + + return gcnew String(scz); + } + finally + { + ReleaseStr(scz); + } + } + + String^ VariableFormatStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzIn) + { + HRESULT hr = S_OK; + LPWSTR scz = NULL; + try + { + hr = VariableFormatString(pVariables, wzIn, &scz, NULL); + TestThrowOnFailure1(hr, L"Failed to format string: '%s'", wzIn); + + return gcnew String(scz); + } + finally + { + ReleaseStr(scz); + } + } + + String^ VariableEscapeStringHelper(LPCWSTR wzIn) + { + HRESULT hr = S_OK; + LPWSTR scz = NULL; + try + { + hr = VariableEscapeString(wzIn, &scz); + TestThrowOnFailure1(hr, L"Failed to escape string: '%s'", wzIn); + + return gcnew String(scz); + } + finally + { + ReleaseStr(scz); + } + } + + bool EvaluateConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition) + { + HRESULT hr = S_OK; + BOOL f = FALSE; + + hr = ConditionEvaluate(pVariables, wzCondition, &f); + TestThrowOnFailure1(hr, L"Failed to evaluate condition: '%s'", wzCondition); + + return f ? true : false; + } + + bool EvaluateFailureConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition) + { + HRESULT hr = S_OK; + BOOL f = FALSE; + + hr = ConditionEvaluate(pVariables, wzCondition, &f); + return E_INVALIDDATA == hr ? true : false; + } + + bool VariableExistsHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) + { + HRESULT hr = S_OK; + BURN_VARIANT value = { }; + + try + { + hr = VariableGetVariant(pVariables, wzVariable, &value); + if (E_NOTFOUND == hr || value.Type == BURN_VARIANT_TYPE_NONE) + { + return false; + } + else + { + TestThrowOnFailure1(hr, L"Failed to find variable: '%s'", wzVariable); + return true; + } + } + finally + { + BVariantUninitialize(&value); + } + } + + int VariableGetTypeHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) + { + HRESULT hr = S_OK; + BURN_VARIANT value = { }; + + try + { + hr = VariableGetVariant(pVariables, wzVariable, &value); + TestThrowOnFailure1(hr, L"Failed to find variable: '%s'", wzVariable); + + return (int)value.Type; + } + finally + { + BVariantUninitialize(&value); + } + } +} +} +} +} +} diff --git a/src/test/BurnUnitTest/VariableHelpers.h b/src/test/BurnUnitTest/VariableHelpers.h new file mode 100644 index 00000000..98c52649 --- /dev/null +++ b/src/test/BurnUnitTest/VariableHelpers.h @@ -0,0 +1,36 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + + +void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue); +void VariableSetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LONGLONG llValue); +void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, DWORD64 qwValue); +System::String^ VariableGetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); +__int64 VariableGetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); +unsigned __int64 VariableGetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); +System::String^ VariableGetFormattedHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); +System::String^ VariableFormatStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzIn); +System::String^ VariableEscapeStringHelper(LPCWSTR wzIn); +bool EvaluateConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition); +bool EvaluateFailureConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition); +bool VariableExistsHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); +int VariableGetTypeHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); + + +} +} +} +} +} diff --git a/src/test/BurnUnitTest/VariableTest.cpp b/src/test/BurnUnitTest/VariableTest.cpp new file mode 100644 index 00000000..3a4caecf --- /dev/null +++ b/src/test/BurnUnitTest/VariableTest.cpp @@ -0,0 +1,480 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" +#undef GetTempPath +#undef GetEnvironmentVariable + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace Xunit; + + public ref class VariableTest : BurnUnitTest + { + public: + [NamedFact] + void VariablesBasicTest() + { + HRESULT hr = S_OK; + BURN_VARIABLES variables = { }; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // set variables + VariableSetStringHelper(&variables, L"PROP1", L"VAL1"); + VariableSetNumericHelper(&variables, L"PROP2", 2); + VariableSetStringHelper(&variables, L"PROP5", L"VAL5"); + VariableSetStringHelper(&variables, L"PROP3", L"VAL3"); + VariableSetStringHelper(&variables, L"PROP4", L"VAL4"); + VariableSetStringHelper(&variables, L"PROP6", L"VAL6"); + VariableSetStringHelper(&variables, L"PROP7", L"7"); + VariableSetVersionHelper(&variables, L"PROP8", MAKEQWORDVERSION(1,1,0,0)); + + // set overwritten variables + VariableSetStringHelper(&variables, L"OVERWRITTEN_STRING", L"ORIGINAL"); + VariableSetNumericHelper(&variables, L"OVERWRITTEN_STRING", 42); + + VariableSetNumericHelper(&variables, L"OVERWRITTEN_NUMBER", 5); + VariableSetStringHelper(&variables, L"OVERWRITTEN_NUMBER", L"NEW"); + + // get and verify variable values + Assert::Equal(gcnew String(L"VAL1"), VariableGetStringHelper(&variables, L"PROP1")); + Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"PROP2")); + Assert::Equal(gcnew String(L"2"), VariableGetStringHelper(&variables, L"PROP2")); + Assert::Equal(gcnew String(L"VAL3"), VariableGetStringHelper(&variables, L"PROP3")); + Assert::Equal(gcnew String(L"VAL4"), VariableGetStringHelper(&variables, L"PROP4")); + Assert::Equal(gcnew String(L"VAL5"), VariableGetStringHelper(&variables, L"PROP5")); + Assert::Equal(gcnew String(L"VAL6"), VariableGetStringHelper(&variables, L"PROP6")); + Assert::Equal(7ll, VariableGetNumericHelper(&variables, L"PROP7")); + Assert::Equal(MAKEQWORDVERSION(1,1,0,0), VariableGetVersionHelper(&variables, L"PROP8")); + Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetStringHelper(&variables, L"PROP8")); + + Assert::Equal(42ll, VariableGetNumericHelper(&variables, L"OVERWRITTEN_STRING")); + Assert::Equal(gcnew String(L"NEW"), VariableGetStringHelper(&variables, L"OVERWRITTEN_NUMBER")); + } + finally + { + VariablesUninitialize(&variables); + } + } + + [NamedFact] + void VariablesParseXmlTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + try + { + LPCWSTR wzDocument = + L"" + L" "; + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = VariablesParseFromXml(&variables, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // get and verify variable values + Assert::Equal((int)BURN_VARIANT_TYPE_NUMERIC, VariableGetTypeHelper(&variables, L"Var1")); + Assert::Equal((int)BURN_VARIANT_TYPE_STRING, VariableGetTypeHelper(&variables, L"Var2")); + Assert::Equal((int)BURN_VARIANT_TYPE_VERSION, VariableGetTypeHelper(&variables, L"Var3")); + Assert::Equal((int)BURN_VARIANT_TYPE_NONE, VariableGetTypeHelper(&variables, L"Var4")); + + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Var1")); + Assert::Equal(gcnew String(L"String value."), VariableGetStringHelper(&variables, L"Var2")); + Assert::Equal(MAKEQWORDVERSION(1,2,3,4), VariableGetVersionHelper(&variables, L"Var3")); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + } + } + + [NamedFact] + void VariablesFormatTest() + { + HRESULT hr = S_OK; + BURN_VARIABLES variables = { }; + LPWSTR scz = NULL; + DWORD cch = 0; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // set variables + VariableSetStringHelper(&variables, L"PROP1", L"VAL1"); + VariableSetStringHelper(&variables, L"PROP2", L"VAL2"); + VariableSetNumericHelper(&variables, L"PROP3", 3); + + // test string formatting + Assert::Equal(gcnew String(L"NOPROP"), VariableFormatStringHelper(&variables, L"NOPROP")); + Assert::Equal(gcnew String(L"VAL1"), VariableFormatStringHelper(&variables, L"[PROP1]")); + Assert::Equal(gcnew String(L" VAL1 "), VariableFormatStringHelper(&variables, L" [PROP1] ")); + Assert::Equal(gcnew String(L"PRE VAL1"), VariableFormatStringHelper(&variables, L"PRE [PROP1]")); + Assert::Equal(gcnew String(L"VAL1 POST"), VariableFormatStringHelper(&variables, L"[PROP1] POST")); + Assert::Equal(gcnew String(L"PRE VAL1 POST"), VariableFormatStringHelper(&variables, L"PRE [PROP1] POST")); + Assert::Equal(gcnew String(L"VAL1 MID VAL2"), VariableFormatStringHelper(&variables, L"[PROP1] MID [PROP2]")); + Assert::Equal(gcnew String(L""), VariableFormatStringHelper(&variables, L"[NONE]")); + Assert::Equal(gcnew String(L""), VariableFormatStringHelper(&variables, L"[prop1]")); + Assert::Equal(gcnew String(L"["), VariableFormatStringHelper(&variables, L"[\\[]")); + Assert::Equal(gcnew String(L"]"), VariableFormatStringHelper(&variables, L"[\\]]")); + Assert::Equal(gcnew String(L"[]"), VariableFormatStringHelper(&variables, L"[]")); + Assert::Equal(gcnew String(L"[NONE"), VariableFormatStringHelper(&variables, L"[NONE")); + Assert::Equal(gcnew String(L"VAL2"), VariableGetFormattedHelper(&variables, L"PROP2")); + Assert::Equal(gcnew String(L"3"), VariableGetFormattedHelper(&variables, L"PROP3")); + + hr = VariableFormatString(&variables, L"PRE [PROP1] POST", &scz, &cch); + TestThrowOnFailure(hr, L"Failed to format string"); + + Assert::Equal((DWORD)lstrlenW(scz), cch); + + hr = VariableFormatString(&variables, L"PRE [PROP1] POST", NULL, &cch); + TestThrowOnFailure(hr, L"Failed to format string"); + + Assert::Equal((DWORD)lstrlenW(scz), cch); + } + finally + { + VariablesUninitialize(&variables); + ReleaseStr(scz); + } + } + + [NamedFact] + void VariablesEscapeTest() + { + // test string escaping + Assert::Equal(gcnew String(L"[\\[]"), VariableEscapeStringHelper(L"[")); + Assert::Equal(gcnew String(L"[\\]]"), VariableEscapeStringHelper(L"]")); + Assert::Equal(gcnew String(L" [\\[]TEXT[\\]] "), VariableEscapeStringHelper(L" [TEXT] ")); + } + + [NamedFact] + void VariablesConditionTest() + { + HRESULT hr = S_OK; + BURN_VARIABLES variables = { }; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // set variables + VariableSetStringHelper(&variables, L"PROP1", L"VAL1"); + VariableSetStringHelper(&variables, L"PROP2", L"VAL2"); + VariableSetStringHelper(&variables, L"PROP3", L"VAL3"); + VariableSetStringHelper(&variables, L"PROP4", L"BEGIN MID END"); + VariableSetNumericHelper(&variables, L"PROP5", 5); + VariableSetNumericHelper(&variables, L"PROP6", 6); + VariableSetStringHelper(&variables, L"PROP7", L""); + VariableSetNumericHelper(&variables, L"PROP8", 0); + VariableSetStringHelper(&variables, L"_PROP9", L"VAL9"); + VariableSetNumericHelper(&variables, L"PROP10", -10); + VariableSetNumericHelper(&variables, L"PROP11", 9223372036854775807ll); + VariableSetNumericHelper(&variables, L"PROP12", -9223372036854775808ll); + VariableSetNumericHelper(&variables, L"PROP13", 0x00010000); + VariableSetNumericHelper(&variables, L"PROP14", 0x00000001); + VariableSetNumericHelper(&variables, L"PROP15", 0x00010001); + VariableSetVersionHelper(&variables, L"PROP16", MAKEQWORDVERSION(0,0,0,0)); + VariableSetVersionHelper(&variables, L"PROP17", MAKEQWORDVERSION(1,0,0,0)); + VariableSetVersionHelper(&variables, L"PROP18", MAKEQWORDVERSION(1,1,0,0)); + VariableSetVersionHelper(&variables, L"PROP19", MAKEQWORDVERSION(1,1,1,0)); + VariableSetVersionHelper(&variables, L"PROP20", MAKEQWORDVERSION(1,1,1,1)); + VariableSetNumericHelper(&variables, L"vPROP21", 1); + VariableSetVersionHelper(&variables, L"PROP22", MAKEQWORDVERSION(65535,65535,65535,65535)); + VariableSetStringHelper(&variables, L"PROP23", L"1.1.1"); + + // test conditions + Assert::True(EvaluateConditionHelper(&variables, L"PROP1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP5")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP7")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP8")); + Assert::True(EvaluateConditionHelper(&variables, L"_PROP9")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP16")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP17")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\"")); + Assert::False(EvaluateConditionHelper(&variables, L"NONE = \"NOT\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 <> \"VAL1\"")); + Assert::True(EvaluateConditionHelper(&variables, L"NONE <> \"NOT\"")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 ~= \"val1\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"val1\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 ~<> \"val1\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 <> \"val1\"")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 = 5")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP5 = 0")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP5 <> 5")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 <> 0")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP10 = -10")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP10 <> -10")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP17 = v1")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP17 = v0")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP17 <> v1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP17 <> v0")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP16 = v0")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP17 = v1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP18 = v1.1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP19 = v1.1.1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP20 = v1.1.1.1")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP20 = v1.1.1.1.0")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP20 = v1.1.1.1.1")); + Assert::True(EvaluateConditionHelper(&variables, L"vPROP21 = 1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP23 = v1.1.1")); + Assert::True(EvaluateConditionHelper(&variables, L"v1.1.1 = PROP23")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 <> v1.1.1")); + Assert::True(EvaluateConditionHelper(&variables, L"v1.1.1 <> PROP1")); + + Assert::False(EvaluateConditionHelper(&variables, L"PROP11 = 9223372036854775806")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP11 = 9223372036854775807")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP11 = 9223372036854775808")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP11 = 92233720368547758070000")); + + Assert::False(EvaluateConditionHelper(&variables, L"PROP12 = -9223372036854775807")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP12 = -9223372036854775808")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP12 = -9223372036854775809")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP12 = -92233720368547758080000")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP22 = v65535.65535.65535.65535")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP22 = v65536.65535.65535.65535")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP22 = v65535.655350000.65535.65535")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 < 6")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP5 < 5")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 > 4")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP5 > 5")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 <= 6")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 <= 5")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP5 <= 4")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 >= 4")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 >= 5")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP5 >= 6")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP4 << \"BEGIN\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP4 << \"END\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP4 >> \"END\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP4 >> \"BEGIN\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP4 >< \"MID\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP4 >< \"NONE\"")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP16 < v1.1")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP16 < v0")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP17 > v0.12")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP17 > v1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP18 >= v1.0")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP18 >= v1.1")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP18 >= v2.1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP19 <= v1.1234.1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP19 <= v1.1.1")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP19 <= v1.0.123")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP6 = \"6\"")); + Assert::True(EvaluateConditionHelper(&variables, L"\"6\" = PROP6")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP6 = \"ABC\"")); + Assert::False(EvaluateConditionHelper(&variables, L"\"ABC\" = PROP6")); + Assert::False(EvaluateConditionHelper(&variables, L"\"ABC\" = PROP6")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP13 << 1")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP13 << 0")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP14 >> 1")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP14 >> 0")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP15 >< 65537")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP15 >< 0")); + + Assert::False(EvaluateConditionHelper(&variables, L"NOT PROP1")); + Assert::True(EvaluateConditionHelper(&variables, L"NOT (PROP1 <> \"VAL1\")")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" AND PROP2 = \"VAL2\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" AND PROP2 = \"NOT\"")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" OR PROP2 = \"VAL2\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" OR PROP2 = \"NOT\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" OR PROP2 = \"VAL2\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" OR PROP2 = \"NOT\"")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\" OR PROP3 = \"NOT\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"VAL3\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"NOT\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND (PROP2 = \"NOT\" OR PROP3 = \"VAL3\")")); + Assert::True(EvaluateConditionHelper(&variables, L"(PROP1 = \"VAL1\" AND PROP2 = \"VAL2\") OR PROP3 = \"NOT\"")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"VAL2\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP3 = \"VAL3\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); + Assert::True(EvaluateConditionHelper(&variables, L"(PROP3 = \"NOT\" OR PROP1 = \"VAL1\") AND PROP2 = \"VAL2\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP3 = \"NOT\" OR (PROP1 = \"VAL1\" AND PROP2 = \"VAL2\")")); + + Assert::True(EvaluateFailureConditionHelper(&variables, L"=")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"(PROP1")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"(PROP1 = \"")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"1A")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"*")); + + Assert::True(EvaluateFailureConditionHelper(&variables, L"1 == 1")); + } + finally + { + VariablesUninitialize(&variables); + } + } + + [NamedFact] + void VariablesSerializationTest() + { + HRESULT hr = S_OK; + BYTE* pbBuffer = NULL; + SIZE_T cbBuffer = 0; + SIZE_T iBuffer = 0; + BURN_VARIABLES variables1 = { }; + BURN_VARIABLES variables2 = { }; + try + { + // serialize + hr = VariableInitialize(&variables1); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + VariableSetStringHelper(&variables1, L"PROP1", L"VAL1"); + VariableSetNumericHelper(&variables1, L"PROP2", 2); + VariableSetVersionHelper(&variables1, L"PROP3", MAKEQWORDVERSION(1,1,1,1)); + VariableSetStringHelper(&variables1, L"PROP4", L"VAL4"); + + hr = VariableSerialize(&variables1, FALSE, &pbBuffer, &cbBuffer); + TestThrowOnFailure(hr, L"Failed to serialize variables."); + + // deserialize + hr = VariableInitialize(&variables2); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + hr = VariableDeserialize(&variables2, FALSE, pbBuffer, cbBuffer, &iBuffer); + TestThrowOnFailure(hr, L"Failed to deserialize variables."); + + Assert::Equal(gcnew String(L"VAL1"), VariableGetStringHelper(&variables2, L"PROP1")); + Assert::Equal(2ll, VariableGetNumericHelper(&variables2, L"PROP2")); + Assert::Equal(MAKEQWORDVERSION(1,1,1,1), VariableGetVersionHelper(&variables2, L"PROP3")); + Assert::Equal(gcnew String(L"VAL4"), VariableGetStringHelper(&variables2, L"PROP4")); + } + finally + { + ReleaseBuffer(pbBuffer); + VariablesUninitialize(&variables1); + VariablesUninitialize(&variables2); + } + } + + [NamedFact] + void VariablesBuiltInTest() + { + HRESULT hr = S_OK; + BURN_VARIABLES variables = { }; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // VersionMsi + Assert::True(EvaluateConditionHelper(&variables, L"VersionMsi >= v1.1")); + + // VersionNT + Assert::True(EvaluateConditionHelper(&variables, L"VersionNT <> v0.0.0.0")); + + // VersionNT64 + if (nullptr == Environment::GetEnvironmentVariable("ProgramFiles(x86)")) + { + Assert::False(EvaluateConditionHelper(&variables, L"VersionNT64")); + } + else + { + Assert::True(EvaluateConditionHelper(&variables, L"VersionNT64")); + } + + // attempt to set a built-in property + hr = VariableSetString(&variables, L"VersionNT", L"VAL", FALSE); + Assert::Equal(E_INVALIDARG, hr); + Assert::False(EvaluateConditionHelper(&variables, L"VersionNT = \"VAL\"")); + + VariableGetNumericHelper(&variables, L"NTProductType"); + VariableGetNumericHelper(&variables, L"NTSuiteBackOffice"); + VariableGetNumericHelper(&variables, L"NTSuiteDataCenter"); + VariableGetNumericHelper(&variables, L"NTSuiteEnterprise"); + VariableGetNumericHelper(&variables, L"NTSuitePersonal"); + VariableGetNumericHelper(&variables, L"NTSuiteSmallBusiness"); + VariableGetNumericHelper(&variables, L"NTSuiteSmallBusinessRestricted"); + VariableGetNumericHelper(&variables, L"NTSuiteWebServer"); + VariableGetNumericHelper(&variables, L"CompatibilityMode"); + VariableGetNumericHelper(&variables, L"Privileged"); + VariableGetNumericHelper(&variables, L"SystemLanguageID"); + VariableGetNumericHelper(&variables, L"TerminalServer"); + VariableGetNumericHelper(&variables, L"UserUILanguageID"); + VariableGetNumericHelper(&variables, L"UserLanguageID"); + + // known folders + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::ApplicationData) + "\\", VariableGetStringHelper(&variables, L"AppDataFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::CommonApplicationData) + "\\", VariableGetStringHelper(&variables, L"CommonAppDataFolder")); + + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::ProgramFiles) + "\\", VariableGetStringHelper(&variables, L"ProgramFilesFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::DesktopDirectory) + "\\", VariableGetStringHelper(&variables, L"DesktopFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Favorites) + "\\", VariableGetStringHelper(&variables, L"FavoritesFolder")); + VariableGetStringHelper(&variables, L"FontsFolder"); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData) + "\\", VariableGetStringHelper(&variables, L"LocalAppDataFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Personal) + "\\", VariableGetStringHelper(&variables, L"PersonalFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Programs) + "\\", VariableGetStringHelper(&variables, L"ProgramMenuFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::SendTo) + "\\", VariableGetStringHelper(&variables, L"SendToFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::StartMenu) + "\\", VariableGetStringHelper(&variables, L"StartMenuFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Startup) + "\\", VariableGetStringHelper(&variables, L"StartupFolder")); + VariableGetStringHelper(&variables, L"SystemFolder"); + VariableGetStringHelper(&variables, L"WindowsFolder"); + VariableGetStringHelper(&variables, L"WindowsVolume"); + + Assert::Equal(System::IO::Path::GetTempPath(), System::IO::Path::GetFullPath(VariableGetStringHelper(&variables, L"TempFolder"))); + + VariableGetStringHelper(&variables, L"AdminToolsFolder"); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::CommonProgramFiles) + "\\", VariableGetStringHelper(&variables, L"CommonFilesFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::MyPictures) + "\\", VariableGetStringHelper(&variables, L"MyPicturesFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Templates) + "\\", VariableGetStringHelper(&variables, L"TemplateFolder")); + + if (Environment::Is64BitOperatingSystem) + { + VariableGetStringHelper(&variables, L"ProgramFiles64Folder"); + VariableGetStringHelper(&variables, L"CommonFiles64Folder"); + VariableGetStringHelper(&variables, L"System64Folder"); + } + } + finally + { + VariablesUninitialize(&variables); + } + } + }; +} +} +} +} +} diff --git a/src/test/BurnUnitTest/precomp.cpp b/src/test/BurnUnitTest/precomp.cpp new file mode 100644 index 00000000..37664a1c --- /dev/null +++ b/src/test/BurnUnitTest/precomp.cpp @@ -0,0 +1,3 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" diff --git a/src/test/BurnUnitTest/precomp.h b/src/test/BurnUnitTest/precomp.h new file mode 100644 index 00000000..1f2ccb8b --- /dev/null +++ b/src/test/BurnUnitTest/precomp.h @@ -0,0 +1,78 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "wininet.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "BootstrapperEngine.h" +#include "BootstrapperApplication.h" + +#include "platform.h" +#include "variant.h" +#include "variable.h" +#include "condition.h" +#include "search.h" +#include "section.h" +#include "approvedexe.h" +#include "container.h" +#include "catalog.h" +#include "payload.h" +#include "cabextract.h" +#include "userexperience.h" +#include "package.h" +#include "update.h" +#include "pseudobundle.h" +#include "registration.h" +#include "plan.h" +#include "pipe.h" +#include "logging.h" +#include "core.h" +#include "cache.h" +#include "apply.h" +#include "exeengine.h" +#include "msiengine.h" +#include "mspengine.h" +#include "msuengine.h" +#include "dependency.h" +#include "elevation.h" +#include "embedded.h" +#include "manifest.h" +#include "splashscreen.h" +#include "bitsengine.h" + +#pragma managed +#include + +#include "BurnTestException.h" +#include "BurnTestFixture.h" +#include "BurnUnitTest.h" +#include "VariableHelpers.h" +#include "ManifestHelpers.h" -- cgit v1.2.3-55-g6feb From ed36894d8b4da2f28972811f39d5e3685964e413 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Thu, 16 Jul 2020 16:31:49 +1000 Subject: Integrate BurnUnitTest into latest v4. --- burn.sln | 8 +++ nuget.config | 1 + src/engine/engine.vcxproj | 8 +-- src/engine/packages.config | 4 +- src/stub/packages.config | 4 +- src/stub/stub.vcxproj | 8 +-- src/test/BurnUnitTest/BurnTestFixture.h | 30 +++++++- src/test/BurnUnitTest/BurnUnitTest.h | 41 +++++------ src/test/BurnUnitTest/BurnUnitTest.rc | 1 - src/test/BurnUnitTest/BurnUnitTest.vcxproj | 44 +++++++++--- src/test/BurnUnitTest/CacheTest.cpp | 9 ++- src/test/BurnUnitTest/ElevationTest.cpp | 7 +- src/test/BurnUnitTest/ManifestTest.cpp | 7 +- src/test/BurnUnitTest/RegistrationTest.cpp | 41 +++++------ src/test/BurnUnitTest/SearchTest.cpp | 86 +++++++++++++---------- src/test/BurnUnitTest/VariableTest.cpp | 106 +++++++++++++++-------------- src/test/BurnUnitTest/packages.config | 14 ++++ src/test/BurnUnitTest/precomp.h | 7 +- 18 files changed, 262 insertions(+), 164 deletions(-) create mode 100644 src/test/BurnUnitTest/packages.config diff --git a/burn.sln b/burn.sln index 0bdee49f..32fe11d5 100644 --- a/burn.sln +++ b/burn.sln @@ -7,6 +7,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "engine", "src\engine\engine EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stub", "src\stub\stub.vcxproj", "{C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BurnUnitTest", "src\test\BurnUnitTest\BurnUnitTest.vcxproj", "{9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -31,6 +33,12 @@ Global {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x64.Build.0 = Release|x64 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x86.ActiveCfg = Release|Win32 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x86.Build.0 = Release|Win32 + {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Debug|x64.ActiveCfg = Debug|Win32 + {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Debug|x86.ActiveCfg = Debug|Win32 + {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Debug|x86.Build.0 = Debug|Win32 + {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Release|x64.ActiveCfg = Release|Win32 + {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Release|x86.ActiveCfg = Release|Win32 + {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/nuget.config b/nuget.config index b6266ac2..ae9532bb 100644 --- a/nuget.config +++ b/nuget.config @@ -2,6 +2,7 @@ + diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index 2fe68c2a..6d064a43 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -3,7 +3,7 @@ - + @@ -161,13 +161,13 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + diff --git a/src/engine/packages.config b/src/engine/packages.config index 52ccddb4..98db4ce4 100644 --- a/src/engine/packages.config +++ b/src/engine/packages.config @@ -1,6 +1,6 @@  - + - + \ No newline at end of file diff --git a/src/stub/packages.config b/src/stub/packages.config index f3abcd35..9aabbf8c 100644 --- a/src/stub/packages.config +++ b/src/stub/packages.config @@ -3,6 +3,6 @@ - - + + \ No newline at end of file diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index 91366cad..d5644161 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -5,7 +5,7 @@ - + @@ -46,7 +46,7 @@ - + @@ -105,7 +105,7 @@ - - + + \ No newline at end of file diff --git a/src/test/BurnUnitTest/BurnTestFixture.h b/src/test/BurnUnitTest/BurnTestFixture.h index b89fe6fa..f158c192 100644 --- a/src/test/BurnUnitTest/BurnTestFixture.h +++ b/src/test/BurnUnitTest/BurnTestFixture.h @@ -13,8 +13,9 @@ namespace Test namespace Bootstrapper { using namespace System; + using namespace WixBuildTools::TestSupport; - public ref class BurnTestFixture + public ref class BurnTestFixture : IDisposable { public: BurnTestFixture() @@ -26,13 +27,40 @@ namespace Bootstrapper TestThrowOnFailure(hr, L"Failed to initialize Regutil."); PlatformInitialize(); + + this->testDirectory = WixBuildTools::TestSupport::TestData::Get(); + + LogInitialize(::GetModuleHandleW(NULL)); + + hr = LogOpen(NULL, L"BurnUnitTest", NULL, L"txt", FALSE, FALSE, NULL); + TestThrowOnFailure(hr, L"Failed to open log."); } ~BurnTestFixture() { XmlUninitialize(); RegUninitialize(); + LogUninitialize(FALSE); + } + + property String^ DataDirectory + { + String^ get() + { + return this->testDirectory; + } } + + property String^ TestDirectory + { + String^ get() + { + return this->testDirectory; + } + } + + private: + String^ testDirectory; }; } } diff --git a/src/test/BurnUnitTest/BurnUnitTest.h b/src/test/BurnUnitTest/BurnUnitTest.h index a4ca2707..ed1d2956 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.h +++ b/src/test/BurnUnitTest/BurnUnitTest.h @@ -13,40 +13,33 @@ namespace Test namespace Bootstrapper { using namespace System; - using namespace WixTest; using namespace Xunit; - public ref class BurnUnitTest : WixTestBase, IUseFixture + [CollectionDefinition("Burn")] + public ref class BurnCollectionDefinition : ICollectionFixture { - public: - BurnUnitTest() - { - } - - virtual void TestInitialize() override - { - WixTestBase::TestInitialize(); - - HRESULT hr = S_OK; - LogInitialize(::GetModuleHandleW(NULL)); + }; - hr = LogOpen(NULL, L"BurnUnitTest", NULL, L"txt", FALSE, FALSE, NULL); - TestThrowOnFailure(hr, L"Failed to open log."); - } - - virtual void TestUninitialize() override + [Collection("Burn")] + public ref class BurnUnitTest + { + public: + BurnUnitTest(BurnTestFixture^ fixture) { - LogUninitialize(FALSE); - - WixTestBase::TestUninitialize(); + this->testContext = fixture; } - virtual void SetFixture(BurnTestFixture^ fixture) + property BurnTestFixture^ TestContext { - // Don't care about the fixture, just need it to be created and disposed. - UNREFERENCED_PARAMETER(fixture); + BurnTestFixture^ get() + { + return this->testContext; + } } + + private: + BurnTestFixture^ testContext; }; } } diff --git a/src/test/BurnUnitTest/BurnUnitTest.rc b/src/test/BurnUnitTest/BurnUnitTest.rc index 159b0b42..3a815db2 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.rc +++ b/src/test/BurnUnitTest/BurnUnitTest.rc @@ -4,4 +4,3 @@ #define VER_ORIGINAL_FILENAME "BurnUnitTest.dll" #define VER_INTERNAL_NAME "setup" #define VER_FILE_DESCRIPTION "WiX Toolset Bootstrapper unit tests" -#include "wix.rc" diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index 5157d0d6..93b3e562 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -2,7 +2,10 @@ - + + + + Debug @@ -22,19 +25,25 @@ Unicode true - + + - $(WixRoot)src\libs\dutil\inc;$(WixRoot)src\burn\inc;$(WixRoot)src\burn\engine;$(WixRoot)src\libs\deputil\inc - cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;wintrust.lib;dutil.lib;deputil.lib;engine.lib;gdiplus.lib + ..\..\engine + cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;wintrust.lib;gdiplus.lib + + + Create + + 4564;4691 + - @@ -47,14 +56,31 @@ + - - - $(XunitPath)\xunit.dll + + + ..\..\..\packages\WixBuildTools.TestSupport.4.0.40\lib\net472\WixBuildTools.TestSupport.dll + + + ..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.40\lib\net472\WixBuildTools.TestSupport.Native.dll - + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + diff --git a/src/test/BurnUnitTest/CacheTest.cpp b/src/test/BurnUnitTest/CacheTest.cpp index 9b3c6e64..6d261842 100644 --- a/src/test/BurnUnitTest/CacheTest.cpp +++ b/src/test/BurnUnitTest/CacheTest.cpp @@ -15,13 +15,16 @@ namespace Bootstrapper { using namespace System; using namespace System::IO; - using namespace WixTest; using namespace Xunit; public ref class CacheTest : BurnUnitTest { public: - [NamedFact] + CacheTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact(Skip = "Currently fails")] void CacheSignatureTest() { HRESULT hr = S_OK; @@ -33,7 +36,7 @@ namespace Bootstrapper try { - pin_ptr dataDirectory = PtrToStringChars(TestContext->DataDirectory); + pin_ptr dataDirectory = PtrToStringChars(this->TestContext->DataDirectory); hr = PathConcat(dataDirectory, L"BurnTestPayloads\\Products\\TestExe\\TestExe.exe", &sczPayloadPath); Assert::True(S_OK == hr, "Failed to get path to test file."); Assert::True(FileExistsEx(sczPayloadPath, NULL), "Test file does not exist."); diff --git a/src/test/BurnUnitTest/ElevationTest.cpp b/src/test/BurnUnitTest/ElevationTest.cpp index bb10ce43..3d144128 100644 --- a/src/test/BurnUnitTest/ElevationTest.cpp +++ b/src/test/BurnUnitTest/ElevationTest.cpp @@ -39,13 +39,16 @@ namespace Bootstrapper using namespace System; using namespace System::IO; using namespace System::Threading; - using namespace WixTest; using namespace Xunit; public ref class ElevationTest : BurnUnitTest { public: - [NamedFact] + ElevationTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] void ElevateTest() { HRESULT hr = S_OK; diff --git a/src/test/BurnUnitTest/ManifestTest.cpp b/src/test/BurnUnitTest/ManifestTest.cpp index 14ead82e..963be156 100644 --- a/src/test/BurnUnitTest/ManifestTest.cpp +++ b/src/test/BurnUnitTest/ManifestTest.cpp @@ -13,13 +13,16 @@ namespace Test namespace Bootstrapper { using namespace System; - using namespace WixTest; using namespace Xunit; public ref class ManifestTest : BurnUnitTest { public: - [NamedFact] + ManifestTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] void ManifestLoadXmlTest() { HRESULT hr = S_OK; diff --git a/src/test/BurnUnitTest/RegistrationTest.cpp b/src/test/BurnUnitTest/RegistrationTest.cpp index 1ab6b8e9..9c7bf4ce 100644 --- a/src/test/BurnUnitTest/RegistrationTest.cpp +++ b/src/test/BurnUnitTest/RegistrationTest.cpp @@ -51,13 +51,16 @@ namespace Bootstrapper using namespace Microsoft::Win32; using namespace System; using namespace System::IO; - using namespace WixTest; using namespace Xunit; public ref class RegistrationTest : BurnUnitTest { public: - [NamedFact] + RegistrationTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] void RegisterBasicTest() { HRESULT hr = S_OK; @@ -115,7 +118,7 @@ namespace Bootstrapper Assert::True(File::Exists(Path::Combine(cacheDirectory, gcnew String(L"setup.exe")))); Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); - Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)(Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr))); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)(Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr))); // end session hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); @@ -145,7 +148,7 @@ namespace Bootstrapper } } - [NamedFact] + [Fact] void RegisterArpMinimumTest() { HRESULT hr = S_OK; @@ -204,7 +207,7 @@ namespace Bootstrapper // verify that registration was created Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); - Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); // complete registration hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); @@ -226,7 +229,7 @@ namespace Bootstrapper // verify that registration was updated Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); - Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); // delete registration hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); @@ -255,7 +258,7 @@ namespace Bootstrapper } } - [NamedFact] + [Fact] void RegisterArpFullTest() { HRESULT hr = S_OK; @@ -316,7 +319,7 @@ namespace Bootstrapper // verify that registration was created Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); - Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); // finish registration hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); @@ -327,15 +330,15 @@ namespace Bootstrapper Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); - Assert::Equal(gcnew String(L"DisplayName1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"DisplayName"), nullptr)); - Assert::Equal(gcnew String(L"1.2.3.4"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"DisplayVersion"), nullptr)); - Assert::Equal(gcnew String(L"Publisher1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Publisher"), nullptr)); - Assert::Equal(gcnew String(L"http://www.microsoft.com/help"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"HelpLink"), nullptr)); - Assert::Equal(gcnew String(L"555-555-5555"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"HelpTelephone"), nullptr)); - Assert::Equal(gcnew String(L"http://www.microsoft.com/about"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"URLInfoAbout"), nullptr)); - Assert::Equal(gcnew String(L"http://www.microsoft.com/update"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"URLUpdateInfo"), nullptr)); - Assert::Equal(gcnew String(L"Comments1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Comments"), nullptr)); - Assert::Equal(gcnew String(L"Contact1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Contact"), nullptr)); + Assert::Equal(gcnew String(L"DisplayName1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"DisplayName"), nullptr)); + Assert::Equal(gcnew String(L"1.2.3.4"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"DisplayVersion"), nullptr)); + Assert::Equal(gcnew String(L"Publisher1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Publisher"), nullptr)); + Assert::Equal(gcnew String(L"http://www.microsoft.com/help"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"HelpLink"), nullptr)); + Assert::Equal(gcnew String(L"555-555-5555"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"HelpTelephone"), nullptr)); + Assert::Equal(gcnew String(L"http://www.microsoft.com/about"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"URLInfoAbout"), nullptr)); + Assert::Equal(gcnew String(L"http://www.microsoft.com/update"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"URLUpdateInfo"), nullptr)); + Assert::Equal(gcnew String(L"Comments1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Comments"), nullptr)); + Assert::Equal(gcnew String(L"Contact1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Contact"), nullptr)); Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"NoModify"), nullptr)); Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"NoRemove"), nullptr)); @@ -349,7 +352,7 @@ namespace Bootstrapper // verify that registration was updated Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); - Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); // delete registration hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); @@ -378,7 +381,7 @@ namespace Bootstrapper } } - [NamedFact] + [Fact(Skip = "Currently fails")] void ResumeTest() { HRESULT hr = S_OK; diff --git a/src/test/BurnUnitTest/SearchTest.cpp b/src/test/BurnUnitTest/SearchTest.cpp index d03db84c..48ab60aa 100644 --- a/src/test/BurnUnitTest/SearchTest.cpp +++ b/src/test/BurnUnitTest/SearchTest.cpp @@ -46,13 +46,18 @@ namespace Bootstrapper public ref class SearchTest : BurnUnitTest { public: - [NamedFact] + SearchTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] void DirectorySearchTest() { HRESULT hr = S_OK; IXMLDOMElement* pixeBundle = NULL; BURN_VARIABLES variables = { }; BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; try { hr = VariableInitialize(&variables); @@ -73,7 +78,7 @@ namespace Bootstrapper // load XML document LoadBundleXmlHelper(wzDocument, &pixeBundle); - hr = SearchesParseFromXml(&searches, pixeBundle); + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); TestThrowOnFailure(hr, L"Failed to parse searches from XML."); // execute searches @@ -92,13 +97,14 @@ namespace Bootstrapper } } - [NamedFact] + [Fact(Skip = "Currently fails")] void FileSearchTest() { HRESULT hr = S_OK; IXMLDOMElement* pixeBundle = NULL; BURN_VARIABLES variables = { }; BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; ULARGE_INTEGER uliVersion = { }; try { @@ -124,7 +130,7 @@ namespace Bootstrapper // load XML document LoadBundleXmlHelper(wzDocument, &pixeBundle); - hr = SearchesParseFromXml(&searches, pixeBundle); + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); TestThrowOnFailure(hr, L"Failed to parse searches from XML."); // execute searches @@ -144,13 +150,14 @@ namespace Bootstrapper } } - [NamedFact] + [Fact] void RegistrySearchTest() { HRESULT hr = S_OK; IXMLDOMElement* pixeBundle = NULL; BURN_VARIABLES variables = { }; BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; HKEY hkey32 = NULL; HKEY hkey64 = NULL; BOOL f64bitMachine = (nullptr != Environment::GetEnvironmentVariable("ProgramFiles(x86)")); @@ -219,7 +226,7 @@ namespace Bootstrapper // load XML document LoadBundleXmlHelper(wzDocument, &pixeBundle); - hr = SearchesParseFromXml(&searches, pixeBundle); + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); TestThrowOnFailure(hr, L"Failed to parse searches from XML."); // execute searches @@ -231,31 +238,31 @@ namespace Bootstrapper Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable2")); Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable3")); Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable4")); - Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable5")); - Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable6")); - Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable7")); - Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable8")); - Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable9")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable5")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable6")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable7")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable8")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable9")); Assert::NotEqual(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable10")); Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable11")); Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable12")); Assert::Equal(MAKEQWORDVERSION(1,1,1,1), VariableGetVersionHelper(&variables, L"Variable13")); Assert::Equal(MAKEQWORDVERSION(1,1,1,1), VariableGetVersionHelper(&variables, L"Variable14")); - Assert::Equal(gcnew String(L"String1"), VariableGetStringHelper(&variables, L"Variable15")); + Assert::Equal(gcnew String(L"String1"), VariableGetStringHelper(&variables, L"Variable15")); Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable16")); Assert::False(VariableExistsHelper(&variables, L"Variable17")); Assert::False(VariableExistsHelper(&variables, L"Variable18")); Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable19")); - Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable20")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable20")); if (f64bitMachine) { - Assert::Equal(gcnew String(L"32-bit"), VariableGetStringHelper(&variables, L"Variable21")); - Assert::Equal(gcnew String(L"64-bit"), VariableGetStringHelper(&variables, L"Variable22")); + Assert::Equal(gcnew String(L"32-bit"), VariableGetStringHelper(&variables, L"Variable21")); + Assert::Equal(gcnew String(L"64-bit"), VariableGetStringHelper(&variables, L"Variable22")); } Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable23")); Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable24")); - Assert::Equal(gcnew String(L"Msi.Package"), VariableGetStringHelper(&variables, L"Variable25")); + Assert::Equal(gcnew String(L"Msi.Package"), VariableGetStringHelper(&variables, L"Variable25")); } finally { @@ -276,13 +283,14 @@ namespace Bootstrapper } } - [NamedFact] + [Fact] void MsiComponentSearchTest() { HRESULT hr = S_OK; IXMLDOMElement* pixeBundle = NULL; BURN_VARIABLES variables = { }; BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; try { hr = VariableInitialize(&variables); @@ -317,7 +325,7 @@ namespace Bootstrapper // load XML document LoadBundleXmlHelper(wzDocument, &pixeBundle); - hr = SearchesParseFromXml(&searches, pixeBundle); + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); TestThrowOnFailure(hr, L"Failed to parse searches from XML."); // execute searches @@ -328,22 +336,22 @@ namespace Bootstrapper Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable1")); Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable2")); Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable3")); - Assert::Equal(gcnew String(L"C:\\directory\\file1.txt"), VariableGetStringHelper(&variables, L"Variable4")); - Assert::Equal(gcnew String(L"C:\\directory\\file2.txt"), VariableGetStringHelper(&variables, L"Variable5")); - Assert::Equal(gcnew String(L"C:\\directory\\file3.txt"), VariableGetStringHelper(&variables, L"Variable6")); - Assert::Equal(gcnew String(L"C:\\directory\\file4.txt"), VariableGetStringHelper(&variables, L"Variable7")); - Assert::Equal(gcnew String(L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\"), VariableGetStringHelper(&variables, L"Variable8")); - Assert::Equal(gcnew String(L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), VariableGetStringHelper(&variables, L"Variable9")); + Assert::Equal(gcnew String(L"C:\\directory\\file1.txt"), VariableGetStringHelper(&variables, L"Variable4")); + Assert::Equal(gcnew String(L"C:\\directory\\file2.txt"), VariableGetStringHelper(&variables, L"Variable5")); + Assert::Equal(gcnew String(L"C:\\directory\\file3.txt"), VariableGetStringHelper(&variables, L"Variable6")); + Assert::Equal(gcnew String(L"C:\\directory\\file4.txt"), VariableGetStringHelper(&variables, L"Variable7")); + Assert::Equal(gcnew String(L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\"), VariableGetStringHelper(&variables, L"Variable8")); + Assert::Equal(gcnew String(L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), VariableGetStringHelper(&variables, L"Variable9")); Assert::Equal(3ll, VariableGetNumericHelper(&variables, L"Variable10")); Assert::Equal(3ll, VariableGetNumericHelper(&variables, L"Variable11")); Assert::Equal(4ll, VariableGetNumericHelper(&variables, L"Variable12")); Assert::Equal(4ll, VariableGetNumericHelper(&variables, L"Variable13")); Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable14")); - Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable15")); - Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable16")); - Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable17")); - Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable18")); - Assert::Equal(gcnew String(L"C:\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\file5.txt"), VariableGetStringHelper(&variables, L"Variable19")); + Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable15")); + Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable16")); + Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable17")); + Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable18")); + Assert::Equal(gcnew String(L"C:\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\file5.txt"), VariableGetStringHelper(&variables, L"Variable19")); } finally { @@ -353,13 +361,14 @@ namespace Bootstrapper } } - [NamedFact] + [Fact] void MsiProductSearchTest() { HRESULT hr = S_OK; IXMLDOMElement* pixeBundle = NULL; BURN_VARIABLES variables = { }; BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; try { hr = VariableInitialize(&variables); @@ -381,7 +390,7 @@ namespace Bootstrapper // load XML document LoadBundleXmlHelper(wzDocument, &pixeBundle); - hr = SearchesParseFromXml(&searches, pixeBundle); + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); TestThrowOnFailure(hr, L"Failed to parse searches from XML."); // execute searches @@ -404,13 +413,14 @@ namespace Bootstrapper } } - [NamedFact] + [Fact] void MsiFeatureSearchTest() { HRESULT hr = S_OK; IXMLDOMElement* pixeBundle = NULL; BURN_VARIABLES variables = { }; BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; try { LPCWSTR wzDocument = @@ -424,7 +434,7 @@ namespace Bootstrapper // load XML document LoadBundleXmlHelper(wzDocument, &pixeBundle); - hr = SearchesParseFromXml(&searches, pixeBundle); + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); TestThrowOnFailure(hr, L"Failed to parse searches from XML."); // execute searches @@ -439,13 +449,14 @@ namespace Bootstrapper } } - [NamedFact] + [Fact] void ConditionalSearchTest() { HRESULT hr = S_OK; IXMLDOMElement* pixeBundle = NULL; BURN_VARIABLES variables = { }; BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; try { LPCWSTR wzDocument = @@ -461,7 +472,7 @@ namespace Bootstrapper // load XML document LoadBundleXmlHelper(wzDocument, &pixeBundle); - hr = SearchesParseFromXml(&searches, pixeBundle); + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); TestThrowOnFailure(hr, L"Failed to parse searches from XML."); // execute searches @@ -480,13 +491,14 @@ namespace Bootstrapper SearchesUninitialize(&searches); } } - [NamedFact] + [Fact] void NoSearchesTest() { HRESULT hr = S_OK; IXMLDOMElement* pixeBundle = NULL; BURN_VARIABLES variables = { }; BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; try { LPCWSTR wzDocument = @@ -499,7 +511,7 @@ namespace Bootstrapper // load XML document LoadBundleXmlHelper(wzDocument, &pixeBundle); - hr = SearchesParseFromXml(&searches, pixeBundle); + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); TestThrowOnFailure(hr, L"Failed to parse searches from XML."); // execute searches diff --git a/src/test/BurnUnitTest/VariableTest.cpp b/src/test/BurnUnitTest/VariableTest.cpp index 3a4caecf..7d670744 100644 --- a/src/test/BurnUnitTest/VariableTest.cpp +++ b/src/test/BurnUnitTest/VariableTest.cpp @@ -20,7 +20,11 @@ namespace Bootstrapper public ref class VariableTest : BurnUnitTest { public: - [NamedFact] + VariableTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] void VariablesBasicTest() { HRESULT hr = S_OK; @@ -48,19 +52,19 @@ namespace Bootstrapper VariableSetStringHelper(&variables, L"OVERWRITTEN_NUMBER", L"NEW"); // get and verify variable values - Assert::Equal(gcnew String(L"VAL1"), VariableGetStringHelper(&variables, L"PROP1")); + Assert::Equal(gcnew String(L"VAL1"), VariableGetStringHelper(&variables, L"PROP1")); Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"PROP2")); - Assert::Equal(gcnew String(L"2"), VariableGetStringHelper(&variables, L"PROP2")); - Assert::Equal(gcnew String(L"VAL3"), VariableGetStringHelper(&variables, L"PROP3")); - Assert::Equal(gcnew String(L"VAL4"), VariableGetStringHelper(&variables, L"PROP4")); - Assert::Equal(gcnew String(L"VAL5"), VariableGetStringHelper(&variables, L"PROP5")); - Assert::Equal(gcnew String(L"VAL6"), VariableGetStringHelper(&variables, L"PROP6")); + Assert::Equal(gcnew String(L"2"), VariableGetStringHelper(&variables, L"PROP2")); + Assert::Equal(gcnew String(L"VAL3"), VariableGetStringHelper(&variables, L"PROP3")); + Assert::Equal(gcnew String(L"VAL4"), VariableGetStringHelper(&variables, L"PROP4")); + Assert::Equal(gcnew String(L"VAL5"), VariableGetStringHelper(&variables, L"PROP5")); + Assert::Equal(gcnew String(L"VAL6"), VariableGetStringHelper(&variables, L"PROP6")); Assert::Equal(7ll, VariableGetNumericHelper(&variables, L"PROP7")); Assert::Equal(MAKEQWORDVERSION(1,1,0,0), VariableGetVersionHelper(&variables, L"PROP8")); - Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetStringHelper(&variables, L"PROP8")); + Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetStringHelper(&variables, L"PROP8")); Assert::Equal(42ll, VariableGetNumericHelper(&variables, L"OVERWRITTEN_STRING")); - Assert::Equal(gcnew String(L"NEW"), VariableGetStringHelper(&variables, L"OVERWRITTEN_NUMBER")); + Assert::Equal(gcnew String(L"NEW"), VariableGetStringHelper(&variables, L"OVERWRITTEN_NUMBER")); } finally { @@ -68,7 +72,7 @@ namespace Bootstrapper } } - [NamedFact] + [Fact] void VariablesParseXmlTest() { HRESULT hr = S_OK; @@ -101,7 +105,7 @@ namespace Bootstrapper Assert::Equal((int)BURN_VARIANT_TYPE_NONE, VariableGetTypeHelper(&variables, L"Var4")); Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Var1")); - Assert::Equal(gcnew String(L"String value."), VariableGetStringHelper(&variables, L"Var2")); + Assert::Equal(gcnew String(L"String value."), VariableGetStringHelper(&variables, L"Var2")); Assert::Equal(MAKEQWORDVERSION(1,2,3,4), VariableGetVersionHelper(&variables, L"Var3")); } finally @@ -111,7 +115,7 @@ namespace Bootstrapper } } - [NamedFact] + [Fact] void VariablesFormatTest() { HRESULT hr = S_OK; @@ -129,21 +133,21 @@ namespace Bootstrapper VariableSetNumericHelper(&variables, L"PROP3", 3); // test string formatting - Assert::Equal(gcnew String(L"NOPROP"), VariableFormatStringHelper(&variables, L"NOPROP")); - Assert::Equal(gcnew String(L"VAL1"), VariableFormatStringHelper(&variables, L"[PROP1]")); - Assert::Equal(gcnew String(L" VAL1 "), VariableFormatStringHelper(&variables, L" [PROP1] ")); - Assert::Equal(gcnew String(L"PRE VAL1"), VariableFormatStringHelper(&variables, L"PRE [PROP1]")); - Assert::Equal(gcnew String(L"VAL1 POST"), VariableFormatStringHelper(&variables, L"[PROP1] POST")); - Assert::Equal(gcnew String(L"PRE VAL1 POST"), VariableFormatStringHelper(&variables, L"PRE [PROP1] POST")); - Assert::Equal(gcnew String(L"VAL1 MID VAL2"), VariableFormatStringHelper(&variables, L"[PROP1] MID [PROP2]")); - Assert::Equal(gcnew String(L""), VariableFormatStringHelper(&variables, L"[NONE]")); - Assert::Equal(gcnew String(L""), VariableFormatStringHelper(&variables, L"[prop1]")); - Assert::Equal(gcnew String(L"["), VariableFormatStringHelper(&variables, L"[\\[]")); - Assert::Equal(gcnew String(L"]"), VariableFormatStringHelper(&variables, L"[\\]]")); - Assert::Equal(gcnew String(L"[]"), VariableFormatStringHelper(&variables, L"[]")); - Assert::Equal(gcnew String(L"[NONE"), VariableFormatStringHelper(&variables, L"[NONE")); - Assert::Equal(gcnew String(L"VAL2"), VariableGetFormattedHelper(&variables, L"PROP2")); - Assert::Equal(gcnew String(L"3"), VariableGetFormattedHelper(&variables, L"PROP3")); + Assert::Equal(gcnew String(L"NOPROP"), VariableFormatStringHelper(&variables, L"NOPROP")); + Assert::Equal(gcnew String(L"VAL1"), VariableFormatStringHelper(&variables, L"[PROP1]")); + Assert::Equal(gcnew String(L" VAL1 "), VariableFormatStringHelper(&variables, L" [PROP1] ")); + Assert::Equal(gcnew String(L"PRE VAL1"), VariableFormatStringHelper(&variables, L"PRE [PROP1]")); + Assert::Equal(gcnew String(L"VAL1 POST"), VariableFormatStringHelper(&variables, L"[PROP1] POST")); + Assert::Equal(gcnew String(L"PRE VAL1 POST"), VariableFormatStringHelper(&variables, L"PRE [PROP1] POST")); + Assert::Equal(gcnew String(L"VAL1 MID VAL2"), VariableFormatStringHelper(&variables, L"[PROP1] MID [PROP2]")); + Assert::Equal(gcnew String(L""), VariableFormatStringHelper(&variables, L"[NONE]")); + Assert::Equal(gcnew String(L""), VariableFormatStringHelper(&variables, L"[prop1]")); + Assert::Equal(gcnew String(L"["), VariableFormatStringHelper(&variables, L"[\\[]")); + Assert::Equal(gcnew String(L"]"), VariableFormatStringHelper(&variables, L"[\\]]")); + Assert::Equal(gcnew String(L"[]"), VariableFormatStringHelper(&variables, L"[]")); + Assert::Equal(gcnew String(L"[NONE"), VariableFormatStringHelper(&variables, L"[NONE")); + Assert::Equal(gcnew String(L"VAL2"), VariableGetFormattedHelper(&variables, L"PROP2")); + Assert::Equal(gcnew String(L"3"), VariableGetFormattedHelper(&variables, L"PROP3")); hr = VariableFormatString(&variables, L"PRE [PROP1] POST", &scz, &cch); TestThrowOnFailure(hr, L"Failed to format string"); @@ -162,16 +166,16 @@ namespace Bootstrapper } } - [NamedFact] + [Fact] void VariablesEscapeTest() { // test string escaping - Assert::Equal(gcnew String(L"[\\[]"), VariableEscapeStringHelper(L"[")); - Assert::Equal(gcnew String(L"[\\]]"), VariableEscapeStringHelper(L"]")); - Assert::Equal(gcnew String(L" [\\[]TEXT[\\]] "), VariableEscapeStringHelper(L" [TEXT] ")); + Assert::Equal(gcnew String(L"[\\[]"), VariableEscapeStringHelper(L"[")); + Assert::Equal(gcnew String(L"[\\]]"), VariableEscapeStringHelper(L"]")); + Assert::Equal(gcnew String(L" [\\[]TEXT[\\]] "), VariableEscapeStringHelper(L" [TEXT] ")); } - [NamedFact] + [Fact] void VariablesConditionTest() { HRESULT hr = S_OK; @@ -346,7 +350,7 @@ namespace Bootstrapper } } - [NamedFact] + [Fact] void VariablesSerializationTest() { HRESULT hr = S_OK; @@ -376,10 +380,10 @@ namespace Bootstrapper hr = VariableDeserialize(&variables2, FALSE, pbBuffer, cbBuffer, &iBuffer); TestThrowOnFailure(hr, L"Failed to deserialize variables."); - Assert::Equal(gcnew String(L"VAL1"), VariableGetStringHelper(&variables2, L"PROP1")); + Assert::Equal(gcnew String(L"VAL1"), VariableGetStringHelper(&variables2, L"PROP1")); Assert::Equal(2ll, VariableGetNumericHelper(&variables2, L"PROP2")); Assert::Equal(MAKEQWORDVERSION(1,1,1,1), VariableGetVersionHelper(&variables2, L"PROP3")); - Assert::Equal(gcnew String(L"VAL4"), VariableGetStringHelper(&variables2, L"PROP4")); + Assert::Equal(gcnew String(L"VAL4"), VariableGetStringHelper(&variables2, L"PROP4")); } finally { @@ -389,7 +393,7 @@ namespace Bootstrapper } } - [NamedFact] + [Fact] void VariablesBuiltInTest() { HRESULT hr = S_OK; @@ -436,29 +440,29 @@ namespace Bootstrapper VariableGetNumericHelper(&variables, L"UserLanguageID"); // known folders - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::ApplicationData) + "\\", VariableGetStringHelper(&variables, L"AppDataFolder")); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::CommonApplicationData) + "\\", VariableGetStringHelper(&variables, L"CommonAppDataFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::ApplicationData) + "\\", VariableGetStringHelper(&variables, L"AppDataFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::CommonApplicationData) + "\\", VariableGetStringHelper(&variables, L"CommonAppDataFolder")); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::ProgramFiles) + "\\", VariableGetStringHelper(&variables, L"ProgramFilesFolder")); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::DesktopDirectory) + "\\", VariableGetStringHelper(&variables, L"DesktopFolder")); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Favorites) + "\\", VariableGetStringHelper(&variables, L"FavoritesFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::ProgramFiles) + "\\", VariableGetStringHelper(&variables, L"ProgramFilesFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::DesktopDirectory) + "\\", VariableGetStringHelper(&variables, L"DesktopFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Favorites) + "\\", VariableGetStringHelper(&variables, L"FavoritesFolder")); VariableGetStringHelper(&variables, L"FontsFolder"); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData) + "\\", VariableGetStringHelper(&variables, L"LocalAppDataFolder")); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Personal) + "\\", VariableGetStringHelper(&variables, L"PersonalFolder")); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Programs) + "\\", VariableGetStringHelper(&variables, L"ProgramMenuFolder")); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::SendTo) + "\\", VariableGetStringHelper(&variables, L"SendToFolder")); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::StartMenu) + "\\", VariableGetStringHelper(&variables, L"StartMenuFolder")); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Startup) + "\\", VariableGetStringHelper(&variables, L"StartupFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData) + "\\", VariableGetStringHelper(&variables, L"LocalAppDataFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Personal) + "\\", VariableGetStringHelper(&variables, L"PersonalFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Programs) + "\\", VariableGetStringHelper(&variables, L"ProgramMenuFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::SendTo) + "\\", VariableGetStringHelper(&variables, L"SendToFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::StartMenu) + "\\", VariableGetStringHelper(&variables, L"StartMenuFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Startup) + "\\", VariableGetStringHelper(&variables, L"StartupFolder")); VariableGetStringHelper(&variables, L"SystemFolder"); VariableGetStringHelper(&variables, L"WindowsFolder"); VariableGetStringHelper(&variables, L"WindowsVolume"); - Assert::Equal(System::IO::Path::GetTempPath(), System::IO::Path::GetFullPath(VariableGetStringHelper(&variables, L"TempFolder"))); + Assert::Equal(System::IO::Path::GetTempPath(), System::IO::Path::GetFullPath(VariableGetStringHelper(&variables, L"TempFolder"))); VariableGetStringHelper(&variables, L"AdminToolsFolder"); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::CommonProgramFiles) + "\\", VariableGetStringHelper(&variables, L"CommonFilesFolder")); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::MyPictures) + "\\", VariableGetStringHelper(&variables, L"MyPicturesFolder")); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Templates) + "\\", VariableGetStringHelper(&variables, L"TemplateFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::CommonProgramFiles) + "\\", VariableGetStringHelper(&variables, L"CommonFilesFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::MyPictures) + "\\", VariableGetStringHelper(&variables, L"MyPicturesFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Templates) + "\\", VariableGetStringHelper(&variables, L"TemplateFolder")); if (Environment::Is64BitOperatingSystem) { diff --git a/src/test/BurnUnitTest/packages.config b/src/test/BurnUnitTest/packages.config new file mode 100644 index 00000000..27527ed6 --- /dev/null +++ b/src/test/BurnUnitTest/packages.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/BurnUnitTest/precomp.h b/src/test/BurnUnitTest/precomp.h index 1f2ccb8b..e288eb3e 100644 --- a/src/test/BurnUnitTest/precomp.h +++ b/src/test/BurnUnitTest/precomp.h @@ -30,22 +30,23 @@ #include #include -#include - #include "BootstrapperEngine.h" #include "BootstrapperApplication.h" +#include "BundleExtensionEngine.h" +#include "BundleExtension.h" #include "platform.h" #include "variant.h" #include "variable.h" #include "condition.h" -#include "search.h" #include "section.h" #include "approvedexe.h" #include "container.h" #include "catalog.h" #include "payload.h" #include "cabextract.h" +#include "burnextension.h" +#include "search.h" #include "userexperience.h" #include "package.h" #include "update.h" -- cgit v1.2.3-55-g6feb From e845e7c68ae792ab2ccd4ca2f054700dde624375 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 17 Jul 2020 21:28:38 +1000 Subject: Add VariantTest. --- src/test/BurnUnitTest/BurnTestFixture.h | 4 + src/test/BurnUnitTest/BurnUnitTest.vcxproj | 1 + src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters | 11 +- src/test/BurnUnitTest/VariantTest.cpp | 197 +++++++++++++++++++++ 4 files changed, 209 insertions(+), 4 deletions(-) create mode 100644 src/test/BurnUnitTest/VariantTest.cpp diff --git a/src/test/BurnUnitTest/BurnTestFixture.h b/src/test/BurnUnitTest/BurnTestFixture.h index f158c192..6b041641 100644 --- a/src/test/BurnUnitTest/BurnTestFixture.h +++ b/src/test/BurnUnitTest/BurnTestFixture.h @@ -26,6 +26,9 @@ namespace Bootstrapper hr = RegInitialize(); TestThrowOnFailure(hr, L"Failed to initialize Regutil."); + hr = CrypInitialize(); + TestThrowOnFailure(hr, L"Failed to initialize Cryputil."); + PlatformInitialize(); this->testDirectory = WixBuildTools::TestSupport::TestData::Get(); @@ -38,6 +41,7 @@ namespace Bootstrapper ~BurnTestFixture() { + CrypUninitialize(); XmlUninitialize(); RegUninitialize(); LogUninitialize(FALSE); diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index 93b3e562..3f3c7b4f 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -46,6 +46,7 @@ + diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters b/src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters index bee5d15e..14261ba0 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters @@ -18,6 +18,9 @@ Source Files + + Source Files + Source Files @@ -27,6 +30,9 @@ Source Files + + Source Files + Source Files @@ -39,10 +45,7 @@ Source Files - - Source Files - - + Source Files diff --git a/src/test/BurnUnitTest/VariantTest.cpp b/src/test/BurnUnitTest/VariantTest.cpp new file mode 100644 index 00000000..dd679f08 --- /dev/null +++ b/src/test/BurnUnitTest/VariantTest.cpp @@ -0,0 +1,197 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace Xunit; + + public ref class VariantTest : BurnUnitTest + { + public: + VariantTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] + void VariantBasicTest() + { + BURN_VARIANT expectedVariants[8]; + BURN_VARIANT actualVariants[8]; + for (DWORD i = 0; i < 8; i++) + { + BVariantUninitialize(expectedVariants + i); + BVariantUninitialize(actualVariants + i); + } + + try + { + InitNumericValue(expectedVariants + 0, 2, FALSE, L"PROP1", actualVariants + 0); + InitStringValue(expectedVariants + 1, L"VAL2", FALSE, L"PROP2", actualVariants + 1); + InitVersionValue(expectedVariants + 2, MAKEQWORDVERSION(1, 1, 0, 0), FALSE, L"PROP3", actualVariants + 2); + InitNoneValue(expectedVariants + 3, FALSE, L"PROP4", actualVariants + 3); + InitNoneValue(expectedVariants + 4, TRUE, L"PROP5", actualVariants + 4); + InitVersionValue(expectedVariants + 5, MAKEQWORDVERSION(1, 1, 1, 0), TRUE, L"PROP6", actualVariants + 5); + InitStringValue(expectedVariants + 6, L"7", TRUE, L"PROP7", actualVariants + 6); + InitNumericValue(expectedVariants + 7, 11, TRUE, L"PROP8", actualVariants + 7); + + VerifyNumericValue(expectedVariants + 0, actualVariants + 0); + VerifyStringValue(expectedVariants + 1, actualVariants + 1); + VerifyVersionValue(expectedVariants + 2, actualVariants + 2); + VerifyNoneValue(expectedVariants + 3, actualVariants + 3); + VerifyNoneValue(expectedVariants + 4, actualVariants + 4); + VerifyVersionValue(expectedVariants + 5, actualVariants + 5); + VerifyStringValue(expectedVariants + 6, actualVariants + 6); + VerifyNumericValue(expectedVariants + 7, actualVariants + 7); + } + finally + { + for (DWORD i = 0; i < 8; i++) + { + BVariantUninitialize(expectedVariants + i); + BVariantUninitialize(actualVariants + i); + } + } + } + + private: + void InitNoneValue(BURN_VARIANT* pValue, BOOL fHidden, LPCWSTR wz, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + pValue->Type = BURN_VARIANT_TYPE_NONE; + + hr = BVariantCopy(pValue, pActualValue); + NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); + + if (fHidden) + { + hr = BVariantSetEncryption(pActualValue, TRUE); + NativeAssert::Succeeded(hr, "Failed to encrypt variant {0}", wz); + + NativeAssert::True(pActualValue->fEncryptValue); + } + } + + void InitNumericValue(BURN_VARIANT* pValue, LONGLONG llValue, BOOL fHidden, LPCWSTR wz, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + pValue->Type = BURN_VARIANT_TYPE_NUMERIC; + pValue->llValue = llValue; + + hr = BVariantCopy(pValue, pActualValue); + NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); + + if (fHidden) + { + hr = BVariantSetEncryption(pActualValue, TRUE); + NativeAssert::Succeeded(hr, "Failed to encrypt variant {0}", wz); + + NativeAssert::True(pActualValue->fEncryptValue); + } + } + + void InitStringValue(BURN_VARIANT* pValue, LPWSTR wzValue, BOOL fHidden, LPCWSTR wz, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + pValue->Type = BURN_VARIANT_TYPE_STRING; + + hr = StrAllocString(&pValue->sczValue, wzValue, 0); + NativeAssert::Succeeded(hr, "Failed to alloc string: {0}", wzValue); + + hr = BVariantCopy(pValue, pActualValue); + NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); + + if (fHidden) + { + hr = BVariantSetEncryption(pActualValue, TRUE); + NativeAssert::Succeeded(hr, "Failed to encrypt variant {0}", wz); + + NativeAssert::True(pActualValue->fEncryptValue); + } + } + + void InitVersionValue(BURN_VARIANT* pValue, DWORD64 qwValue, BOOL fHidden, LPCWSTR wz, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + pValue->Type = BURN_VARIANT_TYPE_VERSION; + pValue->qwValue = qwValue; + + hr = BVariantCopy(pValue, pActualValue); + NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); + + if (fHidden) + { + hr = BVariantSetEncryption(pActualValue, TRUE); + NativeAssert::Succeeded(hr, "Failed to encrypt variant {0}", wz); + + NativeAssert::True(pActualValue->fEncryptValue); + } + } + + void VerifyNumericValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + LONGLONG llValue = 0; + NativeAssert::Equal(BURN_VARIANT_TYPE_NUMERIC, pExpectedValue->Type); + NativeAssert::Equal(BURN_VARIANT_TYPE_NUMERIC, pActualValue->Type); + + hr = BVariantGetNumeric(pActualValue, &llValue); + NativeAssert::Succeeded(hr, "Failed to get numeric value"); + + NativeAssert::Equal(pExpectedValue->llValue, llValue); + } + + void VerifyNoneValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) + { + NativeAssert::Equal(BURN_VARIANT_TYPE_NONE, pExpectedValue->Type); + NativeAssert::Equal(BURN_VARIANT_TYPE_NONE, pActualValue->Type); + NativeAssert::Equal(pExpectedValue->llValue, pActualValue->llValue); + } + + void VerifyStringValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + NativeAssert::Equal(BURN_VARIANT_TYPE_STRING, pExpectedValue->Type); + NativeAssert::Equal(BURN_VARIANT_TYPE_STRING, pActualValue->Type); + + try + { + hr = BVariantGetString(pActualValue, &sczValue); + NativeAssert::Succeeded(hr, "Failed to get numeric value"); + + NativeAssert::StringEqual(pExpectedValue->sczValue, sczValue); + } + finally + { + ReleaseStr(sczValue); + } + } + + void VerifyVersionValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + DWORD64 qwValue = 0; + NativeAssert::Equal(BURN_VARIANT_TYPE_VERSION, pExpectedValue->Type); + NativeAssert::Equal(BURN_VARIANT_TYPE_VERSION, pActualValue->Type); + + hr = BVariantGetVersion(pActualValue, &qwValue); + NativeAssert::Succeeded(hr, "Failed to get numeric value"); + + NativeAssert::Equal(pExpectedValue->qwValue, qwValue); + } + }; +} +} +} +} +} -- cgit v1.2.3-55-g6feb From 6d763d9c86405644cc72530ad64978efd6ba5828 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 17 Jul 2020 21:48:30 +1000 Subject: Only support encrypting string variants. --- src/engine/variant.cpp | 140 +++++++--------------------------- src/engine/variant.h | 3 +- src/test/BurnUnitTest/VariantTest.cpp | 8 +- 3 files changed, 32 insertions(+), 119 deletions(-) diff --git a/src/engine/variant.cpp b/src/engine/variant.cpp index 2a9f08ed..5e9bd29a 100644 --- a/src/engine/variant.cpp +++ b/src/engine/variant.cpp @@ -6,22 +6,12 @@ // internal function declarations -static HRESULT BVariantEncryptNumeric( - __in BURN_VARIANT* pVariant, - __in BOOL fEncrypt - ); - static HRESULT BVariantEncryptString( __in BURN_VARIANT* pVariant, __in BOOL fEncrypt ); -static HRESULT BVariantEncryptVersion( - __in BURN_VARIANT* pVariant, - __in BOOL fEncrypt - ); - -static HRESULT BVariantRetrieveDecryptedNumeric( +static void BVariantRetrieveNumeric( __in BURN_VARIANT* pVariant, __out LONGLONG* pllValue ); @@ -31,7 +21,7 @@ static HRESULT BVariantRetrieveDecryptedString( __out LPWSTR* psczValue ); -static HRESULT BVariantRetrieveDecryptedVersion( +static void BVariantRetrieveVersion( __in BURN_VARIANT* pVariant, __out DWORD64* pqwValue ); @@ -61,7 +51,7 @@ extern "C" HRESULT BVariantGetNumeric( switch (pVariant->Type) { case BURN_VARIANT_TYPE_NUMERIC: - BVariantRetrieveDecryptedNumeric(pVariant, pllValue); + BVariantRetrieveNumeric(pVariant, pllValue); break; case BURN_VARIANT_TYPE_STRING: hr = BVariantRetrieveDecryptedString(pVariant, &sczValue); @@ -76,7 +66,7 @@ extern "C" HRESULT BVariantGetNumeric( StrSecureZeroFreeString(sczValue); break; case BURN_VARIANT_TYPE_VERSION: - BVariantRetrieveDecryptedVersion(pVariant, (DWORD64*)pllValue); + BVariantRetrieveVersion(pVariant, (DWORD64*)pllValue); break; default: hr = E_INVALIDARG; @@ -99,7 +89,7 @@ extern "C" HRESULT BVariantGetString( switch (pVariant->Type) { case BURN_VARIANT_TYPE_NUMERIC: - hr = BVariantRetrieveDecryptedNumeric(pVariant, &llValue); + BVariantRetrieveNumeric(pVariant, &llValue); if (SUCCEEDED(hr)) { hr = StrAllocFormattedSecure(psczValue, L"%I64d", llValue); @@ -111,7 +101,7 @@ extern "C" HRESULT BVariantGetString( hr = BVariantRetrieveDecryptedString(pVariant, psczValue); break; case BURN_VARIANT_TYPE_VERSION: - hr = BVariantRetrieveDecryptedVersion(pVariant, &qwValue); + BVariantRetrieveVersion(pVariant, &qwValue); if (SUCCEEDED(hr)) { hr = StrAllocFormattedSecure(psczValue, L"%hu.%hu.%hu.%hu", @@ -144,7 +134,7 @@ extern "C" HRESULT BVariantGetVersion( switch (pVariant->Type) { case BURN_VARIANT_TYPE_NUMERIC: - BVariantRetrieveDecryptedNumeric(pVariant, (LONGLONG*)pqwValue); + BVariantRetrieveNumeric(pVariant, (LONGLONG*)pqwValue); break; case BURN_VARIANT_TYPE_STRING: hr = BVariantRetrieveDecryptedString(pVariant, &sczValue); @@ -159,7 +149,7 @@ extern "C" HRESULT BVariantGetVersion( StrSecureZeroFreeString(sczValue); break; case BURN_VARIANT_TYPE_VERSION: - BVariantRetrieveDecryptedVersion(pVariant, pqwValue); + BVariantRetrieveVersion(pVariant, pqwValue); break; default: hr = E_INVALIDARG; @@ -175,7 +165,7 @@ extern "C" HRESULT BVariantSetNumeric( ) { HRESULT hr = S_OK; - BOOL fEncryptValue = pVariant->fEncryptValue; + BOOL fEncrypt = pVariant->fEncryptString; if (BURN_VARIANT_TYPE_STRING == pVariant->Type) { @@ -184,7 +174,7 @@ extern "C" HRESULT BVariantSetNumeric( memset(pVariant, 0, sizeof(BURN_VARIANT)); pVariant->llValue = llValue; pVariant->Type = BURN_VARIANT_TYPE_NUMERIC; - BVariantSetEncryption(pVariant, fEncryptValue); + BVariantSetEncryption(pVariant, fEncrypt); return hr; } @@ -196,7 +186,7 @@ extern "C" HRESULT BVariantSetString( ) { HRESULT hr = S_OK; - BOOL fEncryptValue = pVariant->fEncryptValue; + BOOL fEncrypt = pVariant->fEncryptString; if (!wzValue) // if we're nulling out the string, make the variable NONE. { @@ -211,7 +201,7 @@ extern "C" HRESULT BVariantSetString( else { // We're about to copy an unencrypted value. - pVariant->fEncryptValue = FALSE; + pVariant->fEncryptString = FALSE; } hr = StrAllocStringSecure(&pVariant->sczValue, wzValue, cchValue); @@ -221,7 +211,7 @@ extern "C" HRESULT BVariantSetString( } LExit: - BVariantSetEncryption(pVariant, fEncryptValue); + BVariantSetEncryption(pVariant, fEncrypt); return hr; } @@ -231,7 +221,7 @@ extern "C" HRESULT BVariantSetVersion( ) { HRESULT hr = S_OK; - BOOL fEncryptValue = pVariant->fEncryptValue; + BOOL fEncryptValue = pVariant->fEncryptString; if (BURN_VARIANT_TYPE_STRING == pVariant->Type) { @@ -254,7 +244,7 @@ extern "C" HRESULT BVariantSetValue( LONGLONG llValue = 0; LPWSTR sczValue = NULL; DWORD64 qwValue = 0; - BOOL fEncrypt = pVariant->fEncryptValue; + BOOL fEncrypt = pVariant->fEncryptString; switch (pValue->Type) { @@ -341,7 +331,7 @@ extern "C" HRESULT BVariantCopy( } ExitOnFailure(hr, "Failed to copy variant."); - hr = BVariantSetEncryption(pTarget, pSource->fEncryptValue); + hr = BVariantSetEncryption(pTarget, pSource->fEncryptString); LExit: return hr; @@ -354,7 +344,7 @@ extern "C" HRESULT BVariantChangeType( { HRESULT hr = S_OK; BURN_VARIANT variant = { }; - BOOL fEncryptValue = pVariant->fEncryptValue; + BOOL fEncrypt = pVariant->fEncryptString; if (pVariant->Type == type) { @@ -384,7 +374,7 @@ extern "C" HRESULT BVariantChangeType( BVariantUninitialize(pVariant); memcpy_s(pVariant, sizeof(BURN_VARIANT), &variant, sizeof(BURN_VARIANT)); SecureZeroMemory(&variant, sizeof(BURN_VARIANT)); - BVariantSetEncryption(pVariant, fEncryptValue); + BVariantSetEncryption(pVariant, fEncrypt); LExit: return hr; @@ -397,7 +387,7 @@ extern "C" HRESULT BVariantSetEncryption( { HRESULT hr = S_OK; - if (pVariant->fEncryptValue == fEncrypt) + if (pVariant->fEncryptString == fEncrypt) { // The requested encryption state is already applied. ExitFunction(); @@ -406,47 +396,23 @@ extern "C" HRESULT BVariantSetEncryption( switch (pVariant->Type) { case BURN_VARIANT_TYPE_NONE: - hr = S_OK; - break; case BURN_VARIANT_TYPE_NUMERIC: - hr = BVariantEncryptNumeric(pVariant, fEncrypt); + case BURN_VARIANT_TYPE_VERSION: + hr = S_OK; break; case BURN_VARIANT_TYPE_STRING: hr = BVariantEncryptString(pVariant, fEncrypt); break; - case BURN_VARIANT_TYPE_VERSION: - hr = BVariantEncryptVersion(pVariant, fEncrypt); - break; default: hr = E_INVALIDARG; } ExitOnFailure(hr, "Failed to set the variant's encryption state"); - pVariant->fEncryptValue = fEncrypt; + pVariant->fEncryptString = fEncrypt; LExit: return hr; } -static HRESULT BVariantEncryptNumeric( - __in BURN_VARIANT* pVariant, - __in BOOL fEncrypt - ) -{ - HRESULT hr = S_OK; - - if (fEncrypt) - { - hr = CrypEncryptMemory(&pVariant->llValue, sizeof(pVariant->encryptionPadding), VARIANT_ENCRYPTION_SCOPE); - } - else - { - hr = CrypDecryptMemory(&pVariant->llValue, sizeof(pVariant->encryptionPadding), VARIANT_ENCRYPTION_SCOPE); - } - -//LExit: - return hr; -} - static HRESULT BVariantEncryptString( __in BURN_VARIANT* pVariant, __in BOOL fEncrypt @@ -496,50 +462,14 @@ LExit: return hr; } -static HRESULT BVariantEncryptVersion( - __in BURN_VARIANT* pVariant, - __in BOOL fEncrypt - ) -{ - HRESULT hr = S_OK; - - if (fEncrypt) - { - hr = CrypEncryptMemory(&pVariant->qwValue, sizeof(pVariant->encryptionPadding), VARIANT_ENCRYPTION_SCOPE); - } - else - { - hr = CrypDecryptMemory(&pVariant->qwValue, sizeof(pVariant->encryptionPadding), VARIANT_ENCRYPTION_SCOPE); - } - -//LExit: - return hr; -} - -// The contents of pllValue may be sensitive, should keep encrypted and SecureZeroMemory. -static HRESULT BVariantRetrieveDecryptedNumeric( +static void BVariantRetrieveNumeric( __in BURN_VARIANT* pVariant, __out LONGLONG* pllValue ) { - HRESULT hr = S_OK; - Assert(NULL != pllValue); - if (pVariant->fEncryptValue) - { - hr = BVariantEncryptNumeric(pVariant, FALSE); - ExitOnFailure(hr, "Failed to decrypt numeric"); - } *pllValue = pVariant->llValue; - - if (pVariant->fEncryptValue) - { - hr = BVariantEncryptNumeric(pVariant, TRUE); - } - -LExit: - return hr; } // The contents of psczValue may be sensitive, should keep encrypted and SecureZeroFree. @@ -550,13 +480,13 @@ static HRESULT BVariantRetrieveDecryptedString( { HRESULT hr = S_OK; - if (NULL == pVariant->sczValue) + if (!pVariant->sczValue) { *psczValue = NULL; ExitFunction(); } - if (pVariant->fEncryptValue) + if (pVariant->fEncryptString) { hr = BVariantEncryptString(pVariant, FALSE); ExitOnFailure(hr, "Failed to decrypt string"); @@ -565,7 +495,7 @@ static HRESULT BVariantRetrieveDecryptedString( hr = StrAllocStringSecure(psczValue, pVariant->sczValue, 0); ExitOnFailure(hr, "Failed to copy value."); - if (pVariant->fEncryptValue) + if (pVariant->fEncryptString) { hr = BVariantEncryptString(pVariant, TRUE); } @@ -574,28 +504,12 @@ LExit: return hr; } -// The contents of pqwValue may be sensitive, should keep encrypted and SecureZeroMemory. -static HRESULT BVariantRetrieveDecryptedVersion( +static void BVariantRetrieveVersion( __in BURN_VARIANT* pVariant, __out DWORD64* pqwValue ) { - HRESULT hr = S_OK; - Assert(NULL != pqwValue); - if (pVariant->fEncryptValue) - { - hr = BVariantEncryptVersion(pVariant, FALSE); - ExitOnFailure(hr, "Failed to decrypt version"); - } *pqwValue = pVariant->qwValue; - - if (pVariant->fEncryptValue) - { - hr = BVariantEncryptVersion(pVariant, TRUE); - } - -LExit: - return hr; } diff --git a/src/engine/variant.h b/src/engine/variant.h index 9259f05a..73fbe076 100644 --- a/src/engine/variant.h +++ b/src/engine/variant.h @@ -27,10 +27,9 @@ typedef struct _BURN_VARIANT LONGLONG llValue; DWORD64 qwValue; LPWSTR sczValue; - BYTE encryptionPadding[CRYP_ENCRYPT_MEMORY_SIZE]; }; BURN_VARIANT_TYPE Type; - BOOL fEncryptValue; + BOOL fEncryptString; } BURN_VARIANT; diff --git a/src/test/BurnUnitTest/VariantTest.cpp b/src/test/BurnUnitTest/VariantTest.cpp index dd679f08..d16ac699 100644 --- a/src/test/BurnUnitTest/VariantTest.cpp +++ b/src/test/BurnUnitTest/VariantTest.cpp @@ -77,7 +77,7 @@ namespace Bootstrapper hr = BVariantSetEncryption(pActualValue, TRUE); NativeAssert::Succeeded(hr, "Failed to encrypt variant {0}", wz); - NativeAssert::True(pActualValue->fEncryptValue); + NativeAssert::True(pActualValue->fEncryptString); } } @@ -95,7 +95,7 @@ namespace Bootstrapper hr = BVariantSetEncryption(pActualValue, TRUE); NativeAssert::Succeeded(hr, "Failed to encrypt variant {0}", wz); - NativeAssert::True(pActualValue->fEncryptValue); + NativeAssert::True(pActualValue->fEncryptString); } } @@ -115,7 +115,7 @@ namespace Bootstrapper hr = BVariantSetEncryption(pActualValue, TRUE); NativeAssert::Succeeded(hr, "Failed to encrypt variant {0}", wz); - NativeAssert::True(pActualValue->fEncryptValue); + NativeAssert::True(pActualValue->fEncryptString); } } @@ -133,7 +133,7 @@ namespace Bootstrapper hr = BVariantSetEncryption(pActualValue, TRUE); NativeAssert::Succeeded(hr, "Failed to encrypt variant {0}", wz); - NativeAssert::True(pActualValue->fEncryptValue); + NativeAssert::True(pActualValue->fEncryptString); } } -- cgit v1.2.3-55-g6feb From 655a166adbd56ea7036b2001258ede77f58baee0 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 18 Jul 2020 16:48:54 +1000 Subject: Move Burn headers from BootstrapperCore repo. --- appveyor.cmd | 1 + nuget.config | 1 - .../WixToolset.BootstrapperCore.Native.nuspec | 19 + .../WixToolset.BootstrapperCore.Native.proj | 19 + .../build/WixToolset.BootstrapperCore.Native.props | 13 + .../inc/BootstrapperApplication.h | 1054 ++++++++++++++++++++ .../inc/BootstrapperEngine.h | 430 ++++++++ .../inc/BundleExtension.h | 59 ++ .../inc/BundleExtensionEngine.h | 184 ++++ src/engine/engine.vcxproj | 10 +- src/engine/packages.config | 1 - src/engine/precomp.h | 8 +- src/test/BurnUnitTest/BurnUnitTest.vcxproj | 4 +- src/test/BurnUnitTest/packages.config | 1 - 14 files changed, 1788 insertions(+), 16 deletions(-) create mode 100644 src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.nuspec create mode 100644 src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.proj create mode 100644 src/WixToolset.BootstrapperCore.Native/build/WixToolset.BootstrapperCore.Native.props create mode 100644 src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h create mode 100644 src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h create mode 100644 src/WixToolset.BootstrapperCore.Native/inc/BundleExtension.h create mode 100644 src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h diff --git a/appveyor.cmd b/appveyor.cmd index cc3f798a..48033840 100644 --- a/appveyor.cmd +++ b/appveyor.cmd @@ -6,6 +6,7 @@ nuget restore || exit /b msbuild -p:Configuration=Release;Platform=x86 || exit /b msbuild -p:Configuration=Release -t:Pack src\stub\stub.vcxproj || exit /b +msbuild -p:Configuration=Release -t:Pack src\WixToolset.BootstrapperCore.Native\WixToolset.BootstrapperCore.Native.proj || exit /b @popd @endlocal \ No newline at end of file diff --git a/nuget.config b/nuget.config index ae9532bb..405367b4 100644 --- a/nuget.config +++ b/nuget.config @@ -3,7 +3,6 @@ - diff --git a/src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.nuspec b/src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.nuspec new file mode 100644 index 00000000..b10b75d2 --- /dev/null +++ b/src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.nuspec @@ -0,0 +1,19 @@ + + + + $id$ + $version$ + WiX Toolset Team + WiX Toolset Team + MS-RL + https://github.com/wixtoolset/BootstrapperCore + false + $description$ + $copyright$ + + + + + + + diff --git a/src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.proj b/src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.proj new file mode 100644 index 00000000..113fe7ae --- /dev/null +++ b/src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.proj @@ -0,0 +1,19 @@ + + + + + + + WixToolset.BootstrapperCore.Native + WiX Bootstrapper native interfaces + + + + + + + + + + + \ No newline at end of file diff --git a/src/WixToolset.BootstrapperCore.Native/build/WixToolset.BootstrapperCore.Native.props b/src/WixToolset.BootstrapperCore.Native/build/WixToolset.BootstrapperCore.Native.props new file mode 100644 index 00000000..82f81163 --- /dev/null +++ b/src/WixToolset.BootstrapperCore.Native/build/WixToolset.BootstrapperCore.Native.props @@ -0,0 +1,13 @@ + + + + + + + $(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories) + + + $(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories) + + + diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h new file mode 100644 index 00000000..36d788ca --- /dev/null +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -0,0 +1,1054 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +enum BOOTSTRAPPER_DISPLAY +{ + BOOTSTRAPPER_DISPLAY_UNKNOWN, + BOOTSTRAPPER_DISPLAY_EMBEDDED, + BOOTSTRAPPER_DISPLAY_NONE, + BOOTSTRAPPER_DISPLAY_PASSIVE, + BOOTSTRAPPER_DISPLAY_FULL, +}; + +enum BOOTSTRAPPER_RESTART +{ + BOOTSTRAPPER_RESTART_UNKNOWN, + BOOTSTRAPPER_RESTART_NEVER, + BOOTSTRAPPER_RESTART_PROMPT, + BOOTSTRAPPER_RESTART_AUTOMATIC, + BOOTSTRAPPER_RESTART_ALWAYS, +}; + +enum BOOTSTRAPPER_RESUME_TYPE +{ + BOOTSTRAPPER_RESUME_TYPE_NONE, + BOOTSTRAPPER_RESUME_TYPE_INVALID, // resume information is present but invalid + BOOTSTRAPPER_RESUME_TYPE_INTERRUPTED, // relaunched after an unexpected interruption + BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING, // reboot has not taken place yet + BOOTSTRAPPER_RESUME_TYPE_REBOOT, // relaunched after reboot + BOOTSTRAPPER_RESUME_TYPE_SUSPEND, // relaunched after suspend + BOOTSTRAPPER_RESUME_TYPE_ARP, // launched from ARP +}; + +enum BOOTSTRAPPER_ERROR_TYPE +{ + BOOTSTRAPPER_ERROR_TYPE_ELEVATE, // error occurred trying to elevate. + BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER, // error came from windows installer. + BOOTSTRAPPER_ERROR_TYPE_EXE_PACKAGE, // error came from an exe package. + BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER, // error occurred trying to authenticate with HTTP server. + BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY, // error occurred trying to authenticate with HTTP proxy. + BOOTSTRAPPER_ERROR_TYPE_APPLY, // error occurred during apply. +}; + +enum BOOTSTRAPPER_RELATED_OPERATION +{ + BOOTSTRAPPER_RELATED_OPERATION_NONE, + BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE, + BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE, + BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE, + BOOTSTRAPPER_RELATED_OPERATION_REMOVE, + BOOTSTRAPPER_RELATED_OPERATION_INSTALL, + BOOTSTRAPPER_RELATED_OPERATION_REPAIR, +}; + +enum BOOTSTRAPPER_CACHE_OPERATION +{ + BOOTSTRAPPER_CACHE_OPERATION_COPY, + BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD, + BOOTSTRAPPER_CACHE_OPERATION_EXTRACT, +}; + +enum BOOTSTRAPPER_APPLY_RESTART +{ + BOOTSTRAPPER_APPLY_RESTART_NONE, + BOOTSTRAPPER_APPLY_RESTART_REQUIRED, + BOOTSTRAPPER_APPLY_RESTART_INITIATED, +}; + +enum BOOTSTRAPPER_RELATION_TYPE +{ + BOOTSTRAPPER_RELATION_NONE, + BOOTSTRAPPER_RELATION_DETECT, + BOOTSTRAPPER_RELATION_UPGRADE, + BOOTSTRAPPER_RELATION_ADDON, + BOOTSTRAPPER_RELATION_PATCH, + BOOTSTRAPPER_RELATION_DEPENDENT, + BOOTSTRAPPER_RELATION_UPDATE, +}; + +enum BOOTSTRAPPER_APPLICATION_MESSAGE +{ + BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONSTARTUP, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONSHUTDOWN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMSHUTDOWN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTFORWARDCOMPATIBLEBUNDLE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATEBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATECOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGEBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPATIBLEMSIPACKAGE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDMSIPACKAGE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTTARGETMSIPACKAGE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTMSIFEATURE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGECOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGEBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANTARGETMSIPACKAGE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIFEATURE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGECOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATEBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATECOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONPROGRESS, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONERROR, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERCOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGEBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREPROGRESS, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONRESOLVESOURCE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRECOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYCOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGECOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGEBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPATCHTARGET, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROGRESS, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEMSIMESSAGE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEFILESINUSE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGECOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTECOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERCOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYCOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXEBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXECOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIPACKAGE, +}; + +enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION +{ + BOOTSTRAPPER_APPLYCOMPLETE_ACTION_NONE, + // Instructs the engine to restart. + // The engine will not launch again after the machine is rebooted. + // Ignored if reboot was already initiated by OnExecutePackageComplete(). + BOOTSTRAPPER_APPLYCOMPLETE_ACTION_RESTART, +}; + +enum BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION +{ + BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_NONE, + // Instructs the engine to try the acquisition of the package again. + // Ignored if hrStatus is a success. + BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_RETRY, +}; + +enum BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION +{ + BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE, + // Instructs the engine to ignore non-vital package failures and + // continue with the caching. + // Ignored if hrStatus is a success or the package is vital. + BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE, + // Instructs the engine to try the acquisition and verification of the package again. + // Ignored if hrStatus is a success. + BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_RETRY, +}; + +enum BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION +{ + BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE, + // Ignored if hrStatus is a success. + BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION, + // Ignored if hrStatus is a success. + BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION, +}; + +enum BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION +{ + BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_NONE, + // Instructs the engine to ignore non-vital package failures and + // continue with the install. + // Ignored if hrStatus is a success or the package is vital. + BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE, + // Instructs the engine to try the execution of the package again. + // Ignored if hrStatus is a success. + BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RETRY, + // Instructs the engine to stop processing the chain and restart. + // The engine will launch again after the machine is restarted. + BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RESTART, + // Instructs the engine to stop processing the chain and + // suspend the current state. + BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_SUSPEND, +}; + +enum BOOTSTRAPPER_RESOLVESOURCE_ACTION +{ + // Instructs the engine that the source can't be found. + BOOTSTRAPPER_RESOLVESOURCE_ACTION_NONE, + // Instructs the engine to try the local source again. + BOOTSTRAPPER_RESOLVESOURCE_ACTION_RETRY, + // Instructs the engine to try the download source. + BOOTSTRAPPER_RESOLVESOURCE_ACTION_DOWNLOAD, +}; + +enum BOOTSTRAPPER_SHUTDOWN_ACTION +{ + BOOTSTRAPPER_SHUTDOWN_ACTION_NONE, + // Instructs the engine to restart. + // The engine will not launch again after the machine is rebooted. + // Ignored if reboot was already initiated by OnExecutePackageComplete(). + BOOTSTRAPPER_SHUTDOWN_ACTION_RESTART, + // Instructs the engine to unload the bootstrapper application and + // restart the engine which will load the bootstrapper application again. + // Typically used to switch from a native bootstrapper application to a managed one. + BOOTSTRAPPER_SHUTDOWN_ACTION_RELOAD_BOOTSTRAPPER, +}; + +enum BURN_MSI_PROPERTY +{ + BURN_MSI_PROPERTY_NONE, // no property added + BURN_MSI_PROPERTY_INSTALL, // add BURNMSIINSTALL=1 + BURN_MSI_PROPERTY_MODIFY, // add BURNMSIMODIFY=1 + BURN_MSI_PROPERTY_REPAIR, // add BURNMSIREPAIR=1 + BURN_MSI_PROPERTY_UNINSTALL,// add BURNMSIUNINSTALL=1 +}; + +struct BOOTSTRAPPER_COMMAND +{ + DWORD cbSize; + BOOTSTRAPPER_ACTION action; + BOOTSTRAPPER_DISPLAY display; + BOOTSTRAPPER_RESTART restart; + + LPWSTR wzCommandLine; + int nCmdShow; + + BOOTSTRAPPER_RESUME_TYPE resumeType; + HWND hwndSplashScreen; + + // If this was run from a related bundle, specifies the relation type + BOOTSTRAPPER_RELATION_TYPE relationType; + BOOL fPassthrough; + + LPWSTR wzLayoutDirectory; + LPWSTR wzBootstrapperWorkingFolder; + LPWSTR wzBootstrapperApplicationDataPath; +}; + +struct BA_ONAPPLYBEGIN_ARGS +{ + DWORD cbSize; + DWORD dwPhaseCount; +}; + +struct BA_ONAPPLYBEGIN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONAPPLYCOMPLETE_ARGS +{ + DWORD cbSize; + HRESULT hrStatus; + // Indicates whether any package required a reboot or initiated the reboot already. + BOOTSTRAPPER_APPLY_RESTART restart; + BOOTSTRAPPER_APPLYCOMPLETE_ACTION recommendation; +}; + +struct BA_ONAPPLYCOMPLETE_RESULTS +{ + DWORD cbSize; + BOOTSTRAPPER_APPLYCOMPLETE_ACTION action; +}; + +struct BA_ONCACHEACQUIREBEGIN_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageOrContainerId; + LPCWSTR wzPayloadId; + BOOTSTRAPPER_CACHE_OPERATION operation; + LPCWSTR wzSource; +}; + +struct BA_ONCACHEACQUIREBEGIN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONCACHEACQUIRECOMPLETE_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageOrContainerId; + LPCWSTR wzPayloadId; + HRESULT hrStatus; + BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION recommendation; +}; + +struct BA_ONCACHEACQUIRECOMPLETE_RESULTS +{ + DWORD cbSize; + BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION action; +}; + +struct BA_ONCACHEACQUIREPROGRESS_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageOrContainerId; + LPCWSTR wzPayloadId; + DWORD64 dw64Progress; + DWORD64 dw64Total; + DWORD dwOverallPercentage; +}; + +struct BA_ONCACHEACQUIREPROGRESS_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONCACHEBEGIN_ARGS +{ + DWORD cbSize; +}; + +struct BA_ONCACHEBEGIN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONCACHECOMPLETE_ARGS +{ + DWORD cbSize; + HRESULT hrStatus; +}; + +struct BA_ONCACHECOMPLETE_RESULTS +{ + DWORD cbSize; +}; + +struct BA_ONCACHEPACKAGEBEGIN_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; + DWORD cCachePayloads; + DWORD64 dw64PackageCacheSize; +}; + +struct BA_ONCACHEPACKAGEBEGIN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONCACHEPACKAGECOMPLETE_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; + HRESULT hrStatus; + BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION recommendation; +}; + +struct BA_ONCACHEPACKAGECOMPLETE_RESULTS +{ + DWORD cbSize; + BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION action; +}; + +struct BA_ONCACHEVERIFYBEGIN_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageOrContainerId; + LPCWSTR wzPayloadId; +}; + +struct BA_ONCACHEVERIFYBEGIN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONCACHEVERIFYCOMPLETE_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageOrContainerId; + LPCWSTR wzPayloadId; + HRESULT hrStatus; + BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION recommendation; +}; + +struct BA_ONCACHEVERIFYCOMPLETE_RESULTS +{ + DWORD cbSize; + BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action; +}; + +struct BA_ONDETECTBEGIN_ARGS +{ + DWORD cbSize; + BOOL fInstalled; + DWORD cPackages; +}; + +struct BA_ONDETECTBEGIN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONDETECTCOMPATIBLEMSIPACKAGE_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; + LPCWSTR wzCompatiblePackageId; + DWORD64 dw64CompatiblePackageVersion; +}; + +struct BA_ONDETECTCOMPATIBLEMSIPACKAGE_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONDETECTCOMPLETE_ARGS +{ + DWORD cbSize; + HRESULT hrStatus; +}; + +struct BA_ONDETECTCOMPLETE_RESULTS +{ + DWORD cbSize; +}; + +struct BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_ARGS +{ + DWORD cbSize; + LPCWSTR wzBundleId; + BOOTSTRAPPER_RELATION_TYPE relationType; + LPCWSTR wzBundleTag; + BOOL fPerMachine; + DWORD64 dw64Version; +}; + +struct BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_RESULTS +{ + DWORD cbSize; + BOOL fCancel; + BOOL fIgnoreBundle; +}; + +struct BA_ONDETECTMSIFEATURE_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; + LPCWSTR wzFeatureId; + BOOTSTRAPPER_FEATURE_STATE state; +}; + +struct BA_ONDETECTMSIFEATURE_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONDETECTPACKAGEBEGIN_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; +}; + +struct BA_ONDETECTPACKAGEBEGIN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONDETECTPACKAGECOMPLETE_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; + HRESULT hrStatus; + BOOTSTRAPPER_PACKAGE_STATE state; +}; + +struct BA_ONDETECTPACKAGECOMPLETE_RESULTS +{ + DWORD cbSize; +}; + +struct BA_ONDETECTRELATEDBUNDLE_ARGS +{ + DWORD cbSize; + LPCWSTR wzBundleId; + BOOTSTRAPPER_RELATION_TYPE relationType; + LPCWSTR wzBundleTag; + BOOL fPerMachine; + DWORD64 dw64Version; + BOOTSTRAPPER_RELATED_OPERATION operation; +}; + +struct BA_ONDETECTRELATEDBUNDLE_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONDETECTRELATEDMSIPACKAGE_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; + LPCWSTR wzUpgradeCode; + LPCWSTR wzProductCode; + BOOL fPerMachine; + DWORD64 dw64Version; + BOOTSTRAPPER_RELATED_OPERATION operation; +}; + +struct BA_ONDETECTRELATEDMSIPACKAGE_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONDETECTTARGETMSIPACKAGE_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; + LPCWSTR wzProductCode; + BOOTSTRAPPER_PACKAGE_STATE patchState; +}; + +struct BA_ONDETECTTARGETMSIPACKAGE_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONDETECTUPDATE_ARGS +{ + DWORD cbSize; + LPCWSTR wzUpdateLocation; + DWORD64 dw64Size; + DWORD64 dw64Version; + LPCWSTR wzTitle; + LPCWSTR wzSummary; + LPCWSTR wzContentType; + LPCWSTR wzContent; +}; + +struct BA_ONDETECTUPDATE_RESULTS +{ + DWORD cbSize; + BOOL fCancel; + BOOL fStopProcessingUpdates; +}; + +struct BA_ONDETECTUPDATEBEGIN_ARGS +{ + DWORD cbSize; + LPCWSTR wzUpdateLocation; +}; + +struct BA_ONDETECTUPDATEBEGIN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; + BOOL fSkip; +}; + +struct BA_ONDETECTUPDATECOMPLETE_ARGS +{ + DWORD cbSize; + HRESULT hrStatus; +}; + +struct BA_ONDETECTUPDATECOMPLETE_RESULTS +{ + DWORD cbSize; + BOOL fIgnoreError; +}; + +struct BA_ONELEVATEBEGIN_ARGS +{ + DWORD cbSize; +}; + +struct BA_ONELEVATEBEGIN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONELEVATECOMPLETE_ARGS +{ + DWORD cbSize; + HRESULT hrStatus; +}; + +struct BA_ONELEVATECOMPLETE_RESULTS +{ + DWORD cbSize; +}; + +struct BA_ONERROR_ARGS +{ + DWORD cbSize; + BOOTSTRAPPER_ERROR_TYPE errorType; + LPCWSTR wzPackageId; + DWORD dwCode; + LPCWSTR wzError; + DWORD dwUIHint; + DWORD cData; + LPCWSTR* rgwzData; + int nRecommendation; +}; + +struct BA_ONERROR_RESULTS +{ + DWORD cbSize; + int nResult; +}; + +struct BA_ONEXECUTEBEGIN_ARGS +{ + DWORD cbSize; + DWORD cExecutingPackages; +}; + +struct BA_ONEXECUTEBEGIN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONEXECUTECOMPLETE_ARGS +{ + DWORD cbSize; + HRESULT hrStatus; +}; + +struct BA_ONEXECUTECOMPLETE_RESULTS +{ + DWORD cbSize; +}; + +struct BA_ONEXECUTEFILESINUSE_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; + DWORD cFiles; + LPCWSTR* rgwzFiles; + int nRecommendation; +}; + +struct BA_ONEXECUTEFILESINUSE_RESULTS +{ + DWORD cbSize; + int nResult; +}; + +struct BA_ONEXECUTEMSIMESSAGE_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; + INSTALLMESSAGE messageType; + DWORD dwUIHint; + LPCWSTR wzMessage; + DWORD cData; + LPCWSTR* rgwzData; + int nRecommendation; +}; + +struct BA_ONEXECUTEMSIMESSAGE_RESULTS +{ + DWORD cbSize; + int nResult; +}; + +struct BA_ONEXECUTEPACKAGEBEGIN_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; + BOOL fExecute; // false means rollback. + BOOTSTRAPPER_ACTION_STATE action; + INSTALLUILEVEL uiLevel; + BOOL fDisableExternalUiHandler; +}; + +struct BA_ONEXECUTEPACKAGEBEGIN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONEXECUTEPACKAGECOMPLETE_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; + HRESULT hrStatus; + // Indicates whether this package requires a reboot or initiated the reboot already. + BOOTSTRAPPER_APPLY_RESTART restart; + BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION recommendation; +}; + +struct BA_ONEXECUTEPACKAGECOMPLETE_RESULTS +{ + DWORD cbSize; + BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION action; +}; + +struct BA_ONEXECUTEPATCHTARGET_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; + LPCWSTR wzTargetProductCode; +}; + +struct BA_ONEXECUTEPATCHTARGET_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONEXECUTEPROGRESS_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; + DWORD dwProgressPercentage; + DWORD dwOverallPercentage; +}; + +struct BA_ONEXECUTEPROGRESS_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONLAUNCHAPPROVEDEXEBEGIN_ARGS +{ + DWORD cbSize; +}; + +struct BA_ONLAUNCHAPPROVEDEXEBEGIN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONLAUNCHAPPROVEDEXECOMPLETE_ARGS +{ + DWORD cbSize; + HRESULT hrStatus; + // Only valid if the operation succeeded. + DWORD dwProcessId; +}; + +struct BA_ONLAUNCHAPPROVEDEXECOMPLETE_RESULTS +{ + DWORD cbSize; +}; + +struct BA_ONPLANBEGIN_ARGS +{ + DWORD cbSize; + DWORD cPackages; +}; + +struct BA_ONPLANBEGIN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; + LPCWSTR wzCompatiblePackageId; + DWORD64 dw64CompatiblePackageVersion; + BOOTSTRAPPER_REQUEST_STATE recommendedState; +}; + +struct BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; + BOOTSTRAPPER_REQUEST_STATE requestedState; +}; + +struct BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; + LPCWSTR wzCompatiblePackageId; + HRESULT hrStatus; + BOOTSTRAPPER_PACKAGE_STATE state; + BOOTSTRAPPER_REQUEST_STATE requested; + BOOTSTRAPPER_ACTION_STATE execute; + BOOTSTRAPPER_ACTION_STATE rollback; +}; + +struct BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_RESULTS +{ + DWORD cbSize; +}; + +struct BA_ONPLANCOMPLETE_ARGS +{ + DWORD cbSize; + HRESULT hrStatus; +}; + +struct BA_ONPLANCOMPLETE_RESULTS +{ + DWORD cbSize; +}; + +struct BA_ONPLANMSIFEATURE_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; + LPCWSTR wzFeatureId; + BOOTSTRAPPER_FEATURE_STATE recommendedState; +}; + +struct BA_ONPLANMSIFEATURE_RESULTS +{ + DWORD cbSize; + BOOTSTRAPPER_FEATURE_STATE requestedState; + BOOL fCancel; +}; + +struct BA_ONPLANMSIPACKAGE_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; + BOOL fExecute; // false means rollback. + BOOTSTRAPPER_ACTION_STATE action; +}; + +struct BA_ONPLANMSIPACKAGE_RESULTS +{ + DWORD cbSize; + BOOL fCancel; + BURN_MSI_PROPERTY actionMsiProperty; + INSTALLUILEVEL uiLevel; + BOOL fDisableExternalUiHandler; +}; + +struct BA_ONPLANPACKAGEBEGIN_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; + BOOTSTRAPPER_REQUEST_STATE recommendedState; +}; + +struct BA_ONPLANPACKAGEBEGIN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; + BOOTSTRAPPER_REQUEST_STATE requestedState; +}; + +struct BA_ONPLANPACKAGECOMPLETE_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; + HRESULT hrStatus; + BOOTSTRAPPER_PACKAGE_STATE state; + BOOTSTRAPPER_REQUEST_STATE requested; + BOOTSTRAPPER_ACTION_STATE execute; + BOOTSTRAPPER_ACTION_STATE rollback; +}; + +struct BA_ONPLANPACKAGECOMPLETE_RESULTS +{ + DWORD cbSize; +}; + +struct BA_ONPLANRELATEDBUNDLE_ARGS +{ + DWORD cbSize; + LPCWSTR wzBundleId; + BOOTSTRAPPER_REQUEST_STATE recommendedState; +}; + +struct BA_ONPLANRELATEDBUNDLE_RESULTS +{ + DWORD cbSize; + BOOL fCancel; + BOOTSTRAPPER_REQUEST_STATE requestedState; +}; + +struct BA_ONPLANTARGETMSIPACKAGE_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; + LPCWSTR wzProductCode; + BOOTSTRAPPER_REQUEST_STATE recommendedState; +}; + +struct BA_ONPLANTARGETMSIPACKAGE_RESULTS +{ + DWORD cbSize; + BOOTSTRAPPER_REQUEST_STATE requestedState; + BOOL fCancel; +}; + +struct BA_ONPROGRESS_ARGS +{ + DWORD cbSize; + DWORD dwProgressPercentage; + DWORD dwOverallPercentage; +}; + +struct BA_ONPROGRESS_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONREGISTERBEGIN_ARGS +{ + DWORD cbSize; +}; + +struct BA_ONREGISTERBEGIN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONREGISTERCOMPLETE_ARGS +{ + DWORD cbSize; + HRESULT hrStatus; +}; + +struct BA_ONREGISTERCOMPLETE_RESULTS +{ + DWORD cbSize; +}; + +struct BA_ONRESOLVESOURCE_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageOrContainerId; + LPCWSTR wzPayloadId; + LPCWSTR wzLocalSource; + LPCWSTR wzDownloadSource; + BOOTSTRAPPER_RESOLVESOURCE_ACTION recommendation; +}; + +struct BA_ONRESOLVESOURCE_RESULTS +{ + DWORD cbSize; + BOOTSTRAPPER_RESOLVESOURCE_ACTION action; + BOOL fCancel; +}; + +struct BA_ONSHUTDOWN_ARGS +{ + DWORD cbSize; +}; + +struct BA_ONSHUTDOWN_RESULTS +{ + DWORD cbSize; + BOOTSTRAPPER_SHUTDOWN_ACTION action; +}; + +struct BA_ONSTARTUP_ARGS +{ + DWORD cbSize; +}; + +struct BA_ONSTARTUP_RESULTS +{ + DWORD cbSize; +}; + +struct BA_ONSYSTEMSHUTDOWN_ARGS +{ + DWORD cbSize; + DWORD dwEndSession; +}; + +struct BA_ONSYSTEMSHUTDOWN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONUNREGISTERBEGIN_ARGS +{ + DWORD cbSize; +}; + +struct BA_ONUNREGISTERBEGIN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONUNREGISTERCOMPLETE_ARGS +{ + DWORD cbSize; + HRESULT hrStatus; +}; + +struct BA_ONUNREGISTERCOMPLETE_RESULTS +{ + DWORD cbSize; +}; + + + +extern "C" typedef HRESULT(WINAPI *PFN_BOOTSTRAPPER_APPLICATION_PROC)( + __in BOOTSTRAPPER_APPLICATION_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults, + __in_opt LPVOID pvContext + ); + +extern "C" typedef void (WINAPI *PFN_BOOTSTRAPPER_APPLICATION_DESTROY)(); + + + +struct BOOTSTRAPPER_CREATE_ARGS +{ + DWORD cbSize; + DWORD64 qwEngineAPIVersion; + PFN_BOOTSTRAPPER_ENGINE_PROC pfnBootstrapperEngineProc; + LPVOID pvBootstrapperEngineProcContext; + BOOTSTRAPPER_COMMAND* pCommand; +}; + +struct BOOTSTRAPPER_CREATE_RESULTS +{ + DWORD cbSize; + PFN_BOOTSTRAPPER_APPLICATION_PROC pfnBootstrapperApplicationProc; + LPVOID pvBootstrapperApplicationProcContext; + BOOL fDisableUnloading; // indicates the BA dll must not be unloaded after BootstrapperApplicationDestroy. +}; + +extern "C" typedef HRESULT(WINAPI *PFN_BOOTSTRAPPER_APPLICATION_CREATE)( + __in const BOOTSTRAPPER_CREATE_ARGS* pArgs, + __inout BOOTSTRAPPER_CREATE_RESULTS* pResults + ); diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h new file mode 100644 index 00000000..0dcaba75 --- /dev/null +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h @@ -0,0 +1,430 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +#define IDERROR -1 +#define IDNOACTION 0 + +#ifndef FACILITY_WIX +#define FACILITY_WIX 500 +#endif + +static const HRESULT E_SUSPECTED_AV_INTERFERENCE = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIX, 2000); + +// Note that ordering of the enumeration values is important. +// Some code paths use < or > comparisions and simply reording values will break those comparisons. +enum BOOTSTRAPPER_ACTION +{ + BOOTSTRAPPER_ACTION_UNKNOWN, + BOOTSTRAPPER_ACTION_HELP, + BOOTSTRAPPER_ACTION_LAYOUT, + BOOTSTRAPPER_ACTION_UNINSTALL, + BOOTSTRAPPER_ACTION_CACHE, + BOOTSTRAPPER_ACTION_INSTALL, + BOOTSTRAPPER_ACTION_MODIFY, + BOOTSTRAPPER_ACTION_REPAIR, + BOOTSTRAPPER_ACTION_UPDATE_REPLACE, + BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED, +}; + +enum BOOTSTRAPPER_ACTION_STATE +{ + BOOTSTRAPPER_ACTION_STATE_NONE, + BOOTSTRAPPER_ACTION_STATE_UNINSTALL, + BOOTSTRAPPER_ACTION_STATE_INSTALL, + BOOTSTRAPPER_ACTION_STATE_ADMIN_INSTALL, + BOOTSTRAPPER_ACTION_STATE_MODIFY, + BOOTSTRAPPER_ACTION_STATE_REPAIR, + BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE, + BOOTSTRAPPER_ACTION_STATE_MAJOR_UPGRADE, + BOOTSTRAPPER_ACTION_STATE_PATCH, +}; + +enum BOOTSTRAPPER_PACKAGE_STATE +{ + BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN, + BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE, + BOOTSTRAPPER_PACKAGE_STATE_ABSENT, + BOOTSTRAPPER_PACKAGE_STATE_CACHED, + BOOTSTRAPPER_PACKAGE_STATE_PRESENT, + BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED, +}; + +enum BOOTSTRAPPER_REQUEST_STATE +{ + BOOTSTRAPPER_REQUEST_STATE_NONE, + BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT, + BOOTSTRAPPER_REQUEST_STATE_ABSENT, + BOOTSTRAPPER_REQUEST_STATE_CACHE, + BOOTSTRAPPER_REQUEST_STATE_PRESENT, + BOOTSTRAPPER_REQUEST_STATE_REPAIR, +}; + +enum BOOTSTRAPPER_FEATURE_STATE +{ + BOOTSTRAPPER_FEATURE_STATE_UNKNOWN, + BOOTSTRAPPER_FEATURE_STATE_ABSENT, + BOOTSTRAPPER_FEATURE_STATE_ADVERTISED, + BOOTSTRAPPER_FEATURE_STATE_LOCAL, + BOOTSTRAPPER_FEATURE_STATE_SOURCE, +}; + +enum BOOTSTRAPPER_FEATURE_ACTION +{ + BOOTSTRAPPER_FEATURE_ACTION_NONE, + BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL, + BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE, + BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT, + BOOTSTRAPPER_FEATURE_ACTION_REINSTALL, + BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE, + BOOTSTRAPPER_FEATURE_ACTION_REMOVE, +}; + +enum BOOTSTRAPPER_LOG_LEVEL +{ + BOOTSTRAPPER_LOG_LEVEL_NONE, // turns off report (only valid for XXXSetLevel()) + BOOTSTRAPPER_LOG_LEVEL_STANDARD, // written if reporting is on + BOOTSTRAPPER_LOG_LEVEL_VERBOSE, // written only if verbose reporting is on + BOOTSTRAPPER_LOG_LEVEL_DEBUG, // reporting useful when debugging code + BOOTSTRAPPER_LOG_LEVEL_ERROR, // always gets reported, but can never be specified +}; + +enum BOOTSTRAPPER_UPDATE_HASH_TYPE +{ + BOOTSTRAPPER_UPDATE_HASH_TYPE_NONE, + BOOTSTRAPPER_UPDATE_HASH_TYPE_SHA1, +}; + +enum BOOTSTRAPPER_ENGINE_MESSAGE +{ + BOOTSTRAPPER_ENGINE_MESSAGE_GETPACKAGECOUNT, + BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLENUMERIC, + BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLESTRING, + BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLEVERSION, + BOOTSTRAPPER_ENGINE_MESSAGE_FORMATSTRING, + BOOTSTRAPPER_ENGINE_MESSAGE_ESCAPESTRING, + BOOTSTRAPPER_ENGINE_MESSAGE_EVALUATECONDITION, + BOOTSTRAPPER_ENGINE_MESSAGE_LOG, + BOOTSTRAPPER_ENGINE_MESSAGE_SENDEMBEDDEDERROR, + BOOTSTRAPPER_ENGINE_MESSAGE_SENDEMBEDDEDPROGRESS, + BOOTSTRAPPER_ENGINE_MESSAGE_SETUPDATE, + BOOTSTRAPPER_ENGINE_MESSAGE_SETLOCALSOURCE, + BOOTSTRAPPER_ENGINE_MESSAGE_SETDOWNLOADSOURCE, + BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLENUMERIC, + BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLESTRING, + BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLEVERSION, + BOOTSTRAPPER_ENGINE_MESSAGE_CLOSESPLASHSCREEN, + BOOTSTRAPPER_ENGINE_MESSAGE_DETECT, + BOOTSTRAPPER_ENGINE_MESSAGE_PLAN, + BOOTSTRAPPER_ENGINE_MESSAGE_ELEVATE, + BOOTSTRAPPER_ENGINE_MESSAGE_APPLY, + BOOTSTRAPPER_ENGINE_MESSAGE_QUIT, + BOOTSTRAPPER_ENGINE_MESSAGE_LAUNCHAPPROVEDEXE, +}; + +typedef struct _BAENGINE_APPLY_ARGS +{ + DWORD cbSize; + HWND hwndParent; +} BAENGINE_APPLY_ARGS; + +typedef struct _BAENGINE_APPLY_RESULTS +{ + DWORD cbSize; +} BAENGINE_APPLY_RESULTS; + +typedef struct _BAENGINE_CLOSESPLASHSCREEN_ARGS +{ + DWORD cbSize; +} BAENGINE_CLOSESPLASHSCREEN_ARGS; + +typedef struct _BAENGINE_CLOSESPLASHSCREEN_RESULTS +{ + DWORD cbSize; +} BAENGINE_CLOSESPLASHSCREEN_RESULTS; + +typedef struct _BAENGINE_DETECT_ARGS +{ + DWORD cbSize; + HWND hwndParent; +} BAENGINE_DETECT_ARGS; + +typedef struct _BAENGINE_DETECT_RESULTS +{ + DWORD cbSize; +} BAENGINE_DETECT_RESULTS; + +typedef struct _BAENGINE_ELEVATE_ARGS +{ + DWORD cbSize; + HWND hwndParent; +} BAENGINE_ELEVATE_ARGS; + +typedef struct _BAENGINE_ELEVATE_RESULTS +{ + DWORD cbSize; +} BAENGINE_ELEVATE_RESULTS; + +typedef struct _BAENGINE_ESCAPESTRING_ARGS +{ + DWORD cbSize; + LPCWSTR wzIn; +} BAENGINE_ESCAPESTRING_ARGS; + +typedef struct _BAENGINE_ESCAPESTRING_RESULTS +{ + DWORD cbSize; + LPWSTR wzOut; + // Should be initialized to the size of wzOut. + DWORD cchOut; +} BAENGINE_ESCAPESTRING_RESULTS; + +typedef struct _BAENGINE_EVALUATECONDITION_ARGS +{ + DWORD cbSize; + LPCWSTR wzCondition; +} BAENGINE_EVALUATECONDITION_ARGS; + +typedef struct _BAENGINE_EVALUATECONDITION_RESULTS +{ + DWORD cbSize; + BOOL f; +} BAENGINE_EVALUATECONDITION_RESULTS; + +typedef struct _BAENGINE_FORMATSTRING_ARGS +{ + DWORD cbSize; + LPCWSTR wzIn; +} BAENGINE_FORMATSTRING_ARGS; + +typedef struct _BAENGINE_FORMATSTRING_RESULTS +{ + DWORD cbSize; + // The contents of wzOut may be sensitive, should keep encrypted and SecureZeroFree. + LPWSTR wzOut; + // Should be initialized to the size of wzOut. + DWORD cchOut; +} BAENGINE_FORMATSTRING_RESULTS; + +typedef struct _BAENGINE_GETPACKAGECOUNT_ARGS +{ + DWORD cbSize; +} BAENGINE_GETPACKAGECOUNT_ARGS; + +typedef struct _BAENGINE_GETPACKAGECOUNT_RESULTS +{ + DWORD cbSize; + DWORD cPackages; +} BAENGINE_GETPACKAGECOUNT_RESULTS; + +typedef struct _BAENGINE_GETVARIABLENUMERIC_ARGS +{ + DWORD cbSize; + LPCWSTR wzVariable; +} BAENGINE_GETVARIABLENUMERIC_ARGS; + +typedef struct _BAENGINE_GETVARIABLENUMERIC_RESULTS +{ + DWORD cbSize; + // The contents of llValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroMemory. + LONGLONG llValue; +} BAENGINE_GETVARIABLENUMERIC_RESULTS; + +typedef struct _BAENGINE_GETVARIABLESTRING_ARGS +{ + DWORD cbSize; + LPCWSTR wzVariable; +} BAENGINE_GETVARIABLESTRING_ARGS; + +typedef struct _BAENGINE_GETVARIABLESTRING_RESULTS +{ + DWORD cbSize; + // The contents of wzValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroFree. + LPWSTR wzValue; + // Should be initialized to the size of wzValue. + DWORD cchValue; +} BAENGINE_GETVARIABLESTRING_RESULTS; + +typedef struct _BAENGINE_GETVARIABLEVERSION_ARGS +{ + DWORD cbSize; + LPCWSTR wzVariable; +} BAENGINE_GETVARIABLEVERSION_ARGS; + +typedef struct _BAENGINE_GETVARIABLEVERSION_RESULTS +{ + DWORD cbSize; + // The contents of qwValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroMemory. + DWORD64 qwValue; +} BAENGINE_GETVARIABLEVERSION_RESULTS; + +typedef struct _BAENGINE_LAUNCHAPPROVEDEXE_ARGS +{ + DWORD cbSize; + HWND hwndParent; + LPCWSTR wzApprovedExeForElevationId; + LPCWSTR wzArguments; + DWORD dwWaitForInputIdleTimeout; +} BAENGINE_LAUNCHAPPROVEDEXE_ARGS; + +typedef struct _BAENGINE_LAUNCHAPPROVEDEXE_RESULTS +{ + DWORD cbSize; +} BAENGINE_LAUNCHAPPROVEDEXE_RESULTS; + +typedef struct _BAENGINE_LOG_ARGS +{ + DWORD cbSize; + BOOTSTRAPPER_LOG_LEVEL level; + LPCWSTR wzMessage; +} BAENGINE_LOG_ARGS; + +typedef struct _BAENGINE_LOG_RESULTS +{ + DWORD cbSize; +} BAENGINE_LOG_RESULTS; + +typedef struct _BAENGINE_PLAN_ARGS +{ + DWORD cbSize; + BOOTSTRAPPER_ACTION action; +} BAENGINE_PLAN_ARGS; + +typedef struct _BAENGINE_PLAN_RESULTS +{ + DWORD cbSize; +} BAENGINE_PLAN_RESULTS; + +typedef struct _BAENGINE_QUIT_ARGS +{ + DWORD cbSize; + DWORD dwExitCode; +} BAENGINE_QUIT_ARGS; + +typedef struct _BAENGINE_QUIT_RESULTS +{ + DWORD cbSize; +} BAENGINE_QUIT_RESULTS; + +typedef struct _BAENGINE_SENDEMBEDDEDERROR_ARGS +{ + DWORD cbSize; + DWORD dwErrorCode; + LPCWSTR wzMessage; + DWORD dwUIHint; +} BAENGINE_SENDEMBEDDEDERROR_ARGS; + +typedef struct _BAENGINE_SENDEMBEDDEDERROR_RESULTS +{ + DWORD cbSize; + int nResult; +} BAENGINE_SENDEMBEDDEDERROR_RESULTS; + +typedef struct _BAENGINE_SENDEMBEDDEDPROGRESS_ARGS +{ + DWORD cbSize; + DWORD dwProgressPercentage; + DWORD dwOverallProgressPercentage; +} BAENGINE_SENDEMBEDDEDPROGRESS_ARGS; + +typedef struct _BAENGINE_SENDEMBEDDEDPROGRESS_RESULTS +{ + DWORD cbSize; + int nResult; +} BAENGINE_SENDEMBEDDEDPROGRESS_RESULTS; + +typedef struct _BAENGINE_SETDOWNLOADSOURCE_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageOrContainerId; + LPCWSTR wzPayloadId; + LPCWSTR wzUrl; + LPCWSTR wzUser; + LPCWSTR wzPassword; +} BAENGINE_SETDOWNLOADSOURCE_ARGS; + +typedef struct _BAENGINE_SETDOWNLOADSOURCE_RESULTS +{ + DWORD cbSize; +} BAENGINE_SETDOWNLOADSOURCE_RESULTS; + +typedef struct _BAENGINE_SETLOCALSOURCE_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageOrContainerId; + LPCWSTR wzPayloadId; + LPCWSTR wzPath; +} BAENGINE_SETLOCALSOURCE_ARGS; + +typedef struct _BAENGINE_SETLOCALSOURCE_RESULTS +{ + DWORD cbSize; +} BAENGINE_SETLOCALSOURCE_RESULTS; + +typedef struct _BAENGINE_SETUPDATE_ARGS +{ + DWORD cbSize; + LPCWSTR wzLocalSource; + LPCWSTR wzDownloadSource; + DWORD64 qwSize; + BOOTSTRAPPER_UPDATE_HASH_TYPE hashType; + BYTE* rgbHash; + DWORD cbHash; +} BAENGINE_SETUPDATE_ARGS; + +typedef struct _BAENGINE_SETUPDATE_RESULTS +{ + DWORD cbSize; +} BAENGINE_SETUPDATE_RESULTS; + +typedef struct _BAENGINE_SETVARIABLENUMERIC_ARGS +{ + DWORD cbSize; + LPCWSTR wzVariable; + LONGLONG llValue; +} BAENGINE_SETVARIABLENUMERIC_ARGS; + +typedef struct _BAENGINE_SETVARIABLENUMERIC_RESULTS +{ + DWORD cbSize; +} BAENGINE_SETVARIABLENUMERIC_RESULTS; + +typedef struct _BAENGINE_SETVARIABLESTRING_ARGS +{ + DWORD cbSize; + LPCWSTR wzVariable; + LPCWSTR wzValue; +} BAENGINE_SETVARIABLESTRING_ARGS; + +typedef struct _BAENGINE_SETVARIABLESTRING_RESULTS +{ + DWORD cbSize; +} BAENGINE_SETVARIABLESTRING_RESULTS; + +typedef struct _BAENGINE_SETVARIABLEVERSION_ARGS +{ + DWORD cbSize; + LPCWSTR wzVariable; + DWORD64 qwValue; +} BAENGINE_SETVARIABLEVERSION_ARGS; + +typedef struct _BAENGINE_SETVARIABLEVERSION_RESULTS +{ + DWORD cbSize; +} BAENGINE_SETVARIABLEVERSION_RESULTS; + + +extern "C" typedef HRESULT(WINAPI *PFN_BOOTSTRAPPER_ENGINE_PROC)( + __in BOOTSTRAPPER_ENGINE_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults, + __in_opt LPVOID pvContext + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BundleExtension.h b/src/WixToolset.BootstrapperCore.Native/inc/BundleExtension.h new file mode 100644 index 00000000..5c7d1260 --- /dev/null +++ b/src/WixToolset.BootstrapperCore.Native/inc/BundleExtension.h @@ -0,0 +1,59 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +enum BUNDLE_EXTENSION_MESSAGE +{ + BUNDLE_EXTENSION_MESSAGE_SEARCH, +}; + +typedef struct _BUNDLE_EXTENSION_SEARCH_ARGS +{ + DWORD cbSize; + LPCWSTR wzId; + LPCWSTR wzVariable; +} BUNDLE_EXTENSION_SEARCH_ARGS; + +typedef struct _BUNDLE_EXTENSION_SEARCH_RESULTS +{ + DWORD cbSize; +} BUNDLE_EXTENSION_SEARCH_RESULTS; + +extern "C" typedef HRESULT(WINAPI *PFN_BUNDLE_EXTENSION_PROC)( + __in BUNDLE_EXTENSION_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults, + __in_opt LPVOID pvContext + ); + +typedef struct _BUNDLE_EXTENSION_CREATE_ARGS +{ + DWORD cbSize; + DWORD64 qwEngineAPIVersion; + PFN_BUNDLE_EXTENSION_ENGINE_PROC pfnBundleExtensionEngineProc; + LPVOID pvBundleExtensionEngineProcContext; + LPCWSTR wzBootstrapperWorkingFolder; + LPCWSTR wzBundleExtensionDataPath; +} BUNDLE_EXTENSION_CREATE_ARGS; + +typedef struct _BUNDLE_EXTENSION_CREATE_RESULTS +{ + DWORD cbSize; + PFN_BUNDLE_EXTENSION_PROC pfnBundleExtensionProc; + LPVOID pvBundleExtensionProcContext; +} BUNDLE_EXTENSION_CREATE_RESULTS; + +extern "C" typedef HRESULT(WINAPI *PFN_BUNDLE_EXTENSION_CREATE)( + __in const BUNDLE_EXTENSION_CREATE_ARGS* pArgs, + __inout BUNDLE_EXTENSION_CREATE_RESULTS* pResults + ); + +extern "C" typedef void (WINAPI *PFN_BUNDLE_EXTENSION_DESTROY)(); + +#if defined(__cplusplus) +} +#endif diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h b/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h new file mode 100644 index 00000000..03c4f206 --- /dev/null +++ b/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h @@ -0,0 +1,184 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +enum BUNDLE_EXTENSION_LOG_LEVEL +{ + BUNDLE_EXTENSION_LOG_LEVEL_NONE, // turns off report (only valid for XXXSetLevel()) + BUNDLE_EXTENSION_LOG_LEVEL_STANDARD, // written if reporting is on + BUNDLE_EXTENSION_LOG_LEVEL_VERBOSE, // written only if verbose reporting is on + BUNDLE_EXTENSION_LOG_LEVEL_DEBUG, // reporting useful when debugging code + BUNDLE_EXTENSION_LOG_LEVEL_ERROR, // always gets reported, but can never be specified +}; + +enum BUNDLE_EXTENSION_ENGINE_MESSAGE +{ + BUNDLE_EXTENSION_ENGINE_MESSAGE_ESCAPESTRING, + BUNDLE_EXTENSION_ENGINE_MESSAGE_EVALUATECONDITION, + BUNDLE_EXTENSION_ENGINE_MESSAGE_FORMATSTRING, + BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLENUMERIC, + BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLESTRING, + BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLEVERSION, + BUNDLE_EXTENSION_ENGINE_MESSAGE_LOG, + BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLELITERALSTRING, + BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLENUMERIC, + BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLESTRING, + BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLEVERSION, +}; + +typedef struct _BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_ARGS +{ + DWORD cbSize; + LPCWSTR wzIn; +} BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_ARGS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_RESULTS +{ + DWORD cbSize; + LPWSTR wzOut; + // Should be initialized to the size of wzOut. + DWORD cchOut; +} BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_RESULTS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_ARGS +{ + DWORD cbSize; + LPCWSTR wzCondition; +} BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_ARGS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_RESULTS +{ + DWORD cbSize; + BOOL f; +} BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_RESULTS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_FORMATSTRING_ARGS +{ + DWORD cbSize; + LPCWSTR wzIn; +} BUNDLE_EXTENSION_ENGINE_FORMATSTRING_ARGS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_FORMATSTRING_RESULTS +{ + DWORD cbSize; + // The contents of wzOut may be sensitive, should keep encrypted and SecureZeroFree. + LPWSTR wzOut; + // Should be initialized to the size of wzOut. + DWORD cchOut; +} BUNDLE_EXTENSION_ENGINE_FORMATSTRING_RESULTS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_ARGS +{ + DWORD cbSize; + LPCWSTR wzVariable; +} BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_ARGS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_RESULTS +{ + DWORD cbSize; + // The contents of llValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroMemory. + LONGLONG llValue; +} BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_RESULTS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_ARGS +{ + DWORD cbSize; + LPCWSTR wzVariable; +} BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_ARGS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_RESULTS +{ + DWORD cbSize; + // The contents of wzValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroFree. + LPWSTR wzValue; + // Should be initialized to the size of wzValue. + DWORD cchValue; +} BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_RESULTS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_ARGS +{ + DWORD cbSize; + LPCWSTR wzVariable; +} BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_ARGS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_RESULTS +{ + DWORD cbSize; + // The contents of qwValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroMemory. + DWORD64 qwValue; +} BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_RESULTS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_LOG_ARGS +{ + DWORD cbSize; + BUNDLE_EXTENSION_LOG_LEVEL level; + LPCWSTR wzMessage; +} BUNDLE_EXTENSION_ENGINE_LOG_ARGS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_LOG_RESULTS +{ + DWORD cbSize; +} BUNDLE_EXTENSION_ENGINE_LOG_RESULTS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_ARGS +{ + DWORD cbSize; + LPCWSTR wzVariable; + LPCWSTR wzValue; +} BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_ARGS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_RESULTS +{ + DWORD cbSize; +} BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_RESULTS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_ARGS +{ + DWORD cbSize; + LPCWSTR wzVariable; + LONGLONG llValue; +} BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_ARGS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_RESULTS +{ + DWORD cbSize; +} BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_RESULTS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_ARGS +{ + DWORD cbSize; + LPCWSTR wzVariable; + LPCWSTR wzValue; +} BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_ARGS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_RESULTS +{ + DWORD cbSize; +} BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_RESULTS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_ARGS +{ + DWORD cbSize; + LPCWSTR wzVariable; + DWORD64 qwValue; +} BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_ARGS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_RESULTS +{ + DWORD cbSize; +} BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_RESULTS; + +extern "C" typedef HRESULT(WINAPI *PFN_BUNDLE_EXTENSION_ENGINE_PROC)( + __in BUNDLE_EXTENSION_ENGINE_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults, + __in_opt LPVOID pvContext + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index 6d064a43..906792a6 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -2,7 +2,6 @@ - @@ -42,10 +41,6 @@ - - $(ProjectDir)..\inc - - @@ -96,6 +91,10 @@ + + + + @@ -166,7 +165,6 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - diff --git a/src/engine/packages.config b/src/engine/packages.config index 98db4ce4..04a6553e 100644 --- a/src/engine/packages.config +++ b/src/engine/packages.config @@ -1,6 +1,5 @@  - \ No newline at end of file diff --git a/src/engine/precomp.h b/src/engine/precomp.h index 04dfa50e..c60d7c0e 100644 --- a/src/engine/precomp.h +++ b/src/engine/precomp.h @@ -61,10 +61,10 @@ #include #include -#include "BootstrapperEngine.h" -#include "BootstrapperApplication.h" -#include "BundleExtensionEngine.h" -#include "BundleExtension.h" +#include "..\WixToolset.BootstrapperCore.Native\inc\BootstrapperEngine.h" +#include "..\WixToolset.BootstrapperCore.Native\inc\BootstrapperApplication.h" +#include "..\WixToolset.BootstrapperCore.Native\inc\BundleExtensionEngine.h" +#include "..\WixToolset.BootstrapperCore.Native\inc\BundleExtension.h" #include "platform.h" #include "variant.h" diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index 3f3c7b4f..eb2ec4ea 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -4,7 +4,6 @@ - @@ -28,7 +27,7 @@ - ..\..\engine + ..\..\engine;..\..\WixToolset.BootstrapperCore.Native\inc cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;wintrust.lib;gdiplus.lib @@ -81,7 +80,6 @@ - diff --git a/src/test/BurnUnitTest/packages.config b/src/test/BurnUnitTest/packages.config index 27527ed6..24bfe34a 100644 --- a/src/test/BurnUnitTest/packages.config +++ b/src/test/BurnUnitTest/packages.config @@ -9,6 +9,5 @@ - \ No newline at end of file -- cgit v1.2.3-55-g6feb From 92f54db2406108864742bd1dd1a828171b1542bc Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 19 Jul 2020 14:12:59 +1000 Subject: Make NBGV version resource information go into 1033. --- src/Cpp.Build.props | 4 ++++ src/stub/stub.vcxproj | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/Cpp.Build.props b/src/Cpp.Build.props index 44a042c7..ebbf9f71 100644 --- a/src/Cpp.Build.props +++ b/src/Cpp.Build.props @@ -6,6 +6,10 @@ Win32 $(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\ $(OutputPath)$(Platform)\ + + + $(Company) + $(Copyright) diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index d5644161..e6083aa5 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -34,6 +34,9 @@ v142 Unicode Native component of WixToolset.Burn + + 1033 + Burn -- cgit v1.2.3-55-g6feb From ad80d6dc10f9bd6cb6a084857a8fb546cc55e756 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 19 Jul 2020 14:18:58 +1000 Subject: Don't embed application manifest for burn.exe since Core will embed it. --- src/stub/stub.manifest | 18 ------------------ src/stub/stub.vcxproj | 4 +--- 2 files changed, 1 insertion(+), 21 deletions(-) delete mode 100644 src/stub/stub.manifest diff --git a/src/stub/stub.manifest b/src/stub/stub.manifest deleted file mode 100644 index d5767cdb..00000000 --- a/src/stub/stub.manifest +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - WiX Toolset Bootstrapper - true - - - - - - - - - - diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index e6083aa5..e0e6c8c6 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -33,6 +33,7 @@ burn v142 Unicode + false Native component of WixToolset.Burn 1033 @@ -78,9 +79,6 @@ false - - - -- cgit v1.2.3-55-g6feb From d21eed76c48960707561c45c492c10a6a23c052e Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 31 Jul 2020 17:06:35 -0600 Subject: WIXFEAT:4763 Change "string" variable type to literal and add "formatted". --- .../inc/BootstrapperEngine.h | 1 + .../inc/BundleExtensionEngine.h | 14 +-- src/engine/EngineForApplication.cpp | 2 +- src/engine/EngineForExtension.cpp | 30 +----- src/engine/apply.cpp | 4 +- src/engine/cache.cpp | 6 +- src/engine/condition.cpp | 11 ++- src/engine/core.cpp | 8 +- src/engine/engine.cpp | 2 +- src/engine/exeengine.cpp | 6 +- src/engine/logging.cpp | 4 +- src/engine/msiengine.cpp | 6 +- src/engine/mspengine.cpp | 6 +- src/engine/msuengine.cpp | 4 +- src/engine/registration.cpp | 10 +- src/engine/search.cpp | 36 +++++--- src/engine/variable.cpp | 102 ++++++++------------- src/engine/variable.h | 17 +--- src/engine/variant.cpp | 34 +++++-- src/engine/variant.h | 6 +- src/test/BurnUnitTest/SearchTest.cpp | 87 ++++++++++++++++-- src/test/BurnUnitTest/VariableHelpers.cpp | 4 +- src/test/BurnUnitTest/VariableHelpers.h | 2 +- src/test/BurnUnitTest/VariableTest.cpp | 69 ++++++++++---- src/test/BurnUnitTest/VariantTest.cpp | 56 +++++++++-- 25 files changed, 313 insertions(+), 214 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h index 0dcaba75..e3792177 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h @@ -398,6 +398,7 @@ typedef struct _BAENGINE_SETVARIABLESTRING_ARGS DWORD cbSize; LPCWSTR wzVariable; LPCWSTR wzValue; + BOOL fFormatted; } BAENGINE_SETVARIABLESTRING_ARGS; typedef struct _BAENGINE_SETVARIABLESTRING_RESULTS diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h b/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h index 03c4f206..61a55693 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h @@ -24,7 +24,6 @@ enum BUNDLE_EXTENSION_ENGINE_MESSAGE BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLESTRING, BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLEVERSION, BUNDLE_EXTENSION_ENGINE_MESSAGE_LOG, - BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLELITERALSTRING, BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLENUMERIC, BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLESTRING, BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLEVERSION, @@ -124,18 +123,6 @@ typedef struct _BUNDLE_EXTENSION_ENGINE_LOG_RESULTS DWORD cbSize; } BUNDLE_EXTENSION_ENGINE_LOG_RESULTS; -typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_ARGS -{ - DWORD cbSize; - LPCWSTR wzVariable; - LPCWSTR wzValue; -} BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_ARGS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_RESULTS -{ - DWORD cbSize; -} BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_RESULTS; - typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_ARGS { DWORD cbSize; @@ -153,6 +140,7 @@ typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_ARGS DWORD cbSize; LPCWSTR wzVariable; LPCWSTR wzValue; + BOOL fFormatted; } BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_ARGS; typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_RESULTS diff --git a/src/engine/EngineForApplication.cpp b/src/engine/EngineForApplication.cpp index e559b438..81eec2fc 100644 --- a/src/engine/EngineForApplication.cpp +++ b/src/engine/EngineForApplication.cpp @@ -592,7 +592,7 @@ static HRESULT BAEngineSetVariableString( if (wzVariable && *wzVariable) { - hr = VariableSetString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE); + hr = VariableSetString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE, pArgs->fFormatted); ExitOnFailure(hr, "Failed to set string variable."); } else diff --git a/src/engine/EngineForExtension.cpp b/src/engine/EngineForExtension.cpp index 9667dd18..fdfa59b1 100644 --- a/src/engine/EngineForExtension.cpp +++ b/src/engine/EngineForExtension.cpp @@ -245,31 +245,6 @@ LExit: return hr; } -static HRESULT BEEngineSetVariableLiteralString( - __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, - __in const BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_ARGS* pArgs, - __in BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_RESULTS* /*pResults*/ - ) -{ - HRESULT hr = S_OK; - LPCWSTR wzVariable = pArgs->wzVariable; - LPCWSTR wzValue = pArgs->wzValue; - - if (wzVariable && *wzVariable) - { - hr = VariableSetLiteralString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE); - ExitOnFailure(hr, "Failed to set literal string variable."); - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Bundle Extension did not provide variable name."); - } - -LExit: - return hr; -} - static HRESULT BEEngineSetVariableNumeric( __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, __in const BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_ARGS* pArgs, @@ -307,7 +282,7 @@ static HRESULT BEEngineSetVariableString( if (wzVariable && *wzVariable) { - hr = VariableSetString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE); + hr = VariableSetString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE, pArgs->fFormatted); ExitOnFailure(hr, "Failed to set string variable."); } else @@ -383,9 +358,6 @@ HRESULT WINAPI EngineForExtensionProc( case BUNDLE_EXTENSION_ENGINE_MESSAGE_LOG: hr = BEEngineLog(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); break; - case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLELITERALSTRING: - hr = BEEngineSetVariableLiteralString(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); - break; case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLENUMERIC: hr = BEEngineSetVariableNumeric(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); break; diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index eae7c681..9a0f64e1 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -272,7 +272,7 @@ extern "C" HRESULT ApplySetVariables( { HRESULT hr = S_OK; - hr = VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, NULL, TRUE); + hr = VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, NULL, TRUE, FALSE); ExitOnFailure(hr, "Failed to set the bundle forced restart package built-in variable."); LExit: @@ -2493,7 +2493,7 @@ static HRESULT ExecutePackageComplete( if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart) { // Best effort to set the forced restart package variable. - VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, pPackage->sczId, TRUE); + VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, pPackage->sczId, TRUE, FALSE); } // If we're retrying, leave a message in the log file and say everything is okay. diff --git a/src/engine/cache.cpp b/src/engine/cache.cpp index 9338426d..315281bc 100644 --- a/src/engine/cache.cpp +++ b/src/engine/cache.cpp @@ -157,7 +157,7 @@ extern "C" HRESULT CacheInitialize( hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource); if (E_NOTFOUND == hr) { - hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, wzSourceProcessPath, FALSE); + hr = VariableSetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, wzSourceProcessPath, FALSE, FALSE); ExitOnFailure(hr, "Failed to set original source variable."); hr = StrAllocString(&sczOriginalSource, wzSourceProcessPath, 0); @@ -170,7 +170,7 @@ extern "C" HRESULT CacheInitialize( hr = PathGetDirectory(sczOriginalSource, &sczOriginalSourceFolder); ExitOnFailure(hr, "Failed to get directory from original source path."); - hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, sczOriginalSourceFolder, FALSE); + hr = VariableSetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, sczOriginalSourceFolder, FALSE, FALSE); ExitOnFailure(hr, "Failed to set original source directory variable."); } } @@ -549,7 +549,7 @@ extern "C" HRESULT CacheSetLastUsedSource( if (CSTR_EQUAL != nCompare) { - hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, sczSourceFolder, FALSE); + hr = VariableSetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, sczSourceFolder, FALSE, FALSE); ExitOnFailure(hr, "Failed to set last source."); } } diff --git a/src/engine/condition.cpp b/src/engine/condition.cpp index 28391d2d..cd346680 100644 --- a/src/engine/condition.cpp +++ b/src/engine/condition.cpp @@ -439,6 +439,13 @@ static HRESULT ParseValue( { ExitOnRootFailure(hr, "Failed to find variable."); } + + if (BURN_VARIANT_TYPE_FORMATTED == pValue->Type) + { + // TODO: actually format the value? + hr = BVariantChangeType(pValue, BURN_VARIANT_TYPE_STRING); + ExitOnRootFailure(hr, "Failed to change variable '%ls' type for condition '%ls'", pContext->NextSymbol.Value.sczValue, pContext->wzCondition); + } break; case BURN_SYMBOL_TYPE_NUMBER: __fallthrough; @@ -642,7 +649,7 @@ static HRESULT NextSymbol( ++n; // terminating '"' pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LITERAL; - hr = BVariantSetString(&pContext->NextSymbol.Value, &pContext->wzRead[1], n - 2); + hr = BVariantSetString(&pContext->NextSymbol.Value, &pContext->wzRead[1], n - 2, FALSE); ExitOnFailure(hr, "Failed to set symbol value."); break; default: @@ -746,7 +753,7 @@ static HRESULT NextSymbol( { // identifier pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_IDENTIFIER; - hr = BVariantSetString(&pContext->NextSymbol.Value, pContext->wzRead, n); + hr = BVariantSetString(&pContext->NextSymbol.Value, pContext->wzRead, n, FALSE); ExitOnFailure(hr, "Failed to set symbol value."); } } diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 26e74588..c34024fd 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -113,13 +113,13 @@ extern "C" HRESULT CoreInitialize( if (sczSourceProcessPath) { - hr = VariableSetLiteralString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_PATH, sczSourceProcessPath, TRUE); + hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_PATH, sczSourceProcessPath, TRUE, FALSE); ExitOnFailure(hr, "Failed to set source process path variable."); hr = PathGetDirectory(sczSourceProcessPath, &sczSourceProcessFolder); ExitOnFailure(hr, "Failed to get source process folder from path."); - hr = VariableSetLiteralString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, sczSourceProcessFolder, TRUE); + hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, sczSourceProcessFolder, TRUE, FALSE); ExitOnFailure(hr, "Failed to set source process folder variable."); } @@ -127,7 +127,7 @@ extern "C" HRESULT CoreInitialize( // Needs to be done after ManifestLoadXmlFromBuffer. if (sczOriginalSource) { - hr = VariableSetLiteralString(&pEngineState->variables, BURN_BUNDLE_ORIGINAL_SOURCE, sczOriginalSource, FALSE); + hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_ORIGINAL_SOURCE, sczOriginalSource, FALSE, FALSE); ExitOnFailure(hr, "Failed to set original source variable."); } @@ -258,7 +258,7 @@ extern "C" HRESULT CoreDetect( } else { - hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_INSTALLED, NULL, TRUE); + hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_INSTALLED, NULL, TRUE, FALSE); ExitOnFailure(hr, "Failed to unset the bundle installed built-in variable."); } diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 36c58b49..71c37138 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -538,7 +538,7 @@ static HRESULT RunNormal( // If a layout directory was specified on the command-line, set it as a well-known variable. if (pEngineState->command.wzLayoutDirectory && *pEngineState->command.wzLayoutDirectory) { - hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_LAYOUT_DIRECTORY, pEngineState->command.wzLayoutDirectory, FALSE); + hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_LAYOUT_DIRECTORY, pEngineState->command.wzLayoutDirectory, FALSE, FALSE); ExitOnFailure(hr, "Failed to set layout directory variable to value provided from command-line."); } diff --git a/src/engine/exeengine.cpp b/src/engine/exeengine.cpp index 71540d5d..8d6cd7fd 100644 --- a/src/engine/exeengine.cpp +++ b/src/engine/exeengine.cpp @@ -399,7 +399,7 @@ extern "C" HRESULT ExeEngineExecutePackage( ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->exePackage.pPackage->sczId); // Best effort to set the execute package cache folder and action variables. - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE); VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->exePackage.action, TRUE); hr = PathConcat(sczCachedDirectory, pExecuteAction->exePackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczExecutablePath); @@ -591,8 +591,8 @@ LExit: ReleaseFileHandle(hExecutableFile); // Best effort to clear the execute package cache folder and action variables. - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE); + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE); + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE, FALSE); return hr; } diff --git a/src/engine/logging.cpp b/src/engine/logging.cpp index 818532ad..55b65336 100644 --- a/src/engine/logging.cpp +++ b/src/engine/logging.cpp @@ -149,7 +149,7 @@ extern "C" HRESULT LoggingOpen( if (pLog->sczPathVariable && *pLog->sczPathVariable) { - VariableSetString(pVariables, pLog->sczPathVariable, pLog->sczPath, FALSE); // Ignore failure. + VariableSetString(pVariables, pLog->sczPathVariable, pLog->sczPath, FALSE, FALSE); // Ignore failure. } } @@ -220,7 +220,7 @@ extern "C" HRESULT LoggingSetPackageVariable( hr = StrAllocFormatted(&sczLogPath, L"%ls%hs%ls_%03u_%ls%ls.%ls", pLog->sczPrefix, wzSuffix && *wzSuffix ? "_" : "", wzSuffix && *wzSuffix ? wzSuffix : L"", vdwPackageSequence, pPackage->sczId, fRollback ? L"_rollback" : L"", pLog->sczExtension); ExitOnFailure(hr, "Failed to allocate path for package log."); - hr = VariableSetString(pVariables, fRollback ? pPackage->sczRollbackLogPathVariable : pPackage->sczLogPathVariable, sczLogPath, FALSE); + hr = VariableSetString(pVariables, fRollback ? pPackage->sczRollbackLogPathVariable : pPackage->sczLogPathVariable, sczLogPath, FALSE, FALSE); ExitOnFailure(hr, "Failed to set log path into variable."); if (psczLogPath) diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index 8b8121c1..e7cffd62 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -1156,7 +1156,7 @@ extern "C" HRESULT MsiEngineExecutePackage( ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->msiPackage.pPackage->sczId); // Best effort to set the execute package cache folder variable. - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE); hr = PathConcat(sczCachedDirectory, pExecuteAction->msiPackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczMsiPath); ExitOnFailure(hr, "Failed to build MSI path."); @@ -1313,8 +1313,8 @@ LExit: } // Best effort to clear the execute package cache folder and action variables. - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE); + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE); + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE, FALSE); return hr; } diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp index 57321d75..0854862b 100644 --- a/src/engine/mspengine.cpp +++ b/src/engine/mspengine.cpp @@ -488,7 +488,7 @@ extern "C" HRESULT MspEngineExecutePackage( // TODO: Figure out if this makes sense -- the variable is set to the last patch's path only // Best effort to set the execute package cache folder variable. - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE); hr = PathConcat(sczCachedDirectory, pMspPackage->rgPayloads[0].pPayload->sczFilePath, &sczMspPath); ExitOnFailure(hr, "Failed to build MSP path."); @@ -609,8 +609,8 @@ LExit: } // Best effort to clear the execute package cache folder and action variables. - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE); + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE); + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE, FALSE); return hr; } diff --git a/src/engine/msuengine.cpp b/src/engine/msuengine.cpp index 3818e932..0c81873e 100644 --- a/src/engine/msuengine.cpp +++ b/src/engine/msuengine.cpp @@ -310,7 +310,7 @@ extern "C" HRESULT MsuEngineExecutePackage( ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->msuPackage.pPackage->sczId); // Best effort to set the execute package cache folder variable. - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE); hr = PathConcat(sczCachedDirectory, pExecuteAction->msuPackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczMsuPath); ExitOnFailure(hr, "Failed to build MSU path."); @@ -419,7 +419,7 @@ LExit: } // Best effort to clear the execute package cache folder variable. - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE); return hr; } diff --git a/src/engine/registration.cpp b/src/engine/registration.cpp index bbde92b3..eace62ce 100644 --- a/src/engine/registration.cpp +++ b/src/engine/registration.cpp @@ -426,14 +426,14 @@ extern "C" HRESULT RegistrationSetVariables( if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent) { - hr = VariableSetString(pVariables, BURN_BUNDLE_ACTIVE_PARENT, pRegistration->sczActiveParent, TRUE); + hr = VariableSetString(pVariables, BURN_BUNDLE_ACTIVE_PARENT, pRegistration->sczActiveParent, TRUE, FALSE); ExitOnFailure(hr, "Failed to overwrite the bundle active parent built-in variable."); } - hr = VariableSetString(pVariables, BURN_BUNDLE_PROVIDER_KEY, pRegistration->sczProviderKey, TRUE); + hr = VariableSetString(pVariables, BURN_BUNDLE_PROVIDER_KEY, pRegistration->sczProviderKey, TRUE, FALSE); ExitOnFailure(hr, "Failed to overwrite the bundle provider key built-in variable."); - hr = VariableSetString(pVariables, BURN_BUNDLE_TAG, pRegistration->sczTag, TRUE); + hr = VariableSetString(pVariables, BURN_BUNDLE_TAG, pRegistration->sczTag, TRUE, FALSE); ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable."); hr = VariableSetVersion(pVariables, BURN_BUNDLE_VERSION, pRegistration->qwVersion, TRUE); @@ -1129,7 +1129,7 @@ static HRESULT GetBundleManufacturer( hr = VariableGetString(pVariables, BURN_BUNDLE_MANUFACTURER, psczBundleManufacturer); if (E_NOTFOUND == hr) { - hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_MANUFACTURER, pRegistration->sczPublisher, FALSE); + hr = VariableSetString(pVariables, BURN_BUNDLE_MANUFACTURER, pRegistration->sczPublisher, FALSE, FALSE); ExitOnFailure(hr, "Failed to set bundle manufacturer."); hr = StrAllocString(psczBundleManufacturer, pRegistration->sczPublisher, 0); @@ -1151,7 +1151,7 @@ static HRESULT GetBundleName( hr = VariableGetString(pVariables, BURN_BUNDLE_NAME, psczBundleName); if (E_NOTFOUND == hr) { - hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_NAME, pRegistration->sczDisplayName, FALSE); + hr = VariableSetString(pVariables, BURN_BUNDLE_NAME, pRegistration->sczDisplayName, FALSE, FALSE); ExitOnFailure(hr, "Failed to set bundle name."); hr = StrAllocString(psczBundleName, pRegistration->sczDisplayName, 0); diff --git a/src/engine/search.cpp b/src/engine/search.cpp index 16f8e459..2978edd3 100644 --- a/src/engine/search.cpp +++ b/src/engine/search.cpp @@ -239,7 +239,11 @@ extern "C" HRESULT SearchesParseFromXml( hr = XmlGetAttributeEx(pixnNode, L"VariableType", &scz); ExitOnFailure(hr, "Failed to get @VariableType."); - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"formatted", -1)) + { + pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_FORMATTED; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) { pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_NUMERIC; } @@ -403,14 +407,18 @@ extern "C" HRESULT SearchesParseFromXml( { ExitOnFailure(hr, "Failed to get @Value."); - hr = BVariantSetString(&pSearch->SetVariable.value, scz, 0); + hr = BVariantSetString(&pSearch->SetVariable.value, scz, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); // @Type hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); ExitOnFailure(hr, "Failed to get @Type."); - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"formatted", -1)) + { + valueType = BURN_VARIANT_TYPE_FORMATTED; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) { valueType = BURN_VARIANT_TYPE_NUMERIC; } @@ -673,7 +681,7 @@ static HRESULT DirectorySearchPath( } else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) { - hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE); + hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE); ExitOnFailure(hr, "Failed to set directory search path variable."); } else // must have found a file. @@ -793,7 +801,7 @@ static HRESULT FileSearchPath( } else // found our file. { - hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE); + hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE); ExitOnFailure(hr, "Failed to set variable to file search path."); } @@ -933,7 +941,7 @@ static HRESULT RegistrySearchValue( { // What if there is a hidden variable in sczKey? LogStringLine(REPORT_STANDARD, "Registry key not found. Key = '%ls'", sczKey); - hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value); + hr = VariableSetVariant(pVariables, pSearch->sczVariable, &value); ExitOnFailure(hr, "Failed to clear variable."); ExitFunction1(hr = S_OK); } @@ -945,7 +953,7 @@ static HRESULT RegistrySearchValue( { // What if there is a hidden variable in sczKey or sczValue? LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue); - hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value); + hr = VariableSetVariant(pVariables, pSearch->sczVariable, &value); ExitOnFailure(hr, "Failed to clear variable."); ExitFunction1(hr = S_OK); } @@ -995,7 +1003,7 @@ static HRESULT RegistrySearchValue( } __fallthrough; case REG_SZ: - hr = BVariantSetString(&value, (LPCWSTR)pData, 0); + hr = BVariantSetString(&value, (LPCWSTR)pData, 0, FALSE); break; default: ExitOnFailure(hr = E_NOTIMPL, "Unsupported registry key value type. Type = '%u'", dwType); @@ -1006,8 +1014,8 @@ static HRESULT RegistrySearchValue( hr = BVariantChangeType(&value, pSearch->RegistrySearch.VariableType); ExitOnFailure(hr, "Failed to change value type."); - // Set variable as a literal. - hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value); + // Set variable. + hr = VariableSetVariant(pVariables, pSearch->sczVariable, &value); ExitOnFailure(hr, "Failed to set variable."); LExit: @@ -1077,7 +1085,7 @@ static HRESULT MsiComponentSearch( case BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH: if (INSTALLSTATE_ABSENT == is || INSTALLSTATE_LOCAL == is || INSTALLSTATE_SOURCE == is) { - hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE); + hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE); } break; case BURN_MSI_COMPONENT_SEARCH_TYPE_STATE: @@ -1093,7 +1101,7 @@ static HRESULT MsiComponentSearch( wz[1] = L'\0'; } - hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE); + hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE); } break; } @@ -1234,8 +1242,8 @@ static HRESULT MsiProductSearch( hr = BVariantChangeType(&value, type); ExitOnFailure(hr, "Failed to change value type."); - // Set variable as a literal. - hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value); + // Set variable. + hr = VariableSetVariant(pVariables, pSearch->sczVariable, &value); ExitOnFailure(hr, "Failed to set variable."); LExit: diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp index 6322942e..fb4b74e2 100644 --- a/src/engine/variable.cpp +++ b/src/engine/variable.cpp @@ -83,7 +83,6 @@ static HRESULT SetVariableValue( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __in BURN_VARIANT* pVariant, - __in BOOL fLiteral, __in SET_VARIABLE setBuiltin, __in BOOL fLog ); @@ -335,14 +334,22 @@ extern "C" HRESULT VariablesParseFromXml( { ExitOnFailure(hr, "Failed to get @Value."); - hr = BVariantSetString(&value, scz, 0); + hr = BVariantSetString(&value, scz, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); // @Type hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); ExitOnFailure(hr, "Failed to get @Type."); - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"formatted", -1)) + { + if (!fHidden) + { + LogStringLine(REPORT_STANDARD, "Initializing formatted variable '%ls' to value '%ls'", sczId, value.sczValue); + } + valueType = BURN_VARIANT_TYPE_FORMATTED; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) { if (!fHidden) { @@ -637,10 +644,7 @@ extern "C" HRESULT VariableGetFormatted( } ExitOnFailure(hr, "Failed to get variable: %ls", wzVariable); - // Strings need to get expanded unless they're built-in or literal because they're guaranteed not to have embedded variables. - if (BURN_VARIANT_TYPE_STRING == pVariable->Value.Type && - BURN_VARIABLE_INTERNAL_TYPE_NORMAL == pVariable->internalType && - !pVariable->fLiteral) + if (BURN_VARIANT_TYPE_FORMATTED == pVariable->Value.Type) { hr = BVariantGetString(&pVariable->Value, &scz); ExitOnFailure(hr, "Failed to get unformatted string."); @@ -674,39 +678,24 @@ extern "C" HRESULT VariableSetNumeric( variant.llValue = llValue; variant.Type = BURN_VARIANT_TYPE_NUMERIC; - return SetVariableValue(pVariables, wzVariable, &variant, FALSE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); -} - -extern "C" HRESULT VariableSetLiteralString( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __in_z_opt LPCWSTR wzValue, - __in BOOL fOverwriteBuiltIn - ) -{ - BURN_VARIANT variant = { }; - - // We're not going to encrypt this value, so can access the value directly. - variant.sczValue = (LPWSTR)wzValue; - variant.Type = BURN_VARIANT_TYPE_STRING; - - return SetVariableValue(pVariables, wzVariable, &variant, TRUE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); + return SetVariableValue(pVariables, wzVariable, &variant, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); } extern "C" HRESULT VariableSetString( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __in_z_opt LPCWSTR wzValue, - __in BOOL fOverwriteBuiltIn + __in BOOL fOverwriteBuiltIn, + __in BOOL fFormatted ) { BURN_VARIANT variant = { }; // We're not going to encrypt this value, so can access the value directly. variant.sczValue = (LPWSTR)wzValue; - variant.Type = BURN_VARIANT_TYPE_STRING; + variant.Type = fFormatted ? BURN_VARIANT_TYPE_FORMATTED : BURN_VARIANT_TYPE_STRING; - return SetVariableValue(pVariables, wzVariable, &variant, FALSE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); + return SetVariableValue(pVariables, wzVariable, &variant, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); } extern "C" HRESULT VariableSetVersion( @@ -722,16 +711,7 @@ extern "C" HRESULT VariableSetVersion( variant.qwValue = qwValue; variant.Type = BURN_VARIANT_TYPE_VERSION; - return SetVariableValue(pVariables, wzVariable, &variant, FALSE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); -} - -extern "C" HRESULT VariableSetLiteralVariant( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __in BURN_VARIANT* pVariant - ) -{ - return SetVariableValue(pVariables, wzVariable, pVariant, TRUE, SET_VARIABLE_NOT_BUILTIN, TRUE); + return SetVariableValue(pVariables, wzVariable, &variant, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); } extern "C" HRESULT VariableSetVariant( @@ -740,7 +720,7 @@ extern "C" HRESULT VariableSetVariant( __in BURN_VARIANT * pVariant ) { - return SetVariableValue(pVariables, wzVariable, pVariant, FALSE, SET_VARIABLE_NOT_BUILTIN, TRUE); + return SetVariableValue(pVariables, wzVariable, pVariant, SET_VARIABLE_NOT_BUILTIN, TRUE); } // The contents of psczOut may be sensitive, should keep encrypted and SecureZeroFree @@ -888,6 +868,7 @@ extern "C" HRESULT VariableSerialize( SecureZeroMemory(&qw, sizeof(qw)); break; + case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; case BURN_VARIANT_TYPE_STRING: hr = BVariantGetString(&pVariable->Value, &scz); ExitOnFailure(hr, "Failed to get string."); @@ -901,10 +882,6 @@ extern "C" HRESULT VariableSerialize( hr = E_INVALIDARG; ExitOnFailure(hr, "Unsupported variable type."); } - - // Write literal flag. - hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)pVariable->fLiteral); - ExitOnFailure(hr, "Failed to write literal flag."); } LExit: @@ -928,7 +905,6 @@ extern "C" HRESULT VariableDeserialize( DWORD cVariables = 0; LPWSTR sczName = NULL; BOOL fIncluded = FALSE; - BOOL fLiteral = FALSE; BURN_VARIANT value = { }; LPWSTR scz = NULL; DWORD64 qw = 0; @@ -982,11 +958,12 @@ extern "C" HRESULT VariableDeserialize( SecureZeroMemory(&qw, sizeof(qw)); break; + case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; case BURN_VARIANT_TYPE_STRING: hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &scz); ExitOnFailure(hr, "Failed to read variable value as string."); - hr = BVariantSetString(&value, scz, NULL); + hr = BVariantSetString(&value, scz, NULL, BURN_VARIANT_TYPE_FORMATTED == value.Type); ExitOnFailure(hr, "Failed to set variable value."); ReleaseNullStrSecure(scz); @@ -996,12 +973,8 @@ extern "C" HRESULT VariableDeserialize( ExitOnFailure(hr, "Unsupported variable type."); } - // Read variable literal flag. - hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&fLiteral); - ExitOnFailure(hr, "Failed to read variable literal flag."); - // Set variable. - hr = SetVariableValue(pVariables, sczName, &value, fLiteral, fWasPersisted ? SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS : SET_VARIABLE_ANY, FALSE); + hr = SetVariableValue(pVariables, sczName, &value, fWasPersisted ? SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS : SET_VARIABLE_ANY, FALSE); ExitOnFailure(hr, "Failed to set variable."); // Clean up. @@ -1525,7 +1498,6 @@ static HRESULT SetVariableValue( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __in BURN_VARIANT* pVariant, - __in BOOL fLiteral, __in SET_VARIABLE setBuiltin, __in BOOL fLog ) @@ -1587,6 +1559,7 @@ static HRESULT SetVariableValue( LogStringLine(REPORT_STANDARD, "Setting numeric variable '%ls' to value %lld", wzVariable, pVariant->llValue); break; + case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; case BURN_VARIANT_TYPE_STRING: if (!pVariant->sczValue) { @@ -1594,7 +1567,7 @@ static HRESULT SetVariableValue( } else { - LogStringLine(REPORT_STANDARD, "Setting string variable '%ls' to value '%ls'", wzVariable, pVariant->sczValue); + LogStringLine(REPORT_STANDARD, "Setting %ls variable '%ls' to value '%ls'", BURN_VARIANT_TYPE_FORMATTED == pVariant->Type ? L"formatted" : L"string", wzVariable, pVariant->sczValue); } break; @@ -1613,9 +1586,6 @@ static HRESULT SetVariableValue( hr = BVariantSetValue(&pVariables->rgVariables[iVariable].Value, pVariant); ExitOnFailure(hr, "Failed to set value of variable: %ls", wzVariable); - // Update variable literal flag. - pVariables->rgVariables[iVariable].fLiteral = fLiteral; - LExit: ::LeaveCriticalSection(&pVariables->csAccess); @@ -1827,7 +1797,7 @@ static HRESULT InitializeVariableComputerName( } // set value - hr = BVariantSetString(pValue, wzComputerName, 0); + hr = BVariantSetString(pValue, wzComputerName, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: @@ -1875,7 +1845,7 @@ static HRESULT InitializeVariableCsidlFolder( ExitOnRootFailure(hr, "Failed to get shell folder."); // set value - hr = BVariantSetString(pValue, sczPath, 0); + hr = BVariantSetString(pValue, sczPath, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: @@ -1901,7 +1871,7 @@ static HRESULT InitializeVariableTempFolder( } // set value - hr = BVariantSetString(pValue, wzPath, 0); + hr = BVariantSetString(pValue, wzPath, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: @@ -1972,7 +1942,7 @@ static HRESULT InitializeVariableSystemFolder( } // set value - hr = BVariantSetString(pValue, wzSystemFolder, 0); + hr = BVariantSetString(pValue, wzSystemFolder, 0, FALSE); ExitOnFailure(hr, "Failed to set system folder variant value."); LExit: @@ -2003,7 +1973,7 @@ static HRESULT InitializeVariableWindowsVolumeFolder( } // set value - hr = BVariantSetString(pValue, wzVolumePath, 0); + hr = BVariantSetString(pValue, wzVolumePath, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: @@ -2130,7 +2100,7 @@ static HRESULT InitializeVariableString( LPCWSTR wzValue = (LPCWSTR)dwpData; // set value - hr = BVariantSetString(pValue, wzValue, 0); + hr = BVariantSetString(pValue, wzValue, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: @@ -2176,7 +2146,7 @@ static HRESULT InitializeVariableRegistryFolder( ExitOnFailure(hr, "Failed to get 64-bit folder."); // set value - hr = BVariantSetString(pValue, sczPath, 0); + hr = BVariantSetString(pValue, sczPath, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: @@ -2212,7 +2182,7 @@ static HRESULT InitializeVariable6432Folder( } // set value - hr = BVariantSetString(pValue, sczPath, 0); + hr = BVariantSetString(pValue, sczPath, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: @@ -2249,7 +2219,7 @@ static HRESULT InitializeVariableDate( } // set value - hr = BVariantSetString(pValue, sczDate, cchDate); + hr = BVariantSetString(pValue, sczDate, cchDate, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: @@ -2266,7 +2236,7 @@ static HRESULT InitializeVariableInstallerName( HRESULT hr = S_OK; // set value - hr = BVariantSetString(pValue, L"WiX Burn", 0); + hr = BVariantSetString(pValue, L"WiX Burn", 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: @@ -2285,7 +2255,7 @@ static HRESULT InitializeVariableInstallerVersion( ExitOnFailure(hr, "Failed to copy the engine version."); // set value - hr = BVariantSetString(pValue, sczVersion, 0); + hr = BVariantSetString(pValue, sczVersion, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: @@ -2325,7 +2295,7 @@ static HRESULT InitializeVariableLogonUser( } // set value - hr = BVariantSetString(pValue, wzUserName, 0); + hr = BVariantSetString(pValue, wzUserName, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: diff --git a/src/engine/variable.h b/src/engine/variable.h index 63f12cdf..648c5daf 100644 --- a/src/engine/variable.h +++ b/src/engine/variable.h @@ -39,8 +39,7 @@ typedef struct _BURN_VARIABLE { LPWSTR sczName; BURN_VARIANT Value; - BOOL fHidden; - BOOL fLiteral; // if fLiteral, then when formatting this variable its value should be used as is (don't continue recursively formatting). + BOOL fHidden; BOOL fPersisted; // used for late initialization of built-in variables @@ -104,17 +103,12 @@ HRESULT VariableSetNumeric( __in LONGLONG llValue, __in BOOL fOverwriteBuiltIn ); -HRESULT VariableSetLiteralString( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __in_z_opt LPCWSTR wzValue, - __in BOOL fOverwriteBuiltIn - ); HRESULT VariableSetString( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __in_z_opt LPCWSTR wzValue, - __in BOOL fOverwriteBuiltIn + __in BOOL fOverwriteBuiltIn, + __in BOOL fFormatted ); HRESULT VariableSetVersion( __in BURN_VARIABLES* pVariables, @@ -122,11 +116,6 @@ HRESULT VariableSetVersion( __in DWORD64 qwValue, __in BOOL fOverwriteBuiltIn ); -HRESULT VariableSetLiteralVariant( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __in BURN_VARIANT* pVariant - ); HRESULT VariableSetVariant( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, diff --git a/src/engine/variant.cpp b/src/engine/variant.cpp index 5e9bd29a..43bc19c4 100644 --- a/src/engine/variant.cpp +++ b/src/engine/variant.cpp @@ -32,7 +32,8 @@ extern "C" void BVariantUninitialize( __in BURN_VARIANT* pVariant ) { - if (BURN_VARIANT_TYPE_STRING == pVariant->Type) + if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type || + BURN_VARIANT_TYPE_STRING == pVariant->Type) { StrSecureZeroFreeString(pVariant->sczValue); } @@ -53,6 +54,7 @@ extern "C" HRESULT BVariantGetNumeric( case BURN_VARIANT_TYPE_NUMERIC: BVariantRetrieveNumeric(pVariant, pllValue); break; + case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; case BURN_VARIANT_TYPE_STRING: hr = BVariantRetrieveDecryptedString(pVariant, &sczValue); if (SUCCEEDED(hr)) @@ -97,6 +99,7 @@ extern "C" HRESULT BVariantGetString( } SecureZeroMemory(&llValue, sizeof(llValue)); break; + case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; case BURN_VARIANT_TYPE_STRING: hr = BVariantRetrieveDecryptedString(pVariant, psczValue); break; @@ -136,6 +139,7 @@ extern "C" HRESULT BVariantGetVersion( case BURN_VARIANT_TYPE_NUMERIC: BVariantRetrieveNumeric(pVariant, (LONGLONG*)pqwValue); break; + case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; case BURN_VARIANT_TYPE_STRING: hr = BVariantRetrieveDecryptedString(pVariant, &sczValue); if (SUCCEEDED(hr)) @@ -167,7 +171,8 @@ extern "C" HRESULT BVariantSetNumeric( HRESULT hr = S_OK; BOOL fEncrypt = pVariant->fEncryptString; - if (BURN_VARIANT_TYPE_STRING == pVariant->Type) + if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type || + BURN_VARIANT_TYPE_STRING == pVariant->Type) { StrSecureZeroFreeString(pVariant->sczValue); } @@ -182,7 +187,8 @@ extern "C" HRESULT BVariantSetNumeric( extern "C" HRESULT BVariantSetString( __in BURN_VARIANT* pVariant, __in_z_opt LPCWSTR wzValue, - __in DWORD_PTR cchValue + __in DWORD_PTR cchValue, + __in BOOL fFormatted ) { HRESULT hr = S_OK; @@ -194,7 +200,8 @@ extern "C" HRESULT BVariantSetString( } else // assign the value. { - if (BURN_VARIANT_TYPE_STRING != pVariant->Type) + if (BURN_VARIANT_TYPE_FORMATTED != pVariant->Type && + BURN_VARIANT_TYPE_STRING != pVariant->Type) { memset(pVariant, 0, sizeof(BURN_VARIANT)); } @@ -207,7 +214,7 @@ extern "C" HRESULT BVariantSetString( hr = StrAllocStringSecure(&pVariant->sczValue, wzValue, cchValue); ExitOnFailure(hr, "Failed to copy string."); - pVariant->Type = BURN_VARIANT_TYPE_STRING; + pVariant->Type = fFormatted ? BURN_VARIANT_TYPE_FORMATTED : BURN_VARIANT_TYPE_STRING; } LExit: @@ -223,7 +230,8 @@ extern "C" HRESULT BVariantSetVersion( HRESULT hr = S_OK; BOOL fEncryptValue = pVariant->fEncryptString; - if (BURN_VARIANT_TYPE_STRING == pVariant->Type) + if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type || + BURN_VARIANT_TYPE_STRING == pVariant->Type) { StrSecureZeroFreeString(pVariant->sczValue); } @@ -259,11 +267,12 @@ extern "C" HRESULT BVariantSetValue( } SecureZeroMemory(&llValue, sizeof(llValue)); break; + case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; case BURN_VARIANT_TYPE_STRING: hr = BVariantGetString(pValue, &sczValue); if (SUCCEEDED(hr)) { - hr = BVariantSetString(pVariant, sczValue, 0); + hr = BVariantSetString(pVariant, sczValue, 0, BURN_VARIANT_TYPE_FORMATTED == pValue->Type); } StrSecureZeroFreeString(sczValue); break; @@ -310,11 +319,12 @@ extern "C" HRESULT BVariantCopy( } SecureZeroMemory(&llValue, sizeof(llValue)); break; + case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; case BURN_VARIANT_TYPE_STRING: hr = BVariantGetString(pSource, &sczValue); if (SUCCEEDED(hr)) { - hr = BVariantSetString(pTarget, sczValue, 0); + hr = BVariantSetString(pTarget, sczValue, 0, BURN_VARIANT_TYPE_FORMATTED == pSource->Type); } StrSecureZeroFreeString(sczValue); break; @@ -350,6 +360,12 @@ extern "C" HRESULT BVariantChangeType( { ExitFunction(); // variant already is of the requested type } + else if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type && BURN_VARIANT_TYPE_STRING == type || + BURN_VARIANT_TYPE_STRING == pVariant->Type && BURN_VARIANT_TYPE_FORMATTED == type) + { + pVariant->Type = type; + ExitFunction(); + } switch (type) { @@ -359,6 +375,7 @@ extern "C" HRESULT BVariantChangeType( case BURN_VARIANT_TYPE_NUMERIC: hr = BVariantGetNumeric(pVariant, &variant.llValue); break; + case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; case BURN_VARIANT_TYPE_STRING: hr = BVariantGetString(pVariant, &variant.sczValue); break; @@ -400,6 +417,7 @@ extern "C" HRESULT BVariantSetEncryption( case BURN_VARIANT_TYPE_VERSION: hr = S_OK; break; + case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; case BURN_VARIANT_TYPE_STRING: hr = BVariantEncryptString(pVariant, fEncrypt); break; diff --git a/src/engine/variant.h b/src/engine/variant.h index 73fbe076..35463479 100644 --- a/src/engine/variant.h +++ b/src/engine/variant.h @@ -12,8 +12,9 @@ extern "C" { enum BURN_VARIANT_TYPE { BURN_VARIANT_TYPE_NONE, + BURN_VARIANT_TYPE_FORMATTED, BURN_VARIANT_TYPE_NUMERIC, - BURN_VARIANT_TYPE_STRING, + BURN_VARIANT_TYPE_STRING, // when formatting this value should be used as is (don't continue recursively formatting). BURN_VARIANT_TYPE_VERSION, }; @@ -57,7 +58,8 @@ HRESULT BVariantSetNumeric( HRESULT BVariantSetString( __in BURN_VARIANT* pVariant, __in_z_opt LPCWSTR wzValue, - __in DWORD_PTR cchValue + __in DWORD_PTR cchValue, + __in BOOL fFormatted ); HRESULT BVariantSetVersion( __in BURN_VARIANT* pVariant, diff --git a/src/test/BurnUnitTest/SearchTest.cpp b/src/test/BurnUnitTest/SearchTest.cpp index 48ab60aa..32107d87 100644 --- a/src/test/BurnUnitTest/SearchTest.cpp +++ b/src/test/BurnUnitTest/SearchTest.cpp @@ -66,8 +66,8 @@ namespace Bootstrapper pin_ptr wzDirectory1 = PtrToStringChars(this->TestContext->TestDirectory); pin_ptr wzDirectory2 = PtrToStringChars(System::IO::Path::Combine(this->TestContext->TestDirectory, gcnew String(L"none"))); - VariableSetStringHelper(&variables, L"Directory1", wzDirectory1); - VariableSetStringHelper(&variables, L"Directory2", wzDirectory2); + VariableSetStringHelper(&variables, L"Directory1", wzDirectory1, FALSE); + VariableSetStringHelper(&variables, L"Directory2", wzDirectory2, FALSE); LPCWSTR wzDocument = L"" @@ -117,8 +117,8 @@ namespace Bootstrapper hr = FileVersion(wzFile2, &uliVersion.HighPart, &uliVersion.LowPart); TestThrowOnFailure(hr, L"Failed to get DLL version."); - VariableSetStringHelper(&variables, L"File1", wzFile1); - VariableSetStringHelper(&variables, L"File2", wzFile2); + VariableSetStringHelper(&variables, L"File1", wzFile1, FALSE); + VariableSetStringHelper(&variables, L"File2", wzFile2, FALSE); LPCWSTR wzDocument = L"" @@ -191,8 +191,8 @@ namespace Bootstrapper Assert::True(SUCCEEDED(hr)); } - VariableSetStringHelper(&variables, L"MyKey", L"SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"); - VariableSetStringHelper(&variables, L"MyValue", L"String"); + VariableSetStringHelper(&variables, L"MyKey", L"SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value", FALSE); + VariableSetStringHelper(&variables, L"MyValue", L"String", FALSE); LPCWSTR wzDocument = L"" @@ -219,8 +219,9 @@ namespace Bootstrapper L" " L" " L" " - L" " - L" " + L" " + L" " + L" " L""; // load XML document @@ -263,6 +264,7 @@ namespace Bootstrapper Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable23")); Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable24")); Assert::Equal(gcnew String(L"Msi.Package"), VariableGetStringHelper(&variables, L"Variable25")); + Assert::Equal(gcnew String(L"Msi.Package"), VariableGetStringHelper(&variables, L"Variable26")); } finally { @@ -525,6 +527,75 @@ namespace Bootstrapper SearchesUninitialize(&searches); } } + + [Fact] + void SetVariableSearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; + try + { + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // set variables + VariableSetStringHelper(&variables, L"OVERWRITTEN_STRING", L"ORIGINAL", FALSE); + VariableSetNumericHelper(&variables, L"OVERWRITTEN_NUMBER", 5); + VariableSetNumericHelper(&variables, L"REMOVED_NUMBER", 22); + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + + // check variable values + Assert::Equal(gcnew String(L"VAL1"), VariableGetStringHelper(&variables, L"PROP1")); + Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"PROP2")); + Assert::Equal(gcnew String(L"2"), VariableGetStringHelper(&variables, L"PROP2")); + Assert::Equal(gcnew String(L"VAL3"), VariableGetStringHelper(&variables, L"PROP3")); + Assert::Equal(gcnew String(L"VAL4"), VariableGetStringHelper(&variables, L"PROP4")); + Assert::Equal(gcnew String(L"VAL5"), VariableGetStringHelper(&variables, L"PROP5")); + Assert::Equal(gcnew String(L"VAL6"), VariableGetStringHelper(&variables, L"PROP6")); + Assert::Equal(7ll, VariableGetNumericHelper(&variables, L"PROP7")); + Assert::Equal(MAKEQWORDVERSION(1, 1, 0, 0), VariableGetVersionHelper(&variables, L"PROP8")); + Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetStringHelper(&variables, L"PROP8")); + Assert::Equal(gcnew String(L"[VAL9]"), VariableGetStringHelper(&variables, L"PROP9")); + + Assert::Equal(42ll, VariableGetNumericHelper(&variables, L"OVERWRITTEN_STRING")); + Assert::Equal(gcnew String(L"NEW"), VariableGetStringHelper(&variables, L"OVERWRITTEN_NUMBER")); + Assert::Equal((int)BURN_VARIANT_TYPE_NONE, VariableGetTypeHelper(&variables, L"REMOVED_NUMBER")); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } }; } } diff --git a/src/test/BurnUnitTest/VariableHelpers.cpp b/src/test/BurnUnitTest/VariableHelpers.cpp index 9ce46a76..fdfb9191 100644 --- a/src/test/BurnUnitTest/VariableHelpers.cpp +++ b/src/test/BurnUnitTest/VariableHelpers.cpp @@ -17,11 +17,11 @@ namespace Test { namespace Bootstrapper { - void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue) + void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue, BOOL fFormatted) { HRESULT hr = S_OK; - hr = VariableSetString(pVariables, wzVariable, wzValue, FALSE); + hr = VariableSetString(pVariables, wzVariable, wzValue, FALSE, fFormatted); TestThrowOnFailure2(hr, L"Failed to set %s to: %s", wzVariable, wzValue); } diff --git a/src/test/BurnUnitTest/VariableHelpers.h b/src/test/BurnUnitTest/VariableHelpers.h index 98c52649..8c2b081a 100644 --- a/src/test/BurnUnitTest/VariableHelpers.h +++ b/src/test/BurnUnitTest/VariableHelpers.h @@ -14,7 +14,7 @@ namespace Bootstrapper { -void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue); +void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue, BOOL fFormatted); void VariableSetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LONGLONG llValue); void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, DWORD64 qwValue); System::String^ VariableGetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); diff --git a/src/test/BurnUnitTest/VariableTest.cpp b/src/test/BurnUnitTest/VariableTest.cpp index 7d670744..0b49a530 100644 --- a/src/test/BurnUnitTest/VariableTest.cpp +++ b/src/test/BurnUnitTest/VariableTest.cpp @@ -35,21 +35,22 @@ namespace Bootstrapper TestThrowOnFailure(hr, L"Failed to initialize variables."); // set variables - VariableSetStringHelper(&variables, L"PROP1", L"VAL1"); + VariableSetStringHelper(&variables, L"PROP1", L"VAL1", FALSE); VariableSetNumericHelper(&variables, L"PROP2", 2); - VariableSetStringHelper(&variables, L"PROP5", L"VAL5"); - VariableSetStringHelper(&variables, L"PROP3", L"VAL3"); - VariableSetStringHelper(&variables, L"PROP4", L"VAL4"); - VariableSetStringHelper(&variables, L"PROP6", L"VAL6"); - VariableSetStringHelper(&variables, L"PROP7", L"7"); + VariableSetStringHelper(&variables, L"PROP5", L"VAL5", FALSE); + VariableSetStringHelper(&variables, L"PROP3", L"VAL3", FALSE); + VariableSetStringHelper(&variables, L"PROP4", L"VAL4", FALSE); + VariableSetStringHelper(&variables, L"PROP6", L"VAL6", FALSE); + VariableSetStringHelper(&variables, L"PROP7", L"7", FALSE); VariableSetVersionHelper(&variables, L"PROP8", MAKEQWORDVERSION(1,1,0,0)); + VariableSetStringHelper(&variables, L"PROP9", L"[VAL9]", TRUE); // set overwritten variables - VariableSetStringHelper(&variables, L"OVERWRITTEN_STRING", L"ORIGINAL"); + VariableSetStringHelper(&variables, L"OVERWRITTEN_STRING", L"ORIGINAL", FALSE); VariableSetNumericHelper(&variables, L"OVERWRITTEN_STRING", 42); VariableSetNumericHelper(&variables, L"OVERWRITTEN_NUMBER", 5); - VariableSetStringHelper(&variables, L"OVERWRITTEN_NUMBER", L"NEW"); + VariableSetStringHelper(&variables, L"OVERWRITTEN_NUMBER", L"NEW", FALSE); // get and verify variable values Assert::Equal(gcnew String(L"VAL1"), VariableGetStringHelper(&variables, L"PROP1")); @@ -62,6 +63,7 @@ namespace Bootstrapper Assert::Equal(7ll, VariableGetNumericHelper(&variables, L"PROP7")); Assert::Equal(MAKEQWORDVERSION(1,1,0,0), VariableGetVersionHelper(&variables, L"PROP8")); Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetStringHelper(&variables, L"PROP8")); + Assert::Equal(gcnew String(L"[VAL9]"), VariableGetStringHelper(&variables, L"PROP9")); Assert::Equal(42ll, VariableGetNumericHelper(&variables, L"OVERWRITTEN_STRING")); Assert::Equal(gcnew String(L"NEW"), VariableGetStringHelper(&variables, L"OVERWRITTEN_NUMBER")); @@ -87,6 +89,7 @@ namespace Bootstrapper L" "; hr = VariableInitialize(&variables); @@ -103,10 +106,12 @@ namespace Bootstrapper Assert::Equal((int)BURN_VARIANT_TYPE_STRING, VariableGetTypeHelper(&variables, L"Var2")); Assert::Equal((int)BURN_VARIANT_TYPE_VERSION, VariableGetTypeHelper(&variables, L"Var3")); Assert::Equal((int)BURN_VARIANT_TYPE_NONE, VariableGetTypeHelper(&variables, L"Var4")); + Assert::Equal((int)BURN_VARIANT_TYPE_FORMATTED, VariableGetTypeHelper(&variables, L"Var6")); Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Var1")); Assert::Equal(gcnew String(L"String value."), VariableGetStringHelper(&variables, L"Var2")); Assert::Equal(MAKEQWORDVERSION(1,2,3,4), VariableGetVersionHelper(&variables, L"Var3")); + Assert::Equal(gcnew String(L"[Formatted]"), VariableGetStringHelper(&variables, L"Var6")); } finally { @@ -128,9 +133,13 @@ namespace Bootstrapper TestThrowOnFailure(hr, L"Failed to initialize variables."); // set variables - VariableSetStringHelper(&variables, L"PROP1", L"VAL1"); - VariableSetStringHelper(&variables, L"PROP2", L"VAL2"); + VariableSetStringHelper(&variables, L"PROP1", L"VAL1", FALSE); + VariableSetStringHelper(&variables, L"PROP2", L"VAL2", FALSE); VariableSetNumericHelper(&variables, L"PROP3", 3); + VariableSetStringHelper(&variables, L"PROP4", L"[PROP1]", FALSE); + VariableSetStringHelper(&variables, L"PROP5", L"[PROP2]", FALSE); + VariableSetStringHelper(&variables, L"PROP6", L"[PROP4]", TRUE); + VariableSetStringHelper(&variables, L"PROP7", L"[PROP5]", TRUE); // test string formatting Assert::Equal(gcnew String(L"NOPROP"), VariableFormatStringHelper(&variables, L"NOPROP")); @@ -148,6 +157,10 @@ namespace Bootstrapper Assert::Equal(gcnew String(L"[NONE"), VariableFormatStringHelper(&variables, L"[NONE")); Assert::Equal(gcnew String(L"VAL2"), VariableGetFormattedHelper(&variables, L"PROP2")); Assert::Equal(gcnew String(L"3"), VariableGetFormattedHelper(&variables, L"PROP3")); + Assert::Equal(gcnew String(L"[PROP1]"), VariableGetFormattedHelper(&variables, L"PROP4")); + Assert::Equal(gcnew String(L"[PROP2]"), VariableGetFormattedHelper(&variables, L"PROP5")); + Assert::Equal(gcnew String(L"[PROP1]"), VariableGetFormattedHelper(&variables, L"PROP6")); + Assert::Equal(gcnew String(L"[PROP2]"), VariableGetFormattedHelper(&variables, L"PROP7")); hr = VariableFormatString(&variables, L"PRE [PROP1] POST", &scz, &cch); TestThrowOnFailure(hr, L"Failed to format string"); @@ -186,15 +199,15 @@ namespace Bootstrapper TestThrowOnFailure(hr, L"Failed to initialize variables."); // set variables - VariableSetStringHelper(&variables, L"PROP1", L"VAL1"); - VariableSetStringHelper(&variables, L"PROP2", L"VAL2"); - VariableSetStringHelper(&variables, L"PROP3", L"VAL3"); - VariableSetStringHelper(&variables, L"PROP4", L"BEGIN MID END"); + VariableSetStringHelper(&variables, L"PROP1", L"VAL1", FALSE); + VariableSetStringHelper(&variables, L"PROP2", L"VAL2", FALSE); + VariableSetStringHelper(&variables, L"PROP3", L"VAL3", FALSE); + VariableSetStringHelper(&variables, L"PROP4", L"BEGIN MID END", FALSE); VariableSetNumericHelper(&variables, L"PROP5", 5); VariableSetNumericHelper(&variables, L"PROP6", 6); - VariableSetStringHelper(&variables, L"PROP7", L""); + VariableSetStringHelper(&variables, L"PROP7", L"", FALSE); VariableSetNumericHelper(&variables, L"PROP8", 0); - VariableSetStringHelper(&variables, L"_PROP9", L"VAL9"); + VariableSetStringHelper(&variables, L"_PROP9", L"VAL9", FALSE); VariableSetNumericHelper(&variables, L"PROP10", -10); VariableSetNumericHelper(&variables, L"PROP11", 9223372036854775807ll); VariableSetNumericHelper(&variables, L"PROP12", -9223372036854775808ll); @@ -208,7 +221,11 @@ namespace Bootstrapper VariableSetVersionHelper(&variables, L"PROP20", MAKEQWORDVERSION(1,1,1,1)); VariableSetNumericHelper(&variables, L"vPROP21", 1); VariableSetVersionHelper(&variables, L"PROP22", MAKEQWORDVERSION(65535,65535,65535,65535)); - VariableSetStringHelper(&variables, L"PROP23", L"1.1.1"); + VariableSetStringHelper(&variables, L"PROP23", L"1.1.1", FALSE); + VariableSetStringHelper(&variables, L"PROP24", L"[PROP1]", TRUE); + VariableSetStringHelper(&variables, L"PROP25", L"[PROP7]", TRUE); + VariableSetStringHelper(&variables, L"PROP26", L"[PROP8]", TRUE); + VariableSetStringHelper(&variables, L"PROP27", L"[PROP16]", TRUE); // test conditions Assert::True(EvaluateConditionHelper(&variables, L"PROP1")); @@ -218,6 +235,10 @@ namespace Bootstrapper Assert::True(EvaluateConditionHelper(&variables, L"_PROP9")); Assert::False(EvaluateConditionHelper(&variables, L"PROP16")); Assert::True(EvaluateConditionHelper(&variables, L"PROP17")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP24")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP25")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP26")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP27")); Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\"")); Assert::False(EvaluateConditionHelper(&variables, L"NONE = \"NOT\"")); @@ -365,10 +386,11 @@ namespace Bootstrapper hr = VariableInitialize(&variables1); TestThrowOnFailure(hr, L"Failed to initialize variables."); - VariableSetStringHelper(&variables1, L"PROP1", L"VAL1"); + VariableSetStringHelper(&variables1, L"PROP1", L"VAL1", FALSE); VariableSetNumericHelper(&variables1, L"PROP2", 2); VariableSetVersionHelper(&variables1, L"PROP3", MAKEQWORDVERSION(1,1,1,1)); - VariableSetStringHelper(&variables1, L"PROP4", L"VAL4"); + VariableSetStringHelper(&variables1, L"PROP4", L"VAL4", FALSE); + VariableSetStringHelper(&variables1, L"PROP5", L"[PROP1]", TRUE); hr = VariableSerialize(&variables1, FALSE, &pbBuffer, &cbBuffer); TestThrowOnFailure(hr, L"Failed to serialize variables."); @@ -384,6 +406,13 @@ namespace Bootstrapper Assert::Equal(2ll, VariableGetNumericHelper(&variables2, L"PROP2")); Assert::Equal(MAKEQWORDVERSION(1,1,1,1), VariableGetVersionHelper(&variables2, L"PROP3")); Assert::Equal(gcnew String(L"VAL4"), VariableGetStringHelper(&variables2, L"PROP4")); + Assert::Equal(gcnew String(L"[PROP1]"), VariableGetStringHelper(&variables2, L"PROP5")); + + Assert::Equal((int)BURN_VARIANT_TYPE_STRING, VariableGetTypeHelper(&variables2, L"PROP1")); + Assert::Equal((int)BURN_VARIANT_TYPE_NUMERIC, VariableGetTypeHelper(&variables2, L"PROP2")); + Assert::Equal((int)BURN_VARIANT_TYPE_VERSION, VariableGetTypeHelper(&variables2, L"PROP3")); + Assert::Equal((int)BURN_VARIANT_TYPE_STRING, VariableGetTypeHelper(&variables2, L"PROP4")); + Assert::Equal((int)BURN_VARIANT_TYPE_FORMATTED, VariableGetTypeHelper(&variables2, L"PROP5")); } finally { @@ -420,7 +449,7 @@ namespace Bootstrapper } // attempt to set a built-in property - hr = VariableSetString(&variables, L"VersionNT", L"VAL", FALSE); + hr = VariableSetString(&variables, L"VersionNT", L"VAL", FALSE, FALSE); Assert::Equal(E_INVALIDARG, hr); Assert::False(EvaluateConditionHelper(&variables, L"VersionNT = \"VAL\"")); diff --git a/src/test/BurnUnitTest/VariantTest.cpp b/src/test/BurnUnitTest/VariantTest.cpp index d16ac699..c982db72 100644 --- a/src/test/BurnUnitTest/VariantTest.cpp +++ b/src/test/BurnUnitTest/VariantTest.cpp @@ -25,9 +25,9 @@ namespace Bootstrapper [Fact] void VariantBasicTest() { - BURN_VARIANT expectedVariants[8]; - BURN_VARIANT actualVariants[8]; - for (DWORD i = 0; i < 8; i++) + BURN_VARIANT expectedVariants[10]; + BURN_VARIANT actualVariants[10]; + for (DWORD i = 0; i < 10; i++) { BVariantUninitialize(expectedVariants + i); BVariantUninitialize(actualVariants + i); @@ -43,6 +43,8 @@ namespace Bootstrapper InitVersionValue(expectedVariants + 5, MAKEQWORDVERSION(1, 1, 1, 0), TRUE, L"PROP6", actualVariants + 5); InitStringValue(expectedVariants + 6, L"7", TRUE, L"PROP7", actualVariants + 6); InitNumericValue(expectedVariants + 7, 11, TRUE, L"PROP8", actualVariants + 7); + InitFormattedValue(expectedVariants + 8, L"VAL9", FALSE, L"PROP9", actualVariants + 8); + InitFormattedValue(expectedVariants + 9, L"VAL10", TRUE, L"PROP10", actualVariants + 9); VerifyNumericValue(expectedVariants + 0, actualVariants + 0); VerifyStringValue(expectedVariants + 1, actualVariants + 1); @@ -52,10 +54,12 @@ namespace Bootstrapper VerifyVersionValue(expectedVariants + 5, actualVariants + 5); VerifyStringValue(expectedVariants + 6, actualVariants + 6); VerifyNumericValue(expectedVariants + 7, actualVariants + 7); + VerifyFormattedValue(expectedVariants + 8, actualVariants + 8); + VerifyFormattedValue(expectedVariants + 9, actualVariants + 9); } finally { - for (DWORD i = 0; i < 8; i++) + for (DWORD i = 0; i < 10; i++) { BVariantUninitialize(expectedVariants + i); BVariantUninitialize(actualVariants + i); @@ -64,6 +68,26 @@ namespace Bootstrapper } private: + void InitFormattedValue(BURN_VARIANT* pValue, LPWSTR wzValue, BOOL fHidden, LPCWSTR wz, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + pValue->Type = BURN_VARIANT_TYPE_FORMATTED; + + hr = StrAllocString(&pValue->sczValue, wzValue, 0); + NativeAssert::Succeeded(hr, "Failed to alloc string: {0}", wzValue); + + hr = BVariantCopy(pValue, pActualValue); + NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); + + if (fHidden) + { + hr = BVariantSetEncryption(pActualValue, TRUE); + NativeAssert::Succeeded(hr, "Failed to encrypt variant {0}", wz); + + NativeAssert::True(pActualValue->fEncryptString); + } + } + void InitNoneValue(BURN_VARIANT* pValue, BOOL fHidden, LPCWSTR wz, BURN_VARIANT* pActualValue) { HRESULT hr = S_OK; @@ -137,6 +161,26 @@ namespace Bootstrapper } } + void VerifyFormattedValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + NativeAssert::Equal(BURN_VARIANT_TYPE_FORMATTED, pExpectedValue->Type); + NativeAssert::Equal(BURN_VARIANT_TYPE_FORMATTED, pActualValue->Type); + + try + { + hr = BVariantGetString(pActualValue, &sczValue); + NativeAssert::Succeeded(hr, "Failed to get string value"); + + NativeAssert::StringEqual(pExpectedValue->sczValue, sczValue); + } + finally + { + ReleaseStr(sczValue); + } + } + void VerifyNumericValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) { HRESULT hr = S_OK; @@ -167,7 +211,7 @@ namespace Bootstrapper try { hr = BVariantGetString(pActualValue, &sczValue); - NativeAssert::Succeeded(hr, "Failed to get numeric value"); + NativeAssert::Succeeded(hr, "Failed to get string value"); NativeAssert::StringEqual(pExpectedValue->sczValue, sczValue); } @@ -185,7 +229,7 @@ namespace Bootstrapper NativeAssert::Equal(BURN_VARIANT_TYPE_VERSION, pActualValue->Type); hr = BVariantGetVersion(pActualValue, &qwValue); - NativeAssert::Succeeded(hr, "Failed to get numeric value"); + NativeAssert::Succeeded(hr, "Failed to get version value"); NativeAssert::Equal(pExpectedValue->qwValue, qwValue); } -- cgit v1.2.3-55-g6feb From 3f8e35223216ebbe7f6683a5031a5a97bbc66d5a Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 1 Aug 2020 09:36:12 -0600 Subject: Update splash screen to be per-monitor DPI aware. Remove GDI+ from engine since the higher quality scaling isn't worth the additional dependency. --- .../WixToolset.BootstrapperCore.Native.proj | 2 +- src/engine/engine.cpp | 9 + src/engine/engine.vcxproj | 4 +- src/engine/packages.config | 2 +- src/engine/precomp.h | 7 +- src/engine/splashscreen.cpp | 277 ++++++++++++--------- src/stub/packages.config | 2 +- src/stub/stub.vcxproj | 6 +- src/test/BurnUnitTest/BurnUnitTest.vcxproj | 6 +- src/test/BurnUnitTest/packages.config | 2 +- 10 files changed, 180 insertions(+), 137 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.proj b/src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.proj index 113fe7ae..c0a99472 100644 --- a/src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.proj +++ b/src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.proj @@ -1,4 +1,4 @@ - + diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 71c37138..ae5b690c 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -89,6 +89,7 @@ extern "C" HRESULT EngineRun( BOOL fComInitialized = FALSE; BOOL fLogInitialized = FALSE; BOOL fCrypInitialized = FALSE; + BOOL fDpiuInitialized = FALSE; BOOL fRegInitialized = FALSE; BOOL fWiuInitialized = FALSE; BOOL fXmlInitialized = FALSE; @@ -132,6 +133,9 @@ extern "C" HRESULT EngineRun( ExitOnFailure(hr, "Failed to initialize Cryputil."); fCrypInitialized = TRUE; + DpiuInitialize(); + fDpiuInitialized = TRUE; + hr = RegInitialize(); ExitOnFailure(hr, "Failed to initialize Regutil."); fRegInitialized = TRUE; @@ -241,6 +245,11 @@ LExit: RegUninitialize(); } + if (fDpiuInitialized) + { + DpiuUninitialize(); + } + if (fCrypInitialized) { CrypUninitialize(); diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index 906792a6..ef5c1602 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -2,7 +2,7 @@ - + @@ -165,7 +165,7 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + diff --git a/src/engine/packages.config b/src/engine/packages.config index 04a6553e..e7fa32d0 100644 --- a/src/engine/packages.config +++ b/src/engine/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/engine/precomp.h b/src/engine/precomp.h index c60d7c0e..b5c5e65e 100644 --- a/src/engine/precomp.h +++ b/src/engine/precomp.h @@ -6,11 +6,6 @@ #include #include -#pragma warning(push) -#pragma warning(disable:4458) // declaration of 'xxx' hides class member -#include -#pragma warning(pop) - #include #include #include @@ -37,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -60,6 +54,7 @@ #include #include #include +#include #include "..\WixToolset.BootstrapperCore.Native\inc\BootstrapperEngine.h" #include "..\WixToolset.BootstrapperCore.Native\inc\BootstrapperApplication.h" diff --git a/src/engine/splashscreen.cpp b/src/engine/splashscreen.cpp index 1f95886a..cad8c88c 100644 --- a/src/engine/splashscreen.cpp +++ b/src/engine/splashscreen.cpp @@ -2,8 +2,6 @@ #include "precomp.h" -using namespace Gdiplus; - #define BURN_SPLASHSCREEN_CLASS_WINDOW L"WixBurnSplashScreen" #define IDB_SPLASHSCREEN 1 @@ -11,14 +9,16 @@ using namespace Gdiplus; struct SPLASHSCREEN_INFO { - Bitmap* pBitmap; - Point pt; - Size size; + HBITMAP hBitmap; + SIZE defaultDpiSize; + SIZE size; + UINT nDpi; + HWND hWnd; }; struct SPLASHSCREEN_CONTEXT { - HANDLE hIntializedEvent; + HANDLE hInitializedEvent; HINSTANCE hInstance; LPCWSTR wzCaption; @@ -36,14 +36,29 @@ static LRESULT CALLBACK WndProc( __in WPARAM wParam, __in LPARAM lParam ); -static void OnPaint( - __in HDC hdc, - __in SPLASHSCREEN_INFO* pSplashScreen - ); static HRESULT LoadSplashScreen( - __in HMODULE hInstance, + __in SPLASHSCREEN_CONTEXT* pContext, __in SPLASHSCREEN_INFO* pSplashScreen ); +static BOOL OnDpiChanged( + __in SPLASHSCREEN_INFO* pSplashScreen, + __in WPARAM wParam, + __in LPARAM lParam + ); +static void OnEraseBkgnd( + __in SPLASHSCREEN_INFO* pSplashScreen, + __in WPARAM wParam + ); +static void OnNcCreate( + __in HWND hWnd, + __in LPARAM lParam + ); +static void ScaleSplashScreen( + __in SPLASHSCREEN_INFO* pSplashScreen, + __in UINT nDpi, + __in int x, + __in int y + ); // function definitions @@ -63,7 +78,7 @@ extern "C" void SplashScreenCreate( ExitOnNullWithLastError(rgSplashScreenEvents[0], hr, "Failed to create modal event."); // create splash screen thread. - context.hIntializedEvent = rgSplashScreenEvents[0]; + context.hInitializedEvent = rgSplashScreenEvents[0]; context.hInstance = hInstance; context.wzCaption = wzCaption; context.pHwnd = pHwnd; @@ -115,30 +130,17 @@ static DWORD WINAPI ThreadProc( ) { HRESULT hr = S_OK; - - ULONG_PTR token = 0; - GdiplusStartupInput input; - GdiplusStartupOutput output = { }; - SPLASHSCREEN_CONTEXT* pContext = static_cast(pvContext); - SPLASHSCREEN_INFO splashScreen = { }; + + SPLASHSCREEN_INFO splashScreenInfo = { }; WNDCLASSW wc = { }; BOOL fRegistered = TRUE; - HWND hWnd = NULL; BOOL fRet = FALSE; MSG msg = { }; - input.GdiplusVersion = 1; - - hr = GdipInitialize(&input, &token, &output); - ExitOnFailure(hr, "Failed to initialize GDI+."); - - hr = LoadSplashScreen(pContext->hInstance, &splashScreen); - ExitOnFailure(hr, "Failed to load splash screen."); - - // Register the window class and create the window. + // Register the window class. wc.lpfnWndProc = WndProc; wc.hInstance = pContext->hInstance; wc.hCursor = ::LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); @@ -150,12 +152,12 @@ static DWORD WINAPI ThreadProc( fRegistered = TRUE; - hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, wc.lpszClassName, pContext->wzCaption, WS_POPUP | WS_VISIBLE, splashScreen.pt.X, splashScreen.pt.Y, splashScreen.size.Width, splashScreen.size.Height, HWND_DESKTOP, NULL, pContext->hInstance, &splashScreen); - ExitOnNullWithLastError(hWnd, hr, "Failed to create window."); + hr = LoadSplashScreen(pContext, &splashScreenInfo); + ExitOnFailure(hr, "Failed to load splash screen."); // Return the splash screen window and free the main thread waiting for us to be initialized. - *pContext->pHwnd = hWnd; - ::SetEvent(pContext->hIntializedEvent); + *pContext->pHwnd = splashScreenInfo.hWnd; + ::SetEvent(pContext->hInitializedEvent); // Pump messages until the bootstrapper application destroys the window. while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) @@ -165,7 +167,7 @@ static DWORD WINAPI ThreadProc( hr = E_UNEXPECTED; ExitOnFailure(hr, "Unexpected return value from message pump."); } - else if (!::IsDialogMessageW(hWnd, &msg)) + else if (!::IsDialogMessageW(splashScreenInfo.hWnd, &msg)) { ::TranslateMessage(&msg); ::DispatchMessageW(&msg); @@ -178,14 +180,9 @@ LExit: ::UnregisterClassW(BURN_SPLASHSCREEN_CLASS_WINDOW, pContext->hInstance); } - if (splashScreen.pBitmap) - { - delete splashScreen.pBitmap; - } - - if (token) + if (splashScreenInfo.hBitmap) { - GdipUninitialize(token); + ::DeleteObject(splashScreenInfo.hBitmap); } return hr; @@ -204,113 +201,155 @@ static LRESULT CALLBACK WndProc( switch (uMsg) { case WM_NCCREATE: - { - LPCREATESTRUCTW lpcs = reinterpret_cast(lParam); - ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(lpcs->lpCreateParams)); - } + OnNcCreate(hWnd, lParam); break; case WM_NCDESTROY: lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam); ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); + ::PostQuitMessage(0); return lres; case WM_NCHITTEST: return HTCAPTION; // allow window to be moved by grabbing any pixel. - case WM_DESTROY: - ::PostQuitMessage(0); - return 0; + case WM_DPICHANGED: + if (OnDpiChanged(pSplashScreen, wParam, lParam)) + { + return 0; + } + break; case WM_ERASEBKGND: - // The splash screen image will be repainted in its entirety. + OnEraseBkgnd(pSplashScreen, wParam); return 1; - - case WM_PAINT: - { - PAINTSTRUCT ps = { }; - - HDC hdc = BeginPaint(hWnd, &ps); - OnPaint(hdc, pSplashScreen); - EndPaint(hWnd, &ps); - } - return 0; } return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); } -static void OnPaint( - __in HDC hdc, - __in SPLASHSCREEN_INFO* pSplashScreen - ) -{ - // Use high-quality bicubuc stretching from GDI+ which looks better than GDI. - Graphics graphics(hdc); - graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic); - - Rect dst(0, 0, pSplashScreen->size.Width, pSplashScreen->size.Height); - Status status = graphics.DrawImage(pSplashScreen->pBitmap, dst); - -#if DEBUG - HRESULT hr = GdipHresultFromStatus(status); - TraceError(hr, "Failed to draw splash screen bitmap."); -#else - UNREFERENCED_PARAMETER(status); -#endif -} - static HRESULT LoadSplashScreen( - __in HMODULE hInstance, + __in SPLASHSCREEN_CONTEXT* pContext, __in SPLASHSCREEN_INFO* pSplashScreen ) { HRESULT hr = S_OK; - POINT ptCursor = { }; - HMONITOR hMonitor = NULL; - MONITORINFOEXW mi; - HDC hdc = NULL; - UINT dpiX = 0; - UINT dpiY = 0; - - pSplashScreen->pBitmap = Bitmap::FromResource(hInstance, MAKEINTRESOURCEW(IDB_SPLASHSCREEN)); - ExitOnNull(pSplashScreen->pBitmap, hr, E_INVALIDDATA, "Failed to find the splash screen bitmap."); - ExitOnGdipFailure(pSplashScreen->pBitmap->GetLastStatus(), hr, "Failed to load the splash screen bitmap."); - - pSplashScreen->pt.X = CW_USEDEFAULT; - pSplashScreen->pt.Y = CW_USEDEFAULT; - pSplashScreen->size.Width = pSplashScreen->pBitmap->GetWidth(); - pSplashScreen->size.Height = pSplashScreen->pBitmap->GetHeight(); - - // Stretch and center the window on the monitor with the mouse. - if (::GetCursorPos(&ptCursor)) + BITMAP bmp = { }; + POINT pt = { }; + int x = 0; + int y = 0; + DPIU_MONITOR_CONTEXT* pMonitorContext = NULL; + RECT* pMonitorRect = NULL; + + pSplashScreen->nDpi = USER_DEFAULT_SCREEN_DPI; + pSplashScreen->hBitmap = ::LoadBitmapW(pContext->hInstance, MAKEINTRESOURCEW(IDB_SPLASHSCREEN)); + ExitOnNullWithLastError(pSplashScreen->hBitmap, hr, "Failed to load splash screen bitmap."); + + ::GetObject(pSplashScreen->hBitmap, sizeof(bmp), static_cast(&bmp)); + pSplashScreen->defaultDpiSize.cx = pSplashScreen->size.cx = bmp.bmWidth; + pSplashScreen->defaultDpiSize.cy = pSplashScreen->size.cy = bmp.bmHeight; + + // Try to default to the monitor with the mouse, otherwise default to the primary monitor. + if (!::GetCursorPos(&pt)) { - hMonitor = ::MonitorFromPoint(ptCursor, MONITOR_DEFAULTTONEAREST); - if (hMonitor) + pt.x = 0; + pt.y = 0; + } + + // Try to center the window on the chosen monitor. + hr = DpiuGetMonitorContextFromPoint(&pt, &pMonitorContext); + if (SUCCEEDED(hr)) + { + pMonitorRect = &pMonitorContext->mi.rcWork; + if (pMonitorContext->nDpi != pSplashScreen->nDpi) { - ZeroMemory(&mi, sizeof(mi)); - mi.cbSize = sizeof(mi); - - if (::GetMonitorInfoW(hMonitor, &mi)) - { - hdc = ::CreateDCW(L"DISPLAY", mi.szDevice, NULL, NULL); - if (hdc) - { - dpiX = ::GetDeviceCaps(hdc, LOGPIXELSX); - dpiY = ::GetDeviceCaps(hdc, LOGPIXELSY); - - pSplashScreen->size.Width = pSplashScreen->size.Width * dpiX / 96; - pSplashScreen->size.Height = pSplashScreen->size.Height * dpiY / 96; - - ::ReleaseDC(NULL, hdc); - } - - pSplashScreen->pt.X = mi.rcWork.left + (mi.rcWork.right - mi.rcWork.left - pSplashScreen->size.Width) / 2; - pSplashScreen->pt.Y = mi.rcWork.top + (mi.rcWork.bottom - mi.rcWork.top - pSplashScreen->size.Height) / 2; - } + ScaleSplashScreen(pSplashScreen, pMonitorContext->nDpi, pMonitorRect->left, pMonitorRect->top); } + + x = pMonitorRect->left + (pMonitorRect->right - pMonitorRect->left - pSplashScreen->size.cx) / 2; + y = pMonitorRect->top + (pMonitorRect->bottom - pMonitorRect->top - pSplashScreen->size.cy) / 2; } + else + { + hr = S_OK; + x = CW_USEDEFAULT; + y = CW_USEDEFAULT; + } + + pSplashScreen->hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, BURN_SPLASHSCREEN_CLASS_WINDOW, pContext->wzCaption, WS_POPUP | WS_VISIBLE, x, y, pSplashScreen->size.cx, pSplashScreen->size.cy, HWND_DESKTOP, NULL, pContext->hInstance, pSplashScreen); + ExitOnNullWithLastError(pSplashScreen->hWnd, hr, "Failed to create window."); LExit: + MemFree(pMonitorContext); + return hr; } + +static BOOL OnDpiChanged( + __in SPLASHSCREEN_INFO* pSplashScreen, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + UINT nDpi = HIWORD(wParam); + RECT* pRect = reinterpret_cast(lParam); + BOOL fDpiChanged = pSplashScreen->nDpi != nDpi; + + if (fDpiChanged) + { + ScaleSplashScreen(pSplashScreen, nDpi, pRect->left, pRect->top); + } + + return fDpiChanged; +} + +static void OnEraseBkgnd( + __in SPLASHSCREEN_INFO* pSplashScreen, + __in WPARAM wParam + ) +{ + HDC hdc = reinterpret_cast(wParam); + HDC hdcMem = ::CreateCompatibleDC(hdc); + HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pSplashScreen->hBitmap)); + ::StretchBlt(hdc, 0, 0, pSplashScreen->size.cx, pSplashScreen->size.cy, hdcMem, 0, 0, pSplashScreen->defaultDpiSize.cx, pSplashScreen->defaultDpiSize.cy, SRCCOPY); + ::SelectObject(hdcMem, hDefaultBitmap); + ::DeleteDC(hdcMem); +} + +static void OnNcCreate( + __in HWND hWnd, + __in LPARAM lParam + ) +{ + DPIU_WINDOW_CONTEXT windowContext = { }; + CREATESTRUCTW* pCreateStruct = reinterpret_cast(lParam); + SPLASHSCREEN_INFO* pSplashScreen = reinterpret_cast(pCreateStruct->lpCreateParams); + + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(pSplashScreen)); + pSplashScreen->hWnd = hWnd; + + DpiuGetWindowContext(pSplashScreen->hWnd, &windowContext); + + if (windowContext.nDpi != pSplashScreen->nDpi) + { + ScaleSplashScreen(pSplashScreen, windowContext.nDpi, pCreateStruct->x, pCreateStruct->y); + } +} + +static void ScaleSplashScreen( + __in SPLASHSCREEN_INFO* pSplashScreen, + __in UINT nDpi, + __in int x, + __in int y + ) +{ + pSplashScreen->nDpi = nDpi; + + pSplashScreen->size.cx = DpiuScaleValue(pSplashScreen->defaultDpiSize.cx, pSplashScreen->nDpi); + pSplashScreen->size.cy = DpiuScaleValue(pSplashScreen->defaultDpiSize.cy, pSplashScreen->nDpi); + + if (pSplashScreen->hWnd) + { + ::SetWindowPos(pSplashScreen->hWnd, NULL, x, y, pSplashScreen->size.cx, pSplashScreen->size.cy, SWP_NOACTIVATE | SWP_NOZORDER); + } +} diff --git a/src/stub/packages.config b/src/stub/packages.config index 9aabbf8c..6f98d413 100644 --- a/src/stub/packages.config +++ b/src/stub/packages.config @@ -4,5 +4,5 @@ - + \ No newline at end of file diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index e0e6c8c6..c0d79f49 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -5,7 +5,7 @@ - + @@ -62,7 +62,7 @@ true true - cabinet.dll;crypt32.dll;gdiplus.dll;msi.dll;shlwapi.dll;version.dll;wininet.dll;wintrust.dll + cabinet.dll;crypt32.dll;msi.dll;shlwapi.dll;version.dll;wininet.dll;wintrust.dll @@ -107,6 +107,6 @@ - + \ No newline at end of file diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index eb2ec4ea..fda7cb7b 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -4,7 +4,7 @@ - + Debug @@ -28,7 +28,7 @@ ..\..\engine;..\..\WixToolset.BootstrapperCore.Native\inc - cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;wintrust.lib;gdiplus.lib + cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;wintrust.lib @@ -80,6 +80,6 @@ - + diff --git a/src/test/BurnUnitTest/packages.config b/src/test/BurnUnitTest/packages.config index 24bfe34a..e537bcdb 100644 --- a/src/test/BurnUnitTest/packages.config +++ b/src/test/BurnUnitTest/packages.config @@ -9,5 +9,5 @@ - + \ No newline at end of file -- cgit v1.2.3-55-g6feb From 273c69f34311f4f4e5f6b5896e71d0788f12d96a Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 17 Oct 2020 19:12:21 -0500 Subject: WIXFEAT:6210 Change data type of versions to strings. --- .../inc/BootstrapperApplication.h | 12 +-- .../inc/BootstrapperEngine.h | 8 +- .../inc/BundleExtensionEngine.h | 8 +- src/engine/EngineForApplication.cpp | 118 +++++++++++---------- src/engine/EngineForExtension.cpp | 118 +++++++++++---------- src/engine/condition.cpp | 88 ++++++--------- src/engine/dependency.cpp | 10 +- src/engine/detect.cpp | 40 ++++--- src/engine/elevation.cpp | 11 +- src/engine/engine.vcxproj | 4 +- src/engine/logging.cpp | 17 --- src/engine/logging.h | 5 - src/engine/msiengine.cpp | 94 ++++++++++------ src/engine/package.h | 8 +- src/engine/packages.config | 2 +- src/engine/plan.cpp | 10 +- src/engine/plan.h | 2 +- src/engine/precomp.h | 1 + src/engine/registration.cpp | 12 +-- src/engine/registration.h | 4 +- src/engine/relatedbundle.cpp | 9 +- src/engine/search.cpp | 9 +- src/engine/userexperience.cpp | 24 ++--- src/engine/userexperience.h | 12 +-- src/engine/variable.cpp | 75 +++++++------ src/engine/variable.h | 4 +- src/engine/variant.cpp | 71 +++++++------ src/engine/variant.h | 6 +- src/stub/packages.config | 2 +- src/stub/stub.vcxproj | 4 +- src/test/BurnUnitTest/BurnUnitTest.vcxproj | 4 +- src/test/BurnUnitTest/SearchTest.cpp | 17 +-- src/test/BurnUnitTest/VariableHelpers.cpp | 34 ++++-- src/test/BurnUnitTest/VariableHelpers.h | 4 +- src/test/BurnUnitTest/VariableTest.cpp | 32 +++--- src/test/BurnUnitTest/VariantTest.cpp | 52 ++++++--- src/test/BurnUnitTest/packages.config | 2 +- src/test/BurnUnitTest/precomp.h | 1 + 38 files changed, 505 insertions(+), 429 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h index 36d788ca..77d5b2c6 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -416,7 +416,7 @@ struct BA_ONDETECTCOMPATIBLEMSIPACKAGE_ARGS DWORD cbSize; LPCWSTR wzPackageId; LPCWSTR wzCompatiblePackageId; - DWORD64 dw64CompatiblePackageVersion; + LPCWSTR wzCompatiblePackageVersion; }; struct BA_ONDETECTCOMPATIBLEMSIPACKAGE_RESULTS @@ -443,7 +443,7 @@ struct BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_ARGS BOOTSTRAPPER_RELATION_TYPE relationType; LPCWSTR wzBundleTag; BOOL fPerMachine; - DWORD64 dw64Version; + LPCWSTR wzVersion; }; struct BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_RESULTS @@ -499,7 +499,7 @@ struct BA_ONDETECTRELATEDBUNDLE_ARGS BOOTSTRAPPER_RELATION_TYPE relationType; LPCWSTR wzBundleTag; BOOL fPerMachine; - DWORD64 dw64Version; + LPCWSTR wzVersion; BOOTSTRAPPER_RELATED_OPERATION operation; }; @@ -516,7 +516,7 @@ struct BA_ONDETECTRELATEDMSIPACKAGE_ARGS LPCWSTR wzUpgradeCode; LPCWSTR wzProductCode; BOOL fPerMachine; - DWORD64 dw64Version; + LPCWSTR wzVersion; BOOTSTRAPPER_RELATED_OPERATION operation; }; @@ -545,7 +545,7 @@ struct BA_ONDETECTUPDATE_ARGS DWORD cbSize; LPCWSTR wzUpdateLocation; DWORD64 dw64Size; - DWORD64 dw64Version; + LPCWSTR wzVersion; LPCWSTR wzTitle; LPCWSTR wzSummary; LPCWSTR wzContentType; @@ -781,7 +781,7 @@ struct BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_ARGS DWORD cbSize; LPCWSTR wzPackageId; LPCWSTR wzCompatiblePackageId; - DWORD64 dw64CompatiblePackageVersion; + LPCWSTR wzCompatiblePackageVersion; BOOTSTRAPPER_REQUEST_STATE recommendedState; }; diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h index e3792177..a6a87622 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h @@ -258,8 +258,10 @@ typedef struct _BAENGINE_GETVARIABLEVERSION_ARGS typedef struct _BAENGINE_GETVARIABLEVERSION_RESULTS { DWORD cbSize; - // The contents of qwValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroMemory. - DWORD64 qwValue; + // The contents of wzValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroFree. + LPWSTR wzValue; + // Should be initialized to the size of wzValue. + DWORD cchValue; } BAENGINE_GETVARIABLEVERSION_RESULTS; typedef struct _BAENGINE_LAUNCHAPPROVEDEXE_ARGS @@ -410,7 +412,7 @@ typedef struct _BAENGINE_SETVARIABLEVERSION_ARGS { DWORD cbSize; LPCWSTR wzVariable; - DWORD64 qwValue; + LPCWSTR wzValue; } BAENGINE_SETVARIABLEVERSION_ARGS; typedef struct _BAENGINE_SETVARIABLEVERSION_RESULTS diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h b/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h index 61a55693..adcae1af 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h @@ -107,8 +107,10 @@ typedef struct _BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_ARGS typedef struct _BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_RESULTS { DWORD cbSize; - // The contents of qwValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroMemory. - DWORD64 qwValue; + // The contents of wzValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroFree. + LPWSTR wzValue; + // Should be initialized to the size of wzValue. + DWORD cchValue; } BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_RESULTS; typedef struct _BUNDLE_EXTENSION_ENGINE_LOG_ARGS @@ -152,7 +154,7 @@ typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_ARGS { DWORD cbSize; LPCWSTR wzVariable; - DWORD64 qwValue; + LPCWSTR wzValue; } BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_ARGS; typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_RESULTS diff --git a/src/engine/EngineForApplication.cpp b/src/engine/EngineForApplication.cpp index 81eec2fc..d034c2bf 100644 --- a/src/engine/EngineForApplication.cpp +++ b/src/engine/EngineForApplication.cpp @@ -2,6 +2,13 @@ #include "precomp.h" + +static HRESULT CopyStringToBA( + __in LPWSTR wzValue, + __in LPWSTR wzBuffer, + __inout DWORD* pcchBuffer + ); + static HRESULT BAEngineGetPackageCount( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, __in BAENGINE_GETPACKAGECOUNT_ARGS* /*pArgs*/, @@ -46,7 +53,6 @@ static HRESULT BAEngineGetVariableString( { HRESULT hr = S_OK; LPWSTR sczValue = NULL; - size_t cchRemaining = 0; LPCWSTR wzVariable = pArgs->wzVariable; LPWSTR wzValue = pResults->wzValue; DWORD* pcchValue = &pResults->cchValue; @@ -56,24 +62,7 @@ static HRESULT BAEngineGetVariableString( hr = VariableGetString(&pContext->pEngineState->variables, wzVariable, &sczValue); if (SUCCEEDED(hr)) { - if (wzValue) - { - hr = ::StringCchCopyExW(wzValue, *pcchValue, sczValue, NULL, &cchRemaining, STRSAFE_FILL_BEHIND_NULL); - if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) - { - hr = E_MOREDATA; - - ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining); - *pcchValue = cchRemaining + 1; - } - } - else - { - hr = E_MOREDATA; - - ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining); - *pcchValue = cchRemaining + 1; - } + hr = CopyStringToBA(sczValue, wzValue, pcchValue); } } else @@ -92,18 +81,26 @@ static HRESULT BAEngineGetVariableVersion( ) { HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion = NULL; LPCWSTR wzVariable = pArgs->wzVariable; - DWORD64* pqwValue = &pResults->qwValue; + LPWSTR wzValue = pResults->wzValue; + DWORD* pcchValue = &pResults->cchValue; if (wzVariable && *wzVariable) { - hr = VariableGetVersion(&pContext->pEngineState->variables, wzVariable, pqwValue); + hr = VariableGetVersion(&pContext->pEngineState->variables, wzVariable, &pVersion); + if (SUCCEEDED(hr)) + { + hr = CopyStringToBA(pVersion->sczVersion, wzValue, pcchValue); + } } else { hr = E_INVALIDARG; } + ReleaseVerutilVersion(pVersion); + return hr; } @@ -115,33 +112,16 @@ static HRESULT BAEngineFormatString( { HRESULT hr = S_OK; LPWSTR sczValue = NULL; - DWORD cchValue = 0; LPCWSTR wzIn = pArgs->wzIn; LPWSTR wzOut = pResults->wzOut; DWORD* pcchOut = &pResults->cchOut; if (wzIn && *wzIn) { - hr = VariableFormatString(&pContext->pEngineState->variables, wzIn, &sczValue, &cchValue); + hr = VariableFormatString(&pContext->pEngineState->variables, wzIn, &sczValue, NULL); if (SUCCEEDED(hr)) { - if (wzOut) - { - hr = ::StringCchCopyExW(wzOut, *pcchOut, sczValue, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); - if (FAILED(hr)) - { - *pcchOut = cchValue; - if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) - { - hr = E_MOREDATA; - } - } - } - else - { - hr = E_MOREDATA; - *pcchOut = cchValue; - } + hr = CopyStringToBA(sczValue, wzOut, pcchOut); } } else @@ -161,7 +141,6 @@ static HRESULT BAEngineEscapeString( { HRESULT hr = S_OK; LPWSTR sczValue = NULL; - size_t cchRemaining = 0; LPCWSTR wzIn = pArgs->wzIn; LPWSTR wzOut = pResults->wzOut; DWORD* pcchOut = &pResults->cchOut; @@ -171,21 +150,7 @@ static HRESULT BAEngineEscapeString( hr = VariableEscapeString(wzIn, &sczValue); if (SUCCEEDED(hr)) { - if (wzOut) - { - hr = ::StringCchCopyExW(wzOut, *pcchOut, sczValue, NULL, &cchRemaining, STRSAFE_FILL_BEHIND_NULL); - if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) - { - hr = E_MOREDATA; - ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining); - *pcchOut = cchRemaining; - } - } - else - { - ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining); - *pcchOut = cchRemaining; - } + hr = CopyStringToBA(sczValue, wzOut, pcchOut); } } else @@ -613,11 +578,15 @@ static HRESULT BAEngineSetVariableVersion( { HRESULT hr = S_OK; LPCWSTR wzVariable = pArgs->wzVariable; - DWORD64 qwValue = pArgs->qwValue; + LPCWSTR wzValue = pArgs->wzValue; + VERUTIL_VERSION* pVersion = NULL; if (wzVariable && *wzVariable) { - hr = VariableSetVersion(&pContext->pEngineState->variables, wzVariable, qwValue, FALSE); + hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); + ExitOnFailure(hr, "Failed to parse new version value."); + + hr = VariableSetVersion(&pContext->pEngineState->variables, wzVariable, pVersion, FALSE); ExitOnFailure(hr, "Failed to set version variable."); } else @@ -627,6 +596,8 @@ static HRESULT BAEngineSetVariableVersion( } LExit: + ReleaseVerutilVersion(pVersion); + return hr; } @@ -898,3 +869,34 @@ HRESULT WINAPI EngineForApplicationProc( LExit: return hr; } + +static HRESULT CopyStringToBA( + __in LPWSTR wzValue, + __in LPWSTR wzBuffer, + __inout DWORD* pcchBuffer + ) +{ + HRESULT hr = S_OK; + BOOL fTooSmall = !wzBuffer; + + if (!fTooSmall) + { + hr = ::StringCchCopyExW(wzBuffer, *pcchBuffer, wzValue, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + fTooSmall = TRUE; + } + } + + if (fTooSmall) + { + hr = ::StringCchLengthW(wzValue, STRSAFE_MAX_CCH, reinterpret_cast(pcchBuffer)); + if (SUCCEEDED(hr)) + { + hr = E_MOREDATA; + *pcchBuffer += 1; // null terminator. + } + } + + return hr; +} diff --git a/src/engine/EngineForExtension.cpp b/src/engine/EngineForExtension.cpp index fdfa59b1..6ec80a0f 100644 --- a/src/engine/EngineForExtension.cpp +++ b/src/engine/EngineForExtension.cpp @@ -2,6 +2,13 @@ #include "precomp.h" + +static HRESULT CopyStringToBE( + __in LPWSTR wzValue, + __in LPWSTR wzBuffer, + __inout DWORD* pcchBuffer + ); + static HRESULT BEEngineEscapeString( __in BURN_EXTENSION_ENGINE_CONTEXT* /*pContext*/, __in BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_ARGS* pArgs, @@ -10,7 +17,6 @@ static HRESULT BEEngineEscapeString( { HRESULT hr = S_OK; LPWSTR sczValue = NULL; - size_t cchRemaining = 0; LPCWSTR wzIn = pArgs->wzIn; LPWSTR wzOut = pResults->wzOut; DWORD* pcchOut = &pResults->cchOut; @@ -20,21 +26,7 @@ static HRESULT BEEngineEscapeString( hr = VariableEscapeString(wzIn, &sczValue); if (SUCCEEDED(hr)) { - if (wzOut) - { - hr = ::StringCchCopyExW(wzOut, *pcchOut, sczValue, NULL, &cchRemaining, STRSAFE_FILL_BEHIND_NULL); - if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) - { - hr = E_MOREDATA; - ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining); - *pcchOut = cchRemaining; - } - } - else - { - ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining); - *pcchOut = cchRemaining; - } + hr = CopyStringToBE(sczValue, wzOut, pcchOut); } } else @@ -76,33 +68,16 @@ static HRESULT BEEngineFormatString( { HRESULT hr = S_OK; LPWSTR sczValue = NULL; - DWORD cchValue = 0; LPCWSTR wzIn = pArgs->wzIn; LPWSTR wzOut = pResults->wzOut; DWORD* pcchOut = &pResults->cchOut; if (wzIn && *wzIn) { - hr = VariableFormatString(&pContext->pEngineState->variables, wzIn, &sczValue, &cchValue); + hr = VariableFormatString(&pContext->pEngineState->variables, wzIn, &sczValue, NULL); if (SUCCEEDED(hr)) { - if (wzOut) - { - hr = ::StringCchCopyExW(wzOut, *pcchOut, sczValue, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); - if (FAILED(hr)) - { - *pcchOut = cchValue; - if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) - { - hr = E_MOREDATA; - } - } - } - else - { - hr = E_MOREDATA; - *pcchOut = cchValue; - } + hr = CopyStringToBE(sczValue, wzOut, pcchOut); } } else @@ -144,7 +119,6 @@ static HRESULT BEEngineGetVariableString( { HRESULT hr = S_OK; LPWSTR sczValue = NULL; - size_t cchRemaining = 0; LPCWSTR wzVariable = pArgs->wzVariable; LPWSTR wzValue = pResults->wzValue; DWORD* pcchValue = &pResults->cchValue; @@ -154,24 +128,7 @@ static HRESULT BEEngineGetVariableString( hr = VariableGetString(&pContext->pEngineState->variables, wzVariable, &sczValue); if (SUCCEEDED(hr)) { - if (wzValue) - { - hr = ::StringCchCopyExW(wzValue, *pcchValue, sczValue, NULL, &cchRemaining, STRSAFE_FILL_BEHIND_NULL); - if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) - { - hr = E_MOREDATA; - - ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining); - *pcchValue = cchRemaining + 1; - } - } - else - { - hr = E_MOREDATA; - - ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining); - *pcchValue = cchRemaining + 1; - } + hr = CopyStringToBE(sczValue, wzValue, pcchValue); } } else @@ -190,18 +147,26 @@ static HRESULT BEEngineGetVariableVersion( ) { HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion = NULL; LPCWSTR wzVariable = pArgs->wzVariable; - DWORD64* pqwValue = &pResults->qwValue; + LPWSTR wzValue = pResults->wzValue; + DWORD* pcchValue = &pResults->cchValue; if (wzVariable && *wzVariable) { - hr = VariableGetVersion(&pContext->pEngineState->variables, wzVariable, pqwValue); + hr = VariableGetVersion(&pContext->pEngineState->variables, wzVariable, &pVersion); + if (SUCCEEDED(hr)) + { + hr = CopyStringToBE(pVersion->sczVersion, wzValue, pcchValue); + } } else { hr = E_INVALIDARG; } + ReleaseVerutilVersion(pVersion); + return hr; } @@ -303,11 +268,15 @@ static HRESULT BEEngineSetVariableVersion( { HRESULT hr = S_OK; LPCWSTR wzVariable = pArgs->wzVariable; - DWORD64 qwValue = pArgs->qwValue; + LPCWSTR wzValue = pArgs->wzValue; + VERUTIL_VERSION* pVersion = NULL; if (wzVariable && *wzVariable) { - hr = VariableSetVersion(&pContext->pEngineState->variables, wzVariable, qwValue, FALSE); + hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); + ExitOnFailure(hr, "Failed to parse new version value."); + + hr = VariableSetVersion(&pContext->pEngineState->variables, wzVariable, pVersion, FALSE); ExitOnFailure(hr, "Failed to set version variable."); } else @@ -317,6 +286,8 @@ static HRESULT BEEngineSetVariableVersion( } LExit: + ReleaseVerutilVersion(pVersion); + return hr; } @@ -375,3 +346,34 @@ HRESULT WINAPI EngineForExtensionProc( LExit: return hr; } + +static HRESULT CopyStringToBE( + __in LPWSTR wzValue, + __in LPWSTR wzBuffer, + __inout DWORD* pcchBuffer + ) +{ + HRESULT hr = S_OK; + BOOL fTooSmall = !wzBuffer; + + if (!fTooSmall) + { + hr = ::StringCchCopyExW(wzBuffer, *pcchBuffer, wzValue, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + fTooSmall = TRUE; + } + } + + if (fTooSmall) + { + hr = ::StringCchLengthW(wzValue, STRSAFE_MAX_CCH, reinterpret_cast(pcchBuffer)); + if (SUCCEEDED(hr)) + { + hr = E_MOREDATA; + *pcchBuffer += 1; // null terminator. + } + } + + return hr; +} diff --git a/src/engine/condition.cpp b/src/engine/condition.cpp index cd346680..32a7a0b8 100644 --- a/src/engine/condition.cpp +++ b/src/engine/condition.cpp @@ -123,8 +123,8 @@ static HRESULT CompareIntegerValues( ); static HRESULT CompareVersionValues( __in BURN_SYMBOL_TYPE comparison, - __in DWORD64 qwLeftOperand, - __in DWORD64 qwRightOperand, + __in VERUTIL_VERSION* pLeftOperand, + __in VERUTIL_VERSION* pRightOperand, __out BOOL* pfResult ); @@ -379,7 +379,7 @@ static HRESULT ParseTerm( { LONGLONG llValue = 0; LPWSTR sczValue = NULL; - DWORD64 qwValue = 0; + VERUTIL_VERSION* pVersion = NULL; switch (firstValue.Type) { case BURN_VARIANT_TYPE_NONE: @@ -402,12 +402,12 @@ static HRESULT ParseTerm( SecureZeroMemory(&llValue, sizeof(llValue)); break; case BURN_VARIANT_TYPE_VERSION: - hr = BVariantGetVersion(&firstValue, &qwValue); + hr = BVariantGetVersion(&firstValue, &pVersion); if (SUCCEEDED(hr)) { - *pf = 0 != qwValue; + *pf = 0 != *pVersion->sczVersion; } - SecureZeroMemory(&llValue, sizeof(qwValue)); + ReleaseVerutilVersion(pVersion); break; default: ExitFunction1(hr = E_UNEXPECTED); @@ -689,33 +689,14 @@ static HRESULT NextSymbol( if (L'v' == pContext->wzRead[0] && C1_DIGIT & charType) { // version - DWORD cParts = 1; - for (;;) + do { ++n; - if (L'.' == pContext->wzRead[n]) - { - ++cParts; - if (4 < cParts) - { - // error, too many parts in version - pContext->fError = TRUE; - hr = E_INVALIDDATA; - ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Version can have a maximum of 4 parts, at position %d.", pContext->wzCondition, iPosition); - } - } - else - { - ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType); - if (C1_DIGIT != (C1_DIGIT & charType)) - { - break; - } - } - } + ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType); + } while (L'\0' != pContext->wzRead[n] && C1_BLANK != (C1_BLANK & charType)); // Symbols don't encrypt their value, so can access the value directly. - hr = FileVersionFromStringEx(&pContext->wzRead[1], n - 1, &pContext->NextSymbol.Value.qwValue); + hr = VerParseVersion(&pContext->wzRead[1], n - 1, FALSE, &pContext->NextSymbol.Value.pValue); if (FAILED(hr)) { pContext->fError = TRUE; @@ -785,10 +766,10 @@ static HRESULT CompareValues( { HRESULT hr = S_OK; LONGLONG llLeft = 0; - DWORD64 qwLeft = 0; + VERUTIL_VERSION* pVersionLeft = 0; LPWSTR sczLeft = NULL; LONGLONG llRight = 0; - DWORD64 qwRight = 0; + VERUTIL_VERSION* pVersionRight = 0; LPWSTR sczRight = NULL; // get values to compare based on type @@ -810,17 +791,17 @@ static HRESULT CompareValues( } else if (BURN_VARIANT_TYPE_VERSION == leftOperand.Type && BURN_VARIANT_TYPE_VERSION == rightOperand.Type) { - hr = BVariantGetVersion(&leftOperand, &qwLeft); + hr = BVariantGetVersion(&leftOperand, &pVersionLeft); ExitOnFailure(hr, "Failed to get the left version"); - hr = BVariantGetVersion(&rightOperand, &qwRight); + hr = BVariantGetVersion(&rightOperand, &pVersionRight); ExitOnFailure(hr, "Failed to get the right version"); - hr = CompareVersionValues(comparison, qwLeft, qwRight, pfResult); + hr = CompareVersionValues(comparison, pVersionLeft, pVersionRight, pfResult); } else if (BURN_VARIANT_TYPE_VERSION == leftOperand.Type && BURN_VARIANT_TYPE_STRING == rightOperand.Type) { - hr = BVariantGetVersion(&leftOperand, &qwLeft); + hr = BVariantGetVersion(&leftOperand, &pVersionLeft); ExitOnFailure(hr, "Failed to get the left version"); - hr = BVariantGetVersion(&rightOperand, &qwRight); + hr = BVariantGetVersion(&rightOperand, &pVersionRight); if (FAILED(hr)) { if (DISP_E_TYPEMISMATCH != hr) @@ -832,14 +813,14 @@ static HRESULT CompareValues( } else { - hr = CompareVersionValues(comparison, qwLeft, qwRight, pfResult); + hr = CompareVersionValues(comparison, pVersionLeft, pVersionRight, pfResult); } } else if (BURN_VARIANT_TYPE_STRING == leftOperand.Type && BURN_VARIANT_TYPE_VERSION == rightOperand.Type) { - hr = BVariantGetVersion(&rightOperand, &qwRight); + hr = BVariantGetVersion(&rightOperand, &pVersionRight); ExitOnFailure(hr, "Failed to get the right version"); - hr = BVariantGetVersion(&leftOperand, &qwLeft); + hr = BVariantGetVersion(&leftOperand, &pVersionLeft); if (FAILED(hr)) { if (DISP_E_TYPEMISMATCH != hr) @@ -851,7 +832,7 @@ static HRESULT CompareValues( } else { - hr = CompareVersionValues(comparison, qwLeft, qwRight, pfResult); + hr = CompareVersionValues(comparison, pVersionLeft, pVersionRight, pfResult); } } else if (BURN_VARIANT_TYPE_NUMERIC == leftOperand.Type && BURN_VARIANT_TYPE_STRING == rightOperand.Type) @@ -899,10 +880,10 @@ static HRESULT CompareValues( } LExit: - SecureZeroMemory(&qwLeft, sizeof(DWORD64)); + ReleaseVerutilVersion(pVersionLeft); SecureZeroMemory(&llLeft, sizeof(LONGLONG)); StrSecureZeroFreeString(sczLeft); - SecureZeroMemory(&qwRight, sizeof(DWORD64)); + ReleaseVerutilVersion(pVersionRight); SecureZeroMemory(&llRight, sizeof(LONGLONG)); StrSecureZeroFreeString(sczRight); @@ -1010,24 +991,25 @@ LExit: // static HRESULT CompareVersionValues( __in BURN_SYMBOL_TYPE comparison, - __in DWORD64 qwLeftOperand, - __in DWORD64 qwRightOperand, + __in VERUTIL_VERSION* pLeftOperand, + __in VERUTIL_VERSION* pRightOperand, __out BOOL* pfResult ) { HRESULT hr = S_OK; + int nResult = 0; + + hr = VerCompareParsedVersions(pLeftOperand, pRightOperand, &nResult); + ExitOnFailure(hr, "Failed to compare condition versions: '%ls', '%ls'", pLeftOperand->sczVersion, pRightOperand->sczVersion); switch (comparison) { - case BURN_SYMBOL_TYPE_LT: *pfResult = qwLeftOperand < qwRightOperand; break; - case BURN_SYMBOL_TYPE_GT: *pfResult = qwLeftOperand > qwRightOperand; break; - case BURN_SYMBOL_TYPE_LE: *pfResult = qwLeftOperand <= qwRightOperand; break; - case BURN_SYMBOL_TYPE_GE: *pfResult = qwLeftOperand >= qwRightOperand; break; - case BURN_SYMBOL_TYPE_EQ: *pfResult = qwLeftOperand == qwRightOperand; break; - case BURN_SYMBOL_TYPE_NE: *pfResult = qwLeftOperand != qwRightOperand; break; - case BURN_SYMBOL_TYPE_BAND: *pfResult = (qwLeftOperand & qwRightOperand) ? TRUE : FALSE; break; - case BURN_SYMBOL_TYPE_HIEQ: *pfResult = ((qwLeftOperand >> 16) & 0xFFFF) == qwRightOperand; break; - case BURN_SYMBOL_TYPE_LOEQ: *pfResult = (qwLeftOperand & 0xFFFF) == qwRightOperand; break; + case BURN_SYMBOL_TYPE_LT: *pfResult = nResult < 0; break; + case BURN_SYMBOL_TYPE_GT: *pfResult = nResult > 0; break; + case BURN_SYMBOL_TYPE_LE: *pfResult = nResult <= 0; break; + case BURN_SYMBOL_TYPE_GE: *pfResult = nResult >= 0; break; + case BURN_SYMBOL_TYPE_EQ: *pfResult = nResult == 0; break; + case BURN_SYMBOL_TYPE_NE: *pfResult = nResult != 0; break; default: ExitFunction1(hr = E_INVALIDARG); } diff --git a/src/engine/dependency.cpp b/src/engine/dependency.cpp index c7c6e024..c01449b6 100644 --- a/src/engine/dependency.cpp +++ b/src/engine/dependency.cpp @@ -593,20 +593,14 @@ extern "C" HRESULT DependencyRegisterBundle( ) { HRESULT hr = S_OK; - LPWSTR sczVersion = NULL; - hr = FileVersionToStringEx(pRegistration->qwVersion, &sczVersion); - ExitOnFailure(hr, "Failed to format the registration version string."); - - LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_REGISTER, pRegistration->sczProviderKey, sczVersion); + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_REGISTER, pRegistration->sczProviderKey, pRegistration->pVersion->sczVersion); // Register the bundle provider key. - hr = DepRegisterDependency(pRegistration->hkRoot, pRegistration->sczProviderKey, sczVersion, pRegistration->sczDisplayName, pRegistration->sczId, 0); + hr = DepRegisterDependency(pRegistration->hkRoot, pRegistration->sczProviderKey, pRegistration->pVersion->sczVersion, pRegistration->sczDisplayName, pRegistration->sczId, 0); ExitOnFailure(hr, "Failed to register the bundle dependency provider."); LExit: - ReleaseStr(sczVersion); - return hr; } diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp index 7953daf5..9e4681bb 100644 --- a/src/engine/detect.cpp +++ b/src/engine/detect.cpp @@ -90,6 +90,7 @@ extern "C" HRESULT DetectForwardCompatibleBundle( HRESULT hr = S_OK; BOOL fRecommendIgnore = TRUE; BOOL fIgnoreBundle = FALSE; + int nCompareResult = 0; if (pRegistration->sczDetectedProviderKeyBundleId && CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRegistration->sczId, -1)) @@ -122,22 +123,27 @@ extern "C" HRESULT DetectForwardCompatibleBundle( fIgnoreBundle = fRecommendIgnore; if (BOOTSTRAPPER_RELATION_UPGRADE == pRelatedBundle->relationType && - pRegistration->qwVersion <= pRelatedBundle->qwVersion && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRelatedBundle->package.sczId, -1)) { - hr = UserExperienceOnDetectForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->qwVersion, &fIgnoreBundle); - ExitOnRootFailure(hr, "BA aborted detect forward compatible bundle."); + hr = VerCompareParsedVersions(pRegistration->pVersion, pRelatedBundle->pVersion, &nCompareResult); + ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistration->pVersion->sczVersion, pRelatedBundle->pVersion->sczVersion); - if (!fIgnoreBundle) + if (nCompareResult <= 0) { - hr = PseudoBundleInitializePassthrough(&pRegistration->forwardCompatibleBundle, pCommand, NULL, pRegistration->sczActiveParent, pRegistration->sczAncestors, &pRelatedBundle->package); - ExitOnFailure(hr, "Failed to initialize update bundle."); + hr = UserExperienceOnDetectForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, &fIgnoreBundle); + ExitOnRootFailure(hr, "BA aborted detect forward compatible bundle."); - pRegistration->fEnabledForwardCompatibleBundle = TRUE; - } + if (!fIgnoreBundle) + { + hr = PseudoBundleInitializePassthrough(&pRegistration->forwardCompatibleBundle, pCommand, NULL, pRegistration->sczActiveParent, pRegistration->sczAncestors, &pRelatedBundle->package); + ExitOnFailure(hr, "Failed to initialize update bundle."); - LogId(REPORT_STANDARD, MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), LoggingVersionToString(pRelatedBundle->qwVersion), LoggingBoolToString(pRegistration->fEnabledForwardCompatibleBundle)); - break; + pRegistration->fEnabledForwardCompatibleBundle = TRUE; + } + + LogId(REPORT_STANDARD, MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingBoolToString(pRegistration->fEnabledForwardCompatibleBundle)); + break; + } } } } @@ -154,6 +160,7 @@ extern "C" HRESULT DetectReportRelatedBundles( ) { HRESULT hr = S_OK; + int nCompareResult = 0; for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) { @@ -165,11 +172,14 @@ extern "C" HRESULT DetectReportRelatedBundles( case BOOTSTRAPPER_RELATION_UPGRADE: if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < action) { - if (pRegistration->qwVersion > pRelatedBundle->qwVersion) + hr = VerCompareParsedVersions(pRegistration->pVersion, pRelatedBundle->pVersion, &nCompareResult); + ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistration->pVersion, pRelatedBundle->pVersion); + + if (nCompareResult > 0) { operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE; } - else if (pRegistration->qwVersion < pRelatedBundle->qwVersion) + else if (nCompareResult < 0) { operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; } @@ -202,9 +212,9 @@ extern "C" HRESULT DetectReportRelatedBundles( break; } - LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), LoggingVersionToString(pRelatedBundle->qwVersion), LoggingRelatedOperationToString(operation)); + LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingRelatedOperationToString(operation)); - hr = UserExperienceOnDetectRelatedBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->qwVersion, operation); + hr = UserExperienceOnDetectRelatedBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, operation); ExitOnRootFailure(hr, "BA aborted detect related bundle."); } @@ -405,7 +415,7 @@ static HRESULT DetectAtomFeedUpdate( hr = UserExperienceOnDetectUpdate(pUX, pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->wzUrl : NULL, pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->dw64Size : 0, - pAppUpdateEntry->dw64Version, pAppUpdateEntry->wzTitle, + pAppUpdateEntry->pVersion, pAppUpdateEntry->wzTitle, pAppUpdateEntry->wzSummary, pAppUpdateEntry->wzContentType, pAppUpdateEntry->wzContent, &fStopProcessingUpdates); ExitOnRootFailure(hr, "BA aborted detect update."); diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp index d0652270..9ce04630 100644 --- a/src/engine/elevation.cpp +++ b/src/engine/elevation.cpp @@ -1087,7 +1087,7 @@ extern "C" HRESULT ElevationLoadCompatiblePackageAction( hr = BuffWriteString(&pbData, &cbData, pExecuteAction->compatiblePackage.sczInstalledProductCode); ExitOnFailure(hr, "Failed to write installed ProductCode to message buffer."); - hr = BuffWriteNumber64(&pbData, &cbData, pExecuteAction->compatiblePackage.qwInstalledVersion); + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->compatiblePackage.pInstalledVersion->sczVersion); ExitOnFailure(hr, "Failed to write installed version to message buffer."); // Send the message. @@ -2566,6 +2566,7 @@ static HRESULT OnLoadCompatiblePackage( HRESULT hr = S_OK; SIZE_T iData = 0; LPWSTR sczPackage = NULL; + LPWSTR sczVersion = NULL; BURN_EXECUTE_ACTION executeAction = { }; executeAction.type = BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE; @@ -2581,20 +2582,24 @@ static HRESULT OnLoadCompatiblePackage( hr = BuffReadString(pbData, cbData, &iData, &executeAction.compatiblePackage.sczInstalledProductCode); ExitOnFailure(hr, "Failed to read installed ProductCode from message buffer."); - hr = BuffReadNumber64(pbData, cbData, &iData, &executeAction.compatiblePackage.qwInstalledVersion); + hr = BuffReadString(pbData, cbData, &iData, &sczVersion); ExitOnFailure(hr, "Failed to read installed version from message buffer."); + hr = VerParseVersion(sczVersion, 0, FALSE, &executeAction.compatiblePackage.pInstalledVersion); + ExitOnFailure(hr, "Failed to parse installed version from compatible package."); + // Copy the installed data to the reference package. hr = StrAllocString(&executeAction.compatiblePackage.pReferencePackage->Msi.sczInstalledProductCode, executeAction.compatiblePackage.sczInstalledProductCode, 0); ExitOnFailure(hr, "Failed to copy installed ProductCode."); - executeAction.compatiblePackage.pReferencePackage->Msi.qwInstalledVersion = executeAction.compatiblePackage.qwInstalledVersion; + executeAction.compatiblePackage.pReferencePackage->Msi.pInstalledVersion = executeAction.compatiblePackage.pInstalledVersion; // Load the compatible package and add it to the list. hr = MsiEngineAddCompatiblePackage(pPackages, executeAction.compatiblePackage.pReferencePackage, NULL); ExitOnFailure(hr, "Failed to load compatible package."); LExit: + ReleaseStr(sczVersion); ReleaseStr(sczPackage); PlanUninitializeExecuteAction(&executeAction); diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index ef5c1602..d3f5b61e 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -2,7 +2,7 @@ - + @@ -165,7 +165,7 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + diff --git a/src/engine/logging.cpp b/src/engine/logging.cpp index 55b65336..a6412da9 100644 --- a/src/engine/logging.cpp +++ b/src/engine/logging.cpp @@ -583,23 +583,6 @@ extern "C" LPWSTR LoggingStringOrUnknownIfNull( return wz ? wz : L"Unknown"; } -// Note: this function is not thread safe. -extern "C" LPCSTR LoggingVersionToString( - __in DWORD64 dw64Version - ) -{ - static CHAR szVersion[40] = { 0 }; - HRESULT hr = S_OK; - - hr = ::StringCchPrintfA(szVersion, countof(szVersion), "%I64u.%I64u.%I64u.%I64u", dw64Version >> 48 & 0xFFFF, dw64Version >> 32 & 0xFFFF, dw64Version >> 16 & 0xFFFF, dw64Version & 0xFFFF); - if (FAILED(hr)) - { - memset(szVersion, 0, sizeof(szVersion)); - } - - return szVersion; -} - // internal function declarations diff --git a/src/engine/logging.h b/src/engine/logging.h index cea4d31d..22dd54d9 100644 --- a/src/engine/logging.h +++ b/src/engine/logging.h @@ -133,11 +133,6 @@ LPWSTR LoggingStringOrUnknownIfNull( __in LPCWSTR wz ); -// Note: this function is not thread safe. -LPCSTR LoggingVersionToString( - __in DWORD64 dw64Version - ); - #if defined(__cplusplus) } diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index e7cffd62..e274df28 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -79,7 +79,7 @@ extern "C" HRESULT MsiEngineParsePackageFromXml( hr = XmlGetAttributeEx(pixnMsiPackage, L"Version", &scz); ExitOnFailure(hr, "Failed to get @Version."); - hr = FileVersionFromStringEx(scz, 0, &pPackage->Msi.qwVersion); + hr = VerParseVersion(scz, 0, FALSE, &pPackage->Msi.pVersion); ExitOnFailure(hr, "Failed to parse @Version: %ls", scz); // @UpgradeCode @@ -399,6 +399,7 @@ extern "C" HRESULT MsiEngineDetectPackage( Trace(REPORT_STANDARD, "Detecting MSI package 0x%p", pPackage); HRESULT hr = S_OK; + int nCompareResult = 0; LPWSTR sczInstalledVersion = NULL; LPWSTR sczInstalledLanguage = NULL; LPWSTR sczInstalledProductCode = NULL; @@ -407,7 +408,7 @@ extern "C" HRESULT MsiEngineDetectPackage( BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE; BOOTSTRAPPER_RELATED_OPERATION relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE; WCHAR wzProductCode[MAX_GUID_CHARS + 1] = { }; - DWORD64 qwVersion = 0; + VERUTIL_VERSION* pVersion = NULL; UINT uLcid = 0; BOOL fPerMachine = FALSE; @@ -416,18 +417,21 @@ extern "C" HRESULT MsiEngineDetectPackage( hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); if (SUCCEEDED(hr)) { - hr = FileVersionFromStringEx(sczInstalledVersion, 0, &pPackage->Msi.qwInstalledVersion); - ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for ProductCode: %ls", sczInstalledVersion, pPackage->Msi.sczProductCode); + hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pPackage->Msi.pInstalledVersion); + ExitOnFailure(hr, "Failed to parse installed version: '%ls' for ProductCode: %ls", sczInstalledVersion, pPackage->Msi.sczProductCode); // compare versions - if (pPackage->Msi.qwVersion < pPackage->Msi.qwInstalledVersion) + hr = VerCompareParsedVersions(pPackage->Msi.pVersion, pPackage->Msi.pInstalledVersion, &nCompareResult); + ExitOnFailure(hr, "Failed to compare version '%ls' to installed version: '%ls'", pPackage->Msi.pVersion->sczVersion, pPackage->Msi.pInstalledVersion->sczVersion); + + if (nCompareResult < 0) { operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; } else { - if (pPackage->Msi.qwVersion > pPackage->Msi.qwInstalledVersion) + if (nCompareResult > 0) { operation = BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE; } @@ -438,9 +442,9 @@ extern "C" HRESULT MsiEngineDetectPackage( // Report related MSI package to BA. if (BOOTSTRAPPER_RELATED_OPERATION_NONE != operation) { - LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, pPackage->Msi.sczProductCode, LoggingPerMachineToString(pPackage->fPerMachine), LoggingVersionToString(pPackage->Msi.qwInstalledVersion), pPackage->Msi.dwLanguage, LoggingRelatedOperationToString(operation)); + LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, pPackage->Msi.sczProductCode, LoggingPerMachineToString(pPackage->fPerMachine), pPackage->Msi.pInstalledVersion->sczVersion, pPackage->Msi.dwLanguage, LoggingRelatedOperationToString(operation)); - hr = UserExperienceOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pPackage->Msi.sczUpgradeCode, pPackage->Msi.sczProductCode, pPackage->fPerMachine, pPackage->Msi.qwInstalledVersion, operation); + hr = UserExperienceOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pPackage->Msi.sczUpgradeCode, pPackage->Msi.sczProductCode, pPackage->fPerMachine, pPackage->Msi.pInstalledVersion, operation); ExitOnRootFailure(hr, "BA aborted detect related MSI package."); } } @@ -453,21 +457,26 @@ extern "C" HRESULT MsiEngineDetectPackage( hr = WiuGetProductInfoEx(sczInstalledProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); if (SUCCEEDED(hr)) { - hr = FileVersionFromStringEx(sczInstalledVersion, 0, &qwVersion); - ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for ProductCode: %ls", sczInstalledVersion, sczInstalledProductCode); + hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pVersion); + ExitOnFailure(hr, "Failed to parse dependency version: '%ls' for ProductCode: %ls", sczInstalledVersion, sczInstalledProductCode); + + // compare versions + hr = VerCompareParsedVersions(pPackage->Msi.pVersion, pVersion, &nCompareResult); + ExitOnFailure(hr, "Failed to compare version '%ls' to dependency version: '%ls'", pPackage->Msi.pVersion->sczVersion, pVersion->sczVersion); - if (pPackage->Msi.qwVersion < qwVersion) + if (nCompareResult < 0) { LogId(REPORT_STANDARD, MSG_DETECTED_COMPATIBLE_PACKAGE_FROM_PROVIDER, pPackage->sczId, sczInstalledProviderKey, sczInstalledProductCode, sczInstalledVersion, pPackage->Msi.sczProductCode); - hr = UserExperienceOnDetectCompatibleMsiPackage(pUserExperience, pPackage->sczId, sczInstalledProductCode, qwVersion); + hr = UserExperienceOnDetectCompatibleMsiPackage(pUserExperience, pPackage->sczId, sczInstalledProductCode, pVersion); ExitOnRootFailure(hr, "BA aborted detect compatible MSI package."); hr = StrAllocString(&pPackage->Msi.sczInstalledProductCode, sczInstalledProductCode, 0); ExitOnFailure(hr, "Failed to copy the installed ProductCode to the package."); - pPackage->Msi.qwInstalledVersion = qwVersion; + pPackage->Msi.pInstalledVersion = pVersion; pPackage->Msi.fCompatibleInstalled = TRUE; + pVersion = NULL; } } } @@ -524,18 +533,30 @@ extern "C" HRESULT MsiEngineDetectPackage( } } - hr = FileVersionFromStringEx(sczInstalledVersion, 0, &qwVersion); - ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for ProductCode: %ls", sczInstalledVersion, wzProductCode); + hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pVersion); + ExitOnFailure(hr, "Failed to parse related installed version: '%ls' for ProductCode: %ls", sczInstalledVersion, wzProductCode); // compare versions - if (pRelatedMsi->fMinProvided && (pRelatedMsi->fMinInclusive ? (qwVersion < pRelatedMsi->qwMinVersion) : (qwVersion <= pRelatedMsi->qwMinVersion))) + if (pRelatedMsi->fMinProvided) { - continue; + hr = VerCompareParsedVersions(pVersion, pRelatedMsi->pMinVersion, &nCompareResult); + ExitOnFailure(hr, "Failed to compare related installed version '%ls' to related min version: '%ls'", pVersion->sczVersion, pRelatedMsi->pMinVersion->sczVersion); + + if (pRelatedMsi->fMinInclusive ? (nCompareResult < 0) : (nCompareResult <= 0)) + { + continue; + } } - if (pRelatedMsi->fMaxProvided && (pRelatedMsi->fMaxInclusive ? (qwVersion > pRelatedMsi->qwMaxVersion) : (qwVersion >= pRelatedMsi->qwMaxVersion))) + if (pRelatedMsi->fMaxProvided) { - continue; + hr = VerCompareParsedVersions(pVersion, pRelatedMsi->pMaxVersion, &nCompareResult); + ExitOnFailure(hr, "Failed to compare related installed version '%ls' to related max version: '%ls'", pVersion->sczVersion, pRelatedMsi->pMaxVersion->sczVersion); + + if (pRelatedMsi->fMaxInclusive ? (nCompareResult > 0) : (nCompareResult >= 0)) + { + continue; + } } // Filter by language if necessary. @@ -605,10 +626,10 @@ extern "C" HRESULT MsiEngineDetectPackage( operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE; } - LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, wzProductCode, LoggingPerMachineToString(fPerMachine), LoggingVersionToString(qwVersion), uLcid, LoggingRelatedOperationToString(relatedMsiOperation)); + LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, wzProductCode, LoggingPerMachineToString(fPerMachine), pVersion->sczVersion, uLcid, LoggingRelatedOperationToString(relatedMsiOperation)); // Pass to BA. - hr = UserExperienceOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pRelatedMsi->sczUpgradeCode, wzProductCode, fPerMachine, qwVersion, relatedMsiOperation); + hr = UserExperienceOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pRelatedMsi->sczUpgradeCode, wzProductCode, fPerMachine, pVersion, relatedMsiOperation); ExitOnRootFailure(hr, "BA aborted detect related MSI package."); } } @@ -667,6 +688,7 @@ LExit: ReleaseStr(sczInstalledProductCode); ReleaseStr(sczInstalledLanguage); ReleaseStr(sczInstalledVersion); + ReleaseVerutilVersion(pVersion); return hr; } @@ -684,8 +706,9 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( Trace(REPORT_STANDARD, "Planning MSI package 0x%p", pPackage); HRESULT hr = S_OK; - DWORD64 qwVersion = pPackage->Msi.qwVersion; - DWORD64 qwInstalledVersion = pPackage->Msi.qwInstalledVersion; + VERUTIL_VERSION* pVersion = pPackage->Msi.pVersion; + VERUTIL_VERSION* pInstalledVersion = pPackage->Msi.pInstalledVersion; + int nCompareResult = 0; BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; BOOL fFeatureActionDelta = FALSE; @@ -739,10 +762,13 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) { + hr = VerCompareParsedVersions(pVersion, pInstalledVersion, &nCompareResult); + ExitOnFailure(hr, "Failed to compare '%ls' to '%ls' for planning.", pVersion->sczVersion, pInstalledVersion->sczVersion); + // Take a look at the version and determine if this is a potential // minor upgrade (same ProductCode newer ProductVersion), otherwise, // there is a newer version so no work necessary. - if (qwVersion > qwInstalledVersion) + if (nCompareResult > 0) { execute = BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE; } @@ -1014,20 +1040,18 @@ extern "C" HRESULT MsiEngineAddCompatiblePackage( } // Read in the compatible ProductVersion if not already available. - if (pPackage->Msi.qwInstalledVersion) + if (pPackage->Msi.pInstalledVersion) { - pCompatiblePackage->Msi.qwVersion = pPackage->Msi.qwInstalledVersion; - - hr = FileVersionToStringEx(pCompatiblePackage->Msi.qwVersion, &sczInstalledVersion); - ExitOnFailure(hr, "Failed to format version number string."); + hr = VerCopyVersion(pPackage->Msi.pInstalledVersion, &pCompatiblePackage->Msi.pVersion); + ExitOnFailure(hr, "Failed to copy version for compatible package."); } else { hr = WiuGetProductInfoEx(pCompatiblePackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); ExitOnFailure(hr, "Failed to read version from compatible package."); - hr = FileVersionFromStringEx(sczInstalledVersion, 0, &pCompatiblePackage->Msi.qwVersion); - ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for ProductCode: %ls", sczInstalledVersion, pCompatiblePackage->Msi.sczProductCode); + hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pCompatiblePackage->Msi.pVersion); + ExitOnFailure(hr, "Failed to parse version: '%ls' for ProductCode: %ls", sczInstalledVersion, pCompatiblePackage->Msi.sczProductCode); } // For now, copy enough information to support uninstalling the newer, compatible package. @@ -1046,7 +1070,7 @@ extern "C" HRESULT MsiEngineAddCompatiblePackage( ExitOnFailure(hr, "Failed to format log path variable for compatible package."); // Use the default cache ID generation from the binder. - hr = StrAllocFormatted(&pCompatiblePackage->sczCacheId, L"%lsv%ls", pCompatiblePackage->sczId, sczInstalledVersion); + hr = StrAllocFormatted(&pCompatiblePackage->sczCacheId, L"%lsv%ls", pCompatiblePackage->sczId, pCompatiblePackage->Msi.pVersion->sczVersion); ExitOnFailure(hr, "Failed to format cache ID for compatible package."); pCompatiblePackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; @@ -1068,7 +1092,7 @@ extern "C" HRESULT MsiEngineAddCompatiblePackage( ExitOnFailure(hr, "Failed to copy the compatible provider key."); // Assume the package version is the same as the provider version. - hr = StrAllocString(&pCompatibleProvider->sczVersion, sczInstalledVersion, 0); + hr = StrAllocString(&pCompatibleProvider->sczVersion, pCompatiblePackage->Msi.pVersion->sczVersion, 0); ExitOnFailure(hr, "Failed to copy the compatible provider version."); // Assume provider keys are similarly authored for this package. @@ -1479,7 +1503,7 @@ static HRESULT ParseRelatedMsiFromXml( { ExitOnFailure(hr, "Failed to get @MinVersion."); - hr = FileVersionFromStringEx(scz, 0, &pRelatedMsi->qwMinVersion); + hr = VerParseVersion(scz, 0, FALSE, &pRelatedMsi->pMinVersion); ExitOnFailure(hr, "Failed to parse @MinVersion: %ls", scz); // flag that we have a min version @@ -1496,7 +1520,7 @@ static HRESULT ParseRelatedMsiFromXml( { ExitOnFailure(hr, "Failed to get @MaxVersion."); - hr = FileVersionFromStringEx(scz, 0, &pRelatedMsi->qwMaxVersion); + hr = VerParseVersion(scz, 0, FALSE, &pRelatedMsi->pMaxVersion); ExitOnFailure(hr, "Failed to parse @MaxVersion: %ls", scz); // flag that we have a max version diff --git a/src/engine/package.h b/src/engine/package.h index 05965a16..c5873765 100644 --- a/src/engine/package.h +++ b/src/engine/package.h @@ -123,8 +123,8 @@ typedef struct _BURN_MSIFEATURE typedef struct _BURN_RELATED_MSI { LPWSTR sczUpgradeCode; - DWORD64 qwMinVersion; - DWORD64 qwMaxVersion; + VERUTIL_VERSION* pMinVersion; + VERUTIL_VERSION* pMaxVersion; BOOL fMinProvided; BOOL fMaxProvided; BOOL fMinInclusive; @@ -236,9 +236,9 @@ typedef struct _BURN_PACKAGE { LPWSTR sczProductCode; DWORD dwLanguage; - DWORD64 qwVersion; + VERUTIL_VERSION* pVersion; LPWSTR sczInstalledProductCode; - DWORD64 qwInstalledVersion; + VERUTIL_VERSION* pInstalledVersion; LPWSTR sczUpgradeCode; BURN_MSIPROPERTY* rgProperties; diff --git a/src/engine/packages.config b/src/engine/packages.config index e7fa32d0..68222d34 100644 --- a/src/engine/packages.config +++ b/src/engine/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index 0b040bf8..f6b681b6 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -530,7 +530,7 @@ extern "C" HRESULT PlanPackages( pAction->type = BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE; pAction->compatiblePackage.pReferencePackage = pPackage; - pAction->compatiblePackage.qwInstalledVersion = pCompatiblePackage->Msi.qwVersion; + pAction->compatiblePackage.pInstalledVersion = pCompatiblePackage->Msi.pVersion; hr = StrAllocString(&pAction->compatiblePackage.sczInstalledProductCode, pCompatiblePackage->Msi.sczProductCode, 0); ExitOnFailure(hr, "Failed to copy installed ProductCode"); @@ -880,7 +880,7 @@ static HRESULT ProcessPackage( { AssertSz(BURN_PACKAGE_TYPE_MSI == pPackage->type, "Currently only MSI packages have compatible packages."); - hr = UserExperienceOnPlanCompatibleMsiPackageBegin(pUX, pCompatiblePackageParent->sczId, pPackage->sczId, pPackage->Msi.qwVersion, &pPackage->requested); + hr = UserExperienceOnPlanCompatibleMsiPackageBegin(pUX, pCompatiblePackageParent->sczId, pPackage->sczId, pPackage->Msi.pVersion, &pPackage->requested); ExitOnRootFailure(hr, "BA aborted plan compatible MSI package begin."); } else @@ -1207,6 +1207,7 @@ extern "C" HRESULT PlanRelatedBundlesBegin( LPWSTR* rgsczAncestors = NULL; UINT cAncestors = 0; STRINGDICT_HANDLE sdAncestors = NULL; + int nCompareResult = 0; if (pRegistration->sczAncestors) { @@ -1261,7 +1262,10 @@ extern "C" HRESULT PlanRelatedBundlesBegin( case BOOTSTRAPPER_RELATION_UPGRADE: if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < pPlan->action) { - pRelatedBundle->package.requested = (pRegistration->qwVersion > pRelatedBundle->qwVersion) ? BOOTSTRAPPER_REQUEST_STATE_ABSENT : BOOTSTRAPPER_REQUEST_STATE_NONE; + hr = VerCompareParsedVersions(pRegistration->pVersion, pRelatedBundle->pVersion, &nCompareResult); + ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistration->pVersion, pRelatedBundle->pVersion); + + pRelatedBundle->package.requested = (nCompareResult > 0) ? BOOTSTRAPPER_REQUEST_STATE_ABSENT : BOOTSTRAPPER_REQUEST_STATE_NONE; } break; case BOOTSTRAPPER_RELATION_PATCH: __fallthrough; diff --git a/src/engine/plan.h b/src/engine/plan.h index db9745e9..4fd3380e 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -305,7 +305,7 @@ typedef struct _BURN_EXECUTE_ACTION { BURN_PACKAGE* pReferencePackage; LPWSTR sczInstalledProductCode; - DWORD64 qwInstalledVersion; + VERUTIL_VERSION* pInstalledVersion; } compatiblePackage; }; } BURN_EXECUTE_ACTION; diff --git a/src/engine/precomp.h b/src/engine/precomp.h index b5c5e65e..2bceab58 100644 --- a/src/engine/precomp.h +++ b/src/engine/precomp.h @@ -24,6 +24,7 @@ #define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_EXTERNAL #include +#include #include #include #include diff --git a/src/engine/registration.cpp b/src/engine/registration.cpp index eace62ce..3c3dc95d 100644 --- a/src/engine/registration.cpp +++ b/src/engine/registration.cpp @@ -133,7 +133,7 @@ extern "C" HRESULT RegistrationParseFromXml( hr = XmlGetAttributeEx(pixnRegistrationNode, L"Version", &scz); ExitOnFailure(hr, "Failed to get @Version."); - hr = FileVersionFromStringEx(scz, 0, &pRegistration->qwVersion); + hr = VerParseVersion(scz, 0, FALSE, &pRegistration->pVersion); ExitOnFailure(hr, "Failed to parse @Version: %ls", scz); // @ProviderKey @@ -436,7 +436,7 @@ extern "C" HRESULT RegistrationSetVariables( hr = VariableSetString(pVariables, BURN_BUNDLE_TAG, pRegistration->sczTag, TRUE, FALSE); ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable."); - hr = VariableSetVersion(pVariables, BURN_BUNDLE_VERSION, pRegistration->qwVersion, TRUE); + hr = VariableSetVersion(pVariables, BURN_BUNDLE_VERSION, pRegistration->pVersion, TRUE); ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable."); LExit: @@ -630,15 +630,13 @@ extern "C" HRESULT RegistrationSessionBegin( hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, pRegistration->rgsczPatchCodes, pRegistration->cPatchCodes); ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE); - hr = RegWriteStringFormatted(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, L"%hu.%hu.%hu.%hu", - static_cast(pRegistration->qwVersion >> 48), static_cast(pRegistration->qwVersion >> 32), - static_cast(pRegistration->qwVersion >> 16), static_cast(pRegistration->qwVersion)); + hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, pRegistration->pVersion->sczVersion); ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION); - hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_VERSION_MAJOR, static_cast(pRegistration->qwVersion >> 48)); + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_VERSION_MAJOR, pRegistration->pVersion->dwMajor); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_VERSION_MAJOR); - hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_VERSION_MINOR, static_cast(pRegistration->qwVersion >> 32)); + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_VERSION_MINOR, pRegistration->pVersion->dwMinor); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_VERSION_MINOR); if (pRegistration->sczProviderKey) diff --git a/src/engine/registration.h b/src/engine/registration.h index c830a06d..fa7be368 100644 --- a/src/engine/registration.h +++ b/src/engine/registration.h @@ -59,7 +59,7 @@ typedef struct _BURN_RELATED_BUNDLE { BOOTSTRAPPER_RELATION_TYPE relationType; - DWORD64 qwVersion; + VERUTIL_VERSION* pVersion; LPWSTR sczTag; BURN_PACKAGE package; @@ -105,7 +105,7 @@ typedef struct _BURN_REGISTRATION LPWSTR *rgsczPatchCodes; DWORD cPatchCodes; - DWORD64 qwVersion; + VERUTIL_VERSION* pVersion; LPWSTR sczActiveParent; LPWSTR sczProviderKey; LPWSTR sczExecutableName; diff --git a/src/engine/relatedbundle.cpp b/src/engine/relatedbundle.cpp index 87794177..e6d6516a 100644 --- a/src/engine/relatedbundle.cpp +++ b/src/engine/relatedbundle.cpp @@ -396,6 +396,7 @@ static HRESULT LoadRelatedBundleFromKey( { HRESULT hr = S_OK; DWORD64 qwEngineVersion = 0; + LPWSTR sczBundleVersion = NULL; LPWSTR sczCachePath = NULL; DWORD64 qwFileSize = 0; BURN_DEPENDENCY_PROVIDER dependencyProvider = { }; @@ -407,9 +408,12 @@ static HRESULT LoadRelatedBundleFromKey( hr = S_OK; } - hr = RegReadVersion(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, &pRelatedBundle->qwVersion); + hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, &sczBundleVersion); ExitOnFailure(hr, "Failed to read version from registry for bundle: %ls", wzRelatedBundleId); + hr = VerParseVersion(sczBundleVersion, 0, FALSE, &pRelatedBundle->pVersion); + ExitOnFailure(hr, "Failed to parse pseudo bundle version: %ls", sczBundleVersion); + hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, &sczCachePath); ExitOnFailure(hr, "Failed to read cache path from registry for bundle: %ls", wzRelatedBundleId); @@ -423,7 +427,7 @@ static HRESULT LoadRelatedBundleFromKey( dependencyProvider.fImported = TRUE; - hr = FileVersionToStringEx(pRelatedBundle->qwVersion, &dependencyProvider.sczVersion); + hr = StrAllocString(&dependencyProvider.sczVersion, pRelatedBundle->pVersion->sczVersion, 0); ExitOnFailure(hr, "Failed to copy version for bundle: %ls", wzRelatedBundleId); hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME, &dependencyProvider.sczDisplayName); @@ -452,6 +456,7 @@ static HRESULT LoadRelatedBundleFromKey( LExit: DependencyUninitialize(&dependencyProvider); ReleaseStr(sczCachePath); + ReleaseStr(sczBundleVersion); return hr; } diff --git a/src/engine/search.cpp b/src/engine/search.cpp index 2978edd3..1dbcf56b 100644 --- a/src/engine/search.cpp +++ b/src/engine/search.cpp @@ -754,6 +754,7 @@ static HRESULT FileSearchVersion( HRESULT hr = S_OK; ULARGE_INTEGER uliVersion = { }; LPWSTR sczPath = NULL; + VERUTIL_VERSION* pVersion = NULL; // format path hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL); @@ -767,14 +768,18 @@ static HRESULT FileSearchVersion( LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath); ExitFunction1(hr = S_OK); } - ExitOnFailure(hr, "Failed get file version."); + ExitOnFailure(hr, "Failed to get file version."); + + hr = VerVersionFromQword(uliVersion.QuadPart, &pVersion); + ExitOnFailure(hr, "Failed to create version from file version."); // set variable - hr = VariableSetVersion(pVariables, pSearch->sczVariable, uliVersion.QuadPart, FALSE); + hr = VariableSetVersion(pVariables, pSearch->sczVariable, pVersion, FALSE); ExitOnFailure(hr, "Failed to set variable."); LExit: StrSecureZeroFreeString(sczPath); + ReleaseVerutilVersion(pVersion); return hr; } diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index 6b0e3bf5..6ab28cde 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -646,7 +646,7 @@ EXTERN_C BAAPI UserExperienceOnDetectCompatibleMsiPackage( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, __in_z LPCWSTR wzCompatiblePackageId, - __in DWORD64 dw64CompatiblePackageVersion + __in VERUTIL_VERSION* pCompatiblePackageVersion ) { HRESULT hr = S_OK; @@ -656,7 +656,7 @@ EXTERN_C BAAPI UserExperienceOnDetectCompatibleMsiPackage( args.cbSize = sizeof(args); args.wzPackageId = wzPackageId; args.wzCompatiblePackageId = wzCompatiblePackageId; - args.dw64CompatiblePackageVersion = dw64CompatiblePackageVersion; + args.wzCompatiblePackageVersion = pCompatiblePackageVersion->sczVersion; results.cbSize = sizeof(results); @@ -699,7 +699,7 @@ EXTERN_C BAAPI UserExperienceOnDetectForwardCompatibleBundle( __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z LPCWSTR wzBundleTag, __in BOOL fPerMachine, - __in DWORD64 dw64Version, + __in VERUTIL_VERSION* pVersion, __inout BOOL* pfIgnoreBundle ) { @@ -712,7 +712,7 @@ EXTERN_C BAAPI UserExperienceOnDetectForwardCompatibleBundle( args.relationType = relationType; args.wzBundleTag = wzBundleTag; args.fPerMachine = fPerMachine; - args.dw64Version = dw64Version; + args.wzVersion = pVersion->sczVersion; results.cbSize = sizeof(results); results.fIgnoreBundle = *pfIgnoreBundle; @@ -817,7 +817,7 @@ EXTERN_C BAAPI UserExperienceOnDetectRelatedBundle( __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z LPCWSTR wzBundleTag, __in BOOL fPerMachine, - __in DWORD64 dw64Version, + __in VERUTIL_VERSION* pVersion, __in BOOTSTRAPPER_RELATED_OPERATION operation ) { @@ -830,7 +830,7 @@ EXTERN_C BAAPI UserExperienceOnDetectRelatedBundle( args.relationType = relationType; args.wzBundleTag = wzBundleTag; args.fPerMachine = fPerMachine; - args.dw64Version = dw64Version; + args.wzVersion = pVersion->sczVersion; args.operation = operation; results.cbSize = sizeof(results); @@ -853,7 +853,7 @@ EXTERN_C BAAPI UserExperienceOnDetectRelatedMsiPackage( __in_z LPCWSTR wzUpgradeCode, __in_z LPCWSTR wzProductCode, __in BOOL fPerMachine, - __in DWORD64 dw64Version, + __in VERUTIL_VERSION* pVersion, __in BOOTSTRAPPER_RELATED_OPERATION operation ) { @@ -866,7 +866,7 @@ EXTERN_C BAAPI UserExperienceOnDetectRelatedMsiPackage( args.wzUpgradeCode = wzUpgradeCode; args.wzProductCode = wzProductCode; args.fPerMachine = fPerMachine; - args.dw64Version = dw64Version; + args.wzVersion = pVersion->sczVersion; args.operation = operation; results.cbSize = sizeof(results); @@ -917,7 +917,7 @@ EXTERN_C BAAPI UserExperienceOnDetectUpdate( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzUpdateLocation, __in DWORD64 dw64Size, - __in DWORD64 dw64Version, + __in VERUTIL_VERSION* pVersion, __in_z_opt LPCWSTR wzTitle, __in_z_opt LPCWSTR wzSummary, __in_z_opt LPCWSTR wzContentType, @@ -932,7 +932,7 @@ EXTERN_C BAAPI UserExperienceOnDetectUpdate( args.cbSize = sizeof(args); args.wzUpdateLocation = wzUpdateLocation; args.dw64Size = dw64Size; - args.dw64Version = dw64Version; + args.wzVersion = pVersion->sczVersion; args.wzTitle = wzTitle; args.wzSummary = wzSummary; args.wzContentType = wzContentType; @@ -1414,7 +1414,7 @@ EXTERN_C BAAPI UserExperienceOnPlanCompatibleMsiPackageBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, __in_z LPCWSTR wzCompatiblePackageId, - __in DWORD64 dw64CompatiblePackageVersion, + __in VERUTIL_VERSION* pCompatiblePackageVersion, __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState ) { @@ -1425,7 +1425,7 @@ EXTERN_C BAAPI UserExperienceOnPlanCompatibleMsiPackageBegin( args.cbSize = sizeof(args); args.wzPackageId = wzPackageId; args.wzCompatiblePackageId = wzCompatiblePackageId; - args.dw64CompatiblePackageVersion = dw64CompatiblePackageVersion; + args.wzCompatiblePackageVersion = pCompatiblePackageVersion->sczVersion; args.recommendedState = *pRequestedState; results.cbSize = sizeof(results); diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index a5a97ffa..0ebd1b68 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -171,7 +171,7 @@ BAAPI UserExperienceOnDetectCompatibleMsiPackage( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, __in_z LPCWSTR wzCompatiblePackageId, - __in DWORD64 dw64CompatiblePackageVersion + __in VERUTIL_VERSION* pCompatiblePackageVersion ); BAAPI UserExperienceOnDetectComplete( __in BURN_USER_EXPERIENCE* pUserExperience, @@ -183,7 +183,7 @@ BAAPI UserExperienceOnDetectForwardCompatibleBundle( __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z LPCWSTR wzBundleTag, __in BOOL fPerMachine, - __in DWORD64 dw64Version, + __in VERUTIL_VERSION* pVersion, __inout BOOL* pfIgnoreBundle ); BAAPI UserExperienceOnDetectMsiFeature( @@ -208,7 +208,7 @@ BAAPI UserExperienceOnDetectRelatedBundle( __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z LPCWSTR wzBundleTag, __in BOOL fPerMachine, - __in DWORD64 dw64Version, + __in VERUTIL_VERSION* pVersion, __in BOOTSTRAPPER_RELATED_OPERATION operation ); BAAPI UserExperienceOnDetectRelatedMsiPackage( @@ -217,7 +217,7 @@ BAAPI UserExperienceOnDetectRelatedMsiPackage( __in_z LPCWSTR wzUpgradeCode, __in_z LPCWSTR wzProductCode, __in BOOL fPerMachine, - __in DWORD64 dw64Version, + __in VERUTIL_VERSION* pVersion, __in BOOTSTRAPPER_RELATED_OPERATION operation ); BAAPI UserExperienceOnDetectTargetMsiPackage( @@ -230,7 +230,7 @@ BAAPI UserExperienceOnDetectUpdate( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzUpdateLocation, __in DWORD64 dw64Size, - __in DWORD64 dw64Version, + __in VERUTIL_VERSION* pVersion, __in_z_opt LPCWSTR wzTitle, __in_z_opt LPCWSTR wzSummary, __in_z_opt LPCWSTR wzContentType, @@ -333,7 +333,7 @@ BAAPI UserExperienceOnPlanCompatibleMsiPackageBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, __in_z LPCWSTR wzCompatiblePackageId, - __in DWORD64 dw64CompatiblePackageVersion, + __in VERUTIL_VERSION* pCompatiblePackageVersion, __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState ); BAAPI UserExperienceOnPlanCompatibleMsiPackageComplete( diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp index fb4b74e2..3069ccf7 100644 --- a/src/engine/variable.cpp +++ b/src/engine/variable.cpp @@ -268,7 +268,7 @@ extern "C" HRESULT VariableInitialize( {BURN_BUNDLE_SOURCE_PROCESS_FOLDER, InitializeVariableString, NULL, FALSE, TRUE}, {BURN_BUNDLE_TAG, InitializeVariableString, (DWORD_PTR)L"", FALSE, TRUE}, {BURN_BUNDLE_UILEVEL, InitializeVariableNumeric, 0, FALSE, TRUE}, - {BURN_BUNDLE_VERSION, InitializeVariableVersion, 0, FALSE, TRUE}, + {BURN_BUNDLE_VERSION, InitializeVariableVersion, (DWORD_PTR)L"0", FALSE, TRUE}, }; for (DWORD i = 0; i < countof(vrgBuiltInVariables); ++i) @@ -561,11 +561,11 @@ LExit: return hr; } -// The contents of pqwValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroMemory. +// The contents of ppValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroMemory. extern "C" HRESULT VariableGetVersion( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, - __in DWORD64* pqwValue + __in VERUTIL_VERSION** ppValue ) { HRESULT hr = S_OK; @@ -584,7 +584,7 @@ extern "C" HRESULT VariableGetVersion( } ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); - hr = BVariantGetVersion(&pVariable->Value, pqwValue); + hr = BVariantGetVersion(&pVariable->Value, ppValue); ExitOnFailure(hr, "Failed to get value as version for variable: %ls", wzVariable); LExit: @@ -701,14 +701,14 @@ extern "C" HRESULT VariableSetString( extern "C" HRESULT VariableSetVersion( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, - __in DWORD64 qwValue, + __in VERUTIL_VERSION* pValue, __in BOOL fOverwriteBuiltIn ) { BURN_VARIANT variant = { }; // We're not going to encrypt this value, so can access the value directly. - variant.qwValue = qwValue; + variant.pValue = pValue; variant.Type = BURN_VARIANT_TYPE_VERSION; return SetVariableValue(pVariables, wzVariable, &variant, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); @@ -810,7 +810,6 @@ extern "C" HRESULT VariableSerialize( BOOL fIncluded = FALSE; LONGLONG ll = 0; LPWSTR scz = NULL; - DWORD64 qw = 0; ::EnterCriticalSection(&pVariables->csAccess); @@ -859,15 +858,7 @@ extern "C" HRESULT VariableSerialize( SecureZeroMemory(&ll, sizeof(ll)); break; - case BURN_VARIANT_TYPE_VERSION: - hr = BVariantGetVersion(&pVariable->Value, &qw); - ExitOnFailure(hr, "Failed to get version."); - - hr = BuffWriteNumber64(ppbBuffer, piBuffer, qw); - ExitOnFailure(hr, "Failed to write variable value as number."); - - SecureZeroMemory(&qw, sizeof(qw)); - break; + case BURN_VARIANT_TYPE_VERSION: __fallthrough; case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; case BURN_VARIANT_TYPE_STRING: hr = BVariantGetString(&pVariable->Value, &scz); @@ -887,7 +878,6 @@ extern "C" HRESULT VariableSerialize( LExit: ::LeaveCriticalSection(&pVariables->csAccess); SecureZeroMemory(&ll, sizeof(ll)); - SecureZeroMemory(&qw, sizeof(qw)); StrSecureZeroFreeString(scz); return hr; @@ -908,6 +898,7 @@ extern "C" HRESULT VariableDeserialize( BURN_VARIANT value = { }; LPWSTR scz = NULL; DWORD64 qw = 0; + VERUTIL_VERSION* pVersion = NULL; ::EnterCriticalSection(&pVariables->csAccess); @@ -950,10 +941,13 @@ extern "C" HRESULT VariableDeserialize( SecureZeroMemory(&qw, sizeof(qw)); break; case BURN_VARIANT_TYPE_VERSION: - hr = BuffReadNumber64(pbBuffer, cbBuffer, piBuffer, &qw); - ExitOnFailure(hr, "Failed to read variable value as number."); + hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &scz); + ExitOnFailure(hr, "Failed to read variable value as string."); + + hr = VerParseVersion(scz, 0, FALSE, &pVersion); + ExitOnFailure(hr, "Failed to parse variable value as version."); - hr = BVariantSetVersion(&value, qw); + hr = BVariantSetVersion(&value, pVersion); ExitOnFailure(hr, "Failed to set variable value."); SecureZeroMemory(&qw, sizeof(qw)); @@ -984,6 +978,7 @@ extern "C" HRESULT VariableDeserialize( LExit: ::LeaveCriticalSection(&pVariables->csAccess); + ReleaseVerutilVersion(pVersion); ReleaseStr(sczName); BVariantUninitialize(&value); SecureZeroMemory(&qw, sizeof(qw)); @@ -1572,7 +1567,7 @@ static HRESULT SetVariableValue( break; case BURN_VARIANT_TYPE_VERSION: - LogStringLine(REPORT_STANDARD, "Setting version variable '%ls' to value '%hu.%hu.%hu.%hu'", wzVariable, (WORD)(pVariant->qwValue >> 48), (WORD)(pVariant->qwValue >> 32), (WORD)(pVariant->qwValue >> 16), (WORD)(pVariant->qwValue)); + LogStringLine(REPORT_STANDARD, "Setting version variable '%ls' to value '%ls'", wzVariable, pVariant->pValue->sczVersion); break; default: @@ -1609,6 +1604,7 @@ static HRESULT InitializeVariableVersionNT( RTL_GET_VERSION rtlGetVersion = NULL; RTL_OSVERSIONINFOEXW ovix = { }; BURN_VARIANT value = { }; + VERUTIL_VERSION* pVersion = NULL; if (!::GetModuleHandleExW(0, L"ntdll", &ntdll)) { @@ -1630,12 +1626,15 @@ static HRESULT InitializeVariableVersionNT( case OS_INFO_VARIABLE_ServicePackLevel: if (0 != ovix.wServicePackMajor) { - value.qwValue = static_cast(ovix.wServicePackMajor); + value.llValue = static_cast(ovix.wServicePackMajor); value.Type = BURN_VARIANT_TYPE_NUMERIC; } break; case OS_INFO_VARIABLE_VersionNT: - value.qwValue = MAKEQWORDVERSION(ovix.dwMajorVersion, ovix.dwMinorVersion, 0, 0); + hr = VerVersionFromQword(MAKEQWORDVERSION(ovix.dwMajorVersion, ovix.dwMinorVersion, 0, 0), &pVersion); + ExitOnFailure(hr, "Failed to create VersionNT from QWORD."); + + value.pValue = pVersion; value.Type = BURN_VARIANT_TYPE_VERSION; break; case OS_INFO_VARIABLE_VersionNT64: @@ -1647,13 +1646,16 @@ static HRESULT InitializeVariableVersionNT( if (fIsWow64) #endif { - value.qwValue = MAKEQWORDVERSION(ovix.dwMajorVersion, ovix.dwMinorVersion, 0, 0); + hr = VerVersionFromQword(MAKEQWORDVERSION(ovix.dwMajorVersion, ovix.dwMinorVersion, 0, 0), &pVersion); + ExitOnFailure(hr, "Failed to create VersionNT64 from QWORD."); + + value.pValue = pVersion; value.Type = BURN_VARIANT_TYPE_VERSION; } } break; case OS_INFO_VARIABLE_WindowsBuildNumber: - value.qwValue = static_cast(ovix.dwBuildNumber); + value.llValue = static_cast(ovix.dwBuildNumber); value.Type = BURN_VARIANT_TYPE_NUMERIC; default: AssertSz(FALSE, "Unknown OS info type."); @@ -1669,6 +1671,8 @@ LExit: FreeLibrary(ntdll); } + ReleaseVerutilVersion(pVersion); + return hr; } @@ -1805,15 +1809,14 @@ LExit: } static HRESULT InitializeVariableVersionMsi( - __in DWORD_PTR dwpData, + __in DWORD_PTR /*dwpData*/, __inout BURN_VARIANT* pValue ) { - UNREFERENCED_PARAMETER(dwpData); - HRESULT hr = S_OK; DLLGETVERSIONPROC pfnMsiDllGetVersion = NULL; DLLVERSIONINFO msiVersionInfo = { }; + VERUTIL_VERSION* pVersion = NULL; // get DllGetVersion proc address pfnMsiDllGetVersion = (DLLGETVERSIONPROC)::GetProcAddress(::GetModuleHandleW(L"msi"), "DllGetVersion"); @@ -1824,10 +1827,15 @@ static HRESULT InitializeVariableVersionMsi( hr = pfnMsiDllGetVersion(&msiVersionInfo); ExitOnFailure(hr, "Failed to get msi.dll version info."); - hr = BVariantSetVersion(pValue, MAKEQWORDVERSION(msiVersionInfo.dwMajorVersion, msiVersionInfo.dwMinorVersion, 0, 0)); + hr = VerVersionFromQword(MAKEQWORDVERSION(msiVersionInfo.dwMajorVersion, msiVersionInfo.dwMinorVersion, 0, 0), &pVersion); + ExitOnFailure(hr, "Failed to create msi.dll version from QWORD."); + + hr = BVariantSetVersion(pValue, pVersion); ExitOnFailure(hr, "Failed to set variant value."); LExit: + ReleaseVerutilVersion(pVersion); + return hr; } @@ -2270,12 +2278,19 @@ static HRESULT InitializeVariableVersion( ) { HRESULT hr = S_OK; + LPCWSTR wzValue = (LPCWSTR)dwpData; + VERUTIL_VERSION* pVersion = NULL; + + hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); + ExitOnFailure(hr, "Failed to initialize version."); // set value - hr = BVariantSetVersion(pValue, static_cast(dwpData)); + hr = BVariantSetVersion(pValue, pVersion); ExitOnFailure(hr, "Failed to set variant value."); LExit: + ReleaseVerutilVersion(pVersion); + return hr; } diff --git a/src/engine/variable.h b/src/engine/variable.h index 648c5daf..6437c32f 100644 --- a/src/engine/variable.h +++ b/src/engine/variable.h @@ -85,7 +85,7 @@ HRESULT VariableGetString( HRESULT VariableGetVersion( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, - __in DWORD64* pqwValue + __in VERUTIL_VERSION** ppValue ); HRESULT VariableGetVariant( __in BURN_VARIABLES* pVariables, @@ -113,7 +113,7 @@ HRESULT VariableSetString( HRESULT VariableSetVersion( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, - __in DWORD64 qwValue, + __in VERUTIL_VERSION* pValue, __in BOOL fOverwriteBuiltIn ); HRESULT VariableSetVariant( diff --git a/src/engine/variant.cpp b/src/engine/variant.cpp index 43bc19c4..28578691 100644 --- a/src/engine/variant.cpp +++ b/src/engine/variant.cpp @@ -23,7 +23,7 @@ static HRESULT BVariantRetrieveDecryptedString( static void BVariantRetrieveVersion( __in BURN_VARIANT* pVariant, - __out DWORD64* pqwValue + __out VERUTIL_VERSION** ppValue ); // function definitions @@ -48,6 +48,7 @@ extern "C" HRESULT BVariantGetNumeric( { HRESULT hr = S_OK; LPWSTR sczValue = NULL; + VERUTIL_VERSION* pVersionValue = NULL; switch (pVariant->Type) { @@ -68,7 +69,13 @@ extern "C" HRESULT BVariantGetNumeric( StrSecureZeroFreeString(sczValue); break; case BURN_VARIANT_TYPE_VERSION: - BVariantRetrieveVersion(pVariant, (DWORD64*)pllValue); + BVariantRetrieveVersion(pVariant, &pVersionValue); + + hr = StrStringToInt64(pVersionValue->sczVersion, 0, pllValue); + if (FAILED(hr)) + { + hr = DISP_E_TYPEMISMATCH; + } break; default: hr = E_INVALIDARG; @@ -86,7 +93,7 @@ extern "C" HRESULT BVariantGetString( { HRESULT hr = S_OK; LONGLONG llValue = 0; - DWORD64 qwValue = 0; + VERUTIL_VERSION* pVersionValue = NULL; switch (pVariant->Type) { @@ -104,17 +111,9 @@ extern "C" HRESULT BVariantGetString( hr = BVariantRetrieveDecryptedString(pVariant, psczValue); break; case BURN_VARIANT_TYPE_VERSION: - BVariantRetrieveVersion(pVariant, &qwValue); - if (SUCCEEDED(hr)) - { - hr = StrAllocFormattedSecure(psczValue, L"%hu.%hu.%hu.%hu", - (WORD)(qwValue >> 48), - (WORD)(qwValue >> 32), - (WORD)(qwValue >> 16), - (WORD)qwValue); - ExitOnFailure(hr, "Failed to convert version to string."); - } - SecureZeroMemory(&qwValue, sizeof(qwValue)); + BVariantRetrieveVersion(pVariant, &pVersionValue); + + hr = StrAllocStringSecure(psczValue, pVersionValue->sczVersion, 0); break; default: hr = E_INVALIDARG; @@ -125,26 +124,30 @@ LExit: return hr; } -// The contents of pqwValue may be sensitive, should keep encrypted and SecureZeroMemory. +// The contents of ppValue may be sensitive, should keep encrypted and SecureZeroMemory. extern "C" HRESULT BVariantGetVersion( __in BURN_VARIANT* pVariant, - __out DWORD64* pqwValue + __out VERUTIL_VERSION** ppValue ) { HRESULT hr = S_OK; + LONGLONG llValue = 0; LPWSTR sczValue = NULL; + VERUTIL_VERSION* pValue = NULL; switch (pVariant->Type) { case BURN_VARIANT_TYPE_NUMERIC: - BVariantRetrieveNumeric(pVariant, (LONGLONG*)pqwValue); + BVariantRetrieveNumeric(pVariant, &llValue); + + hr = VerVersionFromQword(llValue, ppValue); break; case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; case BURN_VARIANT_TYPE_STRING: hr = BVariantRetrieveDecryptedString(pVariant, &sczValue); if (SUCCEEDED(hr)) { - hr = FileVersionFromStringEx(sczValue, 0, pqwValue); + hr = VerParseVersion(sczValue, 0, FALSE, ppValue); if (FAILED(hr)) { hr = DISP_E_TYPEMISMATCH; @@ -153,7 +156,9 @@ extern "C" HRESULT BVariantGetVersion( StrSecureZeroFreeString(sczValue); break; case BURN_VARIANT_TYPE_VERSION: - BVariantRetrieveVersion(pVariant, pqwValue); + BVariantRetrieveVersion(pVariant, &pValue); + + hr = VerCopyVersion(pValue, ppValue); break; default: hr = E_INVALIDARG; @@ -224,7 +229,7 @@ LExit: extern "C" HRESULT BVariantSetVersion( __in BURN_VARIANT* pVariant, - __in DWORD64 qwValue + __in VERUTIL_VERSION* pValue ) { HRESULT hr = S_OK; @@ -236,7 +241,7 @@ extern "C" HRESULT BVariantSetVersion( StrSecureZeroFreeString(pVariant->sczValue); } memset(pVariant, 0, sizeof(BURN_VARIANT)); - pVariant->qwValue = qwValue; + hr = VerCopyVersion(pValue, &pVariant->pValue); pVariant->Type = BURN_VARIANT_TYPE_VERSION; BVariantSetEncryption(pVariant, fEncryptValue); @@ -251,7 +256,7 @@ extern "C" HRESULT BVariantSetValue( HRESULT hr = S_OK; LONGLONG llValue = 0; LPWSTR sczValue = NULL; - DWORD64 qwValue = 0; + VERUTIL_VERSION* pVersionValue = NULL; BOOL fEncrypt = pVariant->fEncryptString; switch (pValue->Type) @@ -277,12 +282,11 @@ extern "C" HRESULT BVariantSetValue( StrSecureZeroFreeString(sczValue); break; case BURN_VARIANT_TYPE_VERSION: - hr = BVariantGetVersion(pValue, &qwValue); + hr = BVariantGetVersion(pValue, &pVersionValue); if (SUCCEEDED(hr)) { - hr = BVariantSetVersion(pVariant, qwValue); + hr = BVariantSetVersion(pVariant, pVersionValue); } - SecureZeroMemory(&qwValue, sizeof(qwValue)); break; default: hr = E_INVALIDARG; @@ -303,7 +307,7 @@ extern "C" HRESULT BVariantCopy( HRESULT hr = S_OK; LONGLONG llValue = 0; LPWSTR sczValue = NULL; - DWORD64 qwValue = 0; + VERUTIL_VERSION* pVersionValue = 0; BVariantUninitialize(pTarget); @@ -329,12 +333,11 @@ extern "C" HRESULT BVariantCopy( StrSecureZeroFreeString(sczValue); break; case BURN_VARIANT_TYPE_VERSION: - hr = BVariantGetVersion(pSource, &qwValue); + hr = BVariantGetVersion(pSource, &pVersionValue); if (SUCCEEDED(hr)) { - hr = BVariantSetVersion(pTarget, qwValue); + hr = BVariantSetVersion(pTarget, pVersionValue); } - SecureZeroMemory(&qwValue, sizeof(qwValue)); break; default: hr = E_INVALIDARG; @@ -380,13 +383,13 @@ extern "C" HRESULT BVariantChangeType( hr = BVariantGetString(pVariant, &variant.sczValue); break; case BURN_VARIANT_TYPE_VERSION: - hr = BVariantGetVersion(pVariant, &variant.qwValue); + hr = BVariantGetVersion(pVariant, &variant.pValue); break; default: ExitFunction1(hr = E_INVALIDARG); } - ExitOnFailure(hr, "Failed to copy variant value."); variant.Type = type; + ExitOnFailure(hr, "Failed to copy variant value."); BVariantUninitialize(pVariant); memcpy_s(pVariant, sizeof(BURN_VARIANT), &variant, sizeof(BURN_VARIANT)); @@ -524,10 +527,10 @@ LExit: static void BVariantRetrieveVersion( __in BURN_VARIANT* pVariant, - __out DWORD64* pqwValue + __out VERUTIL_VERSION** ppValue ) { - Assert(NULL != pqwValue); + Assert(ppValue); - *pqwValue = pVariant->qwValue; + *ppValue = pVariant->pValue; } diff --git a/src/engine/variant.h b/src/engine/variant.h index 35463479..23303e02 100644 --- a/src/engine/variant.h +++ b/src/engine/variant.h @@ -26,7 +26,7 @@ typedef struct _BURN_VARIANT union { LONGLONG llValue; - DWORD64 qwValue; + VERUTIL_VERSION* pValue; LPWSTR sczValue; }; BURN_VARIANT_TYPE Type; @@ -49,7 +49,7 @@ HRESULT BVariantGetString( ); HRESULT BVariantGetVersion( __in BURN_VARIANT* pVariant, - __out DWORD64* pqwValue + __out VERUTIL_VERSION** ppValue ); HRESULT BVariantSetNumeric( __in BURN_VARIANT* pVariant, @@ -63,7 +63,7 @@ HRESULT BVariantSetString( ); HRESULT BVariantSetVersion( __in BURN_VARIANT* pVariant, - __in DWORD64 qwValue + __in VERUTIL_VERSION* pValue ); /******************************************************************** BVariantSetValue - Convenience function that calls BVariantUninitialize, diff --git a/src/stub/packages.config b/src/stub/packages.config index 6f98d413..3ece6694 100644 --- a/src/stub/packages.config +++ b/src/stub/packages.config @@ -4,5 +4,5 @@ - + \ No newline at end of file diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index c0d79f49..d3c1278c 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -5,7 +5,7 @@ - + @@ -107,6 +107,6 @@ - + \ No newline at end of file diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index fda7cb7b..cc19fa60 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -4,7 +4,7 @@ - + Debug @@ -80,6 +80,6 @@ - + diff --git a/src/test/BurnUnitTest/SearchTest.cpp b/src/test/BurnUnitTest/SearchTest.cpp index 32107d87..d868190d 100644 --- a/src/test/BurnUnitTest/SearchTest.cpp +++ b/src/test/BurnUnitTest/SearchTest.cpp @@ -106,6 +106,7 @@ namespace Bootstrapper BURN_SEARCHES searches = { }; BURN_EXTENSIONS burnExtensions = { }; ULARGE_INTEGER uliVersion = { }; + VERUTIL_VERSION* pVersion = NULL; try { hr = VariableInitialize(&variables); @@ -117,6 +118,9 @@ namespace Bootstrapper hr = FileVersion(wzFile2, &uliVersion.HighPart, &uliVersion.LowPart); TestThrowOnFailure(hr, L"Failed to get DLL version."); + hr = VerVersionFromQword(uliVersion.QuadPart, &pVersion); + NativeAssert::Succeeded(hr, "Failed to create version."); + VariableSetStringHelper(&variables, L"File1", wzFile1, FALSE); VariableSetStringHelper(&variables, L"File2", wzFile2, FALSE); @@ -140,10 +144,11 @@ namespace Bootstrapper // check variable values Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable1")); Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable2")); - Assert::Equal(uliVersion.QuadPart, VariableGetVersionHelper(&variables, L"Variable3")); + Assert::Equal(gcnew String(pVersion->sczVersion), VariableGetVersionHelper(&variables, L"Variable3")); } finally { + ReleaseVerutilVersion(pVersion); ReleaseObject(pixeBundle); VariablesUninitialize(&variables); SearchesUninitialize(&searches); @@ -247,8 +252,8 @@ namespace Bootstrapper Assert::NotEqual(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable10")); Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable11")); Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable12")); - Assert::Equal(MAKEQWORDVERSION(1,1,1,1), VariableGetVersionHelper(&variables, L"Variable13")); - Assert::Equal(MAKEQWORDVERSION(1,1,1,1), VariableGetVersionHelper(&variables, L"Variable14")); + Assert::Equal(gcnew String(L"1.1.1.1"), VariableGetVersionHelper(&variables, L"Variable13")); + Assert::Equal(gcnew String(L"1.1.1.1"), VariableGetVersionHelper(&variables, L"Variable14")); Assert::Equal(gcnew String(L"String1"), VariableGetStringHelper(&variables, L"Variable15")); Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable16")); Assert::False(VariableExistsHelper(&variables, L"Variable17")); @@ -401,11 +406,11 @@ namespace Bootstrapper // check variable values Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable1")); - Assert::Equal(MAKEQWORDVERSION(1,0,0,0), VariableGetVersionHelper(&variables, L"Variable2")); + Assert::Equal(gcnew String(L"1.0.0.0"), VariableGetVersionHelper(&variables, L"Variable2")); Assert::Equal(1033ll, VariableGetNumericHelper(&variables, L"Variable3")); Assert::Equal(5ll, VariableGetNumericHelper(&variables, L"Variable4")); Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable5")); - Assert::Equal(MAKEQWORDVERSION(1,0,0,0), VariableGetVersionHelper(&variables, L"Variable6")); + Assert::Equal(gcnew String(L"1.0.0.0"), VariableGetVersionHelper(&variables, L"Variable6")); } finally { @@ -581,7 +586,7 @@ namespace Bootstrapper Assert::Equal(gcnew String(L"VAL5"), VariableGetStringHelper(&variables, L"PROP5")); Assert::Equal(gcnew String(L"VAL6"), VariableGetStringHelper(&variables, L"PROP6")); Assert::Equal(7ll, VariableGetNumericHelper(&variables, L"PROP7")); - Assert::Equal(MAKEQWORDVERSION(1, 1, 0, 0), VariableGetVersionHelper(&variables, L"PROP8")); + Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetVersionHelper(&variables, L"PROP8")); Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetStringHelper(&variables, L"PROP8")); Assert::Equal(gcnew String(L"[VAL9]"), VariableGetStringHelper(&variables, L"PROP9")); diff --git a/src/test/BurnUnitTest/VariableHelpers.cpp b/src/test/BurnUnitTest/VariableHelpers.cpp index fdfb9191..99ba492a 100644 --- a/src/test/BurnUnitTest/VariableHelpers.cpp +++ b/src/test/BurnUnitTest/VariableHelpers.cpp @@ -33,12 +33,23 @@ namespace Bootstrapper TestThrowOnFailure2(hr, L"Failed to set %s to: %I64d", wzVariable, llValue); } - void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, DWORD64 qwValue) + void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue) { HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion = NULL; - hr = VariableSetVersion(pVariables, wzVariable, qwValue, FALSE); - TestThrowOnFailure2(hr, L"Failed to set %s to: 0x%016I64x", wzVariable, qwValue); + try + { + hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); + TestThrowOnFailure1(hr, L"Failed to parse version '%ls'", wzValue); + + hr = VariableSetVersion(pVariables, wzVariable, pVersion, FALSE); + TestThrowOnFailure2(hr, L"Failed to set %s to: '%ls'", wzVariable, wzValue); + } + finally + { + ReleaseVerutilVersion(pVersion); + } } String^ VariableGetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) @@ -69,15 +80,22 @@ namespace Bootstrapper return llValue; } - unsigned __int64 VariableGetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) + String^ VariableGetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) { HRESULT hr = S_OK; - DWORD64 qwValue = 0; + VERUTIL_VERSION* pValue = NULL; - hr = VariableGetVersion(pVariables, wzVariable, &qwValue); - TestThrowOnFailure1(hr, L"Failed to get: %s", wzVariable); + try + { + hr = VariableGetVersion(pVariables, wzVariable, &pValue); + TestThrowOnFailure1(hr, L"Failed to get: %s", wzVariable); - return qwValue; + return gcnew String(pValue->sczVersion); + } + finally + { + ReleaseVerutilVersion(pValue); + } } String^ VariableGetFormattedHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) diff --git a/src/test/BurnUnitTest/VariableHelpers.h b/src/test/BurnUnitTest/VariableHelpers.h index 8c2b081a..96122219 100644 --- a/src/test/BurnUnitTest/VariableHelpers.h +++ b/src/test/BurnUnitTest/VariableHelpers.h @@ -16,10 +16,10 @@ namespace Bootstrapper void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue, BOOL fFormatted); void VariableSetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LONGLONG llValue); -void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, DWORD64 qwValue); +void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue); System::String^ VariableGetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); __int64 VariableGetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); -unsigned __int64 VariableGetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); +System::String^ VariableGetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); System::String^ VariableGetFormattedHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); System::String^ VariableFormatStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzIn); System::String^ VariableEscapeStringHelper(LPCWSTR wzIn); diff --git a/src/test/BurnUnitTest/VariableTest.cpp b/src/test/BurnUnitTest/VariableTest.cpp index 0b49a530..405c8fab 100644 --- a/src/test/BurnUnitTest/VariableTest.cpp +++ b/src/test/BurnUnitTest/VariableTest.cpp @@ -42,7 +42,7 @@ namespace Bootstrapper VariableSetStringHelper(&variables, L"PROP4", L"VAL4", FALSE); VariableSetStringHelper(&variables, L"PROP6", L"VAL6", FALSE); VariableSetStringHelper(&variables, L"PROP7", L"7", FALSE); - VariableSetVersionHelper(&variables, L"PROP8", MAKEQWORDVERSION(1,1,0,0)); + VariableSetVersionHelper(&variables, L"PROP8", L"1.1.0.0"); VariableSetStringHelper(&variables, L"PROP9", L"[VAL9]", TRUE); // set overwritten variables @@ -61,7 +61,7 @@ namespace Bootstrapper Assert::Equal(gcnew String(L"VAL5"), VariableGetStringHelper(&variables, L"PROP5")); Assert::Equal(gcnew String(L"VAL6"), VariableGetStringHelper(&variables, L"PROP6")); Assert::Equal(7ll, VariableGetNumericHelper(&variables, L"PROP7")); - Assert::Equal(MAKEQWORDVERSION(1,1,0,0), VariableGetVersionHelper(&variables, L"PROP8")); + Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetVersionHelper(&variables, L"PROP8")); Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetStringHelper(&variables, L"PROP8")); Assert::Equal(gcnew String(L"[VAL9]"), VariableGetStringHelper(&variables, L"PROP9")); @@ -110,7 +110,7 @@ namespace Bootstrapper Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Var1")); Assert::Equal(gcnew String(L"String value."), VariableGetStringHelper(&variables, L"Var2")); - Assert::Equal(MAKEQWORDVERSION(1,2,3,4), VariableGetVersionHelper(&variables, L"Var3")); + Assert::Equal(gcnew String(L"1.2.3.4"), VariableGetVersionHelper(&variables, L"Var3")); Assert::Equal(gcnew String(L"[Formatted]"), VariableGetStringHelper(&variables, L"Var6")); } finally @@ -214,13 +214,13 @@ namespace Bootstrapper VariableSetNumericHelper(&variables, L"PROP13", 0x00010000); VariableSetNumericHelper(&variables, L"PROP14", 0x00000001); VariableSetNumericHelper(&variables, L"PROP15", 0x00010001); - VariableSetVersionHelper(&variables, L"PROP16", MAKEQWORDVERSION(0,0,0,0)); - VariableSetVersionHelper(&variables, L"PROP17", MAKEQWORDVERSION(1,0,0,0)); - VariableSetVersionHelper(&variables, L"PROP18", MAKEQWORDVERSION(1,1,0,0)); - VariableSetVersionHelper(&variables, L"PROP19", MAKEQWORDVERSION(1,1,1,0)); - VariableSetVersionHelper(&variables, L"PROP20", MAKEQWORDVERSION(1,1,1,1)); + VariableSetVersionHelper(&variables, L"PROP16", L"0.0.0.0"); + VariableSetVersionHelper(&variables, L"PROP17", L"1.0.0.0"); + VariableSetVersionHelper(&variables, L"PROP18", L"1.1.0.0"); + VariableSetVersionHelper(&variables, L"PROP19", L"1.1.1.0"); + VariableSetVersionHelper(&variables, L"PROP20", L"1.1.1.1"); VariableSetNumericHelper(&variables, L"vPROP21", 1); - VariableSetVersionHelper(&variables, L"PROP22", MAKEQWORDVERSION(65535,65535,65535,65535)); + VariableSetVersionHelper(&variables, L"PROP22", L"65535.65535.65535.65535"); VariableSetStringHelper(&variables, L"PROP23", L"1.1.1", FALSE); VariableSetStringHelper(&variables, L"PROP24", L"[PROP1]", TRUE); VariableSetStringHelper(&variables, L"PROP25", L"[PROP7]", TRUE); @@ -233,7 +233,7 @@ namespace Bootstrapper Assert::False(EvaluateConditionHelper(&variables, L"PROP7")); Assert::False(EvaluateConditionHelper(&variables, L"PROP8")); Assert::True(EvaluateConditionHelper(&variables, L"_PROP9")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP16")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP16")); Assert::True(EvaluateConditionHelper(&variables, L"PROP17")); Assert::True(EvaluateConditionHelper(&variables, L"PROP24")); Assert::True(EvaluateConditionHelper(&variables, L"PROP25")); @@ -268,8 +268,8 @@ namespace Bootstrapper Assert::True(EvaluateConditionHelper(&variables, L"PROP18 = v1.1")); Assert::True(EvaluateConditionHelper(&variables, L"PROP19 = v1.1.1")); Assert::True(EvaluateConditionHelper(&variables, L"PROP20 = v1.1.1.1")); - Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP20 = v1.1.1.1.0")); - Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP20 = v1.1.1.1.1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP20 > v1.1.1.1.0")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP20 > v1.1.1.1.1")); Assert::True(EvaluateConditionHelper(&variables, L"vPROP21 = 1")); Assert::True(EvaluateConditionHelper(&variables, L"PROP23 = v1.1.1")); Assert::True(EvaluateConditionHelper(&variables, L"v1.1.1 = PROP23")); @@ -287,8 +287,8 @@ namespace Bootstrapper Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP12 = -92233720368547758080000")); Assert::True(EvaluateConditionHelper(&variables, L"PROP22 = v65535.65535.65535.65535")); - Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP22 = v65536.65535.65535.65535")); - Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP22 = v65535.655350000.65535.65535")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP22 < v65536.65535.65535.65535")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP22 < v65535.655350000.65535.65535")); Assert::True(EvaluateConditionHelper(&variables, L"PROP5 < 6")); Assert::False(EvaluateConditionHelper(&variables, L"PROP5 < 5")); @@ -388,7 +388,7 @@ namespace Bootstrapper VariableSetStringHelper(&variables1, L"PROP1", L"VAL1", FALSE); VariableSetNumericHelper(&variables1, L"PROP2", 2); - VariableSetVersionHelper(&variables1, L"PROP3", MAKEQWORDVERSION(1,1,1,1)); + VariableSetVersionHelper(&variables1, L"PROP3", L"1.1.1.1"); VariableSetStringHelper(&variables1, L"PROP4", L"VAL4", FALSE); VariableSetStringHelper(&variables1, L"PROP5", L"[PROP1]", TRUE); @@ -404,7 +404,7 @@ namespace Bootstrapper Assert::Equal(gcnew String(L"VAL1"), VariableGetStringHelper(&variables2, L"PROP1")); Assert::Equal(2ll, VariableGetNumericHelper(&variables2, L"PROP2")); - Assert::Equal(MAKEQWORDVERSION(1,1,1,1), VariableGetVersionHelper(&variables2, L"PROP3")); + Assert::Equal(gcnew String(L"1.1.1.1"), VariableGetVersionHelper(&variables2, L"PROP3")); Assert::Equal(gcnew String(L"VAL4"), VariableGetStringHelper(&variables2, L"PROP4")); Assert::Equal(gcnew String(L"[PROP1]"), VariableGetStringHelper(&variables2, L"PROP5")); diff --git a/src/test/BurnUnitTest/VariantTest.cpp b/src/test/BurnUnitTest/VariantTest.cpp index c982db72..34328f53 100644 --- a/src/test/BurnUnitTest/VariantTest.cpp +++ b/src/test/BurnUnitTest/VariantTest.cpp @@ -37,10 +37,10 @@ namespace Bootstrapper { InitNumericValue(expectedVariants + 0, 2, FALSE, L"PROP1", actualVariants + 0); InitStringValue(expectedVariants + 1, L"VAL2", FALSE, L"PROP2", actualVariants + 1); - InitVersionValue(expectedVariants + 2, MAKEQWORDVERSION(1, 1, 0, 0), FALSE, L"PROP3", actualVariants + 2); + InitVersionValue(expectedVariants + 2, L"1.1.0.0", FALSE, L"PROP3", actualVariants + 2); InitNoneValue(expectedVariants + 3, FALSE, L"PROP4", actualVariants + 3); InitNoneValue(expectedVariants + 4, TRUE, L"PROP5", actualVariants + 4); - InitVersionValue(expectedVariants + 5, MAKEQWORDVERSION(1, 1, 1, 0), TRUE, L"PROP6", actualVariants + 5); + InitVersionValue(expectedVariants + 5, L"1.1.1.0", TRUE, L"PROP6", actualVariants + 5); InitStringValue(expectedVariants + 6, L"7", TRUE, L"PROP7", actualVariants + 6); InitNumericValue(expectedVariants + 7, 11, TRUE, L"PROP8", actualVariants + 7); InitFormattedValue(expectedVariants + 8, L"VAL9", FALSE, L"PROP9", actualVariants + 8); @@ -143,21 +143,34 @@ namespace Bootstrapper } } - void InitVersionValue(BURN_VARIANT* pValue, DWORD64 qwValue, BOOL fHidden, LPCWSTR wz, BURN_VARIANT* pActualValue) + void InitVersionValue(BURN_VARIANT* pValue, LPCWSTR wzValue, BOOL fHidden, LPCWSTR wz, BURN_VARIANT* pActualValue) { HRESULT hr = S_OK; - pValue->Type = BURN_VARIANT_TYPE_VERSION; - pValue->qwValue = qwValue; + VERUTIL_VERSION* pVersion = NULL; - hr = BVariantCopy(pValue, pActualValue); - NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); - - if (fHidden) + try { - hr = BVariantSetEncryption(pActualValue, TRUE); - NativeAssert::Succeeded(hr, "Failed to encrypt variant {0}", wz); + hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); + NativeAssert::Succeeded(hr, "Failed to parse version {0}", wzValue); - NativeAssert::True(pActualValue->fEncryptString); + pValue->Type = BURN_VARIANT_TYPE_VERSION; + pValue->pValue = pVersion; + pVersion = NULL; + + hr = BVariantCopy(pValue, pActualValue); + NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); + + if (fHidden) + { + hr = BVariantSetEncryption(pActualValue, TRUE); + NativeAssert::Succeeded(hr, "Failed to encrypt variant {0}", wz); + + NativeAssert::True(pActualValue->fEncryptString); + } + } + finally + { + ReleaseVerutilVersion(pVersion); } } @@ -224,14 +237,21 @@ namespace Bootstrapper void VerifyVersionValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) { HRESULT hr = S_OK; - DWORD64 qwValue = 0; + VERUTIL_VERSION* pValue = NULL; NativeAssert::Equal(BURN_VARIANT_TYPE_VERSION, pExpectedValue->Type); NativeAssert::Equal(BURN_VARIANT_TYPE_VERSION, pActualValue->Type); - hr = BVariantGetVersion(pActualValue, &qwValue); - NativeAssert::Succeeded(hr, "Failed to get version value"); + try + { + hr = BVariantGetVersion(pActualValue, &pValue); + NativeAssert::Succeeded(hr, "Failed to get version value"); - NativeAssert::Equal(pExpectedValue->qwValue, qwValue); + NativeAssert::StringEqual(pExpectedValue->pValue->sczVersion, pActualValue->pValue->sczVersion); + } + finally + { + ReleaseVerutilVersion(pValue); + } } }; } diff --git a/src/test/BurnUnitTest/packages.config b/src/test/BurnUnitTest/packages.config index e537bcdb..74f2523f 100644 --- a/src/test/BurnUnitTest/packages.config +++ b/src/test/BurnUnitTest/packages.config @@ -9,5 +9,5 @@ - + \ No newline at end of file diff --git a/src/test/BurnUnitTest/precomp.h b/src/test/BurnUnitTest/precomp.h index e288eb3e..fea30156 100644 --- a/src/test/BurnUnitTest/precomp.h +++ b/src/test/BurnUnitTest/precomp.h @@ -13,6 +13,7 @@ #include "wininet.h" #include +#include #include #include #include -- cgit v1.2.3-55-g6feb From bafc4f682a798eb375d32c1f4777664aceb1e15f Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 18 Oct 2020 14:05:51 -0500 Subject: Update string versioning. Update condition parsing to allow specific characters for versions. Log every time an invalid version is parsed. --- src/engine/condition.cpp | 125 +++++++++++++++++++-------------- src/engine/engine.mc | 42 +++++++++++ src/engine/msiengine.cpp | 35 +++++++++ src/engine/registration.cpp | 5 ++ src/engine/relatedbundle.cpp | 5 ++ src/engine/variable.cpp | 12 +++- src/engine/variant.cpp | 46 +++++++++++- src/engine/variant.h | 10 +++ src/test/BurnUnitTest/VariableTest.cpp | 1 + 9 files changed, 225 insertions(+), 56 deletions(-) diff --git a/src/engine/condition.cpp b/src/engine/condition.cpp index 32a7a0b8..224eb0da 100644 --- a/src/engine/condition.cpp +++ b/src/engine/condition.cpp @@ -73,6 +73,12 @@ struct BURN_CONDITION_PARSE_CONTEXT BOOL fError; }; +struct BURN_CONDITION_OPERAND +{ + BOOL fHidden; + BURN_VARIANT Value; +}; + // internal function declarations @@ -92,9 +98,9 @@ static HRESULT ParseTerm( __in BURN_CONDITION_PARSE_CONTEXT* pContext, __out BOOL* pf ); -static HRESULT ParseValue( +static HRESULT ParseOperand( __in BURN_CONDITION_PARSE_CONTEXT* pContext, - __out BURN_VARIANT* pValue + __out BURN_CONDITION_OPERAND* pOperand ); static HRESULT Expect( __in BURN_CONDITION_PARSE_CONTEXT* pContext, @@ -103,10 +109,10 @@ static HRESULT Expect( static HRESULT NextSymbol( __in BURN_CONDITION_PARSE_CONTEXT* pContext ); -static HRESULT CompareValues( +static HRESULT CompareOperands( __in BURN_SYMBOL_TYPE comparison, - __in BURN_VARIANT leftOperand, - __in BURN_VARIANT rightOperand, + __in BURN_CONDITION_OPERAND* pLeftOperand, + __in BURN_CONDITION_OPERAND* pRightOperand, __out BOOL* pfResult ); static HRESULT CompareStringValues( @@ -342,8 +348,8 @@ static HRESULT ParseTerm( ) { HRESULT hr = S_OK; - BURN_VARIANT firstValue = { }; - BURN_VARIANT secondValue = { }; + BURN_CONDITION_OPERAND firstOperand = { }; + BURN_CONDITION_OPERAND secondOperand = { }; if (BURN_SYMBOL_TYPE_LPAREN == pContext->NextSymbol.Type) { @@ -359,8 +365,8 @@ static HRESULT ParseTerm( ExitFunction1(hr = S_OK); } - hr = ParseValue(pContext, &firstValue); - ExitOnFailure(hr, "Failed to parse value."); + hr = ParseOperand(pContext, &firstOperand); + ExitOnFailure(hr, "Failed to parse operand."); if (COMPARISON & pContext->NextSymbol.Type) { @@ -369,24 +375,24 @@ static HRESULT ParseTerm( hr = NextSymbol(pContext); ExitOnFailure(hr, "Failed to read next symbol."); - hr = ParseValue(pContext, &secondValue); - ExitOnFailure(hr, "Failed to parse value."); + hr = ParseOperand(pContext, &secondOperand); + ExitOnFailure(hr, "Failed to parse operand."); - hr = CompareValues(comparison, firstValue, secondValue, pf); - ExitOnFailure(hr, "Failed to compare value."); + hr = CompareOperands(comparison, &firstOperand, &secondOperand, pf); + ExitOnFailure(hr, "Failed to compare operands."); } else { LONGLONG llValue = 0; LPWSTR sczValue = NULL; VERUTIL_VERSION* pVersion = NULL; - switch (firstValue.Type) + switch (firstOperand.Value.Type) { case BURN_VARIANT_TYPE_NONE: *pf = FALSE; break; case BURN_VARIANT_TYPE_STRING: - hr = BVariantGetString(&firstValue, &sczValue); + hr = BVariantGetString(&firstOperand.Value, &sczValue); if (SUCCEEDED(hr)) { *pf = sczValue && *sczValue; @@ -394,7 +400,7 @@ static HRESULT ParseTerm( StrSecureZeroFreeString(sczValue); break; case BURN_VARIANT_TYPE_NUMERIC: - hr = BVariantGetNumeric(&firstValue, &llValue); + hr = BVariantGetNumeric(&firstOperand.Value, &llValue); if (SUCCEEDED(hr)) { *pf = 0 != llValue; @@ -402,7 +408,7 @@ static HRESULT ParseTerm( SecureZeroMemory(&llValue, sizeof(llValue)); break; case BURN_VARIANT_TYPE_VERSION: - hr = BVariantGetVersion(&firstValue, &pVersion); + hr = BVariantGetVersionHidden(&firstOperand.Value, firstOperand.fHidden, &pVersion); if (SUCCEEDED(hr)) { *pf = 0 != *pVersion->sczVersion; @@ -415,14 +421,14 @@ static HRESULT ParseTerm( } LExit: - BVariantUninitialize(&firstValue); - BVariantUninitialize(&secondValue); + BVariantUninitialize(&firstOperand.Value); + BVariantUninitialize(&secondOperand.Value); return hr; } -static HRESULT ParseValue( +static HRESULT ParseOperand( __in BURN_CONDITION_PARSE_CONTEXT* pContext, - __out BURN_VARIANT* pValue + __out BURN_CONDITION_OPERAND* pOperand ) { HRESULT hr = S_OK; @@ -434,16 +440,19 @@ static HRESULT ParseValue( Assert(BURN_VARIANT_TYPE_STRING == pContext->NextSymbol.Value.Type); // find variable - hr = VariableGetVariant(pContext->pVariables, pContext->NextSymbol.Value.sczValue, pValue); + hr = VariableGetVariant(pContext->pVariables, pContext->NextSymbol.Value.sczValue, &pOperand->Value); if (E_NOTFOUND != hr) { ExitOnRootFailure(hr, "Failed to find variable."); + + hr = VariableIsHidden(pContext->pVariables, pContext->NextSymbol.Value.sczValue, &pOperand->fHidden); + ExitOnRootFailure(hr, "Failed to get if variable is hidden."); } - if (BURN_VARIANT_TYPE_FORMATTED == pValue->Type) + if (BURN_VARIANT_TYPE_FORMATTED == pOperand->Value.Type) { // TODO: actually format the value? - hr = BVariantChangeType(pValue, BURN_VARIANT_TYPE_STRING); + hr = BVariantChangeType(&pOperand->Value, BURN_VARIANT_TYPE_STRING); ExitOnRootFailure(hr, "Failed to change variable '%ls' type for condition '%ls'", pContext->NextSymbol.Value.sczValue, pContext->wzCondition); } break; @@ -451,8 +460,9 @@ static HRESULT ParseValue( case BURN_SYMBOL_TYPE_NUMBER: __fallthrough; case BURN_SYMBOL_TYPE_LITERAL: __fallthrough; case BURN_SYMBOL_TYPE_VERSION: + pOperand->fHidden = FALSE; // steal value of symbol - memcpy_s(pValue, sizeof(BURN_VARIANT), &pContext->NextSymbol.Value, sizeof(BURN_VARIANT)); + memcpy_s(&pOperand->Value, sizeof(BURN_VARIANT), &pContext->NextSymbol.Value, sizeof(BURN_VARIANT)); memset(&pContext->NextSymbol.Value, 0, sizeof(BURN_VARIANT)); break; @@ -692,8 +702,13 @@ static HRESULT NextSymbol( do { ++n; - ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType); - } while (L'\0' != pContext->wzRead[n] && C1_BLANK != (C1_BLANK & charType)); + } while (pContext->wzRead[n] >= L'0' && pContext->wzRead[n] <= L'9' || + pContext->wzRead[n] >= L'A' && pContext->wzRead[n] <= L'Z' || + pContext->wzRead[n] >= L'a' && pContext->wzRead[n] <= L'z' || + pContext->wzRead[n] == L'_' || + pContext->wzRead[n] == L'+' || + pContext->wzRead[n] == L'-' || + pContext->wzRead[n] == L'.'); // Symbols don't encrypt their value, so can access the value directly. hr = VerParseVersion(&pContext->wzRead[1], n - 1, FALSE, &pContext->NextSymbol.Value.pValue); @@ -703,6 +718,10 @@ static HRESULT NextSymbol( hr = E_INVALIDDATA; ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Invalid version format, at position %d.", pContext->wzCondition, iPosition); } + else if (pContext->NextSymbol.Value.pValue->fInvalid) + { + LogId(REPORT_WARNING, MSG_CONDITION_INVALID_VERSION, pContext->wzCondition, pContext->NextSymbol.Value.pValue->sczVersion); + } pContext->NextSymbol.Value.Type = BURN_VARIANT_TYPE_VERSION; pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_VERSION; @@ -755,12 +774,12 @@ LExit: } // -// CompareValues - compares two variant values using a given comparison. +// CompareOperands - compares two variant values using a given comparison. // -static HRESULT CompareValues( +static HRESULT CompareOperands( __in BURN_SYMBOL_TYPE comparison, - __in BURN_VARIANT leftOperand, - __in BURN_VARIANT rightOperand, + __in BURN_CONDITION_OPERAND* pLeftOperand, + __in BURN_CONDITION_OPERAND* pRightOperand, __out BOOL* pfResult ) { @@ -771,37 +790,39 @@ static HRESULT CompareValues( LONGLONG llRight = 0; VERUTIL_VERSION* pVersionRight = 0; LPWSTR sczRight = NULL; + BURN_VARIANT* pLeftValue = &pLeftOperand->Value; + BURN_VARIANT* pRightValue = &pRightOperand->Value; // get values to compare based on type - if (BURN_VARIANT_TYPE_STRING == leftOperand.Type && BURN_VARIANT_TYPE_STRING == rightOperand.Type) + if (BURN_VARIANT_TYPE_STRING == pLeftValue->Type && BURN_VARIANT_TYPE_STRING == pRightValue->Type) { - hr = BVariantGetString(&leftOperand, &sczLeft); + hr = BVariantGetString(pLeftValue, &sczLeft); ExitOnFailure(hr, "Failed to get the left string"); - hr = BVariantGetString(&rightOperand, &sczRight); + hr = BVariantGetString(pRightValue, &sczRight); ExitOnFailure(hr, "Failed to get the right string"); hr = CompareStringValues(comparison, sczLeft, sczRight, pfResult); } - else if (BURN_VARIANT_TYPE_NUMERIC == leftOperand.Type && BURN_VARIANT_TYPE_NUMERIC == rightOperand.Type) + else if (BURN_VARIANT_TYPE_NUMERIC == pLeftValue->Type && BURN_VARIANT_TYPE_NUMERIC == pRightValue->Type) { - hr = BVariantGetNumeric(&leftOperand, &llLeft); + hr = BVariantGetNumeric(pLeftValue, &llLeft); ExitOnFailure(hr, "Failed to get the left numeric"); - hr = BVariantGetNumeric(&rightOperand, &llRight); + hr = BVariantGetNumeric(pRightValue, &llRight); ExitOnFailure(hr, "Failed to get the right numeric"); hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult); } - else if (BURN_VARIANT_TYPE_VERSION == leftOperand.Type && BURN_VARIANT_TYPE_VERSION == rightOperand.Type) + else if (BURN_VARIANT_TYPE_VERSION == pLeftValue->Type && BURN_VARIANT_TYPE_VERSION == pRightValue->Type) { - hr = BVariantGetVersion(&leftOperand, &pVersionLeft); + hr = BVariantGetVersionHidden(pLeftValue, pLeftOperand->fHidden, &pVersionLeft); ExitOnFailure(hr, "Failed to get the left version"); - hr = BVariantGetVersion(&rightOperand, &pVersionRight); + hr = BVariantGetVersionHidden(pRightValue, pRightOperand->fHidden, &pVersionRight); ExitOnFailure(hr, "Failed to get the right version"); hr = CompareVersionValues(comparison, pVersionLeft, pVersionRight, pfResult); } - else if (BURN_VARIANT_TYPE_VERSION == leftOperand.Type && BURN_VARIANT_TYPE_STRING == rightOperand.Type) + else if (BURN_VARIANT_TYPE_VERSION == pLeftValue->Type && BURN_VARIANT_TYPE_STRING == pRightValue->Type) { - hr = BVariantGetVersion(&leftOperand, &pVersionLeft); + hr = BVariantGetVersionHidden(pLeftValue, pLeftOperand->fHidden, &pVersionLeft); ExitOnFailure(hr, "Failed to get the left version"); - hr = BVariantGetVersion(&rightOperand, &pVersionRight); + hr = BVariantGetVersionHidden(pRightValue, pRightOperand->fHidden, &pVersionRight); if (FAILED(hr)) { if (DISP_E_TYPEMISMATCH != hr) @@ -816,11 +837,11 @@ static HRESULT CompareValues( hr = CompareVersionValues(comparison, pVersionLeft, pVersionRight, pfResult); } } - else if (BURN_VARIANT_TYPE_STRING == leftOperand.Type && BURN_VARIANT_TYPE_VERSION == rightOperand.Type) + else if (BURN_VARIANT_TYPE_STRING == pLeftValue->Type && BURN_VARIANT_TYPE_VERSION == pRightValue->Type) { - hr = BVariantGetVersion(&rightOperand, &pVersionRight); + hr = BVariantGetVersionHidden(pRightValue, pRightOperand->fHidden, &pVersionRight); ExitOnFailure(hr, "Failed to get the right version"); - hr = BVariantGetVersion(&leftOperand, &pVersionLeft); + hr = BVariantGetVersionHidden(pLeftValue, pLeftOperand->fHidden, &pVersionLeft); if (FAILED(hr)) { if (DISP_E_TYPEMISMATCH != hr) @@ -835,11 +856,11 @@ static HRESULT CompareValues( hr = CompareVersionValues(comparison, pVersionLeft, pVersionRight, pfResult); } } - else if (BURN_VARIANT_TYPE_NUMERIC == leftOperand.Type && BURN_VARIANT_TYPE_STRING == rightOperand.Type) + else if (BURN_VARIANT_TYPE_NUMERIC == pLeftValue->Type && BURN_VARIANT_TYPE_STRING == pRightValue->Type) { - hr = BVariantGetNumeric(&leftOperand, &llLeft); + hr = BVariantGetNumeric(pLeftValue, &llLeft); ExitOnFailure(hr, "Failed to get the left numeric"); - hr = BVariantGetNumeric(&rightOperand, &llRight); + hr = BVariantGetNumeric(pRightValue, &llRight); if (FAILED(hr)) { if (DISP_E_TYPEMISMATCH != hr) @@ -854,11 +875,11 @@ static HRESULT CompareValues( hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult); } } - else if (BURN_VARIANT_TYPE_STRING == leftOperand.Type && BURN_VARIANT_TYPE_NUMERIC == rightOperand.Type) + else if (BURN_VARIANT_TYPE_STRING == pLeftValue->Type && BURN_VARIANT_TYPE_NUMERIC == pRightValue->Type) { - hr = BVariantGetNumeric(&rightOperand, &llRight); + hr = BVariantGetNumeric(pRightValue, &llRight); ExitOnFailure(hr, "Failed to get the right numeric"); - hr = BVariantGetNumeric(&leftOperand, &llLeft); + hr = BVariantGetNumeric(pLeftValue, &llLeft); if (FAILED(hr)) { if (DISP_E_TYPEMISMATCH != hr) diff --git a/src/engine/engine.mc b/src/engine/engine.mc index fb2dd6e9..c8cd6d37 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -114,6 +114,13 @@ Language=English Connected to elevated engine. . +MessageId=13 +Severity=Warning +SymbolicName=MSG_MANIFEST_INVALID_VERSION +Language=English +The manifest contains an invalid version string: '%1!ls!' +. + MessageId=51 Severity=Error SymbolicName=MSG_FAILED_PARSE_CONDITION @@ -156,6 +163,13 @@ Language=English Application canceled operation: %2!ls!, error: %1!ls! . +MessageId=57 +Severity=Warning +SymbolicName=MSG_CONDITION_INVALID_VERSION +Language=English +Condition '%1!ls!' contains invalid version string '%2!ls!'. +. + MessageId=100 Severity=Success SymbolicName=MSG_DETECT_BEGIN @@ -233,6 +247,20 @@ Language=English Could not calculate patch applicability for target product code: %1!ls!, context: %2!hs!, reason: 0x%3!x! . +MessageId=122 +Severity=Warning +SymbolicName=MSG_RELATED_PACKAGE_INVALID_VERSION +Language=English +Related package: '%1!ls!' has invalid version: %2!ls! +. + +MessageId=123 +Severity=Warning +SymbolicName=MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION +Language=English +Detected msi package with invalid version, product code: '%1!ls!', version: '%2!ls!' +. + MessageId=151 Severity=Error SymbolicName=MSG_FAILED_DETECT_PACKAGE @@ -836,6 +864,20 @@ Language=English Variable: %1!ls! . +MessageId=411 +Severity=Warning +SymbolicName=MSG_VARIABLE_INVALID_VERSION +Language=English +The variable '%1!ls!' is being set with an invalid version string. +. + +MessageId=412 +Severity=Warning +SymbolicName=MSG_INVALID_VERSION_COERSION +Language=English +The string '%1!ls!' could not be coerced to a valid version. +. + MessageId=420 Severity=Success SymbolicName=MSG_RESUME_AU_STARTING diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index e274df28..47211309 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -82,6 +82,11 @@ extern "C" HRESULT MsiEngineParsePackageFromXml( hr = VerParseVersion(scz, 0, FALSE, &pPackage->Msi.pVersion); ExitOnFailure(hr, "Failed to parse @Version: %ls", scz); + if (pPackage->Msi.pVersion->fInvalid) + { + LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz); + } + // @UpgradeCode hr = XmlGetAttributeEx(pixnMsiPackage, L"UpgradeCode", &pPackage->Msi.sczUpgradeCode); if (E_NOTFOUND != hr) @@ -420,6 +425,11 @@ extern "C" HRESULT MsiEngineDetectPackage( hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pPackage->Msi.pInstalledVersion); ExitOnFailure(hr, "Failed to parse installed version: '%ls' for ProductCode: %ls", sczInstalledVersion, pPackage->Msi.sczProductCode); + if (pPackage->Msi.pInstalledVersion->fInvalid) + { + LogId(REPORT_WARNING, MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION, pPackage->Msi.sczProductCode, sczInstalledVersion); + } + // compare versions hr = VerCompareParsedVersions(pPackage->Msi.pVersion, pPackage->Msi.pInstalledVersion, &nCompareResult); ExitOnFailure(hr, "Failed to compare version '%ls' to installed version: '%ls'", pPackage->Msi.pVersion->sczVersion, pPackage->Msi.pInstalledVersion->sczVersion); @@ -460,6 +470,11 @@ extern "C" HRESULT MsiEngineDetectPackage( hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pVersion); ExitOnFailure(hr, "Failed to parse dependency version: '%ls' for ProductCode: %ls", sczInstalledVersion, sczInstalledProductCode); + if (pVersion->fInvalid) + { + LogId(REPORT_WARNING, MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION, sczInstalledProductCode, sczInstalledVersion); + } + // compare versions hr = VerCompareParsedVersions(pPackage->Msi.pVersion, pVersion, &nCompareResult); ExitOnFailure(hr, "Failed to compare version '%ls' to dependency version: '%ls'", pPackage->Msi.pVersion->sczVersion, pVersion->sczVersion); @@ -536,6 +551,11 @@ extern "C" HRESULT MsiEngineDetectPackage( hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pVersion); ExitOnFailure(hr, "Failed to parse related installed version: '%ls' for ProductCode: %ls", sczInstalledVersion, wzProductCode); + if (pVersion->fInvalid) + { + LogId(REPORT_WARNING, MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION, wzProductCode, sczInstalledVersion); + } + // compare versions if (pRelatedMsi->fMinProvided) { @@ -1052,6 +1072,11 @@ extern "C" HRESULT MsiEngineAddCompatiblePackage( hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pCompatiblePackage->Msi.pVersion); ExitOnFailure(hr, "Failed to parse version: '%ls' for ProductCode: %ls", sczInstalledVersion, pCompatiblePackage->Msi.sczProductCode); + + if (pCompatiblePackage->Msi.pVersion->fInvalid) + { + LogId(REPORT_WARNING, MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION, pCompatiblePackage->Msi.sczProductCode, sczInstalledVersion); + } } // For now, copy enough information to support uninstalling the newer, compatible package. @@ -1506,6 +1531,11 @@ static HRESULT ParseRelatedMsiFromXml( hr = VerParseVersion(scz, 0, FALSE, &pRelatedMsi->pMinVersion); ExitOnFailure(hr, "Failed to parse @MinVersion: %ls", scz); + if (pRelatedMsi->pMinVersion->fInvalid) + { + LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz); + } + // flag that we have a min version pRelatedMsi->fMinProvided = TRUE; @@ -1523,6 +1553,11 @@ static HRESULT ParseRelatedMsiFromXml( hr = VerParseVersion(scz, 0, FALSE, &pRelatedMsi->pMaxVersion); ExitOnFailure(hr, "Failed to parse @MaxVersion: %ls", scz); + if (pRelatedMsi->pMaxVersion->fInvalid) + { + LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz); + } + // flag that we have a max version pRelatedMsi->fMaxProvided = TRUE; diff --git a/src/engine/registration.cpp b/src/engine/registration.cpp index 3c3dc95d..4e8c810d 100644 --- a/src/engine/registration.cpp +++ b/src/engine/registration.cpp @@ -136,6 +136,11 @@ extern "C" HRESULT RegistrationParseFromXml( hr = VerParseVersion(scz, 0, FALSE, &pRegistration->pVersion); ExitOnFailure(hr, "Failed to parse @Version: %ls", scz); + if (pRegistration->pVersion->fInvalid) + { + LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz); + } + // @ProviderKey hr = XmlGetAttributeEx(pixnRegistrationNode, L"ProviderKey", &pRegistration->sczProviderKey); ExitOnFailure(hr, "Failed to get @ProviderKey."); diff --git a/src/engine/relatedbundle.cpp b/src/engine/relatedbundle.cpp index e6d6516a..7b0da4a4 100644 --- a/src/engine/relatedbundle.cpp +++ b/src/engine/relatedbundle.cpp @@ -414,6 +414,11 @@ static HRESULT LoadRelatedBundleFromKey( hr = VerParseVersion(sczBundleVersion, 0, FALSE, &pRelatedBundle->pVersion); ExitOnFailure(hr, "Failed to parse pseudo bundle version: %ls", sczBundleVersion); + if (pRelatedBundle->pVersion->fInvalid) + { + LogId(REPORT_WARNING, MSG_RELATED_PACKAGE_INVALID_VERSION, wzRelatedBundleId, sczBundleVersion); + } + hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, &sczCachePath); ExitOnFailure(hr, "Failed to read cache path from registry for bundle: %ls", wzRelatedBundleId); diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp index 3069ccf7..c2192346 100644 --- a/src/engine/variable.cpp +++ b/src/engine/variable.cpp @@ -393,6 +393,11 @@ extern "C" HRESULT VariablesParseFromXml( hr = BVariantChangeType(&value, valueType); ExitOnFailure(hr, "Failed to change variant type."); + if (BURN_VARIANT_TYPE_VERSION == valueType && value.pValue->fInvalid) + { + LogId(REPORT_WARNING, MSG_VARIABLE_INVALID_VERSION, sczId); + } + // find existing variable hr = FindVariableIndexByName(pVariables, sczId, &iVariable); ExitOnFailure(hr, "Failed to find variable value '%ls'.", sczId); @@ -584,7 +589,7 @@ extern "C" HRESULT VariableGetVersion( } ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); - hr = BVariantGetVersion(&pVariable->Value, ppValue); + hr = BVariantGetVersionHidden(&pVariable->Value, pVariable->fHidden, ppValue); ExitOnFailure(hr, "Failed to get value as version for variable: %ls", wzVariable); LExit: @@ -1575,6 +1580,11 @@ static HRESULT SetVariableValue( break; } } + + if (BURN_VARIANT_TYPE_VERSION == pVariant->Type && pVariant->pValue->fInvalid) + { + LogId(REPORT_WARNING, MSG_VARIABLE_INVALID_VERSION, wzVariable); + } } // Update variable value. diff --git a/src/engine/variant.cpp b/src/engine/variant.cpp index 28578691..9fdb82cd 100644 --- a/src/engine/variant.cpp +++ b/src/engine/variant.cpp @@ -6,6 +6,12 @@ // internal function declarations +static HRESULT GetVersionInternal( + __in BURN_VARIANT* pVariant, + __in BOOL fHidden, + __in BOOL fSilent, + __out VERUTIL_VERSION** ppValue + ); static HRESULT BVariantEncryptString( __in BURN_VARIANT* pVariant, __in BOOL fEncrypt @@ -129,6 +135,36 @@ extern "C" HRESULT BVariantGetVersion( __in BURN_VARIANT* pVariant, __out VERUTIL_VERSION** ppValue ) +{ + return GetVersionInternal(pVariant, FALSE, FALSE, ppValue); +} + +// The contents of ppValue may be sensitive, should keep encrypted and SecureZeroMemory. +extern "C" HRESULT BVariantGetVersionHidden( + __in BURN_VARIANT* pVariant, + __in BOOL fHidden, + __out VERUTIL_VERSION** ppValue + ) +{ + return GetVersionInternal(pVariant, fHidden, FALSE, ppValue); +} + +// The contents of ppValue may be sensitive, should keep encrypted and SecureZeroMemory. +extern "C" HRESULT BVariantGetVersionSilent( + __in BURN_VARIANT* pVariant, + __in BOOL fSilent, + __out VERUTIL_VERSION** ppValue + ) +{ + return GetVersionInternal(pVariant, FALSE, fSilent, ppValue); +} + +static HRESULT GetVersionInternal( + __in BURN_VARIANT* pVariant, + __in BOOL fHidden, + __in BOOL fSilent, + __out VERUTIL_VERSION** ppValue + ) { HRESULT hr = S_OK; LONGLONG llValue = 0; @@ -152,6 +188,10 @@ extern "C" HRESULT BVariantGetVersion( { hr = DISP_E_TYPEMISMATCH; } + else if (!fSilent && (*ppValue)->fInvalid) + { + LogId(REPORT_WARNING, MSG_INVALID_VERSION_COERSION, fHidden ? L"*****" : sczValue); + } } StrSecureZeroFreeString(sczValue); break; @@ -282,7 +322,7 @@ extern "C" HRESULT BVariantSetValue( StrSecureZeroFreeString(sczValue); break; case BURN_VARIANT_TYPE_VERSION: - hr = BVariantGetVersion(pValue, &pVersionValue); + hr = BVariantGetVersionSilent(pValue, TRUE, &pVersionValue); if (SUCCEEDED(hr)) { hr = BVariantSetVersion(pVariant, pVersionValue); @@ -333,7 +373,7 @@ extern "C" HRESULT BVariantCopy( StrSecureZeroFreeString(sczValue); break; case BURN_VARIANT_TYPE_VERSION: - hr = BVariantGetVersion(pSource, &pVersionValue); + hr = BVariantGetVersionSilent(pSource, TRUE, &pVersionValue); if (SUCCEEDED(hr)) { hr = BVariantSetVersion(pTarget, pVersionValue); @@ -383,7 +423,7 @@ extern "C" HRESULT BVariantChangeType( hr = BVariantGetString(pVariant, &variant.sczValue); break; case BURN_VARIANT_TYPE_VERSION: - hr = BVariantGetVersion(pVariant, &variant.pValue); + hr = BVariantGetVersionSilent(pVariant, TRUE, &variant.pValue); break; default: ExitFunction1(hr = E_INVALIDARG); diff --git a/src/engine/variant.h b/src/engine/variant.h index 23303e02..34d7a187 100644 --- a/src/engine/variant.h +++ b/src/engine/variant.h @@ -51,6 +51,16 @@ HRESULT BVariantGetVersion( __in BURN_VARIANT* pVariant, __out VERUTIL_VERSION** ppValue ); +HRESULT BVariantGetVersionHidden( + __in BURN_VARIANT* pVariant, + __in BOOL fHidden, + __out VERUTIL_VERSION** ppValue + ); +HRESULT BVariantGetVersionSilent( + __in BURN_VARIANT* pVariant, + __in BOOL fSilent, + __out VERUTIL_VERSION** ppValue + ); HRESULT BVariantSetNumeric( __in BURN_VARIANT* pVariant, __in LONGLONG llValue diff --git a/src/test/BurnUnitTest/VariableTest.cpp b/src/test/BurnUnitTest/VariableTest.cpp index 405c8fab..f5511199 100644 --- a/src/test/BurnUnitTest/VariableTest.cpp +++ b/src/test/BurnUnitTest/VariableTest.cpp @@ -273,6 +273,7 @@ namespace Bootstrapper Assert::True(EvaluateConditionHelper(&variables, L"vPROP21 = 1")); Assert::True(EvaluateConditionHelper(&variables, L"PROP23 = v1.1.1")); Assert::True(EvaluateConditionHelper(&variables, L"v1.1.1 = PROP23")); + Assert::False(EvaluateConditionHelper(&variables, L"v1.1.1<>PROP23")); Assert::True(EvaluateConditionHelper(&variables, L"PROP1 <> v1.1.1")); Assert::True(EvaluateConditionHelper(&variables, L"v1.1.1 <> PROP1")); -- cgit v1.2.3-55-g6feb From e879388d96157db6a6e7b2ee79871ef7ebbd3015 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 18 Oct 2020 14:10:20 -0500 Subject: Add CompareVersions engine method. --- .../inc/BootstrapperEngine.h | 14 ++++++++++++++ .../inc/BundleExtension.h | 1 + .../inc/BundleExtensionEngine.h | 14 ++++++++++++++ src/engine/EngineForApplication.cpp | 19 +++++++++++++++++++ src/engine/EngineForExtension.cpp | 19 +++++++++++++++++++ src/engine/burnextension.cpp | 3 ++- src/engine/userexperience.cpp | 2 +- 7 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h index a6a87622..13229c29 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h @@ -124,6 +124,7 @@ enum BOOTSTRAPPER_ENGINE_MESSAGE BOOTSTRAPPER_ENGINE_MESSAGE_APPLY, BOOTSTRAPPER_ENGINE_MESSAGE_QUIT, BOOTSTRAPPER_ENGINE_MESSAGE_LAUNCHAPPROVEDEXE, + BOOTSTRAPPER_ENGINE_MESSAGE_COMPAREVERSIONS, }; typedef struct _BAENGINE_APPLY_ARGS @@ -147,6 +148,19 @@ typedef struct _BAENGINE_CLOSESPLASHSCREEN_RESULTS DWORD cbSize; } BAENGINE_CLOSESPLASHSCREEN_RESULTS; +typedef struct _BAENGINE_COMPAREVERSIONS_ARGS +{ + DWORD cbSize; + LPCWSTR wzVersion1; + LPCWSTR wzVersion2; +} BAENGINE_COMPAREVERSIONS_ARGS; + +typedef struct _BAENGINE_COMPAREVERSIONS_RESULTS +{ + DWORD cbSize; + int nResult; +} BAENGINE_COMPAREVERSIONS_RESULTS; + typedef struct _BAENGINE_DETECT_ARGS { DWORD cbSize; diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BundleExtension.h b/src/WixToolset.BootstrapperCore.Native/inc/BundleExtension.h index 5c7d1260..be76a1a5 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BundleExtension.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BundleExtension.h @@ -38,6 +38,7 @@ typedef struct _BUNDLE_EXTENSION_CREATE_ARGS LPVOID pvBundleExtensionEngineProcContext; LPCWSTR wzBootstrapperWorkingFolder; LPCWSTR wzBundleExtensionDataPath; + LPCWSTR wzExtensionId; } BUNDLE_EXTENSION_CREATE_ARGS; typedef struct _BUNDLE_EXTENSION_CREATE_RESULTS diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h b/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h index adcae1af..becb5be3 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h @@ -27,8 +27,22 @@ enum BUNDLE_EXTENSION_ENGINE_MESSAGE BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLENUMERIC, BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLESTRING, BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLEVERSION, + BUNDLE_EXTENSION_ENGINE_MESSAGE_COMPAREVERSIONS, }; +typedef struct _BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_ARGS +{ + DWORD cbSize; + LPCWSTR wzVersion1; + LPCWSTR wzVersion2; +} BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_ARGS; + +typedef struct _BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_RESULTS +{ + DWORD cbSize; + int nResult; +} BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_RESULTS; + typedef struct _BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_ARGS { DWORD cbSize; diff --git a/src/engine/EngineForApplication.cpp b/src/engine/EngineForApplication.cpp index d034c2bf..49ffe700 100644 --- a/src/engine/EngineForApplication.cpp +++ b/src/engine/EngineForApplication.cpp @@ -616,6 +616,22 @@ static HRESULT BAEngineCloseSplashScreen( return S_OK; } +static HRESULT BAEngineCompareVersions( + __in BOOTSTRAPPER_ENGINE_CONTEXT* /*pContext*/, + __in const BAENGINE_COMPAREVERSIONS_ARGS* pArgs, + __in BAENGINE_COMPAREVERSIONS_RESULTS* pResults + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzVersion1 = pArgs->wzVersion1; + LPCWSTR wzVersion2 = pArgs->wzVersion2; + int* pnResult = &pResults->nResult; + + hr = VerCompareStringVersions(wzVersion1, wzVersion2, FALSE, pnResult); + + return hr; +} + static HRESULT BAEngineDetect( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, __in BAENGINE_DETECT_ARGS* pArgs, @@ -861,6 +877,9 @@ HRESULT WINAPI EngineForApplicationProc( case BOOTSTRAPPER_ENGINE_MESSAGE_LAUNCHAPPROVEDEXE: hr = BAEngineLaunchApprovedExe(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); break; + case BOOTSTRAPPER_ENGINE_MESSAGE_COMPAREVERSIONS: + hr = BAEngineCompareVersions(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; default: hr = E_NOTIMPL; break; diff --git a/src/engine/EngineForExtension.cpp b/src/engine/EngineForExtension.cpp index 6ec80a0f..c165e2c2 100644 --- a/src/engine/EngineForExtension.cpp +++ b/src/engine/EngineForExtension.cpp @@ -291,6 +291,22 @@ LExit: return hr; } +static HRESULT BEEngineCompareVersions( + __in BURN_EXTENSION_ENGINE_CONTEXT* /*pContext*/, + __in const BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_ARGS* pArgs, + __in BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_RESULTS* pResults + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzVersion1 = pArgs->wzVersion1; + LPCWSTR wzVersion2 = pArgs->wzVersion2; + int* pnResult = &pResults->nResult; + + hr = VerCompareStringVersions(wzVersion1, wzVersion2, FALSE, pnResult); + + return hr; +} + HRESULT WINAPI EngineForExtensionProc( __in BUNDLE_EXTENSION_ENGINE_MESSAGE message, __in const LPVOID pvArgs, @@ -338,6 +354,9 @@ HRESULT WINAPI EngineForExtensionProc( case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLEVERSION: hr = BEEngineSetVariableVersion(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); break; + case BUNDLE_EXTENSION_ENGINE_MESSAGE_COMPAREVERSIONS: + hr = BEEngineCompareVersions(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; default: hr = E_NOTIMPL; break; diff --git a/src/engine/burnextension.cpp b/src/engine/burnextension.cpp index 59f84eca..157b082f 100644 --- a/src/engine/burnextension.cpp +++ b/src/engine/burnextension.cpp @@ -126,9 +126,10 @@ EXTERN_C HRESULT BurnExtensionLoad( args.cbSize = sizeof(BUNDLE_EXTENSION_CREATE_ARGS); args.pfnBundleExtensionEngineProc = EngineForExtensionProc; args.pvBundleExtensionEngineProcContext = pEngineContext; - args.qwEngineAPIVersion = MAKEQWORDVERSION(0, 0, 0, 1); // TODO: need to decide whether to keep this, and if so when to update it. + args.qwEngineAPIVersion = MAKEQWORDVERSION(2020, 8, 31, 0); args.wzBootstrapperWorkingFolder = pEngineContext->pEngineState->userExperience.sczTempDirectory; args.wzBundleExtensionDataPath = sczBundleExtensionDataPath; + args.wzExtensionId = pExtension->sczId; results.cbSize = sizeof(BUNDLE_EXTENSION_CREATE_RESULTS); diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index 6ab28cde..5f18cdbc 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -97,7 +97,7 @@ extern "C" HRESULT UserExperienceLoad( args.pCommand = pCommand; args.pfnBootstrapperEngineProc = EngineForApplicationProc; args.pvBootstrapperEngineProcContext = pEngineContext; - args.qwEngineAPIVersion = MAKEQWORDVERSION(2020, 5, 14, 0); + args.qwEngineAPIVersion = MAKEQWORDVERSION(2020, 8, 31, 0); results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS); -- cgit v1.2.3-55-g6feb From 60717700e175d38412559113c8a5388c954c7c91 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 18 Oct 2020 14:10:39 -0500 Subject: Get the internal Burn version to be Major.Minor.Patch.BuildNumber. --- src/engine/EngineForApplication.cpp | 2 +- src/engine/engine.vcxproj | 11 +++++++++-- src/engine/pseudobundle.cpp | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/engine/EngineForApplication.cpp b/src/engine/EngineForApplication.cpp index 49ffe700..c5eb5467 100644 --- a/src/engine/EngineForApplication.cpp +++ b/src/engine/EngineForApplication.cpp @@ -369,7 +369,7 @@ static HRESULT BAEngineSetUpdate( sczId = pContext->pEngineState->registration.sczId; } - hr = PseudoBundleInitialize(FILEMAKEVERSION(rmj, rmm, rup, 0), &pContext->pEngineState->update.package, FALSE, sczId, BOOTSTRAPPER_RELATION_UPDATE, BOOTSTRAPPER_PACKAGE_STATE_ABSENT, pContext->pEngineState->registration.sczExecutableName, sczLocalSource ? sczLocalSource : wzLocalSource, wzDownloadSource, qwSize, TRUE, sczCommandline, NULL, NULL, NULL, rgbHash, cbHash); + hr = PseudoBundleInitialize(FILEMAKEVERSION(rmj, rmm, rup, rpr), &pContext->pEngineState->update.package, FALSE, sczId, BOOTSTRAPPER_RELATION_UPDATE, BOOTSTRAPPER_PACKAGE_STATE_ABSENT, pContext->pEngineState->registration.sczExecutableName, sczLocalSource ? sczLocalSource : wzLocalSource, wzDownloadSource, qwSize, TRUE, sczCommandline, NULL, NULL, NULL, rgbHash, cbHash); ExitOnFailure(hr, "Failed to set update bundle."); pContext->pEngineState->update.fUpdateAvailable = TRUE; diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index d3f5b61e..d98cb7e9 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -151,10 +151,17 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" + + $(MajorMinorVersion.Split(`.`)[0]) + $(MajorMinorVersion.Split(`.`)[1]) + 0 + $(BuildNumber) + $(rmj).$(rmm).$(rup).$(rpr) + rmj=$(rmj);rmm=$(rmm);rup=$(rup);rpr=$(rpr);szVerMajorMinorBuild="$(szVerMajorMinorBuild)" + - - rmj=4;rmm=0;rup=$(BuildNumber);szVerMajorMinorBuild="$(BuildVersion)";%(PreprocessorDefinitions) + $(wixver);%(PreprocessorDefinitions) diff --git a/src/engine/pseudobundle.cpp b/src/engine/pseudobundle.cpp index ebdc040a..70f93701 100644 --- a/src/engine/pseudobundle.cpp +++ b/src/engine/pseudobundle.cpp @@ -126,7 +126,7 @@ extern "C" HRESULT PseudoBundleInitialize( } // Only support progress from engines that are compatible (aka: version greater than or equal to last protocol breaking change *and* versions that are older or the same as this engine). - pPackage->Exe.protocol = (FILEMAKEVERSION(3, 6, 2221, 0) <= qwEngineVersion && qwEngineVersion <= FILEMAKEVERSION(rmj, rmm, rup, 0)) ? BURN_EXE_PROTOCOL_TYPE_BURN : BURN_EXE_PROTOCOL_TYPE_NONE; + pPackage->Exe.protocol = (FILEMAKEVERSION(3, 6, 2221, 0) <= qwEngineVersion && qwEngineVersion <= FILEMAKEVERSION(rmj, rmm, rup, rpr)) ? BURN_EXE_PROTOCOL_TYPE_BURN : BURN_EXE_PROTOCOL_TYPE_NONE; // All versions of Burn past v3.9 RTM support suppressing ancestors. pPackage->Exe.fSupportsAncestors = FILEMAKEVERSION(3, 9, 1006, 0) <= qwEngineVersion; -- cgit v1.2.3-55-g6feb From a0c40d5a1b8537a6338dacc27a88d236077ab785 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 18 Oct 2020 16:01:24 -0500 Subject: Allow unsetting version variables. --- src/engine/EngineForApplication.cpp | 7 +++++-- src/engine/EngineForExtension.cpp | 7 +++++-- src/engine/variable.cpp | 15 +++++++++++---- src/engine/variant.cpp | 29 ++++++++++++++++++++++------- 4 files changed, 43 insertions(+), 15 deletions(-) diff --git a/src/engine/EngineForApplication.cpp b/src/engine/EngineForApplication.cpp index c5eb5467..87a0782c 100644 --- a/src/engine/EngineForApplication.cpp +++ b/src/engine/EngineForApplication.cpp @@ -583,8 +583,11 @@ static HRESULT BAEngineSetVariableVersion( if (wzVariable && *wzVariable) { - hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); - ExitOnFailure(hr, "Failed to parse new version value."); + if (wzValue) + { + hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); + ExitOnFailure(hr, "Failed to parse new version value."); + } hr = VariableSetVersion(&pContext->pEngineState->variables, wzVariable, pVersion, FALSE); ExitOnFailure(hr, "Failed to set version variable."); diff --git a/src/engine/EngineForExtension.cpp b/src/engine/EngineForExtension.cpp index c165e2c2..585b74a4 100644 --- a/src/engine/EngineForExtension.cpp +++ b/src/engine/EngineForExtension.cpp @@ -273,8 +273,11 @@ static HRESULT BEEngineSetVariableVersion( if (wzVariable && *wzVariable) { - hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); - ExitOnFailure(hr, "Failed to parse new version value."); + if (wzValue) + { + hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); + ExitOnFailure(hr, "Failed to parse new version value."); + } hr = VariableSetVersion(&pContext->pEngineState->variables, wzVariable, pVersion, FALSE); ExitOnFailure(hr, "Failed to set version variable."); diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp index c2192346..3425b92c 100644 --- a/src/engine/variable.cpp +++ b/src/engine/variable.cpp @@ -1551,7 +1551,7 @@ static HRESULT SetVariableValue( case BURN_VARIANT_TYPE_NONE: if (BURN_VARIANT_TYPE_NONE != pVariables->rgVariables[iVariable].Value.Type) { - LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable, pVariant->sczValue); + LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable); } break; @@ -1563,7 +1563,7 @@ static HRESULT SetVariableValue( case BURN_VARIANT_TYPE_STRING: if (!pVariant->sczValue) { - LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable, pVariant->sczValue); + LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable); } else { @@ -1572,7 +1572,14 @@ static HRESULT SetVariableValue( break; case BURN_VARIANT_TYPE_VERSION: - LogStringLine(REPORT_STANDARD, "Setting version variable '%ls' to value '%ls'", wzVariable, pVariant->pValue->sczVersion); + if (!pVariant->pValue) + { + LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable); + } + else + { + LogStringLine(REPORT_STANDARD, "Setting version variable '%ls' to value '%ls'", wzVariable, pVariant->pValue->sczVersion); + } break; default: @@ -1581,7 +1588,7 @@ static HRESULT SetVariableValue( } } - if (BURN_VARIANT_TYPE_VERSION == pVariant->Type && pVariant->pValue->fInvalid) + if (BURN_VARIANT_TYPE_VERSION == pVariant->Type && pVariant->pValue && pVariant->pValue->fInvalid) { LogId(REPORT_WARNING, MSG_VARIABLE_INVALID_VERSION, wzVariable); } diff --git a/src/engine/variant.cpp b/src/engine/variant.cpp index 9fdb82cd..82f465b2 100644 --- a/src/engine/variant.cpp +++ b/src/engine/variant.cpp @@ -198,7 +198,14 @@ static HRESULT GetVersionInternal( case BURN_VARIANT_TYPE_VERSION: BVariantRetrieveVersion(pVariant, &pValue); - hr = VerCopyVersion(pValue, ppValue); + if (!pValue) + { + *ppValue = NULL; + } + else + { + hr = VerCopyVersion(pValue, ppValue); + } break; default: hr = E_INVALIDARG; @@ -275,14 +282,22 @@ extern "C" HRESULT BVariantSetVersion( HRESULT hr = S_OK; BOOL fEncryptValue = pVariant->fEncryptString; - if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type || - BURN_VARIANT_TYPE_STRING == pVariant->Type) + if (!pValue) // if we're nulling out the version, make the variable NONE. { - StrSecureZeroFreeString(pVariant->sczValue); + BVariantUninitialize(pVariant); } - memset(pVariant, 0, sizeof(BURN_VARIANT)); - hr = VerCopyVersion(pValue, &pVariant->pValue); - pVariant->Type = BURN_VARIANT_TYPE_VERSION; + else // assign the value. + { + if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type || + BURN_VARIANT_TYPE_STRING == pVariant->Type) + { + StrSecureZeroFreeString(pVariant->sczValue); + } + memset(pVariant, 0, sizeof(BURN_VARIANT)); + hr = VerCopyVersion(pValue, &pVariant->pValue); + pVariant->Type = BURN_VARIANT_TYPE_VERSION; + } + BVariantSetEncryption(pVariant, fEncryptValue); return hr; -- cgit v1.2.3-55-g6feb From fac8fc35113cd9c42dc9cc4ea62d2645db863760 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 31 Oct 2020 21:22:52 -0600 Subject: Default to same-version upgrades for bundles. --- src/engine/detect.cpp | 8 ++++---- src/engine/plan.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp index 9e4681bb..176780af 100644 --- a/src/engine/detect.cpp +++ b/src/engine/detect.cpp @@ -175,13 +175,13 @@ extern "C" HRESULT DetectReportRelatedBundles( hr = VerCompareParsedVersions(pRegistration->pVersion, pRelatedBundle->pVersion, &nCompareResult); ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistration->pVersion, pRelatedBundle->pVersion); - if (nCompareResult > 0) + if (nCompareResult < 0) { - operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE; + operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; } - else if (nCompareResult < 0) + else { - operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; + operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE; } } break; diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index f6b681b6..3c0e1c50 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -1265,7 +1265,7 @@ extern "C" HRESULT PlanRelatedBundlesBegin( hr = VerCompareParsedVersions(pRegistration->pVersion, pRelatedBundle->pVersion, &nCompareResult); ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistration->pVersion, pRelatedBundle->pVersion); - pRelatedBundle->package.requested = (nCompareResult > 0) ? BOOTSTRAPPER_REQUEST_STATE_ABSENT : BOOTSTRAPPER_REQUEST_STATE_NONE; + pRelatedBundle->package.requested = (nCompareResult < 0) ? BOOTSTRAPPER_REQUEST_STATE_NONE : BOOTSTRAPPER_REQUEST_STATE_ABSENT; } break; case BOOTSTRAPPER_RELATION_PATCH: __fallthrough; -- cgit v1.2.3-55-g6feb From 79d7d3aa1de224d90d5e3a1174b3158ffe49b6f3 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 31 Oct 2020 22:40:43 -0500 Subject: Fix version log formats that weren't updated to ls. --- src/engine/engine.mc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/engine/engine.mc b/src/engine/engine.mc index c8cd6d37..4bb517f8 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -188,14 +188,14 @@ MessageId=102 Severity=Success SymbolicName=MSG_DETECTED_RELATED_BUNDLE Language=English -Detected related bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!hs!, operation: %5!hs! +Detected related bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!ls!, operation: %5!hs! . MessageId=103 Severity=Success SymbolicName=MSG_DETECTED_RELATED_PACKAGE Language=English -Detected related package: %1!ls!, scope: %2!hs!, version: %3!hs!, language: %4!u! operation: %5!hs! +Detected related package: %1!ls!, scope: %2!hs!, version: %3!ls!, language: %4!u! operation: %5!hs! . MessageId=104 @@ -223,7 +223,7 @@ MessageId=107 Severity=Success SymbolicName=MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE Language=English -Detected forward compatible bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!hs!, enabled: %5!hs! +Detected forward compatible bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!ls!, enabled: %5!hs! . MessageId=108 -- cgit v1.2.3-55-g6feb From b5553689ed1b1bd32f854654f56935c039a9b13b Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 1 Nov 2020 14:16:32 -0600 Subject: WIXFEAT:6258 - Format variables when evaluating condition. --- src/engine/condition.cpp | 11 ++- src/engine/variable.cpp | 114 ++++++++++++++++++++---------- src/engine/variable.h | 3 +- src/test/BurnUnitTest/VariableHelpers.cpp | 4 +- src/test/BurnUnitTest/VariableHelpers.h | 2 +- src/test/BurnUnitTest/VariableTest.cpp | 33 ++++++--- 6 files changed, 112 insertions(+), 55 deletions(-) diff --git a/src/engine/condition.cpp b/src/engine/condition.cpp index 224eb0da..d6038b34 100644 --- a/src/engine/condition.cpp +++ b/src/engine/condition.cpp @@ -432,6 +432,7 @@ static HRESULT ParseOperand( ) { HRESULT hr = S_OK; + LPWSTR sczFormatted = NULL; // Symbols don't encrypt their value, so can access the value directly. switch (pContext->NextSymbol.Type) @@ -451,9 +452,11 @@ static HRESULT ParseOperand( if (BURN_VARIANT_TYPE_FORMATTED == pOperand->Value.Type) { - // TODO: actually format the value? - hr = BVariantChangeType(&pOperand->Value, BURN_VARIANT_TYPE_STRING); - ExitOnRootFailure(hr, "Failed to change variable '%ls' type for condition '%ls'", pContext->NextSymbol.Value.sczValue, pContext->wzCondition); + hr = VariableGetFormatted(pContext->pVariables, pContext->NextSymbol.Value.sczValue, &sczFormatted, &pOperand->fHidden); + ExitOnRootFailure(hr, "Failed to format variable '%ls' for condition '%ls'", pContext->NextSymbol.Value.sczValue, pContext->wzCondition); + + hr = BVariantSetString(&pOperand->Value, sczFormatted, 0, FALSE); + ExitOnRootFailure(hr, "Failed to store formatted value for variable '%ls' for condition '%ls'", pContext->NextSymbol.Value.sczValue, pContext->wzCondition); } break; @@ -477,6 +480,8 @@ static HRESULT ParseOperand( ExitOnFailure(hr, "Failed to read next symbol."); LExit: + StrSecureZeroFreeString(sczFormatted); + return hr; } diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp index 3425b92c..8b6b318b 100644 --- a/src/engine/variable.cpp +++ b/src/engine/variable.cpp @@ -54,7 +54,14 @@ static HRESULT FormatString( __in_z LPCWSTR wzIn, __out_z_opt LPWSTR* psczOut, __out_opt DWORD* pcchOut, - __in BOOL fObfuscateHiddenVariables + __in BOOL fObfuscateHiddenVariables, + __out BOOL* pfContainsHiddenVariable + ); +static HRESULT GetFormatted( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out_z LPWSTR* psczValue, + __out BOOL* pfContainsHiddenVariable ); static HRESULT AddBuiltInVariable( __in BURN_VARIABLES* pVariables, @@ -629,43 +636,18 @@ LExit: extern "C" HRESULT VariableGetFormatted( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, - __out_z LPWSTR* psczValue + __out_z LPWSTR* psczValue, + __out BOOL* pfContainsHiddenVariable ) { HRESULT hr = S_OK; - BURN_VARIABLE* pVariable = NULL; - LPWSTR scz = NULL; - - ::EnterCriticalSection(&pVariables->csAccess); - hr = GetVariable(pVariables, wzVariable, &pVariable); - if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) + if (pfContainsHiddenVariable) { - ExitFunction1(hr = E_NOTFOUND); - } - else if (E_NOTFOUND == hr) - { - ExitFunction(); + *pfContainsHiddenVariable = FALSE; } - ExitOnFailure(hr, "Failed to get variable: %ls", wzVariable); - if (BURN_VARIANT_TYPE_FORMATTED == pVariable->Value.Type) - { - hr = BVariantGetString(&pVariable->Value, &scz); - ExitOnFailure(hr, "Failed to get unformatted string."); - - hr = VariableFormatString(pVariables, scz, psczValue, NULL); - ExitOnFailure(hr, "Failed to format value '%ls' of variable: %ls", pVariable->fHidden ? L"*****" : pVariable->Value.sczValue, wzVariable); - } - else - { - hr = BVariantGetString(&pVariable->Value, psczValue); - ExitOnFailure(hr, "Failed to get value as string for variable: %ls", wzVariable); - } - -LExit: - ::LeaveCriticalSection(&pVariables->csAccess); - StrSecureZeroFreeString(scz); + hr = GetFormatted(pVariables, wzVariable, psczValue, pfContainsHiddenVariable); return hr; } @@ -736,7 +718,7 @@ extern "C" HRESULT VariableFormatString( __out_opt DWORD* pcchOut ) { - return FormatString(pVariables, wzIn, psczOut, pcchOut, FALSE); + return FormatString(pVariables, wzIn, psczOut, pcchOut, FALSE, NULL); } extern "C" HRESULT VariableFormatStringObfuscated( @@ -746,7 +728,7 @@ extern "C" HRESULT VariableFormatStringObfuscated( __out_opt DWORD* pcchOut ) { - return FormatString(pVariables, wzIn, psczOut, pcchOut, TRUE); + return FormatString(pVariables, wzIn, psczOut, pcchOut, TRUE, NULL); } extern "C" HRESULT VariableEscapeString( @@ -1116,7 +1098,8 @@ static HRESULT FormatString( __in_z LPCWSTR wzIn, __out_z_opt LPWSTR* psczOut, __out_opt DWORD* pcchOut, - __in BOOL fObfuscateHiddenVariables + __in BOOL fObfuscateHiddenVariables, + __out BOOL* pfContainsHiddenVariable ) { HRESULT hr = S_OK; @@ -1204,20 +1187,22 @@ static HRESULT FormatString( } else { - if (fObfuscateHiddenVariables) + hr = VariableIsHidden(pVariables, scz, &fHidden); + ExitOnFailure(hr, "Failed to determine variable visibility: '%ls'.", scz); + + if (pfContainsHiddenVariable) { - hr = VariableIsHidden(pVariables, scz, &fHidden); - ExitOnFailure(hr, "Failed to determine variable visibility: '%ls'.", scz); + *pfContainsHiddenVariable |= fHidden; } - if (fHidden) + if (fObfuscateHiddenVariables && fHidden) { hr = StrAllocString(&rgVariables[cVariables], L"*****", 0); } else { // get formatted variable value - hr = VariableGetFormatted(pVariables, scz, &rgVariables[cVariables]); + hr = GetFormatted(pVariables, scz, &rgVariables[cVariables], pfContainsHiddenVariable); if (E_NOTFOUND == hr) // variable not found { hr = StrAllocStringSecure(&rgVariables[cVariables], L"", 0); @@ -1327,6 +1312,57 @@ LExit: return hr; } +// The contents of psczOut may be sensitive, should keep encrypted and SecureZeroFree. +static HRESULT GetFormatted( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out_z LPWSTR* psczValue, + __out BOOL* pfContainsHiddenVariable + ) +{ + HRESULT hr = S_OK; + BURN_VARIABLE* pVariable = NULL; + LPWSTR scz = NULL; + + ::EnterCriticalSection(&pVariables->csAccess); + + hr = GetVariable(pVariables, wzVariable, &pVariable); + if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) + { + ExitFunction1(hr = E_NOTFOUND); + } + else if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get variable: %ls", wzVariable); + + if (pfContainsHiddenVariable) + { + *pfContainsHiddenVariable |= pVariable->fHidden; + } + + if (BURN_VARIANT_TYPE_FORMATTED == pVariable->Value.Type) + { + hr = BVariantGetString(&pVariable->Value, &scz); + ExitOnFailure(hr, "Failed to get unformatted string."); + + hr = FormatString(pVariables, scz, psczValue, NULL, FALSE, pfContainsHiddenVariable); + ExitOnFailure(hr, "Failed to format value '%ls' of variable: %ls", pVariable->fHidden ? L"*****" : pVariable->Value.sczValue, wzVariable); + } + else + { + hr = BVariantGetString(&pVariable->Value, psczValue); + ExitOnFailure(hr, "Failed to get value as string for variable: %ls", wzVariable); + } + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + StrSecureZeroFreeString(scz); + + return hr; +} + static HRESULT AddBuiltInVariable( __in BURN_VARIABLES* pVariables, __in LPCWSTR wzVariable, diff --git a/src/engine/variable.h b/src/engine/variable.h index 6437c32f..713fe6e3 100644 --- a/src/engine/variable.h +++ b/src/engine/variable.h @@ -95,7 +95,8 @@ HRESULT VariableGetVariant( HRESULT VariableGetFormatted( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, - __out_z LPWSTR* psczValue + __out_z LPWSTR* psczValue, + __out BOOL* pfContainsHiddenVariable ); HRESULT VariableSetNumeric( __in BURN_VARIABLES* pVariables, diff --git a/src/test/BurnUnitTest/VariableHelpers.cpp b/src/test/BurnUnitTest/VariableHelpers.cpp index 99ba492a..40f958f8 100644 --- a/src/test/BurnUnitTest/VariableHelpers.cpp +++ b/src/test/BurnUnitTest/VariableHelpers.cpp @@ -98,13 +98,13 @@ namespace Bootstrapper } } - String^ VariableGetFormattedHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) + String^ VariableGetFormattedHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, BOOL* pfContainsHiddenVariable) { HRESULT hr = S_OK; LPWSTR scz = NULL; try { - hr = VariableGetFormatted(pVariables, wzVariable, &scz); + hr = VariableGetFormatted(pVariables, wzVariable, &scz, pfContainsHiddenVariable); TestThrowOnFailure1(hr, L"Failed to get formatted: %s", wzVariable); return gcnew String(scz); diff --git a/src/test/BurnUnitTest/VariableHelpers.h b/src/test/BurnUnitTest/VariableHelpers.h index 96122219..d460c60f 100644 --- a/src/test/BurnUnitTest/VariableHelpers.h +++ b/src/test/BurnUnitTest/VariableHelpers.h @@ -20,7 +20,7 @@ void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LP System::String^ VariableGetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); __int64 VariableGetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); System::String^ VariableGetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); -System::String^ VariableGetFormattedHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); +System::String^ VariableGetFormattedHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, BOOL* pfContainsHiddenVariable); System::String^ VariableFormatStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzIn); System::String^ VariableEscapeStringHelper(LPCWSTR wzIn); bool EvaluateConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition); diff --git a/src/test/BurnUnitTest/VariableTest.cpp b/src/test/BurnUnitTest/VariableTest.cpp index f5511199..676c134e 100644 --- a/src/test/BurnUnitTest/VariableTest.cpp +++ b/src/test/BurnUnitTest/VariableTest.cpp @@ -80,6 +80,7 @@ namespace Bootstrapper HRESULT hr = S_OK; IXMLDOMElement* pixeBundle = NULL; BURN_VARIABLES variables = { }; + BOOL fContainsHiddenData = FALSE; try { LPCWSTR wzDocument = @@ -90,6 +91,7 @@ namespace Bootstrapper L" -- cgit v1.2.3-55-g6feb From d6aceb1277606fe1f1688d40ee0895d0b89c6705 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 15 Nov 2020 14:17:27 -0600 Subject: Run unit tests in the build script. --- appveyor.cmd | 2 ++ appveyor.yml | 2 ++ .../WixToolset.BootstrapperCore.Native.proj | 2 +- src/engine/engine.vcxproj | 8 ++++---- src/engine/packages.config | 4 ++-- src/stub/packages.config | 4 ++-- src/stub/stub.vcxproj | 8 ++++---- src/test/BurnUnitTest/BurnUnitTest.vcxproj | 16 ++++++++-------- src/test/BurnUnitTest/packages.config | 7 ++++--- 9 files changed, 29 insertions(+), 24 deletions(-) diff --git a/appveyor.cmd b/appveyor.cmd index 48033840..82602274 100644 --- a/appveyor.cmd +++ b/appveyor.cmd @@ -3,6 +3,8 @@ nuget restore || exit /b +msbuild -t:Test -p:Configuration=Release src\test\BurnUnitTest || exit /b + msbuild -p:Configuration=Release;Platform=x86 || exit /b msbuild -p:Configuration=Release -t:Pack src\stub\stub.vcxproj || exit /b diff --git a/appveyor.yml b/appveyor.yml index 7c686b04..e4d25586 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,6 +21,8 @@ environment: build_script: - appveyor.cmd +test: off + pull_requests: do_not_increment_build_number: true diff --git a/src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.proj b/src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.proj index c0a99472..30b48b29 100644 --- a/src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.proj +++ b/src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.proj @@ -15,5 +15,5 @@ - + \ No newline at end of file diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index cec57540..84717941 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -2,7 +2,7 @@ - + @@ -167,12 +167,12 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + diff --git a/src/engine/packages.config b/src/engine/packages.config index 68222d34..dc425a64 100644 --- a/src/engine/packages.config +++ b/src/engine/packages.config @@ -1,5 +1,5 @@  - - + + \ No newline at end of file diff --git a/src/stub/packages.config b/src/stub/packages.config index 3ece6694..eb65b3b3 100644 --- a/src/stub/packages.config +++ b/src/stub/packages.config @@ -3,6 +3,6 @@ - - + + \ No newline at end of file diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index d3c1278c..42b614cd 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -5,7 +5,7 @@ - + @@ -50,7 +50,7 @@ - + @@ -106,7 +106,7 @@ - - + + \ No newline at end of file diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index cc19fa60..dde01be1 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -3,8 +3,8 @@ - - + + Debug @@ -63,23 +63,23 @@ - ..\..\..\packages\WixBuildTools.TestSupport.4.0.40\lib\net472\WixBuildTools.TestSupport.dll + ..\..\..\packages\WixBuildTools.TestSupport.4.0.47\lib\net472\WixBuildTools.TestSupport.dll - ..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.40\lib\net472\WixBuildTools.TestSupport.Native.dll + ..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\lib\net472\WixBuildTools.TestSupport.Native.dll - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - + + + diff --git a/src/test/BurnUnitTest/packages.config b/src/test/BurnUnitTest/packages.config index 74f2523f..472349e7 100644 --- a/src/test/BurnUnitTest/packages.config +++ b/src/test/BurnUnitTest/packages.config @@ -6,8 +6,9 @@ + - - - + + + \ No newline at end of file -- cgit v1.2.3-55-g6feb From 0d873d28c2dd18444afa08b748e91f495ed1cf5c Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 15 Nov 2020 19:54:20 -0600 Subject: Add plan tests. --- src/engine/container.cpp | 36 +- src/engine/container.h | 5 +- src/engine/core.cpp | 4 + src/engine/logging.cpp | 21 + src/engine/logging.h | 4 + src/engine/manifest.cpp | 49 +- src/engine/manifest.h | 5 + src/engine/msiengine.cpp | 5 +- src/engine/msiengine.h | 6 + src/engine/plan.cpp | 34 +- src/engine/plan.h | 3 + src/test/BurnUnitTest/BurnUnitTest.vcxproj | 1 + src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters | 3 + src/test/BurnUnitTest/PlanTest.cpp | 982 +++++++++++++++++++++ src/test/BurnUnitTest/precomp.h | 1 + 15 files changed, 1125 insertions(+), 34 deletions(-) create mode 100644 src/test/BurnUnitTest/PlanTest.cpp diff --git a/src/engine/container.cpp b/src/engine/container.cpp index ada9025b..55a16afb 100644 --- a/src/engine/container.cpp +++ b/src/engine/container.cpp @@ -17,7 +17,6 @@ static HRESULT GetAttachedContainerInfo( // function definitions extern "C" HRESULT ContainersParseFromXml( - __in BURN_SECTION* pSection, __in BURN_CONTAINERS* pContainers, __in IXMLDOMNode* pixnBundle ) @@ -128,14 +127,6 @@ extern "C" HRESULT ContainersParseFromXml( ExitOnFailure(hr, "Failed to get @Hash."); } - // If the container is attached, make sure the information in the section matches what the - // manifest contained and get the offset to the container. - if (pContainer->fAttached) - { - hr = SectionGetAttachedContainerInfo(pSection, pContainer->dwAttachedIndex, pContainer->type, &pContainer->qwAttachedOffset, &pContainer->qwFileSize, &pContainer->fActuallyAttached); - ExitOnFailure(hr, "Failed to get attached container information."); - } - // prepare next iteration ReleaseNullObject(pixnNode); } @@ -150,6 +141,33 @@ LExit: return hr; } +extern "C" HRESULT ContainersInitialize( + __in BURN_CONTAINERS* pContainers, + __in BURN_SECTION* pSection + ) +{ + HRESULT hr = S_OK; + + if (pContainers->rgContainers) + { + for (DWORD i = 0; i < pContainers->cContainers; ++i) + { + BURN_CONTAINER* pContainer = &pContainers->rgContainers[i]; + + // If the container is attached, make sure the information in the section matches what the + // manifest contained and get the offset to the container. + if (pContainer->fAttached) + { + hr = SectionGetAttachedContainerInfo(pSection, pContainer->dwAttachedIndex, pContainer->type, &pContainer->qwAttachedOffset, &pContainer->qwFileSize, &pContainer->fActuallyAttached); + ExitOnFailure(hr, "Failed to get attached container information."); + } + } + } + +LExit: + return hr; +} + extern "C" void ContainersUninitialize( __in BURN_CONTAINERS* pContainers ) diff --git a/src/engine/container.h b/src/engine/container.h index 2ca3d7ad..bbd9fa72 100644 --- a/src/engine/container.h +++ b/src/engine/container.h @@ -135,10 +135,13 @@ typedef struct _BURN_CONTAINER_CONTEXT // functions HRESULT ContainersParseFromXml( - __in BURN_SECTION* pSection, __in BURN_CONTAINERS* pContainers, __in IXMLDOMNode* pixnBundle ); +HRESULT ContainersInitialize( + __in BURN_CONTAINERS* pContainers, + __in BURN_SECTION* pSection + ); void ContainersUninitialize( __in BURN_CONTAINERS* pContainers ); diff --git a/src/engine/core.cpp b/src/engine/core.cpp index c34024fd..aeae6bea 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -96,6 +96,9 @@ extern "C" HRESULT CoreInitialize( hr = ManifestLoadXmlFromBuffer(pbBuffer, cbBuffer, pEngineState); ExitOnFailure(hr, "Failed to load manifest."); + hr = ContainersInitialize(&pEngineState->containers, &pEngineState->section); + ExitOnFailure(hr, "Failed to intialize containers."); + // Parse command line. hr = ParseCommandLine(pEngineState->argc, pEngineState->argv, &pEngineState->command, &pEngineState->companionConnection, &pEngineState->embeddedConnection, &pEngineState->variables, &pEngineState->mode, &pEngineState->automaticUpdates, &pEngineState->fDisableSystemRestore, &sczSourceProcessPath, &sczOriginalSource, &pEngineState->fDisableUnelevate, &pEngineState->log.dwAttributes, &pEngineState->log.sczPath, &pEngineState->registration.sczActiveParent, &pEngineState->sczIgnoreDependencies, &pEngineState->registration.sczAncestors, &sczSanitizedCommandLine); ExitOnFailure(hr, "Failed to parse command line."); @@ -411,6 +414,7 @@ extern "C" HRESULT CorePlan( pEngineState->plan.action = action; pEngineState->plan.wzBundleId = pEngineState->registration.sczId; pEngineState->plan.wzBundleProviderKey = pEngineState->registration.sczId; + pEngineState->plan.fDisableRollback = pEngineState->fDisableRollback; hr = PlanSetVariables(action, &pEngineState->variables); ExitOnFailure(hr, "Failed to update action."); diff --git a/src/engine/logging.cpp b/src/engine/logging.cpp index e69303f0..512b562c 100644 --- a/src/engine/logging.cpp +++ b/src/engine/logging.cpp @@ -468,6 +468,27 @@ extern "C" LPCSTR LoggingMsiInstallContext( } } +extern "C" LPCWSTR LoggingBurnMsiPropertyToString( + __in BURN_MSI_PROPERTY burnMsiProperty + ) +{ + switch (burnMsiProperty) + { + case BURN_MSI_PROPERTY_INSTALL: + return BURNMSIINSTALL_PROPERTY_NAME; + case BURN_MSI_PROPERTY_MODIFY: + return BURNMSIMODIFY_PROPERTY_NAME; + case BURN_MSI_PROPERTY_NONE: + return L"(none)"; + case BURN_MSI_PROPERTY_REPAIR: + return BURNMSIREPAIR_PROPERTY_NAME; + case BURN_MSI_PROPERTY_UNINSTALL: + return BURNMSIUNINSTALL_PROPERTY_NAME; + default: + return L"Invalid"; + } +} + extern "C" LPCSTR LoggingPerMachineToString( __in BOOL fPerMachine ) diff --git a/src/engine/logging.h b/src/engine/logging.h index 22dd54d9..381a295b 100644 --- a/src/engine/logging.h +++ b/src/engine/logging.h @@ -101,6 +101,10 @@ LPCSTR LoggingMsiInstallContext( __in MSIINSTALLCONTEXT context ); +LPCWSTR LoggingBurnMsiPropertyToString( + __in BURN_MSI_PROPERTY burnMsiProperty + ); + LPCSTR LoggingPerMachineToString( __in BOOL fPerMachine ); diff --git a/src/engine/manifest.cpp b/src/engine/manifest.cpp index 8783b15e..270b6b74 100644 --- a/src/engine/manifest.cpp +++ b/src/engine/manifest.cpp @@ -3,8 +3,33 @@ #include "precomp.h" +static HRESULT ParseFromXml( + __in IXMLDOMDocument* pixdDocument, + __in BURN_ENGINE_STATE* pEngineState + ); + // function definitions +extern "C" HRESULT ManifestLoadXml( + __in LPCWSTR wzDocument, + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + IXMLDOMDocument* pixdDocument = NULL; + + // load xml document + hr = XmlLoadDocument(wzDocument, &pixdDocument); + ExitOnFailure(hr, "Failed to load manifest as XML document."); + + hr = ParseFromXml(pixdDocument, pEngineState); + +LExit: + ReleaseObject(pixdDocument); + + return hr; +} + extern "C" HRESULT ManifestLoadXmlFromBuffer( __in_bcount(cbBuffer) BYTE* pbBuffer, __in SIZE_T cbBuffer, @@ -13,14 +38,29 @@ extern "C" HRESULT ManifestLoadXmlFromBuffer( { HRESULT hr = S_OK; IXMLDOMDocument* pixdDocument = NULL; - IXMLDOMElement* pixeBundle = NULL; - IXMLDOMNode* pixnLog = NULL; - IXMLDOMNode* pixnChain = NULL; // load xml document hr = XmlLoadDocumentFromBuffer(pbBuffer, cbBuffer, &pixdDocument); ExitOnFailure(hr, "Failed to load manifest as XML document."); + hr = ParseFromXml(pixdDocument, pEngineState); + +LExit: + ReleaseObject(pixdDocument); + + return hr; +} + +static HRESULT ParseFromXml( + __in IXMLDOMDocument* pixdDocument, + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + IXMLDOMNode* pixnLog = NULL; + IXMLDOMNode* pixnChain = NULL; + // get bundle element hr = pixdDocument->get_documentElement(&pixeBundle); ExitOnFailure(hr, "Failed to get bundle element."); @@ -105,7 +145,7 @@ extern "C" HRESULT ManifestLoadXmlFromBuffer( ExitOnFailure(hr, "Failed to parse update."); // parse containers - hr = ContainersParseFromXml(&pEngineState->section, &pEngineState->containers, pixeBundle); + hr = ContainersParseFromXml(&pEngineState->containers, pixeBundle); ExitOnFailure(hr, "Failed to parse containers."); // parse payloads @@ -124,6 +164,5 @@ LExit: ReleaseObject(pixnChain); ReleaseObject(pixnLog); ReleaseObject(pixeBundle); - ReleaseObject(pixdDocument); return hr; } diff --git a/src/engine/manifest.h b/src/engine/manifest.h index 6e535d60..223181d9 100644 --- a/src/engine/manifest.h +++ b/src/engine/manifest.h @@ -11,6 +11,11 @@ extern "C" { // function declarations +HRESULT ManifestLoadXml( + __in LPCWSTR wzDocument, + __in BURN_ENGINE_STATE* pEngineState + ); + HRESULT ManifestLoadXmlFromBuffer( __in_bcount(cbBuffer) BYTE* pbBuffer, __in SIZE_T cbBuffer, diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index 066734d0..c20e2ef8 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -4,10 +4,7 @@ // constants -#define BURNMSIINSTALL_PROPERTY_NAME L"BURNMSIINSTALL" -#define BURNMSIMODIFY_PROPERTY_NAME L"BURNMSIMODIFY" -#define BURNMSIREPAIR_PROPERTY_NAME L"BURNMSIREPAIR" -#define BURNMSIUNINSTALL_PROPERTY_NAME L"BURNMSIUNINSTALL" + // structs diff --git a/src/engine/msiengine.h b/src/engine/msiengine.h index 04c375c2..64bddcf0 100644 --- a/src/engine/msiengine.h +++ b/src/engine/msiengine.h @@ -1,6 +1,12 @@ #pragma once // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. +// constants +#define BURNMSIINSTALL_PROPERTY_NAME L"BURNMSIINSTALL" +#define BURNMSIMODIFY_PROPERTY_NAME L"BURNMSIMODIFY" +#define BURNMSIREPAIR_PROPERTY_NAME L"BURNMSIREPAIR" +#define BURNMSIUNINSTALL_PROPERTY_NAME L"BURNMSIUNINSTALL" + #if defined(__cplusplus) extern "C" { diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index 3c0e1c50..e2a3437d 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -74,7 +74,9 @@ static BOOL AlreadyPlannedCachePackage( __in_z LPCWSTR wzPackageId, __out HANDLE* phSyncpointEvent ); -static DWORD GetNextCheckpointId(); +static DWORD GetNextCheckpointId( + __in BURN_PLAN* pPlan + ); static HRESULT AppendCacheAction( __in BURN_PLAN* pPlan, __out BURN_CACHE_ACTION** ppCacheAction @@ -1621,7 +1623,7 @@ extern "C" HRESULT PlanExecuteCheckpoint( { HRESULT hr = S_OK; BURN_EXECUTE_ACTION* pAction = NULL; - DWORD dwCheckpointId = GetNextCheckpointId(); + DWORD dwCheckpointId = GetNextCheckpointId(pPlan); // execute checkpoint hr = PlanAppendExecuteAction(pPlan, &pAction); @@ -1808,7 +1810,7 @@ extern "C" HRESULT PlanRollbackBoundaryComplete( DWORD dwCheckpointId = 0; // Add checkpoints. - dwCheckpointId = GetNextCheckpointId(); + dwCheckpointId = GetNextCheckpointId(pPlan); hr = PlanAppendExecuteAction(pPlan, &pExecuteAction); ExitOnFailure(hr, "Failed to append execute action."); @@ -2095,7 +2097,7 @@ static HRESULT AddCachePackageHelper( // Cache checkpoints happen before the package is cached because downloading packages' // payloads will not roll themselves back the way installation packages rollback on // failure automatically. - dwCheckpoint = GetNextCheckpointId(); + dwCheckpoint = GetNextCheckpointId(pPlan); hr = AppendCacheAction(pPlan, &pCacheAction); ExitOnFailure(hr, "Failed to append package start action."); @@ -2234,10 +2236,11 @@ static BOOL AlreadyPlannedCachePackage( return fPlanned; } -static DWORD GetNextCheckpointId() +static DWORD GetNextCheckpointId( + __in BURN_PLAN* pPlan + ) { - static DWORD dwCounter = 0; - return ++dwCounter; + return ++pPlan->dwNextCheckpointId; } static HRESULT AppendCacheAction( @@ -3039,11 +3042,11 @@ static void CacheActionLog( break; case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: SIGNAL_SYNCPOINT event handle: 0x%x, skip until retried: %hs", wzBase, iAction, pAction->syncpoint.hEvent, LoggingBoolToString(pAction->fSkipUntilRetried)); + LogStringLine(REPORT_STANDARD, "%ls action[%u]: SIGNAL_SYNCPOINT event handle: 0x%p, skip until retried: %hs", wzBase, iAction, pAction->syncpoint.hEvent, LoggingBoolToString(pAction->fSkipUntilRetried)); break; case BURN_CACHE_ACTION_TYPE_TRANSACTION_BOUNDARY: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: TRANSACTION_BOUNDARY id: %ls, event handle: 0x%x, vital: %ls, transaction: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.hEvent, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no", pAction->rollbackBoundary.pRollbackBoundary->fTransaction ? L"yes" : L"no"); + LogStringLine(REPORT_STANDARD, "%ls action[%u]: TRANSACTION_BOUNDARY id: %ls, event handle: 0x%p, vital: %ls, transaction: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.hEvent, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no", pAction->rollbackBoundary.pRollbackBoundary->fTransaction ? L"yes" : L"no"); break; default: @@ -3066,11 +3069,11 @@ static void ExecuteActionLog( break; case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_PROVIDER package id: %ls, action: %u", wzBase, iAction, pAction->packageProvider.pPackage->sczId, pAction->packageProvider.action); + LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_PROVIDER package id: %ls, action: %hs", wzBase, iAction, pAction->packageProvider.pPackage->sczId, LoggingDependencyActionToString(pAction->packageProvider.action)); break; case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_DEPENDENCY package id: %ls, bundle provider key: %ls, action: %u", wzBase, iAction, pAction->packageDependency.pPackage->sczId, pAction->packageDependency.sczBundleProviderKey, pAction->packageDependency.action); + LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_DEPENDENCY package id: %ls, bundle provider key: %ls, action: %hs", wzBase, iAction, pAction->packageDependency.pPackage->sczId, pAction->packageDependency.sczBundleProviderKey, LoggingDependencyActionToString(pAction->packageDependency.action)); break; case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: @@ -3078,15 +3081,15 @@ static void ExecuteActionLog( break; case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSI_PACKAGE package id: %ls, action: %hs, action msi property: %u, ui level: %u, disable externaluihandler: %ls, log path: %ls, logging attrib: %u", wzBase, iAction, pAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pAction->msiPackage.action), pAction->msiPackage.actionMsiProperty, pAction->msiPackage.uiLevel, pAction->msiPackage.fDisableExternalUiHandler ? L"yes" : L"no", pAction->msiPackage.sczLogPath, pAction->msiPackage.dwLoggingAttributes); + LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSI_PACKAGE package id: %ls, action: %hs, action msi property: %ls, ui level: %u, disable externaluihandler: %ls, log path: %ls, logging attrib: %u", wzBase, iAction, pAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pAction->msiPackage.action), LoggingBurnMsiPropertyToString(pAction->msiPackage.actionMsiProperty), pAction->msiPackage.uiLevel, pAction->msiPackage.fDisableExternalUiHandler ? L"yes" : L"no", pAction->msiPackage.sczLogPath, pAction->msiPackage.dwLoggingAttributes); for (DWORD j = 0; j < pAction->msiPackage.cPatches; ++j) { - LogStringLine(REPORT_STANDARD, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->msiPackage.rgOrderedPatches->dwOrder, pAction->msiPackage.rgOrderedPatches[j].dwOrder, pAction->msiPackage.rgOrderedPatches[j].pPackage->sczId); + LogStringLine(REPORT_STANDARD, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->msiPackage.rgOrderedPatches[j].dwOrder, pAction->msiPackage.rgOrderedPatches[j].pPackage->sczId); } break; case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSP_TARGET package id: %ls, action: %hs, target product code: %ls, target per-machine: %ls, action msi property: %u, ui level: %u, disable externaluihandler: %ls, log path: %ls", wzBase, iAction, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.sczTargetProductCode, pAction->mspTarget.fPerMachineTarget ? L"yes" : L"no", pAction->mspTarget.actionMsiProperty, pAction->mspTarget.uiLevel, pAction->mspTarget.fDisableExternalUiHandler ? L"yes" : L"no", pAction->mspTarget.sczLogPath); + LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSP_TARGET package id: %ls, action: %hs, target product code: %ls, target per-machine: %ls, action msi property: %ls, ui level: %u, disable externaluihandler: %ls, log path: %ls", wzBase, iAction, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.sczTargetProductCode, pAction->mspTarget.fPerMachineTarget ? L"yes" : L"no", LoggingBurnMsiPropertyToString(pAction->mspTarget.actionMsiProperty), pAction->mspTarget.uiLevel, pAction->mspTarget.fDisableExternalUiHandler ? L"yes" : L"no", pAction->mspTarget.sczLogPath); for (DWORD j = 0; j < pAction->mspTarget.cOrderedPatches; ++j) { LogStringLine(REPORT_STANDARD, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->mspTarget.rgOrderedPatches[j].dwOrder, pAction->mspTarget.rgOrderedPatches[j].pPackage->sczId); @@ -3106,7 +3109,7 @@ static void ExecuteActionLog( break; case BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: WAIT_SYNCPOINT event handle: 0x%x", wzBase, iAction, pAction->syncpoint.hEvent); + LogStringLine(REPORT_STANDARD, "%ls action[%u]: WAIT_SYNCPOINT event handle: 0x%p", wzBase, iAction, pAction->syncpoint.hEvent); break; case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE: @@ -3131,6 +3134,7 @@ extern "C" void PlanDump( LogStringLine(REPORT_STANDARD, "Plan action: %hs", LoggingBurnActionToString(pPlan->action)); LogStringLine(REPORT_STANDARD, " per-machine: %hs", LoggingTrueFalseToString(pPlan->fPerMachine)); + LogStringLine(REPORT_STANDARD, " disable-rollback: %hs", LoggingTrueFalseToString(pPlan->fDisableRollback)); LogStringLine(REPORT_STANDARD, " keep registration by default: %hs", LoggingTrueFalseToString(pPlan->fKeepRegistrationDefault)); LogStringLine(REPORT_STANDARD, " estimated size: %llu", pPlan->qwEstimatedSize); diff --git a/src/engine/plan.h b/src/engine/plan.h index 4fd3380e..5fddd72f 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -325,6 +325,7 @@ typedef struct _BURN_PLAN DWORD dwRegistrationOperations; BOOL fKeepRegistrationDefault; BOOL fDisallowRemoval; + BOOL fDisableRollback; DWORD64 qwCacheSizeTotal; @@ -366,6 +367,8 @@ typedef struct _BURN_PLAN BURN_CACHE_PAYLOAD_PROGRESS* rgPayloadProgress; DWORD cPayloadProgress; STRINGDICT_HANDLE shPayloadProgress; + + DWORD dwNextCheckpointId; } BURN_PLAN; diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index dde01be1..71f3ea09 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -36,6 +36,7 @@ + Create diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters b/src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters index 14261ba0..f9461f53 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters @@ -30,6 +30,9 @@ Source Files + + Source Files + Source Files diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp new file mode 100644 index 00000000..86bc646b --- /dev/null +++ b/src/test/BurnUnitTest/PlanTest.cpp @@ -0,0 +1,982 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static HRESULT WINAPI PlanTestBAProc( + __in BOOTSTRAPPER_APPLICATION_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults, + __in_opt LPVOID pvContext + ); + +static LPCWSTR wzMsiTransactionManifest = + L"" + L" " + L" " + L" "; + +static LPCWSTR wzSingleMsiManifest = + L"" + L" " + L" " + L" "; + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace Xunit; + + public ref class PlanTest : BurnUnitTest + { + public: + PlanTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] + void MsiTransactionInstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzMsiTransactionManifest, pEngineState); + DetectPackagesAsAbsent(pEngineState); + DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"1.0.0.0"); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + Assert::Equal(FALSE, pPlan->fKeepRegistrationDefault); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + DWORD dwPackageStart = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", 6, 2, 33741, FALSE); + ValidateCacheAcquireContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE); + ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, dwPackageStart, 6); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"PackageA", TRUE, FALSE, dwPackageStart); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"cab1QmlL013Hqv_44W64R0cvnHn_2c", TRUE, FALSE, dwPackageStart); + ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 8); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageB", 14, 2, 33753, FALSE); + ValidateCacheAcquireContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", TRUE); + ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, dwPackageStart, 2); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageB", L"PackageB", TRUE, FALSE, dwPackageStart); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageB", L"cabQH1Sgh7w2K8tLIftUaaWVhMWt0s", TRUE, FALSE, dwPackageStart); + ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageB", FALSE); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 14); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageC", 22, 2, 33739, FALSE); + ValidateCacheAcquireContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", TRUE); + ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, dwPackageStart, 2); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageC", L"PackageC", TRUE, FALSE, dwPackageStart); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageC", L"cabRT8kdm93olnEAQB2GSO3u0400VI", TRUE, FALSE, dwPackageStart); + ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageC", FALSE); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); + Assert::Equal(24ul, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 8); + ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"PackageB", FALSE); + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 14); + ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"PackageC", FALSE); + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(106166ull, pPlan->qwEstimatedSize); + Assert::Equal(101233ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[7].syncpoint.hEvent); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteRegistration(pPlan, fRollback, dwIndex++, TRUE); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[15].syncpoint.hEvent); + dwExecuteCheckpointId = 9; + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[23].syncpoint.hEvent); + dwExecuteCheckpointId = 15; + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[23].syncpoint.hEvent); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL); + Assert::Equal(34ul, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteRegistration(pPlan, fRollback, dwIndex++, FALSE); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageB"); + dwExecuteCheckpointId = 9; + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageC"); + dwExecuteCheckpointId = 15; + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL); + Assert::Equal(33ul, pPlan->cRollbackActions); + + Assert::Equal(4ul, pPlan->cExecutePackagesTotal); + Assert::Equal(7ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + } + + [Fact] + void MsiTransactionUninstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzMsiTransactionManifest, pEngineState); + DetectPackagesAsPresentAndCached(pEngineState); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + Assert::Equal(TRUE, pPlan->fKeepRegistrationDefault); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(0ull, pPlan->qwEstimatedSize); + Assert::Equal(0ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteRegistration(pPlan, fRollback, dwIndex++, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteRegistration(pPlan, fRollback, dwIndex++, TRUE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(3ul, pPlan->cExecutePackagesTotal); + Assert::Equal(3ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + ValidateCleanAction(pPlan, dwIndex++, L"PackageC"); + ValidateCleanAction(pPlan, dwIndex++, L"PackageB"); + ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{BE27CF2B-9E5F-4500-BAE3-5E0E522FB962}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{388E4963-13AD-4EE7-B907-AA8888F50E54}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{196E43EA-EF92-4FF8-B9AC-A0FD0D225BB4}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + } + + [Fact] + void SingleMsiInstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSingleMsiManifest, pEngineState); + DetectAttachedContainerAsAttached(pEngineState); + DetectPackagesAsAbsent(pEngineState); + DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"0.9.0.0"); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + Assert::Equal(FALSE, pPlan->fKeepRegistrationDefault); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + DWORD dwPackageStart = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageE", 5, 2, 33741, FALSE); + ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, BURN_PLAN_INVALID_ACTION_INDEX, 2); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageE", L"PackageE", TRUE, FALSE, dwPackageStart); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageE", L"cabkAPka1fWa1PyiVdoVPuoB6Qvs3k", TRUE, FALSE, dwPackageStart); + ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageE", FALSE); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"PackageE", FALSE); + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(35381ull, pPlan->qwEstimatedSize); + Assert::Equal(33741ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[6].syncpoint.hEvent); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageE", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageE", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteRegistration(pPlan, fRollback, dwIndex++, TRUE); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageE", L"{4a04385a-0081-44ba-acd1-9e4e95cfc97f}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[6].syncpoint.hEvent); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteRegistration(pPlan, fRollback, dwIndex++, FALSE); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageE"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageE", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageE", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageE", L"{4a04385a-0081-44ba-acd1-9e4e95cfc97f}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(2ul, pPlan->cExecutePackagesTotal); + Assert::Equal(3ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{4a04385a-0081-44ba-acd1-9e4e95cfc97f}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + } + + [Fact] + void SingleMsiUninstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSingleMsiManifest, pEngineState); + DetectPackagesAsPresentAndCached(pEngineState); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + Assert::Equal(TRUE, pPlan->fKeepRegistrationDefault); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(0ull, pPlan->qwEstimatedSize); + Assert::Equal(0ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageE", L"{4a04385a-0081-44ba-acd1-9e4e95cfc97f}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageE", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageE", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteRegistration(pPlan, fRollback, dwIndex++, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageE", L"{4a04385a-0081-44ba-acd1-9e4e95cfc97f}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageE", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageE", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteRegistration(pPlan, fRollback, dwIndex++, TRUE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(1ul, pPlan->cExecutePackagesTotal); + Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + ValidateCleanAction(pPlan, dwIndex++, L"PackageE"); + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{4a04385a-0081-44ba-acd1-9e4e95cfc97f}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{284F56B6-B6C7-404A-B9B5-78F63BF79494}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + } + + private: + // This doesn't initialize everything, just enough for CorePlan to work. + void InitializeEngineStateForCorePlan(LPCWSTR wzManifest, BURN_ENGINE_STATE* pEngineState) + { + HRESULT hr = S_OK; + + ::InitializeCriticalSection(&pEngineState->csActive); + ::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive); + + hr = VariableInitialize(&pEngineState->variables); + NativeAssert::Succeeded(hr, "Failed to initialize variables."); + + hr = ManifestLoadXml(wzManifest, pEngineState); + NativeAssert::Succeeded(hr, "Failed to load manifest."); + + pEngineState->userExperience.pfnBAProc = PlanTestBAProc; + } + + void DetectAttachedContainerAsAttached(BURN_ENGINE_STATE* pEngineState) + { + for (DWORD i = 0; i < pEngineState->containers.cContainers; ++i) + { + BURN_CONTAINER* pContainer = pEngineState->containers.rgContainers + i; + if (pContainer->fAttached) + { + pContainer->fActuallyAttached = TRUE; + } + } + } + + void DetectPackagesAsAbsent(BURN_ENGINE_STATE* pEngineState) + { + DetectReset(&pEngineState->registration, &pEngineState->packages); + PlanReset(&pEngineState->plan, &pEngineState->packages); + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + } + } + + void DetectPackagesAsPresentAndCached(BURN_ENGINE_STATE* pEngineState) + { + DetectReset(&pEngineState->registration, &pEngineState->packages); + PlanReset(&pEngineState->plan, &pEngineState->packages); + + pEngineState->registration.fInstalled = TRUE; + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; + pPackage->cache = BURN_CACHE_STATE_COMPLETE; + + for (DWORD j = 0; j < pPackage->cPayloads; ++j) + { + pPackage->rgPayloads[j].fCached = TRUE; + } + } + } + + HRESULT DetectUpgradeBundle( + __in BURN_ENGINE_STATE* pEngineState, + __in LPCWSTR wzId, + __in LPCWSTR wzVersion + ) + { + HRESULT hr = S_OK; + BURN_RELATED_BUNDLES* pRelatedBundles = &pEngineState->registration.relatedBundles; + BURN_DEPENDENCY_PROVIDER dependencyProvider = { }; + + hr = StrAllocString(&dependencyProvider.sczKey, wzId, 0); + ExitOnFailure(hr, "Failed to copy provider key"); + + dependencyProvider.fImported = TRUE; + + hr = StrAllocString(&dependencyProvider.sczVersion, wzVersion, 0); + ExitOnFailure(hr, "Failed to copy version"); + + hr = MemEnsureArraySize(reinterpret_cast(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5); + ExitOnFailure(hr, "Failed to ensure there is space for related bundles."); + + BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles; + + hr = VerParseVersion(wzVersion, 0, FALSE, &pRelatedBundle->pVersion); + ExitOnFailure(hr, "Failed to parse pseudo bundle version: %ls", wzVersion); + + pRelatedBundle->relationType = BOOTSTRAPPER_RELATION_UPGRADE; + + hr = PseudoBundleInitialize(0, &pRelatedBundle->package, TRUE, wzId, pRelatedBundle->relationType, BOOTSTRAPPER_PACKAGE_STATE_PRESENT, NULL, NULL, NULL, 0, FALSE, L"-quiet", L"-repair -quiet", L"-uninstall -quiet", &dependencyProvider, NULL, 0); + ExitOnFailure(hr, "Failed to initialize related bundle to represent bundle: %ls", wzId); + + ++pRelatedBundles->cRelatedBundles; + + LExit: + return hr; + } + + void ValidateCacheAcquireContainer( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzContainerId, + __in BOOL fSkipUntilRetried + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pAction->type); + NativeAssert::StringEqual(wzContainerId, pAction->extractContainer.pContainer->sczId); + Assert::Equal(fSkipUntilRetried, pAction->fSkipUntilRetried); + } + + BURN_CACHE_ACTION* ValidateCacheActionExists(BURN_PLAN* pPlan, BOOL fRollback, DWORD dwIndex) + { + Assert::InRange(dwIndex + 1ul, 1ul, (fRollback ? pPlan->cRollbackCacheActions : pPlan->cCacheActions)); + return (fRollback ? pPlan->rgRollbackCacheActions : pPlan->rgCacheActions) + dwIndex; + } + + void ValidateCacheCachePayload( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId, + __in LPCWSTR wzPayloadId, + __in BOOL fMove, + __in BOOL fSkipUntilRetried, + __in DWORD iTryAgainAction + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->cachePayload.pPackage->sczId); + NativeAssert::StringEqual(wzPayloadId, pAction->cachePayload.pPayload->sczKey); + Assert::Equal(fMove, pAction->cachePayload.fMove); + Assert::Equal(fSkipUntilRetried, pAction->fSkipUntilRetried); + Assert::Equal(iTryAgainAction, pAction->cachePayload.iTryAgainAction); + } + + void ValidateCacheCheckpoint( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in DWORD dwId + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_CHECKPOINT, pAction->type); + Assert::Equal(dwId, pAction->checkpoint.dwId); + } + + void ValidateCacheExtractContainer( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzContainerId, + __in BOOL fSkipUntilRetried, + __in DWORD iSkipUntilAcquiredByAction, + __in DWORD cPayloads + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, pAction->type); + NativeAssert::StringEqual(wzContainerId, pAction->extractContainer.pContainer->sczId); + Assert::Equal(fSkipUntilRetried, pAction->fSkipUntilRetried); + Assert::Equal(iSkipUntilAcquiredByAction, pAction->extractContainer.iSkipUntilAcquiredByAction); + Assert::Equal(cPayloads, pAction->extractContainer.cPayloads); + } + + DWORD ValidateCachePackageStart( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId, + __in DWORD iPackageCompleteAction, + __in DWORD cCachePayloads, + __in DWORD64 qwCachePayloadSizeTotal, + __in BOOL fSkipUntilRetried + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_PACKAGE_START, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->packageStart.pPackage->sczId); + Assert::Equal(iPackageCompleteAction, pAction->packageStart.iPackageCompleteAction); + Assert::Equal(cCachePayloads, pAction->packageStart.cCachePayloads); + Assert::Equal(qwCachePayloadSizeTotal, pAction->packageStart.qwCachePayloadSizeTotal); + Assert::Equal(fSkipUntilRetried, pAction->fSkipUntilRetried); + return dwIndex + 1; + } + + void ValidateCachePackageStop( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId, + __in BOOL fSkipUntilRetried + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_PACKAGE_STOP, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->packageStop.pPackage->sczId); + Assert::Equal(fSkipUntilRetried, pAction->fSkipUntilRetried); + } + + void ValidateCacheRollbackPackage( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId, + __in BOOL fSkipUntilRetried + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->rollbackPackage.pPackage->sczId); + Assert::Equal(fSkipUntilRetried, pAction->fSkipUntilRetried); + } + + void ValidateCacheSignalSyncpoint( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in BOOL fSkipUntilRetried + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT, pAction->type); + Assert::NotEqual((DWORD_PTR)NULL, (DWORD_PTR)pAction->syncpoint.hEvent); + Assert::Equal(fSkipUntilRetried, pAction->fSkipUntilRetried); + } + + void ValidateCleanAction( + __in BURN_PLAN* pPlan, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId + ) + { + Assert::InRange(dwIndex + 1ul, 1ul, pPlan->cCleanActions); + + BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + dwIndex; + Assert::NotEqual((DWORD_PTR)0, (DWORD_PTR)pCleanAction->pPackage); + NativeAssert::StringEqual(wzPackageId, pCleanAction->pPackage->sczId); + } + + BURN_EXECUTE_ACTION* ValidateExecuteActionExists(BURN_PLAN* pPlan, BOOL fRollback, DWORD dwIndex) + { + Assert::InRange(dwIndex + 1ul, 1ul, (fRollback ? pPlan->cRollbackActions : pPlan->cExecuteActions)); + return (fRollback ? pPlan->rgRollbackActions : pPlan->rgExecuteActions) + dwIndex; + } + + void ValidateExecuteCheckpoint( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in DWORD dwId + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_CHECKPOINT, pAction->type); + Assert::Equal(dwId, pAction->checkpoint.dwId); + } + + void ValidateExecuteExePackage( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId, + __in BOOTSTRAPPER_ACTION_STATE action, + __in LPCWSTR wzIgnoreDependencies + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->exePackage.pPackage->sczId); + Assert::Equal(action, pAction->exePackage.action); + NativeAssert::StringEqual(wzIgnoreDependencies, pAction->exePackage.sczIgnoreDependencies); + } + + void ValidateExecuteMsiPackage( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId, + __in BOOTSTRAPPER_ACTION_STATE action, + __in BURN_MSI_PROPERTY actionMsiProperty, + __in DWORD uiLevel, + __in BOOL fDisableExternalUiHandler, + __in DWORD dwLoggingAttributes + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->msiPackage.pPackage->sczId); + Assert::Equal(action, pAction->msiPackage.action); + Assert::Equal(actionMsiProperty, pAction->msiPackage.actionMsiProperty); + Assert::Equal(uiLevel, pAction->msiPackage.uiLevel); + Assert::Equal(fDisableExternalUiHandler, pAction->msiPackage.fDisableExternalUiHandler); + NativeAssert::NotNull(pAction->msiPackage.sczLogPath); + Assert::Equal(dwLoggingAttributes, pAction->msiPackage.dwLoggingAttributes); + } + + void ValidateExecutePackageDependency( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId, + __in LPCWSTR wzBundleProviderKey, + __in BURN_DEPENDENCY_ACTION action + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->packageDependency.pPackage->sczId); + NativeAssert::StringEqual(wzBundleProviderKey, pAction->packageDependency.sczBundleProviderKey); + Assert::Equal(action, pAction->packageDependency.action); + } + + void ValidateExecutePackageProvider( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId, + __in BURN_DEPENDENCY_ACTION action + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->packageProvider.pPackage->sczId); + Assert::Equal(action, pAction->packageProvider.action); + } + + void ValidateExecuteRegistration( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in BOOL fKeep + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_REGISTRATION, pAction->type); + Assert::Equal(fKeep, pAction->registration.fKeep); + } + + void ValidateExecuteRollbackBoundary( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzId, + __in BOOL fVital, + __in BOOL fTransaction + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY, pAction->type); + NativeAssert::StringEqual(wzId, pAction->rollbackBoundary.pRollbackBoundary->sczId); + Assert::Equal(fVital, pAction->rollbackBoundary.pRollbackBoundary->fVital); + Assert::Equal(fTransaction, pAction->rollbackBoundary.pRollbackBoundary->fTransaction); + } + + void ValidateExecuteUncachePackage( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->uncachePackage.pPackage->sczId); + } + + void ValidateExecuteWaitSyncpoint( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in HANDLE hEvent + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT, pAction->type); + Assert::Equal((DWORD_PTR)hEvent, (DWORD_PTR)pAction->syncpoint.hEvent); + } + + void ValidatePlannedProvider( + __in BURN_PLAN* pPlan, + __in UINT uIndex, + __in LPCWSTR wzKey, + __in LPCWSTR wzName + ) + { + Assert::InRange(uIndex + 1u, 1u, pPlan->cPlannedProviders); + + DEPENDENCY* pProvider = pPlan->rgPlannedProviders + uIndex; + NativeAssert::StringEqual(wzKey, pProvider->sczKey); + NativeAssert::StringEqual(wzName, pProvider->sczName); + } + }; +} +} +} +} +} + +static HRESULT WINAPI PlanTestBAProc( + __in BOOTSTRAPPER_APPLICATION_MESSAGE /*message*/, + __in const LPVOID /*pvArgs*/, + __inout LPVOID /*pvResults*/, + __in_opt LPVOID /*pvContext*/ + ) +{ + return S_OK; +} diff --git a/src/test/BurnUnitTest/precomp.h b/src/test/BurnUnitTest/precomp.h index fea30156..ddbdf9c6 100644 --- a/src/test/BurnUnitTest/precomp.h +++ b/src/test/BurnUnitTest/precomp.h @@ -69,6 +69,7 @@ #include "manifest.h" #include "splashscreen.h" #include "bitsengine.h" +#include "detect.h" #pragma managed #include -- cgit v1.2.3-55-g6feb From 846f5a033d346c1bac51c56d4936cd3118ebea7a Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 15 Nov 2020 20:23:44 -0600 Subject: Clean up the elevation code for MSI transactions. --- src/engine/apply.cpp | 232 +++++++++++++++++------------------------------ src/engine/elevation.cpp | 178 ++++++++++++++++-------------------- src/engine/elevation.h | 17 ++-- src/engine/msiengine.cpp | 42 +++++++++ src/engine/msiengine.h | 5 + 5 files changed, 213 insertions(+), 261 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 9a0f64e1..909fb159 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -198,6 +198,19 @@ static HRESULT ExecuteCompatiblePackageAction( __in BURN_ENGINE_STATE* pEngineState, __in BURN_EXECUTE_ACTION* pAction ); +static HRESULT ExecuteMsiBeginTransaction( + __in BURN_ENGINE_STATE* pEngineState, + __in LPCWSTR wzName, + __in BURN_EXECUTE_CONTEXT* pContext + ); +static HRESULT ExecuteMsiCommitTransaction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_CONTEXT* pContext + ); +static HRESULT ExecuteMsiRollbackTransaction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_CONTEXT* pContext + ); static HRESULT CleanPackage( __in HANDLE hElevatedPipe, __in BURN_PACKAGE* pPackage @@ -228,30 +241,6 @@ static HRESULT ExecutePackageComplete( __out BOOL* pfSuspend ); -static HRESULT DoMsiBeginTransaction( - __in BURN_EXECUTE_CONTEXT *context - , __in BURN_ENGINE_STATE* pEngineState -); -static HRESULT DoMsiCommitTransaction( - __in BURN_EXECUTE_CONTEXT *context - , __in BURN_ENGINE_STATE* pEngineState -); -static HRESULT DoMsiRollbackTransaction( - __in BURN_EXECUTE_CONTEXT *context - , __in BURN_ENGINE_STATE* pEngineState -); -static HRESULT ExecuteMsiBeginTransaction( - __in BURN_EXECUTE_CONTEXT* pContext - , __in BURN_ENGINE_STATE* pEngineState -); -static HRESULT ExecuteMsiCommitTransaction( - __in BURN_EXECUTE_CONTEXT* pContext - , __in BURN_ENGINE_STATE* pEngineState -); -static HRESULT ExecuteMsiRollbackTransaction( - __in BURN_EXECUTE_CONTEXT* pContext - , __in BURN_ENGINE_STATE* pEngineState -); // function definitions @@ -773,7 +762,7 @@ extern "C" HRESULT ApplyExecute( if (fInTransaction) { LogString(REPORT_STANDARD, "Committing MSI transaction\n"); - hr = DoMsiCommitTransaction(&context, pEngineState); + hr = ExecuteMsiCommitTransaction(pEngineState, &context); ExitOnFailure(hr, "Failed committing an MSI transaction"); fInTransaction = FALSE; } @@ -789,7 +778,7 @@ extern "C" HRESULT ApplyExecute( else { LogString(REPORT_STANDARD, "Starting a new MSI transaction\n"); - hr = DoMsiBeginTransaction(&context, pEngineState); + hr = ExecuteMsiBeginTransaction(pEngineState, pExecuteAction->rollbackBoundary.pRollbackBoundary->sczId, &context); ExitOnFailure(hr, "Failed beginning an MSI transaction"); fInTransaction = TRUE; } @@ -855,7 +844,7 @@ extern "C" HRESULT ApplyExecute( if (fInTransaction) { LogString(REPORT_STANDARD, "Committing an MSI transaction\n"); - hr = DoMsiCommitTransaction(&context, pEngineState); + hr = ExecuteMsiCommitTransaction(pEngineState, &context); ExitOnFailure(hr, "Failed committing an MSI transaction"); fInTransaction = FALSE; } @@ -1658,127 +1647,6 @@ static void DoRollbackCache( } } -/* MSI Transactions: - * All MSI/MSP/MSU packages wrapped in MsiBeginTranasaction-MsiEndTransaction pair are installed or uninstalled together. -*/ -static HRESULT ExecuteMsiBeginTransaction( - __in BURN_EXECUTE_CONTEXT* pContext - , __in BURN_ENGINE_STATE* pEngineState -) -{ - HRESULT hr = S_OK; - UINT uResult = ERROR_SUCCESS; - - // Per user/machine context - if (pEngineState->plan.fPerMachine) - { - hr = ElevationMsiBeginTransaction(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pContext); - ExitOnFailure(hr, "Failed to begin an MSI transaction."); - } - else - { - MSIHANDLE hMsiTrns = NULL; - HANDLE hMsiTrnsEvent = NULL; - uResult = MsiBeginTransaction(L"WiX", 0, &hMsiTrns, &hMsiTrnsEvent); - ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction"); - } - -LExit: - return hr; -} - -static HRESULT ExecuteMsiCommitTransaction( - __in BURN_EXECUTE_CONTEXT* pContext - , __in BURN_ENGINE_STATE* pEngineState -) -{ - HRESULT hr = S_OK; - UINT uResult = ERROR_SUCCESS; - - // Per user/machine context - if (pEngineState->plan.fPerMachine) - { - hr = ElevationMsiCommitTransaction(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pContext); - ExitOnFailure(hr, "Failed to commit an MSI transaction."); - } - else - { - uResult = MsiEndTransaction(MSITRANSACTIONSTATE_COMMIT); - ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction"); - } - -LExit: - return hr; -} - -static HRESULT ExecuteMsiRollbackTransaction( - __in BURN_EXECUTE_CONTEXT* pContext - , __in BURN_ENGINE_STATE* pEngineState -) -{ - HRESULT hr = S_OK; - UINT uResult = ERROR_SUCCESS; - - // Per user/machine context - if (pEngineState->plan.fPerMachine) - { - hr = ElevationMsiRollbackTransaction(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pContext); - ExitOnFailure(hr, "Failed to rollback an MSI transaction."); - } - else - { - uResult = MsiEndTransaction(MSITRANSACTIONSTATE_ROLLBACK); - ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction"); - } - -LExit: - return hr; -} - -// Currently, supporting only elevated transactions. -static HRESULT DoMsiBeginTransaction( - __in BURN_EXECUTE_CONTEXT *pContext - , __in BURN_ENGINE_STATE* pEngineState -) -{ - HRESULT hr = S_OK; - - hr = ExecuteMsiBeginTransaction(pContext, pEngineState); - ExitOnFailure(hr, "Failed to execute EXE package."); - -LExit: - return hr; -} - -static HRESULT DoMsiCommitTransaction( - __in BURN_EXECUTE_CONTEXT *pContext - , __in BURN_ENGINE_STATE* pEngineState -) -{ - HRESULT hr = S_OK; - - hr = ExecuteMsiCommitTransaction(pContext, pEngineState); - ExitOnFailure(hr, "Failed to execute EXE package."); - -LExit: - return hr; -} - -static HRESULT DoMsiRollbackTransaction( - __in BURN_EXECUTE_CONTEXT *pContext - , __in BURN_ENGINE_STATE* pEngineState -) -{ - HRESULT hr = S_OK; - - hr = ExecuteMsiRollbackTransaction(pContext, pEngineState); - ExitOnFailure(hr, "Failed to execute EXE package."); - -LExit: - return hr; -} - - static HRESULT DoExecuteAction( __in BURN_ENGINE_STATE* pEngineState, __in BURN_EXECUTE_ACTION* pExecuteAction, @@ -1904,7 +1772,7 @@ static HRESULT DoRollbackActions( __in BOOL fInTransaction, __out BOOL* pfKeepRegistration, __out BOOTSTRAPPER_APPLY_RESTART* pRestart -) + ) { HRESULT hr = S_OK; DWORD iCheckpoint = 0; @@ -1916,7 +1784,7 @@ static HRESULT DoRollbackActions( // Rollback MSI transaction if (fInTransaction) { - hr = DoMsiRollbackTransaction(pContext, pEngineState); + hr = ExecuteMsiRollbackTransaction(pEngineState, pContext); ExitOnFailure(hr, "Failed rolling back transaction"); } @@ -2364,6 +2232,70 @@ LExit: return hr; } +static HRESULT ExecuteMsiBeginTransaction( + __in BURN_ENGINE_STATE* pEngineState, + __in LPCWSTR wzName, + __in BURN_EXECUTE_CONTEXT* /*pContext*/ + ) +{ + HRESULT hr = S_OK; + + if (pEngineState->plan.fPerMachine) + { + hr = ElevationMsiBeginTransaction(pEngineState->companionConnection.hPipe, wzName); + ExitOnFailure(hr, "Failed to begin an elevated MSI transaction."); + } + else + { + hr = MsiEngineBeginTransaction(wzName); + } + +LExit: + return hr; +} + +static HRESULT ExecuteMsiCommitTransaction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_CONTEXT* /*pContext*/ + ) +{ + HRESULT hr = S_OK; + + if (pEngineState->plan.fPerMachine) + { + hr = ElevationMsiCommitTransaction(pEngineState->companionConnection.hPipe); + ExitOnFailure(hr, "Failed to commit an elevated MSI transaction."); + } + else + { + hr = MsiEngineCommitTransaction(); + } + +LExit: + return hr; +} + +static HRESULT ExecuteMsiRollbackTransaction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_CONTEXT* /*pContext*/ + ) +{ + HRESULT hr = S_OK; + + if (pEngineState->plan.fPerMachine) + { + hr = ElevationMsiRollbackTransaction(pEngineState->companionConnection.hPipe); + ExitOnFailure(hr, "Failed to rollback an elevated MSI transaction."); + } + else + { + hr = MsiEngineRollbackTransaction(); + } + +LExit: + return hr; +} + static HRESULT CleanPackage( __in HANDLE hElevatedPipe, __in BURN_PACKAGE* pPackage diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp index 9ce04630..81a48316 100644 --- a/src/engine/elevation.cpp +++ b/src/engine/elevation.cpp @@ -28,17 +28,15 @@ typedef enum _BURN_ELEVATION_MESSAGE_TYPE BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_EMBEDDED_CHILD, BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE, BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE, + BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION, + BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION, + BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE, BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID, - - BURN_ELEVATION_TRANSACTION_BEGIN, - BURN_ELEVATION_TRANSACTION_COMMIT, - BURN_ELEVATION_TRANSACTION_ROLLBACK - } BURN_ELEVATION_MESSAGE_TYPE; @@ -74,24 +72,11 @@ typedef struct _BURN_ELEVATION_CHILD_MESSAGE_CONTEXT BURN_VARIABLES* pVariables; BURN_REGISTRATION* pRegistration; BURN_USER_EXPERIENCE* pUserExperience; - - MSIHANDLE hMsiTrns; - HANDLE hMsiTrnsEvent; } BURN_ELEVATION_CHILD_MESSAGE_CONTEXT; // internal function declarations -static HRESULT OnMsiBeginTransaction( - __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext -); -static HRESULT OnMsiCommitTransaction( - __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext -); -static HRESULT OnMsiRollbackTransaction( - __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext -); - static DWORD WINAPI ElevatedChildCacheThreadProc( __in LPVOID lpThreadParameter ); @@ -248,6 +233,13 @@ static HRESULT OnLaunchApprovedExe( __in BYTE* pbData, __in DWORD cbData ); +static HRESULT OnMsiBeginTransaction( + __in BYTE* pbData, + __in DWORD cbData + ); +static HRESULT OnMsiCommitTransaction(); +static HRESULT OnMsiRollbackTransaction(); + // function definitions @@ -726,62 +718,56 @@ LExit: extern "C" HRESULT ElevationMsiBeginTransaction( __in HANDLE hPipe, - __in_opt HWND hwndParent, - __in LPVOID pvContext -) + __in LPCWSTR wzName + ) { - UNREFERENCED_PARAMETER(hwndParent); HRESULT hr = S_OK; - BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = {}; + BYTE* pbData = NULL; + SIZE_T cbData = 0; DWORD dwResult = ERROR_SUCCESS; - context.pvContext = pvContext; + // serialize message data + hr = BuffWriteString(&pbData, &cbData, wzName); + ExitOnFailure(hr, "Failed to write transaction name to message buffer."); - hr = PipeSendMessage(hPipe, BURN_ELEVATION_TRANSACTION_BEGIN, NULL, 0, NULL, &context, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process."); - ExitOnWin32Error(dwResult, hr, "Failed beginning an elevated MSI transaction"); + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION, NULL, 0, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION message to per-machine process."); + + hr = static_cast(dwResult); LExit: + ReleaseBuffer(pbData); + return hr; } extern "C" HRESULT ElevationMsiCommitTransaction( - __in HANDLE hPipe, - __in_opt HWND hwndParent, - __in LPVOID pvContext -) + __in HANDLE hPipe + ) { - UNREFERENCED_PARAMETER(hwndParent); HRESULT hr = S_OK; - BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = {}; DWORD dwResult = ERROR_SUCCESS; - context.pvContext = pvContext; + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION, NULL, 0, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION message to per-machine process."); - hr = PipeSendMessage(hPipe, BURN_ELEVATION_TRANSACTION_COMMIT, NULL, 0, NULL, &context, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process."); - ExitOnWin32Error(dwResult, hr, "Failed committing an elevated MSI transaction"); + hr = static_cast(dwResult); LExit: return hr; } extern "C" HRESULT ElevationMsiRollbackTransaction( - __in HANDLE hPipe, - __in_opt HWND hwndParent, - __in LPVOID pvContext -) + __in HANDLE hPipe + ) { - UNREFERENCED_PARAMETER(hwndParent); HRESULT hr = S_OK; - BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = {}; DWORD dwResult = ERROR_SUCCESS; - context.pvContext = pvContext; + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION, NULL, 0, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION message to per-machine process."); - hr = PipeSendMessage(hPipe, BURN_ELEVATION_TRANSACTION_ROLLBACK, NULL, 0, NULL, &context, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process."); - ExitOnWin32Error(dwResult, hr, "Failed rolling back an elevated MSI transaction"); + hr = static_cast(dwResult); LExit: return hr; @@ -1543,16 +1529,16 @@ static HRESULT ProcessElevatedChildMessage( switch (pMsg->dwMessage) { - case BURN_ELEVATION_TRANSACTION_BEGIN: - hrResult = OnMsiBeginTransaction(pContext); + case BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION: + hrResult = OnMsiBeginTransaction((BYTE*)pMsg->pvData, pMsg->cbData); break; - case BURN_ELEVATION_TRANSACTION_COMMIT: - hrResult = OnMsiCommitTransaction(pContext); + case BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION: + hrResult = OnMsiCommitTransaction(); break; - case BURN_ELEVATION_TRANSACTION_ROLLBACK: - hrResult = OnMsiRollbackTransaction(pContext); + case BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION: + hrResult = OnMsiRollbackTransaction(); break; case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE: @@ -1690,53 +1676,6 @@ static HRESULT ProcessResult( return hr; } -static HRESULT OnMsiBeginTransaction( - __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext -) -{ - UINT uResult = ERROR_SUCCESS; - HRESULT hr = S_OK; - - pContext->hMsiTrns = NULL; - pContext->hMsiTrnsEvent = NULL; - uResult = MsiBeginTransaction(L"WiX", 0, &pContext->hMsiTrns, &pContext->hMsiTrnsEvent); - ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction"); - -LExit: - return hr; -} - -static HRESULT OnMsiCommitTransaction( - __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext -) -{ - UINT uResult = ERROR_SUCCESS; - HRESULT hr = S_OK; - - uResult = MsiEndTransaction(MSITRANSACTIONSTATE_COMMIT); - ExitOnWin32Error(uResult, hr, "Failed committing an MSI transaction"); - -LExit: - pContext->hMsiTrns = NULL; - pContext->hMsiTrnsEvent = NULL; - return hr; -} - -static HRESULT OnMsiRollbackTransaction( - __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext -) { - UINT uResult = ERROR_SUCCESS; - HRESULT hr = S_OK; - - uResult = MsiEndTransaction(MSITRANSACTIONSTATE_ROLLBACK); - ExitOnWin32Error(uResult, hr, "Failed rolling back an MSI transaction"); - -LExit: - pContext->hMsiTrns = NULL; - pContext->hMsiTrnsEvent = NULL; - return hr; -} - static HRESULT OnApplyInitialize( __in BURN_VARIABLES* pVariables, __in BURN_REGISTRATION* pRegistration, @@ -2845,3 +2784,42 @@ LExit: ApprovedExesUninitializeLaunch(pLaunchApprovedExe); return hr; } + +static HRESULT OnMsiBeginTransaction( + __in BYTE* pbData, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczName = NULL; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &sczName); + ExitOnFailure(hr, "Failed to read transaction name."); + + hr = MsiEngineBeginTransaction(sczName); + +LExit: + ReleaseStr(sczName); + + return hr; +} + +static HRESULT OnMsiCommitTransaction() +{ + HRESULT hr = S_OK; + + hr = MsiEngineCommitTransaction(); + + return hr; +} + +static HRESULT OnMsiRollbackTransaction() +{ + HRESULT hr = S_OK; + + hr = MsiEngineRollbackTransaction(); + + return hr; +} diff --git a/src/engine/elevation.h b/src/engine/elevation.h index d82d9b1c..975981da 100644 --- a/src/engine/elevation.h +++ b/src/engine/elevation.h @@ -159,19 +159,14 @@ HRESULT ElevationChildResumeAutomaticUpdates(); HRESULT ElevationMsiBeginTransaction( __in HANDLE hPipe, - __in_opt HWND hwndParent, - __in LPVOID pvContext -); + __in LPCWSTR wzName + ); HRESULT ElevationMsiCommitTransaction( - __in HANDLE hPipe, - __in_opt HWND hwndParent, - __in LPVOID pvContext -); + __in HANDLE hPipe + ); HRESULT ElevationMsiRollbackTransaction( - __in HANDLE hPipe, - __in_opt HWND hwndParent, - __in LPVOID pvContext -); + __in HANDLE hPipe + ); #ifdef __cplusplus } diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index c20e2ef8..fcd8817d 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -1137,6 +1137,48 @@ LExit: return hr; } +extern "C" HRESULT MsiEngineBeginTransaction( + __in LPCWSTR wzName + ) +{ + HRESULT hr = S_OK; + UINT uResult = ERROR_SUCCESS; + MSIHANDLE hTransactionHandle = NULL; + HANDLE hChangeOfOwnerEvent = NULL; + + uResult = ::MsiBeginTransaction(wzName, 0, &hTransactionHandle, &hChangeOfOwnerEvent); + ExitOnWin32Error(uResult, hr, "Failed to begin an MSI transaction"); + +LExit: + return hr; +} + +extern "C" HRESULT MsiEngineCommitTransaction() +{ + HRESULT hr = S_OK; + UINT uResult = ERROR_SUCCESS; + + uResult = ::MsiEndTransaction(MSITRANSACTIONSTATE_COMMIT); + ExitOnWin32Error(uResult, hr, "Failed to commit the MSI transaction"); + +LExit: + + return hr; +} + +extern "C" HRESULT MsiEngineRollbackTransaction() +{ + HRESULT hr = S_OK; + UINT uResult = ERROR_SUCCESS; + + uResult = ::MsiEndTransaction(MSITRANSACTIONSTATE_ROLLBACK); + ExitOnWin32Error(uResult, hr, "Failed to rollback the MSI transaction"); + +LExit: + + return hr; +} + extern "C" HRESULT MsiEngineExecutePackage( __in_opt HWND hwndParent, __in BURN_EXECUTE_ACTION* pExecuteAction, diff --git a/src/engine/msiengine.h b/src/engine/msiengine.h index 64bddcf0..63393006 100644 --- a/src/engine/msiengine.h +++ b/src/engine/msiengine.h @@ -52,6 +52,11 @@ HRESULT MsiEngineAddCompatiblePackage( __in const BURN_PACKAGE* pPackage, __out_opt BURN_PACKAGE** ppCompatiblePackage ); +HRESULT MsiEngineBeginTransaction( + __in LPCWSTR wzName + ); +HRESULT MsiEngineCommitTransaction(); +HRESULT MsiEngineRollbackTransaction(); HRESULT MsiEngineExecutePackage( __in_opt HWND hwndParent, __in BURN_EXECUTE_ACTION* pExecuteAction, -- cgit v1.2.3-55-g6feb From e71de85e4ec2899ecd01ac236603cf1dddc4a6c7 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 15 Nov 2020 21:38:57 -0600 Subject: Use plan to decide when to begin, commit, or rollback MSI transactions --- src/engine/apply.cpp | 194 ++++++++++++++++++------------------- src/engine/msiengine.cpp | 5 +- src/engine/msiengine.h | 1 + src/engine/mspengine.cpp | 5 +- src/engine/mspengine.h | 1 + src/engine/package.h | 1 + src/engine/plan.cpp | 86 ++++++++++++---- src/engine/plan.h | 20 +++- src/test/BurnUnitTest/PlanTest.cpp | 62 +++++++----- 9 files changed, 226 insertions(+), 149 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 909fb159..53422807 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -3,6 +3,12 @@ #include "precomp.h" +#ifdef DEBUG + #define IgnoreRollbackError(x, f, ...) if (FAILED(x)) { TraceError(x, f, __VA_ARGS__); } +#else + #define IgnoreRollbackError(x, f, ...) +#endif + const DWORD BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS = 2; // structs @@ -134,7 +140,7 @@ static HRESULT DoExecuteAction( __in_opt HANDLE hCacheThread, __in BURN_EXECUTE_CONTEXT* pContext, __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, - __out DWORD* pdwCheckpoint, + __inout BURN_EXECUTE_ACTION_CHECKPOINT** ppCheckpoint, __out BOOL* pfKeepRegistration, __out BOOL* pfSuspend, __out BOOTSTRAPPER_APPLY_RESTART* pRestart @@ -143,7 +149,6 @@ static HRESULT DoRollbackActions( __in BURN_ENGINE_STATE* pEngineState, __in BURN_EXECUTE_CONTEXT* pContext, __in DWORD dwCheckpoint, - __in BOOL fInTransaction, __out BOOL* pfKeepRegistration, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ); @@ -200,15 +205,17 @@ static HRESULT ExecuteCompatiblePackageAction( ); static HRESULT ExecuteMsiBeginTransaction( __in BURN_ENGINE_STATE* pEngineState, - __in LPCWSTR wzName, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, __in BURN_EXECUTE_CONTEXT* pContext ); static HRESULT ExecuteMsiCommitTransaction( __in BURN_ENGINE_STATE* pEngineState, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, __in BURN_EXECUTE_CONTEXT* pContext ); static HRESULT ExecuteMsiRollbackTransaction( __in BURN_ENGINE_STATE* pEngineState, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, __in BURN_EXECUTE_CONTEXT* pContext ); static HRESULT CleanPackage( @@ -732,11 +739,11 @@ extern "C" HRESULT ApplyExecute( ) { HRESULT hr = S_OK; - DWORD dwCheckpoint = 0; + HRESULT hrRollback = S_OK; + BURN_EXECUTE_ACTION_CHECKPOINT* pCheckpoint = NULL; BURN_EXECUTE_CONTEXT context = { }; BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; BOOL fSeekNextRollbackBoundary = FALSE; - BOOL fInTransaction = FALSE; context.pUX = &pEngineState->userExperience; context.cExecutePackagesTotal = pEngineState->plan.cExecutePackagesTotal; @@ -755,36 +762,6 @@ extern "C" HRESULT ApplyExecute( continue; } - // Transaction end/start - if (BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY == pExecuteAction->type) - { - // End previous transaction - if (fInTransaction) - { - LogString(REPORT_STANDARD, "Committing MSI transaction\n"); - hr = ExecuteMsiCommitTransaction(pEngineState, &context); - ExitOnFailure(hr, "Failed committing an MSI transaction"); - fInTransaction = FALSE; - } - - // Start New transaction - if (!fInTransaction && pExecuteAction->rollbackBoundary.pRollbackBoundary && pExecuteAction->rollbackBoundary.pRollbackBoundary->fTransaction) - { - // Transactions don't go together with DisableRollback. - if (pEngineState->fDisableRollback) - { - LogString(REPORT_STANDARD, "Ignoring Transaction flag due to DisableRollback flag\n"); - } - else - { - LogString(REPORT_STANDARD, "Starting a new MSI transaction\n"); - hr = ExecuteMsiBeginTransaction(pEngineState, pExecuteAction->rollbackBoundary.pRollbackBoundary->sczId, &context); - ExitOnFailure(hr, "Failed beginning an MSI transaction"); - fInTransaction = TRUE; - } - } - } - // If we are seeking the next rollback boundary, skip if this action wasn't it. if (fSeekNextRollbackBoundary) { @@ -799,11 +776,11 @@ extern "C" HRESULT ApplyExecute( } // Execute the action. - hr = DoExecuteAction(pEngineState, pExecuteAction, hCacheThread, &context, &pRollbackBoundary, &dwCheckpoint, pfKeepRegistration, pfSuspend, pRestart); + hr = DoExecuteAction(pEngineState, pExecuteAction, hCacheThread, &context, &pRollbackBoundary, &pCheckpoint, pfKeepRegistration, pfSuspend, pRestart); if (*pfSuspend || BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart) { - if (fInTransaction) + if (pCheckpoint && pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction) { hr = E_INVALIDSTATE; LogString(REPORT_ERROR, "Ilegal state: Reboot requested within an MSI transaction. Transaction will rollback."); @@ -816,37 +793,43 @@ extern "C" HRESULT ApplyExecute( if (FAILED(hr)) { - // If we failed, but rollback is disabled just bail with our error code. - if (pEngineState->fDisableRollback) + // If rollback is disabled, keep what we have and always end execution here. + if (pEngineState->plan.fDisableRollback) { + if (pCheckpoint && pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction) + { + hrRollback = ExecuteMsiCommitTransaction(pEngineState, pCheckpoint->pActiveRollbackBoundary, &context); + IgnoreRollbackError(hrRollback, "Failed commit transaction from disable rollback"); + } + *pfRollback = TRUE; break; } - else // the action failed, roll back to previous rollback boundary. + + // If inside a MSI transaction, roll it back. + if (pCheckpoint && pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction) { - HRESULT hrRollback = DoRollbackActions(pEngineState, &context, dwCheckpoint, fInTransaction, pfKeepRegistration, pRestart); - UNREFERENCED_PARAMETER(hrRollback); - fInTransaction = FALSE; + hrRollback = ExecuteMsiRollbackTransaction(pEngineState, pCheckpoint->pActiveRollbackBoundary, &context); + IgnoreRollbackError(hrRollback, "Failed rolling back transaction"); + } - // If the rollback boundary is vital, end execution here. - if (pRollbackBoundary && pRollbackBoundary->fVital) - { - *pfRollback = TRUE; - break; - } + // The action failed, roll back to previous rollback boundary. + if (pCheckpoint) + { + hrRollback = DoRollbackActions(pEngineState, &context, pCheckpoint->dwId, pfKeepRegistration, pRestart); + IgnoreRollbackError(hrRollback, "Failed rollback actions"); + } - // Move forward to next rollback boundary. - fSeekNextRollbackBoundary = TRUE; + // If the rollback boundary is vital, end execution here. + if (pRollbackBoundary && pRollbackBoundary->fVital) + { + *pfRollback = TRUE; + break; } - } - } - if (fInTransaction) - { - LogString(REPORT_STANDARD, "Committing an MSI transaction\n"); - hr = ExecuteMsiCommitTransaction(pEngineState, &context); - ExitOnFailure(hr, "Failed committing an MSI transaction"); - fInTransaction = FALSE; + // Move forward to next rollback boundary. + fSeekNextRollbackBoundary = TRUE; + } } LExit: @@ -1653,7 +1636,7 @@ static HRESULT DoExecuteAction( __in_opt HANDLE hCacheThread, __in BURN_EXECUTE_CONTEXT* pContext, __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, - __out DWORD* pdwCheckpoint, + __inout BURN_EXECUTE_ACTION_CHECKPOINT** ppCheckpoint, __out BOOL* pfKeepRegistration, __out BOOL* pfSuspend, __out BOOTSTRAPPER_APPLY_RESTART* pRestart @@ -1674,7 +1657,7 @@ static HRESULT DoExecuteAction( switch (pExecuteAction->type) { case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT: - *pdwCheckpoint = pExecuteAction->checkpoint.dwId; + *ppCheckpoint = &pExecuteAction->checkpoint; break; case BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT: @@ -1748,6 +1731,18 @@ static HRESULT DoExecuteAction( *ppRollbackBoundary = pExecuteAction->rollbackBoundary.pRollbackBoundary; break; + case BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION: + LogString(REPORT_STANDARD, "Starting a new MSI transaction\n"); + hr = ExecuteMsiBeginTransaction(pEngineState, pExecuteAction->msiTransaction.pRollbackBoundary, pContext); + ExitOnFailure(hr, "Failed to execute begin MSI transaction action."); + break; + + case BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION: + LogString(REPORT_STANDARD, "Committing MSI transaction\n"); + hr = ExecuteMsiCommitTransaction(pEngineState, pExecuteAction->msiTransaction.pRollbackBoundary, pContext); + ExitOnFailure(hr, "Failed to execute commit MSI transaction action."); + break; + case BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP: __fallthrough; case BURN_EXECUTE_ACTION_TYPE_SERVICE_START: __fallthrough; default: @@ -1769,7 +1764,6 @@ static HRESULT DoRollbackActions( __in BURN_ENGINE_STATE* pEngineState, __in BURN_EXECUTE_CONTEXT* pContext, __in DWORD dwCheckpoint, - __in BOOL fInTransaction, __out BOOL* pfKeepRegistration, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ) @@ -1781,13 +1775,6 @@ static HRESULT DoRollbackActions( pContext->fRollback = TRUE; - // Rollback MSI transaction - if (fInTransaction) - { - hr = ExecuteMsiRollbackTransaction(pEngineState, pContext); - ExitOnFailure(hr, "Failed rolling back transaction"); - } - // scan to last checkpoint for (DWORD i = 0; i < pEngineState->plan.cRollbackActions; ++i) { @@ -1827,53 +1814,32 @@ static HRESULT DoRollbackActions( case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: hr = ExecuteExePackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); - TraceError(hr, "Failed to rollback EXE package."); - hr = S_OK; + IgnoreRollbackError(hr, "Failed to rollback EXE package."); break; case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: - if (fInTransaction) - { - LogString(REPORT_STANDARD, "Skipping rolling back an MSI package- already done in transaction rollback\n"); - break; - } hr = ExecuteMsiPackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); - TraceError(hr, "Failed to rollback MSI package."); - hr = S_OK; + IgnoreRollbackError(hr, "Failed to rollback MSI package."); break; case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: - if (fInTransaction) - { - LogString(REPORT_STANDARD, "Skipping rolling back an MSP package- already done in transaction rollback\n"); - break; - } hr = ExecuteMspPackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); - TraceError(hr, "Failed to rollback MSP package."); - hr = S_OK; + IgnoreRollbackError(hr, "Failed to rollback MSP package."); break; case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: - if (fInTransaction) - { - LogString(REPORT_STANDARD, "Skipping rolling back an MSU package- already done in transaction rollback\n"); - break; - } hr = ExecuteMsuPackage(pEngineState, pRollbackAction, pContext, TRUE, FALSE, &fRetryIgnored, &fSuspendIgnored, &restart); - TraceError(hr, "Failed to rollback MSU package."); - hr = S_OK; + IgnoreRollbackError(hr, "Failed to rollback MSU package."); break; case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER: hr = ExecutePackageProviderAction(pEngineState, pRollbackAction, pContext); - TraceError(hr, "Failed to rollback package provider action."); - hr = S_OK; + IgnoreRollbackError(hr, "Failed to rollback package provider action."); break; case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: hr = ExecuteDependencyAction(pEngineState, pRollbackAction, pContext); - TraceError(hr, "Failed to rollback dependency action."); - hr = S_OK; + IgnoreRollbackError(hr, "Failed to rollback dependency action."); break; case BURN_EXECUTE_ACTION_TYPE_REGISTRATION: @@ -1885,6 +1851,7 @@ static HRESULT DoRollbackActions( case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE: hr = CleanPackage(pEngineState->companionConnection.hPipe, pRollbackAction->uncachePackage.pPackage); + IgnoreRollbackError(hr, "Failed to uncache package for rollback."); break; case BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP: __fallthrough; @@ -2234,20 +2201,30 @@ LExit: static HRESULT ExecuteMsiBeginTransaction( __in BURN_ENGINE_STATE* pEngineState, - __in LPCWSTR wzName, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, __in BURN_EXECUTE_CONTEXT* /*pContext*/ ) { HRESULT hr = S_OK; + if (pRollbackBoundary->fActiveTransaction) + { + ExitFunction1(hr = E_INVALIDSTATE); + } + if (pEngineState->plan.fPerMachine) { - hr = ElevationMsiBeginTransaction(pEngineState->companionConnection.hPipe, wzName); + hr = ElevationMsiBeginTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary->sczId); ExitOnFailure(hr, "Failed to begin an elevated MSI transaction."); } else { - hr = MsiEngineBeginTransaction(wzName); + hr = MsiEngineBeginTransaction(pRollbackBoundary->sczId); + } + + if (SUCCEEDED(hr)) + { + pRollbackBoundary->fActiveTransaction = TRUE; } LExit: @@ -2256,11 +2233,17 @@ LExit: static HRESULT ExecuteMsiCommitTransaction( __in BURN_ENGINE_STATE* pEngineState, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, __in BURN_EXECUTE_CONTEXT* /*pContext*/ ) { HRESULT hr = S_OK; + if (!pRollbackBoundary->fActiveTransaction) + { + ExitFunction1(hr = E_INVALIDSTATE); + } + if (pEngineState->plan.fPerMachine) { hr = ElevationMsiCommitTransaction(pEngineState->companionConnection.hPipe); @@ -2271,17 +2254,28 @@ static HRESULT ExecuteMsiCommitTransaction( hr = MsiEngineCommitTransaction(); } + if (SUCCEEDED(hr)) + { + pRollbackBoundary->fActiveTransaction = FALSE; + } + LExit: return hr; } static HRESULT ExecuteMsiRollbackTransaction( __in BURN_ENGINE_STATE* pEngineState, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, __in BURN_EXECUTE_CONTEXT* /*pContext*/ ) { HRESULT hr = S_OK; + if (!pRollbackBoundary->fActiveTransaction) + { + ExitFunction(); + } + if (pEngineState->plan.fPerMachine) { hr = ElevationMsiRollbackTransaction(pEngineState->companionConnection.hPipe); @@ -2293,6 +2287,8 @@ static HRESULT ExecuteMsiRollbackTransaction( } LExit: + pRollbackBoundary->fActiveTransaction = FALSE; + return hr; } diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index fcd8817d..b056cb7e 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -717,7 +717,8 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( __in BURN_PACKAGE* pPackage, __in BURN_VARIABLES* pVariables, __in BURN_USER_EXPERIENCE* pUserExperience, - __out BOOL* pfBARequestedCache + __in BOOL fInsideMsiTransaction, + __out_opt BOOL* pfBARequestedCache ) { Trace(REPORT_STANDARD, "Planning MSI package 0x%p", pPackage); @@ -853,7 +854,7 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( } // Calculate the rollback action if there is an execute action. - if (BOOTSTRAPPER_ACTION_STATE_NONE != execute) + if (BOOTSTRAPPER_ACTION_STATE_NONE != execute && !fInsideMsiTransaction) { switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState) { diff --git a/src/engine/msiengine.h b/src/engine/msiengine.h index 63393006..76030528 100644 --- a/src/engine/msiengine.h +++ b/src/engine/msiengine.h @@ -35,6 +35,7 @@ HRESULT MsiEnginePlanCalculatePackage( __in BURN_PACKAGE* pPackage, __in BURN_VARIABLES* pVariables, __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fInsideMsiTransaction, __out_opt BOOL* pfBARequestedCache ); HRESULT MsiEnginePlanAddPackage( diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp index 0854862b..e14173d1 100644 --- a/src/engine/mspengine.cpp +++ b/src/engine/mspengine.cpp @@ -266,6 +266,7 @@ LExit: extern "C" HRESULT MspEnginePlanCalculatePackage( __in BURN_PACKAGE* pPackage, __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fInsideMsiTransaction, __out BOOL* pfBARequestedCache ) { @@ -329,7 +330,7 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( } // Calculate the rollback action if there is an execute action. - if (BOOTSTRAPPER_ACTION_STATE_NONE != execute) + if (BOOTSTRAPPER_ACTION_STATE_NONE != execute && !fInsideMsiTransaction) { switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState) { @@ -442,7 +443,7 @@ extern "C" HRESULT MspEnginePlanAddPackage( if (BOOTSTRAPPER_ACTION_STATE_NONE != pTargetProduct->rollback) { hr = PlanTargetProduct(display, pUserExperience, TRUE, pPlan, pLog, pVariables, pTargetProduct->rollback, pPackage, pTargetProduct, hCacheEvent); - ExitOnFailure(hr, "Failed to plan rollack target product."); + ExitOnFailure(hr, "Failed to plan rollback target product."); } } diff --git a/src/engine/mspengine.h b/src/engine/mspengine.h index 1f0c31df..e08fe992 100644 --- a/src/engine/mspengine.h +++ b/src/engine/mspengine.h @@ -35,6 +35,7 @@ HRESULT MspEngineDetectPackage( HRESULT MspEnginePlanCalculatePackage( __in BURN_PACKAGE* pPackage, __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fInsideMsiTransaction, __out_opt BOOL* pfBARequestedCache ); HRESULT MspEnginePlanAddPackage( diff --git a/src/engine/package.h b/src/engine/package.h index c5873765..f3e817eb 100644 --- a/src/engine/package.h +++ b/src/engine/package.h @@ -155,6 +155,7 @@ typedef struct _BURN_ROLLBACK_BOUNDARY LPWSTR sczId; BOOL fVital; BOOL fTransaction; + BOOL fActiveTransaction; // only valid during Apply. } BURN_ROLLBACK_BOUNDARY; typedef struct _BURN_PATCH_TARGETCODE diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index e2a3437d..22b7033e 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -23,9 +23,12 @@ static void UninitializeCacheAction( static void ResetPlannedPackageState( __in BURN_PACKAGE* pPackage ); +static void ResetPlannedRollbackBoundaryState( + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary + ); static HRESULT ProcessPackage( __in BOOL fBundlePerMachine, - __in BURN_PACKAGE* pCompatiblePackageParent, + __in_opt BURN_PACKAGE* pCompatiblePackageParent, __in BURN_USER_EXPERIENCE* pUX, __in BURN_PLAN* pPlan, __in BURN_PACKAGE* pPackage, @@ -153,6 +156,7 @@ static HRESULT CalculateExecuteActions( __in BURN_USER_EXPERIENCE* pUserExperience, __in BURN_PACKAGE* pPackage, __in BURN_VARIABLES* pVariables, + __in_opt BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary, __out_opt BOOL* pfBARequestedCache ); static BOOL NeedsCache( @@ -263,6 +267,15 @@ extern "C" void PlanReset( ResetPlannedPackageState(&pPackages->rgPackages[i]); } } + + // Reset the planned state for each rollback boundary. + if (pPackages->rgRollbackBoundaries) + { + for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i) + { + ResetPlannedRollbackBoundaryState(&pPackages->rgRollbackBoundaries[i]); + } + } } extern "C" void PlanUninitializeExecuteAction( @@ -853,7 +866,7 @@ LExit: static HRESULT ProcessPackage( __in BOOL fBundlePerMachine, - __in BURN_PACKAGE* pCompatiblePackageParent, + __in_opt BURN_PACKAGE* pCompatiblePackageParent, __in BURN_USER_EXPERIENCE* pUX, __in BURN_PLAN* pPlan, __in BURN_PACKAGE* pPackage, @@ -1069,7 +1082,7 @@ extern "C" HRESULT PlanCachePackage( BOOL fBARequestedCache = FALSE; // Calculate the execute actions because we need them to decide whether the package should be cached. - hr = CalculateExecuteActions(pUserExperience, pPackage, pVariables, &fBARequestedCache); + hr = CalculateExecuteActions(pUserExperience, pPackage, pVariables, pPlan->pActiveRollbackBoundary, &fBARequestedCache); ExitOnFailure(hr, "Failed to calculate execute actions for package: %ls", pPackage->sczId); if (fBARequestedCache || NeedsCache(pPlan, pPackage)) @@ -1105,7 +1118,7 @@ extern "C" HRESULT PlanExecutePackage( HRESULT hr = S_OK; BOOL fBARequestedCache = FALSE; - hr = CalculateExecuteActions(pUserExperience, pPackage, pVariables, &fBARequestedCache); + hr = CalculateExecuteActions(pUserExperience, pPackage, pVariables, pPlan->pActiveRollbackBoundary, &fBARequestedCache); ExitOnFailure(hr, "Failed to calculate plan actions for package: %ls", pPackage->sczId); // Calculate package states based on reference count and plan certain dependency actions prior to planning the package execute action. @@ -1631,6 +1644,7 @@ extern "C" HRESULT PlanExecuteCheckpoint( pAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT; pAction->checkpoint.dwId = dwCheckpointId; + pAction->checkpoint.pActiveRollbackBoundary = pPlan->pActiveRollbackBoundary; // rollback checkpoint hr = PlanAppendRollbackAction(pPlan, &pAction); @@ -1638,6 +1652,7 @@ extern "C" HRESULT PlanExecuteCheckpoint( pAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT; pAction->checkpoint.dwId = dwCheckpointId; + pAction->checkpoint.pActiveRollbackBoundary = pPlan->pActiveRollbackBoundary; LExit: return hr; @@ -1783,6 +1798,9 @@ extern "C" HRESULT PlanRollbackBoundaryBegin( HRESULT hr = S_OK; BURN_EXECUTE_ACTION* pExecuteAction = NULL; + AssertSz(!pPlan->pActiveRollbackBoundary, "PlanRollbackBoundaryBegin called without completing previous RollbackBoundary"); + pPlan->pActiveRollbackBoundary = pRollbackBoundary; + // Add begin rollback boundary to execute plan. hr = PlanAppendExecuteAction(pPlan, &pExecuteAction); ExitOnFailure(hr, "Failed to append rollback boundary begin action."); @@ -1797,6 +1815,19 @@ extern "C" HRESULT PlanRollbackBoundaryBegin( pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY; pExecuteAction->rollbackBoundary.pRollbackBoundary = pRollbackBoundary; + // Add begin MSI transaction to execute plan. + if (pRollbackBoundary->fTransaction) + { + hr = PlanExecuteCheckpoint(pPlan); + ExitOnFailure(hr, "Failed to append checkpoint before MSI transaction begin action."); + + hr = PlanAppendExecuteAction(pPlan, &pExecuteAction); + ExitOnFailure(hr, "Failed to append MSI transaction begin action."); + + pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION; + pExecuteAction->msiTransaction.pRollbackBoundary = pRollbackBoundary; + } + LExit: return hr; } @@ -1807,22 +1838,24 @@ extern "C" HRESULT PlanRollbackBoundaryComplete( { HRESULT hr = S_OK; BURN_EXECUTE_ACTION* pExecuteAction = NULL; - DWORD dwCheckpointId = 0; + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = pPlan->pActiveRollbackBoundary; - // Add checkpoints. - dwCheckpointId = GetNextCheckpointId(pPlan); + AssertSz(pRollbackBoundary, "PlanRollbackBoundaryComplete called without an active RollbackBoundary"); - hr = PlanAppendExecuteAction(pPlan, &pExecuteAction); - ExitOnFailure(hr, "Failed to append execute action."); + if (pRollbackBoundary && pRollbackBoundary->fTransaction) + { + // Add commit MSI transaction to execute plan. + hr = PlanAppendExecuteAction(pPlan, &pExecuteAction); + ExitOnFailure(hr, "Failed to append MSI transaction commit action."); - pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT; - pExecuteAction->checkpoint.dwId = dwCheckpointId; + pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION; + pExecuteAction->msiTransaction.pRollbackBoundary = pRollbackBoundary; + } - hr = PlanAppendRollbackAction(pPlan, &pExecuteAction); - ExitOnFailure(hr, "Failed to append rollback action."); + pPlan->pActiveRollbackBoundary = NULL; - pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT; - pExecuteAction->checkpoint.dwId = dwCheckpointId; + // Add checkpoints. + hr = PlanExecuteCheckpoint(pPlan); LExit: return hr; @@ -1936,6 +1969,13 @@ static void ResetPlannedPackageState( } } +static void ResetPlannedRollbackBoundaryState( + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary + ) +{ + pRollbackBoundary->fActiveTransaction = FALSE; +} + static HRESULT GetActionDefaultRequestState( __in BOOTSTRAPPER_ACTION action, __in BOOL fPermanent, @@ -2848,10 +2888,12 @@ static HRESULT CalculateExecuteActions( __in BURN_USER_EXPERIENCE* pUserExperience, __in BURN_PACKAGE* pPackage, __in BURN_VARIABLES* pVariables, + __in_opt BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary, __out_opt BOOL* pfBARequestedCache ) { HRESULT hr = S_OK; + BOOL fInsideMsiTransaction = pActiveRollbackBoundary && pActiveRollbackBoundary->fTransaction; // Calculate execute actions. switch (pPackage->type) @@ -2861,11 +2903,11 @@ static HRESULT CalculateExecuteActions( break; case BURN_PACKAGE_TYPE_MSI: - hr = MsiEnginePlanCalculatePackage(pPackage, pVariables, pUserExperience, pfBARequestedCache); + hr = MsiEnginePlanCalculatePackage(pPackage, pVariables, pUserExperience, fInsideMsiTransaction, pfBARequestedCache); break; case BURN_PACKAGE_TYPE_MSP: - hr = MspEnginePlanCalculatePackage(pPackage, pUserExperience, pfBARequestedCache); + hr = MspEnginePlanCalculatePackage(pPackage, pUserExperience, fInsideMsiTransaction, pfBARequestedCache); break; case BURN_PACKAGE_TYPE_MSU: @@ -3065,7 +3107,7 @@ static void ExecuteActionLog( switch (pAction->type) { case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: CHECKPOINT id: %u", wzBase, iAction, pAction->checkpoint.dwId); + LogStringLine(REPORT_STANDARD, "%ls action[%u]: CHECKPOINT id: %u, msi transaction id: %ls", wzBase, iAction, pAction->checkpoint.dwId, pAction->checkpoint.pActiveRollbackBoundary && pAction->checkpoint.pActiveRollbackBoundary->fTransaction ? pAction->checkpoint.pActiveRollbackBoundary->sczId : L"(none)"); break; case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER: @@ -3120,6 +3162,14 @@ static void ExecuteActionLog( LogStringLine(REPORT_STANDARD, "%ls action[%u]: COMPATIBLE_PACKAGE reference id: %ls, installed ProductCode: %ls", wzBase, iAction, pAction->compatiblePackage.pReferencePackage->sczId, pAction->compatiblePackage.sczInstalledProductCode); break; + case BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: BEGIN_MSI_TRANSACTION id: %ls", wzBase, iAction, pAction->msiTransaction.pRollbackBoundary->sczId); + break; + + case BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION: + LogStringLine(REPORT_STANDARD, "%ls action[%u]: COMMIT_MSI_TRANSACTION id: %ls", wzBase, iAction, pAction->msiTransaction.pRollbackBoundary->sczId); + break; + default: AssertSz(FALSE, "Unknown execute action type."); break; diff --git a/src/engine/plan.h b/src/engine/plan.h index 5fddd72f..407c1d48 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -68,6 +68,8 @@ enum BURN_EXECUTE_ACTION_TYPE BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY, BURN_EXECUTE_ACTION_TYPE_REGISTRATION, BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE, + BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION, + BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION, }; enum BURN_CLEAN_ACTION_TYPE @@ -214,16 +216,19 @@ typedef struct _BURN_ORDERED_PATCHES BURN_PACKAGE* pPackage; } BURN_ORDERED_PATCHES; +typedef struct _BURN_EXECUTE_ACTION_CHECKPOINT +{ + DWORD dwId; + BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary; +} BURN_EXECUTE_ACTION_CHECKPOINT; + typedef struct _BURN_EXECUTE_ACTION { BURN_EXECUTE_ACTION_TYPE type; BOOL fDeleted; // used to skip an action after it was planned since deleting actions out of the plan is too hard. union { - struct - { - DWORD dwId; - } checkpoint; + BURN_EXECUTE_ACTION_CHECKPOINT checkpoint; struct { HANDLE hEvent; @@ -307,6 +312,10 @@ typedef struct _BURN_EXECUTE_ACTION LPWSTR sczInstalledProductCode; VERUTIL_VERSION* pInstalledVersion; } compatiblePackage; + struct + { + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary; + } msiTransaction; }; } BURN_EXECUTE_ACTION; @@ -368,7 +377,8 @@ typedef struct _BURN_PLAN DWORD cPayloadProgress; STRINGDICT_HANDLE shPayloadProgress; - DWORD dwNextCheckpointId; + DWORD dwNextCheckpointId; // for plan internal use + BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary; // for plan internal use } BURN_PLAN; diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp index 86bc646b..d3b10678 100644 --- a/src/test/BurnUnitTest/PlanTest.cpp +++ b/src/test/BurnUnitTest/PlanTest.cpp @@ -173,7 +173,7 @@ namespace Bootstrapper ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"cab1QmlL013Hqv_44W64R0cvnHn_2c", TRUE, FALSE, dwPackageStart); ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); - ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 8); + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 9); dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageB", 14, 2, 33753, FALSE); ValidateCacheAcquireContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", TRUE); ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, dwPackageStart, 2); @@ -195,10 +195,6 @@ namespace Bootstrapper dwIndex = 0; ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); - ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 8); - ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"PackageB", FALSE); - ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 14); - ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"PackageC", FALSE); Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); Assert::Equal(106166ull, pPlan->qwEstimatedSize); @@ -220,30 +216,31 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteBeginMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[15].syncpoint.hEvent); - dwExecuteCheckpointId = 9; + dwExecuteCheckpointId += 1; // cache checkpoints ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[23].syncpoint.hEvent); - dwExecuteCheckpointId = 15; + dwExecuteCheckpointId += 1; // cache checkpoints ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCommitMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[23].syncpoint.hEvent); ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL); - Assert::Equal(34ul, pPlan->cExecuteActions); + Assert::Equal(dwIndex, pPlan->cExecuteActions); fRollback = TRUE; dwIndex = 0; @@ -261,29 +258,26 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageB"); - dwExecuteCheckpointId = 9; + dwExecuteCheckpointId += 1; // cache checkpoints ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageC"); - dwExecuteCheckpointId = 15; + dwExecuteCheckpointId += 1; // cache checkpoints ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL); - Assert::Equal(33ul, pPlan->cRollbackActions); + Assert::Equal(dwIndex, pPlan->cRollbackActions); Assert::Equal(4ul, pPlan->cExecutePackagesTotal); Assert::Equal(7ul, pPlan->cOverallProgressTicksTotal); @@ -331,19 +325,20 @@ namespace Bootstrapper DWORD dwExecuteCheckpointId = 1; ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteBeginMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCommitMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -361,19 +356,16 @@ namespace Bootstrapper dwIndex = 0; dwExecuteCheckpointId = 1; ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); @@ -818,6 +810,18 @@ namespace Bootstrapper return (fRollback ? pPlan->rgRollbackActions : pPlan->rgExecuteActions) + dwIndex; } + void ValidateExecuteBeginMsiTransaction( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzRollbackBoundaryId + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION, pAction->type); + NativeAssert::StringEqual(wzRollbackBoundaryId, pAction->msiTransaction.pRollbackBoundary->sczId); + } + void ValidateExecuteCheckpoint( __in BURN_PLAN* pPlan, __in BOOL fRollback, @@ -830,6 +834,18 @@ namespace Bootstrapper Assert::Equal(dwId, pAction->checkpoint.dwId); } + void ValidateExecuteCommitMsiTransaction( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzRollbackBoundaryId + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION, pAction->type); + NativeAssert::StringEqual(wzRollbackBoundaryId, pAction->msiTransaction.pRollbackBoundary->sczId); + } + void ValidateExecuteExePackage( __in BURN_PLAN* pPlan, __in BOOL fRollback, -- cgit v1.2.3-55-g6feb From 7a942746bc535d319bdfa7f17025347ac6913ba2 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 16 Nov 2020 16:47:07 -0600 Subject: Ignore C26812 warning for C style enums. --- src/Cpp.Build.props | 4 ++++ src/CustomizedNativeRecommendedRules.ruleset | 8 ++++++++ src/engine/engine.vcxproj | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 src/CustomizedNativeRecommendedRules.ruleset diff --git a/src/Cpp.Build.props b/src/Cpp.Build.props index ebbf9f71..4d2da36f 100644 --- a/src/Cpp.Build.props +++ b/src/Cpp.Build.props @@ -16,6 +16,10 @@ $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) + + $(MSBuildThisFileDirectory)CustomizedNativeRecommendedRules.ruleset + + $(DisableSpecificCompilerWarnings) diff --git a/src/CustomizedNativeRecommendedRules.ruleset b/src/CustomizedNativeRecommendedRules.ruleset new file mode 100644 index 00000000..142b141c --- /dev/null +++ b/src/CustomizedNativeRecommendedRules.ruleset @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index 84717941..28085ed4 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -175,4 +175,4 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" - + \ No newline at end of file -- cgit v1.2.3-55-g6feb From 7d45238e97c35ccea1f77be9065b9a3ed9213bfb Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 16 Nov 2020 19:05:29 -0600 Subject: Add logging and BA messages around MSI transactions. --- .../inc/BootstrapperApplication.h | 77 +++++++++++ src/engine/apply.cpp | 47 +++++-- src/engine/elevation.cpp | 70 ++++++++-- src/engine/elevation.h | 6 +- src/engine/engine.mc | 42 ++++++ src/engine/msiengine.cpp | 20 ++- src/engine/msiengine.h | 8 +- src/engine/userexperience.cpp | 142 +++++++++++++++++++++ src/engine/userexperience.h | 33 ++++- 9 files changed, 414 insertions(+), 31 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h index 77d5b2c6..cfbb0571 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -135,6 +135,12 @@ enum BOOTSTRAPPER_APPLICATION_MESSAGE BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXEBEGIN, BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXECOMPLETE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIPACKAGE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONBEGINMSITRANSACTIONBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONBEGINMSITRANSACTIONCOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONCOMMITMSITRANSACTIONBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONCOMMITMSITRANSACTIONCOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONROLLBACKMSITRANSACTIONBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONROLLBACKMSITRANSACTIONCOMPLETE, }; enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION @@ -274,6 +280,30 @@ struct BA_ONAPPLYCOMPLETE_RESULTS BOOTSTRAPPER_APPLYCOMPLETE_ACTION action; }; +struct BA_ONBEGINMSITRANSACTIONBEGIN_ARGS +{ + DWORD cbSize; + LPCWSTR wzTransactionId; +}; + +struct BA_ONBEGINMSITRANSACTIONBEGIN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONBEGINMSITRANSACTIONCOMPLETE_ARGS +{ + DWORD cbSize; + LPCWSTR wzTransactionId; + HRESULT hrStatus; +}; + +struct BA_ONBEGINMSITRANSACTIONCOMPLETE_RESULTS +{ + DWORD cbSize; +}; + struct BA_ONCACHEACQUIREBEGIN_ARGS { DWORD cbSize; @@ -398,6 +428,30 @@ struct BA_ONCACHEVERIFYCOMPLETE_RESULTS BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action; }; +struct BA_ONCOMMITMSITRANSACTIONBEGIN_ARGS +{ + DWORD cbSize; + LPCWSTR wzTransactionId; +}; + +struct BA_ONCOMMITMSITRANSACTIONBEGIN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONCOMMITMSITRANSACTIONCOMPLETE_ARGS +{ + DWORD cbSize; + LPCWSTR wzTransactionId; + HRESULT hrStatus; +}; + +struct BA_ONCOMMITMSITRANSACTIONCOMPLETE_RESULTS +{ + DWORD cbSize; +}; + struct BA_ONDETECTBEGIN_ARGS { DWORD cbSize; @@ -963,6 +1017,29 @@ struct BA_ONRESOLVESOURCE_RESULTS BOOL fCancel; }; +struct BA_ONROLLBACKMSITRANSACTIONBEGIN_ARGS +{ + DWORD cbSize; + LPCWSTR wzTransactionId; +}; + +struct BA_ONROLLBACKMSITRANSACTIONBEGIN_RESULTS +{ + DWORD cbSize; +}; + +struct BA_ONROLLBACKMSITRANSACTIONCOMPLETE_ARGS +{ + DWORD cbSize; + LPCWSTR wzTransactionId; + HRESULT hrStatus; +}; + +struct BA_ONROLLBACKMSITRANSACTIONCOMPLETE_RESULTS +{ + DWORD cbSize; +}; + struct BA_ONSHUTDOWN_ARGS { DWORD cbSize; diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 53422807..833d750c 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -783,7 +783,7 @@ extern "C" HRESULT ApplyExecute( if (pCheckpoint && pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction) { hr = E_INVALIDSTATE; - LogString(REPORT_ERROR, "Ilegal state: Reboot requested within an MSI transaction. Transaction will rollback."); + LogId(REPORT_ERROR, MSG_RESTART_REQUEST_DURING_MSI_TRANSACTION, pCheckpoint->pActiveRollbackBoundary->sczId); } else { @@ -796,6 +796,8 @@ extern "C" HRESULT ApplyExecute( // If rollback is disabled, keep what we have and always end execution here. if (pEngineState->plan.fDisableRollback) { + LogId(REPORT_WARNING, MSG_PLAN_ROLLBACK_DISABLED); + if (pCheckpoint && pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction) { hrRollback = ExecuteMsiCommitTransaction(pEngineState, pCheckpoint->pActiveRollbackBoundary, &context); @@ -1732,13 +1734,11 @@ static HRESULT DoExecuteAction( break; case BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION: - LogString(REPORT_STANDARD, "Starting a new MSI transaction\n"); hr = ExecuteMsiBeginTransaction(pEngineState, pExecuteAction->msiTransaction.pRollbackBoundary, pContext); ExitOnFailure(hr, "Failed to execute begin MSI transaction action."); break; case BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION: - LogString(REPORT_STANDARD, "Committing MSI transaction\n"); hr = ExecuteMsiCommitTransaction(pEngineState, pExecuteAction->msiTransaction.pRollbackBoundary, pContext); ExitOnFailure(hr, "Failed to execute commit MSI transaction action."); break; @@ -1899,7 +1899,7 @@ static HRESULT ExecuteExePackage( fBeginCalled = TRUE; // Send package execute begin to BA. - hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->exePackage.pPackage->sczId, !fRollback, pExecuteAction->exePackage.action, INSTALLUILEVEL_NOCHANGE, false); + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->exePackage.pPackage->sczId, !fRollback, pExecuteAction->exePackage.action, INSTALLUILEVEL_NOCHANGE, FALSE); ExitOnRootFailure(hr, "BA aborted execute EXE package begin."); message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; @@ -2090,7 +2090,7 @@ static HRESULT ExecuteMsuPackage( fBeginCalled = TRUE; // Send package execute begin to BA. - hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msuPackage.pPackage->sczId, !fRollback, pExecuteAction->msuPackage.action, INSTALLUILEVEL_NOCHANGE, false); + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msuPackage.pPackage->sczId, !fRollback, pExecuteAction->msuPackage.action, INSTALLUILEVEL_NOCHANGE, FALSE); ExitOnRootFailure(hr, "BA aborted execute MSU package begin."); message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; @@ -2206,12 +2206,17 @@ static HRESULT ExecuteMsiBeginTransaction( ) { HRESULT hr = S_OK; + BOOL fBeginCalled = FALSE; if (pRollbackBoundary->fActiveTransaction) { ExitFunction1(hr = E_INVALIDSTATE); } + fBeginCalled = TRUE; + hr = UserExperienceOnBeginMsiTransactionBegin(&pEngineState->userExperience, pRollbackBoundary->sczId); + ExitOnRootFailure(hr, "BA aborted execute begin MSI transaction."); + if (pEngineState->plan.fPerMachine) { hr = ElevationMsiBeginTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary->sczId); @@ -2228,6 +2233,11 @@ static HRESULT ExecuteMsiBeginTransaction( } LExit: + if (fBeginCalled) + { + UserExperienceOnBeginMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr); + } + return hr; } @@ -2238,20 +2248,25 @@ static HRESULT ExecuteMsiCommitTransaction( ) { HRESULT hr = S_OK; + BOOL fBeginCalled = FALSE; if (!pRollbackBoundary->fActiveTransaction) { ExitFunction1(hr = E_INVALIDSTATE); } + fBeginCalled = TRUE; + hr = UserExperienceOnCommitMsiTransactionBegin(&pEngineState->userExperience, pRollbackBoundary->sczId); + ExitOnRootFailure(hr, "BA aborted execute commit MSI transaction."); + if (pEngineState->plan.fPerMachine) { - hr = ElevationMsiCommitTransaction(pEngineState->companionConnection.hPipe); + hr = ElevationMsiCommitTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary->sczId); ExitOnFailure(hr, "Failed to commit an elevated MSI transaction."); } else { - hr = MsiEngineCommitTransaction(); + hr = MsiEngineCommitTransaction(pRollbackBoundary->sczId); } if (SUCCEEDED(hr)) @@ -2260,6 +2275,11 @@ static HRESULT ExecuteMsiCommitTransaction( } LExit: + if (fBeginCalled) + { + UserExperienceOnCommitMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr); + } + return hr; } @@ -2270,25 +2290,34 @@ static HRESULT ExecuteMsiRollbackTransaction( ) { HRESULT hr = S_OK; + BOOL fBeginCalled = FALSE; if (!pRollbackBoundary->fActiveTransaction) { ExitFunction(); } + fBeginCalled = TRUE; + UserExperienceOnRollbackMsiTransactionBegin(&pEngineState->userExperience, pRollbackBoundary->sczId); + if (pEngineState->plan.fPerMachine) { - hr = ElevationMsiRollbackTransaction(pEngineState->companionConnection.hPipe); + hr = ElevationMsiRollbackTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary->sczId); ExitOnFailure(hr, "Failed to rollback an elevated MSI transaction."); } else { - hr = MsiEngineRollbackTransaction(); + hr = MsiEngineRollbackTransaction(pRollbackBoundary->sczId); } LExit: pRollbackBoundary->fActiveTransaction = FALSE; + if (fBeginCalled) + { + UserExperienceOnRollbackMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr); + } + return hr; } diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp index 81a48316..1d0e1f1d 100644 --- a/src/engine/elevation.cpp +++ b/src/engine/elevation.cpp @@ -237,8 +237,14 @@ static HRESULT OnMsiBeginTransaction( __in BYTE* pbData, __in DWORD cbData ); -static HRESULT OnMsiCommitTransaction(); -static HRESULT OnMsiRollbackTransaction(); +static HRESULT OnMsiCommitTransaction( + __in BYTE* pbData, + __in DWORD cbData + ); +static HRESULT OnMsiRollbackTransaction( + __in BYTE* pbData, + __in DWORD cbData + ); @@ -730,7 +736,7 @@ extern "C" HRESULT ElevationMsiBeginTransaction( hr = BuffWriteString(&pbData, &cbData, wzName); ExitOnFailure(hr, "Failed to write transaction name to message buffer."); - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION, NULL, 0, NULL, NULL, &dwResult); + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION, pbData, cbData, NULL, NULL, &dwResult); ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION message to per-machine process."); hr = static_cast(dwResult); @@ -742,13 +748,20 @@ LExit: } extern "C" HRESULT ElevationMsiCommitTransaction( - __in HANDLE hPipe + __in HANDLE hPipe, + __in LPCWSTR wzName ) { HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; DWORD dwResult = ERROR_SUCCESS; - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION, NULL, 0, NULL, NULL, &dwResult); + // serialize message data + hr = BuffWriteString(&pbData, &cbData, wzName); + ExitOnFailure(hr, "Failed to write transaction name to message buffer."); + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION, pbData, cbData, NULL, NULL, &dwResult); ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION message to per-machine process."); hr = static_cast(dwResult); @@ -758,13 +771,20 @@ LExit: } extern "C" HRESULT ElevationMsiRollbackTransaction( - __in HANDLE hPipe + __in HANDLE hPipe, + __in LPCWSTR wzName ) { HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; DWORD dwResult = ERROR_SUCCESS; - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION, NULL, 0, NULL, NULL, &dwResult); + // serialize message data + hr = BuffWriteString(&pbData, &cbData, wzName); + ExitOnFailure(hr, "Failed to write transaction name to message buffer."); + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION, pbData, cbData, NULL, NULL, &dwResult); ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION message to per-machine process."); hr = static_cast(dwResult); @@ -1534,11 +1554,11 @@ static HRESULT ProcessElevatedChildMessage( break; case BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION: - hrResult = OnMsiCommitTransaction(); + hrResult = OnMsiCommitTransaction((BYTE*)pMsg->pvData, pMsg->cbData); break; case BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION: - hrResult = OnMsiRollbackTransaction(); + hrResult = OnMsiRollbackTransaction((BYTE*)pMsg->pvData, pMsg->cbData); break; case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE: @@ -2806,20 +2826,44 @@ LExit: return hr; } -static HRESULT OnMsiCommitTransaction() +static HRESULT OnMsiCommitTransaction( + __in BYTE* pbData, + __in DWORD cbData + ) { HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczName = NULL; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &sczName); + ExitOnFailure(hr, "Failed to read transaction name."); - hr = MsiEngineCommitTransaction(); + hr = MsiEngineCommitTransaction(sczName); + +LExit: + ReleaseStr(sczName); return hr; } -static HRESULT OnMsiRollbackTransaction() +static HRESULT OnMsiRollbackTransaction( + __in BYTE* pbData, + __in DWORD cbData + ) { HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczName = NULL; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &sczName); + ExitOnFailure(hr, "Failed to read transaction name."); - hr = MsiEngineRollbackTransaction(); + hr = MsiEngineRollbackTransaction(sczName); + +LExit: + ReleaseStr(sczName); return hr; } diff --git a/src/engine/elevation.h b/src/engine/elevation.h index 975981da..96111ae2 100644 --- a/src/engine/elevation.h +++ b/src/engine/elevation.h @@ -162,10 +162,12 @@ HRESULT ElevationMsiBeginTransaction( __in LPCWSTR wzName ); HRESULT ElevationMsiCommitTransaction( - __in HANDLE hPipe + __in HANDLE hPipe, + __in LPCWSTR wzName ); HRESULT ElevationMsiRollbackTransaction( - __in HANDLE hPipe + __in HANDLE hPipe, + __in LPCWSTR wzName ); #ifdef __cplusplus diff --git a/src/engine/engine.mc b/src/engine/engine.mc index 4bb517f8..b36a9527 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -843,6 +843,48 @@ Language=English Ignoring application request to cancel from %1!ls! during rollback. . +MessageId=382 +Severity=Warning +SymbolicName=MSG_PLAN_ROLLBACK_DISABLED +Language=English +Rollback is disabled for this bundle. +. + +MessageId=383 +Severity=Error +SymbolicName=MSG_MSI_TRANSACTIONS_DISABLED +Language=English +Windows Installer rollback is disabled on this computer. It must be enabled for this bundle to proceed. +. + +MessageId=384 +Severity=Success +SymbolicName=MSG_MSI_TRANSACTION_BEGIN +Language=English +Starting a new MSI transaction, id: %1!ls! +. + +MessageId=385 +Severity=Success +SymbolicName=MSG_MSI_TRANSACTION_COMMIT +Language=English +Committing MSI transaction, id: %1!ls! +. + +MessageId=386 +Severity=Warning +SymbolicName=MSG_MSI_TRANSACTION_ROLLBACK +Language=English +Rolling back MSI transaction, id: %1!ls! +. + +MessageId=387 +Severity=Error +SymbolicName=MSG_RESTART_REQUEST_DURING_MSI_TRANSACTION +Language=English +Illegal state: Reboot requested within an MSI transaction, id: %1!ls! +. + MessageId=399 Severity=Success SymbolicName=MSG_APPLY_COMPLETE diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index b056cb7e..c298e219 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -1147,18 +1147,30 @@ extern "C" HRESULT MsiEngineBeginTransaction( MSIHANDLE hTransactionHandle = NULL; HANDLE hChangeOfOwnerEvent = NULL; + LogId(REPORT_STANDARD, MSG_MSI_TRANSACTION_BEGIN, wzName); + uResult = ::MsiBeginTransaction(wzName, 0, &hTransactionHandle, &hChangeOfOwnerEvent); + + if (ERROR_ROLLBACK_DISABLED == uResult) + { + LogId(REPORT_ERROR, MSG_MSI_TRANSACTIONS_DISABLED); + } + ExitOnWin32Error(uResult, hr, "Failed to begin an MSI transaction"); LExit: return hr; } -extern "C" HRESULT MsiEngineCommitTransaction() +extern "C" HRESULT MsiEngineCommitTransaction( + __in LPCWSTR wzName + ) { HRESULT hr = S_OK; UINT uResult = ERROR_SUCCESS; + LogId(REPORT_STANDARD, MSG_MSI_TRANSACTION_COMMIT, wzName); + uResult = ::MsiEndTransaction(MSITRANSACTIONSTATE_COMMIT); ExitOnWin32Error(uResult, hr, "Failed to commit the MSI transaction"); @@ -1167,11 +1179,15 @@ LExit: return hr; } -extern "C" HRESULT MsiEngineRollbackTransaction() +extern "C" HRESULT MsiEngineRollbackTransaction( + __in LPCWSTR wzName + ) { HRESULT hr = S_OK; UINT uResult = ERROR_SUCCESS; + LogId(REPORT_WARNING, MSG_MSI_TRANSACTION_ROLLBACK, wzName); + uResult = ::MsiEndTransaction(MSITRANSACTIONSTATE_ROLLBACK); ExitOnWin32Error(uResult, hr, "Failed to rollback the MSI transaction"); diff --git a/src/engine/msiengine.h b/src/engine/msiengine.h index 76030528..c7cc3bef 100644 --- a/src/engine/msiengine.h +++ b/src/engine/msiengine.h @@ -56,8 +56,12 @@ HRESULT MsiEngineAddCompatiblePackage( HRESULT MsiEngineBeginTransaction( __in LPCWSTR wzName ); -HRESULT MsiEngineCommitTransaction(); -HRESULT MsiEngineRollbackTransaction(); +HRESULT MsiEngineCommitTransaction( + __in LPCWSTR wzName + ); +HRESULT MsiEngineRollbackTransaction( + __in LPCWSTR wzName + ); HRESULT MsiEngineExecutePackage( __in_opt HWND hwndParent, __in BURN_EXECUTE_ACTION* pExecuteAction, diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index 5f18cdbc..08451f93 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -348,6 +348,55 @@ LExit: return hr; } +EXTERN_C BAAPI UserExperienceOnBeginMsiTransactionBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId + ) +{ + HRESULT hr = S_OK; + BA_ONBEGINMSITRANSACTIONBEGIN_ARGS args = { }; + BA_ONBEGINMSITRANSACTIONBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzTransactionId = wzTransactionId; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONBEGINMSITRANSACTIONBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnBeginMsiTransactionBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnBeginMsiTransactionComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONBEGINMSITRANSACTIONCOMPLETE_ARGS args = { }; + BA_ONBEGINMSITRANSACTIONCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzTransactionId = wzTransactionId; + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONBEGINMSITRANSACTIONCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnBeginMsiTransactionComplete failed."); + +LExit: + return hr; +} + EXTERN_C BAAPI UserExperienceOnCacheAcquireBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z_opt LPCWSTR wzPackageOrContainerId, @@ -614,6 +663,55 @@ LExit: return hr; } +EXTERN_C BAAPI UserExperienceOnCommitMsiTransactionBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId + ) +{ + HRESULT hr = S_OK; + BA_ONCOMMITMSITRANSACTIONBEGIN_ARGS args = { }; + BA_ONCOMMITMSITRANSACTIONBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzTransactionId = wzTransactionId; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCOMMITMSITRANSACTIONBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnCommitMsiTransactionBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCommitMsiTransactionComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONCOMMITMSITRANSACTIONCOMPLETE_ARGS args = { }; + BA_ONCOMMITMSITRANSACTIONCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzTransactionId = wzTransactionId; + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCOMMITMSITRANSACTIONCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnCommitMsiTransactionComplete failed."); + +LExit: + return hr; +} + EXTERN_C BAAPI UserExperienceOnDetectBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in BOOL fInstalled, @@ -1799,6 +1897,50 @@ LExit: return hr; } +EXTERN_C BAAPI UserExperienceOnRollbackMsiTransactionBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId + ) +{ + HRESULT hr = S_OK; + BA_ONROLLBACKMSITRANSACTIONBEGIN_ARGS args = { }; + BA_ONROLLBACKMSITRANSACTIONBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzTransactionId = wzTransactionId; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONROLLBACKMSITRANSACTIONBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnRollbackMsiTransactionBegin failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnRollbackMsiTransactionComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONROLLBACKMSITRANSACTIONCOMPLETE_ARGS args = { }; + BA_ONROLLBACKMSITRANSACTIONCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzTransactionId = wzTransactionId; + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONROLLBACKMSITRANSACTIONCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnRollbackMsiTransactionComplete failed."); + +LExit: + return hr; +} + EXTERN_C BAAPI UserExperienceOnShutdown( __in BURN_USER_EXPERIENCE* pUserExperience, __inout BOOTSTRAPPER_SHUTDOWN_ACTION* pAction diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index 0ebd1b68..387ed62b 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -108,7 +108,16 @@ BAAPI UserExperienceOnApplyComplete( __in HRESULT hrStatus, __in BOOTSTRAPPER_APPLY_RESTART restart, __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction -); + ); +BAAPI UserExperienceOnBeginMsiTransactionBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId + ); +BAAPI UserExperienceOnBeginMsiTransactionComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId, + __in HRESULT hrStatus + ); BAAPI UserExperienceOnCacheAcquireBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z_opt LPCWSTR wzPackageOrContainerId, @@ -162,6 +171,15 @@ BAAPI UserExperienceOnCacheVerifyComplete( __in HRESULT hrStatus, __inout BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION* pAction ); +BAAPI UserExperienceOnCommitMsiTransactionBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId + ); +BAAPI UserExperienceOnCommitMsiTransactionComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId, + __in HRESULT hrStatus + ); BAAPI UserExperienceOnDetectBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in BOOL fInstalled, @@ -272,7 +290,7 @@ BAAPI UserExperienceOnExecuteBegin( BAAPI UserExperienceOnExecuteComplete( __in BURN_USER_EXPERIENCE* pUserExperience, __in HRESULT hrStatus -); + ); BAAPI UserExperienceOnExecuteFilesInUse( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, @@ -315,7 +333,7 @@ BAAPI UserExperienceOnExecuteProgress( __in_z LPCWSTR wzPackageId, __in DWORD dwProgressPercentage, __in DWORD dwOverallPercentage, - __inout int* pnResult + __out int* pnResult ); BAAPI UserExperienceOnLaunchApprovedExeBegin( __in BURN_USER_EXPERIENCE* pUserExperience @@ -411,6 +429,15 @@ BAAPI UserExperienceOnResolveSource( __in_z_opt LPCWSTR wzDownloadSource, __inout BOOTSTRAPPER_RESOLVESOURCE_ACTION* pAction ); +BAAPI UserExperienceOnRollbackMsiTransactionBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId + ); +BAAPI UserExperienceOnRollbackMsiTransactionComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId, + __in HRESULT hrStatus + ); BAAPI UserExperienceOnShutdown( __in BURN_USER_EXPERIENCE* pUserExperience, __inout BOOTSTRAPPER_SHUTDOWN_ACTION* pAction -- cgit v1.2.3-55-g6feb From 4ca0a5b2a8711cae9e60cb4075799bffef4ce75a Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 16 Nov 2020 21:59:26 -0600 Subject: Add BA messages around system restore points. --- .../inc/BootstrapperApplication.h | 46 +++++ src/engine/core.cpp | 2 +- src/engine/elevation.cpp | 208 ++++++++++++++++++++- src/engine/elevation.h | 1 + src/engine/userexperience.cpp | 80 ++++++++ src/engine/userexperience.h | 14 ++ 6 files changed, 342 insertions(+), 9 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h index cfbb0571..80b23686 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -141,6 +141,10 @@ enum BOOTSTRAPPER_APPLICATION_MESSAGE BOOTSTRAPPER_APPLICATION_MESSAGE_ONCOMMITMSITRANSACTIONCOMPLETE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONROLLBACKMSITRANSACTIONBEGIN, BOOTSTRAPPER_APPLICATION_MESSAGE_ONROLLBACKMSITRANSACTIONCOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONPAUSEAUTOMATICUPDATESBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONPAUSEAUTOMATICUPDATESCOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMRESTOREPOINTBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMRESTOREPOINTCOMPLETE, }; enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION @@ -818,6 +822,27 @@ struct BA_ONLAUNCHAPPROVEDEXECOMPLETE_RESULTS DWORD cbSize; }; +struct BA_ONPAUSEAUTOMATICUPDATESBEGIN_ARGS +{ + DWORD cbSize; +}; + +struct BA_ONPAUSEAUTOMATICUPDATESBEGIN_RESULTS +{ + DWORD cbSize; +}; + +struct BA_ONPAUSEAUTOMATICUPDATESCOMPLETE_ARGS +{ + DWORD cbSize; + HRESULT hrStatus; +}; + +struct BA_ONPAUSEAUTOMATICUPDATESCOMPLETE_RESULTS +{ + DWORD cbSize; +}; + struct BA_ONPLANBEGIN_ARGS { DWORD cbSize; @@ -1061,6 +1086,27 @@ struct BA_ONSTARTUP_RESULTS DWORD cbSize; }; +struct BA_ONSYSTEMRESTOREPOINTBEGIN_ARGS +{ + DWORD cbSize; +}; + +struct BA_ONSYSTEMRESTOREPOINTBEGIN_RESULTS +{ + DWORD cbSize; +}; + +struct BA_ONSYSTEMRESTOREPOINTCOMPLETE_ARGS +{ + DWORD cbSize; + HRESULT hrStatus; +}; + +struct BA_ONSYSTEMRESTOREPOINTCOMPLETE_RESULTS +{ + DWORD cbSize; +}; + struct BA_ONSYSTEMSHUTDOWN_ARGS { DWORD cbSize; diff --git a/src/engine/core.cpp b/src/engine/core.cpp index aeae6bea..028dc1cc 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -626,7 +626,7 @@ extern "C" HRESULT CoreApply( hr = CoreElevate(pEngineState, pEngineState->userExperience.hwndApply); ExitOnFailure(hr, "Failed to elevate."); - hr = ElevationApplyInitialize(pEngineState->companionConnection.hPipe, &pEngineState->variables, pEngineState->plan.action, pEngineState->automaticUpdates, !pEngineState->fDisableSystemRestore); + hr = ElevationApplyInitialize(pEngineState->companionConnection.hPipe, &pEngineState->userExperience, &pEngineState->variables, pEngineState->plan.action, pEngineState->automaticUpdates, !pEngineState->fDisableSystemRestore); ExitOnFailure(hr, "Another per-machine setup is already executing."); fElevated = TRUE; diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp index 1d0e1f1d..af5610dc 100644 --- a/src/engine/elevation.cpp +++ b/src/engine/elevation.cpp @@ -32,6 +32,10 @@ typedef enum _BURN_ELEVATION_MESSAGE_TYPE BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION, BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION, + BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_BEGIN, + BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_COMPLETE, + BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_BEGIN, + BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_COMPLETE, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE, @@ -42,6 +46,13 @@ typedef enum _BURN_ELEVATION_MESSAGE_TYPE // struct +typedef struct _BURN_ELEVATION_APPLY_INITIALIZE_MESSAGE_CONTEXT +{ + BURN_USER_EXPERIENCE* pBA; + BOOL fPauseCompleteNeeded; + BOOL fSrpCompleteNeeded; +} BURN_ELEVATION_APPLY_INITIALIZE_MESSAGE_CONTEXT; + typedef struct _BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT { PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler; @@ -89,6 +100,11 @@ static HRESULT OnLoadCompatiblePackage( __in BYTE* pbData, __in DWORD cbData ); +static HRESULT ProcessApplyInitializeMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); static HRESULT ProcessGenericExecuteMessages( __in BURN_PIPE_MESSAGE* pMsg, __in_opt LPVOID pvContext, @@ -119,6 +135,7 @@ static HRESULT ProcessResult( __out BOOTSTRAPPER_APPLY_RESTART* pRestart ); static HRESULT OnApplyInitialize( + __in HANDLE hPipe, __in BURN_VARIABLES* pVariables, __in BURN_REGISTRATION* pRegistration, __in HANDLE* phLock, @@ -245,7 +262,20 @@ static HRESULT OnMsiRollbackTransaction( __in BYTE* pbData, __in DWORD cbData ); - +static HRESULT ElevatedOnPauseAUBegin( + __in HANDLE hPipe + ); +static HRESULT ElevatedOnPauseAUComplete( + __in HANDLE hPipe, + __in HRESULT hrStatus + ); +static HRESULT ElevatedOnSystemRestorePointBegin( + __in HANDLE hPipe + ); +static HRESULT ElevatedOnSystemRestorePointComplete( + __in HANDLE hPipe, + __in HRESULT hrStatus + ); // function definitions @@ -321,6 +351,7 @@ LExit: extern "C" HRESULT ElevationApplyInitialize( __in HANDLE hPipe, + __in BURN_USER_EXPERIENCE* pBA, __in BURN_VARIABLES* pVariables, __in BOOTSTRAPPER_ACTION action, __in BURN_AU_PAUSE_ACTION auAction, @@ -331,6 +362,9 @@ extern "C" HRESULT ElevationApplyInitialize( BYTE* pbData = NULL; SIZE_T cbData = 0; DWORD dwResult = 0; + BURN_ELEVATION_APPLY_INITIALIZE_MESSAGE_CONTEXT context = { }; + + context.pBA = pBA; // serialize message data hr = BuffWriteNumber(&pbData, &cbData, (DWORD)action); @@ -346,11 +380,21 @@ extern "C" HRESULT ElevationApplyInitialize( ExitOnFailure(hr, "Failed to write variables."); // send message - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE, pbData, cbData, NULL, NULL, &dwResult); + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE, pbData, cbData, ProcessApplyInitializeMessages, &context, &dwResult); ExitOnFailure(hr, "Failed to send message to per-machine process."); hr = (HRESULT)dwResult; + // Best effort to keep the sequence of BA events sane. + if (context.fPauseCompleteNeeded) + { + UserExperienceOnPauseAUComplete(pBA, hr); + } + if (context.fSrpCompleteNeeded) + { + UserExperienceOnSystemRestorePointComplete(pBA, hr); + } + LExit: ReleaseBuffer(pbData); @@ -1317,6 +1361,68 @@ LExit: return hr; } +static HRESULT ProcessApplyInitializeMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + BURN_ELEVATION_APPLY_INITIALIZE_MESSAGE_CONTEXT* pContext = static_cast(pvContext); + BYTE* pbData = (BYTE*)pMsg->pvData; + SIZE_T iData = 0; + HRESULT hrStatus = S_OK; + HRESULT hrBA = S_OK; + + // Process the message. + switch (pMsg->dwMessage) + { + case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_BEGIN: + pContext->fPauseCompleteNeeded = TRUE; + hrBA = UserExperienceOnPauseAUBegin(pContext->pBA); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_COMPLETE: + // read hrStatus + hr = BuffReadNumber(pbData, pMsg->cbData, &iData, reinterpret_cast(&hrStatus)); + ExitOnFailure(hr, "Failed to read pause AU hrStatus."); + + pContext->fPauseCompleteNeeded = FALSE; + hrBA = UserExperienceOnPauseAUComplete(pContext->pBA, hrStatus); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_BEGIN: + if (pContext->fPauseCompleteNeeded) + { + pContext->fPauseCompleteNeeded = FALSE; + hrBA = UserExperienceOnPauseAUComplete(pContext->pBA, E_INVALIDSTATE); + } + + pContext->fSrpCompleteNeeded = TRUE; + hrBA = UserExperienceOnSystemRestorePointBegin(pContext->pBA); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_COMPLETE: + // read hrStatus + hr = BuffReadNumber(pbData, pMsg->cbData, &iData, reinterpret_cast(&hrStatus)); + ExitOnFailure(hr, "Failed to read system restore point hrStatus."); + + pContext->fSrpCompleteNeeded = FALSE; + hrBA = UserExperienceOnSystemRestorePointComplete(pContext->pBA, hrStatus); + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid apply initialize message."); + break; + } + + *pdwResult = static_cast(hrBA); + +LExit: + return hr; +} + static HRESULT ProcessGenericExecuteMessages( __in BURN_PIPE_MESSAGE* pMsg, __in_opt LPVOID pvContext, @@ -1385,7 +1491,7 @@ static HRESULT ProcessGenericExecuteMessages( } // send message - *pdwResult = (DWORD)pContext->pfnGenericMessageHandler(&message, pContext->pvContext);; + *pdwResult = (DWORD)pContext->pfnGenericMessageHandler(&message, pContext->pvContext); LExit: ReleaseStr(sczMessage); @@ -1562,7 +1668,7 @@ static HRESULT ProcessElevatedChildMessage( break; case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE: - hrResult = OnApplyInitialize(pContext->pVariables, pContext->pRegistration, pContext->phLock, pContext->pfDisabledAutomaticUpdates, (BYTE*)pMsg->pvData, pMsg->cbData); + hrResult = OnApplyInitialize(pContext->hPipe, pContext->pVariables, pContext->pRegistration, pContext->phLock, pContext->pfDisabledAutomaticUpdates, (BYTE*)pMsg->pvData, pMsg->cbData); break; case BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE: @@ -1697,6 +1803,7 @@ static HRESULT ProcessResult( } static HRESULT OnApplyInitialize( + __in HANDLE hPipe, __in BURN_VARIABLES* pVariables, __in BURN_REGISTRATION* pRegistration, __in HANDLE* phLock, @@ -1711,6 +1818,7 @@ static HRESULT OnApplyInitialize( DWORD dwAUAction = 0; DWORD dwTakeSystemRestorePoint = 0; LPWSTR sczBundleName = NULL; + HRESULT hrStatus = S_OK; // Deserialize message data. hr = BuffReadNumber(pbData, cbData, &iData, &dwAction); @@ -1738,9 +1846,12 @@ static HRESULT OnApplyInitialize( // Attempt to pause AU with best effort. if (BURN_AU_PAUSE_ACTION_IFELEVATED == dwAUAction || BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME == dwAUAction) { + hr = ElevatedOnPauseAUBegin(hPipe); + ExitOnFailure(hr, "ElevatedOnPauseAUBegin failed."); + LogId(REPORT_STANDARD, MSG_PAUSE_AU_STARTING); - hr = WuaPauseAutomaticUpdates(); + hrStatus = hr = WuaPauseAutomaticUpdates(); if (FAILED(hr)) { LogId(REPORT_STANDARD, MSG_FAILED_PAUSE_AU, hr); @@ -1754,6 +1865,9 @@ static HRESULT OnApplyInitialize( *pfDisabledWindowsUpdate = TRUE; } } + + hr = ElevatedOnPauseAUComplete(hPipe, hrStatus); + ExitOnFailure(hr, "ElevatedOnPauseAUComplete failed."); } if (dwTakeSystemRestorePoint) @@ -1765,11 +1879,14 @@ static HRESULT OnApplyInitialize( ExitFunction(); } + hr = ElevatedOnSystemRestorePointBegin(hPipe); + ExitOnFailure(hr, "ElevatedOnSystemRestorePointBegin failed."); + LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_STARTING); BOOTSTRAPPER_ACTION action = static_cast(dwAction); SRP_ACTION restoreAction = (BOOTSTRAPPER_ACTION_INSTALL == action) ? SRP_ACTION_INSTALL : (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? SRP_ACTION_UNINSTALL : SRP_ACTION_MODIFY; - hr = SrpCreateRestorePoint(sczBundleName, restoreAction); + hrStatus = hr = SrpCreateRestorePoint(sczBundleName, restoreAction); if (SUCCEEDED(hr)) { LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_SUCCEEDED); @@ -1779,11 +1896,14 @@ static HRESULT OnApplyInitialize( LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_DISABLED); hr = S_OK; } - else if (FAILED(hr)) + else { LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_FAILED, hr); hr = S_OK; } + + hr = ElevatedOnSystemRestorePointComplete(hPipe, hrStatus); + ExitOnFailure(hr, "ElevatedOnSystemRestorePointComplete failed."); } LExit: @@ -2699,7 +2819,7 @@ static int MsiExecuteMessageHandler( // send message hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, (DWORD*)&nResult); - ExitOnFailure(hr, "Failed to send message to per-machine process."); + ExitOnFailure(hr, "Failed to send msi message to per-user process."); LExit: ReleaseBuffer(pbData); @@ -2867,3 +2987,75 @@ LExit: return hr; } + +static HRESULT ElevatedOnPauseAUBegin( + __in HANDLE hPipe + ) +{ + HRESULT hr = S_OK; + DWORD dwResult = 0; + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_BEGIN, NULL, 0, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_BEGIN message to per-user process."); + +LExit: + return hr; +} + +static HRESULT ElevatedOnPauseAUComplete( + __in HANDLE hPipe, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BYTE* pbSendData = NULL; + SIZE_T cbSendData = 0; + DWORD dwResult = 0; + + hr = BuffWriteNumber(&pbSendData, &cbSendData, hrStatus); + ExitOnFailure(hr, "Failed to write the pause au status to message buffer."); + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_COMPLETE, pbSendData, cbSendData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_COMPLETE message to per-user process."); + +LExit: + ReleaseBuffer(pbSendData); + + return hr; +} + +static HRESULT ElevatedOnSystemRestorePointBegin( + __in HANDLE hPipe + ) +{ + HRESULT hr = S_OK; + DWORD dwResult = 0; + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_BEGIN, NULL, 0, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_BEGIN message to per-user process."); + +LExit: + return hr; +} + +static HRESULT ElevatedOnSystemRestorePointComplete( + __in HANDLE hPipe, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BYTE* pbSendData = NULL; + SIZE_T cbSendData = 0; + DWORD dwResult = 0; + + hr = BuffWriteNumber(&pbSendData, &cbSendData, hrStatus); + ExitOnFailure(hr, "Failed to write the system restore point status to message buffer."); + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_COMPLETE, pbSendData, cbSendData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_COMPLETE message to per-user process."); + +LExit: + ReleaseBuffer(pbSendData); + + return hr; +} diff --git a/src/engine/elevation.h b/src/engine/elevation.h index 96111ae2..05fecdf6 100644 --- a/src/engine/elevation.h +++ b/src/engine/elevation.h @@ -14,6 +14,7 @@ HRESULT ElevationElevate( ); HRESULT ElevationApplyInitialize( __in HANDLE hPipe, + __in BURN_USER_EXPERIENCE* pBA, __in BURN_VARIABLES* pVariables, __in BOOTSTRAPPER_ACTION action, __in BURN_AU_PAUSE_ACTION auAction, diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index 08451f93..b5bdc623 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -1482,6 +1482,46 @@ LExit: return hr; } +EXTERN_C BAAPI UserExperienceOnPauseAUBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + BA_ONPAUSEAUTOMATICUPDATESBEGIN_ARGS args = { }; + BA_ONPAUSEAUTOMATICUPDATESBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPAUSEAUTOMATICUPDATESBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnPauseAUBegin failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPauseAUComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONPAUSEAUTOMATICUPDATESCOMPLETE_ARGS args = { }; + BA_ONPAUSEAUTOMATICUPDATESCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPAUSEAUTOMATICUPDATESCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnPauseAUComplete failed."); + +LExit: + return hr; +} + EXTERN_C BAAPI UserExperienceOnPlanBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in DWORD cPackages @@ -1983,6 +2023,46 @@ LExit: return hr; } +EXTERN_C BAAPI UserExperienceOnSystemRestorePointBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + BA_ONSYSTEMRESTOREPOINTBEGIN_ARGS args = { }; + BA_ONSYSTEMRESTOREPOINTBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMRESTOREPOINTBEGIN, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnSystemRestorePointBegin failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnSystemRestorePointComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONSYSTEMRESTOREPOINTCOMPLETE_ARGS args = { }; + BA_ONSYSTEMRESTOREPOINTCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMRESTOREPOINTCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + ExitOnFailure(hr, "BA OnSystemRestorePointComplete failed."); + +LExit: + return hr; +} + EXTERN_C BAAPI UserExperienceOnSystemShutdown( __in BURN_USER_EXPERIENCE* pUserExperience, __in DWORD dwEndSession, diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index 387ed62b..a02d968c 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -343,6 +343,13 @@ BAAPI UserExperienceOnLaunchApprovedExeComplete( __in HRESULT hrStatus, __in DWORD dwProcessId ); +BAAPI UserExperienceOnPauseAUBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +BAAPI UserExperienceOnPauseAUComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ); BAAPI UserExperienceOnPlanBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in DWORD cPackages @@ -445,6 +452,13 @@ BAAPI UserExperienceOnShutdown( BAAPI UserExperienceOnStartup( __in BURN_USER_EXPERIENCE* pUserExperience ); +BAAPI UserExperienceOnSystemRestorePointBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +BAAPI UserExperienceOnSystemRestorePointComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ); BAAPI UserExperienceOnSystemShutdown( __in BURN_USER_EXPERIENCE* pUserExperience, __in DWORD dwEndSession, -- cgit v1.2.3-55-g6feb From 643293e48d176ff78282670512f45b4cf889b0a5 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 17 Nov 2020 14:11:30 -0600 Subject: Allow E_IMPL from BA/bext and check all cbSizes from BA/bext. --- src/engine/EngineForApplication.cpp | 216 +++++++++++++++++++++++------------- src/engine/EngineForExtension.cpp | 97 ++++++++++------ src/engine/burnextension.cpp | 24 +++- src/engine/engine.vcxproj | 2 + src/engine/externalengine.cpp | 30 +++++ src/engine/externalengine.h | 22 ++++ src/engine/precomp.h | 1 + src/engine/userexperience.cpp | 159 +++++++++++++++----------- 8 files changed, 372 insertions(+), 179 deletions(-) create mode 100644 src/engine/externalengine.cpp create mode 100644 src/engine/externalengine.h diff --git a/src/engine/EngineForApplication.cpp b/src/engine/EngineForApplication.cpp index 87a0782c..e3ce7670 100644 --- a/src/engine/EngineForApplication.cpp +++ b/src/engine/EngineForApplication.cpp @@ -5,31 +5,36 @@ static HRESULT CopyStringToBA( __in LPWSTR wzValue, - __in LPWSTR wzBuffer, + __in_opt LPWSTR wzBuffer, __inout DWORD* pcchBuffer ); static HRESULT BAEngineGetPackageCount( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in BAENGINE_GETPACKAGECOUNT_ARGS* /*pArgs*/, - __in BAENGINE_GETPACKAGECOUNT_RESULTS* pResults + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_GETPACKAGECOUNT_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_GETPACKAGECOUNT_RESULTS, pResults); DWORD* pcPackages = &pResults->cPackages; *pcPackages = pContext->pEngineState->packages.cPackages; +LExit: return hr; } static HRESULT BAEngineGetVariableNumeric( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in BAENGINE_GETVARIABLENUMERIC_ARGS* pArgs, - __in BAENGINE_GETVARIABLENUMERIC_RESULTS* pResults + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_GETVARIABLENUMERIC_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_GETVARIABLENUMERIC_RESULTS, pResults); LPCWSTR wzVariable = pArgs->wzVariable; LONGLONG* pllValue = &pResults->llValue; @@ -42,17 +47,20 @@ static HRESULT BAEngineGetVariableNumeric( hr = E_INVALIDARG; } +LExit: return hr; } static HRESULT BAEngineGetVariableString( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in BAENGINE_GETVARIABLESTRING_ARGS* pArgs, - __in BAENGINE_GETVARIABLESTRING_RESULTS* pResults + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; LPWSTR sczValue = NULL; + ValidateMessageArgs(hr, pvArgs, BAENGINE_GETVARIABLESTRING_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_GETVARIABLESTRING_RESULTS, pResults); LPCWSTR wzVariable = pArgs->wzVariable; LPWSTR wzValue = pResults->wzValue; DWORD* pcchValue = &pResults->cchValue; @@ -70,18 +78,21 @@ static HRESULT BAEngineGetVariableString( hr = E_INVALIDARG; } +LExit: StrSecureZeroFreeString(sczValue); return hr; } static HRESULT BAEngineGetVariableVersion( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in BAENGINE_GETVARIABLEVERSION_ARGS* pArgs, - __in BAENGINE_GETVARIABLEVERSION_RESULTS* pResults + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; VERUTIL_VERSION* pVersion = NULL; + ValidateMessageArgs(hr, pvArgs, BAENGINE_GETVARIABLEVERSION_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_GETVARIABLEVERSION_RESULTS, pResults); LPCWSTR wzVariable = pArgs->wzVariable; LPWSTR wzValue = pResults->wzValue; DWORD* pcchValue = &pResults->cchValue; @@ -99,6 +110,7 @@ static HRESULT BAEngineGetVariableVersion( hr = E_INVALIDARG; } +LExit: ReleaseVerutilVersion(pVersion); return hr; @@ -106,12 +118,14 @@ static HRESULT BAEngineGetVariableVersion( static HRESULT BAEngineFormatString( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in BAENGINE_FORMATSTRING_ARGS* pArgs, - __in BAENGINE_FORMATSTRING_RESULTS* pResults + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; LPWSTR sczValue = NULL; + ValidateMessageArgs(hr, pvArgs, BAENGINE_FORMATSTRING_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_FORMATSTRING_RESULTS, pResults); LPCWSTR wzIn = pArgs->wzIn; LPWSTR wzOut = pResults->wzOut; DWORD* pcchOut = &pResults->cchOut; @@ -129,18 +143,21 @@ static HRESULT BAEngineFormatString( hr = E_INVALIDARG; } +LExit: StrSecureZeroFreeString(sczValue); return hr; } static HRESULT BAEngineEscapeString( __in BOOTSTRAPPER_ENGINE_CONTEXT* /*pContext*/, - __in BAENGINE_ESCAPESTRING_ARGS* pArgs, - __in BAENGINE_ESCAPESTRING_RESULTS* pResults + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; LPWSTR sczValue = NULL; + ValidateMessageArgs(hr, pvArgs, BAENGINE_ESCAPESTRING_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_ESCAPESTRING_RESULTS, pResults); LPCWSTR wzIn = pArgs->wzIn; LPWSTR wzOut = pResults->wzOut; DWORD* pcchOut = &pResults->cchOut; @@ -158,17 +175,20 @@ static HRESULT BAEngineEscapeString( hr = E_INVALIDARG; } +LExit: StrSecureZeroFreeString(sczValue); return hr; } static HRESULT BAEngineEvaluateCondition( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in BAENGINE_EVALUATECONDITION_ARGS* pArgs, - __in BAENGINE_EVALUATECONDITION_RESULTS* pResults + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_EVALUATECONDITION_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_EVALUATECONDITION_RESULTS, pResults); LPCWSTR wzCondition = pArgs->wzCondition; BOOL* pf = &pResults->f; @@ -181,16 +201,19 @@ static HRESULT BAEngineEvaluateCondition( hr = E_INVALIDARG; } +LExit: return hr; } static HRESULT BAEngineLog( __in BOOTSTRAPPER_ENGINE_CONTEXT* /*pContext*/, - __in BAENGINE_LOG_ARGS* pArgs, - __in BAENGINE_LOG_RESULTS* /*pResults*/ + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_LOG_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_LOG_RESULTS, pResults); REPORT_LEVEL rl = REPORT_NONE; BOOTSTRAPPER_LOG_LEVEL level = pArgs->level; LPCWSTR wzMessage = pArgs->wzMessage; @@ -226,14 +249,16 @@ LExit: static HRESULT BAEngineSendEmbeddedError( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in BAENGINE_SENDEMBEDDEDERROR_ARGS* pArgs, - __in BAENGINE_SENDEMBEDDEDERROR_RESULTS* pResults + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; BYTE* pbData = NULL; DWORD cbData = 0; DWORD dwResult = 0; + ValidateMessageArgs(hr, pvArgs, BAENGINE_SENDEMBEDDEDERROR_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_SENDEMBEDDEDERROR_RESULTS, pResults); DWORD dwErrorCode = pArgs->dwErrorCode; LPCWSTR wzMessage = pArgs->wzMessage; DWORD dwUIHint = pArgs->dwUIHint; @@ -266,14 +291,16 @@ LExit: static HRESULT BAEngineSendEmbeddedProgress( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in BAENGINE_SENDEMBEDDEDPROGRESS_ARGS* pArgs, - __in BAENGINE_SENDEMBEDDEDPROGRESS_RESULTS* pResults + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; BYTE* pbData = NULL; DWORD cbData = 0; DWORD dwResult = 0; + ValidateMessageArgs(hr, pvArgs, BAENGINE_SENDEMBEDDEDPROGRESS_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_SENDEMBEDDEDPROGRESS_RESULTS, pResults); DWORD dwProgressPercentage = pArgs->dwProgressPercentage; DWORD dwOverallProgressPercentage = pArgs->dwOverallProgressPercentage; int* pnResult = &pResults->nResult; @@ -302,8 +329,8 @@ LExit: static HRESULT BAEngineSetUpdate( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const BAENGINE_SETUPDATE_ARGS* pArgs, - __in BAENGINE_SETUPDATE_RESULTS* /*pResults*/ + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; @@ -313,6 +340,8 @@ static HRESULT BAEngineSetUpdate( UUID guid = { }; WCHAR wzGuid[39]; RPC_STATUS rs = RPC_S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_SETUPDATE_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_SETUPDATE_RESULTS, pResults); LPCWSTR wzLocalSource = pArgs->wzLocalSource; LPCWSTR wzDownloadSource = pArgs->wzDownloadSource; DWORD64 qwSize = pArgs->qwSize; @@ -385,13 +414,15 @@ LExit: static HRESULT BAEngineSetLocalSource( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in BAENGINE_SETLOCALSOURCE_ARGS* pArgs, - __in BAENGINE_SETLOCALSOURCE_RESULTS* /*pResults*/ + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; BURN_CONTAINER* pContainer = NULL; BURN_PAYLOAD* pPayload = NULL; + ValidateMessageArgs(hr, pvArgs, BAENGINE_SETLOCALSOURCE_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_SETLOCALSOURCE_RESULTS, pResults); LPCWSTR wzPackageOrContainerId = pArgs->wzPackageOrContainerId; LPCWSTR wzPayloadId = pArgs->wzPayloadId; LPCWSTR wzPath = pArgs->wzPath; @@ -438,14 +469,16 @@ LExit: static HRESULT BAEngineSetDownloadSource( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in BAENGINE_SETDOWNLOADSOURCE_ARGS* pArgs, - __in BAENGINE_SETDOWNLOADSOURCE_RESULTS* /*pResults*/ + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; BURN_CONTAINER* pContainer = NULL; BURN_PAYLOAD* pPayload = NULL; DOWNLOAD_SOURCE* pDownloadSource = NULL; + ValidateMessageArgs(hr, pvArgs, BAENGINE_SETDOWNLOADSOURCE_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_SETDOWNLOADSOURCE_RESULTS, pResults); LPCWSTR wzPackageOrContainerId = pArgs->wzPackageOrContainerId; LPCWSTR wzPayloadId = pArgs->wzPayloadId; LPCWSTR wzUrl = pArgs->wzUrl; @@ -522,11 +555,13 @@ LExit: static HRESULT BAEngineSetVariableNumeric( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const BAENGINE_SETVARIABLENUMERIC_ARGS* pArgs, - __in BAENGINE_SETVARIABLENUMERIC_RESULTS* /*pResults*/ + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_SETVARIABLENUMERIC_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_SETVARIABLENUMERIC_RESULTS, pResults); LPCWSTR wzVariable = pArgs->wzVariable; LONGLONG llValue = pArgs->llValue; @@ -547,11 +582,13 @@ LExit: static HRESULT BAEngineSetVariableString( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const BAENGINE_SETVARIABLESTRING_ARGS* pArgs, - __in BAENGINE_SETVARIABLESTRING_RESULTS* /*pResults*/ + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_SETVARIABLESTRING_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_SETVARIABLESTRING_RESULTS, pResults); LPCWSTR wzVariable = pArgs->wzVariable; LPCWSTR wzValue = pArgs->wzValue; @@ -572,14 +609,16 @@ LExit: static HRESULT BAEngineSetVariableVersion( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const BAENGINE_SETVARIABLEVERSION_ARGS* pArgs, - __in BAENGINE_SETVARIABLEVERSION_RESULTS* /*pResults*/ + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion = NULL; + ValidateMessageArgs(hr, pvArgs, BAENGINE_SETVARIABLEVERSION_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_SETVARIABLEVERSION_RESULTS, pResults); LPCWSTR wzVariable = pArgs->wzVariable; LPCWSTR wzValue = pArgs->wzValue; - VERUTIL_VERSION* pVersion = NULL; if (wzVariable && *wzVariable) { @@ -606,42 +645,52 @@ LExit: static HRESULT BAEngineCloseSplashScreen( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const BAENGINE_CLOSESPLASHSCREEN_ARGS* /*pArgs*/, - __in BAENGINE_CLOSESPLASHSCREEN_RESULTS* /*pResults*/ + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_CLOSESPLASHSCREEN_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_CLOSESPLASHSCREEN_RESULTS, pResults); + // If the splash screen is still around, close it. if (::IsWindow(pContext->pEngineState->command.hwndSplashScreen)) { ::PostMessageW(pContext->pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0); } - return S_OK; +LExit: + return hr; } static HRESULT BAEngineCompareVersions( __in BOOTSTRAPPER_ENGINE_CONTEXT* /*pContext*/, - __in const BAENGINE_COMPAREVERSIONS_ARGS* pArgs, - __in BAENGINE_COMPAREVERSIONS_RESULTS* pResults + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_COMPAREVERSIONS_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_COMPAREVERSIONS_RESULTS, pResults); LPCWSTR wzVersion1 = pArgs->wzVersion1; LPCWSTR wzVersion2 = pArgs->wzVersion2; int* pnResult = &pResults->nResult; hr = VerCompareStringVersions(wzVersion1, wzVersion2, FALSE, pnResult); +LExit: return hr; } static HRESULT BAEngineDetect( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in BAENGINE_DETECT_ARGS* pArgs, - __in BAENGINE_DETECT_RESULTS* /*pResults*/ + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_DETECT_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_DETECT_RESULTS, pResults); if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_DETECT, 0, reinterpret_cast(pArgs->hwndParent))) { @@ -654,11 +703,13 @@ LExit: static HRESULT BAEnginePlan( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const BAENGINE_PLAN_ARGS* pArgs, - __in BAENGINE_PLAN_RESULTS* /*pResults*/ + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_PLAN_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_PLAN_RESULTS, pResults); BOOTSTRAPPER_ACTION action = pArgs->action; if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_PLAN, 0, action)) @@ -672,11 +723,13 @@ LExit: static HRESULT BAEngineElevate( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const BAENGINE_ELEVATE_ARGS* pArgs, - __in BAENGINE_ELEVATE_RESULTS* /*pResults*/ + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_ELEVATE_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_ELEVATE_RESULTS, pResults); if (INVALID_HANDLE_VALUE != pContext->pEngineState->companionConnection.hPipe) { @@ -693,11 +746,13 @@ LExit: static HRESULT BAEngineApply( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const BAENGINE_APPLY_ARGS* pArgs, - __in BAENGINE_APPLY_RESULTS* /*pResults*/ + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_APPLY_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_APPLY_RESULTS, pResults); ExitOnNull(pArgs->hwndParent, hr, E_INVALIDARG, "BA passed NULL hwndParent to Apply."); if (!::IsWindow(pArgs->hwndParent)) @@ -716,11 +771,13 @@ LExit: static HRESULT BAEngineQuit( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const BAENGINE_QUIT_ARGS* pArgs, - __in BAENGINE_QUIT_RESULTS* /*pResults*/ + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_QUIT_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_QUIT_RESULTS, pResults); if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_QUIT, static_cast(pArgs->dwExitCode), 0)) { @@ -733,19 +790,24 @@ LExit: static HRESULT BAEngineLaunchApprovedExe( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const BAENGINE_LAUNCHAPPROVEDEXE_ARGS* pArgs, - __in BAENGINE_LAUNCHAPPROVEDEXE_RESULTS* /*pResults*/ + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; BURN_APPROVED_EXE* pApprovedExe = NULL; BOOL fLeaveCriticalSection = FALSE; - BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe = (BURN_LAUNCH_APPROVED_EXE*)MemAlloc(sizeof(BURN_LAUNCH_APPROVED_EXE), TRUE); + BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe = NULL; + ValidateMessageArgs(hr, pvArgs, BAENGINE_LAUNCHAPPROVEDEXE_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_LAUNCHAPPROVEDEXE_RESULTS, pResults); HWND hwndParent = pArgs->hwndParent; LPCWSTR wzApprovedExeForElevationId = pArgs->wzApprovedExeForElevationId; LPCWSTR wzArguments = pArgs->wzArguments; DWORD dwWaitForInputIdleTimeout = pArgs->dwWaitForInputIdleTimeout; + pLaunchApprovedExe = (BURN_LAUNCH_APPROVED_EXE*)MemAlloc(sizeof(BURN_LAUNCH_APPROVED_EXE), TRUE); + ExitOnNull(pLaunchApprovedExe, hr, E_OUTOFMEMORY, "Failed to alloc BURN_LAUNCH_APPROVED_EXE"); + ::EnterCriticalSection(&pContext->pEngineState->csActive); fLeaveCriticalSection = TRUE; hr = UserExperienceEnsureEngineInactive(&pContext->pEngineState->userExperience); @@ -812,76 +874,76 @@ HRESULT WINAPI EngineForApplicationProc( switch (message) { case BOOTSTRAPPER_ENGINE_MESSAGE_GETPACKAGECOUNT: - hr = BAEngineGetPackageCount(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineGetPackageCount(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLENUMERIC: - hr = BAEngineGetVariableNumeric(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineGetVariableNumeric(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLESTRING: - hr = BAEngineGetVariableString(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineGetVariableString(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLEVERSION: - hr = BAEngineGetVariableVersion(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineGetVariableVersion(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_FORMATSTRING: - hr = BAEngineFormatString(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineFormatString(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_ESCAPESTRING: - hr = BAEngineEscapeString(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineEscapeString(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_EVALUATECONDITION: - hr = BAEngineEvaluateCondition(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineEvaluateCondition(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_LOG: - hr = BAEngineLog(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineLog(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_SENDEMBEDDEDERROR: - hr = BAEngineSendEmbeddedError(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineSendEmbeddedError(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_SENDEMBEDDEDPROGRESS: - hr = BAEngineSendEmbeddedProgress(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineSendEmbeddedProgress(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_SETUPDATE: - hr = BAEngineSetUpdate(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineSetUpdate(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_SETLOCALSOURCE: - hr = BAEngineSetLocalSource(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineSetLocalSource(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_SETDOWNLOADSOURCE: - hr = BAEngineSetDownloadSource(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineSetDownloadSource(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLENUMERIC: - hr = BAEngineSetVariableNumeric(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineSetVariableNumeric(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLESTRING: - hr = BAEngineSetVariableString(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineSetVariableString(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLEVERSION: - hr = BAEngineSetVariableVersion(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineSetVariableVersion(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_CLOSESPLASHSCREEN: - hr = BAEngineCloseSplashScreen(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineCloseSplashScreen(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_DETECT: - hr = BAEngineDetect(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineDetect(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_PLAN: - hr = BAEnginePlan(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEnginePlan(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_ELEVATE: - hr = BAEngineElevate(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineElevate(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_APPLY: - hr = BAEngineApply(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineApply(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_QUIT: - hr = BAEngineQuit(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineQuit(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_LAUNCHAPPROVEDEXE: - hr = BAEngineLaunchApprovedExe(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineLaunchApprovedExe(pContext, pvArgs, pvResults); break; case BOOTSTRAPPER_ENGINE_MESSAGE_COMPAREVERSIONS: - hr = BAEngineCompareVersions(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BAEngineCompareVersions(pContext, pvArgs, pvResults); break; default: hr = E_NOTIMPL; @@ -894,7 +956,7 @@ LExit: static HRESULT CopyStringToBA( __in LPWSTR wzValue, - __in LPWSTR wzBuffer, + __in_opt LPWSTR wzBuffer, __inout DWORD* pcchBuffer ) { diff --git a/src/engine/EngineForExtension.cpp b/src/engine/EngineForExtension.cpp index 585b74a4..29b6a6b3 100644 --- a/src/engine/EngineForExtension.cpp +++ b/src/engine/EngineForExtension.cpp @@ -11,12 +11,14 @@ static HRESULT CopyStringToBE( static HRESULT BEEngineEscapeString( __in BURN_EXTENSION_ENGINE_CONTEXT* /*pContext*/, - __in BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_ARGS* pArgs, - __in BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_RESULTS* pResults + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; LPWSTR sczValue = NULL; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_RESULTS, pResults); LPCWSTR wzIn = pArgs->wzIn; LPWSTR wzOut = pResults->wzOut; DWORD* pcchOut = &pResults->cchOut; @@ -34,17 +36,20 @@ static HRESULT BEEngineEscapeString( hr = E_INVALIDARG; } +LExit: StrSecureZeroFreeString(sczValue); return hr; } static HRESULT BEEngineEvaluateCondition( __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, - __in BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_ARGS* pArgs, - __in BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_RESULTS* pResults + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_RESULTS, pResults); LPCWSTR wzCondition = pArgs->wzCondition; BOOL* pf = &pResults->f; @@ -57,17 +62,20 @@ static HRESULT BEEngineEvaluateCondition( hr = E_INVALIDARG; } +LExit: return hr; } static HRESULT BEEngineFormatString( __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, - __in BUNDLE_EXTENSION_ENGINE_FORMATSTRING_ARGS* pArgs, - __in BUNDLE_EXTENSION_ENGINE_FORMATSTRING_RESULTS* pResults + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; LPWSTR sczValue = NULL; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_FORMATSTRING_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_FORMATSTRING_RESULTS, pResults); LPCWSTR wzIn = pArgs->wzIn; LPWSTR wzOut = pResults->wzOut; DWORD* pcchOut = &pResults->cchOut; @@ -85,17 +93,20 @@ static HRESULT BEEngineFormatString( hr = E_INVALIDARG; } +LExit: StrSecureZeroFreeString(sczValue); return hr; } static HRESULT BEEngineGetVariableNumeric( __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, - __in BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_ARGS* pArgs, - __in BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_RESULTS* pResults + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_RESULTS, pResults); LPCWSTR wzVariable = pArgs->wzVariable; LONGLONG* pllValue = &pResults->llValue; @@ -108,17 +119,20 @@ static HRESULT BEEngineGetVariableNumeric( hr = E_INVALIDARG; } +LExit: return hr; } static HRESULT BEEngineGetVariableString( __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, - __in BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_ARGS* pArgs, - __in BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_RESULTS* pResults + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; LPWSTR sczValue = NULL; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_RESULTS, pResults); LPCWSTR wzVariable = pArgs->wzVariable; LPWSTR wzValue = pResults->wzValue; DWORD* pcchValue = &pResults->cchValue; @@ -136,18 +150,21 @@ static HRESULT BEEngineGetVariableString( hr = E_INVALIDARG; } +LExit: StrSecureZeroFreeString(sczValue); return hr; } static HRESULT BEEngineGetVariableVersion( __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, - __in BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_ARGS* pArgs, - __in BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_RESULTS* pResults + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; VERUTIL_VERSION* pVersion = NULL; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_RESULTS, pResults); LPCWSTR wzVariable = pArgs->wzVariable; LPWSTR wzValue = pResults->wzValue; DWORD* pcchValue = &pResults->cchValue; @@ -165,6 +182,7 @@ static HRESULT BEEngineGetVariableVersion( hr = E_INVALIDARG; } +LExit: ReleaseVerutilVersion(pVersion); return hr; @@ -172,12 +190,14 @@ static HRESULT BEEngineGetVariableVersion( static HRESULT BEEngineLog( __in BURN_EXTENSION_ENGINE_CONTEXT* /*pContext*/, - __in BUNDLE_EXTENSION_ENGINE_LOG_ARGS* pArgs, - __in BUNDLE_EXTENSION_ENGINE_LOG_RESULTS* /*pResults*/ + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; REPORT_LEVEL rl = REPORT_NONE; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_LOG_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_LOG_RESULTS, pResults); BUNDLE_EXTENSION_LOG_LEVEL level = pArgs->level; LPCWSTR wzMessage = pArgs->wzMessage; @@ -212,11 +232,13 @@ LExit: static HRESULT BEEngineSetVariableNumeric( __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, - __in const BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_ARGS* pArgs, - __in BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_RESULTS* /*pResults*/ + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_RESULTS, pResults); LPCWSTR wzVariable = pArgs->wzVariable; LONGLONG llValue = pArgs->llValue; @@ -237,11 +259,13 @@ LExit: static HRESULT BEEngineSetVariableString( __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, - __in const BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_ARGS* pArgs, - __in BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_RESULTS* /*pResults*/ + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_RESULTS, pResults); LPCWSTR wzVariable = pArgs->wzVariable; LPCWSTR wzValue = pArgs->wzValue; @@ -262,14 +286,16 @@ LExit: static HRESULT BEEngineSetVariableVersion( __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, - __in const BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_ARGS* pArgs, - __in BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_RESULTS* /*pResults*/ + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion = NULL; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_RESULTS, pResults); LPCWSTR wzVariable = pArgs->wzVariable; LPCWSTR wzValue = pArgs->wzValue; - VERUTIL_VERSION* pVersion = NULL; if (wzVariable && *wzVariable) { @@ -296,17 +322,20 @@ LExit: static HRESULT BEEngineCompareVersions( __in BURN_EXTENSION_ENGINE_CONTEXT* /*pContext*/, - __in const BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_ARGS* pArgs, - __in BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_RESULTS* pResults + __in const LPVOID pvArgs, + __inout LPVOID pvResults ) { HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_RESULTS, pResults); LPCWSTR wzVersion1 = pArgs->wzVersion1; LPCWSTR wzVersion2 = pArgs->wzVersion2; int* pnResult = &pResults->nResult; hr = VerCompareStringVersions(wzVersion1, wzVersion2, FALSE, pnResult); +LExit: return hr; } @@ -328,37 +357,37 @@ HRESULT WINAPI EngineForExtensionProc( switch (message) { case BUNDLE_EXTENSION_ENGINE_MESSAGE_ESCAPESTRING: - hr = BEEngineEscapeString(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BEEngineEscapeString(pContext, pvArgs, pvResults); break; case BUNDLE_EXTENSION_ENGINE_MESSAGE_EVALUATECONDITION: - hr = BEEngineEvaluateCondition(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BEEngineEvaluateCondition(pContext, pvArgs, pvResults); break; case BUNDLE_EXTENSION_ENGINE_MESSAGE_FORMATSTRING: - hr = BEEngineFormatString(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BEEngineFormatString(pContext, pvArgs, pvResults); break; case BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLENUMERIC: - hr = BEEngineGetVariableNumeric(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BEEngineGetVariableNumeric(pContext, pvArgs, pvResults); break; case BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLESTRING: - hr = BEEngineGetVariableString(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BEEngineGetVariableString(pContext, pvArgs, pvResults); break; case BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLEVERSION: - hr = BEEngineGetVariableVersion(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BEEngineGetVariableVersion(pContext, pvArgs, pvResults); break; case BUNDLE_EXTENSION_ENGINE_MESSAGE_LOG: - hr = BEEngineLog(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BEEngineLog(pContext, pvArgs, pvResults); break; case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLENUMERIC: - hr = BEEngineSetVariableNumeric(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BEEngineSetVariableNumeric(pContext, pvArgs, pvResults); break; case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLESTRING: - hr = BEEngineSetVariableString(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BEEngineSetVariableString(pContext, pvArgs, pvResults); break; case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLEVERSION: - hr = BEEngineSetVariableVersion(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BEEngineSetVariableVersion(pContext, pvArgs, pvResults); break; case BUNDLE_EXTENSION_ENGINE_MESSAGE_COMPAREVERSIONS: - hr = BEEngineCompareVersions(pContext, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + hr = BEEngineCompareVersions(pContext, pvArgs, pvResults); break; default: hr = E_NOTIMPL; diff --git a/src/engine/burnextension.cpp b/src/engine/burnextension.cpp index 157b082f..7568f75e 100644 --- a/src/engine/burnextension.cpp +++ b/src/engine/burnextension.cpp @@ -2,6 +2,14 @@ #include "precomp.h" + +static HRESULT SendRequiredBextMessage( + __in BURN_EXTENSION* pExtension, + __in BUNDLE_EXTENSION_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ); + // function definitions /******************************************************************* @@ -234,9 +242,23 @@ EXTERN_C BEEAPI BurnExtensionPerformSearch( results.cbSize = sizeof(results); - hr = pExtension->pfnBurnExtensionProc(BUNDLE_EXTENSION_MESSAGE_SEARCH, &args, &results, pExtension->pvBurnExtensionProcContext); + hr = SendRequiredBextMessage(pExtension, BUNDLE_EXTENSION_MESSAGE_SEARCH, &args, &results); ExitOnFailure(hr, "BundleExtension '%ls' Search '%ls' failed.", pExtension->sczId, wzSearchId); LExit: return hr; } + +static HRESULT SendRequiredBextMessage( + __in BURN_EXTENSION* pExtension, + __in BUNDLE_EXTENSION_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + + hr = pExtension->pfnBurnExtensionProc(message, pvArgs, pvResults, pExtension->pvBurnExtensionProcContext); + + return hr; +} diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index 28085ed4..3624d923 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -51,6 +51,7 @@ + @@ -109,6 +110,7 @@ + diff --git a/src/engine/externalengine.cpp b/src/engine/externalengine.cpp new file mode 100644 index 00000000..ef4f931d --- /dev/null +++ b/src/engine/externalengine.cpp @@ -0,0 +1,30 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// function definitions + +// TODO: callers need to provide the original size (at the time of first public release) of the struct instead of the current size. +HRESULT WINAPI ExternalEngineValidateMessageParameter( + __in_opt const LPVOID pv, + __in SIZE_T cbSizeOffset, + __in DWORD dwMinimumSize + ) +{ + HRESULT hr = S_OK; + + if (!pv) + { + ExitFunction1(hr = E_INVALIDARG); + } + + DWORD cbSize = *(DWORD*)((BYTE*)pv + cbSizeOffset); + if (dwMinimumSize < cbSize) + { + ExitFunction1(hr = E_INVALIDARG); + } + +LExit: + return hr; +} diff --git a/src/engine/externalengine.h b/src/engine/externalengine.h new file mode 100644 index 00000000..7910b224 --- /dev/null +++ b/src/engine/externalengine.h @@ -0,0 +1,22 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#define ValidateMessageParameter(x, pv, type) { x = ExternalEngineValidateMessageParameter(pv, offsetof(type, cbSize), sizeof(type)); if (FAILED(x)) { goto LExit; }} +#define ValidateMessageArgs(x, pv, type, identifier) ValidateMessageParameter(x, pv, type); const type* identifier = reinterpret_cast(pv); UNREFERENCED_PARAMETER(identifier) +#define ValidateMessageResults(x, pv, type, identifier) ValidateMessageParameter(x, pv, type); type* identifier = reinterpret_cast(pv); UNREFERENCED_PARAMETER(identifier) + + +#if defined(__cplusplus) +extern "C" { +#endif + +HRESULT WINAPI ExternalEngineValidateMessageParameter( + __in_opt const LPVOID pv, + __in SIZE_T cbSizeOffset, + __in DWORD dwMinimumSize + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/precomp.h b/src/engine/precomp.h index 2bceab58..a8a656d1 100644 --- a/src/engine/precomp.h +++ b/src/engine/precomp.h @@ -100,6 +100,7 @@ #include "bitsengine.h" #include "netfxchainer.h" +#include "externalengine.h" #include "EngineForApplication.h" #include "EngineForExtension.h" #include "engine.messages.h" diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index b5bdc623..ce1662b8 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -17,6 +17,13 @@ static HRESULT FilterExecuteResult( __in LPCWSTR sczEventName ); +static HRESULT SendBAMessage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOTSTRAPPER_APPLICATION_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ); + // function definitions @@ -97,7 +104,7 @@ extern "C" HRESULT UserExperienceLoad( args.pCommand = pCommand; args.pfnBootstrapperEngineProc = EngineForApplicationProc; args.pvBootstrapperEngineProcContext = pEngineContext; - args.qwEngineAPIVersion = MAKEQWORDVERSION(2020, 8, 31, 0); + args.qwEngineAPIVersion = MAKEQWORDVERSION(2020, 11, 17, 0); results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS); @@ -308,7 +315,7 @@ EXTERN_C BAAPI UserExperienceOnApplyBegin( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYBEGIN, &args, &results); ExitOnFailure(hr, "BA OnApplyBegin failed."); if (results.fCancel) @@ -339,7 +346,7 @@ EXTERN_C BAAPI UserExperienceOnApplyComplete( results.cbSize = sizeof(results); results.action = *pAction; - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYCOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnApplyComplete failed."); *pAction = results.action; @@ -362,7 +369,7 @@ EXTERN_C BAAPI UserExperienceOnBeginMsiTransactionBegin( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONBEGINMSITRANSACTIONBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONBEGINMSITRANSACTIONBEGIN, &args, &results); ExitOnFailure(hr, "BA OnBeginMsiTransactionBegin failed."); if (results.fCancel) @@ -390,7 +397,7 @@ EXTERN_C BAAPI UserExperienceOnBeginMsiTransactionComplete( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONBEGINMSITRANSACTIONCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONBEGINMSITRANSACTIONCOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnBeginMsiTransactionComplete failed."); LExit: @@ -417,7 +424,7 @@ EXTERN_C BAAPI UserExperienceOnCacheAcquireBegin( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREBEGIN, &args, &results); ExitOnFailure(hr, "BA OnCacheAcquireBegin failed."); if (results.fCancel) @@ -450,7 +457,7 @@ EXTERN_C BAAPI UserExperienceOnCacheAcquireComplete( results.cbSize = sizeof(results); results.action = args.recommendation; - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRECOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnCacheAcquireComplete failed."); if (FAILED(hrStatus)) @@ -484,7 +491,7 @@ EXTERN_C BAAPI UserExperienceOnCacheAcquireProgress( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREPROGRESS, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREPROGRESS, &args, &results); ExitOnFailure(hr, "BA OnCacheAcquireProgress failed."); if (results.fCancel) @@ -508,7 +515,7 @@ EXTERN_C BAAPI UserExperienceOnCacheBegin( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEBEGIN, &args, &results); ExitOnFailure(hr, "BA OnCacheBegin failed."); if (results.fCancel) @@ -534,7 +541,7 @@ EXTERN_C BAAPI UserExperienceOnCacheComplete( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnCacheComplete failed."); LExit: @@ -559,7 +566,7 @@ EXTERN_C BAAPI UserExperienceOnCachePackageBegin( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGEBEGIN, &args, &results); ExitOnFailure(hr, "BA OnCachePackageBegin failed."); if (results.fCancel) @@ -590,7 +597,7 @@ EXTERN_C BAAPI UserExperienceOnCachePackageComplete( results.cbSize = sizeof(results); results.action = *pAction; - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGECOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnCachePackageComplete failed."); if (FAILED(hrStatus)) @@ -618,7 +625,7 @@ EXTERN_C BAAPI UserExperienceOnCacheVerifyBegin( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYBEGIN, &args, &results); ExitOnFailure(hr, "BA OnCacheVerifyBegin failed."); if (results.fCancel) @@ -651,7 +658,7 @@ EXTERN_C BAAPI UserExperienceOnCacheVerifyComplete( results.cbSize = sizeof(results); results.action = *pAction; - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYCOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnCacheVerifyComplete failed."); if (FAILED(hrStatus)) @@ -677,7 +684,7 @@ EXTERN_C BAAPI UserExperienceOnCommitMsiTransactionBegin( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCOMMITMSITRANSACTIONBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCOMMITMSITRANSACTIONBEGIN, &args, &results); ExitOnFailure(hr, "BA OnCommitMsiTransactionBegin failed."); if (results.fCancel) @@ -705,7 +712,7 @@ EXTERN_C BAAPI UserExperienceOnCommitMsiTransactionComplete( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCOMMITMSITRANSACTIONCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCOMMITMSITRANSACTIONCOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnCommitMsiTransactionComplete failed."); LExit: @@ -728,7 +735,7 @@ EXTERN_C BAAPI UserExperienceOnDetectBegin( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTBEGIN, &args, &results); ExitOnFailure(hr, "BA OnDetectBegin failed."); if (results.fCancel) @@ -758,7 +765,7 @@ EXTERN_C BAAPI UserExperienceOnDetectCompatibleMsiPackage( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPATIBLEMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPATIBLEMSIPACKAGE, &args, &results); ExitOnFailure(hr, "BA OnDetectCompatibleMsiPackage failed."); if (results.fCancel) @@ -784,7 +791,7 @@ EXTERN_C BAAPI UserExperienceOnDetectComplete( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnDetectComplete failed."); LExit: @@ -815,7 +822,7 @@ EXTERN_C BAAPI UserExperienceOnDetectForwardCompatibleBundle( results.cbSize = sizeof(results); results.fIgnoreBundle = *pfIgnoreBundle; - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTFORWARDCOMPATIBLEBUNDLE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTFORWARDCOMPATIBLEBUNDLE, &args, &results); ExitOnFailure(hr, "BA OnDetectForwardCompatibleBundle failed."); if (results.fCancel) @@ -846,7 +853,7 @@ EXTERN_C BAAPI UserExperienceOnDetectMsiFeature( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTMSIFEATURE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTMSIFEATURE, &args, &results); ExitOnFailure(hr, "BA OnDetectMsiFeature failed."); if (results.fCancel) @@ -872,7 +879,7 @@ EXTERN_C BAAPI UserExperienceOnDetectPackageBegin( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGEBEGIN, &args, &results); ExitOnFailure(hr, "BA OnDetectPackageBegin failed."); if (results.fCancel) @@ -902,7 +909,7 @@ EXTERN_C BAAPI UserExperienceOnDetectPackageComplete( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGECOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnDetectPackageComplete failed."); LExit: @@ -933,7 +940,7 @@ EXTERN_C BAAPI UserExperienceOnDetectRelatedBundle( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLE, &args, &results); ExitOnFailure(hr, "BA OnDetectRelatedBundle failed."); if (results.fCancel) @@ -969,7 +976,7 @@ EXTERN_C BAAPI UserExperienceOnDetectRelatedMsiPackage( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDMSIPACKAGE, &args, &results); ExitOnFailure(hr, "BA OnDetectRelatedMsiPackage failed."); if (results.fCancel) @@ -999,7 +1006,7 @@ EXTERN_C BAAPI UserExperienceOnDetectTargetMsiPackage( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTTARGETMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTTARGETMSIPACKAGE, &args, &results); ExitOnFailure(hr, "BA OnDetectTargetMsiPackage failed."); if (results.fCancel) @@ -1039,7 +1046,7 @@ EXTERN_C BAAPI UserExperienceOnDetectUpdate( results.cbSize = sizeof(results); results.fStopProcessingUpdates = *pfStopProcessingUpdates; - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATE, &args, &results); ExitOnFailure(hr, "BA OnDetectUpdate failed."); if (results.fCancel) @@ -1068,7 +1075,7 @@ EXTERN_C BAAPI UserExperienceOnDetectUpdateBegin( results.cbSize = sizeof(results); results.fSkip = *pfSkip; - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATEBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATEBEGIN, &args, &results); ExitOnFailure(hr, "BA OnDetectUpdateBegin failed."); if (results.fCancel) @@ -1097,7 +1104,7 @@ EXTERN_C BAAPI UserExperienceOnDetectUpdateComplete( results.cbSize = sizeof(results); results.fIgnoreError = *pfIgnoreError; - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATECOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnDetectUpdateComplete failed."); if (FAILED(hrStatus)) @@ -1121,7 +1128,7 @@ EXTERN_C BAAPI UserExperienceOnElevateBegin( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATEBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATEBEGIN, &args, &results); ExitOnFailure(hr, "BA OnElevateBegin failed."); if (results.fCancel) @@ -1147,7 +1154,7 @@ EXTERN_C BAAPI UserExperienceOnElevateComplete( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATECOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnElevateComplete failed."); LExit: @@ -1183,7 +1190,7 @@ EXTERN_C BAAPI UserExperienceOnError( results.cbSize = sizeof(results); results.nResult = *pnResult; - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONERROR, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONERROR, &args, &results); ExitOnFailure(hr, "BA OnError failed."); *pnResult = results.nResult; @@ -1206,7 +1213,7 @@ EXTERN_C BAAPI UserExperienceOnExecuteBegin( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEBEGIN, &args, &results); ExitOnFailure(hr, "BA OnExecuteBegin failed."); if (results.fCancel) @@ -1232,7 +1239,7 @@ EXTERN_C BAAPI UserExperienceOnExecuteComplete( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTECOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnExecuteComplete failed."); LExit: @@ -1260,7 +1267,7 @@ EXTERN_C BAAPI UserExperienceOnExecuteFilesInUse( results.cbSize = sizeof(results); results.nResult = *pnResult; - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEFILESINUSE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEFILESINUSE, &args, &results); ExitOnFailure(hr, "BA OnExecuteFilesInUse failed."); *pnResult = results.nResult; @@ -1296,7 +1303,7 @@ EXTERN_C BAAPI UserExperienceOnExecuteMsiMessage( results.cbSize = sizeof(results); results.nResult = *pnResult; - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEMSIMESSAGE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEMSIMESSAGE, &args, &results); ExitOnFailure(hr, "BA OnExecuteMsiMessage failed."); *pnResult = results.nResult; @@ -1327,7 +1334,7 @@ EXTERN_C BAAPI UserExperienceOnExecutePackageBegin( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGEBEGIN, &args, &results); ExitOnFailure(hr, "BA OnExecutePackageBegin failed."); if (results.fCancel) @@ -1360,7 +1367,7 @@ EXTERN_C BAAPI UserExperienceOnExecutePackageComplete( results.cbSize = sizeof(results); results.action = *pAction; - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGECOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnExecutePackageComplete failed."); *pAction = results.action; @@ -1385,7 +1392,7 @@ EXTERN_C BAAPI UserExperienceOnExecutePatchTarget( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPATCHTARGET, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPATCHTARGET, &args, &results); ExitOnFailure(hr, "BA OnExecutePatchTarget failed."); if (results.fCancel) @@ -1416,7 +1423,7 @@ EXTERN_C BAAPI UserExperienceOnExecuteProgress( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROGRESS, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROGRESS, &args, &results); ExitOnFailure(hr, "BA OnExecuteProgress failed."); LExit: @@ -1447,7 +1454,7 @@ EXTERN_C BAAPI UserExperienceOnLaunchApprovedExeBegin( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXEBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXEBEGIN, &args, &results); ExitOnFailure(hr, "BA OnLaunchApprovedExeBegin failed."); if (results.fCancel) @@ -1475,7 +1482,7 @@ EXTERN_C BAAPI UserExperienceOnLaunchApprovedExeComplete( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXECOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnLaunchApprovedExeComplete failed."); LExit: @@ -1494,7 +1501,7 @@ EXTERN_C BAAPI UserExperienceOnPauseAUBegin( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPAUSEAUTOMATICUPDATESBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPAUSEAUTOMATICUPDATESBEGIN, &args, &results); ExitOnFailure(hr, "BA OnPauseAUBegin failed."); LExit: @@ -1515,7 +1522,7 @@ EXTERN_C BAAPI UserExperienceOnPauseAUComplete( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPAUSEAUTOMATICUPDATESCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPAUSEAUTOMATICUPDATESCOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnPauseAUComplete failed."); LExit: @@ -1536,7 +1543,7 @@ EXTERN_C BAAPI UserExperienceOnPlanBegin( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANBEGIN, &args, &results); ExitOnFailure(hr, "BA OnPlanBegin failed."); if (results.fCancel) @@ -1569,7 +1576,7 @@ EXTERN_C BAAPI UserExperienceOnPlanCompatibleMsiPackageBegin( results.cbSize = sizeof(results); results.requestedState = *pRequestedState; - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN, &args, &results); ExitOnFailure(hr, "BA OnPlanCompatibleMsiPackageBegin failed."); if (results.fCancel) @@ -1608,7 +1615,7 @@ EXTERN_C BAAPI UserExperienceOnPlanCompatibleMsiPackageComplete( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnPlanCompatibleMsiPackageComplete failed."); LExit: @@ -1634,7 +1641,7 @@ EXTERN_C BAAPI UserExperienceOnPlanMsiFeature( results.cbSize = sizeof(results); results.requestedState = *pRequestedState; - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIFEATURE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIFEATURE, &args, &results); ExitOnFailure(hr, "BA OnPlanMsiFeature failed."); if (results.fCancel) @@ -1661,7 +1668,7 @@ EXTERN_C BAAPI UserExperienceOnPlanComplete( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnPlanComplete failed."); LExit: @@ -1692,7 +1699,7 @@ EXTERN_C BAAPI UserExperienceOnPlanMsiPackage( results.uiLevel = *pUiLevel; results.fDisableExternalUiHandler = *pfDisableExternalUiHandler; - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIPACKAGE, &args, &results); ExitOnFailure(hr, "BA OnPlanMsiPackage failed."); if (results.fCancel) @@ -1724,7 +1731,7 @@ EXTERN_C BAAPI UserExperienceOnPlanPackageBegin( results.cbSize = sizeof(results); results.requestedState = *pRequestedState; - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGEBEGIN, &args, &results); ExitOnFailure(hr, "BA OnPlanPackageBegin failed."); if (results.fCancel) @@ -1761,7 +1768,7 @@ EXTERN_C BAAPI UserExperienceOnPlanPackageComplete( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGECOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnPlanPackageComplete failed."); LExit: @@ -1785,7 +1792,7 @@ EXTERN_C BAAPI UserExperienceOnPlanRelatedBundle( results.cbSize = sizeof(results); results.requestedState = *pRequestedState; - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLE, &args, &results); ExitOnFailure(hr, "BA OnPlanRelatedBundle failed."); if (results.fCancel) @@ -1817,7 +1824,7 @@ EXTERN_C BAAPI UserExperienceOnPlanTargetMsiPackage( results.cbSize = sizeof(results); results.requestedState = *pRequestedState; - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANTARGETMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANTARGETMSIPACKAGE, &args, &results); ExitOnFailure(hr, "BA OnPlanTargetMsiPackage failed."); if (results.fCancel) @@ -1847,7 +1854,7 @@ EXTERN_C BAAPI UserExperienceOnProgress( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPROGRESS, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPROGRESS, &args, &results); hr = FilterExecuteResult(pUserExperience, hr, fRollback, results.fCancel, L"OnProgress"); return hr; @@ -1865,7 +1872,7 @@ EXTERN_C BAAPI UserExperienceOnRegisterBegin( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERBEGIN, &args, &results); ExitOnFailure(hr, "BA OnRegisterBegin failed."); if (results.fCancel) @@ -1891,7 +1898,7 @@ EXTERN_C BAAPI UserExperienceOnRegisterComplete( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERCOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnRegisterComplete failed."); LExit: @@ -1921,7 +1928,7 @@ EXTERN_C BAAPI UserExperienceOnResolveSource( results.cbSize = sizeof(results); results.action = *pAction; - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONRESOLVESOURCE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONRESOLVESOURCE, &args, &results); ExitOnFailure(hr, "BA OnResolveSource failed."); if (results.fCancel) @@ -1951,7 +1958,7 @@ EXTERN_C BAAPI UserExperienceOnRollbackMsiTransactionBegin( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONROLLBACKMSITRANSACTIONBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONROLLBACKMSITRANSACTIONBEGIN, &args, &results); ExitOnFailure(hr, "BA OnRollbackMsiTransactionBegin failed."); LExit: @@ -1974,7 +1981,7 @@ EXTERN_C BAAPI UserExperienceOnRollbackMsiTransactionComplete( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONROLLBACKMSITRANSACTIONCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONROLLBACKMSITRANSACTIONCOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnRollbackMsiTransactionComplete failed."); LExit: @@ -1995,7 +2002,7 @@ EXTERN_C BAAPI UserExperienceOnShutdown( results.cbSize = sizeof(results); results.action = *pAction; - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONSHUTDOWN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSHUTDOWN, &args, &results); ExitOnFailure(hr, "BA OnShutdown failed."); *pAction = results.action; @@ -2016,7 +2023,7 @@ EXTERN_C BAAPI UserExperienceOnStartup( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONSTARTUP, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSTARTUP, &args, &results); ExitOnFailure(hr, "BA OnStartup failed."); LExit: @@ -2035,7 +2042,7 @@ EXTERN_C BAAPI UserExperienceOnSystemRestorePointBegin( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMRESTOREPOINTBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMRESTOREPOINTBEGIN, &args, &results); ExitOnFailure(hr, "BA OnSystemRestorePointBegin failed."); LExit: @@ -2056,7 +2063,7 @@ EXTERN_C BAAPI UserExperienceOnSystemRestorePointComplete( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMRESTOREPOINTCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMRESTOREPOINTCOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnSystemRestorePointComplete failed."); LExit: @@ -2079,7 +2086,7 @@ EXTERN_C BAAPI UserExperienceOnSystemShutdown( results.cbSize = sizeof(results); results.fCancel = *pfCancel; - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMSHUTDOWN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMSHUTDOWN, &args, &results); ExitOnFailure(hr, "BA OnSystemShutdown failed."); *pfCancel = results.fCancel; @@ -2100,7 +2107,7 @@ EXTERN_C BAAPI UserExperienceOnUnregisterBegin( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERBEGIN, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERBEGIN, &args, &results); ExitOnFailure(hr, "BA OnUnregisterBegin failed."); if (results.fCancel) @@ -2126,7 +2133,7 @@ EXTERN_C BAAPI UserExperienceOnUnregisterComplete( results.cbSize = sizeof(results); - hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERCOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnUnregisterComplete failed."); LExit: @@ -2389,3 +2396,21 @@ static HRESULT FilterExecuteResult( LExit: return hr; } + +static HRESULT SendBAMessage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOTSTRAPPER_APPLICATION_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + + hr = pUserExperience->pfnBAProc(message, pvArgs, pvResults, pUserExperience->pvBAProcContext); + if (hr == E_NOTIMPL) + { + hr = S_OK; + } + + return hr; +} -- cgit v1.2.3-55-g6feb From fdd47bab30235f62a8bcc7a5a88c6d69267046aa Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 17 Nov 2020 18:53:13 -0600 Subject: Consolidate the code for the BA and bext engines. --- src/engine/EngineForApplication.cpp | 528 ++----------------------- src/engine/EngineForExtension.cpp | 191 +-------- src/engine/externalengine.cpp | 760 ++++++++++++++++++++++++++++++++++++ src/engine/externalengine.h | 154 ++++++++ src/engine/pseudobundle.cpp | 4 +- src/engine/pseudobundle.h | 4 +- 6 files changed, 956 insertions(+), 685 deletions(-) diff --git a/src/engine/EngineForApplication.cpp b/src/engine/EngineForApplication.cpp index e3ce7670..361e0f4e 100644 --- a/src/engine/EngineForApplication.cpp +++ b/src/engine/EngineForApplication.cpp @@ -3,12 +3,6 @@ #include "precomp.h" -static HRESULT CopyStringToBA( - __in LPWSTR wzValue, - __in_opt LPWSTR wzBuffer, - __inout DWORD* pcchBuffer - ); - static HRESULT BAEngineGetPackageCount( __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, __in const LPVOID pvArgs, @@ -18,9 +12,8 @@ static HRESULT BAEngineGetPackageCount( HRESULT hr = S_OK; ValidateMessageArgs(hr, pvArgs, BAENGINE_GETPACKAGECOUNT_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_GETPACKAGECOUNT_RESULTS, pResults); - DWORD* pcPackages = &pResults->cPackages; - *pcPackages = pContext->pEngineState->packages.cPackages; + ExternalEngineGetPackageCount(pContext->pEngineState, &pResults->cPackages); LExit: return hr; @@ -35,17 +28,8 @@ static HRESULT BAEngineGetVariableNumeric( HRESULT hr = S_OK; ValidateMessageArgs(hr, pvArgs, BAENGINE_GETVARIABLENUMERIC_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_GETVARIABLENUMERIC_RESULTS, pResults); - LPCWSTR wzVariable = pArgs->wzVariable; - LONGLONG* pllValue = &pResults->llValue; - if (wzVariable && *wzVariable) - { - hr = VariableGetNumeric(&pContext->pEngineState->variables, wzVariable, pllValue); - } - else - { - hr = E_INVALIDARG; - } + hr = ExternalEngineGetVariableNumeric(pContext->pEngineState, pArgs->wzVariable, &pResults->llValue); LExit: return hr; @@ -58,28 +42,12 @@ static HRESULT BAEngineGetVariableString( ) { HRESULT hr = S_OK; - LPWSTR sczValue = NULL; ValidateMessageArgs(hr, pvArgs, BAENGINE_GETVARIABLESTRING_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_GETVARIABLESTRING_RESULTS, pResults); - LPCWSTR wzVariable = pArgs->wzVariable; - LPWSTR wzValue = pResults->wzValue; - DWORD* pcchValue = &pResults->cchValue; - if (wzVariable && *wzVariable) - { - hr = VariableGetString(&pContext->pEngineState->variables, wzVariable, &sczValue); - if (SUCCEEDED(hr)) - { - hr = CopyStringToBA(sczValue, wzValue, pcchValue); - } - } - else - { - hr = E_INVALIDARG; - } + hr = ExternalEngineGetVariableString(pContext->pEngineState, pArgs->wzVariable, pResults->wzValue, &pResults->cchValue); LExit: - StrSecureZeroFreeString(sczValue); return hr; } @@ -90,29 +58,12 @@ static HRESULT BAEngineGetVariableVersion( ) { HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion = NULL; ValidateMessageArgs(hr, pvArgs, BAENGINE_GETVARIABLEVERSION_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_GETVARIABLEVERSION_RESULTS, pResults); - LPCWSTR wzVariable = pArgs->wzVariable; - LPWSTR wzValue = pResults->wzValue; - DWORD* pcchValue = &pResults->cchValue; - if (wzVariable && *wzVariable) - { - hr = VariableGetVersion(&pContext->pEngineState->variables, wzVariable, &pVersion); - if (SUCCEEDED(hr)) - { - hr = CopyStringToBA(pVersion->sczVersion, wzValue, pcchValue); - } - } - else - { - hr = E_INVALIDARG; - } + hr = ExternalEngineGetVariableVersion(pContext->pEngineState, pArgs->wzVariable, pResults->wzValue, &pResults->cchValue); LExit: - ReleaseVerutilVersion(pVersion); - return hr; } @@ -123,28 +74,12 @@ static HRESULT BAEngineFormatString( ) { HRESULT hr = S_OK; - LPWSTR sczValue = NULL; ValidateMessageArgs(hr, pvArgs, BAENGINE_FORMATSTRING_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_FORMATSTRING_RESULTS, pResults); - LPCWSTR wzIn = pArgs->wzIn; - LPWSTR wzOut = pResults->wzOut; - DWORD* pcchOut = &pResults->cchOut; - if (wzIn && *wzIn) - { - hr = VariableFormatString(&pContext->pEngineState->variables, wzIn, &sczValue, NULL); - if (SUCCEEDED(hr)) - { - hr = CopyStringToBA(sczValue, wzOut, pcchOut); - } - } - else - { - hr = E_INVALIDARG; - } + hr = ExternalEngineFormatString(pContext->pEngineState, pArgs->wzIn, pResults->wzOut, &pResults->cchOut); LExit: - StrSecureZeroFreeString(sczValue); return hr; } @@ -155,28 +90,12 @@ static HRESULT BAEngineEscapeString( ) { HRESULT hr = S_OK; - LPWSTR sczValue = NULL; ValidateMessageArgs(hr, pvArgs, BAENGINE_ESCAPESTRING_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_ESCAPESTRING_RESULTS, pResults); - LPCWSTR wzIn = pArgs->wzIn; - LPWSTR wzOut = pResults->wzOut; - DWORD* pcchOut = &pResults->cchOut; - if (wzIn && *wzIn) - { - hr = VariableEscapeString(wzIn, &sczValue); - if (SUCCEEDED(hr)) - { - hr = CopyStringToBA(sczValue, wzOut, pcchOut); - } - } - else - { - hr = E_INVALIDARG; - } + hr = ExternalEngineEscapeString(pArgs->wzIn, pResults->wzOut, &pResults->cchOut); LExit: - StrSecureZeroFreeString(sczValue); return hr; } @@ -189,17 +108,8 @@ static HRESULT BAEngineEvaluateCondition( HRESULT hr = S_OK; ValidateMessageArgs(hr, pvArgs, BAENGINE_EVALUATECONDITION_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_EVALUATECONDITION_RESULTS, pResults); - LPCWSTR wzCondition = pArgs->wzCondition; - BOOL* pf = &pResults->f; - if (wzCondition && *wzCondition) - { - hr = ConditionEvaluate(&pContext->pEngineState->variables, wzCondition, pf); - } - else - { - hr = E_INVALIDARG; - } + hr = ExternalEngineEvaluateCondition(pContext->pEngineState, pArgs->wzCondition, &pResults->f); LExit: return hr; @@ -212,13 +122,11 @@ static HRESULT BAEngineLog( ) { HRESULT hr = S_OK; + REPORT_LEVEL rl = REPORT_NONE; ValidateMessageArgs(hr, pvArgs, BAENGINE_LOG_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_LOG_RESULTS, pResults); - REPORT_LEVEL rl = REPORT_NONE; - BOOTSTRAPPER_LOG_LEVEL level = pArgs->level; - LPCWSTR wzMessage = pArgs->wzMessage; - switch (level) + switch (pArgs->level) { case BOOTSTRAPPER_LOG_LEVEL_STANDARD: rl = REPORT_STANDARD; @@ -240,7 +148,7 @@ static HRESULT BAEngineLog( ExitFunction1(hr = E_INVALIDARG); } - hr = LogStringLine(rl, "%ls", wzMessage); + hr = ExternalEngineLog(rl, pArgs->wzMessage); ExitOnFailure(hr, "Failed to log BA message."); LExit: @@ -254,38 +162,12 @@ static HRESULT BAEngineSendEmbeddedError( ) { HRESULT hr = S_OK; - BYTE* pbData = NULL; - DWORD cbData = 0; - DWORD dwResult = 0; ValidateMessageArgs(hr, pvArgs, BAENGINE_SENDEMBEDDEDERROR_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_SENDEMBEDDEDERROR_RESULTS, pResults); - DWORD dwErrorCode = pArgs->dwErrorCode; - LPCWSTR wzMessage = pArgs->wzMessage; - DWORD dwUIHint = pArgs->dwUIHint; - int* pnResult = &pResults->nResult; - - if (BURN_MODE_EMBEDDED != pContext->pEngineState->mode) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE); - ExitOnRootFailure(hr, "BA requested to send embedded message when not in embedded mode."); - } - - hr = BuffWriteNumber(&pbData, &cbData, dwErrorCode); - ExitOnFailure(hr, "Failed to write error code to message buffer."); - hr = BuffWriteString(&pbData, &cbData, wzMessage ? wzMessage : L""); - ExitOnFailure(hr, "Failed to write message string to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, dwUIHint); - ExitOnFailure(hr, "Failed to write UI hint to message buffer."); - - hr = PipeSendMessage(pContext->pEngineState->embeddedConnection.hPipe, BURN_EMBEDDED_MESSAGE_TYPE_ERROR, pbData, cbData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send embedded message over pipe."); - - *pnResult = static_cast(dwResult); + hr = ExternalEngineSendEmbeddedError(pContext->pEngineState, pArgs->dwErrorCode, pArgs->wzMessage, pArgs->dwUIHint, &pResults->nResult); LExit: - ReleaseBuffer(pbData); return hr; } @@ -296,34 +178,12 @@ static HRESULT BAEngineSendEmbeddedProgress( ) { HRESULT hr = S_OK; - BYTE* pbData = NULL; - DWORD cbData = 0; - DWORD dwResult = 0; ValidateMessageArgs(hr, pvArgs, BAENGINE_SENDEMBEDDEDPROGRESS_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_SENDEMBEDDEDPROGRESS_RESULTS, pResults); - DWORD dwProgressPercentage = pArgs->dwProgressPercentage; - DWORD dwOverallProgressPercentage = pArgs->dwOverallProgressPercentage; - int* pnResult = &pResults->nResult; - - if (BURN_MODE_EMBEDDED != pContext->pEngineState->mode) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE); - ExitOnRootFailure(hr, "BA requested to send embedded progress message when not in embedded mode."); - } - - hr = BuffWriteNumber(&pbData, &cbData, dwProgressPercentage); - ExitOnFailure(hr, "Failed to write progress percentage to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, dwOverallProgressPercentage); - ExitOnFailure(hr, "Failed to write overall progress percentage to message buffer."); - - hr = PipeSendMessage(pContext->pEngineState->embeddedConnection.hPipe, BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS, pbData, cbData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send embedded progress message over pipe."); - *pnResult = static_cast(dwResult); + hr = ExternalEngineSendEmbeddedProgress(pContext->pEngineState, pArgs->dwProgressPercentage, pArgs->dwOverallProgressPercentage, &pResults->nResult); LExit: - ReleaseBuffer(pbData); return hr; } @@ -334,81 +194,12 @@ static HRESULT BAEngineSetUpdate( ) { HRESULT hr = S_OK; - LPCWSTR sczId = NULL; - LPWSTR sczLocalSource = NULL; - LPWSTR sczCommandline = NULL; - UUID guid = { }; - WCHAR wzGuid[39]; - RPC_STATUS rs = RPC_S_OK; ValidateMessageArgs(hr, pvArgs, BAENGINE_SETUPDATE_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_SETUPDATE_RESULTS, pResults); - LPCWSTR wzLocalSource = pArgs->wzLocalSource; - LPCWSTR wzDownloadSource = pArgs->wzDownloadSource; - DWORD64 qwSize = pArgs->qwSize; - BOOTSTRAPPER_UPDATE_HASH_TYPE hashType = pArgs->hashType; - BYTE* rgbHash = pArgs->rgbHash; - DWORD cbHash = pArgs->cbHash; - ::EnterCriticalSection(&pContext->pEngineState->csActive); - - if ((!wzLocalSource || !*wzLocalSource) && (!wzDownloadSource || !*wzDownloadSource)) - { - UpdateUninitialize(&pContext->pEngineState->update); - } - else if (BOOTSTRAPPER_UPDATE_HASH_TYPE_NONE == hashType && (0 != cbHash || rgbHash)) - { - hr = E_INVALIDARG; - } - else if (BOOTSTRAPPER_UPDATE_HASH_TYPE_SHA1 == hashType && (SHA1_HASH_LEN != cbHash || !rgbHash)) - { - hr = E_INVALIDARG; - } - else - { - UpdateUninitialize(&pContext->pEngineState->update); - - if (!wzLocalSource || !*wzLocalSource) - { - hr = StrAllocFormatted(&sczLocalSource, L"update\\%ls", pContext->pEngineState->registration.sczExecutableName); - ExitOnFailure(hr, "Failed to default local update source"); - } - - hr = CoreRecreateCommandLine(&sczCommandline, BOOTSTRAPPER_ACTION_INSTALL, pContext->pEngineState->command.display, pContext->pEngineState->command.restart, BOOTSTRAPPER_RELATION_NONE, FALSE, pContext->pEngineState->registration.sczActiveParent, pContext->pEngineState->registration.sczAncestors, NULL, pContext->pEngineState->command.wzCommandLine); - ExitOnFailure(hr, "Failed to recreate command-line for update bundle."); - - // Per-user bundles would fail to use the downloaded update bundle, as the existing install would already be cached - // at the registration id's location. Here I am generating a random guid, but in the future it would be nice if the - // feed would provide the ID of the update. - if (!pContext->pEngineState->registration.fPerMachine) - { - rs = ::UuidCreate(&guid); - hr = HRESULT_FROM_RPC(rs); - ExitOnFailure(hr, "Failed to create bundle update guid."); - - if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid))) - { - hr = E_OUTOFMEMORY; - ExitOnRootFailure(hr, "Failed to convert bundle update guid into string."); - } - - sczId = wzGuid; - } - else - { - sczId = pContext->pEngineState->registration.sczId; - } - - hr = PseudoBundleInitialize(FILEMAKEVERSION(rmj, rmm, rup, rpr), &pContext->pEngineState->update.package, FALSE, sczId, BOOTSTRAPPER_RELATION_UPDATE, BOOTSTRAPPER_PACKAGE_STATE_ABSENT, pContext->pEngineState->registration.sczExecutableName, sczLocalSource ? sczLocalSource : wzLocalSource, wzDownloadSource, qwSize, TRUE, sczCommandline, NULL, NULL, NULL, rgbHash, cbHash); - ExitOnFailure(hr, "Failed to set update bundle."); - - pContext->pEngineState->update.fUpdateAvailable = TRUE; - } + hr = ExternalEngineSetUpdate(pContext->pEngineState, pArgs->wzLocalSource, pArgs->wzDownloadSource, pArgs->qwSize, pArgs->hashType, pArgs->rgbHash, pArgs->cbHash); LExit: - ::LeaveCriticalSection(&pContext->pEngineState->csActive); - - ReleaseStr(sczCommandline); - ReleaseStr(sczLocalSource); return hr; } @@ -419,51 +210,12 @@ static HRESULT BAEngineSetLocalSource( ) { HRESULT hr = S_OK; - BURN_CONTAINER* pContainer = NULL; - BURN_PAYLOAD* pPayload = NULL; ValidateMessageArgs(hr, pvArgs, BAENGINE_SETLOCALSOURCE_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_SETLOCALSOURCE_RESULTS, pResults); - LPCWSTR wzPackageOrContainerId = pArgs->wzPackageOrContainerId; - LPCWSTR wzPayloadId = pArgs->wzPayloadId; - LPCWSTR wzPath = pArgs->wzPath; - - ::EnterCriticalSection(&pContext->pEngineState->csActive); - hr = UserExperienceEnsureEngineInactive(&pContext->pEngineState->userExperience); - ExitOnFailure(hr, "Engine is active, cannot change engine state."); - - if (!wzPath || !*wzPath) - { - hr = E_INVALIDARG; - } - else if (wzPayloadId && * wzPayloadId) - { - hr = PayloadFindById(&pContext->pEngineState->payloads, wzPayloadId, &pPayload); - ExitOnFailure(hr, "BA requested unknown payload with id: %ls", wzPayloadId); - - if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION); - ExitOnFailure(hr, "BA denied while trying to set source on embedded payload: %ls", wzPayloadId); - } - hr = StrAllocString(&pPayload->sczSourcePath, wzPath, 0); - ExitOnFailure(hr, "Failed to set source path for payload."); - } - else if (wzPackageOrContainerId && *wzPackageOrContainerId) - { - hr = ContainerFindById(&pContext->pEngineState->containers, wzPackageOrContainerId, &pContainer); - ExitOnFailure(hr, "BA requested unknown container with id: %ls", wzPackageOrContainerId); - - hr = StrAllocString(&pContainer->sczSourcePath, wzPath, 0); - ExitOnFailure(hr, "Failed to set source path for container."); - } - else - { - hr = E_INVALIDARG; - } + hr = ExternalEngineSetLocalSource(pContext->pEngineState, pArgs->wzPackageOrContainerId, pArgs->wzPayloadId, pArgs->wzPath); LExit: - ::LeaveCriticalSection(&pContext->pEngineState->csActive); return hr; } @@ -474,82 +226,12 @@ static HRESULT BAEngineSetDownloadSource( ) { HRESULT hr = S_OK; - BURN_CONTAINER* pContainer = NULL; - BURN_PAYLOAD* pPayload = NULL; - DOWNLOAD_SOURCE* pDownloadSource = NULL; ValidateMessageArgs(hr, pvArgs, BAENGINE_SETDOWNLOADSOURCE_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_SETDOWNLOADSOURCE_RESULTS, pResults); - LPCWSTR wzPackageOrContainerId = pArgs->wzPackageOrContainerId; - LPCWSTR wzPayloadId = pArgs->wzPayloadId; - LPCWSTR wzUrl = pArgs->wzUrl; - LPCWSTR wzUser = pArgs->wzUser; - LPCWSTR wzPassword = pArgs->wzPassword; - ::EnterCriticalSection(&pContext->pEngineState->csActive); - hr = UserExperienceEnsureEngineInactive(&pContext->pEngineState->userExperience); - ExitOnFailure(hr, "Engine is active, cannot change engine state."); - - if (wzPayloadId && *wzPayloadId) - { - hr = PayloadFindById(&pContext->pEngineState->payloads, wzPayloadId, &pPayload); - ExitOnFailure(hr, "BA requested unknown payload with id: %ls", wzPayloadId); - - if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION); - ExitOnFailure(hr, "BA denied while trying to set download URL on embedded payload: %ls", wzPayloadId); - } - - pDownloadSource = &pPayload->downloadSource; - } - else if (wzPackageOrContainerId && *wzPackageOrContainerId) - { - hr = ContainerFindById(&pContext->pEngineState->containers, wzPackageOrContainerId, &pContainer); - ExitOnFailure(hr, "BA requested unknown container with id: %ls", wzPackageOrContainerId); - - pDownloadSource = &pContainer->downloadSource; - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "BA did not provide container or payload id."); - } - - if (wzUrl && *wzUrl) - { - hr = StrAllocString(&pDownloadSource->sczUrl, wzUrl, 0); - ExitOnFailure(hr, "Failed to set download URL."); - - if (wzUser && *wzUser) - { - hr = StrAllocString(&pDownloadSource->sczUser, wzUser, 0); - ExitOnFailure(hr, "Failed to set download user."); - - if (wzPassword && *wzPassword) - { - hr = StrAllocString(&pDownloadSource->sczPassword, wzPassword, 0); - ExitOnFailure(hr, "Failed to set download password."); - } - else // no password. - { - ReleaseNullStr(pDownloadSource->sczPassword); - } - } - else // no user means no password either. - { - ReleaseNullStr(pDownloadSource->sczUser); - ReleaseNullStr(pDownloadSource->sczPassword); - } - } - else // no URL provided means clear out the whole download source. - { - ReleaseNullStr(pDownloadSource->sczUrl); - ReleaseNullStr(pDownloadSource->sczUser); - ReleaseNullStr(pDownloadSource->sczPassword); - } + hr = ExternalEngineSetDownloadSource(pContext->pEngineState, pArgs->wzPackageOrContainerId, pArgs->wzPayloadId, pArgs->wzUrl, pArgs->wzUser, pArgs->wzPassword); LExit: - ::LeaveCriticalSection(&pContext->pEngineState->csActive); return hr; } @@ -562,19 +244,8 @@ static HRESULT BAEngineSetVariableNumeric( HRESULT hr = S_OK; ValidateMessageArgs(hr, pvArgs, BAENGINE_SETVARIABLENUMERIC_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_SETVARIABLENUMERIC_RESULTS, pResults); - LPCWSTR wzVariable = pArgs->wzVariable; - LONGLONG llValue = pArgs->llValue; - if (wzVariable && *wzVariable) - { - hr = VariableSetNumeric(&pContext->pEngineState->variables, wzVariable, llValue, FALSE); - ExitOnFailure(hr, "Failed to set numeric variable."); - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "BA did not provide variable name."); - } + hr = ExternalEngineSetVariableNumeric(pContext->pEngineState, pArgs->wzVariable, pArgs->llValue); LExit: return hr; @@ -589,19 +260,8 @@ static HRESULT BAEngineSetVariableString( HRESULT hr = S_OK; ValidateMessageArgs(hr, pvArgs, BAENGINE_SETVARIABLESTRING_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_SETVARIABLESTRING_RESULTS, pResults); - LPCWSTR wzVariable = pArgs->wzVariable; - LPCWSTR wzValue = pArgs->wzValue; - if (wzVariable && *wzVariable) - { - hr = VariableSetString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE, pArgs->fFormatted); - ExitOnFailure(hr, "Failed to set string variable."); - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "BA did not provide variable name."); - } + hr = ExternalEngineSetVariableString(pContext->pEngineState, pArgs->wzVariable, pArgs->wzValue, pArgs->fFormatted); LExit: return hr; @@ -614,32 +274,12 @@ static HRESULT BAEngineSetVariableVersion( ) { HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion = NULL; ValidateMessageArgs(hr, pvArgs, BAENGINE_SETVARIABLEVERSION_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_SETVARIABLEVERSION_RESULTS, pResults); - LPCWSTR wzVariable = pArgs->wzVariable; - LPCWSTR wzValue = pArgs->wzValue; - if (wzVariable && *wzVariable) - { - if (wzValue) - { - hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); - ExitOnFailure(hr, "Failed to parse new version value."); - } - - hr = VariableSetVersion(&pContext->pEngineState->variables, wzVariable, pVersion, FALSE); - ExitOnFailure(hr, "Failed to set version variable."); - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "BA did not provide variable name."); - } + hr = ExternalEngineSetVariableVersion(pContext->pEngineState, pArgs->wzVariable, pArgs->wzValue); LExit: - ReleaseVerutilVersion(pVersion); - return hr; } @@ -653,11 +293,7 @@ static HRESULT BAEngineCloseSplashScreen( ValidateMessageArgs(hr, pvArgs, BAENGINE_CLOSESPLASHSCREEN_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_CLOSESPLASHSCREEN_RESULTS, pResults); - // If the splash screen is still around, close it. - if (::IsWindow(pContext->pEngineState->command.hwndSplashScreen)) - { - ::PostMessageW(pContext->pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0); - } + ExternalEngineCloseSplashScreen(pContext->pEngineState); LExit: return hr; @@ -672,11 +308,8 @@ static HRESULT BAEngineCompareVersions( HRESULT hr = S_OK; ValidateMessageArgs(hr, pvArgs, BAENGINE_COMPAREVERSIONS_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_COMPAREVERSIONS_RESULTS, pResults); - LPCWSTR wzVersion1 = pArgs->wzVersion1; - LPCWSTR wzVersion2 = pArgs->wzVersion2; - int* pnResult = &pResults->nResult; - hr = VerCompareStringVersions(wzVersion1, wzVersion2, FALSE, pnResult); + hr = ExternalEngineCompareVersions(pArgs->wzVersion1, pArgs->wzVersion2, &pResults->nResult); LExit: return hr; @@ -692,10 +325,7 @@ static HRESULT BAEngineDetect( ValidateMessageArgs(hr, pvArgs, BAENGINE_DETECT_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_DETECT_RESULTS, pResults); - if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_DETECT, 0, reinterpret_cast(pArgs->hwndParent))) - { - ExitWithLastError(hr, "Failed to post detect message."); - } + hr = ExternalEngineDetect(pContext->dwThreadId, pArgs->hwndParent); LExit: return hr; @@ -710,12 +340,8 @@ static HRESULT BAEnginePlan( HRESULT hr = S_OK; ValidateMessageArgs(hr, pvArgs, BAENGINE_PLAN_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_PLAN_RESULTS, pResults); - BOOTSTRAPPER_ACTION action = pArgs->action; - if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_PLAN, 0, action)) - { - ExitWithLastError(hr, "Failed to post plan message."); - } + hr = ExternalEnginePlan(pContext->dwThreadId, pArgs->action); LExit: return hr; @@ -731,14 +357,7 @@ static HRESULT BAEngineElevate( ValidateMessageArgs(hr, pvArgs, BAENGINE_ELEVATE_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_ELEVATE_RESULTS, pResults); - if (INVALID_HANDLE_VALUE != pContext->pEngineState->companionConnection.hPipe) - { - hr = HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED); - } - else if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_ELEVATE, 0, reinterpret_cast(pArgs->hwndParent))) - { - ExitWithLastError(hr, "Failed to post elevate message."); - } + hr = ExternalEngineElevate(pContext->pEngineState, pContext->dwThreadId, pArgs->hwndParent); LExit: return hr; @@ -754,16 +373,7 @@ static HRESULT BAEngineApply( ValidateMessageArgs(hr, pvArgs, BAENGINE_APPLY_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_APPLY_RESULTS, pResults); - ExitOnNull(pArgs->hwndParent, hr, E_INVALIDARG, "BA passed NULL hwndParent to Apply."); - if (!::IsWindow(pArgs->hwndParent)) - { - ExitOnFailure(hr = E_INVALIDARG, "BA passed invalid hwndParent to Apply."); - } - - if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_APPLY, 0, reinterpret_cast(pArgs->hwndParent))) - { - ExitWithLastError(hr, "Failed to post apply message."); - } + hr = ExternalEngineApply(pContext->dwThreadId, pArgs->hwndParent); LExit: return hr; @@ -779,10 +389,7 @@ static HRESULT BAEngineQuit( ValidateMessageArgs(hr, pvArgs, BAENGINE_QUIT_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_QUIT_RESULTS, pResults); - if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_QUIT, static_cast(pArgs->dwExitCode), 0)) - { - ExitWithLastError(hr, "Failed to post shutdown message."); - } + hr = ExternalEngineQuit(pContext->dwThreadId, pArgs->dwExitCode); LExit: return hr; @@ -795,64 +402,12 @@ static HRESULT BAEngineLaunchApprovedExe( ) { HRESULT hr = S_OK; - BURN_APPROVED_EXE* pApprovedExe = NULL; - BOOL fLeaveCriticalSection = FALSE; - BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe = NULL; ValidateMessageArgs(hr, pvArgs, BAENGINE_LAUNCHAPPROVEDEXE_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BAENGINE_LAUNCHAPPROVEDEXE_RESULTS, pResults); - HWND hwndParent = pArgs->hwndParent; - LPCWSTR wzApprovedExeForElevationId = pArgs->wzApprovedExeForElevationId; - LPCWSTR wzArguments = pArgs->wzArguments; - DWORD dwWaitForInputIdleTimeout = pArgs->dwWaitForInputIdleTimeout; - pLaunchApprovedExe = (BURN_LAUNCH_APPROVED_EXE*)MemAlloc(sizeof(BURN_LAUNCH_APPROVED_EXE), TRUE); - ExitOnNull(pLaunchApprovedExe, hr, E_OUTOFMEMORY, "Failed to alloc BURN_LAUNCH_APPROVED_EXE"); - - ::EnterCriticalSection(&pContext->pEngineState->csActive); - fLeaveCriticalSection = TRUE; - hr = UserExperienceEnsureEngineInactive(&pContext->pEngineState->userExperience); - ExitOnFailure(hr, "Engine is active, cannot change engine state."); - - if (!wzApprovedExeForElevationId || !*wzApprovedExeForElevationId) - { - ExitFunction1(hr = E_INVALIDARG); - } - - hr = ApprovedExesFindById(&pContext->pEngineState->approvedExes, wzApprovedExeForElevationId, &pApprovedExe); - ExitOnFailure(hr, "BA requested unknown approved exe with id: %ls", wzApprovedExeForElevationId); - - ::LeaveCriticalSection(&pContext->pEngineState->csActive); - fLeaveCriticalSection = FALSE; - - hr = StrAllocString(&pLaunchApprovedExe->sczId, wzApprovedExeForElevationId, NULL); - ExitOnFailure(hr, "Failed to copy the id."); - - if (wzArguments) - { - hr = StrAllocString(&pLaunchApprovedExe->sczArguments, wzArguments, NULL); - ExitOnFailure(hr, "Failed to copy the arguments."); - } - - pLaunchApprovedExe->dwWaitForInputIdleTimeout = dwWaitForInputIdleTimeout; - - pLaunchApprovedExe->hwndParent = hwndParent; - - if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_LAUNCH_APPROVED_EXE, 0, reinterpret_cast(pLaunchApprovedExe))) - { - ExitWithLastError(hr, "Failed to post launch approved exe message."); - } + hr = ExternalEngineLaunchApprovedExe(pContext->pEngineState, pContext->dwThreadId, pArgs->hwndParent, pArgs->wzApprovedExeForElevationId, pArgs->wzArguments, pArgs->dwWaitForInputIdleTimeout); LExit: - if (fLeaveCriticalSection) - { - ::LeaveCriticalSection(&pContext->pEngineState->csActive); - } - - if (FAILED(hr)) - { - ApprovedExesUninitializeLaunch(pLaunchApprovedExe); - } - return hr; } @@ -953,34 +508,3 @@ HRESULT WINAPI EngineForApplicationProc( LExit: return hr; } - -static HRESULT CopyStringToBA( - __in LPWSTR wzValue, - __in_opt LPWSTR wzBuffer, - __inout DWORD* pcchBuffer - ) -{ - HRESULT hr = S_OK; - BOOL fTooSmall = !wzBuffer; - - if (!fTooSmall) - { - hr = ::StringCchCopyExW(wzBuffer, *pcchBuffer, wzValue, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); - if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) - { - fTooSmall = TRUE; - } - } - - if (fTooSmall) - { - hr = ::StringCchLengthW(wzValue, STRSAFE_MAX_CCH, reinterpret_cast(pcchBuffer)); - if (SUCCEEDED(hr)) - { - hr = E_MOREDATA; - *pcchBuffer += 1; // null terminator. - } - } - - return hr; -} diff --git a/src/engine/EngineForExtension.cpp b/src/engine/EngineForExtension.cpp index 29b6a6b3..2e1c98fd 100644 --- a/src/engine/EngineForExtension.cpp +++ b/src/engine/EngineForExtension.cpp @@ -3,12 +3,6 @@ #include "precomp.h" -static HRESULT CopyStringToBE( - __in LPWSTR wzValue, - __in LPWSTR wzBuffer, - __inout DWORD* pcchBuffer - ); - static HRESULT BEEngineEscapeString( __in BURN_EXTENSION_ENGINE_CONTEXT* /*pContext*/, __in const LPVOID pvArgs, @@ -16,28 +10,12 @@ static HRESULT BEEngineEscapeString( ) { HRESULT hr = S_OK; - LPWSTR sczValue = NULL; ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_RESULTS, pResults); - LPCWSTR wzIn = pArgs->wzIn; - LPWSTR wzOut = pResults->wzOut; - DWORD* pcchOut = &pResults->cchOut; - if (wzIn && *wzIn) - { - hr = VariableEscapeString(wzIn, &sczValue); - if (SUCCEEDED(hr)) - { - hr = CopyStringToBE(sczValue, wzOut, pcchOut); - } - } - else - { - hr = E_INVALIDARG; - } + hr = ExternalEngineEscapeString(pArgs->wzIn, pResults->wzOut, &pResults->cchOut); LExit: - StrSecureZeroFreeString(sczValue); return hr; } @@ -50,17 +28,8 @@ static HRESULT BEEngineEvaluateCondition( HRESULT hr = S_OK; ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_RESULTS, pResults); - LPCWSTR wzCondition = pArgs->wzCondition; - BOOL* pf = &pResults->f; - if (wzCondition && *wzCondition) - { - hr = ConditionEvaluate(&pContext->pEngineState->variables, wzCondition, pf); - } - else - { - hr = E_INVALIDARG; - } + hr = ExternalEngineEvaluateCondition(pContext->pEngineState, pArgs->wzCondition, &pResults->f); LExit: return hr; @@ -73,28 +42,12 @@ static HRESULT BEEngineFormatString( ) { HRESULT hr = S_OK; - LPWSTR sczValue = NULL; ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_FORMATSTRING_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_FORMATSTRING_RESULTS, pResults); - LPCWSTR wzIn = pArgs->wzIn; - LPWSTR wzOut = pResults->wzOut; - DWORD* pcchOut = &pResults->cchOut; - if (wzIn && *wzIn) - { - hr = VariableFormatString(&pContext->pEngineState->variables, wzIn, &sczValue, NULL); - if (SUCCEEDED(hr)) - { - hr = CopyStringToBE(sczValue, wzOut, pcchOut); - } - } - else - { - hr = E_INVALIDARG; - } + hr = ExternalEngineFormatString(pContext->pEngineState, pArgs->wzIn, pResults->wzOut, &pResults->cchOut); LExit: - StrSecureZeroFreeString(sczValue); return hr; } @@ -107,17 +60,8 @@ static HRESULT BEEngineGetVariableNumeric( HRESULT hr = S_OK; ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_RESULTS, pResults); - LPCWSTR wzVariable = pArgs->wzVariable; - LONGLONG* pllValue = &pResults->llValue; - if (wzVariable && *wzVariable) - { - hr = VariableGetNumeric(&pContext->pEngineState->variables, wzVariable, pllValue); - } - else - { - hr = E_INVALIDARG; - } + hr = ExternalEngineGetVariableNumeric(pContext->pEngineState, pArgs->wzVariable, &pResults->llValue); LExit: return hr; @@ -130,28 +74,12 @@ static HRESULT BEEngineGetVariableString( ) { HRESULT hr = S_OK; - LPWSTR sczValue = NULL; ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_RESULTS, pResults); - LPCWSTR wzVariable = pArgs->wzVariable; - LPWSTR wzValue = pResults->wzValue; - DWORD* pcchValue = &pResults->cchValue; - if (wzVariable && *wzVariable) - { - hr = VariableGetString(&pContext->pEngineState->variables, wzVariable, &sczValue); - if (SUCCEEDED(hr)) - { - hr = CopyStringToBE(sczValue, wzValue, pcchValue); - } - } - else - { - hr = E_INVALIDARG; - } + hr = ExternalEngineGetVariableString(pContext->pEngineState, pArgs->wzVariable, pResults->wzValue, &pResults->cchValue); LExit: - StrSecureZeroFreeString(sczValue); return hr; } @@ -162,29 +90,12 @@ static HRESULT BEEngineGetVariableVersion( ) { HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion = NULL; ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_RESULTS, pResults); - LPCWSTR wzVariable = pArgs->wzVariable; - LPWSTR wzValue = pResults->wzValue; - DWORD* pcchValue = &pResults->cchValue; - if (wzVariable && *wzVariable) - { - hr = VariableGetVersion(&pContext->pEngineState->variables, wzVariable, &pVersion); - if (SUCCEEDED(hr)) - { - hr = CopyStringToBE(pVersion->sczVersion, wzValue, pcchValue); - } - } - else - { - hr = E_INVALIDARG; - } + hr = ExternalEngineGetVariableVersion(pContext->pEngineState, pArgs->wzVariable, pResults->wzValue, &pResults->cchValue); LExit: - ReleaseVerutilVersion(pVersion); - return hr; } @@ -198,10 +109,8 @@ static HRESULT BEEngineLog( REPORT_LEVEL rl = REPORT_NONE; ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_LOG_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_LOG_RESULTS, pResults); - BUNDLE_EXTENSION_LOG_LEVEL level = pArgs->level; - LPCWSTR wzMessage = pArgs->wzMessage; - switch (level) + switch (pArgs->level) { case BUNDLE_EXTENSION_LOG_LEVEL_STANDARD: rl = REPORT_STANDARD; @@ -223,7 +132,7 @@ static HRESULT BEEngineLog( ExitFunction1(hr = E_INVALIDARG); } - hr = LogStringLine(rl, "%ls", wzMessage); + hr = ExternalEngineLog(rl, pArgs->wzMessage); ExitOnFailure(hr, "Failed to log Bundle Extension message."); LExit: @@ -239,19 +148,8 @@ static HRESULT BEEngineSetVariableNumeric( HRESULT hr = S_OK; ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_RESULTS, pResults); - LPCWSTR wzVariable = pArgs->wzVariable; - LONGLONG llValue = pArgs->llValue; - if (wzVariable && *wzVariable) - { - hr = VariableSetNumeric(&pContext->pEngineState->variables, wzVariable, llValue, FALSE); - ExitOnFailure(hr, "Failed to set numeric variable."); - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Bundle Extension did not provide variable name."); - } + hr = ExternalEngineSetVariableNumeric(pContext->pEngineState, pArgs->wzVariable, pArgs->llValue); LExit: return hr; @@ -266,19 +164,8 @@ static HRESULT BEEngineSetVariableString( HRESULT hr = S_OK; ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_RESULTS, pResults); - LPCWSTR wzVariable = pArgs->wzVariable; - LPCWSTR wzValue = pArgs->wzValue; - if (wzVariable && *wzVariable) - { - hr = VariableSetString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE, pArgs->fFormatted); - ExitOnFailure(hr, "Failed to set string variable."); - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Bundle Extension did not provide variable name."); - } + hr = ExternalEngineSetVariableString(pContext->pEngineState, pArgs->wzVariable, pArgs->wzValue, pArgs->fFormatted); LExit: return hr; @@ -291,32 +178,12 @@ static HRESULT BEEngineSetVariableVersion( ) { HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion = NULL; ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_RESULTS, pResults); - LPCWSTR wzVariable = pArgs->wzVariable; - LPCWSTR wzValue = pArgs->wzValue; - if (wzVariable && *wzVariable) - { - if (wzValue) - { - hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); - ExitOnFailure(hr, "Failed to parse new version value."); - } - - hr = VariableSetVersion(&pContext->pEngineState->variables, wzVariable, pVersion, FALSE); - ExitOnFailure(hr, "Failed to set version variable."); - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Bundle Extension did not provide variable name."); - } + hr = ExternalEngineSetVariableVersion(pContext->pEngineState, pArgs->wzVariable, pArgs->wzValue); LExit: - ReleaseVerutilVersion(pVersion); - return hr; } @@ -329,11 +196,8 @@ static HRESULT BEEngineCompareVersions( HRESULT hr = S_OK; ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_ARGS, pArgs); ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_RESULTS, pResults); - LPCWSTR wzVersion1 = pArgs->wzVersion1; - LPCWSTR wzVersion2 = pArgs->wzVersion2; - int* pnResult = &pResults->nResult; - hr = VerCompareStringVersions(wzVersion1, wzVersion2, FALSE, pnResult); + hr = ExternalEngineCompareVersions(pArgs->wzVersion1, pArgs->wzVersion2, &pResults->nResult); LExit: return hr; @@ -397,34 +261,3 @@ HRESULT WINAPI EngineForExtensionProc( LExit: return hr; } - -static HRESULT CopyStringToBE( - __in LPWSTR wzValue, - __in LPWSTR wzBuffer, - __inout DWORD* pcchBuffer - ) -{ - HRESULT hr = S_OK; - BOOL fTooSmall = !wzBuffer; - - if (!fTooSmall) - { - hr = ::StringCchCopyExW(wzBuffer, *pcchBuffer, wzValue, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); - if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) - { - fTooSmall = TRUE; - } - } - - if (fTooSmall) - { - hr = ::StringCchLengthW(wzValue, STRSAFE_MAX_CCH, reinterpret_cast(pcchBuffer)); - if (SUCCEEDED(hr)) - { - hr = E_MOREDATA; - *pcchBuffer += 1; // null terminator. - } - } - - return hr; -} diff --git a/src/engine/externalengine.cpp b/src/engine/externalengine.cpp index ef4f931d..3d5b3696 100644 --- a/src/engine/externalengine.cpp +++ b/src/engine/externalengine.cpp @@ -3,8 +3,737 @@ #include "precomp.h" +static HRESULT CopyStringToExternal( + __in_z LPWSTR wzValue, + __in_z_opt LPWSTR wzBuffer, + __inout DWORD* pcchBuffer + ); + // function definitions +void ExternalEngineGetPackageCount( + __in BURN_ENGINE_STATE* pEngineState, + __out DWORD* pcPackages + ) +{ + *pcPackages = pEngineState->packages.cPackages; +} + +HRESULT ExternalEngineGetVariableNumeric( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __out LONGLONG* pllValue + ) +{ + HRESULT hr = S_OK; + + if (wzVariable && *wzVariable) + { + hr = VariableGetNumeric(&pEngineState->variables, wzVariable, pllValue); + } + else + { + *pllValue = 0; + hr = E_INVALIDARG; + } + + return hr; +} + +HRESULT ExternalEngineGetVariableString( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __out_ecount_opt(*pcchValue) LPWSTR wzValue, + __inout DWORD* pcchValue + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + if (wzVariable && *wzVariable) + { + hr = VariableGetString(&pEngineState->variables, wzVariable, &sczValue); + if (SUCCEEDED(hr)) + { + hr = CopyStringToExternal(sczValue, wzValue, pcchValue); + } + } + else + { + hr = E_INVALIDARG; + } + + StrSecureZeroFreeString(sczValue); + + return hr; +} + +HRESULT ExternalEngineGetVariableVersion( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __out_ecount_opt(*pcchValue) LPWSTR wzValue, + __inout DWORD* pcchValue + ) +{ + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion = NULL; + + if (wzVariable && *wzVariable) + { + hr = VariableGetVersion(&pEngineState->variables, wzVariable, &pVersion); + if (SUCCEEDED(hr)) + { + hr = CopyStringToExternal(pVersion->sczVersion, wzValue, pcchValue); + } + } + else + { + hr = E_INVALIDARG; + } + + ReleaseVerutilVersion(pVersion); + + return hr; +} + +HRESULT ExternalEngineFormatString( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzIn, + __out_ecount_opt(*pcchOut) LPWSTR wzOut, + __inout DWORD* pcchOut + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + if (wzIn && *wzIn) + { + hr = VariableFormatString(&pEngineState->variables, wzIn, &sczValue, NULL); + if (SUCCEEDED(hr)) + { + hr = CopyStringToExternal(sczValue, wzOut, pcchOut); + } + } + else + { + hr = E_INVALIDARG; + } + + StrSecureZeroFreeString(sczValue); + + return hr; +} + +HRESULT ExternalEngineEscapeString( + __in_z LPCWSTR wzIn, + __out_ecount_opt(*pcchOut) LPWSTR wzOut, + __inout DWORD* pcchOut + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + if (wzIn && *wzIn) + { + hr = VariableEscapeString(wzIn, &sczValue); + if (SUCCEEDED(hr)) + { + hr = CopyStringToExternal(sczValue, wzOut, pcchOut); + } + } + else + { + hr = E_INVALIDARG; + } + + StrSecureZeroFreeString(sczValue); + + return hr; +} + +HRESULT ExternalEngineEvaluateCondition( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzCondition, + __out BOOL* pf + ) +{ + HRESULT hr = S_OK; + + if (wzCondition && *wzCondition) + { + hr = ConditionEvaluate(&pEngineState->variables, wzCondition, pf); + } + else + { + *pf = FALSE; + hr = E_INVALIDARG; + } + + return hr; +} + +HRESULT ExternalEngineLog( + __in REPORT_LEVEL rl, + __in_z LPCWSTR wzMessage + ) +{ + HRESULT hr = S_OK; + + hr = LogStringLine(rl, "%ls", wzMessage); + + return hr; +} + +HRESULT ExternalEngineSendEmbeddedError( + __in BURN_ENGINE_STATE* pEngineState, + __in const DWORD dwErrorCode, + __in_z LPCWSTR wzMessage, + __in const DWORD dwUIHint, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + DWORD cbData = 0; + DWORD dwResult = *pnResult = 0; + + if (BURN_MODE_EMBEDDED != pEngineState->mode) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE); + ExitOnRootFailure(hr, "BA requested to send embedded message when not in embedded mode."); + } + + hr = BuffWriteNumber(&pbData, &cbData, dwErrorCode); + ExitOnFailure(hr, "Failed to write error code to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, wzMessage ? wzMessage : L""); + ExitOnFailure(hr, "Failed to write message string to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, dwUIHint); + ExitOnFailure(hr, "Failed to write UI hint to message buffer."); + + hr = PipeSendMessage(pEngineState->embeddedConnection.hPipe, BURN_EMBEDDED_MESSAGE_TYPE_ERROR, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send embedded message over pipe."); + + *pnResult = static_cast(dwResult); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +HRESULT ExternalEngineSendEmbeddedProgress( + __in BURN_ENGINE_STATE* pEngineState, + __in const DWORD dwProgressPercentage, + __in const DWORD dwOverallProgressPercentage, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + DWORD cbData = 0; + DWORD dwResult = *pnResult = 0; + + if (BURN_MODE_EMBEDDED != pEngineState->mode) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE); + ExitOnRootFailure(hr, "BA requested to send embedded progress message when not in embedded mode."); + } + + hr = BuffWriteNumber(&pbData, &cbData, dwProgressPercentage); + ExitOnFailure(hr, "Failed to write progress percentage to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, dwOverallProgressPercentage); + ExitOnFailure(hr, "Failed to write overall progress percentage to message buffer."); + + hr = PipeSendMessage(pEngineState->embeddedConnection.hPipe, BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send embedded progress message over pipe."); + + *pnResult = static_cast(dwResult); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +HRESULT ExternalEngineSetUpdate( + __in BURN_ENGINE_STATE* pEngineState, + __in_z_opt LPCWSTR wzLocalSource, + __in_z_opt LPCWSTR wzDownloadSource, + __in const DWORD64 qwSize, + __in const BOOTSTRAPPER_UPDATE_HASH_TYPE hashType, + __in_opt const BYTE* rgbHash, + __in const DWORD cbHash + ) +{ + HRESULT hr = S_OK; + LPCWSTR sczId = NULL; + LPWSTR sczLocalSource = NULL; + LPWSTR sczCommandline = NULL; + UUID guid = { }; + WCHAR wzGuid[39]; + RPC_STATUS rs = RPC_S_OK; + + ::EnterCriticalSection(&pEngineState->csActive); + + if ((!wzLocalSource || !*wzLocalSource) && (!wzDownloadSource || !*wzDownloadSource)) + { + UpdateUninitialize(&pEngineState->update); + } + else if (BOOTSTRAPPER_UPDATE_HASH_TYPE_NONE == hashType && (0 != cbHash || rgbHash)) + { + hr = E_INVALIDARG; + } + else if (BOOTSTRAPPER_UPDATE_HASH_TYPE_SHA1 == hashType && (SHA1_HASH_LEN != cbHash || !rgbHash)) + { + hr = E_INVALIDARG; + } + else + { + UpdateUninitialize(&pEngineState->update); + + if (!wzLocalSource || !*wzLocalSource) + { + hr = StrAllocFormatted(&sczLocalSource, L"update\\%ls", pEngineState->registration.sczExecutableName); + ExitOnFailure(hr, "Failed to default local update source"); + } + + hr = CoreRecreateCommandLine(&sczCommandline, BOOTSTRAPPER_ACTION_INSTALL, pEngineState->command.display, pEngineState->command.restart, BOOTSTRAPPER_RELATION_NONE, FALSE, pEngineState->registration.sczActiveParent, pEngineState->registration.sczAncestors, NULL, pEngineState->command.wzCommandLine); + ExitOnFailure(hr, "Failed to recreate command-line for update bundle."); + + // Per-user bundles would fail to use the downloaded update bundle, as the existing install would already be cached + // at the registration id's location. Here I am generating a random guid, but in the future it would be nice if the + // feed would provide the ID of the update. + if (!pEngineState->registration.fPerMachine) + { + rs = ::UuidCreate(&guid); + hr = HRESULT_FROM_RPC(rs); + ExitOnFailure(hr, "Failed to create bundle update guid."); + + if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid))) + { + hr = E_OUTOFMEMORY; + ExitOnRootFailure(hr, "Failed to convert bundle update guid into string."); + } + + sczId = wzGuid; + } + else + { + sczId = pEngineState->registration.sczId; + } + + hr = PseudoBundleInitialize(FILEMAKEVERSION(rmj, rmm, rup, rpr), &pEngineState->update.package, FALSE, sczId, BOOTSTRAPPER_RELATION_UPDATE, BOOTSTRAPPER_PACKAGE_STATE_ABSENT, pEngineState->registration.sczExecutableName, sczLocalSource ? sczLocalSource : wzLocalSource, wzDownloadSource, qwSize, TRUE, sczCommandline, NULL, NULL, NULL, rgbHash, cbHash); + ExitOnFailure(hr, "Failed to set update bundle."); + + pEngineState->update.fUpdateAvailable = TRUE; + } + +LExit: + ::LeaveCriticalSection(&pEngineState->csActive); + + ReleaseStr(sczCommandline); + ReleaseStr(sczLocalSource); + + return hr; +} + +HRESULT ExternalEngineSetLocalSource( + __in BURN_ENGINE_STATE* pEngineState, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in_z LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER* pContainer = NULL; + BURN_PAYLOAD* pPayload = NULL; + + ::EnterCriticalSection(&pEngineState->csActive); + hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience); + ExitOnFailure(hr, "Engine is active, cannot change engine state."); + + if (!wzPath || !*wzPath) + { + hr = E_INVALIDARG; + } + else if (wzPayloadId && *wzPayloadId) + { + hr = PayloadFindById(&pEngineState->payloads, wzPayloadId, &pPayload); + ExitOnFailure(hr, "BA requested unknown payload with id: %ls", wzPayloadId); + + if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION); + ExitOnFailure(hr, "BA denied while trying to set source on embedded payload: %ls", wzPayloadId); + } + + hr = StrAllocString(&pPayload->sczSourcePath, wzPath, 0); + ExitOnFailure(hr, "Failed to set source path for payload."); + } + else if (wzPackageOrContainerId && *wzPackageOrContainerId) + { + hr = ContainerFindById(&pEngineState->containers, wzPackageOrContainerId, &pContainer); + ExitOnFailure(hr, "BA requested unknown container with id: %ls", wzPackageOrContainerId); + + hr = StrAllocString(&pContainer->sczSourcePath, wzPath, 0); + ExitOnFailure(hr, "Failed to set source path for container."); + } + else + { + hr = E_INVALIDARG; + } + +LExit: + ::LeaveCriticalSection(&pEngineState->csActive); + + return hr; +} + +HRESULT ExternalEngineSetDownloadSource( + __in BURN_ENGINE_STATE* pEngineState, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in_z_opt LPCWSTR wzUrl, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER* pContainer = NULL; + BURN_PAYLOAD* pPayload = NULL; + DOWNLOAD_SOURCE* pDownloadSource = NULL; + + ::EnterCriticalSection(&pEngineState->csActive); + hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience); + ExitOnFailure(hr, "Engine is active, cannot change engine state."); + + if (wzPayloadId && *wzPayloadId) + { + hr = PayloadFindById(&pEngineState->payloads, wzPayloadId, &pPayload); + ExitOnFailure(hr, "BA requested unknown payload with id: %ls", wzPayloadId); + + if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION); + ExitOnFailure(hr, "BA denied while trying to set download URL on embedded payload: %ls", wzPayloadId); + } + + pDownloadSource = &pPayload->downloadSource; + } + else if (wzPackageOrContainerId && *wzPackageOrContainerId) + { + hr = ContainerFindById(&pEngineState->containers, wzPackageOrContainerId, &pContainer); + ExitOnFailure(hr, "BA requested unknown container with id: %ls", wzPackageOrContainerId); + + pDownloadSource = &pContainer->downloadSource; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "BA did not provide container or payload id."); + } + + if (wzUrl && *wzUrl) + { + hr = StrAllocString(&pDownloadSource->sczUrl, wzUrl, 0); + ExitOnFailure(hr, "Failed to set download URL."); + + if (wzUser && *wzUser) + { + hr = StrAllocString(&pDownloadSource->sczUser, wzUser, 0); + ExitOnFailure(hr, "Failed to set download user."); + + if (wzPassword && *wzPassword) + { + hr = StrAllocString(&pDownloadSource->sczPassword, wzPassword, 0); + ExitOnFailure(hr, "Failed to set download password."); + } + else // no password. + { + ReleaseNullStr(pDownloadSource->sczPassword); + } + } + else // no user means no password either. + { + ReleaseNullStr(pDownloadSource->sczUser); + ReleaseNullStr(pDownloadSource->sczPassword); + } + } + else // no URL provided means clear out the whole download source. + { + ReleaseNullStr(pDownloadSource->sczUrl); + ReleaseNullStr(pDownloadSource->sczUser); + ReleaseNullStr(pDownloadSource->sczPassword); + } + +LExit: + ::LeaveCriticalSection(&pEngineState->csActive); + + return hr; +} + +HRESULT ExternalEngineSetVariableNumeric( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __in const LONGLONG llValue + ) +{ + HRESULT hr = S_OK; + + if (wzVariable && *wzVariable) + { + hr = VariableSetNumeric(&pEngineState->variables, wzVariable, llValue, FALSE); + ExitOnFailure(hr, "Failed to set numeric variable."); + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "SetVariableNumeric did not provide variable name."); + } + +LExit: + return hr; +} + +HRESULT ExternalEngineSetVariableString( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __in_z_opt LPCWSTR wzValue, + __in const BOOL fFormatted + ) +{ + HRESULT hr = S_OK; + + if (wzVariable && *wzVariable) + { + hr = VariableSetString(&pEngineState->variables, wzVariable, wzValue, FALSE, fFormatted); + ExitOnFailure(hr, "Failed to set string variable."); + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "SetVariableString did not provide variable name."); + } + +LExit: + return hr; +} + +HRESULT ExternalEngineSetVariableVersion( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __in_z_opt LPCWSTR wzValue + ) +{ + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion = NULL; + + if (wzVariable && *wzVariable) + { + if (wzValue) + { + hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); + ExitOnFailure(hr, "Failed to parse new version value."); + } + + hr = VariableSetVersion(&pEngineState->variables, wzVariable, pVersion, FALSE); + ExitOnFailure(hr, "Failed to set version variable."); + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "SetVariableVersion did not provide variable name."); + } + +LExit: + ReleaseVerutilVersion(pVersion); + + return hr; +} + +void ExternalEngineCloseSplashScreen( + __in BURN_ENGINE_STATE* pEngineState + ) +{ + // If the splash screen is still around, close it. + if (::IsWindow(pEngineState->command.hwndSplashScreen)) + { + ::PostMessageW(pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0); + } +} + +HRESULT ExternalEngineCompareVersions( + __in_z LPCWSTR wzVersion1, + __in_z LPCWSTR wzVersion2, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + + hr = VerCompareStringVersions(wzVersion1, wzVersion2, FALSE, pnResult); + + return hr; +} + +HRESULT ExternalEngineDetect( + __in const DWORD dwThreadId, + __in_opt const HWND hwndParent + ) +{ + HRESULT hr = S_OK; + + if (!::PostThreadMessageW(dwThreadId, WM_BURN_DETECT, 0, reinterpret_cast(hwndParent))) + { + ExitWithLastError(hr, "Failed to post detect message."); + } + +LExit: + return hr; +} + +HRESULT ExternalEnginePlan( + __in const DWORD dwThreadId, + __in const BOOTSTRAPPER_ACTION action + ) +{ + HRESULT hr = S_OK; + + if (!::PostThreadMessageW(dwThreadId, WM_BURN_PLAN, 0, action)) + { + ExitWithLastError(hr, "Failed to post plan message."); + } + +LExit: + return hr; +} + +HRESULT ExternalEngineElevate( + __in BURN_ENGINE_STATE* pEngineState, + __in const DWORD dwThreadId, + __in_opt const HWND hwndParent + ) +{ + HRESULT hr = S_OK; + + if (INVALID_HANDLE_VALUE != pEngineState->companionConnection.hPipe) + { + hr = HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED); + } + else if (!::PostThreadMessageW(dwThreadId, WM_BURN_ELEVATE, 0, reinterpret_cast(hwndParent))) + { + ExitWithLastError(hr, "Failed to post elevate message."); + } + +LExit: + return hr; +} + +HRESULT ExternalEngineApply( + __in const DWORD dwThreadId, + __in_opt const HWND hwndParent + ) +{ + HRESULT hr = S_OK; + + ExitOnNull(hwndParent, hr, E_INVALIDARG, "BA passed NULL hwndParent to Apply."); + if (!::IsWindow(hwndParent)) + { + ExitOnFailure(hr = E_INVALIDARG, "BA passed invalid hwndParent to Apply."); + } + + if (!::PostThreadMessageW(dwThreadId, WM_BURN_APPLY, 0, reinterpret_cast(hwndParent))) + { + ExitWithLastError(hr, "Failed to post apply message."); + } + +LExit: + return hr; +} + +HRESULT ExternalEngineQuit( + __in const DWORD dwThreadId, + __in const DWORD dwExitCode + ) +{ + HRESULT hr = S_OK; + + if (!::PostThreadMessageW(dwThreadId, WM_BURN_QUIT, static_cast(dwExitCode), 0)) + { + ExitWithLastError(hr, "Failed to post shutdown message."); + } + +LExit: + return hr; +} + +HRESULT ExternalEngineLaunchApprovedExe( + __in BURN_ENGINE_STATE* pEngineState, + __in const DWORD dwThreadId, + __in_opt const HWND hwndParent, + __in_z LPCWSTR wzApprovedExeForElevationId, + __in_z_opt LPCWSTR wzArguments, + __in const DWORD dwWaitForInputIdleTimeout + ) +{ + HRESULT hr = S_OK; + BURN_APPROVED_EXE* pApprovedExe = NULL; + BOOL fLeaveCriticalSection = FALSE; + BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe = NULL; + + pLaunchApprovedExe = (BURN_LAUNCH_APPROVED_EXE*)MemAlloc(sizeof(BURN_LAUNCH_APPROVED_EXE), TRUE); + ExitOnNull(pLaunchApprovedExe, hr, E_OUTOFMEMORY, "Failed to alloc BURN_LAUNCH_APPROVED_EXE"); + + ::EnterCriticalSection(&pEngineState->csActive); + fLeaveCriticalSection = TRUE; + hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience); + ExitOnFailure(hr, "Engine is active, cannot change engine state."); + + if (!wzApprovedExeForElevationId || !*wzApprovedExeForElevationId) + { + ExitFunction1(hr = E_INVALIDARG); + } + + hr = ApprovedExesFindById(&pEngineState->approvedExes, wzApprovedExeForElevationId, &pApprovedExe); + ExitOnFailure(hr, "BA requested unknown approved exe with id: %ls", wzApprovedExeForElevationId); + + ::LeaveCriticalSection(&pEngineState->csActive); + fLeaveCriticalSection = FALSE; + + hr = StrAllocString(&pLaunchApprovedExe->sczId, wzApprovedExeForElevationId, NULL); + ExitOnFailure(hr, "Failed to copy the id."); + + if (wzArguments) + { + hr = StrAllocString(&pLaunchApprovedExe->sczArguments, wzArguments, NULL); + ExitOnFailure(hr, "Failed to copy the arguments."); + } + + pLaunchApprovedExe->dwWaitForInputIdleTimeout = dwWaitForInputIdleTimeout; + + pLaunchApprovedExe->hwndParent = hwndParent; + + if (!::PostThreadMessageW(dwThreadId, WM_BURN_LAUNCH_APPROVED_EXE, 0, reinterpret_cast(pLaunchApprovedExe))) + { + ExitWithLastError(hr, "Failed to post launch approved exe message."); + } + +LExit: + if (fLeaveCriticalSection) + { + ::LeaveCriticalSection(&pEngineState->csActive); + } + + if (FAILED(hr)) + { + ApprovedExesUninitializeLaunch(pLaunchApprovedExe); + } + + return hr; +} + // TODO: callers need to provide the original size (at the time of first public release) of the struct instead of the current size. HRESULT WINAPI ExternalEngineValidateMessageParameter( __in_opt const LPVOID pv, @@ -28,3 +757,34 @@ HRESULT WINAPI ExternalEngineValidateMessageParameter( LExit: return hr; } + +static HRESULT CopyStringToExternal( + __in_z LPWSTR wzValue, + __in_z_opt LPWSTR wzBuffer, + __inout DWORD* pcchBuffer + ) +{ + HRESULT hr = S_OK; + BOOL fTooSmall = !wzBuffer; + + if (!fTooSmall) + { + hr = ::StringCchCopyExW(wzBuffer, *pcchBuffer, wzValue, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + fTooSmall = TRUE; + } + } + + if (fTooSmall) + { + hr = ::StringCchLengthW(wzValue, STRSAFE_MAX_CCH, reinterpret_cast(pcchBuffer)); + if (SUCCEEDED(hr)) + { + hr = E_MOREDATA; + *pcchBuffer += 1; // null terminator. + } + } + + return hr; +} diff --git a/src/engine/externalengine.h b/src/engine/externalengine.h index 7910b224..3f7bc8c8 100644 --- a/src/engine/externalengine.h +++ b/src/engine/externalengine.h @@ -11,6 +11,160 @@ extern "C" { #endif +void ExternalEngineGetPackageCount( + __in BURN_ENGINE_STATE* pEngineState, + __out DWORD* pcPackages + ); + +HRESULT ExternalEngineGetVariableNumeric( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __out LONGLONG* pllValue + ); + +HRESULT ExternalEngineGetVariableString( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __out_ecount_opt(*pcchValue) LPWSTR wzValue, + __inout DWORD* pcchValue + ); + +HRESULT ExternalEngineGetVariableVersion( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __out_ecount_opt(*pcchValue) LPWSTR wzValue, + __inout DWORD* pcchValue + ); + +HRESULT ExternalEngineFormatString( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzIn, + __out_ecount_opt(*pcchOut) LPWSTR wzOut, + __inout DWORD* pcchOut + ); + +HRESULT ExternalEngineEscapeString( + __in_z LPCWSTR wzIn, + __out_ecount_opt(*pcchOut) LPWSTR wzOut, + __inout DWORD* pcchOut + ); + +HRESULT ExternalEngineEvaluateCondition( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzCondition, + __out BOOL* pf + ); + +HRESULT ExternalEngineLog( + __in REPORT_LEVEL rl, + __in_z LPCWSTR wzMessage + ); + +HRESULT ExternalEngineSendEmbeddedError( + __in BURN_ENGINE_STATE* pEngineState, + __in const DWORD dwErrorCode, + __in_z LPCWSTR wzMessage, + __in const DWORD dwUIHint, + __out int* pnResult + ); + +HRESULT ExternalEngineSendEmbeddedProgress( + __in BURN_ENGINE_STATE* pEngineState, + __in const DWORD dwProgressPercentage, + __in const DWORD dwOverallProgressPercentage, + __out int* pnResult + ); + +HRESULT ExternalEngineSetUpdate( + __in BURN_ENGINE_STATE* pEngineState, + __in_z_opt LPCWSTR wzLocalSource, + __in_z_opt LPCWSTR wzDownloadSource, + __in const DWORD64 qwSize, + __in const BOOTSTRAPPER_UPDATE_HASH_TYPE hashType, + __in_opt const BYTE* rgbHash, + __in const DWORD cbHash + ); + +HRESULT ExternalEngineSetLocalSource( + __in BURN_ENGINE_STATE* pEngineState, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in_z LPCWSTR wzPath + ); + +HRESULT ExternalEngineSetDownloadSource( + __in BURN_ENGINE_STATE* pEngineState, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in_z_opt LPCWSTR wzUrl, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword + ); + +HRESULT ExternalEngineSetVariableNumeric( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __in const LONGLONG llValue + ); + +HRESULT ExternalEngineSetVariableString( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __in_z_opt LPCWSTR wzValue, + __in const BOOL fFormatted + ); + +HRESULT ExternalEngineSetVariableVersion( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __in_z_opt LPCWSTR wzValue + ); + +void ExternalEngineCloseSplashScreen( + __in BURN_ENGINE_STATE* pEngineState + ); + +HRESULT ExternalEngineCompareVersions( + __in_z LPCWSTR wzVersion1, + __in_z LPCWSTR wzVersion2, + __out int* pnResult + ); + +HRESULT ExternalEngineDetect( + __in const DWORD dwThreadId, + __in_opt const HWND hwndParent + ); + +HRESULT ExternalEnginePlan( + __in const DWORD dwThreadId, + __in const BOOTSTRAPPER_ACTION action + ); + +HRESULT ExternalEngineElevate( + __in BURN_ENGINE_STATE* pEngineState, + __in const DWORD dwThreadId, + __in_opt const HWND hwndParent + ); + +HRESULT ExternalEngineApply( + __in const DWORD dwThreadId, + __in_opt const HWND hwndParent + ); + +HRESULT ExternalEngineQuit( + __in const DWORD dwThreadId, + __in const DWORD dwExitCode + ); + +HRESULT ExternalEngineLaunchApprovedExe( + __in BURN_ENGINE_STATE* pEngineState, + __in const DWORD dwThreadId, + __in_opt const HWND hwndParent, + __in_z LPCWSTR wzApprovedExeForElevationId, + __in_z_opt LPCWSTR wzArguments, + __in const DWORD dwWaitForInputIdleTimeout + ); + HRESULT WINAPI ExternalEngineValidateMessageParameter( __in_opt const LPVOID pv, __in SIZE_T cbSizeOffset, diff --git a/src/engine/pseudobundle.cpp b/src/engine/pseudobundle.cpp index 70f93701..0864be3a 100644 --- a/src/engine/pseudobundle.cpp +++ b/src/engine/pseudobundle.cpp @@ -19,8 +19,8 @@ extern "C" HRESULT PseudoBundleInitialize( __in_z_opt LPCWSTR wzRepairArguments, __in_z_opt LPCWSTR wzUninstallArguments, __in_opt BURN_DEPENDENCY_PROVIDER* pDependencyProvider, - __in_opt BYTE* pbHash, - __in DWORD cbHash + __in_opt const BYTE* pbHash, + __in const DWORD cbHash ) { HRESULT hr = S_OK; diff --git a/src/engine/pseudobundle.h b/src/engine/pseudobundle.h index 144f6880..4d3d3052 100644 --- a/src/engine/pseudobundle.h +++ b/src/engine/pseudobundle.h @@ -22,8 +22,8 @@ HRESULT PseudoBundleInitialize( __in_z_opt LPCWSTR wzRepairArguments, __in_z_opt LPCWSTR wzUninstallArguments, __in_opt BURN_DEPENDENCY_PROVIDER* pDependencyProvider, - __in_opt BYTE* pbHash, - __in DWORD cbHash + __in_opt const BYTE* pbHash, + __in const DWORD cbHash ); HRESULT PseudoBundleInitializePassthrough( __in BURN_PACKAGE* pPassthroughPackage, -- cgit v1.2.3-55-g6feb From 6ac2cf5c3dc2df77ba8be2cf12c624e1c4a20e18 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 15 Dec 2020 20:02:20 -0600 Subject: Refactor PlanRelatedBundlesBegin without changing behavior. --- src/engine/plan.cpp | 104 ++++++++++++++++++++++++++++++---------------------- src/engine/plan.h | 8 ++++ 2 files changed, 69 insertions(+), 43 deletions(-) diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index 22b7033e..990ef554 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -1211,6 +1211,65 @@ LExit: return hr; } +extern "C" HRESULT PlanDefaultRelatedBundleRequestState( + __in BOOTSTRAPPER_RELATION_TYPE commandRelationType, + __in BOOTSTRAPPER_RELATION_TYPE relatedBundleRelationType, + __in BOOTSTRAPPER_ACTION action, + __in VERUTIL_VERSION* pRegistrationVersion, + __in VERUTIL_VERSION* pRelatedBundleVersion, + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestState + ) +{ + HRESULT hr = S_OK; + int nCompareResult = 0; + + switch (relatedBundleRelationType) + { + case BOOTSTRAPPER_RELATION_UPGRADE: + if (BOOTSTRAPPER_RELATION_UPGRADE != commandRelationType && BOOTSTRAPPER_ACTION_UNINSTALL < action) + { + hr = VerCompareParsedVersions(pRegistrationVersion, pRelatedBundleVersion, &nCompareResult); + ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistrationVersion ? pRegistrationVersion->sczVersion : NULL, pRelatedBundleVersion ? pRelatedBundleVersion->sczVersion : NULL); + + *pRequestState = (nCompareResult < 0) ? BOOTSTRAPPER_REQUEST_STATE_NONE : BOOTSTRAPPER_REQUEST_STATE_ABSENT; + } + break; + case BOOTSTRAPPER_RELATION_PATCH: __fallthrough; + case BOOTSTRAPPER_RELATION_ADDON: + if (BOOTSTRAPPER_ACTION_UNINSTALL == action) + { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT; + } + else if (BOOTSTRAPPER_ACTION_INSTALL == action || BOOTSTRAPPER_ACTION_MODIFY == action) + { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; + } + else if (BOOTSTRAPPER_ACTION_REPAIR == action) + { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_REPAIR; + } + break; + case BOOTSTRAPPER_RELATION_DEPENDENT: + // Automatically repair dependent bundles to restore missing + // packages after uninstall unless we're being upgraded with the + // assumption that upgrades are cumulative (as intended). + if (BOOTSTRAPPER_RELATION_UPGRADE != commandRelationType && BOOTSTRAPPER_ACTION_UNINSTALL == action) + { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_REPAIR; + } + break; + case BOOTSTRAPPER_RELATION_DETECT: + break; + default: + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Unexpected relation type encountered during plan: %d", relatedBundleRelationType); + break; + } + +LExit: + return hr; +} + extern "C" HRESULT PlanRelatedBundlesBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in BURN_REGISTRATION* pRegistration, @@ -1222,7 +1281,6 @@ extern "C" HRESULT PlanRelatedBundlesBegin( LPWSTR* rgsczAncestors = NULL; UINT cAncestors = 0; STRINGDICT_HANDLE sdAncestors = NULL; - int nCompareResult = 0; if (pRegistration->sczAncestors) { @@ -1272,48 +1330,8 @@ extern "C" HRESULT PlanRelatedBundlesBegin( ExitOnFailure(hr, "Failed to copy self to related bundle ancestors."); } - switch (pRelatedBundle->relationType) - { - case BOOTSTRAPPER_RELATION_UPGRADE: - if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < pPlan->action) - { - hr = VerCompareParsedVersions(pRegistration->pVersion, pRelatedBundle->pVersion, &nCompareResult); - ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistration->pVersion, pRelatedBundle->pVersion); - - pRelatedBundle->package.requested = (nCompareResult < 0) ? BOOTSTRAPPER_REQUEST_STATE_NONE : BOOTSTRAPPER_REQUEST_STATE_ABSENT; - } - break; - case BOOTSTRAPPER_RELATION_PATCH: __fallthrough; - case BOOTSTRAPPER_RELATION_ADDON: - if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) - { - pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_ABSENT; - } - else if (BOOTSTRAPPER_ACTION_INSTALL == pPlan->action || BOOTSTRAPPER_ACTION_MODIFY == pPlan->action) - { - pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_PRESENT; - } - else if (BOOTSTRAPPER_ACTION_REPAIR == pPlan->action) - { - pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_REPAIR; - } - break; - case BOOTSTRAPPER_RELATION_DEPENDENT: - // Automatically repair dependent bundles to restore missing - // packages after uninstall unless we're being upgraded with the - // assumption that upgrades are cumulative (as intended). - if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) - { - pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_REPAIR; - } - break; - case BOOTSTRAPPER_RELATION_DETECT: - break; - default: - hr = E_UNEXPECTED; - ExitOnFailure(hr, "Unexpected relation type encountered during plan: %d", pRelatedBundle->relationType); - break; - } + hr = PlanDefaultRelatedBundleRequestState(relationType, pRelatedBundle->relationType, pPlan->action, pRegistration->pVersion, pRelatedBundle->pVersion, &pRelatedBundle->package.requested); + ExitOnFailure(hr, "Failed to get default request state for related bundle."); pRelatedBundle->package.defaultRequested = pRelatedBundle->package.requested; diff --git a/src/engine/plan.h b/src/engine/plan.h index 407c1d48..e9f3a341 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -477,6 +477,14 @@ HRESULT PlanExecutePackage( __in BURN_VARIABLES* pVariables, __inout HANDLE* phSyncpointEvent ); +HRESULT PlanDefaultRelatedBundleRequestState( + __in BOOTSTRAPPER_RELATION_TYPE commandRelationType, + __in BOOTSTRAPPER_RELATION_TYPE relatedBundleRelationType, + __in BOOTSTRAPPER_ACTION action, + __in VERUTIL_VERSION* pRegistrationVersion, + __in VERUTIL_VERSION* pRelatedBundleVersion, + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestState + ); HRESULT PlanRelatedBundlesBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in BURN_REGISTRATION* pRegistration, -- cgit v1.2.3-55-g6feb From 8701b06765803d1af0672df7cff533662990a82e Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 15 Dec 2020 20:02:53 -0600 Subject: WIXBUG:4539 - Fix overall Cache action planning with related bundles. --- src/engine/plan.cpp | 6 ++++ src/test/BurnUnitTest/PlanTest.cpp | 72 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index 990ef554..ccb7b3ce 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -1223,6 +1223,12 @@ extern "C" HRESULT PlanDefaultRelatedBundleRequestState( HRESULT hr = S_OK; int nCompareResult = 0; + // Never touch related bundles during Cache. + if (BOOTSTRAPPER_ACTION_CACHE == action) + { + ExitFunction1(*pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE); + } + switch (relatedBundleRelationType) { case BOOTSTRAPPER_RELATION_UPGRADE: diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp index d3b10678..5b3dca8d 100644 --- a/src/test/BurnUnitTest/PlanTest.cpp +++ b/src/test/BurnUnitTest/PlanTest.cpp @@ -397,6 +397,78 @@ namespace Bootstrapper Assert::Equal(uIndex, pPlan->cPlannedProviders); } + [Fact] + void SingleMsiCacheTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSingleMsiManifest, pEngineState); + DetectAttachedContainerAsAttached(pEngineState); + DetectPackagesAsAbsent(pEngineState); + DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"0.9.0.0"); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_CACHE); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_CACHE, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + Assert::Equal(FALSE, pPlan->fKeepRegistrationDefault); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + DWORD dwPackageStart = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageE", 5, 2, 33741, FALSE); + ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, BURN_PLAN_INVALID_ACTION_INDEX, 2); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageE", L"PackageE", TRUE, FALSE, dwPackageStart); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageE", L"cabkAPka1fWa1PyiVdoVPuoB6Qvs3k", TRUE, FALSE, dwPackageStart); + ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageE", FALSE); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(0ull, pPlan->qwEstimatedSize); + Assert::Equal(33741ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[6].syncpoint.hEvent); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageE"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageE", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(0ul, pPlan->cExecutePackagesTotal); + Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{4a04385a-0081-44ba-acd1-9e4e95cfc97f}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + } + [Fact] void SingleMsiInstallTest() { -- cgit v1.2.3-55-g6feb From b20a77911c6a2b096f021639e0daadae7430091c Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 15 Dec 2020 20:03:48 -0600 Subject: WIXFEAT:6195 - Don't change current directory for ExePackage. --- src/engine/exeengine.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/engine/exeengine.cpp b/src/engine/exeengine.cpp index 8d6cd7fd..d3ec7676 100644 --- a/src/engine/exeengine.cpp +++ b/src/engine/exeengine.cpp @@ -377,8 +377,6 @@ extern "C" HRESULT ExeEngineExecutePackage( ) { HRESULT hr = S_OK; - WCHAR wzCurrentDirectory[MAX_PATH] = { }; - BOOL fChangedCurrentDirectory = FALSE; int nResult = IDNOACTION; LPCWSTR wzArguments = NULL; LPWSTR sczArguments = NULL; @@ -535,13 +533,8 @@ extern "C" HRESULT ExeEngineExecutePackage( { // Make the cache location of the executable the current directory to help those executables // that expect stuff to be relative to them. - if (::GetCurrentDirectoryW(countof(wzCurrentDirectory), wzCurrentDirectory)) - { - fChangedCurrentDirectory = ::SetCurrentDirectoryW(sczCachedDirectory); - } - - si.cb = sizeof(si); // TODO: hookup the stdin/stdout/stderr pipes for logging purposes? - if (!::CreateProcessW(sczExecutablePath, sczCommand, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) + si.cb = sizeof(si); + if (!::CreateProcessW(sczExecutablePath, sczCommand, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, sczCachedDirectory, &si, &pi)) { ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", sczExecutablePath); } @@ -573,11 +566,6 @@ extern "C" HRESULT ExeEngineExecutePackage( ExitOnRootFailure(hr, "Process returned error: 0x%x", dwExitCode); LExit: - if (fChangedCurrentDirectory) - { - ::SetCurrentDirectoryW(wzCurrentDirectory); - } - StrSecureZeroFreeString(sczArguments); StrSecureZeroFreeString(sczArgumentsFormatted); ReleaseStr(sczArgumentsObfuscated); -- cgit v1.2.3-55-g6feb From 5bab3f6ae97b62bb6e79378010c08a13e48fde5a Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Sat, 26 Dec 2020 21:15:06 -0600 Subject: Upgrade to latest dutil as first step for x64 and ARM64 --- src/engine/elevation.cpp | 8 ++++---- src/engine/embedded.cpp | 4 ++-- src/engine/engine.vcxproj | 4 ++-- src/engine/externalengine.cpp | 4 ++-- src/engine/packages.config | 2 +- src/engine/pipe.cpp | 22 +++++++++++----------- src/engine/pipe.h | 4 ++-- src/engine/registration.cpp | 2 +- src/engine/registration.h | 4 ++-- src/engine/section.cpp | 2 +- src/engine/section.h | 2 +- src/engine/splashscreen.cpp | 2 +- src/engine/uithread.cpp | 2 +- src/engine/variable.cpp | 8 ++++---- src/engine/variable.h | 4 ++-- src/stub/packages.config | 2 +- src/stub/stub.vcxproj | 4 ++-- src/test/BurnUnitTest/BurnUnitTest.vcxproj | 6 +++--- src/test/BurnUnitTest/packages.config | 2 +- 19 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp index af5610dc..94418dc3 100644 --- a/src/engine/elevation.cpp +++ b/src/engine/elevation.cpp @@ -865,7 +865,7 @@ extern "C" HRESULT ElevationExecuteMsiPackage( hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msiPackage.pPackage->sczId); ExitOnFailure(hr, "Failed to write package id to message buffer."); - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)hwndParent); + hr = BuffWritePointer(&pbData, &cbData, (DWORD_PTR)hwndParent); ExitOnFailure(hr, "Failed to write parent hwnd to message buffer."); hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msiPackage.sczLogPath); @@ -944,7 +944,7 @@ extern "C" HRESULT ElevationExecuteMspPackage( hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.pPackage->sczId); ExitOnFailure(hr, "Failed to write package id to message buffer."); - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)hwndParent); + hr = BuffWritePointer(&pbData, &cbData, (DWORD_PTR)hwndParent); ExitOnFailure(hr, "Failed to write parent hwnd to message buffer."); hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.sczTargetProductCode); @@ -2322,7 +2322,7 @@ static HRESULT OnExecuteMsiPackage( hr = PackageFindById(pPackages, sczPackage, &executeAction.msiPackage.pPackage); ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&hwndParent); + hr = BuffReadPointer(pbData, cbData, &iData, (DWORD_PTR*)&hwndParent); ExitOnFailure(hr, "Failed to read parent hwnd."); hr = BuffReadString(pbData, cbData, &iData, &executeAction.msiPackage.sczLogPath); @@ -2420,7 +2420,7 @@ static HRESULT OnExecuteMspPackage( hr = PackageFindById(pPackages, sczPackage, &executeAction.mspTarget.pPackage); ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&hwndParent); + hr = BuffReadPointer(pbData, cbData, &iData, (DWORD_PTR*)&hwndParent); ExitOnFailure(hr, "Failed to read parent hwnd."); executeAction.mspTarget.fPerMachineTarget = TRUE; // we're in the elevated process, clearly we're targeting a per-machine product. diff --git a/src/engine/embedded.cpp b/src/engine/embedded.cpp index 09666980..b512b365 100644 --- a/src/engine/embedded.cpp +++ b/src/engine/embedded.cpp @@ -147,7 +147,7 @@ static HRESULT OnEmbeddedErrorMessage( ) { HRESULT hr = S_OK; - DWORD iData = 0; + SIZE_T iData = 0; GENERIC_EXECUTE_MESSAGE message = { }; LPWSTR sczMessage = NULL; @@ -181,7 +181,7 @@ static HRESULT OnEmbeddedProgress( ) { HRESULT hr = S_OK; - DWORD iData = 0; + SIZE_T iData = 0; GENERIC_EXECUTE_MESSAGE message = { }; message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index 3624d923..d62105ed 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -2,7 +2,7 @@ - + @@ -174,7 +174,7 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + \ No newline at end of file diff --git a/src/engine/externalengine.cpp b/src/engine/externalengine.cpp index 3d5b3696..39878c8b 100644 --- a/src/engine/externalengine.cpp +++ b/src/engine/externalengine.cpp @@ -194,7 +194,7 @@ HRESULT ExternalEngineSendEmbeddedError( { HRESULT hr = S_OK; BYTE* pbData = NULL; - DWORD cbData = 0; + SIZE_T cbData = 0; DWORD dwResult = *pnResult = 0; if (BURN_MODE_EMBEDDED != pEngineState->mode) @@ -232,7 +232,7 @@ HRESULT ExternalEngineSendEmbeddedProgress( { HRESULT hr = S_OK; BYTE* pbData = NULL; - DWORD cbData = 0; + SIZE_T cbData = 0; DWORD dwResult = *pnResult = 0; if (BURN_MODE_EMBEDDED != pEngineState->mode) diff --git a/src/engine/packages.config b/src/engine/packages.config index dc425a64..f074ae43 100644 --- a/src/engine/packages.config +++ b/src/engine/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/engine/pipe.cpp b/src/engine/pipe.cpp index 7ecc4859..eb3eb863 100644 --- a/src/engine/pipe.cpp +++ b/src/engine/pipe.cpp @@ -12,9 +12,9 @@ static const LPCWSTR CACHE_PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls.Cache"; static HRESULT AllocatePipeMessage( __in DWORD dwMessage, __in_bcount_opt(cbData) LPVOID pvData, - __in DWORD cbData, + __in SIZE_T cbData, __out_bcount(cb) LPVOID* ppvMessage, - __out DWORD* cbMessage + __out SIZE_T* cbMessage ); static void FreePipeMessage( __in BURN_PIPE_MESSAGE *pMsg @@ -23,7 +23,7 @@ static HRESULT WritePipeMessage( __in HANDLE hPipe, __in DWORD dwMessage, __in_bcount_opt(cbData) LPVOID pvData, - __in DWORD cbData + __in SIZE_T cbData ); static HRESULT GetPipeMessage( __in HANDLE hPipe, @@ -77,7 +77,7 @@ extern "C" HRESULT PipeSendMessage( __in HANDLE hPipe, __in DWORD dwMessage, __in_bcount_opt(cbData) LPVOID pvData, - __in DWORD cbData, + __in SIZE_T cbData, __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, __in_opt LPVOID pvContext, __out DWORD* pdwResult @@ -665,14 +665,14 @@ LExit: static HRESULT AllocatePipeMessage( __in DWORD dwMessage, __in_bcount_opt(cbData) LPVOID pvData, - __in DWORD cbData, + __in SIZE_T cbData, __out_bcount(cb) LPVOID* ppvMessage, - __out DWORD* cbMessage + __out SIZE_T* cbMessage ) { HRESULT hr = S_OK; LPVOID pv = NULL; - DWORD cb = 0; + SIZE_T cb = 0; // If no data was provided, ensure the count of bytes is zero. if (!pvData) @@ -716,22 +716,22 @@ static HRESULT WritePipeMessage( __in HANDLE hPipe, __in DWORD dwMessage, __in_bcount_opt(cbData) LPVOID pvData, - __in DWORD cbData + __in SIZE_T cbData ) { HRESULT hr = S_OK; LPVOID pv = NULL; - DWORD cb = 0; + SIZE_T cb = 0; hr = AllocatePipeMessage(dwMessage, pvData, cbData, &pv, &cb); ExitOnFailure(hr, "Failed to allocate message to write."); // Write the message. DWORD cbWrote = 0; - DWORD cbTotalWritten = 0; + SIZE_T cbTotalWritten = 0; while (cbTotalWritten < cb) { - if (!::WriteFile(hPipe, pv, cb - cbTotalWritten, &cbWrote, NULL)) + if (!::WriteFile(hPipe, pv, (DWORD)(cb - cbTotalWritten), &cbWrote, NULL)) { ExitWithLastError(hr, "Failed to write message type to pipe."); } diff --git a/src/engine/pipe.h b/src/engine/pipe.h index b6a7742a..085c3a76 100644 --- a/src/engine/pipe.h +++ b/src/engine/pipe.h @@ -17,7 +17,7 @@ typedef struct _BURN_PIPE_CONNECTION HANDLE hCachePipe; } BURN_PIPE_CONNECTION; -typedef enum _BURN_PIPE_MESSAGE_TYPE +typedef enum _BURN_PIPE_MESSAGE_TYPE : DWORD { BURN_PIPE_MESSAGE_TYPE_LOG = 0xF0000001, BURN_PIPE_MESSAGE_TYPE_COMPLETE = 0xF0000002, @@ -58,7 +58,7 @@ HRESULT PipeSendMessage( __in HANDLE hPipe, __in DWORD dwMessage, __in_bcount_opt(cbData) LPVOID pvData, - __in DWORD cbData, + __in SIZE_T cbData, __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, __in_opt LPVOID pvContext, __out DWORD* pdwResult diff --git a/src/engine/registration.cpp b/src/engine/registration.cpp index 4e8c810d..d3732e74 100644 --- a/src/engine/registration.cpp +++ b/src/engine/registration.cpp @@ -982,7 +982,7 @@ LExit: extern "C" HRESULT RegistrationLoadState( __in BURN_REGISTRATION* pRegistration, __out_bcount(*pcbBuffer) BYTE** ppbBuffer, - __out DWORD* pcbBuffer + __out SIZE_T* pcbBuffer ) { // read data from file diff --git a/src/engine/registration.h b/src/engine/registration.h index fa7be368..dc9bc5b7 100644 --- a/src/engine/registration.h +++ b/src/engine/registration.h @@ -196,12 +196,12 @@ HRESULT RegistrationSessionEnd( HRESULT RegistrationSaveState( __in BURN_REGISTRATION* pRegistration, __in_bcount_opt(cbBuffer) BYTE* pbBuffer, - __in_opt DWORD cbBuffer + __in_opt SIZE_T cbBuffer ); HRESULT RegistrationLoadState( __in BURN_REGISTRATION* pRegistration, __out_bcount(*pcbBuffer) BYTE** ppbBuffer, - __out DWORD* pcbBuffer + __out SIZE_T* pcbBuffer ); HRESULT RegistrationGetResumeCommandLine( __in const BURN_REGISTRATION* pRegistration, diff --git a/src/engine/section.cpp b/src/engine/section.cpp index 3adcdd14..3720155c 100644 --- a/src/engine/section.cpp +++ b/src/engine/section.cpp @@ -51,7 +51,7 @@ extern "C" HRESULT SectionInitialize( DWORD dwSignatureOffset = 0; DWORD cbSignature = 0; IMAGE_SECTION_HEADER sectionHeader = { }; - DWORD dwOriginalChecksumAndSignatureOffset = 0; + DWORD_PTR dwOriginalChecksumAndSignatureOffset = 0; BURN_SECTION_HEADER* pBurnSectionHeader = NULL; pSection->hEngineFile = hEngineFile; diff --git a/src/engine/section.h b/src/engine/section.h index 78331469..6c62ba44 100644 --- a/src/engine/section.h +++ b/src/engine/section.h @@ -20,7 +20,7 @@ typedef struct _BURN_SECTION DWORD dwChecksumOffset; DWORD dwCertificateTableOffset; - DWORD dwOriginalChecksumAndSignatureOffset; + DWORD_PTR dwOriginalChecksumAndSignatureOffset; DWORD dwOriginalChecksum; DWORD dwOriginalSignatureOffset; diff --git a/src/engine/splashscreen.cpp b/src/engine/splashscreen.cpp index cad8c88c..90bd5203 100644 --- a/src/engine/splashscreen.cpp +++ b/src/engine/splashscreen.cpp @@ -196,7 +196,7 @@ static LRESULT CALLBACK WndProc( ) { LRESULT lres = 0; - SPLASHSCREEN_INFO* pSplashScreen = reinterpret_cast(::GetWindowLongW(hWnd, GWLP_USERDATA)); + SPLASHSCREEN_INFO* pSplashScreen = reinterpret_cast(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); switch (uMsg) { diff --git a/src/engine/uithread.cpp b/src/engine/uithread.cpp index cf111745..39f92142 100644 --- a/src/engine/uithread.cpp +++ b/src/engine/uithread.cpp @@ -196,7 +196,7 @@ static LRESULT CALLBACK WndProc( BOOL fRet = FALSE; // Always block shutdown in the elevated process, but ask the BA in the non-elevated. - UITHREAD_INFO* pInfo = reinterpret_cast(::GetWindowLongW(hWnd, GWLP_USERDATA)); + UITHREAD_INFO* pInfo = reinterpret_cast(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); if (!pInfo->fElevated) { // TODO: instead of recommending canceling all non-critical shutdowns, maybe we should only recommend cancel diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp index 4bf73a9b..ea84752d 100644 --- a/src/engine/variable.cpp +++ b/src/engine/variable.cpp @@ -53,7 +53,7 @@ static HRESULT FormatString( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzIn, __out_z_opt LPWSTR* psczOut, - __out_opt DWORD* pcchOut, + __out_opt SIZE_T* pcchOut, __in BOOL fObfuscateHiddenVariables, __out BOOL* pfContainsHiddenVariable ); @@ -704,7 +704,7 @@ extern "C" HRESULT VariableFormatString( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzIn, __out_z_opt LPWSTR* psczOut, - __out_opt DWORD* pcchOut + __out_opt SIZE_T* pcchOut ) { return FormatString(pVariables, wzIn, psczOut, pcchOut, FALSE, NULL); @@ -714,7 +714,7 @@ extern "C" HRESULT VariableFormatStringObfuscated( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzIn, __out_z_opt LPWSTR* psczOut, - __out_opt DWORD* pcchOut + __out_opt SIZE_T* pcchOut ) { return FormatString(pVariables, wzIn, psczOut, pcchOut, TRUE, NULL); @@ -1085,7 +1085,7 @@ static HRESULT FormatString( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzIn, __out_z_opt LPWSTR* psczOut, - __out_opt DWORD* pcchOut, + __out_opt SIZE_T* pcchOut, __in BOOL fObfuscateHiddenVariables, __out BOOL* pfContainsHiddenVariable ) diff --git a/src/engine/variable.h b/src/engine/variable.h index 713fe6e3..a38c9daa 100644 --- a/src/engine/variable.h +++ b/src/engine/variable.h @@ -126,13 +126,13 @@ HRESULT VariableFormatString( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzIn, __out_z_opt LPWSTR* psczOut, - __out_opt DWORD* pcchOut + __out_opt SIZE_T* pcchOut ); HRESULT VariableFormatStringObfuscated( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzIn, __out_z_opt LPWSTR* psczOut, - __out_opt DWORD* pcchOut + __out_opt SIZE_T* pcchOut ); HRESULT VariableEscapeString( __in_z LPCWSTR wzIn, diff --git a/src/stub/packages.config b/src/stub/packages.config index eb65b3b3..7681e32c 100644 --- a/src/stub/packages.config +++ b/src/stub/packages.config @@ -4,5 +4,5 @@ - + \ No newline at end of file diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index 42b614cd..aa2f964e 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -5,7 +5,7 @@ - + @@ -107,6 +107,6 @@ - + \ No newline at end of file diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index 71f3ea09..18527de5 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -4,7 +4,7 @@ - + Debug @@ -81,6 +81,6 @@ - + - + \ No newline at end of file diff --git a/src/test/BurnUnitTest/packages.config b/src/test/BurnUnitTest/packages.config index 472349e7..f9d0f75a 100644 --- a/src/test/BurnUnitTest/packages.config +++ b/src/test/BurnUnitTest/packages.config @@ -10,5 +10,5 @@ - + \ No newline at end of file -- cgit v1.2.3-55-g6feb From 32fce16069d47fbf7e08414e9fd31e2a09968395 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 17 Jan 2021 20:15:48 -0600 Subject: Update PlanTest to use manifests created by latest v4. --- src/engine/manifest.cpp | 6 +- src/engine/manifest.h | 4 +- src/test/BurnUnitTest/BurnUnitTest.vcxproj | 4 + src/test/BurnUnitTest/PlanTest.cpp | 300 ++++++++------------- .../BasicFunctionality_BundleA_manifest.xml | 1 + .../PlanTest/MsiTransaction_BundleAv1_manifest.xml | 1 + 6 files changed, 129 insertions(+), 187 deletions(-) create mode 100644 src/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml create mode 100644 src/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml diff --git a/src/engine/manifest.cpp b/src/engine/manifest.cpp index 270b6b74..fe8c6cad 100644 --- a/src/engine/manifest.cpp +++ b/src/engine/manifest.cpp @@ -10,8 +10,8 @@ static HRESULT ParseFromXml( // function definitions -extern "C" HRESULT ManifestLoadXml( - __in LPCWSTR wzDocument, +extern "C" HRESULT ManifestLoadXmlFromFile( + __in LPCWSTR wzPath, __in BURN_ENGINE_STATE* pEngineState ) { @@ -19,7 +19,7 @@ extern "C" HRESULT ManifestLoadXml( IXMLDOMDocument* pixdDocument = NULL; // load xml document - hr = XmlLoadDocument(wzDocument, &pixdDocument); + hr = XmlLoadDocumentFromFile(wzPath, &pixdDocument); ExitOnFailure(hr, "Failed to load manifest as XML document."); hr = ParseFromXml(pixdDocument, pEngineState); diff --git a/src/engine/manifest.h b/src/engine/manifest.h index 223181d9..8c527279 100644 --- a/src/engine/manifest.h +++ b/src/engine/manifest.h @@ -11,8 +11,8 @@ extern "C" { // function declarations -HRESULT ManifestLoadXml( - __in LPCWSTR wzDocument, +HRESULT ManifestLoadXmlFromFile( + __in LPCWSTR wzPath, __in BURN_ENGINE_STATE* pEngineState ); diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index 18527de5..b668f68a 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -60,6 +60,10 @@ + + + + diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp index 5b3dca8d..71e455c3 100644 --- a/src/test/BurnUnitTest/PlanTest.cpp +++ b/src/test/BurnUnitTest/PlanTest.cpp @@ -9,118 +9,8 @@ static HRESULT WINAPI PlanTestBAProc( __in_opt LPVOID pvContext ); -static LPCWSTR wzMsiTransactionManifest = - L"" - L" " - L" " - L" "; - -static LPCWSTR wzSingleMsiManifest = - L"" - L" " - L" " - L" "; +static LPCWSTR wzMsiTransactionManifestFileName = L"MsiTransaction_BundleAv1_manifest.xml"; +static LPCWSTR wzSingleMsiManifestFileName = L"BasicFunctionality_BundleA_manifest.xml"; namespace Microsoft { @@ -150,7 +40,7 @@ namespace Bootstrapper BURN_ENGINE_STATE* pEngineState = &engineState; BURN_PLAN* pPlan = &engineState.plan; - InitializeEngineStateForCorePlan(wzMsiTransactionManifest, pEngineState); + InitializeEngineStateForCorePlan(wzMsiTransactionManifestFileName, pEngineState); DetectPackagesAsAbsent(pEngineState); DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"1.0.0.0"); @@ -166,27 +56,27 @@ namespace Bootstrapper DWORD dwIndex = 0; DWORD dwPackageStart = 0; ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", 6, 2, 33741, FALSE); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", 6, 2, 33743, FALSE); ValidateCacheAcquireContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE); ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, dwPackageStart, 6); ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"PackageA", TRUE, FALSE, dwPackageStart); - ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"cab1QmlL013Hqv_44W64R0cvnHn_2c", TRUE, FALSE, dwPackageStart); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"cab9Ins_fTP3wNwq5Gxo41ch5VUPaQ", TRUE, FALSE, dwPackageStart); ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 9); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageB", 14, 2, 33753, FALSE); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageB", 14, 2, 33743, FALSE); ValidateCacheAcquireContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", TRUE); ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, dwPackageStart, 2); ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageB", L"PackageB", TRUE, FALSE, dwPackageStart); - ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageB", L"cabQH1Sgh7w2K8tLIftUaaWVhMWt0s", TRUE, FALSE, dwPackageStart); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageB", L"cablKtJUKxAbhSMIBwQU6vJ_CDsIkE", TRUE, FALSE, dwPackageStart); ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageB", FALSE); ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 14); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageC", 22, 2, 33739, FALSE); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageC", 22, 2, 33743, FALSE); ValidateCacheAcquireContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", TRUE); ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, dwPackageStart, 2); ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageC", L"PackageC", TRUE, FALSE, dwPackageStart); - ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageC", L"cabRT8kdm93olnEAQB2GSO3u0400VI", TRUE, FALSE, dwPackageStart); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageC", L"cab3wekki1le1R8RPDV2B8_g8jcjZc", TRUE, FALSE, dwPackageStart); ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageC", FALSE); ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); Assert::Equal(24ul, pPlan->cCacheActions); @@ -197,8 +87,8 @@ namespace Bootstrapper ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); - Assert::Equal(106166ull, pPlan->qwEstimatedSize); - Assert::Equal(101233ull, pPlan->qwCacheSizeTotal); + Assert::Equal(107082ull, pPlan->qwEstimatedSize); + Assert::Equal(101229ull, pPlan->qwCacheSizeTotal); fRollback = FALSE; dwIndex = 0; @@ -212,7 +102,7 @@ namespace Bootstrapper ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteRegistration(pPlan, fRollback, dwIndex++, TRUE); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); @@ -225,7 +115,7 @@ namespace Bootstrapper ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[23].syncpoint.hEvent); dwExecuteCheckpointId += 1; // cache checkpoints @@ -234,7 +124,7 @@ namespace Bootstrapper ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCommitMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -253,7 +143,7 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -264,7 +154,7 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageC"); @@ -272,7 +162,7 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -286,7 +176,7 @@ namespace Bootstrapper Assert::Equal(dwIndex, pPlan->cCleanActions); UINT uIndex = 0; - ValidatePlannedProvider(pPlan, uIndex++, L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", NULL); Assert::Equal(uIndex, pPlan->cPlannedProviders); } @@ -298,7 +188,7 @@ namespace Bootstrapper BURN_ENGINE_STATE* pEngineState = &engineState; BURN_PLAN* pPlan = &engineState.plan; - InitializeEngineStateForCorePlan(wzMsiTransactionManifest, pEngineState); + InitializeEngineStateForCorePlan(wzMsiTransactionManifestFileName, pEngineState); DetectPackagesAsPresentAndCached(pEngineState); hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); @@ -327,13 +217,13 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteBeginMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); @@ -342,7 +232,7 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -357,19 +247,19 @@ namespace Bootstrapper dwExecuteCheckpointId = 1; ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -390,10 +280,10 @@ namespace Bootstrapper Assert::Equal(dwIndex, pPlan->cCleanActions); UINT uIndex = 0; - ValidatePlannedProvider(pPlan, uIndex++, L"{c096190a-8bf3-4342-a1d2-94ea9cb853d6}", NULL); - ValidatePlannedProvider(pPlan, uIndex++, L"{BE27CF2B-9E5F-4500-BAE3-5E0E522FB962}", NULL); - ValidatePlannedProvider(pPlan, uIndex++, L"{388E4963-13AD-4EE7-B907-AA8888F50E54}", NULL); - ValidatePlannedProvider(pPlan, uIndex++, L"{196E43EA-EF92-4FF8-B9AC-A0FD0D225BB4}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{A497C5E5-C78B-4F0B-BF72-B33E1DB1C4B8}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{D1D01094-23CE-4AF0-84B6-4A1A133F21D3}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{01E6B748-7B95-4BA9-976D-B6F35076CEF4}", NULL); Assert::Equal(uIndex, pPlan->cPlannedProviders); } @@ -405,7 +295,7 @@ namespace Bootstrapper BURN_ENGINE_STATE* pEngineState = &engineState; BURN_PLAN* pPlan = &engineState.plan; - InitializeEngineStateForCorePlan(wzSingleMsiManifest, pEngineState); + InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); DetectAttachedContainerAsAttached(pEngineState); DetectPackagesAsAbsent(pEngineState); DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"0.9.0.0"); @@ -422,11 +312,11 @@ namespace Bootstrapper DWORD dwIndex = 0; DWORD dwPackageStart = 0; ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageE", 5, 2, 33741, FALSE); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", 5, 2, 33743, FALSE); ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, BURN_PLAN_INVALID_ACTION_INDEX, 2); - ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageE", L"PackageE", TRUE, FALSE, dwPackageStart); - ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageE", L"cabkAPka1fWa1PyiVdoVPuoB6Qvs3k", TRUE, FALSE, dwPackageStart); - ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageE", FALSE); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"PackageA", TRUE, FALSE, dwPackageStart); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"cab9Ins_fTP3wNwq5Gxo41ch5VUPaQ", TRUE, FALSE, dwPackageStart); + ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); Assert::Equal(dwIndex, pPlan->cCacheActions); @@ -435,7 +325,7 @@ namespace Bootstrapper Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); Assert::Equal(0ull, pPlan->qwEstimatedSize); - Assert::Equal(33741ull, pPlan->qwCacheSizeTotal); + Assert::Equal(33743ull, pPlan->qwCacheSizeTotal); fRollback = FALSE; dwIndex = 0; @@ -451,9 +341,9 @@ namespace Bootstrapper dwIndex = 0; dwExecuteCheckpointId = 2; ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageE"); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageE", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); Assert::Equal(dwIndex, pPlan->cRollbackActions); @@ -465,7 +355,7 @@ namespace Bootstrapper Assert::Equal(dwIndex, pPlan->cCleanActions); UINT uIndex = 0; - ValidatePlannedProvider(pPlan, uIndex++, L"{4a04385a-0081-44ba-acd1-9e4e95cfc97f}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); Assert::Equal(uIndex, pPlan->cPlannedProviders); } @@ -477,7 +367,7 @@ namespace Bootstrapper BURN_ENGINE_STATE* pEngineState = &engineState; BURN_PLAN* pPlan = &engineState.plan; - InitializeEngineStateForCorePlan(wzSingleMsiManifest, pEngineState); + InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); DetectAttachedContainerAsAttached(pEngineState); DetectPackagesAsAbsent(pEngineState); DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"0.9.0.0"); @@ -494,22 +384,22 @@ namespace Bootstrapper DWORD dwIndex = 0; DWORD dwPackageStart = 0; ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageE", 5, 2, 33741, FALSE); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", 5, 2, 33743, FALSE); ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, BURN_PLAN_INVALID_ACTION_INDEX, 2); - ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageE", L"PackageE", TRUE, FALSE, dwPackageStart); - ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageE", L"cabkAPka1fWa1PyiVdoVPuoB6Qvs3k", TRUE, FALSE, dwPackageStart); - ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageE", FALSE); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"PackageA", TRUE, FALSE, dwPackageStart); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"cab9Ins_fTP3wNwq5Gxo41ch5VUPaQ", TRUE, FALSE, dwPackageStart); + ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); Assert::Equal(dwIndex, pPlan->cCacheActions); fRollback = TRUE; dwIndex = 0; ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"PackageE", FALSE); + ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); - Assert::Equal(35381ull, pPlan->qwEstimatedSize); - Assert::Equal(33741ull, pPlan->qwCacheSizeTotal); + Assert::Equal(35694ull, pPlan->qwEstimatedSize); + Assert::Equal(33743ull, pPlan->qwCacheSizeTotal); fRollback = FALSE; dwIndex = 0; @@ -518,12 +408,12 @@ namespace Bootstrapper ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[6].syncpoint.hEvent); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageE", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageE", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteRegistration(pPlan, fRollback, dwIndex++, TRUE); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageE", L"{4a04385a-0081-44ba-acd1-9e4e95cfc97f}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[6].syncpoint.hEvent); @@ -535,13 +425,13 @@ namespace Bootstrapper dwExecuteCheckpointId = 2; ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); ValidateExecuteRegistration(pPlan, fRollback, dwIndex++, FALSE); - ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageE"); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageE", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageE", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageE", L"{4a04385a-0081-44ba-acd1-9e4e95cfc97f}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -555,7 +445,7 @@ namespace Bootstrapper Assert::Equal(dwIndex, pPlan->cCleanActions); UINT uIndex = 0; - ValidatePlannedProvider(pPlan, uIndex++, L"{4a04385a-0081-44ba-acd1-9e4e95cfc97f}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); Assert::Equal(uIndex, pPlan->cPlannedProviders); } @@ -567,7 +457,7 @@ namespace Bootstrapper BURN_ENGINE_STATE* pEngineState = &engineState; BURN_PLAN* pPlan = &engineState.plan; - InitializeEngineStateForCorePlan(wzSingleMsiManifest, pEngineState); + InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); DetectPackagesAsPresentAndCached(pEngineState); hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); @@ -594,11 +484,11 @@ namespace Bootstrapper DWORD dwExecuteCheckpointId = 1; ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageE", L"{4a04385a-0081-44ba-acd1-9e4e95cfc97f}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageE", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageE", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteRegistration(pPlan, fRollback, dwIndex++, FALSE); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -608,11 +498,11 @@ namespace Bootstrapper dwIndex = 0; dwExecuteCheckpointId = 1; ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageE", L"{4a04385a-0081-44ba-acd1-9e4e95cfc97f}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageE", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageE", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteRegistration(pPlan, fRollback, dwIndex++, TRUE); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -623,20 +513,21 @@ namespace Bootstrapper Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal); dwIndex = 0; - ValidateCleanAction(pPlan, dwIndex++, L"PackageE"); + ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); Assert::Equal(dwIndex, pPlan->cCleanActions); UINT uIndex = 0; - ValidatePlannedProvider(pPlan, uIndex++, L"{4a04385a-0081-44ba-acd1-9e4e95cfc97f}", NULL); - ValidatePlannedProvider(pPlan, uIndex++, L"{284F56B6-B6C7-404A-B9B5-78F63BF79494}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{64633047-D172-4BBB-B202-64337D15C952}", NULL); Assert::Equal(uIndex, pPlan->cPlannedProviders); } private: // This doesn't initialize everything, just enough for CorePlan to work. - void InitializeEngineStateForCorePlan(LPCWSTR wzManifest, BURN_ENGINE_STATE* pEngineState) + void InitializeEngineStateForCorePlan(LPCWSTR wzManifestFileName, BURN_ENGINE_STATE* pEngineState) { HRESULT hr = S_OK; + LPWSTR sczFilePath = NULL; ::InitializeCriticalSection(&pEngineState->csActive); ::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive); @@ -644,8 +535,22 @@ namespace Bootstrapper hr = VariableInitialize(&pEngineState->variables); NativeAssert::Succeeded(hr, "Failed to initialize variables."); - hr = ManifestLoadXml(wzManifest, pEngineState); - NativeAssert::Succeeded(hr, "Failed to load manifest."); + try + { + pin_ptr dataDirectory = PtrToStringChars(this->TestContext->TestDirectory); + hr = PathConcat(dataDirectory, L"TestData\\PlanTest", &sczFilePath); + NativeAssert::Succeeded(hr, "Failed to get path to test file directory."); + hr = PathConcat(sczFilePath, wzManifestFileName, &sczFilePath); + NativeAssert::Succeeded(hr, "Failed to get path to test file."); + Assert::True(FileExistsEx(sczFilePath, NULL), "Test file does not exist."); + + hr = ManifestLoadXmlFromFile(sczFilePath, pEngineState); + NativeAssert::Succeeded(hr, "Failed to load manifest."); + } + finally + { + ReleaseStr(sczFilePath); + } pEngineState->userExperience.pfnBAProc = PlanTestBAProc; } @@ -662,6 +567,22 @@ namespace Bootstrapper } } + void DetectPackageAsAbsent(BURN_PACKAGE* pPackage) + { + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + } + + void DetectPackageAsPresentAndCached(BURN_PACKAGE* pPackage) + { + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; + pPackage->cache = BURN_CACHE_STATE_COMPLETE; + + for (DWORD i = 0; i < pPackage->cPayloads; ++i) + { + pPackage->rgPayloads[i].fCached = TRUE; + } + } + void DetectPackagesAsAbsent(BURN_ENGINE_STATE* pEngineState) { DetectReset(&pEngineState->registration, &pEngineState->packages); @@ -670,7 +591,7 @@ namespace Bootstrapper for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) { BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; - pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + DetectPackageAsAbsent(pPackage); } } @@ -684,12 +605,27 @@ namespace Bootstrapper for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) { BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; - pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; - pPackage->cache = BURN_CACHE_STATE_COMPLETE; + DetectPackageAsPresentAndCached(pPackage); + } + } - for (DWORD j = 0; j < pPackage->cPayloads; ++j) + void DetectPermanentPackagesAsPresentAndCached(BURN_ENGINE_STATE* pEngineState) + { + DetectReset(&pEngineState->registration, &pEngineState->packages); + PlanReset(&pEngineState->plan, &pEngineState->packages); + + pEngineState->registration.fInstalled = TRUE; + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + if (pPackage->fUninstallable) + { + DetectPackageAsAbsent(pPackage); + } + else { - pPackage->rgPayloads[j].fCached = TRUE; + DetectPackageAsPresentAndCached(pPackage); } } } diff --git a/src/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml b/src/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml new file mode 100644 index 00000000..e5aeb515 --- /dev/null +++ b/src/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml b/src/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml new file mode 100644 index 00000000..6ed7e01b --- /dev/null +++ b/src/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml @@ -0,0 +1 @@ + \ No newline at end of file -- cgit v1.2.3-55-g6feb From 2b1973c7fc4be8dd2a3e0c95ef43d3742278d0cd Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 17 Jan 2021 20:19:24 -0600 Subject: Remove dead code. --- .../inc/BootstrapperEngine.h | 14 -------------- src/engine/apply.cpp | 4 ---- src/engine/dependency.cpp | 2 -- src/engine/exeengine.cpp | 2 +- src/engine/logging.cpp | 6 ------ src/engine/msiengine.cpp | 8 +------- src/engine/mspengine.cpp | 2 +- src/engine/msuengine.cpp | 2 +- src/engine/package.h | 12 +++++++++++- src/engine/plan.cpp | 6 ------ src/engine/plan.h | 6 ------ src/stub/stub.vcxproj | 2 +- 12 files changed, 16 insertions(+), 50 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h index 98a9d77d..9642748b 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h @@ -36,12 +36,9 @@ enum BOOTSTRAPPER_ACTION_STATE BOOTSTRAPPER_ACTION_STATE_NONE, BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BOOTSTRAPPER_ACTION_STATE_INSTALL, - BOOTSTRAPPER_ACTION_STATE_ADMIN_INSTALL, BOOTSTRAPPER_ACTION_STATE_MODIFY, BOOTSTRAPPER_ACTION_STATE_REPAIR, BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE, - BOOTSTRAPPER_ACTION_STATE_MAJOR_UPGRADE, - BOOTSTRAPPER_ACTION_STATE_PATCH, }; enum BOOTSTRAPPER_PACKAGE_STATE @@ -73,17 +70,6 @@ enum BOOTSTRAPPER_FEATURE_STATE BOOTSTRAPPER_FEATURE_STATE_SOURCE, }; -enum BOOTSTRAPPER_FEATURE_ACTION -{ - BOOTSTRAPPER_FEATURE_ACTION_NONE, - BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL, - BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE, - BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT, - BOOTSTRAPPER_FEATURE_ACTION_REINSTALL, - BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE, - BOOTSTRAPPER_FEATURE_ACTION_REMOVE, -}; - enum BOOTSTRAPPER_LOG_LEVEL { BOOTSTRAPPER_LOG_LEVEL_NONE, // turns off report (only valid for XXXSetLevel()) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 833d750c..c5d27277 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -1743,8 +1743,6 @@ static HRESULT DoExecuteAction( ExitOnFailure(hr, "Failed to execute commit MSI transaction action."); break; - case BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP: __fallthrough; - case BURN_EXECUTE_ACTION_TYPE_SERVICE_START: __fallthrough; default: hr = E_UNEXPECTED; ExitOnFailure(hr, "Invalid execute action."); @@ -1854,8 +1852,6 @@ static HRESULT DoRollbackActions( IgnoreRollbackError(hr, "Failed to uncache package for rollback."); break; - case BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP: __fallthrough; - case BURN_EXECUTE_ACTION_TYPE_SERVICE_START: __fallthrough; default: hr = E_UNEXPECTED; ExitOnFailure(hr, "Invalid rollback action: %d.", pRollbackAction->type); diff --git a/src/engine/dependency.cpp b/src/engine/dependency.cpp index c01449b6..af4ab0a1 100644 --- a/src/engine/dependency.cpp +++ b/src/engine/dependency.cpp @@ -922,8 +922,6 @@ static void CalculateDependencyActionStates( case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough; case BOOTSTRAPPER_ACTION_STATE_REPAIR: __fallthrough; case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: __fallthrough; - case BOOTSTRAPPER_ACTION_STATE_MAJOR_UPGRADE: __fallthrough; - case BOOTSTRAPPER_ACTION_STATE_PATCH: *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER; break; } diff --git a/src/engine/exeengine.cpp b/src/engine/exeengine.cpp index d3ec7676..fd33d926 100644 --- a/src/engine/exeengine.cpp +++ b/src/engine/exeengine.cpp @@ -223,7 +223,7 @@ extern "C" HRESULT ExeEnginePlanCalculatePackage( // Calculate the rollback action if there is an execute action. if (BOOTSTRAPPER_ACTION_STATE_NONE != execute) { - switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState) + switch (pPackage->currentState) { case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: switch (pPackage->requested) diff --git a/src/engine/logging.cpp b/src/engine/logging.cpp index 512b562c..49b2bcc3 100644 --- a/src/engine/logging.cpp +++ b/src/engine/logging.cpp @@ -301,18 +301,12 @@ extern "C" LPCSTR LoggingActionStateToString( return "Uninstall"; case BOOTSTRAPPER_ACTION_STATE_INSTALL: return "Install"; - case BOOTSTRAPPER_ACTION_STATE_ADMIN_INSTALL: - return "AdminInstall"; case BOOTSTRAPPER_ACTION_STATE_MODIFY: return "Modify"; case BOOTSTRAPPER_ACTION_STATE_REPAIR: return "Repair"; case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: return "MinorUpgrade"; - case BOOTSTRAPPER_ACTION_STATE_MAJOR_UPGRADE: - return "MajorUpgrade"; - case BOOTSTRAPPER_ACTION_STATE_PATCH: - return "Patch"; default: return "Invalid"; } diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index c298e219..688be7af 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -856,7 +856,7 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( // Calculate the rollback action if there is an execute action. if (BOOTSTRAPPER_ACTION_STATE_NONE != execute && !fInsideMsiTransaction) { - switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState) + switch (pPackage->currentState) { case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough; case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: @@ -1322,12 +1322,6 @@ extern "C" HRESULT MsiEngineExecutePackage( // switch (pExecuteAction->msiPackage.action) { - case BOOTSTRAPPER_ACTION_STATE_ADMIN_INSTALL: - hr = StrAllocConcatSecure(&sczProperties, L" ACTION=ADMIN", 0); - ExitOnFailure(hr, "Failed to add ADMIN property on admin install."); - __fallthrough; - - case BOOTSTRAPPER_ACTION_STATE_MAJOR_UPGRADE: __fallthrough; case BOOTSTRAPPER_ACTION_STATE_INSTALL: hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0); ExitOnFailure(hr, "Failed to add reboot suppression property on install."); diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp index e14173d1..c432b78b 100644 --- a/src/engine/mspengine.cpp +++ b/src/engine/mspengine.cpp @@ -332,7 +332,7 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( // Calculate the rollback action if there is an execute action. if (BOOTSTRAPPER_ACTION_STATE_NONE != execute && !fInsideMsiTransaction) { - switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState) + switch (pPackage->currentState) { case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: switch (requested) diff --git a/src/engine/msuengine.cpp b/src/engine/msuengine.cpp index 0c81873e..641d55b1 100644 --- a/src/engine/msuengine.cpp +++ b/src/engine/msuengine.cpp @@ -146,7 +146,7 @@ extern "C" HRESULT MsuEnginePlanCalculatePackage( // Calculate the rollback action if there is an execute action. if (BOOTSTRAPPER_ACTION_STATE_NONE != execute) { - switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState) + switch (pPackage->currentState) { case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: switch (pPackage->requested) diff --git a/src/engine/package.h b/src/engine/package.h index f3e817eb..ec176f8c 100644 --- a/src/engine/package.h +++ b/src/engine/package.h @@ -67,6 +67,17 @@ enum BURN_PATCH_TARGETCODE_TYPE BURN_PATCH_TARGETCODE_TYPE_UPGRADE, }; +enum BOOTSTRAPPER_FEATURE_ACTION +{ + BOOTSTRAPPER_FEATURE_ACTION_NONE, + BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL, + BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE, + BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT, + BOOTSTRAPPER_FEATURE_ACTION_REINSTALL, + BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE, + BOOTSTRAPPER_FEATURE_ACTION_REMOVE, +}; + // structs typedef struct _BURN_EXE_EXIT_CODE @@ -188,7 +199,6 @@ typedef struct _BURN_PACKAGE BOOTSTRAPPER_PACKAGE_STATE currentState; // only valid after Detect. BURN_CACHE_STATE cache; // only valid after Detect. - BOOTSTRAPPER_PACKAGE_STATE expected; // only valid during Plan. BOOTSTRAPPER_REQUEST_STATE defaultRequested;// only valid during Plan. BOOTSTRAPPER_REQUEST_STATE requested; // only valid during Plan. BOOL fAcquire; // only valid during Plan. diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index ccb7b3ce..0dba36c6 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -306,11 +306,6 @@ extern "C" void PlanUninitializeExecuteAction( ReleaseStr(pExecuteAction->msuPackage.sczLogPath); break; - case BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP: __fallthrough; - case BURN_EXECUTE_ACTION_TYPE_SERVICE_START: - ReleaseStr(pExecuteAction->service.sczServiceName); - break; - case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: ReleaseStr(pExecuteAction->packageDependency.sczBundleProviderKey); break; @@ -1958,7 +1953,6 @@ static void ResetPlannedPackageState( ) { // Reset package state that is a result of planning. - pPackage->expected = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; pPackage->defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE; pPackage->requested = BOOTSTRAPPER_REQUEST_STATE_NONE; pPackage->fAcquire = FALSE; diff --git a/src/engine/plan.h b/src/engine/plan.h index e9f3a341..9d7debe1 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -61,8 +61,6 @@ enum BURN_EXECUTE_ACTION_TYPE BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE, BURN_EXECUTE_ACTION_TYPE_MSP_TARGET, BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE, - BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP, - BURN_EXECUTE_ACTION_TYPE_SERVICE_START, BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER, BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY, BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY, @@ -284,10 +282,6 @@ typedef struct _BURN_EXECUTE_ACTION BOOTSTRAPPER_ACTION_STATE action; } msuPackage; struct - { - LPWSTR sczServiceName; - } service; - struct { BOOL fKeep; } registration; diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index aa2f964e..082b80e5 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -55,7 +55,7 @@ $(ProjectDir)..\engine\inc - cabinet.lib;crypt32.lib;gdiplus.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;wintrust.lib;wuguid.lib;engine.lib;engine.res + cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;wintrust.lib;wuguid.lib;engine.lib;engine.res -- cgit v1.2.3-55-g6feb From 0bda2285a3523a58675320b0b4ff54bc7afe472b Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 18 Jan 2021 22:23:41 -0600 Subject: Fix error tracing, DUTIL_SOURCE_DEFAULT wasn't defined the same in the stub and engine.lib. --- src/stub/precomp.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/stub/precomp.h b/src/stub/precomp.h index c8301a0f..ad0988b5 100644 --- a/src/stub/precomp.h +++ b/src/stub/precomp.h @@ -4,6 +4,10 @@ #include +#include + +#define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_EXTERNAL + #include #include #include -- cgit v1.2.3-55-g6feb From 059e476a8d9af2472503057d7102852e64e9ca0b Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Sat, 26 Dec 2020 22:15:06 -0500 Subject: Enable ControlFlowGuard. --- src/Cpp.Build.props | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Cpp.Build.props b/src/Cpp.Build.props index 4d2da36f..ef9de6f0 100644 --- a/src/Cpp.Build.props +++ b/src/Cpp.Build.props @@ -31,6 +31,7 @@ StdCall true false + Guard -YlprecompDefine /Zc:threadSafeInit- %(AdditionalOptions) true @@ -80,6 +81,7 @@ + MultiThreadedDebugDll @@ -99,6 +101,7 @@ + MultiThreadedDll -- cgit v1.2.3-55-g6feb From a5b86b987bb5a6fbcdb191bbe8b51a621140b4e6 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Sat, 26 Dec 2020 22:15:06 -0500 Subject: Fix code analysis warnings. --- src/engine/apply.cpp | 11 +++++++++-- src/engine/apply.h | 2 +- src/engine/cabextract.cpp | 2 +- src/engine/core.cpp | 2 +- src/engine/elevation.cpp | 4 ++-- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index c5d27277..8e5099d9 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -291,10 +291,12 @@ extern "C" void ApplyReset( extern "C" HRESULT ApplyLock( __in BOOL /*fPerMachine*/, - __out HANDLE* /*phLock*/ + __out HANDLE* phLock ) { HRESULT hr = S_OK; + *phLock = NULL; + #if 0 // eventually figure out the correct way to support this. In its current form, embedded bundles (including related bundles) are hosed. DWORD er = ERROR_SUCCESS; HANDLE hLock = NULL; @@ -459,6 +461,8 @@ extern "C" HRESULT ApplyCache( DWORD iPackageStartAction = BURN_PLAN_INVALID_ACTION_INDEX; DWORD iPackageCompleteAction = BURN_PLAN_INVALID_ACTION_INDEX; + *pfRollback = FALSE; + hr = UserExperienceOnCacheBegin(pUX); ExitOnRootFailure(hr, "BA aborted cache."); @@ -732,7 +736,7 @@ extern "C" HRESULT ApplyExecute( __in BURN_ENGINE_STATE* pEngineState, __in_opt HANDLE hCacheThread, __inout DWORD* pcOverallProgressTicks, - __out BOOL* pfKeepRegistration, + __inout BOOL* pfKeepRegistration, __out BOOL* pfRollback, __out BOOL* pfSuspend, __out BOOTSTRAPPER_APPLY_RESTART* pRestart @@ -749,6 +753,9 @@ extern "C" HRESULT ApplyExecute( context.cExecutePackagesTotal = pEngineState->plan.cExecutePackagesTotal; context.pcOverallProgressTicks = pcOverallProgressTicks; + *pfRollback = FALSE; + *pfSuspend = FALSE; + // Send execute begin to BA. hr = UserExperienceOnExecuteBegin(&pEngineState->userExperience, pEngineState->plan.cExecutePackagesTotal); ExitOnRootFailure(hr, "BA aborted execute begin."); diff --git a/src/engine/apply.h b/src/engine/apply.h index b717251e..00e1fceb 100644 --- a/src/engine/apply.h +++ b/src/engine/apply.h @@ -89,7 +89,7 @@ HRESULT ApplyExecute( __in BURN_ENGINE_STATE* pEngineState, __in_opt HANDLE hCacheThread, __inout DWORD* pcOverallProgressTicks, - __out BOOL* pfKeepRegistration, + __inout BOOL* pfKeepRegistration, __out BOOL* pfRollback, __out BOOL* pfSuspend, __out BOOTSTRAPPER_APPLY_RESTART* pRestart diff --git a/src/engine/cabextract.cpp b/src/engine/cabextract.cpp index 04c2c6ec..5a02ff8a 100644 --- a/src/engine/cabextract.cpp +++ b/src/engine/cabextract.cpp @@ -543,7 +543,7 @@ static INT_PTR CopyFileCallback( // copy stream name hr = StrAllocStringAnsi(pContext->Cabinet.psczStreamName, pFDINotify->psz1, 0, CP_UTF8); - ExitOnFailure(hr, "Failed to copy stream name: %ls", pFDINotify->psz1); + ExitOnFailure(hr, "Failed to copy stream name: %hs", pFDINotify->psz1); // set operation complete event if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent)) diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 028dc1cc..d157d3b3 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -36,7 +36,7 @@ static HRESULT ParseCommandLine( __out_z LPWSTR* psczSanitizedCommandLine ); static HRESULT ParsePipeConnection( - __in LPWSTR* rgArgs, + __in_ecount(3) LPWSTR* rgArgs, __in BURN_PIPE_CONNECTION* pConnection ); static HRESULT DetectPackage( diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp index 94418dc3..fc53b1f8 100644 --- a/src/engine/elevation.cpp +++ b/src/engine/elevation.cpp @@ -107,7 +107,7 @@ static HRESULT ProcessApplyInitializeMessages( ); static HRESULT ProcessGenericExecuteMessages( __in BURN_PIPE_MESSAGE* pMsg, - __in_opt LPVOID pvContext, + __in LPVOID pvContext, __out DWORD* pdwResult ); static HRESULT ProcessMsiPackageMessages( @@ -1425,7 +1425,7 @@ LExit: static HRESULT ProcessGenericExecuteMessages( __in BURN_PIPE_MESSAGE* pMsg, - __in_opt LPVOID pvContext, + __in LPVOID pvContext, __out DWORD* pdwResult ) { -- cgit v1.2.3-55-g6feb From a563fb99daca852422d61ae8599a3dc32fa1bfe6 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Sat, 26 Dec 2020 22:15:06 -0500 Subject: First steps on Burn for x64 and ARM64 --- appveyor.cmd | 2 ++ burn.sln | 16 ++++++++++++++-- src/engine/engine.cpp | 29 ++++++++++++++++++++++++++++- src/engine/engine.mc | 2 +- src/engine/engine.vcxproj | 8 ++++++++ src/engine/variable.cpp | 23 +++++++++++++++-------- src/stub/WixToolset.Burn.nuspec | 4 ++++ src/stub/stub.vcxproj | 8 ++++++++ src/test/BurnUnitTest/BurnUnitTest.vcxproj | 8 ++++++++ 9 files changed, 88 insertions(+), 12 deletions(-) diff --git a/appveyor.cmd b/appveyor.cmd index 82602274..b9cf1258 100644 --- a/appveyor.cmd +++ b/appveyor.cmd @@ -6,6 +6,8 @@ nuget restore || exit /b msbuild -t:Test -p:Configuration=Release src\test\BurnUnitTest || exit /b msbuild -p:Configuration=Release;Platform=x86 || exit /b +msbuild -p:Configuration=Release;Platform=x64 || exit /b +msbuild -p:Configuration=Release;Platform=arm64 || exit /b msbuild -p:Configuration=Release -t:Pack src\stub\stub.vcxproj || exit /b msbuild -p:Configuration=Release -t:Pack src\WixToolset.BootstrapperCore.Native\WixToolset.BootstrapperCore.Native.proj || exit /b diff --git a/burn.sln b/burn.sln index 32fe11d5..6a64b8f0 100644 --- a/burn.sln +++ b/burn.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26730.12 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30711.63 MinimumVisualStudioVersion = 15.0.26124.0 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "engine", "src\engine\engine.vcxproj", "{8119537D-E1D9-6591-D51A-49768A2F9C37}" EndProject @@ -11,31 +11,43 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BurnUnitTest", "src\test\Bu EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 + Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|ARM64.Build.0 = Debug|ARM64 {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|x64.ActiveCfg = Debug|x64 {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|x64.Build.0 = Debug|x64 {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|x86.ActiveCfg = Debug|Win32 {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|x86.Build.0 = Debug|Win32 + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|ARM64.ActiveCfg = Release|ARM64 + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|ARM64.Build.0 = Release|ARM64 {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|x64.ActiveCfg = Release|x64 {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|x64.Build.0 = Release|x64 {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|x86.ActiveCfg = Release|Win32 {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|x86.Build.0 = Release|Win32 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|ARM64.Build.0 = Debug|ARM64 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|x64.ActiveCfg = Debug|x64 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|x64.Build.0 = Debug|x64 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|x86.ActiveCfg = Debug|Win32 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|x86.Build.0 = Debug|Win32 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|ARM64.ActiveCfg = Release|ARM64 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|ARM64.Build.0 = Release|ARM64 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x64.ActiveCfg = Release|x64 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x64.Build.0 = Release|x64 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x86.ActiveCfg = Release|Win32 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x86.Build.0 = Release|Win32 + {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Debug|ARM64.ActiveCfg = Debug|ARM64 {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Debug|x64.ActiveCfg = Debug|Win32 {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Debug|x86.ActiveCfg = Debug|Win32 {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Debug|x86.Build.0 = Debug|Win32 + {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Release|ARM64.ActiveCfg = Release|ARM64 {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Release|x64.ActiveCfg = Release|Win32 {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Release|x86.ActiveCfg = Release|Win32 {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Release|x86.Build.0 = Release|Win32 diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index ae5b690c..3dbfb365 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -93,6 +93,7 @@ extern "C" HRESULT EngineRun( BOOL fRegInitialized = FALSE; BOOL fWiuInitialized = FALSE; BOOL fXmlInitialized = FALSE; + SYSTEM_INFO si = { }; OSVERSIONINFOEXW ovix = { }; LPWSTR sczExePath = NULL; BOOL fRunNormal = FALSE; @@ -155,8 +156,34 @@ extern "C" HRESULT EngineRun( ExitWithLastError(hr, "Failed to get OS info."); } +#if defined(_M_ARM64) + LPCSTR szBurnPlatform = "ARM64"; +#elif defined(_M_AMD64) + LPCSTR szBurnPlatform = "x64"; +#else + LPCSTR szBurnPlatform = "x86"; +#endif + + LPCSTR szMachinePlatform = "unknown architecture"; + ::GetNativeSystemInfo(&si); + switch (si.wProcessorArchitecture) + { + case PROCESSOR_ARCHITECTURE_AMD64: + szMachinePlatform = "x64"; + break; + case PROCESSOR_ARCHITECTURE_ARM: + szMachinePlatform = "ARM"; + break; + case PROCESSOR_ARCHITECTURE_ARM64: + szMachinePlatform = "ARM64"; + break; + case PROCESSOR_ARCHITECTURE_INTEL: + szMachinePlatform = "x86"; + break; + } + PathForCurrentProcess(&sczExePath, NULL); // Ignore failure. - LogId(REPORT_STANDARD, MSG_BURN_INFO, szVerMajorMinorBuild, ovix.dwMajorVersion, ovix.dwMinorVersion, ovix.dwBuildNumber, ovix.wServicePackMajor, sczExePath); + LogId(REPORT_STANDARD, MSG_BURN_INFO, szVerMajorMinorBuild, ovix.dwMajorVersion, ovix.dwMinorVersion, ovix.dwBuildNumber, ovix.wServicePackMajor, sczExePath, szBurnPlatform, szMachinePlatform); ReleaseNullStr(sczExePath); // initialize core diff --git a/src/engine/engine.mc b/src/engine/engine.mc index b36a9527..8e36e84e 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -33,7 +33,7 @@ MessageId=1 Severity=Success SymbolicName=MSG_BURN_INFO Language=English -Burn v%1!hs!, Windows v%2!d!.%3!d! (Build %4!d!: Service Pack %5!d!), path: %6!ls! +Burn %7!hs! v%1!hs!, Windows v%2!d!.%3!d! %8!hs! (Build %4!d!: Service Pack %5!d!), path: %6!ls! . MessageId=2 diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index d62105ed..52f71b1d 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -21,6 +21,14 @@ Release x64 + + Debug + ARM64 + + + Release + ARM64 + diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp index ea84752d..51dbdff4 100644 --- a/src/engine/variable.cpp +++ b/src/engine/variable.cpp @@ -157,10 +157,6 @@ static HRESULT InitializeVariableNumeric( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); -static HRESULT InitializeVariableRegistryFolder( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); static HRESULT InitializeVariable6432Folder( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue @@ -190,6 +186,13 @@ static HRESULT Get64bitFolderFromRegistry( __deref_out_z LPWSTR* psczPath ); +#if !defined(_WIN64) +static HRESULT InitializeVariableRegistryFolder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +#endif + // function definitions @@ -1101,13 +1104,17 @@ static HRESULT FormatString( LPWSTR* rgVariables = NULL; DWORD cVariables = 0; DWORD cch = 0; + size_t cchIn = 0; BOOL fHidden = FALSE; MSIHANDLE hRecord = NULL; ::EnterCriticalSection(&pVariables->csAccess); // allocate buffer for format string - hr = StrAlloc(&sczFormat, lstrlenW(wzIn) + 1); + hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_CCH - 1, &cchIn); + ExitOnFailure(hr, "Failed to length of format string."); + + hr = StrAlloc(&sczFormat, cchIn + 1); ExitOnFailure(hr, "Failed to allocate buffer for format string."); // read out variables from the unformatted string and build a format string @@ -1133,7 +1140,7 @@ static HRESULT FormatString( ExitOnFailure(hr, "Failed to append string."); break; } - cch = wzClose - wzOpen - 1; + cch = (DWORD)(wzClose - wzOpen - 1); if (0 == cch) { @@ -2170,6 +2177,7 @@ LExit: return hr; } +#if !defined(_WIN64) static HRESULT InitializeVariableRegistryFolder( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue @@ -2179,7 +2187,6 @@ static HRESULT InitializeVariableRegistryFolder( int nFolder = (int)dwpData; LPWSTR sczPath = NULL; -#if !defined(_WIN64) BOOL fIsWow64 = FALSE; ProcWow64(::GetCurrentProcess(), &fIsWow64); @@ -2187,7 +2194,6 @@ static HRESULT InitializeVariableRegistryFolder( { ExitFunction(); } -#endif hr = Get64bitFolderFromRegistry(nFolder, &sczPath); ExitOnFailure(hr, "Failed to get 64-bit folder."); @@ -2201,6 +2207,7 @@ LExit: return hr; } +#endif static HRESULT InitializeVariable6432Folder( __in DWORD_PTR dwpData, diff --git a/src/stub/WixToolset.Burn.nuspec b/src/stub/WixToolset.Burn.nuspec index 35392523..f314a8a4 100644 --- a/src/stub/WixToolset.Burn.nuspec +++ b/src/stub/WixToolset.Burn.nuspec @@ -16,5 +16,9 @@ + + + + diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index 082b80e5..a8fbdfeb 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -24,6 +24,14 @@ Release x64 + + Debug + ARM64 + + + Release + ARM64 + diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index b668f68a..1f4a7111 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -6,10 +6,18 @@ + + Debug + ARM64 + Debug Win32 + + Release + ARM64 + Release Win32 -- cgit v1.2.3-55-g6feb From 84e3b69bdd1b6811dec8041ebc2a23020ac4c179 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 27 Jan 2021 16:23:19 -0600 Subject: Always call PlanDump, and make it log at the REPORT_DEBUG level. Fixes #6327. --- src/engine/core.cpp | 2 -- src/engine/plan.cpp | 91 ++++++++++++++++++++++++++--------------------------- src/engine/plan.h | 3 -- 3 files changed, 44 insertions(+), 52 deletions(-) diff --git a/src/engine/core.cpp b/src/engine/core.cpp index d157d3b3..5b2454dc 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -491,9 +491,7 @@ extern "C" HRESULT CorePlan( // Finally, display all packages and related bundles in the log. LogPackages(pUpgradeBundlePackage, pForwardCompatibleBundlePackage, &pEngineState->packages, &pEngineState->registration.relatedBundles, action); -#ifdef DEBUG PlanDump(&pEngineState->plan); -#endif LExit: if (fActivated) diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index 0dba36c6..ffbe495f 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -2,6 +2,8 @@ #include "precomp.h" +#define PlanDumpLevel REPORT_DEBUG + // internal struct definitions struct PLAN_NONPERMANENT_PACKAGE_INDICES @@ -3041,9 +3043,6 @@ LExit: return hr; } - -#ifdef DEBUG - static void CacheActionLog( __in DWORD iAction, __in BURN_CACHE_ACTION* pAction, @@ -3054,59 +3053,59 @@ static void CacheActionLog( switch (pAction->type) { case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: ACQUIRE_CONTAINER id: %ls, source path: %ls, working path: %ls, skip until retried: %hs", wzBase, iAction, pAction->resolveContainer.pContainer->sczId, pAction->resolveContainer.pContainer->sczSourcePath, pAction->resolveContainer.sczUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried)); + LogStringLine(PlanDumpLevel, "%ls action[%u]: ACQUIRE_CONTAINER id: %ls, source path: %ls, working path: %ls, skip until retried: %hs", wzBase, iAction, pAction->resolveContainer.pContainer->sczId, pAction->resolveContainer.pContainer->sczSourcePath, pAction->resolveContainer.sczUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried)); break; case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: ACQUIRE_PAYLOAD package id: %ls, payload id: %ls, source path: %ls, working path: %ls, skip until retried: %hs", wzBase, iAction, pAction->resolvePayload.pPackage ? pAction->resolvePayload.pPackage->sczId : L"", pAction->resolvePayload.pPayload->sczKey, pAction->resolvePayload.pPayload->sczSourcePath, pAction->resolvePayload.sczUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried)); + LogStringLine(PlanDumpLevel, "%ls action[%u]: ACQUIRE_PAYLOAD package id: %ls, payload id: %ls, source path: %ls, working path: %ls, skip until retried: %hs", wzBase, iAction, pAction->resolvePayload.pPackage ? pAction->resolvePayload.pPackage->sczId : L"", pAction->resolvePayload.pPayload->sczKey, pAction->resolvePayload.pPayload->sczSourcePath, pAction->resolvePayload.sczUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried)); break; case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: CACHE_PAYLOAD package id: %ls, payload id: %ls, working path: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->cachePayload.pPackage->sczId, pAction->cachePayload.pPayload->sczKey, pAction->cachePayload.sczUnverifiedPath, pAction->cachePayload.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->cachePayload.iTryAgainAction); + LogStringLine(PlanDumpLevel, "%ls action[%u]: CACHE_PAYLOAD package id: %ls, payload id: %ls, working path: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->cachePayload.pPackage->sczId, pAction->cachePayload.pPayload->sczKey, pAction->cachePayload.sczUnverifiedPath, pAction->cachePayload.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->cachePayload.iTryAgainAction); break; case BURN_CACHE_ACTION_TYPE_CHECKPOINT: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: CHECKPOINT id: %u", wzBase, iAction, pAction->checkpoint.dwId); + LogStringLine(PlanDumpLevel, "%ls action[%u]: CHECKPOINT id: %u", wzBase, iAction, pAction->checkpoint.dwId); break; case BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: EXTRACT_CONTAINER id: %ls, working path: %ls, skip until retried: %hs, skip until acquired by action: %u", wzBase, iAction, pAction->extractContainer.pContainer->sczId, pAction->extractContainer.sczContainerUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried), pAction->extractContainer.iSkipUntilAcquiredByAction); + LogStringLine(PlanDumpLevel, "%ls action[%u]: EXTRACT_CONTAINER id: %ls, working path: %ls, skip until retried: %hs, skip until acquired by action: %u", wzBase, iAction, pAction->extractContainer.pContainer->sczId, pAction->extractContainer.sczContainerUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried), pAction->extractContainer.iSkipUntilAcquiredByAction); for (DWORD j = 0; j < pAction->extractContainer.cPayloads; j++) { - LogStringLine(REPORT_STANDARD, " extract package id: %ls, payload id: %ls, working path: %ls", pAction->extractContainer.rgPayloads[j].pPackage->sczId, pAction->extractContainer.rgPayloads[j].pPayload->sczKey, pAction->extractContainer.rgPayloads[j].sczUnverifiedPath); + LogStringLine(PlanDumpLevel, " extract package id: %ls, payload id: %ls, working path: %ls", pAction->extractContainer.rgPayloads[j].pPackage->sczId, pAction->extractContainer.rgPayloads[j].pPayload->sczKey, pAction->extractContainer.rgPayloads[j].sczUnverifiedPath); } break; case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: LAYOUT_BUNDLE working path: %ls, layout directory: %ls, exe name: %ls, skip until retried: %hs", wzBase, iAction, pAction->bundleLayout.sczUnverifiedPath, pAction->bundleLayout.sczLayoutDirectory, pAction->bundleLayout.sczExecutableName, LoggingBoolToString(pAction->fSkipUntilRetried)); + LogStringLine(PlanDumpLevel, "%ls action[%u]: LAYOUT_BUNDLE working path: %ls, layout directory: %ls, exe name: %ls, skip until retried: %hs", wzBase, iAction, pAction->bundleLayout.sczUnverifiedPath, pAction->bundleLayout.sczLayoutDirectory, pAction->bundleLayout.sczExecutableName, LoggingBoolToString(pAction->fSkipUntilRetried)); break; case BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: LAYOUT_CONTAINER package id: %ls, container id: %ls, working path: %ls, layout directory: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->layoutContainer.pPackage ? pAction->layoutContainer.pPackage->sczId : L"", pAction->layoutContainer.pContainer->sczId, pAction->layoutContainer.sczUnverifiedPath, pAction->layoutContainer.sczLayoutDirectory, pAction->layoutContainer.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->layoutContainer.iTryAgainAction); + LogStringLine(PlanDumpLevel, "%ls action[%u]: LAYOUT_CONTAINER package id: %ls, container id: %ls, working path: %ls, layout directory: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->layoutContainer.pPackage ? pAction->layoutContainer.pPackage->sczId : L"", pAction->layoutContainer.pContainer->sczId, pAction->layoutContainer.sczUnverifiedPath, pAction->layoutContainer.sczLayoutDirectory, pAction->layoutContainer.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->layoutContainer.iTryAgainAction); break; case BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: LAYOUT_PAYLOAD package id: %ls, payload id: %ls, working path: %ls, layout directory: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->layoutPayload.pPackage ? pAction->layoutPayload.pPackage->sczId : L"", pAction->layoutPayload.pPayload->sczKey, pAction->layoutPayload.sczUnverifiedPath, pAction->layoutPayload.sczLayoutDirectory, pAction->layoutPayload.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->layoutPayload.iTryAgainAction); + LogStringLine(PlanDumpLevel, "%ls action[%u]: LAYOUT_PAYLOAD package id: %ls, payload id: %ls, working path: %ls, layout directory: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->layoutPayload.pPackage ? pAction->layoutPayload.pPackage->sczId : L"", pAction->layoutPayload.pPayload->sczKey, pAction->layoutPayload.sczUnverifiedPath, pAction->layoutPayload.sczLayoutDirectory, pAction->layoutPayload.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->layoutPayload.iTryAgainAction); break; case BURN_CACHE_ACTION_TYPE_PACKAGE_START: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_START id: %ls, plan index for skip: %u, payloads to cache: %u, bytes to cache: %llu, skip until retried: %hs", wzBase, iAction, pAction->packageStart.pPackage->sczId, pAction->packageStart.iPackageCompleteAction, pAction->packageStart.cCachePayloads, pAction->packageStart.qwCachePayloadSizeTotal, LoggingBoolToString(pAction->fSkipUntilRetried)); + LogStringLine(PlanDumpLevel, "%ls action[%u]: PACKAGE_START id: %ls, plan index for skip: %u, payloads to cache: %u, bytes to cache: %llu, skip until retried: %hs", wzBase, iAction, pAction->packageStart.pPackage->sczId, pAction->packageStart.iPackageCompleteAction, pAction->packageStart.cCachePayloads, pAction->packageStart.qwCachePayloadSizeTotal, LoggingBoolToString(pAction->fSkipUntilRetried)); break; case BURN_CACHE_ACTION_TYPE_PACKAGE_STOP: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_STOP id: %ls, skip until retried: %hs", wzBase, iAction, pAction->packageStop.pPackage->sczId, LoggingBoolToString(pAction->fSkipUntilRetried)); + LogStringLine(PlanDumpLevel, "%ls action[%u]: PACKAGE_STOP id: %ls, skip until retried: %hs", wzBase, iAction, pAction->packageStop.pPackage->sczId, LoggingBoolToString(pAction->fSkipUntilRetried)); break; case BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: ROLLBACK_PACKAGE id: %ls, skip until retried: %hs", wzBase, iAction, pAction->rollbackPackage.pPackage->sczId, LoggingBoolToString(pAction->fSkipUntilRetried)); + LogStringLine(PlanDumpLevel, "%ls action[%u]: ROLLBACK_PACKAGE id: %ls, skip until retried: %hs", wzBase, iAction, pAction->rollbackPackage.pPackage->sczId, LoggingBoolToString(pAction->fSkipUntilRetried)); break; case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: SIGNAL_SYNCPOINT event handle: 0x%p, skip until retried: %hs", wzBase, iAction, pAction->syncpoint.hEvent, LoggingBoolToString(pAction->fSkipUntilRetried)); + LogStringLine(PlanDumpLevel, "%ls action[%u]: SIGNAL_SYNCPOINT event handle: 0x%p, skip until retried: %hs", wzBase, iAction, pAction->syncpoint.hEvent, LoggingBoolToString(pAction->fSkipUntilRetried)); break; case BURN_CACHE_ACTION_TYPE_TRANSACTION_BOUNDARY: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: TRANSACTION_BOUNDARY id: %ls, event handle: 0x%p, vital: %ls, transaction: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.hEvent, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no", pAction->rollbackBoundary.pRollbackBoundary->fTransaction ? L"yes" : L"no"); + LogStringLine(PlanDumpLevel, "%ls action[%u]: TRANSACTION_BOUNDARY id: %ls, event handle: 0x%p, vital: %ls, transaction: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.hEvent, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no", pAction->rollbackBoundary.pRollbackBoundary->fTransaction ? L"yes" : L"no"); break; default: @@ -3125,67 +3124,67 @@ static void ExecuteActionLog( switch (pAction->type) { case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: CHECKPOINT id: %u, msi transaction id: %ls", wzBase, iAction, pAction->checkpoint.dwId, pAction->checkpoint.pActiveRollbackBoundary && pAction->checkpoint.pActiveRollbackBoundary->fTransaction ? pAction->checkpoint.pActiveRollbackBoundary->sczId : L"(none)"); + LogStringLine(PlanDumpLevel, "%ls action[%u]: CHECKPOINT id: %u, msi transaction id: %ls", wzBase, iAction, pAction->checkpoint.dwId, pAction->checkpoint.pActiveRollbackBoundary && pAction->checkpoint.pActiveRollbackBoundary->fTransaction ? pAction->checkpoint.pActiveRollbackBoundary->sczId : L"(none)"); break; case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_PROVIDER package id: %ls, action: %hs", wzBase, iAction, pAction->packageProvider.pPackage->sczId, LoggingDependencyActionToString(pAction->packageProvider.action)); + LogStringLine(PlanDumpLevel, "%ls action[%u]: PACKAGE_PROVIDER package id: %ls, action: %hs", wzBase, iAction, pAction->packageProvider.pPackage->sczId, LoggingDependencyActionToString(pAction->packageProvider.action)); break; case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_DEPENDENCY package id: %ls, bundle provider key: %ls, action: %hs", wzBase, iAction, pAction->packageDependency.pPackage->sczId, pAction->packageDependency.sczBundleProviderKey, LoggingDependencyActionToString(pAction->packageDependency.action)); + LogStringLine(PlanDumpLevel, "%ls action[%u]: PACKAGE_DEPENDENCY package id: %ls, bundle provider key: %ls, action: %hs", wzBase, iAction, pAction->packageDependency.pPackage->sczId, pAction->packageDependency.sczBundleProviderKey, LoggingDependencyActionToString(pAction->packageDependency.action)); break; case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: EXE_PACKAGE package id: %ls, action: %hs, ignore dependencies: %ls", wzBase, iAction, pAction->exePackage.pPackage->sczId, LoggingActionStateToString(pAction->exePackage.action), pAction->exePackage.sczIgnoreDependencies); + LogStringLine(PlanDumpLevel, "%ls action[%u]: EXE_PACKAGE package id: %ls, action: %hs, ignore dependencies: %ls", wzBase, iAction, pAction->exePackage.pPackage->sczId, LoggingActionStateToString(pAction->exePackage.action), pAction->exePackage.sczIgnoreDependencies); break; case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSI_PACKAGE package id: %ls, action: %hs, action msi property: %ls, ui level: %u, disable externaluihandler: %ls, log path: %ls, logging attrib: %u", wzBase, iAction, pAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pAction->msiPackage.action), LoggingBurnMsiPropertyToString(pAction->msiPackage.actionMsiProperty), pAction->msiPackage.uiLevel, pAction->msiPackage.fDisableExternalUiHandler ? L"yes" : L"no", pAction->msiPackage.sczLogPath, pAction->msiPackage.dwLoggingAttributes); + LogStringLine(PlanDumpLevel, "%ls action[%u]: MSI_PACKAGE package id: %ls, action: %hs, action msi property: %ls, ui level: %u, disable externaluihandler: %ls, log path: %ls, logging attrib: %u", wzBase, iAction, pAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pAction->msiPackage.action), LoggingBurnMsiPropertyToString(pAction->msiPackage.actionMsiProperty), pAction->msiPackage.uiLevel, pAction->msiPackage.fDisableExternalUiHandler ? L"yes" : L"no", pAction->msiPackage.sczLogPath, pAction->msiPackage.dwLoggingAttributes); for (DWORD j = 0; j < pAction->msiPackage.cPatches; ++j) { - LogStringLine(REPORT_STANDARD, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->msiPackage.rgOrderedPatches[j].dwOrder, pAction->msiPackage.rgOrderedPatches[j].pPackage->sczId); + LogStringLine(PlanDumpLevel, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->msiPackage.rgOrderedPatches[j].dwOrder, pAction->msiPackage.rgOrderedPatches[j].pPackage->sczId); } break; case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSP_TARGET package id: %ls, action: %hs, target product code: %ls, target per-machine: %ls, action msi property: %ls, ui level: %u, disable externaluihandler: %ls, log path: %ls", wzBase, iAction, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.sczTargetProductCode, pAction->mspTarget.fPerMachineTarget ? L"yes" : L"no", LoggingBurnMsiPropertyToString(pAction->mspTarget.actionMsiProperty), pAction->mspTarget.uiLevel, pAction->mspTarget.fDisableExternalUiHandler ? L"yes" : L"no", pAction->mspTarget.sczLogPath); + LogStringLine(PlanDumpLevel, "%ls action[%u]: MSP_TARGET package id: %ls, action: %hs, target product code: %ls, target per-machine: %ls, action msi property: %ls, ui level: %u, disable externaluihandler: %ls, log path: %ls", wzBase, iAction, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.sczTargetProductCode, pAction->mspTarget.fPerMachineTarget ? L"yes" : L"no", LoggingBurnMsiPropertyToString(pAction->mspTarget.actionMsiProperty), pAction->mspTarget.uiLevel, pAction->mspTarget.fDisableExternalUiHandler ? L"yes" : L"no", pAction->mspTarget.sczLogPath); for (DWORD j = 0; j < pAction->mspTarget.cOrderedPatches; ++j) { - LogStringLine(REPORT_STANDARD, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->mspTarget.rgOrderedPatches[j].dwOrder, pAction->mspTarget.rgOrderedPatches[j].pPackage->sczId); + LogStringLine(PlanDumpLevel, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->mspTarget.rgOrderedPatches[j].dwOrder, pAction->mspTarget.rgOrderedPatches[j].pPackage->sczId); } break; case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSU_PACKAGE package id: %ls, action: %hs, log path: %ls", wzBase, iAction, pAction->msuPackage.pPackage->sczId, LoggingActionStateToString(pAction->msuPackage.action), pAction->msuPackage.sczLogPath); + LogStringLine(PlanDumpLevel, "%ls action[%u]: MSU_PACKAGE package id: %ls, action: %hs, log path: %ls", wzBase, iAction, pAction->msuPackage.pPackage->sczId, LoggingActionStateToString(pAction->msuPackage.action), pAction->msuPackage.sczLogPath); break; case BURN_EXECUTE_ACTION_TYPE_REGISTRATION: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: REGISTRATION keep: %ls", wzBase, iAction, pAction->registration.fKeep ? L"yes" : L"no"); + LogStringLine(PlanDumpLevel, "%ls action[%u]: REGISTRATION keep: %ls", wzBase, iAction, pAction->registration.fKeep ? L"yes" : L"no"); break; case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: ROLLBACK_BOUNDARY id: %ls, vital: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no"); + LogStringLine(PlanDumpLevel, "%ls action[%u]: ROLLBACK_BOUNDARY id: %ls, vital: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no"); break; case BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: WAIT_SYNCPOINT event handle: 0x%p", wzBase, iAction, pAction->syncpoint.hEvent); + LogStringLine(PlanDumpLevel, "%ls action[%u]: WAIT_SYNCPOINT event handle: 0x%p", wzBase, iAction, pAction->syncpoint.hEvent); break; case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: UNCACHE_PACKAGE id: %ls", wzBase, iAction, pAction->uncachePackage.pPackage->sczId); + LogStringLine(PlanDumpLevel, "%ls action[%u]: UNCACHE_PACKAGE id: %ls", wzBase, iAction, pAction->uncachePackage.pPackage->sczId); break; case BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: COMPATIBLE_PACKAGE reference id: %ls, installed ProductCode: %ls", wzBase, iAction, pAction->compatiblePackage.pReferencePackage->sczId, pAction->compatiblePackage.sczInstalledProductCode); + LogStringLine(PlanDumpLevel, "%ls action[%u]: COMPATIBLE_PACKAGE reference id: %ls, installed ProductCode: %ls", wzBase, iAction, pAction->compatiblePackage.pReferencePackage->sczId, pAction->compatiblePackage.sczInstalledProductCode); break; case BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: BEGIN_MSI_TRANSACTION id: %ls", wzBase, iAction, pAction->msiTransaction.pRollbackBoundary->sczId); + LogStringLine(PlanDumpLevel, "%ls action[%u]: BEGIN_MSI_TRANSACTION id: %ls", wzBase, iAction, pAction->msiTransaction.pRollbackBoundary->sczId); break; case BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION: - LogStringLine(REPORT_STANDARD, "%ls action[%u]: COMMIT_MSI_TRANSACTION id: %ls", wzBase, iAction, pAction->msiTransaction.pRollbackBoundary->sczId); + LogStringLine(PlanDumpLevel, "%ls action[%u]: COMMIT_MSI_TRANSACTION id: %ls", wzBase, iAction, pAction->msiTransaction.pRollbackBoundary->sczId); break; default: @@ -3198,15 +3197,15 @@ extern "C" void PlanDump( __in BURN_PLAN* pPlan ) { - LogStringLine(REPORT_STANDARD, "--- Begin plan dump ---"); + LogStringLine(PlanDumpLevel, "--- Begin plan dump ---"); - LogStringLine(REPORT_STANDARD, "Plan action: %hs", LoggingBurnActionToString(pPlan->action)); - LogStringLine(REPORT_STANDARD, " per-machine: %hs", LoggingTrueFalseToString(pPlan->fPerMachine)); - LogStringLine(REPORT_STANDARD, " disable-rollback: %hs", LoggingTrueFalseToString(pPlan->fDisableRollback)); - LogStringLine(REPORT_STANDARD, " keep registration by default: %hs", LoggingTrueFalseToString(pPlan->fKeepRegistrationDefault)); - LogStringLine(REPORT_STANDARD, " estimated size: %llu", pPlan->qwEstimatedSize); + LogStringLine(PlanDumpLevel, "Plan action: %hs", LoggingBurnActionToString(pPlan->action)); + LogStringLine(PlanDumpLevel, " per-machine: %hs", LoggingTrueFalseToString(pPlan->fPerMachine)); + LogStringLine(PlanDumpLevel, " disable-rollback: %hs", LoggingTrueFalseToString(pPlan->fDisableRollback)); + LogStringLine(PlanDumpLevel, " keep registration by default: %hs", LoggingTrueFalseToString(pPlan->fKeepRegistrationDefault)); + LogStringLine(PlanDumpLevel, " estimated size: %llu", pPlan->qwEstimatedSize); - LogStringLine(REPORT_STANDARD, "Plan cache size: %llu", pPlan->qwCacheSizeTotal); + LogStringLine(PlanDumpLevel, "Plan cache size: %llu", pPlan->qwCacheSizeTotal); for (DWORD i = 0; i < pPlan->cCacheActions; ++i) { CacheActionLog(i, pPlan->rgCacheActions + i, FALSE); @@ -3217,8 +3216,8 @@ extern "C" void PlanDump( CacheActionLog(i, pPlan->rgRollbackCacheActions + i, TRUE); } - LogStringLine(REPORT_STANDARD, "Plan execute package count: %u", pPlan->cExecutePackagesTotal); - LogStringLine(REPORT_STANDARD, " overall progress ticks: %u", pPlan->cOverallProgressTicksTotal); + LogStringLine(PlanDumpLevel, "Plan execute package count: %u", pPlan->cExecutePackagesTotal); + LogStringLine(PlanDumpLevel, " overall progress ticks: %u", pPlan->cOverallProgressTicksTotal); for (DWORD i = 0; i < pPlan->cExecuteActions; ++i) { ExecuteActionLog(i, pPlan->rgExecuteActions + i, FALSE); @@ -3231,15 +3230,13 @@ extern "C" void PlanDump( for (DWORD i = 0; i < pPlan->cCleanActions; ++i) { - LogStringLine(REPORT_STANDARD, " Clean action[%u]: CLEAN_PACKAGE package id: %ls", i, pPlan->rgCleanActions[i].pPackage->sczId); + LogStringLine(PlanDumpLevel, " Clean action[%u]: CLEAN_PACKAGE package id: %ls", i, pPlan->rgCleanActions[i].pPackage->sczId); } for (DWORD i = 0; i < pPlan->cPlannedProviders; ++i) { - LogStringLine(REPORT_STANDARD, " Dependency action[%u]: PLANNED_PROVIDER key: %ls, name: %ls", i, pPlan->rgPlannedProviders[i].sczKey, pPlan->rgPlannedProviders[i].sczName); + LogStringLine(PlanDumpLevel, " Dependency action[%u]: PLANNED_PROVIDER key: %ls, name: %ls", i, pPlan->rgPlannedProviders[i].sczKey, pPlan->rgPlannedProviders[i].sczName); } - LogStringLine(REPORT_STANDARD, "--- End plan dump ---"); + LogStringLine(PlanDumpLevel, "--- End plan dump ---"); } - -#endif diff --git a/src/engine/plan.h b/src/engine/plan.h index 9d7debe1..dad436d4 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -550,12 +550,9 @@ HRESULT PlanSetResumeCommand( __in BOOTSTRAPPER_COMMAND* pCommand, __in BURN_LOGGING* pLog ); - -#ifdef DEBUG void PlanDump( __in BURN_PLAN* pPlan ); -#endif #if defined(__cplusplus) } -- cgit v1.2.3-55-g6feb From 0e41fb8be9690ca7b81ec4df0734ead1978a9cf0 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 27 Jan 2021 20:35:12 -0600 Subject: When logging at the debug level, log errors from all sources. --- src/engine/engine.vcxproj | 1 + src/engine/inc/burnsources.h | 4 ++++ src/engine/precomp.h | 3 +-- src/stub/precomp.h | 3 +-- src/stub/stub.cpp | 14 +++++++++++++- 5 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 src/engine/inc/burnsources.h diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index 52f71b1d..3db6802b 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -119,6 +119,7 @@ + diff --git a/src/engine/inc/burnsources.h b/src/engine/inc/burnsources.h new file mode 100644 index 00000000..bff79ed5 --- /dev/null +++ b/src/engine/inc/burnsources.h @@ -0,0 +1,4 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_EXTERNAL diff --git a/src/engine/precomp.h b/src/engine/precomp.h index a8a656d1..c0019476 100644 --- a/src/engine/precomp.h +++ b/src/engine/precomp.h @@ -20,8 +20,7 @@ #include #include - -#define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_EXTERNAL +#include #include #include diff --git a/src/stub/precomp.h b/src/stub/precomp.h index ad0988b5..bb7ded9c 100644 --- a/src/stub/precomp.h +++ b/src/stub/precomp.h @@ -5,8 +5,7 @@ #include #include - -#define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_EXTERNAL +#include #include #include diff --git a/src/stub/stub.cpp b/src/stub/stub.cpp index d3ace1f3..0cb202e0 100644 --- a/src/stub/stub.cpp +++ b/src/stub/stub.cpp @@ -87,7 +87,19 @@ static void CALLBACK BurnTraceError( __in va_list args ) { - if (DUTIL_SOURCE_DEFAULT == source) + BOOL fLog = FALSE; + + switch (source) + { + case DUTIL_SOURCE_DEFAULT: + fLog = TRUE; + break; + default: + fLog = REPORT_VERBOSE < LogGetLevel(); + break; + } + + if (fLog) { LogErrorStringArgs(hrError, szFormat, args); } -- cgit v1.2.3-55-g6feb From f1f1a124df59e8639c2bcbfa7d3a4b37fb348bb7 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Sun, 31 Jan 2021 19:28:47 -0500 Subject: Remove Burn Authenticode Fixes https://github.com/wixtoolset/issues/issues/6301 --- src/engine/cache.cpp | 252 +---------------------------- src/engine/cache.h | 7 +- src/engine/catalog.cpp | 180 --------------------- src/engine/catalog.h | 56 ------- src/engine/core.cpp | 4 - src/engine/core.h | 1 - src/engine/engine.cpp | 1 - src/engine/engine.vcxproj | 18 +-- src/engine/manifest.cpp | 6 +- src/engine/payload.cpp | 33 ---- src/engine/payload.h | 6 - src/engine/precomp.h | 1 - src/engine/userexperience.cpp | 2 +- src/stub/stub.vcxproj | 4 +- src/test/BurnUnitTest/BurnUnitTest.vcxproj | 2 +- src/test/BurnUnitTest/precomp.h | 1 - 16 files changed, 12 insertions(+), 562 deletions(-) delete mode 100644 src/engine/catalog.cpp delete mode 100644 src/engine/catalog.h diff --git a/src/engine/cache.cpp b/src/engine/cache.cpp index 315281bc..92a79eb9 100644 --- a/src/engine/cache.cpp +++ b/src/engine/cache.cpp @@ -94,15 +94,6 @@ static HRESULT VerifyHash( __in_z LPCWSTR wzUnverifiedPayloadPath, __in HANDLE hFile ); -static HRESULT VerifyPayloadWithCatalog( - __in BURN_PAYLOAD* pPayload, - __in_z LPCWSTR wzUnverifiedPayloadPath, - __in HANDLE hFile - ); -static HRESULT VerifyPayloadAgainstChain( - __in BURN_PAYLOAD* pPayload, - __in PCCERT_CHAIN_CONTEXT pChainContext - ); extern "C" HRESULT CacheInitialize( @@ -189,7 +180,7 @@ LExit: } extern "C" HRESULT CacheEnsureWorkingFolder( - __in_z LPCWSTR wzBundleId, + __in_z_opt LPCWSTR wzBundleId, __deref_out_z_opt LPWSTR* psczWorkingFolder ) { @@ -964,56 +955,6 @@ LExit: return hr; } -extern "C" HRESULT CacheVerifyPayloadSignature( - __in BURN_PAYLOAD* pPayload, - __in_z LPCWSTR wzUnverifiedPayloadPath, - __in HANDLE hFile - ) -{ - HRESULT hr = S_OK; - LONG er = ERROR_SUCCESS; - - GUID guidAuthenticode = WINTRUST_ACTION_GENERIC_VERIFY_V2; - WINTRUST_FILE_INFO wfi = { }; - WINTRUST_DATA wtd = { }; - CRYPT_PROVIDER_DATA* pProviderData = NULL; - CRYPT_PROVIDER_SGNR* pSigner = NULL; - - // Verify the payload assuming online. - wfi.cbStruct = sizeof(wfi); - wfi.pcwszFilePath = wzUnverifiedPayloadPath; - wfi.hFile = hFile; - - wtd.cbStruct = sizeof(wtd); - wtd.dwUnionChoice = WTD_CHOICE_FILE; - wtd.pFile = &wfi; - wtd.dwStateAction = WTD_STATEACTION_VERIFY; - wtd.dwProvFlags = WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT; - wtd.dwUIChoice = WTD_UI_NONE; - - er = ::WinVerifyTrust(static_cast(INVALID_HANDLE_VALUE), &guidAuthenticode, &wtd); - if (er) - { - // Verify the payload assuming offline. - wtd.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL; - - er = ::WinVerifyTrust(static_cast(INVALID_HANDLE_VALUE), &guidAuthenticode, &wtd); - ExitOnWin32Error(er, hr, "Failed authenticode verification of payload: %ls", wzUnverifiedPayloadPath); - } - - pProviderData = WTHelperProvDataFromStateData(wtd.hWVTStateData); - ExitOnNullWithLastError(pProviderData, hr, "Failed to get provider state from authenticode certificate."); - - pSigner = WTHelperGetProvSignerFromChain(pProviderData, 0, FALSE, 0); - ExitOnNullWithLastError(pSigner, hr, "Failed to get signer chain from authenticode certificate."); - - hr = VerifyPayloadAgainstChain(pPayload, pSigner->pChainContext); - ExitOnFailure(hr, "Failed to verify expected payload against actual certificate chain."); - -LExit: - return hr; -} - extern "C" void CacheCleanup( __in BOOL fPerMachine, __in_z LPCWSTR wzBundleId @@ -1098,7 +1039,7 @@ extern "C" void CacheUninitialize() // Internal functions. static HRESULT CalculateWorkingFolder( - __in_z LPCWSTR /*wzBundleId*/, + __in_z_opt LPCWSTR /*wzBundleId*/, __deref_out_z LPWSTR* psczWorkingFolder ) { @@ -1387,18 +1328,7 @@ static HRESULT VerifyThenTransferPayload( ExitWithLastError(hr, "Failed to open payload in working path: %ls", wzUnverifiedPayloadPath); } - // If the payload has a certificate root public key identifier provided, verify the certificate. - if (pPayload->pbCertificateRootPublicKeyIdentifier) - { - hr = CacheVerifyPayloadSignature(pPayload, wzUnverifiedPayloadPath, hFile); - ExitOnFailure(hr, "Failed to verify payload signature: %ls", wzCachedPath); - } - else if (pPayload->pCatalog) // If catalog files are specified, attempt to verify the file with a catalog file - { - hr = VerifyPayloadWithCatalog(pPayload, wzUnverifiedPayloadPath, hFile); - ExitOnFailure(hr, "Failed to verify payload signature: %ls", wzCachedPath); - } - else if (pPayload->pbHash) // the payload should have a hash we can use to verify it. + if (pPayload->pbHash) // the payload should have a hash we can use to verify it. { hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, wzUnverifiedPayloadPath, hFile); ExitOnFailure(hr, "Failed to verify payload hash: %ls", wzCachedPath); @@ -1466,18 +1396,7 @@ static HRESULT VerifyFileAgainstPayload( ExitOnRootFailure(hr, "Failed to open payload at path: %ls", wzVerifyPath); } - // If the payload has a certificate root public key identifier provided, verify the certificate. - if (pPayload->pbCertificateRootPublicKeyIdentifier) - { - hr = CacheVerifyPayloadSignature(pPayload, wzVerifyPath, hFile); - ExitOnFailure(hr, "Failed to verify signature of payload: %ls", pPayload->sczKey); - } - else if (pPayload->pCatalog) // If catalog files are specified, attempt to verify the file with a catalog file - { - hr = VerifyPayloadWithCatalog(pPayload, wzVerifyPath, hFile); - ExitOnFailure(hr, "Failed to verify catalog signature of payload: %ls", pPayload->sczKey); - } - else if (pPayload->pbHash) // the payload should have a hash we can use to verify it. + if (pPayload->pbHash) // the payload should have a hash we can use to verify it. { hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, wzVerifyPath, hFile); ExitOnFailure(hr, "Failed to verify hash of payload: %ls", pPayload->sczKey); @@ -1517,7 +1436,7 @@ LExit: static HRESULT ResetPathPermissions( __in BOOL fPerMachine, - __in LPCWSTR wzPath + __in_z LPCWSTR wzPath ) { HRESULT hr = S_OK; @@ -1863,164 +1782,3 @@ LExit: return hr; } - -static HRESULT VerifyPayloadWithCatalog( - __in BURN_PAYLOAD* pPayload, - __in_z LPCWSTR wzUnverifiedPayloadPath, - __in HANDLE hFile - ) -{ - HRESULT hr = S_FALSE; - DWORD er = ERROR_SUCCESS; - WINTRUST_DATA WinTrustData = { }; - WINTRUST_CATALOG_INFO WinTrustCatalogInfo = { }; - GUID gSubSystemDriver = WINTRUST_ACTION_GENERIC_VERIFY_V2; - LPWSTR sczLowerCaseFile = NULL; - LPWSTR pCurrent = NULL; - LPWSTR sczName = NULL; - DWORD dwHashSize = 0; - DWORD dwTagSize; - LPBYTE pbHash = NULL; - - // Get lower case file name. Older operating systems need a lower case file - // to match in the catalog - hr = StrAllocString(&sczLowerCaseFile, wzUnverifiedPayloadPath, 0); - ExitOnFailure(hr, "Failed to allocate memory"); - - // Go through each character doing the lower case of each letter - pCurrent = sczLowerCaseFile; - while ('\0' != *pCurrent) - { - *pCurrent = (WCHAR)_tolower(*pCurrent); - pCurrent++; - } - - // Get file hash - CryptCATAdminCalcHashFromFileHandle(hFile, &dwHashSize, pbHash, 0); - er = ::GetLastError(); - if (ERROR_INSUFFICIENT_BUFFER == er) - { - pbHash = (LPBYTE)MemAlloc(dwHashSize, TRUE); - if (!CryptCATAdminCalcHashFromFileHandle(hFile, &dwHashSize, pbHash, 0)) - { - ExitWithLastError(hr, "Failed to get file hash."); - } - } - else - { - ExitOnWin32Error(er, hr, "Failed to get file hash."); - } - - // Make the hash into a string. This is the member tag for the catalog - dwTagSize = (dwHashSize * 2) + 1; - hr = StrAlloc(&sczName, dwTagSize); - ExitOnFailure(hr, "Failed to allocate string."); - hr = StrHexEncode(pbHash, dwHashSize, sczName, dwTagSize); - ExitOnFailure(hr, "Failed to encode file hash."); - - // Set up the WinVerifyTrust structures assuming online. - WinTrustData.cbStruct = sizeof(WINTRUST_DATA); - WinTrustData.dwUIChoice = WTD_UI_NONE; - WinTrustData.dwUnionChoice = WTD_CHOICE_CATALOG; - WinTrustData.dwStateAction = WTD_STATEACTION_VERIFY; - WinTrustData.dwProvFlags = WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT; - WinTrustData.pCatalog = &WinTrustCatalogInfo; - - WinTrustCatalogInfo.cbStruct = sizeof(WINTRUST_CATALOG_INFO); - WinTrustCatalogInfo.pbCalculatedFileHash = pbHash; - WinTrustCatalogInfo.cbCalculatedFileHash = dwHashSize; - WinTrustCatalogInfo.hMemberFile = hFile; - WinTrustCatalogInfo.pcwszMemberTag = sczName; - WinTrustCatalogInfo.pcwszMemberFilePath = sczLowerCaseFile; - WinTrustCatalogInfo.pcwszCatalogFilePath = pPayload->pCatalog->sczLocalFilePath; - - hr = ::WinVerifyTrust(static_cast(INVALID_HANDLE_VALUE), &gSubSystemDriver, &WinTrustData); - if (hr) - { - // Set up the WinVerifyTrust structures assuming online. - WinTrustData.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL; - - er = ::WinVerifyTrust(static_cast(INVALID_HANDLE_VALUE), &gSubSystemDriver, &WinTrustData); - - // WinVerifyTrust returns 0 for success, a few different Win32 error codes if it can't - // find the provider, and any other error code is provider specific, so may not - // be an actual Win32 error code - ExitOnWin32Error(er, hr, "Could not verify file %ls.", wzUnverifiedPayloadPath); - } - - // Need to close the WinVerifyTrust action - WinTrustData.dwStateAction = WTD_STATEACTION_CLOSE; - er = ::WinVerifyTrust(static_cast(INVALID_HANDLE_VALUE), &gSubSystemDriver, &WinTrustData); - ExitOnWin32Error(er, hr, "Could not close verify handle."); - -LExit: - ReleaseStr(sczLowerCaseFile); - ReleaseStr(sczName); - ReleaseMem(pbHash); - - return hr; -} - -static HRESULT VerifyPayloadAgainstChain( - __in BURN_PAYLOAD* pPayload, - __in PCCERT_CHAIN_CONTEXT pChainContext - ) -{ - HRESULT hr = S_OK; - PCCERT_CONTEXT pChainElementCertContext = NULL; - - BYTE rgbPublicKeyIdentifier[SHA1_HASH_LEN] = { }; - DWORD cbPublicKeyIdentifier = sizeof(rgbPublicKeyIdentifier); - BYTE* pbThumbprint = NULL; - DWORD cbThumbprint = 0; - - // Walk up the chain looking for a certificate in the chain that matches our expected public key identifier - // and thumbprint (if a thumbprint was provided). - HRESULT hrChainVerification = E_NOTFOUND; // assume we won't find a match. - for (DWORD i = 0; i < pChainContext->rgpChain[0]->cElement; ++i) - { - pChainElementCertContext = pChainContext->rgpChain[0]->rgpElement[i]->pCertContext; - - // Get the certificate's public key identifier. - if (!::CryptHashPublicKeyInfo(NULL, CALG_SHA1, 0, X509_ASN_ENCODING, &pChainElementCertContext->pCertInfo->SubjectPublicKeyInfo, rgbPublicKeyIdentifier, &cbPublicKeyIdentifier)) - { - ExitWithLastError(hr, "Failed to get certificate public key identifier."); - } - - // Compare the certificate's public key identifier with the payload's public key identifier. If they - // match, we're one step closer to the a positive result. - if (pPayload->cbCertificateRootPublicKeyIdentifier == cbPublicKeyIdentifier && - 0 == memcmp(pPayload->pbCertificateRootPublicKeyIdentifier, rgbPublicKeyIdentifier, cbPublicKeyIdentifier)) - { - // If the payload specified a thumbprint for the certificate, verify it. - if (pPayload->pbCertificateRootThumbprint) - { - hr = CertReadProperty(pChainElementCertContext, CERT_SHA1_HASH_PROP_ID, &pbThumbprint, &cbThumbprint); - ExitOnFailure(hr, "Failed to read certificate thumbprint."); - - if (pPayload->cbCertificateRootThumbprint == cbThumbprint && - 0 == memcmp(pPayload->pbCertificateRootThumbprint, pbThumbprint, cbThumbprint)) - { - // If we got here, we found that our payload public key identifier and thumbprint - // matched an element in the certficate chain. - hrChainVerification = S_OK; - break; - } - - ReleaseNullMem(pbThumbprint); - } - else // no thumbprint match necessary so we're good to go. - { - hrChainVerification = S_OK; - break; - } - } - } - hr = hrChainVerification; - ExitOnFailure(hr, "Failed to find expected public key in certificate chain."); - -LExit: - ReleaseMem(pbThumbprint); - - return hr; -} diff --git a/src/engine/cache.h b/src/engine/cache.h index 95e6cb90..f8ad2a90 100644 --- a/src/engine/cache.h +++ b/src/engine/cache.h @@ -16,7 +16,7 @@ HRESULT CacheInitialize( __in_z_opt LPCWSTR wzSourceProcessPath ); HRESULT CacheEnsureWorkingFolder( - __in LPCWSTR wzBundleId, + __in_z LPCWSTR wzBundleId, __deref_out_z_opt LPWSTR* psczWorkingFolder ); HRESULT CacheCalculateBundleWorkingPath( @@ -134,11 +134,6 @@ HRESULT CacheRemovePackage( __in_z LPCWSTR wzPackageId, __in_z LPCWSTR wzCacheId ); -HRESULT CacheVerifyPayloadSignature( - __in BURN_PAYLOAD* pPayload, - __in_z LPCWSTR wzUnverifiedPayloadPath, - __in HANDLE hFile - ); void CacheCleanup( __in BOOL fPerMachine, __in_z LPCWSTR wzBundleId diff --git a/src/engine/catalog.cpp b/src/engine/catalog.cpp deleted file mode 100644 index da086545..00000000 --- a/src/engine/catalog.cpp +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// function definitions - -extern "C" HRESULT CatalogsParseFromXml( - __in BURN_CATALOGS* pCatalogs, - __in IXMLDOMNode* pixnBundle - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnNodes = NULL; - IXMLDOMNode* pixnNode = NULL; - DWORD cNodes = 0; - LPWSTR scz = NULL; - - // select catalog nodes - hr = XmlSelectNodes(pixnBundle, L"Catalog", &pixnNodes); - ExitOnFailure(hr, "Failed to select catalog nodes."); - - // get catalog node count - hr = pixnNodes->get_length((long*)&cNodes); - ExitOnFailure(hr, "Failed to get payload node count."); - if (!cNodes) - { - ExitFunction(); - } - - // allocate memory for catalogs - pCatalogs->rgCatalogs = (BURN_CATALOG*)MemAlloc(sizeof(BURN_CATALOG) * cNodes, TRUE); - ExitOnNull(pCatalogs->rgCatalogs, hr, E_OUTOFMEMORY, "Failed to allocate memory for payload structs."); - - pCatalogs->cCatalogs = cNodes; - - // parse catalog elements - for (DWORD i = 0; i < cNodes; ++i) - { - BURN_CATALOG* pCatalog = &pCatalogs->rgCatalogs[i]; - pCatalog->hFile = INVALID_HANDLE_VALUE; - - hr = XmlNextElement(pixnNodes, &pixnNode, NULL); - ExitOnFailure(hr, "Failed to get next node."); - - // @Id - hr = XmlGetAttributeEx(pixnNode, L"Id", &pCatalog->sczKey); - ExitOnFailure(hr, "Failed to get @Id."); - - // @Payload - hr = XmlGetAttributeEx(pixnNode, L"Payload", &pCatalog->sczPayload); - ExitOnFailure(hr, "Failed to get @Payload."); - - // prepare next iteration - ReleaseNullObject(pixnNode); - } - -LExit: - ReleaseObject(pixnNodes); - ReleaseObject(pixnNode); - ReleaseStr(scz); - - return hr; -} - -extern "C" HRESULT CatalogFindById( - __in BURN_CATALOGS* pCatalogs, - __in_z LPCWSTR wzId, - __out BURN_CATALOG** ppCatalog - ) -{ - HRESULT hr = S_OK; - BURN_CATALOG* pCatalog = NULL; - - for (DWORD i = 0; i < pCatalogs->cCatalogs; ++i) - { - pCatalog = &pCatalogs->rgCatalogs[i]; - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pCatalog->sczKey, -1, wzId, -1)) - { - *ppCatalog = pCatalog; - ExitFunction1(hr = S_OK); - } - } - - hr = E_NOTFOUND; - -LExit: - return hr; -} - -extern "C" HRESULT CatalogLoadFromPayload( - __in BURN_CATALOGS* pCatalogs, - __in BURN_PAYLOADS* pPayloads - ) -{ - HRESULT hr = S_OK; - BURN_CATALOG* pCatalog = NULL; - BURN_PAYLOAD* pPayload = NULL; - - // go through each catalog file - for (DWORD i = 0; i < pCatalogs->cCatalogs; i++) - { - pCatalog = &pCatalogs->rgCatalogs[i]; - - // get the payload for this catalog file - hr = PayloadFindById(pPayloads, pCatalog->sczPayload, &pPayload); - ExitOnFailure(hr, "Failed to find payload for catalog file."); - - // Get the local file name - hr = StrAllocString(&pCatalog->sczLocalFilePath, pPayload->sczLocalFilePath, 0); - ExitOnFailure(hr, "Failed to get catalog local file path"); - - // Get a handle to the file - pCatalog->hFile = ::CreateFileW(pCatalog->sczLocalFilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); - if (INVALID_HANDLE_VALUE == pCatalog->hFile) - { - ExitWithLastError(hr, "Failed to open catalog in working path: %ls", pCatalog->sczLocalFilePath); - } - - // Verify the catalog file - hr = CacheVerifyPayloadSignature(pPayload, pCatalog->sczLocalFilePath, pCatalog->hFile); - ExitOnFailure(hr, "Failed to verify catalog signature: %ls", pCatalog->sczLocalFilePath); - } - -LExit: - return hr; -} - -extern "C" HRESULT CatalogElevatedUpdateCatalogFile( - __in BURN_CATALOGS* pCatalogs, - __in_z LPCWSTR wzId, - __in_z LPCWSTR wzPath - ) -{ - HRESULT hr = S_OK; - BURN_CATALOG* pCatalog = NULL; - - // Find the catalog - hr = CatalogFindById(pCatalogs, wzId, &pCatalog); - ExitOnFailure(hr, "Failed to locate catalog information."); - - if (NULL == pCatalog->sczLocalFilePath) - { - hr = StrAllocString(&pCatalog->sczLocalFilePath, wzPath, 0); - ExitOnFailure(hr, "Failed to allocated catalog path."); - - // Get a handle to the file - pCatalog->hFile = ::CreateFileW(pCatalog->sczLocalFilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); - if (INVALID_HANDLE_VALUE == pCatalog->hFile) - { - ExitWithLastError(hr, "Failed to open catalog in working path: %ls", pCatalog->sczLocalFilePath); - } - } - -LExit: - return hr; -} - -extern "C" void CatalogUninitialize( - __in BURN_CATALOGS* pCatalogs - ) -{ - if (pCatalogs->rgCatalogs) - { - for (DWORD i = 0; i < pCatalogs->cCatalogs; ++i) - { - BURN_CATALOG* pCatalog = &pCatalogs->rgCatalogs[i]; - - ReleaseHandle(pCatalog->hFile); - ReleaseStr(pCatalog->sczKey); - ReleaseStr(pCatalog->sczLocalFilePath); - ReleaseStr(pCatalog->sczPayload); - } - MemFree(pCatalogs->rgCatalogs); - } - - // clear struct - memset(pCatalogs, 0, sizeof(BURN_CATALOGS)); -} diff --git a/src/engine/catalog.h b/src/engine/catalog.h deleted file mode 100644 index 3a87d0d2..00000000 --- a/src/engine/catalog.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - -// structs - -typedef struct _BURN_CATALOG -{ - LPWSTR sczKey; - LPWSTR sczPayload; - - // mutable members - LPWSTR sczLocalFilePath; // location of extracted or downloaded copy - HANDLE hFile; -} BURN_CATALOG; - -typedef struct _BURN_CATALOGS -{ - BURN_CATALOG* rgCatalogs; - DWORD cCatalogs; -} BURN_CATALOGS; - -typedef struct _BURN_PAYLOADS BURN_PAYLOADS; - - -// functions - -HRESULT CatalogsParseFromXml( - __in BURN_CATALOGS* pCatalogs, - __in IXMLDOMNode* pixnBundle - ); -HRESULT CatalogFindById( - __in BURN_CATALOGS* pCatalogs, - __in_z LPCWSTR wzId, - __out BURN_CATALOG** ppCatalog - ); -HRESULT CatalogLoadFromPayload( - __in BURN_CATALOGS* pCatalogs, - __in BURN_PAYLOADS* pPayloads - ); -HRESULT CatalogElevatedUpdateCatalogFile( - __in BURN_CATALOGS* pCatalogs, - __in_z LPCWSTR wzId, - __in_z LPCWSTR wzPath - ); -void CatalogUninitialize( - __in BURN_CATALOGS* pCatalogs - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 5b2454dc..0f5ea08a 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -151,10 +151,6 @@ extern "C" HRESULT CoreInitialize( hr = PayloadExtractFromContainer(&pEngineState->userExperience.payloads, NULL, &containerContext, pEngineState->userExperience.sczTempDirectory); ExitOnFailure(hr, "Failed to extract bootstrapper application payloads."); - // Load the catalog files as soon as they are extracted. - hr = CatalogLoadFromPayload(&pEngineState->catalogs, &pEngineState->userExperience.payloads); - ExitOnFailure(hr, "Failed to load catalog files."); - hr = PathConcat(pEngineState->userExperience.sczTempDirectory, L"BootstrapperApplicationData.xml", &pEngineState->command.wzBootstrapperApplicationDataPath); ExitOnFailure(hr, "Failed to get BootstrapperApplicationDataPath."); diff --git a/src/engine/core.h b/src/engine/core.h index 544c1786..0161eaa0 100644 --- a/src/engine/core.h +++ b/src/engine/core.h @@ -98,7 +98,6 @@ typedef struct _BURN_ENGINE_STATE BURN_USER_EXPERIENCE userExperience; BURN_REGISTRATION registration; BURN_CONTAINERS containers; - BURN_CATALOGS catalogs; BURN_PAYLOADS payloads; BURN_PACKAGES packages; BURN_UPDATE update; diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 3dbfb365..9eee7fde 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -400,7 +400,6 @@ static void UninitializeEngineState( RegistrationUninitialize(&pEngineState->registration); PayloadsUninitialize(&pEngineState->payloads); PackagesUninitialize(&pEngineState->packages); - CatalogUninitialize(&pEngineState->catalogs); SectionUninitialize(&pEngineState->section); ContainersUninitialize(&pEngineState->containers); diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index 3db6802b..cb179a23 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -1,9 +1,7 @@ - - Debug @@ -30,7 +28,6 @@ ARM64 - {8119537D-E1D9-6591-D51A-49768A2F9C37} StaticLibrary @@ -39,22 +36,17 @@ Unicode Native component of WixToolset.Burn - - - - - @@ -95,7 +87,6 @@ - @@ -107,7 +98,6 @@ - @@ -145,11 +135,9 @@ - - Compiling message file... @@ -158,10 +146,7 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" $(IntDir)engine.messages.h;$(IntDir)engine.messages.rc;$(OutDir)engine.res - - + $(MajorMinorVersion.Split(`.`)[0]) $(MajorMinorVersion.Split(`.`)[1]) @@ -176,7 +161,6 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" - diff --git a/src/engine/manifest.cpp b/src/engine/manifest.cpp index fe8c6cad..fa454348 100644 --- a/src/engine/manifest.cpp +++ b/src/engine/manifest.cpp @@ -132,10 +132,6 @@ static HRESULT ParseFromXml( hr = SearchesParseFromXml(&pEngineState->searches, &pEngineState->extensions, pixeBundle); ExitOnFailure(hr, "Failed to parse searches."); - // parse catalog files - hr = CatalogsParseFromXml(&pEngineState->catalogs, pixeBundle); - ExitOnFailure(hr, "Failed to parse catalog files."); - // parse registration hr = RegistrationParseFromXml(&pEngineState->registration, pixeBundle); ExitOnFailure(hr, "Failed to parse registration."); @@ -149,7 +145,7 @@ static HRESULT ParseFromXml( ExitOnFailure(hr, "Failed to parse containers."); // parse payloads - hr = PayloadsParseFromXml(&pEngineState->payloads, &pEngineState->containers, &pEngineState->catalogs, pixeBundle); + hr = PayloadsParseFromXml(&pEngineState->payloads, &pEngineState->containers, pixeBundle); ExitOnFailure(hr, "Failed to parse payloads."); // parse packages diff --git a/src/engine/payload.cpp b/src/engine/payload.cpp index 6833288f..67eebe10 100644 --- a/src/engine/payload.cpp +++ b/src/engine/payload.cpp @@ -18,7 +18,6 @@ static HRESULT FindEmbeddedBySourcePath( extern "C" HRESULT PayloadsParseFromXml( __in BURN_PAYLOADS* pPayloads, __in_opt BURN_CONTAINERS* pContainers, - __in_opt BURN_CATALOGS* pCatalogs, __in IXMLDOMNode* pixnBundle ) { @@ -130,26 +129,6 @@ extern "C" HRESULT PayloadsParseFromXml( ExitOnFailure(hr, "Failed to parse @FileSize."); } - // @CertificateAuthorityKeyIdentifier - hr = XmlGetAttributeEx(pixnNode, L"CertificateRootPublicKeyIdentifier", &scz); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @CertificateRootPublicKeyIdentifier."); - - hr = StrAllocHexDecode(scz, &pPayload->pbCertificateRootPublicKeyIdentifier, &pPayload->cbCertificateRootPublicKeyIdentifier); - ExitOnFailure(hr, "Failed to hex decode @CertificateRootPublicKeyIdentifier."); - } - - // @CertificateThumbprint - hr = XmlGetAttributeEx(pixnNode, L"CertificateRootThumbprint", &scz); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @CertificateRootThumbprint."); - - hr = StrAllocHexDecode(scz, &pPayload->pbCertificateRootThumbprint, &pPayload->cbCertificateRootThumbprint); - ExitOnFailure(hr, "Failed to hex decode @CertificateRootThumbprint."); - } - // @Hash hr = XmlGetAttributeEx(pixnNode, L"Hash", &scz); ExitOnFailure(hr, "Failed to get @Hash."); @@ -157,16 +136,6 @@ extern "C" HRESULT PayloadsParseFromXml( hr = StrAllocHexDecode(scz, &pPayload->pbHash, &pPayload->cbHash); ExitOnFailure(hr, "Failed to hex decode the Payload/@Hash."); - // @Catalog - hr = XmlGetAttributeEx(pixnNode, L"Catalog", &scz); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @Catalog."); - - hr = CatalogFindById(pCatalogs, scz, &pPayload->pCatalog); - ExitOnFailure(hr, "Failed to find catalog."); - } - // prepare next iteration ReleaseNullObject(pixnNode); } @@ -194,8 +163,6 @@ extern "C" void PayloadsUninitialize( ReleaseStr(pPayload->sczKey); ReleaseStr(pPayload->sczFilePath); ReleaseMem(pPayload->pbHash); - ReleaseMem(pPayload->pbCertificateRootThumbprint); - ReleaseMem(pPayload->pbCertificateRootPublicKeyIdentifier); ReleaseStr(pPayload->sczSourcePath); ReleaseStr(pPayload->sczLocalFilePath); ReleaseStr(pPayload->downloadSource.sczUrl); diff --git a/src/engine/payload.h b/src/engine/payload.h index 0c7b68e4..e8639d64 100644 --- a/src/engine/payload.h +++ b/src/engine/payload.h @@ -35,11 +35,6 @@ typedef struct _BURN_PAYLOAD DWORD64 qwFileSize; LPWSTR sczFilePath; // file path relative to the execute location - BURN_CATALOG *pCatalog; // used to verify this payload - BYTE* pbCertificateRootPublicKeyIdentifier; - DWORD cbCertificateRootPublicKeyIdentifier; - BYTE* pbCertificateRootThumbprint; - DWORD cbCertificateRootThumbprint; BYTE* pbHash; DWORD cbHash; @@ -64,7 +59,6 @@ typedef struct _BURN_PAYLOADS HRESULT PayloadsParseFromXml( __in BURN_PAYLOADS* pPayloads, __in_opt BURN_CONTAINERS* pContainers, - __in_opt BURN_CATALOGS* pCatalogs, __in IXMLDOMNode* pixnBundle ); void PayloadsUninitialize( diff --git a/src/engine/precomp.h b/src/engine/precomp.h index c0019476..53fa949a 100644 --- a/src/engine/precomp.h +++ b/src/engine/precomp.h @@ -68,7 +68,6 @@ #include "section.h" #include "approvedexe.h" #include "container.h" -#include "catalog.h" #include "payload.h" #include "cabextract.h" #include "burnextension.h" diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index ce1662b8..8e782e71 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -55,7 +55,7 @@ extern "C" HRESULT UserExperienceParseFromXml( } // parse payloads - hr = PayloadsParseFromXml(&pUserExperience->payloads, NULL, NULL, pixnUserExperienceNode); + hr = PayloadsParseFromXml(&pUserExperience->payloads, NULL, pixnUserExperienceNode); ExitOnFailure(hr, "Failed to parse user experience payloads."); // make sure we have at least one payload diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index a8fbdfeb..da19f3c6 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -63,14 +63,14 @@ $(ProjectDir)..\engine\inc - cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;wintrust.lib;wuguid.lib;engine.lib;engine.res + cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;wuguid.lib;engine.lib;engine.res true true - cabinet.dll;crypt32.dll;msi.dll;shlwapi.dll;version.dll;wininet.dll;wintrust.dll + cabinet.dll;crypt32.dll;msi.dll;shlwapi.dll;version.dll;wininet.dll diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index 1f4a7111..6ac21f9c 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -36,7 +36,7 @@ ..\..\engine;..\..\WixToolset.BootstrapperCore.Native\inc - cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;wintrust.lib + cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib diff --git a/src/test/BurnUnitTest/precomp.h b/src/test/BurnUnitTest/precomp.h index ddbdf9c6..a5db1555 100644 --- a/src/test/BurnUnitTest/precomp.h +++ b/src/test/BurnUnitTest/precomp.h @@ -43,7 +43,6 @@ #include "section.h" #include "approvedexe.h" #include "container.h" -#include "catalog.h" #include "payload.h" #include "cabextract.h" #include "burnextension.h" -- cgit v1.2.3-55-g6feb From cede270b2bd3da6bd8d5205b8834e786c8d6c1ce Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 1 Feb 2021 18:54:54 -0600 Subject: Remove feature to uninstall compatible orphaned MSI packages. --- .../inc/BootstrapperApplication.h | 50 ------- src/engine/apply.cpp | 28 ---- src/engine/core.cpp | 8 -- src/engine/detect.cpp | 2 - src/engine/elevation.cpp | 98 ------------- src/engine/elevation.h | 4 - src/engine/engine.mc | 14 -- src/engine/msiengine.cpp | 156 --------------------- src/engine/msiengine.h | 5 - src/engine/package.cpp | 36 ----- src/engine/package.h | 9 -- src/engine/plan.cpp | 79 +---------- src/engine/plan.h | 7 - src/engine/userexperience.cpp | 99 +------------ src/engine/userexperience.h | 23 --- 15 files changed, 7 insertions(+), 611 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h index 80b23686..c0baa958 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -92,15 +92,12 @@ enum BOOTSTRAPPER_APPLICATION_MESSAGE BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATECOMPLETE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGEBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPATIBLEMSIPACKAGE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDMSIPACKAGE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTTARGETMSIPACKAGE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTMSIFEATURE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGECOMPLETE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGEBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANTARGETMSIPACKAGE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIFEATURE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGECOMPLETE, @@ -469,20 +466,6 @@ struct BA_ONDETECTBEGIN_RESULTS BOOL fCancel; }; -struct BA_ONDETECTCOMPATIBLEMSIPACKAGE_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; - LPCWSTR wzCompatiblePackageId; - LPCWSTR wzCompatiblePackageVersion; -}; - -struct BA_ONDETECTCOMPATIBLEMSIPACKAGE_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - struct BA_ONDETECTCOMPLETE_ARGS { DWORD cbSize; @@ -855,39 +838,6 @@ struct BA_ONPLANBEGIN_RESULTS BOOL fCancel; }; -struct BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; - LPCWSTR wzCompatiblePackageId; - LPCWSTR wzCompatiblePackageVersion; - BOOTSTRAPPER_REQUEST_STATE recommendedState; -}; - -struct BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_RESULTS -{ - DWORD cbSize; - BOOL fCancel; - BOOTSTRAPPER_REQUEST_STATE requestedState; -}; - -struct BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; - LPCWSTR wzCompatiblePackageId; - HRESULT hrStatus; - BOOTSTRAPPER_PACKAGE_STATE state; - BOOTSTRAPPER_REQUEST_STATE requested; - BOOTSTRAPPER_ACTION_STATE execute; - BOOTSTRAPPER_ACTION_STATE rollback; -}; - -struct BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_RESULTS -{ - DWORD cbSize; -}; - struct BA_ONPLANCOMPLETE_ARGS { DWORD cbSize; diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 8e5099d9..dd4932aa 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -199,10 +199,6 @@ static HRESULT ExecuteDependencyAction( __in BURN_EXECUTE_ACTION* pAction, __in BURN_EXECUTE_CONTEXT* pContext ); -static HRESULT ExecuteCompatiblePackageAction( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_EXECUTE_ACTION* pAction - ); static HRESULT ExecuteMsiBeginTransaction( __in BURN_ENGINE_STATE* pEngineState, __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, @@ -1727,11 +1723,6 @@ static HRESULT DoExecuteAction( ExitOnFailure(hr, "Failed to execute dependency action."); break; - case BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE: - hr = ExecuteCompatiblePackageAction(pEngineState, pExecuteAction); - ExitOnFailure(hr, "Failed to execute compatible package action."); - break; - case BURN_EXECUTE_ACTION_TYPE_REGISTRATION: *pfKeepRegistration = pExecuteAction->registration.fKeep; break; @@ -2183,25 +2174,6 @@ LExit: return hr; } -static HRESULT ExecuteCompatiblePackageAction( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_EXECUTE_ACTION* pAction - ) -{ - HRESULT hr = S_OK; - - if (pAction->compatiblePackage.pReferencePackage->fPerMachine) - { - hr = ElevationLoadCompatiblePackageAction(pEngineState->companionConnection.hPipe, pAction); - ExitOnFailure(hr, "Failed to load compatible package on per-machine package."); - } - - // Compatible package already loaded in this process. - -LExit: - return hr; -} - static HRESULT ExecuteMsiBeginTransaction( __in BURN_ENGINE_STATE* pEngineState, __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 0f5ea08a..77e2dd82 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -1692,14 +1692,6 @@ static void LogPackages( LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingBoolToString(pPackage->fAcquire), LoggingBoolToString(pPackage->fUncache), LoggingDependencyActionToString(pPackage->dependencyExecute)); } - for (DWORD i = 0; i < pPackages->cCompatiblePackages; ++i) - { - const DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? pPackages->cCompatiblePackages - 1 - i : i; - const BURN_PACKAGE* pPackage = &pPackages->rgCompatiblePackages[iPackage]; - - LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingBoolToString(pPackage->fAcquire), LoggingBoolToString(pPackage->fUncache), LoggingDependencyActionToString(pPackage->dependencyExecute)); - } - // Display related bundles last if caching, installing, modifying, or repairing. if (BOOTSTRAPPER_ACTION_UNINSTALL < action && 0 < pRelatedBundles->cRelatedBundles) { diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp index 176780af..63e66539 100644 --- a/src/engine/detect.cpp +++ b/src/engine/detect.cpp @@ -63,8 +63,6 @@ extern "C" void DetectReset( pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; } - - pPackage->Msi.fCompatibleInstalled = FALSE; } else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) { diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp index fc53b1f8..cd0c9387 100644 --- a/src/engine/elevation.cpp +++ b/src/engine/elevation.cpp @@ -24,7 +24,6 @@ typedef enum _BURN_ELEVATION_MESSAGE_TYPE BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY, - BURN_ELEVATION_MESSAGE_TYPE_LOAD_COMPATIBLE_PACKAGE, BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_EMBEDDED_CHILD, BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE, BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE, @@ -95,11 +94,6 @@ static HRESULT WaitForElevatedChildCacheThread( __in HANDLE hCacheThread, __in DWORD dwExpectedExitCode ); -static HRESULT OnLoadCompatiblePackage( - __in BURN_PACKAGES* pPackages, - __in BYTE* pbData, - __in DWORD cbData - ); static HRESULT ProcessApplyInitializeMessages( __in BURN_PIPE_MESSAGE* pMsg, __in_opt LPVOID pvContext, @@ -1114,45 +1108,6 @@ LExit: return hr; } -/******************************************************************* - ElevationLoadCompatiblePackageAction - Load compatible package - information from the referenced package. - -*******************************************************************/ -extern "C" HRESULT ElevationLoadCompatiblePackageAction( - __in HANDLE hPipe, - __in BURN_EXECUTE_ACTION* pExecuteAction - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwResult = 0; - BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; - - // Serialize message data. - hr = BuffWriteString(&pbData, &cbData, pExecuteAction->compatiblePackage.pReferencePackage->sczId); - ExitOnFailure(hr, "Failed to write package id to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pExecuteAction->compatiblePackage.sczInstalledProductCode); - ExitOnFailure(hr, "Failed to write installed ProductCode to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pExecuteAction->compatiblePackage.pInstalledVersion->sczVersion); - ExitOnFailure(hr, "Failed to write installed version to message buffer."); - - // Send the message. - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LOAD_COMPATIBLE_PACKAGE, pbData, cbData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LOAD_COMPATIBLE_PACKAGE message to per-machine process."); - - // Ignore the restart since this action only loads data into memory. - hr = ProcessResult(dwResult, &restart); - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - /******************************************************************* ElevationCleanPackage - @@ -1719,10 +1674,6 @@ static HRESULT ProcessElevatedChildMessage( hrResult = OnExecutePackageDependencyAction(pContext->pPackages, &pContext->pRegistration->relatedBundles, (BYTE*)pMsg->pvData, pMsg->cbData); break; - case BURN_ELEVATION_MESSAGE_TYPE_LOAD_COMPATIBLE_PACKAGE: - hrResult = OnLoadCompatiblePackage(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - case BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE: hrResult = OnCleanPackage(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData); break; @@ -2636,55 +2587,6 @@ LExit: return hr; } -static HRESULT OnLoadCompatiblePackage( - __in BURN_PACKAGES* pPackages, - __in BYTE* pbData, - __in DWORD cbData - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - LPWSTR sczPackage = NULL; - LPWSTR sczVersion = NULL; - BURN_EXECUTE_ACTION executeAction = { }; - - executeAction.type = BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE; - - // Deserialize the message data. - hr = BuffReadString(pbData, cbData, &iData, &sczPackage); - ExitOnFailure(hr, "Failed to read package id from message buffer."); - - // Find the reference package. - hr = PackageFindById(pPackages, sczPackage, &executeAction.compatiblePackage.pReferencePackage); - ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); - - hr = BuffReadString(pbData, cbData, &iData, &executeAction.compatiblePackage.sczInstalledProductCode); - ExitOnFailure(hr, "Failed to read installed ProductCode from message buffer."); - - hr = BuffReadString(pbData, cbData, &iData, &sczVersion); - ExitOnFailure(hr, "Failed to read installed version from message buffer."); - - hr = VerParseVersion(sczVersion, 0, FALSE, &executeAction.compatiblePackage.pInstalledVersion); - ExitOnFailure(hr, "Failed to parse installed version from compatible package."); - - // Copy the installed data to the reference package. - hr = StrAllocString(&executeAction.compatiblePackage.pReferencePackage->Msi.sczInstalledProductCode, executeAction.compatiblePackage.sczInstalledProductCode, 0); - ExitOnFailure(hr, "Failed to copy installed ProductCode."); - - executeAction.compatiblePackage.pReferencePackage->Msi.pInstalledVersion = executeAction.compatiblePackage.pInstalledVersion; - - // Load the compatible package and add it to the list. - hr = MsiEngineAddCompatiblePackage(pPackages, executeAction.compatiblePackage.pReferencePackage, NULL); - ExitOnFailure(hr, "Failed to load compatible package."); - -LExit: - ReleaseStr(sczVersion); - ReleaseStr(sczPackage); - PlanUninitializeExecuteAction(&executeAction); - - return hr; -} - static int GenericExecuteMessageHandler( __in GENERIC_EXECUTE_MESSAGE* pMessage, __in LPVOID pvContext diff --git a/src/engine/elevation.h b/src/engine/elevation.h index 05fecdf6..e254dea5 100644 --- a/src/engine/elevation.h +++ b/src/engine/elevation.h @@ -117,10 +117,6 @@ HRESULT ElevationExecutePackageDependencyAction( __in HANDLE hPipe, __in BURN_EXECUTE_ACTION* pExecuteAction ); -HRESULT ElevationLoadCompatiblePackageAction( - __in HANDLE hPipe, - __in BURN_EXECUTE_ACTION* pExecuteAction - ); HRESULT ElevationLaunchElevatedChild( __in HANDLE hPipe, __in BURN_PACKAGE* pPackage, diff --git a/src/engine/engine.mc b/src/engine/engine.mc index 8e36e84e..ad86308c 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -226,13 +226,6 @@ Language=English Detected forward compatible bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!ls!, enabled: %5!hs! . -MessageId=108 -Severity=Success -SymbolicName=MSG_DETECTED_COMPATIBLE_PACKAGE_FROM_PROVIDER -Language=English -Detected compatible package: %1!ls!, provider: %2!ls!, installed: %3!ls!, version: %4!ls!, chained: %5!ls! -. - MessageId=120 Severity=Warning SymbolicName=MSG_DETECT_PACKAGE_NOT_FULLY_CACHED @@ -394,13 +387,6 @@ Language=English Plan skipped related bundle: %1!ls!, type: %2!hs!, because it was previously scheduled. . -MessageId=215 -Severity=Success -SymbolicName=MSG_PLANNED_ORPHAN_PACKAGE_FROM_PROVIDER -Language=English -Will remove orphan package: %1!ls!, installed: %2!ls!, chained: %3!ls! -. - MessageId=216 Severity=Success SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index 688be7af..5bccb375 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -325,7 +325,6 @@ extern "C" void MsiEnginePackageUninitialize( { ReleaseStr(pPackage->Msi.sczProductCode); ReleaseStr(pPackage->Msi.sczUpgradeCode); - ReleaseStr(pPackage->Msi.sczInstalledProductCode); // free features if (pPackage->Msi.rgFeatures) @@ -404,8 +403,6 @@ extern "C" HRESULT MsiEngineDetectPackage( int nCompareResult = 0; LPWSTR sczInstalledVersion = NULL; LPWSTR sczInstalledLanguage = NULL; - LPWSTR sczInstalledProductCode = NULL; - LPWSTR sczInstalledProviderKey = NULL; INSTALLSTATE installState = INSTALLSTATE_UNKNOWN; BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE; BOOTSTRAPPER_RELATED_OPERATION relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE; @@ -457,42 +454,6 @@ extern "C" HRESULT MsiEngineDetectPackage( } else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr || HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr) // package not present. { - // Check for newer, compatible packages based on a fixed provider key. - hr = DependencyDetectProviderKeyPackageId(pPackage, &sczInstalledProviderKey, &sczInstalledProductCode); - if (SUCCEEDED(hr)) - { - hr = WiuGetProductInfoEx(sczInstalledProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); - if (SUCCEEDED(hr)) - { - hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pVersion); - ExitOnFailure(hr, "Failed to parse dependency version: '%ls' for ProductCode: %ls", sczInstalledVersion, sczInstalledProductCode); - - if (pVersion->fInvalid) - { - LogId(REPORT_WARNING, MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION, sczInstalledProductCode, sczInstalledVersion); - } - - // compare versions - hr = VerCompareParsedVersions(pPackage->Msi.pVersion, pVersion, &nCompareResult); - ExitOnFailure(hr, "Failed to compare version '%ls' to dependency version: '%ls'", pPackage->Msi.pVersion->sczVersion, pVersion->sczVersion); - - if (nCompareResult < 0) - { - LogId(REPORT_STANDARD, MSG_DETECTED_COMPATIBLE_PACKAGE_FROM_PROVIDER, pPackage->sczId, sczInstalledProviderKey, sczInstalledProductCode, sczInstalledVersion, pPackage->Msi.sczProductCode); - - hr = UserExperienceOnDetectCompatibleMsiPackage(pUserExperience, pPackage->sczId, sczInstalledProductCode, pVersion); - ExitOnRootFailure(hr, "BA aborted detect compatible MSI package."); - - hr = StrAllocString(&pPackage->Msi.sczInstalledProductCode, sczInstalledProductCode, 0); - ExitOnFailure(hr, "Failed to copy the installed ProductCode to the package."); - - pPackage->Msi.pInstalledVersion = pVersion; - pPackage->Msi.fCompatibleInstalled = TRUE; - pVersion = NULL; - } - } - } - pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; hr = S_OK; } @@ -701,8 +662,6 @@ extern "C" HRESULT MsiEngineDetectPackage( } LExit: - ReleaseStr(sczInstalledProviderKey); - ReleaseStr(sczInstalledProductCode); ReleaseStr(sczInstalledLanguage); ReleaseStr(sczInstalledVersion); ReleaseVerutilVersion(pVersion); @@ -1023,121 +982,6 @@ LExit: return hr; } -extern "C" HRESULT MsiEngineAddCompatiblePackage( - __in BURN_PACKAGES* pPackages, - __in const BURN_PACKAGE* pPackage, - __out_opt BURN_PACKAGE** ppCompatiblePackage - ) -{ - Assert(BURN_PACKAGE_TYPE_MSI == pPackage->type); - - HRESULT hr = S_OK; - BURN_PACKAGE* pCompatiblePackage = NULL; - LPWSTR sczInstalledVersion = NULL; - - // Allocate enough memory all at once so pointers to packages within - // aren't invalidated if we otherwise reallocated. - hr = PackageEnsureCompatiblePackagesArray(pPackages); - ExitOnFailure(hr, "Failed to allocate memory for compatible MSI package."); - - pCompatiblePackage = pPackages->rgCompatiblePackages + pPackages->cCompatiblePackages; - ++pPackages->cCompatiblePackages; - - pCompatiblePackage->type = BURN_PACKAGE_TYPE_MSI; - - // Read in the compatible ProductCode if not already available. - if (pPackage->Msi.sczInstalledProductCode) - { - hr = StrAllocString(&pCompatiblePackage->Msi.sczProductCode, pPackage->Msi.sczInstalledProductCode, 0); - ExitOnFailure(hr, "Failed to copy installed ProductCode to compatible package."); - } - else - { - hr = DependencyDetectProviderKeyPackageId(pPackage, NULL, &pCompatiblePackage->Msi.sczProductCode); - ExitOnFailure(hr, "Failed to detect compatible package from provider key."); - } - - // Read in the compatible ProductVersion if not already available. - if (pPackage->Msi.pInstalledVersion) - { - hr = VerCopyVersion(pPackage->Msi.pInstalledVersion, &pCompatiblePackage->Msi.pVersion); - ExitOnFailure(hr, "Failed to copy version for compatible package."); - } - else - { - hr = WiuGetProductInfoEx(pCompatiblePackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); - ExitOnFailure(hr, "Failed to read version from compatible package."); - - hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pCompatiblePackage->Msi.pVersion); - ExitOnFailure(hr, "Failed to parse version: '%ls' for ProductCode: %ls", sczInstalledVersion, pCompatiblePackage->Msi.sczProductCode); - - if (pCompatiblePackage->Msi.pVersion->fInvalid) - { - LogId(REPORT_WARNING, MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION, pCompatiblePackage->Msi.sczProductCode, sczInstalledVersion); - } - } - - // For now, copy enough information to support uninstalling the newer, compatible package. - hr = StrAllocString(&pCompatiblePackage->sczId, pCompatiblePackage->Msi.sczProductCode, 0); - ExitOnFailure(hr, "Failed to copy installed ProductCode as compatible package ID."); - - pCompatiblePackage->fPerMachine = pPackage->fPerMachine; - pCompatiblePackage->fUninstallable = pPackage->fUninstallable; - pCompatiblePackage->cacheType = pPackage->cacheType; - - // Removing compatible packages is best effort. - pCompatiblePackage->fVital = FALSE; - - // Format a suitable log path variable from the original package. - hr = StrAllocFormatted(&pCompatiblePackage->sczLogPathVariable, L"%ls_Compatible", pPackage->sczLogPathVariable); - ExitOnFailure(hr, "Failed to format log path variable for compatible package."); - - // Use the default cache ID generation from the binder. - hr = StrAllocFormatted(&pCompatiblePackage->sczCacheId, L"%lsv%ls", pCompatiblePackage->sczId, pCompatiblePackage->Msi.pVersion->sczVersion); - ExitOnFailure(hr, "Failed to format cache ID for compatible package."); - - pCompatiblePackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; - pCompatiblePackage->cache = BURN_CACHE_STATE_PARTIAL; // Cannot know if it's complete or not. - - // Copy all the providers to ensure no dependents. - if (pPackage->cDependencyProviders) - { - pCompatiblePackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER) * pPackage->cDependencyProviders, TRUE); - ExitOnNull(pCompatiblePackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate for compatible package providers."); - - for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) - { - BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + i; - BURN_DEPENDENCY_PROVIDER* pCompatibleProvider = pCompatiblePackage->rgDependencyProviders + i; - - // Only need to copy the key for uninstall. - hr = StrAllocString(&pCompatibleProvider->sczKey, pProvider->sczKey, 0); - ExitOnFailure(hr, "Failed to copy the compatible provider key."); - - // Assume the package version is the same as the provider version. - hr = StrAllocString(&pCompatibleProvider->sczVersion, pCompatiblePackage->Msi.pVersion->sczVersion, 0); - ExitOnFailure(hr, "Failed to copy the compatible provider version."); - - // Assume provider keys are similarly authored for this package. - pCompatibleProvider->fImported = pProvider->fImported; - } - - pCompatiblePackage->cDependencyProviders = pPackage->cDependencyProviders; - } - - pCompatiblePackage->type = BURN_PACKAGE_TYPE_MSI; - - if (ppCompatiblePackage) - { - *ppCompatiblePackage = pCompatiblePackage; - } - -LExit: - ReleaseStr(sczInstalledVersion); - - return hr; -} - extern "C" HRESULT MsiEngineBeginTransaction( __in LPCWSTR wzName ) diff --git a/src/engine/msiengine.h b/src/engine/msiengine.h index c7cc3bef..1f450147 100644 --- a/src/engine/msiengine.h +++ b/src/engine/msiengine.h @@ -48,11 +48,6 @@ HRESULT MsiEnginePlanAddPackage( __in_opt HANDLE hCacheEvent, __in BOOL fPlanPackageCacheRollback ); -HRESULT MsiEngineAddCompatiblePackage( - __in BURN_PACKAGES* pPackages, - __in const BURN_PACKAGE* pPackage, - __out_opt BURN_PACKAGE** ppCompatiblePackage - ); HRESULT MsiEngineBeginTransaction( __in LPCWSTR wzName ); diff --git a/src/engine/package.cpp b/src/engine/package.cpp index 02958efd..527766eb 100644 --- a/src/engine/package.cpp +++ b/src/engine/package.cpp @@ -369,15 +369,6 @@ extern "C" void PackagesUninitialize( MemFree(pPackages->rgPackages); } - if (pPackages->rgCompatiblePackages) - { - for (DWORD i = 0; i < pPackages->cCompatiblePackages; ++i) - { - PackageUninitialize(pPackages->rgCompatiblePackages + i); - } - MemFree(pPackages->rgCompatiblePackages); - } - if (pPackages->rgPatchTargetCodes) { for (DWORD i = 0; i < pPackages->cPatchTargetCodes; ++i) @@ -414,17 +405,6 @@ extern "C" HRESULT PackageFindById( } } - for (DWORD i = 0; i < pPackages->cCompatiblePackages; ++i) - { - pPackage = &pPackages->rgCompatiblePackages[i]; - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, wzId, -1)) - { - *ppPackage = pPackage; - ExitFunction1(hr = S_OK); - } - } - hr = E_NOTFOUND; LExit: @@ -510,22 +490,6 @@ LExit: return hr; } -HRESULT PackageEnsureCompatiblePackagesArray( - __in BURN_PACKAGES* pPackages - ) -{ - HRESULT hr = S_OK; - - if (!pPackages->rgCompatiblePackages) - { - pPackages->rgCompatiblePackages = (BURN_PACKAGE*)MemAlloc(sizeof(BURN_PACKAGE) * pPackages->cPackages, TRUE); - ExitOnNull(pPackages->rgCompatiblePackages, hr, E_OUTOFMEMORY, "Failed to allocate memory for compatible packages."); - } - -LExit: - return hr; -} - // internal function declarations diff --git a/src/engine/package.h b/src/engine/package.h index ec176f8c..1a4f7060 100644 --- a/src/engine/package.h +++ b/src/engine/package.h @@ -248,7 +248,6 @@ typedef struct _BURN_PACKAGE LPWSTR sczProductCode; DWORD dwLanguage; VERUTIL_VERSION* pVersion; - LPWSTR sczInstalledProductCode; VERUTIL_VERSION* pInstalledVersion; LPWSTR sczUpgradeCode; @@ -264,8 +263,6 @@ typedef struct _BURN_PACKAGE _BURN_PACKAGE** rgpSlipstreamMspPackages; LPWSTR* rgsczSlipstreamMspPackageIds; DWORD cSlipstreamMspPackages; - - BOOL fCompatibleInstalled; } Msi; struct { @@ -294,9 +291,6 @@ typedef struct _BURN_PACKAGES BURN_PACKAGE* rgPackages; DWORD cPackages; - BURN_PACKAGE* rgCompatiblePackages; - DWORD cCompatiblePackages; - BURN_PATCH_TARGETCODE* rgPatchTargetCodes; DWORD cPatchTargetCodes; @@ -335,9 +329,6 @@ HRESULT PackageGetProperty( __in_z LPCWSTR wzProperty, __out_z_opt LPWSTR* psczValue ); -HRESULT PackageEnsureCompatiblePackagesArray( - __in BURN_PACKAGES* pPackages - ); #if defined(__cplusplus) diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index ffbe495f..a45eab62 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -30,7 +30,6 @@ static void ResetPlannedRollbackBoundaryState( ); static HRESULT ProcessPackage( __in BOOL fBundlePerMachine, - __in_opt BURN_PACKAGE* pCompatiblePackageParent, __in BURN_USER_EXPERIENCE* pUX, __in BURN_PLAN* pPlan, __in BURN_PACKAGE* pPackage, @@ -311,10 +310,6 @@ extern "C" void PlanUninitializeExecuteAction( case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: ReleaseStr(pExecuteAction->packageDependency.sczBundleProviderKey); break; - - case BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE: - ReleaseStr(pExecuteAction->compatiblePackage.sczInstalledProductCode); - break; } } @@ -523,39 +518,8 @@ extern "C" HRESULT PlanPackages( } } - hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices); + hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices); ExitOnFailure(hr, "Failed to process package."); - - // Attempt to remove orphaned packages during uninstall. Currently only MSI packages are supported and should not require source. - if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.fCompatibleInstalled) - { - BURN_PACKAGE* pCompatiblePackage = NULL; - BURN_EXECUTE_ACTION* pAction = NULL; - - // Add the compatible package to the list. - hr = MsiEngineAddCompatiblePackage(pPackages, pPackage, &pCompatiblePackage); - ExitOnFailure(hr, "Failed to add compatible package for package: %ls", pPackage->sczId); - - // Plan to load the compatible package into the elevated engine before its needed. - hr = PlanAppendExecuteAction(pPlan, &pAction); - ExitOnFailure(hr, "Failed to append execute action."); - - pAction->type = BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE; - pAction->compatiblePackage.pReferencePackage = pPackage; - pAction->compatiblePackage.pInstalledVersion = pCompatiblePackage->Msi.pVersion; - - hr = StrAllocString(&pAction->compatiblePackage.sczInstalledProductCode, pCompatiblePackage->Msi.sczProductCode, 0); - ExitOnFailure(hr, "Failed to copy installed ProductCode"); - - // Process the compatible MSI package like any other. - hr = ProcessPackage(fBundlePerMachine, pPackage, pUX, pPlan, pCompatiblePackage, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices); - ExitOnFailure(hr, "Failed to process compatible package."); - - if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pCompatiblePackage->execute) - { - LogId(REPORT_STANDARD, MSG_PLANNED_ORPHAN_PACKAGE_FROM_PROVIDER, pPackage->sczId, pCompatiblePackage->Msi.sczProductCode, pPackage->Msi.sczProductCode); - } - } } // Insert the "keep registration" and "remove registration" actions in the plan when installing the first time and anytime we are uninstalling respectively. @@ -601,15 +565,6 @@ extern "C" HRESULT PlanPackages( ExitOnFailure(hr, "Failed to plan clean package."); } - // Plan best-effort clean up of compatible packages. - for (DWORD i = 0; i < pPackages->cCompatiblePackages; ++i) - { - DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackages->cCompatiblePackages - 1 - i : i; - BURN_PACKAGE* pCompatiblePackage = pPackages->rgCompatiblePackages + iPackage; - - PlanCleanPackage(pPlan, pCompatiblePackage); - } - LExit: return hr; } @@ -809,7 +764,7 @@ extern "C" HRESULT PlanPassThroughBundle( BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; // Plan passthrough package. - hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL); + hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL); ExitOnFailure(hr, "Failed to process passthrough package."); // If we still have an open rollback boundary, complete it. @@ -843,7 +798,7 @@ extern "C" HRESULT PlanUpdateBundle( BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; // Plan update package. - hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL); + hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL); ExitOnFailure(hr, "Failed to process update package."); // If we still have an open rollback boundary, complete it. @@ -863,7 +818,6 @@ LExit: static HRESULT ProcessPackage( __in BOOL fBundlePerMachine, - __in_opt BURN_PACKAGE* pCompatiblePackageParent, __in BURN_USER_EXPERIENCE* pUX, __in BURN_PLAN* pPlan, __in BURN_PACKAGE* pPackage, @@ -888,18 +842,8 @@ static HRESULT ProcessPackage( pPackage->requested = pPackage->defaultRequested; fPlanPackageBegan = TRUE; - if (pCompatiblePackageParent) - { - AssertSz(BURN_PACKAGE_TYPE_MSI == pPackage->type, "Currently only MSI packages have compatible packages."); - - hr = UserExperienceOnPlanCompatibleMsiPackageBegin(pUX, pCompatiblePackageParent->sczId, pPackage->sczId, pPackage->Msi.pVersion, &pPackage->requested); - ExitOnRootFailure(hr, "BA aborted plan compatible MSI package begin."); - } - else - { - hr = UserExperienceOnPlanPackageBegin(pUX, pPackage->sczId, &pPackage->requested); - ExitOnRootFailure(hr, "BA aborted plan package begin."); - } + hr = UserExperienceOnPlanPackageBegin(pUX, pPackage->sczId, &pPackage->requested); + ExitOnRootFailure(hr, "BA aborted plan package begin."); pEffectiveRollbackBoundary = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackage->pRollbackBoundaryBackward : pPackage->pRollbackBoundaryForward; hr = ProcessPackageRollbackBoundary(pPlan, pEffectiveRollbackBoundary, ppRollbackBoundary); @@ -964,14 +908,7 @@ static HRESULT ProcessPackage( LExit: if (fPlanPackageBegan) { - if (pCompatiblePackageParent) - { - UserExperienceOnPlanCompatibleMsiPackageComplete(pUX, pCompatiblePackageParent->sczId, pPackage->sczId, hr, pPackage->currentState, pPackage->requested, pPackage->execute, pPackage->rollback); - } - else - { - UserExperienceOnPlanPackageComplete(pUX, pPackage->sczId, hr, pPackage->currentState, pPackage->requested, pPackage->execute, pPackage->rollback); - } + UserExperienceOnPlanPackageComplete(pUX, pPackage->sczId, hr, pPackage->currentState, pPackage->requested, pPackage->execute, pPackage->rollback); } return hr; @@ -3175,10 +3112,6 @@ static void ExecuteActionLog( LogStringLine(PlanDumpLevel, "%ls action[%u]: UNCACHE_PACKAGE id: %ls", wzBase, iAction, pAction->uncachePackage.pPackage->sczId); break; - case BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE: - LogStringLine(PlanDumpLevel, "%ls action[%u]: COMPATIBLE_PACKAGE reference id: %ls, installed ProductCode: %ls", wzBase, iAction, pAction->compatiblePackage.pReferencePackage->sczId, pAction->compatiblePackage.sczInstalledProductCode); - break; - case BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION: LogStringLine(PlanDumpLevel, "%ls action[%u]: BEGIN_MSI_TRANSACTION id: %ls", wzBase, iAction, pAction->msiTransaction.pRollbackBoundary->sczId); break; diff --git a/src/engine/plan.h b/src/engine/plan.h index dad436d4..cb64f891 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -65,7 +65,6 @@ enum BURN_EXECUTE_ACTION_TYPE BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY, BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY, BURN_EXECUTE_ACTION_TYPE_REGISTRATION, - BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE, BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION, BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION, }; @@ -301,12 +300,6 @@ typedef struct _BURN_EXECUTE_ACTION BURN_DEPENDENCY_ACTION action; } packageDependency; struct - { - BURN_PACKAGE* pReferencePackage; - LPWSTR sczInstalledProductCode; - VERUTIL_VERSION* pInstalledVersion; - } compatiblePackage; - struct { BURN_ROLLBACK_BOUNDARY* pRollbackBoundary; } msiTransaction; diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index 8e782e71..ea8b8a19 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -104,7 +104,7 @@ extern "C" HRESULT UserExperienceLoad( args.pCommand = pCommand; args.pfnBootstrapperEngineProc = EngineForApplicationProc; args.pvBootstrapperEngineProcContext = pEngineContext; - args.qwEngineAPIVersion = MAKEQWORDVERSION(2020, 11, 17, 0); + args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 1, 30, 0); results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS); @@ -747,36 +747,6 @@ LExit: return hr; } -EXTERN_C BAAPI UserExperienceOnDetectCompatibleMsiPackage( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in_z LPCWSTR wzCompatiblePackageId, - __in VERUTIL_VERSION* pCompatiblePackageVersion - ) -{ - HRESULT hr = S_OK; - BA_ONDETECTCOMPATIBLEMSIPACKAGE_ARGS args = { }; - BA_ONDETECTCOMPATIBLEMSIPACKAGE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - args.wzCompatiblePackageId = wzCompatiblePackageId; - args.wzCompatiblePackageVersion = pCompatiblePackageVersion->sczVersion; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPATIBLEMSIPACKAGE, &args, &results); - ExitOnFailure(hr, "BA OnDetectCompatibleMsiPackage failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - EXTERN_C BAAPI UserExperienceOnDetectComplete( __in BURN_USER_EXPERIENCE* pUserExperience, __in HRESULT hrStatus @@ -1555,73 +1525,6 @@ LExit: return hr; } -EXTERN_C BAAPI UserExperienceOnPlanCompatibleMsiPackageBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in_z LPCWSTR wzCompatiblePackageId, - __in VERUTIL_VERSION* pCompatiblePackageVersion, - __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState - ) -{ - HRESULT hr = S_OK; - BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_ARGS args = { }; - BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - args.wzCompatiblePackageId = wzCompatiblePackageId; - args.wzCompatiblePackageVersion = pCompatiblePackageVersion->sczVersion; - args.recommendedState = *pRequestedState; - - results.cbSize = sizeof(results); - results.requestedState = *pRequestedState; - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnPlanCompatibleMsiPackageBegin failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - *pRequestedState = results.requestedState; - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnPlanCompatibleMsiPackageComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in_z LPCWSTR wzCompatiblePackageId, - __in HRESULT hrStatus, - __in BOOTSTRAPPER_PACKAGE_STATE state, - __in BOOTSTRAPPER_REQUEST_STATE requested, - __in BOOTSTRAPPER_ACTION_STATE execute, - __in BOOTSTRAPPER_ACTION_STATE rollback - ) -{ - HRESULT hr = S_OK; - BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_ARGS args = { }; - BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - args.wzCompatiblePackageId = wzCompatiblePackageId; - args.hrStatus = hrStatus; - args.state = state; - args.requested = requested; - args.execute = execute; - args.rollback = rollback; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnPlanCompatibleMsiPackageComplete failed."); - -LExit: - return hr; -} - EXTERN_C BAAPI UserExperienceOnPlanMsiFeature( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index a02d968c..3176e762 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -185,12 +185,6 @@ BAAPI UserExperienceOnDetectBegin( __in BOOL fInstalled, __in DWORD cPackages ); -BAAPI UserExperienceOnDetectCompatibleMsiPackage( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in_z LPCWSTR wzCompatiblePackageId, - __in VERUTIL_VERSION* pCompatiblePackageVersion - ); BAAPI UserExperienceOnDetectComplete( __in BURN_USER_EXPERIENCE* pUserExperience, __in HRESULT hrStatus @@ -354,23 +348,6 @@ BAAPI UserExperienceOnPlanBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in DWORD cPackages ); -BAAPI UserExperienceOnPlanCompatibleMsiPackageBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in_z LPCWSTR wzCompatiblePackageId, - __in VERUTIL_VERSION* pCompatiblePackageVersion, - __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState - ); -BAAPI UserExperienceOnPlanCompatibleMsiPackageComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in_z LPCWSTR wzCompatiblePackageId, - __in HRESULT hrStatus, - __in BOOTSTRAPPER_PACKAGE_STATE state, - __in BOOTSTRAPPER_REQUEST_STATE requested, - __in BOOTSTRAPPER_ACTION_STATE execute, - __in BOOTSTRAPPER_ACTION_STATE rollback - ); BAAPI UserExperienceOnPlanComplete( __in BURN_USER_EXPERIENCE* pUserExperience, __in HRESULT hrStatus -- cgit v1.2.3-55-g6feb From cc5fe7c79aad14819df1b4cb134884b80a945141 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 1 Feb 2021 20:36:39 -0600 Subject: Move registry checks for dependency ref-counting into Detect. --- src/engine/core.cpp | 12 +- src/engine/dependency.cpp | 349 +++++++++++++++++++++---------------- src/engine/dependency.h | 29 +-- src/engine/detect.cpp | 28 +++ src/engine/engine.mc | 11 +- src/engine/package.cpp | 2 +- src/engine/package.h | 4 + src/engine/plan.cpp | 104 ++++++----- src/engine/plan.h | 1 - src/engine/registration.h | 9 +- src/engine/relatedbundle.cpp | 2 +- src/test/BurnUnitTest/PlanTest.cpp | 23 ++- 12 files changed, 354 insertions(+), 220 deletions(-) diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 77e2dd82..ae09ea65 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -97,7 +97,7 @@ extern "C" HRESULT CoreInitialize( ExitOnFailure(hr, "Failed to load manifest."); hr = ContainersInitialize(&pEngineState->containers, &pEngineState->section); - ExitOnFailure(hr, "Failed to intialize containers."); + ExitOnFailure(hr, "Failed to initialize containers."); // Parse command line. hr = ParseCommandLine(pEngineState->argc, pEngineState->argv, &pEngineState->command, &pEngineState->companionConnection, &pEngineState->embeddedConnection, &pEngineState->variables, &pEngineState->mode, &pEngineState->automaticUpdates, &pEngineState->fDisableSystemRestore, &sczSourceProcessPath, &sczOriginalSource, &pEngineState->fDisableUnelevate, &pEngineState->log.dwAttributes, &pEngineState->log.sczPath, &pEngineState->registration.sczActiveParent, &pEngineState->sczIgnoreDependencies, &pEngineState->registration.sczAncestors, &sczSanitizedCommandLine); @@ -105,6 +105,9 @@ extern "C" HRESULT CoreInitialize( LogId(REPORT_STANDARD, MSG_BURN_COMMAND_LINE, sczSanitizedCommandLine ? sczSanitizedCommandLine : L""); + hr = DependencyInitialize(&pEngineState->registration, pEngineState->sczIgnoreDependencies); + ExitOnFailure(hr, "Failed to initialize dependency data."); + // Retain whether bundle was initially run elevated. ProcElevated(::GetCurrentProcess(), &fElevated); @@ -332,6 +335,9 @@ extern "C" HRESULT CoreDetect( } } + hr = DependencyDetect(pEngineState); + ExitOnFailure(hr, "Failed to detect the dependencies."); + // Log the detected states. for (DWORD iPackage = 0; iPackage < pEngineState->packages.cPackages; ++iPackage) { @@ -419,7 +425,7 @@ extern "C" HRESULT CorePlan( hr = PlanSetResumeCommand(&pEngineState->registration, action, &pEngineState->command, &pEngineState->log); ExitOnFailure(hr, "Failed to set resume command"); - hr = DependencyPlanInitialize(pEngineState, &pEngineState->plan); + hr = DependencyPlanInitialize(&pEngineState->registration, &pEngineState->plan); ExitOnFailure(hr, "Failed to initialize the dependencies for the plan."); if (BOOTSTRAPPER_ACTION_LAYOUT == action) @@ -457,7 +463,7 @@ extern "C" HRESULT CorePlan( BOOL fContinuePlanning = TRUE; // assume we'll be able to keep planning after registration. pEngineState->plan.fPerMachine = pEngineState->registration.fPerMachine; // default the scope of the plan to the per-machine state of the bundle. - hr = PlanRegistration(&pEngineState->plan, &pEngineState->registration, pEngineState->command.resumeType, pEngineState->command.relationType, pEngineState->sczIgnoreDependencies, &fContinuePlanning); + hr = PlanRegistration(&pEngineState->plan, &pEngineState->registration, pEngineState->command.resumeType, pEngineState->command.relationType, &fContinuePlanning); ExitOnFailure(hr, "Failed to plan registration."); if (fContinuePlanning) diff --git a/src/engine/dependency.cpp b/src/engine/dependency.cpp index af4ab0a1..1c33aaf2 100644 --- a/src/engine/dependency.cpp +++ b/src/engine/dependency.cpp @@ -10,6 +10,12 @@ const LPCWSTR vcszIgnoreDependenciesDelim = L";"; // internal function declarations +static HRESULT DetectPackageDependents( + __in BURN_PACKAGE* pPackage, + __in STRINGDICT_HANDLE sdIgnoredDependents, + __in const BURN_REGISTRATION* pRegistration + ); + static HRESULT SplitIgnoreDependencies( __in_z LPCWSTR wzIgnoreDependencies, __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, @@ -28,11 +34,9 @@ static HRESULT GetIgnoredDependents( __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents ); -static HRESULT GetProviderInformation( +static BOOL GetProviderExists( __in HKEY hkRoot, - __in_z LPCWSTR wzProviderKey, - __deref_opt_out_z_opt LPWSTR* psczProviderKey, - __deref_opt_out_z_opt LPWSTR* psczId + __in_z LPCWSTR wzProviderKey ); static void CalculateDependencyActionStates( @@ -70,20 +74,18 @@ static void UnregisterPackageDependency( __in_z LPCWSTR wzDependentProviderKey ); -static BOOL PackageProviderExists( - __in const BURN_PACKAGE* pPackage - ); - // functions -extern "C" void DependencyUninitialize( +extern "C" void DependencyUninitializeProvider( __in BURN_DEPENDENCY_PROVIDER* pProvider ) { ReleaseStr(pProvider->sczKey); ReleaseStr(pProvider->sczVersion); ReleaseStr(pProvider->sczDisplayName); + ReleaseDependencyArray(pProvider->rgDependents, pProvider->cDependents); + memset(pProvider, 0, sizeof(BURN_DEPENDENCY_PROVIDER)); } @@ -167,45 +169,33 @@ LExit: return hr; } -extern "C" HRESULT DependencyDetectProviderKeyPackageId( - __in const BURN_PACKAGE* pPackage, - __deref_opt_out_z_opt LPWSTR* psczProviderKey, - __deref_opt_out_z_opt LPWSTR* psczId +extern "C" HRESULT DependencyInitialize( + __in BURN_REGISTRATION* pRegistration, + __in_z_opt LPCWSTR wzIgnoreDependencies ) { - HRESULT hr = E_NOTFOUND; - LPWSTR wzProviderKey = NULL; - HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + HRESULT hr = S_OK; - for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + // If no parent was specified at all, use the bundle id as the self dependent. + if (!pRegistration->sczActiveParent) { - const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; - - // Find the first package id registered for the provider key. - hr = GetProviderInformation(hkRoot, pProvider->sczKey, psczProviderKey, psczId); - if (E_NOTFOUND == hr) - { - continue; - } - ExitOnFailure(hr, "Failed to get the package provider information."); - - ExitFunction(); + pRegistration->wzSelfDependent = pRegistration->sczId; } - - // Older bundles may not have written the id so try the default. - if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + else if (*pRegistration->sczActiveParent) // if parent was specified use that as the self dependent. { - wzProviderKey = pPackage->Msi.sczProductCode; + pRegistration->wzSelfDependent = pRegistration->sczActiveParent; } + // else parent:none was used which means we should not register a dependency on ourself. + + // The current bundle provider key should always be ignored for dependency checks. + hr = DepDependencyArrayAlloc(&pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies, pRegistration->sczProviderKey, NULL); + ExitOnFailure(hr, "Failed to add the bundle provider key to the list of dependencies to ignore."); - if (wzProviderKey) + // Add the list of dependencies to ignore. + if (wzIgnoreDependencies) { - hr = GetProviderInformation(hkRoot, wzProviderKey, psczProviderKey, psczId); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get the package default provider information."); + hr = SplitIgnoreDependencies(wzIgnoreDependencies, &pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies); + ExitOnFailure(hr, "Failed to split the list of dependencies to ignore."); } LExit: @@ -236,23 +226,77 @@ LExit: return hr; } +extern "C" HRESULT DependencyDetect( + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + BURN_REGISTRATION* pRegistration = &pEngineState->registration; + STRINGDICT_HANDLE sdIgnoredDependents = NULL; + BURN_PACKAGE* pPackage = NULL; + + // Always leave this empty so that all dependents get detected. Plan will ignore dependents based on its own logic. + hr = DictCreateStringList(&sdIgnoredDependents, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create the string dictionary."); + + hr = DepCheckDependents(pRegistration->hkRoot, pRegistration->sczProviderKey, 0, sdIgnoredDependents, &pRegistration->rgDependents, &pRegistration->cDependents); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed dependents check on bundle"); + } + else + { + hr = S_OK; + } + + for (DWORD iPackage = 0; iPackage < pEngineState->packages.cPackages; ++iPackage) + { + pPackage = pEngineState->packages.rgPackages + iPackage; + hr = DetectPackageDependents(pPackage, sdIgnoredDependents, pRegistration); + ExitOnFailure(hr, "Failed to detect dependents for package '%ls'", pPackage->sczId); + } + + for (DWORD iRelatedBundle = 0; iRelatedBundle < pEngineState->registration.relatedBundles.cRelatedBundles; ++iRelatedBundle) + { + pPackage = &pEngineState->registration.relatedBundles.rgRelatedBundles[iRelatedBundle].package; + hr = DetectPackageDependents(pPackage, sdIgnoredDependents, pRegistration); + ExitOnFailure(hr, "Failed to detect dependents for related bundle '%ls'", pPackage->sczId); + } + + if (pRegistration->wzSelfDependent) + { + for (DWORD i = 0; i < pRegistration->cDependents; ++i) + { + DEPENDENCY* pDependent = pRegistration->rgDependents + i; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->wzSelfDependent, -1, pDependent->sczKey, -1)) + { + pRegistration->fSelfRegisteredAsDependent = TRUE; + break; + } + } + } + +LExit: + ReleaseDict(sdIgnoredDependents); + + return hr; +} + extern "C" HRESULT DependencyPlanInitialize( - __in const BURN_ENGINE_STATE* pEngineState, + __in const BURN_REGISTRATION* pRegistration, __in BURN_PLAN* pPlan ) { HRESULT hr = S_OK; - // The current bundle provider key should always be ignored for dependency checks. - hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pEngineState->registration.sczProviderKey, NULL); - ExitOnFailure(hr, "Failed to add the bundle provider key to the list of dependencies to ignore."); - - // Add the list of dependencies to ignore to the plan. - if (pEngineState->sczIgnoreDependencies) + // TODO: After adding enumeration to STRINGDICT, a single STRINGDICT_HANDLE can be used everywhere. + for (DWORD i = 0; i < pRegistration->cIgnoredDependencies; ++i) { - // TODO: After adding enumeration to STRINGDICT, a single STRINGDICT_HANDLE can be used everywhere. - hr = SplitIgnoreDependencies(pEngineState->sczIgnoreDependencies, &pPlan->rgPlannedProviders, &pPlan->cPlannedProviders); - ExitOnFailure(hr, "Failed to split the list of dependencies to ignore."); + DEPENDENCY* pDependency = pRegistration->rgIgnoredDependencies + i; + + hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pDependency->sczKey, pDependency->sczName); + ExitOnFailure(hr, "Failed to add the detected provider to the list of dependencies to ignore."); } LExit: @@ -323,9 +367,6 @@ extern "C" HRESULT DependencyPlanPackageBegin( { HRESULT hr = S_OK; STRINGDICT_HANDLE sdIgnoredDependents = NULL; - DEPENDENCY* rgDependents = NULL; - UINT cDependents = 0; - HKEY hkHive = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; BURN_DEPENDENCY_ACTION dependencyExecuteAction = BURN_DEPENDENCY_ACTION_NONE; BURN_DEPENDENCY_ACTION dependencyRollbackAction = BURN_DEPENDENCY_ACTION_NONE; @@ -361,18 +402,31 @@ extern "C" HRESULT DependencyPlanPackageBegin( } else { + hr = S_OK; + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) { - const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + const BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + i; - hr = DepCheckDependents(hkHive, pProvider->sczKey, 0, sdIgnoredDependents, &rgDependents, &cDependents); - if (E_FILENOTFOUND != hr) - { - ExitOnFailure(hr, "Failed dependents check on package provider: %ls", pProvider->sczKey); - } - else + for (DWORD j = 0; j < pProvider->cDependents; ++j) { - hr = S_OK; + const DEPENDENCY* pDependency = pProvider->rgDependents + j; + + hr = DictKeyExists(sdIgnoredDependents, pDependency->sczKey); + if (E_NOTFOUND == hr) + { + hr = S_OK; + + if (!pPackage->fDependencyManagerWasHere) + { + pPackage->fDependencyManagerWasHere = TRUE; + + LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS, pPackage->sczId); + } + + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_DEPENDENT, pDependency->sczKey, LoggingStringOrUnknownIfNull(pDependency->sczName)); + } + ExitOnFailure(hr, "Failed to check the dictionary of ignored dependents."); } } } @@ -382,52 +436,46 @@ extern "C" HRESULT DependencyPlanPackageBegin( CalculateDependencyActionStates(pPackage, pPlan->action, &dependencyExecuteAction, &dependencyRollbackAction); // If dependents were found, change the action to not uninstall the package. - if (0 < cDependents) + if (pPackage->fDependencyManagerWasHere) { - LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS, pPackage->sczId, cDependents); - - for (DWORD i = 0; i < cDependents; ++i) - { - const DEPENDENCY* pDependency = &rgDependents[i]; - LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_DEPENDENT, pDependency->sczKey, LoggingStringOrUnknownIfNull(pDependency->sczName)); - } - - pPackage->fDependencyManagerWasHere = TRUE; pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; } - // Use the calculated dependency actions as the provider actions if there - // are any non-imported providers that need to be registered and the package - // is current (not obsolete). - else if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE != pPackage->currentState) + else { - BOOL fAllImportedProviders = TRUE; // assume all providers were imported. - for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + // Use the calculated dependency actions as the provider actions if there + // are any non-imported providers that need to be registered and the package + // is current (not obsolete). + if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE != pPackage->currentState) { - const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; - if (!pProvider->fImported) + BOOL fAllImportedProviders = TRUE; // assume all providers were imported. + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) { - fAllImportedProviders = FALSE; - break; + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + if (!pProvider->fImported) + { + fAllImportedProviders = FALSE; + break; + } } - } - if (!fAllImportedProviders) - { - pPackage->providerExecute = dependencyExecuteAction; - pPackage->providerRollback = dependencyRollbackAction; + if (!fAllImportedProviders) + { + pPackage->providerExecute = dependencyExecuteAction; + pPackage->providerRollback = dependencyRollbackAction; + } } - } - // If the package will be removed, add its providers to the growing list in the plan. - if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) - { - for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + // If the package will be removed, add its providers to the growing list in the plan. + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) { - const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; - hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, NULL); - ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey); + hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, NULL); + ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey); + } } } @@ -435,7 +483,6 @@ extern "C" HRESULT DependencyPlanPackageBegin( pPackage->dependencyRollback = dependencyRollbackAction; LExit: - ReleaseDependencyArray(rgDependents, cDependents); ReleaseDict(sdIgnoredDependents); return hr; @@ -652,6 +699,58 @@ extern "C" void DependencyUnregisterBundle( // internal functions + +static HRESULT DetectPackageDependents( + __in BURN_PACKAGE* pPackage, + __in STRINGDICT_HANDLE sdIgnoredDependents, + __in const BURN_REGISTRATION* pRegistration + ) +{ + HRESULT hr = S_OK; + HKEY hkHive = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + + // There's currently no point in getting the dependents if the scope doesn't match, + // because they will just get ignored. + if (pRegistration->fPerMachine != pPackage->fPerMachine) + { + ExitFunction(); + } + + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + + hr = DepCheckDependents(hkHive, pProvider->sczKey, 0, sdIgnoredDependents, &pProvider->rgDependents, &pProvider->cDependents); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed dependents check on package provider: %ls", pProvider->sczKey); + + if (!pPackage->fPackageProviderExists && (0 < pProvider->cDependents || GetProviderExists(hkHive, pProvider->sczKey))) + { + pPackage->fPackageProviderExists = TRUE; + } + } + else + { + hr = S_OK; + + if (!pPackage->fPackageProviderExists && GetProviderExists(hkHive, pProvider->sczKey)) + { + pPackage->fPackageProviderExists = TRUE; + } + } + } + + // Older bundles may not have written the id so try the default. + if (!pPackage->fPackageProviderExists && BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.sczProductCode && GetProviderExists(hkHive, pPackage->Msi.sczProductCode)) + { + pPackage->fPackageProviderExists = TRUE; + } + +LExit: + return hr; +} + /******************************************************************** SplitIgnoreDependencies - Splits a semicolon-delimited string into a list of unique dependencies to ignore. @@ -803,52 +902,16 @@ LExit: } /******************************************************************** - GetProviderId - Gets the ID of the package given the provider key. + GetProviderExists - Gets whether the provider key is registered. *********************************************************************/ -static HRESULT GetProviderInformation( +static BOOL GetProviderExists( __in HKEY hkRoot, - __in_z LPCWSTR wzProviderKey, - __deref_opt_out_z_opt LPWSTR* psczProviderKey, - __deref_opt_out_z_opt LPWSTR* psczId + __in_z LPCWSTR wzProviderKey ) { - HRESULT hr = S_OK; - LPWSTR sczId = NULL; - - hr = DepGetProviderInformation(hkRoot, wzProviderKey, &sczId, NULL, NULL); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get the provider key package id."); - - // If the id was registered return it and exit. - if (sczId && *sczId) - { - if (psczProviderKey) - { - hr = StrAllocString(psczProviderKey, wzProviderKey, 0); - ExitOnFailure(hr, "Failed to copy the provider key."); - } - - if (psczId) - { - *psczId = sczId; - sczId = NULL; - } - - ExitFunction(); - } - else - { - hr = E_NOTFOUND; - } - -LExit: - ReleaseStr(sczId); - - return hr; + HRESULT hr = DepGetProviderInformation(hkRoot, wzProviderKey, NULL, NULL, NULL); + return SUCCEEDED(hr); } /******************************************************************** @@ -886,7 +949,7 @@ static void CalculateDependencyActionStates( switch (pPackage->currentState) { case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: - if (!PackageProviderExists(pPackage)) + if (!pPackage->fPackageProviderExists) { break; } @@ -902,7 +965,7 @@ static void CalculateDependencyActionStates( switch (pPackage->currentState) { case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: - if (!PackageProviderExists(pPackage)) + if (!pPackage->fPackageProviderExists) { break; } @@ -1181,15 +1244,3 @@ static void UnregisterPackageDependency( } } } - -/******************************************************************** - PackageProviderExists - Checks if a package provider is registered. - -*********************************************************************/ -static BOOL PackageProviderExists( - __in const BURN_PACKAGE* pPackage - ) -{ - HRESULT hr = DependencyDetectProviderKeyPackageId(pPackage, NULL, NULL); - return SUCCEEDED(hr); -} diff --git a/src/engine/dependency.h b/src/engine/dependency.h index 905857e0..5390bede 100644 --- a/src/engine/dependency.h +++ b/src/engine/dependency.h @@ -14,11 +14,11 @@ const LPCWSTR DEPENDENCY_IGNOREDEPENDENCIES = L"IGNOREDEPENDENCIES"; // function declarations /******************************************************************** - DependencyUninitialize - Frees and zeros memory allocated in the - dependency. + DependencyUninitializeProvider - Frees and zeros memory allocated in + the dependency provider. *********************************************************************/ -void DependencyUninitialize( +void DependencyUninitializeProvider( __in BURN_DEPENDENCY_PROVIDER* pProvider ); @@ -32,16 +32,9 @@ HRESULT DependencyParseProvidersFromXml( __in IXMLDOMNode* pixnPackage ); -/******************************************************************** - DependencyDetectProviderKeyPackageId - Detect if the provider key is - registered and if so what package code is registered. - - Note: Returns E_NOTFOUND if the provider key is not registered. -*********************************************************************/ -HRESULT DependencyDetectProviderKeyPackageId( - __in const BURN_PACKAGE* pPackage, - __deref_opt_out_z_opt LPWSTR* psczProviderKey, - __deref_opt_out_z_opt LPWSTR* psczId +HRESULT DependencyInitialize( + __in BURN_REGISTRATION* pRegistration, + __in_z_opt LPCWSTR wzIgnoreDependencies ); /******************************************************************** @@ -54,12 +47,20 @@ HRESULT DependencyDetectProviderKeyBundleId( __in BURN_REGISTRATION* pRegistration ); +/******************************************************************** + DependencyDetect - Detects dependency information. + +*********************************************************************/ +HRESULT DependencyDetect( + __in BURN_ENGINE_STATE* pEngineState + ); + /******************************************************************** DependencyPlanInitialize - Initializes the plan. *********************************************************************/ HRESULT DependencyPlanInitialize( - __in const BURN_ENGINE_STATE* pEngineState, + __in const BURN_REGISTRATION* pRegistration, __in BURN_PLAN* pPlan ); diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp index 63e66539..3b8d63e2 100644 --- a/src/engine/detect.cpp +++ b/src/engine/detect.cpp @@ -41,12 +41,28 @@ extern "C" void DetectReset( ReleaseNullStr(pRegistration->sczDetectedProviderKeyBundleId); pRegistration->fEnabledForwardCompatibleBundle = FALSE; PackageUninitialize(&pRegistration->forwardCompatibleBundle); + pRegistration->fSelfRegisteredAsDependent = FALSE; + + if (pRegistration->rgIgnoredDependencies) + { + ReleaseDependencyArray(pRegistration->rgIgnoredDependencies, pRegistration->cIgnoredDependencies); + } + pRegistration->rgIgnoredDependencies = NULL; + pRegistration->cIgnoredDependencies = 0; + + if (pRegistration->rgDependents) + { + ReleaseDependencyArray(pRegistration->rgDependents, pRegistration->cDependents); + } + pRegistration->rgDependents = NULL; + pRegistration->cDependents = 0; for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage) { BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage; pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; + pPackage->fPackageProviderExists = FALSE; pPackage->cache = BURN_CACHE_STATE_NONE; for (DWORD iPayload = 0; iPayload < pPackage->cPayloads; ++iPayload) @@ -69,6 +85,18 @@ extern "C" void DetectReset( ReleaseNullMem(pPackage->Msp.rgTargetProducts); pPackage->Msp.cTargetProductCodes = 0; } + + for (DWORD iProvider = 0; iProvider < pPackage->cDependencyProviders; ++iProvider) + { + BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + iProvider; + + if (pProvider->rgDependents) + { + ReleaseDependencyArray(pProvider->rgDependents, pProvider->cDependents); + } + pProvider->rgDependents = NULL; + pProvider->cDependents = 0; + } } for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo) diff --git a/src/engine/engine.mc b/src/engine/engine.mc index ad86308c..d6e9751a 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -356,7 +356,7 @@ MessageId=210 Severity=Warning SymbolicName=MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS Language=English -Plan skipped due to %1!u! remaining dependents +Plan skipped due to remaining dependents: . MessageId=211 @@ -595,7 +595,7 @@ MessageId=327 Severity=Warning SymbolicName=MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS Language=English -Will not uninstall package: %1!ls!, found dependents: %2!d! +Will not uninstall package: %1!ls!, found dependents: . MessageId=328 @@ -640,6 +640,13 @@ Language=English Could not remove bundle dependency provider: %1!ls!, error: 0x%2!x! . +MessageId=334 +Severity=Warning +SymbolicName=MSG_DEPENDENCY_BUNDLE_DEPENDENT +Language=English +Found dependent: %1!ls!, name: %2!ls! +. + MessageId=335 Severity=Success SymbolicName=MSG_ACQUIRE_BUNDLE_PAYLOAD diff --git a/src/engine/package.cpp b/src/engine/package.cpp index 527766eb..701dda08 100644 --- a/src/engine/package.cpp +++ b/src/engine/package.cpp @@ -323,7 +323,7 @@ extern "C" void PackageUninitialize( { for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) { - DependencyUninitialize(pPackage->rgDependencyProviders + i); + DependencyUninitializeProvider(pPackage->rgDependencyProviders + i); } MemFree(pPackage->rgDependencyProviders); } diff --git a/src/engine/package.h b/src/engine/package.h index 1a4f7060..8f801e85 100644 --- a/src/engine/package.h +++ b/src/engine/package.h @@ -159,6 +159,9 @@ typedef struct _BURN_DEPENDENCY_PROVIDER LPWSTR sczVersion; LPWSTR sczDisplayName; BOOL fImported; + + DEPENDENCY* rgDependents; // only valid after Detect. + UINT cDependents; // only valid after Detect. } BURN_DEPENDENCY_PROVIDER; typedef struct _BURN_ROLLBACK_BOUNDARY @@ -199,6 +202,7 @@ typedef struct _BURN_PACKAGE BOOTSTRAPPER_PACKAGE_STATE currentState; // only valid after Detect. BURN_CACHE_STATE cache; // only valid after Detect. + BOOL fPackageProviderExists; // only valid after Detect. BOOTSTRAPPER_REQUEST_STATE defaultRequested;// only valid during Plan. BOOTSTRAPPER_REQUEST_STATE requested; // only valid during Plan. BOOL fAcquire; // only valid during Plan. diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index a45eab62..6f5407b9 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -574,15 +574,12 @@ extern "C" HRESULT PlanRegistration( __in BURN_REGISTRATION* pRegistration, __in BOOTSTRAPPER_RESUME_TYPE resumeType, __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in_z_opt LPCWSTR wzIgnoreDependencies, __out BOOL* pfContinuePlanning ) { HRESULT hr = S_OK; - LPCWSTR wzSelfDependent = NULL; + STRINGDICT_HANDLE sdBundleDependents = NULL; STRINGDICT_HANDLE sdIgnoreDependents = NULL; - DEPENDENCY* rgDependencies = NULL; - UINT cDependencies = 0; pPlan->fRegister = TRUE; // register the bundle since we're modifying machine state. @@ -591,17 +588,6 @@ extern "C" HRESULT PlanRegistration( pPlan->fDisallowRemoval = FALSE; // by default the bundle can be planned to be removed - // If no parent was specified at all, use the bundle id as the self dependent. - if (!pRegistration->sczActiveParent) - { - wzSelfDependent = pRegistration->sczId; - } - else if (*pRegistration->sczActiveParent) // if parent was specified use that as the self dependent. - { - wzSelfDependent = pRegistration->sczActiveParent; - } - // else parent:none was used which means we should not register a dependency on ourself. - if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) { // If our provider key was detected and it points to our current bundle then we can @@ -622,12 +608,12 @@ extern "C" HRESULT PlanRegistration( // If the self-dependent dependent exists, plan its removal. If we did not do this, we // would prevent self-removal. - if (wzSelfDependent && DependencyDependentExists(pRegistration, wzSelfDependent)) + if (pRegistration->fSelfRegisteredAsDependent) { - hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER, wzSelfDependent, pRegistration->sczId); + hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER, pRegistration->wzSelfDependent, pRegistration->sczId); ExitOnFailure(hr, "Failed to allocate registration action."); - hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, wzSelfDependent); + hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, pRegistration->wzSelfDependent); ExitOnFailure(hr, "Failed to add self-dependent to ignore dependents."); } @@ -637,10 +623,20 @@ extern "C" HRESULT PlanRegistration( if (BOOTSTRAPPER_RELATION_UPGRADE != relationType) { // If there were other dependencies to ignore, add them. - if (wzIgnoreDependencies && *wzIgnoreDependencies) + for (DWORD iDependency = 0; iDependency < pRegistration->cIgnoredDependencies; ++iDependency) { - hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, wzIgnoreDependencies); - ExitOnFailure(hr, "Failed to add dependents ignored from command-line."); + DEPENDENCY* pDependency = pRegistration->rgIgnoredDependencies + iDependency; + + hr = DictKeyExists(sdIgnoreDependents, pDependency->sczKey); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to check the dictionary of ignored dependents."); + } + else + { + hr = DictAddKey(sdIgnoreDependents, pDependency->sczKey); + ExitOnFailure(hr, "Failed to add dependent key to ignored dependents."); + } } // For addon or patch bundles, dependent related bundles should be ignored. This allows @@ -661,22 +657,29 @@ extern "C" HRESULT PlanRegistration( } } - // If there are any (non-ignored and not-planned-to-be-removed) dependents left, uninstall. - hr = DepCheckDependents(pRegistration->hkRoot, pRegistration->sczProviderKey, 0, sdIgnoreDependents, &rgDependencies, &cDependencies); - if (E_FILENOTFOUND == hr) + // If there are any (non-ignored and not-planned-to-be-removed) dependents left, skip planning. + for (DWORD iDependent = 0; iDependent < pRegistration->cDependents; ++iDependent) { - hr = S_OK; - } - else if (SUCCEEDED(hr) && cDependencies) - { - // TODO: callback to the BA and let it have the option to ignore any of these dependents? + DEPENDENCY* pDependent = pRegistration->rgDependents + iDependent; + + hr = DictKeyExists(sdIgnoreDependents, pDependent->sczKey); + if (E_NOTFOUND == hr) + { + hr = S_OK; - pPlan->fDisallowRemoval = TRUE; // ensure the registration stays - *pfContinuePlanning = FALSE; // skip the rest of planning. + // TODO: callback to the BA and let it have the option to ignore this dependent? + if (!pPlan->fDisallowRemoval) + { + pPlan->fDisallowRemoval = TRUE; // ensure the registration stays + *pfContinuePlanning = FALSE; // skip the rest of planning. + + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS); + } - LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS, cDependencies); + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_DEPENDENT, pDependent->sczKey, LoggingStringOrUnknownIfNull(pDependent->sczName)); + } + ExitOnFailure(hr, "Failed to check for remaining dependents during planning."); } - ExitOnFailure(hr, "Failed to check for remaining dependents during planning."); } } else @@ -707,6 +710,23 @@ extern "C" HRESULT PlanRegistration( // if broken. pPlan->dependencyRegistrationAction = BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER; + // Create the dictionary of bundle dependents. + hr = DictCreateStringList(&sdBundleDependents, 5, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create the string dictionary."); + + for (DWORD iDependent = 0; iDependent < pRegistration->cDependents; ++iDependent) + { + DEPENDENCY* pDependent = pRegistration->rgDependents + iDependent; + + hr = DictKeyExists(sdBundleDependents, pDependent->sczKey); + if (E_NOTFOUND == hr) + { + hr = DictAddKey(sdBundleDependents, pDependent->sczKey); + ExitOnFailure(hr, "Failed to add dependent key to bundle dependents."); + } + ExitOnFailure(hr, "Failed to check the dictionary of bundle dependents."); + } + // Register each dependent related bundle. The ensures that addons and patches are reference // counted and stick around until the last targeted bundle is removed. for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) @@ -719,11 +739,16 @@ extern "C" HRESULT PlanRegistration( { const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j; - if (!DependencyDependentExists(pRegistration, pProvider->sczKey)) + hr = DictKeyExists(sdBundleDependents, pProvider->sczKey); + if (E_NOTFOUND == hr) { + hr = DictAddKey(sdBundleDependents, pProvider->sczKey); + ExitOnFailure(hr, "Failed to add new dependent key to bundle dependents."); + hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, pProvider->sczKey, pRelatedBundle->package.sczId); ExitOnFailure(hr, "Failed to add registration action for dependent related bundle."); } + ExitOnFailure(hr, "Failed to check the dictionary of bundle dependents."); } } } @@ -731,19 +756,16 @@ extern "C" HRESULT PlanRegistration( // Only do the following if we decided there was a dependent self to register. If so and and an explicit parent was // provided, register dependent self. Otherwise, if this bundle is not an addon or patch bundle then self-regisiter // as our own dependent. - if (wzSelfDependent && (pRegistration->sczActiveParent || !fAddonOrPatchBundle)) + if (pRegistration->wzSelfDependent && !pRegistration->fSelfRegisteredAsDependent && (pRegistration->sczActiveParent || !fAddonOrPatchBundle)) { - if (!DependencyDependentExists(pRegistration, wzSelfDependent)) - { - hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, wzSelfDependent, pRegistration->sczId); - ExitOnFailure(hr, "Failed to add registration action for self dependent."); - } + hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, pRegistration->wzSelfDependent, pRegistration->sczId); + ExitOnFailure(hr, "Failed to add registration action for self dependent."); } } LExit: + ReleaseDict(sdBundleDependents); ReleaseDict(sdIgnoreDependents); - ReleaseDependencyArray(rgDependencies, cDependencies); return hr; } diff --git a/src/engine/plan.h b/src/engine/plan.h index cb64f891..54189973 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -418,7 +418,6 @@ HRESULT PlanRegistration( __in BURN_REGISTRATION* pRegistration, __in BOOTSTRAPPER_RESUME_TYPE resumeType, __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in_z_opt LPCWSTR wzIgnoreDependencies, __out BOOL* pfContinuePlanning ); HRESULT PlanPassThroughBundle( diff --git a/src/engine/registration.h b/src/engine/registration.h index dc9bc5b7..c1e52ac9 100644 --- a/src/engine/registration.h +++ b/src/engine/registration.h @@ -139,8 +139,13 @@ typedef struct _BURN_REGISTRATION // Update registration BURN_UPDATE_REGISTRATION update; - // Only valid after detect. - BURN_RELATED_BUNDLES relatedBundles; + BURN_RELATED_BUNDLES relatedBundles; // Only valid after detect. + DEPENDENCY* rgIgnoredDependencies; // Only valid after detect. + UINT cIgnoredDependencies; // Only valid after detect. + DEPENDENCY* rgDependents; // Only valid after detect. + UINT cDependents; // Only valid after detect. + LPCWSTR wzSelfDependent; // Only valid after detect. + BOOL fSelfRegisteredAsDependent; // Only valid after detect. LPWSTR sczDetectedProviderKeyBundleId; LPWSTR sczAncestors; diff --git a/src/engine/relatedbundle.cpp b/src/engine/relatedbundle.cpp index 7b0da4a4..bc79b954 100644 --- a/src/engine/relatedbundle.cpp +++ b/src/engine/relatedbundle.cpp @@ -459,7 +459,7 @@ static HRESULT LoadRelatedBundleFromKey( ExitOnFailure(hr, "Failed to initialize related bundle to represent bundle: %ls", wzRelatedBundleId); LExit: - DependencyUninitialize(&dependencyProvider); + DependencyUninitializeProvider(&dependencyProvider); ReleaseStr(sczCachePath); ReleaseStr(sczBundleVersion); diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp index 71e455c3..fb4ca246 100644 --- a/src/test/BurnUnitTest/PlanTest.cpp +++ b/src/test/BurnUnitTest/PlanTest.cpp @@ -552,9 +552,23 @@ namespace Bootstrapper ReleaseStr(sczFilePath); } + DependencyInitialize(&pEngineState->registration, NULL); + pEngineState->userExperience.pfnBAProc = PlanTestBAProc; } + void PlanTestDetect(BURN_ENGINE_STATE* pEngineState) + { + HRESULT hr = S_OK; + BURN_REGISTRATION* pRegistration = &pEngineState->registration; + + DetectReset(pRegistration, &pEngineState->packages); + PlanReset(&pEngineState->plan, &pEngineState->packages); + + hr = DepDependencyArrayAlloc(&pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies, pRegistration->sczProviderKey, NULL); + NativeAssert::Succeeded(hr, "Failed to add the bundle provider key to the list of dependencies to ignore."); + } + void DetectAttachedContainerAsAttached(BURN_ENGINE_STATE* pEngineState) { for (DWORD i = 0; i < pEngineState->containers.cContainers; ++i) @@ -585,8 +599,7 @@ namespace Bootstrapper void DetectPackagesAsAbsent(BURN_ENGINE_STATE* pEngineState) { - DetectReset(&pEngineState->registration, &pEngineState->packages); - PlanReset(&pEngineState->plan, &pEngineState->packages); + PlanTestDetect(pEngineState); for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) { @@ -597,8 +610,7 @@ namespace Bootstrapper void DetectPackagesAsPresentAndCached(BURN_ENGINE_STATE* pEngineState) { - DetectReset(&pEngineState->registration, &pEngineState->packages); - PlanReset(&pEngineState->plan, &pEngineState->packages); + PlanTestDetect(pEngineState); pEngineState->registration.fInstalled = TRUE; @@ -611,8 +623,7 @@ namespace Bootstrapper void DetectPermanentPackagesAsPresentAndCached(BURN_ENGINE_STATE* pEngineState) { - DetectReset(&pEngineState->registration, &pEngineState->packages); - PlanReset(&pEngineState->plan, &pEngineState->packages); + PlanTestDetect(pEngineState); pEngineState->registration.fInstalled = TRUE; -- cgit v1.2.3-55-g6feb From bb7d4bdc09d0b52a65b8cf3b5ae629f385fc8011 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 2 Feb 2021 16:56:57 -0600 Subject: Clean up synchronization between the engine and the BA. --- src/engine/apply.cpp | 5 +-- src/engine/core.cpp | 47 ++++----------------- src/engine/core.h | 5 --- src/engine/detect.cpp | 4 +- src/engine/engine.cpp | 7 ++-- src/engine/engine.mc | 2 +- src/engine/externalengine.cpp | 21 +++++----- src/engine/userexperience.cpp | 83 ++++++++++++++++++++------------------ src/engine/userexperience.h | 12 ++++-- src/test/BurnUnitTest/PlanTest.cpp | 3 +- 10 files changed, 80 insertions(+), 109 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index dd4932aa..76831461 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -366,7 +366,7 @@ extern "C" HRESULT ApplyRegister( hr = CoreSaveEngineState(pEngineState); if (FAILED(hr)) { - LogErrorId(hr, MSG_STATE_NOT_SAVED, NULL, NULL, NULL); + LogErrorId(hr, MSG_STATE_NOT_SAVED); hr = S_OK; } @@ -1348,8 +1348,6 @@ static HRESULT PromptForSource( HRESULT hr = S_OK; BOOTSTRAPPER_RESOLVESOURCE_ACTION action = BOOTSTRAPPER_RESOLVESOURCE_ACTION_NONE; - UserExperienceDeactivateEngine(pUX); - hr = UserExperienceOnResolveSource(pUX, wzPackageOrContainerId, wzPayloadId, wzLocalSource, wzDownloadSource, &action); if (FAILED(hr)) { @@ -1376,7 +1374,6 @@ static HRESULT PromptForSource( } LExit: - UserExperienceActivateEngine(pUX, NULL); return hr; } diff --git a/src/engine/core.cpp b/src/engine/core.cpp index ae09ea65..a644d377 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -236,16 +236,12 @@ extern "C" HRESULT CoreDetect( ) { HRESULT hr = S_OK; - BOOL fActivated = FALSE; BOOL fDetectBegan = FALSE; BURN_PACKAGE* pPackage = NULL; HRESULT hrFirstPackageFailure = S_OK; LogId(REPORT_STANDARD, MSG_DETECT_BEGIN, pEngineState->packages.cPackages); - hr = UserExperienceActivateEngine(&pEngineState->userExperience, &fActivated); - ExitOnFailure(hr, "Engine cannot start detect because it is busy with another action."); - // Detect if bundle installed state has changed since start up. This // only happens if Apply() changed the state of bundle (installed or // uninstalled). In that case, Detect() can be used here to reset @@ -369,11 +365,6 @@ LExit: hr = hrFirstPackageFailure; } - if (fActivated) - { - UserExperienceDeactivateEngine(&pEngineState->userExperience); - } - if (fDetectBegan) { UserExperienceOnDetectComplete(&pEngineState->userExperience, hr); @@ -392,7 +383,6 @@ extern "C" HRESULT CorePlan( ) { HRESULT hr = S_OK; - BOOL fActivated = FALSE; BOOL fPlanBegan = FALSE; LPWSTR sczLayoutDirectory = NULL; HANDLE hSyncpointEvent = NULL; @@ -401,9 +391,6 @@ extern "C" HRESULT CorePlan( LogId(REPORT_STANDARD, MSG_PLAN_BEGIN, pEngineState->packages.cPackages, LoggingBurnActionToString(action)); - hr = UserExperienceActivateEngine(&pEngineState->userExperience, &fActivated); - ExitOnFailure(hr, "Engine cannot start plan because it is busy with another action."); - fPlanBegan = TRUE; hr = UserExperienceOnPlanBegin(&pEngineState->userExperience, pEngineState->packages.cPackages); ExitOnRootFailure(hr, "BA aborted plan begin."); @@ -496,11 +483,6 @@ extern "C" HRESULT CorePlan( PlanDump(&pEngineState->plan); LExit: - if (fActivated) - { - UserExperienceDeactivateEngine(&pEngineState->userExperience); - } - if (fPlanBegan) { UserExperienceOnPlanComplete(&pEngineState->userExperience, hr); @@ -551,10 +533,10 @@ extern "C" HRESULT CoreApply( ) { HRESULT hr = S_OK; - BOOL fActivated = FALSE; HANDLE hLock = NULL; DWORD cOverallProgressTicks = 0; HANDLE hCacheThread = NULL; + BOOL fApplyInitialize = FALSE; BOOL fElevated = FALSE; BOOL fRegistered = FALSE; BOOL fKeepRegistration = pEngineState->plan.fKeepRegistrationDefault; @@ -567,9 +549,6 @@ extern "C" HRESULT CoreApply( LogId(REPORT_STANDARD, MSG_APPLY_BEGIN); - hr = UserExperienceActivateEngine(&pEngineState->userExperience, &fActivated); - ExitOnFailure(hr, "Engine cannot start apply because it is busy with another action."); - // Ensure any previous attempts to execute are reset. ApplyReset(&pEngineState->userExperience, &pEngineState->packages); @@ -599,6 +578,7 @@ extern "C" HRESULT CoreApply( ExitOnFailure(hr, "Another per-user setup is already executing."); // Initialize only after getting a lock. + fApplyInitialize = TRUE; ApplyInitialize(); pEngineState->userExperience.hwndApply = hwndParent; @@ -627,7 +607,7 @@ extern "C" HRESULT CoreApply( ExitOnFailure(hr, "Failed to elevate."); hr = ElevationApplyInitialize(pEngineState->companionConnection.hPipe, &pEngineState->userExperience, &pEngineState->variables, pEngineState->plan.action, pEngineState->automaticUpdates, !pEngineState->fDisableSystemRestore); - ExitOnFailure(hr, "Another per-machine setup is already executing."); + ExitOnFailure(hr, "Failed to initialize apply in elevated process."); fElevated = TRUE; } @@ -704,7 +684,10 @@ LExit: pEngineState->userExperience.hwndApply = NULL; - ApplyUninitialize(); + if (fApplyInitialize) + { + ApplyUninitialize(); + } if (hLock) { @@ -712,11 +695,6 @@ LExit: ::CloseHandle(hLock); } - if (fActivated) - { - UserExperienceDeactivateEngine(&pEngineState->userExperience); - } - ReleaseHandle(hCacheThread); UserExperienceOnApplyComplete(&pEngineState->userExperience, hr, restart, &applyCompleteAction); @@ -736,14 +714,10 @@ extern "C" HRESULT CoreLaunchApprovedExe( ) { HRESULT hr = S_OK; - BOOL fActivated = FALSE; DWORD dwProcessId = 0; LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_BEGIN, pLaunchApprovedExe->sczId); - hr = UserExperienceActivateEngine(&pEngineState->userExperience, &fActivated); - ExitOnFailure(hr, "Engine cannot start LaunchApprovedExe because it is busy with another action."); - hr = UserExperienceOnLaunchApprovedExeBegin(&pEngineState->userExperience); ExitOnRootFailure(hr, "BA aborted LaunchApprovedExe begin."); @@ -755,11 +729,6 @@ extern "C" HRESULT CoreLaunchApprovedExe( hr = ElevationLaunchApprovedExe(pEngineState->companionConnection.hPipe, pLaunchApprovedExe, &dwProcessId); LExit: - if (fActivated) - { - UserExperienceDeactivateEngine(&pEngineState->userExperience); - } - UserExperienceOnLaunchApprovedExeComplete(&pEngineState->userExperience, hr, dwProcessId); LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_COMPLETE, hr, dwProcessId); @@ -782,7 +751,7 @@ extern "C" HRESULT CoreQuit( hr = CoreSaveEngineState(pEngineState); if (FAILED(hr)) { - LogErrorId(hr, MSG_STATE_NOT_SAVED, NULL, NULL, NULL); + LogErrorId(hr, MSG_STATE_NOT_SAVED); hr = S_OK; } } diff --git a/src/engine/core.h b/src/engine/core.h index 0161eaa0..fae4bfe5 100644 --- a/src/engine/core.h +++ b/src/engine/core.h @@ -77,11 +77,6 @@ enum BURN_AU_PAUSE_ACTION typedef struct _BURN_ENGINE_STATE { - // synchronization - CRITICAL_SECTION csActive; // Any call from the UX that reads or alters the engine state - // needs to be syncronized through this critical section. - // Note: The engine must never do a UX callback while in this critical section. - // UX flow control //BOOL fSuspend; // Is TRUE when UX made Suspend() call on core. //BOOL fForcedReboot; // Is TRUE when UX made Reboot() call on core. diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp index 3b8d63e2..ff6ba4d7 100644 --- a/src/engine/detect.cpp +++ b/src/engine/detect.cpp @@ -9,7 +9,7 @@ typedef struct _DETECT_AUTHENTICATION_REQUIRED_DATA } DETECT_AUTHENTICATION_REQUIRED_DATA; // internal function definitions -static HRESULT AuthenticationRequired( +static HRESULT WINAPI AuthenticationRequired( __in LPVOID pData, __in HINTERNET hUrl, __in long lHttpCode, @@ -288,7 +288,7 @@ LExit: return hr; } -static HRESULT AuthenticationRequired( +static HRESULT WINAPI AuthenticationRequired( __in LPVOID pData, __in HINTERNET hUrl, __in long lHttpCode, diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 9eee7fde..2c6bad03 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -330,7 +330,6 @@ static HRESULT InitializeEngineState( pEngineState->automaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED; pEngineState->dwElevatedLoggingTlsId = TLS_OUT_OF_INDEXES; - ::InitializeCriticalSection(&pEngineState->csActive); ::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive); PipeConnectionInitialize(&pEngineState->companionConnection); PipeConnectionInitialize(&pEngineState->embeddedConnection); @@ -418,8 +417,6 @@ static void UninitializeEngineState( ::TlsFree(pEngineState->dwElevatedLoggingTlsId); } - ::DeleteCriticalSection(&pEngineState->csActive); - // clear struct memset(pEngineState, 0, sizeof(BURN_ENGINE_STATE)); } @@ -805,6 +802,8 @@ static HRESULT ProcessMessage( { HRESULT hr = S_OK; + UserExperienceActivateEngine(&pEngineState->userExperience); + switch (pmsg->message) { case WM_BURN_DETECT: @@ -832,6 +831,8 @@ static HRESULT ProcessMessage( break; } + UserExperienceDeactivateEngine(&pEngineState->userExperience); + return hr; } diff --git a/src/engine/engine.mc b/src/engine/engine.mc index d6e9751a..b120c5bb 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -938,7 +938,7 @@ MessageId=501 Severity=Warning SymbolicName=MSG_STATE_NOT_SAVED Language=English -The state file could not be saved, error: 0x%1!x!. Continuing... +The state file could not be saved. Continuing... . MessageId=600 diff --git a/src/engine/externalengine.cpp b/src/engine/externalengine.cpp index 39878c8b..f9a06437 100644 --- a/src/engine/externalengine.cpp +++ b/src/engine/externalengine.cpp @@ -276,7 +276,9 @@ HRESULT ExternalEngineSetUpdate( WCHAR wzGuid[39]; RPC_STATUS rs = RPC_S_OK; - ::EnterCriticalSection(&pEngineState->csActive); + ::EnterCriticalSection(&pEngineState->userExperience.csEngineActive); + hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience); + ExitOnFailure(hr, "Engine is active, cannot change engine state."); if ((!wzLocalSource || !*wzLocalSource) && (!wzDownloadSource || !*wzDownloadSource)) { @@ -332,7 +334,7 @@ HRESULT ExternalEngineSetUpdate( } LExit: - ::LeaveCriticalSection(&pEngineState->csActive); + ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive); ReleaseStr(sczCommandline); ReleaseStr(sczLocalSource); @@ -351,7 +353,7 @@ HRESULT ExternalEngineSetLocalSource( BURN_CONTAINER* pContainer = NULL; BURN_PAYLOAD* pPayload = NULL; - ::EnterCriticalSection(&pEngineState->csActive); + ::EnterCriticalSection(&pEngineState->userExperience.csEngineActive); hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience); ExitOnFailure(hr, "Engine is active, cannot change engine state."); @@ -387,7 +389,7 @@ HRESULT ExternalEngineSetLocalSource( } LExit: - ::LeaveCriticalSection(&pEngineState->csActive); + ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive); return hr; } @@ -406,7 +408,7 @@ HRESULT ExternalEngineSetDownloadSource( BURN_PAYLOAD* pPayload = NULL; DOWNLOAD_SOURCE* pDownloadSource = NULL; - ::EnterCriticalSection(&pEngineState->csActive); + ::EnterCriticalSection(&pEngineState->userExperience.csEngineActive); hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience); ExitOnFailure(hr, "Engine is active, cannot change engine state."); @@ -470,7 +472,7 @@ HRESULT ExternalEngineSetDownloadSource( } LExit: - ::LeaveCriticalSection(&pEngineState->csActive); + ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive); return hr; } @@ -686,7 +688,7 @@ HRESULT ExternalEngineLaunchApprovedExe( pLaunchApprovedExe = (BURN_LAUNCH_APPROVED_EXE*)MemAlloc(sizeof(BURN_LAUNCH_APPROVED_EXE), TRUE); ExitOnNull(pLaunchApprovedExe, hr, E_OUTOFMEMORY, "Failed to alloc BURN_LAUNCH_APPROVED_EXE"); - ::EnterCriticalSection(&pEngineState->csActive); + ::EnterCriticalSection(&pEngineState->userExperience.csEngineActive); fLeaveCriticalSection = TRUE; hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience); ExitOnFailure(hr, "Engine is active, cannot change engine state."); @@ -699,9 +701,6 @@ HRESULT ExternalEngineLaunchApprovedExe( hr = ApprovedExesFindById(&pEngineState->approvedExes, wzApprovedExeForElevationId, &pApprovedExe); ExitOnFailure(hr, "BA requested unknown approved exe with id: %ls", wzApprovedExeForElevationId); - ::LeaveCriticalSection(&pEngineState->csActive); - fLeaveCriticalSection = FALSE; - hr = StrAllocString(&pLaunchApprovedExe->sczId, wzApprovedExeForElevationId, NULL); ExitOnFailure(hr, "Failed to copy the id."); @@ -723,7 +722,7 @@ HRESULT ExternalEngineLaunchApprovedExe( LExit: if (fLeaveCriticalSection) { - ::LeaveCriticalSection(&pEngineState->csActive); + ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive); } if (FAILED(hr)) diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index ea8b8a19..3a36cab6 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -24,6 +24,13 @@ static HRESULT SendBAMessage( __inout LPVOID pvResults ); +static HRESULT SendBAMessageFromInactiveEngine( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOTSTRAPPER_APPLICATION_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ); + // function definitions @@ -230,51 +237,31 @@ extern "C" int UserExperienceSendError( return nResult; } -extern "C" HRESULT UserExperienceActivateEngine( - __in BURN_USER_EXPERIENCE* pUserExperience, - __out_opt BOOL* pfActivated +extern "C" void UserExperienceActivateEngine( + __in BURN_USER_EXPERIENCE* pUserExperience ) { - HRESULT hr = S_OK; - BOOL fActivated; - ::EnterCriticalSection(&pUserExperience->csEngineActive); - if (InterlockedCompareExchange(reinterpret_cast(&pUserExperience->fEngineActive), TRUE, FALSE)) - { - AssertSz(FALSE, "Engine should have been deactivated before activating it."); - - fActivated = FALSE; - hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE); - } - else - { - fActivated = TRUE; - } + AssertSz(!pUserExperience->fEngineActive, "Engine should have been deactivated before activating it."); + pUserExperience->fEngineActive = TRUE; ::LeaveCriticalSection(&pUserExperience->csEngineActive); - - if (pfActivated) - { - *pfActivated = fActivated; - } - ExitOnRootFailure(hr, "Engine active cannot be changed because it was already in that state."); - -LExit: - return hr; } extern "C" void UserExperienceDeactivateEngine( __in BURN_USER_EXPERIENCE* pUserExperience ) { - BOOL fActive = InterlockedExchange(reinterpret_cast(&pUserExperience->fEngineActive), FALSE); - fActive = fActive; // prevents warning in "ship" build. - AssertSz(fActive, "Engine should have be active before deactivating it."); + ::EnterCriticalSection(&pUserExperience->csEngineActive); + AssertSz(pUserExperience->fEngineActive, "Engine should have been active before deactivating it."); + pUserExperience->fEngineActive = FALSE; + ::LeaveCriticalSection(&pUserExperience->csEngineActive); } extern "C" HRESULT UserExperienceEnsureEngineInactive( __in BURN_USER_EXPERIENCE* pUserExperience ) { + // Make a slight optimization here by ignoring the critical section, because all callers should have needed to enter it for their operation anyway. HRESULT hr = pUserExperience->fEngineActive ? HRESULT_FROM_WIN32(ERROR_BUSY) : S_OK; ExitOnRootFailure(hr, "Engine is active, cannot proceed."); @@ -346,7 +333,7 @@ EXTERN_C BAAPI UserExperienceOnApplyComplete( results.cbSize = sizeof(results); results.action = *pAction; - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYCOMPLETE, &args, &results); + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYCOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnApplyComplete failed."); *pAction = results.action; @@ -761,7 +748,7 @@ EXTERN_C BAAPI UserExperienceOnDetectComplete( results.cbSize = sizeof(results); - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPLETE, &args, &results); + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnDetectComplete failed."); LExit: @@ -990,7 +977,7 @@ LExit: EXTERN_C BAAPI UserExperienceOnDetectUpdate( __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzUpdateLocation, + __in_z_opt LPCWSTR wzUpdateLocation, __in DWORD64 dw64Size, __in VERUTIL_VERSION* pVersion, __in_z_opt LPCWSTR wzTitle, @@ -1016,7 +1003,7 @@ EXTERN_C BAAPI UserExperienceOnDetectUpdate( results.cbSize = sizeof(results); results.fStopProcessingUpdates = *pfStopProcessingUpdates; - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATE, &args, &results); + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATE, &args, &results); ExitOnFailure(hr, "BA OnDetectUpdate failed."); if (results.fCancel) @@ -1045,7 +1032,7 @@ EXTERN_C BAAPI UserExperienceOnDetectUpdateBegin( results.cbSize = sizeof(results); results.fSkip = *pfSkip; - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATEBEGIN, &args, &results); + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATEBEGIN, &args, &results); ExitOnFailure(hr, "BA OnDetectUpdateBegin failed."); if (results.fCancel) @@ -1074,7 +1061,7 @@ EXTERN_C BAAPI UserExperienceOnDetectUpdateComplete( results.cbSize = sizeof(results); results.fIgnoreError = *pfIgnoreError; - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATECOMPLETE, &args, &results); + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATECOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnDetectUpdateComplete failed."); if (FAILED(hrStatus)) @@ -1124,7 +1111,7 @@ EXTERN_C BAAPI UserExperienceOnElevateComplete( results.cbSize = sizeof(results); - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATECOMPLETE, &args, &results); + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATECOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnElevateComplete failed."); LExit: @@ -1452,7 +1439,7 @@ EXTERN_C BAAPI UserExperienceOnLaunchApprovedExeComplete( results.cbSize = sizeof(results); - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXECOMPLETE, &args, &results); + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXECOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnLaunchApprovedExeComplete failed."); LExit: @@ -1571,7 +1558,7 @@ EXTERN_C BAAPI UserExperienceOnPlanComplete( results.cbSize = sizeof(results); - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPLETE, &args, &results); + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnPlanComplete failed."); LExit: @@ -1831,7 +1818,7 @@ EXTERN_C BAAPI UserExperienceOnResolveSource( results.cbSize = sizeof(results); results.action = *pAction; - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONRESOLVESOURCE, &args, &results); + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONRESOLVESOURCE, &args, &results); ExitOnFailure(hr, "BA OnResolveSource failed."); if (results.fCancel) @@ -2317,3 +2304,21 @@ static HRESULT SendBAMessage( return hr; } + +static HRESULT SendBAMessageFromInactiveEngine( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOTSTRAPPER_APPLICATION_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + + UserExperienceDeactivateEngine(pUserExperience); + + hr = SendBAMessage(pUserExperience, message, pvArgs, pvResults); + + UserExperienceActivateEngine(pUserExperience); + + return hr; +} diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index 3176e762..f51c09ff 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -82,13 +82,17 @@ int UserExperienceSendError( __in DWORD uiFlags, __in int nRecommendation ); -HRESULT UserExperienceActivateEngine( - __in BURN_USER_EXPERIENCE* pUserExperience, - __out_opt BOOL* pfActivated +void UserExperienceActivateEngine( + __in BURN_USER_EXPERIENCE* pUserExperience ); void UserExperienceDeactivateEngine( __in BURN_USER_EXPERIENCE* pUserExperience ); +/******************************************************************** + UserExperienceEnsureEngineInactive - Verifies the engine is inactive. + The caller MUST enter the csActive critical section before calling. + +*********************************************************************/ HRESULT UserExperienceEnsureEngineInactive( __in BURN_USER_EXPERIENCE* pUserExperience ); @@ -240,7 +244,7 @@ BAAPI UserExperienceOnDetectTargetMsiPackage( ); BAAPI UserExperienceOnDetectUpdate( __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzUpdateLocation, + __in_z_opt LPCWSTR wzUpdateLocation, __in DWORD64 dw64Size, __in VERUTIL_VERSION* pVersion, __in_z_opt LPCWSTR wzTitle, diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp index fb4ca246..10b12e7b 100644 --- a/src/test/BurnUnitTest/PlanTest.cpp +++ b/src/test/BurnUnitTest/PlanTest.cpp @@ -529,7 +529,6 @@ namespace Bootstrapper HRESULT hr = S_OK; LPWSTR sczFilePath = NULL; - ::InitializeCriticalSection(&pEngineState->csActive); ::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive); hr = VariableInitialize(&pEngineState->variables); @@ -567,6 +566,8 @@ namespace Bootstrapper hr = DepDependencyArrayAlloc(&pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies, pRegistration->sczProviderKey, NULL); NativeAssert::Succeeded(hr, "Failed to add the bundle provider key to the list of dependencies to ignore."); + + pEngineState->userExperience.fEngineActive = TRUE; } void DetectAttachedContainerAsAttached(BURN_ENGINE_STATE* pEngineState) -- cgit v1.2.3-55-g6feb From 39725a1a6d1c72a6748bd3c306af32bcae6dbf8f Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 2 Feb 2021 16:57:33 -0600 Subject: Require re-Detect after Apply. --- src/engine/core.cpp | 53 +++++++++++++++++++++++++++++++++----- src/engine/core.h | 4 +++ src/engine/engine.cpp | 7 +++++ src/engine/engine.mc | 7 +++++ src/engine/logging.cpp | 23 +++++++++++++++++ src/engine/logging.h | 4 +++ src/test/BurnUnitTest/PlanTest.cpp | 1 + 7 files changed, 92 insertions(+), 7 deletions(-) diff --git a/src/engine/core.cpp b/src/engine/core.cpp index a644d377..a4c118a3 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -242,6 +242,13 @@ extern "C" HRESULT CoreDetect( LogId(REPORT_STANDARD, MSG_DETECT_BEGIN, pEngineState->packages.cPackages); + // Always reset the detect state which means the plan should be reset too. + pEngineState->fDetected = FALSE; + pEngineState->fPlanned = FALSE; + pEngineState->fApplied = FALSE; + DetectReset(&pEngineState->registration, &pEngineState->packages); + PlanReset(&pEngineState->plan, &pEngineState->packages); + // Detect if bundle installed state has changed since start up. This // only happens if Apply() changed the state of bundle (installed or // uninstalled). In that case, Detect() can be used here to reset @@ -266,10 +273,6 @@ extern "C" HRESULT CoreDetect( pEngineState->userExperience.hwndDetect = hwndParent; - // Always reset the detect state which means the plan should be reset too. - DetectReset(&pEngineState->registration, &pEngineState->packages); - PlanReset(&pEngineState->plan, &pEngineState->packages); - hr = SearchesExecute(&pEngineState->searches, &pEngineState->variables); ExitOnFailure(hr, "Failed to execute searches."); @@ -365,6 +368,11 @@ LExit: hr = hrFirstPackageFailure; } + if (SUCCEEDED(hr)) + { + pEngineState->fDetected = TRUE; + } + if (fDetectBegan) { UserExperienceOnDetectComplete(&pEngineState->userExperience, hr); @@ -388,6 +396,7 @@ extern "C" HRESULT CorePlan( HANDLE hSyncpointEvent = NULL; BURN_PACKAGE* pUpgradeBundlePackage = NULL; BURN_PACKAGE* pForwardCompatibleBundlePackage = NULL; + BOOL fContinuePlanning = TRUE; // assume we won't skip planning due to dependencies. LogId(REPORT_STANDARD, MSG_PLAN_BEGIN, pEngineState->packages.cPackages, LoggingBurnActionToString(action)); @@ -395,7 +404,17 @@ extern "C" HRESULT CorePlan( hr = UserExperienceOnPlanBegin(&pEngineState->userExperience, pEngineState->packages.cPackages); ExitOnRootFailure(hr, "BA aborted plan begin."); + if (!pEngineState->fDetected) + { + ExitOnFailure(hr = E_INVALIDSTATE, "Plan cannot be done without a successful Detect."); + } + else if (pEngineState->fApplied) + { + ExitOnFailure(hr = E_INVALIDSTATE, "Plan requires a new successful Detect after calling Apply."); + } + // Always reset the plan. + pEngineState->fPlanned = FALSE; PlanReset(&pEngineState->plan, &pEngineState->packages); // Remember the overall action state in the plan since it shapes the changes @@ -447,7 +466,6 @@ extern "C" HRESULT CorePlan( } else // doing an action that modifies the machine state. { - BOOL fContinuePlanning = TRUE; // assume we'll be able to keep planning after registration. pEngineState->plan.fPerMachine = pEngineState->registration.fPerMachine; // default the scope of the plan to the per-machine state of the bundle. hr = PlanRegistration(&pEngineState->plan, &pEngineState->registration, pEngineState->command.resumeType, pEngineState->command.relationType, &fContinuePlanning); @@ -477,12 +495,20 @@ extern "C" HRESULT CorePlan( hr = PlanFinalizeActions(&pEngineState->plan); ExitOnFailure(hr, "Failed to remove unnecessary actions from plan."); - // Finally, display all packages and related bundles in the log. - LogPackages(pUpgradeBundlePackage, pForwardCompatibleBundlePackage, &pEngineState->packages, &pEngineState->registration.relatedBundles, action); + if (fContinuePlanning) + { + // Finally, display all packages and related bundles in the log. + LogPackages(pUpgradeBundlePackage, pForwardCompatibleBundlePackage, &pEngineState->packages, &pEngineState->registration.relatedBundles, action); + } PlanDump(&pEngineState->plan); LExit: + if (SUCCEEDED(hr)) + { + pEngineState->fPlanned = TRUE; + } + if (fPlanBegan) { UserExperienceOnPlanComplete(&pEngineState->userExperience, hr); @@ -549,6 +575,15 @@ extern "C" HRESULT CoreApply( LogId(REPORT_STANDARD, MSG_APPLY_BEGIN); + if (!pEngineState->fPlanned) + { + ExitOnFailure(hr = E_INVALIDSTATE, "Apply cannot be done without a successful Plan."); + } + else if (pEngineState->fApplied) + { + ExitOnFailure(hr = E_INVALIDSTATE, "Plans cannot be applied multiple times."); + } + // Ensure any previous attempts to execute are reset. ApplyReset(&pEngineState->userExperience, &pEngineState->packages); @@ -564,6 +599,8 @@ extern "C" HRESULT CoreApply( hr = UserExperienceOnApplyBegin(&pEngineState->userExperience, dwPhaseCount); ExitOnRootFailure(hr, "BA aborted apply begin."); + pEngineState->fApplied = TRUE; + // Abort if this bundle already requires a restart. if (BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING == pEngineState->command.resumeType) { @@ -758,6 +795,8 @@ extern "C" HRESULT CoreQuit( LogId(REPORT_STANDARD, MSG_QUIT, nExitCode); + pEngineState->fQuit = TRUE; + ::PostQuitMessage(nExitCode); // go bye-bye. return hr; diff --git a/src/engine/core.h b/src/engine/core.h index fae4bfe5..fd7311e3 100644 --- a/src/engine/core.h +++ b/src/engine/core.h @@ -78,6 +78,10 @@ enum BURN_AU_PAUSE_ACTION typedef struct _BURN_ENGINE_STATE { // UX flow control + BOOL fDetected; + BOOL fPlanned; + BOOL fApplied; + BOOL fQuit; //BOOL fSuspend; // Is TRUE when UX made Suspend() call on core. //BOOL fForcedReboot; // Is TRUE when UX made Reboot() call on core. //BOOL fCancelled; // Is TRUE when UX return cancel on UX OnXXX() methods. diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 2c6bad03..e3ace592 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -804,6 +804,12 @@ static HRESULT ProcessMessage( UserExperienceActivateEngine(&pEngineState->userExperience); + if (pEngineState->fQuit) + { + LogId(REPORT_WARNING, MSG_IGNORE_OPERATION_AFTER_QUIT, LoggingBurnMessageToString(pmsg->message)); + ExitFunction1(hr = E_INVALIDSTATE); + } + switch (pmsg->message) { case WM_BURN_DETECT: @@ -831,6 +837,7 @@ static HRESULT ProcessMessage( break; } +LExit: UserExperienceDeactivateEngine(&pEngineState->userExperience); return hr; diff --git a/src/engine/engine.mc b/src/engine/engine.mc index b120c5bb..d2135839 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -170,6 +170,13 @@ Language=English Condition '%1!ls!' contains invalid version string '%2!ls!'. . +MessageId=58 +Severity=Warning +SymbolicName=MSG_IGNORE_OPERATION_AFTER_QUIT +Language=English +Bootstrapper application already requested to quit, ignoring request: '%1!hs!'. +. + MessageId=100 Severity=Success SymbolicName=MSG_DETECT_BEGIN diff --git a/src/engine/logging.cpp b/src/engine/logging.cpp index 49b2bcc3..9dca527a 100644 --- a/src/engine/logging.cpp +++ b/src/engine/logging.cpp @@ -289,6 +289,29 @@ extern "C" LPCSTR LoggingBurnActionToString( } } +LPCSTR LoggingBurnMessageToString( + __in UINT message + ) +{ + switch (message) + { + case WM_BURN_APPLY: + return "Apply"; + case WM_BURN_DETECT: + return "Detect"; + case WM_BURN_ELEVATE: + return "Elevate"; + case WM_BURN_LAUNCH_APPROVED_EXE: + return "LaunchApprovedExe"; + case WM_BURN_PLAN: + return "Plan"; + case WM_BURN_QUIT: + return "Quit"; + default: + return "Invalid"; + } +} + extern "C" LPCSTR LoggingActionStateToString( __in BOOTSTRAPPER_ACTION_STATE actionState ) diff --git a/src/engine/logging.h b/src/engine/logging.h index 381a295b..b5c6c052 100644 --- a/src/engine/logging.h +++ b/src/engine/logging.h @@ -65,6 +65,10 @@ LPCSTR LoggingBurnActionToString( __in BOOTSTRAPPER_ACTION action ); +LPCSTR LoggingBurnMessageToString( + __in UINT message + ); + LPCSTR LoggingActionStateToString( __in BOOTSTRAPPER_ACTION_STATE actionState ); diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp index 10b12e7b..a65bef4d 100644 --- a/src/test/BurnUnitTest/PlanTest.cpp +++ b/src/test/BurnUnitTest/PlanTest.cpp @@ -568,6 +568,7 @@ namespace Bootstrapper NativeAssert::Succeeded(hr, "Failed to add the bundle provider key to the list of dependencies to ignore."); pEngineState->userExperience.fEngineActive = TRUE; + pEngineState->fDetected = TRUE; } void DetectAttachedContainerAsAttached(BURN_ENGINE_STATE* pEngineState) -- cgit v1.2.3-55-g6feb From fd8c2b0899bfbce07386af245c04eb21dc01cbdf Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 2 Feb 2021 18:09:58 -0600 Subject: Update the logic for determining when the bundle should be registered. The basic rule is that if a non-permanent package is present at the end of the chain, then the bundle should be registered. If no non-permanent packages are present at the end of the chain, then the bundle should not be registered. This required tracking what actually happened with each package during Apply. Include cache status in registration calculation. Include dependency ref-counting when determining whether the bundle should be registered. --- src/engine/apply.cpp | 247 ++++++++++++++++++++++++------ src/engine/apply.h | 4 +- src/engine/core.cpp | 18 ++- src/engine/dependency.cpp | 29 ++++ src/engine/detect.cpp | 2 + src/engine/engine.mc | 19 ++- src/engine/exeengine.cpp | 30 ++++ src/engine/exeengine.h | 4 + src/engine/logging.cpp | 25 +++ src/engine/logging.h | 5 + src/engine/msiengine.cpp | 41 +++++ src/engine/msiengine.h | 5 + src/engine/mspengine.cpp | 109 ++++++++++++- src/engine/mspengine.h | 8 + src/engine/msuengine.cpp | 30 ++++ src/engine/msuengine.h | 4 + src/engine/package.cpp | 1 + src/engine/package.h | 18 +++ src/engine/plan.cpp | 192 ++++++++--------------- src/engine/plan.h | 18 +-- src/test/BurnUnitTest/BurnTestFixture.h | 2 + src/test/BurnUnitTest/PlanTest.cpp | 260 ++++++++++++++++++++++++-------- 22 files changed, 804 insertions(+), 267 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 76831461..8d2f5757 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -46,6 +46,10 @@ static HRESULT WINAPI AuthenticationRequired( __out BOOL* pfRetry ); +static void CalculateKeepRegistration( + __in BURN_ENGINE_STATE* pEngineState, + __inout BOOL* pfKeepRegistration + ); static HRESULT ExecuteDependentRegistrationActions( __in HANDLE hPipe, __in const BURN_REGISTRATION* pRegistration, @@ -141,7 +145,6 @@ static HRESULT DoExecuteAction( __in BURN_EXECUTE_CONTEXT* pContext, __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, __inout BURN_EXECUTE_ACTION_CHECKPOINT** ppCheckpoint, - __out BOOL* pfKeepRegistration, __out BOOL* pfSuspend, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ); @@ -149,7 +152,6 @@ static HRESULT DoRollbackActions( __in BURN_ENGINE_STATE* pEngineState, __in BURN_EXECUTE_CONTEXT* pContext, __in DWORD dwCheckpoint, - __out BOOL* pfKeepRegistration, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ); static HRESULT ExecuteExePackage( @@ -165,6 +167,7 @@ static HRESULT ExecuteMsiPackage( __in BURN_ENGINE_STATE* pEngineState, __in BURN_EXECUTE_ACTION* pExecuteAction, __in BURN_EXECUTE_CONTEXT* pContext, + __in BOOL fInsideMsiTransaction, __in BOOL fRollback, __out BOOL* pfRetry, __out BOOL* pfSuspend, @@ -174,6 +177,7 @@ static HRESULT ExecuteMspPackage( __in BURN_ENGINE_STATE* pEngineState, __in BURN_EXECUTE_ACTION* pExecuteAction, __in BURN_EXECUTE_CONTEXT* pContext, + __in BOOL fInsideMsiTransaction, __in BOOL fRollback, __out BOOL* pfRetry, __out BOOL* pfSuspend, @@ -214,6 +218,10 @@ static HRESULT ExecuteMsiRollbackTransaction( __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, __in BURN_EXECUTE_CONTEXT* pContext ); +static void ResetTransactionRegistrationState( + __in BURN_ENGINE_STATE* pEngineState, + __in BOOL fCommit + ); static HRESULT CleanPackage( __in HANDLE hElevatedPipe, __in BURN_PACKAGE* pPackage @@ -282,6 +290,7 @@ extern "C" void ApplyReset( { BURN_PACKAGE* pPackage = pPackages->rgPackages + i; pPackage->hrCacheResult = S_OK; + pPackage->transactionRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; } } @@ -380,13 +389,15 @@ LExit: extern "C" HRESULT ApplyUnregister( __in BURN_ENGINE_STATE* pEngineState, __in BOOL fFailedOrRollback, - __in BOOL fKeepRegistration, __in BOOL fSuspend, __in BOOTSTRAPPER_APPLY_RESTART restart ) { HRESULT hr = S_OK; BURN_RESUME_MODE resumeMode = BURN_RESUME_MODE_NONE; + BOOL fKeepRegistration = pEngineState->plan.fDisallowRemoval; + + CalculateKeepRegistration(pEngineState, &fKeepRegistration); hr = UserExperienceOnUnregisterBegin(&pEngineState->userExperience); ExitOnRootFailure(hr, "BA aborted unregister begin."); @@ -443,7 +454,7 @@ extern "C" HRESULT ApplyCache( __in BURN_PLAN* pPlan, __in HANDLE hPipe, __inout DWORD* pcOverallProgressTicks, - __out BOOL* pfRollback + __inout BOOL* pfRollback ) { HRESULT hr = S_OK; @@ -732,7 +743,6 @@ extern "C" HRESULT ApplyExecute( __in BURN_ENGINE_STATE* pEngineState, __in_opt HANDLE hCacheThread, __inout DWORD* pcOverallProgressTicks, - __inout BOOL* pfKeepRegistration, __out BOOL* pfRollback, __out BOOL* pfSuspend, __out BOOTSTRAPPER_APPLY_RESTART* pRestart @@ -779,7 +789,7 @@ extern "C" HRESULT ApplyExecute( } // Execute the action. - hr = DoExecuteAction(pEngineState, pExecuteAction, hCacheThread, &context, &pRollbackBoundary, &pCheckpoint, pfKeepRegistration, pfSuspend, pRestart); + hr = DoExecuteAction(pEngineState, pExecuteAction, hCacheThread, &context, &pRollbackBoundary, &pCheckpoint, pfSuspend, pRestart); if (*pfSuspend || BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart) { @@ -821,7 +831,7 @@ extern "C" HRESULT ApplyExecute( // The action failed, roll back to previous rollback boundary. if (pCheckpoint) { - hrRollback = DoRollbackActions(pEngineState, &context, pCheckpoint->dwId, pfKeepRegistration, pRestart); + hrRollback = DoRollbackActions(pEngineState, &context, pCheckpoint->dwId, pRestart); IgnoreRollbackError(hrRollback, "Failed rollback actions"); } @@ -855,14 +865,46 @@ extern "C" void ApplyClean( for (DWORD i = 0; i < pPlan->cCleanActions; ++i) { BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + i; + BURN_PACKAGE* pPackage = pCleanAction->pPackage; - hr = CleanPackage(hPipe, pCleanAction->pPackage); + hr = CleanPackage(hPipe, pPackage); } } // internal helper functions +static void CalculateKeepRegistration( + __in BURN_ENGINE_STATE* pEngineState, + __inout BOOL* pfKeepRegistration + ) +{ + LogId(REPORT_STANDARD, MSG_POST_APPLY_CALCULATE_REGISTRATION); + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + + LogId(REPORT_STANDARD, MSG_POST_APPLY_PACKAGE, pPackage->sczId, LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->installRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->cacheRegistrationState)); + + if (!pPackage->fCanAffectRegistration) + { + continue; + } + + if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + MspEngineFinalizeInstallRegistrationState(pPackage); + } + + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState || + BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState) + { + *pfKeepRegistration = TRUE; + } + } +} + static HRESULT ExecuteDependentRegistrationActions( __in HANDLE hPipe, __in const BURN_REGISTRATION* pRegistration, @@ -1255,6 +1297,15 @@ static HRESULT LayoutOrCacheContainerOrPayload( LARGE_INTEGER liContainerOrPayloadSize = { }; LARGE_INTEGER liZero = { }; BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { }; + BOOL fCanAffectRegistration = FALSE; + + if (!wzLayoutDirectory) + { + Assert(!pContainer); + Assert(pPackage); + + fCanAffectRegistration = pPackage->fCanAffectRegistration; + } liContainerOrPayloadSize.QuadPart = pContainer ? pContainer->qwFileSize : pPayload->qwFileSize; @@ -1297,9 +1348,6 @@ static HRESULT LayoutOrCacheContainerOrPayload( } else // complete the payload. { - Assert(!pContainer); - Assert(pPackage); - hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, wzUnverifiedPath, fMove); } @@ -1307,6 +1355,11 @@ static HRESULT LayoutOrCacheContainerOrPayload( // will get. if (SUCCEEDED(hr)) { + if (fCanAffectRegistration) + { + pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + CacheProgressRoutine(liContainerOrPayloadSize, liContainerOrPayloadSize, liZero, liZero, 0, 0, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, &progress); if (progress.fCancel) { @@ -1639,7 +1692,6 @@ static HRESULT DoExecuteAction( __in BURN_EXECUTE_CONTEXT* pContext, __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, __inout BURN_EXECUTE_ACTION_CHECKPOINT** ppCheckpoint, - __out BOOL* pfKeepRegistration, __out BOOL* pfSuspend, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ) @@ -1651,11 +1703,14 @@ static HRESULT DoExecuteAction( BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; BOOL fRetry = FALSE; BOOL fStopWusaService = FALSE; + BOOL fInsideMsiTransaction = FALSE; pContext->fRollback = FALSE; do { + fInsideMsiTransaction = *ppRollbackBoundary && (*ppRollbackBoundary)->fActiveTransaction; + switch (pExecuteAction->type) { case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT: @@ -1695,12 +1750,12 @@ static HRESULT DoExecuteAction( break; case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: - hr = ExecuteMsiPackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart); + hr = ExecuteMsiPackage(pEngineState, pExecuteAction, pContext, fInsideMsiTransaction, FALSE, &fRetry, pfSuspend, &restart); ExitOnFailure(hr, "Failed to execute MSI package."); break; case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: - hr = ExecuteMspPackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart); + hr = ExecuteMspPackage(pEngineState, pExecuteAction, pContext, fInsideMsiTransaction, FALSE, &fRetry, pfSuspend, &restart); ExitOnFailure(hr, "Failed to execute MSP package."); break; @@ -1720,8 +1775,6 @@ static HRESULT DoExecuteAction( ExitOnFailure(hr, "Failed to execute dependency action."); break; - case BURN_EXECUTE_ACTION_TYPE_REGISTRATION: - *pfKeepRegistration = pExecuteAction->registration.fKeep; break; case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY: @@ -1757,7 +1810,6 @@ static HRESULT DoRollbackActions( __in BURN_ENGINE_STATE* pEngineState, __in BURN_EXECUTE_CONTEXT* pContext, __in DWORD dwCheckpoint, - __out BOOL* pfKeepRegistration, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ) { @@ -1811,12 +1863,12 @@ static HRESULT DoRollbackActions( break; case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: - hr = ExecuteMsiPackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); + hr = ExecuteMsiPackage(pEngineState, pRollbackAction, pContext, FALSE, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); IgnoreRollbackError(hr, "Failed to rollback MSI package."); break; case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: - hr = ExecuteMspPackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); + hr = ExecuteMspPackage(pEngineState, pRollbackAction, pContext, FALSE, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); IgnoreRollbackError(hr, "Failed to rollback MSP package."); break; @@ -1835,10 +1887,6 @@ static HRESULT DoRollbackActions( IgnoreRollbackError(hr, "Failed to rollback dependency action."); break; - case BURN_EXECUTE_ACTION_TYPE_REGISTRATION: - *pfKeepRegistration = pRollbackAction->registration.fKeep; - break; - case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY: ExitFunction1(hr = S_OK); @@ -1878,19 +1926,21 @@ static HRESULT ExecuteExePackage( GENERIC_EXECUTE_MESSAGE message = { }; int nResult = 0; BOOL fBeginCalled = FALSE; + BOOL fExecuted = FALSE; + BURN_PACKAGE* pPackage = pExecuteAction->exePackage.pPackage; - if (FAILED(pExecuteAction->exePackage.pPackage->hrCacheResult)) + if (FAILED(pPackage->hrCacheResult)) { - LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->exePackage.pPackage->sczId, pExecuteAction->exePackage.pPackage->hrCacheResult); + LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult); ExitFunction1(hr = S_OK); } Assert(pContext->fRollback == fRollback); - pContext->pExecutingPackage = pExecuteAction->exePackage.pPackage; + pContext->pExecutingPackage = pPackage; fBeginCalled = TRUE; // Send package execute begin to BA. - hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->exePackage.pPackage->sczId, !fRollback, pExecuteAction->exePackage.action, INSTALLUILEVEL_NOCHANGE, FALSE); + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->exePackage.action, INSTALLUILEVEL_NOCHANGE, FALSE); ExitOnRootFailure(hr, "BA aborted execute EXE package begin."); message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; @@ -1900,8 +1950,10 @@ static HRESULT ExecuteExePackage( hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); ExitOnRootFailure(hr, "BA aborted EXE progress."); + fExecuted = TRUE; + // Execute package. - if (pExecuteAction->exePackage.pPackage->fPerMachine) + if (pPackage->fPerMachine) { hrExecute = ElevationExecuteExePackage(pEngineState->companionConnection.hPipe, pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart); ExitOnFailure(hrExecute, "Failed to configure per-machine EXE package."); @@ -1926,9 +1978,14 @@ static HRESULT ExecuteExePackage( ExitOnRootFailure(hr, "BA aborted EXE package execute progress."); LExit: + if (fExecuted) + { + ExeEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute); + } + if (fBeginCalled) { - hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->exePackage.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); } return hr; @@ -1938,6 +1995,7 @@ static HRESULT ExecuteMsiPackage( __in BURN_ENGINE_STATE* pEngineState, __in BURN_EXECUTE_ACTION* pExecuteAction, __in BURN_EXECUTE_CONTEXT* pContext, + __in BOOL fInsideMsiTransaction, __in BOOL fRollback, __out BOOL* pfRetry, __out BOOL* pfSuspend, @@ -1947,23 +2005,27 @@ static HRESULT ExecuteMsiPackage( HRESULT hr = S_OK; HRESULT hrExecute = S_OK; BOOL fBeginCalled = FALSE; + BOOL fExecuted = FALSE; + BURN_PACKAGE* pPackage = pExecuteAction->msiPackage.pPackage; - if (FAILED(pExecuteAction->msiPackage.pPackage->hrCacheResult)) + if (FAILED(pPackage->hrCacheResult)) { - LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->msiPackage.pPackage->sczId, pExecuteAction->msiPackage.pPackage->hrCacheResult); + LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult); ExitFunction1(hr = S_OK); } Assert(pContext->fRollback == fRollback); - pContext->pExecutingPackage = pExecuteAction->msiPackage.pPackage; + pContext->pExecutingPackage = pPackage; fBeginCalled = TRUE; // Send package execute begin to BA. - hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msiPackage.pPackage->sczId, !fRollback, pExecuteAction->msiPackage.action, pExecuteAction->msiPackage.uiLevel, pExecuteAction->msiPackage.fDisableExternalUiHandler); + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->msiPackage.action, pExecuteAction->msiPackage.uiLevel, pExecuteAction->msiPackage.fDisableExternalUiHandler); ExitOnRootFailure(hr, "BA aborted execute MSI package begin."); + fExecuted = TRUE; + // execute package - if (pExecuteAction->msiPackage.pPackage->fPerMachine) + if (pPackage->fPerMachine) { hrExecute = ElevationExecuteMsiPackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); ExitOnFailure(hrExecute, "Failed to configure per-machine MSI package."); @@ -1981,9 +2043,14 @@ static HRESULT ExecuteMsiPackage( ExitOnRootFailure(hr, "BA aborted MSI package execute progress."); LExit: + if (fExecuted) + { + MsiEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute, fInsideMsiTransaction); + } + if (fBeginCalled) { - hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->msiPackage.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); } return hr; @@ -1993,6 +2060,7 @@ static HRESULT ExecuteMspPackage( __in BURN_ENGINE_STATE* pEngineState, __in BURN_EXECUTE_ACTION* pExecuteAction, __in BURN_EXECUTE_CONTEXT* pContext, + __in BOOL fInsideMsiTransaction, __in BOOL fRollback, __out BOOL* pfRetry, __out BOOL* pfSuspend, @@ -2002,19 +2070,21 @@ static HRESULT ExecuteMspPackage( HRESULT hr = S_OK; HRESULT hrExecute = S_OK; BOOL fBeginCalled = FALSE; + BOOL fExecuted = FALSE; + BURN_PACKAGE* pPackage = pExecuteAction->mspTarget.pPackage; - if (FAILED(pExecuteAction->mspTarget.pPackage->hrCacheResult)) + if (FAILED(pPackage->hrCacheResult)) { - LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->mspTarget.pPackage->sczId, pExecuteAction->mspTarget.pPackage->hrCacheResult); + LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult); ExitFunction1(hr = S_OK); } Assert(pContext->fRollback == fRollback); - pContext->pExecutingPackage = pExecuteAction->mspTarget.pPackage; + pContext->pExecutingPackage = pPackage; fBeginCalled = TRUE; // Send package execute begin to BA. - hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->mspTarget.pPackage->sczId, !fRollback, pExecuteAction->mspTarget.action, pExecuteAction->mspTarget.uiLevel, pExecuteAction->mspTarget.fDisableExternalUiHandler); + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->mspTarget.action, pExecuteAction->mspTarget.uiLevel, pExecuteAction->mspTarget.fDisableExternalUiHandler); ExitOnRootFailure(hr, "BA aborted execute MSP package begin."); // Now send all the patches that target this product code. @@ -2026,6 +2096,8 @@ static HRESULT ExecuteMspPackage( ExitOnRootFailure(hr, "BA aborted execute MSP target."); } + fExecuted = TRUE; + // execute package if (pExecuteAction->mspTarget.fPerMachineTarget) { @@ -2045,9 +2117,14 @@ static HRESULT ExecuteMspPackage( ExitOnRootFailure(hr, "BA aborted MSP package execute progress."); LExit: + if (fExecuted) + { + MspEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute, fInsideMsiTransaction); + } + if (fBeginCalled) { - hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->mspTarget.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); } return hr; @@ -2069,19 +2146,21 @@ static HRESULT ExecuteMsuPackage( GENERIC_EXECUTE_MESSAGE message = { }; int nResult = 0; BOOL fBeginCalled = FALSE; + BOOL fExecuted = FALSE; + BURN_PACKAGE* pPackage = pExecuteAction->msuPackage.pPackage; - if (FAILED(pExecuteAction->msuPackage.pPackage->hrCacheResult)) + if (FAILED(pPackage->hrCacheResult)) { - LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->msuPackage.pPackage->sczId, pExecuteAction->msuPackage.pPackage->hrCacheResult); + LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult); ExitFunction1(hr = S_OK); } Assert(pContext->fRollback == fRollback); - pContext->pExecutingPackage = pExecuteAction->msuPackage.pPackage; + pContext->pExecutingPackage = pPackage; fBeginCalled = TRUE; // Send package execute begin to BA. - hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msuPackage.pPackage->sczId, !fRollback, pExecuteAction->msuPackage.action, INSTALLUILEVEL_NOCHANGE, FALSE); + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->msuPackage.action, INSTALLUILEVEL_NOCHANGE, FALSE); ExitOnRootFailure(hr, "BA aborted execute MSU package begin."); message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; @@ -2091,8 +2170,10 @@ static HRESULT ExecuteMsuPackage( hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); ExitOnRootFailure(hr, "BA aborted MSU progress."); + fExecuted = TRUE; + // execute package - if (pExecuteAction->msuPackage.pPackage->fPerMachine) + if (pPackage->fPerMachine) { hrExecute = ElevationExecuteMsuPackage(pEngineState->companionConnection.hPipe, pExecuteAction, fRollback, fStopWusaService, GenericExecuteMessageHandler, pContext, pRestart); ExitOnFailure(hrExecute, "Failed to configure per-machine MSU package."); @@ -2117,9 +2198,14 @@ static HRESULT ExecuteMsuPackage( ExitOnRootFailure(hr, "BA aborted MSU package execute progress."); LExit: + if (fExecuted) + { + MsuEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute); + } + if (fBeginCalled) { - hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->msuPackage.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); } return hr; @@ -2167,6 +2253,32 @@ static HRESULT ExecuteDependencyAction( ExitOnFailure(hr, "Failed to register the dependency on per-user package."); } + if (pAction->packageDependency.pPackage->fCanAffectRegistration) + { + if (BURN_DEPENDENCY_ACTION_REGISTER == pAction->packageDependency.action) + { + if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pAction->packageDependency.pPackage->cacheRegistrationState) + { + pAction->packageDependency.pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pAction->packageDependency.pPackage->installRegistrationState) + { + pAction->packageDependency.pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + } + else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pAction->packageDependency.action) + { + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pAction->packageDependency.pPackage->cacheRegistrationState) + { + pAction->packageDependency.pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pAction->packageDependency.pPackage->installRegistrationState) + { + pAction->packageDependency.pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + } + } + LExit: return hr; } @@ -2202,6 +2314,8 @@ static HRESULT ExecuteMsiBeginTransaction( if (SUCCEEDED(hr)) { pRollbackBoundary->fActiveTransaction = TRUE; + + ResetTransactionRegistrationState(pEngineState, FALSE); } LExit: @@ -2244,6 +2358,8 @@ static HRESULT ExecuteMsiCommitTransaction( if (SUCCEEDED(hr)) { pRollbackBoundary->fActiveTransaction = FALSE; + + ResetTransactionRegistrationState(pEngineState, TRUE); } LExit: @@ -2285,6 +2401,8 @@ static HRESULT ExecuteMsiRollbackTransaction( LExit: pRollbackBoundary->fActiveTransaction = FALSE; + ResetTransactionRegistrationState(pEngineState, FALSE); + if (fBeginCalled) { UserExperienceOnRollbackMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr); @@ -2293,6 +2411,38 @@ LExit: return hr; } +static void ResetTransactionRegistrationState( + __in BURN_ENGINE_STATE* pEngineState, + __in BOOL fCommit + ) +{ + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + + if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + j; + + if (fCommit && BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN != pTargetProduct->transactionRegistrationState) + { + pTargetProduct->registrationState = pTargetProduct->transactionRegistrationState; + } + + pTargetProduct->transactionRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; + } + } + else if (fCommit && BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN != pPackage->transactionRegistrationState) + { + pPackage->installRegistrationState = pPackage->transactionRegistrationState; + } + + pPackage->transactionRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; + } +} + static HRESULT CleanPackage( __in HANDLE hElevatedPipe, __in BURN_PACKAGE* pPackage @@ -2309,6 +2459,11 @@ static HRESULT CleanPackage( hr = CacheRemovePackage(FALSE, pPackage->sczId, pPackage->sczCacheId); } + if (pPackage->fCanAffectRegistration) + { + pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + return hr; } diff --git a/src/engine/apply.h b/src/engine/apply.h index 00e1fceb..548e147d 100644 --- a/src/engine/apply.h +++ b/src/engine/apply.h @@ -72,7 +72,6 @@ HRESULT ApplyRegister( HRESULT ApplyUnregister( __in BURN_ENGINE_STATE* pEngineState, __in BOOL fFailedOrRollback, - __in BOOL fRollback, __in BOOL fSuspend, __in BOOTSTRAPPER_APPLY_RESTART restart ); @@ -83,13 +82,12 @@ HRESULT ApplyCache( __in BURN_PLAN* pPlan, __in HANDLE hPipe, __inout DWORD* pcOverallProgressTicks, - __out BOOL* pfRollback + __inout BOOL* pfRollback ); HRESULT ApplyExecute( __in BURN_ENGINE_STATE* pEngineState, __in_opt HANDLE hCacheThread, __inout DWORD* pcOverallProgressTicks, - __inout BOOL* pfKeepRegistration, __out BOOL* pfRollback, __out BOOL* pfSuspend, __out BOOTSTRAPPER_APPLY_RESTART* pRestart diff --git a/src/engine/core.cpp b/src/engine/core.cpp index a4c118a3..36471e93 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -331,6 +331,8 @@ extern "C" HRESULT CoreDetect( } pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; + pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; } } @@ -342,7 +344,7 @@ extern "C" HRESULT CoreDetect( { pPackage = pEngineState->packages.rgPackages + iPackage; - LogId(REPORT_STANDARD, MSG_DETECTED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingCacheStateToString(pPackage->cache)); + LogId(REPORT_STANDARD, MSG_DETECTED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingCacheStateToString(pPackage->cache), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->installRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->cacheRegistrationState)); if (BURN_PACKAGE_TYPE_MSI == pPackage->type) { @@ -565,7 +567,6 @@ extern "C" HRESULT CoreApply( BOOL fApplyInitialize = FALSE; BOOL fElevated = FALSE; BOOL fRegistered = FALSE; - BOOL fKeepRegistration = pEngineState->plan.fKeepRegistrationDefault; BOOL fRollback = FALSE; BOOL fSuspend = FALSE; BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; @@ -652,9 +653,9 @@ extern "C" HRESULT CoreApply( // Register. if (pEngineState->plan.fRegister) { + fRegistered = TRUE; hr = ApplyRegister(pEngineState); ExitOnFailure(hr, "Failed to register bundle."); - fRegistered = TRUE; } // Cache. @@ -681,7 +682,7 @@ extern "C" HRESULT CoreApply( // Execute. if (pEngineState->plan.cExecuteActions) { - hr = ApplyExecute(pEngineState, hCacheThread, &cOverallProgressTicks, &fKeepRegistration, &fRollback, &fSuspend, &restart); + hr = ApplyExecute(pEngineState, hCacheThread, &cOverallProgressTicks, &fRollback, &fSuspend, &restart); UserExperienceExecutePhaseComplete(&pEngineState->userExperience, hr); // signal that execute completed. } @@ -711,7 +712,7 @@ LExit: // Unregister. if (fRegistered) { - ApplyUnregister(pEngineState, FAILED(hr) || fRollback, fKeepRegistration || pEngineState->plan.fDisallowRemoval, fSuspend, restart); + ApplyUnregister(pEngineState, FAILED(hr) || fRollback, fSuspend, restart); } if (fElevated) @@ -1611,6 +1612,11 @@ static HRESULT DetectPackagePayloadsCached( pPackage->cache = cache; + if (pPackage->fCanAffectRegistration) + { + pPackage->cacheRegistrationState = BURN_CACHE_STATE_NONE < pPackage->cache ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + LExit: ReleaseStr(sczPayloadCachePath); ReleaseStr(sczCachePath); @@ -1703,7 +1709,7 @@ static void LogPackages( const DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? pPackages->cPackages - 1 - i : i; const BURN_PACKAGE* pPackage = &pPackages->rgPackages[iPackage]; - LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingBoolToString(pPackage->fAcquire), LoggingBoolToString(pPackage->fUncache), LoggingDependencyActionToString(pPackage->dependencyExecute)); + LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingBoolToString(pPackage->fAcquire), LoggingBoolToString(pPackage->fUncache), LoggingDependencyActionToString(pPackage->dependencyExecute), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedInstallRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedCacheRegistrationState)); } // Display related bundles last if caching, installing, modifying, or repairing. diff --git a/src/engine/dependency.cpp b/src/engine/dependency.cpp index 1c33aaf2..a6a8fe4d 100644 --- a/src/engine/dependency.cpp +++ b/src/engine/dependency.cpp @@ -708,6 +708,9 @@ static HRESULT DetectPackageDependents( { HRESULT hr = S_OK; HKEY hkHive = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + BOOL fCanIgnorePresence = pPackage->fCanAffectRegistration && 0 < pPackage->cDependencyProviders && + (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState || BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState); + BOOL fBundleRegisteredAsDependent = FALSE; // There's currently no point in getting the dependents if the scope doesn't match, // because they will just get ignored. @@ -729,6 +732,20 @@ static HRESULT DetectPackageDependents( { pPackage->fPackageProviderExists = TRUE; } + + if (fCanIgnorePresence && !fBundleRegisteredAsDependent) + { + for (DWORD iDependent = 0; iDependent < pProvider->cDependents; ++iDependent) + { + DEPENDENCY* pDependent = pProvider->rgDependents + iDependent; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczId, -1, pDependent->sczKey, -1)) + { + fBundleRegisteredAsDependent = TRUE; + break; + } + } + } } else { @@ -747,6 +764,18 @@ static HRESULT DetectPackageDependents( pPackage->fPackageProviderExists = TRUE; } + if (fCanIgnorePresence && !fBundleRegisteredAsDependent) + { + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState) + { + pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState) + { + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + } + LExit: return hr; } diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp index ff6ba4d7..159df3d0 100644 --- a/src/engine/detect.cpp +++ b/src/engine/detect.cpp @@ -63,6 +63,8 @@ extern "C" void DetectReset( pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; pPackage->fPackageProviderExists = FALSE; + pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; pPackage->cache = BURN_CACHE_STATE_NONE; for (DWORD iPayload = 0; iPayload < pPackage->cPayloads; ++iPayload) diff --git a/src/engine/engine.mc b/src/engine/engine.mc index d2135839..59a05676 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -188,7 +188,7 @@ MessageId=101 Severity=Success SymbolicName=MSG_DETECTED_PACKAGE Language=English -Detected package: %1!ls!, state: %2!hs!, cached: %3!hs! +Detected package: %1!ls!, state: %2!hs!, cached: %3!hs!, install registration state: %4!hs!, cache registration state: %5!hs! . MessageId=102 @@ -300,7 +300,7 @@ MessageId=201 Severity=Success SymbolicName=MSG_PLANNED_PACKAGE Language=English -Planned package: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, cache: %7!hs!, uncache: %8!hs!, dependency: %9!hs! +Planned package: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, cache: %7!hs!, uncache: %8!hs!, dependency: %9!hs!, expected install registration state: %10!hs!, expected cache registration state: %11!hs! . MessageId=202 @@ -829,6 +829,21 @@ Language=English Session end, registration key: %1!ls!, resume: %2!hs!, restart: %3!hs!, disable resume: %4!hs! . +MessageId=373 +Severity=Success +SymbolicName=MSG_POST_APPLY_CALCULATE_REGISTRATION +Language=English +Calculating whether to keep registration +. + + +MessageId=374 +Severity=Success +SymbolicName=MSG_POST_APPLY_PACKAGE +Language=English + package: %1!ls!, install registration state: %2!hs!, cache registration state: %3!hs! +. + MessageId=380 Severity=Warning SymbolicName=MSG_APPLY_SKIPPED diff --git a/src/engine/exeengine.cpp b/src/engine/exeengine.cpp index fd33d926..1ca28473 100644 --- a/src/engine/exeengine.cpp +++ b/src/engine/exeengine.cpp @@ -145,6 +145,11 @@ extern "C" HRESULT ExeEngineDetectPackage( // update detect state pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + if (pPackage->fCanAffectRegistration) + { + pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_CACHED < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + LExit: return hr; } @@ -585,6 +590,31 @@ LExit: return hr; } +extern "C" void ExeEngineUpdateInstallRegistrationState( + __in BURN_EXECUTE_ACTION* pAction, + __in HRESULT hrExecute + ) +{ + BURN_PACKAGE* pPackage = pAction->exePackage.pPackage; + + if (FAILED(hrExecute) || !pPackage->fCanAffectRegistration) + { + ExitFunction(); + } + + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->exePackage.action) + { + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + else + { + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + +LExit: + return; +} + // internal helper functions diff --git a/src/engine/exeengine.h b/src/engine/exeengine.h index 402e51ed..1eac4232 100644 --- a/src/engine/exeengine.h +++ b/src/engine/exeengine.h @@ -41,6 +41,10 @@ HRESULT ExeEngineExecutePackage( __in LPVOID pvContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ); +void ExeEngineUpdateInstallRegistrationState( + __in BURN_EXECUTE_ACTION* pAction, + __in HRESULT hrExecute + ); #if defined(__cplusplus) diff --git a/src/engine/logging.cpp b/src/engine/logging.cpp index 9dca527a..a9646218 100644 --- a/src/engine/logging.cpp +++ b/src/engine/logging.cpp @@ -399,6 +399,31 @@ extern "C" LPCSTR LoggingPackageStateToString( } } +extern "C" LPCSTR LoggingPackageRegistrationStateToString( + __in BOOL fCanAffectRegistration, + __in BURN_PACKAGE_REGISTRATION_STATE registrationState + ) +{ + if (!fCanAffectRegistration) + { + return "(permanent)"; + } + + switch (registrationState) + { + case BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN: + return "Unknown"; + case BURN_PACKAGE_REGISTRATION_STATE_IGNORED: + return "Ignored"; + case BURN_PACKAGE_REGISTRATION_STATE_ABSENT: + return "Absent"; + case BURN_PACKAGE_REGISTRATION_STATE_PRESENT: + return "Present"; + default: + return "Invalid"; + } +} + extern "C" LPCSTR LoggingCacheStateToString( __in BURN_CACHE_STATE cacheState ) diff --git a/src/engine/logging.h b/src/engine/logging.h index b5c6c052..49a8ef5d 100644 --- a/src/engine/logging.h +++ b/src/engine/logging.h @@ -89,6 +89,11 @@ LPCSTR LoggingPackageStateToString( __in BOOTSTRAPPER_PACKAGE_STATE packageState ); +LPCSTR LoggingPackageRegistrationStateToString( + __in BOOL fCanAffectRegistration, + __in BURN_PACKAGE_REGISTRATION_STATE registrationState + ); + LPCSTR LoggingCacheStateToString( __in BURN_CACHE_STATE cacheState ); diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index 5bccb375..4fdf2b7a 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -661,6 +661,11 @@ extern "C" HRESULT MsiEngineDetectPackage( } } + if (pPackage->fCanAffectRegistration) + { + pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_CACHED < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + LExit: ReleaseStr(sczInstalledLanguage); ReleaseStr(sczInstalledVersion); @@ -1397,6 +1402,42 @@ extern "C" HRESULT MsiEngineCalculateInstallUiLevel( return UserExperienceOnPlanMsiPackage(pUserExperience, wzPackageId, fExecute, actionState, pActionMsiProperty, pUiLevel, pfDisableExternalUiHandler); } +extern "C" void MsiEngineUpdateInstallRegistrationState( + __in BURN_EXECUTE_ACTION* pAction, + __in HRESULT hrExecute, + __in BOOL fInsideMsiTransaction + ) +{ + BURN_PACKAGE_REGISTRATION_STATE newState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; + BURN_PACKAGE* pPackage = pAction->msiPackage.pPackage; + + if (FAILED(hrExecute) || !pPackage->fCanAffectRegistration) + { + ExitFunction(); + } + + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->msiPackage.action) + { + newState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + else + { + newState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + + if (fInsideMsiTransaction) + { + pPackage->transactionRegistrationState = newState; + } + else + { + pPackage->installRegistrationState = newState; + } + +LExit: + return; +} + // internal helper functions diff --git a/src/engine/msiengine.h b/src/engine/msiengine.h index 1f450147..d1e46da8 100644 --- a/src/engine/msiengine.h +++ b/src/engine/msiengine.h @@ -88,6 +88,11 @@ HRESULT MsiEngineCalculateInstallUiLevel( __out INSTALLUILEVEL* pUiLevel, __out BOOL* pfDisableExternalUiHandler ); +void MsiEngineUpdateInstallRegistrationState( + __in BURN_EXECUTE_ACTION* pAction, + __in HRESULT hrExecute, + __in BOOL fInsideMsiTransaction + ); #if defined(__cplusplus) } diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp index c432b78b..c0329d79 100644 --- a/src/engine/mspengine.cpp +++ b/src/engine/mspengine.cpp @@ -200,20 +200,26 @@ extern "C" HRESULT MspEngineDetectPackage( HRESULT hr = S_OK; LPWSTR sczState = NULL; + if (pPackage->fCanAffectRegistration) + { + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + if (0 == pPackage->Msp.cTargetProductCodes) { pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; } else { - // Start the package state at the the highest state then loop through all the + // Start the package state at the highest state then loop through all the // target product codes and end up setting the current state to the lowest - // package state applied to the the target product codes. + // package state applied to the target product codes. pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) { BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; + BOOL fInstalled = FALSE; hr = WiuGetPatchInfoEx(pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode, NULL, pTargetProduct->context, INSTALLPROPERTY_PATCHSTATE, &sczState); if (SUCCEEDED(hr)) @@ -221,14 +227,17 @@ extern "C" HRESULT MspEngineDetectPackage( switch (*sczState) { case '1': + fInstalled = TRUE; pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; break; case '2': + fInstalled = TRUE; pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; break; case '4': + fInstalled = TRUE; pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE; break; @@ -249,6 +258,16 @@ extern "C" HRESULT MspEngineDetectPackage( pPackage->currentState = pTargetProduct->patchPackageState; } + if (pPackage->fCanAffectRegistration) + { + pTargetProduct->registrationState = fInstalled ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + + if (fInstalled) + { + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + } + hr = UserExperienceOnDetectTargetMsiPackage(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, pTargetProduct->patchPackageState); ExitOnRootFailure(hr, "BA aborted detect target MSI package."); } @@ -642,6 +661,92 @@ extern "C" void MspEngineSlipstreamUpdateState( } } +extern "C" void MspEngineUpdateInstallRegistrationState( + __in BURN_EXECUTE_ACTION* pAction, + __in HRESULT hrExecute, + __in BOOL fInsideMsiTransaction + ) +{ + BURN_PACKAGE_REGISTRATION_STATE newState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; + + if (FAILED(hrExecute)) + { + ExitFunction(); + } + + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->mspTarget.action) + { + newState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + else + { + newState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + + for (DWORD i = 0; i < pAction->mspTarget.cOrderedPatches; ++i) + { + BURN_ORDERED_PATCHES* pOrderedPatches = pAction->mspTarget.rgOrderedPatches + i; + BURN_PACKAGE* pPackage = pOrderedPatches->pPackage; + BURN_MSPTARGETPRODUCT* pTargetProduct = NULL; + + Assert(BURN_PACKAGE_TYPE_MSP == pPackage->type); + + if (!pPackage->fCanAffectRegistration) + { + continue; + } + + for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j) + { + pTargetProduct = pPackage->Msp.rgTargetProducts + j; + if (pAction->mspTarget.fPerMachineTarget == (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context) && + CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pAction->mspTarget.sczTargetProductCode, -1, pTargetProduct->wzTargetProductCode, -1)) + { + break; + } + + pTargetProduct = NULL; + } + + if (!pTargetProduct) + { + AssertSz(pTargetProduct, "Ordered patch didn't have corresponding target product"); + continue; + } + + if (fInsideMsiTransaction) + { + pTargetProduct->transactionRegistrationState = newState; + } + else + { + pTargetProduct->registrationState = newState; + } + } + +LExit: + return; +} + +extern "C" void MspEngineFinalizeInstallRegistrationState( + __in BURN_PACKAGE* pPackage + ) +{ + Assert(pPackage->fCanAffectRegistration); + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + + for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; + + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pTargetProduct->registrationState) + { + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + break; + } + } +} + // internal helper functions diff --git a/src/engine/mspengine.h b/src/engine/mspengine.h index e08fe992..7cc9a119 100644 --- a/src/engine/mspengine.h +++ b/src/engine/mspengine.h @@ -62,6 +62,14 @@ void MspEngineSlipstreamUpdateState( __in BOOTSTRAPPER_ACTION_STATE execute, __in BOOTSTRAPPER_ACTION_STATE rollback ); +void MspEngineUpdateInstallRegistrationState( + __in BURN_EXECUTE_ACTION* pAction, + __in HRESULT hrExecute, + __in BOOL fInsideMsiTransaction + ); +void MspEngineFinalizeInstallRegistrationState( + __in BURN_PACKAGE* pPackage + ); #if defined(__cplusplus) diff --git a/src/engine/msuengine.cpp b/src/engine/msuengine.cpp index 641d55b1..b7a503ad 100644 --- a/src/engine/msuengine.cpp +++ b/src/engine/msuengine.cpp @@ -69,6 +69,11 @@ extern "C" HRESULT MsuEngineDetectPackage( // update detect state pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + if (pPackage->fCanAffectRegistration) + { + pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_CACHED < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + LExit: return hr; } @@ -424,6 +429,31 @@ LExit: return hr; } +extern "C" void MsuEngineUpdateInstallRegistrationState( + __in BURN_EXECUTE_ACTION* pAction, + __in HRESULT hrExecute + ) +{ + BURN_PACKAGE* pPackage = pAction->msuPackage.pPackage; + + if (FAILED(hrExecute) || !pPackage->fCanAffectRegistration) + { + ExitFunction(); + } + + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->msuPackage.action) + { + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + else + { + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + +LExit: + return; +} + static HRESULT EnsureWUServiceEnabled( __in BOOL fStopWusaService, __out SC_HANDLE* pschWu, diff --git a/src/engine/msuengine.h b/src/engine/msuengine.h index d0323b06..7f57a084 100644 --- a/src/engine/msuengine.h +++ b/src/engine/msuengine.h @@ -41,6 +41,10 @@ HRESULT MsuEngineExecutePackage( __in LPVOID pvContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ); +void MsuEngineUpdateInstallRegistrationState( + __in BURN_EXECUTE_ACTION* pAction, + __in HRESULT hrExecute + ); #if defined(__cplusplus) diff --git a/src/engine/package.cpp b/src/engine/package.cpp index 701dda08..b27b1e07 100644 --- a/src/engine/package.cpp +++ b/src/engine/package.cpp @@ -156,6 +156,7 @@ extern "C" HRESULT PackagesParseFromXml( hr = XmlGetYesNoAttribute(pixnNode, L"Permanent", &pPackage->fUninstallable); ExitOnFailure(hr, "Failed to get @Permanent."); pPackage->fUninstallable = !pPackage->fUninstallable; // TODO: change "Uninstallable" variable name to permanent, until then Uninstallable is the opposite of Permanent so fix the variable. + pPackage->fCanAffectRegistration = pPackage->fUninstallable; // @Vital hr = XmlGetYesNoAttribute(pixnNode, L"Vital", &pPackage->fVital); diff --git a/src/engine/package.h b/src/engine/package.h index 8f801e85..d3225fbc 100644 --- a/src/engine/package.h +++ b/src/engine/package.h @@ -78,6 +78,14 @@ enum BOOTSTRAPPER_FEATURE_ACTION BOOTSTRAPPER_FEATURE_ACTION_REMOVE, }; +enum BURN_PACKAGE_REGISTRATION_STATE +{ + BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, + BURN_PACKAGE_REGISTRATION_STATE_IGNORED, + BURN_PACKAGE_REGISTRATION_STATE_ABSENT, + BURN_PACKAGE_REGISTRATION_STATE_PRESENT, +}; + // structs typedef struct _BURN_EXE_EXIT_CODE @@ -106,6 +114,9 @@ typedef struct _BURN_MSPTARGETPRODUCT BOOTSTRAPPER_PACKAGE_STATE patchPackageState; // only valid after Detect. BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan. BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan. + + BURN_PACKAGE_REGISTRATION_STATE registrationState; // initialized during Detect, updated during Apply. + BURN_PACKAGE_REGISTRATION_STATE transactionRegistrationState;// only valid during Apply inside an MSI transaction. } BURN_MSPTARGETPRODUCT; typedef struct _BURN_MSIPROPERTY @@ -190,6 +201,7 @@ typedef struct _BURN_PACKAGE BOOL fPerMachine; BOOL fUninstallable; BOOL fVital; + BOOL fCanAffectRegistration; BURN_CACHE_TYPE cacheType; LPWSTR sczCacheId; @@ -216,6 +228,12 @@ typedef struct _BURN_PACKAGE BOOL fDependencyManagerWasHere; // only valid during Plan. HRESULT hrCacheResult; // only valid during Apply. + BURN_PACKAGE_REGISTRATION_STATE cacheRegistrationState; // initialized during Detect, updated during Apply. + BURN_PACKAGE_REGISTRATION_STATE installRegistrationState; // initialized during Detect, updated during Apply. + BURN_PACKAGE_REGISTRATION_STATE expectedCacheRegistrationState; // only valid after Plan. + BURN_PACKAGE_REGISTRATION_STATE expectedInstallRegistrationState;// only valid after Plan. + BURN_PACKAGE_REGISTRATION_STATE transactionRegistrationState; // only valid during Apply inside an MSI transaction. + BURN_PACKAGE_PAYLOAD* rgPayloads; DWORD cPayloads; diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index 6f5407b9..1aaec252 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -6,13 +6,6 @@ // internal struct definitions -struct PLAN_NONPERMANENT_PACKAGE_INDICES -{ - DWORD iAfterExecuteFirstNonPermanentPackage; - DWORD iBeforeRollbackFirstNonPermanentPackage; - DWORD iAfterExecuteLastNonPermanentPackage; - DWORD iAfterRollbackLastNonPermanentPackage; -}; // internal function definitions @@ -39,8 +32,7 @@ static HRESULT ProcessPackage( __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z_opt LPCWSTR wzLayoutDirectory, __inout HANDLE* phSyncpointEvent, - __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, - __in_opt PLAN_NONPERMANENT_PACKAGE_INDICES* pNonpermanentPackageIndices + __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary ); static HRESULT ProcessPackageRollbackBoundary( __in BURN_PLAN* pPlan, @@ -479,7 +471,7 @@ extern "C" HRESULT PlanPackages( __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, - __in BOOL fBundleInstalled, + __in BOOL /*fBundleInstalled*/, __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z_opt LPCWSTR wzLayoutDirectory, @@ -490,12 +482,6 @@ extern "C" HRESULT PlanPackages( BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine. BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; - PLAN_NONPERMANENT_PACKAGE_INDICES nonpermanentPackageIndices; - nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX; - nonpermanentPackageIndices.iBeforeRollbackFirstNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX; - nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX; - nonpermanentPackageIndices.iAfterRollbackLastNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX; - // Plan the packages. for (DWORD i = 0; i < pPackages->cPackages; ++i) { @@ -518,34 +504,10 @@ extern "C" HRESULT PlanPackages( } } - hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices); + hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary); ExitOnFailure(hr, "Failed to process package."); } - // Insert the "keep registration" and "remove registration" actions in the plan when installing the first time and anytime we are uninstalling respectively. - if (!fBundleInstalled && (BOOTSTRAPPER_ACTION_INSTALL == pPlan->action || BOOTSTRAPPER_ACTION_MODIFY == pPlan->action || BOOTSTRAPPER_ACTION_REPAIR == pPlan->action)) - { - if (BURN_PLAN_INVALID_ACTION_INDEX == nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage) - { - nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage = pPlan->cExecuteActions; - nonpermanentPackageIndices.iBeforeRollbackFirstNonPermanentPackage = pPlan->cRollbackActions; - } - - hr = PlanKeepRegistration(pPlan, nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage, nonpermanentPackageIndices.iBeforeRollbackFirstNonPermanentPackage); - ExitOnFailure(hr, "Failed to plan install keep registration."); - } - else if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) - { - if (BURN_PLAN_INVALID_ACTION_INDEX == nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage) - { - nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage = pPlan->cExecuteActions; - nonpermanentPackageIndices.iAfterRollbackLastNonPermanentPackage = pPlan->cRollbackActions; - } - - hr = PlanRemoveRegistration(pPlan, nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage, nonpermanentPackageIndices.iAfterRollbackLastNonPermanentPackage); - ExitOnFailure(hr, "Failed to plan uninstall remove registration."); - } - // If we still have an open rollback boundary, complete it. if (pRollbackBoundary) { @@ -572,9 +534,9 @@ LExit: extern "C" HRESULT PlanRegistration( __in BURN_PLAN* pPlan, __in BURN_REGISTRATION* pRegistration, - __in BOOTSTRAPPER_RESUME_TYPE resumeType, + __in BOOTSTRAPPER_RESUME_TYPE /*resumeType*/, __in BOOTSTRAPPER_RELATION_TYPE relationType, - __out BOOL* pfContinuePlanning + __inout BOOL* pfContinuePlanning ) { HRESULT hr = S_OK; @@ -583,9 +545,6 @@ extern "C" HRESULT PlanRegistration( pPlan->fRegister = TRUE; // register the bundle since we're modifying machine state. - // Keep the registration if the bundle was already installed or we are planning after a restart. - pPlan->fKeepRegistrationDefault = (pRegistration->fInstalled || BOOTSTRAPPER_RESUME_TYPE_REBOOT == resumeType); - pPlan->fDisallowRemoval = FALSE; // by default the bundle can be planned to be removed if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) @@ -692,7 +651,7 @@ extern "C" HRESULT PlanRegistration( pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE; pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION; } - else if (BOOTSTRAPPER_ACTION_REPAIR == pPlan->action && !CacheBundleRunningFromCache()) // repairing but not not running from the cache. + else if (BOOTSTRAPPER_ACTION_REPAIR == pPlan->action && !CacheBundleRunningFromCache()) // repairing but not running from the cache. { pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE; pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION; @@ -786,7 +745,7 @@ extern "C" HRESULT PlanPassThroughBundle( BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; // Plan passthrough package. - hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL); + hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary); ExitOnFailure(hr, "Failed to process passthrough package."); // If we still have an open rollback boundary, complete it. @@ -820,7 +779,7 @@ extern "C" HRESULT PlanUpdateBundle( BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; // Plan update package. - hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL); + hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary); ExitOnFailure(hr, "Failed to process update package."); // If we still have an open rollback boundary, complete it. @@ -849,8 +808,7 @@ static HRESULT ProcessPackage( __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z_opt LPCWSTR wzLayoutDirectory, __inout HANDLE* phSyncpointEvent, - __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, - __in_opt PLAN_NONPERMANENT_PACKAGE_INDICES* pNonpermanentPackageIndices + __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary ) { HRESULT hr = S_OK; @@ -871,6 +829,12 @@ static HRESULT ProcessPackage( hr = ProcessPackageRollbackBoundary(pPlan, pEffectiveRollbackBoundary, ppRollbackBoundary); ExitOnFailure(hr, "Failed to process package rollback boundary."); + if (pPackage->fCanAffectRegistration) + { + pPackage->expectedCacheRegistrationState = pPackage->cacheRegistrationState; + pPackage->expectedInstallRegistrationState = pPackage->installRegistrationState; + } + // If the package is in a requested state, plan it. if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested) { @@ -881,26 +845,19 @@ static HRESULT ProcessPackage( } else { - if (pPackage->fUninstallable && pNonpermanentPackageIndices) - { - if (BURN_PLAN_INVALID_ACTION_INDEX == pNonpermanentPackageIndices->iBeforeRollbackFirstNonPermanentPackage) - { - pNonpermanentPackageIndices->iBeforeRollbackFirstNonPermanentPackage = pPlan->cRollbackActions; - } - } - hr = PlanExecutePackage(fBundlePerMachine, display, pUX, pPlan, pPackage, pLog, pVariables, phSyncpointEvent); ExitOnFailure(hr, "Failed to plan execute package."); - if (pPackage->fUninstallable && pNonpermanentPackageIndices) + if (pPackage->fCanAffectRegistration) { - if (BURN_PLAN_INVALID_ACTION_INDEX == pNonpermanentPackageIndices->iAfterExecuteFirstNonPermanentPackage) + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pPackage->execute) { - pNonpermanentPackageIndices->iAfterExecuteFirstNonPermanentPackage = pPlan->cExecuteActions - 1; + pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + else if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) + { + pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; } - - pNonpermanentPackageIndices->iAfterExecuteLastNonPermanentPackage = pPlan->cExecuteActions; - pNonpermanentPackageIndices->iAfterRollbackLastNonPermanentPackage = pPlan->cRollbackActions; } } } @@ -920,6 +877,32 @@ static HRESULT ProcessPackage( } } + if (pPackage->fCanAffectRegistration) + { + if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute) + { + if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pPackage->expectedCacheRegistrationState) + { + pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pPackage->expectedInstallRegistrationState) + { + pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + } + else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pPackage->dependencyExecute) + { + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->expectedCacheRegistrationState) + { + pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->expectedInstallRegistrationState) + { + pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + } + } + // Add the checkpoint after each package and dependency registration action. if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute || BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback || BURN_DEPENDENCY_ACTION_NONE != pPackage->dependencyExecute) { @@ -1572,6 +1555,11 @@ extern "C" HRESULT PlanCleanPackage( pCleanAction->pPackage = pPackage; pPackage->fUncache = TRUE; + + if (pPackage->fCanAffectRegistration) + { + pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } } LExit: @@ -1708,68 +1696,6 @@ LExit: return hr; } -extern "C" HRESULT PlanKeepRegistration( - __in BURN_PLAN* pPlan, - __in DWORD iAfterExecutePackageAction, - __in DWORD iBeforeRollbackPackageAction - ) -{ - HRESULT hr = S_OK; - BURN_EXECUTE_ACTION* pAction = NULL; - - if (BURN_PLAN_INVALID_ACTION_INDEX != iAfterExecutePackageAction) - { - hr = PlanInsertExecuteAction(iAfterExecutePackageAction, pPlan, &pAction); - ExitOnFailure(hr, "Failed to insert keep registration execute action."); - - pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION; - pAction->registration.fKeep = TRUE; - } - - if (BURN_PLAN_INVALID_ACTION_INDEX != iBeforeRollbackPackageAction) - { - hr = PlanInsertRollbackAction(iBeforeRollbackPackageAction, pPlan, &pAction); - ExitOnFailure(hr, "Failed to insert keep registration rollback action."); - - pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION; - pAction->registration.fKeep = FALSE; - } - -LExit: - return hr; -} - -extern "C" HRESULT PlanRemoveRegistration( - __in BURN_PLAN* pPlan, - __in DWORD iAfterExecutePackageAction, - __in DWORD iAfterRollbackPackageAction - ) -{ - HRESULT hr = S_OK; - BURN_EXECUTE_ACTION* pAction = NULL; - - if (BURN_PLAN_INVALID_ACTION_INDEX != iAfterExecutePackageAction) - { - hr = PlanInsertExecuteAction(iAfterExecutePackageAction, pPlan, &pAction); - ExitOnFailure(hr, "Failed to insert remove registration execute action."); - - pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION; - pAction->registration.fKeep = FALSE; - } - - if (BURN_PLAN_INVALID_ACTION_INDEX != iAfterRollbackPackageAction) - { - hr = PlanInsertRollbackAction(iAfterRollbackPackageAction, pPlan, &pAction); - ExitOnFailure(hr, "Failed to insert remove registration rollback action."); - - pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION; - pAction->registration.fKeep = TRUE; - } - -LExit: - return hr; -} - extern "C" HRESULT PlanRollbackBoundaryBegin( __in BURN_PLAN* pPlan, __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary @@ -1925,6 +1851,8 @@ static void ResetPlannedPackageState( pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE; pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE; pPackage->fDependencyManagerWasHere = FALSE; + pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; + pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; if (BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.rgFeatures) { @@ -2198,6 +2126,11 @@ static HRESULT AddCachePackageHelper( // did cache operations to verify the cache is valid so we did not plan the acquisition of the package. pPackage->fAcquire = (BURN_CACHE_STATE_COMPLETE != pPackage->cache); + if (pPackage->fCanAffectRegistration) + { + pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + LExit: return hr; } @@ -3118,10 +3051,6 @@ static void ExecuteActionLog( LogStringLine(PlanDumpLevel, "%ls action[%u]: MSU_PACKAGE package id: %ls, action: %hs, log path: %ls", wzBase, iAction, pAction->msuPackage.pPackage->sczId, LoggingActionStateToString(pAction->msuPackage.action), pAction->msuPackage.sczLogPath); break; - case BURN_EXECUTE_ACTION_TYPE_REGISTRATION: - LogStringLine(PlanDumpLevel, "%ls action[%u]: REGISTRATION keep: %ls", wzBase, iAction, pAction->registration.fKeep ? L"yes" : L"no"); - break; - case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY: LogStringLine(PlanDumpLevel, "%ls action[%u]: ROLLBACK_BOUNDARY id: %ls, vital: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no"); break; @@ -3157,7 +3086,6 @@ extern "C" void PlanDump( LogStringLine(PlanDumpLevel, "Plan action: %hs", LoggingBurnActionToString(pPlan->action)); LogStringLine(PlanDumpLevel, " per-machine: %hs", LoggingTrueFalseToString(pPlan->fPerMachine)); LogStringLine(PlanDumpLevel, " disable-rollback: %hs", LoggingTrueFalseToString(pPlan->fDisableRollback)); - LogStringLine(PlanDumpLevel, " keep registration by default: %hs", LoggingTrueFalseToString(pPlan->fKeepRegistrationDefault)); LogStringLine(PlanDumpLevel, " estimated size: %llu", pPlan->qwEstimatedSize); LogStringLine(PlanDumpLevel, "Plan cache size: %llu", pPlan->qwCacheSizeTotal); diff --git a/src/engine/plan.h b/src/engine/plan.h index 54189973..6c12b5fa 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -64,7 +64,6 @@ enum BURN_EXECUTE_ACTION_TYPE BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER, BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY, BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY, - BURN_EXECUTE_ACTION_TYPE_REGISTRATION, BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION, BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION, }; @@ -281,10 +280,6 @@ typedef struct _BURN_EXECUTE_ACTION BOOTSTRAPPER_ACTION_STATE action; } msuPackage; struct - { - BOOL fKeep; - } registration; - struct { BURN_ROLLBACK_BOUNDARY* pRollbackBoundary; } rollbackBoundary; @@ -319,7 +314,6 @@ typedef struct _BURN_PLAN BOOL fPerMachine; BOOL fRegister; DWORD dwRegistrationOperations; - BOOL fKeepRegistrationDefault; BOOL fDisallowRemoval; BOOL fDisableRollback; @@ -418,7 +412,7 @@ HRESULT PlanRegistration( __in BURN_REGISTRATION* pRegistration, __in BOOTSTRAPPER_RESUME_TYPE resumeType, __in BOOTSTRAPPER_RELATION_TYPE relationType, - __out BOOL* pfContinuePlanning + __inout BOOL* pfContinuePlanning ); HRESULT PlanPassThroughBundle( __in BURN_USER_EXPERIENCE* pUX, @@ -519,16 +513,6 @@ HRESULT PlanAppendRollbackAction( __in BURN_PLAN* pPlan, __out BURN_EXECUTE_ACTION** ppExecuteAction ); -HRESULT PlanKeepRegistration( - __in BURN_PLAN* pPlan, - __in DWORD iAfterExecutePackageAction, - __in DWORD iBeforeRollbackPackageAction - ); -HRESULT PlanRemoveRegistration( - __in BURN_PLAN* pPlan, - __in DWORD iAfterExecutePackageAction, - __in DWORD iAfterRollbackPackageAction - ); HRESULT PlanRollbackBoundaryBegin( __in BURN_PLAN* pPlan, __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary diff --git a/src/test/BurnUnitTest/BurnTestFixture.h b/src/test/BurnUnitTest/BurnTestFixture.h index 6b041641..103972ef 100644 --- a/src/test/BurnUnitTest/BurnTestFixture.h +++ b/src/test/BurnUnitTest/BurnTestFixture.h @@ -35,6 +35,8 @@ namespace Bootstrapper LogInitialize(::GetModuleHandleW(NULL)); + LogSetLevel(REPORT_DEBUG, FALSE); + hr = LogOpen(NULL, L"BurnUnitTest", NULL, L"txt", FALSE, FALSE, NULL); TestThrowOnFailure(hr, L"Failed to open log."); } diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp index a65bef4d..a50c64e1 100644 --- a/src/test/BurnUnitTest/PlanTest.cpp +++ b/src/test/BurnUnitTest/PlanTest.cpp @@ -50,13 +50,12 @@ namespace Bootstrapper Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); Assert::Equal(TRUE, pPlan->fPerMachine); Assert::Equal(FALSE, pPlan->fDisableRollback); - Assert::Equal(FALSE, pPlan->fKeepRegistrationDefault); BOOL fRollback = FALSE; DWORD dwIndex = 0; DWORD dwPackageStart = 0; ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", 6, 2, 33743, FALSE); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, 6, 2, 33743, FALSE); ValidateCacheAcquireContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE); ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, dwPackageStart, 6); ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"PackageA", TRUE, FALSE, dwPackageStart); @@ -64,7 +63,7 @@ namespace Bootstrapper ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 9); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageB", 14, 2, 33743, FALSE); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, 14, 2, 33743, FALSE); ValidateCacheAcquireContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", TRUE); ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, dwPackageStart, 2); ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageB", L"PackageB", TRUE, FALSE, dwPackageStart); @@ -72,7 +71,7 @@ namespace Bootstrapper ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageB", FALSE); ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 14); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageC", 22, 2, 33743, FALSE); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, 22, 2, 33743, FALSE); ValidateCacheAcquireContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", TRUE); ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, dwPackageStart, 2); ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageC", L"PackageC", TRUE, FALSE, dwPackageStart); @@ -99,9 +98,8 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteRegistration(pPlan, fRollback, dwIndex++, TRUE); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -113,7 +111,7 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -122,26 +120,25 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCommitMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[23].syncpoint.hEvent); - ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL); Assert::Equal(dwIndex, pPlan->cExecuteActions); fRollback = TRUE; dwIndex = 0; dwExecuteCheckpointId = 2; ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteRegistration(pPlan, fRollback, dwIndex++, FALSE); ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -166,7 +163,7 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL); Assert::Equal(dwIndex, pPlan->cRollbackActions); Assert::Equal(4ul, pPlan->cExecutePackagesTotal); @@ -197,7 +194,6 @@ namespace Bootstrapper Assert::Equal(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action); Assert::Equal(TRUE, pPlan->fPerMachine); Assert::Equal(FALSE, pPlan->fDisableRollback); - Assert::Equal(TRUE, pPlan->fKeepRegistrationDefault); BOOL fRollback = FALSE; DWORD dwIndex = 0; @@ -220,13 +216,13 @@ namespace Bootstrapper ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCommitMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -236,8 +232,7 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteRegistration(pPlan, fRollback, dwIndex++, FALSE); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); Assert::Equal(dwIndex, pPlan->cExecuteActions); @@ -263,9 +258,8 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteRegistration(pPlan, fRollback, dwIndex++, TRUE); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); Assert::Equal(dwIndex, pPlan->cRollbackActions); @@ -274,9 +268,9 @@ namespace Bootstrapper Assert::Equal(3ul, pPlan->cOverallProgressTicksTotal); dwIndex = 0; - ValidateCleanAction(pPlan, dwIndex++, L"PackageC"); - ValidateCleanAction(pPlan, dwIndex++, L"PackageB"); - ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); + ValidateCleanAction(pPlan, dwIndex++, L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + ValidateCleanAction(pPlan, dwIndex++, L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + ValidateCleanAction(pPlan, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT); Assert::Equal(dwIndex, pPlan->cCleanActions); UINT uIndex = 0; @@ -306,13 +300,12 @@ namespace Bootstrapper Assert::Equal(BOOTSTRAPPER_ACTION_CACHE, pPlan->action); Assert::Equal(TRUE, pPlan->fPerMachine); Assert::Equal(FALSE, pPlan->fDisableRollback); - Assert::Equal(FALSE, pPlan->fKeepRegistrationDefault); BOOL fRollback = FALSE; DWORD dwIndex = 0; DWORD dwPackageStart = 0; ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", 5, 2, 33743, FALSE); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, 5, 2, 33743, FALSE); ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, BURN_PLAN_INVALID_ACTION_INDEX, 2); ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"PackageA", TRUE, FALSE, dwPackageStart); ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"cab9Ins_fTP3wNwq5Gxo41ch5VUPaQ", TRUE, FALSE, dwPackageStart); @@ -378,13 +371,12 @@ namespace Bootstrapper Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); Assert::Equal(TRUE, pPlan->fPerMachine); Assert::Equal(FALSE, pPlan->fDisableRollback); - Assert::Equal(FALSE, pPlan->fKeepRegistrationDefault); BOOL fRollback = FALSE; DWORD dwIndex = 0; DWORD dwPackageStart = 0; ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", 5, 2, 33743, FALSE); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, 5, 2, 33743, FALSE); ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, BURN_PLAN_INVALID_ACTION_INDEX, 2); ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"PackageA", TRUE, FALSE, dwPackageStart); ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"cab9Ins_fTP3wNwq5Gxo41ch5VUPaQ", TRUE, FALSE, dwPackageStart); @@ -410,32 +402,30 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteRegistration(pPlan, fRollback, dwIndex++, TRUE); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[6].syncpoint.hEvent); - ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL); Assert::Equal(dwIndex, pPlan->cExecuteActions); fRollback = TRUE; dwIndex = 0; dwExecuteCheckpointId = 2; ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteRegistration(pPlan, fRollback, dwIndex++, FALSE); ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL); Assert::Equal(dwIndex, pPlan->cRollbackActions); Assert::Equal(2ul, pPlan->cExecutePackagesTotal); @@ -449,6 +439,62 @@ namespace Bootstrapper Assert::Equal(uIndex, pPlan->cPlannedProviders); } + [Fact] + void SingleMsiInstalledWithNoInstalledPackagesModifyTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); + PlanTestDetect(pEngineState); + + pEngineState->registration.fInstalled = TRUE; + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_MODIFY); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_MODIFY, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(0ull, pPlan->qwEstimatedSize); + Assert::Equal(0ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(0ul, pPlan->cExecutePackagesTotal); + Assert::Equal(0ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + } + [Fact] void SingleMsiUninstallTest() { @@ -466,7 +512,6 @@ namespace Bootstrapper Assert::Equal(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action); Assert::Equal(TRUE, pPlan->fPerMachine); Assert::Equal(FALSE, pPlan->fDisableRollback); - Assert::Equal(TRUE, pPlan->fKeepRegistrationDefault); BOOL fRollback = FALSE; DWORD dwIndex = 0; @@ -488,8 +533,7 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteRegistration(pPlan, fRollback, dwIndex++, FALSE); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); Assert::Equal(dwIndex, pPlan->cExecuteActions); @@ -502,9 +546,8 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteRegistration(pPlan, fRollback, dwIndex++, TRUE); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); Assert::Equal(dwIndex, pPlan->cRollbackActions); @@ -513,7 +556,7 @@ namespace Bootstrapper Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal); dwIndex = 0; - ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); + ValidateCleanAction(pPlan, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT); Assert::Equal(dwIndex, pPlan->cCleanActions); UINT uIndex = 0; @@ -522,6 +565,66 @@ namespace Bootstrapper Assert::Equal(uIndex, pPlan->cPlannedProviders); } + [Fact] + void SingleMsiUninstallTestFromUpgradeBundleWithSameExactPackage() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); + DetectAsRelatedUpgradeBundle(&engineState, L"{02940F3E-C83E-452D-BFCF-C943777ACEAE}", L"2.0.0.0"); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(0ull, pPlan->qwEstimatedSize); + Assert::Equal(0ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(0ul, pPlan->cExecutePackagesTotal); + Assert::Equal(0ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + } + private: // This doesn't initialize everything, just enough for CorePlan to work. void InitializeEngineStateForCorePlan(LPCWSTR wzManifestFileName, BURN_ENGINE_STATE* pEngineState) @@ -586,12 +689,22 @@ namespace Bootstrapper void DetectPackageAsAbsent(BURN_PACKAGE* pPackage) { pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + if (pPackage->fCanAffectRegistration) + { + pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } } void DetectPackageAsPresentAndCached(BURN_PACKAGE* pPackage) { pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; pPackage->cache = BURN_CACHE_STATE_COMPLETE; + if (pPackage->fCanAffectRegistration) + { + pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } for (DWORD i = 0; i < pPackage->cPayloads; ++i) { @@ -599,6 +712,19 @@ namespace Bootstrapper } } + void DetectPackageDependent(BURN_PACKAGE* pPackage, LPCWSTR wzId) + { + HRESULT hr = S_OK; + + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + i; + + hr = DepDependencyArrayAlloc(&pProvider->rgDependents, &pProvider->cDependents, wzId, NULL); + NativeAssert::Succeeded(hr, "Failed to add package dependent"); + } + } + void DetectPackagesAsAbsent(BURN_ENGINE_STATE* pEngineState) { PlanTestDetect(pEngineState); @@ -620,6 +746,7 @@ namespace Bootstrapper { BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; DetectPackageAsPresentAndCached(pPackage); + DetectPackageDependent(pPackage, pEngineState->registration.sczId); } } @@ -639,11 +766,12 @@ namespace Bootstrapper else { DetectPackageAsPresentAndCached(pPackage); + DetectPackageDependent(pPackage, pEngineState->registration.sczId); } } } - HRESULT DetectUpgradeBundle( + void DetectUpgradeBundle( __in BURN_ENGINE_STATE* pEngineState, __in LPCWSTR wzId, __in LPCWSTR wzVersion @@ -654,30 +782,48 @@ namespace Bootstrapper BURN_DEPENDENCY_PROVIDER dependencyProvider = { }; hr = StrAllocString(&dependencyProvider.sczKey, wzId, 0); - ExitOnFailure(hr, "Failed to copy provider key"); + NativeAssert::Succeeded(hr, "Failed to copy provider key"); dependencyProvider.fImported = TRUE; hr = StrAllocString(&dependencyProvider.sczVersion, wzVersion, 0); - ExitOnFailure(hr, "Failed to copy version"); + NativeAssert::Succeeded(hr, "Failed to copy version"); hr = MemEnsureArraySize(reinterpret_cast(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5); - ExitOnFailure(hr, "Failed to ensure there is space for related bundles."); + NativeAssert::Succeeded(hr, "Failed to ensure there is space for related bundles."); BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles; hr = VerParseVersion(wzVersion, 0, FALSE, &pRelatedBundle->pVersion); - ExitOnFailure(hr, "Failed to parse pseudo bundle version: %ls", wzVersion); + NativeAssert::Succeeded(hr, "Failed to parse pseudo bundle version: %ls", wzVersion); pRelatedBundle->relationType = BOOTSTRAPPER_RELATION_UPGRADE; hr = PseudoBundleInitialize(0, &pRelatedBundle->package, TRUE, wzId, pRelatedBundle->relationType, BOOTSTRAPPER_PACKAGE_STATE_PRESENT, NULL, NULL, NULL, 0, FALSE, L"-quiet", L"-repair -quiet", L"-uninstall -quiet", &dependencyProvider, NULL, 0); - ExitOnFailure(hr, "Failed to initialize related bundle to represent bundle: %ls", wzId); + NativeAssert::Succeeded(hr, "Failed to initialize related bundle to represent bundle: %ls", wzId); ++pRelatedBundles->cRelatedBundles; + } + + void DetectAsRelatedUpgradeBundle( + __in BURN_ENGINE_STATE* pEngineState, + __in LPCWSTR wzId, + __in LPCWSTR wzVersion + ) + { + HRESULT hr = StrAllocString(&pEngineState->registration.sczAncestors, wzId, 0); + NativeAssert::Succeeded(hr, "Failed to set registration's ancestors"); + + pEngineState->command.relationType = BOOTSTRAPPER_RELATION_UPGRADE; - LExit: - return hr; + DetectPackagesAsPresentAndCached(pEngineState); + DetectUpgradeBundle(pEngineState, wzId, wzVersion); + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + DetectPackageDependent(pPackage, wzId); + } } void ValidateCacheAcquireContainer( @@ -755,6 +901,7 @@ namespace Bootstrapper __in BOOL fRollback, __in DWORD dwIndex, __in LPCWSTR wzPackageId, + __in BURN_PACKAGE_REGISTRATION_STATE expectedCacheRegistrationState, __in DWORD iPackageCompleteAction, __in DWORD cCachePayloads, __in DWORD64 qwCachePayloadSizeTotal, @@ -764,6 +911,7 @@ namespace Bootstrapper BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); Assert::Equal(BURN_CACHE_ACTION_TYPE_PACKAGE_START, pAction->type); NativeAssert::StringEqual(wzPackageId, pAction->packageStart.pPackage->sczId); + Assert::Equal(expectedCacheRegistrationState, pAction->packageStart.pPackage->expectedCacheRegistrationState); Assert::Equal(iPackageCompleteAction, pAction->packageStart.iPackageCompleteAction); Assert::Equal(cCachePayloads, pAction->packageStart.cCachePayloads); Assert::Equal(qwCachePayloadSizeTotal, pAction->packageStart.qwCachePayloadSizeTotal); @@ -815,7 +963,8 @@ namespace Bootstrapper void ValidateCleanAction( __in BURN_PLAN* pPlan, __in DWORD dwIndex, - __in LPCWSTR wzPackageId + __in LPCWSTR wzPackageId, + __in BURN_PACKAGE_REGISTRATION_STATE expectedCacheRegistrationState ) { Assert::InRange(dwIndex + 1ul, 1ul, pPlan->cCleanActions); @@ -823,6 +972,7 @@ namespace Bootstrapper BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + dwIndex; Assert::NotEqual((DWORD_PTR)0, (DWORD_PTR)pCleanAction->pPackage); NativeAssert::StringEqual(wzPackageId, pCleanAction->pPackage->sczId); + Assert::Equal(expectedCacheRegistrationState, pCleanAction->pPackage->expectedCacheRegistrationState); } BURN_EXECUTE_ACTION* ValidateExecuteActionExists(BURN_PLAN* pPlan, BOOL fRollback, DWORD dwIndex) @@ -872,6 +1022,7 @@ namespace Bootstrapper __in BOOL fRollback, __in DWORD dwIndex, __in LPCWSTR wzPackageId, + __in BURN_PACKAGE_REGISTRATION_STATE expectedInstallRegistrationState, __in BOOTSTRAPPER_ACTION_STATE action, __in LPCWSTR wzIgnoreDependencies ) @@ -879,6 +1030,7 @@ namespace Bootstrapper BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); Assert::Equal(BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE, pAction->type); NativeAssert::StringEqual(wzPackageId, pAction->exePackage.pPackage->sczId); + Assert::Equal(expectedInstallRegistrationState, pAction->exePackage.pPackage->expectedInstallRegistrationState); Assert::Equal(action, pAction->exePackage.action); NativeAssert::StringEqual(wzIgnoreDependencies, pAction->exePackage.sczIgnoreDependencies); } @@ -888,6 +1040,7 @@ namespace Bootstrapper __in BOOL fRollback, __in DWORD dwIndex, __in LPCWSTR wzPackageId, + __in BURN_PACKAGE_REGISTRATION_STATE expectedInstallRegistrationState, __in BOOTSTRAPPER_ACTION_STATE action, __in BURN_MSI_PROPERTY actionMsiProperty, __in DWORD uiLevel, @@ -898,6 +1051,7 @@ namespace Bootstrapper BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); Assert::Equal(BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE, pAction->type); NativeAssert::StringEqual(wzPackageId, pAction->msiPackage.pPackage->sczId); + Assert::Equal(expectedInstallRegistrationState, pAction->msiPackage.pPackage->expectedInstallRegistrationState); Assert::Equal(action, pAction->msiPackage.action); Assert::Equal(actionMsiProperty, pAction->msiPackage.actionMsiProperty); Assert::Equal(uiLevel, pAction->msiPackage.uiLevel); @@ -936,18 +1090,6 @@ namespace Bootstrapper Assert::Equal(action, pAction->packageProvider.action); } - void ValidateExecuteRegistration( - __in BURN_PLAN* pPlan, - __in BOOL fRollback, - __in DWORD dwIndex, - __in BOOL fKeep - ) - { - BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_EXECUTE_ACTION_TYPE_REGISTRATION, pAction->type); - Assert::Equal(fKeep, pAction->registration.fKeep); - } - void ValidateExecuteRollbackBoundary( __in BURN_PLAN* pPlan, __in BOOL fRollback, -- cgit v1.2.3-55-g6feb From c6c17104b50936432a3fe9ca214ba9a3dfa32780 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 3 Feb 2021 17:09:50 -0600 Subject: Automatically uninstall the bundle after Quit if eligible. For now, the requirements are: * The bundle is installed and * The bundle is per-user or has already elevated and * No non-permanent packages are installed and * No non-permanent packages are cached and * No related bundle would run by default during uninstall and * The bundle didn't Uninstall/Cache/Install/Modify/Repair and * The BA didn't opt out of this behavior --- .../inc/BootstrapperApplication.h | 4 ++ src/engine/core.cpp | 62 ++++++++++++++++++++-- src/engine/core.h | 3 ++ src/engine/detect.cpp | 21 +++++++- src/engine/detect.h | 3 +- src/engine/engine.cpp | 21 ++++++-- src/engine/engine.mc | 9 +++- src/engine/registration.h | 1 + src/engine/userexperience.cpp | 16 +++++- src/engine/userexperience.h | 3 +- 10 files changed, 131 insertions(+), 12 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h index c0baa958..48bd813d 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -221,6 +221,9 @@ enum BOOTSTRAPPER_SHUTDOWN_ACTION // restart the engine which will load the bootstrapper application again. // Typically used to switch from a native bootstrapper application to a managed one. BOOTSTRAPPER_SHUTDOWN_ACTION_RELOAD_BOOTSTRAPPER, + // Opts out of the engine behavior of trying to uninstall itself + // when no non-permanent packages are installed. + BOOTSTRAPPER_SHUTDOWN_ACTION_SKIP_CLEANUP, }; enum BURN_MSI_PROPERTY @@ -470,6 +473,7 @@ struct BA_ONDETECTCOMPLETE_ARGS { DWORD cbSize; HRESULT hrStatus; + BOOL fEligibleForCleanup; }; struct BA_ONDETECTCOMPLETE_RESULTS diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 36471e93..1503f8d8 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -300,7 +300,7 @@ extern "C" HRESULT CoreDetect( ExitOnFailure(hr, "Failed to detect provider key bundle id."); // Report the related bundles. - hr = DetectReportRelatedBundles(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, pEngineState->command.action); + hr = DetectReportRelatedBundles(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, pEngineState->command.action, &pEngineState->registration.fEligibleForCleanup); ExitOnFailure(hr, "Failed to report detected related bundles."); // Do update detection. @@ -344,6 +344,14 @@ extern "C" HRESULT CoreDetect( { pPackage = pEngineState->packages.rgPackages + iPackage; + // If any packages that can affect registration are present, then the bundle should not automatically be uninstalled. + if (pEngineState->registration.fEligibleForCleanup && pPackage->fCanAffectRegistration && + (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState || + BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState)) + { + pEngineState->registration.fEligibleForCleanup = FALSE; + } + LogId(REPORT_STANDARD, MSG_DETECTED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingCacheStateToString(pPackage->cache), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->installRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->cacheRegistrationState)); if (BURN_PACKAGE_TYPE_MSI == pPackage->type) @@ -377,12 +385,12 @@ LExit: if (fDetectBegan) { - UserExperienceOnDetectComplete(&pEngineState->userExperience, hr); + UserExperienceOnDetectComplete(&pEngineState->userExperience, hr, pEngineState->registration.fEligibleForCleanup); } pEngineState->userExperience.hwndDetect = NULL; - LogId(REPORT_STANDARD, MSG_DETECT_COMPLETE, hr); + LogId(REPORT_STANDARD, MSG_DETECT_COMPLETE, hr, !fDetectBegan ? "(failed)" : LoggingBoolToString(pEngineState->registration.fInstalled), FAILED(hr) ? "(failed)" : LoggingBoolToString(pEngineState->registration.fEligibleForCleanup)); return hr; } @@ -1053,6 +1061,54 @@ LExit: return hr; } +extern "C" HRESULT CoreCleanup( + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + LONGLONG llValue = 0; + BOOL fNeedsElevation = pEngineState->registration.fPerMachine && INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe; + + if (fNeedsElevation) + { + hr = VariableGetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, &llValue); + ExitOnFailure(hr, "Failed to get value of WixBundleElevated variable during cleanup"); + + if (llValue) + { + fNeedsElevation = FALSE; + } + } + + if (pEngineState->fApplied && BOOTSTRAPPER_ACTION_LAYOUT < pEngineState->plan.action && BOOTSTRAPPER_ACTION_UPDATE_REPLACE > pEngineState->plan.action || + fNeedsElevation) + { + ExitFunction(); + } + + if (!pEngineState->fDetected) + { + hr = CoreDetect(pEngineState, pEngineState->hMessageWindow); + ExitOnFailure(hr, "Detect during cleanup failed"); + } + + if (!pEngineState->registration.fEligibleForCleanup) + { + ExitFunction(); + } + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); + ExitOnFailure(hr, "Plan during cleanup failed"); + + hr = CoreApply(pEngineState, pEngineState->hMessageWindow); + ExitOnFailure(hr, "Apply during cleanup failed"); + + // Need to think about cache=always + +LExit: + return hr; +} + // internal helper functions static HRESULT ParseCommandLine( diff --git a/src/engine/core.h b/src/engine/core.h index fd7311e3..47cfd559 100644 --- a/src/engine/core.h +++ b/src/engine/core.h @@ -204,6 +204,9 @@ HRESULT CoreAppendFileHandleSelfToCommandLine( __deref_inout_z LPWSTR* psczCommandLine, __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine ); +HRESULT CoreCleanup( + __in BURN_ENGINE_STATE* pEngineState + ); #if defined(__cplusplus) } diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp index 159df3d0..b702306e 100644 --- a/src/engine/detect.cpp +++ b/src/engine/detect.cpp @@ -42,6 +42,7 @@ extern "C" void DetectReset( pRegistration->fEnabledForwardCompatibleBundle = FALSE; PackageUninitialize(&pRegistration->forwardCompatibleBundle); pRegistration->fSelfRegisteredAsDependent = FALSE; + pRegistration->fEligibleForCleanup = FALSE; if (pRegistration->rgIgnoredDependencies) { @@ -184,11 +185,14 @@ extern "C" HRESULT DetectReportRelatedBundles( __in BURN_USER_EXPERIENCE* pUX, __in BURN_REGISTRATION* pRegistration, __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in BOOTSTRAPPER_ACTION action + __in BOOTSTRAPPER_ACTION action, + __out BOOL* pfEligibleForCleanup ) { HRESULT hr = S_OK; int nCompareResult = 0; + BOOTSTRAPPER_REQUEST_STATE uninstallRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; + *pfEligibleForCleanup = pRegistration->fInstalled; for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) { @@ -201,7 +205,7 @@ extern "C" HRESULT DetectReportRelatedBundles( if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < action) { hr = VerCompareParsedVersions(pRegistration->pVersion, pRelatedBundle->pVersion, &nCompareResult); - ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistration->pVersion, pRelatedBundle->pVersion); + ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistration->pVersion->sczVersion, pRelatedBundle->pVersion->sczVersion); if (nCompareResult < 0) { @@ -244,6 +248,19 @@ extern "C" HRESULT DetectReportRelatedBundles( hr = UserExperienceOnDetectRelatedBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, operation); ExitOnRootFailure(hr, "BA aborted detect related bundle."); + + // For now, if any related bundles will be executed during uninstall by default then never automatically clean up the bundle. + if (*pfEligibleForCleanup) + { + uninstallRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; + hr = PlanDefaultRelatedBundleRequestState(relationType, pRelatedBundle->relationType, BOOTSTRAPPER_ACTION_UNINSTALL, pRegistration->pVersion, pRelatedBundle->pVersion, &uninstallRequestState); + ExitOnFailure(hr, "Failed to get the default request state for related bundle for calculating fEligibleForCleanup"); + + if (BOOTSTRAPPER_REQUEST_STATE_NONE != uninstallRequestState) + { + *pfEligibleForCleanup = FALSE; + } + } } LExit: diff --git a/src/engine/detect.h b/src/engine/detect.h index 01488f1a..7989c9dd 100644 --- a/src/engine/detect.h +++ b/src/engine/detect.h @@ -30,7 +30,8 @@ HRESULT DetectReportRelatedBundles( __in BURN_USER_EXPERIENCE* pUX, __in BURN_REGISTRATION* pRegistration, __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in BOOTSTRAPPER_ACTION action + __in BOOTSTRAPPER_ACTION action, + __out BOOL* pfEligibleForCleanup ); HRESULT DetectUpdate( diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index e3ace592..bc27cb14 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -39,7 +39,8 @@ static HRESULT RunRunOnce( ); static HRESULT RunApplication( __in BURN_ENGINE_STATE* pEngineState, - __out BOOL* pfReloadApp + __out BOOL* pfReloadApp, + __out BOOL* pfSkipCleanup ); static HRESULT ProcessMessage( __in BURN_ENGINE_STATE* pEngineState, @@ -529,6 +530,7 @@ static HRESULT RunNormal( HANDLE hPipesCreatedEvent = NULL; BOOL fContinueExecution = TRUE; BOOL fReloadApp = FALSE; + BOOL fSkipCleanup = FALSE; BURN_EXTENSION_ENGINE_CONTEXT extensionEngineContext = { }; // Initialize logging. @@ -584,11 +586,18 @@ static HRESULT RunNormal( do { fReloadApp = FALSE; + pEngineState->fQuit = FALSE; - hr = RunApplication(pEngineState, &fReloadApp); + hr = RunApplication(pEngineState, &fReloadApp, &fSkipCleanup); ExitOnFailure(hr, "Failed while running "); } while (fReloadApp); + if (!fSkipCleanup) + { + hr = CoreCleanup(pEngineState); + ExitOnFailure(hr, "Failed to cleanup before shutting down"); + } + LExit: BurnExtensionUnload(&pEngineState->extensions); @@ -732,7 +741,8 @@ LExit: static HRESULT RunApplication( __in BURN_ENGINE_STATE* pEngineState, - __out BOOL* pfReloadApp + __out BOOL* pfReloadApp, + __out BOOL* pfSkipCleanup ) { HRESULT hr = S_OK; @@ -787,6 +797,11 @@ LExit: LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RELOAD); *pfReloadApp = TRUE; } + else if (BOOTSTRAPPER_SHUTDOWN_ACTION_SKIP_CLEANUP == shutdownAction) + { + LogId(REPORT_STANDARD, MSG_BA_REQUESTED_SKIP_CLEANUP); + *pfSkipCleanup = TRUE; + } } // Unload BA. diff --git a/src/engine/engine.mc b/src/engine/engine.mc index 59a05676..c90f08e3 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -121,6 +121,13 @@ Language=English The manifest contains an invalid version string: '%1!ls!' . +MessageId=14 +Severity=Success +SymbolicName=MSG_BA_REQUESTED_SKIP_CLEANUP +Language=English +Bootstrapper application opted out of any engine behavior to automatically uninstall the bundle during shutdown. +. + MessageId=51 Severity=Error SymbolicName=MSG_FAILED_PARSE_CONDITION @@ -286,7 +293,7 @@ MessageId=199 Severity=Success SymbolicName=MSG_DETECT_COMPLETE Language=English -Detect complete, result: 0x%1!x! +Detect complete, result: 0x%1!x!, installed: %2!hs!, eligible for cleanup: %3!hs! . MessageId=200 diff --git a/src/engine/registration.h b/src/engine/registration.h index c1e52ac9..55d5a4c8 100644 --- a/src/engine/registration.h +++ b/src/engine/registration.h @@ -146,6 +146,7 @@ typedef struct _BURN_REGISTRATION UINT cDependents; // Only valid after detect. LPCWSTR wzSelfDependent; // Only valid after detect. BOOL fSelfRegisteredAsDependent; // Only valid after detect. + BOOL fEligibleForCleanup; // Only valid after detect. LPWSTR sczDetectedProviderKeyBundleId; LPWSTR sczAncestors; diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index 3a36cab6..a0fb341d 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -736,7 +736,8 @@ LExit: EXTERN_C BAAPI UserExperienceOnDetectComplete( __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus + __in HRESULT hrStatus, + __in BOOL fEligibleForCleanup ) { HRESULT hr = S_OK; @@ -745,6 +746,7 @@ EXTERN_C BAAPI UserExperienceOnDetectComplete( args.cbSize = sizeof(args); args.hrStatus = hrStatus; + args.fEligibleForCleanup = fEligibleForCleanup; results.cbSize = sizeof(results); @@ -2296,12 +2298,18 @@ static HRESULT SendBAMessage( { HRESULT hr = S_OK; + if (!pUserExperience->hUXModule) + { + ExitFunction(); + } + hr = pUserExperience->pfnBAProc(message, pvArgs, pvResults, pUserExperience->pvBAProcContext); if (hr == E_NOTIMPL) { hr = S_OK; } +LExit: return hr; } @@ -2314,11 +2322,17 @@ static HRESULT SendBAMessageFromInactiveEngine( { HRESULT hr = S_OK; + if (!pUserExperience->hUXModule) + { + ExitFunction(); + } + UserExperienceDeactivateEngine(pUserExperience); hr = SendBAMessage(pUserExperience, message, pvArgs, pvResults); UserExperienceActivateEngine(pUserExperience); +LExit: return hr; } diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index f51c09ff..363c0f06 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -191,7 +191,8 @@ BAAPI UserExperienceOnDetectBegin( ); BAAPI UserExperienceOnDetectComplete( __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus + __in HRESULT hrStatus, + __in BOOL fEligibleForCleanup ); BAAPI UserExperienceOnDetectForwardCompatibleBundle( __in BURN_USER_EXPERIENCE* pUserExperience, -- cgit v1.2.3-55-g6feb From dc992c49f30e0d2b912a6449a33b4448ef862f31 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 3 Feb 2021 17:16:21 -0600 Subject: Change the implementation of Cache="always" to request the CACHE state. This makes it possible for the prereq BA to not cache those packages while installing the prereqs, which allows the engine to automatically cleanup if necessary. #6297 for this commit and the previous 6. --- src/engine/plan.cpp | 96 +++++++++++--------------------------- src/engine/plan.h | 9 +--- src/test/BurnUnitTest/PlanTest.cpp | 2 +- 3 files changed, 30 insertions(+), 77 deletions(-) diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index 1aaec252..a11d0e78 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -153,7 +153,6 @@ static HRESULT CalculateExecuteActions( __out_opt BOOL* pfBARequestedCache ); static BOOL NeedsCache( - __in BURN_PLAN* pPlan, __in BURN_PACKAGE* pPackage ); static HRESULT CreateContainerProgress( @@ -323,6 +322,7 @@ extern "C" HRESULT PlanDefaultPackageRequestState( __in BURN_PACKAGE_TYPE packageType, __in BOOTSTRAPPER_PACKAGE_STATE currentState, __in BOOL fPermanent, + __in BURN_CACHE_TYPE cacheType, __in BOOTSTRAPPER_ACTION action, __in BURN_VARIABLES* pVariables, __in_z_opt LPCWSTR wzInstallCondition, @@ -333,12 +333,19 @@ extern "C" HRESULT PlanDefaultPackageRequestState( HRESULT hr = S_OK; BOOTSTRAPPER_REQUEST_STATE defaultRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; BOOL fCondition = FALSE; + BOOL fFallbackToCache = BURN_CACHE_TYPE_ALWAYS == cacheType && BOOTSTRAPPER_ACTION_UNINSTALL != action && BOOTSTRAPPER_PACKAGE_STATE_CACHED > currentState; // If doing layout, then always default to requesting the file be cached. if (BOOTSTRAPPER_ACTION_LAYOUT == action) { *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE; } + else if (BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED == currentState && BOOTSTRAPPER_ACTION_UNINSTALL != action) + { + // Superseded means the package is on the machine but not active, so only uninstall operations are allowed. + // Requesting present makes sure always-cached packages are cached. + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; + } else if (BOOTSTRAPPER_RELATION_PATCH == relationType && BURN_PACKAGE_TYPE_MSP == packageType) { // For patch related bundles, only install a patch if currently absent during install, modify, or repair. @@ -346,22 +353,20 @@ extern "C" HRESULT PlanDefaultPackageRequestState( { *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; } + else if (fFallbackToCache) + { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE; + } else { *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; } } - else if (BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED == currentState && BOOTSTRAPPER_ACTION_UNINSTALL != action) - { - // Superseded means the package is on the machine but not active, so only uninstall operations are allowed. - // All other operations do nothing. - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; - } else if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == currentState && !(BOOTSTRAPPER_ACTION_UNINSTALL == action && BURN_PACKAGE_TYPE_MSP == packageType)) { // Obsolete means the package is not on the machine and should not be installed, *except* patches can be obsolete // and present so allow them to be removed during uninstall. Everyone else, gets nothing. - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; + *pRequestState = fFallbackToCache ? BOOTSTRAPPER_REQUEST_STATE_CACHE : BOOTSTRAPPER_REQUEST_STATE_NONE; } else // pick the best option for the action state and install condition. { @@ -381,6 +386,11 @@ extern "C" HRESULT PlanDefaultPackageRequestState( { *pRequestState = defaultRequestState; } + + if (fFallbackToCache && BOOTSTRAPPER_REQUEST_STATE_CACHE > *pRequestState) + { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE; + } } LExit: @@ -816,7 +826,7 @@ static HRESULT ProcessPackage( BOOL fPlanPackageBegan = FALSE; // Remember the default requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it. - hr = PlanDefaultPackageRequestState(pPackage->type, pPackage->currentState, !pPackage->fUninstallable, pPlan->action, pVariables, pPackage->sczInstallCondition, relationType, &pPackage->defaultRequested); + hr = PlanDefaultPackageRequestState(pPackage->type, pPackage->currentState, !pPackage->fUninstallable, pPackage->cacheType, pPlan->action, pVariables, pPackage->sczInstallCondition, relationType, &pPackage->defaultRequested); ExitOnFailure(hr, "Failed to set default package state."); pPackage->requested = pPackage->defaultRequested; @@ -863,18 +873,9 @@ static HRESULT ProcessPackage( } else if (BOOTSTRAPPER_ACTION_LAYOUT != pPlan->action) { - // All packages that have cacheType set to always should be cached if the bundle is going to be present. - if (BURN_CACHE_TYPE_ALWAYS == pPackage->cacheType && BOOTSTRAPPER_ACTION_INSTALL <= pPlan->action) - { - hr = PlanCachePackage(fBundlePerMachine, pUX, pPlan, pPackage, pVariables, phSyncpointEvent); - ExitOnFailure(hr, "Failed to plan cache package."); - } - else - { - // Make sure the package is properly ref-counted even if no plan is requested. - hr = PlanDependencyActions(fBundlePerMachine, pPlan, pPackage); - ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId); - } + // Make sure the package is properly ref-counted even if no plan is requested. + hr = PlanDependencyActions(fBundlePerMachine, pPlan, pPackage); + ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId); } if (pPackage->fCanAffectRegistration) @@ -1008,41 +1009,6 @@ LExit: return hr; } -extern "C" HRESULT PlanCachePackage( - __in BOOL fPerMachine, - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage, - __in BURN_VARIABLES* pVariables, - __out HANDLE* phSyncpointEvent - ) -{ - HRESULT hr = S_OK; - BOOL fBARequestedCache = FALSE; - - // Calculate the execute actions because we need them to decide whether the package should be cached. - hr = CalculateExecuteActions(pUserExperience, pPackage, pVariables, pPlan->pActiveRollbackBoundary, &fBARequestedCache); - ExitOnFailure(hr, "Failed to calculate execute actions for package: %ls", pPackage->sczId); - - if (fBARequestedCache || NeedsCache(pPlan, pPackage)) - { - hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent); - ExitOnFailure(hr, "Failed to plan cache package."); - - if (pPackage->fPerMachine) - { - pPlan->fPerMachine = TRUE; - } - } - - // Make sure the package is properly ref-counted. - hr = PlanDependencyActions(fPerMachine, pPlan, pPackage); - ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId); - -LExit: - return hr; -} - extern "C" HRESULT PlanExecutePackage( __in BOOL fPerMachine, __in BOOTSTRAPPER_DISPLAY display, @@ -1064,7 +1030,7 @@ extern "C" HRESULT PlanExecutePackage( hr = DependencyPlanPackageBegin(fPerMachine, pPackage, pPlan); ExitOnFailure(hr, "Failed to begin plan dependency actions for package: %ls", pPackage->sczId); - if (fBARequestedCache || NeedsCache(pPlan, pPackage)) + if (fBARequestedCache || NeedsCache(pPackage)) { hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent); ExitOnFailure(hr, "Failed to plan cache package."); @@ -1079,12 +1045,12 @@ extern "C" HRESULT PlanExecutePackage( // Add the cache and install size to estimated size if it will be on the machine at the end of the install if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || - (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_ABSENT < pPackage->requested) || - BURN_CACHE_TYPE_ALWAYS == pPackage->cacheType + BOOTSTRAPPER_REQUEST_STATE_CACHE == pPackage->requested || + (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_ABSENT < pPackage->requested) ) { // If the package will remain in the cache, add the package size to the estimated size - if (BURN_CACHE_TYPE_YES <= pPackage->cacheType) + if (BURN_CACHE_TYPE_NO < pPackage->cacheType) { pPlan->qwEstimatedSize += pPackage->qwSize; } @@ -1511,7 +1477,7 @@ extern "C" HRESULT PlanCleanPackage( // from the cache. Start by noting that we only clean if the package is being acquired or // already cached and the package is not supposed to always be cached. if ((pPackage->fAcquire || BURN_CACHE_STATE_PARTIAL == pPackage->cache || BURN_CACHE_STATE_COMPLETE == pPackage->cache) && - (BURN_CACHE_TYPE_ALWAYS > pPackage->cacheType || BOOTSTRAPPER_ACTION_INSTALL > pPlan->action)) + (BURN_CACHE_TYPE_ALWAYS > pPackage->cacheType || BOOTSTRAPPER_ACTION_CACHE > pPlan->action)) { // The following are all different reasons why the package should be cleaned from the cache. // The else-ifs are used to make the conditions easier to see (rather than have them combined @@ -2836,16 +2802,10 @@ LExit: } static BOOL NeedsCache( - __in BURN_PLAN* pPlan, __in BURN_PACKAGE* pPackage ) { - // All packages that have cacheType set to always should be cached if the bundle is going to be present. - if (BURN_CACHE_TYPE_ALWAYS == pPackage->cacheType && BOOTSTRAPPER_ACTION_INSTALL <= pPlan->action) - { - return TRUE; - } - else if (BURN_PACKAGE_TYPE_EXE == pPackage->type) // Exe packages require the package for all operations (even uninstall). + if (BURN_PACKAGE_TYPE_EXE == pPackage->type) // Exe packages require the package for all operations (even uninstall). { return BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute; } diff --git a/src/engine/plan.h b/src/engine/plan.h index 6c12b5fa..2f6c9dd3 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -380,6 +380,7 @@ HRESULT PlanDefaultPackageRequestState( __in BURN_PACKAGE_TYPE packageType, __in BOOTSTRAPPER_PACKAGE_STATE currentState, __in BOOL fPermanent, + __in BURN_CACHE_TYPE cacheType, __in BOOTSTRAPPER_ACTION action, __in BURN_VARIABLES* pVariables, __in_z_opt LPCWSTR wzInstallCondition, @@ -439,14 +440,6 @@ HRESULT PlanLayoutPackage( __in BURN_PACKAGE* pPackage, __in_z_opt LPCWSTR wzLayoutDirectory ); -HRESULT PlanCachePackage( - __in BOOL fPerMachine, - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage, - __in BURN_VARIABLES* pVariables, - __out HANDLE* phSyncpointEvent - ); HRESULT PlanExecutePackage( __in BOOL fPerMachine, __in BOOTSTRAPPER_DISPLAY display, diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp index a50c64e1..629c293f 100644 --- a/src/test/BurnUnitTest/PlanTest.cpp +++ b/src/test/BurnUnitTest/PlanTest.cpp @@ -317,7 +317,7 @@ namespace Bootstrapper dwIndex = 0; Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); - Assert::Equal(0ull, pPlan->qwEstimatedSize); + Assert::Equal(33743ull, pPlan->qwEstimatedSize); Assert::Equal(33743ull, pPlan->qwCacheSizeTotal); fRollback = FALSE; -- cgit v1.2.3-55-g6feb From c5b9c47a26ca4e9d4eea63b7219b4d34938a5f1a Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Thu, 4 Feb 2021 20:18:02 -0600 Subject: Finalize MSP package registration states before logging them. --- src/engine/apply.cpp | 10 +++++----- src/engine/core.cpp | 2 -- src/engine/mspengine.cpp | 9 ++++++++- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 8d2f5757..7c47ba75 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -885,6 +885,11 @@ static void CalculateKeepRegistration( { BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + MspEngineFinalizeInstallRegistrationState(pPackage); + } + LogId(REPORT_STANDARD, MSG_POST_APPLY_PACKAGE, pPackage->sczId, LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->installRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->cacheRegistrationState)); if (!pPackage->fCanAffectRegistration) @@ -892,11 +897,6 @@ static void CalculateKeepRegistration( continue; } - if (BURN_PACKAGE_TYPE_MSP == pPackage->type) - { - MspEngineFinalizeInstallRegistrationState(pPackage); - } - if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState || BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState) { diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 1503f8d8..644752ff 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -1103,8 +1103,6 @@ extern "C" HRESULT CoreCleanup( hr = CoreApply(pEngineState, pEngineState->hMessageWindow); ExitOnFailure(hr, "Apply during cleanup failed"); - // Need to think about cache=always - LExit: return hr; } diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp index c0329d79..2c3a866a 100644 --- a/src/engine/mspengine.cpp +++ b/src/engine/mspengine.cpp @@ -732,7 +732,11 @@ extern "C" void MspEngineFinalizeInstallRegistrationState( __in BURN_PACKAGE* pPackage ) { - Assert(pPackage->fCanAffectRegistration); + if (!pPackage->fCanAffectRegistration) + { + ExitFunction(); + } + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) @@ -745,6 +749,9 @@ extern "C" void MspEngineFinalizeInstallRegistrationState( break; } } + +LExit: + return; } -- cgit v1.2.3-55-g6feb From d5c041befbb60e7bfce14af2eaf1e8c686524209 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 6 Feb 2021 16:26:59 -0600 Subject: Add ::Sleep(0) in engine loop to unblock PostThreadMessage --- src/engine/engine.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index bc27cb14..98c98661 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -776,6 +776,12 @@ static HRESULT RunApplication( } else { + // When the BA makes a request from its own thread, it's common for the PostThreadMessage in externalengine.cpp + // to block until this thread waits on something. It's also common for Detect and Plan to never wait on something. + // In the extreme case, the engine could be elevating in Apply before the Detect call returned to the BA. + // This helps to avoid that situation, which could be blocking a UI thread. + ::Sleep(0); + ProcessMessage(pEngineState, &msg); } } -- cgit v1.2.3-55-g6feb From 5fc93f9399795156b4a1fbde5f410ca01d94d609 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 6 Feb 2021 17:09:47 -0600 Subject: Cleanup should always be attempted, and add some logging for it. Continuation of #6297 --- src/engine/core.cpp | 23 +++++++++++++++-------- src/engine/core.h | 2 +- src/engine/engine.cpp | 5 ++--- src/engine/engine.mc | 30 +++++++++++++++++++++++++++++- 4 files changed, 47 insertions(+), 13 deletions(-) diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 644752ff..6b32931d 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -1061,7 +1061,7 @@ LExit: return hr; } -extern "C" HRESULT CoreCleanup( +extern "C" void CoreCleanup( __in BURN_ENGINE_STATE* pEngineState ) { @@ -1069,6 +1069,14 @@ extern "C" HRESULT CoreCleanup( LONGLONG llValue = 0; BOOL fNeedsElevation = pEngineState->registration.fPerMachine && INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe; + LogId(REPORT_STANDARD, MSG_CLEANUP_BEGIN); + + if (pEngineState->fApplied && BOOTSTRAPPER_ACTION_LAYOUT < pEngineState->plan.action && BOOTSTRAPPER_ACTION_UPDATE_REPLACE > pEngineState->plan.action) + { + LogId(REPORT_STANDARD, MSG_CLEANUP_SKIPPED_APPLY); + ExitFunction(); + } + if (fNeedsElevation) { hr = VariableGetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, &llValue); @@ -1078,12 +1086,11 @@ extern "C" HRESULT CoreCleanup( { fNeedsElevation = FALSE; } - } - - if (pEngineState->fApplied && BOOTSTRAPPER_ACTION_LAYOUT < pEngineState->plan.action && BOOTSTRAPPER_ACTION_UPDATE_REPLACE > pEngineState->plan.action || - fNeedsElevation) - { - ExitFunction(); + else + { + LogId(REPORT_STANDARD, MSG_CLEANUP_SKIPPED_ELEVATION_REQUIRED); + ExitFunction(); + } } if (!pEngineState->fDetected) @@ -1104,7 +1111,7 @@ extern "C" HRESULT CoreCleanup( ExitOnFailure(hr, "Apply during cleanup failed"); LExit: - return hr; + LogId(REPORT_STANDARD, MSG_CLEANUP_COMPLETE, hr); } // internal helper functions diff --git a/src/engine/core.h b/src/engine/core.h index 47cfd559..f23738c3 100644 --- a/src/engine/core.h +++ b/src/engine/core.h @@ -204,7 +204,7 @@ HRESULT CoreAppendFileHandleSelfToCommandLine( __deref_inout_z LPWSTR* psczCommandLine, __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine ); -HRESULT CoreCleanup( +void CoreCleanup( __in BURN_ENGINE_STATE* pEngineState ); diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 98c98661..7e6e2922 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -592,13 +592,12 @@ static HRESULT RunNormal( ExitOnFailure(hr, "Failed while running "); } while (fReloadApp); +LExit: if (!fSkipCleanup) { - hr = CoreCleanup(pEngineState); - ExitOnFailure(hr, "Failed to cleanup before shutting down"); + CoreCleanup(pEngineState); } -LExit: BurnExtensionUnload(&pEngineState->extensions); // If the message window is still around, close it. diff --git a/src/engine/engine.mc b/src/engine/engine.mc index c90f08e3..8e0a8a66 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -967,7 +967,35 @@ MessageId=501 Severity=Warning SymbolicName=MSG_STATE_NOT_SAVED Language=English -The state file could not be saved. Continuing... +The state file could not be saved, error: %1!ls!. Continuing... +. + +MessageId=502 +Severity=Success +SymbolicName=MSG_CLEANUP_BEGIN +Language=English +Cleanup begin. +. + +MessageId=503 +Severity=Success +SymbolicName=MSG_CLEANUP_SKIPPED_APPLY +Language=English +Cleanup not required due to running Apply. +. + +MessageId=504 +Severity=Success +SymbolicName=MSG_CLEANUP_SKIPPED_ELEVATION_REQUIRED +Language=English +Cleanup check skipped since this per-machine bundle would require elevation. +. + +MessageId=599 +Severity=Success +SymbolicName=MSG_CLEANUP_COMPLETE +Language=English +Cleanup complete, result: 0x%1!x! . MessageId=600 -- cgit v1.2.3-55-g6feb From 63cf6f589a35466aa73ff3ec021b5052be733409 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 7 Feb 2021 18:54:16 -0600 Subject: Add guid to ProjectReferences so .vcx project system understands them. --- src/stub/stub.vcxproj | 6 ++++-- src/test/BurnUnitTest/BurnUnitTest.vcxproj | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index da19f3c6..1b76d866 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -63,7 +63,7 @@ $(ProjectDir)..\engine\inc - cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;wuguid.lib;engine.lib;engine.res + cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;wuguid.lib;engine.res @@ -92,7 +92,9 @@ - + + {8119537D-E1D9-6591-D51A-49768A2F9C37} + diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index 6ac21f9c..a843eddd 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -83,7 +83,9 @@ - + + {8119537D-E1D9-6591-D51A-49768A2F9C37} + -- cgit v1.2.3-55-g6feb From 096784ea5114cb5bf99151cc047d69951035d152 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 7 Feb 2021 18:54:54 -0600 Subject: Require Vista (Server 2008) SP2 or newer. #6318 --- src/engine/condition.cpp | 8 ++------ src/engine/engine.cpp | 10 +++------ src/engine/engine.vcxproj | 4 ++-- src/engine/msuengine.cpp | 6 +----- src/engine/packages.config | 2 +- src/engine/pipe.cpp | 5 +---- src/engine/precomp.h | 1 + src/engine/registration.cpp | 11 ---------- src/engine/variable.cpp | 33 ++++-------------------------- src/stub/packages.config | 2 +- src/stub/stub.vcxproj | 4 ++-- src/test/BurnUnitTest/BurnUnitTest.vcxproj | 4 ++-- src/test/BurnUnitTest/packages.config | 2 +- 13 files changed, 21 insertions(+), 71 deletions(-) diff --git a/src/engine/condition.cpp b/src/engine/condition.cpp index 06591567..316224db 100644 --- a/src/engine/condition.cpp +++ b/src/engine/condition.cpp @@ -187,13 +187,9 @@ extern "C" HRESULT ConditionGlobalCheck( HRESULT hr = S_OK; BOOL fSuccess = TRUE; HRESULT hrError = HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION); - OS_VERSION osv = OS_VERSION_UNKNOWN; - DWORD dwServicePack = 0; - OsGetVersion(&osv, &dwServicePack); - - // Always error on Windows 2000 or lower - if (OS_VERSION_WIN2000 >= osv) + // Only run on Windows Vista SP2 or newer, or Windows Server 2008 SP2 or newer. + if (!::IsWindowsVistaSP2OrGreater()) { fSuccess = FALSE; } diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 7e6e2922..458386d4 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -95,7 +95,7 @@ extern "C" HRESULT EngineRun( BOOL fWiuInitialized = FALSE; BOOL fXmlInitialized = FALSE; SYSTEM_INFO si = { }; - OSVERSIONINFOEXW ovix = { }; + RTL_OSVERSIONINFOEXW ovix = { }; LPWSTR sczExePath = NULL; BOOL fRunNormal = FALSE; BOOL fRestart = FALSE; @@ -150,12 +150,8 @@ extern "C" HRESULT EngineRun( ExitOnFailure(hr, "Failed to initialize XML util."); fXmlInitialized = TRUE; - ovix.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); - #pragma warning(suppress: 4996) - if (!::GetVersionExW((LPOSVERSIONINFOW)&ovix)) - { - ExitWithLastError(hr, "Failed to get OS info."); - } + hr = OsRtlGetVersion(&ovix); + ExitOnFailure(hr, "Failed to get OS info."); #if defined(_M_ARM64) LPCSTR szBurnPlatform = "ARM64"; diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index cb179a23..bc724a40 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -1,7 +1,7 @@ - + Debug @@ -167,7 +167,7 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + \ No newline at end of file diff --git a/src/engine/msuengine.cpp b/src/engine/msuengine.cpp index b7a503ad..499e1da6 100644 --- a/src/engine/msuengine.cpp +++ b/src/engine/msuengine.cpp @@ -90,14 +90,10 @@ extern "C" HRESULT MsuEnginePlanCalculatePackage( BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; BOOL fBARequestedCache = FALSE; - BOOL fAllowUninstall = FALSE; - OS_VERSION osVersion = OS_VERSION_UNKNOWN; - DWORD dwServicePack = 0; // We can only uninstall MSU packages if they have a KB and we are on Win7 or newer. - OsGetVersion(&osVersion, &dwServicePack); - fAllowUninstall = (pPackage->Msu.sczKB && *pPackage->Msu.sczKB) && OS_VERSION_WIN7 <= osVersion; + fAllowUninstall = pPackage->Msu.sczKB && *pPackage->Msu.sczKB && ::IsWindows7OrGreater(); // execute action switch (pPackage->currentState) diff --git a/src/engine/packages.config b/src/engine/packages.config index f074ae43..102b8bee 100644 --- a/src/engine/packages.config +++ b/src/engine/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/engine/pipe.cpp b/src/engine/pipe.cpp index eb3eb863..67eeae87 100644 --- a/src/engine/pipe.cpp +++ b/src/engine/pipe.cpp @@ -391,16 +391,13 @@ extern "C" HRESULT PipeLaunchChildProcess( HRESULT hr = S_OK; DWORD dwCurrentProcessId = ::GetCurrentProcessId(); LPWSTR sczParameters = NULL; - OS_VERSION osVersion = OS_VERSION_UNKNOWN; - DWORD dwServicePack = 0; LPCWSTR wzVerb = NULL; HANDLE hProcess = NULL; hr = StrAllocFormatted(&sczParameters, L"-q -%ls %ls %ls %u", BURN_COMMANDLINE_SWITCH_ELEVATED, pConnection->sczName, pConnection->sczSecret, dwCurrentProcessId); ExitOnFailure(hr, "Failed to allocate parameters for elevated process."); - OsGetVersion(&osVersion, &dwServicePack); - wzVerb = (OS_VERSION_VISTA > osVersion) || !fElevate ? L"open" : L"runas"; + wzVerb = !fElevate ? L"open" : L"runas"; // Since ShellExecuteEx doesn't support passing inherited handles, don't bother with CoreAppendFileHandleSelfToCommandLine. // We could fallback to using ::DuplicateHandle to inject the file handle later if necessary. diff --git a/src/engine/precomp.h b/src/engine/precomp.h index 53fa949a..1b3e9bdc 100644 --- a/src/engine/precomp.h +++ b/src/engine/precomp.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include diff --git a/src/engine/registration.cpp b/src/engine/registration.cpp index d3732e74..a2ed2a0d 100644 --- a/src/engine/registration.cpp +++ b/src/engine/registration.cpp @@ -1178,20 +1178,9 @@ static HRESULT UpdateResumeMode( HKEY hkRun = NULL; LPWSTR sczResumeCommandLine = NULL; LPCWSTR sczResumeKey = REGISTRY_RUN_ONCE_KEY; - OS_VERSION osv = OS_VERSION_UNKNOWN; - DWORD dwServicePack = 0; LogId(REPORT_STANDARD, MSG_SESSION_UPDATE, pRegistration->sczRegistrationKey, LoggingResumeModeToString(resumeMode), LoggingBoolToString(fRestartInitiated), LoggingBoolToString(pRegistration->fDisableResume)); - // On Windows XP and Server 2003, write the resume information to the Run key - // instead of RunOnce. That avoids the problem that driver installation might - // trigger RunOnce commands to be executed before the reboot. - OsGetVersion(&osv, &dwServicePack); - if (osv < OS_VERSION_VISTA) - { - sczResumeKey = REGISTRY_RUN_KEY; - } - // write resume information if (hkRegistration) { diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp index 51dbdff4..fed23151 100644 --- a/src/engine/variable.cpp +++ b/src/engine/variable.cpp @@ -1638,33 +1638,17 @@ LExit: return hr; } -extern "C" typedef NTSTATUS (NTAPI *RTL_GET_VERSION)(_Out_ PRTL_OSVERSIONINFOEXW lpVersionInformation); - static HRESULT InitializeVariableVersionNT( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ) { HRESULT hr = S_OK; - HMODULE ntdll = NULL; - RTL_GET_VERSION rtlGetVersion = NULL; RTL_OSVERSIONINFOEXW ovix = { }; BURN_VARIANT value = { }; VERUTIL_VERSION* pVersion = NULL; - if (!::GetModuleHandleExW(0, L"ntdll", &ntdll)) - { - ExitWithLastError(hr, "Failed to locate NTDLL."); - } - - rtlGetVersion = reinterpret_cast(::GetProcAddress(ntdll, "RtlGetVersion")); - if (NULL == rtlGetVersion) - { - ExitWithLastError(hr, "Failed to locate RtlGetVersion."); - } - - ovix.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); - hr = static_cast(rtlGetVersion(&ovix)); + hr = OsRtlGetVersion(&ovix); ExitOnFailure(hr, "Failed to get OS info."); switch ((OS_INFO_VARIABLE)dwpData) @@ -1712,11 +1696,6 @@ static HRESULT InitializeVariableVersionNT( ExitOnFailure(hr, "Failed to set variant value."); LExit: - if (NULL != ntdll) - { - FreeLibrary(ntdll); - } - ReleaseVerutilVersion(pVersion); return hr; @@ -1728,15 +1707,11 @@ static HRESULT InitializeVariableOsInfo( ) { HRESULT hr = S_OK; - OSVERSIONINFOEXW ovix = { }; + RTL_OSVERSIONINFOEXW ovix = { }; BURN_VARIANT value = { }; - ovix.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); - #pragma warning(suppress: 4996) - if (!::GetVersionExW((LPOSVERSIONINFOW)&ovix)) - { - ExitWithLastError(hr, "Failed to get OS info."); - } + hr = OsRtlGetVersion(&ovix); + ExitOnFailure(hr, "Failed to get OS info."); switch ((OS_INFO_VARIABLE)dwpData) { diff --git a/src/stub/packages.config b/src/stub/packages.config index 7681e32c..3b161457 100644 --- a/src/stub/packages.config +++ b/src/stub/packages.config @@ -4,5 +4,5 @@ - + \ No newline at end of file diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index 1b76d866..f4d34d1d 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -5,7 +5,7 @@ - + @@ -117,6 +117,6 @@ - + \ No newline at end of file diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index a843eddd..62e58942 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -4,7 +4,7 @@ - + Debug @@ -95,6 +95,6 @@ - + \ No newline at end of file diff --git a/src/test/BurnUnitTest/packages.config b/src/test/BurnUnitTest/packages.config index f9d0f75a..e3e97259 100644 --- a/src/test/BurnUnitTest/packages.config +++ b/src/test/BurnUnitTest/packages.config @@ -10,5 +10,5 @@ - + \ No newline at end of file -- cgit v1.2.3-55-g6feb From 422d6e48e2f9bdcec62f9a147cb8c2f34bf73a5f Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Tue, 9 Feb 2021 21:35:37 -0500 Subject: Remove BITS support. Update DUtil dependency. --- src/engine/apply.cpp | 25 +- src/engine/bitsengine.cpp | 505 ----------------------------- src/engine/bitsengine.h | 23 -- src/engine/engine.vcxproj | 6 +- src/engine/packages.config | 2 +- src/engine/precomp.h | 2 - src/stub/packages.config | 2 +- src/stub/stub.vcxproj | 4 +- src/test/BurnUnitTest/BurnUnitTest.vcxproj | 4 +- src/test/BurnUnitTest/packages.config | 2 +- src/test/BurnUnitTest/precomp.h | 1 - 11 files changed, 15 insertions(+), 561 deletions(-) delete mode 100644 src/engine/bitsengine.cpp delete mode 100644 src/engine/bitsengine.h diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 7c47ba75..55141ef9 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -1509,26 +1509,13 @@ static HRESULT DownloadPayload( cacheCallback.pfnCancel = NULL; // TODO: set this cacheCallback.pv = pProgress; - // If the protocol is specially marked, "bits" let's use that. - if (L'b' == pDownloadSource->sczUrl[0] && - L'i' == pDownloadSource->sczUrl[1] && - L't' == pDownloadSource->sczUrl[2] && - L's' == pDownloadSource->sczUrl[3] && - (L':' == pDownloadSource->sczUrl[4] || (L's' == pDownloadSource->sczUrl[4] && L':' == pDownloadSource->sczUrl[5])) - ) - { - hr = BitsDownloadUrl(&cacheCallback, pDownloadSource, wzDestinationPath); - } - else // wininet handles everything else. - { - authenticationData.pUX = pProgress->pUX; - authenticationData.wzPackageOrContainerId = wzPackageOrContainerId; - authenticationData.wzPayloadId = wzPayloadId; - authenticationCallback.pv = static_cast(&authenticationData); - authenticationCallback.pfnAuthenticate = &AuthenticationRequired; + authenticationData.pUX = pProgress->pUX; + authenticationData.wzPackageOrContainerId = wzPackageOrContainerId; + authenticationData.wzPayloadId = wzPayloadId; + authenticationCallback.pv = static_cast(&authenticationData); + authenticationCallback.pfnAuthenticate = &AuthenticationRequired; - hr = DownloadUrl(pDownloadSource, qwDownloadSize, wzDestinationPath, &cacheCallback, &authenticationCallback); - } + hr = DownloadUrl(pDownloadSource, qwDownloadSize, wzDestinationPath, &cacheCallback, &authenticationCallback); ExitOnFailure(hr, "Failed attempt to download URL: '%ls' to: '%ls'", pDownloadSource->sczUrl, wzDestinationPath); LExit: diff --git a/src/engine/bitsengine.cpp b/src/engine/bitsengine.cpp deleted file mode 100644 index b8093b77..00000000 --- a/src/engine/bitsengine.cpp +++ /dev/null @@ -1,505 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// const - -const DWORD BITSENGINE_NO_PROGRESS_TIMEOUT = 2 * 60; -const DWORD BITSENGINE_MSG_WAIT_TIMEOUT = 1; - -// functions - -static HRESULT CreateJob( - __out IBackgroundCopyJob** ppJob - ); -static HRESULT SetCredentials( - __in IBackgroundCopyJob* pJob, - __in_z_opt LPCWSTR wzUser, - __in_z_opt LPCWSTR wzPassword - ); -static void SendError( - __in DOWNLOAD_CACHE_CALLBACK* pCacheCallback, - __in IBackgroundCopyJob* pJob, - __in HRESULT hrError, - __in BG_ERROR_CONTEXT context, - __out_opt BOOL* pfRetry - ); - - -// class - -class CBurnBitsCallback : public IBackgroundCopyCallback -{ -public: // IUnknown - virtual STDMETHODIMP QueryInterface( - __in const IID& riid, - __out void** ppvObject - ) - { - HRESULT hr = S_OK; - - ExitOnNull(ppvObject, hr, E_INVALIDARG, "Invalid argument ppvObject"); - *ppvObject = NULL; - - if (::IsEqualIID(__uuidof(IBackgroundCopyCallback), riid)) - { - *ppvObject = static_cast(this); - } - else if (::IsEqualIID(IID_IUnknown, riid)) - { - *ppvObject = reinterpret_cast(this); - } - else // no interface for requested iid - { - ExitFunction1(hr = E_NOINTERFACE); - } - - AddRef(); - - LExit: - return hr; - } - - virtual STDMETHODIMP_(ULONG) AddRef() - { - return ::InterlockedIncrement(&this->m_cReferences); - } - - virtual STDMETHODIMP_(ULONG) Release() - { - long l = ::InterlockedDecrement(&this->m_cReferences); - if (0 < l) - { - return l; - } - - delete this; - return 0; - } - -public: // IBackgroundCopyCallback - virtual STDMETHODIMP JobTransferred( - __in IBackgroundCopyJob* pJob - ) - { - HRESULT hr = S_OK; - - hr = SendProgress(pJob); - ExitOnFailure(hr, "Failure while sending progress during BITS job transferred."); - - LExit: - if (FAILED(hr)) - { - ProcessResult(BG_ERROR_CONTEXT_NONE, hr); - } - else - { - ::SetEvent(m_hComplete); - } - - return S_OK; // must return S_OK otherwise BITS just keeps calling back. - } - - virtual STDMETHODIMP JobError( - __in IBackgroundCopyJob* /*pJob*/, - __in IBackgroundCopyError* pError - ) - { - HRESULT hr = S_OK; - BG_ERROR_CONTEXT context = BG_ERROR_CONTEXT_NONE; - HRESULT hrError = S_OK; - - hr = pError->GetError(&context, &hrError); - ExitOnFailure(hr, "Failed to get error context."); - - if (SUCCEEDED(hrError)) - { - hr = E_UNEXPECTED; - } - - LExit: - ProcessResult(context, FAILED(hrError) ? hrError : hr); - - return S_OK; // must return S_OK otherwise BITS just keeps calling back. - } - - virtual STDMETHODIMP JobModification( - __in IBackgroundCopyJob* pJob, - __in DWORD /*dwReserved*/ - ) - { - HRESULT hr = S_OK; - BG_JOB_STATE state = BG_JOB_STATE_ERROR; - - ::EnterCriticalSection(&m_cs); - - hr = pJob->GetState(&state); - ExitOnFailure(hr, "Failed to get state during job modification."); - - // If we're actually downloading stuff, let's send progress. - if (BG_JOB_STATE_TRANSFERRING == state) - { - hr = SendProgress(pJob); - ExitOnFailure(hr, "Failure while sending progress during BITS job modification."); - } - - LExit: - ::LeaveCriticalSection(&m_cs); - - ProcessResult(BG_ERROR_CONTEXT_NONE, hr); - - return S_OK; // documentation says to always return S_OK - } - -public: - void Reset() - { - m_hrError = S_OK; - m_contextError = BG_ERROR_CONTEXT_NONE; - - ::ResetEvent(m_hComplete); - } - - HRESULT WaitForCompletion( - __in IBackgroundCopyJob* pJob - ) - { - HRESULT hr = S_OK; - HANDLE rghEvents[1] = { m_hComplete }; - MSG msg = { }; - BOOL fMessageProcessed = FALSE; - - do - { - fMessageProcessed = FALSE; - - switch (::MsgWaitForMultipleObjects(countof(rghEvents), rghEvents, FALSE, BITSENGINE_MSG_WAIT_TIMEOUT * 1000, QS_ALLINPUT)) - { - case WAIT_OBJECT_0: - break; - - case WAIT_OBJECT_0 + 1: - ::PeekMessageW(&msg, NULL, 0, 0, PM_NOREMOVE); - fMessageProcessed = TRUE; - break; - - case WAIT_TIMEOUT: - // Call the progress callback periodically if we are not transferring to ensure that cancelling is responsive - // (progress callback is also handles cancelling). Note that if we are transferring, IBackgroundCopyCallback - // methods handle progress/cancelling. If we are not transferring, the IBackgroundCopyCallback methods may - // not be called until the job times out (minutes for a foreground job, weeks for a background job). - SendProgressIfNotTransferring(pJob); - fMessageProcessed = TRUE; - break; - - default: - ExitWithLastError(hr, "Failed while waiting for download."); - } - } while (fMessageProcessed); - - LExit: - return hr; - } - - void GetError( - __out HRESULT* pHR, - __out BG_ERROR_CONTEXT* pContext - ) - { - *pHR = m_hrError; - *pContext = m_contextError; - } - -private: - HRESULT SendProgress( - __in IBackgroundCopyJob* pJob - ) - { - HRESULT hr = S_OK; - BG_JOB_PROGRESS progress = { }; - - if (m_pCallback && m_pCallback->pfnProgress) - { - hr = pJob->GetProgress(&progress); - ExitOnFailure(hr, "Failed to get progress when BITS job was transferred."); - - hr = CacheSendProgressCallback(m_pCallback, progress.BytesTransferred, progress.BytesTotal, INVALID_HANDLE_VALUE); - ExitOnFailure(hr, "Failed to send progress from BITS job."); - } - - LExit: - return hr; - } - - void SendProgressIfNotTransferring( - __in IBackgroundCopyJob* pJob - ) - { - HRESULT hr = S_OK; - BG_JOB_STATE state = BG_JOB_STATE_ERROR; - - ::EnterCriticalSection(&m_cs); - - hr = pJob->GetState(&state); - ExitOnFailure(hr, "Failed to get BITS job state."); - - if (BG_JOB_STATE_TRANSFERRING != state) - { - hr = SendProgress(pJob); - ExitOnFailure(hr, "Failure while sending progress."); - } - - LExit: - ::LeaveCriticalSection(&m_cs); - - ProcessResult(BG_ERROR_CONTEXT_NONE, hr); - } - - void ProcessResult( - __in BG_ERROR_CONTEXT context, - __in HRESULT hr - ) - { - if (FAILED(hr)) - { - m_contextError = context; - m_hrError = hr; - - ::SetEvent(m_hComplete); - } - } - -public: - CBurnBitsCallback( - __in_opt DOWNLOAD_CACHE_CALLBACK* pCallback, - __out HRESULT* pHR - ) - { - HRESULT hr = S_OK; - - m_cReferences = 1; - ::InitializeCriticalSection(&m_cs); - - m_hComplete = ::CreateEventW(NULL, TRUE, FALSE, NULL); - ExitOnNullWithLastError(m_hComplete, hr, "Failed to create BITS job complete event."); - - m_contextError = BG_ERROR_CONTEXT_NONE; - m_hrError = S_OK; - - m_pCallback = pCallback; - - LExit: - *pHR = hr; - } - - ~CBurnBitsCallback() - { - m_pCallback = NULL; - ReleaseHandle(m_hComplete); - ::DeleteCriticalSection(&m_cs); - } - -private: - long m_cReferences; - CRITICAL_SECTION m_cs; - BG_ERROR_CONTEXT m_contextError; - HRESULT m_hrError; - - HANDLE m_hComplete; - DOWNLOAD_CACHE_CALLBACK* m_pCallback; -}; - - -extern "C" HRESULT BitsDownloadUrl( - __in DOWNLOAD_CACHE_CALLBACK* pCallback, - __in DOWNLOAD_SOURCE* pDownloadSource, - __in_z LPCWSTR wzDestinationPath - ) -{ - HRESULT hr = S_OK; - LPWSTR sczDownloadUrl = NULL; - CBurnBitsCallback* pBitsCallback = NULL; - IBackgroundCopyJob* pJob = NULL; - BOOL fRetry = FALSE; - BG_ERROR_CONTEXT contextError = BG_ERROR_CONTEXT_NONE; - - // If the URL isn't at least 8 characters long (e.g.: "bits://X") then it - // isn't going to do us any good. - if (8 > lstrlenW(pDownloadSource->sczUrl)) - { - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Invalid BITS engine URL: %ls", pDownloadSource->sczUrl); - } - - // Fix the URL to be "http" instead of "bits". - hr = StrAllocString(&sczDownloadUrl, pDownloadSource->sczUrl, 0); - ExitOnFailure(hr, "Failed to copy download URL."); - - sczDownloadUrl[0] = L'h'; - sczDownloadUrl[1] = L't'; - sczDownloadUrl[2] = L't'; - sczDownloadUrl[3] = L'p'; - - // Create and configure the BITS job. - hr = CreateJob(&pJob); - ExitOnFailure(hr, "Failed to create BITS job."); - - hr = SetCredentials(pJob, pDownloadSource->sczUser, pDownloadSource->sczPassword); - ExitOnFailure(hr, "Failed to set credentials for BITS job."); - - hr = pJob->AddFile(sczDownloadUrl, wzDestinationPath); - ExitOnFailure(hr, "Failed to add file to BITS job."); - - // Set the callback into the BITs job. - pBitsCallback = new CBurnBitsCallback(pCallback, &hr); - ExitOnNull(pBitsCallback, hr, E_OUTOFMEMORY, "Failed to create BITS job callback."); - ExitOnFailure(hr, "Failed to initialize BITS job callback."); - - hr = pJob->SetNotifyInterface(pBitsCallback); - ExitOnFailure(hr, "Failed to set callback interface for BITS job."); - - // Go into our retry download loop. - do - { - fRetry = FALSE; - - pBitsCallback->Reset(); // ensure we are ready for the download to start (again?). - - hr = pJob->Resume(); - ExitOnFailure(hr, "Falied to start BITS job."); - - hr = pBitsCallback->WaitForCompletion(pJob); - ExitOnFailure(hr, "Failed while waiting for BITS download."); - - // See if there are any errors. - pBitsCallback->GetError(&hr, &contextError); - if (HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr) - { - ExitFunction(); - } - else if (FAILED(hr)) - { - SendError(pCallback, pJob, hr, contextError, &fRetry); - } - } while (fRetry); - ExitOnFailure(hr, "Failed to download BITS job."); - - // After all that, we should have the file downloaded so complete the job to get - // the file copied to the destination. - hr = pJob->Complete(); - ExitOnFailure(hr, "Failed to complete BITS job."); - -LExit: - if (pJob) - { - pJob->SetNotifyInterface(NULL); - - // If we failed, kill the job. - if (FAILED(hr)) - { - pJob->Cancel(); // TODO: should we cancel if we're going to retry the package? Probably the right thing to do. - } - } - - ReleaseObject(pBitsCallback); - ReleaseObject(pJob); - ReleaseStr(sczDownloadUrl); - - return hr; -} - -static HRESULT CreateJob( - __out IBackgroundCopyJob** ppJob - ) -{ - HRESULT hr = S_OK; - IBackgroundCopyManager* pBitsManager = NULL; - IBackgroundCopyJob* pJob = NULL; - GUID guidJob = { }; - - hr = ::CoCreateInstance(__uuidof(BackgroundCopyManager), NULL, CLSCTX_ALL, __uuidof(IBackgroundCopyManager), reinterpret_cast(&pBitsManager)); - ExitOnFailure(hr, "Failed to create IBackgroundCopyManager."); - - hr = pBitsManager->CreateJob(L"WixBurn", BG_JOB_TYPE_DOWNLOAD, &guidJob, &pJob); - ExitOnFailure(hr, "Failed to create BITS job."); - - hr = pJob->SetNotifyFlags(BG_NOTIFY_JOB_TRANSFERRED | BG_NOTIFY_JOB_ERROR | BG_NOTIFY_JOB_MODIFICATION); - ExitOnFailure(hr, "Failed to set notification flags for BITS job."); - - hr = pJob->SetNoProgressTimeout(BITSENGINE_NO_PROGRESS_TIMEOUT); // use 2 minutes since default is 14 days. - ExitOnFailure(hr, "Failed to set progress timeout."); - - hr = pJob->SetPriority(BG_JOB_PRIORITY_FOREGROUND); - ExitOnFailure(hr, "Failed to set BITS job to foreground."); - - *ppJob = pJob; - pJob = NULL; - -LExit: - ReleaseObject(pJob); - ReleaseObject(pBitsManager); - - return hr; -} - -static HRESULT SetCredentials( - __in IBackgroundCopyJob* pJob, - __in_z_opt LPCWSTR wzUser, - __in_z_opt LPCWSTR wzPassword - ) -{ - HRESULT hr = S_OK; - IBackgroundCopyJob2* pJob2 = NULL; - BG_AUTH_CREDENTIALS ac = { }; - - // If IBackgroundCopyJob2::SetCredentials() is supported, set the username/password. - hr = pJob->QueryInterface(IID_PPV_ARGS(&pJob2)); - if (SUCCEEDED(hr)) - { - ac.Target = BG_AUTH_TARGET_PROXY; - ac.Credentials.Basic.UserName = const_cast(wzUser); - ac.Credentials.Basic.Password = const_cast(wzPassword); - - ac.Scheme = BG_AUTH_SCHEME_NTLM; - hr = pJob2->SetCredentials(&ac); - ExitOnFailure(hr, "Failed to set background copy NTLM credentials"); - - ac.Scheme = BG_AUTH_SCHEME_NEGOTIATE; - hr = pJob2->SetCredentials(&ac); - ExitOnFailure(hr, "Failed to set background copy negotiate credentials"); - } - - hr = S_OK; - -LExit: - ReleaseObject(pJob2); - - return hr; -} - -static void SendError( - __in DOWNLOAD_CACHE_CALLBACK* pCacheCallback, - __in IBackgroundCopyJob* pJob, - __in HRESULT hrError, - __in BG_ERROR_CONTEXT /*context*/, - __out_opt BOOL* pfRetry - ) -{ - HRESULT hr = S_OK; - IBackgroundCopyError* pError = NULL; - LPWSTR pszErrorDescription = NULL; - - hr = pJob->GetError(&pError); - if (SUCCEEDED(hr)) - { - pError->GetErrorDescription(LANGIDFROMLCID(::GetThreadLocale()), &pszErrorDescription); - } - - CacheSendErrorCallback(pCacheCallback, hrError, pszErrorDescription, pfRetry); - - if (pszErrorDescription) - { - ::CoTaskMemFree(pszErrorDescription); - } - ReleaseObject(pError); -} diff --git a/src/engine/bitsengine.h b/src/engine/bitsengine.h deleted file mode 100644 index b1c1d805..00000000 --- a/src/engine/bitsengine.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -// structs - - -// functions - -HRESULT BitsDownloadUrl( - __in DOWNLOAD_CACHE_CALLBACK* pCallback, - __in DOWNLOAD_SOURCE* pDownloadSource, - __in LPCWSTR wzDestinationPath - ); - - -#ifdef __cplusplus -} -#endif diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index bc724a40..a6ab60df 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -1,7 +1,7 @@ - + Debug @@ -45,7 +45,6 @@ - @@ -90,7 +89,6 @@ - @@ -167,7 +165,7 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + \ No newline at end of file diff --git a/src/engine/packages.config b/src/engine/packages.config index 102b8bee..ad6df168 100644 --- a/src/engine/packages.config +++ b/src/engine/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/engine/precomp.h b/src/engine/precomp.h index 1b3e9bdc..4f8e9d46 100644 --- a/src/engine/precomp.h +++ b/src/engine/precomp.h @@ -4,7 +4,6 @@ #include #include -#include #include #include @@ -96,7 +95,6 @@ #include "manifest.h" #include "splashscreen.h" #include "uithread.h" -#include "bitsengine.h" #include "netfxchainer.h" #include "externalengine.h" diff --git a/src/stub/packages.config b/src/stub/packages.config index 3b161457..1c027b04 100644 --- a/src/stub/packages.config +++ b/src/stub/packages.config @@ -4,5 +4,5 @@ - + \ No newline at end of file diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index f4d34d1d..f9a7b5a9 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -5,7 +5,7 @@ - + @@ -117,6 +117,6 @@ - + \ No newline at end of file diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index 62e58942..08dc68e7 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -4,7 +4,7 @@ - + Debug @@ -95,6 +95,6 @@ - + \ No newline at end of file diff --git a/src/test/BurnUnitTest/packages.config b/src/test/BurnUnitTest/packages.config index e3e97259..a35dfe1f 100644 --- a/src/test/BurnUnitTest/packages.config +++ b/src/test/BurnUnitTest/packages.config @@ -10,5 +10,5 @@ - + \ No newline at end of file diff --git a/src/test/BurnUnitTest/precomp.h b/src/test/BurnUnitTest/precomp.h index a5db1555..d2b57d61 100644 --- a/src/test/BurnUnitTest/precomp.h +++ b/src/test/BurnUnitTest/precomp.h @@ -67,7 +67,6 @@ #include "embedded.h" #include "manifest.h" #include "splashscreen.h" -#include "bitsengine.h" #include "detect.h" #pragma managed -- cgit v1.2.3-55-g6feb From bce47190bec208d5899c6dd0da88a3c421f338a7 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Sun, 21 Feb 2021 18:15:09 -0500 Subject: Add `/xlog` switch to get verbose-est Burn logging. --- src/engine/core.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 6b32931d..0ece3f44 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -1153,10 +1153,16 @@ static HRESULT ParseCommandLine( if (argv[i][0] == L'-' || argv[i][0] == L'/') { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"l", -1) || - CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"log", -1)) + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"log", -1) || + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"xlog", -1)) { *pdwLoggingAttributes &= ~BURN_LOGGING_ATTRIBUTE_APPEND; + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], 1, L"x", 1)) + { + *pdwLoggingAttributes |= BURN_LOGGING_ATTRIBUTE_VERBOSE | BURN_LOGGING_ATTRIBUTE_EXTRADEBUG; + } + if (i + 1 >= argc) { ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for log."); -- cgit v1.2.3-55-g6feb From 1fc35ce3263e9351bb719c6c7f9b6351c52ee18c Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 12 Feb 2021 12:43:09 -0600 Subject: Make PlanTest test fDeleted and the expected states of all packages. --- src/test/BurnUnitTest/PlanTest.cpp | 121 ++++++++++++++++++++++++++----------- 1 file changed, 86 insertions(+), 35 deletions(-) diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp index 629c293f..81447ca1 100644 --- a/src/test/BurnUnitTest/PlanTest.cpp +++ b/src/test/BurnUnitTest/PlanTest.cpp @@ -55,7 +55,7 @@ namespace Bootstrapper DWORD dwIndex = 0; DWORD dwPackageStart = 0; ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, 6, 2, 33743, FALSE); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", 6, 2, 33743, FALSE); ValidateCacheAcquireContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE); ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, dwPackageStart, 6); ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"PackageA", TRUE, FALSE, dwPackageStart); @@ -63,7 +63,7 @@ namespace Bootstrapper ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 9); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, 14, 2, 33743, FALSE); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageB", 14, 2, 33743, FALSE); ValidateCacheAcquireContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", TRUE); ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, dwPackageStart, 2); ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageB", L"PackageB", TRUE, FALSE, dwPackageStart); @@ -71,7 +71,7 @@ namespace Bootstrapper ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageB", FALSE); ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 14); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, 22, 2, 33743, FALSE); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageC", 22, 2, 33743, FALSE); ValidateCacheAcquireContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", TRUE); ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, dwPackageStart, 2); ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageC", L"PackageC", TRUE, FALSE, dwPackageStart); @@ -98,7 +98,7 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -111,7 +111,7 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -120,14 +120,14 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCommitMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[23].syncpoint.hEvent); - ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL); Assert::Equal(dwIndex, pPlan->cExecuteActions); fRollback = TRUE; @@ -138,7 +138,7 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -163,7 +163,7 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL); Assert::Equal(dwIndex, pPlan->cRollbackActions); Assert::Equal(4ul, pPlan->cExecutePackagesTotal); @@ -175,6 +175,11 @@ namespace Bootstrapper UINT uIndex = 0; ValidatePlannedProvider(pPlan, uIndex++, L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", NULL); Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(3ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); } [Fact] @@ -216,13 +221,13 @@ namespace Bootstrapper ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCommitMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -232,7 +237,7 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); Assert::Equal(dwIndex, pPlan->cExecuteActions); @@ -258,7 +263,7 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -268,9 +273,9 @@ namespace Bootstrapper Assert::Equal(3ul, pPlan->cOverallProgressTicksTotal); dwIndex = 0; - ValidateCleanAction(pPlan, dwIndex++, L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_ABSENT); - ValidateCleanAction(pPlan, dwIndex++, L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_ABSENT); - ValidateCleanAction(pPlan, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + ValidateCleanAction(pPlan, dwIndex++, L"PackageC"); + ValidateCleanAction(pPlan, dwIndex++, L"PackageB"); + ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); Assert::Equal(dwIndex, pPlan->cCleanActions); UINT uIndex = 0; @@ -279,6 +284,11 @@ namespace Bootstrapper ValidatePlannedProvider(pPlan, uIndex++, L"{D1D01094-23CE-4AF0-84B6-4A1A133F21D3}", NULL); ValidatePlannedProvider(pPlan, uIndex++, L"{01E6B748-7B95-4BA9-976D-B6F35076CEF4}", NULL); Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(3ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); } [Fact] @@ -305,7 +315,7 @@ namespace Bootstrapper DWORD dwIndex = 0; DWORD dwPackageStart = 0; ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, 5, 2, 33743, FALSE); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", 5, 2, 33743, FALSE); ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, BURN_PLAN_INVALID_ACTION_INDEX, 2); ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"PackageA", TRUE, FALSE, dwPackageStart); ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"cab9Ins_fTP3wNwq5Gxo41ch5VUPaQ", TRUE, FALSE, dwPackageStart); @@ -350,6 +360,9 @@ namespace Bootstrapper UINT uIndex = 0; ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(1ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); } [Fact] @@ -376,7 +389,7 @@ namespace Bootstrapper DWORD dwIndex = 0; DWORD dwPackageStart = 0; ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, 5, 2, 33743, FALSE); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", 5, 2, 33743, FALSE); ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, BURN_PLAN_INVALID_ACTION_INDEX, 2); ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"PackageA", TRUE, FALSE, dwPackageStart); ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"cab9Ins_fTP3wNwq5Gxo41ch5VUPaQ", TRUE, FALSE, dwPackageStart); @@ -402,13 +415,13 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[6].syncpoint.hEvent); - ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL); Assert::Equal(dwIndex, pPlan->cExecuteActions); fRollback = TRUE; @@ -419,13 +432,13 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL); Assert::Equal(dwIndex, pPlan->cRollbackActions); Assert::Equal(2ul, pPlan->cExecutePackagesTotal); @@ -437,6 +450,9 @@ namespace Bootstrapper UINT uIndex = 0; ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(1ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); } [Fact] @@ -448,7 +464,7 @@ namespace Bootstrapper BURN_PLAN* pPlan = &engineState.plan; InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); - PlanTestDetect(pEngineState); + DetectPackagesAsAbsent(pEngineState); pEngineState->registration.fInstalled = TRUE; @@ -493,6 +509,9 @@ namespace Bootstrapper UINT uIndex = 0; ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(1ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); } [Fact] @@ -533,7 +552,7 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); Assert::Equal(dwIndex, pPlan->cExecuteActions); @@ -546,7 +565,7 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -556,13 +575,16 @@ namespace Bootstrapper Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal); dwIndex = 0; - ValidateCleanAction(pPlan, dwIndex++, L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); Assert::Equal(dwIndex, pPlan->cCleanActions); UINT uIndex = 0; ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); ValidatePlannedProvider(pPlan, uIndex++, L"{64633047-D172-4BBB-B202-64337D15C952}", NULL); Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(1ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); } [Fact] @@ -623,6 +645,9 @@ namespace Bootstrapper UINT uIndex = 0; ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(1ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_IGNORED, BURN_PACKAGE_REGISTRATION_STATE_IGNORED); } private: @@ -901,7 +926,6 @@ namespace Bootstrapper __in BOOL fRollback, __in DWORD dwIndex, __in LPCWSTR wzPackageId, - __in BURN_PACKAGE_REGISTRATION_STATE expectedCacheRegistrationState, __in DWORD iPackageCompleteAction, __in DWORD cCachePayloads, __in DWORD64 qwCachePayloadSizeTotal, @@ -911,7 +935,6 @@ namespace Bootstrapper BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); Assert::Equal(BURN_CACHE_ACTION_TYPE_PACKAGE_START, pAction->type); NativeAssert::StringEqual(wzPackageId, pAction->packageStart.pPackage->sczId); - Assert::Equal(expectedCacheRegistrationState, pAction->packageStart.pPackage->expectedCacheRegistrationState); Assert::Equal(iPackageCompleteAction, pAction->packageStart.iPackageCompleteAction); Assert::Equal(cCachePayloads, pAction->packageStart.cCachePayloads); Assert::Equal(qwCachePayloadSizeTotal, pAction->packageStart.qwCachePayloadSizeTotal); @@ -963,8 +986,7 @@ namespace Bootstrapper void ValidateCleanAction( __in BURN_PLAN* pPlan, __in DWORD dwIndex, - __in LPCWSTR wzPackageId, - __in BURN_PACKAGE_REGISTRATION_STATE expectedCacheRegistrationState + __in LPCWSTR wzPackageId ) { Assert::InRange(dwIndex + 1ul, 1ul, pPlan->cCleanActions); @@ -972,7 +994,6 @@ namespace Bootstrapper BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + dwIndex; Assert::NotEqual((DWORD_PTR)0, (DWORD_PTR)pCleanAction->pPackage); NativeAssert::StringEqual(wzPackageId, pCleanAction->pPackage->sczId); - Assert::Equal(expectedCacheRegistrationState, pCleanAction->pPackage->expectedCacheRegistrationState); } BURN_EXECUTE_ACTION* ValidateExecuteActionExists(BURN_PLAN* pPlan, BOOL fRollback, DWORD dwIndex) @@ -991,6 +1012,7 @@ namespace Bootstrapper BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); Assert::Equal(BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION, pAction->type); NativeAssert::StringEqual(wzRollbackBoundaryId, pAction->msiTransaction.pRollbackBoundary->sczId); + Assert::Equal(FALSE, pAction->fDeleted); } void ValidateExecuteCheckpoint( @@ -1003,6 +1025,7 @@ namespace Bootstrapper BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); Assert::Equal(BURN_EXECUTE_ACTION_TYPE_CHECKPOINT, pAction->type); Assert::Equal(dwId, pAction->checkpoint.dwId); + Assert::Equal(FALSE, pAction->fDeleted); } void ValidateExecuteCommitMsiTransaction( @@ -1015,6 +1038,7 @@ namespace Bootstrapper BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); Assert::Equal(BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION, pAction->type); NativeAssert::StringEqual(wzRollbackBoundaryId, pAction->msiTransaction.pRollbackBoundary->sczId); + Assert::Equal(FALSE, pAction->fDeleted); } void ValidateExecuteExePackage( @@ -1022,7 +1046,6 @@ namespace Bootstrapper __in BOOL fRollback, __in DWORD dwIndex, __in LPCWSTR wzPackageId, - __in BURN_PACKAGE_REGISTRATION_STATE expectedInstallRegistrationState, __in BOOTSTRAPPER_ACTION_STATE action, __in LPCWSTR wzIgnoreDependencies ) @@ -1030,9 +1053,9 @@ namespace Bootstrapper BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); Assert::Equal(BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE, pAction->type); NativeAssert::StringEqual(wzPackageId, pAction->exePackage.pPackage->sczId); - Assert::Equal(expectedInstallRegistrationState, pAction->exePackage.pPackage->expectedInstallRegistrationState); Assert::Equal(action, pAction->exePackage.action); NativeAssert::StringEqual(wzIgnoreDependencies, pAction->exePackage.sczIgnoreDependencies); + Assert::Equal(FALSE, pAction->fDeleted); } void ValidateExecuteMsiPackage( @@ -1040,7 +1063,6 @@ namespace Bootstrapper __in BOOL fRollback, __in DWORD dwIndex, __in LPCWSTR wzPackageId, - __in BURN_PACKAGE_REGISTRATION_STATE expectedInstallRegistrationState, __in BOOTSTRAPPER_ACTION_STATE action, __in BURN_MSI_PROPERTY actionMsiProperty, __in DWORD uiLevel, @@ -1051,13 +1073,13 @@ namespace Bootstrapper BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); Assert::Equal(BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE, pAction->type); NativeAssert::StringEqual(wzPackageId, pAction->msiPackage.pPackage->sczId); - Assert::Equal(expectedInstallRegistrationState, pAction->msiPackage.pPackage->expectedInstallRegistrationState); Assert::Equal(action, pAction->msiPackage.action); Assert::Equal(actionMsiProperty, pAction->msiPackage.actionMsiProperty); Assert::Equal(uiLevel, pAction->msiPackage.uiLevel); Assert::Equal(fDisableExternalUiHandler, pAction->msiPackage.fDisableExternalUiHandler); NativeAssert::NotNull(pAction->msiPackage.sczLogPath); Assert::Equal(dwLoggingAttributes, pAction->msiPackage.dwLoggingAttributes); + Assert::Equal(FALSE, pAction->fDeleted); } void ValidateExecutePackageDependency( @@ -1074,6 +1096,7 @@ namespace Bootstrapper NativeAssert::StringEqual(wzPackageId, pAction->packageDependency.pPackage->sczId); NativeAssert::StringEqual(wzBundleProviderKey, pAction->packageDependency.sczBundleProviderKey); Assert::Equal(action, pAction->packageDependency.action); + Assert::Equal(FALSE, pAction->fDeleted); } void ValidateExecutePackageProvider( @@ -1088,6 +1111,7 @@ namespace Bootstrapper Assert::Equal(BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER, pAction->type); NativeAssert::StringEqual(wzPackageId, pAction->packageProvider.pPackage->sczId); Assert::Equal(action, pAction->packageProvider.action); + Assert::Equal(FALSE, pAction->fDeleted); } void ValidateExecuteRollbackBoundary( @@ -1104,6 +1128,7 @@ namespace Bootstrapper NativeAssert::StringEqual(wzId, pAction->rollbackBoundary.pRollbackBoundary->sczId); Assert::Equal(fVital, pAction->rollbackBoundary.pRollbackBoundary->fVital); Assert::Equal(fTransaction, pAction->rollbackBoundary.pRollbackBoundary->fTransaction); + Assert::Equal(FALSE, pAction->fDeleted); } void ValidateExecuteUncachePackage( @@ -1116,6 +1141,7 @@ namespace Bootstrapper BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); Assert::Equal(BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE, pAction->type); NativeAssert::StringEqual(wzPackageId, pAction->uncachePackage.pPackage->sczId); + Assert::Equal(FALSE, pAction->fDeleted); } void ValidateExecuteWaitSyncpoint( @@ -1128,6 +1154,31 @@ namespace Bootstrapper BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); Assert::Equal(BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT, pAction->type); Assert::Equal((DWORD_PTR)hEvent, (DWORD_PTR)pAction->syncpoint.hEvent); + Assert::Equal(FALSE, pAction->fDeleted); + } + + void ValidateNonPermanentPackageExpectedStates( + __in BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzPackageId, + __in BURN_PACKAGE_REGISTRATION_STATE expectedCacheState, + __in BURN_PACKAGE_REGISTRATION_STATE expectedInstallState + ) + { + NativeAssert::StringEqual(wzPackageId, pPackage->sczId); + Assert::Equal(TRUE, pPackage->fCanAffectRegistration); + Assert::Equal(expectedCacheState, pPackage->expectedCacheRegistrationState); + Assert::Equal(expectedInstallState, pPackage->expectedInstallRegistrationState); + } + + void ValidatePermanentPackageExpectedStates( + __in BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzPackageId + ) + { + NativeAssert::StringEqual(wzPackageId, pPackage->sczId); + Assert::Equal(FALSE, pPackage->fCanAffectRegistration); + Assert::Equal(BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, pPackage->expectedCacheRegistrationState); + Assert::Equal(BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, pPackage->expectedInstallRegistrationState); } void ValidatePlannedProvider( -- cgit v1.2.3-55-g6feb From b6862716cd27cefa541b85c63dd30dc3f0749d87 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 15 Feb 2021 13:25:35 -0600 Subject: Rename *TargetMsiPackage BA events to *PatchTarget. --- .../inc/BootstrapperApplication.h | 12 ++++++------ src/engine/mspengine.cpp | 8 ++++---- src/engine/userexperience.cpp | 20 ++++++++++---------- src/engine/userexperience.h | 4 ++-- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h index 48bd813d..1ba25cd7 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -93,12 +93,12 @@ enum BOOTSTRAPPER_APPLICATION_MESSAGE BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGEBEGIN, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDMSIPACKAGE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTTARGETMSIPACKAGE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPATCHTARGET, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTMSIFEATURE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGECOMPLETE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGEBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANTARGETMSIPACKAGE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPATCHTARGET, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIFEATURE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGECOMPLETE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYBEGIN, @@ -571,7 +571,7 @@ struct BA_ONDETECTRELATEDMSIPACKAGE_RESULTS BOOL fCancel; }; -struct BA_ONDETECTTARGETMSIPACKAGE_ARGS +struct BA_ONDETECTPATCHTARGET_ARGS { DWORD cbSize; LPCWSTR wzPackageId; @@ -579,7 +579,7 @@ struct BA_ONDETECTTARGETMSIPACKAGE_ARGS BOOTSTRAPPER_PACKAGE_STATE patchState; }; -struct BA_ONDETECTTARGETMSIPACKAGE_RESULTS +struct BA_ONDETECTPATCHTARGET_RESULTS { DWORD cbSize; BOOL fCancel; @@ -929,7 +929,7 @@ struct BA_ONPLANRELATEDBUNDLE_RESULTS BOOTSTRAPPER_REQUEST_STATE requestedState; }; -struct BA_ONPLANTARGETMSIPACKAGE_ARGS +struct BA_ONPLANPATCHTARGET_ARGS { DWORD cbSize; LPCWSTR wzPackageId; @@ -937,7 +937,7 @@ struct BA_ONPLANTARGETMSIPACKAGE_ARGS BOOTSTRAPPER_REQUEST_STATE recommendedState; }; -struct BA_ONPLANTARGETMSIPACKAGE_RESULTS +struct BA_ONPLANPATCHTARGET_RESULTS { DWORD cbSize; BOOTSTRAPPER_REQUEST_STATE requestedState; diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp index 2c3a866a..7f591d82 100644 --- a/src/engine/mspengine.cpp +++ b/src/engine/mspengine.cpp @@ -268,8 +268,8 @@ extern "C" HRESULT MspEngineDetectPackage( } } - hr = UserExperienceOnDetectTargetMsiPackage(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, pTargetProduct->patchPackageState); - ExitOnRootFailure(hr, "BA aborted detect target MSI package."); + hr = UserExperienceOnDetectPatchTarget(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, pTargetProduct->patchPackageState); + ExitOnRootFailure(hr, "BA aborted detect patch target."); } } @@ -300,8 +300,8 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - hr = UserExperienceOnPlanTargetMsiPackage(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, &requested); - ExitOnRootFailure(hr, "BA aborted plan target MSI package."); + hr = UserExperienceOnPlanPatchTarget(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, &requested); + ExitOnRootFailure(hr, "BA aborted plan patch target."); // Calculate the execute action. switch (pTargetProduct->patchPackageState) diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index a0fb341d..12c3f6df 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -947,7 +947,7 @@ LExit: return hr; } -EXTERN_C BAAPI UserExperienceOnDetectTargetMsiPackage( +EXTERN_C BAAPI UserExperienceOnDetectPatchTarget( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, __in_z LPCWSTR wzProductCode, @@ -955,8 +955,8 @@ EXTERN_C BAAPI UserExperienceOnDetectTargetMsiPackage( ) { HRESULT hr = S_OK; - BA_ONDETECTTARGETMSIPACKAGE_ARGS args = { }; - BA_ONDETECTTARGETMSIPACKAGE_RESULTS results = { }; + BA_ONDETECTPATCHTARGET_ARGS args = { }; + BA_ONDETECTPATCHTARGET_RESULTS results = { }; args.cbSize = sizeof(args); args.wzPackageId = wzPackageId; @@ -965,8 +965,8 @@ EXTERN_C BAAPI UserExperienceOnDetectTargetMsiPackage( results.cbSize = sizeof(results); - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTTARGETMSIPACKAGE, &args, &results); - ExitOnFailure(hr, "BA OnDetectTargetMsiPackage failed."); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPATCHTARGET, &args, &results); + ExitOnFailure(hr, "BA OnDetectPatchTarget failed."); if (results.fCancel) { @@ -1697,7 +1697,7 @@ LExit: return hr; } -EXTERN_C BAAPI UserExperienceOnPlanTargetMsiPackage( +EXTERN_C BAAPI UserExperienceOnPlanPatchTarget( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, __in_z LPCWSTR wzProductCode, @@ -1705,8 +1705,8 @@ EXTERN_C BAAPI UserExperienceOnPlanTargetMsiPackage( ) { HRESULT hr = S_OK; - BA_ONPLANTARGETMSIPACKAGE_ARGS args = { }; - BA_ONPLANTARGETMSIPACKAGE_RESULTS results = { }; + BA_ONPLANPATCHTARGET_ARGS args = { }; + BA_ONPLANPATCHTARGET_RESULTS results = { }; args.cbSize = sizeof(args); args.wzPackageId = wzPackageId; @@ -1716,8 +1716,8 @@ EXTERN_C BAAPI UserExperienceOnPlanTargetMsiPackage( results.cbSize = sizeof(results); results.requestedState = *pRequestedState; - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANTARGETMSIPACKAGE, &args, &results); - ExitOnFailure(hr, "BA OnPlanTargetMsiPackage failed."); + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPATCHTARGET, &args, &results); + ExitOnFailure(hr, "BA OnPlanPatchTarget failed."); if (results.fCancel) { diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index 363c0f06..930e7268 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -237,7 +237,7 @@ BAAPI UserExperienceOnDetectRelatedMsiPackage( __in VERUTIL_VERSION* pVersion, __in BOOTSTRAPPER_RELATED_OPERATION operation ); -BAAPI UserExperienceOnDetectTargetMsiPackage( +BAAPI UserExperienceOnDetectPatchTarget( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, __in_z LPCWSTR wzProductCode, @@ -391,7 +391,7 @@ BAAPI UserExperienceOnPlanRelatedBundle( __in_z LPCWSTR wzBundleId, __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState ); -BAAPI UserExperienceOnPlanTargetMsiPackage( +BAAPI UserExperienceOnPlanPatchTarget( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, __in_z LPCWSTR wzProductCode, -- cgit v1.2.3-55-g6feb From dbd55be5e707f07eb044c8c7f13c3dfd246148c0 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 15 Feb 2021 17:36:45 -0600 Subject: Initialize exe package ancestors during CoreInitialize instead of Plan. --- src/engine/core.cpp | 43 +++++++++++++++++++++++++++++++++++--- src/engine/core.h | 3 +++ src/engine/exeengine.cpp | 9 ++++---- src/engine/package.h | 2 +- src/engine/plan.cpp | 29 ++----------------------- src/engine/pseudobundle.cpp | 4 ++-- src/engine/pseudobundle.h | 4 ++-- src/engine/registration.cpp | 1 + src/engine/registration.h | 1 + src/test/BurnUnitTest/PlanTest.cpp | 3 ++- 10 files changed, 58 insertions(+), 41 deletions(-) diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 0ece3f44..b90d4b92 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -104,9 +104,9 @@ extern "C" HRESULT CoreInitialize( ExitOnFailure(hr, "Failed to parse command line."); LogId(REPORT_STANDARD, MSG_BURN_COMMAND_LINE, sczSanitizedCommandLine ? sczSanitizedCommandLine : L""); - - hr = DependencyInitialize(&pEngineState->registration, pEngineState->sczIgnoreDependencies); - ExitOnFailure(hr, "Failed to initialize dependency data."); + + hr = CoreInitializeConstants(pEngineState); + ExitOnFailure(hr, "Failed to initialize contants."); // Retain whether bundle was initially run elevated. ProcElevated(::GetCurrentProcess(), &fElevated); @@ -173,6 +173,43 @@ LExit: return hr; } +extern "C" HRESULT CoreInitializeConstants( + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + BURN_REGISTRATION* pRegistration = &pEngineState->registration; + + hr = DependencyInitialize(pRegistration, pEngineState->sczIgnoreDependencies); + ExitOnFailure(hr, "Failed to initialize dependency data."); + + // Support passing Ancestors to embedded burn bundles. + if (pRegistration->sczAncestors && *pRegistration->sczAncestors) + { + hr = StrAllocFormatted(&pRegistration->sczBundlePackageAncestors, L"%ls;%ls", pRegistration->sczAncestors, pRegistration->sczId); + ExitOnFailure(hr, "Failed to copy ancestors and self to bundle package ancestors."); + } + else + { + hr = StrAllocString(&pRegistration->sczBundlePackageAncestors, pRegistration->sczId, 0); + ExitOnFailure(hr, "Failed to copy self to bundle package ancestors."); + } + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + + if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) // TODO: Don't assume exePackages with burn protocol are bundles. + { + // Pass along any ancestors and ourself to prevent infinite loops. + pPackage->Exe.wzAncestors = pRegistration->sczBundlePackageAncestors; + } + } + +LExit: + return hr; +} + extern "C" HRESULT CoreSerializeEngineState( __in BURN_ENGINE_STATE* pEngineState, __inout BYTE** ppbBuffer, diff --git a/src/engine/core.h b/src/engine/core.h index f23738c3..d98c7646 100644 --- a/src/engine/core.h +++ b/src/engine/core.h @@ -138,6 +138,9 @@ typedef struct _BURN_ENGINE_STATE HRESULT CoreInitialize( __in BURN_ENGINE_STATE* pEngineState ); +HRESULT CoreInitializeConstants( + __in BURN_ENGINE_STATE* pEngineState + ); HRESULT CoreSerializeEngineState( __in BURN_ENGINE_STATE* pEngineState, __inout BYTE** ppbBuffer, diff --git a/src/engine/exeengine.cpp b/src/engine/exeengine.cpp index 1ca28473..f734edca 100644 --- a/src/engine/exeengine.cpp +++ b/src/engine/exeengine.cpp @@ -105,7 +105,6 @@ extern "C" void ExeEnginePackageUninitialize( ReleaseStr(pPackage->Exe.sczRepairArguments); ReleaseStr(pPackage->Exe.sczUninstallArguments); ReleaseStr(pPackage->Exe.sczIgnoreDependencies); - ReleaseStr(pPackage->Exe.sczAncestors); //ReleaseStr(pPackage->Exe.sczProgressSwitch); ReleaseMem(pPackage->Exe.rgExitCodes); @@ -334,9 +333,9 @@ extern "C" HRESULT ExeEnginePlanAddPackage( ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore."); } - if (pPackage->Exe.sczAncestors) + if (pPackage->Exe.wzAncestors) { - hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.sczAncestors, 0); + hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.wzAncestors, 0); ExitOnFailure(hr, "Failed to allocate the list of ancestors."); } @@ -359,9 +358,9 @@ extern "C" HRESULT ExeEnginePlanAddPackage( ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore."); } - if (pPackage->Exe.sczAncestors) + if (pPackage->Exe.wzAncestors) { - hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.sczAncestors, 0); + hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.wzAncestors, 0); ExitOnFailure(hr, "Failed to allocate the list of ancestors."); } diff --git a/src/engine/package.h b/src/engine/package.h index d3225fbc..71aecd95 100644 --- a/src/engine/package.h +++ b/src/engine/package.h @@ -250,7 +250,7 @@ typedef struct _BURN_PACKAGE LPWSTR sczRepairArguments; LPWSTR sczUninstallArguments; LPWSTR sczIgnoreDependencies; - LPWSTR sczAncestors; + LPCWSTR wzAncestors; // points directly into engine state. BOOL fPseudoBundle; diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index a11d0e78..99c87163 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -475,7 +475,7 @@ LExit: } extern "C" HRESULT PlanPackages( - __in BURN_REGISTRATION* pRegistration, + __in BURN_REGISTRATION* /*pRegistration*/, __in BURN_USER_EXPERIENCE* pUX, __in BURN_PACKAGES* pPackages, __in BURN_PLAN* pPlan, @@ -498,22 +498,6 @@ extern "C" HRESULT PlanPackages( DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackages->cPackages - 1 - i : i; BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage; - // Support passing Ancestors to embedded burn bundles - if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) - { - // Pass along any ancestors and ourself to prevent infinite loops. - if (pRegistration->sczAncestors && *pRegistration->sczAncestors) - { - hr = StrAllocFormatted(&pPackage->Exe.sczAncestors, L"%ls;%ls", pRegistration->sczAncestors, pRegistration->sczId); - ExitOnFailure(hr, "Failed to copy ancestors and self to related bundle ancestors."); - } - else - { - hr = StrAllocString(&pPackage->Exe.sczAncestors, pRegistration->sczId, 0); - ExitOnFailure(hr, "Failed to copy self to related bundle ancestors."); - } - } - hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary); ExitOnFailure(hr, "Failed to process package."); } @@ -1230,16 +1214,7 @@ extern "C" HRESULT PlanRelatedBundlesBegin( } // Pass along any ancestors and ourself to prevent infinite loops. - if (pRegistration->sczAncestors && *pRegistration->sczAncestors) - { - hr = StrAllocFormatted(&pRelatedBundle->package.Exe.sczAncestors, L"%ls;%ls", pRegistration->sczAncestors, pRegistration->sczId); - ExitOnFailure(hr, "Failed to copy ancestors and self to related bundle ancestors."); - } - else - { - hr = StrAllocString(&pRelatedBundle->package.Exe.sczAncestors, pRegistration->sczId, 0); - ExitOnFailure(hr, "Failed to copy self to related bundle ancestors."); - } + pRelatedBundle->package.Exe.wzAncestors = pRegistration->sczBundlePackageAncestors; hr = PlanDefaultRelatedBundleRequestState(relationType, pRelatedBundle->relationType, pPlan->action, pRegistration->pVersion, pRelatedBundle->pVersion, &pRelatedBundle->package.requested); ExitOnFailure(hr, "Failed to get default request state for related bundle."); diff --git a/src/engine/pseudobundle.cpp b/src/engine/pseudobundle.cpp index 0864be3a..3b05ea0b 100644 --- a/src/engine/pseudobundle.cpp +++ b/src/engine/pseudobundle.cpp @@ -159,8 +159,8 @@ extern "C" HRESULT PseudoBundleInitializePassthrough( __in BURN_PACKAGE* pPassthroughPackage, __in BOOTSTRAPPER_COMMAND* pCommand, __in_z_opt LPCWSTR wzAppendLogPath, - __in_z_opt LPWSTR wzActiveParent, - __in_z_opt LPWSTR wzAncestors, + __in_z_opt LPCWSTR wzActiveParent, + __in_z_opt LPCWSTR wzAncestors, __in BURN_PACKAGE* pPackage ) { diff --git a/src/engine/pseudobundle.h b/src/engine/pseudobundle.h index 4d3d3052..3b8157a0 100644 --- a/src/engine/pseudobundle.h +++ b/src/engine/pseudobundle.h @@ -29,8 +29,8 @@ HRESULT PseudoBundleInitializePassthrough( __in BURN_PACKAGE* pPassthroughPackage, __in BOOTSTRAPPER_COMMAND* pCommand, __in_z_opt LPCWSTR wzAppendLogPath, - __in_z_opt LPWSTR wzActiveParent, - __in_z_opt LPWSTR wzAncestors, + __in_z_opt LPCWSTR wzActiveParent, + __in_z_opt LPCWSTR wzAncestors, __in BURN_PACKAGE* pPackage ); diff --git a/src/engine/registration.cpp b/src/engine/registration.cpp index a2ed2a0d..9c821422 100644 --- a/src/engine/registration.cpp +++ b/src/engine/registration.cpp @@ -396,6 +396,7 @@ extern "C" void RegistrationUninitialize( ReleaseStr(pRegistration->sczDetectedProviderKeyBundleId); ReleaseStr(pRegistration->sczAncestors); + ReleaseStr(pRegistration->sczBundlePackageAncestors); RelatedBundlesUninitialize(&pRegistration->relatedBundles); // clear struct diff --git a/src/engine/registration.h b/src/engine/registration.h index 55d5a4c8..56bcb1f0 100644 --- a/src/engine/registration.h +++ b/src/engine/registration.h @@ -150,6 +150,7 @@ typedef struct _BURN_REGISTRATION LPWSTR sczDetectedProviderKeyBundleId; LPWSTR sczAncestors; + LPWSTR sczBundlePackageAncestors; BOOL fEnabledForwardCompatibleBundle; BURN_PACKAGE forwardCompatibleBundle; diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp index 81447ca1..42c11968 100644 --- a/src/test/BurnUnitTest/PlanTest.cpp +++ b/src/test/BurnUnitTest/PlanTest.cpp @@ -679,7 +679,8 @@ namespace Bootstrapper ReleaseStr(sczFilePath); } - DependencyInitialize(&pEngineState->registration, NULL); + hr = CoreInitializeConstants(pEngineState); + NativeAssert::Succeeded(hr, "Failed to initialize core constants"); pEngineState->userExperience.pfnBAProc = PlanTestBAProc; } -- cgit v1.2.3-55-g6feb From b29af5d005c2cc802aa60a123d435042038ba8ef Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Thu, 18 Feb 2021 10:44:49 -0600 Subject: Get all request states up front before building the plan. --- .../inc/BootstrapperApplication.h | 19 +- src/engine/core.cpp | 20 +- src/engine/engine.mc | 2 +- src/engine/msiengine.cpp | 62 +++-- src/engine/msiengine.h | 7 +- src/engine/mspengine.cpp | 36 ++- src/engine/mspengine.h | 5 +- src/engine/package.h | 10 +- src/engine/plan.cpp | 256 ++++++++++++++------- src/engine/plan.h | 5 +- src/engine/userexperience.cpp | 39 +++- src/engine/userexperience.h | 13 +- 12 files changed, 317 insertions(+), 157 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h index 1ba25cd7..0a89b3f4 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -142,6 +142,7 @@ enum BOOTSTRAPPER_APPLICATION_MESSAGE BOOTSTRAPPER_APPLICATION_MESSAGE_ONPAUSEAUTOMATICUPDATESCOMPLETE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMRESTOREPOINTBEGIN, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMRESTOREPOINTCOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDPACKAGE, }; enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION @@ -885,10 +886,25 @@ struct BA_ONPLANMSIPACKAGE_RESULTS BOOL fDisableExternalUiHandler; }; +struct BA_ONPLANNEDPACKAGE_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageId; + BOOTSTRAPPER_ACTION_STATE execute; + BOOTSTRAPPER_ACTION_STATE rollback; +}; + +struct BA_ONPLANNEDPACKAGE_RESULTS +{ + DWORD cbSize; +}; + struct BA_ONPLANPACKAGEBEGIN_ARGS { DWORD cbSize; LPCWSTR wzPackageId; + BOOTSTRAPPER_PACKAGE_STATE state; + BOOL fInstallCondition; BOOTSTRAPPER_REQUEST_STATE recommendedState; }; @@ -904,10 +920,7 @@ struct BA_ONPLANPACKAGECOMPLETE_ARGS DWORD cbSize; LPCWSTR wzPackageId; HRESULT hrStatus; - BOOTSTRAPPER_PACKAGE_STATE state; BOOTSTRAPPER_REQUEST_STATE requested; - BOOTSTRAPPER_ACTION_STATE execute; - BOOTSTRAPPER_ACTION_STATE rollback; }; struct BA_ONPLANPACKAGECOMPLETE_RESULTS diff --git a/src/engine/core.cpp b/src/engine/core.cpp index b90d4b92..b68681fb 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -490,7 +490,7 @@ extern "C" HRESULT CorePlan( ExitOnFailure(hr, "Failed to plan the layout of the bundle."); // Plan the packages' layout. - hr = PlanPackages(&pEngineState->registration, &pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, FALSE, pEngineState->command.display, pEngineState->command.relationType, sczLayoutDirectory, &hSyncpointEvent); + hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, sczLayoutDirectory, &hSyncpointEvent); ExitOnFailure(hr, "Failed to plan packages."); } else if (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == action || BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED == action) @@ -529,7 +529,7 @@ extern "C" HRESULT CorePlan( hr = PlanRelatedBundlesBegin(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->plan); ExitOnFailure(hr, "Failed to plan related bundles."); - hr = PlanPackages(&pEngineState->registration, &pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->registration.fInstalled, pEngineState->command.display, pEngineState->command.relationType, NULL, &hSyncpointEvent); + hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, NULL, &hSyncpointEvent); ExitOnFailure(hr, "Failed to plan packages."); // Schedule the update of related bundles last. @@ -538,10 +538,6 @@ extern "C" HRESULT CorePlan( } } - // Remove unnecessary actions. - hr = PlanFinalizeActions(&pEngineState->plan); - ExitOnFailure(hr, "Failed to remove unnecessary actions from plan."); - if (fContinuePlanning) { // Finally, display all packages and related bundles in the log. @@ -1814,6 +1810,18 @@ static void LogPackages( const BURN_PACKAGE* pPackage = &pPackages->rgPackages[iPackage]; LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingBoolToString(pPackage->fAcquire), LoggingBoolToString(pPackage->fUncache), LoggingDependencyActionToString(pPackage->dependencyExecute), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedInstallRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedCacheRegistrationState)); + + if (BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.cFeatures) + { + LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURES, pPackage->Msi.cFeatures, pPackage->sczId); + + for (DWORD j = 0; j < pPackage->Msi.cFeatures; ++j) + { + const BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[j]; + + LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURE, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState), LoggingMsiFeatureStateToString(pFeature->defaultRequested), LoggingMsiFeatureStateToString(pFeature->requested), LoggingMsiFeatureActionToString(pFeature->execute), LoggingMsiFeatureActionToString(pFeature->rollback)); + } + } } // Display related bundles last if caching, installing, modifying, or repairing. diff --git a/src/engine/engine.mc b/src/engine/engine.mc index 8e0a8a66..fb11430b 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -326,7 +326,7 @@ Planned feature: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: MessageId=204 Severity=Success -SymbolicName=MSG_PLAN_MSI_FEATURES +SymbolicName=MSG_PLANNED_MSI_FEATURES Language=English Plan %1!u! msi features for package: %2!ls! . diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index 4fdf2b7a..252a17b7 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -674,13 +674,46 @@ LExit: return hr; } +extern "C" HRESULT MsiEnginePlanInitializePackage( + __in BURN_PACKAGE* pPackage, + __in BURN_VARIABLES* pVariables, + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + + if (pPackage->Msi.cFeatures) + { + // get feature request states + for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) + { + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; + + // Evaluate feature conditions. + hr = EvaluateActionStateConditions(pVariables, pFeature->sczAddLocalCondition, pFeature->sczAddSourceCondition, pFeature->sczAdvertiseCondition, &pFeature->defaultRequested); + ExitOnFailure(hr, "Failed to evaluate requested state conditions."); + + hr = EvaluateActionStateConditions(pVariables, pFeature->sczRollbackAddLocalCondition, pFeature->sczRollbackAddSourceCondition, pFeature->sczRollbackAdvertiseCondition, &pFeature->expectedState); + ExitOnFailure(hr, "Failed to evaluate expected state conditions."); + + // Remember the default feature requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it. + pFeature->requested = pFeature->defaultRequested; + + // Send plan MSI feature message to BA. + hr = UserExperienceOnPlanMsiFeature(pUserExperience, pPackage->sczId, pFeature->sczId, &pFeature->requested); + ExitOnRootFailure(hr, "BA aborted plan MSI feature."); + } + } + +LExit: + return hr; +} + // // PlanCalculate - calculates the execute and rollback state for the requested package state. // extern "C" HRESULT MsiEnginePlanCalculatePackage( __in BURN_PACKAGE* pPackage, - __in BURN_VARIABLES* pVariables, - __in BURN_USER_EXPERIENCE* pUserExperience, __in BOOL fInsideMsiTransaction, __out_opt BOOL* pfBARequestedCache ) @@ -702,38 +735,17 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( // If the package is present and we're repairing it. BOOL fRepairingPackage = (BOOTSTRAPPER_PACKAGE_STATE_CACHED < pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested); - LogId(REPORT_STANDARD, MSG_PLAN_MSI_FEATURES, pPackage->Msi.cFeatures, pPackage->sczId); - // plan features for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) { BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; - BOOTSTRAPPER_FEATURE_STATE defaultFeatureRequestedState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; - BOOTSTRAPPER_FEATURE_STATE featureRequestedState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; - BOOTSTRAPPER_FEATURE_STATE featureExpectedState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; - - // Evaluate feature conditions. - hr = EvaluateActionStateConditions(pVariables, pFeature->sczAddLocalCondition, pFeature->sczAddSourceCondition, pFeature->sczAdvertiseCondition, &defaultFeatureRequestedState); - ExitOnFailure(hr, "Failed to evaluate requested state conditions."); - - hr = EvaluateActionStateConditions(pVariables, pFeature->sczRollbackAddLocalCondition, pFeature->sczRollbackAddSourceCondition, pFeature->sczRollbackAdvertiseCondition, &featureExpectedState); - ExitOnFailure(hr, "Failed to evaluate expected state conditions."); - - // Remember the default feature requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it. - featureRequestedState = defaultFeatureRequestedState; - - // Send plan MSI feature message to BA. - hr = UserExperienceOnPlanMsiFeature(pUserExperience, pPackage->sczId, pFeature->sczId, &featureRequestedState); - ExitOnRootFailure(hr, "BA aborted plan MSI feature."); // Calculate feature actions. - hr = CalculateFeatureAction(pFeature->currentState, featureRequestedState, fRepairingPackage, &pFeature->execute, &fFeatureActionDelta); + hr = CalculateFeatureAction(pFeature->currentState, pFeature->requested, fRepairingPackage, &pFeature->execute, &fFeatureActionDelta); ExitOnFailure(hr, "Failed to calculate execute feature state."); - hr = CalculateFeatureAction(featureRequestedState, BOOTSTRAPPER_FEATURE_ACTION_NONE == pFeature->execute ? featureExpectedState : pFeature->currentState, FALSE, &pFeature->rollback, &fRollbackFeatureActionDelta); + hr = CalculateFeatureAction(pFeature->requested, BOOTSTRAPPER_FEATURE_ACTION_NONE == pFeature->execute ? pFeature->expectedState : pFeature->currentState, FALSE, &pFeature->rollback, &fRollbackFeatureActionDelta); ExitOnFailure(hr, "Failed to calculate rollback feature state."); - - LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURE, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState), LoggingMsiFeatureStateToString(defaultFeatureRequestedState), LoggingMsiFeatureStateToString(featureRequestedState), LoggingMsiFeatureActionToString(pFeature->execute), LoggingMsiFeatureActionToString(pFeature->rollback)); } } diff --git a/src/engine/msiengine.h b/src/engine/msiengine.h index d1e46da8..b06866cd 100644 --- a/src/engine/msiengine.h +++ b/src/engine/msiengine.h @@ -31,10 +31,13 @@ HRESULT MsiEngineDetectPackage( __in BURN_PACKAGE* pPackage, __in BURN_USER_EXPERIENCE* pUserExperience ); -HRESULT MsiEnginePlanCalculatePackage( +HRESULT MsiEnginePlanInitializePackage( __in BURN_PACKAGE* pPackage, __in BURN_VARIABLES* pVariables, - __in BURN_USER_EXPERIENCE* pUserExperience, + __in BURN_USER_EXPERIENCE* pUserExperience + ); +HRESULT MsiEnginePlanCalculatePackage( + __in BURN_PACKAGE* pPackage, __in BOOL fInsideMsiTransaction, __out_opt BOOL* pfBARequestedCache ); diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp index 7f591d82..f742ecef 100644 --- a/src/engine/mspengine.cpp +++ b/src/engine/mspengine.cpp @@ -279,12 +279,32 @@ LExit: return hr; } +extern "C" HRESULT MspEnginePlanInitializePackage( + __in BURN_PACKAGE* pPackage, + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; + + pTargetProduct->requested = pPackage->requested; + + hr = UserExperienceOnPlanPatchTarget(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, &pTargetProduct->requested); + ExitOnRootFailure(hr, "BA aborted plan patch target."); + } + +LExit: + return hr; +} + // // PlanCalculate - calculates the execute and rollback state for the requested package state. // extern "C" HRESULT MspEnginePlanCalculatePackage( __in BURN_PACKAGE* pPackage, - __in BURN_USER_EXPERIENCE* pUserExperience, __in BOOL fInsideMsiTransaction, __out BOOL* pfBARequestedCache ) @@ -296,18 +316,14 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( { BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; - BOOTSTRAPPER_REQUEST_STATE requested = pPackage->requested; BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - hr = UserExperienceOnPlanPatchTarget(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, &requested); - ExitOnRootFailure(hr, "BA aborted plan patch target."); - // Calculate the execute action. switch (pTargetProduct->patchPackageState) { case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: - switch (requested) + switch (pTargetProduct->requested) { case BOOTSTRAPPER_REQUEST_STATE_REPAIR: execute = BOOTSTRAPPER_ACTION_STATE_REPAIR; @@ -329,7 +345,7 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( break; case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: - switch (requested) + switch (pTargetProduct->requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: @@ -354,7 +370,7 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( switch (pPackage->currentState) { case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: - switch (requested) + switch (pTargetProduct->requested) { case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_ABSENT: @@ -369,7 +385,7 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough; case BOOTSTRAPPER_PACKAGE_STATE_CACHED: - switch (requested) + switch (pTargetProduct->requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: @@ -408,8 +424,6 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( *pfBARequestedCache = fBARequestedCache; } -LExit: - return hr; } diff --git a/src/engine/mspengine.h b/src/engine/mspengine.h index 7cc9a119..7ee01e8e 100644 --- a/src/engine/mspengine.h +++ b/src/engine/mspengine.h @@ -32,9 +32,12 @@ HRESULT MspEngineDetectPackage( __in BURN_PACKAGE* pPackage, __in BURN_USER_EXPERIENCE* pUserExperience ); +HRESULT MspEnginePlanInitializePackage( + __in BURN_PACKAGE* pPackage, + __in BURN_USER_EXPERIENCE* pUserExperience + ); HRESULT MspEnginePlanCalculatePackage( __in BURN_PACKAGE* pPackage, - __in BURN_USER_EXPERIENCE* pUserExperience, __in BOOL fInsideMsiTransaction, __out_opt BOOL* pfBARequestedCache ); diff --git a/src/engine/package.h b/src/engine/package.h index 71aecd95..a728fcc6 100644 --- a/src/engine/package.h +++ b/src/engine/package.h @@ -112,6 +112,7 @@ typedef struct _BURN_MSPTARGETPRODUCT BOOL fSlipstream; BOOTSTRAPPER_PACKAGE_STATE patchPackageState; // only valid after Detect. + BOOTSTRAPPER_REQUEST_STATE requested; // only valid during Plan. BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan. BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan. @@ -137,9 +138,12 @@ typedef struct _BURN_MSIFEATURE LPWSTR sczRollbackAddSourceCondition; LPWSTR sczRollbackAdvertiseCondition; - BOOTSTRAPPER_FEATURE_STATE currentState; // only valid after Detect. - BOOTSTRAPPER_FEATURE_ACTION execute; // only valid during Plan. - BOOTSTRAPPER_FEATURE_ACTION rollback; // only valid during Plan. + BOOTSTRAPPER_FEATURE_STATE currentState; // only valid after Detect. + BOOTSTRAPPER_FEATURE_STATE expectedState; // only valid during Plan. + BOOTSTRAPPER_FEATURE_STATE defaultRequested; // only valid during Plan. + BOOTSTRAPPER_FEATURE_STATE requested; // only valid during Plan. + BOOTSTRAPPER_FEATURE_ACTION execute; // only valid during Plan. + BOOTSTRAPPER_FEATURE_ACTION rollback; // only valid during Plan. } BURN_MSIFEATURE; typedef struct _BURN_RELATED_MSI diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index 99c87163..29adfb42 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -21,6 +21,26 @@ static void ResetPlannedPackageState( static void ResetPlannedRollbackBoundaryState( __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary ); +static HRESULT PlanPackagesHelper( + __in BURN_PACKAGE* rgPackages, + __in DWORD cPackages, + __in BOOL fPlanCleanPackages, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in_z_opt LPCWSTR wzLayoutDirectory, + __inout HANDLE* phSyncpointEvent + ); +static HRESULT InitializePackage( + __in BURN_PLAN* pPlan, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_VARIABLES* pVariables, + __in BURN_PACKAGE* pPackage, + __in BOOTSTRAPPER_RELATION_TYPE relationType + ); static HRESULT ProcessPackage( __in BOOL fBundlePerMachine, __in BURN_USER_EXPERIENCE* pUX, @@ -29,7 +49,6 @@ static HRESULT ProcessPackage( __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z_opt LPCWSTR wzLayoutDirectory, __inout HANDLE* phSyncpointEvent, __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary @@ -146,9 +165,7 @@ static HRESULT PlanDependencyActions( __in BURN_PACKAGE* pPackage ); static HRESULT CalculateExecuteActions( - __in BURN_USER_EXPERIENCE* pUserExperience, __in BURN_PACKAGE* pPackage, - __in BURN_VARIABLES* pVariables, __in_opt BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary, __out_opt BOOL* pfBARequestedCache ); @@ -324,15 +341,13 @@ extern "C" HRESULT PlanDefaultPackageRequestState( __in BOOL fPermanent, __in BURN_CACHE_TYPE cacheType, __in BOOTSTRAPPER_ACTION action, - __in BURN_VARIABLES* pVariables, - __in_z_opt LPCWSTR wzInstallCondition, + __in BOOL fInstallCondition, __in BOOTSTRAPPER_RELATION_TYPE relationType, __out BOOTSTRAPPER_REQUEST_STATE* pRequestState ) { HRESULT hr = S_OK; BOOTSTRAPPER_REQUEST_STATE defaultRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; - BOOL fCondition = FALSE; BOOL fFallbackToCache = BURN_CACHE_TYPE_ALWAYS == cacheType && BOOTSTRAPPER_ACTION_UNINSTALL != action && BOOTSTRAPPER_PACKAGE_STATE_CACHED > currentState; // If doing layout, then always default to requesting the file be cached. @@ -373,14 +388,11 @@ extern "C" HRESULT PlanDefaultPackageRequestState( hr = GetActionDefaultRequestState(action, fPermanent, currentState, &defaultRequestState); ExitOnFailure(hr, "Failed to get default request state for action."); - // If there is an install condition (and we're doing an install) evaluate the condition + // If we're doing an install, use the install condition // to determine whether to use the default request state or make the package absent. - if (BOOTSTRAPPER_ACTION_UNINSTALL != action && wzInstallCondition && *wzInstallCondition) + if (BOOTSTRAPPER_ACTION_UNINSTALL != action && !fInstallCondition) { - hr = ConditionEvaluate(pVariables, wzInstallCondition, &fCondition); - ExitOnFailure(hr, "Failed to evaluate install condition."); - - *pRequestState = fCondition ? defaultRequestState : BOOTSTRAPPER_REQUEST_STATE_ABSENT; + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT; } else // just set the package to the default request state. { @@ -475,13 +487,11 @@ LExit: } extern "C" HRESULT PlanPackages( - __in BURN_REGISTRATION* /*pRegistration*/, __in BURN_USER_EXPERIENCE* pUX, __in BURN_PACKAGES* pPackages, __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, - __in BOOL /*fBundleInstalled*/, __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z_opt LPCWSTR wzLayoutDirectory, @@ -489,39 +499,9 @@ extern "C" HRESULT PlanPackages( ) { HRESULT hr = S_OK; - BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine. - BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; - - // Plan the packages. - for (DWORD i = 0; i < pPackages->cPackages; ++i) - { - DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackages->cPackages - 1 - i : i; - BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage; - - hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary); - ExitOnFailure(hr, "Failed to process package."); - } + + hr = PlanPackagesHelper(pPackages->rgPackages, pPackages->cPackages, TRUE, pUX, pPlan, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent); - // If we still have an open rollback boundary, complete it. - if (pRollbackBoundary) - { - hr = PlanRollbackBoundaryComplete(pPlan); - ExitOnFailure(hr, "Failed to plan rollback boundary begin."); - - pRollbackBoundary = NULL; - } - - // Plan clean up of packages. - for (DWORD i = 0; i < pPackages->cPackages; ++i) - { - DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackages->cPackages - 1 - i : i; - BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage; - - hr = PlanCleanPackage(pPlan, pPackage); - ExitOnFailure(hr, "Failed to plan clean package."); - } - -LExit: return hr; } @@ -735,24 +715,13 @@ extern "C" HRESULT PlanPassThroughBundle( ) { HRESULT hr = S_OK; - BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine. - BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; // Plan passthrough package. - hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary); + // Passthrough packages are never cleaned up by the calling bundle (they delete themselves when appropriate) + // so we don't need to plan clean up. + hr = PlanPackagesHelper(pPackage, 1, FALSE, pUX, pPlan, pLog, pVariables, display, relationType, NULL, phSyncpointEvent); ExitOnFailure(hr, "Failed to process passthrough package."); - // If we still have an open rollback boundary, complete it. - if (pRollbackBoundary) - { - hr = PlanRollbackBoundaryComplete(pPlan); - ExitOnFailure(hr, "Failed to plan rollback boundary for passthrough package."); - } - - // Notice that the PlanCleanPackage() function is purposefully missing here. Passthrough packages - // are never cleaned up by the calling bundle (they delete themselves when appropriate) so we don't - // need to plan clean up. - LExit: return hr; } @@ -769,28 +738,152 @@ extern "C" HRESULT PlanUpdateBundle( ) { HRESULT hr = S_OK; - BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine. - BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; // Plan update package. - hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary); + hr = PlanPackagesHelper(pPackage, 1, TRUE, pUX, pPlan, pLog, pVariables, display, relationType, NULL, phSyncpointEvent); ExitOnFailure(hr, "Failed to process update package."); +LExit: + return hr; +} + +static HRESULT PlanPackagesHelper( + __in BURN_PACKAGE* rgPackages, + __in DWORD cPackages, + __in BOOL fPlanCleanPackages, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in_z_opt LPCWSTR wzLayoutDirectory, + __inout HANDLE* phSyncpointEvent + ) +{ + HRESULT hr = S_OK; + BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine. + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; + + // Initialize the packages. + for (DWORD i = 0; i < cPackages; ++i) + { + DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i; + BURN_PACKAGE* pPackage = rgPackages + iPackage; + + hr = InitializePackage(pPlan, pUX, pVariables, pPackage, relationType); + ExitOnFailure(hr, "Failed to initialize package."); + } + + // Initialize the patch targets after all packages, since they could rely on the requested state of packages that are after the patch's package in the chain. + for (DWORD i = 0; i < cPackages; ++i) + { + DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i; + BURN_PACKAGE* pPackage = rgPackages + iPackage; + + if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + hr = MspEnginePlanInitializePackage(pPackage, pUX); + ExitOnFailure(hr, "Failed to initialize plan package: %ls", pPackage->sczId); + } + } + + // Plan the packages. + for (DWORD i = 0; i < cPackages; ++i) + { + DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i; + BURN_PACKAGE* pPackage = rgPackages + iPackage; + + hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary); + ExitOnFailure(hr, "Failed to process package."); + } + // If we still have an open rollback boundary, complete it. if (pRollbackBoundary) { hr = PlanRollbackBoundaryComplete(pPlan); - ExitOnFailure(hr, "Failed to plan rollback boundary for update package."); + ExitOnFailure(hr, "Failed to plan final rollback boundary complete."); + + pRollbackBoundary = NULL; } - // Plan clean up of update package. - hr = PlanCleanPackage(pPlan, pPackage); - ExitOnFailure(hr, "Failed to plan clean of update package."); + if (fPlanCleanPackages) + { + // Plan clean up of packages. + for (DWORD i = 0; i < cPackages; ++i) + { + DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i; + BURN_PACKAGE* pPackage = rgPackages + iPackage; + + hr = PlanCleanPackage(pPlan, pPackage); + ExitOnFailure(hr, "Failed to plan clean package."); + } + } + + // Remove unnecessary actions. + hr = PlanFinalizeActions(pPlan); + ExitOnFailure(hr, "Failed to remove unnecessary actions from plan."); + + // Let the BA know the actions that were planned. + for (DWORD i = 0; i < cPackages; ++i) + { + DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i; + BURN_PACKAGE* pPackage = rgPackages + iPackage; + + UserExperienceOnPlannedPackage(pUX, pPackage->sczId, pPackage->execute, pPackage->rollback); + } LExit: return hr; } +static HRESULT InitializePackage( + __in BURN_PLAN* pPlan, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_VARIABLES* pVariables, + __in BURN_PACKAGE* pPackage, + __in BOOTSTRAPPER_RELATION_TYPE relationType + ) +{ + HRESULT hr = S_OK; + BOOL fInstallCondition = FALSE; + BOOL fBeginCalled = FALSE; + + if (pPackage->sczInstallCondition && *pPackage->sczInstallCondition) + { + hr = ConditionEvaluate(pVariables, pPackage->sczInstallCondition, &fInstallCondition); + ExitOnFailure(hr, "Failed to evaluate install condition."); + } + else + { + fInstallCondition = TRUE; + } + + // Remember the default requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it. + hr = PlanDefaultPackageRequestState(pPackage->type, pPackage->currentState, !pPackage->fUninstallable, pPackage->cacheType, pPlan->action, fInstallCondition, relationType, &pPackage->defaultRequested); + ExitOnFailure(hr, "Failed to set default package state."); + + pPackage->requested = pPackage->defaultRequested; + fBeginCalled = TRUE; + + hr = UserExperienceOnPlanPackageBegin(pUX, pPackage->sczId, pPackage->currentState, fInstallCondition, &pPackage->requested); + ExitOnRootFailure(hr, "BA aborted plan package begin."); + + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + { + hr = MsiEnginePlanInitializePackage(pPackage, pVariables, pUX); + ExitOnFailure(hr, "Failed to initialize plan package: %ls", pPackage->sczId); + } + +LExit: + if (fBeginCalled) + { + UserExperienceOnPlanPackageComplete(pUX, pPackage->sczId, hr, pPackage->requested); + } + + return hr; +} + static HRESULT ProcessPackage( __in BOOL fBundlePerMachine, __in BURN_USER_EXPERIENCE* pUX, @@ -799,7 +892,6 @@ static HRESULT ProcessPackage( __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z_opt LPCWSTR wzLayoutDirectory, __inout HANDLE* phSyncpointEvent, __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary @@ -807,17 +899,6 @@ static HRESULT ProcessPackage( { HRESULT hr = S_OK; BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary = NULL; - BOOL fPlanPackageBegan = FALSE; - - // Remember the default requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it. - hr = PlanDefaultPackageRequestState(pPackage->type, pPackage->currentState, !pPackage->fUninstallable, pPackage->cacheType, pPlan->action, pVariables, pPackage->sczInstallCondition, relationType, &pPackage->defaultRequested); - ExitOnFailure(hr, "Failed to set default package state."); - - pPackage->requested = pPackage->defaultRequested; - fPlanPackageBegan = TRUE; - - hr = UserExperienceOnPlanPackageBegin(pUX, pPackage->sczId, &pPackage->requested); - ExitOnRootFailure(hr, "BA aborted plan package begin."); pEffectiveRollbackBoundary = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackage->pRollbackBoundaryBackward : pPackage->pRollbackBoundaryForward; hr = ProcessPackageRollbackBoundary(pPlan, pEffectiveRollbackBoundary, ppRollbackBoundary); @@ -896,11 +977,6 @@ static HRESULT ProcessPackage( } LExit: - if (fPlanPackageBegan) - { - UserExperienceOnPlanPackageComplete(pUX, pPackage->sczId, hr, pPackage->currentState, pPackage->requested, pPackage->execute, pPackage->rollback); - } - return hr; } @@ -1007,7 +1083,7 @@ extern "C" HRESULT PlanExecutePackage( HRESULT hr = S_OK; BOOL fBARequestedCache = FALSE; - hr = CalculateExecuteActions(pUserExperience, pPackage, pVariables, pPlan->pActiveRollbackBoundary, &fBARequestedCache); + hr = CalculateExecuteActions(pPackage, pPlan->pActiveRollbackBoundary, &fBARequestedCache); ExitOnFailure(hr, "Failed to calculate plan actions for package: %ls", pPackage->sczId); // Calculate package states based on reference count and plan certain dependency actions prior to planning the package execute action. @@ -1801,6 +1877,9 @@ static void ResetPlannedPackageState( { BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; + pFeature->expectedState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; + pFeature->defaultRequested = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; + pFeature->requested = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; pFeature->execute = BOOTSTRAPPER_FEATURE_ACTION_NONE; pFeature->rollback = BOOTSTRAPPER_FEATURE_ACTION_NONE; } @@ -1811,6 +1890,7 @@ static void ResetPlannedPackageState( { BURN_MSPTARGETPRODUCT* pTargetProduct = &pPackage->Msp.rgTargetProducts[i]; + pTargetProduct->requested = BOOTSTRAPPER_REQUEST_STATE_NONE; pTargetProduct->execute = BOOTSTRAPPER_ACTION_STATE_NONE; pTargetProduct->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; } @@ -2738,9 +2818,7 @@ LExit: } static HRESULT CalculateExecuteActions( - __in BURN_USER_EXPERIENCE* pUserExperience, __in BURN_PACKAGE* pPackage, - __in BURN_VARIABLES* pVariables, __in_opt BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary, __out_opt BOOL* pfBARequestedCache ) @@ -2756,11 +2834,11 @@ static HRESULT CalculateExecuteActions( break; case BURN_PACKAGE_TYPE_MSI: - hr = MsiEnginePlanCalculatePackage(pPackage, pVariables, pUserExperience, fInsideMsiTransaction, pfBARequestedCache); + hr = MsiEnginePlanCalculatePackage(pPackage, fInsideMsiTransaction, pfBARequestedCache); break; case BURN_PACKAGE_TYPE_MSP: - hr = MspEnginePlanCalculatePackage(pPackage, pUserExperience, fInsideMsiTransaction, pfBARequestedCache); + hr = MspEnginePlanCalculatePackage(pPackage, fInsideMsiTransaction, pfBARequestedCache); break; case BURN_PACKAGE_TYPE_MSU: diff --git a/src/engine/plan.h b/src/engine/plan.h index 2f6c9dd3..23e4e312 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -382,8 +382,7 @@ HRESULT PlanDefaultPackageRequestState( __in BOOL fPermanent, __in BURN_CACHE_TYPE cacheType, __in BOOTSTRAPPER_ACTION action, - __in BURN_VARIABLES* pVariables, - __in_z_opt LPCWSTR wzInstallCondition, + __in BOOL fInstallCondition, __in BOOTSTRAPPER_RELATION_TYPE relationType, __out BOOTSTRAPPER_REQUEST_STATE* pRequestState ); @@ -396,13 +395,11 @@ HRESULT PlanLayoutBundle( __out_z LPWSTR* psczLayoutDirectory ); HRESULT PlanPackages( - __in BURN_REGISTRATION* pRegistration, __in BURN_USER_EXPERIENCE* pUX, __in BURN_PACKAGES* pPackages, __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, - __in BOOL fBundleInstalled, __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z_opt LPCWSTR wzLayoutDirectory, diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index 12c3f6df..88b07d68 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -111,7 +111,7 @@ extern "C" HRESULT UserExperienceLoad( args.pCommand = pCommand; args.pfnBootstrapperEngineProc = EngineForApplicationProc; args.pvBootstrapperEngineProcContext = pEngineContext; - args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 1, 30, 0); + args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 2, 18, 0); results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS); @@ -1606,9 +1606,36 @@ LExit: return hr; } +EXTERN_C BAAPI UserExperienceOnPlannedPackage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in BOOTSTRAPPER_ACTION_STATE execute, + __in BOOTSTRAPPER_ACTION_STATE rollback + ) +{ + HRESULT hr = S_OK; + BA_ONPLANNEDPACKAGE_ARGS args = { }; + BA_ONPLANNEDPACKAGE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.execute = execute; + args.rollback = rollback; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDPACKAGE, &args, &results); + ExitOnFailure(hr, "BA OnPlannedPackage failed."); + +LExit: + return hr; +} + EXTERN_C BAAPI UserExperienceOnPlanPackageBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, + __in BOOTSTRAPPER_PACKAGE_STATE state, + __in BOOL fInstallCondition, __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState ) { @@ -1618,6 +1645,8 @@ EXTERN_C BAAPI UserExperienceOnPlanPackageBegin( args.cbSize = sizeof(args); args.wzPackageId = wzPackageId; + args.state = state; + args.fInstallCondition = fInstallCondition; args.recommendedState = *pRequestedState; results.cbSize = sizeof(results); @@ -1640,10 +1669,7 @@ EXTERN_C BAAPI UserExperienceOnPlanPackageComplete( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, __in HRESULT hrStatus, - __in BOOTSTRAPPER_PACKAGE_STATE state, - __in BOOTSTRAPPER_REQUEST_STATE requested, - __in BOOTSTRAPPER_ACTION_STATE execute, - __in BOOTSTRAPPER_ACTION_STATE rollback + __in BOOTSTRAPPER_REQUEST_STATE requested ) { HRESULT hr = S_OK; @@ -1653,10 +1679,7 @@ EXTERN_C BAAPI UserExperienceOnPlanPackageComplete( args.cbSize = sizeof(args); args.wzPackageId = wzPackageId; args.hrStatus = hrStatus; - args.state = state; args.requested = requested; - args.execute = execute; - args.rollback = rollback; results.cbSize = sizeof(results); diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index 930e7268..f02e6279 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -372,19 +372,24 @@ BAAPI UserExperienceOnPlanMsiPackage( __inout INSTALLUILEVEL* pUiLevel, __inout BOOL* pfDisableExternalUiHandler ); +BAAPI UserExperienceOnPlannedPackage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in BOOTSTRAPPER_ACTION_STATE execute, + __in BOOTSTRAPPER_ACTION_STATE rollback + ); BAAPI UserExperienceOnPlanPackageBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, + __in BOOTSTRAPPER_PACKAGE_STATE state, + __in BOOL fInstallCondition, __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState ); BAAPI UserExperienceOnPlanPackageComplete( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, __in HRESULT hrStatus, - __in BOOTSTRAPPER_PACKAGE_STATE state, - __in BOOTSTRAPPER_REQUEST_STATE requested, - __in BOOTSTRAPPER_ACTION_STATE execute, - __in BOOTSTRAPPER_ACTION_STATE rollback + __in BOOTSTRAPPER_REQUEST_STATE requested ); BAAPI UserExperienceOnPlanRelatedBundle( __in BURN_USER_EXPERIENCE* pUserExperience, -- cgit v1.2.3-55-g6feb From 837f2e309c8ef0476668c342f612dfe1bb26cb87 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Thu, 18 Feb 2021 10:45:20 -0600 Subject: Only the requested state and Cache=always should affect requestedCache. --- src/engine/exeengine.cpp | 24 +----------------------- src/engine/exeengine.h | 3 +-- src/engine/msiengine.cpp | 14 +------------- src/engine/msiengine.h | 3 +-- src/engine/mspengine.cpp | 14 +------------- src/engine/mspengine.h | 3 +-- src/engine/msuengine.cpp | 13 +------------ src/engine/msuengine.h | 3 +-- src/engine/package.cpp | 1 - src/engine/package.h | 1 - src/engine/plan.cpp | 39 ++++++++++++++++++++------------------- 11 files changed, 28 insertions(+), 90 deletions(-) diff --git a/src/engine/exeengine.cpp b/src/engine/exeengine.cpp index f734edca..cee755e3 100644 --- a/src/engine/exeengine.cpp +++ b/src/engine/exeengine.cpp @@ -157,25 +157,12 @@ LExit: // PlanCalculate - calculates the execute and rollback state for the requested package state. // extern "C" HRESULT ExeEnginePlanCalculatePackage( - __in BURN_PACKAGE* pPackage, - __out_opt BOOL* pfBARequestedCache + __in BURN_PACKAGE* pPackage ) { HRESULT hr = S_OK; - //BOOL fCondition = FALSE; - //BOOTSTRAPPER_PACKAGE_STATE expected = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - BOOL fBARequestedCache = FALSE; - - //// evaluate rollback install condition - //if (pPackage->sczRollbackInstallCondition) - //{ - // hr = ConditionEvaluate(pVariables, pPackage->sczRollbackInstallCondition, &fCondition); - // ExitOnFailure(hr, "Failed to evaluate rollback install condition."); - - // expected = fCondition ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT; - //} // execute action switch (pPackage->currentState) @@ -209,10 +196,6 @@ extern "C" HRESULT ExeEnginePlanCalculatePackage( case BOOTSTRAPPER_REQUEST_STATE_REPAIR: execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; break; - case BOOTSTRAPPER_REQUEST_STATE_CACHE: - execute = BOOTSTRAPPER_ACTION_STATE_NONE; - fBARequestedCache = TRUE; - break; default: execute = BOOTSTRAPPER_ACTION_STATE_NONE; break; @@ -273,11 +256,6 @@ extern "C" HRESULT ExeEnginePlanCalculatePackage( pPackage->execute = execute; pPackage->rollback = rollback; - if (pfBARequestedCache) - { - *pfBARequestedCache = fBARequestedCache; - } - LExit: return hr; } diff --git a/src/engine/exeengine.h b/src/engine/exeengine.h index 1eac4232..88a884eb 100644 --- a/src/engine/exeengine.h +++ b/src/engine/exeengine.h @@ -21,8 +21,7 @@ HRESULT ExeEngineDetectPackage( __in BURN_VARIABLES* pVariables ); HRESULT ExeEnginePlanCalculatePackage( - __in BURN_PACKAGE* pPackage, - __out_opt BOOL* pfBARequestedCache + __in BURN_PACKAGE* pPackage ); HRESULT ExeEnginePlanAddPackage( __in_opt DWORD *pdwInsertSequence, diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index 252a17b7..801bf9a8 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -714,8 +714,7 @@ LExit: // extern "C" HRESULT MsiEnginePlanCalculatePackage( __in BURN_PACKAGE* pPackage, - __in BOOL fInsideMsiTransaction, - __out_opt BOOL* pfBARequestedCache + __in BOOL fInsideMsiTransaction ) { Trace(REPORT_STANDARD, "Planning MSI package 0x%p", pPackage); @@ -728,7 +727,6 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; BOOL fFeatureActionDelta = FALSE; BOOL fRollbackFeatureActionDelta = FALSE; - BOOL fBARequestedCache = FALSE; if (pPackage->Msi.cFeatures) { @@ -813,11 +811,6 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; break; - case BOOTSTRAPPER_REQUEST_STATE_CACHE: - execute = BOOTSTRAPPER_ACTION_STATE_NONE; - fBARequestedCache = TRUE; - break; - default: execute = BOOTSTRAPPER_ACTION_STATE_NONE; break; @@ -880,11 +873,6 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( pPackage->execute = execute; pPackage->rollback = rollback; - if (pfBARequestedCache) - { - *pfBARequestedCache = fBARequestedCache; - } - LExit: return hr; } diff --git a/src/engine/msiengine.h b/src/engine/msiengine.h index b06866cd..fe742a16 100644 --- a/src/engine/msiengine.h +++ b/src/engine/msiengine.h @@ -38,8 +38,7 @@ HRESULT MsiEnginePlanInitializePackage( ); HRESULT MsiEnginePlanCalculatePackage( __in BURN_PACKAGE* pPackage, - __in BOOL fInsideMsiTransaction, - __out_opt BOOL* pfBARequestedCache + __in BOOL fInsideMsiTransaction ); HRESULT MsiEnginePlanAddPackage( __in BOOTSTRAPPER_DISPLAY display, diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp index f742ecef..81e85e4c 100644 --- a/src/engine/mspengine.cpp +++ b/src/engine/mspengine.cpp @@ -305,12 +305,10 @@ LExit: // extern "C" HRESULT MspEnginePlanCalculatePackage( __in BURN_PACKAGE* pPackage, - __in BOOL fInsideMsiTransaction, - __out BOOL* pfBARequestedCache + __in BOOL fInsideMsiTransaction ) { HRESULT hr = S_OK; - BOOL fBARequestedCache = FALSE; for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) { @@ -352,11 +350,6 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; break; - case BOOTSTRAPPER_REQUEST_STATE_CACHE: - execute = BOOTSTRAPPER_ACTION_STATE_NONE; - fBARequestedCache = TRUE; - break; - default: execute = BOOTSTRAPPER_ACTION_STATE_NONE; break; @@ -419,11 +412,6 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( } } - if (pfBARequestedCache) - { - *pfBARequestedCache = fBARequestedCache; - } - return hr; } diff --git a/src/engine/mspengine.h b/src/engine/mspengine.h index 7ee01e8e..28682169 100644 --- a/src/engine/mspengine.h +++ b/src/engine/mspengine.h @@ -38,8 +38,7 @@ HRESULT MspEnginePlanInitializePackage( ); HRESULT MspEnginePlanCalculatePackage( __in BURN_PACKAGE* pPackage, - __in BOOL fInsideMsiTransaction, - __out_opt BOOL* pfBARequestedCache + __in BOOL fInsideMsiTransaction ); HRESULT MspEnginePlanAddPackage( __in BOOTSTRAPPER_DISPLAY display, diff --git a/src/engine/msuengine.cpp b/src/engine/msuengine.cpp index 499e1da6..91bbf361 100644 --- a/src/engine/msuengine.cpp +++ b/src/engine/msuengine.cpp @@ -82,14 +82,12 @@ LExit: // PlanCalculate - calculates the execute and rollback state for the requested package state. // extern "C" HRESULT MsuEnginePlanCalculatePackage( - __in BURN_PACKAGE* pPackage, - __out_opt BOOL* pfBARequestedCache + __in BURN_PACKAGE* pPackage ) { HRESULT hr = S_OK; BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - BOOL fBARequestedCache = FALSE; BOOL fAllowUninstall = FALSE; // We can only uninstall MSU packages if they have a KB and we are on Win7 or newer. @@ -129,10 +127,6 @@ extern "C" HRESULT MsuEnginePlanCalculatePackage( execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; break; - case BOOTSTRAPPER_REQUEST_STATE_CACHE: - execute = BOOTSTRAPPER_ACTION_STATE_NONE; - fBARequestedCache = TRUE; - default: execute = BOOTSTRAPPER_ACTION_STATE_NONE; break; @@ -187,11 +181,6 @@ extern "C" HRESULT MsuEnginePlanCalculatePackage( pPackage->execute = execute; pPackage->rollback = rollback; - if (pfBARequestedCache) - { - *pfBARequestedCache = fBARequestedCache; - } - LExit: return hr; } diff --git a/src/engine/msuengine.h b/src/engine/msuengine.h index 7f57a084..cd966b0a 100644 --- a/src/engine/msuengine.h +++ b/src/engine/msuengine.h @@ -21,8 +21,7 @@ HRESULT MsuEngineDetectPackage( __in BURN_VARIABLES* pVariables ); HRESULT MsuEnginePlanCalculatePackage( - __in BURN_PACKAGE* pPackage, - __out_opt BOOL* pfBARequestedCache + __in BURN_PACKAGE* pPackage ); HRESULT MsuEnginePlanAddPackage( __in BURN_PACKAGE* pPackage, diff --git a/src/engine/package.cpp b/src/engine/package.cpp index b27b1e07..bb61cdcd 100644 --- a/src/engine/package.cpp +++ b/src/engine/package.cpp @@ -317,7 +317,6 @@ extern "C" void PackageUninitialize( ReleaseStr(pPackage->sczLogPathVariable); ReleaseStr(pPackage->sczRollbackLogPathVariable); ReleaseStr(pPackage->sczInstallCondition); - ReleaseStr(pPackage->sczRollbackInstallCondition); ReleaseStr(pPackage->sczCacheId); if (pPackage->rgDependencyProviders) diff --git a/src/engine/package.h b/src/engine/package.h index a728fcc6..5feb3f46 100644 --- a/src/engine/package.h +++ b/src/engine/package.h @@ -201,7 +201,6 @@ typedef struct _BURN_PACKAGE LPWSTR sczRollbackLogPathVariable; // name of the variable that will be set to the rollback path. LPWSTR sczInstallCondition; - LPWSTR sczRollbackInstallCondition; BOOL fPerMachine; BOOL fUninstallable; BOOL fVital; diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index 29adfb42..d99fdde2 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -166,11 +166,11 @@ static HRESULT PlanDependencyActions( ); static HRESULT CalculateExecuteActions( __in BURN_PACKAGE* pPackage, - __in_opt BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary, - __out_opt BOOL* pfBARequestedCache + __in_opt BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary ); static BOOL NeedsCache( - __in BURN_PACKAGE* pPackage + __in BURN_PACKAGE* pPackage, + __in BOOL fExecute ); static HRESULT CreateContainerProgress( __in BURN_PLAN* pPlan, @@ -1081,24 +1081,24 @@ extern "C" HRESULT PlanExecutePackage( ) { HRESULT hr = S_OK; - BOOL fBARequestedCache = FALSE; + BOOL fRequestedCache = BOOTSTRAPPER_REQUEST_STATE_CACHE == pPackage->requested || + BOOTSTRAPPER_REQUEST_STATE_ABSENT < pPackage->requested && BURN_CACHE_TYPE_ALWAYS == pPackage->cacheType; - hr = CalculateExecuteActions(pPackage, pPlan->pActiveRollbackBoundary, &fBARequestedCache); + hr = CalculateExecuteActions(pPackage, pPlan->pActiveRollbackBoundary); ExitOnFailure(hr, "Failed to calculate plan actions for package: %ls", pPackage->sczId); // Calculate package states based on reference count and plan certain dependency actions prior to planning the package execute action. hr = DependencyPlanPackageBegin(fPerMachine, pPackage, pPlan); ExitOnFailure(hr, "Failed to begin plan dependency actions for package: %ls", pPackage->sczId); - if (fBARequestedCache || NeedsCache(pPackage)) + if (fRequestedCache || NeedsCache(pPackage, TRUE)) { hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent); ExitOnFailure(hr, "Failed to plan cache package."); } - else if (BURN_CACHE_STATE_COMPLETE != pPackage->cache && // if the package is not in the cache, disable any rollback that would require the package from the cache. - (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pPackage->rollback || (BURN_PACKAGE_TYPE_EXE == pPackage->type && BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)) - ) + else if (BURN_CACHE_STATE_COMPLETE != pPackage->cache && NeedsCache(pPackage, FALSE)) { + // If the package is not in the cache, disable any rollback that would require the package from the cache. LogId(REPORT_STANDARD, MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE, pPackage->sczId, LoggingCacheStateToString(pPackage->cache), LoggingActionStateToString(pPackage->rollback)); pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; } @@ -1431,7 +1431,7 @@ extern "C" HRESULT PlanRelatedBundlesComplete( if (BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested) { - hr = ExeEnginePlanCalculatePackage(&pRelatedBundle->package, NULL); + hr = ExeEnginePlanCalculatePackage(&pRelatedBundle->package); ExitOnFailure(hr, "Failed to calcuate plan for related bundle: %ls", pRelatedBundle->package.sczId); // Calculate package states based on reference count for addon and patch related bundles. @@ -2819,8 +2819,7 @@ LExit: static HRESULT CalculateExecuteActions( __in BURN_PACKAGE* pPackage, - __in_opt BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary, - __out_opt BOOL* pfBARequestedCache + __in_opt BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary ) { HRESULT hr = S_OK; @@ -2830,19 +2829,19 @@ static HRESULT CalculateExecuteActions( switch (pPackage->type) { case BURN_PACKAGE_TYPE_EXE: - hr = ExeEnginePlanCalculatePackage(pPackage, pfBARequestedCache); + hr = ExeEnginePlanCalculatePackage(pPackage); break; case BURN_PACKAGE_TYPE_MSI: - hr = MsiEnginePlanCalculatePackage(pPackage, fInsideMsiTransaction, pfBARequestedCache); + hr = MsiEnginePlanCalculatePackage(pPackage, fInsideMsiTransaction); break; case BURN_PACKAGE_TYPE_MSP: - hr = MspEnginePlanCalculatePackage(pPackage, fInsideMsiTransaction, pfBARequestedCache); + hr = MspEnginePlanCalculatePackage(pPackage, fInsideMsiTransaction); break; case BURN_PACKAGE_TYPE_MSU: - hr = MsuEnginePlanCalculatePackage(pPackage, pfBARequestedCache); + hr = MsuEnginePlanCalculatePackage(pPackage); break; default: @@ -2855,16 +2854,18 @@ LExit: } static BOOL NeedsCache( - __in BURN_PACKAGE* pPackage + __in BURN_PACKAGE* pPackage, + __in BOOL fExecute ) { + BOOTSTRAPPER_ACTION_STATE action = fExecute ? pPackage->execute : pPackage->rollback; if (BURN_PACKAGE_TYPE_EXE == pPackage->type) // Exe packages require the package for all operations (even uninstall). { - return BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute; + return BOOTSTRAPPER_ACTION_STATE_NONE != action; } else // The other package types can uninstall without the original package. { - return BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pPackage->execute; + return BOOTSTRAPPER_ACTION_STATE_UNINSTALL < action; } } -- cgit v1.2.3-55-g6feb From d0d93beac0b79fa9c3d43398813954988afda18f Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 21 Feb 2021 11:54:54 -0600 Subject: Add logging for patch target products. --- src/engine/core.cpp | 11 ++++++++++ src/engine/elevation.cpp | 7 ------- src/engine/engine.mc | 14 +++++++++++++ src/engine/logging.cpp | 18 +++++++++++++++++ src/engine/logging.h | 5 +++++ src/engine/mspengine.cpp | 6 +++--- src/engine/package.h | 10 ++++++++++ src/engine/plan.cpp | 52 ++++++++++++++++++++++++++++++++---------------- src/engine/plan.h | 6 ++---- 9 files changed, 98 insertions(+), 31 deletions(-) diff --git a/src/engine/core.cpp b/src/engine/core.cpp index b68681fb..1a079973 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -1822,6 +1822,17 @@ static void LogPackages( LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURE, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState), LoggingMsiFeatureStateToString(pFeature->defaultRequested), LoggingMsiFeatureStateToString(pFeature->requested), LoggingMsiFeatureActionToString(pFeature->execute), LoggingMsiFeatureActionToString(pFeature->rollback)); } } + else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.cTargetProductCodes) + { + LogId(REPORT_STANDARD, MSG_PLANNED_MSP_TARGETS, pPackage->Msp.cTargetProductCodes, pPackage->sczId); + + for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j) + { + const BURN_MSPTARGETPRODUCT* pTargetProduct = &pPackage->Msp.rgTargetProducts[j]; + + LogId(REPORT_STANDARD, MSG_PLANNED_MSP_TARGET, pTargetProduct->wzTargetProductCode, LoggingPackageStateToString(pTargetProduct->patchPackageState), LoggingRequestStateToString(pTargetProduct->defaultRequested), LoggingRequestStateToString(pTargetProduct->requested), LoggingMspTargetActionToString(pTargetProduct->execute, pTargetProduct->executeSkip), LoggingMspTargetActionToString(pTargetProduct->rollback, pTargetProduct->rollbackSkip)); + } + } } // Display related bundles last if caching, installing, modifying, or repairing. diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp index cd0c9387..e4af1840 100644 --- a/src/engine/elevation.cpp +++ b/src/engine/elevation.cpp @@ -855,7 +855,6 @@ extern "C" HRESULT ElevationExecuteMsiPackage( DWORD dwResult = 0; // serialize message data - // TODO: for patching we might not have a package hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msiPackage.pPackage->sczId); ExitOnFailure(hr, "Failed to write package id to message buffer."); @@ -964,9 +963,6 @@ extern "C" HRESULT ElevationExecuteMspPackage( for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i) { - hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->mspTarget.rgOrderedPatches[i].dwOrder); - ExitOnFailure(hr, "Failed to write ordered patch order to message buffer."); - hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage->sczId); ExitOnFailure(hr, "Failed to write ordered patch id to message buffer."); } @@ -2404,9 +2400,6 @@ static HRESULT OnExecuteMspPackage( for (DWORD i = 0; i < executeAction.mspTarget.cOrderedPatches; ++i) { - hr = BuffReadNumber(pbData, cbData, &iData, &executeAction.mspTarget.rgOrderedPatches[i].dwOrder); - ExitOnFailure(hr, "Failed to read ordered patch order number."); - hr = BuffReadString(pbData, cbData, &iData, &sczPackage); ExitOnFailure(hr, "Failed to read ordered patch package id."); diff --git a/src/engine/engine.mc b/src/engine/engine.mc index fb11430b..1cdde207 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -415,6 +415,20 @@ Language=English Plan skipped dependent bundle repair: %1!ls!, type: %2!hs!, because no packages are being executed during this uninstall operation. . +MessageId=218 +Severity=Success +SymbolicName=MSG_PLANNED_MSP_TARGETS +Language=English +Plan %1!u! patch targets for package: %2!ls! +. + +MessageId=219 +Severity=Success +SymbolicName=MSG_PLANNED_MSP_TARGET +Language=English +Planned patch target: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs! +. + MessageId=299 Severity=Success SymbolicName=MSG_PLAN_COMPLETE diff --git a/src/engine/logging.cpp b/src/engine/logging.cpp index a9646218..d66f7cf5 100644 --- a/src/engine/logging.cpp +++ b/src/engine/logging.cpp @@ -531,6 +531,24 @@ extern "C" LPCWSTR LoggingBurnMsiPropertyToString( } } +extern "C" LPCSTR LoggingMspTargetActionToString( + __in BOOTSTRAPPER_ACTION_STATE action, + __in BURN_PATCH_SKIP_STATE skipState + ) +{ + switch (skipState) + { + case BURN_PATCH_SKIP_STATE_NONE: + return LoggingActionStateToString(action); + case BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL: + return "Skipped (target uninstall)"; + case BURN_PATCH_SKIP_STATE_SLIPSTREAM: + return "Skipped (slipstream)"; + default: + return "Invalid"; + } +} + extern "C" LPCSTR LoggingPerMachineToString( __in BOOL fPerMachine ) diff --git a/src/engine/logging.h b/src/engine/logging.h index 49a8ef5d..a69e34c5 100644 --- a/src/engine/logging.h +++ b/src/engine/logging.h @@ -114,6 +114,11 @@ LPCWSTR LoggingBurnMsiPropertyToString( __in BURN_MSI_PROPERTY burnMsiProperty ); +LPCSTR LoggingMspTargetActionToString( + __in BOOTSTRAPPER_ACTION_STATE action, + __in BURN_PATCH_SKIP_STATE skipState + ); + LPCSTR LoggingPerMachineToString( __in BOOL fPerMachine ); diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp index 81e85e4c..14db27a6 100644 --- a/src/engine/mspengine.cpp +++ b/src/engine/mspengine.cpp @@ -290,7 +290,7 @@ extern "C" HRESULT MspEnginePlanInitializePackage( { BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; - pTargetProduct->requested = pPackage->requested; + pTargetProduct->defaultRequested = pTargetProduct->requested = pPackage->requested; hr = UserExperienceOnPlanPatchTarget(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, &pTargetProduct->requested); ExitOnRootFailure(hr, "BA aborted plan patch target."); @@ -1086,14 +1086,14 @@ static HRESULT PlanTargetProduct( hr = MemEnsureArraySize(reinterpret_cast(&pAction->mspTarget.rgOrderedPatches), pAction->mspTarget.cOrderedPatches + 1, sizeof(BURN_ORDERED_PATCHES), 2); ExitOnFailure(hr, "Failed grow array of ordered patches."); - pAction->mspTarget.rgOrderedPatches[pAction->mspTarget.cOrderedPatches].dwOrder = pTargetProduct->dwOrder; + pAction->mspTarget.rgOrderedPatches[pAction->mspTarget.cOrderedPatches].pTargetProduct = pTargetProduct; pAction->mspTarget.rgOrderedPatches[pAction->mspTarget.cOrderedPatches].pPackage = pPackage; ++pAction->mspTarget.cOrderedPatches; // Insertion sort to keep the patches ordered. for (DWORD i = pAction->mspTarget.cOrderedPatches - 1; i > 0; --i) { - if (pAction->mspTarget.rgOrderedPatches[i].dwOrder < pAction->mspTarget.rgOrderedPatches[i - 1].dwOrder) + if (pAction->mspTarget.rgOrderedPatches[i].pTargetProduct->dwOrder < pAction->mspTarget.rgOrderedPatches[i - 1].pTargetProduct->dwOrder) { BURN_ORDERED_PATCHES temp = pAction->mspTarget.rgOrderedPatches[i - 1]; pAction->mspTarget.rgOrderedPatches[i - 1] = pAction->mspTarget.rgOrderedPatches[i]; diff --git a/src/engine/package.h b/src/engine/package.h index 5feb3f46..3a243c7d 100644 --- a/src/engine/package.h +++ b/src/engine/package.h @@ -86,6 +86,13 @@ enum BURN_PACKAGE_REGISTRATION_STATE BURN_PACKAGE_REGISTRATION_STATE_PRESENT, }; +enum BURN_PATCH_SKIP_STATE +{ + BURN_PATCH_SKIP_STATE_NONE, + BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL, + BURN_PATCH_SKIP_STATE_SLIPSTREAM, +}; + // structs typedef struct _BURN_EXE_EXIT_CODE @@ -112,9 +119,12 @@ typedef struct _BURN_MSPTARGETPRODUCT BOOL fSlipstream; BOOTSTRAPPER_PACKAGE_STATE patchPackageState; // only valid after Detect. + BOOTSTRAPPER_REQUEST_STATE defaultRequested; // only valid during Plan. BOOTSTRAPPER_REQUEST_STATE requested; // only valid during Plan. BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan. BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan. + BURN_PATCH_SKIP_STATE executeSkip; // only valid during Plan. + BURN_PATCH_SKIP_STATE rollbackSkip; // only valid during Plan. BURN_PACKAGE_REGISTRATION_STATE registrationState; // initialized during Detect, updated during Apply. BURN_PACKAGE_REGISTRATION_STATE transactionRegistrationState;// only valid during Apply inside an MSI transaction. diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index d99fdde2..95ea0b05 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -149,7 +149,7 @@ static BURN_CACHE_ACTION* ProcessSharedPayload( __in BURN_PLAN* pPlan, __in BURN_PAYLOAD* pPayload ); -static HRESULT RemoveUnnecessaryActions( +static void RemoveUnnecessaryActions( __in BOOL fExecute, __in BURN_EXECUTE_ACTION* rgActions, __in DWORD cActions @@ -302,7 +302,6 @@ extern "C" void PlanUninitializeExecuteAction( ReleaseStr(pExecuteAction->msiPackage.sczLogPath); ReleaseMem(pExecuteAction->msiPackage.rgFeatures); ReleaseMem(pExecuteAction->msiPackage.rgSlipstreamPatches); - ReleaseMem(pExecuteAction->msiPackage.rgOrderedPatches); break; case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: @@ -1499,11 +1498,9 @@ extern "C" HRESULT PlanFinalizeActions( { HRESULT hr = S_OK; - hr = RemoveUnnecessaryActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions); - ExitOnFailure(hr, "Failed to remove unnecessary execute actions."); + RemoveUnnecessaryActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions); - hr = RemoveUnnecessaryActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions); - ExitOnFailure(hr, "Failed to remove unnecessary execute actions."); + RemoveUnnecessaryActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions); hr = FinalizeSlipstreamPatchActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions); ExitOnFailure(hr, "Failed to finalize slipstream execute actions."); @@ -1890,9 +1887,12 @@ static void ResetPlannedPackageState( { BURN_MSPTARGETPRODUCT* pTargetProduct = &pPackage->Msp.rgTargetProducts[i]; + pTargetProduct->defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE; pTargetProduct->requested = BOOTSTRAPPER_REQUEST_STATE_NONE; pTargetProduct->execute = BOOTSTRAPPER_ACTION_STATE_NONE; pTargetProduct->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + pTargetProduct->executeSkip = BURN_PATCH_SKIP_STATE_NONE; + pTargetProduct->rollbackSkip = BURN_PATCH_SKIP_STATE_NONE; } } } @@ -2704,13 +2704,12 @@ static BURN_CACHE_ACTION* ProcessSharedPayload( return pAcquireAction; } -static HRESULT RemoveUnnecessaryActions( +static void RemoveUnnecessaryActions( __in BOOL fExecute, __in BURN_EXECUTE_ACTION* rgActions, __in DWORD cActions ) { - HRESULT hr = S_OK; LPCSTR szExecuteOrRollback = fExecute ? "execute" : "rollback"; for (DWORD i = 0; i < cActions; ++i) @@ -2722,10 +2721,11 @@ static HRESULT RemoveUnnecessaryActions( if (BURN_EXECUTE_ACTION_TYPE_MSP_TARGET == pAction->type && pAction->mspTarget.pChainedTargetPackage) { BOOTSTRAPPER_ACTION_STATE chainedTargetPackageAction = fExecute ? pAction->mspTarget.pChainedTargetPackage->execute : pAction->mspTarget.pChainedTargetPackage->rollback; + BURN_PATCH_SKIP_STATE skipState = BURN_PATCH_SKIP_STATE_NONE; if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == chainedTargetPackageAction) { + skipState = BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL; LogId(REPORT_STANDARD, MSG_PLAN_SKIP_PATCH_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback); - pAction->fDeleted = TRUE; } else if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < chainedTargetPackageAction && pAction->mspTarget.fSlipstream && BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pAction->mspTarget.action) { @@ -2737,14 +2737,31 @@ static HRESULT RemoveUnnecessaryActions( // is already on the machine. The slipstream must be installed standalone if the MSI is being repaired. if (BOOTSTRAPPER_ACTION_STATE_REPAIR != chainedTargetPackageAction || BOOTSTRAPPER_ACTION_STATE_REPAIR == pAction->mspTarget.action) { + skipState = BURN_PATCH_SKIP_STATE_SLIPSTREAM; LogId(REPORT_STANDARD, MSG_PLAN_SKIP_SLIPSTREAM_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback); - pAction->fDeleted = TRUE; + } + } + + if (BURN_PATCH_SKIP_STATE_NONE != skipState) + { + pAction->fDeleted = TRUE; + + for (DWORD j = 0; j < pAction->mspTarget.cOrderedPatches; ++j) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pAction->mspTarget.rgOrderedPatches[j].pTargetProduct; + + if (fExecute) + { + pTargetProduct->executeSkip = skipState; + } + else + { + pTargetProduct->rollbackSkip = skipState; + } } } } } - - return hr; } static HRESULT FinalizeSlipstreamPatchActions( @@ -3047,17 +3064,13 @@ static void ExecuteActionLog( case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: LogStringLine(PlanDumpLevel, "%ls action[%u]: MSI_PACKAGE package id: %ls, action: %hs, action msi property: %ls, ui level: %u, disable externaluihandler: %ls, log path: %ls, logging attrib: %u", wzBase, iAction, pAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pAction->msiPackage.action), LoggingBurnMsiPropertyToString(pAction->msiPackage.actionMsiProperty), pAction->msiPackage.uiLevel, pAction->msiPackage.fDisableExternalUiHandler ? L"yes" : L"no", pAction->msiPackage.sczLogPath, pAction->msiPackage.dwLoggingAttributes); - for (DWORD j = 0; j < pAction->msiPackage.cPatches; ++j) - { - LogStringLine(PlanDumpLevel, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->msiPackage.rgOrderedPatches[j].dwOrder, pAction->msiPackage.rgOrderedPatches[j].pPackage->sczId); - } break; case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: LogStringLine(PlanDumpLevel, "%ls action[%u]: MSP_TARGET package id: %ls, action: %hs, target product code: %ls, target per-machine: %ls, action msi property: %ls, ui level: %u, disable externaluihandler: %ls, log path: %ls", wzBase, iAction, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.sczTargetProductCode, pAction->mspTarget.fPerMachineTarget ? L"yes" : L"no", LoggingBurnMsiPropertyToString(pAction->mspTarget.actionMsiProperty), pAction->mspTarget.uiLevel, pAction->mspTarget.fDisableExternalUiHandler ? L"yes" : L"no", pAction->mspTarget.sczLogPath); for (DWORD j = 0; j < pAction->mspTarget.cOrderedPatches; ++j) { - LogStringLine(PlanDumpLevel, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->mspTarget.rgOrderedPatches[j].dwOrder, pAction->mspTarget.rgOrderedPatches[j].pPackage->sczId); + LogStringLine(PlanDumpLevel, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->mspTarget.rgOrderedPatches[j].pTargetProduct->dwOrder, pAction->mspTarget.rgOrderedPatches[j].pPackage->sczId); } break; @@ -3089,6 +3102,11 @@ static void ExecuteActionLog( AssertSz(FALSE, "Unknown execute action type."); break; } + + if (pAction->fDeleted) + { + LogStringLine(PlanDumpLevel, " (deleted action)"); + } } extern "C" void PlanDump( diff --git a/src/engine/plan.h b/src/engine/plan.h index 23e4e312..5e981561 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -208,8 +208,9 @@ typedef struct _BURN_CACHE_ACTION typedef struct _BURN_ORDERED_PATCHES { - DWORD dwOrder; BURN_PACKAGE* pPackage; + + BURN_MSPTARGETPRODUCT* pTargetProduct; // only valid in the unelevated engine. } BURN_ORDERED_PATCHES; typedef struct _BURN_EXECUTE_ACTION_CHECKPOINT @@ -253,9 +254,6 @@ typedef struct _BURN_EXECUTE_ACTION BOOTSTRAPPER_FEATURE_ACTION* rgFeatures; BOOTSTRAPPER_ACTION_STATE* rgSlipstreamPatches; - - BURN_ORDERED_PATCHES* rgOrderedPatches; - DWORD cPatches; } msiPackage; struct { -- cgit v1.2.3-55-g6feb From 4f4c85ed66f1b2dfb1bec76d54d7b50c637d5bfa Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 22 Feb 2021 16:16:12 -0600 Subject: Add patch target for slipstream MSI package even if not installed. Fixes #3897 --- src/engine/core.cpp | 3 + src/engine/detect.cpp | 10 + src/engine/msiengine.cpp | 56 +++- src/engine/msiengine.h | 3 + src/engine/mspengine.cpp | 196 ++++++++----- src/engine/mspengine.h | 16 +- src/engine/package.cpp | 23 +- src/engine/package.h | 21 +- src/engine/plan.cpp | 4 +- src/test/BurnUnitTest/BurnUnitTest.vcxproj | 1 + src/test/BurnUnitTest/PlanTest.cpp | 303 ++++++++++++++++++++- .../PlanTest/Slipstream_BundleA_manifest.xml | 1 + 12 files changed, 546 insertions(+), 91 deletions(-) create mode 100644 src/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 1a079973..cb7ebbfd 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -351,6 +351,9 @@ extern "C" HRESULT CoreDetect( { hr = MspEngineDetectInitialize(&pEngineState->packages); ExitOnFailure(hr, "Failed to initialize MSP engine detection."); + + hr = MsiEngineDetectInitialize(&pEngineState->packages); + ExitOnFailure(hr, "Failed to initialize MSI engine detection."); } for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp index b702306e..9bb58487 100644 --- a/src/engine/detect.cpp +++ b/src/engine/detect.cpp @@ -82,6 +82,16 @@ extern "C" void DetectReset( pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; } + + for (DWORD iSlipstreamMsp = 0; iSlipstreamMsp < pPackage->Msi.cSlipstreamMspPackages; ++iSlipstreamMsp) + { + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + iSlipstreamMsp; + + pSlipstreamMsp->dwMsiChainedPatchIndex = BURN_PACKAGE_INVALID_PATCH_INDEX; + } + + ReleaseNullMem(pPackage->Msi.rgChainedPatches); + pPackage->Msi.cChainedPatches = 0; } else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) { diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index 801bf9a8..ae105155 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -213,8 +213,8 @@ extern "C" HRESULT MsiEngineParsePackageFromXml( if (cNodes) { - pPackage->Msi.rgpSlipstreamMspPackages = reinterpret_cast(MemAlloc(sizeof(BURN_PACKAGE*) * cNodes, TRUE)); - ExitOnNull(pPackage->Msi.rgpSlipstreamMspPackages, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP packages."); + pPackage->Msi.rgSlipstreamMsps = reinterpret_cast(MemAlloc(sizeof(BURN_SLIPSTREAM_MSP) * cNodes, TRUE)); + ExitOnNull(pPackage->Msi.rgSlipstreamMsps, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP packages."); pPackage->Msi.rgsczSlipstreamMspPackageIds = reinterpret_cast(MemAlloc(sizeof(LPWSTR*) * cNodes, TRUE)); ExitOnNull(pPackage->Msi.rgsczSlipstreamMspPackageIds, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP ids."); @@ -383,15 +383,52 @@ extern "C" void MsiEnginePackageUninitialize( MemFree(pPackage->Msi.rgsczSlipstreamMspPackageIds); } - if (pPackage->Msi.rgpSlipstreamMspPackages) + if (pPackage->Msi.rgSlipstreamMsps) { - MemFree(pPackage->Msi.rgpSlipstreamMspPackages); + MemFree(pPackage->Msi.rgSlipstreamMsps); + } + + if (pPackage->Msi.rgChainedPatches) + { + MemFree(pPackage->Msi.rgChainedPatches); } // clear struct memset(&pPackage->Msi, 0, sizeof(pPackage->Msi)); } +extern "C" HRESULT MsiEngineDetectInitialize( + __in BURN_PACKAGES* pPackages + ) +{ + AssertSz(pPackages->cPatchInfo, "MsiEngineDetectInitialize() should only be called if there are MSP packages."); + + HRESULT hr = S_OK; + + // Add target products for slipstream MSIs that weren't detected. + for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage) + { + BURN_PACKAGE* pMsiPackage = pPackages->rgPackages + iPackage; + if (BURN_PACKAGE_TYPE_MSI == pMsiPackage->type) + { + for (DWORD j = 0; j < pMsiPackage->Msi.cSlipstreamMspPackages; ++j) + { + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pMsiPackage->Msi.rgSlipstreamMsps + j; + Assert(pSlipstreamMsp->pMspPackage && BURN_PACKAGE_TYPE_MSP == pSlipstreamMsp->pMspPackage->type); + + if (pSlipstreamMsp->pMspPackage && BURN_PACKAGE_INVALID_PATCH_INDEX == pSlipstreamMsp->dwMsiChainedPatchIndex) + { + hr = MspEngineAddMissingSlipstreamTarget(pMsiPackage, pSlipstreamMsp); + ExitOnFailure(hr, "Failed to add slipstreamed target product code to package: %ls", pSlipstreamMsp->pMspPackage->sczId); + } + } + } + } + +LExit: + return hr; +} + extern "C" HRESULT MsiEngineDetectPackage( __in BURN_PACKAGE* pPackage, __in BURN_USER_EXPERIENCE* pUserExperience @@ -971,15 +1008,6 @@ extern "C" HRESULT MsiEnginePlanAddPackage( pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes; } - // Update any slipstream patches' state. - for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) - { - BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[i]; - AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches."); - - MspEngineSlipstreamUpdateState(pMspPackage, pPackage->execute, pPackage->rollback); - } - LExit: ReleaseMem(rgFeatureActions); ReleaseMem(rgRollbackFeatureActions); @@ -1897,7 +1925,7 @@ static HRESULT ConcatPatchProperty( { for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) { - BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[i]; + BURN_PACKAGE* pMspPackage = pPackage->Msi.rgSlipstreamMsps[i].pMspPackage; AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches."); BOOTSTRAPPER_ACTION_STATE patchExecuteAction = rgSlipstreamPatchActions[i]; diff --git a/src/engine/msiengine.h b/src/engine/msiengine.h index fe742a16..6c7c83e5 100644 --- a/src/engine/msiengine.h +++ b/src/engine/msiengine.h @@ -27,6 +27,9 @@ HRESULT MsiEngineParsePropertiesFromXml( void MsiEnginePackageUninitialize( __in BURN_PACKAGE* pPackage ); +HRESULT MsiEngineDetectInitialize( + __in BURN_PACKAGES* pPackages + ); HRESULT MsiEngineDetectPackage( __in BURN_PACKAGE* pPackage, __in BURN_USER_EXPERIENCE* pUserExperience diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp index 14db27a6..4b7f9a0a 100644 --- a/src/engine/mspengine.cpp +++ b/src/engine/mspengine.cpp @@ -30,18 +30,23 @@ static HRESULT AddPossibleTargetProduct( __inout DWORD* pcPossibleTargetProducts ); static HRESULT AddDetectedTargetProduct( - __in BURN_PACKAGES* pPackages, __in BURN_PACKAGE* pPackage, __in DWORD dwOrder, __in_z LPCWSTR wzProductCode, - __in MSIINSTALLCONTEXT context + __in MSIINSTALLCONTEXT context, + __out DWORD* pdwTargetProductIndex + ); +static HRESULT AddMsiChainedPatch( + __in BURN_PACKAGE* pPackage, + __in BURN_PACKAGE* pMspPackage, + __in DWORD dwMspTargetProductIndex, + __out DWORD* pdwChainedPatchIndex ); -static void DeterminePatchChainedTarget( +static HRESULT DeterminePatchChainedTarget( __in BURN_PACKAGES* pPackages, __in BURN_PACKAGE* pMspPackage, __in LPCWSTR wzTargetProductCode, - __out BURN_PACKAGE** ppChainedTargetPackage, - __out BOOL* pfSlipstreamed + __in DWORD dwMspTargetProductIndex ); static HRESULT PlanTargetProduct( __in BOOTSTRAPPER_DISPLAY display, @@ -159,16 +164,20 @@ extern "C" HRESULT MspEngineDetectInitialize( { for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo) { - if (ERROR_SUCCESS == pPackages->rgPatchInfo[iPatchInfo].uStatus) - { - BURN_PACKAGE* pMspPackage = pPackages->rgPatchInfoToPackage[iPatchInfo]; - Assert(BURN_PACKAGE_TYPE_MSP == pMspPackage->type); + hr = HRESULT_FROM_WIN32(pPackages->rgPatchInfo[iPatchInfo].uStatus); + BURN_PACKAGE* pMspPackage = pPackages->rgPatchInfoToPackage[iPatchInfo]; + Assert(BURN_PACKAGE_TYPE_MSP == pMspPackage->type); + if (S_OK == hr) + { // Note that we do add superseded and obsolete MSP packages. Package Detect and Plan will sort them out later. - hr = AddDetectedTargetProduct(pPackages, pMspPackage, pPackages->rgPatchInfo[iPatchInfo].dwOrder, pPossibleTargetProduct->wzProductCode, pPossibleTargetProduct->context); + hr = MspEngineAddDetectedTargetProduct(pPackages, pMspPackage, pPackages->rgPatchInfo[iPatchInfo].dwOrder, pPossibleTargetProduct->wzProductCode, pPossibleTargetProduct->context); ExitOnFailure(hr, "Failed to add target product code to package: %ls", pMspPackage->sczId); } - // TODO: should we log something for this error case? + else + { + LogStringLine(REPORT_DEBUG, " 0x%x: Patch applicability failed for package: %ls", hr, pMspPackage->sczId); + } } } else @@ -192,6 +201,54 @@ LExit: return hr; } +extern "C" HRESULT MspEngineAddDetectedTargetProduct( + __in BURN_PACKAGES* pPackages, + __in BURN_PACKAGE* pPackage, + __in DWORD dwOrder, + __in_z LPCWSTR wzProductCode, + __in MSIINSTALLCONTEXT context + ) +{ + HRESULT hr = S_OK; + DWORD dwTargetProductIndex = 0; + + hr = AddDetectedTargetProduct(pPackage, dwOrder, wzProductCode, context, &dwTargetProductIndex); + ExitOnFailure(hr, "Failed to add detected target product."); + + hr = DeterminePatchChainedTarget(pPackages, pPackage, wzProductCode, dwTargetProductIndex); + ExitOnFailure(hr, "Failed to determine patch chained target."); + +LExit: + return hr; +} + +extern "C" HRESULT MspEngineAddMissingSlipstreamTarget( + __in BURN_PACKAGE* pMsiPackage, + __in BURN_SLIPSTREAM_MSP* pSlipstreamMsp + ) +{ + HRESULT hr = S_OK; + DWORD dwTargetProductIndex = 0; + BURN_MSPTARGETPRODUCT* pTargetProduct = NULL; + DWORD dwChainedPatchIndex = 0; + + hr = AddDetectedTargetProduct(pSlipstreamMsp->pMspPackage, 0, pMsiPackage->Msi.sczProductCode, pMsiPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, &dwTargetProductIndex); + ExitOnFailure(hr, "Failed to add missing slipstream target."); + + pTargetProduct = pSlipstreamMsp->pMspPackage->Msp.rgTargetProducts + dwTargetProductIndex; + pTargetProduct->fSlipstream = TRUE; + pTargetProduct->fSlipstreamRequired = TRUE; + pTargetProduct->pChainedTargetPackage = pMsiPackage; + + hr = AddMsiChainedPatch(pMsiPackage, pSlipstreamMsp->pMspPackage, dwTargetProductIndex, &dwChainedPatchIndex); + ExitOnFailure(hr, "Failed to add chained patch."); + + pSlipstreamMsp->dwMsiChainedPatchIndex = dwChainedPatchIndex; + +LExit: + return hr; +} + extern "C" HRESULT MspEngineDetectPackage( __in BURN_PACKAGE* pPackage, __in BURN_USER_EXPERIENCE* pUserExperience @@ -219,7 +276,6 @@ extern "C" HRESULT MspEngineDetectPackage( for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) { BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; - BOOL fInstalled = FALSE; hr = WiuGetPatchInfoEx(pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode, NULL, pTargetProduct->context, INSTALLPROPERTY_PATCHSTATE, &sczState); if (SUCCEEDED(hr)) @@ -227,17 +283,17 @@ extern "C" HRESULT MspEngineDetectPackage( switch (*sczState) { case '1': - fInstalled = TRUE; + pTargetProduct->fInstalled = TRUE; pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; break; case '2': - fInstalled = TRUE; + pTargetProduct->fInstalled = TRUE; pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; break; case '4': - fInstalled = TRUE; + pTargetProduct->fInstalled = TRUE; pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE; break; @@ -246,7 +302,7 @@ extern "C" HRESULT MspEngineDetectPackage( break; } } - else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PATCH) == hr) + else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PATCH) == hr || HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr) { pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; hr = S_OK; @@ -260,9 +316,9 @@ extern "C" HRESULT MspEngineDetectPackage( if (pPackage->fCanAffectRegistration) { - pTargetProduct->registrationState = fInstalled ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + pTargetProduct->registrationState = pTargetProduct->fInstalled ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - if (fInstalled) + if (pTargetProduct->fInstalled) { pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; } @@ -290,6 +346,13 @@ extern "C" HRESULT MspEnginePlanInitializePackage( { BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; + if (!pTargetProduct->fInstalled && pTargetProduct->fSlipstreamRequired && BOOTSTRAPPER_REQUEST_STATE_PRESENT > pTargetProduct->pChainedTargetPackage->requested) + { + // There's no way to apply the patch if the target isn't installed. + pTargetProduct->defaultRequested = pTargetProduct->requested = BOOTSTRAPPER_REQUEST_STATE_NONE; + continue; + } + pTargetProduct->defaultRequested = pTargetProduct->requested = pPackage->requested; hr = UserExperienceOnPlanPatchTarget(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, &pTargetProduct->requested); @@ -637,32 +700,6 @@ LExit: return hr; } -extern "C" void MspEngineSlipstreamUpdateState( - __in BURN_PACKAGE* pPackage, - __in BOOTSTRAPPER_ACTION_STATE execute, - __in BOOTSTRAPPER_ACTION_STATE rollback - ) -{ - Assert(BURN_PACKAGE_TYPE_MSP == pPackage->type); - - // If the dependency manager set our state then that means something else - // is dependent on our package. That trumps whatever the slipstream update - // state might set. - if (!pPackage->fDependencyManagerWasHere) - { - // The highest aggregate action state found will be returned. - if (pPackage->execute < execute) - { - pPackage->execute = execute; - } - - if (pPackage->rollback < rollback) - { - pPackage->rollback = rollback; - } - } -} - extern "C" void MspEngineUpdateInstallRegistrationState( __in BURN_EXECUTE_ACTION* pAction, __in HRESULT hrExecute, @@ -926,43 +963,68 @@ LExit: } static HRESULT AddDetectedTargetProduct( - __in BURN_PACKAGES* pPackages, __in BURN_PACKAGE* pPackage, __in DWORD dwOrder, __in_z LPCWSTR wzProductCode, - __in MSIINSTALLCONTEXT context + __in MSIINSTALLCONTEXT context, + __out DWORD* pdwTargetProductIndex ) { HRESULT hr = S_OK; + BURN_MSPTARGETPRODUCT* pTargetProduct = NULL; + + *pdwTargetProductIndex = BURN_PACKAGE_INVALID_PATCH_INDEX; hr = MemEnsureArraySize(reinterpret_cast(&pPackage->Msp.rgTargetProducts), pPackage->Msp.cTargetProductCodes + 1, sizeof(BURN_MSPTARGETPRODUCT), 5); ExitOnFailure(hr, "Failed to ensure enough target product codes were allocated."); - hr = ::StringCchCopyW(pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].wzTargetProductCode, countof(pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].wzTargetProductCode), wzProductCode); + pTargetProduct = pPackage->Msp.rgTargetProducts + pPackage->Msp.cTargetProductCodes; + + hr = ::StringCchCopyW(pTargetProduct->wzTargetProductCode, countof(pTargetProduct->wzTargetProductCode), wzProductCode); ExitOnFailure(hr, "Failed to copy target product code."); - DeterminePatchChainedTarget(pPackages, pPackage, wzProductCode, - &pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].pChainedTargetPackage, - &pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].fSlipstream); + pTargetProduct->context = context; + pTargetProduct->dwOrder = dwOrder; - pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].context = context; - pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].dwOrder = dwOrder; + *pdwTargetProductIndex = pPackage->Msp.cTargetProductCodes; ++pPackage->Msp.cTargetProductCodes; LExit: return hr; } -static void DeterminePatchChainedTarget( +static HRESULT AddMsiChainedPatch( + __in BURN_PACKAGE* pPackage, + __in BURN_PACKAGE* pMspPackage, + __in DWORD dwMspTargetProductIndex, + __out DWORD* pdwChainedPatchIndex + ) +{ + HRESULT hr = S_OK; + + hr = MemEnsureArraySize(reinterpret_cast(&pPackage->Msi.rgChainedPatches), pPackage->Msi.cChainedPatches + 1, sizeof(BURN_CHAINED_PATCH), 5); + ExitOnFailure(hr, "Failed to ensure enough chained patches were allocated."); + + BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + pPackage->Msi.cChainedPatches; + pChainedPatch->pMspPackage = pMspPackage; + pChainedPatch->dwMspTargetProductIndex = dwMspTargetProductIndex; + + *pdwChainedPatchIndex = pPackage->Msi.cChainedPatches; + ++pPackage->Msi.cChainedPatches; +LExit: + return hr; +} + +static HRESULT DeterminePatchChainedTarget( __in BURN_PACKAGES* pPackages, __in BURN_PACKAGE* pMspPackage, __in LPCWSTR wzTargetProductCode, - __out BURN_PACKAGE** ppChainedTargetPackage, - __out BOOL* pfSlipstreamed + __in DWORD dwMspTargetProductIndex ) { - BURN_PACKAGE* pTargetMsiPackage = NULL; - BOOL fSlipstreamed = FALSE; + HRESULT hr = S_OK; + DWORD dwChainedPatchIndex = 0; + BURN_MSPTARGETPRODUCT* pTargetProduct = pMspPackage->Msp.rgTargetProducts + dwMspTargetProductIndex; for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage) { @@ -970,15 +1032,19 @@ static void DeterminePatchChainedTarget( if (BURN_PACKAGE_TYPE_MSI == pPackage->type && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzTargetProductCode, -1, pPackage->Msi.sczProductCode, -1)) { - pTargetMsiPackage = pPackage; + pTargetProduct->pChainedTargetPackage = pPackage; + + hr = AddMsiChainedPatch(pPackage, pMspPackage, dwMspTargetProductIndex, &dwChainedPatchIndex); + ExitOnFailure(hr, "Failed to add chained patch."); for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) { - BURN_PACKAGE* pSlipstreamMsp = pPackage->Msi.rgpSlipstreamMspPackages[j]; - if (pSlipstreamMsp == pMspPackage) + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + j; + if (pSlipstreamMsp->pMspPackage == pMspPackage) { - AssertSz(!fSlipstreamed, "An MSP should only show up as a slipstreamed patch in an MSI once."); - fSlipstreamed = TRUE; + AssertSz(BURN_PACKAGE_INVALID_PATCH_INDEX == pSlipstreamMsp->dwMsiChainedPatchIndex, "An MSP should only show up as a slipstreamed patch in an MSI once."); + pTargetProduct->fSlipstream = TRUE; + pSlipstreamMsp->dwMsiChainedPatchIndex = dwChainedPatchIndex; break; } } @@ -987,10 +1053,8 @@ static void DeterminePatchChainedTarget( } } - *ppChainedTargetPackage = pTargetMsiPackage; - *pfSlipstreamed = fSlipstreamed; - - return; +LExit: + return hr; } static HRESULT PlanTargetProduct( diff --git a/src/engine/mspengine.h b/src/engine/mspengine.h index 28682169..1530954b 100644 --- a/src/engine/mspengine.h +++ b/src/engine/mspengine.h @@ -28,6 +28,17 @@ void MspEnginePackageUninitialize( HRESULT MspEngineDetectInitialize( __in BURN_PACKAGES* pPackages ); +HRESULT MspEngineAddDetectedTargetProduct( + __in BURN_PACKAGES* pPackages, + __in BURN_PACKAGE* pPackage, + __in DWORD dwOrder, + __in_z LPCWSTR wzProductCode, + __in MSIINSTALLCONTEXT context + ); +HRESULT MspEngineAddMissingSlipstreamTarget( + __in BURN_PACKAGE* pMsiPackage, + __in BURN_SLIPSTREAM_MSP* pSlipstreamMsp + ); HRESULT MspEngineDetectPackage( __in BURN_PACKAGE* pPackage, __in BURN_USER_EXPERIENCE* pUserExperience @@ -59,11 +70,6 @@ HRESULT MspEngineExecutePackage( __in LPVOID pvContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ); -void MspEngineSlipstreamUpdateState( - __in BURN_PACKAGE* pMspPackage, - __in BOOTSTRAPPER_ACTION_STATE execute, - __in BOOTSTRAPPER_ACTION_STATE rollback - ); void MspEngineUpdateInstallRegistrationState( __in BURN_EXECUTE_ACTION* pAction, __in HRESULT hrExecute, diff --git a/src/engine/package.cpp b/src/engine/package.cpp index bb61cdcd..115866f3 100644 --- a/src/engine/package.cpp +++ b/src/engine/package.cpp @@ -282,7 +282,9 @@ extern "C" HRESULT PackagesParseFromXml( { if (pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k] && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k], -1)) { - pMsiPackage->Msi.rgpSlipstreamMspPackages[k] = pPackage; + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pMsiPackage->Msi.rgSlipstreamMsps + k; + pSlipstreamMsp->pMspPackage = pPackage; + pSlipstreamMsp->dwMsiChainedPatchIndex = BURN_PACKAGE_INVALID_PATCH_INDEX; ReleaseNullStr(pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k]); // we don't need the slipstream package id any longer so free it. } @@ -295,6 +297,25 @@ extern "C" HRESULT PackagesParseFromXml( AssertSz(pPackages->cPatchInfo == cMspPackages, "Count of packages patch info should be equal to the number of MSP packages."); +#if DEBUG + // Loop through all MSI packages seeing if any of them are missing their slipstream MSP. + for (DWORD i = 0; i < pPackages->cPackages; ++i) + { + BURN_PACKAGE* pPackage = &pPackages->rgPackages[i]; + + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + { + for (DWORD k = 0; k < pPackage->Msi.cSlipstreamMspPackages; ++k) + { + if (pPackage->Msi.rgsczSlipstreamMspPackageIds[k]) + { + AssertSz(FALSE, "MSI slipstream MSP package doesn't exist."); + } + } + } + } +#endif + hr = ParsePatchTargetCode(pPackages, pixnBundle); ExitOnFailure(hr, "Failed to parse target product codes."); diff --git a/src/engine/package.h b/src/engine/package.h index 3a243c7d..283afa57 100644 --- a/src/engine/package.h +++ b/src/engine/package.h @@ -14,6 +14,8 @@ typedef _BURN_PACKAGE BURN_PACKAGE; // constants +const DWORD BURN_PACKAGE_INVALID_PATCH_INDEX = 0x80000000; + enum BURN_EXE_EXIT_CODE_TYPE { BURN_EXE_EXIT_CODE_TYPE_NONE, @@ -116,7 +118,9 @@ typedef struct _BURN_MSPTARGETPRODUCT DWORD dwOrder; WCHAR wzTargetProductCode[39]; BURN_PACKAGE* pChainedTargetPackage; + BOOL fInstalled; BOOL fSlipstream; + BOOL fSlipstreamRequired; // this means the target product is not present on the machine, but is available in the chain as a slipstream target. BOOTSTRAPPER_PACKAGE_STATE patchPackageState; // only valid after Detect. BOOTSTRAPPER_REQUEST_STATE defaultRequested; // only valid during Plan. @@ -172,6 +176,18 @@ typedef struct _BURN_RELATED_MSI DWORD cLanguages; } BURN_RELATED_MSI; +typedef struct _BURN_CHAINED_PATCH +{ + BURN_PACKAGE* pMspPackage; + DWORD dwMspTargetProductIndex; // index into the Msp.rgTargetProducts +} BURN_CHAINED_PATCH; + +typedef struct _BURN_SLIPSTREAM_MSP +{ + BURN_PACKAGE* pMspPackage; + DWORD dwMsiChainedPatchIndex; // index into the Msi.rgChainedPatches +} BURN_SLIPSTREAM_MSP; + typedef struct _BURN_PACKAGE_PAYLOAD { BURN_PAYLOAD* pPayload; @@ -295,9 +311,12 @@ typedef struct _BURN_PACKAGE BURN_RELATED_MSI* rgRelatedMsis; DWORD cRelatedMsis; - _BURN_PACKAGE** rgpSlipstreamMspPackages; + BURN_SLIPSTREAM_MSP* rgSlipstreamMsps; LPWSTR* rgsczSlipstreamMspPackageIds; DWORD cSlipstreamMspPackages; + + BURN_CHAINED_PATCH* rgChainedPatches; + DWORD cChainedPatches; } Msi; struct { diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index 95ea0b05..86a07dfb 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -2168,7 +2168,7 @@ static HRESULT AddCacheSlipstreamMsps( for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) { - BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[i]; + BURN_PACKAGE* pMspPackage = pPackage->Msi.rgSlipstreamMsps[i].pMspPackage; AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches."); hr = AddCachePackageHelper(pPlan, pMspPackage, &hIgnored); @@ -2791,7 +2791,7 @@ static HRESULT FinalizeSlipstreamPatchActions( { for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) { - BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[j]; + BURN_PACKAGE* pMspPackage = pPackage->Msi.rgSlipstreamMsps[j].pMspPackage; AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches."); pAction->msiPackage.rgSlipstreamPatches[j] = fExecute ? pMspPackage->execute : pMspPackage->rollback; diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index 08dc68e7..65f99168 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -71,6 +71,7 @@ + diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp index 42c11968..820f88c2 100644 --- a/src/test/BurnUnitTest/PlanTest.cpp +++ b/src/test/BurnUnitTest/PlanTest.cpp @@ -11,6 +11,7 @@ static HRESULT WINAPI PlanTestBAProc( static LPCWSTR wzMsiTransactionManifestFileName = L"MsiTransaction_BundleAv1_manifest.xml"; static LPCWSTR wzSingleMsiManifestFileName = L"BasicFunctionality_BundleA_manifest.xml"; +static LPCWSTR wzSlipstreamManifestFileName = L"Slipstream_BundleA_manifest.xml"; namespace Microsoft { @@ -650,6 +651,210 @@ namespace Bootstrapper ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_IGNORED, BURN_PACKAGE_REGISTRATION_STATE_IGNORED); } + [Fact] + void SlipstreamInstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSlipstreamManifestFileName, pEngineState); + DetectPermanentPackagesAsPresentAndCached(pEngineState); + PlanTestDetectPatchInitialize(pEngineState); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + DWORD dwPackageStart = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PatchA", 4, 1, 20480, FALSE); + ValidateCacheAcquirePayload(pPlan, fRollback, dwIndex++, L"PatchA", L"PatchA", FALSE); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PatchA", L"PatchA", TRUE, FALSE, dwPackageStart); + ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PatchA", FALSE); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 2); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", 10, 1, 32768, FALSE); + ValidateCacheAcquirePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"PackageA", FALSE); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"PackageA", TRUE, FALSE, dwPackageStart); + ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 2); + ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(3055111ull, pPlan->qwEstimatedSize); + Assert::Equal(53248ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 3; + BURN_EXECUTE_ACTION* pExecuteAction = NULL; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[11].syncpoint.hEvent); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[5].syncpoint.hEvent); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_REGISTER); + pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_INSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE); + ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 3; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PatchA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE); + ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA"); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(2ul, pPlan->cExecutePackagesTotal); + Assert::Equal(4ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(3ul, pEngineState->packages.cPackages); + ValidatePermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"NetFx48Web"); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PatchA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + } + + [Fact] + void SlipstreamUninstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSlipstreamManifestFileName, pEngineState); + DetectPackagesAsPresentAndCached(pEngineState); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(0ull, pPlan->qwEstimatedSize); + Assert::Equal(0ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 1; + BURN_EXECUTE_ACTION* pExecuteAction = NULL; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_UNREGISTER); + pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE); + ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_INSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE); + ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(2ul, pPlan->cExecutePackagesTotal); + Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + ValidateCleanAction(pPlan, dwIndex++, L"PatchA"); + ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{0A5113E3-06A5-4CE0-8E83-9EB42F6764A6}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(3ul, pEngineState->packages.cPackages); + ValidatePermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"NetFx48Web"); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PatchA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + } + private: // This doesn't initialize everything, just enough for CorePlan to work. void InitializeEngineStateForCorePlan(LPCWSTR wzManifestFileName, BURN_ENGINE_STATE* pEngineState) @@ -700,6 +905,30 @@ namespace Bootstrapper pEngineState->fDetected = TRUE; } + void PlanTestDetectPatchInitialize(BURN_ENGINE_STATE* pEngineState) + { + HRESULT hr = MsiEngineDetectInitialize(&pEngineState->packages); + NativeAssert::Succeeded(hr, "MsiEngineDetectInitialize failed"); + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + + if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + j; + + if (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN == pTargetProduct->patchPackageState) + { + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + } + } + } + } + } + void DetectAttachedContainerAsAttached(BURN_ENGINE_STATE* pEngineState) { for (DWORD i = 0; i < pEngineState->containers.cContainers; ++i) @@ -773,6 +1002,21 @@ namespace Bootstrapper BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; DetectPackageAsPresentAndCached(pPackage); DetectPackageDependent(pPackage, pEngineState->registration.sczId); + + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + { + for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) + { + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; + + BURN_PACKAGE* pMspPackage = pPackage->Msi.rgSlipstreamMsps[j].pMspPackage; + MspEngineAddDetectedTargetProduct(&pEngineState->packages, pMspPackage, j, pPackage->Msi.sczProductCode, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED); + + BURN_MSPTARGETPRODUCT* pTargetProduct = pMspPackage->Msp.rgTargetProducts + (pMspPackage->Msp.cTargetProductCodes - 1); + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; + pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + } } } @@ -862,7 +1106,23 @@ namespace Bootstrapper { BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); Assert::Equal(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pAction->type); - NativeAssert::StringEqual(wzContainerId, pAction->extractContainer.pContainer->sczId); + NativeAssert::StringEqual(wzContainerId, pAction->resolveContainer.pContainer->sczId); + Assert::Equal(fSkipUntilRetried, pAction->fSkipUntilRetried); + } + + void ValidateCacheAcquirePayload( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId, + __in LPCWSTR wzPayloadId, + __in BOOL fSkipUntilRetried + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->resolvePayload.pPackage->sczId); + NativeAssert::StringEqual(wzPayloadId, pAction->resolvePayload.pPayload->sczKey); Assert::Equal(fSkipUntilRetried, pAction->fSkipUntilRetried); } @@ -1063,7 +1323,7 @@ namespace Bootstrapper __in BURN_PLAN* pPlan, __in BOOL fRollback, __in DWORD dwIndex, - __in LPCWSTR wzPackageId, + __in_z LPCWSTR wzPackageId, __in BOOTSTRAPPER_ACTION_STATE action, __in BURN_MSI_PROPERTY actionMsiProperty, __in DWORD uiLevel, @@ -1083,6 +1343,45 @@ namespace Bootstrapper Assert::Equal(FALSE, pAction->fDeleted); } + BURN_EXECUTE_ACTION* ValidateDeletedExecuteMspTarget( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in_z LPCWSTR wzPackageId, + __in BOOTSTRAPPER_ACTION_STATE action, + __in_z LPCWSTR wzTargetProductCode, + __in BOOL fPerMachineTarget, + __in BURN_MSI_PROPERTY actionMsiProperty, + __in DWORD uiLevel, + __in BOOL fDisableExternalUiHandler, + __in BOOL fDeleted + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_MSP_TARGET, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->mspTarget.pPackage->sczId); + Assert::Equal(action, pAction->mspTarget.action); + NativeAssert::StringEqual(wzTargetProductCode, pAction->mspTarget.sczTargetProductCode); + Assert::Equal(fPerMachineTarget, pAction->mspTarget.fPerMachineTarget); + Assert::Equal(actionMsiProperty, pAction->mspTarget.actionMsiProperty); + Assert::Equal(uiLevel, pAction->mspTarget.uiLevel); + Assert::Equal(fDisableExternalUiHandler, pAction->mspTarget.fDisableExternalUiHandler); + NativeAssert::NotNull(pAction->mspTarget.sczLogPath); + Assert::Equal(fDeleted, pAction->fDeleted); + return pAction; + } + + void ValidateExecuteMspTargetPatch( + __in BURN_EXECUTE_ACTION* pAction, + __in DWORD dwIndex, + __in_z LPCWSTR wzPackageId + ) + { + Assert::InRange(dwIndex + 1ul, 1ul, pAction->mspTarget.cOrderedPatches); + BURN_ORDERED_PATCHES* pOrderedPatch = pAction->mspTarget.rgOrderedPatches + dwIndex; + NativeAssert::StringEqual(wzPackageId, pOrderedPatch->pPackage->sczId); + } + void ValidateExecutePackageDependency( __in BURN_PLAN* pPlan, __in BOOL fRollback, diff --git a/src/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml b/src/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml new file mode 100644 index 00000000..4b5cab6f --- /dev/null +++ b/src/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml @@ -0,0 +1 @@ + \ No newline at end of file -- cgit v1.2.3-55-g6feb From a98115d996d65834e7c8d593c10d2cfa66096ccd Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 22 Feb 2021 19:58:44 -0600 Subject: Fix patch registration states during plan and apply. Add logging for slipstreamed patches. #6297 --- src/engine/apply.cpp | 32 ++++- src/engine/core.cpp | 25 +++- src/engine/dependency.cpp | 12 ++ src/engine/elevation.cpp | 23 +-- src/engine/engine.mc | 22 ++- src/engine/msiengine.cpp | 60 ++++++-- src/engine/msiengine.h | 1 + src/engine/mspengine.cpp | 38 ++++- src/engine/package.h | 5 +- src/engine/plan.cpp | 287 +++++++++++++++++++++---------------- src/engine/plan.h | 1 - src/test/BurnUnitTest/PlanTest.cpp | 2 +- 12 files changed, 344 insertions(+), 164 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 55141ef9..44ff9429 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -2032,7 +2032,7 @@ static HRESULT ExecuteMsiPackage( LExit: if (fExecuted) { - MsiEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute, fInsideMsiTransaction); + MsiEngineUpdateInstallRegistrationState(pExecuteAction, fRollback, hrExecute, fInsideMsiTransaction); } if (fBeginCalled) @@ -2248,7 +2248,20 @@ static HRESULT ExecuteDependencyAction( { pAction->packageDependency.pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; } - if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pAction->packageDependency.pPackage->installRegistrationState) + + if (BURN_PACKAGE_TYPE_MSP == pAction->packageDependency.pPackage->type) + { + for (DWORD i = 0; i < pAction->packageDependency.pPackage->Msp.cTargetProductCodes; ++i) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pAction->packageDependency.pPackage->Msp.rgTargetProducts + i; + + if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pTargetProduct->registrationState) + { + pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + } + } + else if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pAction->packageDependency.pPackage->installRegistrationState) { pAction->packageDependency.pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; } @@ -2259,7 +2272,20 @@ static HRESULT ExecuteDependencyAction( { pAction->packageDependency.pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; } - if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pAction->packageDependency.pPackage->installRegistrationState) + + if (BURN_PACKAGE_TYPE_MSP == pAction->packageDependency.pPackage->type) + { + for (DWORD i = 0; i < pAction->packageDependency.pPackage->Msp.cTargetProductCodes; ++i) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pAction->packageDependency.pPackage->Msp.rgTargetProducts + i; + + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pTargetProduct->registrationState) + { + pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + } + } + else if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pAction->packageDependency.pPackage->installRegistrationState) { pAction->packageDependency.pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; } diff --git a/src/engine/core.cpp b/src/engine/core.cpp index cb7ebbfd..0a19ac8e 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -1814,15 +1814,30 @@ static void LogPackages( LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingBoolToString(pPackage->fAcquire), LoggingBoolToString(pPackage->fUncache), LoggingDependencyActionToString(pPackage->dependencyExecute), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedInstallRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedCacheRegistrationState)); - if (BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.cFeatures) + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) { - LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURES, pPackage->Msi.cFeatures, pPackage->sczId); + if (pPackage->Msi.cFeatures) + { + LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURES, pPackage->Msi.cFeatures, pPackage->sczId); + + for (DWORD j = 0; j < pPackage->Msi.cFeatures; ++j) + { + const BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[j]; - for (DWORD j = 0; j < pPackage->Msi.cFeatures; ++j) + LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURE, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState), LoggingMsiFeatureStateToString(pFeature->defaultRequested), LoggingMsiFeatureStateToString(pFeature->requested), LoggingMsiFeatureActionToString(pFeature->execute), LoggingMsiFeatureActionToString(pFeature->rollback)); + } + } + + if (pPackage->Msi.cSlipstreamMspPackages) { - const BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[j]; + LogId(REPORT_STANDARD, MSG_PLANNED_SLIPSTREAMED_MSP_TARGETS, pPackage->Msi.cSlipstreamMspPackages, pPackage->sczId); + + for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) + { + const BURN_SLIPSTREAM_MSP* pSlipstreamMsp = &pPackage->Msi.rgSlipstreamMsps[j]; - LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURE, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState), LoggingMsiFeatureStateToString(pFeature->defaultRequested), LoggingMsiFeatureStateToString(pFeature->requested), LoggingMsiFeatureActionToString(pFeature->execute), LoggingMsiFeatureActionToString(pFeature->rollback)); + LogId(REPORT_STANDARD, MSG_PLANNED_SLIPSTREAMED_MSP_TARGET, pSlipstreamMsp->pMspPackage->sczId, LoggingActionStateToString(pSlipstreamMsp->execute), LoggingActionStateToString(pSlipstreamMsp->rollback)); + } } } else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.cTargetProductCodes) diff --git a/src/engine/dependency.cpp b/src/engine/dependency.cpp index a6a8fe4d..3d978740 100644 --- a/src/engine/dependency.cpp +++ b/src/engine/dependency.cpp @@ -774,6 +774,18 @@ static HRESULT DetectPackageDependents( { pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; } + if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; + + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pTargetProduct->registrationState) + { + pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + } + } } LExit: diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp index e4af1840..c9ed9810 100644 --- a/src/engine/elevation.cpp +++ b/src/engine/elevation.cpp @@ -855,6 +855,9 @@ extern "C" HRESULT ElevationExecuteMsiPackage( DWORD dwResult = 0; // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fRollback); + ExitOnFailure(hr, "Failed to write rollback flag to message buffer."); + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msiPackage.pPackage->sczId); ExitOnFailure(hr, "Failed to write package id to message buffer."); @@ -886,16 +889,15 @@ extern "C" HRESULT ElevationExecuteMsiPackage( // Slipstream patches actions. for (DWORD i = 0; i < pExecuteAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++i) { - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.rgSlipstreamPatches[i]); + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pExecuteAction->msiPackage.pPackage->Msi.rgSlipstreamMsps + i; + BOOTSTRAPPER_ACTION_STATE* pAction = fRollback ? &pSlipstreamMsp->rollback : &pSlipstreamMsp->execute; + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)*pAction); ExitOnFailure(hr, "Failed to write slipstream patch action to message buffer."); } hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); ExitOnFailure(hr, "Failed to write variables."); - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fRollback); - ExitOnFailure(hr, "Failed to write rollback flag to message buffer."); - // send message context.pfnMessageHandler = pfnMessageHandler; @@ -2263,6 +2265,9 @@ static HRESULT OnExecuteMsiPackage( executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE; // Deserialize message data. + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback); + ExitOnFailure(hr, "Failed to read rollback flag."); + hr = BuffReadString(pbData, cbData, &iData, &sczPackage); ExitOnFailure(hr, "Failed to read MSI package id."); @@ -2303,12 +2308,11 @@ static HRESULT OnExecuteMsiPackage( // Read slipstream patches actions. if (executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages) { - executeAction.msiPackage.rgSlipstreamPatches = (BOOTSTRAPPER_ACTION_STATE*)MemAlloc(executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages * sizeof(BOOTSTRAPPER_ACTION_STATE), TRUE); - ExitOnNull(executeAction.msiPackage.rgSlipstreamPatches, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream patch actions."); - for (DWORD i = 0; i < executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++i) { - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.rgSlipstreamPatches[i]); + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = executeAction.msiPackage.pPackage->Msi.rgSlipstreamMsps + i; + BOOTSTRAPPER_ACTION_STATE* pAction = fRollback ? &pSlipstreamMsp->rollback : &pSlipstreamMsp->execute; + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)pAction); ExitOnFailure(hr, "Failed to read slipstream action."); } } @@ -2316,9 +2320,6 @@ static HRESULT OnExecuteMsiPackage( hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); ExitOnFailure(hr, "Failed to read variables."); - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback); - ExitOnFailure(hr, "Failed to read rollback flag."); - // Execute MSI package. hr = MsiEngineExecutePackage(hwndParent, &executeAction, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &msiRestart); ExitOnFailure(hr, "Failed to execute MSI package."); diff --git a/src/engine/engine.mc b/src/engine/engine.mc index 1cdde207..f7b18c59 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -321,14 +321,14 @@ MessageId=203 Severity=Success SymbolicName=MSG_PLANNED_MSI_FEATURE Language=English -Planned feature: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute action: %5!hs!, rollback action: %6!hs! + Planned feature: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute action: %5!hs!, rollback action: %6!hs! . MessageId=204 Severity=Success SymbolicName=MSG_PLANNED_MSI_FEATURES Language=English -Plan %1!u! msi features for package: %2!ls! + Plan %1!u! msi features for package: %2!ls! . MessageId=205 @@ -419,14 +419,28 @@ MessageId=218 Severity=Success SymbolicName=MSG_PLANNED_MSP_TARGETS Language=English -Plan %1!u! patch targets for package: %2!ls! + Plan %1!u! patch targets for package: %2!ls! . MessageId=219 Severity=Success SymbolicName=MSG_PLANNED_MSP_TARGET Language=English -Planned patch target: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs! + Planned patch target: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs! +. + +MessageId=220 +Severity=Success +SymbolicName=MSG_PLANNED_SLIPSTREAMED_MSP_TARGETS +Language=English + Plan %1!u! slipstream patches for package: %2!ls! +. + +MessageId=221 +Severity=Success +SymbolicName=MSG_PLANNED_SLIPSTREAMED_MSP_TARGET +Language=English + Planned slipstreamed patch: %1!ls!, execute: %2!hs!, rollback: %3!hs! . MessageId=299 diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index ae105155..f0aa784e 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -42,7 +42,7 @@ static HRESULT ConcatFeatureActionProperties( ); static HRESULT ConcatPatchProperty( __in BURN_PACKAGE* pPackage, - __in_opt BOOTSTRAPPER_ACTION_STATE* rgSlipstreamPatchActions, + __in BOOL fRollback, __inout_z LPWSTR* psczArguments ); static void RegisterSourceDirectory( @@ -1180,10 +1180,10 @@ extern "C" HRESULT MsiEngineExecutePackage( ExitOnFailure(hr, "Failed to add feature action properties to obfuscated argument string."); // add slipstream patch properties - hr = ConcatPatchProperty(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgSlipstreamPatches, &sczProperties); + hr = ConcatPatchProperty(pExecuteAction->msiPackage.pPackage, fRollback, &sczProperties); ExitOnFailure(hr, "Failed to add patch properties to argument string."); - hr = ConcatPatchProperty(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgSlipstreamPatches, &sczObfuscatedProperties); + hr = ConcatPatchProperty(pExecuteAction->msiPackage.pPackage, fRollback, &sczObfuscatedProperties); ExitOnFailure(hr, "Failed to add patch properties to obfuscated argument string."); hr = MsiEngineConcatActionProperty(pExecuteAction->msiPackage.actionMsiProperty, &sczProperties); @@ -1432,6 +1432,7 @@ extern "C" HRESULT MsiEngineCalculateInstallUiLevel( extern "C" void MsiEngineUpdateInstallRegistrationState( __in BURN_EXECUTE_ACTION* pAction, + __in BOOL fRollback, __in HRESULT hrExecute, __in BOOL fInsideMsiTransaction ) @@ -1462,6 +1463,49 @@ extern "C" void MsiEngineUpdateInstallRegistrationState( pPackage->installRegistrationState = newState; } + if (BURN_PACKAGE_REGISTRATION_STATE_ABSENT == newState) + { + for (DWORD i = 0; i < pPackage->Msi.cChainedPatches; ++i) + { + BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + i; + BURN_MSPTARGETPRODUCT* pTargetProduct = pChainedPatch->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex; + + if (fInsideMsiTransaction) + { + pTargetProduct->transactionRegistrationState = newState; + } + else + { + pTargetProduct->registrationState = newState; + } + } + } + else + { + for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) + { + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + i; + BOOTSTRAPPER_ACTION_STATE patchExecuteAction = fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute; + + if (BOOTSTRAPPER_ACTION_STATE_INSTALL > patchExecuteAction) + { + continue; + } + + BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + pSlipstreamMsp->dwMsiChainedPatchIndex; + BURN_MSPTARGETPRODUCT* pTargetProduct = pChainedPatch->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex; + + if (fInsideMsiTransaction) + { + pTargetProduct->transactionRegistrationState = newState; + } + else + { + pTargetProduct->registrationState = newState; + } + } + } + LExit: return; } @@ -1911,7 +1955,7 @@ LExit: static HRESULT ConcatPatchProperty( __in BURN_PACKAGE* pPackage, - __in_opt BOOTSTRAPPER_ACTION_STATE* rgSlipstreamPatchActions, + __in BOOL fRollback, __inout_z LPWSTR* psczArguments ) { @@ -1921,14 +1965,14 @@ static HRESULT ConcatPatchProperty( LPWSTR sczPatches = NULL; // If there are slipstream patch actions, build up their patch action. - if (rgSlipstreamPatchActions) + if (pPackage->Msi.cSlipstreamMspPackages) { for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) { - BURN_PACKAGE* pMspPackage = pPackage->Msi.rgSlipstreamMsps[i].pMspPackage; - AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches."); + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + i; + BURN_PACKAGE* pMspPackage = pSlipstreamMsp->pMspPackage; + BOOTSTRAPPER_ACTION_STATE patchExecuteAction = fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute; - BOOTSTRAPPER_ACTION_STATE patchExecuteAction = rgSlipstreamPatchActions[i]; if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < patchExecuteAction) { hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory); diff --git a/src/engine/msiengine.h b/src/engine/msiengine.h index 6c7c83e5..e2dc5e82 100644 --- a/src/engine/msiengine.h +++ b/src/engine/msiengine.h @@ -95,6 +95,7 @@ HRESULT MsiEngineCalculateInstallUiLevel( ); void MsiEngineUpdateInstallRegistrationState( __in BURN_EXECUTE_ACTION* pAction, + __in BOOL fRollback, __in HRESULT hrExecute, __in BOOL fInsideMsiTransaction ); diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp index 4b7f9a0a..ed105d24 100644 --- a/src/engine/mspengine.cpp +++ b/src/engine/mspengine.cpp @@ -372,6 +372,7 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( ) { HRESULT hr = S_OK; + BOOL fWillUninstallAll = TRUE; for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) { @@ -388,6 +389,7 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( { case BOOTSTRAPPER_REQUEST_STATE_REPAIR: execute = BOOTSTRAPPER_ACTION_STATE_REPAIR; + fWillUninstallAll = FALSE; break; case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough; @@ -401,6 +403,7 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( default: execute = BOOTSTRAPPER_ACTION_STATE_NONE; + fWillUninstallAll = FALSE; break; } break; @@ -411,6 +414,7 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; + fWillUninstallAll = FALSE; break; default: @@ -418,6 +422,13 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( break; } break; + + default: + if (pTargetProduct->fInstalled) + { + fWillUninstallAll = FALSE; + } + break; } // Calculate the rollback action if there is an execute action. @@ -475,6 +486,13 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( } } + // The dependency manager will do the wrong thing if the package level action is UNINSTALL + // when the patch will still be applied to at least one product. + if (!fWillUninstallAll && BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) + { + pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; + } + return hr; } @@ -776,16 +794,22 @@ extern "C" void MspEngineFinalizeInstallRegistrationState( ExitFunction(); } - pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - - for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) + if (!pPackage->Msp.cTargetProductCodes) { - BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + else + { + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; - if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pTargetProduct->registrationState) + for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) { - pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - break; + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; + + if (pPackage->installRegistrationState < pTargetProduct->registrationState) + { + pPackage->installRegistrationState = pTargetProduct->registrationState; + } } } diff --git a/src/engine/package.h b/src/engine/package.h index 283afa57..3a95852e 100644 --- a/src/engine/package.h +++ b/src/engine/package.h @@ -83,8 +83,8 @@ enum BOOTSTRAPPER_FEATURE_ACTION enum BURN_PACKAGE_REGISTRATION_STATE { BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, - BURN_PACKAGE_REGISTRATION_STATE_IGNORED, BURN_PACKAGE_REGISTRATION_STATE_ABSENT, + BURN_PACKAGE_REGISTRATION_STATE_IGNORED, BURN_PACKAGE_REGISTRATION_STATE_PRESENT, }; @@ -186,6 +186,9 @@ typedef struct _BURN_SLIPSTREAM_MSP { BURN_PACKAGE* pMspPackage; DWORD dwMsiChainedPatchIndex; // index into the Msi.rgChainedPatches + + BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan. + BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan. } BURN_SLIPSTREAM_MSP; typedef struct _BURN_PACKAGE_PAYLOAD diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index 86a07dfb..b3cb0ee3 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -154,11 +154,15 @@ static void RemoveUnnecessaryActions( __in BURN_EXECUTE_ACTION* rgActions, __in DWORD cActions ); -static HRESULT FinalizeSlipstreamPatchActions( +static void FinalizePatchActions( __in BOOL fExecute, __in BURN_EXECUTE_ACTION* rgActions, __in DWORD cActions ); +static void CalculateExpectedRegistrationStates( + __in BURN_PACKAGE* rgPackages, + __in DWORD cPackages + ); static HRESULT PlanDependencyActions( __in BOOL fBundlePerMachine, __in BURN_PLAN* pPlan, @@ -301,7 +305,6 @@ extern "C" void PlanUninitializeExecuteAction( case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: ReleaseStr(pExecuteAction->msiPackage.sczLogPath); ReleaseMem(pExecuteAction->msiPackage.rgFeatures); - ReleaseMem(pExecuteAction->msiPackage.rgSlipstreamPatches); break; case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: @@ -823,6 +826,8 @@ static HRESULT PlanPackagesHelper( hr = PlanFinalizeActions(pPlan); ExitOnFailure(hr, "Failed to remove unnecessary actions from plan."); + CalculateExpectedRegistrationStates(rgPackages, cPackages); + // Let the BA know the actions that were planned. for (DWORD i = 0; i < cPackages; ++i) { @@ -848,6 +853,12 @@ static HRESULT InitializePackage( BOOL fInstallCondition = FALSE; BOOL fBeginCalled = FALSE; + if (pPackage->fCanAffectRegistration) + { + pPackage->expectedCacheRegistrationState = pPackage->cacheRegistrationState; + pPackage->expectedInstallRegistrationState = pPackage->installRegistrationState; + } + if (pPackage->sczInstallCondition && *pPackage->sczInstallCondition) { hr = ConditionEvaluate(pVariables, pPackage->sczInstallCondition, &fInstallCondition); @@ -903,68 +914,24 @@ static HRESULT ProcessPackage( hr = ProcessPackageRollbackBoundary(pPlan, pEffectiveRollbackBoundary, ppRollbackBoundary); ExitOnFailure(hr, "Failed to process package rollback boundary."); - if (pPackage->fCanAffectRegistration) + if (BOOTSTRAPPER_ACTION_LAYOUT == pPlan->action) { - pPackage->expectedCacheRegistrationState = pPackage->cacheRegistrationState; - pPackage->expectedInstallRegistrationState = pPackage->installRegistrationState; + hr = PlanLayoutPackage(pPlan, pPackage, wzLayoutDirectory); + ExitOnFailure(hr, "Failed to plan layout package."); } - - // If the package is in a requested state, plan it. - if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested) + else { - if (BOOTSTRAPPER_ACTION_LAYOUT == pPlan->action) - { - hr = PlanLayoutPackage(pPlan, pPackage, wzLayoutDirectory); - ExitOnFailure(hr, "Failed to plan layout package."); - } - else + if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested) { + // If the package is in a requested state, plan it. hr = PlanExecutePackage(fBundlePerMachine, display, pUX, pPlan, pPackage, pLog, pVariables, phSyncpointEvent); ExitOnFailure(hr, "Failed to plan execute package."); - - if (pPackage->fCanAffectRegistration) - { - if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pPackage->execute) - { - pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } - else if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) - { - pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - } - } - } - } - else if (BOOTSTRAPPER_ACTION_LAYOUT != pPlan->action) - { - // Make sure the package is properly ref-counted even if no plan is requested. - hr = PlanDependencyActions(fBundlePerMachine, pPlan, pPackage); - ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId); - } - - if (pPackage->fCanAffectRegistration) - { - if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute) - { - if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pPackage->expectedCacheRegistrationState) - { - pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } - if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pPackage->expectedInstallRegistrationState) - { - pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } } - else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pPackage->dependencyExecute) + else { - if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->expectedCacheRegistrationState) - { - pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; - } - if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->expectedInstallRegistrationState) - { - pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; - } + // Make sure the package is properly ref-counted even if no plan is requested. + hr = PlanDependencyActions(fBundlePerMachine, pPlan, pPackage); + ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId); } } @@ -1498,17 +1465,14 @@ extern "C" HRESULT PlanFinalizeActions( { HRESULT hr = S_OK; - RemoveUnnecessaryActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions); + FinalizePatchActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions); - RemoveUnnecessaryActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions); + FinalizePatchActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions); - hr = FinalizeSlipstreamPatchActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions); - ExitOnFailure(hr, "Failed to finalize slipstream execute actions."); + RemoveUnnecessaryActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions); - hr = FinalizeSlipstreamPatchActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions); - ExitOnFailure(hr, "Failed to finalize slipstream rollback actions."); + RemoveUnnecessaryActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions); -LExit: return hr; } @@ -1868,7 +1832,7 @@ static void ResetPlannedPackageState( pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; - if (BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.rgFeatures) + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) { for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) { @@ -1880,6 +1844,14 @@ static void ResetPlannedPackageState( pFeature->execute = BOOTSTRAPPER_FEATURE_ACTION_NONE; pFeature->rollback = BOOTSTRAPPER_FEATURE_ACTION_NONE; } + + for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) + { + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = &pPackage->Msi.rgSlipstreamMsps[i]; + + pSlipstreamMsp->execute = BOOTSTRAPPER_ACTION_STATE_NONE; + pSlipstreamMsp->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + } } else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.rgTargetProducts) { @@ -2716,101 +2688,165 @@ static void RemoveUnnecessaryActions( { BURN_EXECUTE_ACTION* pAction = rgActions + i; - // If this MSP targets a package in the chain, check the target's execute state - // to see if this patch should be skipped. if (BURN_EXECUTE_ACTION_TYPE_MSP_TARGET == pAction->type && pAction->mspTarget.pChainedTargetPackage) { + BURN_MSPTARGETPRODUCT* pFirstTargetProduct = pAction->mspTarget.rgOrderedPatches->pTargetProduct; + BURN_PATCH_SKIP_STATE skipState = fExecute ? pFirstTargetProduct->executeSkip : pFirstTargetProduct->rollbackSkip; BOOTSTRAPPER_ACTION_STATE chainedTargetPackageAction = fExecute ? pAction->mspTarget.pChainedTargetPackage->execute : pAction->mspTarget.pChainedTargetPackage->rollback; - BURN_PATCH_SKIP_STATE skipState = BURN_PATCH_SKIP_STATE_NONE; - if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == chainedTargetPackageAction) - { - skipState = BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL; - LogId(REPORT_STANDARD, MSG_PLAN_SKIP_PATCH_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback); - } - else if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < chainedTargetPackageAction && pAction->mspTarget.fSlipstream && BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pAction->mspTarget.action) - { - // If the slipstream target is being installed or upgraded (not uninstalled or repaired) then we will slipstream so skip - // this action to install the patch standalone. Also, if the slipstream target is being repaired and the patch is being - // repaired, skip this operation since it will be redundant. - // - // The primary goal here is to ensure that a slipstream patch that is yet not installed is installed even if the MSI - // is already on the machine. The slipstream must be installed standalone if the MSI is being repaired. - if (BOOTSTRAPPER_ACTION_STATE_REPAIR != chainedTargetPackageAction || BOOTSTRAPPER_ACTION_STATE_REPAIR == pAction->mspTarget.action) - { - skipState = BURN_PATCH_SKIP_STATE_SLIPSTREAM; - LogId(REPORT_STANDARD, MSG_PLAN_SKIP_SLIPSTREAM_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback); - } - } - if (BURN_PATCH_SKIP_STATE_NONE != skipState) + switch (skipState) { + case BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL: pAction->fDeleted = TRUE; - - for (DWORD j = 0; j < pAction->mspTarget.cOrderedPatches; ++j) - { - BURN_MSPTARGETPRODUCT* pTargetProduct = pAction->mspTarget.rgOrderedPatches[j].pTargetProduct; - - if (fExecute) - { - pTargetProduct->executeSkip = skipState; - } - else - { - pTargetProduct->rollbackSkip = skipState; - } - } + LogId(REPORT_STANDARD, MSG_PLAN_SKIP_PATCH_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback); + break; + case BURN_PATCH_SKIP_STATE_SLIPSTREAM: + pAction->fDeleted = TRUE; + LogId(REPORT_STANDARD, MSG_PLAN_SKIP_SLIPSTREAM_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback); + break; } } } } -static HRESULT FinalizeSlipstreamPatchActions( +static void FinalizePatchActions( __in BOOL fExecute, __in BURN_EXECUTE_ACTION* rgActions, __in DWORD cActions ) { - HRESULT hr = S_OK; - for (DWORD i = 0; i < cActions; ++i) { BURN_EXECUTE_ACTION* pAction = rgActions + i; - // If this MSI package contains slipstream patches store the slipstream actions. - if (BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE == pAction->type && pAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages) + if (BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE == pAction->type) { BURN_PACKAGE* pPackage = pAction->msiPackage.pPackage; + AssertSz(BOOTSTRAPPER_ACTION_STATE_NONE < pAction->msiPackage.action, "Planned execute MSI action to do nothing"); - // By default all slipstream actions will be initialized to "no action" (aka: 0). - pAction->msiPackage.rgSlipstreamPatches = (BOOTSTRAPPER_ACTION_STATE*)MemAlloc(sizeof(BOOTSTRAPPER_ACTION_STATE) * pPackage->Msi.cSlipstreamMspPackages, TRUE); - ExitOnNull(pAction->msiPackage.rgSlipstreamPatches, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch actions."); + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->msiPackage.action) + { + // If we are uninstalling the MSI, we must skip all the patches. + for (DWORD j = 0; j < pPackage->Msi.cChainedPatches; ++j) + { + BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + j; + BURN_MSPTARGETPRODUCT* pTargetProduct = pChainedPatch->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex; - // If we are uninstalling or repairing the MSI, we must ignore all the slipstream patches because they cannot - // be applied right now. - if (BOOTSTRAPPER_ACTION_STATE_REPAIR != pAction->msiPackage.action && BOOTSTRAPPER_ACTION_STATE_UNINSTALL != pAction->msiPackage.action) + if (fExecute) + { + pTargetProduct->execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; + pTargetProduct->executeSkip = BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL; + } + else + { + pTargetProduct->rollback = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; + pTargetProduct->rollbackSkip = BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL; + } + } + } + else { + // If the slipstream target is being installed or upgraded (not uninstalled or repaired) then we will slipstream so skip + // the patch's standalone action. Also, if the slipstream target is being repaired and the patch is being + // repaired, skip this operation since it will be redundant. + // + // The primary goal here is to ensure that a slipstream patch that is yet not installed is installed even if the MSI + // is already on the machine. The slipstream must be installed standalone if the MSI is being repaired. for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) { - BURN_PACKAGE* pMspPackage = pPackage->Msi.rgSlipstreamMsps[j].pMspPackage; - AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches."); - - pAction->msiPackage.rgSlipstreamPatches[j] = fExecute ? pMspPackage->execute : pMspPackage->rollback; - for (DWORD k = 0; k < pMspPackage->Msp.cTargetProductCodes; ++k) + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + j; + BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + pSlipstreamMsp->dwMsiChainedPatchIndex; + BURN_MSPTARGETPRODUCT* pTargetProduct = pSlipstreamMsp->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex; + BOOTSTRAPPER_ACTION_STATE action = fExecute ? pTargetProduct->execute : pTargetProduct->rollback; + BOOL fSlipstream = BOOTSTRAPPER_ACTION_STATE_UNINSTALL < action && + (BOOTSTRAPPER_ACTION_STATE_REPAIR != pAction->msiPackage.action || BOOTSTRAPPER_ACTION_STATE_REPAIR == action); + + if (fSlipstream) { - BURN_MSPTARGETPRODUCT* pTargetProduct = pMspPackage->Msp.rgTargetProducts + k; - if (pPackage == pTargetProduct->pChainedTargetPackage) + if (fExecute) + { + pSlipstreamMsp->execute = action; + pTargetProduct->executeSkip = BURN_PATCH_SKIP_STATE_SLIPSTREAM; + } + else { - pAction->msiPackage.rgSlipstreamPatches[j] = fExecute ? pTargetProduct->execute : pTargetProduct->rollback; - break; + pSlipstreamMsp->rollback = action; + pTargetProduct->rollbackSkip = BURN_PATCH_SKIP_STATE_SLIPSTREAM; } } } } } } +} -LExit: - return hr; +static void CalculateExpectedRegistrationStates( + __in BURN_PACKAGE* rgPackages, + __in DWORD cPackages + ) +{ + for (DWORD i = 0; i < cPackages; ++i) + { + BURN_PACKAGE* pPackage = rgPackages + i; + + // MspPackages can have actions throughout the plan, so the plan needed to be finalized before anything could be calculated. + if (BURN_PACKAGE_TYPE_MSP == pPackage->type && !pPackage->fDependencyManagerWasHere) + { + pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; + pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + + for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + j; + + // The highest aggregate action state found will be used. + if (pPackage->execute < pTargetProduct->execute) + { + pPackage->execute = pTargetProduct->execute; + } + + if (pPackage->rollback < pTargetProduct->rollback) + { + pPackage->rollback = pTargetProduct->rollback; + } + } + } + + if (pPackage->fCanAffectRegistration) + { + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pPackage->execute) + { + pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + else if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) + { + pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + + if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute) + { + if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pPackage->expectedCacheRegistrationState) + { + pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pPackage->expectedInstallRegistrationState) + { + pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + } + else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pPackage->dependencyExecute) + { + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->expectedCacheRegistrationState) + { + pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->expectedInstallRegistrationState) + { + pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + } + } + } } static HRESULT PlanDependencyActions( @@ -3064,6 +3100,11 @@ static void ExecuteActionLog( case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: LogStringLine(PlanDumpLevel, "%ls action[%u]: MSI_PACKAGE package id: %ls, action: %hs, action msi property: %ls, ui level: %u, disable externaluihandler: %ls, log path: %ls, logging attrib: %u", wzBase, iAction, pAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pAction->msiPackage.action), LoggingBurnMsiPropertyToString(pAction->msiPackage.actionMsiProperty), pAction->msiPackage.uiLevel, pAction->msiPackage.fDisableExternalUiHandler ? L"yes" : L"no", pAction->msiPackage.sczLogPath, pAction->msiPackage.dwLoggingAttributes); + for (DWORD j = 0; j < pAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++j) + { + const BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pAction->msiPackage.pPackage->Msi.rgSlipstreamMsps + j; + LogStringLine(PlanDumpLevel, " Patch[%u]: msp package id: %ls, action: %hs", j, pSlipstreamMsp->pMspPackage->sczId, LoggingActionStateToString(fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute)); + } break; case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: diff --git a/src/engine/plan.h b/src/engine/plan.h index 5e981561..5e1985ea 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -253,7 +253,6 @@ typedef struct _BURN_EXECUTE_ACTION BOOTSTRAPPER_ACTION_STATE action; BOOTSTRAPPER_FEATURE_ACTION* rgFeatures; - BOOTSTRAPPER_ACTION_STATE* rgSlipstreamPatches; } msiPackage; struct { diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp index 820f88c2..40a61fe3 100644 --- a/src/test/BurnUnitTest/PlanTest.cpp +++ b/src/test/BurnUnitTest/PlanTest.cpp @@ -760,7 +760,7 @@ namespace Bootstrapper Assert::Equal(3ul, pEngineState->packages.cPackages); ValidatePermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"NetFx48Web"); ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); - ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PatchA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PatchA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); } [Fact] -- cgit v1.2.3-55-g6feb From b5c4d92528dd088d0065c634eb1a353c424a4441 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 24 Feb 2021 17:02:31 -0600 Subject: Always remove all dependencies the bundle could have registered. #6297 --- src/engine/apply.cpp | 2 +- src/engine/dependency.cpp | 17 ++++++++++++++++- src/engine/dependency.h | 3 ++- src/engine/elevation.cpp | 6 ++++-- src/engine/registration.cpp | 3 ++- src/engine/registration.h | 1 + src/test/BurnUnitTest/RegistrationTest.cpp | 18 +++++++++++------- 7 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 44ff9429..f57a56fe 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -435,7 +435,7 @@ extern "C" HRESULT ApplyUnregister( } else { - hr = RegistrationSessionEnd(&pEngineState->registration, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction); + hr = RegistrationSessionEnd(&pEngineState->registration, &pEngineState->packages, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction); ExitOnFailure(hr, "Failed to end session in per-user process."); } diff --git a/src/engine/dependency.cpp b/src/engine/dependency.cpp index 3d978740..4833de94 100644 --- a/src/engine/dependency.cpp +++ b/src/engine/dependency.cpp @@ -680,10 +680,12 @@ LExit: } extern "C" void DependencyUnregisterBundle( - __in const BURN_REGISTRATION* pRegistration + __in const BURN_REGISTRATION* pRegistration, + __in const BURN_PACKAGES* pPackages ) { HRESULT hr = S_OK; + LPCWSTR wzDependentProviderKey = pRegistration->sczId; // Remove the bundle provider key. hr = DepUnregisterDependency(pRegistration->hkRoot, pRegistration->sczProviderKey); @@ -695,6 +697,19 @@ extern "C" void DependencyUnregisterBundle( { LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_UNREGISTERED_FAILED, pRegistration->sczProviderKey, hr); } + + // Best effort to make sure this bundle is not registered as a dependent for anything. + for (DWORD i = 0; i < pPackages->cPackages; ++i) + { + const BURN_PACKAGE* pPackage = pPackages->rgPackages + i; + UnregisterPackageDependency(pPackage->fPerMachine, pPackage, wzDependentProviderKey); + } + + for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) + { + const BURN_PACKAGE* pPackage = &pRegistration->relatedBundles.rgRelatedBundles[i].package; + UnregisterPackageDependency(pPackage->fPerMachine, pPackage, wzDependentProviderKey); + } } // internal functions diff --git a/src/engine/dependency.h b/src/engine/dependency.h index 5390bede..efb9f2f2 100644 --- a/src/engine/dependency.h +++ b/src/engine/dependency.h @@ -169,7 +169,8 @@ HRESULT DependencyProcessDependentRegistration( Note: Does not check for existing dependents before removing the key. *********************************************************************/ void DependencyUnregisterBundle( - __in const BURN_REGISTRATION* pRegistration + __in const BURN_REGISTRATION* pRegistration, + __in const BURN_PACKAGES* pPackages ); #if defined(__cplusplus) diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp index c9ed9810..7c5dae4b 100644 --- a/src/engine/elevation.cpp +++ b/src/engine/elevation.cpp @@ -154,6 +154,7 @@ static HRESULT OnSessionResume( __in DWORD cbData ); static HRESULT OnSessionEnd( + __in BURN_PACKAGES* pPackages, __in BURN_REGISTRATION* pRegistration, __in BYTE* pbData, __in DWORD cbData @@ -1637,7 +1638,7 @@ static HRESULT ProcessElevatedChildMessage( break; case BURN_ELEVATION_MESSAGE_TYPE_SESSION_END: - hrResult = OnSessionEnd(pContext->pRegistration, (BYTE*)pMsg->pvData, pMsg->cbData); + hrResult = OnSessionEnd(pContext->pPackages, pContext->pRegistration, (BYTE*)pMsg->pvData, pMsg->cbData); break; case BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE: @@ -1954,6 +1955,7 @@ LExit: } static HRESULT OnSessionEnd( + __in BURN_PACKAGES* pPackages, __in BURN_REGISTRATION* pRegistration, __in BYTE* pbData, __in DWORD cbData @@ -1976,7 +1978,7 @@ static HRESULT OnSessionEnd( ExitOnFailure(hr, "Failed to read dependency registration action."); // suspend session in per-machine process - hr = RegistrationSessionEnd(pRegistration, (BURN_RESUME_MODE)dwResumeMode, (BOOTSTRAPPER_APPLY_RESTART)dwRestart, (BURN_DEPENDENCY_REGISTRATION_ACTION)dwDependencyRegistrationAction); + hr = RegistrationSessionEnd(pRegistration, pPackages, (BURN_RESUME_MODE)dwResumeMode, (BOOTSTRAPPER_APPLY_RESTART)dwRestart, (BURN_DEPENDENCY_REGISTRATION_ACTION)dwDependencyRegistrationAction); ExitOnFailure(hr, "Failed to suspend registration session."); LExit: diff --git a/src/engine/registration.cpp b/src/engine/registration.cpp index 9c821422..dc4b88bf 100644 --- a/src/engine/registration.cpp +++ b/src/engine/registration.cpp @@ -869,6 +869,7 @@ LExit: *******************************************************************/ extern "C" HRESULT RegistrationSessionEnd( __in BURN_REGISTRATION* pRegistration, + __in BURN_PACKAGES* pPackages, __in BURN_RESUME_MODE resumeMode, __in BOOTSTRAPPER_APPLY_RESTART restart, __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction @@ -912,7 +913,7 @@ extern "C" HRESULT RegistrationSessionEnd( BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER == dependencyRegistrationAction) { // Remove the bundle dependency key. - DependencyUnregisterBundle(pRegistration); + DependencyUnregisterBundle(pRegistration, pPackages); } // Delete update registration key. diff --git a/src/engine/registration.h b/src/engine/registration.h index 56bcb1f0..4aca5a05 100644 --- a/src/engine/registration.h +++ b/src/engine/registration.h @@ -196,6 +196,7 @@ HRESULT RegistrationSessionResume( ); HRESULT RegistrationSessionEnd( __in BURN_REGISTRATION* pRegistration, + __in BURN_PACKAGES* pPackages, __in BURN_RESUME_MODE resumeMode, __in BOOTSTRAPPER_APPLY_RESTART restart, __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction diff --git a/src/test/BurnUnitTest/RegistrationTest.cpp b/src/test/BurnUnitTest/RegistrationTest.cpp index 9c7bf4ce..1687385b 100644 --- a/src/test/BurnUnitTest/RegistrationTest.cpp +++ b/src/test/BurnUnitTest/RegistrationTest.cpp @@ -71,6 +71,7 @@ namespace Bootstrapper BOOTSTRAPPER_COMMAND command = { }; BURN_REGISTRATION registration = { }; BURN_LOGGING logging = { }; + BURN_PACKAGES packages = { }; String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); try { @@ -121,7 +122,7 @@ namespace Bootstrapper Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)(Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr))); // end session - hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + hr = RegistrationSessionEnd(®istration, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); TestThrowOnFailure(hr, L"Failed to unregister bundle."); // verify that registration was removed @@ -159,6 +160,7 @@ namespace Bootstrapper BOOTSTRAPPER_COMMAND command = { }; BURN_REGISTRATION registration = { }; BURN_LOGGING logging = { }; + BURN_PACKAGES packages = { }; String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); try { @@ -210,7 +212,7 @@ namespace Bootstrapper Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); // complete registration - hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); + hr = RegistrationSessionEnd(®istration, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); TestThrowOnFailure(hr, L"Failed to unregister bundle."); // verify that registration was updated @@ -232,7 +234,7 @@ namespace Bootstrapper Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); // delete registration - hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + hr = RegistrationSessionEnd(®istration, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); TestThrowOnFailure(hr, L"Failed to unregister bundle."); // verify that registration was removed @@ -269,6 +271,7 @@ namespace Bootstrapper BOOTSTRAPPER_COMMAND command = { }; BURN_REGISTRATION registration = { }; BURN_LOGGING logging = { }; + BURN_PACKAGES packages = { }; String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); try { @@ -322,7 +325,7 @@ namespace Bootstrapper Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); // finish registration - hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); + hr = RegistrationSessionEnd(®istration, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); TestThrowOnFailure(hr, L"Failed to register bundle."); // verify that registration was updated @@ -355,7 +358,7 @@ namespace Bootstrapper Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); // delete registration - hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + hr = RegistrationSessionEnd(®istration, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); TestThrowOnFailure(hr, L"Failed to unregister bundle."); // verify that registration was removed @@ -392,6 +395,7 @@ namespace Bootstrapper BOOTSTRAPPER_COMMAND command = { }; BURN_REGISTRATION registration = { }; BURN_LOGGING logging = { }; + BURN_PACKAGES packages = { }; BYTE rgbData[256] = { }; BOOTSTRAPPER_RESUME_TYPE resumeType = BOOTSTRAPPER_RESUME_TYPE_NONE; BYTE* pbBuffer = NULL; @@ -459,7 +463,7 @@ namespace Bootstrapper Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_INTERRUPTED, (int)resumeType); // suspend session - hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_SUSPEND, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); + hr = RegistrationSessionEnd(®istration, &packages, BURN_RESUME_MODE_SUSPEND, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); TestThrowOnFailure(hr, L"Failed to suspend session."); // verify that run key was removed @@ -486,7 +490,7 @@ namespace Bootstrapper Assert::NotEqual((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); // end session - hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + hr = RegistrationSessionEnd(®istration, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); TestThrowOnFailure(hr, L"Failed to unregister bundle."); // read resume type after session -- cgit v1.2.3-55-g6feb From 7cf03f0ecc0a54062548656fadcacfba996cd459 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 24 Feb 2021 17:30:28 -0600 Subject: Loosen restrictions for actions that don't affect machine state. --- src/engine/core.cpp | 11 +++++------ src/engine/core.h | 1 - src/engine/plan.cpp | 2 +- src/engine/plan.h | 3 ++- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 0a19ac8e..2f18e4d2 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -282,7 +282,6 @@ extern "C" HRESULT CoreDetect( // Always reset the detect state which means the plan should be reset too. pEngineState->fDetected = FALSE; pEngineState->fPlanned = FALSE; - pEngineState->fApplied = FALSE; DetectReset(&pEngineState->registration, &pEngineState->packages); PlanReset(&pEngineState->plan, &pEngineState->packages); @@ -458,7 +457,7 @@ extern "C" HRESULT CorePlan( { ExitOnFailure(hr = E_INVALIDSTATE, "Plan cannot be done without a successful Detect."); } - else if (pEngineState->fApplied) + else if (pEngineState->plan.fAffectedMachineState) { ExitOnFailure(hr = E_INVALIDSTATE, "Plan requires a new successful Detect after calling Apply."); } @@ -624,7 +623,7 @@ extern "C" HRESULT CoreApply( { ExitOnFailure(hr = E_INVALIDSTATE, "Apply cannot be done without a successful Plan."); } - else if (pEngineState->fApplied) + else if (pEngineState->plan.fAffectedMachineState) { ExitOnFailure(hr = E_INVALIDSTATE, "Plans cannot be applied multiple times."); } @@ -644,7 +643,7 @@ extern "C" HRESULT CoreApply( hr = UserExperienceOnApplyBegin(&pEngineState->userExperience, dwPhaseCount); ExitOnRootFailure(hr, "BA aborted apply begin."); - pEngineState->fApplied = TRUE; + pEngineState->plan.fAffectedMachineState = pEngineState->plan.fCanAffectMachineState; // Abort if this bundle already requires a restart. if (BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING == pEngineState->command.resumeType) @@ -695,7 +694,7 @@ extern "C" HRESULT CoreApply( } // Register. - if (pEngineState->plan.fRegister) + if (pEngineState->plan.fCanAffectMachineState) { fRegistered = TRUE; hr = ApplyRegister(pEngineState); @@ -1107,7 +1106,7 @@ extern "C" void CoreCleanup( LogId(REPORT_STANDARD, MSG_CLEANUP_BEGIN); - if (pEngineState->fApplied && BOOTSTRAPPER_ACTION_LAYOUT < pEngineState->plan.action && BOOTSTRAPPER_ACTION_UPDATE_REPLACE > pEngineState->plan.action) + if (pEngineState->plan.fAffectedMachineState) { LogId(REPORT_STANDARD, MSG_CLEANUP_SKIPPED_APPLY); ExitFunction(); diff --git a/src/engine/core.h b/src/engine/core.h index d98c7646..75a61614 100644 --- a/src/engine/core.h +++ b/src/engine/core.h @@ -80,7 +80,6 @@ typedef struct _BURN_ENGINE_STATE // UX flow control BOOL fDetected; BOOL fPlanned; - BOOL fApplied; BOOL fQuit; //BOOL fSuspend; // Is TRUE when UX made Suspend() call on core. //BOOL fForcedReboot; // Is TRUE when UX made Reboot() call on core. diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index b3cb0ee3..d6cc63ad 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -519,7 +519,7 @@ extern "C" HRESULT PlanRegistration( STRINGDICT_HANDLE sdBundleDependents = NULL; STRINGDICT_HANDLE sdIgnoreDependents = NULL; - pPlan->fRegister = TRUE; // register the bundle since we're modifying machine state. + pPlan->fCanAffectMachineState = TRUE; // register the bundle since we're modifying machine state. pPlan->fDisallowRemoval = FALSE; // by default the bundle can be planned to be removed diff --git a/src/engine/plan.h b/src/engine/plan.h index 5e1985ea..c679d368 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -309,10 +309,11 @@ typedef struct _BURN_PLAN LPWSTR wzBundleId; // points directly into parent the ENGINE_STATE. LPWSTR wzBundleProviderKey; // points directly into parent the ENGINE_STATE. BOOL fPerMachine; - BOOL fRegister; + BOOL fCanAffectMachineState; DWORD dwRegistrationOperations; BOOL fDisallowRemoval; BOOL fDisableRollback; + BOOL fAffectedMachineState; DWORD64 qwCacheSizeTotal; -- cgit v1.2.3-55-g6feb From 100944a40f363c1a8ce0cd8c5c27728d1db0912e Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 24 Feb 2021 17:42:31 -0600 Subject: Let the BA request the bundle to stay installed from OnUnregisterBegin. #6297 --- .../inc/BootstrapperApplication.h | 3 ++- src/engine/apply.cpp | 2 +- src/engine/userexperience.cpp | 10 ++++++---- src/engine/userexperience.h | 3 ++- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h index 0a89b3f4..6cf3477c 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -1089,12 +1089,13 @@ struct BA_ONSYSTEMSHUTDOWN_RESULTS struct BA_ONUNREGISTERBEGIN_ARGS { DWORD cbSize; + BOOL fKeepRegistration; }; struct BA_ONUNREGISTERBEGIN_RESULTS { DWORD cbSize; - BOOL fCancel; + BOOL fForceKeepRegistration; }; struct BA_ONUNREGISTERCOMPLETE_ARGS diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index f57a56fe..3fbab61a 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -399,7 +399,7 @@ extern "C" HRESULT ApplyUnregister( CalculateKeepRegistration(pEngineState, &fKeepRegistration); - hr = UserExperienceOnUnregisterBegin(&pEngineState->userExperience); + hr = UserExperienceOnUnregisterBegin(&pEngineState->userExperience, &fKeepRegistration); ExitOnRootFailure(hr, "BA aborted unregister begin."); // Calculate the correct resume mode. If a restart has been initiated, that trumps all other diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index 88b07d68..84e88718 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -111,7 +111,7 @@ extern "C" HRESULT UserExperienceLoad( args.pCommand = pCommand; args.pfnBootstrapperEngineProc = EngineForApplicationProc; args.pvBootstrapperEngineProcContext = pEngineContext; - args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 2, 18, 0); + args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 2, 24, 0); results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS); @@ -2011,7 +2011,8 @@ LExit: } EXTERN_C BAAPI UserExperienceOnUnregisterBegin( - __in BURN_USER_EXPERIENCE* pUserExperience + __in BURN_USER_EXPERIENCE* pUserExperience, + __inout BOOL* pfKeepRegistration ) { HRESULT hr = S_OK; @@ -2019,15 +2020,16 @@ EXTERN_C BAAPI UserExperienceOnUnregisterBegin( BA_ONUNREGISTERBEGIN_RESULTS results = { }; args.cbSize = sizeof(args); + args.fKeepRegistration = *pfKeepRegistration; results.cbSize = sizeof(results); hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERBEGIN, &args, &results); ExitOnFailure(hr, "BA OnUnregisterBegin failed."); - if (results.fCancel) + if (!args.fKeepRegistration && results.fForceKeepRegistration) { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + *pfKeepRegistration = TRUE; } LExit: diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index f02e6279..754a9030 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -452,7 +452,8 @@ BAAPI UserExperienceOnSystemShutdown( __inout BOOL* pfCancel ); BAAPI UserExperienceOnUnregisterBegin( - __in BURN_USER_EXPERIENCE* pUserExperience + __in BURN_USER_EXPERIENCE* pUserExperience, + __inout BOOL* pfKeepRegistration ); BAAPI UserExperienceOnUnregisterComplete( __in BURN_USER_EXPERIENCE* pUserExperience, -- cgit v1.2.3-55-g6feb From 227518090282bba4d973d4efe910623879218a62 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 2 Mar 2021 15:04:14 -0600 Subject: Upgrade to latest dutil for SHA512. #3992 --- src/engine/engine.vcxproj | 4 ++-- src/engine/packages.config | 2 +- src/engine/precomp.h | 1 - src/stub/packages.config | 2 +- src/stub/stub.vcxproj | 4 ++-- src/test/BurnUnitTest/BurnUnitTest.vcxproj | 4 ++-- src/test/BurnUnitTest/packages.config | 2 +- 7 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index a6ab60df..263edf12 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -1,7 +1,7 @@ - + Debug @@ -165,7 +165,7 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + \ No newline at end of file diff --git a/src/engine/packages.config b/src/engine/packages.config index ad6df168..56c62f29 100644 --- a/src/engine/packages.config +++ b/src/engine/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/engine/precomp.h b/src/engine/precomp.h index 4f8e9d46..4ab636fc 100644 --- a/src/engine/precomp.h +++ b/src/engine/precomp.h @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include diff --git a/src/stub/packages.config b/src/stub/packages.config index 1c027b04..7d0212ac 100644 --- a/src/stub/packages.config +++ b/src/stub/packages.config @@ -4,5 +4,5 @@ - + \ No newline at end of file diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index f9a7b5a9..f13d5ed9 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -5,7 +5,7 @@ - + @@ -117,6 +117,6 @@ - + \ No newline at end of file diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index 65f99168..869ba7a2 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -4,7 +4,7 @@ - + Debug @@ -96,6 +96,6 @@ - + \ No newline at end of file diff --git a/src/test/BurnUnitTest/packages.config b/src/test/BurnUnitTest/packages.config index a35dfe1f..e232db0a 100644 --- a/src/test/BurnUnitTest/packages.config +++ b/src/test/BurnUnitTest/packages.config @@ -10,5 +10,5 @@ - + \ No newline at end of file -- cgit v1.2.3-55-g6feb From 7f128f4639b6a14217780d69a0615b44d36f2f1b Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 2 Mar 2021 15:05:10 -0600 Subject: Use SHA512 instead of SHA1. #3992 --- src/Cpp.Build.props | 2 +- .../inc/BootstrapperEngine.h | 2 +- src/engine/cache.cpp | 12 ++++++------ src/engine/cache.h | 2 +- src/engine/externalengine.cpp | 2 +- src/engine/userexperience.cpp | 2 +- src/test/BurnUnitTest/BurnUnitTest.vcxproj | 1 + src/test/BurnUnitTest/CacheTest.cpp | 8 ++++---- .../BurnUnitTest/TestData/CacheTest/CacheSignatureTest.File | 1 + 9 files changed, 17 insertions(+), 15 deletions(-) create mode 100644 src/test/BurnUnitTest/TestData/CacheTest/CacheSignatureTest.File diff --git a/src/Cpp.Build.props b/src/Cpp.Build.props index ef9de6f0..a734aab0 100644 --- a/src/Cpp.Build.props +++ b/src/Cpp.Build.props @@ -25,7 +25,7 @@ $(DisableSpecificCompilerWarnings) Level4 $(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) + WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0600;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) Use precomp.h StdCall diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h index 9642748b..c0e4ded1 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h @@ -82,7 +82,7 @@ enum BOOTSTRAPPER_LOG_LEVEL enum BOOTSTRAPPER_UPDATE_HASH_TYPE { BOOTSTRAPPER_UPDATE_HASH_TYPE_NONE, - BOOTSTRAPPER_UPDATE_HASH_TYPE_SHA1, + BOOTSTRAPPER_UPDATE_HASH_TYPE_SHA512, }; enum BOOTSTRAPPER_ENGINE_MESSAGE diff --git a/src/engine/cache.cpp b/src/engine/cache.cpp index 92a79eb9..2349a357 100644 --- a/src/engine/cache.cpp +++ b/src/engine/cache.cpp @@ -273,7 +273,7 @@ extern "C" HRESULT CacheCalculatePayloadWorkingPath( ExitOnFailure(hr, "Failed to get working folder for payload."); hr = StrAllocConcat(psczWorkingPath, pPayload->sczKey, 0); - ExitOnFailure(hr, "Failed to append SHA1 hash as payload unverified path."); + ExitOnFailure(hr, "Failed to append Id as payload unverified path."); LExit: return hr; @@ -291,7 +291,7 @@ extern "C" HRESULT CacheCalculateContainerWorkingPath( ExitOnFailure(hr, "Failed to get working folder for container."); hr = StrAllocConcat(psczWorkingPath, pContainer->sczHash, 0); - ExitOnFailure(hr, "Failed to append SHA1 hash as container unverified path."); + ExitOnFailure(hr, "Failed to append hash as container unverified path."); LExit: return hr; @@ -1750,23 +1750,23 @@ static HRESULT VerifyHash( UNREFERENCED_PARAMETER(wzUnverifiedPayloadPath); HRESULT hr = S_OK; - BYTE rgbActualHash[SHA1_HASH_LEN] = { }; + BYTE rgbActualHash[SHA512_HASH_LEN] = { }; DWORD64 qwHashedBytes; LPWSTR pszExpected = NULL; LPWSTR pszActual = NULL; // TODO: create a cryp hash file that sends progress. - hr = CrypHashFileHandle(hFile, PROV_RSA_FULL, CALG_SHA1, rgbActualHash, sizeof(rgbActualHash), &qwHashedBytes); + hr = CrypHashFileHandle(hFile, PROV_RSA_AES, CALG_SHA_512, rgbActualHash, sizeof(rgbActualHash), &qwHashedBytes); ExitOnFailure(hr, "Failed to calculate hash for path: %ls", wzUnverifiedPayloadPath); // Compare hashes. - if (cbHash != sizeof(rgbActualHash) || 0 != memcmp(pbHash, rgbActualHash, SHA1_HASH_LEN)) + if (cbHash != sizeof(rgbActualHash) || 0 != memcmp(pbHash, rgbActualHash, SHA512_HASH_LEN)) { hr = CRYPT_E_HASH_VALUE; // Best effort to log the expected and actual hash value strings. if (SUCCEEDED(StrAllocHexEncode(pbHash, cbHash, &pszExpected)) && - SUCCEEDED(StrAllocHexEncode(rgbActualHash, SHA1_HASH_LEN, &pszActual))) + SUCCEEDED(StrAllocHexEncode(rgbActualHash, (SIZE_T)qwHashedBytes, &pszActual))) { ExitOnFailure(hr, "Hash mismatch for path: %ls, expected: %ls, actual: %ls", wzUnverifiedPayloadPath, pszExpected, pszActual); } diff --git a/src/engine/cache.h b/src/engine/cache.h index f8ad2a90..a00c50b7 100644 --- a/src/engine/cache.h +++ b/src/engine/cache.h @@ -16,7 +16,7 @@ HRESULT CacheInitialize( __in_z_opt LPCWSTR wzSourceProcessPath ); HRESULT CacheEnsureWorkingFolder( - __in_z LPCWSTR wzBundleId, + __in_z_opt LPCWSTR wzBundleId, __deref_out_z_opt LPWSTR* psczWorkingFolder ); HRESULT CacheCalculateBundleWorkingPath( diff --git a/src/engine/externalengine.cpp b/src/engine/externalengine.cpp index f9a06437..26ab9fba 100644 --- a/src/engine/externalengine.cpp +++ b/src/engine/externalengine.cpp @@ -288,7 +288,7 @@ HRESULT ExternalEngineSetUpdate( { hr = E_INVALIDARG; } - else if (BOOTSTRAPPER_UPDATE_HASH_TYPE_SHA1 == hashType && (SHA1_HASH_LEN != cbHash || !rgbHash)) + else if (BOOTSTRAPPER_UPDATE_HASH_TYPE_SHA512 == hashType && (SHA512_HASH_LEN != cbHash || !rgbHash)) { hr = E_INVALIDARG; } diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index 84e88718..40a30c5d 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -111,7 +111,7 @@ extern "C" HRESULT UserExperienceLoad( args.pCommand = pCommand; args.pfnBootstrapperEngineProc = EngineForApplicationProc; args.pvBootstrapperEngineProcContext = pEngineContext; - args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 2, 24, 0); + args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 3, 2, 0); results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS); diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index 869ba7a2..2ca7219e 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -69,6 +69,7 @@ + diff --git a/src/test/BurnUnitTest/CacheTest.cpp b/src/test/BurnUnitTest/CacheTest.cpp index 6d261842..fc0b4531 100644 --- a/src/test/BurnUnitTest/CacheTest.cpp +++ b/src/test/BurnUnitTest/CacheTest.cpp @@ -24,7 +24,7 @@ namespace Bootstrapper { } - [Fact(Skip = "Currently fails")] + [Fact] void CacheSignatureTest() { HRESULT hr = S_OK; @@ -36,12 +36,12 @@ namespace Bootstrapper try { - pin_ptr dataDirectory = PtrToStringChars(this->TestContext->DataDirectory); - hr = PathConcat(dataDirectory, L"BurnTestPayloads\\Products\\TestExe\\TestExe.exe", &sczPayloadPath); + pin_ptr dataDirectory = PtrToStringChars(this->TestContext->TestDirectory); + hr = PathConcat(dataDirectory, L"TestData\\CacheTest\\CacheSignatureTest.File", &sczPayloadPath); Assert::True(S_OK == hr, "Failed to get path to test file."); Assert::True(FileExistsEx(sczPayloadPath, NULL), "Test file does not exist."); - hr = StrAllocHexDecode(L"232BD16B78C1926F95D637731E1EE5379A3C4222", &pb, &cb); + hr = StrAllocHexDecode(L"25e61cd83485062b70713aebddd3fe4992826cb121466fddc8de3eacb1e42f39d4bdd8455d95eec8c9529ced4c0296ab861931fe2c86df2f2b4e8d259a6d9223", &pb, &cb); Assert::Equal(S_OK, hr); package.fPerMachine = FALSE; diff --git a/src/test/BurnUnitTest/TestData/CacheTest/CacheSignatureTest.File b/src/test/BurnUnitTest/TestData/CacheTest/CacheSignatureTest.File new file mode 100644 index 00000000..896ac017 --- /dev/null +++ b/src/test/BurnUnitTest/TestData/CacheTest/CacheSignatureTest.File @@ -0,0 +1 @@ +This file has a known hash. \ No newline at end of file -- cgit v1.2.3-55-g6feb From 838d10a319d0c35ed71fcd3e14d2a830883dbbbd Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Sun, 7 Mar 2021 19:28:18 -0500 Subject: Correctly handle missing/null values with ~<> operator. (That's case-insensitive non-equal.) Fixes https://github.com/wixtoolset/issues/issues/5372 --- src/engine/condition.cpp | 2 +- src/test/BurnUnitTest/VariableTest.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/engine/condition.cpp b/src/engine/condition.cpp index 316224db..56fe76c2 100644 --- a/src/engine/condition.cpp +++ b/src/engine/condition.cpp @@ -896,7 +896,7 @@ static HRESULT CompareOperands( else { // not a combination that can be compared - *pfResult = (BURN_SYMBOL_TYPE_NE == comparison); + *pfResult = (BURN_SYMBOL_TYPE_NE == comparison || BURN_SYMBOL_TYPE_NE_I == comparison); } LExit: diff --git a/src/test/BurnUnitTest/VariableTest.cpp b/src/test/BurnUnitTest/VariableTest.cpp index 676c134e..ab45a73f 100644 --- a/src/test/BurnUnitTest/VariableTest.cpp +++ b/src/test/BurnUnitTest/VariableTest.cpp @@ -258,7 +258,10 @@ namespace Bootstrapper Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\"")); Assert::False(EvaluateConditionHelper(&variables, L"NONE = \"NOT\"")); Assert::False(EvaluateConditionHelper(&variables, L"PROP1 <> \"VAL1\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 ~<> \"VAL1\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 ~<> \"Val1\"")); Assert::True(EvaluateConditionHelper(&variables, L"NONE <> \"NOT\"")); + Assert::True(EvaluateConditionHelper(&variables, L"NONE ~<> \"NOT\"")); Assert::True(EvaluateConditionHelper(&variables, L"PROP1 ~= \"val1\"")); Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"val1\"")); -- cgit v1.2.3-55-g6feb From 8b25ff41809c63c92d5acb06a3f8792cb58170e6 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 9 Mar 2021 13:46:58 -0600 Subject: Consider the bundle as eligible for cleanup if running from the cache. --- src/engine/detect.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp index 9bb58487..4265cf9b 100644 --- a/src/engine/detect.cpp +++ b/src/engine/detect.cpp @@ -202,7 +202,7 @@ extern "C" HRESULT DetectReportRelatedBundles( HRESULT hr = S_OK; int nCompareResult = 0; BOOTSTRAPPER_REQUEST_STATE uninstallRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; - *pfEligibleForCleanup = pRegistration->fInstalled; + *pfEligibleForCleanup = pRegistration->fInstalled || CacheBundleRunningFromCache(); for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) { -- cgit v1.2.3-55-g6feb From 778b65643f19df94947d390a5a17023043d840b4 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 9 Mar 2021 14:05:08 -0600 Subject: Ensure bundle is registered and cached when modifying machine state. #5702 --- src/engine/plan.cpp | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index d6cc63ad..4f29209f 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -523,6 +523,19 @@ extern "C" HRESULT PlanRegistration( pPlan->fDisallowRemoval = FALSE; // by default the bundle can be planned to be removed + // Ensure the bundle is cached if not running from the cache. + if (!CacheBundleRunningFromCache()) + { + pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE; + } + + // Always write registration since things may have changed or it just needs to be "fixed up". + pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION; + + // Always update our estimated size registration when installing/modify/repair since things + // may have been added or removed or it just needs to be "fixed up". + pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE; + if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) { // If our provider key was detected and it points to our current bundle then we can @@ -621,26 +634,6 @@ extern "C" HRESULT PlanRegistration( { BOOL fAddonOrPatchBundle = (pRegistration->cAddonCodes || pRegistration->cPatchCodes); - // If the bundle is not cached or will not be cached after restart, ensure the bundle is cached. - if (!FileExistsAfterRestart(pRegistration->sczCacheExecutablePath, NULL)) - { - pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE; - pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION; - } - else if (BOOTSTRAPPER_ACTION_REPAIR == pPlan->action && !CacheBundleRunningFromCache()) // repairing but not running from the cache. - { - pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE; - pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION; - } - else if (BOOTSTRAPPER_ACTION_REPAIR == pPlan->action) // just repair, make sure the registration is "fixed up". - { - pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION; - } - - // Always update our estimated size registration when installing/modify/repair since things - // may have been added or removed or it just needs to be "fixed up". - pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE; - // Always plan to write our provider key registration when installing/modify/repair to "fix it" // if broken. pPlan->dependencyRegistrationAction = BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER; -- cgit v1.2.3-55-g6feb From 10ef9d5bfbf81f454113a1c2716009831a916222 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 10 Mar 2021 15:47:59 -0600 Subject: Determine whether to ignore forward compatible bundles during Plan. --- .../inc/BootstrapperApplication.h | 20 ++++++- src/engine/core.cpp | 65 ++++++++++----------- src/engine/dependency.cpp | 23 +++----- src/engine/dependency.h | 10 ---- src/engine/detect.cpp | 48 +++------------ src/engine/detect.h | 3 +- src/engine/engine.mc | 2 +- src/engine/plan.cpp | 68 ++++++++++++++++++++++ src/engine/plan.h | 10 ++++ src/engine/registration.h | 6 +- src/engine/userexperience.cpp | 43 ++++++++++++-- src/engine/userexperience.h | 12 +++- 12 files changed, 200 insertions(+), 110 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h index 6cf3477c..c3242167 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -143,6 +143,7 @@ enum BOOTSTRAPPER_APPLICATION_MESSAGE BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMRESTOREPOINTBEGIN, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMRESTOREPOINTCOMPLETE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDPACKAGE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANFORWARDCOMPATIBLEBUNDLE, }; enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION @@ -496,7 +497,6 @@ struct BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_RESULTS { DWORD cbSize; BOOL fCancel; - BOOL fIgnoreBundle; }; struct BA_ONDETECTMSIFEATURE_ARGS @@ -854,6 +854,24 @@ struct BA_ONPLANCOMPLETE_RESULTS DWORD cbSize; }; +struct BA_ONPLANFORWARDCOMPATIBLEBUNDLE_ARGS +{ + DWORD cbSize; + LPCWSTR wzBundleId; + BOOTSTRAPPER_RELATION_TYPE relationType; + LPCWSTR wzBundleTag; + BOOL fPerMachine; + LPCWSTR wzVersion; + BOOL fRecommendedIgnoreBundle; +}; + +struct BA_ONPLANFORWARDCOMPATIBLEBUNDLE_RESULTS +{ + DWORD cbSize; + BOOL fCancel; + BOOL fIgnoreBundle; +}; + struct BA_ONPLANMSIFEATURE_ARGS { DWORD cbSize; diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 2f18e4d2..eb8a84fe 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -319,15 +319,8 @@ extern "C" HRESULT CoreDetect( hr = DependencyDetectProviderKeyBundleId(&pEngineState->registration); if (SUCCEEDED(hr)) { - hr = DetectForwardCompatibleBundle(&pEngineState->userExperience, &pEngineState->command, &pEngineState->registration); + hr = DetectForwardCompatibleBundles(&pEngineState->userExperience, &pEngineState->registration); ExitOnFailure(hr, "Failed to detect forward compatible bundle."); - - // If a forward compatible bundle was detected, skip rest of bundle detection - // since we will passthrough. - if (pEngineState->registration.fEnabledForwardCompatibleBundle) - { - ExitFunction(); - } } else if (E_NOTFOUND == hr) { @@ -504,39 +497,45 @@ extern "C" HRESULT CorePlan( hr = PlanUpdateBundle(&pEngineState->userExperience, pUpgradeBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, &hSyncpointEvent); ExitOnFailure(hr, "Failed to plan update."); } - else if (pEngineState->registration.fEnabledForwardCompatibleBundle) + else { - Assert(!pEngineState->plan.fPerMachine); + hr = PlanForwardCompatibleBundles(&pEngineState->userExperience, &pEngineState->command, &pEngineState->plan, &pEngineState->registration, action); + ExitOnFailure(hr, "Failed to plan forward compatible bundles."); - pForwardCompatibleBundlePackage = &pEngineState->registration.forwardCompatibleBundle; - - hr = PlanPassThroughBundle(&pEngineState->userExperience, pForwardCompatibleBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, &hSyncpointEvent); - ExitOnFailure(hr, "Failed to plan passthrough."); - } - else // doing an action that modifies the machine state. - { - pEngineState->plan.fPerMachine = pEngineState->registration.fPerMachine; // default the scope of the plan to the per-machine state of the bundle. + if (pEngineState->plan.fEnabledForwardCompatibleBundle) + { + Assert(!pEngineState->plan.fPerMachine); - hr = PlanRegistration(&pEngineState->plan, &pEngineState->registration, pEngineState->command.resumeType, pEngineState->command.relationType, &fContinuePlanning); - ExitOnFailure(hr, "Failed to plan registration."); + pForwardCompatibleBundlePackage = &pEngineState->plan.forwardCompatibleBundle; - if (fContinuePlanning) + hr = PlanPassThroughBundle(&pEngineState->userExperience, pForwardCompatibleBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, &hSyncpointEvent); + ExitOnFailure(hr, "Failed to plan passthrough."); + } + else // doing an action that modifies the machine state. { - // Remember the early index, because we want to be able to insert some related bundles - // into the plan before other executed packages. This particularly occurs for uninstallation - // of addons and patches, which should be uninstalled before the main product. - DWORD dwExecuteActionEarlyIndex = pEngineState->plan.cExecuteActions; + pEngineState->plan.fPerMachine = pEngineState->registration.fPerMachine; // default the scope of the plan to the per-machine state of the bundle. - // Plan the related bundles first to support downgrades with ref-counting. - hr = PlanRelatedBundlesBegin(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->plan); - ExitOnFailure(hr, "Failed to plan related bundles."); + hr = PlanRegistration(&pEngineState->plan, &pEngineState->registration, pEngineState->command.resumeType, pEngineState->command.relationType, &fContinuePlanning); + ExitOnFailure(hr, "Failed to plan registration."); - hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, NULL, &hSyncpointEvent); - ExitOnFailure(hr, "Failed to plan packages."); + if (fContinuePlanning) + { + // Remember the early index, because we want to be able to insert some related bundles + // into the plan before other executed packages. This particularly occurs for uninstallation + // of addons and patches, which should be uninstalled before the main product. + DWORD dwExecuteActionEarlyIndex = pEngineState->plan.cExecuteActions; - // Schedule the update of related bundles last. - hr = PlanRelatedBundlesComplete(&pEngineState->registration, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, &hSyncpointEvent, dwExecuteActionEarlyIndex); - ExitOnFailure(hr, "Failed to schedule related bundles."); + // Plan the related bundles first to support downgrades with ref-counting. + hr = PlanRelatedBundlesBegin(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->plan); + ExitOnFailure(hr, "Failed to plan related bundles."); + + hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, NULL, &hSyncpointEvent); + ExitOnFailure(hr, "Failed to plan packages."); + + // Schedule the update of related bundles last. + hr = PlanRelatedBundlesComplete(&pEngineState->registration, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, &hSyncpointEvent, dwExecuteActionEarlyIndex); + ExitOnFailure(hr, "Failed to schedule related bundles."); + } } } diff --git a/src/engine/dependency.cpp b/src/engine/dependency.cpp index 4833de94..9ab76551 100644 --- a/src/engine/dependency.cpp +++ b/src/engine/dependency.cpp @@ -234,6 +234,8 @@ extern "C" HRESULT DependencyDetect( BURN_REGISTRATION* pRegistration = &pEngineState->registration; STRINGDICT_HANDLE sdIgnoredDependents = NULL; BURN_PACKAGE* pPackage = NULL; + BOOL fSelfDependent = NULL != pRegistration->wzSelfDependent; + BOOL fActiveParent = NULL != pRegistration->sczActiveParent && NULL != *pRegistration->sczActiveParent; // Always leave this empty so that all dependents get detected. Plan will ignore dependents based on its own logic. hr = DictCreateStringList(&sdIgnoredDependents, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); @@ -263,16 +265,20 @@ extern "C" HRESULT DependencyDetect( ExitOnFailure(hr, "Failed to detect dependents for related bundle '%ls'", pPackage->sczId); } - if (pRegistration->wzSelfDependent) + if (fSelfDependent || fActiveParent) { for (DWORD i = 0; i < pRegistration->cDependents; ++i) { DEPENDENCY* pDependent = pRegistration->rgDependents + i; - if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->wzSelfDependent, -1, pDependent->sczKey, -1)) + if (fActiveParent && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczActiveParent, -1, pDependent->sczKey, -1)) + { + pRegistration->fParentRegisteredAsDependent = TRUE; + } + + if (fSelfDependent && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->wzSelfDependent, -1, pDependent->sczKey, -1)) { pRegistration->fSelfRegisteredAsDependent = TRUE; - break; } } } @@ -348,17 +354,6 @@ LExit: return hr; } -extern "C" BOOL DependencyDependentExists( - __in const BURN_REGISTRATION* pRegistration, - __in_z LPCWSTR wzDependentProviderKey - ) -{ - HRESULT hr = S_OK; - - hr = DepDependentExists(pRegistration->hkRoot, pRegistration->sczProviderKey, wzDependentProviderKey); - return SUCCEEDED(hr); -} - extern "C" HRESULT DependencyPlanPackageBegin( __in BOOL fPerMachine, __in BURN_PACKAGE* pPackage, diff --git a/src/engine/dependency.h b/src/engine/dependency.h index efb9f2f2..06a01a20 100644 --- a/src/engine/dependency.h +++ b/src/engine/dependency.h @@ -84,16 +84,6 @@ HRESULT DependencyAddIgnoreDependencies( __in_z LPCWSTR wzAddIgnoreDependencies ); -/******************************************************************** - DependencyDependentExists - Checks to see if the provider key is - already dependent on this bundle. - -*********************************************************************/ -BOOL DependencyDependentExists( - __in const BURN_REGISTRATION* pRegistration, - __in_z LPCWSTR wzDependentProviderKey - ); - /******************************************************************** DependencyPlanPackageBegin - Updates the dependency registration action depending on the calculated state for the package. diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp index 4265cf9b..74e8b9ca 100644 --- a/src/engine/detect.cpp +++ b/src/engine/detect.cpp @@ -39,9 +39,9 @@ extern "C" void DetectReset( { RelatedBundlesUninitialize(&pRegistration->relatedBundles); ReleaseNullStr(pRegistration->sczDetectedProviderKeyBundleId); - pRegistration->fEnabledForwardCompatibleBundle = FALSE; - PackageUninitialize(&pRegistration->forwardCompatibleBundle); pRegistration->fSelfRegisteredAsDependent = FALSE; + pRegistration->fParentRegisteredAsDependent = FALSE; + pRegistration->fForwardCompatibleBundleExists = FALSE; pRegistration->fEligibleForCleanup = FALSE; if (pRegistration->rgIgnoredDependencies) @@ -120,46 +120,20 @@ extern "C" void DetectReset( } } -extern "C" HRESULT DetectForwardCompatibleBundle( +extern "C" HRESULT DetectForwardCompatibleBundles( __in BURN_USER_EXPERIENCE* pUX, - __in BOOTSTRAPPER_COMMAND* pCommand, __in BURN_REGISTRATION* pRegistration ) { HRESULT hr = S_OK; - BOOL fRecommendIgnore = TRUE; - BOOL fIgnoreBundle = FALSE; int nCompareResult = 0; if (pRegistration->sczDetectedProviderKeyBundleId && CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRegistration->sczId, -1)) { - // Only change the recommendation if an active parent was provided. - if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent) - { - // On install, recommend running the forward compatible bundle because there is an active parent. This - // will essentially register the parent with the forward compatible bundle. - if (BOOTSTRAPPER_ACTION_INSTALL == pCommand->action) - { - fRecommendIgnore = FALSE; - } - else if (BOOTSTRAPPER_ACTION_UNINSTALL == pCommand->action || - BOOTSTRAPPER_ACTION_MODIFY == pCommand->action || - BOOTSTRAPPER_ACTION_REPAIR == pCommand->action) - { - // When modifying the bundle, only recommend running the forward compatible bundle if the parent - // is already registered as a dependent of the provider key. - if (DependencyDependentExists(pRegistration, pRegistration->sczActiveParent)) - { - fRecommendIgnore = FALSE; - } - } - } - for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) { BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle; - fIgnoreBundle = fRecommendIgnore; if (BOOTSTRAPPER_RELATION_UPGRADE == pRelatedBundle->relationType && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRelatedBundle->package.sczId, -1)) @@ -169,19 +143,13 @@ extern "C" HRESULT DetectForwardCompatibleBundle( if (nCompareResult <= 0) { - hr = UserExperienceOnDetectForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, &fIgnoreBundle); - ExitOnRootFailure(hr, "BA aborted detect forward compatible bundle."); - - if (!fIgnoreBundle) - { - hr = PseudoBundleInitializePassthrough(&pRegistration->forwardCompatibleBundle, pCommand, NULL, pRegistration->sczActiveParent, pRegistration->sczAncestors, &pRelatedBundle->package); - ExitOnFailure(hr, "Failed to initialize update bundle."); + pRelatedBundle->fForwardCompatible = TRUE; + pRegistration->fForwardCompatibleBundleExists = TRUE; - pRegistration->fEnabledForwardCompatibleBundle = TRUE; - } + hr = UserExperienceOnDetectForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion); + ExitOnRootFailure(hr, "BA aborted detect forward compatible bundle."); - LogId(REPORT_STANDARD, MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingBoolToString(pRegistration->fEnabledForwardCompatibleBundle)); - break; + LogId(REPORT_STANDARD, MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion); } } } diff --git a/src/engine/detect.h b/src/engine/detect.h index 7989c9dd..9bc34882 100644 --- a/src/engine/detect.h +++ b/src/engine/detect.h @@ -20,9 +20,8 @@ void DetectReset( __in BURN_PACKAGES* pPackages ); -HRESULT DetectForwardCompatibleBundle( +HRESULT DetectForwardCompatibleBundles( __in BURN_USER_EXPERIENCE* pUX, - __in BOOTSTRAPPER_COMMAND* pCommand, __in BURN_REGISTRATION* pRegistration ); diff --git a/src/engine/engine.mc b/src/engine/engine.mc index f7b18c59..687d2b60 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -237,7 +237,7 @@ MessageId=107 Severity=Success SymbolicName=MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE Language=English -Detected forward compatible bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!ls!, enabled: %5!hs! +Detected forward compatible bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!ls! . MessageId=120 diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index 4f29209f..87607382 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -194,6 +194,8 @@ extern "C" void PlanReset( __in BURN_PACKAGES* pPackages ) { + PackageUninitialize(&pPlan->forwardCompatibleBundle); + if (pPlan->rgRegistrationActions) { for (DWORD i = 0; i < pPlan->cRegistrationActions; ++i) @@ -488,6 +490,72 @@ LExit: return hr; } +extern "C" HRESULT PlanForwardCompatibleBundles( + __in BURN_USER_EXPERIENCE* pUX, + __in BOOTSTRAPPER_COMMAND* pCommand, + __in BURN_PLAN* pPlan, + __in BURN_REGISTRATION* pRegistration, + __in BOOTSTRAPPER_ACTION action + ) +{ + HRESULT hr = S_OK; + BOOL fRecommendIgnore = TRUE; + BOOL fIgnoreBundle = FALSE; + + if (!pRegistration->fForwardCompatibleBundleExists) + { + ExitFunction(); + } + + // Only change the recommendation if an active parent was provided. + if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent) + { + // On install, recommend running the forward compatible bundle because there is an active parent. This + // will essentially register the parent with the forward compatible bundle. + if (BOOTSTRAPPER_ACTION_INSTALL == action) + { + fRecommendIgnore = FALSE; + } + else if (BOOTSTRAPPER_ACTION_UNINSTALL == action || + BOOTSTRAPPER_ACTION_MODIFY == action || + BOOTSTRAPPER_ACTION_REPAIR == action) + { + // When modifying the bundle, only recommend running the forward compatible bundle if the parent + // is already registered as a dependent of the provider key. + if (pRegistration->fParentRegisteredAsDependent) + { + fRecommendIgnore = FALSE; + } + } + } + + for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) + { + BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle; + if (!pRelatedBundle->fForwardCompatible) + { + continue; + } + + fIgnoreBundle = fRecommendIgnore; + + hr = UserExperienceOnPlanForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, &fIgnoreBundle); + ExitOnRootFailure(hr, "BA aborted plan forward compatible bundle."); + + if (!fIgnoreBundle) + { + hr = PseudoBundleInitializePassthrough(&pPlan->forwardCompatibleBundle, pCommand, NULL, pRegistration->sczActiveParent, pRegistration->sczAncestors, &pRelatedBundle->package); + ExitOnFailure(hr, "Failed to initialize pass through bundle."); + + pPlan->fEnabledForwardCompatibleBundle = TRUE; + break; + } + } + +LExit: + return hr; +} + extern "C" HRESULT PlanPackages( __in BURN_USER_EXPERIENCE* pUX, __in BURN_PACKAGES* pPackages, diff --git a/src/engine/plan.h b/src/engine/plan.h index c679d368..e72186c7 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -322,6 +322,9 @@ typedef struct _BURN_PLAN DWORD cExecutePackagesTotal; DWORD cOverallProgressTicksTotal; + BOOL fEnabledForwardCompatibleBundle; + BURN_PACKAGE forwardCompatibleBundle; + BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction; BURN_DEPENDENT_REGISTRATION_ACTION* rgRegistrationActions; @@ -392,6 +395,13 @@ HRESULT PlanLayoutBundle( __in BURN_PAYLOADS* pPayloads, __out_z LPWSTR* psczLayoutDirectory ); +HRESULT PlanForwardCompatibleBundles( + __in BURN_USER_EXPERIENCE* pUX, + __in BOOTSTRAPPER_COMMAND* pCommand, + __in BURN_PLAN* pPlan, + __in BURN_REGISTRATION* pRegistration, + __in BOOTSTRAPPER_ACTION action + ); HRESULT PlanPackages( __in BURN_USER_EXPERIENCE* pUX, __in BURN_PACKAGES* pPackages, diff --git a/src/engine/registration.h b/src/engine/registration.h index 4aca5a05..bb87b6e9 100644 --- a/src/engine/registration.h +++ b/src/engine/registration.h @@ -58,6 +58,7 @@ typedef struct _BURN_UPDATE_REGISTRATION typedef struct _BURN_RELATED_BUNDLE { BOOTSTRAPPER_RELATION_TYPE relationType; + BOOL fForwardCompatible; VERUTIL_VERSION* pVersion; LPWSTR sczTag; @@ -146,14 +147,13 @@ typedef struct _BURN_REGISTRATION UINT cDependents; // Only valid after detect. LPCWSTR wzSelfDependent; // Only valid after detect. BOOL fSelfRegisteredAsDependent; // Only valid after detect. + BOOL fParentRegisteredAsDependent; // Only valid after detect. + BOOL fForwardCompatibleBundleExists; // Only valid after detect. BOOL fEligibleForCleanup; // Only valid after detect. LPWSTR sczDetectedProviderKeyBundleId; LPWSTR sczAncestors; LPWSTR sczBundlePackageAncestors; - - BOOL fEnabledForwardCompatibleBundle; - BURN_PACKAGE forwardCompatibleBundle; } BURN_REGISTRATION; diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index 40a30c5d..e1e32a87 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -763,8 +763,7 @@ EXTERN_C BAAPI UserExperienceOnDetectForwardCompatibleBundle( __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z LPCWSTR wzBundleTag, __in BOOL fPerMachine, - __in VERUTIL_VERSION* pVersion, - __inout BOOL* pfIgnoreBundle + __in VERUTIL_VERSION* pVersion ) { HRESULT hr = S_OK; @@ -779,7 +778,6 @@ EXTERN_C BAAPI UserExperienceOnDetectForwardCompatibleBundle( args.wzVersion = pVersion->sczVersion; results.cbSize = sizeof(results); - results.fIgnoreBundle = *pfIgnoreBundle; hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTFORWARDCOMPATIBLEBUNDLE, &args, &results); ExitOnFailure(hr, "BA OnDetectForwardCompatibleBundle failed."); @@ -788,7 +786,6 @@ EXTERN_C BAAPI UserExperienceOnDetectForwardCompatibleBundle( { hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); } - *pfIgnoreBundle = results.fIgnoreBundle; LExit: return hr; @@ -1567,6 +1564,44 @@ LExit: return hr; } +EXTERN_C BAAPI UserExperienceOnPlanForwardCompatibleBundle( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzBundleId, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in_z LPCWSTR wzBundleTag, + __in BOOL fPerMachine, + __in VERUTIL_VERSION* pVersion, + __inout BOOL* pfIgnoreBundle + ) +{ + HRESULT hr = S_OK; + BA_ONPLANFORWARDCOMPATIBLEBUNDLE_ARGS args = { }; + BA_ONPLANFORWARDCOMPATIBLEBUNDLE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzBundleId = wzBundleId; + args.relationType = relationType; + args.wzBundleTag = wzBundleTag; + args.fPerMachine = fPerMachine; + args.wzVersion = pVersion->sczVersion; + args.fRecommendedIgnoreBundle = *pfIgnoreBundle; + + results.cbSize = sizeof(results); + results.fIgnoreBundle = *pfIgnoreBundle; + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANFORWARDCOMPATIBLEBUNDLE, &args, &results); + ExitOnFailure(hr, "BA OnPlanForwardCompatibleBundle failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + *pfIgnoreBundle = results.fIgnoreBundle; + +LExit: + return hr; +} + EXTERN_C BAAPI UserExperienceOnPlanMsiPackage( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index 754a9030..eccc0786 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -200,8 +200,7 @@ BAAPI UserExperienceOnDetectForwardCompatibleBundle( __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z LPCWSTR wzBundleTag, __in BOOL fPerMachine, - __in VERUTIL_VERSION* pVersion, - __inout BOOL* pfIgnoreBundle + __in VERUTIL_VERSION* pVersion ); BAAPI UserExperienceOnDetectMsiFeature( __in BURN_USER_EXPERIENCE* pUserExperience, @@ -357,6 +356,15 @@ BAAPI UserExperienceOnPlanComplete( __in BURN_USER_EXPERIENCE* pUserExperience, __in HRESULT hrStatus ); +BAAPI UserExperienceOnPlanForwardCompatibleBundle( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzBundleId, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in_z LPCWSTR wzBundleTag, + __in BOOL fPerMachine, + __in VERUTIL_VERSION* pVersion, + __inout BOOL* pfIgnoreBundle + ); BAAPI UserExperienceOnPlanMsiFeature( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, -- cgit v1.2.3-55-g6feb From af68033509730ffe01602f839861a47287bb709f Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 10 Mar 2021 18:18:38 -0600 Subject: Handle when related bundles have an uninstall key but aren't cached. #4991 --- .../inc/BootstrapperApplication.h | 2 + src/engine/core.cpp | 39 ++++++--- src/engine/dependency.cpp | 8 +- src/engine/detect.cpp | 17 ++-- src/engine/engine.mc | 11 ++- src/engine/externalengine.cpp | 2 +- src/engine/plan.cpp | 11 +++ src/engine/pseudobundle.cpp | 5 +- src/engine/pseudobundle.h | 1 + src/engine/registration.h | 1 + src/engine/relatedbundle.cpp | 16 +++- src/engine/userexperience.cpp | 8 +- src/engine/userexperience.h | 6 +- src/test/BurnUnitTest/PlanTest.cpp | 95 +++++++++++++++++++++- 14 files changed, 188 insertions(+), 34 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h index c3242167..fb4b6ea3 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -491,6 +491,7 @@ struct BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_ARGS LPCWSTR wzBundleTag; BOOL fPerMachine; LPCWSTR wzVersion; + BOOL fMissingFromCache; }; struct BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_RESULTS @@ -547,6 +548,7 @@ struct BA_ONDETECTRELATEDBUNDLE_ARGS BOOL fPerMachine; LPCWSTR wzVersion; BOOTSTRAPPER_RELATED_OPERATION operation; + BOOL fMissingFromCache; }; struct BA_ONDETECTRELATEDBUNDLE_RESULTS diff --git a/src/engine/core.cpp b/src/engine/core.cpp index eb8a84fe..50ed6ea0 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -59,6 +59,10 @@ static void LogPackages( __in const BURN_RELATED_BUNDLES* pRelatedBundles, __in const BOOTSTRAPPER_ACTION action ); +static void LogRelatedBundles( + __in const BURN_RELATED_BUNDLES* pRelatedBundles, + __in BOOL fReverse + ); // function definitions @@ -1793,15 +1797,9 @@ static void LogPackages( else { // Display related bundles first if uninstalling. - if (BOOTSTRAPPER_ACTION_UNINSTALL == action && 0 < pRelatedBundles->cRelatedBundles) + if (BOOTSTRAPPER_ACTION_UNINSTALL == action) { - for (int i = pRelatedBundles->cRelatedBundles - 1; 0 <= i; --i) - { - const BURN_RELATED_BUNDLE* pRelatedBundle = &pRelatedBundles->rgRelatedBundles[i]; - const BURN_PACKAGE* pPackage = &pRelatedBundle->package; - - LogId(REPORT_STANDARD, MSG_PLANNED_RELATED_BUNDLE, pPackage->sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingDependencyActionToString(pPackage->dependencyExecute)); - } + LogRelatedBundles(pRelatedBundles, TRUE); } // Display all the packages in the log. @@ -1852,13 +1850,28 @@ static void LogPackages( } // Display related bundles last if caching, installing, modifying, or repairing. - if (BOOTSTRAPPER_ACTION_UNINSTALL < action && 0 < pRelatedBundles->cRelatedBundles) + if (BOOTSTRAPPER_ACTION_UNINSTALL < action) { - for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i) - { - const BURN_RELATED_BUNDLE* pRelatedBundle = &pRelatedBundles->rgRelatedBundles[i]; - const BURN_PACKAGE* pPackage = &pRelatedBundle->package; + LogRelatedBundles(pRelatedBundles, FALSE); + } + } +} + +static void LogRelatedBundles( + __in const BURN_RELATED_BUNDLES* pRelatedBundles, + __in BOOL fReverse + ) +{ + if (0 < pRelatedBundles->cRelatedBundles) + { + for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i) + { + const DWORD iRelatedBundle = fReverse ? pRelatedBundles->cRelatedBundles - 1 - i : i; + const BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + iRelatedBundle; + const BURN_PACKAGE* pPackage = &pRelatedBundle->package; + if (pRelatedBundle->fPlannable) + { LogId(REPORT_STANDARD, MSG_PLANNED_RELATED_BUNDLE, pPackage->sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingDependencyActionToString(pPackage->dependencyExecute)); } } diff --git a/src/engine/dependency.cpp b/src/engine/dependency.cpp index 9ab76551..51aca239 100644 --- a/src/engine/dependency.cpp +++ b/src/engine/dependency.cpp @@ -260,7 +260,13 @@ extern "C" HRESULT DependencyDetect( for (DWORD iRelatedBundle = 0; iRelatedBundle < pEngineState->registration.relatedBundles.cRelatedBundles; ++iRelatedBundle) { - pPackage = &pEngineState->registration.relatedBundles.rgRelatedBundles[iRelatedBundle].package; + BURN_RELATED_BUNDLE* pRelatedBundle = pEngineState->registration.relatedBundles.rgRelatedBundles + iRelatedBundle; + if (!pRelatedBundle->fPlannable) + { + continue; + } + + pPackage = &pRelatedBundle->package; hr = DetectPackageDependents(pPackage, sdIgnoredDependents, pRegistration); ExitOnFailure(hr, "Failed to detect dependents for related bundle '%ls'", pPackage->sczId); } diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp index 74e8b9ca..8ca74986 100644 --- a/src/engine/detect.cpp +++ b/src/engine/detect.cpp @@ -143,13 +143,16 @@ extern "C" HRESULT DetectForwardCompatibleBundles( if (nCompareResult <= 0) { - pRelatedBundle->fForwardCompatible = TRUE; - pRegistration->fForwardCompatibleBundleExists = TRUE; + if (pRelatedBundle->fPlannable) + { + pRelatedBundle->fForwardCompatible = TRUE; + pRegistration->fForwardCompatibleBundleExists = TRUE; + } - hr = UserExperienceOnDetectForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion); + hr = UserExperienceOnDetectForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, BURN_CACHE_STATE_COMPLETE != pRelatedBundle->package.cache); ExitOnRootFailure(hr, "BA aborted detect forward compatible bundle."); - LogId(REPORT_STANDARD, MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion); + LogId(REPORT_STANDARD, MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingCacheStateToString(pRelatedBundle->package.cache)); } } } @@ -222,13 +225,13 @@ extern "C" HRESULT DetectReportRelatedBundles( break; } - LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingRelatedOperationToString(operation)); + LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingRelatedOperationToString(operation), LoggingCacheStateToString(pRelatedBundle->package.cache)); - hr = UserExperienceOnDetectRelatedBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, operation); + hr = UserExperienceOnDetectRelatedBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, operation, BURN_CACHE_STATE_COMPLETE != pRelatedBundle->package.cache); ExitOnRootFailure(hr, "BA aborted detect related bundle."); // For now, if any related bundles will be executed during uninstall by default then never automatically clean up the bundle. - if (*pfEligibleForCleanup) + if (*pfEligibleForCleanup && pRelatedBundle->fPlannable) { uninstallRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; hr = PlanDefaultRelatedBundleRequestState(relationType, pRelatedBundle->relationType, BOOTSTRAPPER_ACTION_UNINSTALL, pRegistration->pVersion, pRelatedBundle->pVersion, &uninstallRequestState); diff --git a/src/engine/engine.mc b/src/engine/engine.mc index 687d2b60..a793540a 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -202,7 +202,7 @@ MessageId=102 Severity=Success SymbolicName=MSG_DETECTED_RELATED_BUNDLE Language=English -Detected related bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!ls!, operation: %5!hs! +Detected related bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!ls!, operation: %5!hs!, cached: %6!hs! . MessageId=103 @@ -237,7 +237,14 @@ MessageId=107 Severity=Success SymbolicName=MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE Language=English -Detected forward compatible bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!ls! +Detected forward compatible bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!ls!, cached: %5!hs! +. + +MessageId=108 +Severity=Warning +SymbolicName=MSG_DETECT_RELATED_BUNDLE_NOT_FULLY_CACHED +Language=English +Detected partially cached related bundle: %1!ls!, cache path: %2!ls!, reason: 0x%3!x! . MessageId=120 diff --git a/src/engine/externalengine.cpp b/src/engine/externalengine.cpp index 26ab9fba..d881544c 100644 --- a/src/engine/externalengine.cpp +++ b/src/engine/externalengine.cpp @@ -327,7 +327,7 @@ HRESULT ExternalEngineSetUpdate( sczId = pEngineState->registration.sczId; } - hr = PseudoBundleInitialize(FILEMAKEVERSION(rmj, rmm, rup, rpr), &pEngineState->update.package, FALSE, sczId, BOOTSTRAPPER_RELATION_UPDATE, BOOTSTRAPPER_PACKAGE_STATE_ABSENT, pEngineState->registration.sczExecutableName, sczLocalSource ? sczLocalSource : wzLocalSource, wzDownloadSource, qwSize, TRUE, sczCommandline, NULL, NULL, NULL, rgbHash, cbHash); + hr = PseudoBundleInitialize(FILEMAKEVERSION(rmj, rmm, rup, rpr), &pEngineState->update.package, FALSE, sczId, BOOTSTRAPPER_RELATION_UPDATE, BOOTSTRAPPER_PACKAGE_STATE_ABSENT, BURN_CACHE_STATE_NONE, pEngineState->registration.sczExecutableName, sczLocalSource ? sczLocalSource : wzLocalSource, wzDownloadSource, qwSize, TRUE, sczCommandline, NULL, NULL, NULL, rgbHash, cbHash); ExitOnFailure(hr, "Failed to set update bundle."); pEngineState->update.fUpdateAvailable = TRUE; diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index 87607382..a4b8d0c1 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -1292,6 +1292,12 @@ extern "C" HRESULT PlanRelatedBundlesBegin( for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) { BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i; + + if (!pRelatedBundle->fPlannable) + { + continue; + } + pRelatedBundle->package.defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE; pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE; @@ -1417,6 +1423,11 @@ extern "C" HRESULT PlanRelatedBundlesComplete( DWORD *pdwInsertIndex = NULL; BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i; + if (!pRelatedBundle->fPlannable) + { + continue; + } + // Do not execute if a major upgrade to the related bundle is an embedded bundle (Provider keys are the same) if (0 < pRelatedBundle->package.cDependencyProviders) { diff --git a/src/engine/pseudobundle.cpp b/src/engine/pseudobundle.cpp index 3b05ea0b..63300065 100644 --- a/src/engine/pseudobundle.cpp +++ b/src/engine/pseudobundle.cpp @@ -10,6 +10,7 @@ extern "C" HRESULT PseudoBundleInitialize( __in_z LPCWSTR wzId, __in BOOTSTRAPPER_RELATION_TYPE relationType, __in BOOTSTRAPPER_PACKAGE_STATE state, + __in BURN_CACHE_STATE cacheState, __in_z LPCWSTR wzFilePath, __in_z LPCWSTR wzLocalSource, __in_z_opt LPCWSTR wzDownloadSource, @@ -66,14 +67,14 @@ extern "C" HRESULT PseudoBundleInitialize( memcpy_s(pPackage->rgPayloads->pPayload->pbHash, pPackage->rgPayloads->pPayload->cbHash, pbHash, cbHash); } - pPackage->rgPayloads->fCached = (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_CACHED == state); + pPackage->rgPayloads->fCached = BURN_CACHE_STATE_NONE < cacheState; pPackage->Exe.fPseudoBundle = TRUE; pPackage->type = BURN_PACKAGE_TYPE_EXE; pPackage->fPerMachine = fPerMachine; pPackage->currentState = state; - pPackage->cache = (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_CACHED == state) ? BURN_CACHE_STATE_COMPLETE : BURN_CACHE_STATE_NONE; + pPackage->cache = cacheState; pPackage->qwInstallSize = qwSize; pPackage->qwSize = qwSize; pPackage->fVital = fVital; diff --git a/src/engine/pseudobundle.h b/src/engine/pseudobundle.h index 3b8157a0..c7940976 100644 --- a/src/engine/pseudobundle.h +++ b/src/engine/pseudobundle.h @@ -13,6 +13,7 @@ HRESULT PseudoBundleInitialize( __in_z LPCWSTR wzId, __in BOOTSTRAPPER_RELATION_TYPE relationType, __in BOOTSTRAPPER_PACKAGE_STATE state, + __in BURN_CACHE_STATE cacheState, __in_z LPCWSTR wzFilePath, __in_z LPCWSTR wzLocalSource, __in_z_opt LPCWSTR wzDownloadSource, diff --git a/src/engine/registration.h b/src/engine/registration.h index bb87b6e9..e0418fa3 100644 --- a/src/engine/registration.h +++ b/src/engine/registration.h @@ -62,6 +62,7 @@ typedef struct _BURN_RELATED_BUNDLE VERUTIL_VERSION* pVersion; LPWSTR sczTag; + BOOL fPlannable; BURN_PACKAGE package; } BURN_RELATED_BUNDLE; diff --git a/src/engine/relatedbundle.cpp b/src/engine/relatedbundle.cpp index bc79b954..a4948a88 100644 --- a/src/engine/relatedbundle.cpp +++ b/src/engine/relatedbundle.cpp @@ -398,6 +398,7 @@ static HRESULT LoadRelatedBundleFromKey( DWORD64 qwEngineVersion = 0; LPWSTR sczBundleVersion = NULL; LPWSTR sczCachePath = NULL; + BURN_CACHE_STATE cacheState = BURN_CACHE_STATE_NONE; DWORD64 qwFileSize = 0; BURN_DEPENDENCY_PROVIDER dependencyProvider = { }; @@ -423,7 +424,18 @@ static HRESULT LoadRelatedBundleFromKey( ExitOnFailure(hr, "Failed to read cache path from registry for bundle: %ls", wzRelatedBundleId); hr = FileSize(sczCachePath, reinterpret_cast(&qwFileSize)); - ExitOnFailure(hr, "Failed to get size of pseudo bundle: %ls", sczCachePath); + if (SUCCEEDED(hr)) + { + cacheState = BURN_CACHE_STATE_COMPLETE; + } + else if (E_FILENOTFOUND != hr) + { + cacheState = BURN_CACHE_STATE_PARTIAL; + LogId(REPORT_STANDARD, MSG_DETECT_RELATED_BUNDLE_NOT_FULLY_CACHED, wzRelatedBundleId, sczCachePath, hr); + } + hr = S_OK; + + pRelatedBundle->fPlannable = BURN_CACHE_STATE_COMPLETE == cacheState; hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY, &dependencyProvider.sczKey); if (E_FILENOTFOUND != hr) @@ -452,7 +464,7 @@ static HRESULT LoadRelatedBundleFromKey( pRelatedBundle->relationType = relationType; hr = PseudoBundleInitialize(qwEngineVersion, &pRelatedBundle->package, fPerMachine, wzRelatedBundleId, pRelatedBundle->relationType, - BOOTSTRAPPER_PACKAGE_STATE_PRESENT, sczCachePath, sczCachePath, NULL, qwFileSize, FALSE, + BOOTSTRAPPER_PACKAGE_STATE_PRESENT, cacheState, sczCachePath, sczCachePath, NULL, qwFileSize, FALSE, L"-quiet", L"-repair -quiet", L"-uninstall -quiet", (dependencyProvider.sczKey && *dependencyProvider.sczKey) ? &dependencyProvider : NULL, NULL, 0); diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index e1e32a87..ad1529ea 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -763,7 +763,8 @@ EXTERN_C BAAPI UserExperienceOnDetectForwardCompatibleBundle( __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z LPCWSTR wzBundleTag, __in BOOL fPerMachine, - __in VERUTIL_VERSION* pVersion + __in VERUTIL_VERSION* pVersion, + __in BOOL fMissingFromCache ) { HRESULT hr = S_OK; @@ -776,6 +777,7 @@ EXTERN_C BAAPI UserExperienceOnDetectForwardCompatibleBundle( args.wzBundleTag = wzBundleTag; args.fPerMachine = fPerMachine; args.wzVersion = pVersion->sczVersion; + args.fMissingFromCache = fMissingFromCache; results.cbSize = sizeof(results); @@ -879,7 +881,8 @@ EXTERN_C BAAPI UserExperienceOnDetectRelatedBundle( __in_z LPCWSTR wzBundleTag, __in BOOL fPerMachine, __in VERUTIL_VERSION* pVersion, - __in BOOTSTRAPPER_RELATED_OPERATION operation + __in BOOTSTRAPPER_RELATED_OPERATION operation, + __in BOOL fMissingFromCache ) { HRESULT hr = S_OK; @@ -893,6 +896,7 @@ EXTERN_C BAAPI UserExperienceOnDetectRelatedBundle( args.fPerMachine = fPerMachine; args.wzVersion = pVersion->sczVersion; args.operation = operation; + args.fMissingFromCache = fMissingFromCache; results.cbSize = sizeof(results); diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index eccc0786..bac79e33 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -200,7 +200,8 @@ BAAPI UserExperienceOnDetectForwardCompatibleBundle( __in BOOTSTRAPPER_RELATION_TYPE relationType, __in_z LPCWSTR wzBundleTag, __in BOOL fPerMachine, - __in VERUTIL_VERSION* pVersion + __in VERUTIL_VERSION* pVersion, + __in BOOL fMissingFromCache ); BAAPI UserExperienceOnDetectMsiFeature( __in BURN_USER_EXPERIENCE* pUserExperience, @@ -225,7 +226,8 @@ BAAPI UserExperienceOnDetectRelatedBundle( __in_z LPCWSTR wzBundleTag, __in BOOL fPerMachine, __in VERUTIL_VERSION* pVersion, - __in BOOTSTRAPPER_RELATED_OPERATION operation + __in BOOTSTRAPPER_RELATED_OPERATION operation, + __in BOOL fMissingFromCache ); BAAPI UserExperienceOnDetectRelatedMsiPackage( __in BURN_USER_EXPERIENCE* pUserExperience, diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp index 40a61fe3..2a34cb16 100644 --- a/src/test/BurnUnitTest/PlanTest.cpp +++ b/src/test/BurnUnitTest/PlanTest.cpp @@ -292,6 +292,94 @@ namespace Bootstrapper ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); } + [Fact] + void RelatedBundleMissingFromCacheTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); + DetectAttachedContainerAsAttached(pEngineState); + DetectPackagesAsAbsent(pEngineState); + BURN_RELATED_BUNDLE* pRelatedBundle = DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"0.9.0.0"); + pRelatedBundle->fPlannable = FALSE; + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + DWORD dwPackageStart = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", 5, 2, 33743, FALSE); + ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, BURN_PLAN_INVALID_ACTION_INDEX, 2); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"PackageA", TRUE, FALSE, dwPackageStart); + ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"cab9Ins_fTP3wNwq5Gxo41ch5VUPaQ", TRUE, FALSE, dwPackageStart); + ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(35694ull, pPlan->qwEstimatedSize); + Assert::Equal(33743ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[6].syncpoint.hEvent); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(1ul, pPlan->cExecutePackagesTotal); + Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(1ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + } + [Fact] void SingleMsiCacheTest() { @@ -1041,7 +1129,7 @@ namespace Bootstrapper } } - void DetectUpgradeBundle( + BURN_RELATED_BUNDLE* DetectUpgradeBundle( __in BURN_ENGINE_STATE* pEngineState, __in LPCWSTR wzId, __in LPCWSTR wzVersion @@ -1067,12 +1155,15 @@ namespace Bootstrapper hr = VerParseVersion(wzVersion, 0, FALSE, &pRelatedBundle->pVersion); NativeAssert::Succeeded(hr, "Failed to parse pseudo bundle version: %ls", wzVersion); + pRelatedBundle->fPlannable = TRUE; pRelatedBundle->relationType = BOOTSTRAPPER_RELATION_UPGRADE; - hr = PseudoBundleInitialize(0, &pRelatedBundle->package, TRUE, wzId, pRelatedBundle->relationType, BOOTSTRAPPER_PACKAGE_STATE_PRESENT, NULL, NULL, NULL, 0, FALSE, L"-quiet", L"-repair -quiet", L"-uninstall -quiet", &dependencyProvider, NULL, 0); + hr = PseudoBundleInitialize(0, &pRelatedBundle->package, TRUE, wzId, pRelatedBundle->relationType, BOOTSTRAPPER_PACKAGE_STATE_PRESENT, BURN_CACHE_STATE_COMPLETE, NULL, NULL, NULL, 0, FALSE, L"-quiet", L"-repair -quiet", L"-uninstall -quiet", &dependencyProvider, NULL, 0); NativeAssert::Succeeded(hr, "Failed to initialize related bundle to represent bundle: %ls", wzId); ++pRelatedBundles->cRelatedBundles; + + return pRelatedBundle; } void DetectAsRelatedUpgradeBundle( -- cgit v1.2.3-55-g6feb From b7582318f6cb6e166f5ca22128caea2a97551a1f Mon Sep 17 00:00:00 2001 From: Nir Bar Date: Wed, 17 Mar 2021 14:45:03 -0500 Subject: Use wiutil to start/end msi transactions Release MSI transaction handles immediately contributes to #5386 --- src/engine/apply.cpp | 40 +++++++-------- src/engine/elevation.cpp | 111 +++++++++++++++++++++++++++++++++--------- src/engine/elevation.h | 6 +-- src/engine/msiengine.cpp | 32 ++++++------ src/engine/msiengine.h | 6 +-- src/engine/package.cpp | 27 ++++++++++ src/engine/package.h | 6 +++ src/engine/plan.cpp | 1 + src/engine/userexperience.cpp | 10 ---- src/engine/userexperience.h | 5 -- 10 files changed, 163 insertions(+), 81 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 3fbab61a..77080c76 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -821,16 +821,16 @@ extern "C" HRESULT ApplyExecute( break; } - // If inside a MSI transaction, roll it back. - if (pCheckpoint && pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction) - { - hrRollback = ExecuteMsiRollbackTransaction(pEngineState, pCheckpoint->pActiveRollbackBoundary, &context); - IgnoreRollbackError(hrRollback, "Failed rolling back transaction"); - } - - // The action failed, roll back to previous rollback boundary. if (pCheckpoint) { + // If inside a MSI transaction, roll it back. + if (pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction) + { + hrRollback = ExecuteMsiRollbackTransaction(pEngineState, pCheckpoint->pActiveRollbackBoundary, &context); + IgnoreRollbackError(hrRollback, "Failed rolling back transaction"); + } + + // The action failed, roll back to previous rollback boundary. hrRollback = DoRollbackActions(pEngineState, &context, pCheckpoint->dwId, pRestart); IgnoreRollbackError(hrRollback, "Failed rollback actions"); } @@ -2316,12 +2316,12 @@ static HRESULT ExecuteMsiBeginTransaction( if (pEngineState->plan.fPerMachine) { - hr = ElevationMsiBeginTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary->sczId); + hr = ElevationMsiBeginTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary); ExitOnFailure(hr, "Failed to begin an elevated MSI transaction."); } else { - hr = MsiEngineBeginTransaction(pRollbackBoundary->sczId); + hr = MsiEngineBeginTransaction(pRollbackBoundary); } if (SUCCEEDED(hr)) @@ -2347,25 +2347,25 @@ static HRESULT ExecuteMsiCommitTransaction( ) { HRESULT hr = S_OK; - BOOL fBeginCalled = FALSE; + BOOL fCommitBeginCalled = FALSE; if (!pRollbackBoundary->fActiveTransaction) { ExitFunction1(hr = E_INVALIDSTATE); } - fBeginCalled = TRUE; + fCommitBeginCalled = TRUE; hr = UserExperienceOnCommitMsiTransactionBegin(&pEngineState->userExperience, pRollbackBoundary->sczId); ExitOnRootFailure(hr, "BA aborted execute commit MSI transaction."); if (pEngineState->plan.fPerMachine) { - hr = ElevationMsiCommitTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary->sczId); + hr = ElevationMsiCommitTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary); ExitOnFailure(hr, "Failed to commit an elevated MSI transaction."); } else { - hr = MsiEngineCommitTransaction(pRollbackBoundary->sczId); + hr = MsiEngineCommitTransaction(pRollbackBoundary); } if (SUCCEEDED(hr)) @@ -2376,7 +2376,7 @@ static HRESULT ExecuteMsiCommitTransaction( } LExit: - if (fBeginCalled) + if (fCommitBeginCalled) { UserExperienceOnCommitMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr); } @@ -2391,24 +2391,24 @@ static HRESULT ExecuteMsiRollbackTransaction( ) { HRESULT hr = S_OK; - BOOL fBeginCalled = FALSE; + BOOL fRollbackBeginCalled = FALSE; if (!pRollbackBoundary->fActiveTransaction) { ExitFunction(); } - fBeginCalled = TRUE; + fRollbackBeginCalled = TRUE; UserExperienceOnRollbackMsiTransactionBegin(&pEngineState->userExperience, pRollbackBoundary->sczId); if (pEngineState->plan.fPerMachine) { - hr = ElevationMsiRollbackTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary->sczId); + hr = ElevationMsiRollbackTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary); ExitOnFailure(hr, "Failed to rollback an elevated MSI transaction."); } else { - hr = MsiEngineRollbackTransaction(pRollbackBoundary->sczId); + hr = MsiEngineRollbackTransaction(pRollbackBoundary); } LExit: @@ -2416,7 +2416,7 @@ LExit: ResetTransactionRegistrationState(pEngineState, FALSE); - if (fBeginCalled) + if (fRollbackBeginCalled) { UserExperienceOnRollbackMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr); } diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp index 7c5dae4b..1737bf5b 100644 --- a/src/engine/elevation.cpp +++ b/src/engine/elevation.cpp @@ -246,14 +246,17 @@ static HRESULT OnLaunchApprovedExe( __in DWORD cbData ); static HRESULT OnMsiBeginTransaction( + __in BURN_PACKAGES* pPackages, __in BYTE* pbData, __in DWORD cbData ); static HRESULT OnMsiCommitTransaction( + __in BURN_PACKAGES* pPackages, __in BYTE* pbData, __in DWORD cbData ); static HRESULT OnMsiRollbackTransaction( + __in BURN_PACKAGES* pPackages, __in BYTE* pbData, __in DWORD cbData ); @@ -763,7 +766,7 @@ LExit: extern "C" HRESULT ElevationMsiBeginTransaction( __in HANDLE hPipe, - __in LPCWSTR wzName + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary ) { HRESULT hr = S_OK; @@ -772,9 +775,12 @@ extern "C" HRESULT ElevationMsiBeginTransaction( DWORD dwResult = ERROR_SUCCESS; // serialize message data - hr = BuffWriteString(&pbData, &cbData, wzName); + hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczId); ExitOnFailure(hr, "Failed to write transaction name to message buffer."); + hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczLogPath); + ExitOnFailure(hr, "Failed to write transaction log path to message buffer."); + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION, pbData, cbData, NULL, NULL, &dwResult); ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION message to per-machine process."); @@ -788,7 +794,7 @@ LExit: extern "C" HRESULT ElevationMsiCommitTransaction( __in HANDLE hPipe, - __in LPCWSTR wzName + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary ) { HRESULT hr = S_OK; @@ -797,9 +803,12 @@ extern "C" HRESULT ElevationMsiCommitTransaction( DWORD dwResult = ERROR_SUCCESS; // serialize message data - hr = BuffWriteString(&pbData, &cbData, wzName); + hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczId); ExitOnFailure(hr, "Failed to write transaction name to message buffer."); + hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczLogPath); + ExitOnFailure(hr, "Failed to write transaction log path to message buffer."); + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION, pbData, cbData, NULL, NULL, &dwResult); ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION message to per-machine process."); @@ -811,7 +820,7 @@ LExit: extern "C" HRESULT ElevationMsiRollbackTransaction( __in HANDLE hPipe, - __in LPCWSTR wzName + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary ) { HRESULT hr = S_OK; @@ -820,9 +829,12 @@ extern "C" HRESULT ElevationMsiRollbackTransaction( DWORD dwResult = ERROR_SUCCESS; // serialize message data - hr = BuffWriteString(&pbData, &cbData, wzName); + hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczId); ExitOnFailure(hr, "Failed to write transaction name to message buffer."); + hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczLogPath); + ExitOnFailure(hr, "Failed to write transaction log path to message buffer."); + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION, pbData, cbData, NULL, NULL, &dwResult); ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION message to per-machine process."); @@ -1610,15 +1622,15 @@ static HRESULT ProcessElevatedChildMessage( switch (pMsg->dwMessage) { case BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION: - hrResult = OnMsiBeginTransaction((BYTE*)pMsg->pvData, pMsg->cbData); + hrResult = OnMsiBeginTransaction(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData); break; case BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION: - hrResult = OnMsiCommitTransaction((BYTE*)pMsg->pvData, pMsg->cbData); + hrResult = OnMsiCommitTransaction(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData); break; case BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION: - hrResult = OnMsiRollbackTransaction((BYTE*)pMsg->pvData, pMsg->cbData); + hrResult = OnMsiRollbackTransaction(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData); break; case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE: @@ -2824,64 +2836,115 @@ LExit: } static HRESULT OnMsiBeginTransaction( + __in BURN_PACKAGES* pPackages, __in BYTE* pbData, __in DWORD cbData ) { HRESULT hr = S_OK; SIZE_T iData = 0; - LPWSTR sczName = NULL; + LPWSTR sczId = NULL; + LPWSTR sczLogPath = NULL; + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; // Deserialize message data. - hr = BuffReadString(pbData, cbData, &iData, &sczName); - ExitOnFailure(hr, "Failed to read transaction name."); + hr = BuffReadString(pbData, cbData, &iData, &sczId); + ExitOnFailure(hr, "Failed to read rollback boundary id."); - hr = MsiEngineBeginTransaction(sczName); + hr = BuffReadString(pbData, cbData, &iData, &sczLogPath); + ExitOnFailure(hr, "Failed to read transaction log path."); + + PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary); + ExitOnFailure(hr, "Failed to find rollback boundary: %ls", sczId); + + pRollbackBoundary->sczLogPath = sczLogPath; + + hr = MsiEngineBeginTransaction(pRollbackBoundary); LExit: - ReleaseStr(sczName); + ReleaseStr(sczId); + ReleaseStr(sczLogPath); + + if (pRollbackBoundary) + { + pRollbackBoundary->sczLogPath = NULL; + } return hr; } static HRESULT OnMsiCommitTransaction( + __in BURN_PACKAGES* pPackages, __in BYTE* pbData, __in DWORD cbData ) { HRESULT hr = S_OK; SIZE_T iData = 0; - LPWSTR sczName = NULL; + LPWSTR sczId = NULL; + LPWSTR sczLogPath = NULL; + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; // Deserialize message data. - hr = BuffReadString(pbData, cbData, &iData, &sczName); - ExitOnFailure(hr, "Failed to read transaction name."); + hr = BuffReadString(pbData, cbData, &iData, &sczId); + ExitOnFailure(hr, "Failed to read rollback boundary id."); - hr = MsiEngineCommitTransaction(sczName); + hr = BuffReadString(pbData, cbData, &iData, &sczLogPath); + ExitOnFailure(hr, "Failed to read transaction log path."); + + PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary); + ExitOnFailure(hr, "Failed to find rollback boundary: %ls", sczId); + + pRollbackBoundary->sczLogPath = sczLogPath; + + hr = MsiEngineCommitTransaction(pRollbackBoundary); LExit: - ReleaseStr(sczName); + ReleaseStr(sczId); + ReleaseStr(sczLogPath); + + if (pRollbackBoundary) + { + pRollbackBoundary->sczLogPath = NULL; + } return hr; } static HRESULT OnMsiRollbackTransaction( + __in BURN_PACKAGES* pPackages, __in BYTE* pbData, __in DWORD cbData ) { HRESULT hr = S_OK; SIZE_T iData = 0; - LPWSTR sczName = NULL; + LPWSTR sczId = NULL; + LPWSTR sczLogPath = NULL; + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; // Deserialize message data. - hr = BuffReadString(pbData, cbData, &iData, &sczName); - ExitOnFailure(hr, "Failed to read transaction name."); + hr = BuffReadString(pbData, cbData, &iData, &sczId); + ExitOnFailure(hr, "Failed to read rollback boundary id."); - hr = MsiEngineRollbackTransaction(sczName); + hr = BuffReadString(pbData, cbData, &iData, &sczLogPath); + ExitOnFailure(hr, "Failed to read transaction log path."); + + PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary); + ExitOnFailure(hr, "Failed to find rollback boundary: %ls", sczId); + + pRollbackBoundary->sczLogPath = sczLogPath; + + hr = MsiEngineRollbackTransaction(pRollbackBoundary); LExit: - ReleaseStr(sczName); + ReleaseStr(sczId); + ReleaseStr(sczLogPath); + + if (pRollbackBoundary) + { + pRollbackBoundary->sczLogPath = NULL; + } return hr; } diff --git a/src/engine/elevation.h b/src/engine/elevation.h index e254dea5..9ce8cef9 100644 --- a/src/engine/elevation.h +++ b/src/engine/elevation.h @@ -156,15 +156,15 @@ HRESULT ElevationChildResumeAutomaticUpdates(); HRESULT ElevationMsiBeginTransaction( __in HANDLE hPipe, - __in LPCWSTR wzName + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary ); HRESULT ElevationMsiCommitTransaction( __in HANDLE hPipe, - __in LPCWSTR wzName + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary ); HRESULT ElevationMsiRollbackTransaction( __in HANDLE hPipe, - __in LPCWSTR wzName + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary ); #ifdef __cplusplus diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index f0aa784e..6c5b760b 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -1016,40 +1016,41 @@ LExit: } extern "C" HRESULT MsiEngineBeginTransaction( - __in LPCWSTR wzName + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary ) { HRESULT hr = S_OK; - UINT uResult = ERROR_SUCCESS; MSIHANDLE hTransactionHandle = NULL; HANDLE hChangeOfOwnerEvent = NULL; - LogId(REPORT_STANDARD, MSG_MSI_TRANSACTION_BEGIN, wzName); + LogId(REPORT_STANDARD, MSG_MSI_TRANSACTION_BEGIN, pRollbackBoundary->sczId); - uResult = ::MsiBeginTransaction(wzName, 0, &hTransactionHandle, &hChangeOfOwnerEvent); + hr = WiuBeginTransaction(pRollbackBoundary->sczId, 0, &hTransactionHandle, &hChangeOfOwnerEvent, WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE, pRollbackBoundary->sczLogPath); - if (ERROR_ROLLBACK_DISABLED == uResult) + if (HRESULT_FROM_WIN32(ERROR_ROLLBACK_DISABLED) == hr) { LogId(REPORT_ERROR, MSG_MSI_TRANSACTIONS_DISABLED); } - ExitOnWin32Error(uResult, hr, "Failed to begin an MSI transaction"); + ExitOnFailure(hr, "Failed to begin an MSI transaction"); LExit: + ReleaseMsi(hTransactionHandle); + ReleaseHandle(hChangeOfOwnerEvent); + return hr; } extern "C" HRESULT MsiEngineCommitTransaction( - __in LPCWSTR wzName + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary ) { HRESULT hr = S_OK; - UINT uResult = ERROR_SUCCESS; - LogId(REPORT_STANDARD, MSG_MSI_TRANSACTION_COMMIT, wzName); + LogId(REPORT_STANDARD, MSG_MSI_TRANSACTION_COMMIT, pRollbackBoundary->sczId); - uResult = ::MsiEndTransaction(MSITRANSACTIONSTATE_COMMIT); - ExitOnWin32Error(uResult, hr, "Failed to commit the MSI transaction"); + hr = WiuEndTransaction(MSITRANSACTIONSTATE_COMMIT, WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE, pRollbackBoundary->sczLogPath); + ExitOnFailure(hr, "Failed to commit the MSI transaction"); LExit: @@ -1057,16 +1058,15 @@ LExit: } extern "C" HRESULT MsiEngineRollbackTransaction( - __in LPCWSTR wzName + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary ) { HRESULT hr = S_OK; - UINT uResult = ERROR_SUCCESS; - LogId(REPORT_WARNING, MSG_MSI_TRANSACTION_ROLLBACK, wzName); + LogId(REPORT_WARNING, MSG_MSI_TRANSACTION_ROLLBACK, pRollbackBoundary->sczId); - uResult = ::MsiEndTransaction(MSITRANSACTIONSTATE_ROLLBACK); - ExitOnWin32Error(uResult, hr, "Failed to rollback the MSI transaction"); + hr = WiuEndTransaction(MSITRANSACTIONSTATE_ROLLBACK, WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE, pRollbackBoundary->sczLogPath); + ExitOnFailure(hr, "Failed to rollback the MSI transaction"); LExit: diff --git a/src/engine/msiengine.h b/src/engine/msiengine.h index e2dc5e82..99f97413 100644 --- a/src/engine/msiengine.h +++ b/src/engine/msiengine.h @@ -54,13 +54,13 @@ HRESULT MsiEnginePlanAddPackage( __in BOOL fPlanPackageCacheRollback ); HRESULT MsiEngineBeginTransaction( - __in LPCWSTR wzName + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary ); HRESULT MsiEngineCommitTransaction( - __in LPCWSTR wzName + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary ); HRESULT MsiEngineRollbackTransaction( - __in LPCWSTR wzName + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary ); HRESULT MsiEngineExecutePackage( __in_opt HWND hwndParent, diff --git a/src/engine/package.cpp b/src/engine/package.cpp index 115866f3..124b356b 100644 --- a/src/engine/package.cpp +++ b/src/engine/package.cpp @@ -377,6 +377,7 @@ extern "C" void PackagesUninitialize( for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i) { ReleaseStr(pPackages->rgRollbackBoundaries[i].sczId); + ReleaseStr(pPackages->rgRollbackBoundaries[i].sczLogPath); } MemFree(pPackages->rgRollbackBoundaries); } @@ -511,6 +512,32 @@ LExit: return hr; } +extern "C" HRESULT PackageFindRollbackBoundaryById( + __in BURN_PACKAGES* pPackages, + __in_z LPCWSTR wzId, + __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary + ) +{ + HRESULT hr = S_OK; + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; + + for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i) + { + pRollbackBoundary = &pPackages->rgRollbackBoundaries[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pRollbackBoundary->sczId, -1, wzId, -1)) + { + *ppRollbackBoundary = pRollbackBoundary; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + // internal function declarations diff --git a/src/engine/package.h b/src/engine/package.h index 3a95852e..42f1febe 100644 --- a/src/engine/package.h +++ b/src/engine/package.h @@ -214,6 +214,7 @@ typedef struct _BURN_ROLLBACK_BOUNDARY BOOL fVital; BOOL fTransaction; BOOL fActiveTransaction; // only valid during Apply. + LPWSTR sczLogPath; } BURN_ROLLBACK_BOUNDARY; typedef struct _BURN_PATCH_TARGETCODE @@ -386,6 +387,11 @@ HRESULT PackageGetProperty( __in_z LPCWSTR wzProperty, __out_z_opt LPWSTR* psczValue ); +HRESULT PackageFindRollbackBoundaryById( + __in BURN_PACKAGES* pPackages, + __in_z LPCWSTR wzId, + __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary + ); #if defined(__cplusplus) diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index a4b8d0c1..65da4ab3 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -1946,6 +1946,7 @@ static void ResetPlannedRollbackBoundaryState( ) { pRollbackBoundary->fActiveTransaction = FALSE; + ReleaseNullStr(pRollbackBoundary->sczLogPath); } static HRESULT GetActionDefaultRequestState( diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index ad1529ea..5a225651 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -2117,16 +2117,6 @@ extern "C" int UserExperienceCheckExecuteResult( return nResult; } -extern "C" HRESULT UserExperienceInterpretResult( - __in BURN_USER_EXPERIENCE* /*pUserExperience*/, - __in DWORD dwAllowedResults, - __in int nResult - ) -{ - int nFilteredResult = FilterResult(dwAllowedResults, nResult); - return IDOK == nFilteredResult || IDNOACTION == nFilteredResult ? S_OK : IDCANCEL == nFilteredResult || IDABORT == nFilteredResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); -} - extern "C" HRESULT UserExperienceInterpretExecuteResult( __in BURN_USER_EXPERIENCE* pUserExperience, __in BOOL fRollback, diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index bac79e33..cef9d769 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -469,11 +469,6 @@ BAAPI UserExperienceOnUnregisterComplete( __in BURN_USER_EXPERIENCE* pUserExperience, __in HRESULT hrStatus ); -HRESULT UserExperienceInterpretResult( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in DWORD dwAllowedResults, - __in int nResult - ); int UserExperienceCheckExecuteResult( __in BURN_USER_EXPERIENCE* pUserExperience, __in BOOL fRollback, -- cgit v1.2.3-55-g6feb From 65b905667b8567cd9b40c220eb18bd729276e7a6 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Sun, 28 Mar 2021 22:17:56 -0400 Subject: Include bundle reboot-pending in RebootPending variable. Fixes https://github.com/wixtoolset/issues/issues/5332 --- src/engine/core.h | 1 + src/engine/registration.cpp | 63 ++++++++++++++--- src/engine/variable.cpp | 43 ----------- src/test/BurnUnitTest/RegistrationTest.cpp | 110 +++++++++++++++++++++++++++++ 4 files changed, 163 insertions(+), 54 deletions(-) diff --git a/src/engine/core.h b/src/engine/core.h index 75a61614..b4e0e0d3 100644 --- a/src/engine/core.h +++ b/src/engine/core.h @@ -46,6 +46,7 @@ const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_FOLDER = L"WixBundleSourceProcessFolder const LPCWSTR BURN_BUNDLE_TAG = L"WixBundleTag"; const LPCWSTR BURN_BUNDLE_UILEVEL = L"WixBundleUILevel"; const LPCWSTR BURN_BUNDLE_VERSION = L"WixBundleVersion"; +const LPCWSTR BURN_REBOOT_PENDING = L"RebootPending"; // The following constants must stay in sync with src\wix\Binder.cs const LPCWSTR BURN_BUNDLE_NAME = L"WixBundleName"; diff --git a/src/engine/registration.cpp b/src/engine/registration.cpp index dc4b88bf..fc5ae627 100644 --- a/src/engine/registration.cpp +++ b/src/engine/registration.cpp @@ -92,6 +92,10 @@ static HRESULT UpdateBundleNameRegistration( __in BURN_VARIABLES* pVariables, __in HKEY hkRegistration ); +static BOOL IsWuRebootPending(); +static BOOL IsBundleRebootPending( + __in BURN_REGISTRATION* pRegistration +); // function definitions @@ -443,7 +447,10 @@ extern "C" HRESULT RegistrationSetVariables( ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable."); hr = VariableSetVersion(pVariables, BURN_BUNDLE_VERSION, pRegistration->pVersion, TRUE); - ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable."); + ExitOnFailure(hr, "Failed to overwrite the bundle version built-in variable."); + + hr = VariableSetNumeric(pVariables, BURN_REBOOT_PENDING, IsBundleRebootPending(pRegistration) || IsWuRebootPending(), TRUE); + ExitOnFailure(hr, "Failed to overwrite the bundle reboot-pending built-in variable."); LExit: ReleaseStr(sczBundleManufacturer); @@ -491,17 +498,10 @@ extern "C" HRESULT RegistrationDetectResumeType( ) { HRESULT hr = S_OK; - LPWSTR sczRebootRequiredKey = NULL; - HKEY hkRebootRequired = NULL; HKEY hkRegistration = NULL; DWORD dwResume = 0; - // Check to see if a restart is pending for this bundle. - hr = StrAllocFormatted(&sczRebootRequiredKey, REGISTRY_REBOOT_PENDING_FORMAT, pRegistration->sczRegistrationKey); - ExitOnFailure(hr, "Failed to format pending restart registry key to read."); - - hr = RegOpen(pRegistration->hkRoot, sczRebootRequiredKey, KEY_QUERY_VALUE, &hkRebootRequired); - if (SUCCEEDED(hr)) + if (IsBundleRebootPending(pRegistration)) { *pResumeType = BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING; ExitFunction1(hr = S_OK); @@ -554,8 +554,6 @@ extern "C" HRESULT RegistrationDetectResumeType( LExit: ReleaseRegKey(hkRegistration); - ReleaseRegKey(hkRebootRequired); - ReleaseStr(sczRebootRequiredKey); return hr; } @@ -1591,3 +1589,46 @@ LExit: return hr; } + +static BOOL IsWuRebootPending() +{ + HRESULT hr = S_OK; + BOOL fRebootPending = FALSE; + + // Do a best effort to ask WU if a reboot is required. If anything goes + // wrong then let's pretend a reboot is not required. + hr = ::CoInitialize(NULL); + if (SUCCEEDED(hr) || RPC_E_CHANGED_MODE == hr) + { + hr = WuaRestartRequired(&fRebootPending); + if (FAILED(hr)) + { + fRebootPending = FALSE; + } + + ::CoUninitialize(); + } + + return fRebootPending; +} + +static BOOL IsBundleRebootPending(BURN_REGISTRATION* pRegistration) +{ + HRESULT hr = S_OK; + LPWSTR sczRebootRequiredKey = NULL; + HKEY hkRebootRequired = NULL; + BOOL fBundleRebootPending = FALSE; + + // Check to see if a restart is pending for this bundle. + hr = StrAllocFormatted(&sczRebootRequiredKey, REGISTRY_REBOOT_PENDING_FORMAT, pRegistration->sczRegistrationKey); + ExitOnFailure(hr, "Failed to format pending restart registry key to read."); + + hr = RegOpen(pRegistration->hkRoot, sczRebootRequiredKey, KEY_QUERY_VALUE, &hkRebootRequired); + fBundleRebootPending = SUCCEEDED(hr); + +LExit: + ReleaseStr(sczRebootRequiredKey); + ReleaseRegKey(hkRebootRequired); + + return fBundleRebootPending; +} diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp index fed23151..d0c67504 100644 --- a/src/engine/variable.cpp +++ b/src/engine/variable.cpp @@ -133,10 +133,6 @@ static HRESULT InitializeVariablePrivileged( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); -static HRESULT InitializeVariableRebootPending( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); static HRESULT InitializeSystemLanguageID( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue @@ -247,7 +243,6 @@ extern "C" HRESULT VariableInitialize( #endif {L"ProgramFiles6432Folder", InitializeVariable6432Folder, CSIDL_PROGRAM_FILES}, {L"ProgramMenuFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAMS}, - {L"RebootPending", InitializeVariableRebootPending, 0}, {L"SendToFolder", InitializeVariableCsidlFolder, CSIDL_SENDTO}, {L"ServicePackLevel", InitializeVariableVersionNT, OS_INFO_VARIABLE_ServicePackLevel}, {L"StartMenuFolder", InitializeVariableCsidlFolder, CSIDL_STARTMENU}, @@ -2031,44 +2026,6 @@ LExit: return hr; } -static HRESULT InitializeVariableRebootPending( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ) -{ - UNREFERENCED_PARAMETER(dwpData); - - HRESULT hr = S_OK; - BOOL fRebootPending = FALSE; - BOOL fComInitialized = FALSE; - - // Do a best effort to ask WU if a reboot is required. If anything goes - // wrong then let's pretend a reboot is not required. - hr = ::CoInitialize(NULL); - if (SUCCEEDED(hr) || RPC_E_CHANGED_MODE == hr) - { - fComInitialized = TRUE; - - hr = WuaRestartRequired(&fRebootPending); - if (FAILED(hr)) - { - fRebootPending = FALSE; - hr = S_OK; - } - } - - hr = BVariantSetNumeric(pValue, fRebootPending); - ExitOnFailure(hr, "Failed to set reboot pending variant value."); - -LExit: - if (fComInitialized) - { - ::CoUninitialize(); - } - - return hr; -} - static HRESULT InitializeSystemLanguageID( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue diff --git a/src/test/BurnUnitTest/RegistrationTest.cpp b/src/test/BurnUnitTest/RegistrationTest.cpp index 1687385b..883a1258 100644 --- a/src/test/BurnUnitTest/RegistrationTest.cpp +++ b/src/test/BurnUnitTest/RegistrationTest.cpp @@ -73,6 +73,7 @@ namespace Bootstrapper BURN_LOGGING logging = { }; BURN_PACKAGES packages = { }; String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); + try { // set mock API's @@ -260,6 +261,115 @@ namespace Bootstrapper } } + [Fact] + void RegisterVariablesTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + LPWSTR sczCurrentProcess = NULL; + BURN_VARIABLES variables = { }; + BURN_USER_EXPERIENCE userExperience = { }; + BOOTSTRAPPER_COMMAND command = { }; + BURN_REGISTRATION registration = { }; + BURN_LOGGING logging = { }; + BURN_PACKAGES packages = { }; + String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); + try + { + // set mock API's + RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); + + Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH)); + + logging.sczPath = L"BurnUnitTest.txt"; + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + hr = UserExperienceParseFromXml(&userExperience, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse UX from XML."); + + hr = RegistrationParseFromXml(®istration, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse registration from XML."); + + hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging); + TestThrowOnFailure(hr, L"Failed to set registration resume command."); + + hr = PathForCurrentProcess(&sczCurrentProcess, NULL); + TestThrowOnFailure(hr, L"Failed to get current process path."); + + // + // install + // + + // write registration + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, &userExperience, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + // verify that registration was created + Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // complete registration + hr = RegistrationSessionEnd(®istration, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_REQUIRED, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); + TestThrowOnFailure(hr, L"Failed to unregister bundle."); + + // verify that registration variables were updated + registration.fInstalled = TRUE; + + hr = RegistrationSetVariables(®istration, &variables); + TestThrowOnFailure(hr, L"Failed to set registration variables."); + + Assert::Equal(1ll, VariableGetNumericHelper(&variables, BURN_BUNDLE_INSTALLED)); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, BURN_REBOOT_PENDING)); + Assert::Equal(gcnew String(L"foo"), VariableGetStringHelper(&variables, BURN_BUNDLE_TAG)); + Assert::Equal(gcnew String(L"bar"), VariableGetStringHelper(&variables, BURN_BUNDLE_PROVIDER_KEY)); + Assert::Equal(gcnew String(L"1.0.0.0"), VariableGetVersionHelper(&variables, BURN_BUNDLE_VERSION)); + + // + // uninstall + // + + // delete registration + hr = RegistrationSessionEnd(®istration, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + TestThrowOnFailure(hr, L"Failed to unregister bundle."); + + // verify that registration was removed + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + } + finally + { + ReleaseStr(sczCurrentProcess); + ReleaseObject(pixeBundle); + UserExperienceUninitialize(&userExperience); + RegistrationUninitialize(®istration); + VariablesUninitialize(&variables); + + Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH)); + if (Directory::Exists(cacheDirectory)) + { + Directory::Delete(cacheDirectory, true); + } + + RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + } + } + [Fact] void RegisterArpFullTest() { -- cgit v1.2.3-55-g6feb From 5cbe372f0a512babc5db7f519fc407eb40606346 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 30 Mar 2021 19:04:12 -0500 Subject: Rename cache types in the manifest to remove, keep, force. Contributes to #5125 --- src/engine/engine.vcxproj | 4 ++-- src/engine/package.cpp | 6 +++--- src/engine/packages.config | 2 +- src/stub/packages.config | 2 +- src/stub/stub.vcxproj | 4 ++-- src/test/BurnUnitTest/BurnUnitTest.vcxproj | 4 ++-- .../TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml | 2 +- .../TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml | 2 +- .../BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml | 2 +- src/test/BurnUnitTest/packages.config | 2 +- 10 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index 263edf12..e418e6f4 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -1,7 +1,7 @@ - + Debug @@ -165,7 +165,7 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + \ No newline at end of file diff --git a/src/engine/package.cpp b/src/engine/package.cpp index 124b356b..6ff59fe4 100644 --- a/src/engine/package.cpp +++ b/src/engine/package.cpp @@ -116,15 +116,15 @@ extern "C" HRESULT PackagesParseFromXml( hr = XmlGetAttributeEx(pixnNode, L"Cache", &scz); if (SUCCEEDED(hr)) { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"no", -1)) + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"remove", -1)) { pPackage->cacheType = BURN_CACHE_TYPE_NO; } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"yes", -1)) + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"keep", -1)) { pPackage->cacheType = BURN_CACHE_TYPE_YES; } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"always", -1)) + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"force", -1)) { pPackage->cacheType = BURN_CACHE_TYPE_ALWAYS; } diff --git a/src/engine/packages.config b/src/engine/packages.config index 56c62f29..84e3078b 100644 --- a/src/engine/packages.config +++ b/src/engine/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/stub/packages.config b/src/stub/packages.config index 7d0212ac..44bf19aa 100644 --- a/src/stub/packages.config +++ b/src/stub/packages.config @@ -4,5 +4,5 @@ - + \ No newline at end of file diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index f13d5ed9..f235a1fd 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -5,7 +5,7 @@ - + @@ -117,6 +117,6 @@ - + \ No newline at end of file diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index 2ca7219e..529ab727 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -4,7 +4,7 @@ - + Debug @@ -97,6 +97,6 @@ - + \ No newline at end of file diff --git a/src/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml b/src/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml index e5aeb515..65e3c63d 100644 --- a/src/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml +++ b/src/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml b/src/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml index 6ed7e01b..cca9a982 100644 --- a/src/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml +++ b/src/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml b/src/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml index 4b5cab6f..996976b2 100644 --- a/src/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml +++ b/src/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/test/BurnUnitTest/packages.config b/src/test/BurnUnitTest/packages.config index e232db0a..accce624 100644 --- a/src/test/BurnUnitTest/packages.config +++ b/src/test/BurnUnitTest/packages.config @@ -10,5 +10,5 @@ - + \ No newline at end of file -- cgit v1.2.3-55-g6feb From e9a4f673511dd06a8209f3e4037ad20f153d6caa Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 30 Mar 2021 19:38:33 -0500 Subject: Skip bundle dependent checking when ignoring dependencies includes ALL. Fixes #6391. --- src/engine/dependency.cpp | 14 +++++-- src/engine/plan.cpp | 95 ++++++++++++++++++++++++----------------------- src/engine/plan.h | 1 + src/engine/registration.h | 1 + 4 files changed, 62 insertions(+), 49 deletions(-) diff --git a/src/engine/dependency.cpp b/src/engine/dependency.cpp index 51aca239..1bd0c7d4 100644 --- a/src/engine/dependency.cpp +++ b/src/engine/dependency.cpp @@ -19,7 +19,8 @@ static HRESULT DetectPackageDependents( static HRESULT SplitIgnoreDependencies( __in_z LPCWSTR wzIgnoreDependencies, __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, - __inout LPUINT pcDependencies + __inout LPUINT pcDependencies, + __out BOOL* pfIgnoreAll ); static HRESULT JoinIgnoreDependencies( @@ -194,7 +195,7 @@ extern "C" HRESULT DependencyInitialize( // Add the list of dependencies to ignore. if (wzIgnoreDependencies) { - hr = SplitIgnoreDependencies(wzIgnoreDependencies, &pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies); + hr = SplitIgnoreDependencies(wzIgnoreDependencies, &pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies, &pRegistration->fIgnoreAllDependents); ExitOnFailure(hr, "Failed to split the list of dependencies to ignore."); } @@ -816,12 +817,14 @@ LExit: static HRESULT SplitIgnoreDependencies( __in_z LPCWSTR wzIgnoreDependencies, __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, - __inout LPUINT pcDependencies + __inout LPUINT pcDependencies, + __out BOOL* pfIgnoreAll ) { HRESULT hr = S_OK; LPWSTR wzContext = NULL; STRINGDICT_HANDLE sdIgnoreDependencies = NULL; + *pfIgnoreAll = FALSE; // Create a dictionary to hold unique dependencies. hr = DictCreateStringList(&sdIgnoreDependencies, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); @@ -842,6 +845,11 @@ static HRESULT SplitIgnoreDependencies( hr = DictAddKey(sdIgnoreDependencies, wzToken); ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzToken); + + if (!*pfIgnoreAll && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, L"ALL", -1, wzToken, -1)) + { + *pfIgnoreAll = TRUE; + } } } diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index 65da4ab3..c75c1753 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -588,8 +588,8 @@ extern "C" HRESULT PlanRegistration( STRINGDICT_HANDLE sdIgnoreDependents = NULL; pPlan->fCanAffectMachineState = TRUE; // register the bundle since we're modifying machine state. - pPlan->fDisallowRemoval = FALSE; // by default the bundle can be planned to be removed + pPlan->fIgnoreAllDependents = pRegistration->fIgnoreAllDependents; // Ensure the bundle is cached if not running from the cache. if (!CacheBundleRunningFromCache()) @@ -633,68 +633,71 @@ extern "C" HRESULT PlanRegistration( ExitOnFailure(hr, "Failed to add self-dependent to ignore dependents."); } - // If we are not doing an upgrade, we check to see if there are still dependents on us and if so we skip planning. - // However, when being upgraded, we always execute our uninstall because a newer version of us is probably - // already on the machine and we need to clean up the stuff specific to this bundle. - if (BOOTSTRAPPER_RELATION_UPGRADE != relationType) + if (!pPlan->fIgnoreAllDependents) { - // If there were other dependencies to ignore, add them. - for (DWORD iDependency = 0; iDependency < pRegistration->cIgnoredDependencies; ++iDependency) + // If we are not doing an upgrade, we check to see if there are still dependents on us and if so we skip planning. + // However, when being upgraded, we always execute our uninstall because a newer version of us is probably + // already on the machine and we need to clean up the stuff specific to this bundle. + if (BOOTSTRAPPER_RELATION_UPGRADE != relationType) { - DEPENDENCY* pDependency = pRegistration->rgIgnoredDependencies + iDependency; - - hr = DictKeyExists(sdIgnoreDependents, pDependency->sczKey); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to check the dictionary of ignored dependents."); - } - else + // If there were other dependencies to ignore, add them. + for (DWORD iDependency = 0; iDependency < pRegistration->cIgnoredDependencies; ++iDependency) { - hr = DictAddKey(sdIgnoreDependents, pDependency->sczKey); - ExitOnFailure(hr, "Failed to add dependent key to ignored dependents."); - } - } + DEPENDENCY* pDependency = pRegistration->rgIgnoredDependencies + iDependency; - // For addon or patch bundles, dependent related bundles should be ignored. This allows - // that addon or patch to be removed even though bundles it targets still are registered. - for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) - { - const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i; + hr = DictKeyExists(sdIgnoreDependents, pDependency->sczKey); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to check the dictionary of ignored dependents."); + } + else + { + hr = DictAddKey(sdIgnoreDependents, pDependency->sczKey); + ExitOnFailure(hr, "Failed to add dependent key to ignored dependents."); + } + } - if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType) + // For addon or patch bundles, dependent related bundles should be ignored. This allows + // that addon or patch to be removed even though bundles it targets still are registered. + for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) { - for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j) + const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i; + + if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType) { - const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j; + for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j; - hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, pProvider->sczKey); - ExitOnFailure(hr, "Failed to add dependent bundle provider key to ignore dependents."); + hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, pProvider->sczKey); + ExitOnFailure(hr, "Failed to add dependent bundle provider key to ignore dependents."); + } } } - } - - // If there are any (non-ignored and not-planned-to-be-removed) dependents left, skip planning. - for (DWORD iDependent = 0; iDependent < pRegistration->cDependents; ++iDependent) - { - DEPENDENCY* pDependent = pRegistration->rgDependents + iDependent; - hr = DictKeyExists(sdIgnoreDependents, pDependent->sczKey); - if (E_NOTFOUND == hr) + // If there are any (non-ignored and not-planned-to-be-removed) dependents left, skip planning. + for (DWORD iDependent = 0; iDependent < pRegistration->cDependents; ++iDependent) { - hr = S_OK; + DEPENDENCY* pDependent = pRegistration->rgDependents + iDependent; - // TODO: callback to the BA and let it have the option to ignore this dependent? - if (!pPlan->fDisallowRemoval) + hr = DictKeyExists(sdIgnoreDependents, pDependent->sczKey); + if (E_NOTFOUND == hr) { - pPlan->fDisallowRemoval = TRUE; // ensure the registration stays - *pfContinuePlanning = FALSE; // skip the rest of planning. + hr = S_OK; - LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS); - } + // TODO: callback to the BA and let it have the option to ignore this dependent? + if (!pPlan->fDisallowRemoval) + { + pPlan->fDisallowRemoval = TRUE; // ensure the registration stays + *pfContinuePlanning = FALSE; // skip the rest of planning. - LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_DEPENDENT, pDependent->sczKey, LoggingStringOrUnknownIfNull(pDependent->sczName)); + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS); + } + + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_DEPENDENT, pDependent->sczKey, LoggingStringOrUnknownIfNull(pDependent->sczName)); + } + ExitOnFailure(hr, "Failed to check for remaining dependents during planning."); } - ExitOnFailure(hr, "Failed to check for remaining dependents during planning."); } } } diff --git a/src/engine/plan.h b/src/engine/plan.h index e72186c7..77f82e1f 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -314,6 +314,7 @@ typedef struct _BURN_PLAN BOOL fDisallowRemoval; BOOL fDisableRollback; BOOL fAffectedMachineState; + BOOL fIgnoreAllDependents; DWORD64 qwCacheSizeTotal; diff --git a/src/engine/registration.h b/src/engine/registration.h index e0418fa3..39fee65f 100644 --- a/src/engine/registration.h +++ b/src/engine/registration.h @@ -146,6 +146,7 @@ typedef struct _BURN_REGISTRATION UINT cIgnoredDependencies; // Only valid after detect. DEPENDENCY* rgDependents; // Only valid after detect. UINT cDependents; // Only valid after detect. + BOOL fIgnoreAllDependents; // Only valid after detect. LPCWSTR wzSelfDependent; // Only valid after detect. BOOL fSelfRegisteredAsDependent; // Only valid after detect. BOOL fParentRegisteredAsDependent; // Only valid after detect. -- cgit v1.2.3-55-g6feb From ad5aeb25c459196938cc88c404406bbbe1df9061 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Mon, 12 Apr 2021 14:17:41 -0700 Subject: Port support for ISO/IEC 19770-2:2105 (aka SWID Tags-2) Fixes wixtoolset/issues#8380 --- src/engine/apply.cpp | 2 +- src/engine/elevation.cpp | 6 ++-- src/engine/engine.vcxproj | 4 +-- src/engine/packages.config | 2 +- src/engine/registration.cpp | 56 +++++++++++++++++------------- src/engine/registration.h | 2 ++ src/stub/packages.config | 2 +- src/stub/stub.vcxproj | 4 +-- src/test/BurnUnitTest/BurnUnitTest.vcxproj | 4 +-- src/test/BurnUnitTest/RegistrationTest.cpp | 22 ++++++------ src/test/BurnUnitTest/VariableTest.cpp | 6 ++-- src/test/BurnUnitTest/packages.config | 2 +- 12 files changed, 61 insertions(+), 51 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 77080c76..dc40c50d 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -435,7 +435,7 @@ extern "C" HRESULT ApplyUnregister( } else { - hr = RegistrationSessionEnd(&pEngineState->registration, &pEngineState->packages, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction); + hr = RegistrationSessionEnd(&pEngineState->registration, &pEngineState->variables, &pEngineState->packages, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction); ExitOnFailure(hr, "Failed to end session in per-user process."); } diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp index 1737bf5b..82d5a9b1 100644 --- a/src/engine/elevation.cpp +++ b/src/engine/elevation.cpp @@ -156,6 +156,7 @@ static HRESULT OnSessionResume( static HRESULT OnSessionEnd( __in BURN_PACKAGES* pPackages, __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, __in BYTE* pbData, __in DWORD cbData ); @@ -1650,7 +1651,7 @@ static HRESULT ProcessElevatedChildMessage( break; case BURN_ELEVATION_MESSAGE_TYPE_SESSION_END: - hrResult = OnSessionEnd(pContext->pPackages, pContext->pRegistration, (BYTE*)pMsg->pvData, pMsg->cbData); + hrResult = OnSessionEnd(pContext->pPackages, pContext->pRegistration, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); break; case BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE: @@ -1969,6 +1970,7 @@ LExit: static HRESULT OnSessionEnd( __in BURN_PACKAGES* pPackages, __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, __in BYTE* pbData, __in DWORD cbData ) @@ -1990,7 +1992,7 @@ static HRESULT OnSessionEnd( ExitOnFailure(hr, "Failed to read dependency registration action."); // suspend session in per-machine process - hr = RegistrationSessionEnd(pRegistration, pPackages, (BURN_RESUME_MODE)dwResumeMode, (BOOTSTRAPPER_APPLY_RESTART)dwRestart, (BURN_DEPENDENCY_REGISTRATION_ACTION)dwDependencyRegistrationAction); + hr = RegistrationSessionEnd(pRegistration, pVariables, pPackages, (BURN_RESUME_MODE)dwResumeMode, (BOOTSTRAPPER_APPLY_RESTART)dwRestart, (BURN_DEPENDENCY_REGISTRATION_ACTION)dwDependencyRegistrationAction); ExitOnFailure(hr, "Failed to suspend registration session."); LExit: diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index e418e6f4..666b3f37 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -1,7 +1,7 @@ - + Debug @@ -165,7 +165,7 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + \ No newline at end of file diff --git a/src/engine/packages.config b/src/engine/packages.config index 84e3078b..1d09f0a7 100644 --- a/src/engine/packages.config +++ b/src/engine/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/engine/registration.cpp b/src/engine/registration.cpp index fc5ae627..b8a2283f 100644 --- a/src/engine/registration.cpp +++ b/src/engine/registration.cpp @@ -31,6 +31,7 @@ const LPCWSTR REGISTRY_BUNDLE_UNINSTALL_STRING = L"UninstallString"; const LPCWSTR REGISTRY_BUNDLE_RESUME_COMMAND_LINE = L"BundleResumeCommandLine"; const LPCWSTR REGISTRY_BUNDLE_VERSION_MAJOR = L"VersionMajor"; const LPCWSTR REGISTRY_BUNDLE_VERSION_MINOR = L"VersionMinor"; +const LPCWSTR SWIDTAG_FOLDER = L"swidtag"; // internal function declarations @@ -67,11 +68,11 @@ static HRESULT FormatUpdateRegistrationKey( __out_z LPWSTR* psczKey ); static HRESULT WriteSoftwareTags( - __in BOOL fPerMachine, + __in BURN_VARIABLES* pVariables, __in BURN_SOFTWARE_TAGS* pSoftwareTags ); static HRESULT RemoveSoftwareTags( - __in BOOL fPerMachine, + __in BURN_VARIABLES* pVariables, __in BURN_SOFTWARE_TAGS* pSoftwareTags ); static HRESULT WriteUpdateRegistration( @@ -392,6 +393,7 @@ extern "C" void RegistrationUninitialize( { ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczFilename); ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczRegid); + ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczPath); ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczTag); } @@ -778,7 +780,7 @@ extern "C" HRESULT RegistrationSessionBegin( if (pRegistration->softwareTags.cSoftwareTags) { - hr = WriteSoftwareTags(pRegistration->fPerMachine, &pRegistration->softwareTags); + hr = WriteSoftwareTags(pVariables, &pRegistration->softwareTags); ExitOnFailure(hr, "Failed to write software tags."); } @@ -867,6 +869,7 @@ LExit: *******************************************************************/ extern "C" HRESULT RegistrationSessionEnd( __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, __in BURN_PACKAGES* pPackages, __in BURN_RESUME_MODE resumeMode, __in BOOTSTRAPPER_APPLY_RESTART restart, @@ -920,7 +923,7 @@ extern "C" HRESULT RegistrationSessionEnd( RemoveUpdateRegistration(pRegistration); } - RemoveSoftwareTags(pRegistration->fPerMachine, &pRegistration->softwareTags); + RemoveSoftwareTags(pVariables, &pRegistration->softwareTags); // Delete registration key. hr = RegDelete(pRegistration->hkRoot, pRegistration->sczRegistrationKey, REG_KEY_DEFAULT, FALSE); @@ -1063,6 +1066,9 @@ static HRESULT ParseSoftwareTagsFromXml( hr = XmlGetAttributeEx(pixnNode, L"Regid", &pSoftwareTag->sczRegid); ExitOnFailure(hr, "Failed to get @Regid."); + hr = XmlGetAttributeEx(pixnNode, L"Path", &pSoftwareTag->sczPath); + ExitOnFailure(hr, "Failed to get @Path."); + hr = XmlGetText(pixnNode, &bstrTagXml); ExitOnFailure(hr, "Failed to get SoftwareTag text."); @@ -1370,30 +1376,30 @@ LExit: } static HRESULT WriteSoftwareTags( - __in BOOL fPerMachine, + __in BURN_VARIABLES* pVariables, __in BURN_SOFTWARE_TAGS* pSoftwareTags ) { HRESULT hr = S_OK; LPWSTR sczRootFolder = NULL; - LPWSTR sczRegidFolder = NULL; + LPWSTR sczTagFolder = NULL; LPWSTR sczPath = NULL; - hr = PathGetKnownFolder(fPerMachine ? CSIDL_COMMON_APPDATA : CSIDL_LOCAL_APPDATA, &sczRootFolder); - ExitOnFailure(hr, "Failed to find local %hs appdata directory.", fPerMachine ? "per-machine" : "per-user"); - for (DWORD iTag = 0; iTag < pSoftwareTags->cSoftwareTags; ++iTag) { BURN_SOFTWARE_TAG* pSoftwareTag = pSoftwareTags->rgSoftwareTags + iTag; - hr = PathConcat(sczRootFolder, pSoftwareTag->sczRegid, &sczRegidFolder); - ExitOnFailure(hr, "Failed to allocate regid folder path."); + hr = VariableFormatString(pVariables, pSoftwareTag->sczPath, &sczRootFolder, NULL); + ExitOnFailure(hr, "Failed to format tag folder path."); - hr = PathConcat(sczRegidFolder, pSoftwareTag->sczFilename, &sczPath); + hr = PathConcat(sczRootFolder, SWIDTAG_FOLDER, &sczTagFolder); ExitOnFailure(hr, "Failed to allocate regid folder path."); - hr = DirEnsureExists(sczRegidFolder, NULL); - ExitOnFailure(hr, "Failed to create regid folder: %ls", sczRegidFolder); + hr = PathConcat(sczTagFolder, pSoftwareTag->sczFilename, &sczPath); + ExitOnFailure(hr, "Failed to allocate regid file path."); + + hr = DirEnsureExists(sczTagFolder, NULL); + ExitOnFailure(hr, "Failed to create regid folder: %ls", sczTagFolder); hr = FileWrite(sczPath, FILE_ATTRIBUTE_NORMAL, reinterpret_cast(pSoftwareTag->sczTag), lstrlenA(pSoftwareTag->sczTag), NULL); ExitOnFailure(hr, "Failed to write tag xml to file: %ls", sczPath); @@ -1401,44 +1407,44 @@ static HRESULT WriteSoftwareTags( LExit: ReleaseStr(sczPath); - ReleaseStr(sczRegidFolder); + ReleaseStr(sczTagFolder); ReleaseStr(sczRootFolder); return hr; } static HRESULT RemoveSoftwareTags( - __in BOOL fPerMachine, + __in BURN_VARIABLES* pVariables, __in BURN_SOFTWARE_TAGS* pSoftwareTags ) { HRESULT hr = S_OK; LPWSTR sczRootFolder = NULL; - LPWSTR sczRegidFolder = NULL; + LPWSTR sczTagFolder = NULL; LPWSTR sczPath = NULL; - hr = PathGetKnownFolder(fPerMachine ? CSIDL_COMMON_APPDATA : CSIDL_LOCAL_APPDATA, &sczRootFolder); - ExitOnFailure(hr, "Failed to find local %hs appdata directory.", fPerMachine ? "per-machine" : "per-user"); - for (DWORD iTag = 0; iTag < pSoftwareTags->cSoftwareTags; ++iTag) { BURN_SOFTWARE_TAG* pSoftwareTag = pSoftwareTags->rgSoftwareTags + iTag; - hr = PathConcat(sczRootFolder, pSoftwareTag->sczRegid, &sczRegidFolder); - ExitOnFailure(hr, "Failed to allocate regid folder path."); + hr = VariableFormatString(pVariables, pSoftwareTag->sczPath, &sczRootFolder, NULL); + ExitOnFailure(hr, "Failed to format tag folder path."); - hr = PathConcat(sczRegidFolder, pSoftwareTag->sczFilename, &sczPath); + hr = PathConcat(sczRootFolder, SWIDTAG_FOLDER, &sczTagFolder); ExitOnFailure(hr, "Failed to allocate regid folder path."); + hr = PathConcat(sczTagFolder, pSoftwareTag->sczFilename, &sczPath); + ExitOnFailure(hr, "Failed to allocate regid file path."); + // Best effort to delete the software tag file and the regid folder. FileEnsureDelete(sczPath); - ::RemoveDirectoryW(sczRegidFolder); + DirDeleteEmptyDirectoriesToRoot(sczTagFolder, 0); } LExit: ReleaseStr(sczPath); - ReleaseStr(sczRegidFolder); + ReleaseStr(sczTagFolder); ReleaseStr(sczRootFolder); return hr; diff --git a/src/engine/registration.h b/src/engine/registration.h index 39fee65f..ed0641eb 100644 --- a/src/engine/registration.h +++ b/src/engine/registration.h @@ -77,6 +77,7 @@ typedef struct _BURN_SOFTWARE_TAG { LPWSTR sczFilename; LPWSTR sczRegid; + LPWSTR sczPath; LPSTR sczTag; } BURN_SOFTWARE_TAG; @@ -198,6 +199,7 @@ HRESULT RegistrationSessionResume( ); HRESULT RegistrationSessionEnd( __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, __in BURN_PACKAGES* pPackages, __in BURN_RESUME_MODE resumeMode, __in BOOTSTRAPPER_APPLY_RESTART restart, diff --git a/src/stub/packages.config b/src/stub/packages.config index 44bf19aa..eb05a40b 100644 --- a/src/stub/packages.config +++ b/src/stub/packages.config @@ -4,5 +4,5 @@ - + \ No newline at end of file diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index f235a1fd..ae9c21df 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -5,7 +5,7 @@ - + @@ -117,6 +117,6 @@ - + \ No newline at end of file diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index 529ab727..b57ebb16 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -4,7 +4,7 @@ - + Debug @@ -97,6 +97,6 @@ - + \ No newline at end of file diff --git a/src/test/BurnUnitTest/RegistrationTest.cpp b/src/test/BurnUnitTest/RegistrationTest.cpp index 883a1258..2cb66c3f 100644 --- a/src/test/BurnUnitTest/RegistrationTest.cpp +++ b/src/test/BurnUnitTest/RegistrationTest.cpp @@ -123,7 +123,7 @@ namespace Bootstrapper Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)(Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr))); // end session - hr = RegistrationSessionEnd(®istration, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); TestThrowOnFailure(hr, L"Failed to unregister bundle."); // verify that registration was removed @@ -213,7 +213,7 @@ namespace Bootstrapper Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); // complete registration - hr = RegistrationSessionEnd(®istration, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); TestThrowOnFailure(hr, L"Failed to unregister bundle."); // verify that registration was updated @@ -235,7 +235,7 @@ namespace Bootstrapper Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); // delete registration - hr = RegistrationSessionEnd(®istration, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); TestThrowOnFailure(hr, L"Failed to unregister bundle."); // verify that registration was removed @@ -324,7 +324,7 @@ namespace Bootstrapper Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); // complete registration - hr = RegistrationSessionEnd(®istration, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_REQUIRED, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_REQUIRED, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); TestThrowOnFailure(hr, L"Failed to unregister bundle."); // verify that registration variables were updated @@ -344,7 +344,7 @@ namespace Bootstrapper // // delete registration - hr = RegistrationSessionEnd(®istration, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); TestThrowOnFailure(hr, L"Failed to unregister bundle."); // verify that registration was removed @@ -435,7 +435,7 @@ namespace Bootstrapper Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); // finish registration - hr = RegistrationSessionEnd(®istration, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); TestThrowOnFailure(hr, L"Failed to register bundle."); // verify that registration was updated @@ -468,7 +468,7 @@ namespace Bootstrapper Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); // delete registration - hr = RegistrationSessionEnd(®istration, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); TestThrowOnFailure(hr, L"Failed to unregister bundle."); // verify that registration was removed @@ -509,7 +509,7 @@ namespace Bootstrapper BYTE rgbData[256] = { }; BOOTSTRAPPER_RESUME_TYPE resumeType = BOOTSTRAPPER_RESUME_TYPE_NONE; BYTE* pbBuffer = NULL; - DWORD cbBuffer = 0; + SIZE_T cbBuffer = 0; String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); try { @@ -573,7 +573,7 @@ namespace Bootstrapper Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_INTERRUPTED, (int)resumeType); // suspend session - hr = RegistrationSessionEnd(®istration, &packages, BURN_RESUME_MODE_SUSPEND, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_SUSPEND, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); TestThrowOnFailure(hr, L"Failed to suspend session."); // verify that run key was removed @@ -589,7 +589,7 @@ namespace Bootstrapper hr = RegistrationLoadState(®istration, &pbBuffer, &cbBuffer); TestThrowOnFailure(hr, L"Failed to load state."); - Assert::Equal((DWORD)sizeof(rgbData), cbBuffer); + Assert::Equal((SIZE_T)sizeof(rgbData), cbBuffer); Assert::True(0 == memcmp(pbBuffer, rgbData, sizeof(rgbData))); // write active resume mode @@ -600,7 +600,7 @@ namespace Bootstrapper Assert::NotEqual((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); // end session - hr = RegistrationSessionEnd(®istration, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); TestThrowOnFailure(hr, L"Failed to unregister bundle."); // read resume type after session diff --git a/src/test/BurnUnitTest/VariableTest.cpp b/src/test/BurnUnitTest/VariableTest.cpp index ab45a73f..5c9dce03 100644 --- a/src/test/BurnUnitTest/VariableTest.cpp +++ b/src/test/BurnUnitTest/VariableTest.cpp @@ -134,7 +134,7 @@ namespace Bootstrapper HRESULT hr = S_OK; BURN_VARIABLES variables = { }; LPWSTR scz = NULL; - DWORD cch = 0; + SIZE_T cch = 0; BOOL fContainsHiddenData = FALSE; try { @@ -180,12 +180,12 @@ namespace Bootstrapper hr = VariableFormatString(&variables, L"PRE [PROP1] POST", &scz, &cch); TestThrowOnFailure(hr, L"Failed to format string"); - Assert::Equal((DWORD)lstrlenW(scz), cch); + Assert::Equal((SIZE_T)lstrlenW(scz), cch); hr = VariableFormatString(&variables, L"PRE [PROP1] POST", NULL, &cch); TestThrowOnFailure(hr, L"Failed to format string"); - Assert::Equal((DWORD)lstrlenW(scz), cch); + Assert::Equal((SIZE_T)lstrlenW(scz), cch); } finally { diff --git a/src/test/BurnUnitTest/packages.config b/src/test/BurnUnitTest/packages.config index accce624..a7b88aa1 100644 --- a/src/test/BurnUnitTest/packages.config +++ b/src/test/BurnUnitTest/packages.config @@ -10,5 +10,5 @@ - + \ No newline at end of file -- cgit v1.2.3-55-g6feb From 648133ee7f1206d1208630d5935a4846508fd5b9 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Mon, 12 Apr 2021 20:51:23 -0700 Subject: Enhanced reboot pending detection --- src/engine/engine.mc | 6 +++++ src/engine/registration.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/engine/engine.mc b/src/engine/engine.mc index a793540a..d0897a95 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -1068,3 +1068,9 @@ Language=English Skipping MSI property '%1!ls!' because condition '%2!ls!' evaluates to %3!hs!. . +MessageId=701 +Severity=Warning +SymbolicName=MSG_PENDING_REBOOT_DETECTED +Language=English +A reboot is pending from a prior execution of this bundle: %1!ls!. Apply will be blocked. Continuing... +. diff --git a/src/engine/registration.cpp b/src/engine/registration.cpp index b8a2283f..9e27b177 100644 --- a/src/engine/registration.cpp +++ b/src/engine/registration.cpp @@ -97,6 +97,7 @@ static BOOL IsWuRebootPending(); static BOOL IsBundleRebootPending( __in BURN_REGISTRATION* pRegistration ); +static BOOL IsRegistryRebootPending(); // function definitions @@ -451,7 +452,7 @@ extern "C" HRESULT RegistrationSetVariables( hr = VariableSetVersion(pVariables, BURN_BUNDLE_VERSION, pRegistration->pVersion, TRUE); ExitOnFailure(hr, "Failed to overwrite the bundle version built-in variable."); - hr = VariableSetNumeric(pVariables, BURN_REBOOT_PENDING, IsBundleRebootPending(pRegistration) || IsWuRebootPending(), TRUE); + hr = VariableSetNumeric(pVariables, BURN_REBOOT_PENDING, IsBundleRebootPending(pRegistration) || IsWuRebootPending() || IsRegistryRebootPending(), TRUE); ExitOnFailure(hr, "Failed to overwrite the bundle reboot-pending built-in variable."); LExit: @@ -505,6 +506,8 @@ extern "C" HRESULT RegistrationDetectResumeType( if (IsBundleRebootPending(pRegistration)) { + LogId(REPORT_STANDARD, MSG_PENDING_REBOOT_DETECTED, pRegistration->sczRegistrationKey); + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING; ExitFunction1(hr = S_OK); } @@ -1638,3 +1641,62 @@ LExit: return fBundleRebootPending; } + +static BOOL IsRegistryRebootPending() +{ + HRESULT hr = S_OK; + DWORD dwValue; + HKEY hk = NULL; + BOOL fRebootPending = FALSE; + + hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\ServerManager", L"CurrentRebootAttempts", TRUE, &dwValue); + fRebootPending = SUCCEEDED(hr) && 0 < dwValue; + + if (!fRebootPending) + { + hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Updates", L"UpdateExeVolatile", TRUE, &dwValue); + fRebootPending = SUCCEEDED(hr) && 0 < dwValue; + + if (!fRebootPending) + { + fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootPending", NULL, TRUE); + + if (!fRebootPending) + { + fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootInProgress", NULL, TRUE); + + if (!fRebootPending) + { + hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update", L"AUState", TRUE, &dwValue); + fRebootPending = SUCCEEDED(hr) && 8 == dwValue; + + if (!fRebootPending) + { + fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager", L"PendingFileRenameOperations", TRUE); + + if (!fRebootPending) + { + fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager", L"PendingFileRenameOperations2", TRUE); + + if (!fRebootPending) + { + hr = RegOpen(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\FileRenameOperations", KEY_READ | KEY_WOW64_64KEY, &hk); + if (SUCCEEDED(hr)) + { + DWORD cSubKeys = 0; + DWORD cValues = 0; + hr = RegQueryKey(hk, &cSubKeys, &cValues); + fRebootPending = SUCCEEDED(hr) && (0 < cSubKeys || 0 < cValues); + } + } + } + } + } + } + } + } + + ReleaseRegKey(hk); + + return fRebootPending; +} -- cgit v1.2.3-55-g6feb From 74c40b3fddc937d1884c82355f9fed8a5481ea4a Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Mon, 12 Apr 2021 21:00:07 -0700 Subject: Avoid activating the engine's windows --- src/engine/pipe.cpp | 2 +- src/engine/uithread.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/engine/pipe.cpp b/src/engine/pipe.cpp index 67eeae87..47963e9d 100644 --- a/src/engine/pipe.cpp +++ b/src/engine/pipe.cpp @@ -401,7 +401,7 @@ extern "C" HRESULT PipeLaunchChildProcess( // Since ShellExecuteEx doesn't support passing inherited handles, don't bother with CoreAppendFileHandleSelfToCommandLine. // We could fallback to using ::DuplicateHandle to inject the file handle later if necessary. - hr = ShelExec(wzExecutablePath, sczParameters, wzVerb, NULL, SW_HIDE, hwndParent, &hProcess); + hr = ShelExec(wzExecutablePath, sczParameters, wzVerb, NULL, SW_SHOWNA, hwndParent, &hProcess); ExitOnFailure(hr, "Failed to launch elevated child process: %ls", wzExecutablePath); pConnection->dwProcessId = ::GetProcessId(hProcess); diff --git a/src/engine/uithread.cpp b/src/engine/uithread.cpp index 39f92142..433cb171 100644 --- a/src/engine/uithread.cpp +++ b/src/engine/uithread.cpp @@ -134,9 +134,11 @@ static DWORD WINAPI ThreadProc( info.pUserExperience = &pEngineState->userExperience; // Create the window to handle reboots without activating it. - hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, wc.lpszClassName, NULL, WS_POPUP | WS_VISIBLE, CW_USEDEFAULT, SW_SHOWNA, 0, 0, HWND_DESKTOP, NULL, pContext->hInstance, &info); + hWnd = ::CreateWindowExW(WS_EX_NOACTIVATE, wc.lpszClassName, NULL, WS_POPUP, 0, 0, 0, 0, HWND_DESKTOP, NULL, pContext->hInstance, &info); ExitOnNullWithLastError(hWnd, hr, "Failed to create window."); + ::ShowWindow(hWnd, SW_SHOWNA); + // Persist the window handle and let the caller know we've initialized. pEngineState->hMessageWindow = hWnd; ::SetEvent(pContext->hInitializedEvent); -- cgit v1.2.3-55-g6feb From 8b47ea48058fb96e2e647b8cfa5d5b939bee35bc Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Mon, 12 Apr 2021 22:49:25 -0700 Subject: Detect system MSI packages before user managed packages --- src/engine/msiengine.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index 6c5b760b..4f6062ea 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -522,19 +522,19 @@ extern "C" HRESULT MsiEngineDetectPackage( } // get product version - hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); + hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr) { - ExitOnFailure(hr, "Failed to get version for product in user unmanaged context: %ls", wzProductCode); - fPerMachine = FALSE; + ExitOnFailure(hr, "Failed to get version for product in machine context: %ls", wzProductCode); + fPerMachine = TRUE; } else { - hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); + hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr) { - ExitOnFailure(hr, "Failed to get version for product in machine context: %ls", wzProductCode); - fPerMachine = TRUE; + ExitOnFailure(hr, "Failed to get version for product in user unmanaged context: %ls", wzProductCode); + fPerMachine = FALSE; } else { -- cgit v1.2.3-55-g6feb From 70adfc49cb05da4e7b9eb50a0c47635d2d20b366 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Mon, 12 Apr 2021 23:52:13 -0700 Subject: Layout using the source engine handle --- src/engine/apply.cpp | 44 ++++++++++++++++++++++++++---- src/engine/engine.vcxproj | 4 +-- src/engine/packages.config | 2 +- src/stub/packages.config | 2 +- src/stub/stub.vcxproj | 4 +-- src/test/BurnUnitTest/BurnUnitTest.vcxproj | 4 +-- src/test/BurnUnitTest/packages.config | 6 ++-- 7 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index dc40c50d..2815f7da 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -69,6 +69,7 @@ static void UpdateCacheSuccessProgress( __inout DWORD64* pqwSuccessfulCachedProgress ); static HRESULT LayoutBundle( + __in HANDLE hSourceEngineFile, __in BURN_USER_EXPERIENCE* pUX, __in BURN_VARIABLES* pVariables, __in HANDLE hPipe, @@ -114,6 +115,7 @@ static HRESULT PromptForSource( ); static HRESULT CopyPayload( __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, + __in HANDLE hSourceFile, __in_z LPCWSTR wzSourcePath, __in_z LPCWSTR wzDestinationPath ); @@ -510,7 +512,7 @@ extern "C" HRESULT ApplyCache( break; case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: - hr = LayoutBundle(pUX, pVariables, hPipe, pCacheAction->bundleLayout.sczExecutableName, pCacheAction->bundleLayout.sczLayoutDirectory, pCacheAction->bundleLayout.sczUnverifiedPath, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal); + hr = LayoutBundle(hSourceEngineFile, pUX, pVariables, hPipe, pCacheAction->bundleLayout.sczExecutableName, pCacheAction->bundleLayout.sczLayoutDirectory, pCacheAction->bundleLayout.sczUnverifiedPath, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal); if (SUCCEEDED(hr)) { UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); @@ -1053,6 +1055,7 @@ static void UpdateCacheSuccessProgress( } static HRESULT LayoutBundle( + __in HANDLE hSourceEngineFile, __in BURN_USER_EXPERIENCE* pUX, __in BURN_VARIABLES* pVariables, __in HANDLE hPipe, @@ -1090,7 +1093,7 @@ static HRESULT LayoutBundle( hr = PathCompare(sczBundlePath, sczDestinationPath, &nEquivalentPaths); ExitOnFailure(hr, "Failed to determine if layout bundle path was equivalent with current process path."); - if (CSTR_EQUAL == nEquivalentPaths) + if (CSTR_EQUAL == nEquivalentPaths && FileExistsEx(sczDestinationPath, NULL)) { ExitFunction1(hr = S_OK); } @@ -1112,7 +1115,7 @@ static HRESULT LayoutBundle( hr = UserExperienceOnCacheAcquireBegin(pUX, NULL, NULL, BOOTSTRAPPER_CACHE_OPERATION_COPY, sczBundlePath); ExitOnRootFailure(hr, "BA aborted cache acquire begin."); - hr = CopyPayload(&progress, sczBundlePath, wzUnverifiedPath); + hr = CopyPayload(&progress, hSourceEngineFile, sczBundlePath, wzUnverifiedPath); // Error handling happens after sending complete message to BA. UserExperienceOnCacheAcquireComplete(pUX, NULL, NULL, hr, &fRetryAcquire); @@ -1239,7 +1242,7 @@ static HRESULT AcquireContainerOrPayload( hr = UserExperienceOnCacheAcquireBegin(pUX, wzPackageOrContainerId, wzPayloadId, BOOTSTRAPPER_CACHE_OPERATION_COPY, sczSourceFullPath); ExitOnRootFailure(hr, "BA aborted cache acquire begin."); - hr = CopyPayload(&progress, sczSourceFullPath, wzDestinationPath); + hr = CopyPayload(&progress, INVALID_HANDLE_VALUE, sczSourceFullPath, wzDestinationPath); // Error handling happens after sending complete message to BA. // We successfully copied from a source location, set that as the last used source. @@ -1432,6 +1435,7 @@ LExit: static HRESULT CopyPayload( __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, + __in HANDLE hSourceFile, __in_z LPCWSTR wzSourcePath, __in_z LPCWSTR wzDestinationPath ) @@ -1440,6 +1444,8 @@ static HRESULT CopyPayload( DWORD dwFileAttributes = 0; LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L""; LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : L""; + HANDLE hDestinationFile = INVALID_HANDLE_VALUE; + HANDLE hSourceOpenedFile = INVALID_HANDLE_VALUE; DWORD dwLogId = pProgress->pContainer ? (pProgress->pPayload ? MSG_ACQUIRE_CONTAINER_PAYLOAD : MSG_ACQUIRE_CONTAINER) : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "copy", wzSourcePath); @@ -1457,7 +1463,30 @@ static HRESULT CopyPayload( } } - if (!::CopyFileExW(wzSourcePath, wzDestinationPath, CacheProgressRoutine, pProgress, &pProgress->fCancel, 0)) + if (INVALID_HANDLE_VALUE == hSourceFile) + { + hSourceOpenedFile = ::CreateFileW(wzSourcePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hSourceOpenedFile) + { + ExitWithLastError(hr, "Failed to open source file to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); + } + + hSourceFile = hSourceOpenedFile; + } + else + { + hr = FileSetPointer(hSourceFile, 0, NULL, FILE_BEGIN); + ExitOnRootFailure(hr, "Failed to read from start of source file to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); + } + + hDestinationFile = ::CreateFileW(wzDestinationPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hDestinationFile) + { + ExitWithLastError(hr, "Failed to open destination file to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); + } + + hr = FileCopyUsingHandlesWithProgress(hSourceFile, hDestinationFile, 0, CacheProgressRoutine, pProgress); + if (FAILED(hr)) { if (pProgress->fCancel) { @@ -1466,11 +1495,14 @@ static HRESULT CopyPayload( } else { - ExitWithLastError(hr, "Failed attempt to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); + ExitOnRootFailure(hr, "Failed attempt to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); } } LExit: + ReleaseFileHandle(hDestinationFile); + ReleaseFileHandle(hSourceOpenedFile); + return hr; } diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index 666b3f37..a6deb18d 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -1,7 +1,7 @@ - + Debug @@ -165,7 +165,7 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + \ No newline at end of file diff --git a/src/engine/packages.config b/src/engine/packages.config index 1d09f0a7..125a949e 100644 --- a/src/engine/packages.config +++ b/src/engine/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/stub/packages.config b/src/stub/packages.config index eb05a40b..4f75128a 100644 --- a/src/stub/packages.config +++ b/src/stub/packages.config @@ -4,5 +4,5 @@ - + \ No newline at end of file diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index ae9c21df..2b8bd8ea 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -2,10 +2,10 @@ + - @@ -117,6 +117,6 @@ - + \ No newline at end of file diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index b57ebb16..11f96590 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -3,8 +3,8 @@ + - Debug @@ -97,6 +97,6 @@ - + \ No newline at end of file diff --git a/src/test/BurnUnitTest/packages.config b/src/test/BurnUnitTest/packages.config index a7b88aa1..21d87cf8 100644 --- a/src/test/BurnUnitTest/packages.config +++ b/src/test/BurnUnitTest/packages.config @@ -1,6 +1,9 @@  + + + @@ -8,7 +11,4 @@ - - - \ No newline at end of file -- cgit v1.2.3-55-g6feb From 6f7bb97daa1ae6bf2486b1c566c29e1b524b7548 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Tue, 13 Apr 2021 00:09:00 -0700 Subject: Add MEND request state to repair any missing files in MSI packages --- .../inc/BootstrapperEngine.h | 2 ++ src/engine/dependency.cpp | 2 ++ src/engine/logging.cpp | 4 ++++ src/engine/msiengine.cpp | 21 +++++++++++++++------ 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h index c0e4ded1..99e9b9f0 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h @@ -37,6 +37,7 @@ enum BOOTSTRAPPER_ACTION_STATE BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BOOTSTRAPPER_ACTION_STATE_INSTALL, BOOTSTRAPPER_ACTION_STATE_MODIFY, + BOOTSTRAPPER_ACTION_STATE_MEND, BOOTSTRAPPER_ACTION_STATE_REPAIR, BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE, }; @@ -58,6 +59,7 @@ enum BOOTSTRAPPER_REQUEST_STATE BOOTSTRAPPER_REQUEST_STATE_ABSENT, BOOTSTRAPPER_REQUEST_STATE_CACHE, BOOTSTRAPPER_REQUEST_STATE_PRESENT, + BOOTSTRAPPER_REQUEST_STATE_MEND, BOOTSTRAPPER_REQUEST_STATE_REPAIR, }; diff --git a/src/engine/dependency.cpp b/src/engine/dependency.cpp index 1bd0c7d4..c4af207a 100644 --- a/src/engine/dependency.cpp +++ b/src/engine/dependency.cpp @@ -1025,6 +1025,7 @@ static void CalculateDependencyActionStates( } break; case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_MEND: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: // Register if the package is requested but already installed. switch (pPackage->currentState) @@ -1048,6 +1049,7 @@ static void CalculateDependencyActionStates( break; case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough; case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough; + case BOOTSTRAPPER_ACTION_STATE_MEND: __fallthrough; case BOOTSTRAPPER_ACTION_STATE_REPAIR: __fallthrough; case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: __fallthrough; *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER; diff --git a/src/engine/logging.cpp b/src/engine/logging.cpp index d66f7cf5..67f39c85 100644 --- a/src/engine/logging.cpp +++ b/src/engine/logging.cpp @@ -326,6 +326,8 @@ extern "C" LPCSTR LoggingActionStateToString( return "Install"; case BOOTSTRAPPER_ACTION_STATE_MODIFY: return "Modify"; + case BOOTSTRAPPER_ACTION_STATE_MEND: + return "Mend"; case BOOTSTRAPPER_ACTION_STATE_REPAIR: return "Repair"; case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: @@ -665,6 +667,8 @@ extern "C" LPCSTR LoggingRequestStateToString( return "Cache"; case BOOTSTRAPPER_REQUEST_STATE_PRESENT: return "Present"; + case BOOTSTRAPPER_REQUEST_STATE_MEND: + return "Mend"; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: return "Repair"; default: diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index 4f6062ea..269fc831 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -789,7 +789,7 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( { case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough; case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: - if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) + if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_MEND == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) { hr = VerCompareParsedVersions(pVersion, pInstalledVersion, &nCompareResult); ExitOnFailure(hr, "Failed to compare '%ls' to '%ls' for planning.", pVersion->sczVersion, pInstalledVersion->sczVersion); @@ -801,6 +801,10 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( { execute = BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE; } + else if (BOOTSTRAPPER_REQUEST_STATE_MEND == pPackage->requested) + { + execute = BOOTSTRAPPER_ACTION_STATE_MEND; + } else if (BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) { execute = BOOTSTRAPPER_ACTION_STATE_REPAIR; @@ -829,6 +833,7 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( switch (pPackage->requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_MEND: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; break; @@ -844,6 +849,7 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( switch (pPackage->requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_MEND: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; break; @@ -887,10 +893,12 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough; case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough; case BOOTSTRAPPER_PACKAGE_STATE_CACHED: - // If we requested to put the package on the machine then remove the package during rollback - // if the package is uninstallable. - if ((BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) && - pPackage->fUninstallable) + // If the package is uninstallable and we requested to put the package on the machine then + // remove the package during rollback. + if (pPackage->fUninstallable && + (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || + BOOTSTRAPPER_REQUEST_STATE_MEND == pPackage->requested || + BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested)) { rollback = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; } @@ -1228,11 +1236,12 @@ extern "C" HRESULT MsiEngineExecutePackage( break; case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough; + case BOOTSTRAPPER_ACTION_STATE_MEND: __fallthrough; case BOOTSTRAPPER_ACTION_STATE_REPAIR: { LPCWSTR wzReinstallAll = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action || pExecuteAction->msiPackage.pPackage->Msi.cFeatures) ? L"" : L" REINSTALL=ALL"; - LPCWSTR wzReinstallMode = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action) ? L"o" : L"e"; + LPCWSTR wzReinstallMode = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action || BOOTSTRAPPER_ACTION_STATE_MEND == pExecuteAction->msiPackage.action) ? L"o" : L"e"; hr = StrAllocFormattedSecure(&sczProperties, L"%ls%ls REINSTALLMODE=\"cmus%ls\" REBOOT=ReallySuppress", sczProperties ? sczProperties : L"", wzReinstallAll, wzReinstallMode); ExitOnFailure(hr, "Failed to add reinstall mode and reboot suppression properties on repair."); -- cgit v1.2.3-55-g6feb From d32f770ca05748df9e356444c7e617d5eeedb60c Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Tue, 13 Apr 2021 18:16:55 -0700 Subject: Allow BA to update feed source Fixes wixtoolset/issues#5568 --- .../inc/BootstrapperEngine.h | 12 ++++++++ src/engine/EngineForApplication.cpp | 19 +++++++++++++ src/engine/detect.cpp | 9 +++++- src/engine/externalengine.cpp | 32 ++++++++++++++++++++++ src/engine/externalengine.h | 5 ++++ src/engine/userexperience.cpp | 2 +- 6 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h index 99e9b9f0..0a974563 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h @@ -112,6 +112,7 @@ enum BOOTSTRAPPER_ENGINE_MESSAGE BOOTSTRAPPER_ENGINE_MESSAGE_APPLY, BOOTSTRAPPER_ENGINE_MESSAGE_QUIT, BOOTSTRAPPER_ENGINE_MESSAGE_LAUNCHAPPROVEDEXE, + BOOTSTRAPPER_ENGINE_MESSAGE_SETUPDATESOURCE, BOOTSTRAPPER_ENGINE_MESSAGE_COMPAREVERSIONS, }; @@ -276,6 +277,17 @@ typedef struct _BAENGINE_LAUNCHAPPROVEDEXE_RESULTS DWORD cbSize; } BAENGINE_LAUNCHAPPROVEDEXE_RESULTS; +typedef struct _BAENGINE_SETUPDATESOURCE_ARGS +{ + DWORD cbSize; + LPCWSTR wzUrl; +} BAENGINE_SETUPDATESOURCE_ARGS; + +typedef struct _BAENGINE_SETUPDATESOURCE_RESULTS +{ + DWORD cbSize; +} BAENGINE_SETUPDATESOURCE_RESULTS; + typedef struct _BAENGINE_LOG_ARGS { DWORD cbSize; diff --git a/src/engine/EngineForApplication.cpp b/src/engine/EngineForApplication.cpp index 361e0f4e..83d88ba1 100644 --- a/src/engine/EngineForApplication.cpp +++ b/src/engine/EngineForApplication.cpp @@ -411,6 +411,22 @@ LExit: return hr; } +static HRESULT BAEngineSetUpdateSource( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_SETUPDATESOURCE_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_SETUPDATESOURCE_RESULTS, pResults); + + hr = ExternalEngineSetUpdateSource(pContext->pEngineState, pArgs->wzUrl); + +LExit: + return hr; +} + HRESULT WINAPI EngineForApplicationProc( __in BOOTSTRAPPER_ENGINE_MESSAGE message, __in const LPVOID pvArgs, @@ -497,6 +513,9 @@ HRESULT WINAPI EngineForApplicationProc( case BOOTSTRAPPER_ENGINE_MESSAGE_LAUNCHAPPROVEDEXE: hr = BAEngineLaunchApprovedExe(pContext, pvArgs, pvResults); break; + case BOOTSTRAPPER_ENGINE_MESSAGE_SETUPDATESOURCE: + hr = BAEngineSetUpdateSource(pContext, pvArgs, pvResults); + break; case BOOTSTRAPPER_ENGINE_MESSAGE_COMPAREVERSIONS: hr = BAEngineCompareVersions(pContext, pvArgs, pvResults); break; diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp index 8ca74986..3a58f3dc 100644 --- a/src/engine/detect.cpp +++ b/src/engine/detect.cpp @@ -258,6 +258,7 @@ extern "C" HRESULT DetectUpdate( BOOL fBeginCalled = FALSE; BOOL fSkip = TRUE; BOOL fIgnoreError = FALSE; + LPWSTR sczOriginalSource = NULL; // If no update source was specified, skip update detection. if (!pUpdate->sczUpdateSource || !*pUpdate->sczUpdateSource) @@ -266,7 +267,11 @@ extern "C" HRESULT DetectUpdate( } fBeginCalled = TRUE; - hr = UserExperienceOnDetectUpdateBegin(pUX, pUpdate->sczUpdateSource, &fSkip); + + hr = StrAllocString(&sczOriginalSource, pUpdate->sczUpdateSource, 0); + ExitOnFailure(hr, "Failed to duplicate update feed source."); + + hr = UserExperienceOnDetectUpdateBegin(pUX, sczOriginalSource, &fSkip); ExitOnRootFailure(hr, "BA aborted detect update begin."); if (!fSkip) @@ -276,6 +281,8 @@ extern "C" HRESULT DetectUpdate( } LExit: + ReleaseStr(sczOriginalSource); + if (fBeginCalled) { UserExperienceOnDetectUpdateComplete(pUX, hr, &fIgnoreError); diff --git a/src/engine/externalengine.cpp b/src/engine/externalengine.cpp index d881544c..fffd96bf 100644 --- a/src/engine/externalengine.cpp +++ b/src/engine/externalengine.cpp @@ -733,6 +733,38 @@ LExit: return hr; } +HRESULT ExternalEngineSetUpdateSource( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzUrl + ) +{ + HRESULT hr = S_OK; + BOOL fLeaveCriticalSection = FALSE; + + ::EnterCriticalSection(&pEngineState->userExperience.csEngineActive); + fLeaveCriticalSection = TRUE; + hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience); + ExitOnFailure(hr, "Engine is active, cannot change engine state."); + + if (wzUrl && *wzUrl) + { + hr = StrAllocString(&pEngineState->update.sczUpdateSource, wzUrl, 0); + ExitOnFailure(hr, "Failed to set feed download URL."); + } + else // no URL provided means clear out the whole download source. + { + ReleaseNullStr(pEngineState->update.sczUpdateSource); + } + +LExit: + if (fLeaveCriticalSection) + { + ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive); + } + + return hr; +} + // TODO: callers need to provide the original size (at the time of first public release) of the struct instead of the current size. HRESULT WINAPI ExternalEngineValidateMessageParameter( __in_opt const LPVOID pv, diff --git a/src/engine/externalengine.h b/src/engine/externalengine.h index 3f7bc8c8..a007e5b2 100644 --- a/src/engine/externalengine.h +++ b/src/engine/externalengine.h @@ -165,6 +165,11 @@ HRESULT ExternalEngineLaunchApprovedExe( __in const DWORD dwWaitForInputIdleTimeout ); +HRESULT ExternalEngineSetUpdateSource( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzUrl + ); + HRESULT WINAPI ExternalEngineValidateMessageParameter( __in_opt const LPVOID pv, __in SIZE_T cbSizeOffset, diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index 5a225651..b81dbfb2 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -111,7 +111,7 @@ extern "C" HRESULT UserExperienceLoad( args.pCommand = pCommand; args.pfnBootstrapperEngineProc = EngineForApplicationProc; args.pvBootstrapperEngineProcContext = pEngineContext; - args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 3, 2, 0); + args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 4, 14, 0); results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS); -- cgit v1.2.3-55-g6feb From 941c47e5a3f57ce9626b447a95740b1444e69343 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 16 Apr 2021 09:40:18 -0500 Subject: Detect a package as cached if any of its payloads exist. Detect is supposed to be fast, so it can't fully verify every payload for every package. The engine was wasting its time by trying to verify file sizes without the hash. Even worse, it was making decisions during planning based on that insufficient verification. Contributes to #3640 --- src/engine/apply.cpp | 2 + src/engine/cache.cpp | 25 +++++++++---- src/engine/core.cpp | 44 ++++++++-------------- src/engine/detect.cpp | 10 ++--- src/engine/engine.mc | 8 ++-- src/engine/exeengine.cpp | 5 +-- src/engine/exeengine.h | 3 +- src/engine/externalengine.cpp | 2 +- src/engine/logging.cpp | 17 --------- src/engine/logging.h | 4 -- src/engine/msiengine.cpp | 5 +-- src/engine/msiengine.h | 3 +- src/engine/mspengine.cpp | 5 +-- src/engine/mspengine.h | 3 +- src/engine/msuengine.cpp | 5 +-- src/engine/msuengine.h | 3 +- src/engine/package.h | 13 ++----- src/engine/plan.cpp | 76 ++++++++++++++++---------------------- src/engine/plan.h | 13 ++----- src/engine/pseudobundle.cpp | 8 ++-- src/engine/pseudobundle.h | 2 +- src/engine/relatedbundle.cpp | 19 ++++------ src/test/BurnUnitTest/PlanTest.cpp | 7 ++-- 23 files changed, 111 insertions(+), 171 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 2815f7da..b79bf934 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -1910,6 +1910,8 @@ static HRESULT DoRollbackActions( ExitFunction1(hr = S_OK); case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE: + // TODO: This used to be skipped if the package was already cached. + // Need to figure out new logic for when (if?) to skip it. hr = CleanPackage(pEngineState->companionConnection.hPipe, pRollbackAction->uncachePackage.pPackage); IgnoreRollbackError(hr, "Failed to uncache package for rollback."); break; diff --git a/src/engine/cache.cpp b/src/engine/cache.cpp index 2349a357..667ca9e0 100644 --- a/src/engine/cache.cpp +++ b/src/engine/cache.cpp @@ -91,6 +91,7 @@ static HRESULT RemoveBundleOrPackage( static HRESULT VerifyHash( __in BYTE* pbHash, __in DWORD cbHash, + __in DWORD64 qwFileSize, __in_z LPCWSTR wzUnverifiedPayloadPath, __in HANDLE hFile ); @@ -1288,7 +1289,7 @@ static HRESULT VerifyThenTransferContainer( // Container should have a hash we can use to verify with. if (pContainer->pbHash) { - hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, wzUnverifiedContainerPath, hFile); + hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, pContainer->qwFileSize, wzUnverifiedContainerPath, hFile); ExitOnFailure(hr, "Failed to verify container hash: %ls", wzCachedPath); } @@ -1330,7 +1331,7 @@ static HRESULT VerifyThenTransferPayload( if (pPayload->pbHash) // the payload should have a hash we can use to verify it. { - hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, wzUnverifiedPayloadPath, hFile); + hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, wzUnverifiedPayloadPath, hFile); ExitOnFailure(hr, "Failed to verify payload hash: %ls", wzCachedPath); } @@ -1398,7 +1399,7 @@ static HRESULT VerifyFileAgainstPayload( if (pPayload->pbHash) // the payload should have a hash we can use to verify it. { - hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, wzVerifyPath, hFile); + hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, wzVerifyPath, hFile); ExitOnFailure(hr, "Failed to verify hash of payload: %ls", pPayload->sczKey); } @@ -1712,7 +1713,7 @@ static HRESULT RemoveBundleOrPackage( } } - if (FAILED(hr)) + if (E_PATHNOTFOUND != hr && FAILED(hr)) { LogId(REPORT_STANDARD, fBundle ? MSG_UNABLE_UNCACHE_BUNDLE : MSG_UNABLE_UNCACHE_PACKAGE, wzBundleOrPackageId, sczDirectory, hr); hr = S_OK; @@ -1743,6 +1744,7 @@ LExit: static HRESULT VerifyHash( __in BYTE* pbHash, __in DWORD cbHash, + __in DWORD64 qwFileSize, __in_z LPCWSTR wzUnverifiedPayloadPath, __in HANDLE hFile ) @@ -1751,22 +1753,31 @@ static HRESULT VerifyHash( HRESULT hr = S_OK; BYTE rgbActualHash[SHA512_HASH_LEN] = { }; - DWORD64 qwHashedBytes; + DWORD64 qwHashedBytes = 0; + LONGLONG llSize = 0; LPWSTR pszExpected = NULL; LPWSTR pszActual = NULL; + hr = FileSizeByHandle(hFile, &llSize); + ExitOnFailure(hr, "Failed to get file size for path: %ls", wzUnverifiedPayloadPath); + + if (static_cast(llSize) != qwFileSize) + { + ExitOnFailure(hr = ERROR_FILE_CORRUPT, "File size mismatch for path: %ls, expected: %llu, actual: %lld", wzUnverifiedPayloadPath, qwFileSize, llSize); + } + // TODO: create a cryp hash file that sends progress. hr = CrypHashFileHandle(hFile, PROV_RSA_AES, CALG_SHA_512, rgbActualHash, sizeof(rgbActualHash), &qwHashedBytes); ExitOnFailure(hr, "Failed to calculate hash for path: %ls", wzUnverifiedPayloadPath); // Compare hashes. - if (cbHash != sizeof(rgbActualHash) || 0 != memcmp(pbHash, rgbActualHash, SHA512_HASH_LEN)) + if (cbHash != sizeof(rgbActualHash) || 0 != memcmp(pbHash, rgbActualHash, sizeof(rgbActualHash))) { hr = CRYPT_E_HASH_VALUE; // Best effort to log the expected and actual hash value strings. if (SUCCEEDED(StrAllocHexEncode(pbHash, cbHash, &pszExpected)) && - SUCCEEDED(StrAllocHexEncode(rgbActualHash, (SIZE_T)qwHashedBytes, &pszActual))) + SUCCEEDED(StrAllocHexEncode(rgbActualHash, sizeof(rgbActualHash), &pszActual))) { ExitOnFailure(hr, "Hash mismatch for path: %ls, expected: %ls, actual: %ls", wzUnverifiedPayloadPath, pszExpected, pszActual); } diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 50ed6ea0..7341e4b8 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -388,7 +388,7 @@ extern "C" HRESULT CoreDetect( pEngineState->registration.fEligibleForCleanup = FALSE; } - LogId(REPORT_STANDARD, MSG_DETECTED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingCacheStateToString(pPackage->cache), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->installRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->cacheRegistrationState)); + LogId(REPORT_STANDARD, MSG_DETECTED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingBoolToString(pPackage->fCached), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->installRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->cacheRegistrationState)); if (BURN_PACKAGE_TYPE_MSI == pPackage->type) { @@ -439,7 +439,6 @@ extern "C" HRESULT CorePlan( HRESULT hr = S_OK; BOOL fPlanBegan = FALSE; LPWSTR sczLayoutDirectory = NULL; - HANDLE hSyncpointEvent = NULL; BURN_PACKAGE* pUpgradeBundlePackage = NULL; BURN_PACKAGE* pForwardCompatibleBundlePackage = NULL; BOOL fContinuePlanning = TRUE; // assume we won't skip planning due to dependencies. @@ -489,7 +488,7 @@ extern "C" HRESULT CorePlan( ExitOnFailure(hr, "Failed to plan the layout of the bundle."); // Plan the packages' layout. - hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, sczLayoutDirectory, &hSyncpointEvent); + hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, sczLayoutDirectory); ExitOnFailure(hr, "Failed to plan packages."); } else if (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == action || BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED == action) @@ -498,7 +497,7 @@ extern "C" HRESULT CorePlan( pUpgradeBundlePackage = &pEngineState->update.package; - hr = PlanUpdateBundle(&pEngineState->userExperience, pUpgradeBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, &hSyncpointEvent); + hr = PlanUpdateBundle(&pEngineState->userExperience, pUpgradeBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType); ExitOnFailure(hr, "Failed to plan update."); } else @@ -512,7 +511,7 @@ extern "C" HRESULT CorePlan( pForwardCompatibleBundlePackage = &pEngineState->plan.forwardCompatibleBundle; - hr = PlanPassThroughBundle(&pEngineState->userExperience, pForwardCompatibleBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, &hSyncpointEvent); + hr = PlanPassThroughBundle(&pEngineState->userExperience, pForwardCompatibleBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType); ExitOnFailure(hr, "Failed to plan passthrough."); } else // doing an action that modifies the machine state. @@ -533,11 +532,11 @@ extern "C" HRESULT CorePlan( hr = PlanRelatedBundlesBegin(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->plan); ExitOnFailure(hr, "Failed to plan related bundles."); - hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, NULL, &hSyncpointEvent); + hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, NULL); ExitOnFailure(hr, "Failed to plan packages."); // Schedule the update of related bundles last. - hr = PlanRelatedBundlesComplete(&pEngineState->registration, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, &hSyncpointEvent, dwExecuteActionEarlyIndex); + hr = PlanRelatedBundlesComplete(&pEngineState->registration, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, dwExecuteActionEarlyIndex); ExitOnFailure(hr, "Failed to schedule related bundles."); } } @@ -1669,9 +1668,8 @@ static HRESULT DetectPackagePayloadsCached( { HRESULT hr = S_OK; LPWSTR sczCachePath = NULL; - BURN_CACHE_STATE cache = BURN_CACHE_STATE_NONE; // assume the package will not be cached. + BOOL fCached = FALSE; // assume the package is not cached. LPWSTR sczPayloadCachePath = NULL; - LONGLONG llSize = 0; if (pPackage->sczCacheId && *pPackage->sczCacheId) { @@ -1681,9 +1679,7 @@ static HRESULT DetectPackagePayloadsCached( // If the cached directory exists, we have something. if (DirExists(sczCachePath, NULL)) { - cache = BURN_CACHE_STATE_COMPLETE; // assume all payloads are cached. - - // Check all payloads to see if any are missing or not the right size. + // Check all payloads to see if they exist. for (DWORD i = 0; i < pPackage->cPayloads; ++i) { BURN_PACKAGE_PAYLOAD* pPackagePayload = pPackage->rgPayloads + i; @@ -1691,35 +1687,25 @@ static HRESULT DetectPackagePayloadsCached( hr = PathConcat(sczCachePath, pPackagePayload->pPayload->sczFilePath, &sczPayloadCachePath); ExitOnFailure(hr, "Failed to concat payload cache path."); - hr = FileSize(sczPayloadCachePath, &llSize); - if (SUCCEEDED(hr) && static_cast(llSize) != pPackagePayload->pPayload->qwFileSize) - { - hr = HRESULT_FROM_WIN32(ERROR_FILE_CORRUPT); // size did not match expectations, so cache must have the wrong file. - } - - if (SUCCEEDED(hr)) + if (FileExistsEx(sczPayloadCachePath, NULL)) { - // TODO: should we do a full on hash verification on the file to ensure - // the exact right file is cached? - + // TODO: We shouldn't track whether the payload was cached since all we did was check whether the file exists. pPackagePayload->fCached = TRUE; + fCached = TRUE; } else { - LogId(REPORT_STANDARD, MSG_DETECT_PACKAGE_NOT_FULLY_CACHED, pPackage->sczId, pPackagePayload->pPayload->sczKey, hr); - - cache = BURN_CACHE_STATE_PARTIAL; // found a payload that was not cached so we are partial. - hr = S_OK; + LogId(REPORT_STANDARD, MSG_DETECT_PACKAGE_NOT_FULLY_CACHED, pPackage->sczId, pPackagePayload->pPayload->sczKey); } } } } - pPackage->cache = cache; + pPackage->fCached = fCached; if (pPackage->fCanAffectRegistration) { - pPackage->cacheRegistrationState = BURN_CACHE_STATE_NONE < pPackage->cache ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + pPackage->cacheRegistrationState = pPackage->fCached ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; } LExit: @@ -1808,7 +1794,7 @@ static void LogPackages( const DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? pPackages->cPackages - 1 - i : i; const BURN_PACKAGE* pPackage = &pPackages->rgPackages[iPackage]; - LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingBoolToString(pPackage->fAcquire), LoggingBoolToString(pPackage->fUncache), LoggingDependencyActionToString(pPackage->dependencyExecute), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedInstallRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedCacheRegistrationState)); + LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingBoolToString(pPackage->fPlannedCache), LoggingBoolToString(pPackage->fPlannedUncache), LoggingDependencyActionToString(pPackage->dependencyExecute), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedInstallRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedCacheRegistrationState)); if (BURN_PACKAGE_TYPE_MSI == pPackage->type) { diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp index 3a58f3dc..355b49f5 100644 --- a/src/engine/detect.cpp +++ b/src/engine/detect.cpp @@ -67,7 +67,7 @@ extern "C" void DetectReset( pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; - pPackage->cache = BURN_CACHE_STATE_NONE; + pPackage->fCached = FALSE; for (DWORD iPayload = 0; iPayload < pPackage->cPayloads; ++iPayload) { BURN_PACKAGE_PAYLOAD* pPayload = pPackage->rgPayloads + iPayload; @@ -149,10 +149,10 @@ extern "C" HRESULT DetectForwardCompatibleBundles( pRegistration->fForwardCompatibleBundleExists = TRUE; } - hr = UserExperienceOnDetectForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, BURN_CACHE_STATE_COMPLETE != pRelatedBundle->package.cache); + hr = UserExperienceOnDetectForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, !pRelatedBundle->package.fCached); ExitOnRootFailure(hr, "BA aborted detect forward compatible bundle."); - LogId(REPORT_STANDARD, MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingCacheStateToString(pRelatedBundle->package.cache)); + LogId(REPORT_STANDARD, MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingBoolToString(pRelatedBundle->package.fCached)); } } } @@ -225,9 +225,9 @@ extern "C" HRESULT DetectReportRelatedBundles( break; } - LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingRelatedOperationToString(operation), LoggingCacheStateToString(pRelatedBundle->package.cache)); + LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingRelatedOperationToString(operation), LoggingBoolToString(pRelatedBundle->package.fCached)); - hr = UserExperienceOnDetectRelatedBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, operation, BURN_CACHE_STATE_COMPLETE != pRelatedBundle->package.cache); + hr = UserExperienceOnDetectRelatedBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, operation, !pRelatedBundle->package.fCached); ExitOnRootFailure(hr, "BA aborted detect related bundle."); // For now, if any related bundles will be executed during uninstall by default then never automatically clean up the bundle. diff --git a/src/engine/engine.mc b/src/engine/engine.mc index d0897a95..365da1d9 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -242,16 +242,16 @@ Detected forward compatible bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version MessageId=108 Severity=Warning -SymbolicName=MSG_DETECT_RELATED_BUNDLE_NOT_FULLY_CACHED +SymbolicName=MSG_DETECT_RELATED_BUNDLE_NOT_CACHED Language=English -Detected partially cached related bundle: %1!ls!, cache path: %2!ls!, reason: 0x%3!x! +Detected related bundle missing from cache: %1!ls!, cache path: %2!ls! . MessageId=120 Severity=Warning SymbolicName=MSG_DETECT_PACKAGE_NOT_FULLY_CACHED Language=English -Detected partially cached package: %1!ls!, invalid payload: %2!ls!, reason: 0x%3!x! +Detected partially cached package: %1!ls!, missing payload: %2!ls! . MessageId=121 @@ -363,7 +363,7 @@ MessageId=208 Severity=Warning SymbolicName=MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE Language=English -Plan disabled rollback for package: %1!ls!, due to incomplete cache: %2!hs!, original rollback action: %3!hs! +Plan disabled rollback due to incomplete cache for package: %1!ls!, original rollback action: %2!hs! . MessageId=209 diff --git a/src/engine/exeengine.cpp b/src/engine/exeengine.cpp index cee755e3..46905fc3 100644 --- a/src/engine/exeengine.cpp +++ b/src/engine/exeengine.cpp @@ -269,8 +269,7 @@ extern "C" HRESULT ExeEnginePlanAddPackage( __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, - __in_opt HANDLE hCacheEvent, - __in BOOL fPlanPackageCacheRollback + __in_opt HANDLE hCacheEvent ) { HRESULT hr = S_OK; @@ -279,7 +278,7 @@ extern "C" HRESULT ExeEnginePlanAddPackage( // add wait for cache if (hCacheEvent) { - hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent, fPlanPackageCacheRollback); + hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent); ExitOnFailure(hr, "Failed to plan package cache syncpoint"); } diff --git a/src/engine/exeengine.h b/src/engine/exeengine.h index 88a884eb..e032ea01 100644 --- a/src/engine/exeengine.h +++ b/src/engine/exeengine.h @@ -29,8 +29,7 @@ HRESULT ExeEnginePlanAddPackage( __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, - __in_opt HANDLE hCacheEvent, - __in BOOL fPlanPackageCacheRollback + __in_opt HANDLE hCacheEvent ); HRESULT ExeEngineExecutePackage( __in BURN_EXECUTE_ACTION* pExecuteAction, diff --git a/src/engine/externalengine.cpp b/src/engine/externalengine.cpp index fffd96bf..7e9bb25c 100644 --- a/src/engine/externalengine.cpp +++ b/src/engine/externalengine.cpp @@ -327,7 +327,7 @@ HRESULT ExternalEngineSetUpdate( sczId = pEngineState->registration.sczId; } - hr = PseudoBundleInitialize(FILEMAKEVERSION(rmj, rmm, rup, rpr), &pEngineState->update.package, FALSE, sczId, BOOTSTRAPPER_RELATION_UPDATE, BOOTSTRAPPER_PACKAGE_STATE_ABSENT, BURN_CACHE_STATE_NONE, pEngineState->registration.sczExecutableName, sczLocalSource ? sczLocalSource : wzLocalSource, wzDownloadSource, qwSize, TRUE, sczCommandline, NULL, NULL, NULL, rgbHash, cbHash); + hr = PseudoBundleInitialize(FILEMAKEVERSION(rmj, rmm, rup, rpr), &pEngineState->update.package, FALSE, sczId, BOOTSTRAPPER_RELATION_UPDATE, BOOTSTRAPPER_PACKAGE_STATE_ABSENT, FALSE, pEngineState->registration.sczExecutableName, sczLocalSource ? sczLocalSource : wzLocalSource, wzDownloadSource, qwSize, TRUE, sczCommandline, NULL, NULL, NULL, rgbHash, cbHash); ExitOnFailure(hr, "Failed to set update bundle."); pEngineState->update.fUpdateAvailable = TRUE; diff --git a/src/engine/logging.cpp b/src/engine/logging.cpp index 67f39c85..fd2343fa 100644 --- a/src/engine/logging.cpp +++ b/src/engine/logging.cpp @@ -426,23 +426,6 @@ extern "C" LPCSTR LoggingPackageRegistrationStateToString( } } -extern "C" LPCSTR LoggingCacheStateToString( - __in BURN_CACHE_STATE cacheState - ) -{ - switch (cacheState) - { - case BURN_CACHE_STATE_NONE: - return "None"; - case BURN_CACHE_STATE_PARTIAL: - return "Partial"; - case BURN_CACHE_STATE_COMPLETE: - return "Complete"; - default: - return "Invalid"; - } -} - extern "C" LPCSTR LoggingMsiFeatureStateToString( __in BOOTSTRAPPER_FEATURE_STATE featureState ) diff --git a/src/engine/logging.h b/src/engine/logging.h index a69e34c5..601039f9 100644 --- a/src/engine/logging.h +++ b/src/engine/logging.h @@ -94,10 +94,6 @@ LPCSTR LoggingPackageRegistrationStateToString( __in BURN_PACKAGE_REGISTRATION_STATE registrationState ); -LPCSTR LoggingCacheStateToString( - __in BURN_CACHE_STATE cacheState - ); - LPCSTR LoggingMsiFeatureStateToString( __in BOOTSTRAPPER_FEATURE_STATE featureState ); diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index 269fc831..35ca5c62 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -932,8 +932,7 @@ extern "C" HRESULT MsiEnginePlanAddPackage( __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, - __in_opt HANDLE hCacheEvent, - __in BOOL fPlanPackageCacheRollback + __in_opt HANDLE hCacheEvent ) { HRESULT hr = S_OK; @@ -963,7 +962,7 @@ extern "C" HRESULT MsiEnginePlanAddPackage( // add wait for cache if (hCacheEvent) { - hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent, fPlanPackageCacheRollback); + hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent); ExitOnFailure(hr, "Failed to plan package cache syncpoint"); } diff --git a/src/engine/msiengine.h b/src/engine/msiengine.h index 99f97413..8b5bcdd0 100644 --- a/src/engine/msiengine.h +++ b/src/engine/msiengine.h @@ -50,8 +50,7 @@ HRESULT MsiEnginePlanAddPackage( __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, - __in_opt HANDLE hCacheEvent, - __in BOOL fPlanPackageCacheRollback + __in_opt HANDLE hCacheEvent ); HRESULT MsiEngineBeginTransaction( __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp index ed105d24..5ccb6718 100644 --- a/src/engine/mspengine.cpp +++ b/src/engine/mspengine.cpp @@ -506,8 +506,7 @@ extern "C" HRESULT MspEnginePlanAddPackage( __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, - __in_opt HANDLE hCacheEvent, - __in BOOL fPlanPackageCacheRollback + __in_opt HANDLE hCacheEvent ) { HRESULT hr = S_OK; @@ -517,7 +516,7 @@ extern "C" HRESULT MspEnginePlanAddPackage( // add wait for cache if (hCacheEvent) { - hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent, fPlanPackageCacheRollback); + hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent); ExitOnFailure(hr, "Failed to plan package cache syncpoint"); } diff --git a/src/engine/mspengine.h b/src/engine/mspengine.h index 1530954b..79998030 100644 --- a/src/engine/mspengine.h +++ b/src/engine/mspengine.h @@ -58,8 +58,7 @@ HRESULT MspEnginePlanAddPackage( __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, - __in_opt HANDLE hCacheEvent, - __in BOOL fPlanPackageCacheRollback + __in_opt HANDLE hCacheEvent ); HRESULT MspEngineExecutePackage( __in_opt HWND hwndParent, diff --git a/src/engine/msuengine.cpp b/src/engine/msuengine.cpp index 91bbf361..59f9a656 100644 --- a/src/engine/msuengine.cpp +++ b/src/engine/msuengine.cpp @@ -193,8 +193,7 @@ extern "C" HRESULT MsuEnginePlanAddPackage( __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, - __in HANDLE hCacheEvent, - __in BOOL fPlanPackageCacheRollback + __in HANDLE hCacheEvent ) { HRESULT hr = S_OK; @@ -203,7 +202,7 @@ extern "C" HRESULT MsuEnginePlanAddPackage( // add wait for cache if (hCacheEvent) { - hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent, fPlanPackageCacheRollback); + hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent); ExitOnFailure(hr, "Failed to plan package cache syncpoint"); } diff --git a/src/engine/msuengine.h b/src/engine/msuengine.h index cd966b0a..fda7a5ab 100644 --- a/src/engine/msuengine.h +++ b/src/engine/msuengine.h @@ -28,8 +28,7 @@ HRESULT MsuEnginePlanAddPackage( __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, - __in HANDLE hCacheEvent, - __in BOOL fPlanPackageCacheRollback + __in HANDLE hCacheEvent ); HRESULT MsuEngineExecutePackage( __in BURN_EXECUTE_ACTION* pExecuteAction, diff --git a/src/engine/package.h b/src/engine/package.h index 42f1febe..262262ab 100644 --- a/src/engine/package.h +++ b/src/engine/package.h @@ -41,13 +41,6 @@ enum BURN_PACKAGE_TYPE BURN_PACKAGE_TYPE_MSU, }; -enum BURN_CACHE_STATE -{ - BURN_CACHE_STATE_NONE, - BURN_CACHE_STATE_PARTIAL, - BURN_CACHE_STATE_COMPLETE, -}; - enum BURN_CACHE_TYPE { BURN_CACHE_TYPE_NO, @@ -246,12 +239,12 @@ typedef struct _BURN_PACKAGE BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryBackward; // used during uninstall. BOOTSTRAPPER_PACKAGE_STATE currentState; // only valid after Detect. - BURN_CACHE_STATE cache; // only valid after Detect. + BOOL fCached; // only valid after Detect. BOOL fPackageProviderExists; // only valid after Detect. BOOTSTRAPPER_REQUEST_STATE defaultRequested;// only valid during Plan. BOOTSTRAPPER_REQUEST_STATE requested; // only valid during Plan. - BOOL fAcquire; // only valid during Plan. - BOOL fUncache; // only valid during Plan. + BOOL fPlannedCache; // only valid during Plan. + BOOL fPlannedUncache; // only valid during Plan. BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan. BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan. BURN_DEPENDENCY_ACTION providerExecute; // only valid during Plan. diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index c75c1753..187b1f15 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -31,8 +31,7 @@ static HRESULT PlanPackagesHelper( __in BURN_VARIABLES* pVariables, __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in_z_opt LPCWSTR wzLayoutDirectory, - __inout HANDLE* phSyncpointEvent + __in_z_opt LPCWSTR wzLayoutDirectory ); static HRESULT InitializePackage( __in BURN_PLAN* pPlan, @@ -564,13 +563,12 @@ extern "C" HRESULT PlanPackages( __in BURN_VARIABLES* pVariables, __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in_z_opt LPCWSTR wzLayoutDirectory, - __inout HANDLE* phSyncpointEvent + __in_z_opt LPCWSTR wzLayoutDirectory ) { HRESULT hr = S_OK; - hr = PlanPackagesHelper(pPackages->rgPackages, pPackages->cPackages, TRUE, pUX, pPlan, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent); + hr = PlanPackagesHelper(pPackages->rgPackages, pPackages->cPackages, TRUE, pUX, pPlan, pLog, pVariables, display, relationType, wzLayoutDirectory); return hr; } @@ -776,8 +774,7 @@ extern "C" HRESULT PlanPassThroughBundle( __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __inout HANDLE* phSyncpointEvent + __in BOOTSTRAPPER_RELATION_TYPE relationType ) { HRESULT hr = S_OK; @@ -785,7 +782,7 @@ extern "C" HRESULT PlanPassThroughBundle( // Plan passthrough package. // Passthrough packages are never cleaned up by the calling bundle (they delete themselves when appropriate) // so we don't need to plan clean up. - hr = PlanPackagesHelper(pPackage, 1, FALSE, pUX, pPlan, pLog, pVariables, display, relationType, NULL, phSyncpointEvent); + hr = PlanPackagesHelper(pPackage, 1, FALSE, pUX, pPlan, pLog, pVariables, display, relationType, NULL); ExitOnFailure(hr, "Failed to process passthrough package."); LExit: @@ -799,14 +796,13 @@ extern "C" HRESULT PlanUpdateBundle( __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __inout HANDLE* phSyncpointEvent + __in BOOTSTRAPPER_RELATION_TYPE relationType ) { HRESULT hr = S_OK; // Plan update package. - hr = PlanPackagesHelper(pPackage, 1, TRUE, pUX, pPlan, pLog, pVariables, display, relationType, NULL, phSyncpointEvent); + hr = PlanPackagesHelper(pPackage, 1, TRUE, pUX, pPlan, pLog, pVariables, display, relationType, NULL); ExitOnFailure(hr, "Failed to process update package."); LExit: @@ -823,13 +819,13 @@ static HRESULT PlanPackagesHelper( __in BURN_VARIABLES* pVariables, __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in_z_opt LPCWSTR wzLayoutDirectory, - __inout HANDLE* phSyncpointEvent + __in_z_opt LPCWSTR wzLayoutDirectory ) { HRESULT hr = S_OK; BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine. BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; + HANDLE hSyncpointEvent = NULL; // Initialize the packages. for (DWORD i = 0; i < cPackages; ++i) @@ -860,7 +856,7 @@ static HRESULT PlanPackagesHelper( DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i; BURN_PACKAGE* pPackage = rgPackages + iPackage; - hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary); + hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, wzLayoutDirectory, &hSyncpointEvent, &pRollbackBoundary); ExitOnFailure(hr, "Failed to process package."); } @@ -1126,10 +1122,11 @@ extern "C" HRESULT PlanExecutePackage( hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent); ExitOnFailure(hr, "Failed to plan cache package."); } - else if (BURN_CACHE_STATE_COMPLETE != pPackage->cache && NeedsCache(pPackage, FALSE)) + else if (!pPackage->fCached && NeedsCache(pPackage, FALSE)) { + // TODO: this decision should be made during apply instead of plan based on whether the package is actually cached. // If the package is not in the cache, disable any rollback that would require the package from the cache. - LogId(REPORT_STANDARD, MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE, pPackage->sczId, LoggingCacheStateToString(pPackage->cache), LoggingActionStateToString(pPackage->rollback)); + LogId(REPORT_STANDARD, MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE, pPackage->sczId, LoggingActionStateToString(pPackage->rollback)); pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; } @@ -1162,19 +1159,19 @@ extern "C" HRESULT PlanExecutePackage( switch (pPackage->type) { case BURN_PACKAGE_TYPE_EXE: - hr = ExeEnginePlanAddPackage(NULL, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire); + hr = ExeEnginePlanAddPackage(NULL, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent); break; case BURN_PACKAGE_TYPE_MSI: - hr = MsiEnginePlanAddPackage(display, pUserExperience, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire); + hr = MsiEnginePlanAddPackage(display, pUserExperience, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent); break; case BURN_PACKAGE_TYPE_MSP: - hr = MspEnginePlanAddPackage(display, pUserExperience, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire); + hr = MspEnginePlanAddPackage(display, pUserExperience, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent); break; case BURN_PACKAGE_TYPE_MSU: - hr = MsuEnginePlanAddPackage(pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire); + hr = MsuEnginePlanAddPackage(pPackage, pPlan, pLog, pVariables, *phSyncpointEvent); break; default: @@ -1368,7 +1365,6 @@ extern "C" HRESULT PlanRelatedBundlesComplete( __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, - __inout HANDLE* phSyncpointEvent, __in DWORD dwExecuteActionEarlyIndex ) { @@ -1488,7 +1484,7 @@ extern "C" HRESULT PlanRelatedBundlesComplete( } } - hr = ExeEnginePlanAddPackage(pdwInsertIndex, &pRelatedBundle->package, pPlan, pLog, pVariables, *phSyncpointEvent, FALSE); + hr = ExeEnginePlanAddPackage(pdwInsertIndex, &pRelatedBundle->package, pPlan, pLog, pVariables, NULL); ExitOnFailure(hr, "Failed to add to plan related bundle: %ls", pRelatedBundle->package.sczId); // Calculate package states based on reference count for addon and patch related bundles. @@ -1560,11 +1556,8 @@ extern "C" HRESULT PlanCleanPackage( BOOL fPlanCleanPackage = FALSE; BURN_CLEAN_ACTION* pCleanAction = NULL; - // The following is a complex set of logic that determines when a package should be cleaned - // from the cache. Start by noting that we only clean if the package is being acquired or - // already cached and the package is not supposed to always be cached. - if ((pPackage->fAcquire || BURN_CACHE_STATE_PARTIAL == pPackage->cache || BURN_CACHE_STATE_COMPLETE == pPackage->cache) && - (BURN_CACHE_TYPE_ALWAYS > pPackage->cacheType || BOOTSTRAPPER_ACTION_CACHE > pPlan->action)) + // The following is a complex set of logic that determines when a package should be cleaned from the cache. + if (BURN_CACHE_TYPE_ALWAYS > pPackage->cacheType || BOOTSTRAPPER_ACTION_CACHE > pPlan->action) { // The following are all different reasons why the package should be cleaned from the cache. // The else-ifs are used to make the conditions easier to see (rather than have them combined @@ -1607,7 +1600,7 @@ extern "C" HRESULT PlanCleanPackage( pCleanAction->pPackage = pPackage; - pPackage->fUncache = TRUE; + pPackage->fPlannedUncache = TRUE; if (pPackage->fCanAffectRegistration) { @@ -1622,8 +1615,7 @@ LExit: extern "C" HRESULT PlanExecuteCacheSyncAndRollback( __in BURN_PLAN* pPlan, __in BURN_PACKAGE* pPackage, - __in HANDLE hCacheEvent, - __in BOOL fPlanPackageCacheRollback + __in HANDLE hCacheEvent ) { HRESULT hr = S_OK; @@ -1635,17 +1627,14 @@ extern "C" HRESULT PlanExecuteCacheSyncAndRollback( pAction->type = BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT; pAction->syncpoint.hEvent = hCacheEvent; - if (fPlanPackageCacheRollback) - { - hr = PlanAppendRollbackAction(pPlan, &pAction); - ExitOnFailure(hr, "Failed to append rollback action."); + hr = PlanAppendRollbackAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append rollback action."); - pAction->type = BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE; - pAction->uncachePackage.pPackage = pPackage; + pAction->type = BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE; + pAction->uncachePackage.pPackage = pPackage; - hr = PlanExecuteCheckpoint(pPlan); - ExitOnFailure(hr, "Failed to append execute checkpoint for cache rollback."); - } + hr = PlanExecuteCheckpoint(pPlan); + ExitOnFailure(hr, "Failed to append execute checkpoint for cache rollback."); LExit: return hr; @@ -1895,8 +1884,8 @@ static void ResetPlannedPackageState( // Reset package state that is a result of planning. pPackage->defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE; pPackage->requested = BOOTSTRAPPER_REQUEST_STATE_NONE; - pPackage->fAcquire = FALSE; - pPackage->fUncache = FALSE; + pPackage->fPlannedCache = FALSE; + pPackage->fPlannedUncache = FALSE; pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; pPackage->providerExecute = BURN_DEPENDENCY_ACTION_NONE; @@ -2191,10 +2180,7 @@ static HRESULT AddCachePackageHelper( ++pPlan->cOverallProgressTicksTotal; - // If the package was not already fully cached then note that we planned the cache here. Otherwise, we only - // did cache operations to verify the cache is valid so we did not plan the acquisition of the package. - pPackage->fAcquire = (BURN_CACHE_STATE_COMPLETE != pPackage->cache); - + pPackage->fPlannedCache = TRUE; if (pPackage->fCanAffectRegistration) { pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; diff --git a/src/engine/plan.h b/src/engine/plan.h index 77f82e1f..0024b0aa 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -411,8 +411,7 @@ HRESULT PlanPackages( __in BURN_VARIABLES* pVariables, __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in_z_opt LPCWSTR wzLayoutDirectory, - __inout HANDLE* phSyncpointEvent + __in_z_opt LPCWSTR wzLayoutDirectory ); HRESULT PlanRegistration( __in BURN_PLAN* pPlan, @@ -428,8 +427,7 @@ HRESULT PlanPassThroughBundle( __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __inout HANDLE* phSyncpointEvent + __in BOOTSTRAPPER_RELATION_TYPE relationType ); HRESULT PlanUpdateBundle( __in BURN_USER_EXPERIENCE* pUX, @@ -438,8 +436,7 @@ HRESULT PlanUpdateBundle( __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __inout HANDLE* phSyncpointEvent + __in BOOTSTRAPPER_RELATION_TYPE relationType ); HRESULT PlanLayoutPackage( __in BURN_PLAN* pPlan, @@ -475,7 +472,6 @@ HRESULT PlanRelatedBundlesComplete( __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, - __inout HANDLE* phSyncpointEvent, __in DWORD dwExecuteActionEarlyIndex ); HRESULT PlanFinalizeActions( @@ -488,8 +484,7 @@ HRESULT PlanCleanPackage( HRESULT PlanExecuteCacheSyncAndRollback( __in BURN_PLAN* pPlan, __in BURN_PACKAGE* pPackage, - __in HANDLE hCacheEvent, - __in BOOL fPlanPackageCacheRollback + __in HANDLE hCacheEvent ); HRESULT PlanExecuteCheckpoint( __in BURN_PLAN* pPlan diff --git a/src/engine/pseudobundle.cpp b/src/engine/pseudobundle.cpp index 63300065..73fbb019 100644 --- a/src/engine/pseudobundle.cpp +++ b/src/engine/pseudobundle.cpp @@ -10,7 +10,7 @@ extern "C" HRESULT PseudoBundleInitialize( __in_z LPCWSTR wzId, __in BOOTSTRAPPER_RELATION_TYPE relationType, __in BOOTSTRAPPER_PACKAGE_STATE state, - __in BURN_CACHE_STATE cacheState, + __in BOOL fCached, __in_z LPCWSTR wzFilePath, __in_z LPCWSTR wzLocalSource, __in_z_opt LPCWSTR wzDownloadSource, @@ -67,14 +67,14 @@ extern "C" HRESULT PseudoBundleInitialize( memcpy_s(pPackage->rgPayloads->pPayload->pbHash, pPackage->rgPayloads->pPayload->cbHash, pbHash, cbHash); } - pPackage->rgPayloads->fCached = BURN_CACHE_STATE_NONE < cacheState; + pPackage->rgPayloads->fCached = fCached; pPackage->Exe.fPseudoBundle = TRUE; pPackage->type = BURN_PACKAGE_TYPE_EXE; pPackage->fPerMachine = fPerMachine; pPackage->currentState = state; - pPackage->cache = cacheState; + pPackage->fCached = fCached; pPackage->qwInstallSize = qwSize; pPackage->qwSize = qwSize; pPackage->fVital = fVital; @@ -216,7 +216,7 @@ extern "C" HRESULT PseudoBundleInitializePassthrough( pPassthroughPackage->fPerMachine = FALSE; // passthrough bundles are always launched per-user. pPassthroughPackage->type = pPackage->type; pPassthroughPackage->currentState = pPackage->currentState; - pPassthroughPackage->cache = pPackage->cache; + pPassthroughPackage->fCached = pPackage->fCached; pPassthroughPackage->qwInstallSize = pPackage->qwInstallSize; pPassthroughPackage->qwSize = pPackage->qwSize; pPassthroughPackage->fVital = pPackage->fVital; diff --git a/src/engine/pseudobundle.h b/src/engine/pseudobundle.h index c7940976..9fb530aa 100644 --- a/src/engine/pseudobundle.h +++ b/src/engine/pseudobundle.h @@ -13,7 +13,7 @@ HRESULT PseudoBundleInitialize( __in_z LPCWSTR wzId, __in BOOTSTRAPPER_RELATION_TYPE relationType, __in BOOTSTRAPPER_PACKAGE_STATE state, - __in BURN_CACHE_STATE cacheState, + __in BOOL fCached, __in_z LPCWSTR wzFilePath, __in_z LPCWSTR wzLocalSource, __in_z_opt LPCWSTR wzDownloadSource, diff --git a/src/engine/relatedbundle.cpp b/src/engine/relatedbundle.cpp index a4948a88..6953c678 100644 --- a/src/engine/relatedbundle.cpp +++ b/src/engine/relatedbundle.cpp @@ -391,14 +391,14 @@ static HRESULT LoadRelatedBundleFromKey( __in HKEY hkBundleId, __in BOOL fPerMachine, __in BOOTSTRAPPER_RELATION_TYPE relationType, - __inout BURN_RELATED_BUNDLE *pRelatedBundle + __inout BURN_RELATED_BUNDLE* pRelatedBundle ) { HRESULT hr = S_OK; DWORD64 qwEngineVersion = 0; LPWSTR sczBundleVersion = NULL; LPWSTR sczCachePath = NULL; - BURN_CACHE_STATE cacheState = BURN_CACHE_STATE_NONE; + BOOL fCached = FALSE; DWORD64 qwFileSize = 0; BURN_DEPENDENCY_PROVIDER dependencyProvider = { }; @@ -423,19 +423,16 @@ static HRESULT LoadRelatedBundleFromKey( hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, &sczCachePath); ExitOnFailure(hr, "Failed to read cache path from registry for bundle: %ls", wzRelatedBundleId); - hr = FileSize(sczCachePath, reinterpret_cast(&qwFileSize)); - if (SUCCEEDED(hr)) + if (FileExistsEx(sczCachePath, NULL)) { - cacheState = BURN_CACHE_STATE_COMPLETE; + fCached = TRUE; } - else if (E_FILENOTFOUND != hr) + else { - cacheState = BURN_CACHE_STATE_PARTIAL; - LogId(REPORT_STANDARD, MSG_DETECT_RELATED_BUNDLE_NOT_FULLY_CACHED, wzRelatedBundleId, sczCachePath, hr); + LogId(REPORT_STANDARD, MSG_DETECT_RELATED_BUNDLE_NOT_CACHED, wzRelatedBundleId, sczCachePath); } - hr = S_OK; - pRelatedBundle->fPlannable = BURN_CACHE_STATE_COMPLETE == cacheState; + pRelatedBundle->fPlannable = fCached; hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY, &dependencyProvider.sczKey); if (E_FILENOTFOUND != hr) @@ -464,7 +461,7 @@ static HRESULT LoadRelatedBundleFromKey( pRelatedBundle->relationType = relationType; hr = PseudoBundleInitialize(qwEngineVersion, &pRelatedBundle->package, fPerMachine, wzRelatedBundleId, pRelatedBundle->relationType, - BOOTSTRAPPER_PACKAGE_STATE_PRESENT, cacheState, sczCachePath, sczCachePath, NULL, qwFileSize, FALSE, + BOOTSTRAPPER_PACKAGE_STATE_PRESENT, fCached, sczCachePath, sczCachePath, NULL, qwFileSize, FALSE, L"-quiet", L"-repair -quiet", L"-uninstall -quiet", (dependencyProvider.sczKey && *dependencyProvider.sczKey) ? &dependencyProvider : NULL, NULL, 0); diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp index 2a34cb16..2ebbca74 100644 --- a/src/test/BurnUnitTest/PlanTest.cpp +++ b/src/test/BurnUnitTest/PlanTest.cpp @@ -127,7 +127,6 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCommitMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[23].syncpoint.hEvent); ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL); Assert::Equal(dwIndex, pPlan->cExecuteActions); @@ -509,7 +508,6 @@ namespace Bootstrapper ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[6].syncpoint.hEvent); ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL); Assert::Equal(dwIndex, pPlan->cExecuteActions); @@ -593,6 +591,7 @@ namespace Bootstrapper Assert::Equal(0ul, pPlan->cOverallProgressTicksTotal); dwIndex = 0; + ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); Assert::Equal(dwIndex, pPlan->cCleanActions); UINT uIndex = 0; @@ -1042,7 +1041,7 @@ namespace Bootstrapper void DetectPackageAsPresentAndCached(BURN_PACKAGE* pPackage) { pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; - pPackage->cache = BURN_CACHE_STATE_COMPLETE; + pPackage->fCached = TRUE; if (pPackage->fCanAffectRegistration) { pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; @@ -1158,7 +1157,7 @@ namespace Bootstrapper pRelatedBundle->fPlannable = TRUE; pRelatedBundle->relationType = BOOTSTRAPPER_RELATION_UPGRADE; - hr = PseudoBundleInitialize(0, &pRelatedBundle->package, TRUE, wzId, pRelatedBundle->relationType, BOOTSTRAPPER_PACKAGE_STATE_PRESENT, BURN_CACHE_STATE_COMPLETE, NULL, NULL, NULL, 0, FALSE, L"-quiet", L"-repair -quiet", L"-uninstall -quiet", &dependencyProvider, NULL, 0); + hr = PseudoBundleInitialize(0, &pRelatedBundle->package, TRUE, wzId, pRelatedBundle->relationType, BOOTSTRAPPER_PACKAGE_STATE_PRESENT, TRUE, NULL, NULL, NULL, 0, FALSE, L"-quiet", L"-repair -quiet", L"-uninstall -quiet", &dependencyProvider, NULL, 0); NativeAssert::Succeeded(hr, "Failed to initialize related bundle to represent bundle: %ls", wzId); ++pRelatedBundles->cRelatedBundles; -- cgit v1.2.3-55-g6feb From c88806b89293f5bb92c42e90230e48be6b79b7f4 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 16 Apr 2021 09:42:01 -0500 Subject: Detect whether the bundle is cached. --- .../inc/BootstrapperApplication.h | 1 + src/engine/cache.cpp | 1 + src/engine/core.cpp | 8 ++++---- src/engine/detect.cpp | 2 +- src/engine/engine.mc | 2 +- src/engine/registration.cpp | 7 ++++--- src/engine/registration.h | 4 ++-- src/engine/userexperience.cpp | 2 ++ src/engine/userexperience.h | 1 + 9 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h index fb4b6ea3..2d086f38 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -463,6 +463,7 @@ struct BA_ONDETECTBEGIN_ARGS DWORD cbSize; BOOL fInstalled; DWORD cPackages; + BOOL fCached; }; struct BA_ONDETECTBEGIN_RESULTS diff --git a/src/engine/cache.cpp b/src/engine/cache.cpp index 667ca9e0..46c2650a 100644 --- a/src/engine/cache.cpp +++ b/src/engine/cache.cpp @@ -733,6 +733,7 @@ extern "C" HRESULT CacheCompleteBundle( hr = PathConcat(sczTargetDirectory, wzExecutableName, &sczTargetPath); ExitOnFailure(hr, "Failed to combine completed path with engine file name."); + // We can't just use wzExecutablePath because we needed to call CreateCompletedPath to ensure that the destination was secured. Assert(CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzExecutablePath, -1, sczTargetPath, -1)); // If the bundle is running out of the package cache then we don't need to copy it there diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 7341e4b8..42759d3f 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -239,7 +239,7 @@ extern "C" HRESULT CoreQueryRegistration( SIZE_T iBuffer = 0; // Detect if bundle is already installed. - hr = RegistrationDetectInstalled(&pEngineState->registration, &pEngineState->registration.fInstalled); + hr = RegistrationDetectInstalled(&pEngineState->registration); ExitOnFailure(hr, "Failed to detect bundle install state."); // detect resume type @@ -293,7 +293,7 @@ extern "C" HRESULT CoreDetect( // only happens if Apply() changed the state of bundle (installed or // uninstalled). In that case, Detect() can be used here to reset // the installed state. - hr = RegistrationDetectInstalled(&pEngineState->registration, &pEngineState->registration.fInstalled); + hr = RegistrationDetectInstalled(&pEngineState->registration); ExitOnFailure(hr, "Failed to detect bundle install state."); if (pEngineState->registration.fInstalled) @@ -308,7 +308,7 @@ extern "C" HRESULT CoreDetect( } fDetectBegan = TRUE; - hr = UserExperienceOnDetectBegin(&pEngineState->userExperience, pEngineState->registration.fInstalled, pEngineState->packages.cPackages); + hr = UserExperienceOnDetectBegin(&pEngineState->userExperience, pEngineState->registration.fCached, pEngineState->registration.fInstalled, pEngineState->packages.cPackages); ExitOnRootFailure(hr, "UX aborted detect begin."); pEngineState->userExperience.hwndDetect = hwndParent; @@ -426,7 +426,7 @@ LExit: pEngineState->userExperience.hwndDetect = NULL; - LogId(REPORT_STANDARD, MSG_DETECT_COMPLETE, hr, !fDetectBegan ? "(failed)" : LoggingBoolToString(pEngineState->registration.fInstalled), FAILED(hr) ? "(failed)" : LoggingBoolToString(pEngineState->registration.fEligibleForCleanup)); + LogId(REPORT_STANDARD, MSG_DETECT_COMPLETE, hr, !fDetectBegan ? "(failed)" : LoggingBoolToString(pEngineState->registration.fInstalled), !fDetectBegan ? "(failed)" : LoggingBoolToString(pEngineState->registration.fCached), FAILED(hr) ? "(failed)" : LoggingBoolToString(pEngineState->registration.fEligibleForCleanup)); return hr; } diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp index 355b49f5..844816ba 100644 --- a/src/engine/detect.cpp +++ b/src/engine/detect.cpp @@ -173,7 +173,7 @@ extern "C" HRESULT DetectReportRelatedBundles( HRESULT hr = S_OK; int nCompareResult = 0; BOOTSTRAPPER_REQUEST_STATE uninstallRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; - *pfEligibleForCleanup = pRegistration->fInstalled || CacheBundleRunningFromCache(); + *pfEligibleForCleanup = pRegistration->fInstalled || pRegistration->fCached; for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) { diff --git a/src/engine/engine.mc b/src/engine/engine.mc index 365da1d9..71c27cad 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -300,7 +300,7 @@ MessageId=199 Severity=Success SymbolicName=MSG_DETECT_COMPLETE Language=English -Detect complete, result: 0x%1!x!, installed: %2!hs!, eligible for cleanup: %3!hs! +Detect complete, result: 0x%1!x!, installed: %2!hs!, cached: %3!hs!, eligible for cleanup: %4!hs! . MessageId=200 diff --git a/src/engine/registration.cpp b/src/engine/registration.cpp index 9e27b177..7435f292 100644 --- a/src/engine/registration.cpp +++ b/src/engine/registration.cpp @@ -463,14 +463,15 @@ LExit: } extern "C" HRESULT RegistrationDetectInstalled( - __in BURN_REGISTRATION* pRegistration, - __out BOOL* pfInstalled + __in BURN_REGISTRATION* pRegistration ) { HRESULT hr = S_OK; HKEY hkRegistration = NULL; DWORD dwInstalled = 0; + pRegistration->fCached = FileExistsEx(pRegistration->sczCacheExecutablePath, NULL); + // open registration key hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration); if (SUCCEEDED(hr)) @@ -484,7 +485,7 @@ extern "C" HRESULT RegistrationDetectInstalled( hr = S_OK; } - *pfInstalled = (1 == dwInstalled); + pRegistration->fInstalled = (1 == dwInstalled); ReleaseRegKey(hkRegistration); return hr; diff --git a/src/engine/registration.h b/src/engine/registration.h index ed0641eb..af1b42e4 100644 --- a/src/engine/registration.h +++ b/src/engine/registration.h @@ -92,6 +92,7 @@ typedef struct _BURN_REGISTRATION BOOL fPerMachine; BOOL fRegisterArp; BOOL fDisableResume; + BOOL fCached; BOOL fInstalled; LPWSTR sczId; LPWSTR sczTag; @@ -174,8 +175,7 @@ HRESULT RegistrationSetVariables( __in BURN_VARIABLES* pVariables ); HRESULT RegistrationDetectInstalled( - __in BURN_REGISTRATION* pRegistration, - __out BOOL* pfInstalled + __in BURN_REGISTRATION* pRegistration ); HRESULT RegistrationDetectResumeType( __in BURN_REGISTRATION* pRegistration, diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index b81dbfb2..c1641675 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -708,6 +708,7 @@ LExit: EXTERN_C BAAPI UserExperienceOnDetectBegin( __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fCached, __in BOOL fInstalled, __in DWORD cPackages ) @@ -719,6 +720,7 @@ EXTERN_C BAAPI UserExperienceOnDetectBegin( args.cbSize = sizeof(args); args.cPackages = cPackages; args.fInstalled = fInstalled; + args.fCached = fCached; results.cbSize = sizeof(results); diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index cef9d769..a1fb67a0 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -186,6 +186,7 @@ BAAPI UserExperienceOnCommitMsiTransactionComplete( ); BAAPI UserExperienceOnDetectBegin( __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fCached, __in BOOL fInstalled, __in DWORD cPackages ); -- cgit v1.2.3-55-g6feb From 90cdf39e6e6b7d676ca33bee031fa2b865bb5fbd Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 16 Apr 2021 10:09:26 -0500 Subject: Don't plan payloads. Contributes to #3640 and #5253 --- src/engine/apply.cpp | 758 +++++++++++++++---------------- src/engine/cache.cpp | 15 +- src/engine/cache.h | 1 + src/engine/container.cpp | 12 +- src/engine/container.h | 7 +- src/engine/core.cpp | 26 +- src/engine/core.h | 2 + src/engine/detect.cpp | 5 - src/engine/engine.mc | 4 +- src/engine/exeengine.cpp | 32 +- src/engine/manifest.cpp | 2 +- src/engine/msiengine.cpp | 47 +- src/engine/mspengine.cpp | 3 +- src/engine/msuengine.cpp | 12 +- src/engine/package.cpp | 14 +- src/engine/package.h | 9 +- src/engine/payload.cpp | 41 +- src/engine/payload.h | 12 + src/engine/plan.cpp | 893 ++++++------------------------------- src/engine/plan.h | 106 +---- src/engine/pseudobundle.cpp | 75 +--- src/engine/relatedbundle.cpp | 9 +- src/engine/userexperience.cpp | 2 +- src/test/BurnUnitTest/PlanTest.cpp | 211 ++------- 24 files changed, 718 insertions(+), 1580 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index b79bf934..7e03ebf9 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -13,18 +13,28 @@ const DWORD BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS = 2; // structs -struct BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT +typedef struct _BURN_CACHE_CONTEXT { BURN_USER_EXPERIENCE* pUX; + BURN_VARIABLES* pVariables; + BURN_PAYLOADS* pPayloads; + HANDLE hPipe; + HANDLE hSourceEngineFile; + DWORD64 qwTotalCacheSize; + DWORD64 qwSuccessfulCacheProgress; + LPCWSTR wzLayoutDirectory; +} BURN_CACHE_CONTEXT; + +typedef struct _BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT +{ + BURN_CACHE_CONTEXT* pCacheContext; BURN_CONTAINER* pContainer; BURN_PACKAGE* pPackage; BURN_PAYLOAD* pPayload; - DWORD64 qwCacheProgress; - DWORD64 qwTotalCacheSize; BOOL fCancel; BOOL fError; -}; +} BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT; typedef struct _BURN_EXECUTE_CONTEXT { @@ -56,50 +66,49 @@ static HRESULT ExecuteDependentRegistrationActions( __in_ecount(cActions) const BURN_DEPENDENT_REGISTRATION_ACTION* rgActions, __in DWORD cActions ); -static HRESULT ExtractContainer( - __in HANDLE hSourceEngineFile, - __in BURN_CONTAINER* pContainer, - __in_z LPCWSTR wzContainerPath, - __in_ecount(cExtractPayloads) BURN_EXTRACT_PAYLOAD* rgExtractPayloads, - __in DWORD cExtractPayloads +static HRESULT ApplyCachePackage( + __in BURN_CACHE_CONTEXT* pContext, + __in BURN_PACKAGE* pPackage ); -static void UpdateCacheSuccessProgress( - __in BURN_PLAN* pPlan, - __in BURN_CACHE_ACTION* pCacheAction, - __inout DWORD64* pqwSuccessfulCachedProgress +static HRESULT ApplyExtractContainer( + __in BURN_CACHE_CONTEXT* pContext, + __in BURN_CONTAINER* pContainer + ); +static HRESULT ApplyLayoutBundle( + __in BURN_CACHE_CONTEXT* pContext, + __in BURN_PAYLOAD_GROUP* pPayloads, + __in_z LPCWSTR wzExecutableName, + __in_z LPCWSTR wzUnverifiedPath + ); +static HRESULT ApplyLayoutContainer( + __in BURN_CACHE_CONTEXT* pContext, + __in BURN_CONTAINER* pContainer + ); +static HRESULT ApplyProcessPayload( + __in BURN_CACHE_CONTEXT* pContext, + __in_opt BURN_PACKAGE* pPackage, + __in BURN_PAYLOAD* pPayload + ); +static HRESULT ExtractContainer( + __in BURN_CACHE_CONTEXT* pContext, + __in BURN_CONTAINER* pContainer ); static HRESULT LayoutBundle( - __in HANDLE hSourceEngineFile, - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_VARIABLES* pVariables, - __in HANDLE hPipe, + __in BURN_CACHE_CONTEXT* pContext, __in_z LPCWSTR wzExecutableName, - __in_z LPCWSTR wzLayoutDirectory, - __in_z LPCWSTR wzUnverifiedPath, - __in DWORD64 qwSuccessfulCacheProgress, - __in DWORD64 qwTotalCacheSize + __in_z LPCWSTR wzUnverifiedPath ); static HRESULT AcquireContainerOrPayload( - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_VARIABLES* pVariables, + __in BURN_CACHE_CONTEXT* pContext, __in_opt BURN_CONTAINER* pContainer, __in_opt BURN_PACKAGE* pPackage, - __in_opt BURN_PAYLOAD* pPayload, - __in LPCWSTR wzDestinationPath, - __in DWORD64 qwSuccessfulCacheProgress, - __in DWORD64 qwTotalCacheSize + __in_opt BURN_PAYLOAD* pPayload ); static HRESULT LayoutOrCacheContainerOrPayload( - __in BURN_USER_EXPERIENCE* pUX, - __in HANDLE hPipe, + __in BURN_CACHE_CONTEXT* pContext, __in_opt BURN_CONTAINER* pContainer, __in_opt BURN_PACKAGE* pPackage, __in_opt BURN_PAYLOAD* pPayload, - __in BOOL fAlreadyProvidedProgress, - __in DWORD64 qwSuccessfullyCacheProgress, - __in DWORD64 qwTotalCacheSize, - __in_z_opt LPCWSTR wzLayoutDirectory, - __in_z LPCWSTR wzUnverifiedPath, __in BOOL fMove, __in DWORD cTryAgainAttempts, __out BOOL* pfRetry @@ -461,268 +470,83 @@ extern "C" HRESULT ApplyCache( { HRESULT hr = S_OK; DWORD dwCheckpoint = 0; - BOOL fRetry = FALSE; - DWORD iRetryAction = BURN_PLAN_INVALID_ACTION_INDEX; - BURN_PACKAGE* pStartedPackage = NULL; - DWORD64 qwSuccessfulCachedProgress = 0; - - // Allow us to retry and skip packages. - DWORD iPackageStartAction = BURN_PLAN_INVALID_ACTION_INDEX; - DWORD iPackageCompleteAction = BURN_PLAN_INVALID_ACTION_INDEX; + BURN_CACHE_CONTEXT cacheContext = { }; + BURN_PACKAGE* pPackage = NULL; *pfRollback = FALSE; hr = UserExperienceOnCacheBegin(pUX); ExitOnRootFailure(hr, "BA aborted cache."); - do - { - hr = S_OK; - fRetry = FALSE; + cacheContext.hSourceEngineFile = hSourceEngineFile; + cacheContext.pPayloads = pPlan->pPayloads; + cacheContext.pUX = pUX; + cacheContext.pVariables = pVariables; + cacheContext.qwTotalCacheSize = pPlan->qwCacheSizeTotal; + cacheContext.wzLayoutDirectory = pPlan->sczLayoutDirectory; - // Allow us to retry just a container or payload. - LPCWSTR wzRetryId = NULL; - DWORD iRetryContainerOrPayloadAction = BURN_PLAN_INVALID_ACTION_INDEX; - BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION cachePackageCompleteAction = BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE; + for (DWORD i = 0; i < pPlan->cCacheActions; ++i) + { + BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + i; + cacheContext.hPipe = hPipe; + pPackage = NULL; - // cache actions - for (DWORD i = (BURN_PLAN_INVALID_ACTION_INDEX == iRetryAction) ? 0 : iRetryAction; SUCCEEDED(hr) && i < pPlan->cCacheActions; ++i) + switch (pCacheAction->type) { - BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + i; - BOOL fRetryContainerOrPayload = FALSE; - cachePackageCompleteAction = BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE; - - if (pCacheAction->fSkipUntilRetried) - { - // If this action was retried, let's make sure it will not be skipped any longer. - if (iRetryAction == i) - { - pCacheAction->fSkipUntilRetried = FALSE; - } - else // skip the action. - { - continue; - } - } - - switch (pCacheAction->type) - { - case BURN_CACHE_ACTION_TYPE_CHECKPOINT: - dwCheckpoint = pCacheAction->checkpoint.dwId; - break; - - case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: - hr = LayoutBundle(hSourceEngineFile, pUX, pVariables, hPipe, pCacheAction->bundleLayout.sczExecutableName, pCacheAction->bundleLayout.sczLayoutDirectory, pCacheAction->bundleLayout.sczUnverifiedPath, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal); - if (SUCCEEDED(hr)) - { - UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); - ++(*pcOverallProgressTicks); - - hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks); - if (FAILED(hr)) - { - LogErrorId(hr, MSG_USER_CANCELED, L"layout bundle", NULL, NULL); - } - } - break; - - case BURN_CACHE_ACTION_TYPE_PACKAGE_START: - iPackageStartAction = i; // if we retry this package, we'll start here in the plan. - iPackageCompleteAction = pCacheAction->packageStart.iPackageCompleteAction; // if we ignore this package, we'll start after the complete action in the plan. - pStartedPackage = pCacheAction->packageStart.pPackage; - - hr = UserExperienceOnCachePackageBegin(pUX, pStartedPackage->sczId, pCacheAction->packageStart.cCachePayloads, pCacheAction->packageStart.qwCachePayloadSizeTotal); - if (FAILED(hr)) - { - LogErrorId(hr, MSG_USER_CANCELED, L"begin cache package", pStartedPackage->sczId, NULL); - } - break; - - case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER: - hr = AcquireContainerOrPayload(pUX, pVariables, pCacheAction->resolveContainer.pContainer, NULL, NULL, pCacheAction->resolveContainer.sczUnverifiedPath, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal); - if (SUCCEEDED(hr)) - { - UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); - } - else - { - LogErrorId(hr, MSG_FAILED_ACQUIRE_CONTAINER, pCacheAction->resolveContainer.pContainer->sczId, pCacheAction->resolveContainer.sczUnverifiedPath, NULL); - } - break; - - case BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER: - // If this action is to be skipped until the acquire action is not skipped and the other - // action is still being skipped then skip this action. - if (BURN_PLAN_INVALID_ACTION_INDEX != pCacheAction->extractContainer.iSkipUntilAcquiredByAction && pPlan->rgCacheActions[pCacheAction->extractContainer.iSkipUntilAcquiredByAction].fSkipUntilRetried) - { - break; - } - - hr = ExtractContainer(hSourceEngineFile, pCacheAction->extractContainer.pContainer, pCacheAction->extractContainer.sczContainerUnverifiedPath, pCacheAction->extractContainer.rgPayloads, pCacheAction->extractContainer.cPayloads); - if (FAILED(hr)) - { - LogErrorId(hr, MSG_FAILED_EXTRACT_CONTAINER, pCacheAction->extractContainer.pContainer->sczId, pCacheAction->extractContainer.sczContainerUnverifiedPath, NULL); - } - break; - - case BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER: - hr = LayoutOrCacheContainerOrPayload(pUX, hPipe, pCacheAction->layoutContainer.pContainer, pCacheAction->layoutContainer.pPackage, NULL, pPlan->rgContainerProgress[pCacheAction->layoutContainer.iProgress].fCachedDuringApply, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal, pCacheAction->layoutContainer.sczLayoutDirectory, pCacheAction->layoutContainer.sczUnverifiedPath, pCacheAction->layoutContainer.fMove, pCacheAction->layoutContainer.cTryAgainAttempts, &fRetryContainerOrPayload); - if (SUCCEEDED(hr)) - { - UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); - } - else - { - LogErrorId(hr, MSG_FAILED_LAYOUT_CONTAINER, pCacheAction->layoutContainer.pContainer->sczId, pCacheAction->layoutContainer.sczLayoutDirectory, pCacheAction->layoutContainer.sczUnverifiedPath); - - if (fRetryContainerOrPayload) - { - wzRetryId = pCacheAction->layoutContainer.pContainer->sczId; - iRetryContainerOrPayloadAction = pCacheAction->layoutContainer.iTryAgainAction; - - ++pCacheAction->layoutContainer.cTryAgainAttempts; - } - } - break; - - case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD: - hr = AcquireContainerOrPayload(pUX, pVariables, NULL, pCacheAction->resolvePayload.pPackage, pCacheAction->resolvePayload.pPayload, pCacheAction->resolvePayload.sczUnverifiedPath, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal); - if (SUCCEEDED(hr)) - { - UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); - } - else - { - LogErrorId(hr, MSG_FAILED_ACQUIRE_PAYLOAD, pCacheAction->resolvePayload.pPayload->sczKey, pCacheAction->resolvePayload.sczUnverifiedPath, NULL); - } - break; - - case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD: - hr = LayoutOrCacheContainerOrPayload(pUX, pCacheAction->cachePayload.pPackage->fPerMachine ? hPipe : INVALID_HANDLE_VALUE, NULL, pCacheAction->cachePayload.pPackage, pCacheAction->cachePayload.pPayload, pPlan->rgPayloadProgress[pCacheAction->cachePayload.iProgress].fCachedDuringApply, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal, NULL, pCacheAction->cachePayload.sczUnverifiedPath, pCacheAction->cachePayload.fMove, pCacheAction->cachePayload.cTryAgainAttempts, &fRetryContainerOrPayload); - if (SUCCEEDED(hr)) - { - UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); - } - else - { - LogErrorId(hr, MSG_FAILED_CACHE_PAYLOAD, pCacheAction->cachePayload.pPayload->sczKey, pCacheAction->cachePayload.sczUnverifiedPath, NULL); - - if (fRetryContainerOrPayload) - { - wzRetryId = pCacheAction->cachePayload.pPayload->sczKey; - iRetryContainerOrPayloadAction = pCacheAction->cachePayload.iTryAgainAction; - - ++pCacheAction->cachePayload.cTryAgainAttempts; - } - } - break; - - case BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD: - hr = LayoutOrCacheContainerOrPayload(pUX, hPipe, NULL, pCacheAction->layoutPayload.pPackage, pCacheAction->layoutPayload.pPayload, pPlan->rgPayloadProgress[pCacheAction->layoutPayload.iProgress].fCachedDuringApply, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal, pCacheAction->layoutPayload.sczLayoutDirectory, pCacheAction->layoutPayload.sczUnverifiedPath, pCacheAction->layoutPayload.fMove, pCacheAction->layoutPayload.cTryAgainAttempts, &fRetryContainerOrPayload); - if (SUCCEEDED(hr)) - { - UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); - } - else - { - LogErrorId(hr, MSG_FAILED_LAYOUT_PAYLOAD, pCacheAction->layoutPayload.pPayload->sczKey, pCacheAction->layoutPayload.sczLayoutDirectory, pCacheAction->layoutPayload.sczUnverifiedPath); - - if (fRetryContainerOrPayload) - { - wzRetryId = pCacheAction->layoutPayload.pPayload->sczKey; - iRetryContainerOrPayloadAction = pCacheAction->layoutPayload.iTryAgainAction; - - ++pCacheAction->layoutPayload.cTryAgainAttempts; - } - } - break; - - case BURN_CACHE_ACTION_TYPE_PACKAGE_STOP: - AssertSz(pStartedPackage == pCacheAction->packageStop.pPackage, "Expected package started cached to be the same as the package checkpointed."); + case BURN_CACHE_ACTION_TYPE_CHECKPOINT: + dwCheckpoint = pCacheAction->checkpoint.dwId; + break; - hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks + 1); - if (FAILED(hr)) - { - LogErrorId(hr, MSG_USER_CANCELED, L"end cache package", NULL, NULL); - } - else - { - ++(*pcOverallProgressTicks); + case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: + hr = ApplyLayoutBundle(&cacheContext, pCacheAction->bundleLayout.pPayloadGroup, pCacheAction->bundleLayout.sczExecutableName, pCacheAction->bundleLayout.sczUnverifiedPath); + ExitOnFailure(hr, "Failed cache action: %ls", L"layout bundle"); - UserExperienceOnCachePackageComplete(pUX, pStartedPackage->sczId, hr, &cachePackageCompleteAction); + ++(*pcOverallProgressTicks); - pStartedPackage->hrCacheResult = hr; + hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks); + LogExitOnFailure(hr, MSG_USER_CANCELED, "Cancel during cache: %ls", L"layout bundle"); - iPackageStartAction = BURN_PLAN_INVALID_ACTION_INDEX; - iPackageCompleteAction = BURN_PLAN_INVALID_ACTION_INDEX; - pStartedPackage = NULL; - } - break; + break; - case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT: - if (!::SetEvent(pCacheAction->syncpoint.hEvent)) - { - ExitWithLastError(hr, "Failed to set syncpoint event."); - } - break; + case BURN_CACHE_ACTION_TYPE_PACKAGE: + pPackage = pCacheAction->package.pPackage; - default: - AssertSz(FALSE, "Unknown cache action."); - break; + if (!pPackage->fPerMachine && !cacheContext.wzLayoutDirectory) + { + cacheContext.hPipe = INVALID_HANDLE_VALUE; } - } - - if (BURN_PLAN_INVALID_ACTION_INDEX != iRetryContainerOrPayloadAction) - { - Assert(wzRetryId); - LogErrorId(hr, MSG_APPLY_RETRYING_PAYLOAD, wzRetryId, NULL, NULL); + hr = ApplyCachePackage(&cacheContext, pPackage); + ExitOnFailure(hr, "Failed cache action: %ls", L"cache package"); - iRetryAction = iRetryContainerOrPayloadAction; - fRetry = TRUE; - } - else if (pStartedPackage) - { - Assert(BURN_PLAN_INVALID_ACTION_INDEX != iPackageStartAction); - Assert(BURN_PLAN_INVALID_ACTION_INDEX != iPackageCompleteAction); + ++(*pcOverallProgressTicks); - cachePackageCompleteAction = SUCCEEDED(hr) || pStartedPackage->fVital ? BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE; - UserExperienceOnCachePackageComplete(pUX, pStartedPackage->sczId, hr, &cachePackageCompleteAction); - if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_RETRY == cachePackageCompleteAction) - { - LogErrorId(hr, MSG_APPLY_RETRYING_PACKAGE, pStartedPackage->sczId, NULL, NULL); + hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks); + LogExitOnFailure(hr, MSG_USER_CANCELED, "Cancel during cache: %ls", L"cache package"); - iRetryAction = iPackageStartAction; - fRetry = TRUE; - } - else if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE == cachePackageCompleteAction && !pStartedPackage->fVital) // ignore non-vital download failures. - { - LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, pStartedPackage->sczId, hr); + break; - ++(*pcOverallProgressTicks); // add progress even though we didn't fully cache the package. + case BURN_CACHE_ACTION_TYPE_CONTAINER: + Assert(pPlan->sczLayoutDirectory); + hr = ApplyLayoutContainer(&cacheContext, pCacheAction->container.pContainer); + ExitOnFailure(hr, "Failed cache action: %ls", L"layout container"); + + break; - iRetryAction = iPackageCompleteAction + 1; - fRetry = TRUE; + case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT: + if (!::SetEvent(pCacheAction->syncpoint.hEvent)) + { + ExitWithLastError(hr, "Failed to set syncpoint event."); } + break; - pStartedPackage->hrCacheResult = hr; - - iPackageStartAction = BURN_PLAN_INVALID_ACTION_INDEX; - iPackageCompleteAction = BURN_PLAN_INVALID_ACTION_INDEX; - pStartedPackage = NULL; - } - else - { - Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageStartAction); - Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageCompleteAction); + default: + AssertSz(FALSE, "Unknown cache action."); + break; } - } while (fRetry); + } LExit: - Assert(NULL == pStartedPackage); - Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageStartAction); - Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageCompleteAction); - if (FAILED(hr)) { DoRollbackCache(pUX, pPlan, hPipe, dwCheckpoint); @@ -936,12 +760,216 @@ LExit: return hr; } +static HRESULT ApplyCachePackage( + __in BURN_CACHE_CONTEXT* pContext, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION cachePackageCompleteAction = BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE; + + for (;;) + { + hr = UserExperienceOnCachePackageBegin(pContext->pUX, pPackage->sczId, pPackage->payloads.cPayloads, pPackage->payloads.qwTotalSize); + LogExitOnFailure(hr, MSG_USER_CANCELED, "Cancel during cache: %ls: %ls", L"begin cache package", pPackage->sczId); + + for (DWORD i = 0; i < pPackage->payloads.cPayloads; ++i) + { + BURN_PAYLOAD* pPayload = pPackage->payloads.rgpPayloads[i]; + + hr = ApplyProcessPayload(pContext, pPackage, pPayload); + if (FAILED(hr)) + { + break; + } + } + + pPackage->hrCacheResult = hr; + cachePackageCompleteAction = SUCCEEDED(hr) || pPackage->fVital ? BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE; + UserExperienceOnCachePackageComplete(pContext->pUX, pPackage->sczId, hr, &cachePackageCompleteAction); + + if (SUCCEEDED(hr)) + { + break; + } + + if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_RETRY == cachePackageCompleteAction) + { + // TODO: the progress needs to account for the payloads (potentially) being recached. + LogErrorId(hr, MSG_APPLY_RETRYING_PACKAGE, pPackage->sczId, NULL, NULL); + + continue; + } + else if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE == cachePackageCompleteAction && !pPackage->fVital) // ignore non-vital download failures. + { + LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, pPackage->sczId, hr); + hr = S_OK; + } + + break; + } + +LExit: + return hr; +} + +static HRESULT ApplyExtractContainer( + __in BURN_CACHE_CONTEXT* pContext, + __in BURN_CONTAINER* pContainer + ) +{ + HRESULT hr = S_OK; + + if (!pContainer->fActuallyAttached) + { + hr = AcquireContainerOrPayload(pContext, pContainer, NULL, NULL); + LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_CONTAINER, "Failed to acquire container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath); + } + + pContext->qwSuccessfulCacheProgress += pContainer->qwFileSize; + + hr = ExtractContainer(pContext, pContainer); + LogExitOnFailure(hr, MSG_FAILED_EXTRACT_CONTAINER, "Failed to extract payloads from container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath); + + pContext->qwSuccessfulCacheProgress += pContainer->qwFileSize; + +LExit: + return hr; +} + +static HRESULT ApplyLayoutBundle( + __in BURN_CACHE_CONTEXT* pContext, + __in BURN_PAYLOAD_GROUP* pPayloads, + __in_z LPCWSTR wzExecutableName, + __in_z LPCWSTR wzUnverifiedPath + ) +{ + HRESULT hr = S_OK; + + hr = LayoutBundle(pContext, wzExecutableName, wzUnverifiedPath); + ExitOnFailure(hr, "Failed to layout bundle."); + + for (DWORD i = 0; i < pPayloads->cPayloads; ++i) + { + BURN_PAYLOAD* pPayload = pPayloads->rgpPayloads[i]; + + hr = ApplyProcessPayload(pContext, NULL, pPayload); + ExitOnFailure(hr, "Failed to layout bundle payload: %ls", pPayload->sczKey); + } + +LExit: + return hr; +} + +static HRESULT ApplyLayoutContainer( + __in BURN_CACHE_CONTEXT* pContext, + __in BURN_CONTAINER* pContainer + ) +{ + HRESULT hr = S_OK; + DWORD cTryAgainAttempts = 0; + BOOL fRetry = FALSE; + + Assert(!pContainer->fAttached); + + for (;;) + { + fRetry = FALSE; + + hr = AcquireContainerOrPayload(pContext, pContainer, NULL, NULL); + LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_CONTAINER, "Failed to acquire container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath); + + pContext->qwSuccessfulCacheProgress += pContainer->qwFileSize; + + hr = LayoutOrCacheContainerOrPayload(pContext, pContainer, NULL, NULL, TRUE, cTryAgainAttempts, &fRetry); + if (SUCCEEDED(hr)) + { + pContext->qwSuccessfulCacheProgress += pContainer->qwFileSize; + break; + } + else + { + LogErrorId(hr, MSG_FAILED_LAYOUT_CONTAINER, pContainer->sczId, pContext->wzLayoutDirectory, pContainer->sczUnverifiedPath); + + if (!fRetry) + { + ExitFunction(); + } + + ++cTryAgainAttempts; + pContext->qwSuccessfulCacheProgress -= pContainer->qwFileSize; + LogErrorId(hr, MSG_APPLY_RETRYING_PAYLOAD, pContainer->sczId, NULL, NULL); + } + } + +LExit: + return hr; +} + +static HRESULT ApplyProcessPayload( + __in BURN_CACHE_CONTEXT* pContext, + __in_opt BURN_PACKAGE* pPackage, + __in BURN_PAYLOAD* pPayload + ) +{ + HRESULT hr = S_OK; + DWORD cTryAgainAttempts = 0; + BOOL fRetry = FALSE; + + Assert(pContext->pPayloads || pContext->wzLayoutDirectory); + + for (;;) + { + fRetry = FALSE; + + if (pPayload->pContainer) + { + if (pContext->wzLayoutDirectory) + { + ExitFunction(); + } + + // TODO: only extract container if payload isn't already cached and isn't already extracted + hr = ApplyExtractContainer(pContext, pPayload->pContainer); + ExitOnFailure(hr, "Failed to extract container for payload: %ls", pPayload->sczKey); + } + else + { + hr = AcquireContainerOrPayload(pContext, NULL, pPackage, pPayload); + LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_PAYLOAD, "Failed to acquire payload: %ls to working path: %ls", pPayload->sczKey, pPayload->sczUnverifiedPath); + } + + pContext->qwSuccessfulCacheProgress += pPayload->qwFileSize; + + // TODO: set fMove to TRUE appropriately + hr = LayoutOrCacheContainerOrPayload(pContext, NULL, pPackage, pPayload, FALSE, cTryAgainAttempts, &fRetry); + if (SUCCEEDED(hr)) + { + pContext->qwSuccessfulCacheProgress += pPayload->qwFileSize; + break; + } + else + { + LogErrorId(hr, pContext->wzLayoutDirectory ? MSG_FAILED_LAYOUT_PAYLOAD : MSG_FAILED_CACHE_PAYLOAD, pPayload->sczKey, pContext->wzLayoutDirectory, pPayload->sczUnverifiedPath); + + if (!fRetry) + { + ExitFunction(); + } + + ++cTryAgainAttempts; + pContext->qwSuccessfulCacheProgress -= pPayload->qwFileSize; + LogErrorId(hr, MSG_APPLY_RETRYING_PAYLOAD, pPayload->sczKey, NULL, NULL); + } + } + +LExit: + return hr; +} + static HRESULT ExtractContainer( - __in HANDLE hSourceEngineFile, - __in BURN_CONTAINER* pContainer, - __in_z LPCWSTR wzContainerPath, - __in_ecount(cExtractPayloads) BURN_EXTRACT_PAYLOAD* rgExtractPayloads, - __in DWORD cExtractPayloads + __in BURN_CACHE_CONTEXT* pContext, + __in BURN_CONTAINER* pContainer ) { HRESULT hr = S_OK; @@ -952,20 +980,20 @@ static HRESULT ExtractContainer( // If the container is actually attached, then it was planned to be acquired through hSourceEngineFile. if (pContainer->fActuallyAttached) { - hContainerHandle = hSourceEngineFile; + hContainerHandle = pContext->hSourceEngineFile; } - hr = ContainerOpen(&context, pContainer, hContainerHandle, wzContainerPath); + hr = ContainerOpen(&context, pContainer, hContainerHandle, pContainer->sczUnverifiedPath); ExitOnFailure(hr, "Failed to open container: %ls.", pContainer->sczId); while (S_OK == (hr = ContainerNextStream(&context, &sczExtractPayloadId))) { BOOL fExtracted = FALSE; - for (DWORD iExtract = 0; iExtract < cExtractPayloads; ++iExtract) + for (DWORD iExtract = 0; iExtract < pContext->pPayloads->cPayloads; ++iExtract) { - BURN_EXTRACT_PAYLOAD* pExtract = rgExtractPayloads + iExtract; - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczExtractPayloadId, -1, pExtract->pPayload->sczSourcePath, -1)) + BURN_PAYLOAD* pExtract = pContext->pPayloads->rgPayloads + iExtract; + if (pExtract->sczUnverifiedPath && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczExtractPayloadId, -1, pExtract->sczSourcePath, -1)) { // TODO: Send progress when extracting stream to file. hr = ContainerStreamToFile(&context, pExtract->sczUnverifiedPath); @@ -996,74 +1024,10 @@ LExit: return hr; } -static void UpdateCacheSuccessProgress( - __in BURN_PLAN* pPlan, - __in BURN_CACHE_ACTION* pCacheAction, - __inout DWORD64* pqwSuccessfulCachedProgress - ) -{ - switch (pCacheAction->type) - { - case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: - *pqwSuccessfulCachedProgress += pCacheAction->bundleLayout.qwBundleSize; - break; - - case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER: - if (!pPlan->rgContainerProgress[pCacheAction->resolveContainer.iProgress].fCachedDuringApply) - { - pPlan->rgContainerProgress[pCacheAction->resolveContainer.iProgress].fCachedDuringApply = TRUE; - *pqwSuccessfulCachedProgress += pCacheAction->resolveContainer.pContainer->qwFileSize; - } - break; - - case BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER: - if (!pPlan->rgContainerProgress[pCacheAction->layoutContainer.iProgress].fCachedDuringApply) - { - pPlan->rgContainerProgress[pCacheAction->layoutContainer.iProgress].fCachedDuringApply = TRUE; - *pqwSuccessfulCachedProgress += pCacheAction->layoutContainer.pContainer->qwFileSize; - } - break; - - case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD: - if (!pPlan->rgPayloadProgress[pCacheAction->resolvePayload.iProgress].fCachedDuringApply) - { - pPlan->rgPayloadProgress[pCacheAction->resolvePayload.iProgress].fCachedDuringApply = TRUE; - *pqwSuccessfulCachedProgress += pCacheAction->resolvePayload.pPayload->qwFileSize; - } - break; - - case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD: - if (!pPlan->rgPayloadProgress[pCacheAction->cachePayload.iProgress].fCachedDuringApply) - { - pPlan->rgPayloadProgress[pCacheAction->cachePayload.iProgress].fCachedDuringApply = TRUE; - *pqwSuccessfulCachedProgress += pCacheAction->cachePayload.pPayload->qwFileSize; - } - break; - - case BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD: - if (!pPlan->rgPayloadProgress[pCacheAction->layoutPayload.iProgress].fCachedDuringApply) - { - pPlan->rgPayloadProgress[pCacheAction->layoutPayload.iProgress].fCachedDuringApply = TRUE; - *pqwSuccessfulCachedProgress += pCacheAction->layoutPayload.pPayload->qwFileSize; - } - break; - - default: - AssertSz(FALSE, "Unexpected cache action type."); - break; - } -} - static HRESULT LayoutBundle( - __in HANDLE hSourceEngineFile, - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_VARIABLES* pVariables, - __in HANDLE hPipe, + __in BURN_CACHE_CONTEXT* pContext, __in_z LPCWSTR wzExecutableName, - __in_z LPCWSTR wzLayoutDirectory, - __in_z LPCWSTR wzUnverifiedPath, - __in DWORD64 qwSuccessfulCacheProgress, - __in DWORD64 qwTotalCacheSize + __in_z LPCWSTR wzUnverifiedPath ) { HRESULT hr = S_OK; @@ -1074,7 +1038,7 @@ static HRESULT LayoutBundle( BOOL fRetry = FALSE; BOOL fRetryAcquire = FALSE; - hr = VariableGetString(pVariables, BURN_BUNDLE_SOURCE_PROCESS_PATH, &sczBundlePath); + hr = VariableGetString(pContext->pVariables, BURN_BUNDLE_SOURCE_PROCESS_PATH, &sczBundlePath); if (FAILED(hr)) { if (E_NOTFOUND != hr) @@ -1086,7 +1050,7 @@ static HRESULT LayoutBundle( ExitOnFailure(hr, "Failed to get path to bundle to layout."); } - hr = PathConcat(wzLayoutDirectory, wzExecutableName, &sczDestinationPath); + hr = PathConcat(pContext->wzLayoutDirectory, wzExecutableName, &sczDestinationPath); ExitOnFailure(hr, "Failed to concat layout path for bundle."); // If the destination path is the currently running bundle, bail. @@ -1098,9 +1062,7 @@ static HRESULT LayoutBundle( ExitFunction1(hr = S_OK); } - progress.pUX = pUX; - progress.qwCacheProgress = qwSuccessfulCacheProgress; - progress.qwTotalCacheSize = qwTotalCacheSize; + progress.pCacheContext = pContext; do { @@ -1112,13 +1074,13 @@ static HRESULT LayoutBundle( fRetryAcquire = FALSE; progress.fCancel = FALSE; - hr = UserExperienceOnCacheAcquireBegin(pUX, NULL, NULL, BOOTSTRAPPER_CACHE_OPERATION_COPY, sczBundlePath); + hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, NULL, NULL, BOOTSTRAPPER_CACHE_OPERATION_COPY, sczBundlePath); ExitOnRootFailure(hr, "BA aborted cache acquire begin."); - hr = CopyPayload(&progress, hSourceEngineFile, sczBundlePath, wzUnverifiedPath); + hr = CopyPayload(&progress, pContext->hSourceEngineFile, sczBundlePath, wzUnverifiedPath); // Error handling happens after sending complete message to BA. - UserExperienceOnCacheAcquireComplete(pUX, NULL, NULL, hr, &fRetryAcquire); + UserExperienceOnCacheAcquireComplete(pContext->pUX, NULL, NULL, hr, &fRetryAcquire); if (fRetryAcquire) { continue; @@ -1130,20 +1092,20 @@ static HRESULT LayoutBundle( do { - hr = UserExperienceOnCacheVerifyBegin(pUX, NULL, NULL); + hr = UserExperienceOnCacheVerifyBegin(pContext->pUX, NULL, NULL); ExitOnRootFailure(hr, "BA aborted cache verify begin."); - if (INVALID_HANDLE_VALUE != hPipe) + if (INVALID_HANDLE_VALUE != pContext->hPipe) { - hr = ElevationLayoutBundle(hPipe, wzLayoutDirectory, wzUnverifiedPath); + hr = ElevationLayoutBundle(pContext->hPipe, pContext->wzLayoutDirectory, wzUnverifiedPath); } else { - hr = CacheLayoutBundle(wzExecutableName, wzLayoutDirectory, wzUnverifiedPath); + hr = CacheLayoutBundle(wzExecutableName, pContext->wzLayoutDirectory, wzUnverifiedPath); } BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE; - UserExperienceOnCacheVerifyComplete(pUX, NULL, NULL, hr, &action); + UserExperienceOnCacheVerifyComplete(pContext->pUX, NULL, NULL, hr, &action); if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action) { hr = S_FALSE; // retry verify. @@ -1154,7 +1116,7 @@ static HRESULT LayoutBundle( } } while (S_FALSE == hr); } while (fRetry); - LogExitOnFailure(hr, MSG_FAILED_LAYOUT_BUNDLE, "Failed to layout bundle: %ls to layout directory: %ls", sczBundlePath, wzLayoutDirectory); + LogExitOnFailure(hr, MSG_FAILED_LAYOUT_BUNDLE, "Failed to layout bundle: %ls to layout directory: %ls", sczBundlePath, pContext->wzLayoutDirectory); LExit: ReleaseStr(sczDestinationPath); @@ -1164,14 +1126,10 @@ LExit: } static HRESULT AcquireContainerOrPayload( - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_VARIABLES* pVariables, + __in BURN_CACHE_CONTEXT* pContext, __in_opt BURN_CONTAINER* pContainer, __in_opt BURN_PACKAGE* pPackage, - __in_opt BURN_PAYLOAD* pPayload, - __in LPCWSTR wzDestinationPath, - __in DWORD64 qwSuccessfulCacheProgress, - __in DWORD64 qwTotalCacheSize + __in_opt BURN_PAYLOAD* pPayload ) { AssertSz(pContainer || pPayload, "Must provide a container or a payload."); @@ -1180,17 +1138,16 @@ static HRESULT AcquireContainerOrPayload( int nEquivalentPaths = 0; LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : NULL; LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : NULL; + LPCWSTR wzDestinationPath = pContainer ? pContainer->sczUnverifiedPath: pPayload->sczUnverifiedPath; LPCWSTR wzRelativePath = pContainer ? pContainer->sczFilePath : pPayload->sczFilePath; LPWSTR sczSourceFullPath = NULL; BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { }; BOOL fRetry = FALSE; + progress.pCacheContext = pContext; progress.pContainer = pContainer; progress.pPackage = pPackage; progress.pPayload = pPayload; - progress.pUX = pUX; - progress.qwCacheProgress = qwSuccessfulCacheProgress; - progress.qwTotalCacheSize = qwTotalCacheSize; do { @@ -1204,7 +1161,7 @@ static HRESULT AcquireContainerOrPayload( fRetry = FALSE; progress.fCancel = FALSE; - hr = CacheFindLocalSource(wzSourcePath, pVariables, &fFoundLocal, &sczSourceFullPath); + hr = CacheFindLocalSource(wzSourcePath, wzDestinationPath, pContext->pVariables, &fFoundLocal, &sczSourceFullPath); ExitOnFailure(hr, "Failed to search local source."); if (fFoundLocal) // the file exists locally, so copy it. @@ -1220,7 +1177,7 @@ static HRESULT AcquireContainerOrPayload( DWORD dwLogId = pContainer ? (wzPayloadId ? MSG_PROMPT_CONTAINER_PAYLOAD_SOURCE : MSG_PROMPT_CONTAINER_SOURCE) : pPackage ? MSG_PROMPT_PACKAGE_PAYLOAD_SOURCE : MSG_PROMPT_BUNDLE_PAYLOAD_SOURCE; LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId ? wzPackageOrContainerId : L"", wzPayloadId ? wzPayloadId : L"", sczSourceFullPath); - hr = PromptForSource(pUX, wzPackageOrContainerId, wzPayloadId, sczSourceFullPath, wzDownloadUrl, &fRetry, &fDownload); + hr = PromptForSource(pContext->pUX, wzPackageOrContainerId, wzPayloadId, sczSourceFullPath, wzDownloadUrl, &fRetry, &fDownload); // If the BA requested download then ensure a download url is available (it may have been set // during PromptForSource so we need to check again). @@ -1239,7 +1196,7 @@ static HRESULT AcquireContainerOrPayload( if (fCopy) { - hr = UserExperienceOnCacheAcquireBegin(pUX, wzPackageOrContainerId, wzPayloadId, BOOTSTRAPPER_CACHE_OPERATION_COPY, sczSourceFullPath); + hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId, BOOTSTRAPPER_CACHE_OPERATION_COPY, sczSourceFullPath); ExitOnRootFailure(hr, "BA aborted cache acquire begin."); hr = CopyPayload(&progress, INVALID_HANDLE_VALUE, sczSourceFullPath, wzDestinationPath); @@ -1248,12 +1205,12 @@ static HRESULT AcquireContainerOrPayload( // We successfully copied from a source location, set that as the last used source. if (SUCCEEDED(hr)) { - CacheSetLastUsedSource(pVariables, sczSourceFullPath, wzRelativePath); + CacheSetLastUsedSource(pContext->pVariables, sczSourceFullPath, wzRelativePath); } } else if (fDownload) { - hr = UserExperienceOnCacheAcquireBegin(pUX, wzPackageOrContainerId, wzPayloadId, BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD, wzDownloadUrl); + hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId, BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD, wzDownloadUrl); ExitOnRootFailure(hr, "BA aborted cache download payload begin."); hr = DownloadPayload(&progress, wzDestinationPath); @@ -1262,7 +1219,7 @@ static HRESULT AcquireContainerOrPayload( if (fCopy || fDownload) { - UserExperienceOnCacheAcquireComplete(pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry); + UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry); if (fRetry) { hr = S_OK; @@ -1279,16 +1236,10 @@ LExit: } static HRESULT LayoutOrCacheContainerOrPayload( - __in BURN_USER_EXPERIENCE* pUX, - __in HANDLE hPipe, + __in BURN_CACHE_CONTEXT* pContext, __in_opt BURN_CONTAINER* pContainer, __in_opt BURN_PACKAGE* pPackage, __in_opt BURN_PAYLOAD* pPayload, - __in BOOL fAlreadyProvidedProgress, - __in DWORD64 qwSuccessfulCachedProgress, - __in DWORD64 qwTotalCacheSize, - __in_z_opt LPCWSTR wzLayoutDirectory, - __in_z LPCWSTR wzUnverifiedPath, __in BOOL fMove, __in DWORD cTryAgainAttempts, __out BOOL* pfRetry @@ -1296,13 +1247,14 @@ static HRESULT LayoutOrCacheContainerOrPayload( { HRESULT hr = S_OK; LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : L""; + LPCWSTR wzUnverifiedPath = pContainer ? pContainer->sczUnverifiedPath : pPayload->sczUnverifiedPath; LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : L""; LARGE_INTEGER liContainerOrPayloadSize = { }; LARGE_INTEGER liZero = { }; BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { }; BOOL fCanAffectRegistration = FALSE; - if (!wzLayoutDirectory) + if (!pContext->wzLayoutDirectory) { Assert(!pContainer); Assert(pPackage); @@ -1312,41 +1264,31 @@ static HRESULT LayoutOrCacheContainerOrPayload( liContainerOrPayloadSize.QuadPart = pContainer ? pContainer->qwFileSize : pPayload->qwFileSize; + progress.pCacheContext = pContext; progress.pContainer = pContainer; progress.pPackage = pPackage; progress.pPayload = pPayload; - progress.pUX = pUX; - progress.qwTotalCacheSize = qwTotalCacheSize; - if (fAlreadyProvidedProgress) - { - Assert(qwSuccessfulCachedProgress >= static_cast(liContainerOrPayloadSize.QuadPart)); - progress.qwCacheProgress = qwSuccessfulCachedProgress - liContainerOrPayloadSize.QuadPart; // remove the payload size, since it was marked successful thus included in the successful size already. - } - else - { - progress.qwCacheProgress = qwSuccessfulCachedProgress; - } *pfRetry = FALSE; do { - hr = UserExperienceOnCacheVerifyBegin(pUX, wzPackageOrContainerId, wzPayloadId); + hr = UserExperienceOnCacheVerifyBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId); ExitOnRootFailure(hr, "BA aborted cache verify begin."); - if (INVALID_HANDLE_VALUE != hPipe) // pass the decision off to the elevated process. + if (INVALID_HANDLE_VALUE != pContext->hPipe) // pass the decision off to the elevated process. { - hr = ElevationCacheOrLayoutContainerOrPayload(hPipe, pContainer, pPackage, pPayload, wzLayoutDirectory, wzUnverifiedPath, fMove); + hr = ElevationCacheOrLayoutContainerOrPayload(pContext->hPipe, pContainer, pPackage, pPayload, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove); } - else if (wzLayoutDirectory) // layout the container or payload. + else if (pContext->wzLayoutDirectory) // layout the container or payload. { if (pContainer) { - hr = CacheLayoutContainer(pContainer, wzLayoutDirectory, wzUnverifiedPath, fMove); + hr = CacheLayoutContainer(pContainer, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove); } else { - hr = CacheLayoutPayload(pPayload, wzLayoutDirectory, wzUnverifiedPath, fMove); + hr = CacheLayoutPayload(pPayload, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove); } } else // complete the payload. @@ -1376,7 +1318,7 @@ static HRESULT LayoutOrCacheContainerOrPayload( } BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = FAILED(hr) && cTryAgainAttempts < BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS ? BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION : BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE; - UserExperienceOnCacheVerifyComplete(pUX, wzPackageOrContainerId, wzPayloadId, hr, &action); + UserExperienceOnCacheVerifyComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &action); if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action) { hr = S_FALSE; // retry verify. @@ -1541,7 +1483,7 @@ static HRESULT DownloadPayload( cacheCallback.pfnCancel = NULL; // TODO: set this cacheCallback.pv = pProgress; - authenticationData.pUX = pProgress->pUX; + authenticationData.pUX = pProgress->pCacheContext->pUX; authenticationData.wzPackageOrContainerId = wzPackageOrContainerId; authenticationData.wzPayloadId = wzPayloadId; authenticationCallback.pv = static_cast(&authenticationData); @@ -1630,15 +1572,15 @@ static DWORD CALLBACK CacheProgressRoutine( BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress = static_cast(lpData); LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : NULL; LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : NULL; - DWORD64 qwCacheProgress = pProgress->qwCacheProgress + TotalBytesTransferred.QuadPart; - if (qwCacheProgress > pProgress->qwTotalCacheSize) + DWORD64 qwCacheProgress = pProgress->pCacheContext->qwSuccessfulCacheProgress + TotalBytesTransferred.QuadPart; + if (qwCacheProgress > pProgress->pCacheContext->qwTotalCacheSize) { - AssertSz(FALSE, "Apply has cached more than Plan envisioned."); - qwCacheProgress = pProgress->qwTotalCacheSize; + //AssertSz(FALSE, "Apply has cached more than Plan envisioned."); + qwCacheProgress = pProgress->pCacheContext->qwTotalCacheSize; } - DWORD dwOverallPercentage = pProgress->qwTotalCacheSize ? static_cast(qwCacheProgress * 100 / pProgress->qwTotalCacheSize) : 0; + DWORD dwOverallPercentage = pProgress->pCacheContext->qwTotalCacheSize ? static_cast(qwCacheProgress * 100 / pProgress->pCacheContext->qwTotalCacheSize) : 0; - hr = UserExperienceOnCacheAcquireProgress(pProgress->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); + hr = UserExperienceOnCacheAcquireProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); if (HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr) { dwResult = PROGRESS_CANCEL; diff --git a/src/engine/cache.cpp b/src/engine/cache.cpp index 46c2650a..6ddbfb50 100644 --- a/src/engine/cache.cpp +++ b/src/engine/cache.cpp @@ -391,6 +391,7 @@ LExit: extern "C" HRESULT CacheFindLocalSource( __in_z LPCWSTR wzSourcePath, + __in_z LPCWSTR wzDestinationPath, __in BURN_VARIABLES* pVariables, __out BOOL* pfFound, __out_z LPWSTR* psczSourceFullPath @@ -403,7 +404,7 @@ extern "C" HRESULT CacheFindLocalSource( LPWSTR sczLastSourceFolder = NULL; LPWSTR sczLayoutPath = NULL; LPWSTR sczLayoutFolder = NULL; - LPCWSTR rgwzSearchPaths[3] = { }; + LPCWSTR rgwzSearchPaths[4] = { }; DWORD cSearchPaths = 0; // If the source path provided is a full path, obviously that is where we should be looking. @@ -414,8 +415,12 @@ extern "C" HRESULT CacheFindLocalSource( } else { + // Use the destination path first. + rgwzSearchPaths[0] = wzDestinationPath; + cSearchPaths = 1; + // If we're not running from cache or we couldn't get the last source, use - // the source path location first. In the case where we are in the bundle's + // the source path location. In the case where we are in the bundle's // package cache and couldn't find a last used source we unfortunately will // be picking the package cache path which isn't likely to have what we are // looking for. @@ -428,8 +433,8 @@ extern "C" HRESULT CacheFindLocalSource( hr = PathConcat(sczSourceProcessFolder, wzSourcePath, &sczCurrentPath); ExitOnFailure(hr, "Failed to combine last source with source."); - rgwzSearchPaths[0] = sczCurrentPath; - cSearchPaths = 1; + rgwzSearchPaths[cSearchPaths] = sczCurrentPath; + ++cSearchPaths; } // If we have a last used source and it does not duplicate the existing search path, @@ -439,7 +444,7 @@ extern "C" HRESULT CacheFindLocalSource( hr = PathConcat(sczLastSourceFolder, wzSourcePath, &sczLastSourcePath); ExitOnFailure(hr, "Failed to combine last source with source."); - if (0 == cSearchPaths || CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, rgwzSearchPaths[0], -1, sczLastSourcePath, -1)) + if (1 == cSearchPaths || CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, rgwzSearchPaths[1], -1, sczLastSourcePath, -1)) { rgwzSearchPaths[cSearchPaths] = sczLastSourcePath; ++cSearchPaths; diff --git a/src/engine/cache.h b/src/engine/cache.h index a00c50b7..52b111e9 100644 --- a/src/engine/cache.h +++ b/src/engine/cache.h @@ -54,6 +54,7 @@ HRESULT CacheGetResumePath( ); HRESULT CacheFindLocalSource( __in_z LPCWSTR wzSourcePath, + __in_z LPCWSTR wzDestinationPath, __in BURN_VARIABLES* pVariables, __out BOOL* pfFound, __out_z LPWSTR* psczSourceFullPath diff --git a/src/engine/container.cpp b/src/engine/container.cpp index 55a16afb..2db2412c 100644 --- a/src/engine/container.cpp +++ b/src/engine/container.cpp @@ -3,17 +3,6 @@ #include "precomp.h" -// internal function declarations - -static HRESULT GetAttachedContainerInfo( - __in HANDLE hFile, - __in DWORD iContainerIndex, - __out DWORD* pdwFormat, - __out DWORD64* pqwOffset, - __out DWORD64* pqwSize - ); - - // function definitions extern "C" HRESULT ContainersParseFromXml( @@ -186,6 +175,7 @@ extern "C" void ContainersUninitialize( ReleaseStr(pContainer->downloadSource.sczUrl); ReleaseStr(pContainer->downloadSource.sczUser); ReleaseStr(pContainer->downloadSource.sczPassword); + ReleaseStr(pContainer->sczUnverifiedPath); } MemFree(pContainers->rgContainers); } diff --git a/src/engine/container.h b/src/engine/container.h index bbd9fa72..d38016cc 100644 --- a/src/engine/container.h +++ b/src/engine/container.h @@ -65,7 +65,6 @@ typedef struct _BURN_CONTAINER DWORD64 qwFileSize; LPWSTR sczHash; LPWSTR sczFilePath; // relative path to container. - LPWSTR sczSourcePath; DOWNLOAD_SOURCE downloadSource; BYTE* pbHash; @@ -73,8 +72,10 @@ typedef struct _BURN_CONTAINER DWORD64 qwAttachedOffset; BOOL fActuallyAttached; // indicates whether an attached container is attached or missing. - //LPWSTR* rgsczPayloads; - //DWORD cPayloads; + // mutable members + BOOL fPlanned; + LPWSTR sczSourcePath; + LPWSTR sczUnverifiedPath; } BURN_CONTAINER; typedef struct _BURN_CONTAINERS diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 42759d3f..98aa943e 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -287,7 +287,7 @@ extern "C" HRESULT CoreDetect( pEngineState->fDetected = FALSE; pEngineState->fPlanned = FALSE; DetectReset(&pEngineState->registration, &pEngineState->packages); - PlanReset(&pEngineState->plan, &pEngineState->packages); + PlanReset(&pEngineState->plan, &pEngineState->containers, &pEngineState->packages, &pEngineState->layoutPayloads); // Detect if bundle installed state has changed since start up. This // only happens if Apply() changed the state of bundle (installed or @@ -438,7 +438,6 @@ extern "C" HRESULT CorePlan( { HRESULT hr = S_OK; BOOL fPlanBegan = FALSE; - LPWSTR sczLayoutDirectory = NULL; BURN_PACKAGE* pUpgradeBundlePackage = NULL; BURN_PACKAGE* pForwardCompatibleBundlePackage = NULL; BOOL fContinuePlanning = TRUE; // assume we won't skip planning due to dependencies. @@ -460,11 +459,12 @@ extern "C" HRESULT CorePlan( // Always reset the plan. pEngineState->fPlanned = FALSE; - PlanReset(&pEngineState->plan, &pEngineState->packages); + PlanReset(&pEngineState->plan, &pEngineState->containers, &pEngineState->packages, &pEngineState->layoutPayloads); // Remember the overall action state in the plan since it shapes the changes // we make everywhere. pEngineState->plan.action = action; + pEngineState->plan.pPayloads = &pEngineState->payloads; pEngineState->plan.wzBundleId = pEngineState->registration.sczId; pEngineState->plan.wzBundleProviderKey = pEngineState->registration.sczId; pEngineState->plan.fDisableRollback = pEngineState->fDisableRollback; @@ -484,11 +484,11 @@ extern "C" HRESULT CorePlan( Assert(!pEngineState->plan.fPerMachine); // Plan the bundle's layout. - hr = PlanLayoutBundle(&pEngineState->plan, pEngineState->registration.sczExecutableName, pEngineState->section.qwBundleSize, &pEngineState->variables, &pEngineState->payloads, &sczLayoutDirectory); + hr = PlanLayoutBundle(&pEngineState->plan, pEngineState->registration.sczExecutableName, pEngineState->section.qwBundleSize, &pEngineState->variables, &pEngineState->layoutPayloads); ExitOnFailure(hr, "Failed to plan the layout of the bundle."); // Plan the packages' layout. - hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, sczLayoutDirectory); + hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType); ExitOnFailure(hr, "Failed to plan packages."); } else if (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == action || BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED == action) @@ -532,7 +532,7 @@ extern "C" HRESULT CorePlan( hr = PlanRelatedBundlesBegin(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->plan); ExitOnFailure(hr, "Failed to plan related bundles."); - hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, NULL); + hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType); ExitOnFailure(hr, "Failed to plan packages."); // Schedule the update of related bundles last. @@ -562,7 +562,6 @@ LExit: } LogId(REPORT_STANDARD, MSG_PLAN_COMPLETE, hr); - ReleaseStr(sczLayoutDirectory); return hr; } @@ -1679,23 +1678,22 @@ static HRESULT DetectPackagePayloadsCached( // If the cached directory exists, we have something. if (DirExists(sczCachePath, NULL)) { - // Check all payloads to see if they exist. - for (DWORD i = 0; i < pPackage->cPayloads; ++i) + // Check all payloads to see if any exist. + for (DWORD i = 0; i < pPackage->payloads.cPayloads; ++i) { - BURN_PACKAGE_PAYLOAD* pPackagePayload = pPackage->rgPayloads + i; + BURN_PAYLOAD* pPayload = pPackage->payloads.rgpPayloads[i]; - hr = PathConcat(sczCachePath, pPackagePayload->pPayload->sczFilePath, &sczPayloadCachePath); + hr = PathConcat(sczCachePath, pPayload->sczFilePath, &sczPayloadCachePath); ExitOnFailure(hr, "Failed to concat payload cache path."); if (FileExistsEx(sczPayloadCachePath, NULL)) { - // TODO: We shouldn't track whether the payload was cached since all we did was check whether the file exists. - pPackagePayload->fCached = TRUE; fCached = TRUE; + break; } else { - LogId(REPORT_STANDARD, MSG_DETECT_PACKAGE_NOT_FULLY_CACHED, pPackage->sczId, pPackagePayload->pPayload->sczKey); + LogId(REPORT_STANDARD, MSG_DETECT_PACKAGE_NOT_FULLY_CACHED, pPackage->sczId, pPayload->sczKey); } } } diff --git a/src/engine/core.h b/src/engine/core.h index b4e0e0d3..e96440bb 100644 --- a/src/engine/core.h +++ b/src/engine/core.h @@ -112,6 +112,8 @@ typedef struct _BURN_ENGINE_STATE BURN_LOGGING log; + BURN_PAYLOAD_GROUP layoutPayloads; + BURN_PLAN plan; BURN_MODE mode; diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp index 844816ba..dc35e747 100644 --- a/src/engine/detect.cpp +++ b/src/engine/detect.cpp @@ -68,11 +68,6 @@ extern "C" void DetectReset( pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; pPackage->fCached = FALSE; - for (DWORD iPayload = 0; iPayload < pPackage->cPayloads; ++iPayload) - { - BURN_PACKAGE_PAYLOAD* pPayload = pPackage->rgPayloads + iPayload; - pPayload->fCached = FALSE; - } if (BURN_PACKAGE_TYPE_MSI == pPackage->type) { diff --git a/src/engine/engine.mc b/src/engine/engine.mc index 71c27cad..c8b46806 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -552,7 +552,7 @@ MessageId=314 Severity=Error SymbolicName=MSG_FAILED_CACHE_PAYLOAD Language=English -Failed to cache payload: %2!ls! from working path: %3!ls!, error: %1!ls!. +Failed to cache payload: %2!ls! from working path: %4!ls!, error: %1!ls!. . MessageId=315 @@ -574,7 +574,7 @@ MessageId=317 Severity=Error SymbolicName=MSG_FAILED_LAYOUT_PAYLOAD Language=English -Failed to layout payload: %2!ls! to layout directory: %3!ls!, error: %1!ls!. +Failed to layout payload: %2!ls! from working path: %4!ls! to layout directory: %3!ls!, error: %1!ls!. . MessageId=318 diff --git a/src/engine/exeengine.cpp b/src/engine/exeengine.cpp index 46905fc3..9b1b6973 100644 --- a/src/engine/exeengine.cpp +++ b/src/engine/exeengine.cpp @@ -372,31 +372,33 @@ extern "C" HRESULT ExeEngineExecutePackage( PROCESS_INFORMATION pi = { }; DWORD dwExitCode = 0; GENERIC_EXECUTE_MESSAGE message = { }; + BURN_PACKAGE* pPackage = pExecuteAction->exePackage.pPackage; + BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgpPayloads[0]; // get cached executable path - hr = CacheGetCompletedPath(pExecuteAction->exePackage.pPackage->fPerMachine, pExecuteAction->exePackage.pPackage->sczCacheId, &sczCachedDirectory); - ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->exePackage.pPackage->sczId); + hr = CacheGetCompletedPath(pPackage->fPerMachine, pPackage->sczCacheId, &sczCachedDirectory); + ExitOnFailure(hr, "Failed to get cached path for package: %ls", pPackage->sczId); // Best effort to set the execute package cache folder and action variables. VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE); VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->exePackage.action, TRUE); - hr = PathConcat(sczCachedDirectory, pExecuteAction->exePackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczExecutablePath); + hr = PathConcat(sczCachedDirectory, pPackagePayload->sczFilePath, &sczExecutablePath); ExitOnFailure(hr, "Failed to build executable path."); // pick arguments switch (pExecuteAction->exePackage.action) { case BOOTSTRAPPER_ACTION_STATE_INSTALL: - wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczInstallArguments; + wzArguments = pPackage->Exe.sczInstallArguments; break; case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: - wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczUninstallArguments; + wzArguments = pPackage->Exe.sczUninstallArguments; break; case BOOTSTRAPPER_ACTION_STATE_REPAIR: - wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczRepairArguments; + wzArguments = pPackage->Exe.sczRepairArguments; break; default: @@ -408,9 +410,9 @@ extern "C" HRESULT ExeEngineExecutePackage( hr = StrAllocString(&sczArguments, wzArguments && *wzArguments ? wzArguments : L"", 0); ExitOnFailure(hr, "Failed to copy package arguments."); - for (DWORD i = 0; i < pExecuteAction->exePackage.pPackage->Exe.cCommandLineArguments; ++i) + for (DWORD i = 0; i < pPackage->Exe.cCommandLineArguments; ++i) { - BURN_EXE_COMMAND_LINE_ARGUMENT* commandLineArgument = &pExecuteAction->exePackage.pPackage->Exe.rgCommandLineArguments[i]; + BURN_EXE_COMMAND_LINE_ARGUMENT* commandLineArgument = &pPackage->Exe.rgCommandLineArguments[i]; BOOL fCondition = FALSE; hr = ConditionEvaluate(pVariables, commandLineArgument->sczCondition, &fCondition); @@ -468,10 +470,10 @@ extern "C" HRESULT ExeEngineExecutePackage( } ExitOnFailure(hr, "Failed to create obfuscated executable command."); - if (pExecuteAction->exePackage.pPackage->Exe.fSupportsAncestors) + if (pPackage->Exe.fSupportsAncestors) { // Add the list of dependencies to ignore, if any, to the burn command line. - if (pExecuteAction->exePackage.sczIgnoreDependencies && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol) + if (pExecuteAction->exePackage.sczIgnoreDependencies && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) { hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies); ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line."); @@ -491,21 +493,21 @@ extern "C" HRESULT ExeEngineExecutePackage( } } - if (BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol) + if (BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) { hr = CoreAppendFileHandleSelfToCommandLine(sczExecutablePath, &hExecutableFile, &sczCommand, &sczCommandObfuscated); ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF); } // Log before we add the secret pipe name and client token for embedded processes. - LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->exePackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->exePackage.action), sczExecutablePath, sczCommandObfuscated); + LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, LoggingActionStateToString(pExecuteAction->exePackage.action), sczExecutablePath, sczCommandObfuscated); - if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol) + if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) { hr = EmbeddedRunBundle(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode); ExitOnFailure(hr, "Failed to run bundle as embedded from path: %ls", sczExecutablePath); } - else if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_NETFX4 == pExecuteAction->exePackage.pPackage->Exe.protocol) + else if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_NETFX4 == pPackage->Exe.protocol) { hr = NetFxRunChainer(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode); ExitOnFailure(hr, "Failed to run netfx chainer: %ls", sczExecutablePath); @@ -543,7 +545,7 @@ extern "C" HRESULT ExeEngineExecutePackage( } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr); } - hr = HandleExitCode(pExecuteAction->exePackage.pPackage, dwExitCode, pRestart); + hr = HandleExitCode(pPackage, dwExitCode, pRestart); ExitOnRootFailure(hr, "Process returned error: 0x%x", dwExitCode); LExit: diff --git a/src/engine/manifest.cpp b/src/engine/manifest.cpp index fa454348..b1740083 100644 --- a/src/engine/manifest.cpp +++ b/src/engine/manifest.cpp @@ -145,7 +145,7 @@ static HRESULT ParseFromXml( ExitOnFailure(hr, "Failed to parse containers."); // parse payloads - hr = PayloadsParseFromXml(&pEngineState->payloads, &pEngineState->containers, pixeBundle); + hr = PayloadsParseFromXml(&pEngineState->payloads, &pEngineState->containers, &pEngineState->layoutPayloads, pixeBundle); ExitOnFailure(hr, "Failed to parse payloads."); // parse packages diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index 35ca5c62..b081b9ca 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -1099,6 +1099,8 @@ extern "C" HRESULT MsiEngineExecutePackage( LPWSTR sczMsiPath = NULL; LPWSTR sczProperties = NULL; LPWSTR sczObfuscatedProperties = NULL; + BURN_PACKAGE* pPackage = pExecuteAction->msiPackage.pPackage; + BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgpPayloads[0]; // During rollback, if the package is already in the rollback state we expect don't // touch it again. @@ -1106,10 +1108,10 @@ extern "C" HRESULT MsiEngineExecutePackage( { if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->msiPackage.action) { - hr = WiuGetProductInfoEx(pExecuteAction->msiPackage.pPackage->Msi.sczProductCode, NULL, pExecuteAction->msiPackage.pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); + hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); if (FAILED(hr)) // package not present. { - LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pExecuteAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_ABSENT)); + LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_ABSENT)); hr = S_OK; ExitFunction(); @@ -1117,10 +1119,10 @@ extern "C" HRESULT MsiEngineExecutePackage( } else if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->msiPackage.action) { - hr = WiuGetProductInfoEx(pExecuteAction->msiPackage.pPackage->Msi.sczProductCode, NULL, pExecuteAction->msiPackage.pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); + hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); if (SUCCEEDED(hr)) // package present. { - LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pExecuteAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_PRESENT)); + LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_PRESENT)); hr = S_OK; ExitFunction(); @@ -1141,13 +1143,13 @@ extern "C" HRESULT MsiEngineExecutePackage( if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL != pExecuteAction->msiPackage.action) { // get cached MSI path - hr = CacheGetCompletedPath(pExecuteAction->msiPackage.pPackage->fPerMachine, pExecuteAction->msiPackage.pPackage->sczCacheId, &sczCachedDirectory); - ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->msiPackage.pPackage->sczId); + hr = CacheGetCompletedPath(pPackage->fPerMachine, pPackage->sczCacheId, &sczCachedDirectory); + ExitOnFailure(hr, "Failed to get cached path for package: %ls", pPackage->sczId); // Best effort to set the execute package cache folder variable. VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE); - hr = PathConcat(sczCachedDirectory, pExecuteAction->msiPackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczMsiPath); + hr = PathConcat(sczCachedDirectory, pPackagePayload->sczFilePath, &sczMsiPath); ExitOnFailure(hr, "Failed to build MSI path."); } @@ -1169,28 +1171,28 @@ extern "C" HRESULT MsiEngineExecutePackage( if (pExecuteAction->msiPackage.sczLogPath && *pExecuteAction->msiPackage.sczLogPath) { hr = WiuEnableLog(dwLogMode, pExecuteAction->msiPackage.sczLogPath, 0); - ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pExecuteAction->msiPackage.pPackage->sczId, pExecuteAction->msiPackage.sczLogPath); + ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pPackage->sczId, pExecuteAction->msiPackage.sczLogPath); } // set up properties - hr = MsiEngineConcatProperties(pExecuteAction->msiPackage.pPackage->Msi.rgProperties, pExecuteAction->msiPackage.pPackage->Msi.cProperties, pVariables, fRollback, &sczProperties, FALSE); + hr = MsiEngineConcatProperties(pPackage->Msi.rgProperties, pPackage->Msi.cProperties, pVariables, fRollback, &sczProperties, FALSE); ExitOnFailure(hr, "Failed to add properties to argument string."); - hr = MsiEngineConcatProperties(pExecuteAction->msiPackage.pPackage->Msi.rgProperties, pExecuteAction->msiPackage.pPackage->Msi.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE); + hr = MsiEngineConcatProperties(pPackage->Msi.rgProperties, pPackage->Msi.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE); ExitOnFailure(hr, "Failed to add obfuscated properties to argument string."); // add feature action properties - hr = ConcatFeatureActionProperties(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgFeatures, &sczProperties); + hr = ConcatFeatureActionProperties(pPackage, pExecuteAction->msiPackage.rgFeatures, &sczProperties); ExitOnFailure(hr, "Failed to add feature action properties to argument string."); - hr = ConcatFeatureActionProperties(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgFeatures, &sczObfuscatedProperties); + hr = ConcatFeatureActionProperties(pPackage, pExecuteAction->msiPackage.rgFeatures, &sczObfuscatedProperties); ExitOnFailure(hr, "Failed to add feature action properties to obfuscated argument string."); // add slipstream patch properties - hr = ConcatPatchProperty(pExecuteAction->msiPackage.pPackage, fRollback, &sczProperties); + hr = ConcatPatchProperty(pPackage, fRollback, &sczProperties); ExitOnFailure(hr, "Failed to add patch properties to argument string."); - hr = ConcatPatchProperty(pExecuteAction->msiPackage.pPackage, fRollback, &sczObfuscatedProperties); + hr = ConcatPatchProperty(pPackage, fRollback, &sczObfuscatedProperties); ExitOnFailure(hr, "Failed to add patch properties to obfuscated argument string."); hr = MsiEngineConcatActionProperty(pExecuteAction->msiPackage.actionMsiProperty, &sczProperties); @@ -1199,7 +1201,7 @@ extern "C" HRESULT MsiEngineExecutePackage( hr = MsiEngineConcatActionProperty(pExecuteAction->msiPackage.actionMsiProperty, &sczObfuscatedProperties); ExitOnFailure(hr, "Failed to add action property to obfuscated argument string."); - LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), sczMsiPath, sczObfuscatedProperties ? sczObfuscatedProperties : L""); + LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), sczMsiPath, sczObfuscatedProperties ? sczObfuscatedProperties : L""); // // Do the actual action. @@ -1213,13 +1215,13 @@ extern "C" HRESULT MsiEngineExecutePackage( hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart); ExitOnFailure(hr, "Failed to install MSI package."); - RegisterSourceDirectory(pExecuteAction->msiPackage.pPackage, sczMsiPath); + RegisterSourceDirectory(pPackage, sczMsiPath); break; case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: // If feature selection is not enabled, then reinstall the existing features to ensure they get // updated. - if (0 == pExecuteAction->msiPackage.pPackage->Msi.cFeatures) + if (0 == pPackage->Msi.cFeatures) { hr = StrAllocConcatSecure(&sczProperties, L" REINSTALL=ALL", 0); ExitOnFailure(hr, "Failed to add reinstall all property on minor upgrade."); @@ -1231,7 +1233,7 @@ extern "C" HRESULT MsiEngineExecutePackage( hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart); ExitOnFailure(hr, "Failed to perform minor upgrade of MSI package."); - RegisterSourceDirectory(pExecuteAction->msiPackage.pPackage, sczMsiPath); + RegisterSourceDirectory(pPackage, sczMsiPath); break; case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough; @@ -1239,7 +1241,7 @@ extern "C" HRESULT MsiEngineExecutePackage( case BOOTSTRAPPER_ACTION_STATE_REPAIR: { LPCWSTR wzReinstallAll = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action || - pExecuteAction->msiPackage.pPackage->Msi.cFeatures) ? L"" : L" REINSTALL=ALL"; + pPackage->Msi.cFeatures) ? L"" : L" REINSTALL=ALL"; LPCWSTR wzReinstallMode = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action || BOOTSTRAPPER_ACTION_STATE_MEND == pExecuteAction->msiPackage.action) ? L"o" : L"e"; hr = StrAllocFormattedSecure(&sczProperties, L"%ls%ls REINSTALLMODE=\"cmus%ls\" REBOOT=ReallySuppress", sczProperties ? sczProperties : L"", wzReinstallAll, wzReinstallMode); @@ -1262,10 +1264,10 @@ extern "C" HRESULT MsiEngineExecutePackage( hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES); ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties."); - hr = WiuConfigureProductEx(pExecuteAction->msiPackage.pPackage->Msi.sczProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, sczProperties, &restart); + hr = WiuConfigureProductEx(pPackage->Msi.sczProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, sczProperties, &restart); if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr) { - LogId(REPORT_STANDARD, MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE, pExecuteAction->msiPackage.pPackage->sczId); + LogId(REPORT_STANDARD, MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE, pPackage->sczId); hr = S_OK; } ExitOnFailure(hr, "Failed to uninstall MSI package."); @@ -1979,6 +1981,7 @@ static HRESULT ConcatPatchProperty( { BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + i; BURN_PACKAGE* pMspPackage = pSlipstreamMsp->pMspPackage; + BURN_PAYLOAD* pMspPackagePayload = pMspPackage->payloads.rgpPayloads[0]; BOOTSTRAPPER_ACTION_STATE patchExecuteAction = fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute; if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < patchExecuteAction) @@ -1986,7 +1989,7 @@ static HRESULT ConcatPatchProperty( hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory); ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId); - hr = PathConcat(sczCachedDirectory, pMspPackage->rgPayloads[0].pPayload->sczFilePath, &sczMspPath); + hr = PathConcat(sczCachedDirectory, pMspPackagePayload->sczFilePath, &sczMspPath); ExitOnFailure(hr, "Failed to build MSP path."); if (!sczPatches) diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp index 5ccb6718..6addfa90 100644 --- a/src/engine/mspengine.cpp +++ b/src/engine/mspengine.cpp @@ -581,6 +581,7 @@ extern "C" HRESULT MspEngineExecutePackage( { LPCWSTR wzAppend = NULL; BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage; + BURN_PAYLOAD* pMspPackagePayload = pMspPackage->payloads.rgpPayloads[0]; AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Invalid package type added to ordered patches."); if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->mspTarget.action) @@ -592,7 +593,7 @@ extern "C" HRESULT MspEngineExecutePackage( // Best effort to set the execute package cache folder variable. VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE); - hr = PathConcat(sczCachedDirectory, pMspPackage->rgPayloads[0].pPayload->sczFilePath, &sczMspPath); + hr = PathConcat(sczCachedDirectory, pMspPackagePayload->sczFilePath, &sczMspPath); ExitOnFailure(hr, "Failed to build MSP path."); wzAppend = sczMspPath; diff --git a/src/engine/msuengine.cpp b/src/engine/msuengine.cpp index 59f9a656..02ceb0c6 100644 --- a/src/engine/msuengine.cpp +++ b/src/engine/msuengine.cpp @@ -264,6 +264,8 @@ extern "C" HRESULT MsuEngineExecutePackage( GENERIC_EXECUTE_MESSAGE message = { }; DWORD dwExitCode = 0; BOOL fUseSysNativePath = FALSE; + BURN_PACKAGE* pPackage = pExecuteAction->msuPackage.pPackage; + BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgpPayloads[0]; #if !defined(_WIN64) hr = ProcWow64(::GetCurrentProcess(), &fUseSysNativePath); @@ -295,13 +297,13 @@ extern "C" HRESULT MsuEngineExecutePackage( { case BOOTSTRAPPER_ACTION_STATE_INSTALL: // get cached MSU path - hr = CacheGetCompletedPath(TRUE, pExecuteAction->msuPackage.pPackage->sczCacheId, &sczCachedDirectory); - ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->msuPackage.pPackage->sczId); + hr = CacheGetCompletedPath(TRUE, pPackage->sczCacheId, &sczCachedDirectory); + ExitOnFailure(hr, "Failed to get cached path for package: %ls", pPackage->sczId); // Best effort to set the execute package cache folder variable. VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE); - hr = PathConcat(sczCachedDirectory, pExecuteAction->msuPackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczMsuPath); + hr = PathConcat(sczCachedDirectory, pPackagePayload->sczFilePath, &sczMsuPath); ExitOnFailure(hr, "Failed to build MSU path."); // format command @@ -311,7 +313,7 @@ extern "C" HRESULT MsuEngineExecutePackage( case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: // format command - hr = StrAllocFormatted(&sczCommand, L"\"%ls\" /uninstall /kb:%ls /quiet /norestart", sczWusaPath, pExecuteAction->msuPackage.pPackage->Msu.sczKB); + hr = StrAllocFormatted(&sczCommand, L"\"%ls\" /uninstall /kb:%ls /quiet /norestart", sczWusaPath, pPackage->Msu.sczKB); ExitOnFailure(hr, "Failed to format MSU uninstall command."); break; @@ -329,7 +331,7 @@ extern "C" HRESULT MsuEngineExecutePackage( ExitOnFailure(hr, "Failed to append log path to MSU command-line."); } - LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->msuPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msuPackage.action), sczMsuPath ? sczMsuPath : pExecuteAction->msuPackage.pPackage->Msu.sczKB, sczCommand); + LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, LoggingActionStateToString(pExecuteAction->msuPackage.action), sczMsuPath ? sczMsuPath : pPackage->Msu.sczKB, sczCommand); hr = EnsureWUServiceEnabled(fStopWusaService, &schWu, &fWuWasDisabled); ExitOnFailure(hr, "Failed to ensure WU service was enabled to install MSU package."); diff --git a/src/engine/package.cpp b/src/engine/package.cpp index 6ff59fe4..ecf1488b 100644 --- a/src/engine/package.cpp +++ b/src/engine/package.cpp @@ -349,7 +349,7 @@ extern "C" void PackageUninitialize( MemFree(pPackage->rgDependencyProviders); } - ReleaseMem(pPackage->rgPayloads); + ReleaseMem(pPackage->payloads.rgpPayloads); switch (pPackage->type) { @@ -567,16 +567,14 @@ static HRESULT ParsePayloadRefsFromXml( } // allocate memory for payload pointers - pPackage->rgPayloads = (BURN_PACKAGE_PAYLOAD*)MemAlloc(sizeof(BURN_PACKAGE_PAYLOAD) * cNodes, TRUE); - ExitOnNull(pPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate memory for package payloads."); + pPackage->payloads.rgpPayloads = (BURN_PAYLOAD**)MemAlloc(sizeof(BURN_PAYLOAD*) * cNodes, TRUE); + ExitOnNull(pPackage->payloads.rgpPayloads, hr, E_OUTOFMEMORY, "Failed to allocate memory for package payloads."); - pPackage->cPayloads = cNodes; + pPackage->payloads.cPayloads = cNodes; // parse package elements for (DWORD i = 0; i < cNodes; ++i) { - BURN_PACKAGE_PAYLOAD* pPackagePayload = &pPackage->rgPayloads[i]; - hr = XmlNextElement(pixnNodes, &pixnNode, NULL); ExitOnFailure(hr, "Failed to get next node."); @@ -585,9 +583,11 @@ static HRESULT ParsePayloadRefsFromXml( ExitOnFailure(hr, "Failed to get Id attribute."); // find payload - hr = PayloadFindById(pPayloads, sczId, &pPackagePayload->pPayload); + hr = PayloadFindById(pPayloads, sczId, &pPackage->payloads.rgpPayloads[i]); ExitOnFailure(hr, "Failed to find payload."); + pPackage->payloads.qwTotalSize += pPackage->payloads.rgpPayloads[i]->qwFileSize; + // prepare next iteration ReleaseNullObject(pixnNode); } diff --git a/src/engine/package.h b/src/engine/package.h index 262262ab..34a3af26 100644 --- a/src/engine/package.h +++ b/src/engine/package.h @@ -184,12 +184,6 @@ typedef struct _BURN_SLIPSTREAM_MSP BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan. } BURN_SLIPSTREAM_MSP; -typedef struct _BURN_PACKAGE_PAYLOAD -{ - BURN_PAYLOAD* pPayload; - BOOL fCached; -} BURN_PACKAGE_PAYLOAD; - typedef struct _BURN_DEPENDENCY_PROVIDER { LPWSTR sczKey; @@ -260,8 +254,7 @@ typedef struct _BURN_PACKAGE BURN_PACKAGE_REGISTRATION_STATE expectedInstallRegistrationState;// only valid after Plan. BURN_PACKAGE_REGISTRATION_STATE transactionRegistrationState; // only valid during Apply inside an MSI transaction. - BURN_PACKAGE_PAYLOAD* rgPayloads; - DWORD cPayloads; + BURN_PAYLOAD_GROUP payloads; BURN_DEPENDENCY_PROVIDER* rgDependencyProviders; DWORD cDependencyProviders; diff --git a/src/engine/payload.cpp b/src/engine/payload.cpp index 67eebe10..2be39b23 100644 --- a/src/engine/payload.cpp +++ b/src/engine/payload.cpp @@ -18,6 +18,7 @@ static HRESULT FindEmbeddedBySourcePath( extern "C" HRESULT PayloadsParseFromXml( __in BURN_PAYLOADS* pPayloads, __in_opt BURN_CONTAINERS* pContainers, + __in_opt BURN_PAYLOAD_GROUP* pLayoutPayloads, __in IXMLDOMNode* pixnBundle ) { @@ -136,6 +137,17 @@ extern "C" HRESULT PayloadsParseFromXml( hr = StrAllocHexDecode(scz, &pPayload->pbHash, &pPayload->cbHash); ExitOnFailure(hr, "Failed to hex decode the Payload/@Hash."); + if (pPayload->fLayoutOnly && pLayoutPayloads) + { + hr = MemEnsureArraySize(reinterpret_cast(&pLayoutPayloads->rgpPayloads), pLayoutPayloads->cPayloads + 1, sizeof(BURN_PAYLOAD*), 5); + ExitOnNull(pPayloads->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate memory for layout payloads."); + + pLayoutPayloads->rgpPayloads[pLayoutPayloads->cPayloads] = pPayload; + ++pLayoutPayloads->cPayloads; + + pLayoutPayloads->qwTotalSize += pPayload->qwFileSize; + } + // prepare next iteration ReleaseNullObject(pixnNode); } @@ -150,6 +162,24 @@ LExit: return hr; } +extern "C" void PayloadUninitialize( + __in BURN_PAYLOAD* pPayload + ) +{ + if (pPayload) + { + ReleaseStr(pPayload->sczKey); + ReleaseStr(pPayload->sczFilePath); + ReleaseMem(pPayload->pbHash); + ReleaseStr(pPayload->sczSourcePath); + ReleaseStr(pPayload->sczLocalFilePath); + ReleaseStr(pPayload->downloadSource.sczUrl); + ReleaseStr(pPayload->downloadSource.sczUser); + ReleaseStr(pPayload->downloadSource.sczPassword); + ReleaseStr(pPayload->sczUnverifiedPath); + } +} + extern "C" void PayloadsUninitialize( __in BURN_PAYLOADS* pPayloads ) @@ -158,16 +188,7 @@ extern "C" void PayloadsUninitialize( { for (DWORD i = 0; i < pPayloads->cPayloads; ++i) { - BURN_PAYLOAD* pPayload = &pPayloads->rgPayloads[i]; - - ReleaseStr(pPayload->sczKey); - ReleaseStr(pPayload->sczFilePath); - ReleaseMem(pPayload->pbHash); - ReleaseStr(pPayload->sczSourcePath); - ReleaseStr(pPayload->sczLocalFilePath); - ReleaseStr(pPayload->downloadSource.sczUrl); - ReleaseStr(pPayload->downloadSource.sczUser); - ReleaseStr(pPayload->downloadSource.sczPassword); + PayloadUninitialize(pPayloads->rgPayloads + i); } MemFree(pPayloads->rgPayloads); } diff --git a/src/engine/payload.h b/src/engine/payload.h index e8639d64..ba555766 100644 --- a/src/engine/payload.h +++ b/src/engine/payload.h @@ -45,6 +45,8 @@ typedef struct _BURN_PAYLOAD // mutable members BURN_PAYLOAD_STATE state; LPWSTR sczLocalFilePath; // location of extracted or downloaded copy + + LPWSTR sczUnverifiedPath; } BURN_PAYLOAD; typedef struct _BURN_PAYLOADS @@ -53,14 +55,24 @@ typedef struct _BURN_PAYLOADS DWORD cPayloads; } BURN_PAYLOADS; +typedef struct _BURN_PAYLOAD_GROUP +{ + BURN_PAYLOAD** rgpPayloads; + DWORD cPayloads; + DWORD64 qwTotalSize; +} BURN_PAYLOAD_GROUP; // functions HRESULT PayloadsParseFromXml( __in BURN_PAYLOADS* pPayloads, __in_opt BURN_CONTAINERS* pContainers, + __in_opt BURN_PAYLOAD_GROUP* pLayoutPayloads, __in IXMLDOMNode* pixnBundle ); +void PayloadUninitialize( + __in BURN_PAYLOAD* pPayload + ); void PayloadsUninitialize( __in BURN_PAYLOADS* pPayloads ); diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index 187b1f15..ce577da5 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -15,6 +15,15 @@ static void UninitializeRegistrationAction( static void UninitializeCacheAction( __in BURN_CACHE_ACTION* pCacheAction ); +static void ResetPlannedContainerState( + __in BURN_CONTAINER* pContainer + ); +static void ResetPlannedPayloadsState( + __in BURN_PAYLOADS* pPayloads + ); +static void ResetPlannedPayloadGroupState( + __in BURN_PAYLOAD_GROUP* pPayloadGroup + ); static void ResetPlannedPackageState( __in BURN_PACKAGE* pPackage ); @@ -30,8 +39,7 @@ static HRESULT PlanPackagesHelper( __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in_z_opt LPCWSTR wzLayoutDirectory + __in BOOTSTRAPPER_RELATION_TYPE relationType ); static HRESULT InitializePackage( __in BURN_PLAN* pPlan, @@ -48,7 +56,6 @@ static HRESULT ProcessPackage( __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, __in BOOTSTRAPPER_DISPLAY display, - __in_z_opt LPCWSTR wzLayoutDirectory, __inout HANDLE* phSyncpointEvent, __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary ); @@ -99,54 +106,9 @@ static HRESULT AppendRollbackCacheAction( __in BURN_PLAN* pPlan, __out BURN_CACHE_ACTION** ppCacheAction ); -static HRESULT AppendLayoutContainerAction( - __in BURN_PLAN* pPlan, - __in_opt BURN_PACKAGE* pPackage, - __in DWORD iPackageStartAction, - __in BURN_CONTAINER* pContainer, - __in BOOL fContainerCached, - __in_z LPCWSTR wzLayoutDirectory - ); -static HRESULT AppendCacheOrLayoutPayloadAction( - __in BURN_PLAN* pPlan, - __in_opt BURN_PACKAGE* pPackage, - __in DWORD iPackageStartAction, - __in BURN_PAYLOAD* pPayload, - __in BOOL fPayloadCached, - __in_z_opt LPCWSTR wzLayoutDirectory - ); -static BOOL FindContainerCacheAction( - __in BURN_CACHE_ACTION_TYPE type, - __in BURN_PLAN* pPlan, - __in BURN_CONTAINER* pContainer, - __in DWORD iSearchStart, - __in DWORD iSearchEnd, - __out_opt BURN_CACHE_ACTION** ppCacheAction, - __out_opt DWORD* piCacheAction - ); -static HRESULT CreateContainerAcquireAndExtractAction( +static HRESULT ProcessPayloadGroup( __in BURN_PLAN* pPlan, - __in BURN_CONTAINER* pContainer, - __in DWORD iPackageStartAction, - __in BOOL fPayloadCached, - __out BURN_CACHE_ACTION** ppContainerExtractAction, - __out DWORD* piContainerTryAgainAction - ); -static HRESULT AddAcquireContainer( - __in BURN_PLAN* pPlan, - __in BURN_CONTAINER* pContainer, - __out_opt BURN_CACHE_ACTION** ppCacheAction, - __out_opt DWORD* piCacheAction - ); -static HRESULT AddExtractPayload( - __in BURN_CACHE_ACTION* pCacheAction, - __in_opt BURN_PACKAGE* pPackage, - __in BURN_PAYLOAD* pPayload, - __in_z LPCWSTR wzPayloadWorkingPath - ); -static BURN_CACHE_ACTION* ProcessSharedPayload( - __in BURN_PLAN* pPlan, - __in BURN_PAYLOAD* pPayload + __in BURN_PAYLOAD_GROUP* pPayloadGroup ); static void RemoveUnnecessaryActions( __in BOOL fExecute, @@ -175,24 +137,17 @@ static BOOL NeedsCache( __in BURN_PACKAGE* pPackage, __in BOOL fExecute ); -static HRESULT CreateContainerProgress( - __in BURN_PLAN* pPlan, - __in BURN_CONTAINER* pContainer, - __out BURN_CACHE_CONTAINER_PROGRESS** ppContainerProgress - ); -static HRESULT CreatePayloadProgress( - __in BURN_PLAN* pPlan, - __in BURN_PAYLOAD* pPayload, - __out BURN_CACHE_PAYLOAD_PROGRESS** ppPayloadProgress - ); // function definitions extern "C" void PlanReset( __in BURN_PLAN* pPlan, - __in BURN_PACKAGES* pPackages + __in BURN_CONTAINERS* pContainers, + __in BURN_PACKAGES* pPackages, + __in BURN_PAYLOAD_GROUP* pLayoutPayloads ) { + ReleaseNullStr(pPlan->sczLayoutDirectory); PackageUninitialize(&pPlan->forwardCompatibleBundle); if (pPlan->rgRegistrationActions) @@ -271,8 +226,21 @@ extern "C" void PlanReset( ReleaseDict(pPlan->shPayloadProgress); } + if (pPlan->pPayloads) + { + ResetPlannedPayloadsState(pPlan->pPayloads); + } + memset(pPlan, 0, sizeof(BURN_PLAN)); + if (pContainers->rgContainers) + { + for (DWORD i = 0; i < pContainers->cContainers; ++i) + { + ResetPlannedContainerState(&pContainers->rgContainers[i]); + } + } + // Reset the planned actions for each package. if (pPackages->rgPackages) { @@ -282,6 +250,8 @@ extern "C" void PlanReset( } } + ResetPlannedPayloadGroupState(pLayoutPayloads); + // Reset the planned state for each rollback boundary. if (pPackages->rgRollbackBoundaries) { @@ -417,34 +387,35 @@ extern "C" HRESULT PlanLayoutBundle( __in_z LPCWSTR wzExecutableName, __in DWORD64 qwBundleSize, __in BURN_VARIABLES* pVariables, - __in BURN_PAYLOADS* pPayloads, - __out_z LPWSTR* psczLayoutDirectory + __in BURN_PAYLOAD_GROUP* pLayoutPayloads ) { HRESULT hr = S_OK; BURN_CACHE_ACTION* pCacheAction = NULL; LPWSTR sczExecutablePath = NULL; - LPWSTR sczLayoutDirectory = NULL; // Get the layout directory. - hr = VariableGetString(pVariables, BURN_BUNDLE_LAYOUT_DIRECTORY, &sczLayoutDirectory); + hr = VariableGetString(pVariables, BURN_BUNDLE_LAYOUT_DIRECTORY, &pPlan->sczLayoutDirectory); if (E_NOTFOUND == hr) // if not set, use the current directory as the layout directory. { - hr = VariableGetString(pVariables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, &sczLayoutDirectory); + hr = VariableGetString(pVariables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, &pPlan->sczLayoutDirectory); if (E_NOTFOUND == hr) // if not set, use the current directory as the layout directory. { hr = PathForCurrentProcess(&sczExecutablePath, NULL); ExitOnFailure(hr, "Failed to get path for current executing process as layout directory."); - hr = PathGetDirectory(sczExecutablePath, &sczLayoutDirectory); + hr = PathGetDirectory(sczExecutablePath, &pPlan->sczLayoutDirectory); ExitOnFailure(hr, "Failed to get executing process as layout directory."); } } ExitOnFailure(hr, "Failed to get bundle layout directory property."); - hr = PathBackslashTerminate(&sczLayoutDirectory); + hr = PathBackslashTerminate(&pPlan->sczLayoutDirectory); ExitOnFailure(hr, "Failed to ensure layout directory is backslash terminated."); + hr = ProcessPayloadGroup(pPlan, pLayoutPayloads); + ExitOnFailure(hr, "Failed to process payload group for bundle."); + // Plan the layout of the bundle engine itself. hr = AppendCacheAction(pPlan, &pCacheAction); ExitOnFailure(hr, "Failed to append bundle start action."); @@ -454,36 +425,17 @@ extern "C" HRESULT PlanLayoutBundle( hr = StrAllocString(&pCacheAction->bundleLayout.sczExecutableName, wzExecutableName, 0); ExitOnFailure(hr, "Failed to to copy executable name for bundle."); - hr = StrAllocString(&pCacheAction->bundleLayout.sczLayoutDirectory, sczLayoutDirectory, 0); - ExitOnFailure(hr, "Failed to to copy layout directory for bundle."); - hr = CacheCalculateBundleLayoutWorkingPath(pPlan->wzBundleId, &pCacheAction->bundleLayout.sczUnverifiedPath); ExitOnFailure(hr, "Failed to calculate bundle layout working path."); pCacheAction->bundleLayout.qwBundleSize = qwBundleSize; + pCacheAction->bundleLayout.pPayloadGroup = pLayoutPayloads; - pPlan->qwCacheSizeTotal += qwBundleSize; + pPlan->qwCacheSizeTotal += 2 * qwBundleSize; ++pPlan->cOverallProgressTicksTotal; - // Plan the layout of layout-only payloads. - for (DWORD i = 0; i < pPayloads->cPayloads; ++i) - { - BURN_PAYLOAD* pPayload = pPayloads->rgPayloads + i; - if (pPayload->fLayoutOnly) - { - // TODO: determine if a payload already exists in the layout and pass appropriate value fPayloadCached - // (instead of always FALSE). - hr = AppendCacheOrLayoutPayloadAction(pPlan, NULL, BURN_PLAN_INVALID_ACTION_INDEX, pPayload, FALSE, sczLayoutDirectory); - ExitOnFailure(hr, "Failed to plan layout payload."); - } - } - - *psczLayoutDirectory = sczLayoutDirectory; - sczLayoutDirectory = NULL; - LExit: - ReleaseStr(sczLayoutDirectory); ReleaseStr(sczExecutablePath); return hr; @@ -562,13 +514,12 @@ extern "C" HRESULT PlanPackages( __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in_z_opt LPCWSTR wzLayoutDirectory + __in BOOTSTRAPPER_RELATION_TYPE relationType ) { HRESULT hr = S_OK; - hr = PlanPackagesHelper(pPackages->rgPackages, pPackages->cPackages, TRUE, pUX, pPlan, pLog, pVariables, display, relationType, wzLayoutDirectory); + hr = PlanPackagesHelper(pPackages->rgPackages, pPackages->cPackages, TRUE, pUX, pPlan, pLog, pVariables, display, relationType); return hr; } @@ -782,7 +733,7 @@ extern "C" HRESULT PlanPassThroughBundle( // Plan passthrough package. // Passthrough packages are never cleaned up by the calling bundle (they delete themselves when appropriate) // so we don't need to plan clean up. - hr = PlanPackagesHelper(pPackage, 1, FALSE, pUX, pPlan, pLog, pVariables, display, relationType, NULL); + hr = PlanPackagesHelper(pPackage, 1, FALSE, pUX, pPlan, pLog, pVariables, display, relationType); ExitOnFailure(hr, "Failed to process passthrough package."); LExit: @@ -802,7 +753,7 @@ extern "C" HRESULT PlanUpdateBundle( HRESULT hr = S_OK; // Plan update package. - hr = PlanPackagesHelper(pPackage, 1, TRUE, pUX, pPlan, pLog, pVariables, display, relationType, NULL); + hr = PlanPackagesHelper(pPackage, 1, TRUE, pUX, pPlan, pLog, pVariables, display, relationType); ExitOnFailure(hr, "Failed to process update package."); LExit: @@ -818,8 +769,7 @@ static HRESULT PlanPackagesHelper( __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in_z_opt LPCWSTR wzLayoutDirectory + __in BOOTSTRAPPER_RELATION_TYPE relationType ) { HRESULT hr = S_OK; @@ -856,7 +806,7 @@ static HRESULT PlanPackagesHelper( DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i; BURN_PACKAGE* pPackage = rgPackages + iPackage; - hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, wzLayoutDirectory, &hSyncpointEvent, &pRollbackBoundary); + hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, &hSyncpointEvent, &pRollbackBoundary); ExitOnFailure(hr, "Failed to process package."); } @@ -962,7 +912,6 @@ static HRESULT ProcessPackage( __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, __in BOOTSTRAPPER_DISPLAY display, - __in_z_opt LPCWSTR wzLayoutDirectory, __inout HANDLE* phSyncpointEvent, __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary ) @@ -976,7 +925,7 @@ static HRESULT ProcessPackage( if (BOOTSTRAPPER_ACTION_LAYOUT == pPlan->action) { - hr = PlanLayoutPackage(pPlan, pPackage, wzLayoutDirectory); + hr = PlanLayoutPackage(pPlan, pPackage); ExitOnFailure(hr, "Failed to plan layout package."); } else @@ -1035,59 +984,69 @@ LExit: return hr; } -extern "C" HRESULT PlanLayoutPackage( +extern "C" HRESULT PlanLayoutContainer( __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage, - __in_z_opt LPCWSTR wzLayoutDirectory + __in BURN_CONTAINER* pContainer ) { HRESULT hr = S_OK; BURN_CACHE_ACTION* pCacheAction = NULL; - DWORD iPackageStartAction = 0; - hr = AppendCacheAction(pPlan, &pCacheAction); - ExitOnFailure(hr, "Failed to append package start action."); + Assert(!pContainer->fPlanned); + pContainer->fPlanned = TRUE; - pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_START; - pCacheAction->packageStart.pPackage = pPackage; + if (pPlan->sczLayoutDirectory) + { + if (!pContainer->fAttached) + { + hr = AppendCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to append package start action."); - // Remember the index for the package start action (which is now the last in the cache - // actions array) because the array may be resized later and move around in memory. - iPackageStartAction = pPlan->cCacheActions - 1; + pCacheAction->type = BURN_CACHE_ACTION_TYPE_CONTAINER; + pCacheAction->container.pContainer = pContainer; - // If any of the package payloads are not cached, add them to the plan. - for (DWORD i = 0; i < pPackage->cPayloads; ++i) + pPlan->qwCacheSizeTotal += 2 * pContainer->qwFileSize; + } + } + else { - BURN_PACKAGE_PAYLOAD* pPackagePayload = &pPackage->rgPayloads[i]; + pPlan->qwCacheSizeTotal += 2 * pContainer->qwFileSize; + } - // If doing layout and the package is in a container. - if (wzLayoutDirectory && pPackagePayload->pPayload->pContainer) + if (!pContainer->sczUnverifiedPath) + { + if (pContainer->fActuallyAttached) { - // TODO: determine if a container already exists in the layout and pass appropriate value fPayloadCached (instead of always FALSE). - hr = AppendLayoutContainerAction(pPlan, pPackage, iPackageStartAction, pPackagePayload->pPayload->pContainer, FALSE, wzLayoutDirectory); - ExitOnFailure(hr, "Failed to append layout container action."); + hr = PathForCurrentProcess(&pContainer->sczUnverifiedPath, NULL); + ExitOnFailure(hr, "Failed to get path for executing module as attached container working path."); } else { - // TODO: determine if a payload already exists in the layout and pass appropriate value fPayloadCached (instead of always FALSE). - hr = AppendCacheOrLayoutPayloadAction(pPlan, pPackage, iPackageStartAction, pPackagePayload->pPayload, FALSE, wzLayoutDirectory); - ExitOnFailure(hr, "Failed to append cache/layout payload action."); + hr = CacheCalculateContainerWorkingPath(pPlan->wzBundleId, pContainer, &pContainer->sczUnverifiedPath); + ExitOnFailure(hr, "Failed to calculate unverified path for container."); } - - Assert(BURN_CACHE_ACTION_TYPE_PACKAGE_START == pPlan->rgCacheActions[iPackageStartAction].type); - ++pPlan->rgCacheActions[iPackageStartAction].packageStart.cCachePayloads; - pPlan->rgCacheActions[iPackageStartAction].packageStart.qwCachePayloadSizeTotal += pPackagePayload->pPayload->qwFileSize; } - // Create package stop action. - hr = AppendCacheAction(pPlan, &pCacheAction); - ExitOnFailure(hr, "Failed to append cache action."); +LExit: + return hr; +} - pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_STOP; - pCacheAction->packageStop.pPackage = pPackage; +extern "C" HRESULT PlanLayoutPackage( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + BURN_CACHE_ACTION* pCacheAction = NULL; + + hr = ProcessPayloadGroup(pPlan, &pPackage->payloads); + ExitOnFailure(hr, "Failed to process payload group for package: %ls.", pPackage->sczId); - // Update the start action with the location of the complete action. - pPlan->rgCacheActions[iPackageStartAction].packageStart.iPackageCompleteAction = pPlan->cCacheActions - 1; + hr = AppendCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to append package start action."); + + pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE; + pCacheAction->package.pPackage = pPackage; ++pPlan->cOverallProgressTicksTotal; @@ -1854,29 +1813,37 @@ static void UninitializeCacheAction( case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: ReleaseStr(pCacheAction->bundleLayout.sczExecutableName); - ReleaseStr(pCacheAction->bundleLayout.sczLayoutDirectory); ReleaseStr(pCacheAction->bundleLayout.sczUnverifiedPath); break; + } +} - case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER: - ReleaseStr(pCacheAction->resolveContainer.sczUnverifiedPath); - break; - - case BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER: - ReleaseStr(pCacheAction->extractContainer.sczContainerUnverifiedPath); - ReleaseMem(pCacheAction->extractContainer.rgPayloads); - break; +static void ResetPlannedContainerState( + __in BURN_CONTAINER* pContainer + ) +{ + pContainer->fPlanned = FALSE; +} - case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD: - ReleaseStr(pCacheAction->resolvePayload.sczUnverifiedPath); - break; +static void ResetPlannedPayloadsState( + __in BURN_PAYLOADS* pPayloads + ) +{ + for (DWORD i = 0; i < pPayloads->cPayloads; ++i) + { + BURN_PAYLOAD* pPayload = pPayloads->rgPayloads + i; - case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD: - ReleaseStr(pCacheAction->cachePayload.sczUnverifiedPath); - break; + pPayload->state = BURN_PAYLOAD_STATE_NONE; + ReleaseNullStr(pPayload->sczLocalFilePath); } } +static void ResetPlannedPayloadGroupState( + __in BURN_PAYLOAD_GROUP* /*pPayloadGroup*/ + ) +{ +} + static void ResetPlannedPackageState( __in BURN_PACKAGE* pPackage ) @@ -1931,6 +1898,8 @@ static void ResetPlannedPackageState( pTargetProduct->rollbackSkip = BURN_PATCH_SKIP_STATE_NONE; } } + + ResetPlannedPayloadGroupState(&pPackage->payloads); } static void ResetPlannedRollbackBoundaryState( @@ -2091,7 +2060,6 @@ static HRESULT AddCachePackageHelper( HRESULT hr = S_OK; BURN_CACHE_ACTION* pCacheAction = NULL; DWORD dwCheckpoint = 0; - DWORD iPackageStartAction = 0; BOOL fPlanned = AlreadyPlannedCachePackage(pPlan, pPackage->sczId, phSyncpointEvent); if (fPlanned) @@ -2105,7 +2073,7 @@ static HRESULT AddCachePackageHelper( dwCheckpoint = GetNextCheckpointId(pPlan); hr = AppendCacheAction(pPlan, &pCacheAction); - ExitOnFailure(hr, "Failed to append package start action."); + ExitOnFailure(hr, "Failed to append checkpoint before package start action."); pCacheAction->type = BURN_CACHE_ACTION_TYPE_CHECKPOINT; pCacheAction->checkpoint.dwId = dwCheckpoint; @@ -2123,50 +2091,8 @@ static HRESULT AddCachePackageHelper( pCacheAction->checkpoint.dwId = dwCheckpoint; } - // Plan the package start. - hr = AppendCacheAction(pPlan, &pCacheAction); - ExitOnFailure(hr, "Failed to append package start action."); - - pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_START; - pCacheAction->packageStart.pPackage = pPackage; - - // Remember the index for the package start action (which is now the last in the cache - // actions array) because we have to update this action after processing all the payloads - // and the array may be resized later which would move a pointer around in memory. - iPackageStartAction = pPlan->cCacheActions - 1; - - if (fPlanCacheRollback) - { - // Create a package cache rollback action. - hr = AppendRollbackCacheAction(pPlan, &pCacheAction); - ExitOnFailure(hr, "Failed to append rollback cache action."); - - pCacheAction->type = BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE; - pCacheAction->rollbackPackage.pPackage = pPackage; - } - - // Add all the payload cache operations to the plan for this package. - for (DWORD i = 0; i < pPackage->cPayloads; ++i) - { - BURN_PACKAGE_PAYLOAD* pPackagePayload = &pPackage->rgPayloads[i]; - - hr = AppendCacheOrLayoutPayloadAction(pPlan, pPackage, iPackageStartAction, pPackagePayload->pPayload, pPackagePayload->fCached, NULL); - ExitOnFailure(hr, "Failed to append payload cache action."); - - Assert(BURN_CACHE_ACTION_TYPE_PACKAGE_START == pPlan->rgCacheActions[iPackageStartAction].type); - ++pPlan->rgCacheActions[iPackageStartAction].packageStart.cCachePayloads; - pPlan->rgCacheActions[iPackageStartAction].packageStart.qwCachePayloadSizeTotal += pPackagePayload->pPayload->qwFileSize; - } - - // Create package stop action. - hr = AppendCacheAction(pPlan, &pCacheAction); - ExitOnFailure(hr, "Failed to append cache action."); - - pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_STOP; - pCacheAction->packageStop.pPackage = pPackage; - - // Update the start action with the location of the complete action. - pPlan->rgCacheActions[iPackageStartAction].packageStart.iPackageCompleteAction = pPlan->cCacheActions - 1; + hr = PlanLayoutPackage(pPlan, pPackage); + ExitOnFailure(hr, "Failed to plan cache for package."); // Create syncpoint action. hr = AppendCacheAction(pPlan, &pCacheAction); @@ -2178,8 +2104,6 @@ static HRESULT AddCachePackageHelper( *phSyncpointEvent = pCacheAction->syncpoint.hEvent; - ++pPlan->cOverallProgressTicksTotal; - pPackage->fPlannedCache = TRUE; if (pPackage->fCanAffectRegistration) { @@ -2225,9 +2149,9 @@ static BOOL AlreadyPlannedCachePackage( { BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + iCacheAction; - if (BURN_CACHE_ACTION_TYPE_PACKAGE_STOP == pCacheAction->type) + if (BURN_CACHE_ACTION_TYPE_PACKAGE == pCacheAction->type) { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pCacheAction->packageStop.pPackage->sczId, -1, wzPackageId, -1)) + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pCacheAction->package.pPackage->sczId, -1, wzPackageId, -1)) { if (iCacheAction + 1 < pPlan->cCacheActions && BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT == pPlan->rgCacheActions[iCacheAction + 1].type) { @@ -2284,460 +2208,39 @@ LExit: return hr; } -static HRESULT AppendLayoutContainerAction( - __in BURN_PLAN* pPlan, - __in_opt BURN_PACKAGE* pPackage, - __in DWORD iPackageStartAction, - __in BURN_CONTAINER* pContainer, - __in BOOL fContainerCached, - __in_z LPCWSTR wzLayoutDirectory - ) -{ - HRESULT hr = S_OK; - BURN_CACHE_ACTION* pAcquireAction = NULL; - DWORD iAcquireAction = BURN_PLAN_INVALID_ACTION_INDEX; - LPWSTR sczContainerWorkingPath = NULL; - BURN_CACHE_ACTION* pCacheAction = NULL; - BURN_CACHE_CONTAINER_PROGRESS* pContainerProgress = NULL; - - // No need to do anything if the container is already cached or is attached to the bundle (since the - // bundle itself will already have a layout action). - if (fContainerCached || pContainer->fAttached) - { - ExitFunction(); - } - - // Ensure the container is being acquired. If it is, then some earlier package already planned the layout of this container so - // don't do it again. Otherwise, plan away! - if (!FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pPlan, pContainer, 0, iPackageStartAction, NULL, NULL)) - { - hr = AddAcquireContainer(pPlan, pContainer, &pAcquireAction, &iAcquireAction); - ExitOnFailure(hr, "Failed to append acquire container action for layout to plan."); - - Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pAcquireAction->type); - - // Create the layout container action. - hr = StrAllocString(&sczContainerWorkingPath, pAcquireAction->resolveContainer.sczUnverifiedPath, 0); - ExitOnFailure(hr, "Failed to copy container working path for layout."); - - hr = AppendCacheAction(pPlan, &pCacheAction); - ExitOnFailure(hr, "Failed to append cache action to cache payload."); - - hr = CreateContainerProgress(pPlan, pContainer, &pContainerProgress); - ExitOnFailure(hr, "Failed to create container progress."); - - hr = StrAllocString(&pCacheAction->layoutContainer.sczLayoutDirectory, wzLayoutDirectory, 0); - ExitOnFailure(hr, "Failed to copy layout directory into plan."); - - pCacheAction->type = BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER; - pCacheAction->layoutContainer.pPackage = pPackage; - pCacheAction->layoutContainer.pContainer = pContainer; - pCacheAction->layoutContainer.iProgress = pContainerProgress->iIndex; - pCacheAction->layoutContainer.fMove = TRUE; - pCacheAction->layoutContainer.iTryAgainAction = iAcquireAction; - pCacheAction->layoutContainer.sczUnverifiedPath = sczContainerWorkingPath; - sczContainerWorkingPath = NULL; - } - -LExit: - ReleaseNullStr(sczContainerWorkingPath); - - return hr; -} - -static HRESULT AppendCacheOrLayoutPayloadAction( +static HRESULT ProcessPayloadGroup( __in BURN_PLAN* pPlan, - __in_opt BURN_PACKAGE* pPackage, - __in DWORD iPackageStartAction, - __in BURN_PAYLOAD* pPayload, - __in BOOL fPayloadCached, - __in_z_opt LPCWSTR wzLayoutDirectory + __in BURN_PAYLOAD_GROUP* pPayloadGroup ) { HRESULT hr = S_OK; - LPWSTR sczPayloadWorkingPath = NULL; - BURN_CACHE_ACTION* pCacheAction = NULL; - DWORD iTryAgainAction = BURN_PLAN_INVALID_ACTION_INDEX; - BURN_CACHE_PAYLOAD_PROGRESS* pPayloadProgress = NULL; - - hr = CacheCalculatePayloadWorkingPath(pPlan->wzBundleId, pPayload, &sczPayloadWorkingPath); - ExitOnFailure(hr, "Failed to calculate unverified path for payload."); - - // If the payload is in a container, ensure the container is being acquired - // then add this payload to the list of payloads to extract already in the plan. - if (pPayload->pContainer) - { - BURN_CACHE_ACTION* pPreviousPackageExtractAction = NULL; - BURN_CACHE_ACTION* pThisPackageExtractAction = NULL; - - // If the payload is not already cached, then add it to the first extract container action in the plan. Extracting - // all the needed payloads from the container in a single pass is the most efficient way to extract files from - // containers. If there is not an extract container action before our package, that is okay because we'll create - // an extract container action for our package in a second anyway. - if (!fPayloadCached) - { - if (FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, pPlan, pPayload->pContainer, 0, iPackageStartAction, &pPreviousPackageExtractAction, NULL)) - { - hr = AddExtractPayload(pPreviousPackageExtractAction, pPackage, pPayload, sczPayloadWorkingPath); - ExitOnFailure(hr, "Failed to add extract payload action to previous package."); - } - } - - // If there is already an extract container action after our package start action then try to find an acquire action - // that is matched with it. If there is an acquire action then that is our "try again" action, otherwise we'll use the existing - // extract action as the "try again" action. - if (FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, pPlan, pPayload->pContainer, iPackageStartAction, BURN_PLAN_INVALID_ACTION_INDEX, &pThisPackageExtractAction, &iTryAgainAction)) - { - DWORD iAcquireAction = BURN_PLAN_INVALID_ACTION_INDEX; - if (FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pPlan, pPayload->pContainer, iPackageStartAction, iTryAgainAction, NULL, &iAcquireAction)) - { - iTryAgainAction = iAcquireAction; - } - } - else // did not find an extract container action for our package. - { - // Ensure there is an extract action (and maybe an acquire action) for every package that has payloads. The - // acquire and extract action will be skipped if the payload is already cached or was added to a previous - // package's extract action above. - // - // These actions always exist (even when they are likely to be skipped) so that "try again" will not - // jump so far back in the plan that you end up extracting payloads for other packages. With these actions - // "try again" will only retry the extraction for payloads in this package. - hr = CreateContainerAcquireAndExtractAction(pPlan, pPayload->pContainer, iPackageStartAction, pPreviousPackageExtractAction ? TRUE : fPayloadCached, &pThisPackageExtractAction, &iTryAgainAction); - ExitOnFailure(hr, "Failed to create container extract action."); - } - ExitOnFailure(hr, "Failed while searching for package's container extract action."); - - // We *always* add the payload to this package's extract action even though the extract action - // is probably being skipped until retry if there was a previous package extract action. - hr = AddExtractPayload(pThisPackageExtractAction, pPackage, pPayload, sczPayloadWorkingPath); - ExitOnFailure(hr, "Failed to add extract payload to current package."); - } - else // add a payload acquire action to the plan. - { - // Try to find an existing acquire action for this payload. If one is not found, - // we'll create it. At the same time we will change any cache/layout payload actions - // that would "MOVE" the file to "COPY" so that our new cache/layout action below - // can do the move. - pCacheAction = ProcessSharedPayload(pPlan, pPayload); - if (!pCacheAction) - { - hr = AppendCacheAction(pPlan, &pCacheAction); - ExitOnFailure(hr, "Failed to append cache action to acquire payload."); - - pCacheAction->type = BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD; - pCacheAction->fSkipUntilRetried = fPayloadCached; - pCacheAction->resolvePayload.pPackage = pPackage; - pCacheAction->resolvePayload.pPayload = pPayload; - hr = StrAllocString(&pCacheAction->resolvePayload.sczUnverifiedPath, sczPayloadWorkingPath, 0); - ExitOnFailure(hr, "Failed to copy unverified path for payload to acquire."); - } - - iTryAgainAction = static_cast(pCacheAction - pPlan->rgCacheActions); - pCacheAction = NULL; - } - Assert(BURN_PLAN_INVALID_ACTION_INDEX != iTryAgainAction); - Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pPlan->rgCacheActions[iTryAgainAction].type || - BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pPlan->rgCacheActions[iTryAgainAction].type || - BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD == pPlan->rgCacheActions[iTryAgainAction].type); - - hr = AppendCacheAction(pPlan, &pCacheAction); - ExitOnFailure(hr, "Failed to append cache action to cache payload."); - - hr = CreatePayloadProgress(pPlan, pPayload, &pPayloadProgress); - ExitOnFailure(hr, "Failed to create payload progress."); - - if (!wzLayoutDirectory) + for (DWORD i = 0; i < pPayloadGroup->cPayloads; ++i) { - pCacheAction->type = BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD; - pCacheAction->cachePayload.pPackage = pPackage; - pCacheAction->cachePayload.pPayload = pPayload; - pCacheAction->cachePayload.iProgress = pPayloadProgress->iIndex; - pCacheAction->cachePayload.fMove = TRUE; - pCacheAction->cachePayload.iTryAgainAction = iTryAgainAction; - pCacheAction->cachePayload.sczUnverifiedPath = sczPayloadWorkingPath; - sczPayloadWorkingPath = NULL; - } - else - { - hr = StrAllocString(&pCacheAction->layoutPayload.sczLayoutDirectory, wzLayoutDirectory, 0); - ExitOnFailure(hr, "Failed to copy layout directory into plan."); - - pCacheAction->type = BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD; - pCacheAction->layoutPayload.pPackage = pPackage; - pCacheAction->layoutPayload.pPayload = pPayload; - pCacheAction->layoutPayload.iProgress = pPayloadProgress->iIndex; - pCacheAction->layoutPayload.fMove = TRUE; - pCacheAction->layoutPayload.iTryAgainAction = iTryAgainAction; - pCacheAction->layoutPayload.sczUnverifiedPath = sczPayloadWorkingPath; - sczPayloadWorkingPath = NULL; - } - - pCacheAction = NULL; - -LExit: - ReleaseStr(sczPayloadWorkingPath); - - return hr; -} - -static BOOL FindContainerCacheAction( - __in BURN_CACHE_ACTION_TYPE type, - __in BURN_PLAN* pPlan, - __in BURN_CONTAINER* pContainer, - __in DWORD iSearchStart, - __in DWORD iSearchEnd, - __out_opt BURN_CACHE_ACTION** ppCacheAction, - __out_opt DWORD* piCacheAction - ) -{ - BOOL fFound = FALSE; // assume we won't find what we are looking for. - - Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == type || BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == type); - - iSearchStart = (BURN_PLAN_INVALID_ACTION_INDEX == iSearchStart) ? 0 : iSearchStart; - iSearchEnd = (BURN_PLAN_INVALID_ACTION_INDEX == iSearchEnd) ? pPlan->cCacheActions : iSearchEnd; + BURN_PAYLOAD* pPayload = pPayloadGroup->rgpPayloads[i]; - for (DWORD iSearch = iSearchStart; iSearch < iSearchEnd; ++iSearch) - { - BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + iSearch; - if (pCacheAction->type == type && - ((BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pCacheAction->type && pCacheAction->resolveContainer.pContainer == pContainer) || - (BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pCacheAction->type && pCacheAction->extractContainer.pContainer == pContainer))) + if (pPayload->pContainer && !pPayload->pContainer->fPlanned) { - if (ppCacheAction) - { - *ppCacheAction = pCacheAction; - } - - if (piCacheAction) - { - *piCacheAction = iSearch; - } - - fFound = TRUE; - break; + hr = PlanLayoutContainer(pPlan, pPayload->pContainer); + ExitOnFailure(hr, "Failed to plan container: %ls", pPayload->pContainer->sczId); } - } - - return fFound; -} -static HRESULT CreateContainerAcquireAndExtractAction( - __in BURN_PLAN* pPlan, - __in BURN_CONTAINER* pContainer, - __in DWORD iPackageStartAction, - __in BOOL fPayloadCached, - __out BURN_CACHE_ACTION** ppContainerExtractAction, - __out DWORD* piContainerTryAgainAction - ) -{ - HRESULT hr = S_OK; - DWORD iAcquireAction = BURN_PLAN_INVALID_ACTION_INDEX; - BURN_CACHE_ACTION* pContainerExtractAction = NULL; - DWORD iExtractAction = BURN_PLAN_INVALID_ACTION_INDEX; - DWORD iTryAgainAction = BURN_PLAN_INVALID_ACTION_INDEX; - LPWSTR sczContainerWorkingPath = NULL; - - // If the container is actually attached to the executable then we will not need an acquire - // container action. - if (!pContainer->fActuallyAttached) - { - BURN_CACHE_ACTION* pAcquireContainerAction = NULL; - - // If there is no plan to acquire the container then add acquire action since we - // can't extract stuff out of a container until we acquire the container. - if (!FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pPlan, pContainer, iPackageStartAction, BURN_PLAN_INVALID_ACTION_INDEX, &pAcquireContainerAction, &iAcquireAction)) + if (!pPlan->sczLayoutDirectory || !pPayload->pContainer) { - hr = AddAcquireContainer(pPlan, pContainer, &pAcquireContainerAction, &iAcquireAction); - ExitOnFailure(hr, "Failed to append acquire container action to plan."); - - pAcquireContainerAction->fSkipUntilRetried = TRUE; // we'll start by assuming the acquire is not necessary and the fPayloadCached below will set us straight if wrong. + pPlan->qwCacheSizeTotal += 2 * pPayload->qwFileSize; } - Assert(BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction); - Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pAcquireContainerAction->type); - Assert(pContainer == pAcquireContainerAction->resolveContainer.pContainer); - } - - Assert((pContainer->fActuallyAttached && BURN_PLAN_INVALID_ACTION_INDEX == iAcquireAction) || - (!pContainer->fActuallyAttached && BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction)); - - // If we do not find an action for extracting payloads from this container, create it now. - if (!FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, pPlan, pContainer, (BURN_PLAN_INVALID_ACTION_INDEX == iAcquireAction) ? iPackageStartAction : iAcquireAction, BURN_PLAN_INVALID_ACTION_INDEX, &pContainerExtractAction, &iExtractAction)) - { - // Attached containers that are actually attached use the executable path for their working path. - if (pContainer->fActuallyAttached) + if (!pPayload->sczUnverifiedPath) { - Assert(BURN_PLAN_INVALID_ACTION_INDEX == iAcquireAction); - - hr = PathForCurrentProcess(&sczContainerWorkingPath, NULL); - ExitOnFailure(hr, "Failed to get path for executing module as attached container working path."); + hr = CacheCalculatePayloadWorkingPath(pPlan->wzBundleId, pPayload, &pPayload->sczUnverifiedPath); + ExitOnFailure(hr, "Failed to calculate unverified path for payload."); } - else // use the acquired working path as the location of the container. - { - Assert(BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction); - - hr = StrAllocString(&sczContainerWorkingPath, pPlan->rgCacheActions[iAcquireAction].resolveContainer.sczUnverifiedPath, 0); - ExitOnFailure(hr, "Failed to copy container unverified path for cache action to extract container."); - } - - hr = AppendCacheAction(pPlan, &pContainerExtractAction); - ExitOnFailure(hr, "Failed to append cache action to extract payloads from container."); - - iExtractAction = pPlan->cCacheActions - 1; - - pContainerExtractAction->type = BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER; - pContainerExtractAction->fSkipUntilRetried = pContainer->fActuallyAttached; // assume we can skip the extract engine when the container is already attached and the fPayloadCached below will set us straight if wrong. - pContainerExtractAction->extractContainer.pContainer = pContainer; - pContainerExtractAction->extractContainer.iSkipUntilAcquiredByAction = iAcquireAction; - pContainerExtractAction->extractContainer.sczContainerUnverifiedPath = sczContainerWorkingPath; - sczContainerWorkingPath = NULL; - } - - Assert(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pContainerExtractAction->type); - Assert(BURN_PLAN_INVALID_ACTION_INDEX != iExtractAction); - - // If there is an acquire action, that is our try again action. Otherwise, we'll use the extract action. - iTryAgainAction = (BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction) ? iAcquireAction : iExtractAction; - - // If the try again action thinks it can be skipped but the payload is not cached, - // ensure the action will not be skipped. - BURN_CACHE_ACTION* pTryAgainAction = pPlan->rgCacheActions + iTryAgainAction; - Assert((BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pTryAgainAction->type && pContainer == pTryAgainAction->resolveContainer.pContainer) || - (BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pTryAgainAction->type && pContainer == pTryAgainAction->extractContainer.pContainer)); - if (pTryAgainAction->fSkipUntilRetried && !fPayloadCached) - { - pTryAgainAction->fSkipUntilRetried = FALSE; - } - - *ppContainerExtractAction = pContainerExtractAction; - *piContainerTryAgainAction = iTryAgainAction; - -LExit: - ReleaseStr(sczContainerWorkingPath); - - return hr; -} - -static HRESULT AddAcquireContainer( - __in BURN_PLAN* pPlan, - __in BURN_CONTAINER* pContainer, - __out_opt BURN_CACHE_ACTION** ppCacheAction, - __out_opt DWORD* piCacheAction - ) -{ - HRESULT hr = S_OK; - LPWSTR sczContainerWorkingPath = NULL; - BURN_CACHE_ACTION* pAcquireContainerAction = NULL; - BURN_CACHE_CONTAINER_PROGRESS* pContainerProgress = NULL; - - hr = CacheCalculateContainerWorkingPath(pPlan->wzBundleId, pContainer, &sczContainerWorkingPath); - ExitOnFailure(hr, "Failed to calculate unverified path for container."); - - hr = AppendCacheAction(pPlan, &pAcquireContainerAction); - ExitOnFailure(hr, "Failed to append acquire container action to plan."); - - hr = CreateContainerProgress(pPlan, pContainer, &pContainerProgress); - ExitOnFailure(hr, "Failed to create container progress."); - - pAcquireContainerAction->type = BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER; - pAcquireContainerAction->resolveContainer.pContainer = pContainer; - pAcquireContainerAction->resolveContainer.iProgress = pContainerProgress->iIndex; - pAcquireContainerAction->resolveContainer.sczUnverifiedPath = sczContainerWorkingPath; - sczContainerWorkingPath = NULL; - - if (ppCacheAction) - { - *ppCacheAction = pAcquireContainerAction; - } - - if (piCacheAction) - { - *piCacheAction = pPlan->cCacheActions - 1; } -LExit: - ReleaseStr(sczContainerWorkingPath); - - return hr; -} - -static HRESULT AddExtractPayload( - __in BURN_CACHE_ACTION* pCacheAction, - __in_opt BURN_PACKAGE* pPackage, - __in BURN_PAYLOAD* pPayload, - __in_z LPCWSTR wzPayloadWorkingPath - ) -{ - HRESULT hr = S_OK; - - Assert(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pCacheAction->type); - - hr = MemEnsureArraySize(reinterpret_cast(&pCacheAction->extractContainer.rgPayloads), pCacheAction->extractContainer.cPayloads + 1, sizeof(BURN_EXTRACT_PAYLOAD), 5); - ExitOnFailure(hr, "Failed to grow list of payloads to extract from container."); - - BURN_EXTRACT_PAYLOAD* pExtractPayload = pCacheAction->extractContainer.rgPayloads + pCacheAction->extractContainer.cPayloads; - pExtractPayload->pPackage = pPackage; - pExtractPayload->pPayload = pPayload; - hr = StrAllocString(&pExtractPayload->sczUnverifiedPath, wzPayloadWorkingPath, 0); - ExitOnFailure(hr, "Failed to copy unverified path for payload to extract."); - ++pCacheAction->extractContainer.cPayloads; - LExit: return hr; } -static BURN_CACHE_ACTION* ProcessSharedPayload( - __in BURN_PLAN* pPlan, - __in BURN_PAYLOAD* pPayload - ) -{ - BURN_CACHE_ACTION* pAcquireAction = NULL; -#ifdef DEBUG - DWORD cMove = 0; -#endif - - for (DWORD i = 0; i < pPlan->cCacheActions; ++i) - { - BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + i; - - if (BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD == pCacheAction->type && - pCacheAction->resolvePayload.pPayload == pPayload) - { - AssertSz(!pAcquireAction, "There should be at most one acquire cache action per payload."); - pAcquireAction = pCacheAction; - } - else if (BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD == pCacheAction->type && - pCacheAction->cachePayload.pPayload == pPayload && - pCacheAction->cachePayload.fMove) - { - // Since we found a shared payload, change its operation from MOVE to COPY. - pCacheAction->cachePayload.fMove = FALSE; - - AssertSz(1 == ++cMove, "Shared payload should be moved once and only once."); -#ifndef DEBUG - break; -#endif - } - else if (BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD == pCacheAction->type && - pCacheAction->layoutPayload.pPayload == pPayload && - pCacheAction->layoutPayload.fMove) - { - // Since we found a shared payload, change its operation from MOVE to COPY if necessary - pCacheAction->layoutPayload.fMove = FALSE; - - AssertSz(1 == ++cMove, "Shared payload should be moved once and only once."); -#ifndef DEBUG - break; -#endif - } - } - - return pAcquireAction; -} - static void RemoveUnnecessaryActions( __in BOOL fExecute, __in BURN_EXECUTE_ACTION* rgActions, @@ -2984,86 +2487,6 @@ static BOOL NeedsCache( } } -static HRESULT CreateContainerProgress( - __in BURN_PLAN* pPlan, - __in BURN_CONTAINER* pContainer, - __out BURN_CACHE_CONTAINER_PROGRESS** ppContainerProgress - ) -{ - HRESULT hr = S_OK; - BURN_CACHE_CONTAINER_PROGRESS* pContainerProgress = NULL; - - hr = MemEnsureArraySize(reinterpret_cast(&pPlan->rgContainerProgress), pPlan->cContainerProgress + 1, sizeof(BURN_CACHE_CONTAINER_PROGRESS), 5); - ExitOnFailure(hr, "Failed to grow container progress list."); - - if (!pPlan->shContainerProgress) - { - hr = DictCreateWithEmbeddedKey(&pPlan->shContainerProgress, 5, reinterpret_cast(&pPlan->rgContainerProgress), offsetof(BURN_CACHE_CONTAINER_PROGRESS, wzId), DICT_FLAG_NONE); - ExitOnFailure(hr, "Failed to create container progress dictionary."); - } - - hr = DictGetValue(pPlan->shContainerProgress, pContainer->sczId, reinterpret_cast(&pContainerProgress)); - if (E_NOTFOUND == hr) - { - pContainerProgress = &pPlan->rgContainerProgress[pPlan->cContainerProgress]; - pContainerProgress->iIndex = pPlan->cContainerProgress; - pContainerProgress->pContainer = pContainer; - pContainerProgress->wzId = pContainer->sczId; - - hr = DictAddValue(pPlan->shContainerProgress, pContainerProgress); - ExitOnFailure(hr, "Failed to add \"%ls\" to the container progress dictionary.", pContainerProgress->wzId); - - ++pPlan->cContainerProgress; - pPlan->qwCacheSizeTotal += pContainer->qwFileSize; - } - ExitOnFailure(hr, "Failed to retrieve \"%ls\" from the container progress dictionary.", pContainer->sczId); - - *ppContainerProgress = pContainerProgress; - -LExit: - return hr; -} - -static HRESULT CreatePayloadProgress( - __in BURN_PLAN* pPlan, - __in BURN_PAYLOAD* pPayload, - __out BURN_CACHE_PAYLOAD_PROGRESS** ppPayloadProgress - ) -{ - HRESULT hr = S_OK; - BURN_CACHE_PAYLOAD_PROGRESS* pPayloadProgress = NULL; - - hr = MemEnsureArraySize(reinterpret_cast(&pPlan->rgPayloadProgress), pPlan->cPayloadProgress + 1, sizeof(BURN_CACHE_PAYLOAD_PROGRESS), 5); - ExitOnFailure(hr, "Failed to grow payload progress list."); - - if (!pPlan->shPayloadProgress) - { - hr = DictCreateWithEmbeddedKey(&pPlan->shPayloadProgress, 5, reinterpret_cast(&pPlan->rgPayloadProgress), offsetof(BURN_CACHE_PAYLOAD_PROGRESS, wzId), DICT_FLAG_NONE); - ExitOnFailure(hr, "Failed to create payload progress dictionary."); - } - - hr = DictGetValue(pPlan->shPayloadProgress, pPayload->sczKey, reinterpret_cast(&pPayloadProgress)); - if (E_NOTFOUND == hr) - { - pPayloadProgress = &pPlan->rgPayloadProgress[pPlan->cPayloadProgress]; - pPayloadProgress->iIndex = pPlan->cPayloadProgress; - pPayloadProgress->pPayload = pPayload; - pPayloadProgress->wzId = pPayload->sczKey; - - hr = DictAddValue(pPlan->shPayloadProgress, pPayloadProgress); - ExitOnFailure(hr, "Failed to add \"%ls\" to the payload progress dictionary.", pPayloadProgress->wzId); - - ++pPlan->cPayloadProgress; - pPlan->qwCacheSizeTotal += pPayload->qwFileSize; - } - ExitOnFailure(hr, "Failed to retrieve \"%ls\" from the payload progress dictionary.", pPayload->sczKey); - - *ppPayloadProgress = pPayloadProgress; - -LExit: - return hr; -} - static void CacheActionLog( __in DWORD iAction, __in BURN_CACHE_ACTION* pAction, @@ -3073,60 +2496,28 @@ static void CacheActionLog( LPCWSTR wzBase = fRollback ? L" Rollback cache" : L" Cache"; switch (pAction->type) { - case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER: - LogStringLine(PlanDumpLevel, "%ls action[%u]: ACQUIRE_CONTAINER id: %ls, source path: %ls, working path: %ls, skip until retried: %hs", wzBase, iAction, pAction->resolveContainer.pContainer->sczId, pAction->resolveContainer.pContainer->sczSourcePath, pAction->resolveContainer.sczUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried)); - break; - - case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD: - LogStringLine(PlanDumpLevel, "%ls action[%u]: ACQUIRE_PAYLOAD package id: %ls, payload id: %ls, source path: %ls, working path: %ls, skip until retried: %hs", wzBase, iAction, pAction->resolvePayload.pPackage ? pAction->resolvePayload.pPackage->sczId : L"", pAction->resolvePayload.pPayload->sczKey, pAction->resolvePayload.pPayload->sczSourcePath, pAction->resolvePayload.sczUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried)); - break; - - case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD: - LogStringLine(PlanDumpLevel, "%ls action[%u]: CACHE_PAYLOAD package id: %ls, payload id: %ls, working path: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->cachePayload.pPackage->sczId, pAction->cachePayload.pPayload->sczKey, pAction->cachePayload.sczUnverifiedPath, pAction->cachePayload.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->cachePayload.iTryAgainAction); - break; - case BURN_CACHE_ACTION_TYPE_CHECKPOINT: LogStringLine(PlanDumpLevel, "%ls action[%u]: CHECKPOINT id: %u", wzBase, iAction, pAction->checkpoint.dwId); break; - case BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER: - LogStringLine(PlanDumpLevel, "%ls action[%u]: EXTRACT_CONTAINER id: %ls, working path: %ls, skip until retried: %hs, skip until acquired by action: %u", wzBase, iAction, pAction->extractContainer.pContainer->sczId, pAction->extractContainer.sczContainerUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried), pAction->extractContainer.iSkipUntilAcquiredByAction); - for (DWORD j = 0; j < pAction->extractContainer.cPayloads; j++) - { - LogStringLine(PlanDumpLevel, " extract package id: %ls, payload id: %ls, working path: %ls", pAction->extractContainer.rgPayloads[j].pPackage->sczId, pAction->extractContainer.rgPayloads[j].pPayload->sczKey, pAction->extractContainer.rgPayloads[j].sczUnverifiedPath); - } - break; - case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: - LogStringLine(PlanDumpLevel, "%ls action[%u]: LAYOUT_BUNDLE working path: %ls, layout directory: %ls, exe name: %ls, skip until retried: %hs", wzBase, iAction, pAction->bundleLayout.sczUnverifiedPath, pAction->bundleLayout.sczLayoutDirectory, pAction->bundleLayout.sczExecutableName, LoggingBoolToString(pAction->fSkipUntilRetried)); - break; - - case BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER: - LogStringLine(PlanDumpLevel, "%ls action[%u]: LAYOUT_CONTAINER package id: %ls, container id: %ls, working path: %ls, layout directory: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->layoutContainer.pPackage ? pAction->layoutContainer.pPackage->sczId : L"", pAction->layoutContainer.pContainer->sczId, pAction->layoutContainer.sczUnverifiedPath, pAction->layoutContainer.sczLayoutDirectory, pAction->layoutContainer.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->layoutContainer.iTryAgainAction); - break; - - case BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD: - LogStringLine(PlanDumpLevel, "%ls action[%u]: LAYOUT_PAYLOAD package id: %ls, payload id: %ls, working path: %ls, layout directory: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->layoutPayload.pPackage ? pAction->layoutPayload.pPackage->sczId : L"", pAction->layoutPayload.pPayload->sczKey, pAction->layoutPayload.sczUnverifiedPath, pAction->layoutPayload.sczLayoutDirectory, pAction->layoutPayload.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->layoutPayload.iTryAgainAction); + LogStringLine(PlanDumpLevel, "%ls action[%u]: LAYOUT_BUNDLE working path: %ls, exe name: %ls", wzBase, iAction, pAction->bundleLayout.sczUnverifiedPath, pAction->bundleLayout.sczExecutableName); break; - case BURN_CACHE_ACTION_TYPE_PACKAGE_START: - LogStringLine(PlanDumpLevel, "%ls action[%u]: PACKAGE_START id: %ls, plan index for skip: %u, payloads to cache: %u, bytes to cache: %llu, skip until retried: %hs", wzBase, iAction, pAction->packageStart.pPackage->sczId, pAction->packageStart.iPackageCompleteAction, pAction->packageStart.cCachePayloads, pAction->packageStart.qwCachePayloadSizeTotal, LoggingBoolToString(pAction->fSkipUntilRetried)); + case BURN_CACHE_ACTION_TYPE_CONTAINER: + LogStringLine(PlanDumpLevel, "%ls action[%u]: CONTAINER container id: %ls, working path: %ls", wzBase, iAction, pAction->container.pContainer->sczId, pAction->container.pContainer->sczUnverifiedPath); break; - case BURN_CACHE_ACTION_TYPE_PACKAGE_STOP: - LogStringLine(PlanDumpLevel, "%ls action[%u]: PACKAGE_STOP id: %ls, skip until retried: %hs", wzBase, iAction, pAction->packageStop.pPackage->sczId, LoggingBoolToString(pAction->fSkipUntilRetried)); + case BURN_CACHE_ACTION_TYPE_PACKAGE: + LogStringLine(PlanDumpLevel, "%ls action[%u]: PACKAGE id: %ls", wzBase, iAction, pAction->package.pPackage->sczId); break; case BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE: - LogStringLine(PlanDumpLevel, "%ls action[%u]: ROLLBACK_PACKAGE id: %ls, skip until retried: %hs", wzBase, iAction, pAction->rollbackPackage.pPackage->sczId, LoggingBoolToString(pAction->fSkipUntilRetried)); + LogStringLine(PlanDumpLevel, "%ls action[%u]: ROLLBACK_PACKAGE id: %ls", wzBase, iAction, pAction->rollbackPackage.pPackage->sczId); break; case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT: - LogStringLine(PlanDumpLevel, "%ls action[%u]: SIGNAL_SYNCPOINT event handle: 0x%p, skip until retried: %hs", wzBase, iAction, pAction->syncpoint.hEvent, LoggingBoolToString(pAction->fSkipUntilRetried)); - break; - - case BURN_CACHE_ACTION_TYPE_TRANSACTION_BOUNDARY: - LogStringLine(PlanDumpLevel, "%ls action[%u]: TRANSACTION_BOUNDARY id: %ls, event handle: 0x%p, vital: %ls, transaction: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.hEvent, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no", pAction->rollbackBoundary.pRollbackBoundary->fTransaction ? L"yes" : L"no"); + LogStringLine(PlanDumpLevel, "%ls action[%u]: SIGNAL_SYNCPOINT event handle: 0x%p", wzBase, iAction, pAction->syncpoint.hEvent); break; default: @@ -3222,6 +2613,10 @@ extern "C" void PlanDump( LogStringLine(PlanDumpLevel, " per-machine: %hs", LoggingTrueFalseToString(pPlan->fPerMachine)); LogStringLine(PlanDumpLevel, " disable-rollback: %hs", LoggingTrueFalseToString(pPlan->fDisableRollback)); LogStringLine(PlanDumpLevel, " estimated size: %llu", pPlan->qwEstimatedSize); + if (pPlan->sczLayoutDirectory) + { + LogStringLine(PlanDumpLevel, " layout directory: %ls", pPlan->sczLayoutDirectory); + } LogStringLine(PlanDumpLevel, "Plan cache size: %llu", pPlan->qwCacheSizeTotal); for (DWORD i = 0; i < pPlan->cCacheActions; ++i) diff --git a/src/engine/plan.h b/src/engine/plan.h index 0024b0aa..4ba2df6a 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -38,17 +38,10 @@ enum BURN_CACHE_ACTION_TYPE BURN_CACHE_ACTION_TYPE_NONE, BURN_CACHE_ACTION_TYPE_CHECKPOINT, BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE, - BURN_CACHE_ACTION_TYPE_PACKAGE_START, - BURN_CACHE_ACTION_TYPE_PACKAGE_STOP, + BURN_CACHE_ACTION_TYPE_PACKAGE, BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE, BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT, - BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, - BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, - BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER, - BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD, - BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD, - BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD, - BURN_CACHE_ACTION_TYPE_TRANSACTION_BOUNDARY, + BURN_CACHE_ACTION_TYPE_CONTAINER, }; enum BURN_EXECUTE_ACTION_TYPE @@ -78,13 +71,6 @@ enum BURN_CLEAN_ACTION_TYPE // structs -typedef struct _BURN_EXTRACT_PAYLOAD -{ - BURN_PACKAGE* pPackage; - BURN_PAYLOAD* pPayload; - LPWSTR sczUnverifiedPath; -} BURN_EXTRACT_PAYLOAD; - typedef struct _BURN_DEPENDENT_REGISTRATION_ACTION { BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type; @@ -111,7 +97,6 @@ typedef struct _BURN_CACHE_PAYLOAD_PROGRESS typedef struct _BURN_CACHE_ACTION { BURN_CACHE_ACTION_TYPE type; - BOOL fSkipUntilRetried; union { struct @@ -121,21 +106,14 @@ typedef struct _BURN_CACHE_ACTION struct { LPWSTR sczExecutableName; - LPWSTR sczLayoutDirectory; LPWSTR sczUnverifiedPath; DWORD64 qwBundleSize; + BURN_PAYLOAD_GROUP* pPayloadGroup; } bundleLayout; struct { BURN_PACKAGE* pPackage; - DWORD cCachePayloads; - DWORD64 qwCachePayloadSizeTotal; - DWORD iPackageCompleteAction; - } packageStart; - struct - { - BURN_PACKAGE* pPackage; - } packageStop; + } package; struct { BURN_PACKAGE* pPackage; @@ -147,62 +125,7 @@ typedef struct _BURN_CACHE_ACTION struct { BURN_CONTAINER* pContainer; - DWORD iProgress; - LPWSTR sczUnverifiedPath; - } resolveContainer; - struct - { - BURN_CONTAINER* pContainer; - DWORD iSkipUntilAcquiredByAction; - LPWSTR sczContainerUnverifiedPath; - - BURN_EXTRACT_PAYLOAD* rgPayloads; - DWORD cPayloads; - } extractContainer; - struct - { - BURN_PACKAGE* pPackage; - BURN_CONTAINER* pContainer; - DWORD iProgress; - DWORD iTryAgainAction; - DWORD cTryAgainAttempts; - LPWSTR sczLayoutDirectory; - LPWSTR sczUnverifiedPath; - BOOL fMove; - } layoutContainer; - struct - { - BURN_PACKAGE* pPackage; - BURN_PAYLOAD* pPayload; - DWORD iProgress; - LPWSTR sczUnverifiedPath; - } resolvePayload; - struct - { - BURN_PACKAGE* pPackage; - BURN_PAYLOAD* pPayload; - DWORD iProgress; - DWORD iTryAgainAction; - DWORD cTryAgainAttempts; - LPWSTR sczUnverifiedPath; - BOOL fMove; - } cachePayload; - struct - { - BURN_PACKAGE* pPackage; - BURN_PAYLOAD* pPayload; - DWORD iProgress; - DWORD iTryAgainAction; - DWORD cTryAgainAttempts; - LPWSTR sczLayoutDirectory; - LPWSTR sczUnverifiedPath; - BOOL fMove; - } layoutPayload; - struct - { - BURN_ROLLBACK_BOUNDARY* pRollbackBoundary; - HANDLE hEvent; - } rollbackBoundary; + } container; }; } BURN_CACHE_ACTION; @@ -306,6 +229,7 @@ typedef struct _BURN_CLEAN_ACTION typedef struct _BURN_PLAN { BOOTSTRAPPER_ACTION action; + BURN_PAYLOADS* pPayloads; // points directly into parent the ENGINE_STATE. LPWSTR wzBundleId; // points directly into parent the ENGINE_STATE. LPWSTR wzBundleProviderKey; // points directly into parent the ENGINE_STATE. BOOL fPerMachine; @@ -315,6 +239,7 @@ typedef struct _BURN_PLAN BOOL fDisableRollback; BOOL fAffectedMachineState; BOOL fIgnoreAllDependents; + LPWSTR sczLayoutDirectory; DWORD64 qwCacheSizeTotal; @@ -369,7 +294,9 @@ typedef struct _BURN_PLAN void PlanReset( __in BURN_PLAN* pPlan, - __in BURN_PACKAGES* pPackages + __in BURN_CONTAINERS* pContainers, + __in BURN_PACKAGES* pPackages, + __in BURN_PAYLOAD_GROUP* pLayoutPayloads ); void PlanUninitializeExecuteAction( __in BURN_EXECUTE_ACTION* pExecuteAction @@ -393,8 +320,7 @@ HRESULT PlanLayoutBundle( __in_z LPCWSTR wzExecutableName, __in DWORD64 qwBundleSize, __in BURN_VARIABLES* pVariables, - __in BURN_PAYLOADS* pPayloads, - __out_z LPWSTR* psczLayoutDirectory + __in BURN_PAYLOAD_GROUP* pLayoutPayloads ); HRESULT PlanForwardCompatibleBundles( __in BURN_USER_EXPERIENCE* pUX, @@ -410,8 +336,7 @@ HRESULT PlanPackages( __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables, __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in_z_opt LPCWSTR wzLayoutDirectory + __in BOOTSTRAPPER_RELATION_TYPE relationType ); HRESULT PlanRegistration( __in BURN_PLAN* pPlan, @@ -438,10 +363,13 @@ HRESULT PlanUpdateBundle( __in BOOTSTRAPPER_DISPLAY display, __in BOOTSTRAPPER_RELATION_TYPE relationType ); +HRESULT PlanLayoutContainer( + __in BURN_PLAN* pPlan, + __in BURN_CONTAINER* pContainer + ); HRESULT PlanLayoutPackage( __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage, - __in_z_opt LPCWSTR wzLayoutDirectory + __in BURN_PACKAGE* pPackage ); HRESULT PlanExecutePackage( __in BOOL fPerMachine, diff --git a/src/engine/pseudobundle.cpp b/src/engine/pseudobundle.cpp index 73fbb019..db25096b 100644 --- a/src/engine/pseudobundle.cpp +++ b/src/engine/pseudobundle.cpp @@ -26,6 +26,7 @@ extern "C" HRESULT PseudoBundleInitialize( { HRESULT hr = S_OK; LPWSTR sczRelationTypeCommandLineSwitch = NULL; + BURN_PAYLOAD* pPayload = NULL; LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType); if (wzRelationTypeCommandLine) @@ -34,41 +35,40 @@ extern "C" HRESULT PseudoBundleInitialize( } // Initialize the single payload, and fill out all the necessary fields - pPackage->rgPayloads = (BURN_PACKAGE_PAYLOAD *)MemAlloc(sizeof(BURN_PACKAGE_PAYLOAD), TRUE); - ExitOnNull(pPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate space for burn package payload inside of related bundle struct"); - pPackage->cPayloads = 1; + pPackage->payloads.rgpPayloads = (BURN_PAYLOAD**)MemAlloc(sizeof(BURN_PAYLOAD*), TRUE); + ExitOnNull(pPackage->payloads.rgpPayloads, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload group inside of related bundle struct"); + pPackage->payloads.cPayloads = 1; - pPackage->rgPayloads->pPayload = (BURN_PAYLOAD *)MemAlloc(sizeof(BURN_PAYLOAD), TRUE); - ExitOnNull(pPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload inside of related bundle struct"); - pPackage->rgPayloads->pPayload->packaging = BURN_PAYLOAD_PACKAGING_EXTERNAL; - pPackage->rgPayloads->pPayload->qwFileSize = qwSize; + pPayload = (BURN_PAYLOAD*)MemAlloc(sizeof(BURN_PAYLOAD), TRUE); + ExitOnNull(pPayload, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload inside of related bundle struct"); + pPackage->payloads.rgpPayloads[0] = pPayload; + pPayload->packaging = BURN_PAYLOAD_PACKAGING_EXTERNAL; + pPayload->qwFileSize = qwSize; - hr = StrAllocString(&pPackage->rgPayloads->pPayload->sczKey, wzId, 0); + hr = StrAllocString(&pPayload->sczKey, wzId, 0); ExitOnFailure(hr, "Failed to copy key for pseudo bundle payload."); - hr = StrAllocString(&pPackage->rgPayloads->pPayload->sczFilePath, wzFilePath, 0); + hr = StrAllocString(&pPayload->sczFilePath, wzFilePath, 0); ExitOnFailure(hr, "Failed to copy filename for pseudo bundle."); - hr = StrAllocString(&pPackage->rgPayloads->pPayload->sczSourcePath, wzLocalSource, 0); + hr = StrAllocString(&pPayload->sczSourcePath, wzLocalSource, 0); ExitOnFailure(hr, "Failed to copy local source path for pseudo bundle."); if (wzDownloadSource && *wzDownloadSource) { - hr = StrAllocString(&pPackage->rgPayloads->pPayload->downloadSource.sczUrl, wzDownloadSource, 0); + hr = StrAllocString(&pPayload->downloadSource.sczUrl, wzDownloadSource, 0); ExitOnFailure(hr, "Failed to copy download source for pseudo bundle."); } if (pbHash) { - pPackage->rgPayloads->pPayload->pbHash = static_cast(MemAlloc(cbHash, FALSE)); - ExitOnNull(pPackage->rgPayloads->pPayload->pbHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for pseudo bundle payload hash."); + pPayload->pbHash = static_cast(MemAlloc(cbHash, FALSE)); + ExitOnNull(pPayload->pbHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for pseudo bundle payload hash."); - pPackage->rgPayloads->pPayload->cbHash = cbHash; - memcpy_s(pPackage->rgPayloads->pPayload->pbHash, pPackage->rgPayloads->pPayload->cbHash, pbHash, cbHash); + pPayload->cbHash = cbHash; + memcpy_s(pPayload->pbHash, pPayload->cbHash, pbHash, cbHash); } - pPackage->rgPayloads->fCached = fCached; - pPackage->Exe.fPseudoBundle = TRUE; pPackage->type = BURN_PACKAGE_TYPE_EXE; @@ -171,44 +171,13 @@ extern "C" HRESULT PseudoBundleInitializePassthrough( LPWSTR sczArguments = NULL; // Initialize the payloads, and copy the necessary fields. - pPassthroughPackage->rgPayloads = (BURN_PACKAGE_PAYLOAD *)MemAlloc(sizeof(BURN_PACKAGE_PAYLOAD) * pPackage->cPayloads, TRUE); - ExitOnNull(pPassthroughPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate space for burn package payload inside of passthrough bundle."); - pPassthroughPackage->cPayloads = pPackage->cPayloads; + pPassthroughPackage->payloads.rgpPayloads = (BURN_PAYLOAD**)MemAlloc(sizeof(BURN_PAYLOAD*) * pPackage->payloads.cPayloads, TRUE); + ExitOnNull(pPassthroughPackage->payloads.rgpPayloads, hr, E_OUTOFMEMORY, "Failed to allocate space for burn package payload inside of passthrough bundle."); + pPassthroughPackage->payloads.cPayloads = pPackage->payloads.cPayloads; - for (DWORD iPayload = 0; iPayload < pPackage->cPayloads; ++iPayload) + for (DWORD iPayload = 0; iPayload < pPackage->payloads.cPayloads; ++iPayload) { - BURN_PACKAGE_PAYLOAD* pPayload = pPackage->rgPayloads + iPayload; - - pPassthroughPackage->rgPayloads[iPayload].pPayload = (BURN_PAYLOAD *)MemAlloc(sizeof(BURN_PAYLOAD), TRUE); - ExitOnNull(pPassthroughPackage->rgPayloads[iPayload].pPayload, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload inside of related bundle struct"); - pPassthroughPackage->rgPayloads[iPayload].pPayload->packaging = pPayload->pPayload->packaging; - pPassthroughPackage->rgPayloads[iPayload].pPayload->qwFileSize = pPayload->pPayload->qwFileSize; - - hr = StrAllocString(&pPassthroughPackage->rgPayloads[iPayload].pPayload->sczKey, pPayload->pPayload->sczKey, 0); - ExitOnFailure(hr, "Failed to copy key for passthrough pseudo bundle payload."); - - hr = StrAllocString(&pPassthroughPackage->rgPayloads[iPayload].pPayload->sczFilePath, pPayload->pPayload->sczFilePath, 0); - ExitOnFailure(hr, "Failed to copy filename for passthrough pseudo bundle."); - - hr = StrAllocString(&pPassthroughPackage->rgPayloads[iPayload].pPayload->sczSourcePath, pPayload->pPayload->sczSourcePath, 0); - ExitOnFailure(hr, "Failed to copy local source path for passthrough pseudo bundle."); - - if (pPayload->pPayload->downloadSource.sczUrl) - { - hr = StrAllocString(&pPassthroughPackage->rgPayloads[iPayload].pPayload->downloadSource.sczUrl, pPayload->pPayload->downloadSource.sczUrl, 0); - ExitOnFailure(hr, "Failed to copy download source for passthrough pseudo bundle."); - } - - if (pPayload->pPayload->pbHash) - { - pPassthroughPackage->rgPayloads[iPayload].pPayload->pbHash = static_cast(MemAlloc(pPayload->pPayload->cbHash, FALSE)); - ExitOnNull(pPassthroughPackage->rgPayloads[iPayload].pPayload->pbHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for pseudo bundle payload hash."); - - pPassthroughPackage->rgPayloads[iPayload].pPayload->cbHash = pPayload->pPayload->cbHash; - memcpy_s(pPassthroughPackage->rgPayloads[iPayload].pPayload->pbHash, pPassthroughPackage->rgPayloads[iPayload].pPayload->cbHash, pPayload->pPayload->pbHash, pPayload->pPayload->cbHash); - } - - pPassthroughPackage->rgPayloads[iPayload].fCached = pPayload->fCached; + pPassthroughPackage->payloads.rgpPayloads[iPayload] = pPackage->payloads.rgpPayloads[iPayload]; } pPassthroughPackage->Exe.fPseudoBundle = TRUE; diff --git a/src/engine/relatedbundle.cpp b/src/engine/relatedbundle.cpp index 6953c678..a79be020 100644 --- a/src/engine/relatedbundle.cpp +++ b/src/engine/relatedbundle.cpp @@ -80,7 +80,14 @@ extern "C" void RelatedBundlesUninitialize( { for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i) { - PackageUninitialize(&pRelatedBundles->rgRelatedBundles[i].package); + BURN_PACKAGE* pPackage = &pRelatedBundles->rgRelatedBundles[i].package; + + for (DWORD j = 0; j < pPackage->payloads.cPayloads; ++j) + { + PayloadUninitialize(pPackage->payloads.rgpPayloads[j]); + } + + PackageUninitialize(pPackage); ReleaseStr(pRelatedBundles->rgRelatedBundles[i].sczTag); } diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index c1641675..b6bd65dc 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -62,7 +62,7 @@ extern "C" HRESULT UserExperienceParseFromXml( } // parse payloads - hr = PayloadsParseFromXml(&pUserExperience->payloads, NULL, pixnUserExperienceNode); + hr = PayloadsParseFromXml(&pUserExperience->payloads, NULL, NULL, pixnUserExperienceNode); ExitOnFailure(hr, "Failed to parse user experience payloads."); // make sure we have at least one payload diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp index 2ebbca74..aa9deaf6 100644 --- a/src/test/BurnUnitTest/PlanTest.cpp +++ b/src/test/BurnUnitTest/PlanTest.cpp @@ -54,47 +54,30 @@ namespace Bootstrapper BOOL fRollback = FALSE; DWORD dwIndex = 0; - DWORD dwPackageStart = 0; ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", 6, 2, 33743, FALSE); - ValidateCacheAcquireContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE); - ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, dwPackageStart, 6); - ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"PackageA", TRUE, FALSE, dwPackageStart); - ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"cab9Ins_fTP3wNwq5Gxo41ch5VUPaQ", TRUE, FALSE, dwPackageStart); - ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); - ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 9); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageB", 14, 2, 33743, FALSE); - ValidateCacheAcquireContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", TRUE); - ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, dwPackageStart, 2); - ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageB", L"PackageB", TRUE, FALSE, dwPackageStart); - ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageB", L"cablKtJUKxAbhSMIBwQU6vJ_CDsIkE", TRUE, FALSE, dwPackageStart); - ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageB", FALSE); - ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageB"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 14); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageC", 22, 2, 33743, FALSE); - ValidateCacheAcquireContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", TRUE); - ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, dwPackageStart, 2); - ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageC", L"PackageC", TRUE, FALSE, dwPackageStart); - ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageC", L"cab3wekki1le1R8RPDV2B8_g8jcjZc", TRUE, FALSE, dwPackageStart); - ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageC", FALSE); - ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); - Assert::Equal(24ul, pPlan->cCacheActions); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageC"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); + Assert::Equal(dwIndex, pPlan->cCacheActions); fRollback = TRUE; dwIndex = 0; ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); Assert::Equal(107082ull, pPlan->qwEstimatedSize); - Assert::Equal(101229ull, pPlan->qwCacheSizeTotal); + Assert::Equal(202458ull, pPlan->qwCacheSizeTotal); fRollback = FALSE; dwIndex = 0; DWORD dwExecuteCheckpointId = 2; ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[7].syncpoint.hEvent); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); @@ -107,7 +90,7 @@ namespace Bootstrapper ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteBeginMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); - ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[15].syncpoint.hEvent); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[5].syncpoint.hEvent); dwExecuteCheckpointId += 1; // cache checkpoints ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -116,7 +99,7 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[23].syncpoint.hEvent); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[8].syncpoint.hEvent); dwExecuteCheckpointId += 1; // cache checkpoints ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -314,30 +297,24 @@ namespace Bootstrapper BOOL fRollback = FALSE; DWORD dwIndex = 0; - DWORD dwPackageStart = 0; ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", 5, 2, 33743, FALSE); - ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, BURN_PLAN_INVALID_ACTION_INDEX, 2); - ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"PackageA", TRUE, FALSE, dwPackageStart); - ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"cab9Ins_fTP3wNwq5Gxo41ch5VUPaQ", TRUE, FALSE, dwPackageStart); - ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); - ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); Assert::Equal(dwIndex, pPlan->cCacheActions); fRollback = TRUE; dwIndex = 0; ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); Assert::Equal(35694ull, pPlan->qwEstimatedSize); - Assert::Equal(33743ull, pPlan->qwCacheSizeTotal); + Assert::Equal(67486ull, pPlan->qwCacheSizeTotal); fRollback = FALSE; dwIndex = 0; DWORD dwExecuteCheckpointId = 2; ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[6].syncpoint.hEvent); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); @@ -401,14 +378,9 @@ namespace Bootstrapper BOOL fRollback = FALSE; DWORD dwIndex = 0; - DWORD dwPackageStart = 0; ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", 5, 2, 33743, FALSE); - ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, BURN_PLAN_INVALID_ACTION_INDEX, 2); - ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"PackageA", TRUE, FALSE, dwPackageStart); - ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"cab9Ins_fTP3wNwq5Gxo41ch5VUPaQ", TRUE, FALSE, dwPackageStart); - ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); - ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); Assert::Equal(dwIndex, pPlan->cCacheActions); fRollback = TRUE; @@ -416,13 +388,13 @@ namespace Bootstrapper Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); Assert::Equal(33743ull, pPlan->qwEstimatedSize); - Assert::Equal(33743ull, pPlan->qwCacheSizeTotal); + Assert::Equal(67486ull, pPlan->qwCacheSizeTotal); fRollback = FALSE; dwIndex = 0; DWORD dwExecuteCheckpointId = 2; ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[6].syncpoint.hEvent); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -475,30 +447,24 @@ namespace Bootstrapper BOOL fRollback = FALSE; DWORD dwIndex = 0; - DWORD dwPackageStart = 0; ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", 5, 2, 33743, FALSE); - ValidateCacheExtractContainer(pPlan, fRollback, dwIndex++, L"WixAttachedContainer", FALSE, BURN_PLAN_INVALID_ACTION_INDEX, 2); - ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"PackageA", TRUE, FALSE, dwPackageStart); - ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"cab9Ins_fTP3wNwq5Gxo41ch5VUPaQ", TRUE, FALSE, dwPackageStart); - ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); - ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); Assert::Equal(dwIndex, pPlan->cCacheActions); fRollback = TRUE; dwIndex = 0; ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); Assert::Equal(35694ull, pPlan->qwEstimatedSize); - Assert::Equal(33743ull, pPlan->qwCacheSizeTotal); + Assert::Equal(67486ull, pPlan->qwCacheSizeTotal); fRollback = FALSE; dwIndex = 0; DWORD dwExecuteCheckpointId = 2; ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[6].syncpoint.hEvent); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); @@ -759,36 +725,28 @@ namespace Bootstrapper BOOL fRollback = FALSE; DWORD dwIndex = 0; - DWORD dwPackageStart = 0; ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PatchA", 4, 1, 20480, FALSE); - ValidateCacheAcquirePayload(pPlan, fRollback, dwIndex++, L"PatchA", L"PatchA", FALSE); - ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PatchA", L"PatchA", TRUE, FALSE, dwPackageStart); - ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PatchA", FALSE); - ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PatchA"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 2); - dwPackageStart = ValidateCachePackageStart(pPlan, fRollback, dwIndex++, L"PackageA", 10, 1, 32768, FALSE); - ValidateCacheAcquirePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"PackageA", FALSE); - ValidateCacheCachePayload(pPlan, fRollback, dwIndex++, L"PackageA", L"PackageA", TRUE, FALSE, dwPackageStart); - ValidateCachePackageStop(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); - ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, FALSE); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); Assert::Equal(dwIndex, pPlan->cCacheActions); fRollback = TRUE; dwIndex = 0; ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 2); - ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"PackageA", FALSE); Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); Assert::Equal(3055111ull, pPlan->qwEstimatedSize); - Assert::Equal(53248ull, pPlan->qwCacheSizeTotal); + Assert::Equal(106496ull, pPlan->qwCacheSizeTotal); fRollback = FALSE; dwIndex = 0; DWORD dwExecuteCheckpointId = 3; BURN_EXECUTE_ACTION* pExecuteAction = NULL; ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[11].syncpoint.hEvent); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[5].syncpoint.hEvent); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); @@ -797,7 +755,7 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[5].syncpoint.hEvent); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_REGISTER); @@ -983,7 +941,7 @@ namespace Bootstrapper BURN_REGISTRATION* pRegistration = &pEngineState->registration; DetectReset(pRegistration, &pEngineState->packages); - PlanReset(&pEngineState->plan, &pEngineState->packages); + PlanReset(&pEngineState->plan, &pEngineState->containers, &pEngineState->packages, &pEngineState->layoutPayloads); hr = DepDependencyArrayAlloc(&pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies, pRegistration->sczProviderKey, NULL); NativeAssert::Succeeded(hr, "Failed to add the bundle provider key to the list of dependencies to ignore."); @@ -1047,11 +1005,6 @@ namespace Bootstrapper pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; } - - for (DWORD i = 0; i < pPackage->cPayloads; ++i) - { - pPackage->rgPayloads[i].fCached = TRUE; - } } void DetectPackageDependent(BURN_PACKAGE* pPackage, LPCWSTR wzId) @@ -1186,34 +1139,16 @@ namespace Bootstrapper } } - void ValidateCacheAcquireContainer( - __in BURN_PLAN* pPlan, - __in BOOL fRollback, - __in DWORD dwIndex, - __in LPCWSTR wzContainerId, - __in BOOL fSkipUntilRetried - ) - { - BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pAction->type); - NativeAssert::StringEqual(wzContainerId, pAction->resolveContainer.pContainer->sczId); - Assert::Equal(fSkipUntilRetried, pAction->fSkipUntilRetried); - } - - void ValidateCacheAcquirePayload( + void ValidateCacheContainer( __in BURN_PLAN* pPlan, __in BOOL fRollback, __in DWORD dwIndex, - __in LPCWSTR wzPackageId, - __in LPCWSTR wzPayloadId, - __in BOOL fSkipUntilRetried + __in LPCWSTR wzContainerId ) { BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD, pAction->type); - NativeAssert::StringEqual(wzPackageId, pAction->resolvePayload.pPackage->sczId); - NativeAssert::StringEqual(wzPayloadId, pAction->resolvePayload.pPayload->sczKey); - Assert::Equal(fSkipUntilRetried, pAction->fSkipUntilRetried); + Assert::Equal(BURN_CACHE_ACTION_TYPE_CONTAINER, pAction->type); + NativeAssert::StringEqual(wzContainerId, pAction->container.pContainer->sczId); } BURN_CACHE_ACTION* ValidateCacheActionExists(BURN_PLAN* pPlan, BOOL fRollback, DWORD dwIndex) @@ -1222,26 +1157,6 @@ namespace Bootstrapper return (fRollback ? pPlan->rgRollbackCacheActions : pPlan->rgCacheActions) + dwIndex; } - void ValidateCacheCachePayload( - __in BURN_PLAN* pPlan, - __in BOOL fRollback, - __in DWORD dwIndex, - __in LPCWSTR wzPackageId, - __in LPCWSTR wzPayloadId, - __in BOOL fMove, - __in BOOL fSkipUntilRetried, - __in DWORD iTryAgainAction - ) - { - BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD, pAction->type); - NativeAssert::StringEqual(wzPackageId, pAction->cachePayload.pPackage->sczId); - NativeAssert::StringEqual(wzPayloadId, pAction->cachePayload.pPayload->sczKey); - Assert::Equal(fMove, pAction->cachePayload.fMove); - Assert::Equal(fSkipUntilRetried, pAction->fSkipUntilRetried); - Assert::Equal(iTryAgainAction, pAction->cachePayload.iTryAgainAction); - } - void ValidateCacheCheckpoint( __in BURN_PLAN* pPlan, __in BOOL fRollback, @@ -1254,84 +1169,40 @@ namespace Bootstrapper Assert::Equal(dwId, pAction->checkpoint.dwId); } - void ValidateCacheExtractContainer( - __in BURN_PLAN* pPlan, - __in BOOL fRollback, - __in DWORD dwIndex, - __in LPCWSTR wzContainerId, - __in BOOL fSkipUntilRetried, - __in DWORD iSkipUntilAcquiredByAction, - __in DWORD cPayloads - ) - { - BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, pAction->type); - NativeAssert::StringEqual(wzContainerId, pAction->extractContainer.pContainer->sczId); - Assert::Equal(fSkipUntilRetried, pAction->fSkipUntilRetried); - Assert::Equal(iSkipUntilAcquiredByAction, pAction->extractContainer.iSkipUntilAcquiredByAction); - Assert::Equal(cPayloads, pAction->extractContainer.cPayloads); - } - - DWORD ValidateCachePackageStart( + DWORD ValidateCachePackage( __in BURN_PLAN* pPlan, __in BOOL fRollback, __in DWORD dwIndex, - __in LPCWSTR wzPackageId, - __in DWORD iPackageCompleteAction, - __in DWORD cCachePayloads, - __in DWORD64 qwCachePayloadSizeTotal, - __in BOOL fSkipUntilRetried + __in LPCWSTR wzPackageId ) { BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_CACHE_ACTION_TYPE_PACKAGE_START, pAction->type); - NativeAssert::StringEqual(wzPackageId, pAction->packageStart.pPackage->sczId); - Assert::Equal(iPackageCompleteAction, pAction->packageStart.iPackageCompleteAction); - Assert::Equal(cCachePayloads, pAction->packageStart.cCachePayloads); - Assert::Equal(qwCachePayloadSizeTotal, pAction->packageStart.qwCachePayloadSizeTotal); - Assert::Equal(fSkipUntilRetried, pAction->fSkipUntilRetried); + Assert::Equal(BURN_CACHE_ACTION_TYPE_PACKAGE, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->package.pPackage->sczId); return dwIndex + 1; } - void ValidateCachePackageStop( - __in BURN_PLAN* pPlan, - __in BOOL fRollback, - __in DWORD dwIndex, - __in LPCWSTR wzPackageId, - __in BOOL fSkipUntilRetried - ) - { - BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_CACHE_ACTION_TYPE_PACKAGE_STOP, pAction->type); - NativeAssert::StringEqual(wzPackageId, pAction->packageStop.pPackage->sczId); - Assert::Equal(fSkipUntilRetried, pAction->fSkipUntilRetried); - } - void ValidateCacheRollbackPackage( __in BURN_PLAN* pPlan, __in BOOL fRollback, __in DWORD dwIndex, - __in LPCWSTR wzPackageId, - __in BOOL fSkipUntilRetried + __in LPCWSTR wzPackageId ) { BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); Assert::Equal(BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE, pAction->type); NativeAssert::StringEqual(wzPackageId, pAction->rollbackPackage.pPackage->sczId); - Assert::Equal(fSkipUntilRetried, pAction->fSkipUntilRetried); } void ValidateCacheSignalSyncpoint( __in BURN_PLAN* pPlan, __in BOOL fRollback, - __in DWORD dwIndex, - __in BOOL fSkipUntilRetried + __in DWORD dwIndex ) { BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); Assert::Equal(BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT, pAction->type); Assert::NotEqual((DWORD_PTR)NULL, (DWORD_PTR)pAction->syncpoint.hEvent); - Assert::Equal(fSkipUntilRetried, pAction->fSkipUntilRetried); } void ValidateCleanAction( -- cgit v1.2.3-55-g6feb From b941c2754748251520dc5032d11396c9844fad8e Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 16 Apr 2021 10:18:24 -0500 Subject: Verify file in the cache before trying to acquire it. --- src/engine/apply.cpp | 36 ++++++++++++++++++++----- src/engine/cache.cpp | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/engine/cache.h | 10 ++++++- src/engine/package.h | 1 + src/engine/plan.cpp | 2 ++ 5 files changed, 116 insertions(+), 9 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 7e03ebf9..dffbc6d6 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -768,6 +768,12 @@ static HRESULT ApplyCachePackage( HRESULT hr = S_OK; BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION cachePackageCompleteAction = BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE; + if (!pPackage->sczCacheFolder && !pContext->wzLayoutDirectory) + { + hr = CacheGetCompletedPath(pPackage->fPerMachine, pPackage->sczCacheId, &pPackage->sczCacheFolder); + ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", pPackage->sczCacheId); + } + for (;;) { hr = UserExperienceOnCachePackageBegin(pContext->pUX, pPackage->sczId, pPackage->payloads.cPayloads, pPackage->payloads.qwTotalSize); @@ -872,6 +878,14 @@ static HRESULT ApplyLayoutContainer( Assert(!pContainer->fAttached); + hr = CacheVerifyContainer(pContainer, pContext->wzLayoutDirectory); + if (SUCCEEDED(hr)) + { + // TODO: send Acquire and Verify messages to BA? + pContext->qwSuccessfulCacheProgress += pContainer->qwFileSize * 2; + ExitFunction(); + } + for (;;) { fRetry = FALSE; @@ -916,7 +930,20 @@ static HRESULT ApplyProcessPayload( DWORD cTryAgainAttempts = 0; BOOL fRetry = FALSE; - Assert(pContext->pPayloads || pContext->wzLayoutDirectory); + Assert(pContext->pPayloads && pPackage || pContext->wzLayoutDirectory); + + if (pPayload->pContainer && pContext->wzLayoutDirectory) + { + ExitFunction(); + } + + hr = CacheVerifyPayload(pPayload, pContext->wzLayoutDirectory ? pContext->wzLayoutDirectory : pPackage->sczCacheFolder); + if (SUCCEEDED(hr)) + { + // TODO: send Acquire and Verify messages to BA? + pContext->qwSuccessfulCacheProgress += pPayload->qwFileSize * 2; + ExitFunction(); + } for (;;) { @@ -924,12 +951,7 @@ static HRESULT ApplyProcessPayload( if (pPayload->pContainer) { - if (pContext->wzLayoutDirectory) - { - ExitFunction(); - } - - // TODO: only extract container if payload isn't already cached and isn't already extracted + // TODO: only extract container if payload isn't already extracted hr = ApplyExtractContainer(pContext, pPayload->pContainer); ExitOnFailure(hr, "Failed to extract container for payload: %ls", pPayload->sczKey); } diff --git a/src/engine/cache.cpp b/src/engine/cache.cpp index 6ddbfb50..6ac313e0 100644 --- a/src/engine/cache.cpp +++ b/src/engine/cache.cpp @@ -57,6 +57,10 @@ static HRESULT TransferWorkingPathToUnverifiedPath( __in_z LPCWSTR wzUnverifiedPayloadPath, __in BOOL fMove ); +static HRESULT VerifyFileAgainstContainer( + __in BURN_CONTAINER* pContainer, + __in_z LPCWSTR wzVerifyPath + ); static HRESULT VerifyFileAgainstPayload( __in BURN_PAYLOAD* pPayload, __in_z LPCWSTR wzVerifyPath @@ -837,7 +841,7 @@ LExit: extern "C" HRESULT CacheCompletePayload( __in BOOL fPerMachine, __in BURN_PAYLOAD* pPayload, - __in_z_opt LPCWSTR wzCacheId, + __in_z LPCWSTR wzCacheId, __in_z LPCWSTR wzWorkingPayloadPath, __in BOOL fMove ) @@ -910,6 +914,44 @@ LExit: return hr; } +extern "C" HRESULT CacheVerifyContainer( + __in BURN_CONTAINER* pContainer, + __in_z LPCWSTR wzCachedDirectory + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCachedPath = NULL; + + hr = PathConcat(wzCachedDirectory, pContainer->sczFilePath, &sczCachedPath); + ExitOnFailure(hr, "Failed to concat complete cached path."); + + hr = VerifyFileAgainstContainer(pContainer, sczCachedPath); + +LExit: + ReleaseStr(sczCachedPath); + + return hr; +} + +extern "C" HRESULT CacheVerifyPayload( + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzCachedDirectory + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCachedPath = NULL; + + hr = PathConcat(wzCachedDirectory, pPayload->sczFilePath, &sczCachedPath); + ExitOnFailure(hr, "Failed to concat complete cached path."); + + hr = VerifyFileAgainstPayload(pPayload, sczCachedPath); + +LExit: + ReleaseStr(sczCachedPath); + + return hr; +} + extern "C" HRESULT CacheRemoveWorkingFolder( __in_z_opt LPCWSTR wzBundleId ) @@ -1383,6 +1425,38 @@ LExit: return hr; } +static HRESULT VerifyFileAgainstContainer( + __in BURN_CONTAINER* pContainer, + __in_z LPCWSTR wzVerifyPath + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + + // Get the container on disk actual hash. + hFile = ::CreateFileW(wzVerifyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (E_PATHNOTFOUND == hr || E_FILENOTFOUND == hr) + { + ExitFunction(); // do not log error when the file was not found. + } + ExitOnRootFailure(hr, "Failed to open container at path: %ls", wzVerifyPath); + } + + if (pContainer->pbHash) // the container should have a hash we can use to verify it. + { + hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, pContainer->qwFileSize, wzVerifyPath, hFile); + ExitOnFailure(hr, "Failed to verify hash of container: %ls", pContainer->sczId); + } + +LExit: + ReleaseFileHandle(hFile); + + return hr; +} + static HRESULT VerifyFileAgainstPayload( __in BURN_PAYLOAD* pPayload, __in_z LPCWSTR wzVerifyPath diff --git a/src/engine/cache.h b/src/engine/cache.h index 52b111e9..acc7acb7 100644 --- a/src/engine/cache.h +++ b/src/engine/cache.h @@ -119,10 +119,18 @@ HRESULT CacheLayoutPayload( HRESULT CacheCompletePayload( __in BOOL fPerMachine, __in BURN_PAYLOAD* pPayload, - __in_z_opt LPCWSTR wzCacheId, + __in_z LPCWSTR wzCacheId, __in_z LPCWSTR wzUnverifiedPayloadPath, __in BOOL fMove ); +HRESULT CacheVerifyContainer( + __in BURN_CONTAINER* pContainer, + __in_z LPCWSTR wzCachedDirectory + ); +HRESULT CacheVerifyPayload( + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzCachedDirectory + ); HRESULT CacheRemoveWorkingFolder( __in_z_opt LPCWSTR wzBundleId ); diff --git a/src/engine/package.h b/src/engine/package.h index 34a3af26..2091af94 100644 --- a/src/engine/package.h +++ b/src/engine/package.h @@ -246,6 +246,7 @@ typedef struct _BURN_PACKAGE BURN_DEPENDENCY_ACTION dependencyExecute; // only valid during Plan. BURN_DEPENDENCY_ACTION dependencyRollback; // only valid during Plan. BOOL fDependencyManagerWasHere; // only valid during Plan. + LPWSTR sczCacheFolder; // only valid during Apply. HRESULT hrCacheResult; // only valid during Apply. BURN_PACKAGE_REGISTRATION_STATE cacheRegistrationState; // initialized during Detect, updated during Apply. diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index ce577da5..cfe4893b 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -1863,6 +1863,8 @@ static void ResetPlannedPackageState( pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; + ReleaseNullStr(pPackage->sczCacheFolder); + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) { for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) -- cgit v1.2.3-55-g6feb From 5d6046bee5021052da4a666c1e2ceeb0f16af349 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 16 Apr 2021 10:20:41 -0500 Subject: Replace OnResolveSource with OnCacheAcquireResolving Inactivate the engine during OnCacheAcquireBegin and Complete to allow setting the source from there. Fixes #3640 Contributes to #5253 --- .../inc/BootstrapperApplication.h | 63 +++--- src/engine/apply.cpp | 242 +++++++++++---------- src/engine/cache.cpp | 203 ++++++++++------- src/engine/cache.h | 9 +- src/engine/engine.mc | 32 +-- src/engine/externalengine.cpp | 43 ++-- src/engine/userexperience.cpp | 126 +++++++---- src/engine/userexperience.h | 26 ++- 8 files changed, 413 insertions(+), 331 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h index 2d086f38..cf330c29 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -54,8 +54,13 @@ enum BOOTSTRAPPER_RELATED_OPERATION enum BOOTSTRAPPER_CACHE_OPERATION { + // There is no source available. + BOOTSTRAPPER_CACHE_OPERATION_NONE, + // Copy the payload or container from the chosen local source. BOOTSTRAPPER_CACHE_OPERATION_COPY, + // Download the payload or container using the download URL. BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD, + // Extract the payload from the container. BOOTSTRAPPER_CACHE_OPERATION_EXTRACT, }; @@ -112,7 +117,7 @@ enum BOOTSTRAPPER_APPLICATION_MESSAGE BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGEBEGIN, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREBEGIN, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREPROGRESS, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONRESOLVESOURCE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRERESOLVING, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRECOMPLETE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYBEGIN, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYCOMPLETE, @@ -158,7 +163,7 @@ enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION enum BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION { BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_NONE, - // Instructs the engine to try the acquisition of the package again. + // Instructs the engine to try the acquisition of the payload again. // Ignored if hrStatus is a success. BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_RETRY, }; @@ -202,16 +207,6 @@ enum BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_SUSPEND, }; -enum BOOTSTRAPPER_RESOLVESOURCE_ACTION -{ - // Instructs the engine that the source can't be found. - BOOTSTRAPPER_RESOLVESOURCE_ACTION_NONE, - // Instructs the engine to try the local source again. - BOOTSTRAPPER_RESOLVESOURCE_ACTION_RETRY, - // Instructs the engine to try the download source. - BOOTSTRAPPER_RESOLVESOURCE_ACTION_DOWNLOAD, -}; - enum BOOTSTRAPPER_SHUTDOWN_ACTION { BOOTSTRAPPER_SHUTDOWN_ACTION_NONE, @@ -315,14 +310,17 @@ struct BA_ONCACHEACQUIREBEGIN_ARGS DWORD cbSize; LPCWSTR wzPackageOrContainerId; LPCWSTR wzPayloadId; - BOOTSTRAPPER_CACHE_OPERATION operation; LPCWSTR wzSource; + LPCWSTR wzDownloadUrl; + LPCWSTR wzPayloadContainerId; + BOOTSTRAPPER_CACHE_OPERATION recommendation; }; struct BA_ONCACHEACQUIREBEGIN_RESULTS { DWORD cbSize; BOOL fCancel; + BOOTSTRAPPER_CACHE_OPERATION action; }; struct BA_ONCACHEACQUIRECOMPLETE_ARGS @@ -356,6 +354,28 @@ struct BA_ONCACHEACQUIREPROGRESS_RESULTS BOOL fCancel; }; +struct BA_ONCACHEACQUIRERESOLVING_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageOrContainerId; + LPCWSTR wzPayloadId; + LPCWSTR* rgSearchPaths; + DWORD cSearchPaths; + BOOL fFoundLocal; + DWORD dwRecommendedSearchPath; + LPCWSTR wzDownloadUrl; + LPCWSTR wzPayloadContainerId; + BOOTSTRAPPER_CACHE_OPERATION recommendation; +}; + +struct BA_ONCACHEACQUIRERESOLVING_RESULTS +{ + DWORD cbSize; + DWORD dwChosenSearchPath; + BOOTSTRAPPER_CACHE_OPERATION action; + BOOL fCancel; +}; + struct BA_ONCACHEBEGIN_ARGS { DWORD cbSize; @@ -1013,23 +1033,6 @@ struct BA_ONREGISTERCOMPLETE_RESULTS DWORD cbSize; }; -struct BA_ONRESOLVESOURCE_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageOrContainerId; - LPCWSTR wzPayloadId; - LPCWSTR wzLocalSource; - LPCWSTR wzDownloadSource; - BOOTSTRAPPER_RESOLVESOURCE_ACTION recommendation; -}; - -struct BA_ONRESOLVESOURCE_RESULTS -{ - DWORD cbSize; - BOOTSTRAPPER_RESOLVESOURCE_ACTION action; - BOOL fCancel; -}; - struct BA_ONROLLBACKMSITRANSACTIONBEGIN_ARGS { DWORD cbSize; diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index dffbc6d6..dc9f905b 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -23,6 +23,9 @@ typedef struct _BURN_CACHE_CONTEXT DWORD64 qwTotalCacheSize; DWORD64 qwSuccessfulCacheProgress; LPCWSTR wzLayoutDirectory; + LPWSTR* rgSearchPaths; + DWORD cSearchPaths; + DWORD cSearchPathsMax; } BURN_CACHE_CONTEXT; typedef struct _BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT @@ -113,15 +116,6 @@ static HRESULT LayoutOrCacheContainerOrPayload( __in DWORD cTryAgainAttempts, __out BOOL* pfRetry ); -static HRESULT PromptForSource( - __in BURN_USER_EXPERIENCE* pUX, - __in_z LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in_z LPCWSTR wzLocalSource, - __in_z_opt LPCWSTR wzDownloadSource, - __out BOOL* pfRetry, - __out BOOL* pfDownload - ); static HRESULT CopyPayload( __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, __in HANDLE hSourceFile, @@ -485,6 +479,9 @@ extern "C" HRESULT ApplyCache( cacheContext.qwTotalCacheSize = pPlan->qwCacheSizeTotal; cacheContext.wzLayoutDirectory = pPlan->sczLayoutDirectory; + hr = MemAllocArray(reinterpret_cast(&cacheContext.rgSearchPaths), sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); + ExitOnNull(cacheContext.rgSearchPaths, hr, E_OUTOFMEMORY, "Failed to allocate cache search paths array."); + for (DWORD i = 0; i < pPlan->cCacheActions; ++i) { BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + i; @@ -561,6 +558,12 @@ LExit: CacheCleanup(FALSE, pPlan->wzBundleId); + for (DWORD i = 0; i < cacheContext.cSearchPathsMax; ++i) + { + ReleaseNullStr(cacheContext.rgSearchPaths[i]); + } + ReleaseMem(cacheContext.rgSearchPaths); + UserExperienceOnCacheComplete(pUX, hr); return hr; } @@ -949,17 +952,8 @@ static HRESULT ApplyProcessPayload( { fRetry = FALSE; - if (pPayload->pContainer) - { - // TODO: only extract container if payload isn't already extracted - hr = ApplyExtractContainer(pContext, pPayload->pContainer); - ExitOnFailure(hr, "Failed to extract container for payload: %ls", pPayload->sczKey); - } - else - { - hr = AcquireContainerOrPayload(pContext, NULL, pPackage, pPayload); - LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_PAYLOAD, "Failed to acquire payload: %ls to working path: %ls", pPayload->sczKey, pPayload->sczUnverifiedPath); - } + hr = AcquireContainerOrPayload(pContext, NULL, pPackage, pPayload); + LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_PAYLOAD, "Failed to acquire payload: %ls to working path: %ls", pPayload->sczKey, pPayload->sczUnverifiedPath); pContext->qwSuccessfulCacheProgress += pPayload->qwFileSize; @@ -1054,8 +1048,10 @@ static HRESULT LayoutBundle( { HRESULT hr = S_OK; LPWSTR sczBundlePath = NULL; + LPWSTR sczBundleDownloadUrl = NULL; LPWSTR sczDestinationPath = NULL; int nEquivalentPaths = 0; + BOOTSTRAPPER_CACHE_OPERATION cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE; BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { }; BOOL fRetry = FALSE; BOOL fRetryAcquire = FALSE; @@ -1096,7 +1092,7 @@ static HRESULT LayoutBundle( fRetryAcquire = FALSE; progress.fCancel = FALSE; - hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, NULL, NULL, BOOTSTRAPPER_CACHE_OPERATION_COPY, sczBundlePath); + hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, NULL, NULL, &sczBundlePath, &sczBundleDownloadUrl, NULL, &cacheOperation); ExitOnRootFailure(hr, "BA aborted cache acquire begin."); hr = CopyPayload(&progress, pContext->hSourceEngineFile, sczBundlePath, wzUnverifiedPath); @@ -1142,6 +1138,7 @@ static HRESULT LayoutBundle( LExit: ReleaseStr(sczDestinationPath); + ReleaseStr(sczBundleDownloadUrl); ReleaseStr(sczBundlePath); return hr; @@ -1160,11 +1157,16 @@ static HRESULT AcquireContainerOrPayload( int nEquivalentPaths = 0; LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : NULL; LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : NULL; + LPCWSTR wzPayloadContainerId = pPayload && pPayload->pContainer ? pPayload->pContainer->sczId : NULL; LPCWSTR wzDestinationPath = pContainer ? pContainer->sczUnverifiedPath: pPayload->sczUnverifiedPath; LPCWSTR wzRelativePath = pContainer ? pContainer->sczFilePath : pPayload->sczFilePath; - LPWSTR sczSourceFullPath = NULL; BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { }; + BOOL fBeginCalled = FALSE; BOOL fRetry = FALSE; + DWORD dwChosenSearchPath = 0; + BOOTSTRAPPER_CACHE_OPERATION cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE; + LPWSTR* pwzDownloadUrl = pContainer ? &pContainer->downloadSource.sczUrl : &pPayload->downloadSource.sczUrl; + LPWSTR* pwzSourcePath = pContainer ? &pContainer->sczSourcePath : &pPayload->sczSourcePath; progress.pCacheContext = pContext; progress.pContainer = pContainer; @@ -1173,86 +1175,148 @@ static HRESULT AcquireContainerOrPayload( do { - LPCWSTR wzDownloadUrl = pContainer ? pContainer->downloadSource.sczUrl : pPayload->downloadSource.sczUrl; - LPCWSTR wzSourcePath = pContainer ? pContainer->sczSourcePath : pPayload->sczSourcePath; - BOOL fFoundLocal = FALSE; - BOOL fCopy = FALSE; - BOOL fDownload = FALSE; + pContext->cSearchPaths = 0; + dwChosenSearchPath = 0; fRetry = FALSE; progress.fCancel = FALSE; + fBeginCalled = TRUE; - hr = CacheFindLocalSource(wzSourcePath, wzDestinationPath, pContext->pVariables, &fFoundLocal, &sczSourceFullPath); - ExitOnFailure(hr, "Failed to search local source."); - - if (fFoundLocal) // the file exists locally, so copy it. - { - // If the source path and destination path are different, do the copy (otherwise there's no point). - hr = PathCompare(sczSourceFullPath, wzDestinationPath, &nEquivalentPaths); - ExitOnFailure(hr, "Failed to determine if payload source path was equivalent to the destination path."); + hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId, pwzSourcePath, pwzDownloadUrl, wzPayloadContainerId, &cacheOperation); + ExitOnRootFailure(hr, "BA aborted cache acquire begin."); - fCopy = (CSTR_EQUAL != nEquivalentPaths); - } - else // can't find the file locally, so prompt for source. + // Skip the Resolving event and probing local paths if the BA already knew it wanted to download or extract. + if (BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD != cacheOperation && + BOOTSTRAPPER_CACHE_OPERATION_EXTRACT != cacheOperation) { - DWORD dwLogId = pContainer ? (wzPayloadId ? MSG_PROMPT_CONTAINER_PAYLOAD_SOURCE : MSG_PROMPT_CONTAINER_SOURCE) : pPackage ? MSG_PROMPT_PACKAGE_PAYLOAD_SOURCE : MSG_PROMPT_BUNDLE_PAYLOAD_SOURCE; - LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId ? wzPackageOrContainerId : L"", wzPayloadId ? wzPayloadId : L"", sczSourceFullPath); + hr = CacheGetLocalSourcePaths(wzRelativePath, *pwzSourcePath, wzDestinationPath, pContext->wzLayoutDirectory, pContext->pVariables, &pContext->rgSearchPaths, &pContext->cSearchPaths); + ExitOnFailure(hr, "Failed to search local source."); - hr = PromptForSource(pContext->pUX, wzPackageOrContainerId, wzPayloadId, sczSourceFullPath, wzDownloadUrl, &fRetry, &fDownload); + for (DWORD i = 0; i < pContext->cSearchPaths; ++i) + { + // If the file exists locally, choose it. + if (FileExistsEx(pContext->rgSearchPaths[i], NULL)) + { + dwChosenSearchPath = i; + + fFoundLocal = TRUE; + break; + } + } - // If the BA requested download then ensure a download url is available (it may have been set - // during PromptForSource so we need to check again). - if (fDownload) + if (BOOTSTRAPPER_CACHE_OPERATION_COPY == cacheOperation) + { + if (!fFoundLocal) + { + cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE; + } + } + else { - wzDownloadUrl = pContainer ? pContainer->downloadSource.sczUrl : pPayload->downloadSource.sczUrl; - if (!wzDownloadUrl || !*wzDownloadUrl) + if (fFoundLocal) // the file exists locally, so copy it. + { + cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_COPY; + } + else if (wzPayloadContainerId) + { + cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_EXTRACT; + } + else if (*pwzDownloadUrl && **pwzDownloadUrl) { - hr = E_INVALIDARG; + cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD; } } - // Log the error - LogExitOnFailure(hr, MSG_PAYLOAD_FILE_NOT_PRESENT, "Failed while prompting for source (original path '%ls').", sczSourceFullPath); + // Let the BA have a chance to override the action, but their chance to change the source is during begin or complete. + hr = UserExperienceOnCacheAcquireResolving(pContext->pUX, wzPackageOrContainerId, wzPayloadId, pContext->rgSearchPaths, pContext->cSearchPaths, fFoundLocal, &dwChosenSearchPath, *pwzDownloadUrl, wzPayloadContainerId, &cacheOperation); + ExitOnRootFailure(hr, "BA aborted cache acquire resolving."); } - if (fCopy) + switch (cacheOperation) { - hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId, BOOTSTRAPPER_CACHE_OPERATION_COPY, sczSourceFullPath); - ExitOnRootFailure(hr, "BA aborted cache acquire begin."); + case BOOTSTRAPPER_CACHE_OPERATION_COPY: + // If the source path and destination path are different, do the copy (otherwise there's no point). + hr = PathCompare(pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath, &nEquivalentPaths); + ExitOnFailure(hr, "Failed to determine if payload paths were equivalent, source: %ls, destination: %ls.", pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath); - hr = CopyPayload(&progress, INVALID_HANDLE_VALUE, sczSourceFullPath, wzDestinationPath); - // Error handling happens after sending complete message to BA. + if (CSTR_EQUAL != nEquivalentPaths) + { + hr = CopyPayload(&progress, INVALID_HANDLE_VALUE, pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath); + // Error handling happens after sending complete message to BA. + + // TODO: wait for verification? + // We successfully copied from a source location, set that as the last used source. + if (SUCCEEDED(hr)) + { + CacheSetLastUsedSource(pContext->pVariables, pContext->rgSearchPaths[dwChosenSearchPath], wzRelativePath); + } + } - // We successfully copied from a source location, set that as the last used source. - if (SUCCEEDED(hr)) + fBeginCalled = FALSE; + UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry); + if (fRetry) { - CacheSetLastUsedSource(pContext->pVariables, sczSourceFullPath, wzRelativePath); + hr = S_OK; } - } - else if (fDownload) - { - hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId, BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD, wzDownloadUrl); - ExitOnRootFailure(hr, "BA aborted cache download payload begin."); + ExitOnFailure(hr, "Failed to acquire payload from: '%ls' to working path: '%ls'", pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath); + + break; + case BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD: hr = DownloadPayload(&progress, wzDestinationPath); // Error handling happens after sending complete message to BA. - } - if (fCopy || fDownload) - { + fBeginCalled = FALSE; + UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry); + if (fRetry) + { + hr = S_OK; + } + + ExitOnFailure(hr, "Failed to acquire payload from: '%ls' to working path: '%ls'", *pwzDownloadUrl, wzDestinationPath); + + break; + case BOOTSTRAPPER_CACHE_OPERATION_EXTRACT: + Assert(pPayload->pContainer); + + hr = ApplyExtractContainer(pContext, pPayload->pContainer); + // Error handling happens after sending complete message to BA. + + fBeginCalled = FALSE; + UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry); + if (fRetry) + { + hr = S_OK; + } + + ExitOnFailure(hr, "Failed to extract container for payload: %ls", wzPayloadId); + + break; + case BOOTSTRAPPER_CACHE_OPERATION_NONE: + hr = E_FILENOTFOUND; + LogErrorId(hr, MSG_RESOLVE_SOURCE_FAILED, wzPayloadId, pPackage ? pPackage->sczId : NULL, pContainer ? pContainer->sczId : NULL); + + fBeginCalled = FALSE; UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry); if (fRetry) { hr = S_OK; } + + ExitOnFailure(hr, "Failed to resolve source, payload: %ls, package: %ls, container: %ls", wzPayloadId, pPackage ? pPackage->sczId : NULL, pContainer ? pContainer->sczId : NULL); + + break; } - ExitOnFailure(hr, "Failed to acquire payload from: '%ls' to working path: '%ls'", fCopy ? sczSourceFullPath : wzDownloadUrl, wzDestinationPath); } while (fRetry); - ExitOnFailure(hr, "Failed to find external payload to cache."); LExit: - ReleaseStr(sczSourceFullPath); + if (fBeginCalled) + { + UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry); + } + + pContext->cSearchPathsMax = max(pContext->cSearchPaths, pContext->cSearchPathsMax); return hr; } @@ -1355,48 +1419,6 @@ LExit: return hr; } -static HRESULT PromptForSource( - __in BURN_USER_EXPERIENCE* pUX, - __in_z LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in_z LPCWSTR wzLocalSource, - __in_z_opt LPCWSTR wzDownloadSource, - __inout BOOL* pfRetry, - __inout BOOL* pfDownload - ) -{ - HRESULT hr = S_OK; - BOOTSTRAPPER_RESOLVESOURCE_ACTION action = BOOTSTRAPPER_RESOLVESOURCE_ACTION_NONE; - - hr = UserExperienceOnResolveSource(pUX, wzPackageOrContainerId, wzPayloadId, wzLocalSource, wzDownloadSource, &action); - if (FAILED(hr)) - { - ExitFunction(); - } - - switch (action) - { - case BOOTSTRAPPER_RESOLVESOURCE_ACTION_NONE: - hr = E_FILENOTFOUND; - break; - - case BOOTSTRAPPER_RESOLVESOURCE_ACTION_RETRY: - *pfRetry = TRUE; - break; - - case BOOTSTRAPPER_RESOLVESOURCE_ACTION_DOWNLOAD: - *pfDownload = TRUE; - break; - - default: - hr = E_INVALIDARG; - break; - } - -LExit: - return hr; -} - static HRESULT CopyPayload( __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, __in HANDLE hSourceFile, diff --git a/src/engine/cache.cpp b/src/engine/cache.cpp index 6ac313e0..84b7f131 100644 --- a/src/engine/cache.cpp +++ b/src/engine/cache.cpp @@ -11,7 +11,7 @@ static const DWORD FILE_OPERATION_RETRY_WAIT = 2000; static BOOL vfInitializedCache = FALSE; static BOOL vfRunningFromCache = FALSE; -static LPWSTR vsczSourceProcessPath = NULL; +static LPWSTR vsczSourceProcessFolder = NULL; static LPWSTR vsczWorkingFolder = NULL; static LPWSTR vsczDefaultUserPackageCache = NULL; static LPWSTR vsczDefaultMachinePackageCache = NULL; @@ -140,8 +140,8 @@ extern "C" HRESULT CacheInitialize( wzSourceProcessPath = sczCurrentPath; } - hr = StrAllocString(&vsczSourceProcessPath, wzSourceProcessPath, 0); - ExitOnFailure(hr, "Failed to initialize cache source path."); + hr = PathGetDirectory(wzSourceProcessPath, &vsczSourceProcessFolder); + ExitOnFailure(hr, "Failed to initialize cache source folder."); // If we're not running from the cache, ensure the original source is set. if (!vfRunningFromCache) @@ -393,112 +393,166 @@ LExit: return hr; } -extern "C" HRESULT CacheFindLocalSource( +extern "C" HRESULT CacheGetLocalSourcePaths( + __in_z LPCWSTR wzRelativePath, __in_z LPCWSTR wzSourcePath, __in_z LPCWSTR wzDestinationPath, + __in_z_opt LPCWSTR wzLayoutDirectory, __in BURN_VARIABLES* pVariables, - __out BOOL* pfFound, - __out_z LPWSTR* psczSourceFullPath + __inout LPWSTR** prgSearchPaths, + __out DWORD* pcSearchPaths ) { HRESULT hr = S_OK; - LPWSTR sczSourceProcessFolder = NULL; LPWSTR sczCurrentPath = NULL; - LPWSTR sczLastSourcePath = NULL; LPWSTR sczLastSourceFolder = NULL; - LPWSTR sczLayoutPath = NULL; - LPWSTR sczLayoutFolder = NULL; - LPCWSTR rgwzSearchPaths[4] = { }; + LPWSTR* psczPath = NULL; + BOOL fPreferSourcePathLocation = FALSE; + BOOL fTryLastFolder = FALSE; + BOOL fTryRelativePath = FALSE; + BOOL fSourceIsAbsolute = FALSE; DWORD cSearchPaths = 0; - // If the source path provided is a full path, obviously that is where we should be looking. - if (PathIsAbsolute(wzSourcePath)) + AssertSz(vfInitializedCache, "Cache wasn't initialized"); + + hr = GetLastUsedSourceFolder(pVariables, &sczLastSourceFolder); + fPreferSourcePathLocation = !vfRunningFromCache || FAILED(hr); + fTryLastFolder = SUCCEEDED(hr) && sczLastSourceFolder && *sczLastSourceFolder && CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, vsczSourceProcessFolder, -1, sczLastSourceFolder, -1); + fTryRelativePath = CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzSourcePath, -1, wzRelativePath, -1); + fSourceIsAbsolute = PathIsAbsolute(wzSourcePath); + + // If the source path provided is a full path, try that first. + if (fSourceIsAbsolute) { - rgwzSearchPaths[0] = wzSourcePath; - cSearchPaths = 1; + hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); + ExitOnFailure(hr, "Failed to ensure size for search paths array."); + + psczPath = *prgSearchPaths + cSearchPaths; + ++cSearchPaths; + + hr = StrAllocString(psczPath, wzSourcePath, 0); + ExitOnFailure(hr, "Failed to copy absolute source path."); } - else + + // Try the destination path next. + hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); + ExitOnFailure(hr, "Failed to ensure size for search paths array."); + + psczPath = *prgSearchPaths + cSearchPaths; + ++cSearchPaths; + + hr = StrAllocString(psczPath, wzDestinationPath, 0); + ExitOnFailure(hr, "Failed to copy absolute source path."); + + if (!fSourceIsAbsolute) { - // Use the destination path first. - rgwzSearchPaths[0] = wzDestinationPath; - cSearchPaths = 1; - - // If we're not running from cache or we couldn't get the last source, use - // the source path location. In the case where we are in the bundle's - // package cache and couldn't find a last used source we unfortunately will - // be picking the package cache path which isn't likely to have what we are - // looking for. - hr = GetLastUsedSourceFolder(pVariables, &sczLastSourceFolder); - if (!vfRunningFromCache || FAILED(hr)) + // Calculate the source path location. + // In the case where we are in the bundle's package cache and + // couldn't find a last used source that will be the package cache path + // which isn't likely to have what we are looking for. + hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); + ExitOnFailure(hr, "Failed to ensure size for search paths array."); + + hr = PathConcat(vsczSourceProcessFolder, wzSourcePath, &sczCurrentPath); + ExitOnFailure(hr, "Failed to combine source process folder with source."); + + // If we're not running from cache or we couldn't get the last source, + // try the source path location next. + if (fPreferSourcePathLocation) { - hr = PathGetDirectory(vsczSourceProcessPath, &sczSourceProcessFolder); - ExitOnFailure(hr, "Failed to get current process directory."); + (*prgSearchPaths)[cSearchPaths] = sczCurrentPath; + ++cSearchPaths; + sczCurrentPath = NULL; + } - hr = PathConcat(sczSourceProcessFolder, wzSourcePath, &sczCurrentPath); + // If we have a last used source and it is not the source path location, + // add the last used source to the search path next. + if (fTryLastFolder) + { + hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); + ExitOnFailure(hr, "Failed to ensure size for search paths array."); + + psczPath = *prgSearchPaths + cSearchPaths; + ++cSearchPaths; + + hr = PathConcat(sczLastSourceFolder, wzSourcePath, psczPath); ExitOnFailure(hr, "Failed to combine last source with source."); + } - rgwzSearchPaths[cSearchPaths] = sczCurrentPath; + if (!fPreferSourcePathLocation) + { + (*prgSearchPaths)[cSearchPaths] = sczCurrentPath; ++cSearchPaths; + sczCurrentPath = NULL; } - // If we have a last used source and it does not duplicate the existing search path, - // add the last used source to the search path second. - if (sczLastSourceFolder && *sczLastSourceFolder) + // Also consider the layout directory if doing Layout. + if (wzLayoutDirectory) { - hr = PathConcat(sczLastSourceFolder, wzSourcePath, &sczLastSourcePath); - ExitOnFailure(hr, "Failed to combine last source with source."); + hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); + ExitOnFailure(hr, "Failed to ensure size for search paths array."); - if (1 == cSearchPaths || CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, rgwzSearchPaths[1], -1, sczLastSourcePath, -1)) - { - rgwzSearchPaths[cSearchPaths] = sczLastSourcePath; - ++cSearchPaths; - } + psczPath = *prgSearchPaths + cSearchPaths; + ++cSearchPaths; + + hr = PathConcat(wzLayoutDirectory, wzSourcePath, psczPath); + ExitOnFailure(hr, "Failed to combine layout source with source."); } + } - // Also consider the layout directory if set on the command line or by the BA. - hr = VariableGetString(pVariables, BURN_BUNDLE_LAYOUT_DIRECTORY, &sczLayoutFolder); - if (E_NOTFOUND != hr) + if (fTryRelativePath) + { + hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); + ExitOnFailure(hr, "Failed to ensure size for search paths array."); + + hr = PathConcat(vsczSourceProcessFolder, wzRelativePath, &sczCurrentPath); + ExitOnFailure(hr, "Failed to combine source process folder with relative."); + + if (fPreferSourcePathLocation) { - ExitOnFailure(hr, "Failed to get bundle layout directory property."); + (*prgSearchPaths)[cSearchPaths] = sczCurrentPath; + ++cSearchPaths; + sczCurrentPath = NULL; + } - hr = PathConcat(sczLayoutFolder, wzSourcePath, &sczLayoutPath); - ExitOnFailure(hr, "Failed to combine layout source with source."); + if (fTryLastFolder) + { + hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); + ExitOnFailure(hr, "Failed to ensure size for search paths array."); - rgwzSearchPaths[cSearchPaths] = sczLayoutPath; + psczPath = *prgSearchPaths + cSearchPaths; ++cSearchPaths; + + hr = PathConcat(sczLastSourceFolder, wzRelativePath, psczPath); + ExitOnFailure(hr, "Failed to combine last source with relative."); } - } - *pfFound = FALSE; // assume we won't find the file locally. + if (!fPreferSourcePathLocation) + { + (*prgSearchPaths)[cSearchPaths] = sczCurrentPath; + ++cSearchPaths; + sczCurrentPath = NULL; + } - for (DWORD i = 0; i < cSearchPaths; ++i) - { - // If the file exists locally, copy its path. - if (FileExistsEx(rgwzSearchPaths[i], NULL)) + if (wzLayoutDirectory) { - hr = StrAllocString(psczSourceFullPath, rgwzSearchPaths[i], 0); - ExitOnFailure(hr, "Failed to copy source path."); + hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); + ExitOnFailure(hr, "Failed to ensure size for search paths array."); - *pfFound = TRUE; - break; - } - } + psczPath = *prgSearchPaths + cSearchPaths; + ++cSearchPaths; - // If nothing was found, return the first thing in our search path as the - // best path where we thought we should have found the file. - if (!*pfFound) - { - hr = StrAllocString(psczSourceFullPath, rgwzSearchPaths[0], 0); - ExitOnFailure(hr, "Failed to copy source path."); + hr = PathConcat(wzLayoutDirectory, wzSourcePath, psczPath); + ExitOnFailure(hr, "Failed to combine layout source with relative."); + } } LExit: ReleaseStr(sczCurrentPath); - ReleaseStr(sczSourceProcessFolder); ReleaseStr(sczLastSourceFolder); - ReleaseStr(sczLastSourcePath); - ReleaseStr(sczLayoutFolder); - ReleaseStr(sczLayoutPath); + + AssertSz(cSearchPaths <= BURN_CACHE_MAX_SEARCH_PATHS, "Got more than BURN_CACHE_MAX_SEARCH_PATHS search paths"); + *pcSearchPaths = cSearchPaths; return hr; } @@ -1079,7 +1133,7 @@ extern "C" void CacheUninitialize() ReleaseNullStr(vsczDefaultMachinePackageCache); ReleaseNullStr(vsczDefaultUserPackageCache); ReleaseNullStr(vsczWorkingFolder); - ReleaseNullStr(vsczSourceProcessPath); + ReleaseNullStr(vsczSourceProcessFolder); vfRunningFromCache = FALSE; vfInitializedCache = FALSE; @@ -1224,17 +1278,12 @@ static HRESULT GetLastUsedSourceFolder( ) { HRESULT hr = S_OK; - LPWSTR sczOriginalSource = NULL; hr = VariableGetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, psczLastSource); if (E_NOTFOUND == hr) { // Try the original source folder. - hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource); - if (SUCCEEDED(hr)) - { - hr = PathGetDirectory(sczOriginalSource, psczLastSource); - } + hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, psczLastSource); } return hr; diff --git a/src/engine/cache.h b/src/engine/cache.h index acc7acb7..eb964f58 100644 --- a/src/engine/cache.h +++ b/src/engine/cache.h @@ -1,6 +1,7 @@ #pragma once // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. +#define BURN_CACHE_MAX_SEARCH_PATHS 7 #ifdef __cplusplus extern "C" { @@ -52,12 +53,14 @@ HRESULT CacheGetResumePath( __in_z LPCWSTR wzPayloadWorkingPath, __deref_out_z LPWSTR* psczResumePath ); -HRESULT CacheFindLocalSource( +HRESULT CacheGetLocalSourcePaths( + __in_z LPCWSTR wzRelativePath, __in_z LPCWSTR wzSourcePath, __in_z LPCWSTR wzDestinationPath, + __in_z_opt LPCWSTR wzLayoutDirectory, __in BURN_VARIABLES* pVariables, - __out BOOL* pfFound, - __out_z LPWSTR* psczSourceFullPath + __inout LPWSTR** prgSearchPaths, + __out DWORD* pcSearchPaths ); HRESULT CacheSetLastUsedSource( __in BURN_VARIABLES* pVariables, diff --git a/src/engine/engine.mc b/src/engine/engine.mc index c8b46806..f03bc1ea 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -151,9 +151,9 @@ Bundle global condition check didn't succeed - aborting without loading applicat MessageId=54 Severity=Error -SymbolicName=MSG_PAYLOAD_FILE_NOT_PRESENT +SymbolicName=MSG_RESOLVE_SOURCE_FAILED Language=English -Failed to resolve source for file: %2!ls!, error: %1!ls!. +Failed to resolve source for payload: %2!ls!, package: %3!ls!, container: %4!ls!, error: %1!ls!. . MessageId=55 @@ -724,34 +724,6 @@ Language=English Acquiring package: %1!ls!, payload: %2!ls!, %3!hs! from: %4!ls! . -MessageId=340 -Severity=Warning -SymbolicName=MSG_PROMPT_BUNDLE_PAYLOAD_SOURCE -Language=English -Prompt for source of bundle payload: %2!ls!, path: %3!ls! -. - -MessageId=341 -Severity=Warning -SymbolicName=MSG_PROMPT_CONTAINER_SOURCE -Language=English -Prompt for source of container: %1!ls!, path: %3!ls! -. - -MessageId=342 -Severity=Warning -SymbolicName=MSG_PROMPT_CONTAINER_PAYLOAD_SOURCE -Language=English -Prompt for source of container: %1!ls!, payload: %2!ls!, path: %3!ls! -. - -MessageId=343 -Severity=Warning -SymbolicName=MSG_PROMPT_PACKAGE_PAYLOAD_SOURCE -Language=English -Prompt for source of package: %1!ls!, payload: %2!ls!, path: %3!ls! -. - MessageId=348 Severity=Warning SymbolicName=MSG_APPLY_RETRYING_PACKAGE diff --git a/src/engine/externalengine.cpp b/src/engine/externalengine.cpp index 7e9bb25c..d6c44736 100644 --- a/src/engine/externalengine.cpp +++ b/src/engine/externalengine.cpp @@ -269,8 +269,7 @@ HRESULT ExternalEngineSetUpdate( ) { HRESULT hr = S_OK; - LPCWSTR sczId = NULL; - LPWSTR sczLocalSource = NULL; + LPWSTR sczFilePath = NULL; LPWSTR sczCommandline = NULL; UUID guid = { }; WCHAR wzGuid[39]; @@ -296,38 +295,30 @@ HRESULT ExternalEngineSetUpdate( { UpdateUninitialize(&pEngineState->update); - if (!wzLocalSource || !*wzLocalSource) - { - hr = StrAllocFormatted(&sczLocalSource, L"update\\%ls", pEngineState->registration.sczExecutableName); - ExitOnFailure(hr, "Failed to default local update source"); - } - hr = CoreRecreateCommandLine(&sczCommandline, BOOTSTRAPPER_ACTION_INSTALL, pEngineState->command.display, pEngineState->command.restart, BOOTSTRAPPER_RELATION_NONE, FALSE, pEngineState->registration.sczActiveParent, pEngineState->registration.sczAncestors, NULL, pEngineState->command.wzCommandLine); ExitOnFailure(hr, "Failed to recreate command-line for update bundle."); - // Per-user bundles would fail to use the downloaded update bundle, as the existing install would already be cached - // at the registration id's location. Here I am generating a random guid, but in the future it would be nice if the - // feed would provide the ID of the update. - if (!pEngineState->registration.fPerMachine) + // Bundles would fail to use the downloaded update bundle, as the running bundle would be one of the search paths. + // Here I am generating a random guid, but in the future it would be nice if the feed would provide the ID of the update. + rs = ::UuidCreate(&guid); + hr = HRESULT_FROM_RPC(rs); + ExitOnFailure(hr, "Failed to create bundle update guid."); + + if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid))) { - rs = ::UuidCreate(&guid); - hr = HRESULT_FROM_RPC(rs); - ExitOnFailure(hr, "Failed to create bundle update guid."); + hr = E_OUTOFMEMORY; + ExitOnRootFailure(hr, "Failed to convert bundle update guid into string."); + } - if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid))) - { - hr = E_OUTOFMEMORY; - ExitOnRootFailure(hr, "Failed to convert bundle update guid into string."); - } + hr = StrAllocFormatted(&sczFilePath, L"%ls\\%ls", wzGuid, pEngineState->registration.sczExecutableName); + ExitOnFailure(hr, "Failed to build bundle update file path."); - sczId = wzGuid; - } - else + if (!wzLocalSource || !*wzLocalSource) { - sczId = pEngineState->registration.sczId; + wzLocalSource = sczFilePath; } - hr = PseudoBundleInitialize(FILEMAKEVERSION(rmj, rmm, rup, rpr), &pEngineState->update.package, FALSE, sczId, BOOTSTRAPPER_RELATION_UPDATE, BOOTSTRAPPER_PACKAGE_STATE_ABSENT, FALSE, pEngineState->registration.sczExecutableName, sczLocalSource ? sczLocalSource : wzLocalSource, wzDownloadSource, qwSize, TRUE, sczCommandline, NULL, NULL, NULL, rgbHash, cbHash); + hr = PseudoBundleInitialize(FILEMAKEVERSION(rmj, rmm, rup, rpr), &pEngineState->update.package, FALSE, pEngineState->registration.sczId, BOOTSTRAPPER_RELATION_UPDATE, BOOTSTRAPPER_PACKAGE_STATE_ABSENT, FALSE, sczFilePath, wzLocalSource, wzDownloadSource, qwSize, TRUE, sczCommandline, NULL, NULL, NULL, rgbHash, cbHash); ExitOnFailure(hr, "Failed to set update bundle."); pEngineState->update.fUpdateAvailable = TRUE; @@ -337,7 +328,7 @@ LExit: ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive); ReleaseStr(sczCommandline); - ReleaseStr(sczLocalSource); + ReleaseStr(sczFilePath); return hr; } diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index b6bd65dc..f6ae1491 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -395,29 +395,46 @@ EXTERN_C BAAPI UserExperienceOnCacheAcquireBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z_opt LPCWSTR wzPackageOrContainerId, __in_z_opt LPCWSTR wzPayloadId, - __in BOOTSTRAPPER_CACHE_OPERATION operation, - __in_z LPCWSTR wzSource + __in_z LPWSTR* pwzSource, + __in_z LPWSTR* pwzDownloadUrl, + __in_z_opt LPCWSTR wzPayloadContainerId, + __out BOOTSTRAPPER_CACHE_OPERATION* pCacheOperation ) { HRESULT hr = S_OK; BA_ONCACHEACQUIREBEGIN_ARGS args = { }; BA_ONCACHEACQUIREBEGIN_RESULTS results = { }; + *pCacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE; args.cbSize = sizeof(args); args.wzPackageOrContainerId = wzPackageOrContainerId; args.wzPayloadId = wzPayloadId; - args.operation = operation; - args.wzSource = wzSource; + args.wzSource = *pwzSource; + args.wzDownloadUrl = *pwzDownloadUrl; + args.wzPayloadContainerId = wzPayloadContainerId; + args.recommendation = *pCacheOperation; results.cbSize = sizeof(results); + results.action = *pCacheOperation; - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREBEGIN, &args, &results); + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREBEGIN, &args, &results); ExitOnFailure(hr, "BA OnCacheAcquireBegin failed."); if (results.fCancel) { hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); } + else + { + // Verify the BA requested an action that is possible. + if (BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD == results.action && *pwzDownloadUrl && **pwzDownloadUrl || + BOOTSTRAPPER_CACHE_OPERATION_EXTRACT == results.action && wzPayloadContainerId || + BOOTSTRAPPER_CACHE_OPERATION_COPY == results.action || + BOOTSTRAPPER_CACHE_OPERATION_NONE == results.action) + { + *pCacheOperation = results.action; + } + } LExit: return hr; @@ -444,7 +461,7 @@ EXTERN_C BAAPI UserExperienceOnCacheAcquireComplete( results.cbSize = sizeof(results); results.action = args.recommendation; - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRECOMPLETE, &args, &results); + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRECOMPLETE, &args, &results); ExitOnFailure(hr, "BA OnCacheAcquireComplete failed."); if (FAILED(hrStatus)) @@ -490,6 +507,64 @@ LExit: return hr; } +EXTERN_C BAAPI UserExperienceOnCacheAcquireResolving( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in_z LPWSTR* rgSearchPaths, + __in DWORD cSearchPaths, + __in BOOL fFoundLocal, + __in DWORD* pdwChosenSearchPath, + __in_z_opt LPCWSTR wzDownloadUrl, + __in_z_opt LPCWSTR wzPayloadContainerId, + __inout BOOTSTRAPPER_CACHE_OPERATION* pCacheOperation + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEACQUIRERESOLVING_ARGS args = { }; + BA_ONCACHEACQUIRERESOLVING_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageOrContainerId = wzPackageOrContainerId; + args.wzPayloadId = wzPayloadId; + args.rgSearchPaths = const_cast(rgSearchPaths); + args.cSearchPaths = cSearchPaths; + args.fFoundLocal = fFoundLocal; + args.dwRecommendedSearchPath = *pdwChosenSearchPath; + args.wzDownloadUrl = wzDownloadUrl; + args.recommendation = *pCacheOperation; + + results.cbSize = sizeof(results); + results.dwChosenSearchPath = *pdwChosenSearchPath; + results.action = *pCacheOperation; + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRERESOLVING, &args, &results); + ExitOnFailure(hr, "BA OnCacheAcquireResolving failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + else + { + // Verify the BA requested an action that is possible. + if (BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD == results.action && wzDownloadUrl && *wzDownloadUrl || + BOOTSTRAPPER_CACHE_OPERATION_EXTRACT == results.action && wzPayloadContainerId || + BOOTSTRAPPER_CACHE_OPERATION_NONE == results.action) + { + *pCacheOperation = results.action; + } + else if (BOOTSTRAPPER_CACHE_OPERATION_COPY == results.action && results.dwChosenSearchPath < cSearchPaths) + { + *pdwChosenSearchPath = results.dwChosenSearchPath; + *pCacheOperation = results.action; + } + } + +LExit: + return hr; +} + EXTERN_C BAAPI UserExperienceOnCacheBegin( __in BURN_USER_EXPERIENCE* pUserExperience ) @@ -1861,45 +1936,6 @@ LExit: return hr; } -EXTERN_C BAAPI UserExperienceOnResolveSource( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in_z LPCWSTR wzLocalSource, - __in_z_opt LPCWSTR wzDownloadSource, - __inout BOOTSTRAPPER_RESOLVESOURCE_ACTION* pAction - ) -{ - HRESULT hr = S_OK; - BA_ONRESOLVESOURCE_ARGS args = { }; - BA_ONRESOLVESOURCE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageOrContainerId = wzPackageOrContainerId; - args.wzPayloadId = wzPayloadId; - args.wzLocalSource = wzLocalSource; - args.wzDownloadSource = wzDownloadSource; - args.recommendation = *pAction; - - results.cbSize = sizeof(results); - results.action = *pAction; - - hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONRESOLVESOURCE, &args, &results); - ExitOnFailure(hr, "BA OnResolveSource failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - else - { - *pAction = results.action; - } - -LExit: - return hr; -} - EXTERN_C BAAPI UserExperienceOnRollbackMsiTransactionBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in LPCWSTR wzTransactionId diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index a1fb67a0..dd1fb086 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -126,8 +126,10 @@ BAAPI UserExperienceOnCacheAcquireBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z_opt LPCWSTR wzPackageOrContainerId, __in_z_opt LPCWSTR wzPayloadId, - __in BOOTSTRAPPER_CACHE_OPERATION operation, - __in_z LPCWSTR wzSource + __in_z LPWSTR* pwzSource, + __in_z LPWSTR* pwzDownloadUrl, + __in_z_opt LPCWSTR wzPayloadContainerId, + __out BOOTSTRAPPER_CACHE_OPERATION* pCacheOperation ); BAAPI UserExperienceOnCacheAcquireComplete( __in BURN_USER_EXPERIENCE* pUserExperience, @@ -144,6 +146,18 @@ BAAPI UserExperienceOnCacheAcquireProgress( __in DWORD64 dw64Total, __in DWORD dwOverallPercentage ); +BAAPI UserExperienceOnCacheAcquireResolving( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in_z LPWSTR* rgSearchPaths, + __in DWORD cSearchPaths, + __in BOOL fFoundLocal, + __in DWORD* pdwChosenSearchPath, + __in_z_opt LPCWSTR wzDownloadUrl, + __in_z_opt LPCWSTR wzPayloadContainerId, + __inout BOOTSTRAPPER_CACHE_OPERATION* pCacheOperation + ); BAAPI UserExperienceOnCacheBegin( __in BURN_USER_EXPERIENCE* pUserExperience ); @@ -426,14 +440,6 @@ BAAPI UserExperienceOnRegisterComplete( __in BURN_USER_EXPERIENCE* pUserExperience, __in HRESULT hrStatus ); -BAAPI UserExperienceOnResolveSource( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in_z LPCWSTR wzLocalSource, - __in_z_opt LPCWSTR wzDownloadSource, - __inout BOOTSTRAPPER_RESOLVESOURCE_ACTION* pAction - ); BAAPI UserExperienceOnRollbackMsiTransactionBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in LPCWSTR wzTransactionId -- cgit v1.2.3-55-g6feb From 66360b60b0298c88d32ce2b5e5ce5befa1c09ff8 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 16 Apr 2021 10:21:26 -0500 Subject: Only set the last used folder if the target passed verification. --- src/engine/apply.cpp | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index dc9f905b..c39a7746 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -26,6 +26,7 @@ typedef struct _BURN_CACHE_CONTEXT LPWSTR* rgSearchPaths; DWORD cSearchPaths; DWORD cSearchPathsMax; + LPWSTR sczLastUsedFolderCandidate; } BURN_CACHE_CONTEXT; typedef struct _BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT @@ -563,6 +564,7 @@ LExit: ReleaseNullStr(cacheContext.rgSearchPaths[i]); } ReleaseMem(cacheContext.rgSearchPaths); + ReleaseStr(cacheContext.sczLastUsedFolderCandidate); UserExperienceOnCacheComplete(pUX, hr); return hr; @@ -840,9 +842,17 @@ static HRESULT ApplyExtractContainer( hr = ExtractContainer(pContext, pContainer); LogExitOnFailure(hr, MSG_FAILED_EXTRACT_CONTAINER, "Failed to extract payloads from container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath); + if (pContext->sczLastUsedFolderCandidate) + { + // We successfully copied from a source location, set that as the last used source. + CacheSetLastUsedSource(pContext->pVariables, pContext->sczLastUsedFolderCandidate, pContainer->sczFilePath); + } + pContext->qwSuccessfulCacheProgress += pContainer->qwFileSize; LExit: + ReleaseNullStr(pContext->sczLastUsedFolderCandidate); + return hr; } @@ -901,6 +911,12 @@ static HRESULT ApplyLayoutContainer( hr = LayoutOrCacheContainerOrPayload(pContext, pContainer, NULL, NULL, TRUE, cTryAgainAttempts, &fRetry); if (SUCCEEDED(hr)) { + if (pContext->sczLastUsedFolderCandidate) + { + // We successfully copied from a source location, set that as the last used source. + CacheSetLastUsedSource(pContext->pVariables, pContext->sczLastUsedFolderCandidate, pContainer->sczFilePath); + } + pContext->qwSuccessfulCacheProgress += pContainer->qwFileSize; break; } @@ -915,11 +931,14 @@ static HRESULT ApplyLayoutContainer( ++cTryAgainAttempts; pContext->qwSuccessfulCacheProgress -= pContainer->qwFileSize; + ReleaseNullStr(pContext->sczLastUsedFolderCandidate); LogErrorId(hr, MSG_APPLY_RETRYING_PAYLOAD, pContainer->sczId, NULL, NULL); } } LExit: + ReleaseNullStr(pContext->sczLastUsedFolderCandidate); + return hr; } @@ -961,6 +980,12 @@ static HRESULT ApplyProcessPayload( hr = LayoutOrCacheContainerOrPayload(pContext, NULL, pPackage, pPayload, FALSE, cTryAgainAttempts, &fRetry); if (SUCCEEDED(hr)) { + if (pContext->sczLastUsedFolderCandidate) + { + // We successfully copied from a source location, set that as the last used source. + CacheSetLastUsedSource(pContext->pVariables, pContext->sczLastUsedFolderCandidate, pPayload->sczFilePath); + } + pContext->qwSuccessfulCacheProgress += pPayload->qwFileSize; break; } @@ -975,11 +1000,14 @@ static HRESULT ApplyProcessPayload( ++cTryAgainAttempts; pContext->qwSuccessfulCacheProgress -= pPayload->qwFileSize; + ReleaseNullStr(pContext->sczLastUsedFolderCandidate); LogErrorId(hr, MSG_APPLY_RETRYING_PAYLOAD, pPayload->sczKey, NULL, NULL); } } LExit: + ReleaseNullStr(pContext->sczLastUsedFolderCandidate); + return hr; } @@ -1245,12 +1273,9 @@ static HRESULT AcquireContainerOrPayload( hr = CopyPayload(&progress, INVALID_HANDLE_VALUE, pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath); // Error handling happens after sending complete message to BA. - // TODO: wait for verification? - // We successfully copied from a source location, set that as the last used source. - if (SUCCEEDED(hr)) - { - CacheSetLastUsedSource(pContext->pVariables, pContext->rgSearchPaths[dwChosenSearchPath], wzRelativePath); - } + // Store the source path so it can be used as the LastUsedFolder if it passes verification. + pContext->sczLastUsedFolderCandidate = pContext->rgSearchPaths[dwChosenSearchPath]; + pContext->rgSearchPaths[dwChosenSearchPath] = NULL; } fBeginCalled = FALSE; -- cgit v1.2.3-55-g6feb From 9a061c70f8d87d4f4703bd88a0eaae98c3cfc1d5 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 16 Apr 2021 10:29:10 -0500 Subject: Always send OnCacheAcquireProgress at least once per payload. Always send OnCacheAcquireProgress between OnCacheAcquireBegin and OnCacheAcquireComplete. Track the successful cache acquisition progress during the final progress call. --- src/engine/apply.cpp | 317 +++++++++++++++++++++++++-------------------------- src/engine/engine.mc | 21 ++++ src/engine/plan.cpp | 8 +- 3 files changed, 185 insertions(+), 161 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index c39a7746..e62db4d2 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -37,7 +37,7 @@ typedef struct _BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT BURN_PAYLOAD* pPayload; BOOL fCancel; - BOOL fError; + HRESULT hrError; } BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT; typedef struct _BURN_EXECUTE_CONTEXT @@ -102,12 +102,16 @@ static HRESULT LayoutBundle( __in_z LPCWSTR wzExecutableName, __in_z LPCWSTR wzUnverifiedPath ); -static HRESULT AcquireContainerOrPayload( +static HRESULT ApplyAcquireContainerOrPayload( __in BURN_CACHE_CONTEXT* pContext, __in_opt BURN_CONTAINER* pContainer, __in_opt BURN_PACKAGE* pPackage, __in_opt BURN_PAYLOAD* pPayload ); +static HRESULT AcquireContainerOrPayload( + __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, + __out BOOL* pfRetry + ); static HRESULT LayoutOrCacheContainerOrPayload( __in BURN_CACHE_CONTEXT* pContext, __in_opt BURN_CONTAINER* pContainer, @@ -127,6 +131,10 @@ static HRESULT DownloadPayload( __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, __in_z LPCWSTR wzDestinationPath ); +static HRESULT CompleteCacheProgress( + __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pContext, + __in DWORD64 qwFileSize + ); static DWORD CALLBACK CacheProgressRoutine( __in LARGE_INTEGER TotalFileSize, __in LARGE_INTEGER TotalBytesTransferred, @@ -833,12 +841,10 @@ static HRESULT ApplyExtractContainer( if (!pContainer->fActuallyAttached) { - hr = AcquireContainerOrPayload(pContext, pContainer, NULL, NULL); + hr = ApplyAcquireContainerOrPayload(pContext, pContainer, NULL, NULL); LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_CONTAINER, "Failed to acquire container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath); } - pContext->qwSuccessfulCacheProgress += pContainer->qwFileSize; - hr = ExtractContainer(pContext, pContainer); LogExitOnFailure(hr, MSG_FAILED_EXTRACT_CONTAINER, "Failed to extract payloads from container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath); @@ -903,11 +909,9 @@ static HRESULT ApplyLayoutContainer( { fRetry = FALSE; - hr = AcquireContainerOrPayload(pContext, pContainer, NULL, NULL); + hr = ApplyAcquireContainerOrPayload(pContext, pContainer, NULL, NULL); LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_CONTAINER, "Failed to acquire container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath); - pContext->qwSuccessfulCacheProgress += pContainer->qwFileSize; - hr = LayoutOrCacheContainerOrPayload(pContext, pContainer, NULL, NULL, TRUE, cTryAgainAttempts, &fRetry); if (SUCCEEDED(hr)) { @@ -932,7 +936,7 @@ static HRESULT ApplyLayoutContainer( ++cTryAgainAttempts; pContext->qwSuccessfulCacheProgress -= pContainer->qwFileSize; ReleaseNullStr(pContext->sczLastUsedFolderCandidate); - LogErrorId(hr, MSG_APPLY_RETRYING_PAYLOAD, pContainer->sczId, NULL, NULL); + LogErrorId(hr, MSG_APPLY_RETRYING_CONTAINER, pContainer->sczId, NULL, NULL); } } @@ -971,11 +975,9 @@ static HRESULT ApplyProcessPayload( { fRetry = FALSE; - hr = AcquireContainerOrPayload(pContext, NULL, pPackage, pPayload); + hr = ApplyAcquireContainerOrPayload(pContext, NULL, pPackage, pPayload); LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_PAYLOAD, "Failed to acquire payload: %ls to working path: %ls", pPayload->sczKey, pPayload->sczUnverifiedPath); - pContext->qwSuccessfulCacheProgress += pPayload->qwFileSize; - // TODO: set fMove to TRUE appropriately hr = LayoutOrCacheContainerOrPayload(pContext, NULL, pPackage, pPayload, FALSE, cTryAgainAttempts, &fRetry); if (SUCCEEDED(hr)) @@ -1172,7 +1174,7 @@ LExit: return hr; } -static HRESULT AcquireContainerOrPayload( +static HRESULT ApplyAcquireContainerOrPayload( __in BURN_CACHE_CONTEXT* pContext, __in_opt BURN_CONTAINER* pContainer, __in_opt BURN_PACKAGE* pPackage, @@ -1181,6 +1183,43 @@ static HRESULT AcquireContainerOrPayload( { AssertSz(pContainer || pPayload, "Must provide a container or a payload."); + HRESULT hr = S_OK; + BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { }; + BOOL fRetry = FALSE; + + progress.pCacheContext = pContext; + progress.pContainer = pContainer; + progress.pPackage = pPackage; + progress.pPayload = pPayload; + + do + { + hr = AcquireContainerOrPayload(&progress, &fRetry); + + if (fRetry) + { + hr = S_OK; + LogErrorId(hr, pContainer ? MSG_APPLY_RETRYING_ACQUIRE_CONTAINER : MSG_APPLY_RETRYING_ACQUIRE_PAYLOAD, pContainer ? pContainer->sczId : pPayload->sczKey, NULL, NULL); + } + + ExitOnFailure(hr, "Failed to acquire %hs: %ls", pContainer ? "container" : "payload", pContainer ? pContainer->sczId : pPayload->sczKey); + } while (fRetry); + +LExit: + return hr; +} + +static HRESULT AcquireContainerOrPayload( + __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, + __out BOOL* pfRetry + ) +{ + BURN_CACHE_CONTEXT* pContext = pProgress->pCacheContext; + BURN_CONTAINER* pContainer = pProgress->pContainer; + BURN_PACKAGE* pPackage = pProgress->pPackage; + BURN_PAYLOAD* pPayload = pProgress->pPayload; + AssertSz(pContainer || pPayload, "Must provide a container or a payload."); + HRESULT hr = S_OK; int nEquivalentPaths = 0; LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : NULL; @@ -1188,158 +1227,106 @@ static HRESULT AcquireContainerOrPayload( LPCWSTR wzPayloadContainerId = pPayload && pPayload->pContainer ? pPayload->pContainer->sczId : NULL; LPCWSTR wzDestinationPath = pContainer ? pContainer->sczUnverifiedPath: pPayload->sczUnverifiedPath; LPCWSTR wzRelativePath = pContainer ? pContainer->sczFilePath : pPayload->sczFilePath; - BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { }; - BOOL fBeginCalled = FALSE; - BOOL fRetry = FALSE; DWORD dwChosenSearchPath = 0; BOOTSTRAPPER_CACHE_OPERATION cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE; LPWSTR* pwzDownloadUrl = pContainer ? &pContainer->downloadSource.sczUrl : &pPayload->downloadSource.sczUrl; LPWSTR* pwzSourcePath = pContainer ? &pContainer->sczSourcePath : &pPayload->sczSourcePath; + BOOL fFoundLocal = FALSE; - progress.pCacheContext = pContext; - progress.pContainer = pContainer; - progress.pPackage = pPackage; - progress.pPayload = pPayload; - - do - { - BOOL fFoundLocal = FALSE; + pContext->cSearchPaths = 0; + *pfRetry = FALSE; + pProgress->fCancel = FALSE; - pContext->cSearchPaths = 0; - dwChosenSearchPath = 0; - fRetry = FALSE; - progress.fCancel = FALSE; - fBeginCalled = TRUE; + hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId, pwzSourcePath, pwzDownloadUrl, wzPayloadContainerId, &cacheOperation); + ExitOnRootFailure(hr, "BA aborted cache acquire begin."); - hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId, pwzSourcePath, pwzDownloadUrl, wzPayloadContainerId, &cacheOperation); - ExitOnRootFailure(hr, "BA aborted cache acquire begin."); + // Skip the Resolving event and probing local paths if the BA already knew it wanted to download or extract. + if (BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD != cacheOperation && + BOOTSTRAPPER_CACHE_OPERATION_EXTRACT != cacheOperation) + { + hr = CacheGetLocalSourcePaths(wzRelativePath, *pwzSourcePath, wzDestinationPath, pContext->wzLayoutDirectory, pContext->pVariables, &pContext->rgSearchPaths, &pContext->cSearchPaths); + ExitOnFailure(hr, "Failed to search local source."); - // Skip the Resolving event and probing local paths if the BA already knew it wanted to download or extract. - if (BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD != cacheOperation && - BOOTSTRAPPER_CACHE_OPERATION_EXTRACT != cacheOperation) + for (DWORD i = 0; i < pContext->cSearchPaths; ++i) { - hr = CacheGetLocalSourcePaths(wzRelativePath, *pwzSourcePath, wzDestinationPath, pContext->wzLayoutDirectory, pContext->pVariables, &pContext->rgSearchPaths, &pContext->cSearchPaths); - ExitOnFailure(hr, "Failed to search local source."); - - for (DWORD i = 0; i < pContext->cSearchPaths; ++i) + // If the file exists locally, choose it. + if (FileExistsEx(pContext->rgSearchPaths[i], NULL)) { - // If the file exists locally, choose it. - if (FileExistsEx(pContext->rgSearchPaths[i], NULL)) - { - dwChosenSearchPath = i; + dwChosenSearchPath = i; - fFoundLocal = TRUE; - break; - } + fFoundLocal = TRUE; + break; } + } - if (BOOTSTRAPPER_CACHE_OPERATION_COPY == cacheOperation) - { - if (!fFoundLocal) - { - cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE; - } - } - else + if (BOOTSTRAPPER_CACHE_OPERATION_COPY == cacheOperation) + { + if (!fFoundLocal) { - if (fFoundLocal) // the file exists locally, so copy it. - { - cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_COPY; - } - else if (wzPayloadContainerId) - { - cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_EXTRACT; - } - else if (*pwzDownloadUrl && **pwzDownloadUrl) - { - cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD; - } + cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE; } - - // Let the BA have a chance to override the action, but their chance to change the source is during begin or complete. - hr = UserExperienceOnCacheAcquireResolving(pContext->pUX, wzPackageOrContainerId, wzPayloadId, pContext->rgSearchPaths, pContext->cSearchPaths, fFoundLocal, &dwChosenSearchPath, *pwzDownloadUrl, wzPayloadContainerId, &cacheOperation); - ExitOnRootFailure(hr, "BA aborted cache acquire resolving."); } - - switch (cacheOperation) + else { - case BOOTSTRAPPER_CACHE_OPERATION_COPY: - // If the source path and destination path are different, do the copy (otherwise there's no point). - hr = PathCompare(pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath, &nEquivalentPaths); - ExitOnFailure(hr, "Failed to determine if payload paths were equivalent, source: %ls, destination: %ls.", pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath); - - if (CSTR_EQUAL != nEquivalentPaths) + if (fFoundLocal) // the file exists locally, so copy it. { - hr = CopyPayload(&progress, INVALID_HANDLE_VALUE, pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath); - // Error handling happens after sending complete message to BA. - - // Store the source path so it can be used as the LastUsedFolder if it passes verification. - pContext->sczLastUsedFolderCandidate = pContext->rgSearchPaths[dwChosenSearchPath]; - pContext->rgSearchPaths[dwChosenSearchPath] = NULL; + cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_COPY; } - - fBeginCalled = FALSE; - UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry); - if (fRetry) + else if (wzPayloadContainerId) { - hr = S_OK; + cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_EXTRACT; } - - ExitOnFailure(hr, "Failed to acquire payload from: '%ls' to working path: '%ls'", pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath); - - break; - case BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD: - hr = DownloadPayload(&progress, wzDestinationPath); - // Error handling happens after sending complete message to BA. - - fBeginCalled = FALSE; - UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry); - if (fRetry) + else if (*pwzDownloadUrl && **pwzDownloadUrl) { - hr = S_OK; + cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD; } + } - ExitOnFailure(hr, "Failed to acquire payload from: '%ls' to working path: '%ls'", *pwzDownloadUrl, wzDestinationPath); + // Let the BA have a chance to override the action, but their chance to change the source is during begin or complete. + hr = UserExperienceOnCacheAcquireResolving(pContext->pUX, wzPackageOrContainerId, wzPayloadId, pContext->rgSearchPaths, pContext->cSearchPaths, fFoundLocal, &dwChosenSearchPath, *pwzDownloadUrl, wzPayloadContainerId, &cacheOperation); + ExitOnRootFailure(hr, "BA aborted cache acquire resolving."); + } - break; - case BOOTSTRAPPER_CACHE_OPERATION_EXTRACT: - Assert(pPayload->pContainer); + switch (cacheOperation) + { + case BOOTSTRAPPER_CACHE_OPERATION_COPY: + // If the source path and destination path are different, do the copy (otherwise there's no point). + hr = PathCompare(pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath, &nEquivalentPaths); + ExitOnFailure(hr, "Failed to determine if payload paths were equivalent, source: %ls, destination: %ls.", pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath); - hr = ApplyExtractContainer(pContext, pPayload->pContainer); - // Error handling happens after sending complete message to BA. + if (CSTR_EQUAL != nEquivalentPaths) + { + hr = CopyPayload(pProgress, INVALID_HANDLE_VALUE, pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath); + ExitOnFailure(hr, "Failed to copy payload: %ls", wzPayloadId); - fBeginCalled = FALSE; - UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry); - if (fRetry) - { - hr = S_OK; - } + // Store the source path so it can be used as the LastUsedFolder if it passes verification. + pContext->sczLastUsedFolderCandidate = pContext->rgSearchPaths[dwChosenSearchPath]; + pContext->rgSearchPaths[dwChosenSearchPath] = NULL; + } - ExitOnFailure(hr, "Failed to extract container for payload: %ls", wzPayloadId); + break; + case BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD: + hr = DownloadPayload(pProgress, wzDestinationPath); + ExitOnFailure(hr, "Failed to download payload: %ls", wzPayloadId); - break; - case BOOTSTRAPPER_CACHE_OPERATION_NONE: - hr = E_FILENOTFOUND; - LogErrorId(hr, MSG_RESOLVE_SOURCE_FAILED, wzPayloadId, pPackage ? pPackage->sczId : NULL, pContainer ? pContainer->sczId : NULL); + break; + case BOOTSTRAPPER_CACHE_OPERATION_EXTRACT: + Assert(pPayload->pContainer); - fBeginCalled = FALSE; - UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry); - if (fRetry) - { - hr = S_OK; - } + hr = ApplyExtractContainer(pContext, pPayload->pContainer); + ExitOnFailure(hr, "Failed to extract container for payload: %ls", wzPayloadId); - ExitOnFailure(hr, "Failed to resolve source, payload: %ls, package: %ls, container: %ls", wzPayloadId, pPackage ? pPackage->sczId : NULL, pContainer ? pContainer->sczId : NULL); + break; + default: + hr = E_FILENOTFOUND; + LogExitOnFailure(hr, MSG_RESOLVE_SOURCE_FAILED, "Failed to resolve source, payload: %ls, package: %ls, container: %ls", wzPayloadId, pPackage ? pPackage->sczId : NULL, pContainer ? pContainer->sczId : NULL); + } - break; - } - } while (fRetry); + // Send 100% complete here. This is sometimes the only progress sent to the BA. + hr = CompleteCacheProgress(pProgress, pContainer ? pContainer->qwFileSize : pPayload->qwFileSize); LExit: - if (fBeginCalled) - { - UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry); - } + UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, pfRetry); pContext->cSearchPathsMax = max(pContext->cSearchPaths, pContext->cSearchPathsMax); @@ -1360,9 +1347,6 @@ static HRESULT LayoutOrCacheContainerOrPayload( LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : L""; LPCWSTR wzUnverifiedPath = pContainer ? pContainer->sczUnverifiedPath : pPayload->sczUnverifiedPath; LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : L""; - LARGE_INTEGER liContainerOrPayloadSize = { }; - LARGE_INTEGER liZero = { }; - BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { }; BOOL fCanAffectRegistration = FALSE; if (!pContext->wzLayoutDirectory) @@ -1373,13 +1357,6 @@ static HRESULT LayoutOrCacheContainerOrPayload( fCanAffectRegistration = pPackage->fCanAffectRegistration; } - liContainerOrPayloadSize.QuadPart = pContainer ? pContainer->qwFileSize : pPayload->qwFileSize; - - progress.pCacheContext = pContext; - progress.pContainer = pContainer; - progress.pPackage = pPackage; - progress.pPayload = pPayload; - *pfRetry = FALSE; do @@ -1407,25 +1384,9 @@ static HRESULT LayoutOrCacheContainerOrPayload( hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, wzUnverifiedPath, fMove); } - // If succeeded, send 100% complete here. If the payload was already cached this is the first progress the BA - // will get. - if (SUCCEEDED(hr)) + if (SUCCEEDED(hr) && fCanAffectRegistration) { - if (fCanAffectRegistration) - { - pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } - - CacheProgressRoutine(liContainerOrPayloadSize, liContainerOrPayloadSize, liZero, liZero, 0, 0, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, &progress); - if (progress.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - else if (progress.fError) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); - } - ExitOnRootFailure(hr, "BA aborted verify of %hs: %ls", pContainer ? "container" : "payload", pContainer ? wzPackageOrContainerId : wzPayloadId); + pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; } BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = FAILED(hr) && cTryAgainAttempts < BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS ? BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION : BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE; @@ -1624,6 +1585,39 @@ LExit: return hr; } +static HRESULT CompleteCacheProgress( + __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pContext, + __in DWORD64 qwFileSize + ) +{ + HRESULT hr = S_OK; + LARGE_INTEGER liContainerOrPayloadSize = { }; + LARGE_INTEGER liZero = { }; + DWORD dwResult = 0; + + liContainerOrPayloadSize.QuadPart = qwFileSize; + + dwResult = CacheProgressRoutine(liContainerOrPayloadSize, liContainerOrPayloadSize, liZero, liZero, 0, 0, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, pContext); + + if (PROGRESS_CONTINUE == dwResult) + { + pContext->pCacheContext->qwSuccessfulCacheProgress += qwFileSize; + } + else if (PROGRESS_CANCEL == dwResult) + { + if (pContext->fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + else + { + hr = pContext->hrError; + } + } + + return hr; +} + static DWORD CALLBACK CacheProgressRoutine( __in LARGE_INTEGER TotalFileSize, __in LARGE_INTEGER TotalBytesTransferred, @@ -1650,6 +1644,9 @@ static DWORD CALLBACK CacheProgressRoutine( DWORD dwOverallPercentage = pProgress->pCacheContext->qwTotalCacheSize ? static_cast(qwCacheProgress * 100 / pProgress->pCacheContext->qwTotalCacheSize) : 0; hr = UserExperienceOnCacheAcquireProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); + ExitOnRootFailure(hr, "BA aborted acquire of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); + +LExit: if (HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr) { dwResult = PROGRESS_CANCEL; @@ -1658,7 +1655,7 @@ static DWORD CALLBACK CacheProgressRoutine( else if (FAILED(hr)) { dwResult = PROGRESS_CANCEL; - pProgress->fError = TRUE; + pProgress->hrError = hr; } else { diff --git a/src/engine/engine.mc b/src/engine/engine.mc index f03bc1ea..0e19d3bb 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -724,6 +724,13 @@ Language=English Acquiring package: %1!ls!, payload: %2!ls!, %3!hs! from: %4!ls! . +MessageId=347 +Severity=Warning +SymbolicName=MSG_APPLY_RETRYING_CONTAINER +Language=English +Application requested retry of container: %2!ls!, encountered error: %1!ls!. Retrying... +. + MessageId=348 Severity=Warning SymbolicName=MSG_APPLY_RETRYING_PACKAGE @@ -780,6 +787,20 @@ Language=English Unable to register source directory: %1!ls!, product: %2!ls!, reason: 0x%3!x!. Continuing... . +MessageId=356 +Severity=Warning +SymbolicName=MSG_APPLY_RETRYING_ACQUIRE_CONTAINER +Language=English +Application requested retry acquire of container: %2!ls!, encountered error: %1!ls!. Retrying... +. + +MessageId=357 +Severity=Warning +SymbolicName=MSG_APPLY_RETRYING_ACQUIRE_PAYLOAD +Language=English +Application requested retry acquire of payload: %2!ls!, encountered error: %1!ls!. Retrying... +. + MessageId=358 Severity=Success SymbolicName=MSG_PAUSE_AU_STARTING diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index cfe4893b..a37dcc89 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -1010,7 +1010,13 @@ extern "C" HRESULT PlanLayoutContainer( } else { - pPlan->qwCacheSizeTotal += 2 * pContainer->qwFileSize; + if (!pContainer->fActuallyAttached) + { + pPlan->qwCacheSizeTotal += pContainer->qwFileSize; + } + + // TODO: This should be the sum of all uncompressed payloads in the container, ideally restricted to the payloads that were actually planned. + pPlan->qwCacheSizeTotal += pContainer->qwFileSize; } if (!pContainer->sczUnverifiedPath) -- cgit v1.2.3-55-g6feb From 12a5bf684009c743a9de56f48681466a2cfdda02 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 16 Apr 2021 10:31:49 -0500 Subject: Track progress of bundle being laid out. --- src/engine/apply.cpp | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index e62db4d2..f3821769 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -82,7 +82,8 @@ static HRESULT ApplyLayoutBundle( __in BURN_CACHE_CONTEXT* pContext, __in BURN_PAYLOAD_GROUP* pPayloads, __in_z LPCWSTR wzExecutableName, - __in_z LPCWSTR wzUnverifiedPath + __in_z LPCWSTR wzUnverifiedPath, + __in DWORD64 qwBundleSize ); static HRESULT ApplyLayoutContainer( __in BURN_CACHE_CONTEXT* pContext, @@ -100,7 +101,8 @@ static HRESULT ExtractContainer( static HRESULT LayoutBundle( __in BURN_CACHE_CONTEXT* pContext, __in_z LPCWSTR wzExecutableName, - __in_z LPCWSTR wzUnverifiedPath + __in_z LPCWSTR wzUnverifiedPath, + __in DWORD64 qwBundleSize ); static HRESULT ApplyAcquireContainerOrPayload( __in BURN_CACHE_CONTEXT* pContext, @@ -504,7 +506,7 @@ extern "C" HRESULT ApplyCache( break; case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: - hr = ApplyLayoutBundle(&cacheContext, pCacheAction->bundleLayout.pPayloadGroup, pCacheAction->bundleLayout.sczExecutableName, pCacheAction->bundleLayout.sczUnverifiedPath); + hr = ApplyLayoutBundle(&cacheContext, pCacheAction->bundleLayout.pPayloadGroup, pCacheAction->bundleLayout.sczExecutableName, pCacheAction->bundleLayout.sczUnverifiedPath, pCacheAction->bundleLayout.qwBundleSize); ExitOnFailure(hr, "Failed cache action: %ls", L"layout bundle"); ++(*pcOverallProgressTicks); @@ -866,12 +868,13 @@ static HRESULT ApplyLayoutBundle( __in BURN_CACHE_CONTEXT* pContext, __in BURN_PAYLOAD_GROUP* pPayloads, __in_z LPCWSTR wzExecutableName, - __in_z LPCWSTR wzUnverifiedPath + __in_z LPCWSTR wzUnverifiedPath, + __in DWORD64 qwBundleSize ) { HRESULT hr = S_OK; - hr = LayoutBundle(pContext, wzExecutableName, wzUnverifiedPath); + hr = LayoutBundle(pContext, wzExecutableName, wzUnverifiedPath, qwBundleSize); ExitOnFailure(hr, "Failed to layout bundle."); for (DWORD i = 0; i < pPayloads->cPayloads; ++i) @@ -1073,7 +1076,8 @@ LExit: static HRESULT LayoutBundle( __in BURN_CACHE_CONTEXT* pContext, __in_z LPCWSTR wzExecutableName, - __in_z LPCWSTR wzUnverifiedPath + __in_z LPCWSTR wzUnverifiedPath, + __in DWORD64 qwBundleSize ) { HRESULT hr = S_OK; @@ -1107,6 +1111,8 @@ static HRESULT LayoutBundle( if (CSTR_EQUAL == nEquivalentPaths && FileExistsEx(sczDestinationPath, NULL)) { + // TODO: send Acquire and Verify messages to BA? + pContext->qwSuccessfulCacheProgress += 2 * qwBundleSize; ExitFunction1(hr = S_OK); } @@ -1128,6 +1134,12 @@ static HRESULT LayoutBundle( hr = CopyPayload(&progress, pContext->hSourceEngineFile, sczBundlePath, wzUnverifiedPath); // Error handling happens after sending complete message to BA. + // If succeeded, send 100% complete here to make sure progress was sent to the BA. + if (SUCCEEDED(hr)) + { + hr = CompleteCacheProgress(&progress, qwBundleSize); + } + UserExperienceOnCacheAcquireComplete(pContext->pUX, NULL, NULL, hr, &fRetryAcquire); if (fRetryAcquire) { @@ -1163,9 +1175,16 @@ static HRESULT LayoutBundle( fRetry = TRUE; // go back and retry acquire. } } while (S_FALSE == hr); + + if (fRetry) + { + pContext->qwSuccessfulCacheProgress -= qwBundleSize; + } } while (fRetry); LogExitOnFailure(hr, MSG_FAILED_LAYOUT_BUNDLE, "Failed to layout bundle: %ls to layout directory: %ls", sczBundlePath, pContext->wzLayoutDirectory); + pContext->qwSuccessfulCacheProgress += qwBundleSize; + LExit: ReleaseStr(sczDestinationPath); ReleaseStr(sczBundleDownloadUrl); -- cgit v1.2.3-55-g6feb From 31539e7a5baf0f75f3cd0e4764c003bb6a8310ce Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 16 Apr 2021 10:38:06 -0500 Subject: Make sure payload unverified path is not read-only during acquisition. --- src/engine/apply.cpp | 61 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index f3821769..a2ec023e 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -123,6 +123,9 @@ static HRESULT LayoutOrCacheContainerOrPayload( __in DWORD cTryAgainAttempts, __out BOOL* pfRetry ); +static HRESULT PreparePayloadDestinationPath( + __in_z LPCWSTR wzDestinationPath + ); static HRESULT CopyPayload( __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, __in HANDLE hSourceFile, @@ -1044,6 +1047,9 @@ static HRESULT ExtractContainer( BURN_PAYLOAD* pExtract = pContext->pPayloads->rgPayloads + iExtract; if (pExtract->sczUnverifiedPath && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczExtractPayloadId, -1, pExtract->sczSourcePath, -1)) { + hr = PreparePayloadDestinationPath(pExtract->sczUnverifiedPath); + ExitOnFailure(hr, "Failed to prepare payload destination path: %ls", pExtract->sczUnverifiedPath); + // TODO: Send progress when extracting stream to file. hr = ContainerStreamToFile(&context, pExtract->sczUnverifiedPath); ExitOnFailure(hr, "Failed to extract payload: %ls from container: %ls", sczExtractPayloadId, pContainer->sczId); @@ -1424,22 +1430,12 @@ LExit: return hr; } -static HRESULT CopyPayload( - __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, - __in HANDLE hSourceFile, - __in_z LPCWSTR wzSourcePath, +static HRESULT PreparePayloadDestinationPath( __in_z LPCWSTR wzDestinationPath ) { HRESULT hr = S_OK; DWORD dwFileAttributes = 0; - LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L""; - LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : L""; - HANDLE hDestinationFile = INVALID_HANDLE_VALUE; - HANDLE hSourceOpenedFile = INVALID_HANDLE_VALUE; - - DWORD dwLogId = pProgress->pContainer ? (pProgress->pPayload ? MSG_ACQUIRE_CONTAINER_PAYLOAD : MSG_ACQUIRE_CONTAINER) : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; - LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "copy", wzSourcePath); // If the destination file already exists, clear the readonly bit to avoid E_ACCESSDENIED. if (FileExistsEx(wzDestinationPath, &dwFileAttributes)) @@ -1454,6 +1450,34 @@ static HRESULT CopyPayload( } } +LExit: + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + hr = S_OK; + } + + return hr; +} + +static HRESULT CopyPayload( + __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, + __in HANDLE hSourceFile, + __in_z LPCWSTR wzSourcePath, + __in_z LPCWSTR wzDestinationPath + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L""; + LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : L""; + HANDLE hDestinationFile = INVALID_HANDLE_VALUE; + HANDLE hSourceOpenedFile = INVALID_HANDLE_VALUE; + + DWORD dwLogId = pProgress->pContainer ? (pProgress->pPayload ? MSG_ACQUIRE_CONTAINER_PAYLOAD : MSG_ACQUIRE_CONTAINER) : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; + LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "copy", wzSourcePath); + + hr = PreparePayloadDestinationPath(wzDestinationPath); + ExitOnFailure(hr, "Failed to prepare payload destination path: %ls", wzDestinationPath); + if (INVALID_HANDLE_VALUE == hSourceFile) { hSourceOpenedFile = ::CreateFileW(wzSourcePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); @@ -1503,7 +1527,6 @@ static HRESULT DownloadPayload( ) { HRESULT hr = S_OK; - DWORD dwFileAttributes = 0; LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L""; LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : L""; DOWNLOAD_SOURCE* pDownloadSource = pProgress->pContainer ? &pProgress->pContainer->downloadSource : &pProgress->pPayload->downloadSource; @@ -1515,18 +1538,8 @@ static HRESULT DownloadPayload( DWORD dwLogId = pProgress->pContainer ? (pProgress->pPayload ? MSG_ACQUIRE_CONTAINER_PAYLOAD : MSG_ACQUIRE_CONTAINER) : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "download", pDownloadSource->sczUrl); - // If the destination file already exists, clear the readonly bit to avoid E_ACCESSDENIED. - if (FileExistsEx(wzDestinationPath, &dwFileAttributes)) - { - if (FILE_ATTRIBUTE_READONLY & dwFileAttributes) - { - dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY; - if (!::SetFileAttributes(wzDestinationPath, dwFileAttributes)) - { - ExitWithLastError(hr, "Failed to clear readonly bit on payload destination path: %ls", wzDestinationPath); - } - } - } + hr = PreparePayloadDestinationPath(wzDestinationPath); + ExitOnFailure(hr, "Failed to prepare payload destination path: %ls", wzDestinationPath); cacheCallback.pfnProgress = CacheProgressRoutine; cacheCallback.pfnCancel = NULL; // TODO: set this -- cgit v1.2.3-55-g6feb From acf86637a6350d269e1ae1aa907e38f5138a0fa9 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 16 Apr 2021 10:43:21 -0500 Subject: Add OnCacheVerifyProgress, though currently it only reports at the end. --- .../inc/BootstrapperApplication.h | 17 ++++ src/engine/apply.cpp | 92 ++++++++++++++-------- src/engine/userexperience.cpp | 34 ++++++++ src/engine/userexperience.h | 8 ++ 4 files changed, 118 insertions(+), 33 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h index cf330c29..9fb6ffff 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -149,6 +149,7 @@ enum BOOTSTRAPPER_APPLICATION_MESSAGE BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMRESTOREPOINTCOMPLETE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDPACKAGE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANFORWARDCOMPATIBLEBUNDLE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYPROGRESS, }; enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION @@ -454,6 +455,22 @@ struct BA_ONCACHEVERIFYCOMPLETE_RESULTS BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action; }; +struct BA_ONCACHEVERIFYPROGRESS_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageOrContainerId; + LPCWSTR wzPayloadId; + DWORD64 dw64Progress; + DWORD64 dw64Total; + DWORD dwOverallPercentage; +}; + +struct BA_ONCACHEVERIFYPROGRESS_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + struct BA_ONCOMMITMSITRANSACTIONBEGIN_ARGS { DWORD cbSize; diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index a2ec023e..6d1743e2 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -11,6 +11,12 @@ const DWORD BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS = 2; +enum BURN_CACHE_PROGRESS_TYPE +{ + BURN_CACHE_PROGRESS_TYPE_ACQUIRE, + BURN_CACHE_PROGRESS_TYPE_VERIFY, +}; + // structs typedef struct _BURN_CACHE_CONTEXT @@ -29,16 +35,17 @@ typedef struct _BURN_CACHE_CONTEXT LPWSTR sczLastUsedFolderCandidate; } BURN_CACHE_CONTEXT; -typedef struct _BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT +typedef struct _BURN_CACHE_PROGRESS_CONTEXT { BURN_CACHE_CONTEXT* pCacheContext; + BURN_CACHE_PROGRESS_TYPE type; BURN_CONTAINER* pContainer; BURN_PACKAGE* pPackage; BURN_PAYLOAD* pPayload; BOOL fCancel; HRESULT hrError; -} BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT; +} BURN_CACHE_PROGRESS_CONTEXT; typedef struct _BURN_EXECUTE_CONTEXT { @@ -111,7 +118,7 @@ static HRESULT ApplyAcquireContainerOrPayload( __in_opt BURN_PAYLOAD* pPayload ); static HRESULT AcquireContainerOrPayload( - __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, + __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, __out BOOL* pfRetry ); static HRESULT LayoutOrCacheContainerOrPayload( @@ -127,17 +134,17 @@ static HRESULT PreparePayloadDestinationPath( __in_z LPCWSTR wzDestinationPath ); static HRESULT CopyPayload( - __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, + __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, __in HANDLE hSourceFile, __in_z LPCWSTR wzSourcePath, __in_z LPCWSTR wzDestinationPath ); static HRESULT DownloadPayload( - __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, + __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, __in_z LPCWSTR wzDestinationPath ); static HRESULT CompleteCacheProgress( - __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pContext, + __in BURN_CACHE_PROGRESS_CONTEXT* pContext, __in DWORD64 qwFileSize ); static DWORD CALLBACK CacheProgressRoutine( @@ -921,13 +928,6 @@ static HRESULT ApplyLayoutContainer( hr = LayoutOrCacheContainerOrPayload(pContext, pContainer, NULL, NULL, TRUE, cTryAgainAttempts, &fRetry); if (SUCCEEDED(hr)) { - if (pContext->sczLastUsedFolderCandidate) - { - // We successfully copied from a source location, set that as the last used source. - CacheSetLastUsedSource(pContext->pVariables, pContext->sczLastUsedFolderCandidate, pContainer->sczFilePath); - } - - pContext->qwSuccessfulCacheProgress += pContainer->qwFileSize; break; } else @@ -988,13 +988,6 @@ static HRESULT ApplyProcessPayload( hr = LayoutOrCacheContainerOrPayload(pContext, NULL, pPackage, pPayload, FALSE, cTryAgainAttempts, &fRetry); if (SUCCEEDED(hr)) { - if (pContext->sczLastUsedFolderCandidate) - { - // We successfully copied from a source location, set that as the last used source. - CacheSetLastUsedSource(pContext->pVariables, pContext->sczLastUsedFolderCandidate, pPayload->sczFilePath); - } - - pContext->qwSuccessfulCacheProgress += pPayload->qwFileSize; break; } else @@ -1092,10 +1085,12 @@ static HRESULT LayoutBundle( LPWSTR sczDestinationPath = NULL; int nEquivalentPaths = 0; BOOTSTRAPPER_CACHE_OPERATION cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE; - BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { }; + BURN_CACHE_PROGRESS_CONTEXT progress = { }; BOOL fRetry = FALSE; BOOL fRetryAcquire = FALSE; + progress.pCacheContext = pContext; + hr = VariableGetString(pContext->pVariables, BURN_BUNDLE_SOURCE_PROCESS_PATH, &sczBundlePath); if (FAILED(hr)) { @@ -1122,12 +1117,11 @@ static HRESULT LayoutBundle( ExitFunction1(hr = S_OK); } - progress.pCacheContext = pContext; - do { hr = S_OK; fRetry = FALSE; + progress.type = BURN_CACHE_PROGRESS_TYPE_ACQUIRE; for (;;) { @@ -1156,6 +1150,8 @@ static HRESULT LayoutBundle( break; } + progress.type = BURN_CACHE_PROGRESS_TYPE_VERIFY; + do { hr = UserExperienceOnCacheVerifyBegin(pContext->pUX, NULL, NULL); @@ -1170,6 +1166,11 @@ static HRESULT LayoutBundle( hr = CacheLayoutBundle(wzExecutableName, pContext->wzLayoutDirectory, wzUnverifiedPath); } + if (SUCCEEDED(hr)) + { + hr = CompleteCacheProgress(&progress, qwBundleSize); + } + BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE; UserExperienceOnCacheVerifyComplete(pContext->pUX, NULL, NULL, hr, &action); if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action) @@ -1189,8 +1190,6 @@ static HRESULT LayoutBundle( } while (fRetry); LogExitOnFailure(hr, MSG_FAILED_LAYOUT_BUNDLE, "Failed to layout bundle: %ls to layout directory: %ls", sczBundlePath, pContext->wzLayoutDirectory); - pContext->qwSuccessfulCacheProgress += qwBundleSize; - LExit: ReleaseStr(sczDestinationPath); ReleaseStr(sczBundleDownloadUrl); @@ -1209,10 +1208,11 @@ static HRESULT ApplyAcquireContainerOrPayload( AssertSz(pContainer || pPayload, "Must provide a container or a payload."); HRESULT hr = S_OK; - BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { }; + BURN_CACHE_PROGRESS_CONTEXT progress = { }; BOOL fRetry = FALSE; progress.pCacheContext = pContext; + progress.type = BURN_CACHE_PROGRESS_TYPE_ACQUIRE; progress.pContainer = pContainer; progress.pPackage = pPackage; progress.pPayload = pPayload; @@ -1235,7 +1235,7 @@ LExit: } static HRESULT AcquireContainerOrPayload( - __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, + __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, __out BOOL* pfRetry ) { @@ -1373,6 +1373,7 @@ static HRESULT LayoutOrCacheContainerOrPayload( LPCWSTR wzUnverifiedPath = pContainer ? pContainer->sczUnverifiedPath : pPayload->sczUnverifiedPath; LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : L""; BOOL fCanAffectRegistration = FALSE; + BURN_CACHE_PROGRESS_CONTEXT progress = { }; if (!pContext->wzLayoutDirectory) { @@ -1383,6 +1384,11 @@ static HRESULT LayoutOrCacheContainerOrPayload( } *pfRetry = FALSE; + progress.pCacheContext = pContext; + progress.type = BURN_CACHE_PROGRESS_TYPE_VERIFY; + progress.pContainer = pContainer; + progress.pPackage = pPackage; + progress.pPayload = pPayload; do { @@ -1409,6 +1415,11 @@ static HRESULT LayoutOrCacheContainerOrPayload( hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, wzUnverifiedPath, fMove); } + if (SUCCEEDED(hr)) + { + hr = CompleteCacheProgress(&progress, pContainer ? pContainer->qwFileSize : pPayload->qwFileSize); + } + if (SUCCEEDED(hr) && fCanAffectRegistration) { pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; @@ -1460,7 +1471,7 @@ LExit: } static HRESULT CopyPayload( - __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, + __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, __in HANDLE hSourceFile, __in_z LPCWSTR wzSourcePath, __in_z LPCWSTR wzDestinationPath @@ -1522,7 +1533,7 @@ LExit: } static HRESULT DownloadPayload( - __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, + __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, __in_z LPCWSTR wzDestinationPath ) { @@ -1618,7 +1629,7 @@ LExit: } static HRESULT CompleteCacheProgress( - __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pContext, + __in BURN_CACHE_PROGRESS_CONTEXT* pContext, __in DWORD64 qwFileSize ) { @@ -1634,6 +1645,12 @@ static HRESULT CompleteCacheProgress( if (PROGRESS_CONTINUE == dwResult) { pContext->pCacheContext->qwSuccessfulCacheProgress += qwFileSize; + + if (BURN_CACHE_PROGRESS_TYPE_VERIFY == pContext->type && pContext->pCacheContext->sczLastUsedFolderCandidate) + { + // We successfully copied from a source location, set that as the last used source. + CacheSetLastUsedSource(pContext->pCacheContext->pVariables, pContext->pCacheContext->sczLastUsedFolderCandidate, pContext->pContainer ? pContext->pContainer->sczFilePath : pContext->pPayload->sczFilePath); + } } else if (PROGRESS_CANCEL == dwResult) { @@ -1664,7 +1681,7 @@ static DWORD CALLBACK CacheProgressRoutine( { HRESULT hr = S_OK; DWORD dwResult = PROGRESS_CONTINUE; - BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress = static_cast(lpData); + BURN_CACHE_PROGRESS_CONTEXT* pProgress = static_cast(lpData); LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : NULL; LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : NULL; DWORD64 qwCacheProgress = pProgress->pCacheContext->qwSuccessfulCacheProgress + TotalBytesTransferred.QuadPart; @@ -1675,8 +1692,17 @@ static DWORD CALLBACK CacheProgressRoutine( } DWORD dwOverallPercentage = pProgress->pCacheContext->qwTotalCacheSize ? static_cast(qwCacheProgress * 100 / pProgress->pCacheContext->qwTotalCacheSize) : 0; - hr = UserExperienceOnCacheAcquireProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); - ExitOnRootFailure(hr, "BA aborted acquire of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); + switch (pProgress->type) + { + case BURN_CACHE_PROGRESS_TYPE_ACQUIRE: + hr = UserExperienceOnCacheAcquireProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); + ExitOnRootFailure(hr, "BA aborted acquire of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); + break; + case BURN_CACHE_PROGRESS_TYPE_VERIFY: + hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); + ExitOnRootFailure(hr, "BA aborted verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); + break; + } LExit: if (HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr) diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index f6ae1491..b42eb5a7 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -732,6 +732,40 @@ LExit: return hr; } +EXTERN_C BAAPI UserExperienceOnCacheVerifyProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in DWORD dwOverallPercentage + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEVERIFYPROGRESS_ARGS args = { }; + BA_ONCACHEVERIFYPROGRESS_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageOrContainerId = wzPackageOrContainerId; + args.wzPayloadId = wzPayloadId; + args.dw64Progress = dw64Progress; + args.dw64Total = dw64Total; + args.dwOverallPercentage = dwOverallPercentage; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYPROGRESS, &args, &results); + ExitOnFailure(hr, "BA OnCacheVerifyProgress failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + EXTERN_C BAAPI UserExperienceOnCommitMsiTransactionBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in LPCWSTR wzTransactionId diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index dd1fb086..e09c9ec1 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -189,6 +189,14 @@ BAAPI UserExperienceOnCacheVerifyComplete( __in HRESULT hrStatus, __inout BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION* pAction ); +BAAPI UserExperienceOnCacheVerifyProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in DWORD dwOverallPercentage + ); BAAPI UserExperienceOnCommitMsiTransactionBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in LPCWSTR wzTransactionId -- cgit v1.2.3-55-g6feb From e09a129ee276457d02d19a6444657cf3a2c73905 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 16 Apr 2021 10:44:11 -0500 Subject: UX payloads are never external. --- src/engine/apply.cpp | 2 +- src/engine/cache.cpp | 16 ---------------- src/engine/cache.h | 1 - src/engine/elevation.cpp | 6 ++---- src/engine/registration.cpp | 3 +-- src/engine/registration.h | 1 - src/test/BurnUnitTest/RegistrationTest.cpp | 14 +++++++------- 7 files changed, 11 insertions(+), 32 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 6d1743e2..5826f513 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -388,7 +388,7 @@ extern "C" HRESULT ApplyRegister( } else { - hr = RegistrationSessionBegin(sczEngineWorkingPath, &pEngineState->registration, &pEngineState->variables, &pEngineState->userExperience, pEngineState->plan.dwRegistrationOperations, pEngineState->plan.dependencyRegistrationAction, pEngineState->plan.qwEstimatedSize); + hr = RegistrationSessionBegin(sczEngineWorkingPath, &pEngineState->registration, &pEngineState->variables, pEngineState->plan.dwRegistrationOperations, pEngineState->plan.dependencyRegistrationAction, pEngineState->plan.qwEstimatedSize); ExitOnFailure(hr, "Failed to begin registration session."); } } diff --git a/src/engine/cache.cpp b/src/engine/cache.cpp index 84b7f131..2299d26d 100644 --- a/src/engine/cache.cpp +++ b/src/engine/cache.cpp @@ -776,7 +776,6 @@ extern "C" HRESULT CacheCompleteBundle( __in BOOL fPerMachine, __in_z LPCWSTR wzExecutableName, __in_z LPCWSTR wzBundleId, - __in BURN_PAYLOADS* pUxPayloads, __in_z LPCWSTR wzSourceBundlePath #ifdef DEBUG , __in_z LPCWSTR wzExecutablePath @@ -824,21 +823,6 @@ extern "C" HRESULT CacheCompleteBundle( hr = PathGetDirectory(wzSourceBundlePath, &sczSourceDirectory); ExitOnFailure(hr, "Failed to get directory from engine working path: %ls", wzSourceBundlePath); - // Cache external UX payloads to completed path. - for (DWORD i = 0; i < pUxPayloads->cPayloads; ++i) - { - BURN_PAYLOAD* pPayload = &pUxPayloads->rgPayloads[i]; - - if (BURN_PAYLOAD_PACKAGING_EXTERNAL == pPayload->packaging) - { - hr = PathConcat(sczSourceDirectory, pPayload->sczSourcePath, &sczPayloadSourcePath); - ExitOnFailure(hr, "Failed to build payload source path."); - - hr = CacheCompletePayload(fPerMachine, pPayload, wzBundleId, sczPayloadSourcePath, FALSE); - ExitOnFailure(hr, "Failed to complete the cache of payload: %ls", pPayload->sczKey); - } - } - LExit: ReleaseStr(sczPayloadSourcePath); ReleaseStr(sczSourceDirectory); diff --git a/src/engine/cache.h b/src/engine/cache.h index eb964f58..cf062a85 100644 --- a/src/engine/cache.h +++ b/src/engine/cache.h @@ -101,7 +101,6 @@ HRESULT CacheCompleteBundle( __in BOOL fPerMachine, __in_z LPCWSTR wzExecutableName, __in_z LPCWSTR wzBundleId, - __in BURN_PAYLOADS* pUxPayloads, __in_z LPCWSTR wzSourceBundlePath #ifdef DEBUG , __in_z LPCWSTR wzExecutablePath diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp index 82d5a9b1..d37907cf 100644 --- a/src/engine/elevation.cpp +++ b/src/engine/elevation.cpp @@ -143,7 +143,6 @@ static HRESULT OnApplyUninitialize( static HRESULT OnSessionBegin( __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables, - __in BURN_USER_EXPERIENCE* pUserExperience, __in BYTE* pbData, __in DWORD cbData ); @@ -1643,7 +1642,7 @@ static HRESULT ProcessElevatedChildMessage( break; case BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN: - hrResult = OnSessionBegin(pContext->pRegistration, pContext->pVariables, pContext->pUserExperience, (BYTE*)pMsg->pvData, pMsg->cbData); + hrResult = OnSessionBegin(pContext->pRegistration, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); break; case BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME: @@ -1895,7 +1894,6 @@ static HRESULT OnApplyUninitialize( static HRESULT OnSessionBegin( __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables, - __in BURN_USER_EXPERIENCE* pUserExperience, __in BYTE* pbData, __in DWORD cbData ) @@ -1930,7 +1928,7 @@ static HRESULT OnSessionBegin( ExitOnFailure(hr, "Failed to read variables."); // Begin session in per-machine process. - hr = RegistrationSessionBegin(sczEngineWorkingPath, pRegistration, pVariables, pUserExperience, dwRegistrationOperations, (BURN_DEPENDENCY_REGISTRATION_ACTION)dwDependencyRegistrationAction, qwEstimatedSize); + hr = RegistrationSessionBegin(sczEngineWorkingPath, pRegistration, pVariables, dwRegistrationOperations, (BURN_DEPENDENCY_REGISTRATION_ACTION)dwDependencyRegistrationAction, qwEstimatedSize); ExitOnFailure(hr, "Failed to begin registration session."); LExit: diff --git a/src/engine/registration.cpp b/src/engine/registration.cpp index 7435f292..19da543c 100644 --- a/src/engine/registration.cpp +++ b/src/engine/registration.cpp @@ -593,7 +593,6 @@ extern "C" HRESULT RegistrationSessionBegin( __in_z LPCWSTR wzEngineWorkingPath, __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables, - __in BURN_USER_EXPERIENCE* pUserExperience, __in DWORD dwRegistrationOptions, __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction, __in DWORD64 qwEstimatedSize @@ -609,7 +608,7 @@ extern "C" HRESULT RegistrationSessionBegin( // Cache bundle executable. if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE) { - hr = CacheCompleteBundle(pRegistration->fPerMachine, pRegistration->sczExecutableName, pRegistration->sczId, &pUserExperience->payloads, wzEngineWorkingPath + hr = CacheCompleteBundle(pRegistration->fPerMachine, pRegistration->sczExecutableName, pRegistration->sczId, wzEngineWorkingPath #ifdef DEBUG , pRegistration->sczCacheExecutablePath #endif diff --git a/src/engine/registration.h b/src/engine/registration.h index af1b42e4..6d8a6d2a 100644 --- a/src/engine/registration.h +++ b/src/engine/registration.h @@ -188,7 +188,6 @@ HRESULT RegistrationSessionBegin( __in_z LPCWSTR wzEngineWorkingPath, __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables, - __in BURN_USER_EXPERIENCE* pUserExperience, __in DWORD dwRegistrationOptions, __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction, __in DWORD64 qwEstimatedSize diff --git a/src/test/BurnUnitTest/RegistrationTest.cpp b/src/test/BurnUnitTest/RegistrationTest.cpp index 2cb66c3f..7b126f61 100644 --- a/src/test/BurnUnitTest/RegistrationTest.cpp +++ b/src/test/BurnUnitTest/RegistrationTest.cpp @@ -112,7 +112,7 @@ namespace Bootstrapper TestThrowOnFailure(hr, L"Failed to get current process path."); // write registration - hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, &userExperience, BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE | BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE | BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); TestThrowOnFailure(hr, L"Failed to register bundle."); // verify that registration was created @@ -205,7 +205,7 @@ namespace Bootstrapper // // write registration - hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, &userExperience, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); TestThrowOnFailure(hr, L"Failed to register bundle."); // verify that registration was created @@ -226,7 +226,7 @@ namespace Bootstrapper // // write registration - hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, &userExperience, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, 0); + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, 0); TestThrowOnFailure(hr, L"Failed to register bundle."); // verify that registration was updated @@ -316,7 +316,7 @@ namespace Bootstrapper // // write registration - hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, &userExperience, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); TestThrowOnFailure(hr, L"Failed to register bundle."); // verify that registration was created @@ -427,7 +427,7 @@ namespace Bootstrapper // // write registration - hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, &userExperience, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); TestThrowOnFailure(hr, L"Failed to register bundle."); // verify that registration was created @@ -460,7 +460,7 @@ namespace Bootstrapper // // write registration - hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, &userExperience, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, 0); + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, 0); TestThrowOnFailure(hr, L"Failed to register bundle."); // verify that registration was updated @@ -560,7 +560,7 @@ namespace Bootstrapper Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_NONE, (int)resumeType); // begin session - hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, &userExperience, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); TestThrowOnFailure(hr, L"Failed to register bundle."); hr = RegistrationSaveState(®istration, rgbData, sizeof(rgbData)); -- cgit v1.2.3-55-g6feb From d7b0329e16ba9cae4a33970e26591ae5f1d98f0d Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 16 Apr 2021 10:47:54 -0500 Subject: Add OnCacheContainerOrPayloadVerify* for a file already in the cache. --- .../inc/BootstrapperApplication.h | 45 +++++++++++ src/engine/apply.cpp | 85 ++++++++++++++++++--- src/engine/userexperience.cpp | 87 ++++++++++++++++++++++ src/engine/userexperience.h | 19 +++++ 4 files changed, 227 insertions(+), 9 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h index 9fb6ffff..edb981a9 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -150,6 +150,9 @@ enum BOOTSTRAPPER_APPLICATION_MESSAGE BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDPACKAGE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANFORWARDCOMPATIBLEBUNDLE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYPROGRESS, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECONTAINERORPAYLOADVERIFYBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECONTAINERORPAYLOADVERIFYCOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECONTAINERORPAYLOADVERIFYPROGRESS, }; enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION @@ -399,6 +402,48 @@ struct BA_ONCACHECOMPLETE_RESULTS DWORD cbSize; }; +struct BA_ONCACHECONTAINERORPAYLOADVERIFYBEGIN_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageOrContainerId; + LPCWSTR wzPayloadId; +}; + +struct BA_ONCACHECONTAINERORPAYLOADVERIFYBEGIN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONCACHECONTAINERORPAYLOADVERIFYCOMPLETE_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageOrContainerId; + LPCWSTR wzPayloadId; + HRESULT hrStatus; +}; + +struct BA_ONCACHECONTAINERORPAYLOADVERIFYCOMPLETE_RESULTS +{ + DWORD cbSize; +}; + +struct BA_ONCACHECONTAINERORPAYLOADVERIFYPROGRESS_ARGS +{ + DWORD cbSize; + LPCWSTR wzPackageOrContainerId; + LPCWSTR wzPayloadId; + DWORD64 dw64Progress; + DWORD64 dw64Total; + DWORD dwOverallPercentage; +}; + +struct BA_ONCACHECONTAINERORPAYLOADVERIFYPROGRESS_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + struct BA_ONCACHEPACKAGEBEGIN_ARGS { DWORD cbSize; diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 5826f513..f40b3841 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -15,6 +15,7 @@ enum BURN_CACHE_PROGRESS_TYPE { BURN_CACHE_PROGRESS_TYPE_ACQUIRE, BURN_CACHE_PROGRESS_TYPE_VERIFY, + BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY, }; // structs @@ -101,6 +102,12 @@ static HRESULT ApplyProcessPayload( __in_opt BURN_PACKAGE* pPackage, __in BURN_PAYLOAD* pPayload ); +static HRESULT ApplyCacheVerifyContainerOrPayload( + __in BURN_CACHE_CONTEXT* pContext, + __in_opt BURN_CONTAINER* pContainer, + __in_opt BURN_PACKAGE* pPackage, + __in_opt BURN_PAYLOAD* pPayload + ); static HRESULT ExtractContainer( __in BURN_CACHE_CONTEXT* pContext, __in BURN_CONTAINER* pContainer @@ -910,11 +917,9 @@ static HRESULT ApplyLayoutContainer( Assert(!pContainer->fAttached); - hr = CacheVerifyContainer(pContainer, pContext->wzLayoutDirectory); + hr = ApplyCacheVerifyContainerOrPayload(pContext, pContainer, NULL, NULL); if (SUCCEEDED(hr)) { - // TODO: send Acquire and Verify messages to BA? - pContext->qwSuccessfulCacheProgress += pContainer->qwFileSize * 2; ExitFunction(); } @@ -969,11 +974,9 @@ static HRESULT ApplyProcessPayload( ExitFunction(); } - hr = CacheVerifyPayload(pPayload, pContext->wzLayoutDirectory ? pContext->wzLayoutDirectory : pPackage->sczCacheFolder); + hr = ApplyCacheVerifyContainerOrPayload(pContext, NULL, pPackage, pPayload); if (SUCCEEDED(hr)) { - // TODO: send Acquire and Verify messages to BA? - pContext->qwSuccessfulCacheProgress += pPayload->qwFileSize * 2; ExitFunction(); } @@ -1012,6 +1015,54 @@ LExit: return hr; } +static HRESULT ApplyCacheVerifyContainerOrPayload( + __in BURN_CACHE_CONTEXT* pContext, + __in_opt BURN_CONTAINER* pContainer, + __in_opt BURN_PACKAGE* pPackage, + __in_opt BURN_PAYLOAD* pPayload + ) +{ + AssertSz(pContainer || pPayload, "Must provide a container or a payload."); + + HRESULT hr = S_OK; + BURN_CACHE_PROGRESS_CONTEXT progress = { }; + LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : NULL; + LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : NULL; + + progress.pCacheContext = pContext; + progress.pContainer = pContainer; + progress.pPackage = pPackage; + progress.pPayload = pPayload; + progress.type = BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY; + + hr = UserExperienceOnCacheContainerOrPayloadVerifyBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId); + ExitOnRootFailure(hr, "BA aborted cache container or payload verify begin."); + + if (pContainer) + { + hr = CacheVerifyContainer(pContainer, pContext->wzLayoutDirectory); + } + else + { + hr = CacheVerifyPayload(pPayload, pContext->wzLayoutDirectory ? pContext->wzLayoutDirectory : pPackage->sczCacheFolder); + } + + // This was best effort to avoid acquiring the container or payload. + if (FAILED(hr)) + { + ExitFunction(); + } + + pContext->qwSuccessfulCacheProgress += pContainer ? pContainer->qwFileSize : pPayload->qwFileSize; + + hr = CompleteCacheProgress(&progress, pContainer ? pContainer->qwFileSize : pPayload->qwFileSize); + +LExit: + UserExperienceOnCacheContainerOrPayloadVerifyComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr); + + return hr; +} + static HRESULT ExtractContainer( __in BURN_CACHE_CONTEXT* pContext, __in BURN_CONTAINER* pContainer @@ -1112,9 +1163,21 @@ static HRESULT LayoutBundle( if (CSTR_EQUAL == nEquivalentPaths && FileExistsEx(sczDestinationPath, NULL)) { - // TODO: send Acquire and Verify messages to BA? - pContext->qwSuccessfulCacheProgress += 2 * qwBundleSize; - ExitFunction1(hr = S_OK); + hr = UserExperienceOnCacheContainerOrPayloadVerifyBegin(pContext->pUX, NULL, NULL); + if (FAILED(hr)) + { + UserExperienceOnCacheContainerOrPayloadVerifyComplete(pContext->pUX, NULL, NULL, hr); + ExitOnRootFailure(hr, "BA aborted cache payload verify begin."); + } + + pContext->qwSuccessfulCacheProgress += qwBundleSize; + + progress.type = BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY; + hr = CompleteCacheProgress(&progress, qwBundleSize); + + UserExperienceOnCacheContainerOrPayloadVerifyComplete(pContext->pUX, NULL, NULL, hr); + + ExitFunction(); } do @@ -1702,6 +1765,10 @@ static DWORD CALLBACK CacheProgressRoutine( hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); ExitOnRootFailure(hr, "BA aborted verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); break; + case BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY: + hr = UserExperienceOnCacheContainerOrPayloadVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); + ExitOnRootFailure(hr, "BA aborted container or payload verify: %ls", wzPayloadId); + break; } LExit: diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index b42eb5a7..02c67fc5 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -610,6 +610,93 @@ LExit: return hr; } +EXTERN_C BAAPI UserExperienceOnCacheContainerOrPayloadVerifyBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId + ) +{ + HRESULT hr = S_OK; + BA_ONCACHECONTAINERORPAYLOADVERIFYBEGIN_ARGS args = { }; + BA_ONCACHECONTAINERORPAYLOADVERIFYBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageOrContainerId = wzPackageOrContainerId; + args.wzPayloadId = wzPayloadId; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECONTAINERORPAYLOADVERIFYBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnCacheContainerOrPayloadVerifyBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCacheContainerOrPayloadVerifyComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONCACHECONTAINERORPAYLOADVERIFYCOMPLETE_ARGS args = { }; + BA_ONCACHECONTAINERORPAYLOADVERIFYCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageOrContainerId = wzPackageOrContainerId; + args.wzPayloadId = wzPayloadId; + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECONTAINERORPAYLOADVERIFYCOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnCacheContainerOrPayloadVerifyComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCacheContainerOrPayloadVerifyProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in DWORD dwOverallPercentage + ) +{ + HRESULT hr = S_OK; + BA_ONCACHECONTAINERORPAYLOADVERIFYPROGRESS_ARGS args = { }; + BA_ONCACHECONTAINERORPAYLOADVERIFYPROGRESS_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageOrContainerId = wzPackageOrContainerId; + args.wzPayloadId = wzPayloadId; + args.dw64Progress = dw64Progress; + args.dw64Total = dw64Total; + args.dwOverallPercentage = dwOverallPercentage; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECONTAINERORPAYLOADVERIFYPROGRESS, &args, &results); + ExitOnFailure(hr, "BA OnCacheContainerOrPayloadVerifyProgress failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + EXTERN_C BAAPI UserExperienceOnCachePackageBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index e09c9ec1..d3dfb810 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -165,6 +165,25 @@ BAAPI UserExperienceOnCacheComplete( __in BURN_USER_EXPERIENCE* pUserExperience, __in HRESULT hrStatus ); +BAAPI UserExperienceOnCacheContainerOrPayloadVerifyBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId + ); +BAAPI UserExperienceOnCacheContainerOrPayloadVerifyComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in HRESULT hrStatus + ); +BAAPI UserExperienceOnCacheContainerOrPayloadVerifyProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in DWORD dwOverallPercentage + ); BAAPI UserExperienceOnCachePackageBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, -- cgit v1.2.3-55-g6feb From 8c77de737aaea1b4857c724c730446bca8da2dd0 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 16 Apr 2021 10:48:38 -0500 Subject: Elevate for CacheVerifyContainer/Payload. --- src/engine/apply.cpp | 15 +++--- src/engine/cache.cpp | 68 ++++++++++++++++-------- src/engine/elevation.cpp | 136 ++++++++++++++++++++++++++++++++++++++++++++++- src/engine/elevation.h | 7 +++ src/engine/engine.mc | 14 +++++ 5 files changed, 209 insertions(+), 31 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index f40b3841..0eb8a710 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -538,6 +538,9 @@ extern "C" HRESULT ApplyCache( if (!pPackage->fPerMachine && !cacheContext.wzLayoutDirectory) { + hr = CacheGetCompletedPath(FALSE, pPackage->sczCacheId, &pPackage->sczCacheFolder); + ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", pPackage->sczCacheId); + cacheContext.hPipe = INVALID_HANDLE_VALUE; } @@ -800,12 +803,6 @@ static HRESULT ApplyCachePackage( HRESULT hr = S_OK; BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION cachePackageCompleteAction = BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE; - if (!pPackage->sczCacheFolder && !pContext->wzLayoutDirectory) - { - hr = CacheGetCompletedPath(pPackage->fPerMachine, pPackage->sczCacheId, &pPackage->sczCacheFolder); - ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", pPackage->sczCacheId); - } - for (;;) { hr = UserExperienceOnCachePackageBegin(pContext->pUX, pPackage->sczId, pPackage->payloads.cPayloads, pPackage->payloads.qwTotalSize); @@ -1038,7 +1035,11 @@ static HRESULT ApplyCacheVerifyContainerOrPayload( hr = UserExperienceOnCacheContainerOrPayloadVerifyBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId); ExitOnRootFailure(hr, "BA aborted cache container or payload verify begin."); - if (pContainer) + if (INVALID_HANDLE_VALUE != pContext->hPipe) + { + hr = ElevationCacheVerifyContainerOrPayload(pContext->hPipe, pContainer, pPackage, pPayload, pContext->wzLayoutDirectory); + } + else if (pContainer) { hr = CacheVerifyContainer(pContainer, pContext->wzLayoutDirectory); } diff --git a/src/engine/cache.cpp b/src/engine/cache.cpp index 2299d26d..9aa94d1d 100644 --- a/src/engine/cache.cpp +++ b/src/engine/cache.cpp @@ -59,11 +59,13 @@ static HRESULT TransferWorkingPathToUnverifiedPath( ); static HRESULT VerifyFileAgainstContainer( __in BURN_CONTAINER* pContainer, - __in_z LPCWSTR wzVerifyPath + __in_z LPCWSTR wzVerifyPath, + __in BOOL fAlreadyCached ); static HRESULT VerifyFileAgainstPayload( __in BURN_PAYLOAD* pPayload, - __in_z LPCWSTR wzVerifyPath + __in_z LPCWSTR wzVerifyPath, + __in BOOL fAlreadyCached ); static HRESULT ResetPathPermissions( __in BOOL fPerMachine, @@ -896,19 +898,11 @@ extern "C" HRESULT CacheCompletePayload( ExitOnFailure(hr, "Failed to concat complete cached path."); // If the cached file matches what we expected, we're good. - hr = VerifyFileAgainstPayload(pPayload, sczCachedPath); + hr = VerifyFileAgainstPayload(pPayload, sczCachedPath, TRUE); if (SUCCEEDED(hr)) { - ::DecryptFileW(sczCachedPath, 0); // Let's try to make sure it's not encrypted. - LogId(REPORT_STANDARD, MSG_VERIFIED_EXISTING_PAYLOAD, pPayload->sczKey, sczCachedPath); ExitFunction(); } - else if (E_PATHNOTFOUND != hr && E_FILENOTFOUND != hr) - { - LogErrorId(hr, MSG_FAILED_VERIFY_PAYLOAD, pPayload->sczKey, sczCachedPath, NULL); - - FileEnsureDelete(sczCachedPath); // if the file existed but did not verify correctly, make it go away. - } hr = CreateUnverifiedPath(fPerMachine, pPayload->sczKey, &sczUnverifiedPayloadPath); ExitOnFailure(hr, "Failed to create unverified path."); @@ -928,14 +922,8 @@ extern "C" HRESULT CacheCompletePayload( hr = ResetPathPermissions(fPerMachine, sczUnverifiedPayloadPath); ExitOnFailure(hr, "Failed to reset permissions on unverified cached payload: %ls", pPayload->sczKey); - hr = VerifyFileAgainstPayload(pPayload, sczUnverifiedPayloadPath); - if (FAILED(hr)) - { - LogErrorId(hr, MSG_FAILED_VERIFY_PAYLOAD, pPayload->sczKey, sczUnverifiedPayloadPath, NULL); - - FileEnsureDelete(sczUnverifiedPayloadPath); // if the file did not verify correctly, make it go away. - ExitFunction(); - } + hr = VerifyFileAgainstPayload(pPayload, sczUnverifiedPayloadPath, FALSE); + LogExitOnFailure(hr, MSG_FAILED_VERIFY_PAYLOAD, "Failed to verify payload: %ls at path: %ls", pPayload->sczKey, sczUnverifiedPayloadPath, NULL); LogId(REPORT_STANDARD, MSG_VERIFIED_ACQUIRED_PAYLOAD, pPayload->sczKey, sczUnverifiedPayloadPath, fMove ? "moving" : "copying", sczCachedPath); @@ -963,7 +951,7 @@ extern "C" HRESULT CacheVerifyContainer( hr = PathConcat(wzCachedDirectory, pContainer->sczFilePath, &sczCachedPath); ExitOnFailure(hr, "Failed to concat complete cached path."); - hr = VerifyFileAgainstContainer(pContainer, sczCachedPath); + hr = VerifyFileAgainstContainer(pContainer, sczCachedPath, TRUE); LExit: ReleaseStr(sczCachedPath); @@ -982,7 +970,7 @@ extern "C" HRESULT CacheVerifyPayload( hr = PathConcat(wzCachedDirectory, pPayload->sczFilePath, &sczCachedPath); ExitOnFailure(hr, "Failed to concat complete cached path."); - hr = VerifyFileAgainstPayload(pPayload, sczCachedPath); + hr = VerifyFileAgainstPayload(pPayload, sczCachedPath, TRUE); LExit: ReleaseStr(sczCachedPath); @@ -1460,7 +1448,8 @@ LExit: static HRESULT VerifyFileAgainstContainer( __in BURN_CONTAINER* pContainer, - __in_z LPCWSTR wzVerifyPath + __in_z LPCWSTR wzVerifyPath, + __in BOOL fAlreadyCached ) { HRESULT hr = S_OK; @@ -1484,15 +1473,32 @@ static HRESULT VerifyFileAgainstContainer( ExitOnFailure(hr, "Failed to verify hash of container: %ls", pContainer->sczId); } + if (fAlreadyCached) + { + LogId(REPORT_STANDARD, MSG_VERIFIED_EXISTING_CONTAINER, pContainer->sczId, wzVerifyPath); + ::DecryptFileW(wzVerifyPath, 0); // Let's try to make sure it's not encrypted. + } + LExit: ReleaseFileHandle(hFile); + if (FAILED(hr) && E_PATHNOTFOUND != hr && E_FILENOTFOUND != hr) + { + if (fAlreadyCached) + { + LogErrorId(hr, MSG_FAILED_VERIFY_CONTAINER, pContainer->sczId, wzVerifyPath, NULL); + } + + FileEnsureDelete(wzVerifyPath); // if the file existed but did not verify correctly, make it go away. + } + return hr; } static HRESULT VerifyFileAgainstPayload( __in BURN_PAYLOAD* pPayload, - __in_z LPCWSTR wzVerifyPath + __in_z LPCWSTR wzVerifyPath, + __in BOOL fAlreadyCached ) { HRESULT hr = S_OK; @@ -1516,9 +1522,25 @@ static HRESULT VerifyFileAgainstPayload( ExitOnFailure(hr, "Failed to verify hash of payload: %ls", pPayload->sczKey); } + if (fAlreadyCached) + { + LogId(REPORT_STANDARD, MSG_VERIFIED_EXISTING_PAYLOAD, pPayload->sczKey, wzVerifyPath); + ::DecryptFileW(wzVerifyPath, 0); // Let's try to make sure it's not encrypted. + } + LExit: ReleaseFileHandle(hFile); + if (FAILED(hr) && E_PATHNOTFOUND != hr && E_FILENOTFOUND != hr) + { + if (fAlreadyCached) + { + LogErrorId(hr, MSG_FAILED_VERIFY_PAYLOAD, pPayload->sczKey, wzVerifyPath, NULL); + } + + FileEnsureDelete(wzVerifyPath); // if the file existed but did not verify correctly, make it go away. + } + return hr; } diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp index d37907cf..2dd61a81 100644 --- a/src/engine/elevation.cpp +++ b/src/engine/elevation.cpp @@ -16,6 +16,7 @@ typedef enum _BURN_ELEVATION_MESSAGE_TYPE BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE, BURN_ELEVATION_MESSAGE_TYPE_LAYOUT_BUNDLE, BURN_ELEVATION_MESSAGE_TYPE_CACHE_OR_LAYOUT_CONTAINER_OR_PAYLOAD, + BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_CONTAINER_OR_PAYLOAD, BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP, BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE, @@ -176,6 +177,13 @@ static HRESULT OnCacheOrLayoutContainerOrPayload( __in BYTE* pbData, __in DWORD cbData ); +static HRESULT OnCacheVerifyContainerOrPayload( + __in BURN_CONTAINERS* pContainers, + __in BURN_PACKAGES* pPackages, + __in BURN_PAYLOADS* pPayloads, + __in BYTE* pbData, + __in DWORD cbData + ); static void OnCacheCleanup( __in_z LPCWSTR wzBundleId ); @@ -657,6 +665,44 @@ LExit: return hr; } +extern "C" HRESULT ElevationCacheVerifyContainerOrPayload( + __in HANDLE hPipe, + __in_opt BURN_CONTAINER* pContainer, + __in_opt BURN_PACKAGE* pPackage, + __in_opt BURN_PAYLOAD* pPayload, + __in_z_opt LPCWSTR wzLayoutDirectory + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + + // serialize message data + hr = BuffWriteString(&pbData, &cbData, pContainer ? pContainer->sczId : NULL); + ExitOnFailure(hr, "Failed to write container id to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pPackage ? pPackage->sczId : NULL); + ExitOnFailure(hr, "Failed to write package id to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pPayload ? pPayload->sczKey : NULL); + ExitOnFailure(hr, "Failed to write payload id to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, wzLayoutDirectory); + ExitOnFailure(hr, "Failed to write layout directory to message buffer."); + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_CONTAINER_OR_PAYLOAD, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_CONTAINER_OR_PAYLOAD message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + /******************************************************************* ElevationCacheCleanup - @@ -1724,6 +1770,10 @@ static HRESULT ProcessElevatedChildCacheMessage( hrResult = OnCacheOrLayoutContainerOrPayload(pContext->pContainers, pContext->pPackages, pContext->pPayloads, (BYTE*)pMsg->pvData, pMsg->cbData); break; + case BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_CONTAINER_OR_PAYLOAD: + hrResult = OnCacheVerifyContainerOrPayload(pContext->pContainers, pContext->pPackages, pContext->pPayloads, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + case BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP: OnCacheCleanup(pContext->pRegistration->sczId); hrResult = S_OK; @@ -2062,7 +2112,7 @@ static HRESULT OnCacheOrLayoutContainerOrPayload( // Deserialize message data. hr = BuffReadString(pbData, cbData, &iData, &scz); - ExitOnFailure(hr, "Failed to read package id."); + ExitOnFailure(hr, "Failed to read container id."); if (scz && *scz) { @@ -2135,6 +2185,90 @@ LExit: return hr; } +static HRESULT OnCacheVerifyContainerOrPayload( + __in BURN_CONTAINERS* pContainers, + __in BURN_PACKAGES* pPackages, + __in BURN_PAYLOADS* pPayloads, + __in BYTE* pbData, + __in DWORD cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR scz = NULL; + BURN_CONTAINER* pContainer = NULL; + BURN_PACKAGE* pPackage = NULL; + BURN_PAYLOAD* pPayload = NULL; + LPWSTR sczCacheDirectory = NULL; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &scz); + ExitOnFailure(hr, "Failed to read container id."); + + if (scz && *scz) + { + hr = ContainerFindById(pContainers, scz, &pContainer); + ExitOnFailure(hr, "Failed to find container: %ls", scz); + } + + hr = BuffReadString(pbData, cbData, &iData, &scz); + ExitOnFailure(hr, "Failed to read package id."); + + if (scz && *scz) + { + hr = PackageFindById(pPackages, scz, &pPackage); + ExitOnFailure(hr, "Failed to find package: %ls", scz); + } + + hr = BuffReadString(pbData, cbData, &iData, &scz); + ExitOnFailure(hr, "Failed to read payload id."); + + if (scz && *scz) + { + hr = PayloadFindById(pPayloads, scz, &pPayload); + ExitOnFailure(hr, "Failed to find payload: %ls", scz); + } + + hr = BuffReadString(pbData, cbData, &iData, &sczCacheDirectory); + ExitOnFailure(hr, "Failed to read layout directory."); + + if (!sczCacheDirectory || !*sczCacheDirectory) + { + if (!pPackage) + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid data passed to cache verify payload."); + } + + hr = CacheGetCompletedPath(TRUE, pPackage->sczCacheId, &sczCacheDirectory); + ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", pPackage->sczCacheId); + } + + if (pContainer) + { + Assert(!pPackage); + Assert(!pPayload); + + hr = CacheVerifyContainer(pContainer, sczCacheDirectory); + } + else if (pPayload) + { + hr = CacheVerifyPayload(pPayload, sczCacheDirectory); + } + else + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid data passed to cache or layout payload."); + } + // Nothing should be logged on failure. + +LExit: + ReleaseStr(sczCacheDirectory); + ReleaseStr(scz); + + return hr; +} + static void OnCacheCleanup( __in_z LPCWSTR wzBundleId ) diff --git a/src/engine/elevation.h b/src/engine/elevation.h index 9ce8cef9..da67e3f3 100644 --- a/src/engine/elevation.h +++ b/src/engine/elevation.h @@ -64,6 +64,13 @@ HRESULT ElevationCacheOrLayoutContainerOrPayload( __in_z LPCWSTR wzUnverifiedPath, __in BOOL fMove ); +HRESULT ElevationCacheVerifyContainerOrPayload( + __in HANDLE hPipe, + __in_opt BURN_CONTAINER* pContainer, + __in_opt BURN_PACKAGE* pPackage, + __in_opt BURN_PAYLOAD* pPayload, + __in_z_opt LPCWSTR wzLayoutDirectory + ); HRESULT ElevationCacheCleanup( __in HANDLE hPipe ); diff --git a/src/engine/engine.mc b/src/engine/engine.mc index 0e19d3bb..6e0695bc 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -478,6 +478,13 @@ Language=English Acquired payload: %1!ls! to working path: %2!ls! from: %4!ls!. . +MessageId=303 +Severity=Success +SymbolicName=MSG_VERIFIED_EXISTING_CONTAINER +Language=English +Verified existing container: %1!ls! at path: %2!ls!. +. + MessageId=304 Severity=Success SymbolicName=MSG_VERIFIED_EXISTING_PAYLOAD @@ -724,6 +731,13 @@ Language=English Acquiring package: %1!ls!, payload: %2!ls!, %3!hs! from: %4!ls! . +MessageId=339 +Severity=Error +SymbolicName=MSG_FAILED_VERIFY_CONTAINER +Language=English +Failed to verify container: %2!ls! at path: %3!ls!, error: %1!ls!. Deleting file. +. + MessageId=347 Severity=Warning SymbolicName=MSG_APPLY_RETRYING_CONTAINER -- cgit v1.2.3-55-g6feb From 22fb11c03329380fcffff253c7b2d4d1fccd23b4 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 16 Apr 2021 10:52:26 -0500 Subject: Add BURN_PAYLOAD_GROUP_ITEM to be able to move payloads during caching. --- src/engine/apply.cpp | 61 ++++++++++++++++++++++++++++++++------------ src/engine/core.cpp | 4 +-- src/engine/exeengine.cpp | 2 +- src/engine/msiengine.cpp | 4 +-- src/engine/mspengine.cpp | 2 +- src/engine/msuengine.cpp | 2 +- src/engine/package.cpp | 14 +++++----- src/engine/payload.cpp | 8 +++--- src/engine/payload.h | 13 ++++++++-- src/engine/plan.cpp | 16 +++++++++--- src/engine/pseudobundle.cpp | 18 ++++++------- src/engine/relatedbundle.cpp | 4 +-- 12 files changed, 98 insertions(+), 50 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 0eb8a710..bd294dfa 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -100,7 +100,7 @@ static HRESULT ApplyLayoutContainer( static HRESULT ApplyProcessPayload( __in BURN_CACHE_CONTEXT* pContext, __in_opt BURN_PACKAGE* pPackage, - __in BURN_PAYLOAD* pPayload + __in BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem ); static HRESULT ApplyCacheVerifyContainerOrPayload( __in BURN_CACHE_CONTEXT* pContext, @@ -132,8 +132,7 @@ static HRESULT LayoutOrCacheContainerOrPayload( __in BURN_CACHE_CONTEXT* pContext, __in_opt BURN_CONTAINER* pContainer, __in_opt BURN_PACKAGE* pPackage, - __in_opt BURN_PAYLOAD* pPayload, - __in BOOL fMove, + __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem, __in DWORD cTryAgainAttempts, __out BOOL* pfRetry ); @@ -805,14 +804,14 @@ static HRESULT ApplyCachePackage( for (;;) { - hr = UserExperienceOnCachePackageBegin(pContext->pUX, pPackage->sczId, pPackage->payloads.cPayloads, pPackage->payloads.qwTotalSize); + hr = UserExperienceOnCachePackageBegin(pContext->pUX, pPackage->sczId, pPackage->payloads.cItems, pPackage->payloads.qwTotalSize); LogExitOnFailure(hr, MSG_USER_CANCELED, "Cancel during cache: %ls: %ls", L"begin cache package", pPackage->sczId); - for (DWORD i = 0; i < pPackage->payloads.cPayloads; ++i) + for (DWORD i = 0; i < pPackage->payloads.cItems; ++i) { - BURN_PAYLOAD* pPayload = pPackage->payloads.rgpPayloads[i]; + BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem = pPackage->payloads.rgItems + i; - hr = ApplyProcessPayload(pContext, pPackage, pPayload); + hr = ApplyProcessPayload(pContext, pPackage, pPayloadGroupItem); if (FAILED(hr)) { break; @@ -831,6 +830,16 @@ static HRESULT ApplyCachePackage( if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_RETRY == cachePackageCompleteAction) { // TODO: the progress needs to account for the payloads (potentially) being recached. + for (DWORD i = 0; i < pPackage->payloads.cItems; ++i) + { + BURN_PAYLOAD_GROUP_ITEM* pItem = pPackage->payloads.rgItems + i; + if (pItem->fCached) + { + pItem->pPayload->cRemainingInstances += 1; + pItem->fCached = FALSE; + } + } + LogErrorId(hr, MSG_APPLY_RETRYING_PACKAGE, pPackage->sczId, NULL, NULL); continue; @@ -891,12 +900,12 @@ static HRESULT ApplyLayoutBundle( hr = LayoutBundle(pContext, wzExecutableName, wzUnverifiedPath, qwBundleSize); ExitOnFailure(hr, "Failed to layout bundle."); - for (DWORD i = 0; i < pPayloads->cPayloads; ++i) + for (DWORD i = 0; i < pPayloads->cItems; ++i) { - BURN_PAYLOAD* pPayload = pPayloads->rgpPayloads[i]; + BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem = pPayloads->rgItems + i; - hr = ApplyProcessPayload(pContext, NULL, pPayload); - ExitOnFailure(hr, "Failed to layout bundle payload: %ls", pPayload->sczKey); + hr = ApplyProcessPayload(pContext, NULL, pPayloadGroupItem); + ExitOnFailure(hr, "Failed to layout bundle payload: %ls", pPayloadGroupItem->pPayload->sczKey); } LExit: @@ -927,7 +936,7 @@ static HRESULT ApplyLayoutContainer( hr = ApplyAcquireContainerOrPayload(pContext, pContainer, NULL, NULL); LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_CONTAINER, "Failed to acquire container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath); - hr = LayoutOrCacheContainerOrPayload(pContext, pContainer, NULL, NULL, TRUE, cTryAgainAttempts, &fRetry); + hr = LayoutOrCacheContainerOrPayload(pContext, pContainer, NULL, NULL, cTryAgainAttempts, &fRetry); if (SUCCEEDED(hr)) { break; @@ -957,12 +966,13 @@ LExit: static HRESULT ApplyProcessPayload( __in BURN_CACHE_CONTEXT* pContext, __in_opt BURN_PACKAGE* pPackage, - __in BURN_PAYLOAD* pPayload + __in BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem ) { HRESULT hr = S_OK; DWORD cTryAgainAttempts = 0; BOOL fRetry = FALSE; + BURN_PAYLOAD* pPayload = pPayloadGroupItem->pPayload; Assert(pContext->pPayloads && pPackage || pContext->wzLayoutDirectory); @@ -984,8 +994,7 @@ static HRESULT ApplyProcessPayload( hr = ApplyAcquireContainerOrPayload(pContext, NULL, pPackage, pPayload); LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_PAYLOAD, "Failed to acquire payload: %ls to working path: %ls", pPayload->sczKey, pPayload->sczUnverifiedPath); - // TODO: set fMove to TRUE appropriately - hr = LayoutOrCacheContainerOrPayload(pContext, NULL, pPackage, pPayload, FALSE, cTryAgainAttempts, &fRetry); + hr = LayoutOrCacheContainerOrPayload(pContext, NULL, pPackage, pPayloadGroupItem, cTryAgainAttempts, &fRetry); if (SUCCEEDED(hr)) { break; @@ -1426,18 +1435,30 @@ static HRESULT LayoutOrCacheContainerOrPayload( __in BURN_CACHE_CONTEXT* pContext, __in_opt BURN_CONTAINER* pContainer, __in_opt BURN_PACKAGE* pPackage, - __in_opt BURN_PAYLOAD* pPayload, - __in BOOL fMove, + __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem, __in DWORD cTryAgainAttempts, __out BOOL* pfRetry ) { HRESULT hr = S_OK; + BURN_PAYLOAD* pPayload = pPayloadGroupItem ? pPayloadGroupItem->pPayload : NULL; LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : L""; LPCWSTR wzUnverifiedPath = pContainer ? pContainer->sczUnverifiedPath : pPayload->sczUnverifiedPath; LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : L""; BOOL fCanAffectRegistration = FALSE; BURN_CACHE_PROGRESS_CONTEXT progress = { }; + BOOL fMove = !pPayload || 1 == pPayload->cRemainingInstances; + + if (pContainer) + { + Assert(!pPayloadGroupItem); + } + else + { + Assert(pPayload); + AssertSz(0 < pPayload->cRemainingInstances, "Laying out payload more times than planned."); + AssertSz(!pPayloadGroupItem->fCached, "Laying out payload group item that was already cached."); + } if (!pContext->wzLayoutDirectory) { @@ -1501,6 +1522,12 @@ static HRESULT LayoutOrCacheContainerOrPayload( } } while (S_FALSE == hr); + if (SUCCEEDED(hr) && pPayloadGroupItem) + { + pPayload->cRemainingInstances -= 1; + pPayloadGroupItem->fCached = TRUE; + } + LExit: return hr; } diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 98aa943e..baba55f6 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -1679,9 +1679,9 @@ static HRESULT DetectPackagePayloadsCached( if (DirExists(sczCachePath, NULL)) { // Check all payloads to see if any exist. - for (DWORD i = 0; i < pPackage->payloads.cPayloads; ++i) + for (DWORD i = 0; i < pPackage->payloads.cItems; ++i) { - BURN_PAYLOAD* pPayload = pPackage->payloads.rgpPayloads[i]; + BURN_PAYLOAD* pPayload = pPackage->payloads.rgItems[i].pPayload; hr = PathConcat(sczCachePath, pPayload->sczFilePath, &sczPayloadCachePath); ExitOnFailure(hr, "Failed to concat payload cache path."); diff --git a/src/engine/exeengine.cpp b/src/engine/exeengine.cpp index 9b1b6973..0b53e266 100644 --- a/src/engine/exeengine.cpp +++ b/src/engine/exeengine.cpp @@ -373,7 +373,7 @@ extern "C" HRESULT ExeEngineExecutePackage( DWORD dwExitCode = 0; GENERIC_EXECUTE_MESSAGE message = { }; BURN_PACKAGE* pPackage = pExecuteAction->exePackage.pPackage; - BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgpPayloads[0]; + BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload; // get cached executable path hr = CacheGetCompletedPath(pPackage->fPerMachine, pPackage->sczCacheId, &sczCachedDirectory); diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index b081b9ca..e3dc4671 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -1100,7 +1100,7 @@ extern "C" HRESULT MsiEngineExecutePackage( LPWSTR sczProperties = NULL; LPWSTR sczObfuscatedProperties = NULL; BURN_PACKAGE* pPackage = pExecuteAction->msiPackage.pPackage; - BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgpPayloads[0]; + BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload; // During rollback, if the package is already in the rollback state we expect don't // touch it again. @@ -1981,7 +1981,7 @@ static HRESULT ConcatPatchProperty( { BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + i; BURN_PACKAGE* pMspPackage = pSlipstreamMsp->pMspPackage; - BURN_PAYLOAD* pMspPackagePayload = pMspPackage->payloads.rgpPayloads[0]; + BURN_PAYLOAD* pMspPackagePayload = pMspPackage->payloads.rgItems[0].pPayload; BOOTSTRAPPER_ACTION_STATE patchExecuteAction = fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute; if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < patchExecuteAction) diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp index 6addfa90..d5673700 100644 --- a/src/engine/mspengine.cpp +++ b/src/engine/mspengine.cpp @@ -581,7 +581,7 @@ extern "C" HRESULT MspEngineExecutePackage( { LPCWSTR wzAppend = NULL; BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage; - BURN_PAYLOAD* pMspPackagePayload = pMspPackage->payloads.rgpPayloads[0]; + BURN_PAYLOAD* pMspPackagePayload = pMspPackage->payloads.rgItems[0].pPayload; AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Invalid package type added to ordered patches."); if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->mspTarget.action) diff --git a/src/engine/msuengine.cpp b/src/engine/msuengine.cpp index 02ceb0c6..f807bf6b 100644 --- a/src/engine/msuengine.cpp +++ b/src/engine/msuengine.cpp @@ -265,7 +265,7 @@ extern "C" HRESULT MsuEngineExecutePackage( DWORD dwExitCode = 0; BOOL fUseSysNativePath = FALSE; BURN_PACKAGE* pPackage = pExecuteAction->msuPackage.pPackage; - BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgpPayloads[0]; + BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload; #if !defined(_WIN64) hr = ProcWow64(::GetCurrentProcess(), &fUseSysNativePath); diff --git a/src/engine/package.cpp b/src/engine/package.cpp index ecf1488b..dd4e498a 100644 --- a/src/engine/package.cpp +++ b/src/engine/package.cpp @@ -349,7 +349,7 @@ extern "C" void PackageUninitialize( MemFree(pPackage->rgDependencyProviders); } - ReleaseMem(pPackage->payloads.rgpPayloads); + ReleaseMem(pPackage->payloads.rgItems); switch (pPackage->type) { @@ -567,14 +567,16 @@ static HRESULT ParsePayloadRefsFromXml( } // allocate memory for payload pointers - pPackage->payloads.rgpPayloads = (BURN_PAYLOAD**)MemAlloc(sizeof(BURN_PAYLOAD*) * cNodes, TRUE); - ExitOnNull(pPackage->payloads.rgpPayloads, hr, E_OUTOFMEMORY, "Failed to allocate memory for package payloads."); + pPackage->payloads.rgItems = (BURN_PAYLOAD_GROUP_ITEM*)MemAlloc(sizeof(BURN_PAYLOAD_GROUP_ITEM) * cNodes, TRUE); + ExitOnNull(pPackage->payloads.rgItems, hr, E_OUTOFMEMORY, "Failed to allocate memory for package payloads."); - pPackage->payloads.cPayloads = cNodes; + pPackage->payloads.cItems = cNodes; // parse package elements for (DWORD i = 0; i < cNodes; ++i) { + BURN_PAYLOAD_GROUP_ITEM* pPackagePayload = pPackage->payloads.rgItems + i; + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); ExitOnFailure(hr, "Failed to get next node."); @@ -583,10 +585,10 @@ static HRESULT ParsePayloadRefsFromXml( ExitOnFailure(hr, "Failed to get Id attribute."); // find payload - hr = PayloadFindById(pPayloads, sczId, &pPackage->payloads.rgpPayloads[i]); + hr = PayloadFindById(pPayloads, sczId, &pPackagePayload->pPayload); ExitOnFailure(hr, "Failed to find payload."); - pPackage->payloads.qwTotalSize += pPackage->payloads.rgpPayloads[i]->qwFileSize; + pPackage->payloads.qwTotalSize += pPackagePayload->pPayload->qwFileSize; // prepare next iteration ReleaseNullObject(pixnNode); diff --git a/src/engine/payload.cpp b/src/engine/payload.cpp index 2be39b23..28ab6f45 100644 --- a/src/engine/payload.cpp +++ b/src/engine/payload.cpp @@ -139,11 +139,11 @@ extern "C" HRESULT PayloadsParseFromXml( if (pPayload->fLayoutOnly && pLayoutPayloads) { - hr = MemEnsureArraySize(reinterpret_cast(&pLayoutPayloads->rgpPayloads), pLayoutPayloads->cPayloads + 1, sizeof(BURN_PAYLOAD*), 5); - ExitOnNull(pPayloads->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate memory for layout payloads."); + hr = MemEnsureArraySize(reinterpret_cast(&pLayoutPayloads->rgItems), pLayoutPayloads->cItems + 1, sizeof(BURN_PAYLOAD_GROUP_ITEM), 5); + ExitOnFailure(hr, "Failed to allocate memory for layout payloads."); - pLayoutPayloads->rgpPayloads[pLayoutPayloads->cPayloads] = pPayload; - ++pLayoutPayloads->cPayloads; + pLayoutPayloads->rgItems[pLayoutPayloads->cItems].pPayload = pPayload; + ++pLayoutPayloads->cItems; pLayoutPayloads->qwTotalSize += pPayload->qwFileSize; } diff --git a/src/engine/payload.h b/src/engine/payload.h index ba555766..75132269 100644 --- a/src/engine/payload.h +++ b/src/engine/payload.h @@ -47,6 +47,7 @@ typedef struct _BURN_PAYLOAD LPWSTR sczLocalFilePath; // location of extracted or downloaded copy LPWSTR sczUnverifiedPath; + DWORD cRemainingInstances; } BURN_PAYLOAD; typedef struct _BURN_PAYLOADS @@ -55,10 +56,18 @@ typedef struct _BURN_PAYLOADS DWORD cPayloads; } BURN_PAYLOADS; +typedef struct _BURN_PAYLOAD_GROUP_ITEM +{ + BURN_PAYLOAD* pPayload; + + // mutable members + BOOL fCached; +} BURN_PAYLOAD_GROUP_ITEM; + typedef struct _BURN_PAYLOAD_GROUP { - BURN_PAYLOAD** rgpPayloads; - DWORD cPayloads; + BURN_PAYLOAD_GROUP_ITEM* rgItems; + DWORD cItems; DWORD64 qwTotalSize; } BURN_PAYLOAD_GROUP; diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index a37dcc89..f7434216 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -1839,15 +1839,22 @@ static void ResetPlannedPayloadsState( { BURN_PAYLOAD* pPayload = pPayloads->rgPayloads + i; + pPayload->cRemainingInstances = 0; pPayload->state = BURN_PAYLOAD_STATE_NONE; ReleaseNullStr(pPayload->sczLocalFilePath); } } static void ResetPlannedPayloadGroupState( - __in BURN_PAYLOAD_GROUP* /*pPayloadGroup*/ + __in BURN_PAYLOAD_GROUP* pPayloadGroup ) { + for (DWORD i = 0; i < pPayloadGroup->cItems; ++i) + { + BURN_PAYLOAD_GROUP_ITEM* pItem = pPayloadGroup->rgItems + i; + + pItem->fCached = FALSE; + } } static void ResetPlannedPackageState( @@ -2223,9 +2230,12 @@ static HRESULT ProcessPayloadGroup( { HRESULT hr = S_OK; - for (DWORD i = 0; i < pPayloadGroup->cPayloads; ++i) + for (DWORD i = 0; i < pPayloadGroup->cItems; ++i) { - BURN_PAYLOAD* pPayload = pPayloadGroup->rgpPayloads[i]; + BURN_PAYLOAD_GROUP_ITEM* pItem = pPayloadGroup->rgItems + i; + BURN_PAYLOAD* pPayload = pItem->pPayload; + + pPayload->cRemainingInstances += 1; if (pPayload->pContainer && !pPayload->pContainer->fPlanned) { diff --git a/src/engine/pseudobundle.cpp b/src/engine/pseudobundle.cpp index db25096b..180cc621 100644 --- a/src/engine/pseudobundle.cpp +++ b/src/engine/pseudobundle.cpp @@ -35,13 +35,13 @@ extern "C" HRESULT PseudoBundleInitialize( } // Initialize the single payload, and fill out all the necessary fields - pPackage->payloads.rgpPayloads = (BURN_PAYLOAD**)MemAlloc(sizeof(BURN_PAYLOAD*), TRUE); - ExitOnNull(pPackage->payloads.rgpPayloads, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload group inside of related bundle struct"); - pPackage->payloads.cPayloads = 1; + pPackage->payloads.rgItems = (BURN_PAYLOAD_GROUP_ITEM*)MemAlloc(sizeof(BURN_PAYLOAD_GROUP_ITEM), TRUE); + ExitOnNull(pPackage->payloads.rgItems, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload group inside of related bundle struct"); + pPackage->payloads.cItems = 1; pPayload = (BURN_PAYLOAD*)MemAlloc(sizeof(BURN_PAYLOAD), TRUE); ExitOnNull(pPayload, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload inside of related bundle struct"); - pPackage->payloads.rgpPayloads[0] = pPayload; + pPackage->payloads.rgItems[0].pPayload = pPayload; pPayload->packaging = BURN_PAYLOAD_PACKAGING_EXTERNAL; pPayload->qwFileSize = qwSize; @@ -171,13 +171,13 @@ extern "C" HRESULT PseudoBundleInitializePassthrough( LPWSTR sczArguments = NULL; // Initialize the payloads, and copy the necessary fields. - pPassthroughPackage->payloads.rgpPayloads = (BURN_PAYLOAD**)MemAlloc(sizeof(BURN_PAYLOAD*) * pPackage->payloads.cPayloads, TRUE); - ExitOnNull(pPassthroughPackage->payloads.rgpPayloads, hr, E_OUTOFMEMORY, "Failed to allocate space for burn package payload inside of passthrough bundle."); - pPassthroughPackage->payloads.cPayloads = pPackage->payloads.cPayloads; + pPassthroughPackage->payloads.rgItems = (BURN_PAYLOAD_GROUP_ITEM*)MemAlloc(sizeof(BURN_PAYLOAD_GROUP_ITEM) * pPackage->payloads.cItems, TRUE); + ExitOnNull(pPassthroughPackage->payloads.rgItems, hr, E_OUTOFMEMORY, "Failed to allocate space for burn package payload inside of passthrough bundle."); + pPassthroughPackage->payloads.cItems = pPackage->payloads.cItems; - for (DWORD iPayload = 0; iPayload < pPackage->payloads.cPayloads; ++iPayload) + for (DWORD iPayload = 0; iPayload < pPackage->payloads.cItems; ++iPayload) { - pPassthroughPackage->payloads.rgpPayloads[iPayload] = pPackage->payloads.rgpPayloads[iPayload]; + pPassthroughPackage->payloads.rgItems[iPayload].pPayload = pPackage->payloads.rgItems[iPayload].pPayload; } pPassthroughPackage->Exe.fPseudoBundle = TRUE; diff --git a/src/engine/relatedbundle.cpp b/src/engine/relatedbundle.cpp index a79be020..d3c856a6 100644 --- a/src/engine/relatedbundle.cpp +++ b/src/engine/relatedbundle.cpp @@ -82,9 +82,9 @@ extern "C" void RelatedBundlesUninitialize( { BURN_PACKAGE* pPackage = &pRelatedBundles->rgRelatedBundles[i].package; - for (DWORD j = 0; j < pPackage->payloads.cPayloads; ++j) + for (DWORD j = 0; j < pPackage->payloads.cItems; ++j) { - PayloadUninitialize(pPackage->payloads.rgpPayloads[j]); + PayloadUninitialize(pPackage->payloads.rgItems[j].pPayload); } PackageUninitialize(pPackage); -- cgit v1.2.3-55-g6feb From 70bb69c0e5f589fb55d3e36a5e81e9a7d0e56814 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 16 Apr 2021 10:54:49 -0500 Subject: Remove the package's cache progress when retrying the whole package. --- src/engine/apply.cpp | 75 +++++++++++++++++++++++++++++++--------------------- src/engine/payload.h | 1 + src/engine/plan.cpp | 1 + 3 files changed, 47 insertions(+), 30 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index bd294dfa..5402e55c 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -42,7 +42,7 @@ typedef struct _BURN_CACHE_PROGRESS_CONTEXT BURN_CACHE_PROGRESS_TYPE type; BURN_CONTAINER* pContainer; BURN_PACKAGE* pPackage; - BURN_PAYLOAD* pPayload; + BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem; BOOL fCancel; HRESULT hrError; @@ -106,7 +106,7 @@ static HRESULT ApplyCacheVerifyContainerOrPayload( __in BURN_CACHE_CONTEXT* pContext, __in_opt BURN_CONTAINER* pContainer, __in_opt BURN_PACKAGE* pPackage, - __in_opt BURN_PAYLOAD* pPayload + __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem ); static HRESULT ExtractContainer( __in BURN_CACHE_CONTEXT* pContext, @@ -122,7 +122,7 @@ static HRESULT ApplyAcquireContainerOrPayload( __in BURN_CACHE_CONTEXT* pContext, __in_opt BURN_CONTAINER* pContainer, __in_opt BURN_PACKAGE* pPackage, - __in_opt BURN_PAYLOAD* pPayload + __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem ); static HRESULT AcquireContainerOrPayload( __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, @@ -829,7 +829,6 @@ static HRESULT ApplyCachePackage( if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_RETRY == cachePackageCompleteAction) { - // TODO: the progress needs to account for the payloads (potentially) being recached. for (DWORD i = 0; i < pPackage->payloads.cItems; ++i) { BURN_PAYLOAD_GROUP_ITEM* pItem = pPackage->payloads.rgItems + i; @@ -838,6 +837,12 @@ static HRESULT ApplyCachePackage( pItem->pPayload->cRemainingInstances += 1; pItem->fCached = FALSE; } + + if (pItem->qwCommittedCacheProgress) + { + pContext->qwSuccessfulCacheProgress -= pItem->qwCommittedCacheProgress; + pItem->qwCommittedCacheProgress = 0; + } } LogErrorId(hr, MSG_APPLY_RETRYING_PACKAGE, pPackage->sczId, NULL, NULL); @@ -981,7 +986,7 @@ static HRESULT ApplyProcessPayload( ExitFunction(); } - hr = ApplyCacheVerifyContainerOrPayload(pContext, NULL, pPackage, pPayload); + hr = ApplyCacheVerifyContainerOrPayload(pContext, NULL, pPackage, pPayloadGroupItem); if (SUCCEEDED(hr)) { ExitFunction(); @@ -991,7 +996,7 @@ static HRESULT ApplyProcessPayload( { fRetry = FALSE; - hr = ApplyAcquireContainerOrPayload(pContext, NULL, pPackage, pPayload); + hr = ApplyAcquireContainerOrPayload(pContext, NULL, pPackage, pPayloadGroupItem); LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_PAYLOAD, "Failed to acquire payload: %ls to working path: %ls", pPayload->sczKey, pPayload->sczUnverifiedPath); hr = LayoutOrCacheContainerOrPayload(pContext, NULL, pPackage, pPayloadGroupItem, cTryAgainAttempts, &fRetry); @@ -1009,7 +1014,8 @@ static HRESULT ApplyProcessPayload( } ++cTryAgainAttempts; - pContext->qwSuccessfulCacheProgress -= pPayload->qwFileSize; + pContext->qwSuccessfulCacheProgress -= pPayloadGroupItem->qwCommittedCacheProgress; + pPayloadGroupItem->qwCommittedCacheProgress = 0; ReleaseNullStr(pContext->sczLastUsedFolderCandidate); LogErrorId(hr, MSG_APPLY_RETRYING_PAYLOAD, pPayload->sczKey, NULL, NULL); } @@ -1025,20 +1031,21 @@ static HRESULT ApplyCacheVerifyContainerOrPayload( __in BURN_CACHE_CONTEXT* pContext, __in_opt BURN_CONTAINER* pContainer, __in_opt BURN_PACKAGE* pPackage, - __in_opt BURN_PAYLOAD* pPayload + __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem ) { - AssertSz(pContainer || pPayload, "Must provide a container or a payload."); + AssertSz(pContainer || pPayloadGroupItem, "Must provide a container or a payload."); HRESULT hr = S_OK; BURN_CACHE_PROGRESS_CONTEXT progress = { }; LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : NULL; - LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : NULL; + LPCWSTR wzPayloadId = pPayloadGroupItem ? pPayloadGroupItem->pPayload->sczKey : NULL; + DWORD64 qwFileSize = pContainer ? pContainer->qwFileSize : pPayloadGroupItem->pPayload->qwFileSize; progress.pCacheContext = pContext; progress.pContainer = pContainer; progress.pPackage = pPackage; - progress.pPayload = pPayload; + progress.pPayloadGroupItem = pPayloadGroupItem; progress.type = BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY; hr = UserExperienceOnCacheContainerOrPayloadVerifyBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId); @@ -1046,7 +1053,7 @@ static HRESULT ApplyCacheVerifyContainerOrPayload( if (INVALID_HANDLE_VALUE != pContext->hPipe) { - hr = ElevationCacheVerifyContainerOrPayload(pContext->hPipe, pContainer, pPackage, pPayload, pContext->wzLayoutDirectory); + hr = ElevationCacheVerifyContainerOrPayload(pContext->hPipe, pContainer, pPackage, pPayloadGroupItem->pPayload, pContext->wzLayoutDirectory); } else if (pContainer) { @@ -1054,7 +1061,7 @@ static HRESULT ApplyCacheVerifyContainerOrPayload( } else { - hr = CacheVerifyPayload(pPayload, pContext->wzLayoutDirectory ? pContext->wzLayoutDirectory : pPackage->sczCacheFolder); + hr = CacheVerifyPayload(pPayloadGroupItem->pPayload, pContext->wzLayoutDirectory ? pContext->wzLayoutDirectory : pPackage->sczCacheFolder); } // This was best effort to avoid acquiring the container or payload. @@ -1063,9 +1070,13 @@ static HRESULT ApplyCacheVerifyContainerOrPayload( ExitFunction(); } - pContext->qwSuccessfulCacheProgress += pContainer ? pContainer->qwFileSize : pPayload->qwFileSize; + pContext->qwSuccessfulCacheProgress += qwFileSize; + if (pPayloadGroupItem) + { + pPayloadGroupItem->qwCommittedCacheProgress += qwFileSize; + } - hr = CompleteCacheProgress(&progress, pContainer ? pContainer->qwFileSize : pPayload->qwFileSize); + hr = CompleteCacheProgress(&progress, qwFileSize); LExit: UserExperienceOnCacheContainerOrPayloadVerifyComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr); @@ -1275,10 +1286,10 @@ static HRESULT ApplyAcquireContainerOrPayload( __in BURN_CACHE_CONTEXT* pContext, __in_opt BURN_CONTAINER* pContainer, __in_opt BURN_PACKAGE* pPackage, - __in_opt BURN_PAYLOAD* pPayload + __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem ) { - AssertSz(pContainer || pPayload, "Must provide a container or a payload."); + AssertSz(pContainer || pPayloadGroupItem, "Must provide a container or a payload."); HRESULT hr = S_OK; BURN_CACHE_PROGRESS_CONTEXT progress = { }; @@ -1288,7 +1299,7 @@ static HRESULT ApplyAcquireContainerOrPayload( progress.type = BURN_CACHE_PROGRESS_TYPE_ACQUIRE; progress.pContainer = pContainer; progress.pPackage = pPackage; - progress.pPayload = pPayload; + progress.pPayloadGroupItem = pPayloadGroupItem; do { @@ -1297,10 +1308,10 @@ static HRESULT ApplyAcquireContainerOrPayload( if (fRetry) { hr = S_OK; - LogErrorId(hr, pContainer ? MSG_APPLY_RETRYING_ACQUIRE_CONTAINER : MSG_APPLY_RETRYING_ACQUIRE_PAYLOAD, pContainer ? pContainer->sczId : pPayload->sczKey, NULL, NULL); + LogErrorId(hr, pContainer ? MSG_APPLY_RETRYING_ACQUIRE_CONTAINER : MSG_APPLY_RETRYING_ACQUIRE_PAYLOAD, pContainer ? pContainer->sczId : pPayloadGroupItem->pPayload->sczKey, NULL, NULL); } - ExitOnFailure(hr, "Failed to acquire %hs: %ls", pContainer ? "container" : "payload", pContainer ? pContainer->sczId : pPayload->sczKey); + ExitOnFailure(hr, "Failed to acquire %hs: %ls", pContainer ? "container" : "payload", pContainer ? pContainer->sczId : pPayloadGroupItem->pPayload->sczKey); } while (fRetry); LExit: @@ -1315,7 +1326,7 @@ static HRESULT AcquireContainerOrPayload( BURN_CACHE_CONTEXT* pContext = pProgress->pCacheContext; BURN_CONTAINER* pContainer = pProgress->pContainer; BURN_PACKAGE* pPackage = pProgress->pPackage; - BURN_PAYLOAD* pPayload = pProgress->pPayload; + BURN_PAYLOAD* pPayload = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload : NULL; AssertSz(pContainer || pPayload, "Must provide a container or a payload."); HRESULT hr = S_OK; @@ -1473,7 +1484,7 @@ static HRESULT LayoutOrCacheContainerOrPayload( progress.type = BURN_CACHE_PROGRESS_TYPE_VERIFY; progress.pContainer = pContainer; progress.pPackage = pPackage; - progress.pPayload = pPayload; + progress.pPayloadGroupItem = pPayloadGroupItem; do { @@ -1570,11 +1581,11 @@ static HRESULT CopyPayload( { HRESULT hr = S_OK; LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L""; - LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : L""; + LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : L""; HANDLE hDestinationFile = INVALID_HANDLE_VALUE; HANDLE hSourceOpenedFile = INVALID_HANDLE_VALUE; - DWORD dwLogId = pProgress->pContainer ? (pProgress->pPayload ? MSG_ACQUIRE_CONTAINER_PAYLOAD : MSG_ACQUIRE_CONTAINER) : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; + DWORD dwLogId = pProgress->pContainer ? (pProgress->pPayloadGroupItem ? MSG_ACQUIRE_CONTAINER_PAYLOAD : MSG_ACQUIRE_CONTAINER) : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "copy", wzSourcePath); hr = PreparePayloadDestinationPath(wzDestinationPath); @@ -1630,14 +1641,14 @@ static HRESULT DownloadPayload( { HRESULT hr = S_OK; LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L""; - LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : L""; - DOWNLOAD_SOURCE* pDownloadSource = pProgress->pContainer ? &pProgress->pContainer->downloadSource : &pProgress->pPayload->downloadSource; - DWORD64 qwDownloadSize = pProgress->pContainer ? pProgress->pContainer->qwFileSize : pProgress->pPayload->qwFileSize; + LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : L""; + DOWNLOAD_SOURCE* pDownloadSource = pProgress->pContainer ? &pProgress->pContainer->downloadSource : &pProgress->pPayloadGroupItem->pPayload->downloadSource; + DWORD64 qwDownloadSize = pProgress->pContainer ? pProgress->pContainer->qwFileSize : pProgress->pPayloadGroupItem->pPayload->qwFileSize; DOWNLOAD_CACHE_CALLBACK cacheCallback = { }; DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { }; APPLY_AUTHENTICATION_REQUIRED_DATA authenticationData = { }; - DWORD dwLogId = pProgress->pContainer ? (pProgress->pPayload ? MSG_ACQUIRE_CONTAINER_PAYLOAD : MSG_ACQUIRE_CONTAINER) : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; + DWORD dwLogId = pProgress->pContainer ? (pProgress->pPayloadGroupItem ? MSG_ACQUIRE_CONTAINER_PAYLOAD : MSG_ACQUIRE_CONTAINER) : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "download", pDownloadSource->sczUrl); hr = PreparePayloadDestinationPath(wzDestinationPath); @@ -1736,11 +1747,15 @@ static HRESULT CompleteCacheProgress( if (PROGRESS_CONTINUE == dwResult) { pContext->pCacheContext->qwSuccessfulCacheProgress += qwFileSize; + if (pContext->pPayloadGroupItem) + { + pContext->pPayloadGroupItem->qwCommittedCacheProgress += qwFileSize; + } if (BURN_CACHE_PROGRESS_TYPE_VERIFY == pContext->type && pContext->pCacheContext->sczLastUsedFolderCandidate) { // We successfully copied from a source location, set that as the last used source. - CacheSetLastUsedSource(pContext->pCacheContext->pVariables, pContext->pCacheContext->sczLastUsedFolderCandidate, pContext->pContainer ? pContext->pContainer->sczFilePath : pContext->pPayload->sczFilePath); + CacheSetLastUsedSource(pContext->pCacheContext->pVariables, pContext->pCacheContext->sczLastUsedFolderCandidate, pContext->pContainer ? pContext->pContainer->sczFilePath : pContext->pPayloadGroupItem->pPayload->sczFilePath); } } else if (PROGRESS_CANCEL == dwResult) @@ -1774,7 +1789,7 @@ static DWORD CALLBACK CacheProgressRoutine( DWORD dwResult = PROGRESS_CONTINUE; BURN_CACHE_PROGRESS_CONTEXT* pProgress = static_cast(lpData); LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : NULL; - LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : NULL; + LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : NULL; DWORD64 qwCacheProgress = pProgress->pCacheContext->qwSuccessfulCacheProgress + TotalBytesTransferred.QuadPart; if (qwCacheProgress > pProgress->pCacheContext->qwTotalCacheSize) { diff --git a/src/engine/payload.h b/src/engine/payload.h index 75132269..aa174d66 100644 --- a/src/engine/payload.h +++ b/src/engine/payload.h @@ -62,6 +62,7 @@ typedef struct _BURN_PAYLOAD_GROUP_ITEM // mutable members BOOL fCached; + DWORD64 qwCommittedCacheProgress; } BURN_PAYLOAD_GROUP_ITEM; typedef struct _BURN_PAYLOAD_GROUP diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index f7434216..55fe7ddf 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -1854,6 +1854,7 @@ static void ResetPlannedPayloadGroupState( BURN_PAYLOAD_GROUP_ITEM* pItem = pPayloadGroup->rgItems + i; pItem->fCached = FALSE; + pItem->qwCommittedCacheProgress = 0; } } -- cgit v1.2.3-55-g6feb From c308746132f3ab89458b446f659f3d4073758da6 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 16 Apr 2021 10:56:24 -0500 Subject: When extracting a container use the uncompressed file size for progress Remove the container's cache progress when reextracting Skip extracting payloads that are already cached --- src/engine/apply.cpp | 17 ++++++++++++++--- src/engine/container.h | 2 ++ src/engine/plan.cpp | 16 +++++++++++++--- src/test/BurnUnitTest/PlanTest.cpp | 8 ++++---- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 5402e55c..f80baf26 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -869,6 +869,12 @@ static HRESULT ApplyExtractContainer( { HRESULT hr = S_OK; + if (pContainer->qwCommittedCacheProgress) + { + pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedCacheProgress; + pContainer->qwCommittedCacheProgress = 0; + } + if (!pContainer->fActuallyAttached) { hr = ApplyAcquireContainerOrPayload(pContext, pContainer, NULL, NULL); @@ -884,7 +890,8 @@ static HRESULT ApplyExtractContainer( CacheSetLastUsedSource(pContext->pVariables, pContext->sczLastUsedFolderCandidate, pContainer->sczFilePath); } - pContext->qwSuccessfulCacheProgress += pContainer->qwFileSize; + pContext->qwSuccessfulCacheProgress += pContainer->qwExtractSizeTotal; + pContainer->qwCommittedCacheProgress += pContainer->qwExtractSizeTotal; LExit: ReleaseNullStr(pContext->sczLastUsedFolderCandidate); @@ -1110,7 +1117,7 @@ static HRESULT ExtractContainer( for (DWORD iExtract = 0; iExtract < pContext->pPayloads->cPayloads; ++iExtract) { BURN_PAYLOAD* pExtract = pContext->pPayloads->rgPayloads + iExtract; - if (pExtract->sczUnverifiedPath && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczExtractPayloadId, -1, pExtract->sczSourcePath, -1)) + if (pExtract->sczUnverifiedPath && pExtract->cRemainingInstances && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczExtractPayloadId, -1, pExtract->sczSourcePath, -1)) { hr = PreparePayloadDestinationPath(pExtract->sczUnverifiedPath); ExitOnFailure(hr, "Failed to prepare payload destination path: %ls", pExtract->sczUnverifiedPath); @@ -1747,7 +1754,11 @@ static HRESULT CompleteCacheProgress( if (PROGRESS_CONTINUE == dwResult) { pContext->pCacheContext->qwSuccessfulCacheProgress += qwFileSize; - if (pContext->pPayloadGroupItem) + if (pContext->pContainer) + { + pContext->pContainer->qwCommittedCacheProgress += qwFileSize; + } + else if (pContext->pPayloadGroupItem) { pContext->pPayloadGroupItem->qwCommittedCacheProgress += qwFileSize; } diff --git a/src/engine/container.h b/src/engine/container.h index d38016cc..7c5c2b5f 100644 --- a/src/engine/container.h +++ b/src/engine/container.h @@ -76,6 +76,8 @@ typedef struct _BURN_CONTAINER BOOL fPlanned; LPWSTR sczSourcePath; LPWSTR sczUnverifiedPath; + DWORD64 qwExtractSizeTotal; + DWORD64 qwCommittedCacheProgress; } BURN_CONTAINER; typedef struct _BURN_CONTAINERS diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index 55fe7ddf..8421d87b 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -431,6 +431,7 @@ extern "C" HRESULT PlanLayoutBundle( pCacheAction->bundleLayout.qwBundleSize = qwBundleSize; pCacheAction->bundleLayout.pPayloadGroup = pLayoutPayloads; + // Acquire + Verify pPlan->qwCacheSizeTotal += 2 * qwBundleSize; ++pPlan->cOverallProgressTicksTotal; @@ -1005,6 +1006,7 @@ extern "C" HRESULT PlanLayoutContainer( pCacheAction->type = BURN_CACHE_ACTION_TYPE_CONTAINER; pCacheAction->container.pContainer = pContainer; + // Acquire + Verify pPlan->qwCacheSizeTotal += 2 * pContainer->qwFileSize; } } @@ -1012,11 +1014,9 @@ extern "C" HRESULT PlanLayoutContainer( { if (!pContainer->fActuallyAttached) { + // Acquire pPlan->qwCacheSizeTotal += pContainer->qwFileSize; } - - // TODO: This should be the sum of all uncompressed payloads in the container, ideally restricted to the payloads that were actually planned. - pPlan->qwCacheSizeTotal += pContainer->qwFileSize; } if (!pContainer->sczUnverifiedPath) @@ -1829,6 +1829,8 @@ static void ResetPlannedContainerState( ) { pContainer->fPlanned = FALSE; + pContainer->qwExtractSizeTotal = 0; + pContainer->qwCommittedCacheProgress = 0; } static void ResetPlannedPayloadsState( @@ -2246,9 +2248,17 @@ static HRESULT ProcessPayloadGroup( if (!pPlan->sczLayoutDirectory || !pPayload->pContainer) { + // Acquire + Verify pPlan->qwCacheSizeTotal += 2 * pPayload->qwFileSize; } + if (!pPlan->sczLayoutDirectory && pPayload->pContainer && 1 == pPayload->cRemainingInstances) + { + // Extract + pPlan->qwCacheSizeTotal += pPayload->qwFileSize; + pPayload->pContainer->qwExtractSizeTotal += pPayload->qwFileSize; + } + if (!pPayload->sczUnverifiedPath) { hr = CacheCalculatePayloadWorkingPath(pPlan->wzBundleId, pPayload, &pPayload->sczUnverifiedPath); diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp index aa9deaf6..c073696b 100644 --- a/src/test/BurnUnitTest/PlanTest.cpp +++ b/src/test/BurnUnitTest/PlanTest.cpp @@ -71,7 +71,7 @@ namespace Bootstrapper Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); Assert::Equal(107082ull, pPlan->qwEstimatedSize); - Assert::Equal(202458ull, pPlan->qwCacheSizeTotal); + Assert::Equal(303687ull, pPlan->qwCacheSizeTotal); fRollback = FALSE; dwIndex = 0; @@ -308,7 +308,7 @@ namespace Bootstrapper Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); Assert::Equal(35694ull, pPlan->qwEstimatedSize); - Assert::Equal(67486ull, pPlan->qwCacheSizeTotal); + Assert::Equal(101229ull, pPlan->qwCacheSizeTotal); fRollback = FALSE; dwIndex = 0; @@ -388,7 +388,7 @@ namespace Bootstrapper Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); Assert::Equal(33743ull, pPlan->qwEstimatedSize); - Assert::Equal(67486ull, pPlan->qwCacheSizeTotal); + Assert::Equal(101229ull, pPlan->qwCacheSizeTotal); fRollback = FALSE; dwIndex = 0; @@ -458,7 +458,7 @@ namespace Bootstrapper Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); Assert::Equal(35694ull, pPlan->qwEstimatedSize); - Assert::Equal(67486ull, pPlan->qwCacheSizeTotal); + Assert::Equal(101229ull, pPlan->qwCacheSizeTotal); fRollback = FALSE; dwIndex = 0; -- cgit v1.2.3-55-g6feb From b1d1e523f5cdadce0cbf105179b33c014d5ec9eb Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 16 Apr 2021 13:38:16 -0500 Subject: Add OnCachePayloadExtract*. --- .../inc/BootstrapperApplication.h | 45 +++++++++++ src/engine/apply.cpp | 58 ++++++++++++++- src/engine/container.h | 1 + src/engine/plan.cpp | 1 + src/engine/userexperience.cpp | 87 ++++++++++++++++++++++ src/engine/userexperience.h | 19 +++++ 6 files changed, 207 insertions(+), 4 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h index edb981a9..e1920107 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -153,6 +153,9 @@ enum BOOTSTRAPPER_APPLICATION_MESSAGE BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECONTAINERORPAYLOADVERIFYBEGIN, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECONTAINERORPAYLOADVERIFYCOMPLETE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECONTAINERORPAYLOADVERIFYPROGRESS, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPAYLOADEXTRACTBEGIN, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPAYLOADEXTRACTCOMPLETE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPAYLOADEXTRACTPROGRESS, }; enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION @@ -472,6 +475,48 @@ struct BA_ONCACHEPACKAGECOMPLETE_RESULTS BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION action; }; +struct BA_ONCACHEPAYLOADEXTRACTBEGIN_ARGS +{ + DWORD cbSize; + LPCWSTR wzContainerId; + LPCWSTR wzPayloadId; +}; + +struct BA_ONCACHEPAYLOADEXTRACTBEGIN_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + +struct BA_ONCACHEPAYLOADEXTRACTCOMPLETE_ARGS +{ + DWORD cbSize; + LPCWSTR wzContainerId; + LPCWSTR wzPayloadId; + HRESULT hrStatus; +}; + +struct BA_ONCACHEPAYLOADEXTRACTCOMPLETE_RESULTS +{ + DWORD cbSize; +}; + +struct BA_ONCACHEPAYLOADEXTRACTPROGRESS_ARGS +{ + DWORD cbSize; + LPCWSTR wzContainerId; + LPCWSTR wzPayloadId; + DWORD64 dw64Progress; + DWORD64 dw64Total; + DWORD dwOverallPercentage; +}; + +struct BA_ONCACHEPAYLOADEXTRACTPROGRESS_RESULTS +{ + DWORD cbSize; + BOOL fCancel; +}; + struct BA_ONCACHEVERIFYBEGIN_ARGS { DWORD cbSize; diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index f80baf26..ab7fa077 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -16,6 +16,7 @@ enum BURN_CACHE_PROGRESS_TYPE BURN_CACHE_PROGRESS_TYPE_ACQUIRE, BURN_CACHE_PROGRESS_TYPE_VERIFY, BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY, + BURN_CACHE_PROGRESS_TYPE_EXTRACT, }; // structs @@ -43,6 +44,7 @@ typedef struct _BURN_CACHE_PROGRESS_CONTEXT BURN_CONTAINER* pContainer; BURN_PACKAGE* pPackage; BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem; + BURN_PAYLOAD* pPayload; BOOL fCancel; HRESULT hrError; @@ -875,6 +877,12 @@ static HRESULT ApplyExtractContainer( pContainer->qwCommittedCacheProgress = 0; } + if (pContainer->qwCommittedExtractProgress) + { + pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedExtractProgress; + pContainer->qwCommittedExtractProgress = 0; + } + if (!pContainer->fActuallyAttached) { hr = ApplyAcquireContainerOrPayload(pContext, pContainer, NULL, NULL); @@ -890,8 +898,18 @@ static HRESULT ApplyExtractContainer( CacheSetLastUsedSource(pContext->pVariables, pContext->sczLastUsedFolderCandidate, pContainer->sczFilePath); } - pContext->qwSuccessfulCacheProgress += pContainer->qwExtractSizeTotal; - pContainer->qwCommittedCacheProgress += pContainer->qwExtractSizeTotal; + if (pContainer->qwExtractSizeTotal < pContainer->qwCommittedExtractProgress) + { + AssertSz(FALSE, "Container extracted more than planned."); + pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedExtractProgress; + pContext->qwSuccessfulCacheProgress += pContainer->qwExtractSizeTotal; + } + else + { + pContext->qwSuccessfulCacheProgress += pContainer->qwExtractSizeTotal - pContainer->qwCommittedExtractProgress; + } + + pContainer->qwCommittedExtractProgress = pContainer->qwExtractSizeTotal; LExit: ReleaseNullStr(pContext->sczLastUsedFolderCandidate); @@ -1100,6 +1118,11 @@ static HRESULT ExtractContainer( BURN_CONTAINER_CONTEXT context = { }; HANDLE hContainerHandle = INVALID_HANDLE_VALUE; LPWSTR sczExtractPayloadId = NULL; + BURN_CACHE_PROGRESS_CONTEXT progress = { }; + + progress.pCacheContext = pContext; + progress.pContainer = pContainer; + progress.type = BURN_CACHE_PROGRESS_TYPE_EXTRACT; // If the container is actually attached, then it was planned to be acquired through hSourceEngineFile. if (pContainer->fActuallyAttached) @@ -1119,11 +1142,29 @@ static HRESULT ExtractContainer( BURN_PAYLOAD* pExtract = pContext->pPayloads->rgPayloads + iExtract; if (pExtract->sczUnverifiedPath && pExtract->cRemainingInstances && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczExtractPayloadId, -1, pExtract->sczSourcePath, -1)) { + progress.pPayload = pExtract; + hr = PreparePayloadDestinationPath(pExtract->sczUnverifiedPath); ExitOnFailure(hr, "Failed to prepare payload destination path: %ls", pExtract->sczUnverifiedPath); + hr = UserExperienceOnCachePayloadExtractBegin(pContext->pUX, pContainer->sczId, pExtract->sczKey); + if (FAILED(hr)) + { + UserExperienceOnCachePayloadExtractComplete(pContext->pUX, pContainer->sczId, pExtract->sczKey, hr); + ExitOnRootFailure(hr, "BA aborted cache payload extract begin."); + } + // TODO: Send progress when extracting stream to file. hr = ContainerStreamToFile(&context, pExtract->sczUnverifiedPath); + // Error handling happens after sending complete message to BA. + + // If succeeded, send 100% complete here to make sure progress was sent to the BA. + if (SUCCEEDED(hr)) + { + hr = CompleteCacheProgress(&progress, pExtract->qwFileSize); + } + + UserExperienceOnCachePayloadExtractComplete(pContext->pUX, pContainer->sczId, pExtract->sczKey, hr); ExitOnFailure(hr, "Failed to extract payload: %ls from container: %ls", sczExtractPayloadId, pContainer->sczId); fExtracted = TRUE; @@ -1754,7 +1795,12 @@ static HRESULT CompleteCacheProgress( if (PROGRESS_CONTINUE == dwResult) { pContext->pCacheContext->qwSuccessfulCacheProgress += qwFileSize; - if (pContext->pContainer) + + if (pContext->pPayload) + { + pContext->pContainer->qwCommittedExtractProgress += qwFileSize; + } + else if (pContext->pContainer) { pContext->pContainer->qwCommittedCacheProgress += qwFileSize; } @@ -1800,7 +1846,7 @@ static DWORD CALLBACK CacheProgressRoutine( DWORD dwResult = PROGRESS_CONTINUE; BURN_CACHE_PROGRESS_CONTEXT* pProgress = static_cast(lpData); LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : NULL; - LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : NULL; + LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : pProgress->pPayload ? pProgress->pPayload->sczKey : NULL; DWORD64 qwCacheProgress = pProgress->pCacheContext->qwSuccessfulCacheProgress + TotalBytesTransferred.QuadPart; if (qwCacheProgress > pProgress->pCacheContext->qwTotalCacheSize) { @@ -1823,6 +1869,10 @@ static DWORD CALLBACK CacheProgressRoutine( hr = UserExperienceOnCacheContainerOrPayloadVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); ExitOnRootFailure(hr, "BA aborted container or payload verify: %ls", wzPayloadId); break; + case BURN_CACHE_PROGRESS_TYPE_EXTRACT: + hr = UserExperienceOnCachePayloadExtractProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); + ExitOnRootFailure(hr, "BA aborted extract container: %ls, payload: %ls", wzPackageOrContainerId, wzPayloadId); + break; } LExit: diff --git a/src/engine/container.h b/src/engine/container.h index 7c5c2b5f..3174eb38 100644 --- a/src/engine/container.h +++ b/src/engine/container.h @@ -78,6 +78,7 @@ typedef struct _BURN_CONTAINER LPWSTR sczUnverifiedPath; DWORD64 qwExtractSizeTotal; DWORD64 qwCommittedCacheProgress; + DWORD64 qwCommittedExtractProgress; } BURN_CONTAINER; typedef struct _BURN_CONTAINERS diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index 8421d87b..bf929835 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -1831,6 +1831,7 @@ static void ResetPlannedContainerState( pContainer->fPlanned = FALSE; pContainer->qwExtractSizeTotal = 0; pContainer->qwCommittedCacheProgress = 0; + pContainer->qwCommittedExtractProgress = 0; } static void ResetPlannedPayloadsState( diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index 02c67fc5..279a00b5 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -758,6 +758,93 @@ LExit: return hr; } +EXTERN_C BAAPI UserExperienceOnCachePayloadExtractBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzContainerId, + __in_z_opt LPCWSTR wzPayloadId + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEPAYLOADEXTRACTBEGIN_ARGS args = { }; + BA_ONCACHEPAYLOADEXTRACTBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzContainerId = wzContainerId; + args.wzPayloadId = wzPayloadId; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPAYLOADEXTRACTBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnCachePayloadExtractBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCachePayloadExtractComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEPAYLOADEXTRACTCOMPLETE_ARGS args = { }; + BA_ONCACHEPAYLOADEXTRACTCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzContainerId = wzContainerId; + args.wzPayloadId = wzPayloadId; + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPAYLOADEXTRACTCOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnCachePayloadExtractComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCachePayloadExtractProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in DWORD dwOverallPercentage + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEPAYLOADEXTRACTPROGRESS_ARGS args = { }; + BA_ONCACHEPAYLOADEXTRACTPROGRESS_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzContainerId = wzContainerId; + args.wzPayloadId = wzPayloadId; + args.dw64Progress = dw64Progress; + args.dw64Total = dw64Total; + args.dwOverallPercentage = dwOverallPercentage; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPAYLOADEXTRACTPROGRESS, &args, &results); + ExitOnFailure(hr, "BA OnCachePayloadExtractProgress failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + EXTERN_C BAAPI UserExperienceOnCacheVerifyBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z_opt LPCWSTR wzPackageOrContainerId, diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index d3dfb810..a848e60d 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -196,6 +196,25 @@ BAAPI UserExperienceOnCachePackageComplete( __in HRESULT hrStatus, __inout BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION* pAction ); +BAAPI UserExperienceOnCachePayloadExtractBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzContainerId, + __in_z_opt LPCWSTR wzPayloadId + ); +BAAPI UserExperienceOnCachePayloadExtractComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in HRESULT hrStatus + ); +BAAPI UserExperienceOnCachePayloadExtractProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in DWORD dwOverallPercentage + ); BAAPI UserExperienceOnCacheVerifyBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z_opt LPCWSTR wzPackageOrContainerId, -- cgit v1.2.3-55-g6feb From cc240536956e3ef6981599dfff05aa5628e910ac Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 16 Apr 2021 13:40:25 -0500 Subject: Perform all layout operations in the BA process. --- src/engine/apply.cpp | 27 +++--- src/engine/elevation.cpp | 214 +++++++---------------------------------------- src/engine/elevation.h | 21 ++--- 3 files changed, 45 insertions(+), 217 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index ab7fa077..cf904405 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -1076,13 +1076,13 @@ static HRESULT ApplyCacheVerifyContainerOrPayload( hr = UserExperienceOnCacheContainerOrPayloadVerifyBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId); ExitOnRootFailure(hr, "BA aborted cache container or payload verify begin."); - if (INVALID_HANDLE_VALUE != pContext->hPipe) + if (pContainer) { - hr = ElevationCacheVerifyContainerOrPayload(pContext->hPipe, pContainer, pPackage, pPayloadGroupItem->pPayload, pContext->wzLayoutDirectory); + hr = CacheVerifyContainer(pContainer, pContext->wzLayoutDirectory); } - else if (pContainer) + else if (!pContext->wzLayoutDirectory && INVALID_HANDLE_VALUE != pContext->hPipe) { - hr = CacheVerifyContainer(pContainer, pContext->wzLayoutDirectory); + hr = ElevationCacheVerifyPayload(pContext->hPipe, pPackage, pPayloadGroupItem->pPayload); } else { @@ -1289,14 +1289,7 @@ static HRESULT LayoutBundle( hr = UserExperienceOnCacheVerifyBegin(pContext->pUX, NULL, NULL); ExitOnRootFailure(hr, "BA aborted cache verify begin."); - if (INVALID_HANDLE_VALUE != pContext->hPipe) - { - hr = ElevationLayoutBundle(pContext->hPipe, pContext->wzLayoutDirectory, wzUnverifiedPath); - } - else - { - hr = CacheLayoutBundle(wzExecutableName, pContext->wzLayoutDirectory, wzUnverifiedPath); - } + hr = CacheLayoutBundle(wzExecutableName, pContext->wzLayoutDirectory, wzUnverifiedPath); if (SUCCEEDED(hr)) { @@ -1539,11 +1532,7 @@ static HRESULT LayoutOrCacheContainerOrPayload( hr = UserExperienceOnCacheVerifyBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId); ExitOnRootFailure(hr, "BA aborted cache verify begin."); - if (INVALID_HANDLE_VALUE != pContext->hPipe) // pass the decision off to the elevated process. - { - hr = ElevationCacheOrLayoutContainerOrPayload(pContext->hPipe, pContainer, pPackage, pPayload, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove); - } - else if (pContext->wzLayoutDirectory) // layout the container or payload. + if (pContext->wzLayoutDirectory) // layout the container or payload. { if (pContainer) { @@ -1554,6 +1543,10 @@ static HRESULT LayoutOrCacheContainerOrPayload( hr = CacheLayoutPayload(pPayload, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove); } } + else if (INVALID_HANDLE_VALUE != pContext->hPipe) // pass the decision off to the elevated process. + { + hr = ElevationCacheCompletePayload(pContext->hPipe, pPackage, pPayload, wzUnverifiedPath, fMove); + } else // complete the payload. { hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, wzUnverifiedPath, fMove); diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp index 2dd61a81..502c5462 100644 --- a/src/engine/elevation.cpp +++ b/src/engine/elevation.cpp @@ -14,9 +14,8 @@ typedef enum _BURN_ELEVATION_MESSAGE_TYPE BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME, BURN_ELEVATION_MESSAGE_TYPE_SESSION_END, BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE, - BURN_ELEVATION_MESSAGE_TYPE_LAYOUT_BUNDLE, - BURN_ELEVATION_MESSAGE_TYPE_CACHE_OR_LAYOUT_CONTAINER_OR_PAYLOAD, - BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_CONTAINER_OR_PAYLOAD, + BURN_ELEVATION_MESSAGE_TYPE_CACHE_COMPLETE_PAYLOAD, + BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_PAYLOAD, BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP, BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE, @@ -165,20 +164,13 @@ static HRESULT OnSaveState( __in BYTE* pbData, __in DWORD cbData ); -static HRESULT OnLayoutBundle( - __in_z LPCWSTR wzExecutableName, - __in BYTE* pbData, - __in DWORD cbData - ); -static HRESULT OnCacheOrLayoutContainerOrPayload( - __in BURN_CONTAINERS* pContainers, +static HRESULT OnCacheCompletePayload( __in BURN_PACKAGES* pPackages, __in BURN_PAYLOADS* pPayloads, __in BYTE* pbData, __in DWORD cbData ); -static HRESULT OnCacheVerifyContainerOrPayload( - __in BURN_CONTAINERS* pContainers, +static HRESULT OnCacheVerifyPayload( __in BURN_PACKAGES* pPackages, __in BURN_PAYLOADS* pPayloads, __in BYTE* pbData, @@ -582,49 +574,13 @@ LExit: } /******************************************************************* - ElevationLayoutBundle - + ElevationCacheCompletePayload - *******************************************************************/ -extern "C" HRESULT ElevationLayoutBundle( +extern "C" HRESULT ElevationCacheCompletePayload( __in HANDLE hPipe, - __in_z LPCWSTR wzLayoutDirectory, - __in_z LPCWSTR wzUnverifiedPath - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwResult = 0; - - // serialize message data - hr = BuffWriteString(&pbData, &cbData, wzLayoutDirectory); - ExitOnFailure(hr, "Failed to write layout directory to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, wzUnverifiedPath); - ExitOnFailure(hr, "Failed to write payload unverified path to message buffer."); - - // send message - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LAYOUT_BUNDLE, pbData, cbData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LAYOUT_BUNDLE message to per-machine process."); - - hr = (HRESULT)dwResult; - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -/******************************************************************* - ElevationCacheOrLayoutPayload - - -*******************************************************************/ -extern "C" HRESULT ElevationCacheOrLayoutContainerOrPayload( - __in HANDLE hPipe, - __in_opt BURN_CONTAINER* pContainer, - __in_opt BURN_PACKAGE* pPackage, - __in_opt BURN_PAYLOAD* pPayload, - __in_z_opt LPCWSTR wzLayoutDirectory, + __in BURN_PACKAGE* pPackage, + __in BURN_PAYLOAD* pPayload, __in_z LPCWSTR wzUnverifiedPath, __in BOOL fMove ) @@ -635,18 +591,12 @@ extern "C" HRESULT ElevationCacheOrLayoutContainerOrPayload( DWORD dwResult = 0; // serialize message data - hr = BuffWriteString(&pbData, &cbData, pContainer ? pContainer->sczId : NULL); - ExitOnFailure(hr, "Failed to write container id to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pPackage ? pPackage->sczId : NULL); + hr = BuffWriteString(&pbData, &cbData, pPackage->sczId); ExitOnFailure(hr, "Failed to write package id to message buffer."); - hr = BuffWriteString(&pbData, &cbData, pPayload ? pPayload->sczKey : NULL); + hr = BuffWriteString(&pbData, &cbData, pPayload->sczKey); ExitOnFailure(hr, "Failed to write payload id to message buffer."); - hr = BuffWriteString(&pbData, &cbData, wzLayoutDirectory); - ExitOnFailure(hr, "Failed to write layout directory to message buffer."); - hr = BuffWriteString(&pbData, &cbData, wzUnverifiedPath); ExitOnFailure(hr, "Failed to write unverified path to message buffer."); @@ -654,8 +604,8 @@ extern "C" HRESULT ElevationCacheOrLayoutContainerOrPayload( ExitOnFailure(hr, "Failed to write move flag to message buffer."); // send message - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_OR_LAYOUT_CONTAINER_OR_PAYLOAD, pbData, cbData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_OR_LAYOUT_CONTAINER_OR_PAYLOAD message to per-machine process."); + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_COMPLETE_PAYLOAD, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_COMPLETE_PAYLOAD message to per-machine process."); hr = (HRESULT)dwResult; @@ -665,12 +615,10 @@ LExit: return hr; } -extern "C" HRESULT ElevationCacheVerifyContainerOrPayload( +extern "C" HRESULT ElevationCacheVerifyPayload( __in HANDLE hPipe, - __in_opt BURN_CONTAINER* pContainer, - __in_opt BURN_PACKAGE* pPackage, - __in_opt BURN_PAYLOAD* pPayload, - __in_z_opt LPCWSTR wzLayoutDirectory + __in BURN_PACKAGE* pPackage, + __in BURN_PAYLOAD* pPayload ) { HRESULT hr = S_OK; @@ -679,21 +627,15 @@ extern "C" HRESULT ElevationCacheVerifyContainerOrPayload( DWORD dwResult = 0; // serialize message data - hr = BuffWriteString(&pbData, &cbData, pContainer ? pContainer->sczId : NULL); - ExitOnFailure(hr, "Failed to write container id to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pPackage ? pPackage->sczId : NULL); + hr = BuffWriteString(&pbData, &cbData, pPackage->sczId); ExitOnFailure(hr, "Failed to write package id to message buffer."); - hr = BuffWriteString(&pbData, &cbData, pPayload ? pPayload->sczKey : NULL); + hr = BuffWriteString(&pbData, &cbData, pPayload->sczKey); ExitOnFailure(hr, "Failed to write payload id to message buffer."); - hr = BuffWriteString(&pbData, &cbData, wzLayoutDirectory); - ExitOnFailure(hr, "Failed to write layout directory to message buffer."); - // send message - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_CONTAINER_OR_PAYLOAD, pbData, cbData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_CONTAINER_OR_PAYLOAD message to per-machine process."); + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_PAYLOAD, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_PAYLOAD message to per-machine process."); hr = (HRESULT)dwResult; @@ -1762,16 +1704,12 @@ static HRESULT ProcessElevatedChildCacheMessage( switch (pMsg->dwMessage) { - case BURN_ELEVATION_MESSAGE_TYPE_LAYOUT_BUNDLE: - hrResult = OnLayoutBundle(pContext->pRegistration->sczExecutableName, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_CACHE_OR_LAYOUT_CONTAINER_OR_PAYLOAD: - hrResult = OnCacheOrLayoutContainerOrPayload(pContext->pContainers, pContext->pPackages, pContext->pPayloads, (BYTE*)pMsg->pvData, pMsg->cbData); + case BURN_ELEVATION_MESSAGE_TYPE_CACHE_COMPLETE_PAYLOAD: + hrResult = OnCacheCompletePayload(pContext->pPackages, pContext->pPayloads, (BYTE*)pMsg->pvData, pMsg->cbData); break; - case BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_CONTAINER_OR_PAYLOAD: - hrResult = OnCacheVerifyContainerOrPayload(pContext->pContainers, pContext->pPackages, pContext->pPayloads, (BYTE*)pMsg->pvData, pMsg->cbData); + case BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_PAYLOAD: + hrResult = OnCacheVerifyPayload(pContext->pPackages, pContext->pPayloads, (BYTE*)pMsg->pvData, pMsg->cbData); break; case BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP: @@ -2063,37 +2001,7 @@ LExit: return hr; } -static HRESULT OnLayoutBundle( - __in_z LPCWSTR wzExecutableName, - __in BYTE* pbData, - __in DWORD cbData - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - LPWSTR sczLayoutDirectory = NULL; - LPWSTR sczUnverifiedPath = NULL; - - // Deserialize message data. - hr = BuffReadString(pbData, cbData, &iData, &sczLayoutDirectory); - ExitOnFailure(hr, "Failed to read layout directory."); - - hr = BuffReadString(pbData, cbData, &iData, &sczUnverifiedPath); - ExitOnFailure(hr, "Failed to read unverified bundle path."); - - // Layout the bundle. - hr = CacheLayoutBundle(wzExecutableName, sczLayoutDirectory, sczUnverifiedPath); - ExitOnFailure(hr, "Failed to layout bundle from: %ls", sczUnverifiedPath); - -LExit: - ReleaseStr(sczUnverifiedPath); - ReleaseStr(sczLayoutDirectory); - - return hr; -} - -static HRESULT OnCacheOrLayoutContainerOrPayload( - __in BURN_CONTAINERS* pContainers, +static HRESULT OnCacheCompletePayload( __in BURN_PACKAGES* pPackages, __in BURN_PAYLOADS* pPayloads, __in BYTE* pbData, @@ -2103,23 +2011,12 @@ static HRESULT OnCacheOrLayoutContainerOrPayload( HRESULT hr = S_OK; SIZE_T iData = 0; LPWSTR scz = NULL; - BURN_CONTAINER* pContainer = NULL; BURN_PACKAGE* pPackage = NULL; BURN_PAYLOAD* pPayload = NULL; - LPWSTR sczLayoutDirectory = NULL; LPWSTR sczUnverifiedPath = NULL; BOOL fMove = FALSE; // Deserialize message data. - hr = BuffReadString(pbData, cbData, &iData, &scz); - ExitOnFailure(hr, "Failed to read container id."); - - if (scz && *scz) - { - hr = ContainerFindById(pContainers, scz, &pContainer); - ExitOnFailure(hr, "Failed to find container: %ls", scz); - } - hr = BuffReadString(pbData, cbData, &iData, &scz); ExitOnFailure(hr, "Failed to read package id."); @@ -2138,55 +2035,31 @@ static HRESULT OnCacheOrLayoutContainerOrPayload( ExitOnFailure(hr, "Failed to find payload: %ls", scz); } - hr = BuffReadString(pbData, cbData, &iData, &sczLayoutDirectory); - ExitOnFailure(hr, "Failed to read layout directory."); - hr = BuffReadString(pbData, cbData, &iData, &sczUnverifiedPath); ExitOnFailure(hr, "Failed to read unverified path."); hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fMove); ExitOnFailure(hr, "Failed to read move flag."); - // Layout payload. - if (sczLayoutDirectory && *sczLayoutDirectory) + if (pPackage && pPayload) // complete payload. { - if (pContainer) - { - Assert(!pPackage); - Assert(!pPayload); - - hr = CacheLayoutContainer(pContainer, sczLayoutDirectory, sczUnverifiedPath, fMove); - ExitOnFailure(hr, "Failed to layout container from: %ls to %ls", sczUnverifiedPath, sczLayoutDirectory); - } - else - { - hr = CacheLayoutPayload(pPayload, sczLayoutDirectory, sczUnverifiedPath, fMove); - ExitOnFailure(hr, "Failed to layout payload from: %ls to %ls", sczUnverifiedPath, sczLayoutDirectory); - } - } - else if (pPackage) // complete payload. - { - Assert(!pContainer); - hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, sczUnverifiedPath, fMove); ExitOnFailure(hr, "Failed to cache payload: %ls", pPayload->sczKey); } else { hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Invalid data passed to cache or layout payload."); + ExitOnRootFailure(hr, "Invalid data passed to cache complete payload."); } LExit: ReleaseStr(sczUnverifiedPath); - ReleaseStr(sczLayoutDirectory); ReleaseStr(scz); return hr; } -static HRESULT OnCacheVerifyContainerOrPayload( - __in BURN_CONTAINERS* pContainers, +static HRESULT OnCacheVerifyPayload( __in BURN_PACKAGES* pPackages, __in BURN_PAYLOADS* pPayloads, __in BYTE* pbData, @@ -2196,21 +2069,11 @@ static HRESULT OnCacheVerifyContainerOrPayload( HRESULT hr = S_OK; SIZE_T iData = 0; LPWSTR scz = NULL; - BURN_CONTAINER* pContainer = NULL; BURN_PACKAGE* pPackage = NULL; BURN_PAYLOAD* pPayload = NULL; LPWSTR sczCacheDirectory = NULL; // Deserialize message data. - hr = BuffReadString(pbData, cbData, &iData, &scz); - ExitOnFailure(hr, "Failed to read container id."); - - if (scz && *scz) - { - hr = ContainerFindById(pContainers, scz, &pContainer); - ExitOnFailure(hr, "Failed to find container: %ls", scz); - } - hr = BuffReadString(pbData, cbData, &iData, &scz); ExitOnFailure(hr, "Failed to read package id."); @@ -2229,36 +2092,17 @@ static HRESULT OnCacheVerifyContainerOrPayload( ExitOnFailure(hr, "Failed to find payload: %ls", scz); } - hr = BuffReadString(pbData, cbData, &iData, &sczCacheDirectory); - ExitOnFailure(hr, "Failed to read layout directory."); - - if (!sczCacheDirectory || !*sczCacheDirectory) + if (pPackage && pPayload) { - if (!pPackage) - { - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Invalid data passed to cache verify payload."); - } - hr = CacheGetCompletedPath(TRUE, pPackage->sczCacheId, &sczCacheDirectory); ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", pPackage->sczCacheId); - } - - if (pContainer) - { - Assert(!pPackage); - Assert(!pPayload); - hr = CacheVerifyContainer(pContainer, sczCacheDirectory); - } - else if (pPayload) - { hr = CacheVerifyPayload(pPayload, sczCacheDirectory); } else { hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Invalid data passed to cache or layout payload."); + ExitOnRootFailure(hr, "Invalid data passed to cache verify payload."); } // Nothing should be logged on failure. diff --git a/src/engine/elevation.h b/src/engine/elevation.h index da67e3f3..fd7ee110 100644 --- a/src/engine/elevation.h +++ b/src/engine/elevation.h @@ -50,26 +50,17 @@ HRESULT ElevationSaveState( __in_bcount(cbBuffer) BYTE* pbBuffer, __in SIZE_T cbBuffer ); -HRESULT ElevationLayoutBundle( +HRESULT ElevationCacheCompletePayload( __in HANDLE hPipe, - __in_z LPCWSTR wzLayoutDirectory, - __in_z LPCWSTR wzUnverifiedPath - ); -HRESULT ElevationCacheOrLayoutContainerOrPayload( - __in HANDLE hPipe, - __in_opt BURN_CONTAINER* pContainer, - __in_opt BURN_PACKAGE* pPackage, - __in_opt BURN_PAYLOAD* pPayload, - __in_z_opt LPCWSTR wzLayoutDirectory, + __in BURN_PACKAGE* pPackage, + __in BURN_PAYLOAD* pPayload, __in_z LPCWSTR wzUnverifiedPath, __in BOOL fMove ); -HRESULT ElevationCacheVerifyContainerOrPayload( +HRESULT ElevationCacheVerifyPayload( __in HANDLE hPipe, - __in_opt BURN_CONTAINER* pContainer, - __in_opt BURN_PACKAGE* pPackage, - __in_opt BURN_PAYLOAD* pPayload, - __in_z_opt LPCWSTR wzLayoutDirectory + __in BURN_PACKAGE* pPackage, + __in BURN_PAYLOAD* pPayload ); HRESULT ElevationCacheCleanup( __in HANDLE hPipe -- cgit v1.2.3-55-g6feb From 26151ceeb5c57e3fd0bf73e9c13d8d72b41cce74 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 16 Apr 2021 13:53:26 -0500 Subject: Make sure OnCache*Begin events always pair with their complete event. --- src/engine/apply.cpp | 121 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 85 insertions(+), 36 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index cf904405..304c88f4 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -802,26 +802,34 @@ static HRESULT ApplyCachePackage( ) { HRESULT hr = S_OK; + BOOL fCanceledBegin = FALSE; BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION cachePackageCompleteAction = BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE; for (;;) { - hr = UserExperienceOnCachePackageBegin(pContext->pUX, pPackage->sczId, pPackage->payloads.cItems, pPackage->payloads.qwTotalSize); - LogExitOnFailure(hr, MSG_USER_CANCELED, "Cancel during cache: %ls: %ls", L"begin cache package", pPackage->sczId); + fCanceledBegin = FALSE; - for (DWORD i = 0; i < pPackage->payloads.cItems; ++i) + hr = UserExperienceOnCachePackageBegin(pContext->pUX, pPackage->sczId, pPackage->payloads.cItems, pPackage->payloads.qwTotalSize); + if (FAILED(hr)) { - BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem = pPackage->payloads.rgItems + i; - - hr = ApplyProcessPayload(pContext, pPackage, pPayloadGroupItem); - if (FAILED(hr)) + fCanceledBegin = TRUE; + } + else + { + for (DWORD i = 0; i < pPackage->payloads.cItems; ++i) { - break; + BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem = pPackage->payloads.rgItems + i; + + hr = ApplyProcessPayload(pContext, pPackage, pPayloadGroupItem); + if (FAILED(hr)) + { + break; + } } } pPackage->hrCacheResult = hr; - cachePackageCompleteAction = SUCCEEDED(hr) || pPackage->fVital ? BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE; + cachePackageCompleteAction = SUCCEEDED(hr) || pPackage->fVital || fCanceledBegin ? BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE; UserExperienceOnCachePackageComplete(pContext->pUX, pPackage->sczId, hr, &cachePackageCompleteAction); if (SUCCEEDED(hr)) @@ -856,6 +864,10 @@ static HRESULT ApplyCachePackage( LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, pPackage->sczId, hr); hr = S_OK; } + else if (fCanceledBegin) + { + LogExitOnFailure(hr, MSG_USER_CANCELED, "Cancel during cache: %ls: %ls", L"begin cache package", pPackage->sczId); + } break; } @@ -1208,6 +1220,7 @@ static HRESULT LayoutBundle( BURN_CACHE_PROGRESS_CONTEXT progress = { }; BOOL fRetry = FALSE; BOOL fRetryAcquire = FALSE; + BOOL fCanceledBegin = FALSE; progress.pCacheContext = pContext; @@ -1259,17 +1272,24 @@ static HRESULT LayoutBundle( { fRetryAcquire = FALSE; progress.fCancel = FALSE; + fCanceledBegin = FALSE; hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, NULL, NULL, &sczBundlePath, &sczBundleDownloadUrl, NULL, &cacheOperation); - ExitOnRootFailure(hr, "BA aborted cache acquire begin."); - hr = CopyPayload(&progress, pContext->hSourceEngineFile, sczBundlePath, wzUnverifiedPath); - // Error handling happens after sending complete message to BA. - - // If succeeded, send 100% complete here to make sure progress was sent to the BA. - if (SUCCEEDED(hr)) + if (FAILED(hr)) { - hr = CompleteCacheProgress(&progress, qwBundleSize); + fCanceledBegin = TRUE; + } + else + { + hr = CopyPayload(&progress, pContext->hSourceEngineFile, sczBundlePath, wzUnverifiedPath); + // Error handling happens after sending complete message to BA. + + // If succeeded, send 100% complete here to make sure progress was sent to the BA. + if (SUCCEEDED(hr)) + { + hr = CompleteCacheProgress(&progress, qwBundleSize); + } } UserExperienceOnCacheAcquireComplete(pContext->pUX, NULL, NULL, hr, &fRetryAcquire); @@ -1277,6 +1297,10 @@ static HRESULT LayoutBundle( { continue; } + else if (fCanceledBegin) + { + ExitOnRootFailure(hr, "BA aborted cache acquire begin."); + } ExitOnFailure(hr, "Failed to copy bundle from: '%ls' to: '%ls'", sczBundlePath, wzUnverifiedPath); break; @@ -1286,14 +1310,22 @@ static HRESULT LayoutBundle( do { - hr = UserExperienceOnCacheVerifyBegin(pContext->pUX, NULL, NULL); - ExitOnRootFailure(hr, "BA aborted cache verify begin."); + fCanceledBegin = FALSE; - hr = CacheLayoutBundle(wzExecutableName, pContext->wzLayoutDirectory, wzUnverifiedPath); + hr = UserExperienceOnCacheVerifyBegin(pContext->pUX, NULL, NULL); - if (SUCCEEDED(hr)) + if (FAILED(hr)) { - hr = CompleteCacheProgress(&progress, qwBundleSize); + fCanceledBegin = TRUE; + } + else + { + hr = CacheLayoutBundle(wzExecutableName, pContext->wzLayoutDirectory, wzUnverifiedPath); + + if (SUCCEEDED(hr)) + { + hr = CompleteCacheProgress(&progress, qwBundleSize); + } } BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE; @@ -1306,6 +1338,10 @@ static HRESULT LayoutBundle( { fRetry = TRUE; // go back and retry acquire. } + else if (fCanceledBegin) + { + ExitOnRootFailure(hr, "BA aborted cache verify begin."); + } } while (S_FALSE == hr); if (fRetry) @@ -1500,6 +1536,7 @@ static HRESULT LayoutOrCacheContainerOrPayload( BOOL fCanAffectRegistration = FALSE; BURN_CACHE_PROGRESS_CONTEXT progress = { }; BOOL fMove = !pPayload || 1 == pPayload->cRemainingInstances; + BOOL fCanceledBegin = FALSE; if (pContainer) { @@ -1529,27 +1566,35 @@ static HRESULT LayoutOrCacheContainerOrPayload( do { + fCanceledBegin = FALSE; + hr = UserExperienceOnCacheVerifyBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId); - ExitOnRootFailure(hr, "BA aborted cache verify begin."); - if (pContext->wzLayoutDirectory) // layout the container or payload. + if (FAILED(hr)) + { + fCanceledBegin = TRUE; + } + else { - if (pContainer) + if (pContext->wzLayoutDirectory) // layout the container or payload. { - hr = CacheLayoutContainer(pContainer, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove); + if (pContainer) + { + hr = CacheLayoutContainer(pContainer, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove); + } + else + { + hr = CacheLayoutPayload(pPayload, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove); + } } - else + else if (INVALID_HANDLE_VALUE != pContext->hPipe) // pass the decision off to the elevated process. { - hr = CacheLayoutPayload(pPayload, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove); + hr = ElevationCacheCompletePayload(pContext->hPipe, pPackage, pPayload, wzUnverifiedPath, fMove); + } + else // complete the payload. + { + hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, wzUnverifiedPath, fMove); } - } - else if (INVALID_HANDLE_VALUE != pContext->hPipe) // pass the decision off to the elevated process. - { - hr = ElevationCacheCompletePayload(pContext->hPipe, pPackage, pPayload, wzUnverifiedPath, fMove); - } - else // complete the payload. - { - hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, wzUnverifiedPath, fMove); } if (SUCCEEDED(hr)) @@ -1562,7 +1607,7 @@ static HRESULT LayoutOrCacheContainerOrPayload( pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; } - BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = FAILED(hr) && cTryAgainAttempts < BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS ? BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION : BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE; + BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = FAILED(hr) && !fCanceledBegin && cTryAgainAttempts < BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS ? BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION : BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE; UserExperienceOnCacheVerifyComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &action); if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action) { @@ -1572,6 +1617,10 @@ static HRESULT LayoutOrCacheContainerOrPayload( { *pfRetry = TRUE; // go back and retry acquire. } + else if (fCanceledBegin) + { + ExitOnRootFailure(hr, "BA aborted cache verify begin."); + } } while (S_FALSE == hr); if (SUCCEEDED(hr) && pPayloadGroupItem) -- cgit v1.2.3-55-g6feb From d4c76dd11f5a096b4fd3ee9c5efc1f44559ac3da Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 17 Apr 2021 20:58:32 -0500 Subject: Send more progress during cache verification. Add verifyStep to OnCacheVerifyProgress. Send OnContainerOrPayloadVerify events only if file existed in cache. --- .../inc/BootstrapperApplication.h | 8 + src/engine/apply.cpp | 171 +++++++++----- src/engine/cache.cpp | 263 ++++++++++++++++----- src/engine/cache.h | 74 +++++- src/engine/elevation.cpp | 253 +++++++++++++++++++- src/engine/elevation.h | 10 +- src/engine/plan.cpp | 18 +- src/engine/userexperience.cpp | 4 +- src/engine/userexperience.h | 3 +- src/test/BurnUnitTest/CacheTest.cpp | 46 +++- src/test/BurnUnitTest/PlanTest.cpp | 10 +- 11 files changed, 712 insertions(+), 148 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h index e1920107..d8994c26 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -64,6 +64,13 @@ enum BOOTSTRAPPER_CACHE_OPERATION BOOTSTRAPPER_CACHE_OPERATION_EXTRACT, }; +enum BOOTSTRAPPER_CACHE_VERIFY_STEP +{ + BOOTSTRAPPER_CACHE_VERIFY_STEP_STAGE, + BOOTSTRAPPER_CACHE_VERIFY_STEP_HASH, + BOOTSTRAPPER_CACHE_VERIFY_STEP_FINALIZE, +}; + enum BOOTSTRAPPER_APPLY_RESTART { BOOTSTRAPPER_APPLY_RESTART_NONE, @@ -553,6 +560,7 @@ struct BA_ONCACHEVERIFYPROGRESS_ARGS DWORD64 dw64Progress; DWORD64 dw64Total; DWORD dwOverallPercentage; + BOOTSTRAPPER_CACHE_VERIFY_STEP verifyStep; }; struct BA_ONCACHEVERIFYPROGRESS_RESULTS diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 304c88f4..6f6225b4 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -14,9 +14,12 @@ const DWORD BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS = 2; enum BURN_CACHE_PROGRESS_TYPE { BURN_CACHE_PROGRESS_TYPE_ACQUIRE, - BURN_CACHE_PROGRESS_TYPE_VERIFY, BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY, BURN_CACHE_PROGRESS_TYPE_EXTRACT, + BURN_CACHE_PROGRESS_TYPE_FINALIZE, + BURN_CACHE_PROGRESS_TYPE_HASH, + BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY, + BURN_CACHE_PROGRESS_TYPE_STAGE, }; // structs @@ -151,6 +154,10 @@ static HRESULT DownloadPayload( __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, __in_z LPCWSTR wzDestinationPath ); +static HRESULT CALLBACK CacheMessageHandler( + __in BURN_CACHE_MESSAGE* pMessage, + __in LPVOID pvContext + ); static HRESULT CompleteCacheProgress( __in BURN_CACHE_PROGRESS_CONTEXT* pContext, __in DWORD64 qwFileSize @@ -993,7 +1000,8 @@ static HRESULT ApplyLayoutContainer( } ++cTryAgainAttempts; - pContext->qwSuccessfulCacheProgress -= pContainer->qwFileSize; + pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedCacheProgress; + pContainer->qwCommittedCacheProgress = 0; ReleaseNullStr(pContext->sczLastUsedFolderCandidate); LogErrorId(hr, MSG_APPLY_RETRYING_CONTAINER, pContainer->sczId, NULL, NULL); } @@ -1075,49 +1083,25 @@ static HRESULT ApplyCacheVerifyContainerOrPayload( HRESULT hr = S_OK; BURN_CACHE_PROGRESS_CONTEXT progress = { }; - LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : NULL; - LPCWSTR wzPayloadId = pPayloadGroupItem ? pPayloadGroupItem->pPayload->sczKey : NULL; - DWORD64 qwFileSize = pContainer ? pContainer->qwFileSize : pPayloadGroupItem->pPayload->qwFileSize; progress.pCacheContext = pContext; progress.pContainer = pContainer; progress.pPackage = pPackage; progress.pPayloadGroupItem = pPayloadGroupItem; - progress.type = BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY; - - hr = UserExperienceOnCacheContainerOrPayloadVerifyBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId); - ExitOnRootFailure(hr, "BA aborted cache container or payload verify begin."); if (pContainer) { - hr = CacheVerifyContainer(pContainer, pContext->wzLayoutDirectory); + hr = CacheVerifyContainer(pContainer, pContext->wzLayoutDirectory, CacheMessageHandler, CacheProgressRoutine, &progress); } else if (!pContext->wzLayoutDirectory && INVALID_HANDLE_VALUE != pContext->hPipe) { - hr = ElevationCacheVerifyPayload(pContext->hPipe, pPackage, pPayloadGroupItem->pPayload); + hr = ElevationCacheVerifyPayload(pContext->hPipe, pPackage, pPayloadGroupItem->pPayload, CacheMessageHandler, CacheProgressRoutine, &progress); } else { - hr = CacheVerifyPayload(pPayloadGroupItem->pPayload, pContext->wzLayoutDirectory ? pContext->wzLayoutDirectory : pPackage->sczCacheFolder); - } - - // This was best effort to avoid acquiring the container or payload. - if (FAILED(hr)) - { - ExitFunction(); + hr = CacheVerifyPayload(pPayloadGroupItem->pPayload, pContext->wzLayoutDirectory ? pContext->wzLayoutDirectory : pPackage->sczCacheFolder, CacheMessageHandler, CacheProgressRoutine, &progress); } - pContext->qwSuccessfulCacheProgress += qwFileSize; - if (pPayloadGroupItem) - { - pPayloadGroupItem->qwCommittedCacheProgress += qwFileSize; - } - - hr = CompleteCacheProgress(&progress, qwFileSize); - -LExit: - UserExperienceOnCacheContainerOrPayloadVerifyComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr); - return hr; } @@ -1252,8 +1236,6 @@ static HRESULT LayoutBundle( ExitOnRootFailure(hr, "BA aborted cache payload verify begin."); } - pContext->qwSuccessfulCacheProgress += qwBundleSize; - progress.type = BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY; hr = CompleteCacheProgress(&progress, qwBundleSize); @@ -1306,8 +1288,6 @@ static HRESULT LayoutBundle( break; } - progress.type = BURN_CACHE_PROGRESS_TYPE_VERIFY; - do { fCanceledBegin = FALSE; @@ -1320,12 +1300,7 @@ static HRESULT LayoutBundle( } else { - hr = CacheLayoutBundle(wzExecutableName, pContext->wzLayoutDirectory, wzUnverifiedPath); - - if (SUCCEEDED(hr)) - { - hr = CompleteCacheProgress(&progress, qwBundleSize); - } + hr = CacheLayoutBundle(wzExecutableName, pContext->wzLayoutDirectory, wzUnverifiedPath, qwBundleSize, CacheMessageHandler, CacheProgressRoutine, &progress); } BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE; @@ -1346,7 +1321,7 @@ static HRESULT LayoutBundle( if (fRetry) { - pContext->qwSuccessfulCacheProgress -= qwBundleSize; + pContext->qwSuccessfulCacheProgress -= qwBundleSize; // Acquire } } while (fRetry); LogExitOnFailure(hr, MSG_FAILED_LAYOUT_BUNDLE, "Failed to layout bundle: %ls to layout directory: %ls", sczBundlePath, pContext->wzLayoutDirectory); @@ -1559,7 +1534,6 @@ static HRESULT LayoutOrCacheContainerOrPayload( *pfRetry = FALSE; progress.pCacheContext = pContext; - progress.type = BURN_CACHE_PROGRESS_TYPE_VERIFY; progress.pContainer = pContainer; progress.pPackage = pPackage; progress.pPayloadGroupItem = pPayloadGroupItem; @@ -1580,28 +1554,23 @@ static HRESULT LayoutOrCacheContainerOrPayload( { if (pContainer) { - hr = CacheLayoutContainer(pContainer, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove); + hr = CacheLayoutContainer(pContainer, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress); } else { - hr = CacheLayoutPayload(pPayload, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove); + hr = CacheLayoutPayload(pPayload, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress); } } else if (INVALID_HANDLE_VALUE != pContext->hPipe) // pass the decision off to the elevated process. { - hr = ElevationCacheCompletePayload(pContext->hPipe, pPackage, pPayload, wzUnverifiedPath, fMove); + hr = ElevationCacheCompletePayload(pContext->hPipe, pPackage, pPayload, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress); } else // complete the payload. { - hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, wzUnverifiedPath, fMove); + hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress); } } - if (SUCCEEDED(hr)) - { - hr = CompleteCacheProgress(&progress, pContainer ? pContainer->qwFileSize : pPayload->qwFileSize); - } - if (SUCCEEDED(hr) && fCanAffectRegistration) { pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; @@ -1820,6 +1789,54 @@ LExit: return hr; } +static HRESULT CALLBACK CacheMessageHandler( + __in BURN_CACHE_MESSAGE* pMessage, + __in LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + BURN_CACHE_PROGRESS_CONTEXT* pProgress = static_cast(pvContext); + LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : NULL; + LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : pProgress->pPayload ? pProgress->pPayload->sczKey : NULL; + + switch (pMessage->type) + { + case BURN_CACHE_MESSAGE_BEGIN: + switch (pMessage->begin.cacheStep) + { + case BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE: + pProgress->type = BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY; + hr = UserExperienceOnCacheContainerOrPayloadVerifyBegin(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId); + break; + case BURN_CACHE_STEP_HASH_TO_SKIP_VERIFY: + pProgress->type = BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY; + break; + case BURN_CACHE_STEP_STAGE: + pProgress->type = BURN_CACHE_PROGRESS_TYPE_STAGE; + break; + case BURN_CACHE_STEP_HASH: + pProgress->type = BURN_CACHE_PROGRESS_TYPE_HASH; + break; + case BURN_CACHE_STEP_FINALIZE: + pProgress->type = BURN_CACHE_PROGRESS_TYPE_FINALIZE; + break; + } + break; + case BURN_CACHE_MESSAGE_SUCCESS: + hr = CompleteCacheProgress(pProgress, pMessage->success.qwFileSize); + break; + case BURN_CACHE_MESSAGE_COMPLETE: + switch (pProgress->type) + { + case BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY: + hr = UserExperienceOnCacheContainerOrPayloadVerifyComplete(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, hr); + break; + } + } + + return hr; +} + static HRESULT CompleteCacheProgress( __in BURN_CACHE_PROGRESS_CONTEXT* pContext, __in DWORD64 qwFileSize @@ -1829,9 +1846,29 @@ static HRESULT CompleteCacheProgress( LARGE_INTEGER liContainerOrPayloadSize = { }; LARGE_INTEGER liZero = { }; DWORD dwResult = 0; + DWORD64 qwCommitSize = 0; liContainerOrPayloadSize.QuadPart = qwFileSize; + // Need to commit the steps that were skipped. + if (BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY == pContext->type || BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY == pContext->type) + { + Assert(!pContext->pPayload); + + qwCommitSize = qwFileSize * (pContext->pCacheContext->wzLayoutDirectory ? 2 : 3); // Acquire (+ Stage) + Hash + Finalize - 1 (that's added later) + + pContext->pCacheContext->qwSuccessfulCacheProgress += qwCommitSize; + + if (pContext->pContainer) + { + pContext->pContainer->qwCommittedCacheProgress += qwCommitSize; + } + else if (pContext->pPayloadGroupItem) + { + pContext->pPayloadGroupItem->qwCommittedCacheProgress += qwCommitSize; + } + } + dwResult = CacheProgressRoutine(liContainerOrPayloadSize, liContainerOrPayloadSize, liZero, liZero, 0, 0, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, pContext); if (PROGRESS_CONTINUE == dwResult) @@ -1851,7 +1888,7 @@ static HRESULT CompleteCacheProgress( pContext->pPayloadGroupItem->qwCommittedCacheProgress += qwFileSize; } - if (BURN_CACHE_PROGRESS_TYPE_VERIFY == pContext->type && pContext->pCacheContext->sczLastUsedFolderCandidate) + if (BURN_CACHE_PROGRESS_TYPE_FINALIZE == pContext->type && pContext->pCacheContext->sczLastUsedFolderCandidate) { // We successfully copied from a source location, set that as the last used source. CacheSetLastUsedSource(pContext->pCacheContext->pVariables, pContext->pCacheContext->sczLastUsedFolderCandidate, pContext->pContainer ? pContext->pContainer->sczFilePath : pContext->pPayloadGroupItem->pPayload->sczFilePath); @@ -1867,6 +1904,20 @@ static HRESULT CompleteCacheProgress( { hr = pContext->hrError; } + + if (qwCommitSize) + { + pContext->pCacheContext->qwSuccessfulCacheProgress -= qwCommitSize; + + if (pContext->pContainer) + { + pContext->pContainer->qwCommittedCacheProgress -= qwCommitSize; + } + else if (pContext->pPayloadGroupItem) + { + pContext->pPayloadGroupItem->qwCommittedCacheProgress -= qwCommitSize; + } + } } return hr; @@ -1903,9 +1954,21 @@ static DWORD CALLBACK CacheProgressRoutine( hr = UserExperienceOnCacheAcquireProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); ExitOnRootFailure(hr, "BA aborted acquire of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); break; - case BURN_CACHE_PROGRESS_TYPE_VERIFY: - hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); - ExitOnRootFailure(hr, "BA aborted verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); + case BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY: + hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_HASH); + ExitOnRootFailure(hr, "BA aborted payload verify step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); + break; + case BURN_CACHE_PROGRESS_TYPE_STAGE: + hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_STAGE); + ExitOnRootFailure(hr, "BA aborted stage step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); + break; + case BURN_CACHE_PROGRESS_TYPE_HASH: + hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_HASH); + ExitOnRootFailure(hr, "BA aborted hash step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); + break; + case BURN_CACHE_PROGRESS_TYPE_FINALIZE: + hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_FINALIZE); + ExitOnRootFailure(hr, "BA aborted finalize step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); break; case BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY: hr = UserExperienceOnCacheContainerOrPayloadVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); diff --git a/src/engine/cache.cpp b/src/engine/cache.cpp index 9aa94d1d..16f2c1e5 100644 --- a/src/engine/cache.cpp +++ b/src/engine/cache.cpp @@ -44,28 +44,47 @@ static HRESULT VerifyThenTransferContainer( __in BURN_CONTAINER* pContainer, __in_z LPCWSTR wzCachedPath, __in_z LPCWSTR wzUnverifiedContainerPath, - __in BOOL fMove + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ); static HRESULT VerifyThenTransferPayload( __in BURN_PAYLOAD* pPayload, __in_z LPCWSTR wzCachedPath, __in_z LPCWSTR wzUnverifiedPayloadPath, - __in BOOL fMove + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ); -static HRESULT TransferWorkingPathToUnverifiedPath( - __in_z LPCWSTR wzWorkingPath, - __in_z LPCWSTR wzUnverifiedPayloadPath, - __in BOOL fMove +static HRESULT CacheTransferFileWithRetry( + __in_z LPCWSTR wzSourcePath, + __in_z LPCWSTR wzDestinationPath, + __in BOOL fMove, + __in BURN_CACHE_STEP cacheStep, + __in DWORD64 qwFileSize, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ); static HRESULT VerifyFileAgainstContainer( __in BURN_CONTAINER* pContainer, __in_z LPCWSTR wzVerifyPath, - __in BOOL fAlreadyCached + __in BOOL fAlreadyCached, + __in BURN_CACHE_STEP cacheStep, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ); static HRESULT VerifyFileAgainstPayload( __in BURN_PAYLOAD* pPayload, __in_z LPCWSTR wzVerifyPath, - __in BOOL fAlreadyCached + __in BOOL fAlreadyCached, + __in BURN_CACHE_STEP cacheStep, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ); static HRESULT ResetPathPermissions( __in BOOL fPerMachine, @@ -99,7 +118,26 @@ static HRESULT VerifyHash( __in DWORD cbHash, __in DWORD64 qwFileSize, __in_z LPCWSTR wzUnverifiedPayloadPath, - __in HANDLE hFile + __in HANDLE hFile, + __in BURN_CACHE_STEP cacheStep, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ); +static HRESULT SendCacheBeginMessage( + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPVOID pContext, + __in BURN_CACHE_STEP cacheStep + ); +static HRESULT SendCacheSuccessMessage( + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPVOID pContext, + __in DWORD64 qwFileSize + ); +static HRESULT SendCacheCompleteMessage( + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPVOID pContext, + __in HRESULT hrStatus ); @@ -754,7 +792,11 @@ LExit: extern "C" HRESULT CacheLayoutBundle( __in_z LPCWSTR wzExecutableName, __in_z LPCWSTR wzLayoutDirectory, - __in_z LPCWSTR wzSourceBundlePath + __in_z LPCWSTR wzSourceBundlePath, + __in DWORD64 qwBundleSize, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ) { HRESULT hr = S_OK; @@ -765,7 +807,7 @@ extern "C" HRESULT CacheLayoutBundle( LogStringLine(REPORT_STANDARD, "Layout bundle from: '%ls' to: '%ls'", wzSourceBundlePath, sczTargetPath); - hr = FileEnsureMoveWithRetry(wzSourceBundlePath, sczTargetPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); + hr = CacheTransferFileWithRetry(wzSourceBundlePath, sczTargetPath, TRUE, BURN_CACHE_STEP_FINALIZE, qwBundleSize, pfnCacheMessageHandler, pfnProgress, pContext); ExitOnFailure(hr, "Failed to layout bundle from: '%ls' to '%ls'", wzSourceBundlePath, sczTargetPath); LExit: @@ -838,7 +880,10 @@ extern "C" HRESULT CacheLayoutContainer( __in BURN_CONTAINER* pContainer, __in_z_opt LPCWSTR wzLayoutDirectory, __in_z LPCWSTR wzUnverifiedContainerPath, - __in BOOL fMove + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ) { HRESULT hr = S_OK; @@ -847,7 +892,7 @@ extern "C" HRESULT CacheLayoutContainer( hr = PathConcat(wzLayoutDirectory, pContainer->sczFilePath, &sczCachedPath); ExitOnFailure(hr, "Failed to concat complete cached path."); - hr = VerifyThenTransferContainer(pContainer, sczCachedPath, wzUnverifiedContainerPath, fMove); + hr = VerifyThenTransferContainer(pContainer, sczCachedPath, wzUnverifiedContainerPath, fMove, pfnCacheMessageHandler, pfnProgress, pContext); ExitOnFailure(hr, "Failed to layout container from cached path: %ls", sczCachedPath); LExit: @@ -860,7 +905,10 @@ extern "C" HRESULT CacheLayoutPayload( __in BURN_PAYLOAD* pPayload, __in_z_opt LPCWSTR wzLayoutDirectory, __in_z LPCWSTR wzUnverifiedPayloadPath, - __in BOOL fMove + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ) { HRESULT hr = S_OK; @@ -869,7 +917,7 @@ extern "C" HRESULT CacheLayoutPayload( hr = PathConcat(wzLayoutDirectory, pPayload->sczFilePath, &sczCachedPath); ExitOnFailure(hr, "Failed to concat complete cached path."); - hr = VerifyThenTransferPayload(pPayload, sczCachedPath, wzUnverifiedPayloadPath, fMove); + hr = VerifyThenTransferPayload(pPayload, sczCachedPath, wzUnverifiedPayloadPath, fMove, pfnCacheMessageHandler, pfnProgress, pContext); ExitOnFailure(hr, "Failed to layout payload from cached payload: %ls", sczCachedPath); LExit: @@ -883,7 +931,10 @@ extern "C" HRESULT CacheCompletePayload( __in BURN_PAYLOAD* pPayload, __in_z LPCWSTR wzCacheId, __in_z LPCWSTR wzWorkingPayloadPath, - __in BOOL fMove + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ) { HRESULT hr = S_OK; @@ -898,7 +949,7 @@ extern "C" HRESULT CacheCompletePayload( ExitOnFailure(hr, "Failed to concat complete cached path."); // If the cached file matches what we expected, we're good. - hr = VerifyFileAgainstPayload(pPayload, sczCachedPath, TRUE); + hr = VerifyFileAgainstPayload(pPayload, sczCachedPath, TRUE, BURN_CACHE_STEP_HASH_TO_SKIP_VERIFY, pfnCacheMessageHandler, pfnProgress, pContext); if (SUCCEEDED(hr)) { ExitFunction(); @@ -910,10 +961,21 @@ extern "C" HRESULT CacheCompletePayload( // If the working path exists, let's get it into the unverified path so we can reset the ACLs and verify the file. if (FileExistsEx(wzWorkingPayloadPath, NULL)) { - hr = TransferWorkingPathToUnverifiedPath(wzWorkingPayloadPath, sczUnverifiedPayloadPath, fMove); + hr = CacheTransferFileWithRetry(wzWorkingPayloadPath, sczUnverifiedPayloadPath, fMove, BURN_CACHE_STEP_STAGE, pPayload->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext); ExitOnFailure(hr, "Failed to transfer working path to unverified path for payload: %ls.", pPayload->sczKey); } - else if (!FileExistsEx(sczUnverifiedPayloadPath, NULL)) // if the working path and unverified path do not exist, nothing we can do. + else if (FileExistsEx(sczUnverifiedPayloadPath, NULL)) + { + // Make sure the staging progress is sent even though there was nothing to do. + hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, BURN_CACHE_STEP_STAGE); + if (SUCCEEDED(hr)) + { + hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, pPayload->qwFileSize); + } + SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr); + ExitOnFailure(hr, "Aborted transferring working path to unverified path for payload: %ls.", pPayload->sczKey); + } + else // if the working path and unverified path do not exist, nothing we can do. { hr = E_FILENOTFOUND; ExitOnFailure(hr, "Failed to find payload: %ls in working path: %ls and unverified path: %ls", pPayload->sczKey, wzWorkingPayloadPath, sczUnverifiedPayloadPath); @@ -922,12 +984,12 @@ extern "C" HRESULT CacheCompletePayload( hr = ResetPathPermissions(fPerMachine, sczUnverifiedPayloadPath); ExitOnFailure(hr, "Failed to reset permissions on unverified cached payload: %ls", pPayload->sczKey); - hr = VerifyFileAgainstPayload(pPayload, sczUnverifiedPayloadPath, FALSE); + hr = VerifyFileAgainstPayload(pPayload, sczUnverifiedPayloadPath, FALSE, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext); LogExitOnFailure(hr, MSG_FAILED_VERIFY_PAYLOAD, "Failed to verify payload: %ls at path: %ls", pPayload->sczKey, sczUnverifiedPayloadPath, NULL); LogId(REPORT_STANDARD, MSG_VERIFIED_ACQUIRED_PAYLOAD, pPayload->sczKey, sczUnverifiedPayloadPath, fMove ? "moving" : "copying", sczCachedPath); - hr = FileEnsureMoveWithRetry(sczUnverifiedPayloadPath, sczCachedPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); + hr = CacheTransferFileWithRetry(sczUnverifiedPayloadPath, sczCachedPath, TRUE, BURN_CACHE_STEP_FINALIZE, pPayload->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext); ExitOnFailure(hr, "Failed to move verified file to complete payload path: %ls", sczCachedPath); ::DecryptFileW(sczCachedPath, 0); // Let's try to make sure it's not encrypted. @@ -942,7 +1004,10 @@ LExit: extern "C" HRESULT CacheVerifyContainer( __in BURN_CONTAINER* pContainer, - __in_z LPCWSTR wzCachedDirectory + __in_z LPCWSTR wzCachedDirectory, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ) { HRESULT hr = S_OK; @@ -951,7 +1016,7 @@ extern "C" HRESULT CacheVerifyContainer( hr = PathConcat(wzCachedDirectory, pContainer->sczFilePath, &sczCachedPath); ExitOnFailure(hr, "Failed to concat complete cached path."); - hr = VerifyFileAgainstContainer(pContainer, sczCachedPath, TRUE); + hr = VerifyFileAgainstContainer(pContainer, sczCachedPath, TRUE, BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE, pfnCacheMessageHandler, pfnProgress, pContext); LExit: ReleaseStr(sczCachedPath); @@ -961,7 +1026,10 @@ LExit: extern "C" HRESULT CacheVerifyPayload( __in BURN_PAYLOAD* pPayload, - __in_z LPCWSTR wzCachedDirectory + __in_z LPCWSTR wzCachedDirectory, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ) { HRESULT hr = S_OK; @@ -970,7 +1038,7 @@ extern "C" HRESULT CacheVerifyPayload( hr = PathConcat(wzCachedDirectory, pPayload->sczFilePath, &sczCachedPath); ExitOnFailure(hr, "Failed to concat complete cached path."); - hr = VerifyFileAgainstPayload(pPayload, sczCachedPath, TRUE); + hr = VerifyFileAgainstPayload(pPayload, sczCachedPath, TRUE, BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE, pfnCacheMessageHandler, pfnProgress, pContext); LExit: ReleaseStr(sczCachedPath); @@ -1342,7 +1410,10 @@ static HRESULT VerifyThenTransferContainer( __in BURN_CONTAINER* pContainer, __in_z LPCWSTR wzCachedPath, __in_z LPCWSTR wzUnverifiedContainerPath, - __in BOOL fMove + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ) { HRESULT hr = S_OK; @@ -1358,22 +1429,13 @@ static HRESULT VerifyThenTransferContainer( // Container should have a hash we can use to verify with. if (pContainer->pbHash) { - hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, pContainer->qwFileSize, wzUnverifiedContainerPath, hFile); + hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, pContainer->qwFileSize, wzUnverifiedContainerPath, hFile, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext); ExitOnFailure(hr, "Failed to verify container hash: %ls", wzCachedPath); } LogStringLine(REPORT_STANDARD, "%ls container from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedContainerPath, wzCachedPath); - if (fMove) - { - hr = FileEnsureMoveWithRetry(wzUnverifiedContainerPath, wzCachedPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); - ExitOnFailure(hr, "Failed to move %ls to %ls", wzUnverifiedContainerPath, wzCachedPath); - } - else - { - hr = FileEnsureCopyWithRetry(wzUnverifiedContainerPath, wzCachedPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); - ExitOnFailure(hr, "Failed to copy %ls to %ls", wzUnverifiedContainerPath, wzCachedPath); - } + hr = CacheTransferFileWithRetry(wzUnverifiedContainerPath, wzCachedPath, fMove, BURN_CACHE_STEP_FINALIZE, pContainer->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext); LExit: ReleaseFileHandle(hFile); @@ -1385,7 +1447,10 @@ static HRESULT VerifyThenTransferPayload( __in BURN_PAYLOAD* pPayload, __in_z LPCWSTR wzCachedPath, __in_z LPCWSTR wzUnverifiedPayloadPath, - __in BOOL fMove + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ) { HRESULT hr = S_OK; @@ -1400,22 +1465,13 @@ static HRESULT VerifyThenTransferPayload( if (pPayload->pbHash) // the payload should have a hash we can use to verify it. { - hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, wzUnverifiedPayloadPath, hFile); + hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, wzUnverifiedPayloadPath, hFile, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext); ExitOnFailure(hr, "Failed to verify payload hash: %ls", wzCachedPath); } LogStringLine(REPORT_STANDARD, "%ls payload from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedPayloadPath, wzCachedPath); - if (fMove) - { - hr = FileEnsureMoveWithRetry(wzUnverifiedPayloadPath, wzCachedPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); - ExitOnFailure(hr, "Failed to move %ls to %ls", wzUnverifiedPayloadPath, wzCachedPath); - } - else - { - hr = FileEnsureCopyWithRetry(wzUnverifiedPayloadPath, wzCachedPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); - ExitOnFailure(hr, "Failed to copy %ls to %ls", wzUnverifiedPayloadPath, wzCachedPath); - } + hr = CacheTransferFileWithRetry(wzUnverifiedPayloadPath, wzCachedPath, fMove, BURN_CACHE_STEP_FINALIZE, pPayload->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext); LExit: ReleaseFileHandle(hFile); @@ -1423,33 +1479,50 @@ LExit: return hr; } -static HRESULT TransferWorkingPathToUnverifiedPath( - __in_z LPCWSTR wzWorkingPath, - __in_z LPCWSTR wzUnverifiedPayloadPath, - __in BOOL fMove +static HRESULT CacheTransferFileWithRetry( + __in_z LPCWSTR wzSourcePath, + __in_z LPCWSTR wzDestinationPath, + __in BOOL fMove, + __in BURN_CACHE_STEP cacheStep, + __in DWORD64 qwFileSize, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE /*pfnProgress*/, + __in LPVOID pContext ) { HRESULT hr = S_OK; + hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, cacheStep); + ExitOnFailure(hr, "Aborted cache file transfer begin."); + + // TODO: send progress during the file transfer. if (fMove) { - hr = FileEnsureMoveWithRetry(wzWorkingPath, wzUnverifiedPayloadPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); - ExitOnFailure(hr, "Failed to move %ls to %ls", wzWorkingPath, wzUnverifiedPayloadPath); + hr = FileEnsureMoveWithRetry(wzSourcePath, wzDestinationPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); + ExitOnFailure(hr, "Failed to move %ls to %ls", wzSourcePath, wzDestinationPath); } else { - hr = FileEnsureCopyWithRetry(wzWorkingPath, wzUnverifiedPayloadPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); - ExitOnFailure(hr, "Failed to copy %ls to %ls", wzWorkingPath, wzUnverifiedPayloadPath); + hr = FileEnsureCopyWithRetry(wzSourcePath, wzDestinationPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); + ExitOnFailure(hr, "Failed to copy %ls to %ls", wzSourcePath, wzDestinationPath); } + hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, qwFileSize); + LExit: + SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr); + return hr; } static HRESULT VerifyFileAgainstContainer( __in BURN_CONTAINER* pContainer, __in_z LPCWSTR wzVerifyPath, - __in BOOL fAlreadyCached + __in BOOL fAlreadyCached, + __in BURN_CACHE_STEP cacheStep, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ) { HRESULT hr = S_OK; @@ -1469,7 +1542,7 @@ static HRESULT VerifyFileAgainstContainer( if (pContainer->pbHash) // the container should have a hash we can use to verify it. { - hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, pContainer->qwFileSize, wzVerifyPath, hFile); + hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, pContainer->qwFileSize, wzVerifyPath, hFile, cacheStep, pfnCacheMessageHandler, pfnProgress, pContext); ExitOnFailure(hr, "Failed to verify hash of container: %ls", pContainer->sczId); } @@ -1498,7 +1571,11 @@ LExit: static HRESULT VerifyFileAgainstPayload( __in BURN_PAYLOAD* pPayload, __in_z LPCWSTR wzVerifyPath, - __in BOOL fAlreadyCached + __in BOOL fAlreadyCached, + __in BURN_CACHE_STEP cacheStep, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ) { HRESULT hr = S_OK; @@ -1518,7 +1595,7 @@ static HRESULT VerifyFileAgainstPayload( if (pPayload->pbHash) // the payload should have a hash we can use to verify it. { - hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, wzVerifyPath, hFile); + hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, wzVerifyPath, hFile, cacheStep, pfnCacheMessageHandler, pfnProgress, pContext); ExitOnFailure(hr, "Failed to verify hash of payload: %ls", pPayload->sczKey); } @@ -1881,7 +1958,11 @@ static HRESULT VerifyHash( __in DWORD cbHash, __in DWORD64 qwFileSize, __in_z LPCWSTR wzUnverifiedPayloadPath, - __in HANDLE hFile + __in HANDLE hFile, + __in BURN_CACHE_STEP cacheStep, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE /*pfnProgress*/, + __in LPVOID pContext ) { UNREFERENCED_PARAMETER(wzUnverifiedPayloadPath); @@ -1893,6 +1974,9 @@ static HRESULT VerifyHash( LPWSTR pszExpected = NULL; LPWSTR pszActual = NULL; + hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, cacheStep); + ExitOnFailure(hr, "Aborted cache verify hash begin."); + hr = FileSizeByHandle(hFile, &llSize); ExitOnFailure(hr, "Failed to get file size for path: %ls", wzUnverifiedPayloadPath); @@ -1922,9 +2006,64 @@ static HRESULT VerifyHash( } } + hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, qwFileSize); + LExit: + SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr); + ReleaseStr(pszActual); ReleaseStr(pszExpected); return hr; } + +static HRESULT SendCacheBeginMessage( + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPVOID pContext, + __in BURN_CACHE_STEP cacheStep + ) +{ + HRESULT hr = S_OK; + BURN_CACHE_MESSAGE message = { }; + + message.type = BURN_CACHE_MESSAGE_BEGIN; + message.begin.cacheStep = cacheStep; + + hr = pfnCacheMessageHandler(&message, pContext); + + return hr; +} + +static HRESULT SendCacheSuccessMessage( + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPVOID pContext, + __in DWORD64 qwFileSize + ) +{ + HRESULT hr = S_OK; + BURN_CACHE_MESSAGE message = { }; + + message.type = BURN_CACHE_MESSAGE_SUCCESS; + message.success.qwFileSize = qwFileSize; + + hr = pfnCacheMessageHandler(&message, pContext); + + return hr; +} + +static HRESULT SendCacheCompleteMessage( + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPVOID pContext, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BURN_CACHE_MESSAGE message = { }; + + message.type = BURN_CACHE_MESSAGE_COMPLETE; + message.complete.hrStatus = hrStatus; + + hr = pfnCacheMessageHandler(&message, pContext); + + return hr; +} diff --git a/src/engine/cache.h b/src/engine/cache.h index cf062a85..cd964649 100644 --- a/src/engine/cache.h +++ b/src/engine/cache.h @@ -7,7 +7,48 @@ extern "C" { #endif -// structs + +enum BURN_CACHE_MESSAGE_TYPE +{ + BURN_CACHE_MESSAGE_BEGIN, + BURN_CACHE_MESSAGE_SUCCESS, + BURN_CACHE_MESSAGE_COMPLETE, +}; + +enum BURN_CACHE_STEP +{ + BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE, + BURN_CACHE_STEP_HASH_TO_SKIP_VERIFY, + BURN_CACHE_STEP_STAGE, + BURN_CACHE_STEP_HASH, + BURN_CACHE_STEP_FINALIZE, +}; + +typedef struct _BURN_CACHE_MESSAGE +{ + BURN_CACHE_MESSAGE_TYPE type; + + union + { + struct + { + BURN_CACHE_STEP cacheStep; + } begin; + struct + { + DWORD64 qwFileSize; + } success; + struct + { + HRESULT hrStatus; + } complete; + }; +} BURN_CACHE_MESSAGE; + +typedef HRESULT(CALLBACK* PFN_BURNCACHEMESSAGEHANDLER)( + __in BURN_CACHE_MESSAGE* pMessage, + __in LPVOID pvContext + ); // functions @@ -95,7 +136,11 @@ HRESULT CacheBundleToWorkingDirectory( HRESULT CacheLayoutBundle( __in_z LPCWSTR wzExecutableName, __in_z LPCWSTR wzLayoutDirectory, - __in_z LPCWSTR wzSourceBundlePath + __in_z LPCWSTR wzSourceBundlePath, + __in DWORD64 qwBundleSize, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ); HRESULT CacheCompleteBundle( __in BOOL fPerMachine, @@ -110,28 +155,43 @@ HRESULT CacheLayoutContainer( __in BURN_CONTAINER* pContainer, __in_z_opt LPCWSTR wzLayoutDirectory, __in_z LPCWSTR wzUnverifiedContainerPath, - __in BOOL fMove + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ); HRESULT CacheLayoutPayload( __in BURN_PAYLOAD* pPayload, __in_z_opt LPCWSTR wzLayoutDirectory, __in_z LPCWSTR wzUnverifiedPayloadPath, - __in BOOL fMove + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ); HRESULT CacheCompletePayload( __in BOOL fPerMachine, __in BURN_PAYLOAD* pPayload, __in_z LPCWSTR wzCacheId, __in_z LPCWSTR wzUnverifiedPayloadPath, - __in BOOL fMove + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ); HRESULT CacheVerifyContainer( __in BURN_CONTAINER* pContainer, - __in_z LPCWSTR wzCachedDirectory + __in_z LPCWSTR wzCachedDirectory, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ); HRESULT CacheVerifyPayload( __in BURN_PAYLOAD* pPayload, - __in_z LPCWSTR wzCachedDirectory + __in_z LPCWSTR wzCachedDirectory, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ); HRESULT CacheRemoveWorkingFolder( __in_z_opt LPCWSTR wzBundleId diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp index 502c5462..3a448923 100644 --- a/src/engine/elevation.cpp +++ b/src/engine/elevation.cpp @@ -35,11 +35,15 @@ typedef enum _BURN_ELEVATION_MESSAGE_TYPE BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_COMPLETE, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_BEGIN, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_COMPLETE, + BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_BEGIN, + BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_COMPLETE, + BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_SUCCESS, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE, BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID, + BURN_ELEVATION_MESSAGE_TYPE_PROGRESS_ROUTINE, } BURN_ELEVATION_MESSAGE_TYPE; @@ -52,6 +56,13 @@ typedef struct _BURN_ELEVATION_APPLY_INITIALIZE_MESSAGE_CONTEXT BOOL fSrpCompleteNeeded; } BURN_ELEVATION_APPLY_INITIALIZE_MESSAGE_CONTEXT; +typedef struct _BURN_ELEVATION_CACHE_MESSAGE_CONTEXT +{ + PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler; + LPPROGRESS_ROUTINE pfnProgress; + LPVOID pvContext; +} BURN_ELEVATION_CACHE_MESSAGE_CONTEXT; + typedef struct _BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT { PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler; @@ -99,6 +110,11 @@ static HRESULT ProcessApplyInitializeMessages( __in_opt LPVOID pvContext, __out DWORD* pdwResult ); +static HRESULT ProcessBurnCacheMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in LPVOID pvContext, + __out DWORD* pdwResult + ); static HRESULT ProcessGenericExecuteMessages( __in BURN_PIPE_MESSAGE* pMsg, __in LPVOID pvContext, @@ -114,6 +130,12 @@ static HRESULT ProcessLaunchApprovedExeMessages( __in_opt LPVOID pvContext, __out DWORD* pdwResult ); +static HRESULT ProcessProgressRoutineMessage( + __in BURN_PIPE_MESSAGE* pMsg, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pvContext, + __out DWORD* pdwResult + ); static HRESULT ProcessElevatedChildMessage( __in BURN_PIPE_MESSAGE* pMsg, __in_opt LPVOID pvContext, @@ -165,12 +187,14 @@ static HRESULT OnSaveState( __in DWORD cbData ); static HRESULT OnCacheCompletePayload( + __in HANDLE hPipe, __in BURN_PACKAGES* pPackages, __in BURN_PAYLOADS* pPayloads, __in BYTE* pbData, __in DWORD cbData ); static HRESULT OnCacheVerifyPayload( + __in HANDLE hPipe, __in BURN_PACKAGES* pPackages, __in BURN_PAYLOADS* pPayloads, __in BYTE* pbData, @@ -225,6 +249,21 @@ static HRESULT OnExecutePackageDependencyAction( __in BYTE* pbData, __in DWORD cbData ); +static HRESULT CALLBACK BurnCacheMessageHandler( + __in BURN_CACHE_MESSAGE* pMessage, + __in LPVOID pvContext + ); +static DWORD CALLBACK ElevatedProgressRoutine( + __in LARGE_INTEGER TotalFileSize, + __in LARGE_INTEGER TotalBytesTransferred, + __in LARGE_INTEGER StreamSize, + __in LARGE_INTEGER StreamBytesTransferred, + __in DWORD dwStreamNumber, + __in DWORD dwCallbackReason, + __in HANDLE hSourceFile, + __in HANDLE hDestinationFile, + __in_opt LPVOID lpData + ); static int GenericExecuteMessageHandler( __in GENERIC_EXECUTE_MESSAGE* pMessage, __in LPVOID pvContext @@ -582,13 +621,21 @@ extern "C" HRESULT ElevationCacheCompletePayload( __in BURN_PACKAGE* pPackage, __in BURN_PAYLOAD* pPayload, __in_z LPCWSTR wzUnverifiedPath, - __in BOOL fMove + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ) { HRESULT hr = S_OK; BYTE* pbData = NULL; SIZE_T cbData = 0; DWORD dwResult = 0; + BURN_ELEVATION_CACHE_MESSAGE_CONTEXT context = { }; + + context.pfnCacheMessageHandler = pfnCacheMessageHandler; + context.pfnProgress = pfnProgress; + context.pvContext = pContext; // serialize message data hr = BuffWriteString(&pbData, &cbData, pPackage->sczId); @@ -604,7 +651,7 @@ extern "C" HRESULT ElevationCacheCompletePayload( ExitOnFailure(hr, "Failed to write move flag to message buffer."); // send message - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_COMPLETE_PAYLOAD, pbData, cbData, NULL, NULL, &dwResult); + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_COMPLETE_PAYLOAD, pbData, cbData, ProcessBurnCacheMessages, &context, &dwResult); ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_COMPLETE_PAYLOAD message to per-machine process."); hr = (HRESULT)dwResult; @@ -618,13 +665,21 @@ LExit: extern "C" HRESULT ElevationCacheVerifyPayload( __in HANDLE hPipe, __in BURN_PACKAGE* pPackage, - __in BURN_PAYLOAD* pPayload + __in BURN_PAYLOAD* pPayload, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ) { HRESULT hr = S_OK; BYTE* pbData = NULL; SIZE_T cbData = 0; DWORD dwResult = 0; + BURN_ELEVATION_CACHE_MESSAGE_CONTEXT context = { }; + + context.pfnCacheMessageHandler = pfnCacheMessageHandler; + context.pfnProgress = pfnProgress; + context.pvContext = pContext; // serialize message data hr = BuffWriteString(&pbData, &cbData, pPackage->sczId); @@ -634,7 +689,7 @@ extern "C" HRESULT ElevationCacheVerifyPayload( ExitOnFailure(hr, "Failed to write payload id to message buffer."); // send message - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_PAYLOAD, pbData, cbData, NULL, NULL, &dwResult); + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_PAYLOAD, pbData, cbData, ProcessBurnCacheMessages, &context, &dwResult); ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_PAYLOAD message to per-machine process."); hr = (HRESULT)dwResult; @@ -1377,6 +1432,69 @@ LExit: return hr; } +static HRESULT ProcessBurnCacheMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + BURN_ELEVATION_CACHE_MESSAGE_CONTEXT* pContext = static_cast(pvContext); + BURN_CACHE_MESSAGE message = { }; + BOOL fProgressRoutine = FALSE; + + // Process the message. + switch (pMsg->dwMessage) + { + case BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_BEGIN: + // read message parameters + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, reinterpret_cast(&message.begin.cacheStep)); + ExitOnFailure(hr, "Failed to read begin cache step."); + + message.type = BURN_CACHE_MESSAGE_BEGIN; + break; + + case BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_COMPLETE: + // read message parameters + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, reinterpret_cast(&message.complete.hrStatus)); + ExitOnFailure(hr, "Failed to read complete hresult."); + + message.type = BURN_CACHE_MESSAGE_COMPLETE; + break; + + case BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_SUCCESS: + // read message parameters + hr = BuffReadNumber64((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.success.qwFileSize); + ExitOnFailure(hr, "Failed to read begin cache step."); + + message.type = BURN_CACHE_MESSAGE_SUCCESS; + break; + + case BURN_ELEVATION_MESSAGE_TYPE_PROGRESS_ROUTINE: + fProgressRoutine = TRUE; + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid burn cache message."); + break; + } + + if (fProgressRoutine) + { + hr = ProcessProgressRoutineMessage(pMsg, pContext->pfnProgress, pContext->pvContext, pdwResult); + } + else + { + hr = pContext->pfnCacheMessageHandler(&message, pContext->pvContext); + *pdwResult = static_cast(hr); + } + +LExit: + return hr; +} + static HRESULT ProcessGenericExecuteMessages( __in BURN_PIPE_MESSAGE* pMsg, __in LPVOID pvContext, @@ -1596,11 +1714,41 @@ LExit: return hr; } +static HRESULT ProcessProgressRoutineMessage( + __in BURN_PIPE_MESSAGE* pMsg, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LARGE_INTEGER liTotalFileSize = { }; + LARGE_INTEGER liTotalBytesTransferred = { }; + LARGE_INTEGER liStreamSize = { }; + LARGE_INTEGER liStreamBytesTransferred = { }; + DWORD dwStreamNumber = 0; + DWORD dwCallbackReason = CALLBACK_CHUNK_FINISHED; + HANDLE hSourceFile = INVALID_HANDLE_VALUE; + HANDLE hDestinationFile = INVALID_HANDLE_VALUE; + + hr = BuffReadNumber64((BYTE*)pMsg->pvData, pMsg->cbData, &iData, reinterpret_cast(&liTotalFileSize.QuadPart)); + ExitOnFailure(hr, "Failed to read total file size for progress."); + + hr = BuffReadNumber64((BYTE*)pMsg->pvData, pMsg->cbData, &iData, reinterpret_cast(&liTotalBytesTransferred.QuadPart)); + ExitOnFailure(hr, "Failed to read total bytes transferred for progress."); + + *pdwResult = pfnProgress(liTotalFileSize, liTotalBytesTransferred, liStreamSize, liStreamBytesTransferred, dwStreamNumber, dwCallbackReason, hSourceFile, hDestinationFile, pvContext); + +LExit: + return hr; +} + static HRESULT ProcessElevatedChildMessage( __in BURN_PIPE_MESSAGE* pMsg, __in_opt LPVOID pvContext, __out DWORD* pdwResult -) + ) { HRESULT hr = S_OK; BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = static_cast(pvContext); @@ -1705,11 +1853,11 @@ static HRESULT ProcessElevatedChildCacheMessage( switch (pMsg->dwMessage) { case BURN_ELEVATION_MESSAGE_TYPE_CACHE_COMPLETE_PAYLOAD: - hrResult = OnCacheCompletePayload(pContext->pPackages, pContext->pPayloads, (BYTE*)pMsg->pvData, pMsg->cbData); + hrResult = OnCacheCompletePayload(pContext->hPipe, pContext->pPackages, pContext->pPayloads, (BYTE*)pMsg->pvData, pMsg->cbData); break; case BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_PAYLOAD: - hrResult = OnCacheVerifyPayload(pContext->pPackages, pContext->pPayloads, (BYTE*)pMsg->pvData, pMsg->cbData); + hrResult = OnCacheVerifyPayload(pContext->hPipe, pContext->pPackages, pContext->pPayloads, (BYTE*)pMsg->pvData, pMsg->cbData); break; case BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP: @@ -2002,6 +2150,7 @@ LExit: } static HRESULT OnCacheCompletePayload( + __in HANDLE hPipe, __in BURN_PACKAGES* pPackages, __in BURN_PAYLOADS* pPayloads, __in BYTE* pbData, @@ -2043,7 +2192,7 @@ static HRESULT OnCacheCompletePayload( if (pPackage && pPayload) // complete payload. { - hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, sczUnverifiedPath, fMove); + hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, sczUnverifiedPath, fMove, BurnCacheMessageHandler, ElevatedProgressRoutine, hPipe); ExitOnFailure(hr, "Failed to cache payload: %ls", pPayload->sczKey); } else @@ -2060,6 +2209,7 @@ LExit: } static HRESULT OnCacheVerifyPayload( + __in HANDLE hPipe, __in BURN_PACKAGES* pPackages, __in BURN_PAYLOADS* pPayloads, __in BYTE* pbData, @@ -2097,7 +2247,7 @@ static HRESULT OnCacheVerifyPayload( hr = CacheGetCompletedPath(TRUE, pPackage->sczCacheId, &sczCacheDirectory); ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", pPackage->sczCacheId); - hr = CacheVerifyPayload(pPayload, sczCacheDirectory); + hr = CacheVerifyPayload(pPayload, sczCacheDirectory, BurnCacheMessageHandler, ElevatedProgressRoutine, hPipe); } else { @@ -2573,6 +2723,91 @@ LExit: return hr; } +static HRESULT CALLBACK BurnCacheMessageHandler( + __in BURN_CACHE_MESSAGE* pMessage, + __in LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + DWORD dwResult = 0; + HANDLE hPipe = (HANDLE)pvContext; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwMessage = 0; + + switch (pMessage->type) + { + case BURN_CACHE_MESSAGE_BEGIN: + // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, pMessage->begin.cacheStep); + ExitOnFailure(hr, "Failed to write progress percentage to message buffer."); + + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_BEGIN; + break; + + case BURN_CACHE_MESSAGE_COMPLETE: + // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, pMessage->complete.hrStatus); + ExitOnFailure(hr, "Failed to write error code to message buffer."); + + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_COMPLETE; + break; + + case BURN_CACHE_MESSAGE_SUCCESS: + hr = BuffWriteNumber64(&pbData, &cbData, pMessage->success.qwFileSize); + ExitOnFailure(hr, "Failed to count of files in use to message buffer."); + + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_SUCCESS; + break; + } + + // send message + hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send burn cache message to per-user process."); + + hr = dwResult; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +static DWORD CALLBACK ElevatedProgressRoutine( + __in LARGE_INTEGER TotalFileSize, + __in LARGE_INTEGER TotalBytesTransferred, + __in LARGE_INTEGER /*StreamSize*/, + __in LARGE_INTEGER /*StreamBytesTransferred*/, + __in DWORD /*dwStreamNumber*/, + __in DWORD /*dwCallbackReason*/, + __in HANDLE /*hSourceFile*/, + __in HANDLE /*hDestinationFile*/, + __in_opt LPVOID lpData + ) +{ + HRESULT hr = S_OK; + DWORD dwResult = 0; + HANDLE hPipe = (HANDLE)lpData; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwMessage = BURN_ELEVATION_MESSAGE_TYPE_PROGRESS_ROUTINE; + + hr = BuffWriteNumber64(&pbData, &cbData, TotalFileSize.QuadPart); + ExitOnFailure(hr, "Failed to write total file size progress to message buffer."); + + hr = BuffWriteNumber64(&pbData, &cbData, TotalBytesTransferred.QuadPart); + ExitOnFailure(hr, "Failed to write total bytes transferred progress to message buffer."); + + // send message + hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send progress routine message to per-user process."); + +LExit: + ReleaseBuffer(pbData); + + return dwResult; +} + static int GenericExecuteMessageHandler( __in GENERIC_EXECUTE_MESSAGE* pMessage, __in LPVOID pvContext diff --git a/src/engine/elevation.h b/src/engine/elevation.h index fd7ee110..9244f36c 100644 --- a/src/engine/elevation.h +++ b/src/engine/elevation.h @@ -55,12 +55,18 @@ HRESULT ElevationCacheCompletePayload( __in BURN_PACKAGE* pPackage, __in BURN_PAYLOAD* pPayload, __in_z LPCWSTR wzUnverifiedPath, - __in BOOL fMove + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ); HRESULT ElevationCacheVerifyPayload( __in HANDLE hPipe, __in BURN_PACKAGE* pPackage, - __in BURN_PAYLOAD* pPayload + __in BURN_PAYLOAD* pPayload, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext ); HRESULT ElevationCacheCleanup( __in HANDLE hPipe diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index bf929835..f662c445 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -431,8 +431,8 @@ extern "C" HRESULT PlanLayoutBundle( pCacheAction->bundleLayout.qwBundleSize = qwBundleSize; pCacheAction->bundleLayout.pPayloadGroup = pLayoutPayloads; - // Acquire + Verify - pPlan->qwCacheSizeTotal += 2 * qwBundleSize; + // Acquire + Verify + Finalize + pPlan->qwCacheSizeTotal += 3 * qwBundleSize; ++pPlan->cOverallProgressTicksTotal; @@ -1006,8 +1006,8 @@ extern "C" HRESULT PlanLayoutContainer( pCacheAction->type = BURN_CACHE_ACTION_TYPE_CONTAINER; pCacheAction->container.pContainer = pContainer; - // Acquire + Verify - pPlan->qwCacheSizeTotal += 2 * pContainer->qwFileSize; + // Acquire + Verify + Finalize + pPlan->qwCacheSizeTotal += 3 * pContainer->qwFileSize; } } else @@ -2249,8 +2249,14 @@ static HRESULT ProcessPayloadGroup( if (!pPlan->sczLayoutDirectory || !pPayload->pContainer) { - // Acquire + Verify - pPlan->qwCacheSizeTotal += 2 * pPayload->qwFileSize; + // Acquire + Verify + Finalize + pPlan->qwCacheSizeTotal += 3 * pPayload->qwFileSize; + + if (!pPlan->sczLayoutDirectory) + { + // Staging + pPlan->qwCacheSizeTotal += pPayload->qwFileSize; + } } if (!pPlan->sczLayoutDirectory && pPayload->pContainer && 1 == pPayload->cRemainingInstances) diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index 279a00b5..8c6f29f3 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -912,7 +912,8 @@ EXTERN_C BAAPI UserExperienceOnCacheVerifyProgress( __in_z_opt LPCWSTR wzPayloadId, __in DWORD64 dw64Progress, __in DWORD64 dw64Total, - __in DWORD dwOverallPercentage + __in DWORD dwOverallPercentage, + __in BOOTSTRAPPER_CACHE_VERIFY_STEP verifyStep ) { HRESULT hr = S_OK; @@ -925,6 +926,7 @@ EXTERN_C BAAPI UserExperienceOnCacheVerifyProgress( args.dw64Progress = dw64Progress; args.dw64Total = dw64Total; args.dwOverallPercentage = dwOverallPercentage; + args.verifyStep = verifyStep; results.cbSize = sizeof(results); diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index a848e60d..58e0431e 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -233,7 +233,8 @@ BAAPI UserExperienceOnCacheVerifyProgress( __in_z_opt LPCWSTR wzPayloadId, __in DWORD64 dw64Progress, __in DWORD64 dw64Total, - __in DWORD dwOverallPercentage + __in DWORD dwOverallPercentage, + __in BOOTSTRAPPER_CACHE_VERIFY_STEP verifyStep ); BAAPI UserExperienceOnCommitMsiTransactionBegin( __in BURN_USER_EXPERIENCE* pUserExperience, diff --git a/src/test/BurnUnitTest/CacheTest.cpp b/src/test/BurnUnitTest/CacheTest.cpp index fc0b4531..d0cc237f 100644 --- a/src/test/BurnUnitTest/CacheTest.cpp +++ b/src/test/BurnUnitTest/CacheTest.cpp @@ -2,6 +2,26 @@ #include "precomp.h" +static HRESULT CALLBACK CacheTestEventRoutine( + __in BURN_CACHE_MESSAGE* pMessage, + __in LPVOID pvContext + ); + +static DWORD CALLBACK CacheTestProgressRoutine( + __in LARGE_INTEGER TotalFileSize, + __in LARGE_INTEGER TotalBytesTransferred, + __in LARGE_INTEGER StreamSize, + __in LARGE_INTEGER StreamBytesTransferred, + __in DWORD dwStreamNumber, + __in DWORD dwCallbackReason, + __in HANDLE hSourceFile, + __in HANDLE hDestinationFile, + __in_opt LPVOID lpData + ); + +typedef struct _CACHE_TEST_CONTEXT +{ +} CACHE_TEST_CONTEXT; namespace Microsoft { @@ -33,6 +53,7 @@ namespace Bootstrapper LPWSTR sczPayloadPath = NULL; BYTE* pb = NULL; DWORD cb = NULL; + CACHE_TEST_CONTEXT context = { }; try { @@ -51,7 +72,7 @@ namespace Bootstrapper payload.pbHash = pb; payload.cbHash = cb; - hr = CacheCompletePayload(package.fPerMachine, &payload, package.sczCacheId, sczPayloadPath, FALSE); + hr = CacheCompletePayload(package.fPerMachine, &payload, package.sczCacheId, sczPayloadPath, FALSE, CacheTestEventRoutine, CacheTestProgressRoutine, &context); Assert::Equal(S_OK, hr); } finally @@ -73,3 +94,26 @@ namespace Bootstrapper } } } + +static HRESULT CALLBACK CacheTestEventRoutine( + __in BURN_CACHE_MESSAGE* /*pMessage*/, + __in LPVOID /*pvContext*/ + ) +{ + return S_OK; +} + +static DWORD CALLBACK CacheTestProgressRoutine( + __in LARGE_INTEGER /*TotalFileSize*/, + __in LARGE_INTEGER /*TotalBytesTransferred*/, + __in LARGE_INTEGER /*StreamSize*/, + __in LARGE_INTEGER /*StreamBytesTransferred*/, + __in DWORD /*dwStreamNumber*/, + __in DWORD /*dwCallbackReason*/, + __in HANDLE /*hSourceFile*/, + __in HANDLE /*hDestinationFile*/, + __in_opt LPVOID /*lpData*/ + ) +{ + return PROGRESS_QUIET; +} diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp index c073696b..a7c1d83c 100644 --- a/src/test/BurnUnitTest/PlanTest.cpp +++ b/src/test/BurnUnitTest/PlanTest.cpp @@ -71,7 +71,7 @@ namespace Bootstrapper Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); Assert::Equal(107082ull, pPlan->qwEstimatedSize); - Assert::Equal(303687ull, pPlan->qwCacheSizeTotal); + Assert::Equal(506145ull, pPlan->qwCacheSizeTotal); fRollback = FALSE; dwIndex = 0; @@ -308,7 +308,7 @@ namespace Bootstrapper Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); Assert::Equal(35694ull, pPlan->qwEstimatedSize); - Assert::Equal(101229ull, pPlan->qwCacheSizeTotal); + Assert::Equal(168715ull, pPlan->qwCacheSizeTotal); fRollback = FALSE; dwIndex = 0; @@ -388,7 +388,7 @@ namespace Bootstrapper Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); Assert::Equal(33743ull, pPlan->qwEstimatedSize); - Assert::Equal(101229ull, pPlan->qwCacheSizeTotal); + Assert::Equal(168715ull, pPlan->qwCacheSizeTotal); fRollback = FALSE; dwIndex = 0; @@ -458,7 +458,7 @@ namespace Bootstrapper Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); Assert::Equal(35694ull, pPlan->qwEstimatedSize); - Assert::Equal(101229ull, pPlan->qwCacheSizeTotal); + Assert::Equal(168715ull, pPlan->qwCacheSizeTotal); fRollback = FALSE; dwIndex = 0; @@ -739,7 +739,7 @@ namespace Bootstrapper Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); Assert::Equal(3055111ull, pPlan->qwEstimatedSize); - Assert::Equal(106496ull, pPlan->qwCacheSizeTotal); + Assert::Equal(212992ull, pPlan->qwCacheSizeTotal); fRollback = FALSE; dwIndex = 0; -- cgit v1.2.3-55-g6feb From 707e77212e105cd7fa8a74baca6efa3ae3e6d6b3 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 17 Apr 2021 21:07:27 -0500 Subject: Clean up caching log messages. --- src/engine/apply.cpp | 12 ++++++------ src/engine/engine.mc | 31 +++++++++++++++++++------------ 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 6f6225b4..1bc01d44 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -862,13 +862,13 @@ static HRESULT ApplyCachePackage( } } - LogErrorId(hr, MSG_APPLY_RETRYING_PACKAGE, pPackage->sczId, NULL, NULL); + LogErrorId(hr, MSG_CACHE_RETRYING_PACKAGE, pPackage->sczId, NULL, NULL); continue; } else if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE == cachePackageCompleteAction && !pPackage->fVital) // ignore non-vital download failures. { - LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, pPackage->sczId, hr); + LogId(REPORT_STANDARD, MSG_CACHE_CONTINUING_NONVITAL_PACKAGE, pPackage->sczId, hr); hr = S_OK; } else if (fCanceledBegin) @@ -1003,7 +1003,7 @@ static HRESULT ApplyLayoutContainer( pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedCacheProgress; pContainer->qwCommittedCacheProgress = 0; ReleaseNullStr(pContext->sczLastUsedFolderCandidate); - LogErrorId(hr, MSG_APPLY_RETRYING_CONTAINER, pContainer->sczId, NULL, NULL); + LogErrorId(hr, MSG_CACHE_RETRYING_CONTAINER, pContainer->sczId, NULL, NULL); } } @@ -1062,7 +1062,7 @@ static HRESULT ApplyProcessPayload( pContext->qwSuccessfulCacheProgress -= pPayloadGroupItem->qwCommittedCacheProgress; pPayloadGroupItem->qwCommittedCacheProgress = 0; ReleaseNullStr(pContext->sczLastUsedFolderCandidate); - LogErrorId(hr, MSG_APPLY_RETRYING_PAYLOAD, pPayload->sczKey, NULL, NULL); + LogErrorId(hr, MSG_CACHE_RETRYING_PAYLOAD, pPayload->sczKey, NULL, NULL); } } @@ -1644,7 +1644,7 @@ static HRESULT CopyPayload( HANDLE hDestinationFile = INVALID_HANDLE_VALUE; HANDLE hSourceOpenedFile = INVALID_HANDLE_VALUE; - DWORD dwLogId = pProgress->pContainer ? (pProgress->pPayloadGroupItem ? MSG_ACQUIRE_CONTAINER_PAYLOAD : MSG_ACQUIRE_CONTAINER) : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; + DWORD dwLogId = pProgress->pContainer ? MSG_ACQUIRE_CONTAINER : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "copy", wzSourcePath); hr = PreparePayloadDestinationPath(wzDestinationPath); @@ -1707,7 +1707,7 @@ static HRESULT DownloadPayload( DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { }; APPLY_AUTHENTICATION_REQUIRED_DATA authenticationData = { }; - DWORD dwLogId = pProgress->pContainer ? (pProgress->pPayloadGroupItem ? MSG_ACQUIRE_CONTAINER_PAYLOAD : MSG_ACQUIRE_CONTAINER) : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; + DWORD dwLogId = pProgress->pContainer ? MSG_ACQUIRE_CONTAINER : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "download", pDownloadSource->sczUrl); hr = PreparePayloadDestinationPath(wzDestinationPath); diff --git a/src/engine/engine.mc b/src/engine/engine.mc index 6e0695bc..25d5b4e4 100644 --- a/src/engine/engine.mc +++ b/src/engine/engine.mc @@ -717,13 +717,6 @@ Language=English Acquiring container: %1!ls!, %3!hs! from: %4!ls! . -MessageId=337 -Severity=Success -SymbolicName=MSG_ACQUIRE_CONTAINER_PAYLOAD -Language=English -Acquiring container: %1!ls!, payload: %2!ls!, %3!hs! from: %4!ls! -. - MessageId=338 Severity=Success SymbolicName=MSG_ACQUIRE_PACKAGE_PAYLOAD @@ -738,25 +731,39 @@ Language=English Failed to verify container: %2!ls! at path: %3!ls!, error: %1!ls!. Deleting file. . +MessageId=340 +Severity=Warning +SymbolicName=MSG_CACHE_CONTINUING_NONVITAL_PACKAGE +Language=English +Cached non-vital package: %1!ls!, encountered error: 0x%2!x!. Continuing... +. + +MessageId=346 +Severity=Warning +SymbolicName=MSG_CACHE_RETRYING_PACKAGE +Language=English +Application requested retry of caching package: %1!ls!, encountered error: 0x%2!x!. Retrying... +. + MessageId=347 Severity=Warning -SymbolicName=MSG_APPLY_RETRYING_CONTAINER +SymbolicName=MSG_CACHE_RETRYING_CONTAINER Language=English -Application requested retry of container: %2!ls!, encountered error: %1!ls!. Retrying... +Application requested retry of caching container: %2!ls!, encountered error: %1!ls!. Retrying... . MessageId=348 Severity=Warning SymbolicName=MSG_APPLY_RETRYING_PACKAGE Language=English -Application requested retry of package: %1!ls!, encountered error: 0x%2!x!. Retrying... +Application requested retry of executing package: %1!ls!, encountered error: 0x%2!x!. Retrying... . MessageId=349 Severity=Warning -SymbolicName=MSG_APPLY_RETRYING_PAYLOAD +SymbolicName=MSG_CACHE_RETRYING_PAYLOAD Language=English -Application requested retry of payload: %2!ls!, encountered error: %1!ls!. Retrying... +Application requested retry of caching payload: %2!ls!, encountered error: %1!ls!. Retrying... . MessageId=350 -- cgit v1.2.3-55-g6feb From 61a8d39f689222faa677e4bd79475cd77795c57a Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 19 Apr 2021 17:11:46 -0500 Subject: Allow setting source from OnCacheAcquireResolving. --- .../inc/BootstrapperApplication.h | 18 ++++- src/engine/apply.cpp | 82 ++++++++++++++-------- src/engine/cache.cpp | 11 ++- src/engine/cache.h | 3 +- src/engine/userexperience.cpp | 17 ++--- src/engine/userexperience.h | 4 +- 6 files changed, 92 insertions(+), 43 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h index d8994c26..603df890 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -64,6 +64,20 @@ enum BOOTSTRAPPER_CACHE_OPERATION BOOTSTRAPPER_CACHE_OPERATION_EXTRACT, }; +enum BOOTSTRAPPER_CACHE_RESOLVE_OPERATION +{ + // There is no source available. + BOOTSTRAPPER_CACHE_RESOLVE_NONE, + // Copy the payload or container from the chosen local source. + BOOTSTRAPPER_CACHE_RESOLVE_LOCAL, + // Download the payload or container from the download URL. + BOOTSTRAPPER_CACHE_RESOLVE_DOWNLOAD, + // Extract the payload from the container. + BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER, + // Look again for the payload or container locally. + BOOTSTRAPPER_CACHE_RESOLVE_RETRY, +}; + enum BOOTSTRAPPER_CACHE_VERIFY_STEP { BOOTSTRAPPER_CACHE_VERIFY_STEP_STAGE, @@ -379,14 +393,14 @@ struct BA_ONCACHEACQUIRERESOLVING_ARGS DWORD dwRecommendedSearchPath; LPCWSTR wzDownloadUrl; LPCWSTR wzPayloadContainerId; - BOOTSTRAPPER_CACHE_OPERATION recommendation; + BOOTSTRAPPER_CACHE_RESOLVE_OPERATION recommendation; }; struct BA_ONCACHEACQUIRERESOLVING_RESULTS { DWORD cbSize; DWORD dwChosenSearchPath; - BOOTSTRAPPER_CACHE_OPERATION action; + BOOTSTRAPPER_CACHE_RESOLVE_OPERATION action; BOOL fCancel; }; diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 1bc01d44..9cba0483 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -1390,6 +1390,7 @@ static HRESULT AcquireContainerOrPayload( LPCWSTR wzRelativePath = pContainer ? pContainer->sczFilePath : pPayload->sczFilePath; DWORD dwChosenSearchPath = 0; BOOTSTRAPPER_CACHE_OPERATION cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE; + BOOTSTRAPPER_CACHE_RESOLVE_OPERATION resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_NONE; LPWSTR* pwzDownloadUrl = pContainer ? &pContainer->downloadSource.sczUrl : &pPayload->downloadSource.sczUrl; LPWSTR* pwzSourcePath = pContainer ? &pContainer->sczSourcePath : &pPayload->sczSourcePath; BOOL fFoundLocal = FALSE; @@ -1405,47 +1406,70 @@ static HRESULT AcquireContainerOrPayload( if (BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD != cacheOperation && BOOTSTRAPPER_CACHE_OPERATION_EXTRACT != cacheOperation) { - hr = CacheGetLocalSourcePaths(wzRelativePath, *pwzSourcePath, wzDestinationPath, pContext->wzLayoutDirectory, pContext->pVariables, &pContext->rgSearchPaths, &pContext->cSearchPaths); - ExitOnFailure(hr, "Failed to search local source."); - - for (DWORD i = 0; i < pContext->cSearchPaths; ++i) + do { - // If the file exists locally, choose it. - if (FileExistsEx(pContext->rgSearchPaths[i], NULL)) - { - dwChosenSearchPath = i; + fFoundLocal = FALSE; + resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_NONE; + dwChosenSearchPath = 0; - fFoundLocal = TRUE; - break; - } - } + hr = CacheGetLocalSourcePaths(wzRelativePath, *pwzSourcePath, wzDestinationPath, pContext->wzLayoutDirectory, pContext->pVariables, &pContext->rgSearchPaths, &pContext->cSearchPaths, &dwChosenSearchPath); + ExitOnFailure(hr, "Failed to search local source."); - if (BOOTSTRAPPER_CACHE_OPERATION_COPY == cacheOperation) - { - if (!fFoundLocal) + for (DWORD i = 0; i < pContext->cSearchPaths; ++i) { - cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE; + // If the file exists locally, choose it. + if (FileExistsEx(pContext->rgSearchPaths[i], NULL)) + { + dwChosenSearchPath = i; + + fFoundLocal = TRUE; + break; + } } - } - else - { - if (fFoundLocal) // the file exists locally, so copy it. + + if (BOOTSTRAPPER_CACHE_OPERATION_COPY == cacheOperation) { - cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_COPY; + if (fFoundLocal) + { + resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_LOCAL; + } } - else if (wzPayloadContainerId) + else { - cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_EXTRACT; + if (fFoundLocal) // the file exists locally, so copy it. + { + resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_LOCAL; + } + else if (wzPayloadContainerId) + { + resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER; + } + else if (*pwzDownloadUrl && **pwzDownloadUrl) + { + resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_DOWNLOAD; + } } - else if (*pwzDownloadUrl && **pwzDownloadUrl) + + // Let the BA have a chance to override the source. + hr = UserExperienceOnCacheAcquireResolving(pContext->pUX, wzPackageOrContainerId, wzPayloadId, pContext->rgSearchPaths, pContext->cSearchPaths, fFoundLocal, &dwChosenSearchPath, pwzDownloadUrl, wzPayloadContainerId, &resolveOperation); + ExitOnRootFailure(hr, "BA aborted cache acquire resolving."); + + switch (resolveOperation) { + case BOOTSTRAPPER_CACHE_RESOLVE_LOCAL: + cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_COPY; + break; + case BOOTSTRAPPER_CACHE_RESOLVE_DOWNLOAD: cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD; + break; + case BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER: + cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_EXTRACT; + break; + case BOOTSTRAPPER_CACHE_RESOLVE_RETRY: + pContext->cSearchPathsMax = max(pContext->cSearchPaths, pContext->cSearchPathsMax); + break; } - } - - // Let the BA have a chance to override the action, but their chance to change the source is during begin or complete. - hr = UserExperienceOnCacheAcquireResolving(pContext->pUX, wzPackageOrContainerId, wzPayloadId, pContext->rgSearchPaths, pContext->cSearchPaths, fFoundLocal, &dwChosenSearchPath, *pwzDownloadUrl, wzPayloadContainerId, &cacheOperation); - ExitOnRootFailure(hr, "BA aborted cache acquire resolving."); + } while (BOOTSTRAPPER_CACHE_RESOLVE_RETRY == resolveOperation); } switch (cacheOperation) diff --git a/src/engine/cache.cpp b/src/engine/cache.cpp index 16f2c1e5..764de065 100644 --- a/src/engine/cache.cpp +++ b/src/engine/cache.cpp @@ -440,7 +440,8 @@ extern "C" HRESULT CacheGetLocalSourcePaths( __in_z_opt LPCWSTR wzLayoutDirectory, __in BURN_VARIABLES* pVariables, __inout LPWSTR** prgSearchPaths, - __out DWORD* pcSearchPaths + __out DWORD* pcSearchPaths, + __out DWORD* pdwLikelySearchPath ) { HRESULT hr = S_OK; @@ -452,6 +453,7 @@ extern "C" HRESULT CacheGetLocalSourcePaths( BOOL fTryRelativePath = FALSE; BOOL fSourceIsAbsolute = FALSE; DWORD cSearchPaths = 0; + DWORD dwLikelySearchPath = 0; AssertSz(vfInitializedCache, "Cache wasn't initialized"); @@ -473,6 +475,12 @@ extern "C" HRESULT CacheGetLocalSourcePaths( hr = StrAllocString(psczPath, wzSourcePath, 0); ExitOnFailure(hr, "Failed to copy absolute source path."); } + else + { + // If none of the paths exist, then most BAs will want to prompt the user with a possible path. + // The destination path is a temporary location and so not really a possible path. + dwLikelySearchPath = 1; + } // Try the destination path next. hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); @@ -593,6 +601,7 @@ LExit: AssertSz(cSearchPaths <= BURN_CACHE_MAX_SEARCH_PATHS, "Got more than BURN_CACHE_MAX_SEARCH_PATHS search paths"); *pcSearchPaths = cSearchPaths; + *pdwLikelySearchPath = dwLikelySearchPath; return hr; } diff --git a/src/engine/cache.h b/src/engine/cache.h index cd964649..afa18e47 100644 --- a/src/engine/cache.h +++ b/src/engine/cache.h @@ -101,7 +101,8 @@ HRESULT CacheGetLocalSourcePaths( __in_z_opt LPCWSTR wzLayoutDirectory, __in BURN_VARIABLES* pVariables, __inout LPWSTR** prgSearchPaths, - __out DWORD* pcSearchPaths + __out DWORD* pcSearchPaths, + __out DWORD* pdwLikelySearchPath ); HRESULT CacheSetLastUsedSource( __in BURN_VARIABLES* pVariables, diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index 8c6f29f3..7e68d664 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -515,9 +515,9 @@ EXTERN_C BAAPI UserExperienceOnCacheAcquireResolving( __in DWORD cSearchPaths, __in BOOL fFoundLocal, __in DWORD* pdwChosenSearchPath, - __in_z_opt LPCWSTR wzDownloadUrl, + __in_z_opt LPWSTR* pwzDownloadUrl, __in_z_opt LPCWSTR wzPayloadContainerId, - __inout BOOTSTRAPPER_CACHE_OPERATION* pCacheOperation + __inout BOOTSTRAPPER_CACHE_RESOLVE_OPERATION* pCacheOperation ) { HRESULT hr = S_OK; @@ -531,14 +531,14 @@ EXTERN_C BAAPI UserExperienceOnCacheAcquireResolving( args.cSearchPaths = cSearchPaths; args.fFoundLocal = fFoundLocal; args.dwRecommendedSearchPath = *pdwChosenSearchPath; - args.wzDownloadUrl = wzDownloadUrl; + args.wzDownloadUrl = *pwzDownloadUrl; args.recommendation = *pCacheOperation; results.cbSize = sizeof(results); results.dwChosenSearchPath = *pdwChosenSearchPath; results.action = *pCacheOperation; - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRERESOLVING, &args, &results); + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRERESOLVING, &args, &results); ExitOnFailure(hr, "BA OnCacheAcquireResolving failed."); if (results.fCancel) @@ -548,13 +548,14 @@ EXTERN_C BAAPI UserExperienceOnCacheAcquireResolving( else { // Verify the BA requested an action that is possible. - if (BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD == results.action && wzDownloadUrl && *wzDownloadUrl || - BOOTSTRAPPER_CACHE_OPERATION_EXTRACT == results.action && wzPayloadContainerId || - BOOTSTRAPPER_CACHE_OPERATION_NONE == results.action) + if (BOOTSTRAPPER_CACHE_RESOLVE_DOWNLOAD == results.action && *pwzDownloadUrl && **pwzDownloadUrl || + BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER == results.action && wzPayloadContainerId || + BOOTSTRAPPER_CACHE_RESOLVE_RETRY == results.action || + BOOTSTRAPPER_CACHE_RESOLVE_NONE == results.action) { *pCacheOperation = results.action; } - else if (BOOTSTRAPPER_CACHE_OPERATION_COPY == results.action && results.dwChosenSearchPath < cSearchPaths) + else if (BOOTSTRAPPER_CACHE_RESOLVE_LOCAL == results.action && results.dwChosenSearchPath < cSearchPaths) { *pdwChosenSearchPath = results.dwChosenSearchPath; *pCacheOperation = results.action; diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index 58e0431e..e1041624 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -154,9 +154,9 @@ BAAPI UserExperienceOnCacheAcquireResolving( __in DWORD cSearchPaths, __in BOOL fFoundLocal, __in DWORD* pdwChosenSearchPath, - __in_z_opt LPCWSTR wzDownloadUrl, + __in_z_opt LPWSTR* pwzDownloadUrl, __in_z_opt LPCWSTR wzPayloadContainerId, - __inout BOOTSTRAPPER_CACHE_OPERATION* pCacheOperation + __inout BOOTSTRAPPER_CACHE_RESOLVE_OPERATION* pCacheOperation ); BAAPI UserExperienceOnCacheBegin( __in BURN_USER_EXPERIENCE* pUserExperience -- cgit v1.2.3-55-g6feb From 666196071cf29d9b489e598a604ae0998c98b6de Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 25 Apr 2021 21:44:40 -0500 Subject: For payloads in a container, prefer the container over local paths. Still consider the destination path to avoid extracting the container for every payload. #3640 --- src/engine/apply.cpp | 45 ++++++++++++++++++++++++++++++++++++--------- src/engine/cache.cpp | 6 +++++- src/engine/cache.h | 3 ++- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 9cba0483..b831be9c 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -1359,8 +1359,8 @@ static HRESULT ApplyAcquireContainerOrPayload( if (fRetry) { - hr = S_OK; LogErrorId(hr, pContainer ? MSG_APPLY_RETRYING_ACQUIRE_CONTAINER : MSG_APPLY_RETRYING_ACQUIRE_PAYLOAD, pContainer ? pContainer->sczId : pPayloadGroupItem->pPayload->sczKey, NULL, NULL); + hr = S_OK; } ExitOnFailure(hr, "Failed to acquire %hs: %ls", pContainer ? "container" : "payload", pContainer ? pContainer->sczId : pPayloadGroupItem->pPayload->sczKey); @@ -1389,11 +1389,13 @@ static HRESULT AcquireContainerOrPayload( LPCWSTR wzDestinationPath = pContainer ? pContainer->sczUnverifiedPath: pPayload->sczUnverifiedPath; LPCWSTR wzRelativePath = pContainer ? pContainer->sczFilePath : pPayload->sczFilePath; DWORD dwChosenSearchPath = 0; + DWORD dwDestinationSearchPath = 0; BOOTSTRAPPER_CACHE_OPERATION cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE; BOOTSTRAPPER_CACHE_RESOLVE_OPERATION resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_NONE; LPWSTR* pwzDownloadUrl = pContainer ? &pContainer->downloadSource.sczUrl : &pPayload->downloadSource.sczUrl; LPWSTR* pwzSourcePath = pContainer ? &pContainer->sczSourcePath : &pPayload->sczSourcePath; BOOL fFoundLocal = FALSE; + BOOL fPreferExtract = FALSE; pContext->cSearchPaths = 0; *pfRetry = FALSE; @@ -1409,21 +1411,42 @@ static HRESULT AcquireContainerOrPayload( do { fFoundLocal = FALSE; + fPreferExtract = FALSE; resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_NONE; dwChosenSearchPath = 0; + dwDestinationSearchPath = 0; - hr = CacheGetLocalSourcePaths(wzRelativePath, *pwzSourcePath, wzDestinationPath, pContext->wzLayoutDirectory, pContext->pVariables, &pContext->rgSearchPaths, &pContext->cSearchPaths, &dwChosenSearchPath); + hr = CacheGetLocalSourcePaths(wzRelativePath, *pwzSourcePath, wzDestinationPath, pContext->wzLayoutDirectory, pContext->pVariables, &pContext->rgSearchPaths, &pContext->cSearchPaths, &dwChosenSearchPath, &dwDestinationSearchPath); ExitOnFailure(hr, "Failed to search local source."); - for (DWORD i = 0; i < pContext->cSearchPaths; ++i) + // When a payload comes from a container, the container has the highest chance of being correct. + // But we want to avoid extracting the container multiple times. + // So only consider the destination path, which means the container was already extracted. + if (wzPayloadContainerId) { - // If the file exists locally, choose it. - if (FileExistsEx(pContext->rgSearchPaths[i], NULL)) + if (FileExistsEx(pContext->rgSearchPaths[dwDestinationSearchPath], NULL)) { - dwChosenSearchPath = i; - fFoundLocal = TRUE; - break; + dwChosenSearchPath = dwDestinationSearchPath; + } + else + { + fPreferExtract = TRUE; + } + } + + if (!fFoundLocal) + { + for (DWORD i = 0; i < pContext->cSearchPaths; ++i) + { + // If the file exists locally, choose it. + if (FileExistsEx(pContext->rgSearchPaths[i], NULL)) + { + dwChosenSearchPath = i; + + fFoundLocal = TRUE; + break; + } } } @@ -1436,7 +1459,11 @@ static HRESULT AcquireContainerOrPayload( } else { - if (fFoundLocal) // the file exists locally, so copy it. + if (fPreferExtract) // the file comes from a container which hasn't been extracted yet, so extract it. + { + resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER; + } + else if (fFoundLocal) // the file exists locally, so copy it. { resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_LOCAL; } diff --git a/src/engine/cache.cpp b/src/engine/cache.cpp index 764de065..2a40010d 100644 --- a/src/engine/cache.cpp +++ b/src/engine/cache.cpp @@ -441,7 +441,8 @@ extern "C" HRESULT CacheGetLocalSourcePaths( __in BURN_VARIABLES* pVariables, __inout LPWSTR** prgSearchPaths, __out DWORD* pcSearchPaths, - __out DWORD* pdwLikelySearchPath + __out DWORD* pdwLikelySearchPath, + __out DWORD* pdwDestinationSearchPath ) { HRESULT hr = S_OK; @@ -454,6 +455,7 @@ extern "C" HRESULT CacheGetLocalSourcePaths( BOOL fSourceIsAbsolute = FALSE; DWORD cSearchPaths = 0; DWORD dwLikelySearchPath = 0; + DWORD dwDestinationSearchPath = 0; AssertSz(vfInitializedCache, "Cache wasn't initialized"); @@ -486,6 +488,7 @@ extern "C" HRESULT CacheGetLocalSourcePaths( hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); ExitOnFailure(hr, "Failed to ensure size for search paths array."); + dwDestinationSearchPath = cSearchPaths; psczPath = *prgSearchPaths + cSearchPaths; ++cSearchPaths; @@ -602,6 +605,7 @@ LExit: AssertSz(cSearchPaths <= BURN_CACHE_MAX_SEARCH_PATHS, "Got more than BURN_CACHE_MAX_SEARCH_PATHS search paths"); *pcSearchPaths = cSearchPaths; *pdwLikelySearchPath = dwLikelySearchPath; + *pdwDestinationSearchPath = dwDestinationSearchPath; return hr; } diff --git a/src/engine/cache.h b/src/engine/cache.h index afa18e47..a2ac1696 100644 --- a/src/engine/cache.h +++ b/src/engine/cache.h @@ -102,7 +102,8 @@ HRESULT CacheGetLocalSourcePaths( __in BURN_VARIABLES* pVariables, __inout LPWSTR** prgSearchPaths, __out DWORD* pcSearchPaths, - __out DWORD* pdwLikelySearchPath + __out DWORD* pdwLikelySearchPath, + __out DWORD* pdwDestinationSearchPath ); HRESULT CacheSetLastUsedSource( __in BURN_VARIABLES* pVariables, -- cgit v1.2.3-55-g6feb From abdde5b4193ecedadcc772f00ff314e1880475b6 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 25 Apr 2021 21:46:34 -0500 Subject: UX payloads are never external, part 2. --- src/engine/cache.cpp | 32 ++------------------------------ src/engine/cache.h | 2 -- src/engine/core.cpp | 4 ++-- src/engine/engine.cpp | 2 +- 4 files changed, 5 insertions(+), 35 deletions(-) diff --git a/src/engine/cache.cpp b/src/engine/cache.cpp index 2a40010d..fcc7f72d 100644 --- a/src/engine/cache.cpp +++ b/src/engine/cache.cpp @@ -97,7 +97,6 @@ static HRESULT CopyEngineToWorkingFolder( __in_z LPCWSTR wzSourcePath, __in_z LPCWSTR wzWorkingFolderName, __in_z LPCWSTR wzExecutableName, - __in BURN_PAYLOADS* pUxPayloads, __in BURN_SECTION* pSection, __deref_out_z_opt LPWSTR* psczEngineWorkingPath ); @@ -743,7 +742,6 @@ extern "C" BOOL CacheBundleRunningFromCache() } extern "C" HRESULT CacheBundleToCleanRoom( - __in BURN_PAYLOADS* pUxPayloads, __in BURN_SECTION* pSection, __deref_out_z_opt LPWSTR* psczCleanRoomBundlePath ) @@ -757,7 +755,7 @@ extern "C" HRESULT CacheBundleToCleanRoom( wzExecutableName = PathFile(sczSourcePath); - hr = CopyEngineToWorkingFolder(sczSourcePath, BUNDLE_CLEAN_ROOM_WORKING_FOLDER_NAME, wzExecutableName, pUxPayloads, pSection, psczCleanRoomBundlePath); + hr = CopyEngineToWorkingFolder(sczSourcePath, BUNDLE_CLEAN_ROOM_WORKING_FOLDER_NAME, wzExecutableName, pSection, psczCleanRoomBundlePath); ExitOnFailure(hr, "Failed to cache bundle to clean room."); LExit: @@ -769,7 +767,6 @@ LExit: extern "C" HRESULT CacheBundleToWorkingDirectory( __in_z LPCWSTR /*wzBundleId*/, __in_z LPCWSTR wzExecutableName, - __in BURN_PAYLOADS* pUxPayloads, __in BURN_SECTION* pSection, __deref_out_z_opt LPWSTR* psczEngineWorkingPath ) @@ -792,7 +789,7 @@ extern "C" HRESULT CacheBundleToWorkingDirectory( } else // otherwise, carry on putting the bundle in the working folder. { - hr = CopyEngineToWorkingFolder(sczSourcePath, BUNDLE_WORKING_FOLDER_NAME, wzExecutableName, pUxPayloads, pSection, psczEngineWorkingPath); + hr = CopyEngineToWorkingFolder(sczSourcePath, BUNDLE_WORKING_FOLDER_NAME, wzExecutableName, pSection, psczEngineWorkingPath); ExitOnFailure(hr, "Failed to copy engine to working folder."); } @@ -1767,7 +1764,6 @@ static HRESULT CopyEngineToWorkingFolder( __in_z LPCWSTR wzSourcePath, __in_z LPCWSTR wzWorkingFolderName, __in_z LPCWSTR wzExecutableName, - __in BURN_PAYLOADS* pUxPayloads, __in BURN_SECTION* pSection, __deref_out_z_opt LPWSTR* psczEngineWorkingPath ) @@ -1796,30 +1792,6 @@ static HRESULT CopyEngineToWorkingFolder( hr = CopyEngineWithSignatureFixup(pSection->hEngineFile, wzSourcePath, sczTargetPath, pSection); ExitOnFailure(hr, "Failed to copy engine: '%ls' to working path: %ls", wzSourcePath, sczTargetPath); - // Copy external UX payloads to working path. - for (DWORD i = 0; i < pUxPayloads->cPayloads; ++i) - { - BURN_PAYLOAD* pPayload = &pUxPayloads->rgPayloads[i]; - - if (BURN_PAYLOAD_PACKAGING_EXTERNAL == pPayload->packaging) - { - if (!sczSourceDirectory) - { - hr = PathGetDirectory(wzSourcePath, &sczSourceDirectory); - ExitOnFailure(hr, "Failed to get directory from engine path: %ls", wzSourcePath); - } - - hr = PathConcat(sczSourceDirectory, pPayload->sczSourcePath, &sczPayloadSourcePath); - ExitOnFailure(hr, "Failed to build payload source path for working copy."); - - hr = PathConcat(sczTargetDirectory, pPayload->sczFilePath, &sczPayloadTargetPath); - ExitOnFailure(hr, "Failed to build payload target path for working copy."); - - hr = FileEnsureCopyWithRetry(sczPayloadSourcePath, sczPayloadTargetPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); - ExitOnFailure(hr, "Failed to copy UX payload from: '%ls' to: '%ls'", sczPayloadSourcePath, sczPayloadTargetPath); - } - } - if (psczEngineWorkingPath) { hr = StrAllocString(psczEngineWorkingPath, sczTargetPath, 0); diff --git a/src/engine/cache.h b/src/engine/cache.h index a2ac1696..0152d33b 100644 --- a/src/engine/cache.h +++ b/src/engine/cache.h @@ -124,14 +124,12 @@ void CacheSendErrorCallback( ); BOOL CacheBundleRunningFromCache(); HRESULT CacheBundleToCleanRoom( - __in BURN_PAYLOADS* pUxPayloads, __in BURN_SECTION* pSection, __deref_out_z_opt LPWSTR* psczCleanRoomBundlePath ); HRESULT CacheBundleToWorkingDirectory( __in_z LPCWSTR wzBundleId, __in_z LPCWSTR wzExecutableName, - __in BURN_PAYLOADS* pUxPayloads, __in BURN_SECTION* pSection, __deref_out_z_opt LPWSTR* psczEngineWorkingPath ); diff --git a/src/engine/core.cpp b/src/engine/core.cpp index baba55f6..aea614d2 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -579,7 +579,7 @@ extern "C" HRESULT CoreElevate( // If the elevated companion pipe isn't created yet, let's make that happen. if (!pEngineState->sczBundleEngineWorkingPath) { - hr = CacheBundleToWorkingDirectory(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &pEngineState->userExperience.payloads, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath); + hr = CacheBundleToWorkingDirectory(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath); ExitOnFailure(hr, "Failed to cache engine to working directory."); } @@ -678,7 +678,7 @@ extern "C" HRESULT CoreApply( // Ensure the engine is cached to the working path. if (!pEngineState->sczBundleEngineWorkingPath) { - hr = CacheBundleToWorkingDirectory(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &pEngineState->userExperience.payloads, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath); + hr = CacheBundleToWorkingDirectory(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath); ExitOnFailure(hr, "Failed to cache engine to working directory."); } diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 458386d4..bb4061a7 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -454,7 +454,7 @@ static HRESULT RunUntrusted( } else { - hr = CacheBundleToCleanRoom(&pEngineState->userExperience.payloads, &pEngineState->section, &sczCachedCleanRoomBundlePath); + hr = CacheBundleToCleanRoom(&pEngineState->section, &sczCachedCleanRoomBundlePath); ExitOnFailure(hr, "Failed to cache to clean room."); wzCleanRoomBundlePath = sczCachedCleanRoomBundlePath; -- cgit v1.2.3-55-g6feb From dd16dd2344ca3c750a8fc52c1e27a605fd25940d Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 25 Apr 2021 21:47:32 -0500 Subject: DOWNLOAD is not a valid packaging value. --- src/engine/payload.cpp | 13 +++---------- src/engine/payload.h | 1 - 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/engine/payload.cpp b/src/engine/payload.cpp index 28ab6f45..f29fa2bd 100644 --- a/src/engine/payload.cpp +++ b/src/engine/payload.cpp @@ -67,11 +67,7 @@ extern "C" HRESULT PayloadsParseFromXml( hr = XmlGetAttributeEx(pixnNode, L"Packaging", &scz); ExitOnFailure(hr, "Failed to get @Packaging."); - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"download", -1)) - { - pPayload->packaging = BURN_PAYLOAD_PACKAGING_DOWNLOAD; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"embedded", -1)) + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"embedded", -1)) { pPayload->packaging = BURN_PAYLOAD_PACKAGING_EMBEDDED; } @@ -108,14 +104,11 @@ extern "C" HRESULT PayloadsParseFromXml( // @SourcePath hr = XmlGetAttributeEx(pixnNode, L"SourcePath", &pPayload->sczSourcePath); - if (E_NOTFOUND != hr || BURN_PAYLOAD_PACKAGING_DOWNLOAD != pPayload->packaging) - { - ExitOnFailure(hr, "Failed to get @SourcePath."); - } + ExitOnFailure(hr, "Failed to get @SourcePath."); // @DownloadUrl hr = XmlGetAttributeEx(pixnNode, L"DownloadUrl", &pPayload->downloadSource.sczUrl); - if (E_NOTFOUND != hr || BURN_PAYLOAD_PACKAGING_DOWNLOAD == pPayload->packaging) + if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get @DownloadUrl."); } diff --git a/src/engine/payload.h b/src/engine/payload.h index aa174d66..ad10509d 100644 --- a/src/engine/payload.h +++ b/src/engine/payload.h @@ -12,7 +12,6 @@ extern "C" { enum BURN_PAYLOAD_PACKAGING { BURN_PAYLOAD_PACKAGING_NONE, - BURN_PAYLOAD_PACKAGING_DOWNLOAD, BURN_PAYLOAD_PACKAGING_EMBEDDED, BURN_PAYLOAD_PACKAGING_EXTERNAL, }; -- cgit v1.2.3-55-g6feb From d291d27f94d0702bcd4ffd6fb72125c8996b3aef Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 25 Apr 2021 21:50:14 -0500 Subject: Share code to map stream name to payload when extracting containers. --- src/engine/apply.cpp | 21 ++++++++++++--------- src/engine/core.cpp | 2 +- src/engine/payload.cpp | 46 ++++++---------------------------------------- src/engine/payload.h | 3 +-- 4 files changed, 20 insertions(+), 52 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index b831be9c..4570ffb9 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -1113,7 +1113,8 @@ static HRESULT ExtractContainer( HRESULT hr = S_OK; BURN_CONTAINER_CONTEXT context = { }; HANDLE hContainerHandle = INVALID_HANDLE_VALUE; - LPWSTR sczExtractPayloadId = NULL; + LPWSTR sczStreamName = NULL; + BURN_PAYLOAD* pExtract = NULL; BURN_CACHE_PROGRESS_CONTEXT progress = { }; progress.pCacheContext = pContext; @@ -1129,14 +1130,17 @@ static HRESULT ExtractContainer( hr = ContainerOpen(&context, pContainer, hContainerHandle, pContainer->sczUnverifiedPath); ExitOnFailure(hr, "Failed to open container: %ls.", pContainer->sczId); - while (S_OK == (hr = ContainerNextStream(&context, &sczExtractPayloadId))) + while (S_OK == (hr = ContainerNextStream(&context, &sczStreamName))) { BOOL fExtracted = FALSE; - for (DWORD iExtract = 0; iExtract < pContext->pPayloads->cPayloads; ++iExtract) + hr = PayloadFindEmbeddedBySourcePath(pContext->pPayloads, sczStreamName, &pExtract); + if (E_NOTFOUND != hr) { - BURN_PAYLOAD* pExtract = pContext->pPayloads->rgPayloads + iExtract; - if (pExtract->sczUnverifiedPath && pExtract->cRemainingInstances && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczExtractPayloadId, -1, pExtract->sczSourcePath, -1)) + ExitOnFailure(hr, "Failed to find embedded payload by source path: %ls container: %ls", sczStreamName, pContainer->sczId); + + // Skip payloads that weren't planned or have already been cached. + if (pExtract->sczUnverifiedPath && pExtract->cRemainingInstances) { progress.pPayload = pExtract; @@ -1161,17 +1165,16 @@ static HRESULT ExtractContainer( } UserExperienceOnCachePayloadExtractComplete(pContext->pUX, pContainer->sczId, pExtract->sczKey, hr); - ExitOnFailure(hr, "Failed to extract payload: %ls from container: %ls", sczExtractPayloadId, pContainer->sczId); + ExitOnFailure(hr, "Failed to extract payload: %ls from container: %ls", sczStreamName, pContainer->sczId); fExtracted = TRUE; - break; } } if (!fExtracted) { hr = ContainerSkipStream(&context); - ExitOnFailure(hr, "Failed to skip the extraction of payload: %ls from container: %ls", sczExtractPayloadId, pContainer->sczId); + ExitOnFailure(hr, "Failed to skip the extraction of payload: %ls from container: %ls", sczStreamName, pContainer->sczId); } } @@ -1182,7 +1185,7 @@ static HRESULT ExtractContainer( ExitOnFailure(hr, "Failed to extract all payloads from container: %ls", pContainer->sczId); LExit: - ReleaseStr(sczExtractPayloadId); + ReleaseStr(sczStreamName); ContainerClose(&context); return hr; diff --git a/src/engine/core.cpp b/src/engine/core.cpp index aea614d2..969b94a0 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -155,7 +155,7 @@ extern "C" HRESULT CoreInitialize( hr = UserExperienceEnsureWorkingFolder(pEngineState->registration.sczId, &pEngineState->userExperience.sczTempDirectory); ExitOnFailure(hr, "Failed to get unique temporary folder for bootstrapper application."); - hr = PayloadExtractFromContainer(&pEngineState->userExperience.payloads, NULL, &containerContext, pEngineState->userExperience.sczTempDirectory); + hr = PayloadExtractUXContainer(&pEngineState->userExperience.payloads, &containerContext, pEngineState->userExperience.sczTempDirectory); ExitOnFailure(hr, "Failed to extract bootstrapper application payloads."); hr = PathConcat(pEngineState->userExperience.sczTempDirectory, L"BootstrapperApplicationData.xml", &pEngineState->command.wzBootstrapperApplicationDataPath); diff --git a/src/engine/payload.cpp b/src/engine/payload.cpp index f29fa2bd..72eb3476 100644 --- a/src/engine/payload.cpp +++ b/src/engine/payload.cpp @@ -190,9 +190,8 @@ extern "C" void PayloadsUninitialize( memset(pPayloads, 0, sizeof(BURN_PAYLOADS)); } -extern "C" HRESULT PayloadExtractFromContainer( +extern "C" HRESULT PayloadExtractUXContainer( __in BURN_PAYLOADS* pPayloads, - __in_opt BURN_CONTAINER* pContainer, __in BURN_CONTAINER_CONTEXT* pContainerContext, __in_z LPCWSTR wzTargetDir ) @@ -215,7 +214,7 @@ extern "C" HRESULT PayloadExtractFromContainer( ExitOnFailure(hr, "Failed to get next stream."); // find payload by stream name - hr = FindEmbeddedBySourcePath(pPayloads, pContainer, sczStreamName, &pPayload); + hr = PayloadFindEmbeddedBySourcePath(pPayloads, sczStreamName, &pPayload); ExitOnFailure(hr, "Failed to find embedded payload: %ls", sczStreamName); // make file path @@ -241,15 +240,11 @@ extern "C" HRESULT PayloadExtractFromContainer( { pPayload = &pPayloads->rgPayloads[i]; - // if the payload is part of the container - if (!pContainer || pPayload->pContainer == pContainer) + // if the payload has not been acquired + if (BURN_PAYLOAD_STATE_ACQUIRED > pPayload->state) { - // if the payload has not been acquired - if (BURN_PAYLOAD_STATE_ACQUIRED > pPayload->state) - { - hr = E_INVALIDDATA; - ExitOnRootFailure(hr, "Payload was not found in container: %ls", pPayload->sczKey); - } + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Payload was not found in container: %ls", pPayload->sczKey); } } @@ -317,32 +312,3 @@ LExit: // internal function definitions - -static HRESULT FindEmbeddedBySourcePath( - __in BURN_PAYLOADS* pPayloads, - __in_opt BURN_CONTAINER* pContainer, - __in_z LPCWSTR wzStreamName, - __out BURN_PAYLOAD** ppPayload - ) -{ - HRESULT hr = S_OK; - - for (DWORD i = 0; i < pPayloads->cPayloads; ++i) - { - BURN_PAYLOAD* pPayload = &pPayloads->rgPayloads[i]; - - if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging && (!pContainer || pPayload->pContainer == pContainer)) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPayload->sczSourcePath, -1, wzStreamName, -1)) - { - *ppPayload = pPayload; - ExitFunction1(hr = S_OK); - } - } - } - - hr = E_NOTFOUND; - -LExit: - return hr; -} diff --git a/src/engine/payload.h b/src/engine/payload.h index ad10509d..f28b437f 100644 --- a/src/engine/payload.h +++ b/src/engine/payload.h @@ -85,9 +85,8 @@ void PayloadUninitialize( void PayloadsUninitialize( __in BURN_PAYLOADS* pPayloads ); -HRESULT PayloadExtractFromContainer( +HRESULT PayloadExtractUXContainer( __in BURN_PAYLOADS* pPayloads, - __in_opt BURN_CONTAINER* pContainer, __in BURN_CONTAINER_CONTEXT* pContainerContext, __in_z LPCWSTR wzTargetDir ); -- cgit v1.2.3-55-g6feb From bf31c11edf14789d22ce6542549a807d5c5ff086 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 25 Apr 2021 21:52:28 -0500 Subject: Add support for downloading embedded payloads. #5253 --- src/engine/apply.cpp | 29 +++++++++++++++++++---------- src/engine/container.h | 1 + src/engine/externalengine.cpp | 12 ------------ src/engine/plan.cpp | 1 + 4 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 4570ffb9..74f57f6a 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -1422,19 +1422,19 @@ static HRESULT AcquireContainerOrPayload( hr = CacheGetLocalSourcePaths(wzRelativePath, *pwzSourcePath, wzDestinationPath, pContext->wzLayoutDirectory, pContext->pVariables, &pContext->rgSearchPaths, &pContext->cSearchPaths, &dwChosenSearchPath, &dwDestinationSearchPath); ExitOnFailure(hr, "Failed to search local source."); - // When a payload comes from a container, the container has the highest chance of being correct. - // But we want to avoid extracting the container multiple times. - // So only consider the destination path, which means the container was already extracted. if (wzPayloadContainerId) { + // When a payload comes from a container, the container has the highest chance of being correct. + // But we want to avoid extracting the container multiple times. + // So only consider the destination path, which means the container was already extracted. if (FileExistsEx(pContext->rgSearchPaths[dwDestinationSearchPath], NULL)) { fFoundLocal = TRUE; dwChosenSearchPath = dwDestinationSearchPath; } - else + else // don't prefer the container if extracting it already failed. { - fPreferExtract = TRUE; + fPreferExtract = SUCCEEDED(pPayload->pContainer->hrExtract); } } @@ -1470,14 +1470,14 @@ static HRESULT AcquireContainerOrPayload( { resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_LOCAL; } - else if (wzPayloadContainerId) - { - resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER; - } else if (*pwzDownloadUrl && **pwzDownloadUrl) { resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_DOWNLOAD; } + else if (wzPayloadContainerId) + { + resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER; + } } // Let the BA have a chance to override the source. @@ -1526,7 +1526,7 @@ static HRESULT AcquireContainerOrPayload( break; case BOOTSTRAPPER_CACHE_OPERATION_EXTRACT: - Assert(pPayload->pContainer); + Assert(pPayload && pPayload->pContainer); hr = ApplyExtractContainer(pContext, pPayload->pContainer); ExitOnFailure(hr, "Failed to extract container for payload: %ls", wzPayloadId); @@ -1541,6 +1541,15 @@ static HRESULT AcquireContainerOrPayload( hr = CompleteCacheProgress(pProgress, pContainer ? pContainer->qwFileSize : pPayload->qwFileSize); LExit: + if (BOOTSTRAPPER_CACHE_OPERATION_EXTRACT == cacheOperation) + { + if (FAILED(hr) && SUCCEEDED(pPayload->pContainer->hrExtract) && + (fFoundLocal || pPayload->downloadSource.sczUrl && *pPayload->downloadSource.sczUrl)) + { + *pfRetry = TRUE; + } + pPayload->pContainer->hrExtract = hr; + } UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, pfRetry); pContext->cSearchPathsMax = max(pContext->cSearchPaths, pContext->cSearchPathsMax); diff --git a/src/engine/container.h b/src/engine/container.h index 3174eb38..88fe48f1 100644 --- a/src/engine/container.h +++ b/src/engine/container.h @@ -79,6 +79,7 @@ typedef struct _BURN_CONTAINER DWORD64 qwExtractSizeTotal; DWORD64 qwCommittedCacheProgress; DWORD64 qwCommittedExtractProgress; + HRESULT hrExtract; } BURN_CONTAINER; typedef struct _BURN_CONTAINERS diff --git a/src/engine/externalengine.cpp b/src/engine/externalengine.cpp index d6c44736..63177722 100644 --- a/src/engine/externalengine.cpp +++ b/src/engine/externalengine.cpp @@ -357,12 +357,6 @@ HRESULT ExternalEngineSetLocalSource( hr = PayloadFindById(&pEngineState->payloads, wzPayloadId, &pPayload); ExitOnFailure(hr, "BA requested unknown payload with id: %ls", wzPayloadId); - if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION); - ExitOnFailure(hr, "BA denied while trying to set source on embedded payload: %ls", wzPayloadId); - } - hr = StrAllocString(&pPayload->sczSourcePath, wzPath, 0); ExitOnFailure(hr, "Failed to set source path for payload."); } @@ -408,12 +402,6 @@ HRESULT ExternalEngineSetDownloadSource( hr = PayloadFindById(&pEngineState->payloads, wzPayloadId, &pPayload); ExitOnFailure(hr, "BA requested unknown payload with id: %ls", wzPayloadId); - if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION); - ExitOnFailure(hr, "BA denied while trying to set download URL on embedded payload: %ls", wzPayloadId); - } - pDownloadSource = &pPayload->downloadSource; } else if (wzPackageOrContainerId && *wzPackageOrContainerId) diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index f662c445..e5c1ee36 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -1832,6 +1832,7 @@ static void ResetPlannedContainerState( pContainer->qwExtractSizeTotal = 0; pContainer->qwCommittedCacheProgress = 0; pContainer->qwCommittedExtractProgress = 0; + pContainer->hrExtract = S_OK; } static void ResetPlannedPayloadsState( -- cgit v1.2.3-55-g6feb From 14cdda3c489d6b9801f05939044e67b13939b42d Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 25 Apr 2021 22:44:23 -0500 Subject: Set source of attached containers to WixBundleOriginalSource if set. Use file size when probing local files. #5586 --- src/engine/apply.cpp | 50 +++++++++++++++++++++++++++++++++++++++++++++++--- src/engine/engine.cpp | 27 +++++++++++++++++++++++++-- 2 files changed, 72 insertions(+), 5 deletions(-) diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp index 74f57f6a..58d41b28 100644 --- a/src/engine/apply.cpp +++ b/src/engine/apply.cpp @@ -133,6 +133,11 @@ static HRESULT AcquireContainerOrPayload( __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, __out BOOL* pfRetry ); +static BOOL IsValidLocalFile( + __in_z LPCWSTR wzFilePath, + __in DWORD64 qwFileSize, + __in BOOL fMinimumFileSize + ); static HRESULT LayoutOrCacheContainerOrPayload( __in BURN_CACHE_CONTEXT* pContext, __in_opt BURN_CONTAINER* pContainer, @@ -1399,6 +1404,25 @@ static HRESULT AcquireContainerOrPayload( LPWSTR* pwzSourcePath = pContainer ? &pContainer->sczSourcePath : &pPayload->sczSourcePath; BOOL fFoundLocal = FALSE; BOOL fPreferExtract = FALSE; + DWORD64 qwFileSize = 0; + BOOL fMinimumFileSize = FALSE; + + if (pContainer) + { + if (pContainer->fAttached) + { + fMinimumFileSize = TRUE; + qwFileSize = pContainer->qwAttachedOffset + pContainer->qwFileSize; + } + else if (pContainer->pbHash && pContext->wzLayoutDirectory) + { + qwFileSize = pContainer->qwFileSize; + } + } + else if (pPayload->pbHash) + { + qwFileSize = pPayload->qwFileSize; + } pContext->cSearchPaths = 0; *pfRetry = FALSE; @@ -1427,7 +1451,7 @@ static HRESULT AcquireContainerOrPayload( // When a payload comes from a container, the container has the highest chance of being correct. // But we want to avoid extracting the container multiple times. // So only consider the destination path, which means the container was already extracted. - if (FileExistsEx(pContext->rgSearchPaths[dwDestinationSearchPath], NULL)) + if (IsValidLocalFile(pContext->rgSearchPaths[dwDestinationSearchPath], qwFileSize, fMinimumFileSize)) { fFoundLocal = TRUE; dwChosenSearchPath = dwDestinationSearchPath; @@ -1442,8 +1466,8 @@ static HRESULT AcquireContainerOrPayload( { for (DWORD i = 0; i < pContext->cSearchPaths; ++i) { - // If the file exists locally, choose it. - if (FileExistsEx(pContext->rgSearchPaths[i], NULL)) + // If the file exists locally with the correct size, choose it. + if (IsValidLocalFile(pContext->rgSearchPaths[i], qwFileSize, fMinimumFileSize)) { dwChosenSearchPath = i; @@ -1557,6 +1581,26 @@ LExit: return hr; } +static BOOL IsValidLocalFile( + __in_z LPCWSTR wzFilePath, + __in DWORD64 qwFileSize, + __in BOOL fMinimumFileSize + ) +{ + LONGLONG llFileSize = 0; + + if (!qwFileSize) + { + return FileExistsEx(wzFilePath, NULL); + } + else + { + return SUCCEEDED(FileSize(wzFilePath, &llFileSize)) && + (static_cast(llFileSize) == qwFileSize || + fMinimumFileSize && static_cast(llFileSize) > qwFileSize); + } +} + static HRESULT LayoutOrCacheContainerOrPayload( __in BURN_CACHE_CONTEXT* pContext, __in_opt BURN_CONTAINER* pContainer, diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index bb4061a7..e2728d7f 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -523,7 +523,8 @@ static HRESULT RunNormal( ) { HRESULT hr = S_OK; - HANDLE hPipesCreatedEvent = NULL; + LPWSTR sczOriginalSource = NULL; + LPWSTR sczCopiedOriginalSource = NULL; BOOL fContinueExecution = TRUE; BOOL fReloadApp = FALSE; BOOL fSkipCleanup = FALSE; @@ -558,6 +559,27 @@ static HRESULT RunNormal( hr = CoreQueryRegistration(pEngineState); ExitOnFailure(hr, "Failed to query registration."); + // Best effort to set the source of attached containers to BURN_BUNDLE_ORIGINAL_SOURCE. + hr = VariableGetString(&pEngineState->variables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource); + if (SUCCEEDED(hr)) + { + for (DWORD i = 0; i < pEngineState->containers.cContainers; ++i) + { + BURN_CONTAINER* pContainer = pEngineState->containers.rgContainers + i; + if (pContainer->fAttached) + { + hr = StrAllocString(&sczCopiedOriginalSource, sczOriginalSource, 0); + if (SUCCEEDED(hr)) + { + ReleaseNullStr(pContainer->sczSourcePath); + pContainer->sczSourcePath = sczCopiedOriginalSource; + sczCopiedOriginalSource = NULL; + } + } + } + } + hr = S_OK; + // Set some built-in variables before loading the BA. hr = PlanSetVariables(pEngineState->command.action, &pEngineState->variables); ExitOnFailure(hr, "Failed to set action variables."); @@ -613,7 +635,8 @@ LExit: ::PostMessageW(pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0); } - ReleaseHandle(hPipesCreatedEvent); + ReleaseStr(sczOriginalSource); + ReleaseStr(sczCopiedOriginalSource); return hr; } -- cgit v1.2.3-55-g6feb From e78138558fe17d8a91929c87b2a6d0c9a482d78a Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 28 Apr 2021 16:43:01 -0500 Subject: Clean up 32-bit assumptions. --- src/engine/cache.cpp | 12 ++-- src/engine/condition.cpp | 24 ++++++- src/engine/core.cpp | 9 ++- src/engine/elevation.cpp | 90 +++++++++++++------------ src/engine/embedded.cpp | 8 +-- src/engine/engine.cpp | 9 ++- src/engine/engine.vcxproj | 4 +- src/engine/exeengine.cpp | 2 +- src/engine/logging.cpp | 6 +- src/engine/packages.config | 2 +- src/engine/pipe.cpp | 105 ++++++++--------------------- src/engine/pipe.h | 2 +- src/engine/variable.cpp | 2 +- src/stub/packages.config | 2 +- src/stub/stub.vcxproj | 4 +- src/test/BurnUnitTest/BurnUnitTest.vcxproj | 16 ++--- src/test/BurnUnitTest/packages.config | 6 +- 17 files changed, 139 insertions(+), 164 deletions(-) diff --git a/src/engine/cache.cpp b/src/engine/cache.cpp index fcc7f72d..59daf139 100644 --- a/src/engine/cache.cpp +++ b/src/engine/cache.cpp @@ -1119,7 +1119,7 @@ extern "C" void CacheCleanup( LPWSTR sczDelete = NULL; HANDLE hFind = INVALID_HANDLE_VALUE; WIN32_FIND_DATAW wfd = { }; - DWORD cFileName = 0; + size_t cchFileName = 0; hr = CacheGetCompletedPath(fPerMachine, UNVERIFIED_CACHE_FOLDER_NAME, &sczFolder); if (SUCCEEDED(hr)) @@ -1146,17 +1146,15 @@ extern "C" void CacheCleanup( continue; } - // For extra safety and to silence OACR. - wfd.cFileName[MAX_PATH - 1] = L'\0'; - // Skip resume files (they end with ".R"). - cFileName = lstrlenW(wfd.cFileName); - if (2 < cFileName && L'.' == wfd.cFileName[cFileName - 2] && (L'R' == wfd.cFileName[cFileName - 1] || L'r' == wfd.cFileName[cFileName - 1])) + hr = ::StringCchLengthW(wfd.cFileName, MAX_PATH, &cchFileName); + if (FAILED(hr) || + 2 < cchFileName && L'.' == wfd.cFileName[cchFileName - 2] && (L'R' == wfd.cFileName[cchFileName - 1] || L'r' == wfd.cFileName[cchFileName - 1])) { continue; } - hr = PathConcat(sczFolder, wfd.cFileName, &sczDelete); + hr = PathConcatCch(sczFolder, 0, wfd.cFileName, cchFileName, &sczDelete); if (SUCCEEDED(hr)) { hr = FileEnsureDelete(sczDelete); diff --git a/src/engine/condition.cpp b/src/engine/condition.cpp index 56fe76c2..b7cd7413 100644 --- a/src/engine/condition.cpp +++ b/src/engine/condition.cpp @@ -513,6 +513,7 @@ static HRESULT NextSymbol( { HRESULT hr = S_OK; WORD charType = 0; + ptrdiff_t cchPosition = 0; DWORD iPosition = 0; DWORD n = 0; @@ -530,7 +531,13 @@ static HRESULT NextSymbol( } ++pContext->wzRead; } - iPosition = (DWORD)(pContext->wzRead - pContext->wzCondition); + + cchPosition = pContext->wzRead - pContext->wzCondition; + if (DWORD_MAX < cchPosition || 0 > cchPosition) + { + ExitOnFailure(hr = E_INVALIDARG, "Symbol was too long: %ls", pContext->wzCondition); + } + iPosition = (DWORD)cchPosition; // read depending on first character type switch (pContext->wzRead[0]) @@ -922,8 +929,19 @@ static HRESULT CompareStringValues( { HRESULT hr = S_OK; DWORD dwCompareString = (comparison & INSENSITIVE) ? NORM_IGNORECASE : 0; - int cchLeft = lstrlenW(wzLeftOperand); - int cchRight = lstrlenW(wzRightOperand); + size_t cchLeftSize = 0; + size_t cchRightSize = 0; + int cchLeft = 0; + int cchRight = 0; + + hr = ::StringCchLengthW(wzLeftOperand, STRSAFE_MAX_CCH, &cchLeftSize); + ExitOnRootFailure(hr, "Failed to get length of left string: %ls", wzLeftOperand); + + hr = ::StringCchLengthW(wzRightOperand, STRSAFE_MAX_CCH, &cchRightSize); + ExitOnRootFailure(hr, "Failed to get length of right string: %ls", wzRightOperand); + + cchLeft = static_cast(cchLeftSize); + cchRight = static_cast(cchRightSize); switch (comparison) { diff --git a/src/engine/core.cpp b/src/engine/core.cpp index 969b94a0..a915dad0 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -1050,7 +1050,7 @@ extern "C" HRESULT CoreAppendFileHandleAttachedToCommandLine( ExitWithLastError(hr, "Failed to duplicate file handle for attached container."); } - hr = StrAllocFormattedSecure(psczCommandLine, L"%ls -%ls=%u", *psczCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, hExecutableFile); + hr = StrAllocFormattedSecure(psczCommandLine, L"%ls -%ls=%Iu", *psczCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, reinterpret_cast(hExecutableFile)); ExitOnFailure(hr, "Failed to append the file handle to the command line."); *phExecutableFile = hExecutableFile; @@ -1078,12 +1078,12 @@ extern "C" HRESULT CoreAppendFileHandleSelfToCommandLine( hExecutableFile = ::CreateFileW(wzExecutablePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, &securityAttributes, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE != hExecutableFile) { - hr = StrAllocFormattedSecure(psczCommandLine, L"%ls -%ls=%u", *psczCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, hExecutableFile); + hr = StrAllocFormattedSecure(psczCommandLine, L"%ls -%ls=%Iu", *psczCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, reinterpret_cast(hExecutableFile)); ExitOnFailure(hr, "Failed to append the file handle to the command line."); if (psczObfuscatedCommandLine) { - hr = StrAllocFormatted(psczObfuscatedCommandLine, L"%ls -%ls=%u", *psczObfuscatedCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, hExecutableFile); + hr = StrAllocFormatted(psczObfuscatedCommandLine, L"%ls -%ls=%Iu", *psczObfuscatedCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, reinterpret_cast(hExecutableFile)); ExitOnFailure(hr, "Failed to append the file handle to the obfuscated command line."); } @@ -1499,8 +1499,7 @@ static HRESULT ParseCommandLine( { // Already processed in InitializeEngineState. } - else if (lstrlenW(&argv[i][1]) >= lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX) && - CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX), BURN_COMMANDLINE_SWITCH_PREFIX, lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX))) + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX), BURN_COMMANDLINE_SWITCH_PREFIX, lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX))) { // Skip (but log) any other private burn switches we don't recognize, so that // adding future private variables doesn't break old bundles diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp index 3a448923..9d1b8fc7 100644 --- a/src/engine/elevation.cpp +++ b/src/engine/elevation.cpp @@ -157,7 +157,7 @@ static HRESULT OnApplyInitialize( __in HANDLE* phLock, __in BOOL* pfDisabledWindowsUpdate, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ); static HRESULT OnApplyUninitialize( __in HANDLE* phLock @@ -166,39 +166,39 @@ static HRESULT OnSessionBegin( __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ); static HRESULT OnSessionResume( __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ); static HRESULT OnSessionEnd( __in BURN_PACKAGES* pPackages, __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ); static HRESULT OnSaveState( __in BURN_REGISTRATION* pRegistration, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ); static HRESULT OnCacheCompletePayload( __in HANDLE hPipe, __in BURN_PACKAGES* pPackages, __in BURN_PAYLOADS* pPayloads, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ); static HRESULT OnCacheVerifyPayload( __in HANDLE hPipe, __in BURN_PACKAGES* pPackages, __in BURN_PAYLOADS* pPayloads, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ); static void OnCacheCleanup( __in_z LPCWSTR wzBundleId @@ -206,7 +206,7 @@ static void OnCacheCleanup( static HRESULT OnProcessDependentRegistration( __in const BURN_REGISTRATION* pRegistration, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ); static HRESULT OnExecuteExePackage( __in HANDLE hPipe, @@ -214,40 +214,40 @@ static HRESULT OnExecuteExePackage( __in BURN_RELATED_BUNDLES* pRelatedBundles, __in BURN_VARIABLES* pVariables, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ); static HRESULT OnExecuteMsiPackage( __in HANDLE hPipe, __in BURN_PACKAGES* pPackages, __in BURN_VARIABLES* pVariables, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ); static HRESULT OnExecuteMspPackage( __in HANDLE hPipe, __in BURN_PACKAGES* pPackages, __in BURN_VARIABLES* pVariables, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ); static HRESULT OnExecuteMsuPackage( __in HANDLE hPipe, __in BURN_PACKAGES* pPackages, __in BURN_VARIABLES* pVariables, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ); static HRESULT OnExecutePackageProviderAction( __in BURN_PACKAGES* pPackages, __in BURN_RELATED_BUNDLES* pRelatedBundles, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ); static HRESULT OnExecutePackageDependencyAction( __in BURN_PACKAGES* pPackages, __in BURN_RELATED_BUNDLES* pRelatedBundles, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ); static HRESULT CALLBACK BurnCacheMessageHandler( __in BURN_CACHE_MESSAGE* pMessage, @@ -275,29 +275,29 @@ static int MsiExecuteMessageHandler( static HRESULT OnCleanPackage( __in BURN_PACKAGES* pPackages, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ); static HRESULT OnLaunchApprovedExe( __in HANDLE hPipe, __in BURN_APPROVED_EXES* pApprovedExes, __in BURN_VARIABLES* pVariables, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ); static HRESULT OnMsiBeginTransaction( __in BURN_PACKAGES* pPackages, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ); static HRESULT OnMsiCommitTransaction( __in BURN_PACKAGES* pPackages, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ); static HRESULT OnMsiRollbackTransaction( __in BURN_PACKAGES* pPackages, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ); static HRESULT ElevatedOnPauseAUBegin( __in HANDLE hPipe @@ -603,7 +603,7 @@ HRESULT ElevationSaveState( DWORD dwResult = 0; // send message - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE, pbBuffer, (DWORD)cbBuffer, NULL, NULL, &dwResult); + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE, pbBuffer, cbBuffer, NULL, NULL, &dwResult); ExitOnFailure(hr, "Failed to send message to per-machine process."); hr = (HRESULT)dwResult; @@ -858,6 +858,8 @@ extern "C" HRESULT ElevationMsiCommitTransaction( hr = static_cast(dwResult); LExit: + ReleaseBuffer(pbData); + return hr; } @@ -884,6 +886,8 @@ extern "C" HRESULT ElevationMsiRollbackTransaction( hr = static_cast(dwResult); LExit: + ReleaseBuffer(pbData); + return hr; } @@ -1612,7 +1616,7 @@ static HRESULT ProcessMsiPackageMessages( message.rgwzData = (LPCWSTR*)rgwzMsiData; } - hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, (DWORD*)&message.dwAllowedResults); + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.dwAllowedResults); ExitOnFailure(hr, "Failed to read UI flags."); // Process the rest of the message. @@ -1907,7 +1911,7 @@ static HRESULT OnApplyInitialize( __in HANDLE* phLock, __in BOOL* pfDisabledWindowsUpdate, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ) { HRESULT hr = S_OK; @@ -2031,7 +2035,7 @@ static HRESULT OnSessionBegin( __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ) { HRESULT hr = S_OK; @@ -2077,7 +2081,7 @@ static HRESULT OnSessionResume( __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ) { HRESULT hr = S_OK; @@ -2106,7 +2110,7 @@ static HRESULT OnSessionEnd( __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ) { HRESULT hr = S_OK; @@ -2136,7 +2140,7 @@ LExit: static HRESULT OnSaveState( __in BURN_REGISTRATION* pRegistration, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ) { HRESULT hr = S_OK; @@ -2154,7 +2158,7 @@ static HRESULT OnCacheCompletePayload( __in BURN_PACKAGES* pPackages, __in BURN_PAYLOADS* pPayloads, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ) { HRESULT hr = S_OK; @@ -2213,7 +2217,7 @@ static HRESULT OnCacheVerifyPayload( __in BURN_PACKAGES* pPackages, __in BURN_PAYLOADS* pPayloads, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ) { HRESULT hr = S_OK; @@ -2273,7 +2277,7 @@ static void OnCacheCleanup( static HRESULT OnProcessDependentRegistration( __in const BURN_REGISTRATION* pRegistration, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ) { HRESULT hr = S_OK; @@ -2309,7 +2313,7 @@ static HRESULT OnExecuteExePackage( __in BURN_RELATED_BUNDLES* pRelatedBundles, __in BURN_VARIABLES* pVariables, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ) { HRESULT hr = S_OK; @@ -2393,7 +2397,7 @@ static HRESULT OnExecuteMsiPackage( __in BURN_PACKAGES* pPackages, __in BURN_VARIABLES* pVariables, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ) { HRESULT hr = S_OK; @@ -2490,7 +2494,7 @@ static HRESULT OnExecuteMspPackage( __in BURN_PACKAGES* pPackages, __in BURN_VARIABLES* pVariables, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ) { HRESULT hr = S_OK; @@ -2585,7 +2589,7 @@ static HRESULT OnExecuteMsuPackage( __in BURN_PACKAGES* pPackages, __in BURN_VARIABLES* pVariables, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ) { HRESULT hr = S_OK; @@ -2644,7 +2648,7 @@ static HRESULT OnExecutePackageProviderAction( __in BURN_PACKAGES* pPackages, __in BURN_RELATED_BUNDLES* pRelatedBundles, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ) { HRESULT hr = S_OK; @@ -2684,7 +2688,7 @@ static HRESULT OnExecutePackageDependencyAction( __in BURN_PACKAGES* pPackages, __in BURN_RELATED_BUNDLES* pRelatedBundles, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ) { HRESULT hr = S_OK; @@ -2953,7 +2957,7 @@ LExit: static HRESULT OnCleanPackage( __in BURN_PACKAGES* pPackages, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ) { HRESULT hr = S_OK; @@ -2982,7 +2986,7 @@ static HRESULT OnLaunchApprovedExe( __in BURN_APPROVED_EXES* pApprovedExes, __in BURN_VARIABLES* pVariables, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ) { HRESULT hr = S_OK; @@ -3051,7 +3055,7 @@ LExit: static HRESULT OnMsiBeginTransaction( __in BURN_PACKAGES* pPackages, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ) { HRESULT hr = S_OK; @@ -3067,7 +3071,7 @@ static HRESULT OnMsiBeginTransaction( hr = BuffReadString(pbData, cbData, &iData, &sczLogPath); ExitOnFailure(hr, "Failed to read transaction log path."); - PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary); + hr = PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary); ExitOnFailure(hr, "Failed to find rollback boundary: %ls", sczId); pRollbackBoundary->sczLogPath = sczLogPath; @@ -3089,7 +3093,7 @@ LExit: static HRESULT OnMsiCommitTransaction( __in BURN_PACKAGES* pPackages, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ) { HRESULT hr = S_OK; @@ -3105,7 +3109,7 @@ static HRESULT OnMsiCommitTransaction( hr = BuffReadString(pbData, cbData, &iData, &sczLogPath); ExitOnFailure(hr, "Failed to read transaction log path."); - PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary); + hr = PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary); ExitOnFailure(hr, "Failed to find rollback boundary: %ls", sczId); pRollbackBoundary->sczLogPath = sczLogPath; @@ -3127,7 +3131,7 @@ LExit: static HRESULT OnMsiRollbackTransaction( __in BURN_PACKAGES* pPackages, __in BYTE* pbData, - __in DWORD cbData + __in SIZE_T cbData ) { HRESULT hr = S_OK; @@ -3143,7 +3147,7 @@ static HRESULT OnMsiRollbackTransaction( hr = BuffReadString(pbData, cbData, &iData, &sczLogPath); ExitOnFailure(hr, "Failed to read transaction log path."); - PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary); + hr = PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary); ExitOnFailure(hr, "Failed to find rollback boundary: %ls", sczId); pRollbackBoundary->sczLogPath = sczLogPath; diff --git a/src/engine/embedded.cpp b/src/engine/embedded.cpp index b512b365..03898ebd 100644 --- a/src/engine/embedded.cpp +++ b/src/engine/embedded.cpp @@ -22,14 +22,14 @@ static HRESULT OnEmbeddedErrorMessage( __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, __in LPVOID pvContext, __in_bcount(cbData) BYTE* pbData, - __in DWORD cbData, + __in SIZE_T cbData, __out DWORD* pdwResult ); static HRESULT OnEmbeddedProgress( __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, __in LPVOID pvContext, __in_bcount(cbData) BYTE* pbData, - __in DWORD cbData, + __in SIZE_T cbData, __out DWORD* pdwResult ); @@ -142,7 +142,7 @@ static HRESULT OnEmbeddedErrorMessage( __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, __in LPVOID pvContext, __in_bcount(cbData) BYTE* pbData, - __in DWORD cbData, + __in SIZE_T cbData, __out DWORD* pdwResult ) { @@ -176,7 +176,7 @@ static HRESULT OnEmbeddedProgress( __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, __in LPVOID pvContext, __in_bcount(cbData) BYTE* pbData, - __in DWORD cbData, + __in SIZE_T cbData, __out DWORD* pdwResult ) { diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index e2728d7f..8f024e98 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -324,6 +324,7 @@ static HRESULT InitializeEngineState( LPCWSTR wzParam = NULL; HANDLE hSectionFile = hEngineFile; HANDLE hSourceEngineFile = INVALID_HANDLE_VALUE; + DWORD64 qw = 0; pEngineState->automaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED; pEngineState->dwElevatedLoggingTlsId = TLS_OUT_OF_INDEXES; @@ -343,8 +344,10 @@ static HRESULT InitializeEngineState( ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED); } - hr = StrStringToUInt32(wzParam, 0, reinterpret_cast(&hSourceEngineFile)); + hr = StrStringToUInt64(wzParam, 0, &qw); ExitOnFailure(hr, "Failed to parse file handle: '%ls'", (wzParam)); + + hSourceEngineFile = (HANDLE)qw; } if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &pEngineState->argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF), BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF))) { @@ -354,8 +357,10 @@ static HRESULT InitializeEngineState( ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF); } - hr = StrStringToUInt32(wzParam, 0, reinterpret_cast(&hSectionFile)); + hr = StrStringToUInt64(wzParam, 0, &qw); ExitOnFailure(hr, "Failed to parse file handle: '%ls'", (wzParam)); + + hSectionFile = (HANDLE)qw; } } } diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index a6deb18d..9e90ee19 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -1,7 +1,7 @@ - + Debug @@ -166,6 +166,6 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + \ No newline at end of file diff --git a/src/engine/exeengine.cpp b/src/engine/exeengine.cpp index 0b53e266..8900984f 100644 --- a/src/engine/exeengine.cpp +++ b/src/engine/exeengine.cpp @@ -448,7 +448,7 @@ extern "C" HRESULT ExeEngineExecutePackage( } // build command - if (0 < lstrlenW(sczArguments)) + if (*sczArguments) { hr = VariableFormatString(pVariables, sczArguments, &sczArgumentsFormatted, NULL); ExitOnFailure(hr, "Failed to format argument string."); diff --git a/src/engine/logging.cpp b/src/engine/logging.cpp index fd2343fa..a1159c41 100644 --- a/src/engine/logging.cpp +++ b/src/engine/logging.cpp @@ -717,10 +717,10 @@ static HRESULT GetNonSessionSpecificTempFolder( { HRESULT hr = S_OK; WCHAR wzTempFolder[MAX_PATH] = { }; - DWORD cchTempFolder = 0; + SIZE_T cchTempFolder = 0; DWORD dwSessionId = 0; LPWSTR sczSessionId = 0; - DWORD cchSessionId = 0; + SIZE_T cchSessionId = 0; if (!::GetTempPathW(countof(wzTempFolder), wzTempFolder)) { @@ -740,7 +740,7 @@ static HRESULT GetNonSessionSpecificTempFolder( hr = ::StringCchLengthW(sczSessionId, STRSAFE_MAX_CCH, reinterpret_cast(&cchSessionId)); ExitOnFailure(hr, "Failed to get length of session id string."); - if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzTempFolder + cchTempFolder - cchSessionId, cchSessionId, sczSessionId, cchSessionId)) + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzTempFolder + cchTempFolder - cchSessionId, static_cast(cchSessionId), sczSessionId, static_cast(cchSessionId))) { cchTempFolder -= cchSessionId; } diff --git a/src/engine/packages.config b/src/engine/packages.config index 125a949e..7219a3da 100644 --- a/src/engine/packages.config +++ b/src/engine/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/engine/pipe.cpp b/src/engine/pipe.cpp index 47963e9d..a9fd24e8 100644 --- a/src/engine/pipe.cpp +++ b/src/engine/pipe.cpp @@ -429,7 +429,6 @@ extern "C" HRESULT PipeWaitForChildConnect( DWORD cbSecret = lstrlenW(wzSecret) * sizeof(WCHAR); DWORD dwCurrentProcessId = ::GetCurrentProcessId(); DWORD dwAck = 0; - DWORD cb = 0; for (DWORD i = 0; i < countof(hPipes) && INVALID_HANDLE_VALUE != hPipes[i]; ++i) { @@ -487,26 +486,18 @@ extern "C" HRESULT PipeWaitForChildConnect( } // Prove we are the one that created the elevated process by passing the secret. - if (!::WriteFile(hPipe, &cbSecret, sizeof(cbSecret), &cb, NULL)) - { - ExitWithLastError(hr, "Failed to write secret length to pipe."); - } + hr = FileWriteHandle(hPipe, reinterpret_cast(&cbSecret), sizeof(cbSecret)); + ExitOnFailure(hr, "Failed to write secret length to pipe."); - if (!::WriteFile(hPipe, wzSecret, cbSecret, &cb, NULL)) - { - ExitWithLastError(hr, "Failed to write secret to pipe."); - } + hr = FileWriteHandle(hPipe, reinterpret_cast(wzSecret), cbSecret); + ExitOnFailure(hr, "Failed to write secret to pipe."); - if (!::WriteFile(hPipe, &dwCurrentProcessId, sizeof(dwCurrentProcessId), &cb, NULL)) - { - ExitWithLastError(hr, "Failed to write our process id to pipe."); - } + hr = FileWriteHandle(hPipe, reinterpret_cast(&dwCurrentProcessId), sizeof(dwCurrentProcessId)); + ExitOnFailure(hr, "Failed to write our process id to pipe."); // Wait until the elevated process responds that it is ready to go. - if (!::ReadFile(hPipe, &dwAck, sizeof(dwAck), &cb, NULL)) - { - ExitWithLastError(hr, "Failed to read ACK from pipe."); - } + hr = FileReadHandle(hPipe, reinterpret_cast(&dwAck), sizeof(dwAck)); + ExitOnFailure(hr, "Failed to read ACK from pipe."); // The ACK should match out expected child process id. //if (pConnection->dwProcessId != dwAck) @@ -724,17 +715,8 @@ static HRESULT WritePipeMessage( ExitOnFailure(hr, "Failed to allocate message to write."); // Write the message. - DWORD cbWrote = 0; - SIZE_T cbTotalWritten = 0; - while (cbTotalWritten < cb) - { - if (!::WriteFile(hPipe, pv, (DWORD)(cb - cbTotalWritten), &cbWrote, NULL)) - { - ExitWithLastError(hr, "Failed to write message type to pipe."); - } - - cbTotalWritten += cbWrote; - } + hr = FileWriteHandle(hPipe, reinterpret_cast(pv), cb); + ExitOnFailure(hr, "Failed to write message type to pipe."); LExit: ReleaseMem(pv); @@ -747,46 +729,25 @@ static HRESULT GetPipeMessage( ) { HRESULT hr = S_OK; - DWORD rgdwMessageAndByteCount[2] = { }; - DWORD cb = 0; - DWORD cbRead = 0; + BYTE pbMessageAndByteCount[sizeof(DWORD) + sizeof(SIZE_T)] = { }; - while (cbRead < sizeof(rgdwMessageAndByteCount)) + hr = FileReadHandle(hPipe, pbMessageAndByteCount, sizeof(pbMessageAndByteCount)); + if (HRESULT_FROM_WIN32(ERROR_BROKEN_PIPE) == hr) { - if (!::ReadFile(hPipe, reinterpret_cast(rgdwMessageAndByteCount) + cbRead, sizeof(rgdwMessageAndByteCount) - cbRead, &cb, NULL)) - { - DWORD er = ::GetLastError(); - if (ERROR_MORE_DATA == er) - { - hr = S_OK; - } - else if (ERROR_BROKEN_PIPE == er) // parent process shut down, time to exit. - { - memset(rgdwMessageAndByteCount, 0, sizeof(rgdwMessageAndByteCount)); - hr = S_FALSE; - break; - } - else - { - hr = HRESULT_FROM_WIN32(er); - } - ExitOnRootFailure(hr, "Failed to read message from pipe."); - } - - cbRead += cb; + memset(pbMessageAndByteCount, 0, sizeof(pbMessageAndByteCount)); + hr = S_FALSE; } + ExitOnFailure(hr, "Failed to read message from pipe."); - pMsg->dwMessage = rgdwMessageAndByteCount[0]; - pMsg->cbData = rgdwMessageAndByteCount[1]; + pMsg->dwMessage = *(DWORD*)(pbMessageAndByteCount); + pMsg->cbData = *(SIZE_T*)(pbMessageAndByteCount + sizeof(DWORD)); if (pMsg->cbData) { pMsg->pvData = MemAlloc(pMsg->cbData, FALSE); ExitOnNull(pMsg->pvData, hr, E_OUTOFMEMORY, "Failed to allocate data for message."); - if (!::ReadFile(hPipe, pMsg->pvData, pMsg->cbData, &cb, NULL)) - { - ExitWithLastError(hr, "Failed to read data for message."); - } + hr = FileReadHandle(hPipe, reinterpret_cast(pMsg->pvData), pMsg->cbData); + ExitOnFailure(hr, "Failed to read data for message."); pMsg->fAllocatedData = TRUE; } @@ -810,15 +771,11 @@ static HRESULT ChildPipeConnected( LPWSTR sczVerificationSecret = NULL; DWORD cbVerificationSecret = 0; DWORD dwVerificationProcessId = 0; - DWORD dwRead = 0; DWORD dwAck = ::GetCurrentProcessId(); // send our process id as the ACK. - DWORD cb = 0; // Read the verification secret. - if (!::ReadFile(hPipe, &cbVerificationSecret, sizeof(cbVerificationSecret), &dwRead, NULL)) - { - ExitWithLastError(hr, "Failed to read size of verification secret from parent pipe."); - } + hr = FileReadHandle(hPipe, reinterpret_cast(&cbVerificationSecret), sizeof(cbVerificationSecret)); + ExitOnFailure(hr, "Failed to read size of verification secret from parent pipe."); if (255 < cbVerificationSecret / sizeof(WCHAR)) { @@ -829,10 +786,8 @@ static HRESULT ChildPipeConnected( hr = StrAlloc(&sczVerificationSecret, cbVerificationSecret / sizeof(WCHAR) + 1); ExitOnFailure(hr, "Failed to allocate buffer for verification secret."); - if (!::ReadFile(hPipe, sczVerificationSecret, cbVerificationSecret, &dwRead, NULL)) - { - ExitWithLastError(hr, "Failed to read verification secret from parent pipe."); - } + FileReadHandle(hPipe, reinterpret_cast(sczVerificationSecret), cbVerificationSecret); + ExitOnFailure(hr, "Failed to read verification secret from parent pipe."); // Verify the secrets match. if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, 0, sczVerificationSecret, -1, wzSecret, -1)) @@ -842,10 +797,8 @@ static HRESULT ChildPipeConnected( } // Read the verification process id. - if (!::ReadFile(hPipe, &dwVerificationProcessId, sizeof(dwVerificationProcessId), &dwRead, NULL)) - { - ExitWithLastError(hr, "Failed to read verification process id from parent pipe."); - } + hr = FileReadHandle(hPipe, reinterpret_cast(&dwVerificationProcessId), sizeof(dwVerificationProcessId)); + ExitOnFailure(hr, "Failed to read verification process id from parent pipe."); // If a process id was not provided, we'll trust the process id from the parent. if (*pdwProcessId == 0) @@ -859,10 +812,8 @@ static HRESULT ChildPipeConnected( } // All is well, tell the parent process. - if (!::WriteFile(hPipe, &dwAck, sizeof(dwAck), &cb, NULL)) - { - ExitWithLastError(hr, "Failed to inform parent process that child is running."); - } + hr = FileWriteHandle(hPipe, reinterpret_cast(&dwAck), sizeof(dwAck)); + ExitOnFailure(hr, "Failed to inform parent process that child is running."); LExit: ReleaseStr(sczVerificationSecret); diff --git a/src/engine/pipe.h b/src/engine/pipe.h index 085c3a76..429cd824 100644 --- a/src/engine/pipe.h +++ b/src/engine/pipe.h @@ -27,7 +27,7 @@ typedef enum _BURN_PIPE_MESSAGE_TYPE : DWORD typedef struct _BURN_PIPE_MESSAGE { DWORD dwMessage; - DWORD cbData; + SIZE_T cbData; BOOL fAllocatedData; LPVOID pvData; diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp index d0c67504..6f818ff3 100644 --- a/src/engine/variable.cpp +++ b/src/engine/variable.cpp @@ -1106,7 +1106,7 @@ static HRESULT FormatString( ::EnterCriticalSection(&pVariables->csAccess); // allocate buffer for format string - hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_CCH - 1, &cchIn); + hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_LENGTH, &cchIn); ExitOnFailure(hr, "Failed to length of format string."); hr = StrAlloc(&sczFormat, cchIn + 1); diff --git a/src/stub/packages.config b/src/stub/packages.config index 4f75128a..a98c0c8e 100644 --- a/src/stub/packages.config +++ b/src/stub/packages.config @@ -4,5 +4,5 @@ - + \ No newline at end of file diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index 2b8bd8ea..38710865 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -2,7 +2,7 @@ - + @@ -117,6 +117,6 @@ - + \ No newline at end of file diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index 11f96590..99db505d 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -3,8 +3,8 @@ - - + + Debug @@ -78,10 +78,10 @@ - ..\..\..\packages\WixBuildTools.TestSupport.4.0.47\lib\net472\WixBuildTools.TestSupport.dll + ..\..\..\packages\WixBuildTools.TestSupport.4.0.50\lib\net472\WixBuildTools.TestSupport.dll - ..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\lib\net472\WixBuildTools.TestSupport.Native.dll + ..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.50\lib\net472\WixBuildTools.TestSupport.Native.dll @@ -90,13 +90,13 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - + + + \ No newline at end of file diff --git a/src/test/BurnUnitTest/packages.config b/src/test/BurnUnitTest/packages.config index 21d87cf8..1eb30932 100644 --- a/src/test/BurnUnitTest/packages.config +++ b/src/test/BurnUnitTest/packages.config @@ -1,9 +1,9 @@  - - - + + + -- cgit v1.2.3-55-g6feb From 7099dd38ab902e7fb92706314fa8710a34f165a5 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 28 Apr 2021 16:43:23 -0500 Subject: size_t-ify BootstrapperEngine.h and BundleExtensionEngine.h --- .../inc/BootstrapperEngine.h | 8 ++++---- .../inc/BundleExtensionEngine.h | 8 ++++---- src/engine/burnextension.cpp | 2 +- src/engine/externalengine.cpp | 14 +++++++------- src/engine/externalengine.h | 8 ++++---- src/engine/userexperience.cpp | 2 +- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h index 0a974563..f6804733 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h @@ -183,7 +183,7 @@ typedef struct _BAENGINE_ESCAPESTRING_RESULTS DWORD cbSize; LPWSTR wzOut; // Should be initialized to the size of wzOut. - DWORD cchOut; + SIZE_T cchOut; } BAENGINE_ESCAPESTRING_RESULTS; typedef struct _BAENGINE_EVALUATECONDITION_ARGS @@ -209,7 +209,7 @@ typedef struct _BAENGINE_FORMATSTRING_RESULTS DWORD cbSize; LPWSTR wzOut; // Should be initialized to the size of wzOut. - DWORD cchOut; + SIZE_T cchOut; } BAENGINE_FORMATSTRING_RESULTS; typedef struct _BAENGINE_GETPACKAGECOUNT_ARGS @@ -246,7 +246,7 @@ typedef struct _BAENGINE_GETVARIABLESTRING_RESULTS DWORD cbSize; LPWSTR wzValue; // Should be initialized to the size of wzValue. - DWORD cchValue; + SIZE_T cchValue; } BAENGINE_GETVARIABLESTRING_RESULTS; typedef struct _BAENGINE_GETVARIABLEVERSION_ARGS @@ -260,7 +260,7 @@ typedef struct _BAENGINE_GETVARIABLEVERSION_RESULTS DWORD cbSize; LPWSTR wzValue; // Should be initialized to the size of wzValue. - DWORD cchValue; + SIZE_T cchValue; } BAENGINE_GETVARIABLEVERSION_RESULTS; typedef struct _BAENGINE_LAUNCHAPPROVEDEXE_ARGS diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h b/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h index 003ff635..b397ec16 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h @@ -54,7 +54,7 @@ typedef struct _BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_RESULTS DWORD cbSize; LPWSTR wzOut; // Should be initialized to the size of wzOut. - DWORD cchOut; + SIZE_T cchOut; } BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_RESULTS; typedef struct _BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_ARGS @@ -80,7 +80,7 @@ typedef struct _BUNDLE_EXTENSION_ENGINE_FORMATSTRING_RESULTS DWORD cbSize; LPWSTR wzOut; // Should be initialized to the size of wzOut. - DWORD cchOut; + SIZE_T cchOut; } BUNDLE_EXTENSION_ENGINE_FORMATSTRING_RESULTS; typedef struct _BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_ARGS @@ -106,7 +106,7 @@ typedef struct _BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_RESULTS DWORD cbSize; LPWSTR wzValue; // Should be initialized to the size of wzValue. - DWORD cchValue; + SIZE_T cchValue; } BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_RESULTS; typedef struct _BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_ARGS @@ -120,7 +120,7 @@ typedef struct _BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_RESULTS DWORD cbSize; LPWSTR wzValue; // Should be initialized to the size of wzValue. - DWORD cchValue; + SIZE_T cchValue; } BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_RESULTS; typedef struct _BUNDLE_EXTENSION_ENGINE_LOG_ARGS diff --git a/src/engine/burnextension.cpp b/src/engine/burnextension.cpp index 7568f75e..475df1c5 100644 --- a/src/engine/burnextension.cpp +++ b/src/engine/burnextension.cpp @@ -134,7 +134,7 @@ EXTERN_C HRESULT BurnExtensionLoad( args.cbSize = sizeof(BUNDLE_EXTENSION_CREATE_ARGS); args.pfnBundleExtensionEngineProc = EngineForExtensionProc; args.pvBundleExtensionEngineProcContext = pEngineContext; - args.qwEngineAPIVersion = MAKEQWORDVERSION(2020, 8, 31, 0); + args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 4, 27, 0); args.wzBootstrapperWorkingFolder = pEngineContext->pEngineState->userExperience.sczTempDirectory; args.wzBundleExtensionDataPath = sczBundleExtensionDataPath; args.wzExtensionId = pExtension->sczId; diff --git a/src/engine/externalengine.cpp b/src/engine/externalengine.cpp index 63177722..51a0e229 100644 --- a/src/engine/externalengine.cpp +++ b/src/engine/externalengine.cpp @@ -6,7 +6,7 @@ static HRESULT CopyStringToExternal( __in_z LPWSTR wzValue, __in_z_opt LPWSTR wzBuffer, - __inout DWORD* pcchBuffer + __inout SIZE_T* pcchBuffer ); // function definitions @@ -44,7 +44,7 @@ HRESULT ExternalEngineGetVariableString( __in BURN_ENGINE_STATE* pEngineState, __in_z LPCWSTR wzVariable, __out_ecount_opt(*pcchValue) LPWSTR wzValue, - __inout DWORD* pcchValue + __inout SIZE_T* pcchValue ) { HRESULT hr = S_OK; @@ -72,7 +72,7 @@ HRESULT ExternalEngineGetVariableVersion( __in BURN_ENGINE_STATE* pEngineState, __in_z LPCWSTR wzVariable, __out_ecount_opt(*pcchValue) LPWSTR wzValue, - __inout DWORD* pcchValue + __inout SIZE_T* pcchValue ) { HRESULT hr = S_OK; @@ -100,7 +100,7 @@ HRESULT ExternalEngineFormatString( __in BURN_ENGINE_STATE* pEngineState, __in_z LPCWSTR wzIn, __out_ecount_opt(*pcchOut) LPWSTR wzOut, - __inout DWORD* pcchOut + __inout SIZE_T* pcchOut ) { HRESULT hr = S_OK; @@ -127,7 +127,7 @@ HRESULT ExternalEngineFormatString( HRESULT ExternalEngineEscapeString( __in_z LPCWSTR wzIn, __out_ecount_opt(*pcchOut) LPWSTR wzOut, - __inout DWORD* pcchOut + __inout SIZE_T* pcchOut ) { HRESULT hr = S_OK; @@ -771,7 +771,7 @@ LExit: static HRESULT CopyStringToExternal( __in_z LPWSTR wzValue, __in_z_opt LPWSTR wzBuffer, - __inout DWORD* pcchBuffer + __inout SIZE_T* pcchBuffer ) { HRESULT hr = S_OK; @@ -788,7 +788,7 @@ static HRESULT CopyStringToExternal( if (fTooSmall) { - hr = ::StringCchLengthW(wzValue, STRSAFE_MAX_CCH, reinterpret_cast(pcchBuffer)); + hr = ::StringCchLengthW(wzValue, STRSAFE_MAX_LENGTH, reinterpret_cast(pcchBuffer)); if (SUCCEEDED(hr)) { hr = E_MOREDATA; diff --git a/src/engine/externalengine.h b/src/engine/externalengine.h index a007e5b2..2903615d 100644 --- a/src/engine/externalengine.h +++ b/src/engine/externalengine.h @@ -26,27 +26,27 @@ HRESULT ExternalEngineGetVariableString( __in BURN_ENGINE_STATE* pEngineState, __in_z LPCWSTR wzVariable, __out_ecount_opt(*pcchValue) LPWSTR wzValue, - __inout DWORD* pcchValue + __inout SIZE_T* pcchValue ); HRESULT ExternalEngineGetVariableVersion( __in BURN_ENGINE_STATE* pEngineState, __in_z LPCWSTR wzVariable, __out_ecount_opt(*pcchValue) LPWSTR wzValue, - __inout DWORD* pcchValue + __inout SIZE_T* pcchValue ); HRESULT ExternalEngineFormatString( __in BURN_ENGINE_STATE* pEngineState, __in_z LPCWSTR wzIn, __out_ecount_opt(*pcchOut) LPWSTR wzOut, - __inout DWORD* pcchOut + __inout SIZE_T* pcchOut ); HRESULT ExternalEngineEscapeString( __in_z LPCWSTR wzIn, __out_ecount_opt(*pcchOut) LPWSTR wzOut, - __inout DWORD* pcchOut + __inout SIZE_T* pcchOut ); HRESULT ExternalEngineEvaluateCondition( diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index 7e68d664..ab631951 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -111,7 +111,7 @@ extern "C" HRESULT UserExperienceLoad( args.pCommand = pCommand; args.pfnBootstrapperEngineProc = EngineForApplicationProc; args.pvBootstrapperEngineProcContext = pEngineContext; - args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 4, 14, 0); + args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 4, 27, 0); results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS); -- cgit v1.2.3-55-g6feb From 752f0e0576dc27e937c553ed9dce5576bd388e95 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 28 Apr 2021 16:43:48 -0500 Subject: Let BA override the package's cache type during Plan. Rename cache types to REMOVE, KEEP, FORCE. Change implementation of FORCE to not be through CACHED request state. Create package condition enum so BA can tell when InstallCondition wasn't specified. Tell BA when package is cached. Tell BA when package is planned to be cached and uncached. --- .../inc/BootstrapperApplication.h | 22 ++++- .../inc/BootstrapperEngine.h | 1 - src/engine/core.cpp | 8 +- src/engine/dependency.cpp | 1 - src/engine/exeengine.cpp | 2 +- src/engine/externalengine.cpp | 7 +- src/engine/logging.cpp | 2 - src/engine/msiengine.cpp | 20 +---- src/engine/mspengine.cpp | 1 - src/engine/msuengine.cpp | 2 +- src/engine/package.cpp | 8 +- src/engine/package.h | 10 +-- src/engine/plan.cpp | 97 ++++++++++++---------- src/engine/plan.h | 3 +- src/engine/userexperience.cpp | 22 +++-- src/engine/userexperience.h | 13 ++- 16 files changed, 117 insertions(+), 102 deletions(-) diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h index 603df890..2a6d5c8a 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -103,6 +103,20 @@ enum BOOTSTRAPPER_RELATION_TYPE BOOTSTRAPPER_RELATION_UPDATE, }; +enum BOOTSTRAPPER_CACHE_TYPE +{ + BOOTSTRAPPER_CACHE_TYPE_REMOVE, + BOOTSTRAPPER_CACHE_TYPE_KEEP, + BOOTSTRAPPER_CACHE_TYPE_FORCE, +}; + +enum BOOTSTRAPPER_PACKAGE_CONDITION_RESULT +{ + BOOTSTRAPPER_PACKAGE_CONDITION_DEFAULT, + BOOTSTRAPPER_PACKAGE_CONDITION_FALSE, + BOOTSTRAPPER_PACKAGE_CONDITION_TRUE, +}; + enum BOOTSTRAPPER_APPLICATION_MESSAGE { BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTBEGIN, @@ -682,6 +696,7 @@ struct BA_ONDETECTPACKAGECOMPLETE_ARGS LPCWSTR wzPackageId; HRESULT hrStatus; BOOTSTRAPPER_PACKAGE_STATE state; + BOOL fCached; }; struct BA_ONDETECTPACKAGECOMPLETE_RESULTS @@ -1062,6 +1077,8 @@ struct BA_ONPLANNEDPACKAGE_ARGS LPCWSTR wzPackageId; BOOTSTRAPPER_ACTION_STATE execute; BOOTSTRAPPER_ACTION_STATE rollback; + BOOL fPlannedCache; + BOOL fPlannedUncache; }; struct BA_ONPLANNEDPACKAGE_RESULTS @@ -1074,8 +1091,10 @@ struct BA_ONPLANPACKAGEBEGIN_ARGS DWORD cbSize; LPCWSTR wzPackageId; BOOTSTRAPPER_PACKAGE_STATE state; - BOOL fInstallCondition; + BOOL fCached; + BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition; BOOTSTRAPPER_REQUEST_STATE recommendedState; + BOOTSTRAPPER_CACHE_TYPE recommendedCacheType; }; struct BA_ONPLANPACKAGEBEGIN_RESULTS @@ -1083,6 +1102,7 @@ struct BA_ONPLANPACKAGEBEGIN_RESULTS DWORD cbSize; BOOL fCancel; BOOTSTRAPPER_REQUEST_STATE requestedState; + BOOTSTRAPPER_CACHE_TYPE requestedCacheType; }; struct BA_ONPLANPACKAGECOMPLETE_ARGS diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h index f6804733..9c9b38a5 100644 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h +++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h @@ -47,7 +47,6 @@ enum BOOTSTRAPPER_PACKAGE_STATE BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN, BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE, BOOTSTRAPPER_PACKAGE_STATE_ABSENT, - BOOTSTRAPPER_PACKAGE_STATE_CACHED, BOOTSTRAPPER_PACKAGE_STATE_PRESENT, BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED, }; diff --git a/src/engine/core.cpp b/src/engine/core.cpp index a915dad0..535043af 100644 --- a/src/engine/core.cpp +++ b/src/engine/core.cpp @@ -1640,12 +1640,6 @@ static HRESULT DetectPackage( ExitOnRootFailure(hr, "Package type not supported by detect yet."); } - // TODO: consider how to notify the UX that a package is cached. - //else if (BOOTSTRAPPER_PACKAGE_STATE_CACHED > pPackage->currentState && pPackage->fCached) - //{ - // pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_CACHED; - //} - LExit: if (FAILED(hr)) { @@ -1654,7 +1648,7 @@ LExit: if (fBegan) { - UserExperienceOnDetectPackageComplete(&pEngineState->userExperience, pPackage->sczId, hr, pPackage->currentState); + UserExperienceOnDetectPackageComplete(&pEngineState->userExperience, pPackage->sczId, hr, pPackage->currentState, pPackage->fCached); } return hr; diff --git a/src/engine/dependency.cpp b/src/engine/dependency.cpp index c4af207a..876cd8b3 100644 --- a/src/engine/dependency.cpp +++ b/src/engine/dependency.cpp @@ -1065,7 +1065,6 @@ static void CalculateDependencyActionStates( { case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough; case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough; - case BOOTSTRAPPER_PACKAGE_STATE_CACHED: *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_UNREGISTER; break; } diff --git a/src/engine/exeengine.cpp b/src/engine/exeengine.cpp index 8900984f..c0ba93e0 100644 --- a/src/engine/exeengine.cpp +++ b/src/engine/exeengine.cpp @@ -146,7 +146,7 @@ extern "C" HRESULT ExeEngineDetectPackage( if (pPackage->fCanAffectRegistration) { - pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_CACHED < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; } LExit: diff --git a/src/engine/externalengine.cpp b/src/engine/externalengine.cpp index 51a0e229..409353e4 100644 --- a/src/engine/externalengine.cpp +++ b/src/engine/externalengine.cpp @@ -582,6 +582,11 @@ HRESULT ExternalEnginePlan( { HRESULT hr = S_OK; + if (BOOTSTRAPPER_ACTION_LAYOUT > action || BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED < action) + { + ExitOnRootFailure(hr = E_INVALIDARG, "BA passed invalid action to Plan: %u.", action); + } + if (!::PostThreadMessageW(dwThreadId, WM_BURN_PLAN, 0, action)) { ExitWithLastError(hr, "Failed to post plan message."); @@ -622,7 +627,7 @@ HRESULT ExternalEngineApply( ExitOnNull(hwndParent, hr, E_INVALIDARG, "BA passed NULL hwndParent to Apply."); if (!::IsWindow(hwndParent)) { - ExitOnFailure(hr = E_INVALIDARG, "BA passed invalid hwndParent to Apply."); + ExitOnRootFailure(hr = E_INVALIDARG, "BA passed invalid hwndParent to Apply."); } if (!::PostThreadMessageW(dwThreadId, WM_BURN_APPLY, 0, reinterpret_cast(hwndParent))) diff --git a/src/engine/logging.cpp b/src/engine/logging.cpp index a1159c41..065ef907 100644 --- a/src/engine/logging.cpp +++ b/src/engine/logging.cpp @@ -390,8 +390,6 @@ extern "C" LPCSTR LoggingPackageStateToString( return "Obsolete"; case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: return "Absent"; - case BOOTSTRAPPER_PACKAGE_STATE_CACHED: - return "Cached"; case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: return "Present"; case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp index e3dc4671..3e96e5f9 100644 --- a/src/engine/msiengine.cpp +++ b/src/engine/msiengine.cpp @@ -700,7 +700,7 @@ extern "C" HRESULT MsiEngineDetectPackage( if (pPackage->fCanAffectRegistration) { - pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_CACHED < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; } LExit: @@ -768,7 +768,7 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( if (pPackage->Msi.cFeatures) { // If the package is present and we're repairing it. - BOOL fRepairingPackage = (BOOTSTRAPPER_PACKAGE_STATE_CACHED < pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested); + BOOL fRepairingPackage = (BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested); // plan features for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) @@ -829,21 +829,6 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( } break; - case BOOTSTRAPPER_PACKAGE_STATE_CACHED: - switch (pPackage->requested) - { - case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_MEND: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_REPAIR: - execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; - break; - - default: - execute = BOOTSTRAPPER_ACTION_STATE_NONE; - break; - } - break; - case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough; case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: switch (pPackage->requested) @@ -892,7 +877,6 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough; case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough; - case BOOTSTRAPPER_PACKAGE_STATE_CACHED: // If the package is uninstallable and we requested to put the package on the machine then // remove the package during rollback. if (pPackage->fUninstallable && diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp index d5673700..6d58d324 100644 --- a/src/engine/mspengine.cpp +++ b/src/engine/mspengine.cpp @@ -451,7 +451,6 @@ extern "C" HRESULT MspEnginePlanCalculatePackage( break; case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough; - case BOOTSTRAPPER_PACKAGE_STATE_CACHED: switch (pTargetProduct->requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; diff --git a/src/engine/msuengine.cpp b/src/engine/msuengine.cpp index f807bf6b..6003123b 100644 --- a/src/engine/msuengine.cpp +++ b/src/engine/msuengine.cpp @@ -71,7 +71,7 @@ extern "C" HRESULT MsuEngineDetectPackage( if (pPackage->fCanAffectRegistration) { - pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_CACHED < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; } LExit: diff --git a/src/engine/package.cpp b/src/engine/package.cpp index dd4e498a..3f8c8b0f 100644 --- a/src/engine/package.cpp +++ b/src/engine/package.cpp @@ -118,20 +118,20 @@ extern "C" HRESULT PackagesParseFromXml( { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"remove", -1)) { - pPackage->cacheType = BURN_CACHE_TYPE_NO; + pPackage->authoredCacheType = BOOTSTRAPPER_CACHE_TYPE_REMOVE; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"keep", -1)) { - pPackage->cacheType = BURN_CACHE_TYPE_YES; + pPackage->authoredCacheType = BOOTSTRAPPER_CACHE_TYPE_KEEP; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"force", -1)) { - pPackage->cacheType = BURN_CACHE_TYPE_ALWAYS; + pPackage->authoredCacheType = BOOTSTRAPPER_CACHE_TYPE_FORCE; } else { hr = E_UNEXPECTED; - ExitOnFailure(hr, "Invalid cache type: %ls", scz); + ExitOnRootFailure(hr, "Invalid cache type: %ls", scz); } } ExitOnFailure(hr, "Failed to get @Cache."); diff --git a/src/engine/package.h b/src/engine/package.h index 2091af94..89a3d6e9 100644 --- a/src/engine/package.h +++ b/src/engine/package.h @@ -41,13 +41,6 @@ enum BURN_PACKAGE_TYPE BURN_PACKAGE_TYPE_MSU, }; -enum BURN_CACHE_TYPE -{ - BURN_CACHE_TYPE_NO, - BURN_CACHE_TYPE_YES, - BURN_CACHE_TYPE_ALWAYS, -}; - enum BURN_DEPENDENCY_ACTION { BURN_DEPENDENCY_ACTION_NONE, @@ -223,7 +216,7 @@ typedef struct _BURN_PACKAGE BOOL fVital; BOOL fCanAffectRegistration; - BURN_CACHE_TYPE cacheType; + BOOTSTRAPPER_CACHE_TYPE authoredCacheType; LPWSTR sczCacheId; DWORD64 qwInstallSize; @@ -235,6 +228,7 @@ typedef struct _BURN_PACKAGE BOOTSTRAPPER_PACKAGE_STATE currentState; // only valid after Detect. BOOL fCached; // only valid after Detect. BOOL fPackageProviderExists; // only valid after Detect. + BOOTSTRAPPER_CACHE_TYPE cacheType; // only valid during Plan. BOOTSTRAPPER_REQUEST_STATE defaultRequested;// only valid during Plan. BOOTSTRAPPER_REQUEST_STATE requested; // only valid during Plan. BOOL fPlannedCache; // only valid during Plan. diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp index e5c1ee36..9a4aa5f1 100644 --- a/src/engine/plan.cpp +++ b/src/engine/plan.cpp @@ -137,6 +137,10 @@ static BOOL NeedsCache( __in BURN_PACKAGE* pPackage, __in BOOL fExecute ); +static BOOL ForceCache( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage + ); // function definitions @@ -312,28 +316,20 @@ extern "C" HRESULT PlanDefaultPackageRequestState( __in BURN_PACKAGE_TYPE packageType, __in BOOTSTRAPPER_PACKAGE_STATE currentState, __in BOOL fPermanent, - __in BURN_CACHE_TYPE cacheType, __in BOOTSTRAPPER_ACTION action, - __in BOOL fInstallCondition, + __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, __in BOOTSTRAPPER_RELATION_TYPE relationType, __out BOOTSTRAPPER_REQUEST_STATE* pRequestState ) { HRESULT hr = S_OK; BOOTSTRAPPER_REQUEST_STATE defaultRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; - BOOL fFallbackToCache = BURN_CACHE_TYPE_ALWAYS == cacheType && BOOTSTRAPPER_ACTION_UNINSTALL != action && BOOTSTRAPPER_PACKAGE_STATE_CACHED > currentState; - // If doing layout, then always default to requesting the file be cached. + // If doing layout, then always default to requesting the package be cached. if (BOOTSTRAPPER_ACTION_LAYOUT == action) { *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE; } - else if (BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED == currentState && BOOTSTRAPPER_ACTION_UNINSTALL != action) - { - // Superseded means the package is on the machine but not active, so only uninstall operations are allowed. - // Requesting present makes sure always-cached packages are cached. - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; - } else if (BOOTSTRAPPER_RELATION_PATCH == relationType && BURN_PACKAGE_TYPE_MSP == packageType) { // For patch related bundles, only install a patch if currently absent during install, modify, or repair. @@ -341,20 +337,22 @@ extern "C" HRESULT PlanDefaultPackageRequestState( { *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; } - else if (fFallbackToCache) - { - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE; - } else { *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; } } + else if (BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED == currentState && BOOTSTRAPPER_ACTION_UNINSTALL != action) + { + // Superseded means the package is on the machine but not active, so only uninstall operations are allowed. + // All other operations do nothing. + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; + } else if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == currentState && !(BOOTSTRAPPER_ACTION_UNINSTALL == action && BURN_PACKAGE_TYPE_MSP == packageType)) { // Obsolete means the package is not on the machine and should not be installed, *except* patches can be obsolete // and present so allow them to be removed during uninstall. Everyone else, gets nothing. - *pRequestState = fFallbackToCache ? BOOTSTRAPPER_REQUEST_STATE_CACHE : BOOTSTRAPPER_REQUEST_STATE_NONE; + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; } else // pick the best option for the action state and install condition. { @@ -363,7 +361,7 @@ extern "C" HRESULT PlanDefaultPackageRequestState( // If we're doing an install, use the install condition // to determine whether to use the default request state or make the package absent. - if (BOOTSTRAPPER_ACTION_UNINSTALL != action && !fInstallCondition) + if (BOOTSTRAPPER_ACTION_UNINSTALL != action && BOOTSTRAPPER_PACKAGE_CONDITION_FALSE == installCondition) { *pRequestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT; } @@ -371,11 +369,6 @@ extern "C" HRESULT PlanDefaultPackageRequestState( { *pRequestState = defaultRequestState; } - - if (fFallbackToCache && BOOTSTRAPPER_REQUEST_STATE_CACHE > *pRequestState) - { - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE; - } } LExit: @@ -844,8 +837,8 @@ static HRESULT PlanPackagesHelper( { DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i; BURN_PACKAGE* pPackage = rgPackages + iPackage; - - UserExperienceOnPlannedPackage(pUX, pPackage->sczId, pPackage->execute, pPackage->rollback); + + UserExperienceOnPlannedPackage(pUX, pPackage->sczId, pPackage->execute, pPackage->rollback, pPackage->fPlannedCache, pPackage->fPlannedUncache); } LExit: @@ -861,6 +854,7 @@ static HRESULT InitializePackage( ) { HRESULT hr = S_OK; + BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition = BOOTSTRAPPER_PACKAGE_CONDITION_DEFAULT; BOOL fInstallCondition = FALSE; BOOL fBeginCalled = FALSE; @@ -874,20 +868,18 @@ static HRESULT InitializePackage( { hr = ConditionEvaluate(pVariables, pPackage->sczInstallCondition, &fInstallCondition); ExitOnFailure(hr, "Failed to evaluate install condition."); - } - else - { - fInstallCondition = TRUE; + + installCondition = fInstallCondition ? BOOTSTRAPPER_PACKAGE_CONDITION_TRUE : BOOTSTRAPPER_PACKAGE_CONDITION_FALSE; } // Remember the default requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it. - hr = PlanDefaultPackageRequestState(pPackage->type, pPackage->currentState, !pPackage->fUninstallable, pPackage->cacheType, pPlan->action, fInstallCondition, relationType, &pPackage->defaultRequested); + hr = PlanDefaultPackageRequestState(pPackage->type, pPackage->currentState, !pPackage->fUninstallable, pPlan->action, installCondition, relationType, &pPackage->defaultRequested); ExitOnFailure(hr, "Failed to set default package state."); pPackage->requested = pPackage->defaultRequested; fBeginCalled = TRUE; - hr = UserExperienceOnPlanPackageBegin(pUX, pPackage->sczId, pPackage->currentState, fInstallCondition, &pPackage->requested); + hr = UserExperienceOnPlanPackageBegin(pUX, pPackage->sczId, pPackage->currentState, pPackage->fCached, installCondition, &pPackage->requested, &pPackage->cacheType); ExitOnRootFailure(hr, "BA aborted plan package begin."); if (BURN_PACKAGE_TYPE_MSI == pPackage->type) @@ -926,8 +918,11 @@ static HRESULT ProcessPackage( if (BOOTSTRAPPER_ACTION_LAYOUT == pPlan->action) { - hr = PlanLayoutPackage(pPlan, pPackage); - ExitOnFailure(hr, "Failed to plan layout package."); + if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested) + { + hr = PlanLayoutPackage(pPlan, pPackage); + ExitOnFailure(hr, "Failed to plan layout package."); + } } else { @@ -939,6 +934,17 @@ static HRESULT ProcessPackage( } else { + if (ForceCache(pPlan, pPackage)) + { + hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent); + ExitOnFailure(hr, "Failed to plan cache package."); + + if (pPackage->fPerMachine) + { + pPlan->fPerMachine = TRUE; + } + } + // Make sure the package is properly ref-counted even if no plan is requested. hr = PlanDependencyActions(fBundlePerMachine, pPlan, pPackage); ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId); @@ -1072,8 +1078,7 @@ extern "C" HRESULT PlanExecutePackage( ) { HRESULT hr = S_OK; - BOOL fRequestedCache = BOOTSTRAPPER_REQUEST_STATE_CACHE == pPackage->requested || - BOOTSTRAPPER_REQUEST_STATE_ABSENT < pPackage->requested && BURN_CACHE_TYPE_ALWAYS == pPackage->cacheType; + BOOL fRequestedCache = BOOTSTRAPPER_REQUEST_STATE_CACHE == pPackage->requested || ForceCache(pPlan, pPackage); hr = CalculateExecuteActions(pPackage, pPlan->pActiveRollbackBoundary); ExitOnFailure(hr, "Failed to calculate plan actions for package: %ls", pPackage->sczId); @@ -1097,12 +1102,12 @@ extern "C" HRESULT PlanExecutePackage( // Add the cache and install size to estimated size if it will be on the machine at the end of the install if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || - BOOTSTRAPPER_REQUEST_STATE_CACHE == pPackage->requested || + fRequestedCache || (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_ABSENT < pPackage->requested) ) { // If the package will remain in the cache, add the package size to the estimated size - if (BURN_CACHE_TYPE_NO < pPackage->cacheType) + if (BOOTSTRAPPER_CACHE_TYPE_REMOVE < pPackage->cacheType) { pPlan->qwEstimatedSize += pPackage->qwSize; } @@ -1522,12 +1527,12 @@ extern "C" HRESULT PlanCleanPackage( BURN_CLEAN_ACTION* pCleanAction = NULL; // The following is a complex set of logic that determines when a package should be cleaned from the cache. - if (BURN_CACHE_TYPE_ALWAYS > pPackage->cacheType || BOOTSTRAPPER_ACTION_CACHE > pPlan->action) + if (BOOTSTRAPPER_CACHE_TYPE_FORCE > pPackage->cacheType || BOOTSTRAPPER_ACTION_CACHE > pPlan->action) { // The following are all different reasons why the package should be cleaned from the cache. // The else-ifs are used to make the conditions easier to see (rather than have them combined // in one huge condition). - if (BURN_CACHE_TYPE_YES > pPackage->cacheType) // easy, package is not supposed to stay cached. + if (BOOTSTRAPPER_CACHE_TYPE_KEEP > pPackage->cacheType) // easy, package is not supposed to stay cached. { fPlanCleanPackage = TRUE; } @@ -1867,6 +1872,7 @@ static void ResetPlannedPackageState( ) { // Reset package state that is a result of planning. + pPackage->cacheType = pPackage->authoredCacheType; pPackage->defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE; pPackage->requested = BOOTSTRAPPER_REQUEST_STATE_NONE; pPackage->fPlannedCache = FALSE; @@ -1948,10 +1954,6 @@ static HRESULT GetActionDefaultRequestState( *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; break; - case BOOTSTRAPPER_PACKAGE_STATE_CACHED: - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; - break; - default: *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE; break; @@ -1979,10 +1981,6 @@ static HRESULT GetActionDefaultRequestState( *pRequestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT; break; - case BOOTSTRAPPER_PACKAGE_STATE_CACHED: - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE; - break; - case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; break; @@ -2524,6 +2522,15 @@ static BOOL NeedsCache( } } +static BOOL ForceCache( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage + ) +{ + // All packages that have cacheType set to force should be cached if the bundle is going to be present. + return BOOTSTRAPPER_CACHE_TYPE_FORCE == pPackage->cacheType && BOOTSTRAPPER_ACTION_UNINSTALL < pPlan->action; +} + static void CacheActionLog( __in DWORD iAction, __in BURN_CACHE_ACTION* pAction, diff --git a/src/engine/plan.h b/src/engine/plan.h index 4ba2df6a..00ab5516 100644 --- a/src/engine/plan.h +++ b/src/engine/plan.h @@ -309,9 +309,8 @@ HRESULT PlanDefaultPackageRequestState( __in BURN_PACKAGE_TYPE packageType, __in BOOTSTRAPPER_PACKAGE_STATE currentState, __in BOOL fPermanent, - __in BURN_CACHE_TYPE cacheType, __in BOOTSTRAPPER_ACTION action, - __in BOOL fInstallCondition, + __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, __in BOOTSTRAPPER_RELATION_TYPE relationType, __out BOOTSTRAPPER_REQUEST_STATE* pRequestState ); diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp index ab631951..2215a070 100644 --- a/src/engine/userexperience.cpp +++ b/src/engine/userexperience.cpp @@ -1141,7 +1141,8 @@ EXTERN_C BAAPI UserExperienceOnDetectPackageComplete( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, __in HRESULT hrStatus, - __in BOOTSTRAPPER_PACKAGE_STATE state + __in BOOTSTRAPPER_PACKAGE_STATE state, + __in BOOL fCached ) { HRESULT hr = S_OK; @@ -1152,6 +1153,7 @@ EXTERN_C BAAPI UserExperienceOnDetectPackageComplete( args.wzPackageId = wzPackageId; args.hrStatus = hrStatus; args.state = state; + args.fCached = fCached; results.cbSize = sizeof(results); @@ -1937,7 +1939,9 @@ EXTERN_C BAAPI UserExperienceOnPlannedPackage( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, __in BOOTSTRAPPER_ACTION_STATE execute, - __in BOOTSTRAPPER_ACTION_STATE rollback + __in BOOTSTRAPPER_ACTION_STATE rollback, + __in BOOL fPlannedCache, + __in BOOL fPlannedUncache ) { HRESULT hr = S_OK; @@ -1948,6 +1952,8 @@ EXTERN_C BAAPI UserExperienceOnPlannedPackage( args.wzPackageId = wzPackageId; args.execute = execute; args.rollback = rollback; + args.fPlannedCache = fPlannedCache; + args.fPlannedUncache = fPlannedUncache; results.cbSize = sizeof(results); @@ -1962,8 +1968,10 @@ EXTERN_C BAAPI UserExperienceOnPlanPackageBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, __in BOOTSTRAPPER_PACKAGE_STATE state, - __in BOOL fInstallCondition, - __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState + __in BOOL fCached, + __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState, + __inout BOOTSTRAPPER_CACHE_TYPE* pRequestedCacheType ) { HRESULT hr = S_OK; @@ -1973,11 +1981,14 @@ EXTERN_C BAAPI UserExperienceOnPlanPackageBegin( args.cbSize = sizeof(args); args.wzPackageId = wzPackageId; args.state = state; - args.fInstallCondition = fInstallCondition; + args.fCached = fCached; + args.installCondition = installCondition; args.recommendedState = *pRequestedState; + args.recommendedCacheType = *pRequestedCacheType; results.cbSize = sizeof(results); results.requestedState = *pRequestedState; + results.requestedCacheType = *pRequestedCacheType; hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGEBEGIN, &args, &results); ExitOnFailure(hr, "BA OnPlanPackageBegin failed."); @@ -1987,6 +1998,7 @@ EXTERN_C BAAPI UserExperienceOnPlanPackageBegin( hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); } *pRequestedState = results.requestedState; + *pRequestedCacheType = results.requestedCacheType; LExit: return hr; diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h index e1041624..f2453dca 100644 --- a/src/engine/userexperience.h +++ b/src/engine/userexperience.h @@ -279,7 +279,8 @@ BAAPI UserExperienceOnDetectPackageComplete( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, __in HRESULT hrStatus, - __in BOOTSTRAPPER_PACKAGE_STATE state + __in BOOTSTRAPPER_PACKAGE_STATE state, + __in BOOL fCached ); BAAPI UserExperienceOnDetectRelatedBundle( __in BURN_USER_EXPERIENCE* pUserExperience, @@ -448,14 +449,18 @@ BAAPI UserExperienceOnPlannedPackage( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, __in BOOTSTRAPPER_ACTION_STATE execute, - __in BOOTSTRAPPER_ACTION_STATE rollback + __in BOOTSTRAPPER_ACTION_STATE rollback, + __in BOOL fPlannedCache, + __in BOOL fPlannedUncache ); BAAPI UserExperienceOnPlanPackageBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in_z LPCWSTR wzPackageId, __in BOOTSTRAPPER_PACKAGE_STATE state, - __in BOOL fInstallCondition, - __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState + __in BOOL fCached, + __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState, + __inout BOOTSTRAPPER_CACHE_TYPE* pRequestedCacheType ); BAAPI UserExperienceOnPlanPackageComplete( __in BURN_USER_EXPERIENCE* pUserExperience, -- cgit v1.2.3-55-g6feb From b6c44ffcf2ef7a706598fe218523ec377e96cc47 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Wed, 21 Apr 2021 11:53:24 -0700 Subject: Migrate Burn API headers to balutil repository --- appveyor.cmd | 1 - src/Cpp.Build.props | 115 -- .../WixToolset.BootstrapperCore.Native.nuspec | 19 - .../WixToolset.BootstrapperCore.Native.proj | 19 - .../build/WixToolset.BootstrapperCore.Native.props | 13 - .../inc/BootstrapperApplication.h | 1318 -------------------- .../inc/BootstrapperEngine.h | 442 ------- .../inc/BundleExtension.h | 60 - .../inc/BundleExtensionEngine.h | 184 --- src/engine/engine.vcxproj | 21 +- src/engine/precomp.h | 8 +- src/stub/WixToolset.Burn.nuspec | 24 - src/test/BurnUnitTest/BurnUnitTest.vcxproj | 25 +- src/test/BurnUnitTest/packages.config | 7 +- 14 files changed, 42 insertions(+), 2214 deletions(-) delete mode 100644 src/Cpp.Build.props delete mode 100644 src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.nuspec delete mode 100644 src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.proj delete mode 100644 src/WixToolset.BootstrapperCore.Native/build/WixToolset.BootstrapperCore.Native.props delete mode 100644 src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h delete mode 100644 src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h delete mode 100644 src/WixToolset.BootstrapperCore.Native/inc/BundleExtension.h delete mode 100644 src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h delete mode 100644 src/stub/WixToolset.Burn.nuspec diff --git a/appveyor.cmd b/appveyor.cmd index b9cf1258..4f18fbee 100644 --- a/appveyor.cmd +++ b/appveyor.cmd @@ -10,7 +10,6 @@ msbuild -p:Configuration=Release;Platform=x64 || exit /b msbuild -p:Configuration=Release;Platform=arm64 || exit /b msbuild -p:Configuration=Release -t:Pack src\stub\stub.vcxproj || exit /b -msbuild -p:Configuration=Release -t:Pack src\WixToolset.BootstrapperCore.Native\WixToolset.BootstrapperCore.Native.proj || exit /b @popd @endlocal \ No newline at end of file diff --git a/src/Cpp.Build.props b/src/Cpp.Build.props deleted file mode 100644 index a734aab0..00000000 --- a/src/Cpp.Build.props +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - Win32 - $(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\ - $(OutputPath)$(Platform)\ - - - $(Company) - $(Copyright) - - - - $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) - - - - $(MSBuildThisFileDirectory)CustomizedNativeRecommendedRules.ruleset - - - - - $(DisableSpecificCompilerWarnings) - Level4 - $(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0600;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) - Use - precomp.h - StdCall - true - false - Guard - -YlprecompDefine - /Zc:threadSafeInit- %(AdditionalOptions) - true - - - $(ArmPreprocessorDefinitions);%(PreprocessorDefinitions) - $(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories) - - - $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories) - - - $(ProjectSubSystem) - $(ProjectModuleDefinitionFile) - $(ResourceOnlyDll) - true - $(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies) - $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories) - /IGNORE:4099 %(AdditionalOptions) - - - - - - NoExtensions - - - - - CDecl - - - - - OldStyle - true - true - - - - - Disabled - EnableFastChecks - _DEBUG;DEBUG;%(PreprocessorDefinitions) - MultiThreadedDebug - - - - - - - MultiThreadedDebugDll - - - - - MinSpace - NDEBUG;%(PreprocessorDefinitions) - true - true - MultiThreaded - - - true - true - - - - - - - MultiThreadedDll - - - - - $(LinkKeyFile) - $(LinkDelaySign) - - - diff --git a/src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.nuspec b/src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.nuspec deleted file mode 100644 index b10b75d2..00000000 --- a/src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.nuspec +++ /dev/null @@ -1,19 +0,0 @@ - - - - $id$ - $version$ - WiX Toolset Team - WiX Toolset Team - MS-RL - https://github.com/wixtoolset/BootstrapperCore - false - $description$ - $copyright$ - - - - - - - diff --git a/src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.proj b/src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.proj deleted file mode 100644 index 30b48b29..00000000 --- a/src/WixToolset.BootstrapperCore.Native/WixToolset.BootstrapperCore.Native.proj +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - WixToolset.BootstrapperCore.Native - WiX Bootstrapper native interfaces - - - - - - - - - - - \ No newline at end of file diff --git a/src/WixToolset.BootstrapperCore.Native/build/WixToolset.BootstrapperCore.Native.props b/src/WixToolset.BootstrapperCore.Native/build/WixToolset.BootstrapperCore.Native.props deleted file mode 100644 index 82f81163..00000000 --- a/src/WixToolset.BootstrapperCore.Native/build/WixToolset.BootstrapperCore.Native.props +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - $(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories) - - - $(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories) - - - diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h deleted file mode 100644 index 2a6d5c8a..00000000 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ /dev/null @@ -1,1318 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -enum BOOTSTRAPPER_DISPLAY -{ - BOOTSTRAPPER_DISPLAY_UNKNOWN, - BOOTSTRAPPER_DISPLAY_EMBEDDED, - BOOTSTRAPPER_DISPLAY_NONE, - BOOTSTRAPPER_DISPLAY_PASSIVE, - BOOTSTRAPPER_DISPLAY_FULL, -}; - -enum BOOTSTRAPPER_RESTART -{ - BOOTSTRAPPER_RESTART_UNKNOWN, - BOOTSTRAPPER_RESTART_NEVER, - BOOTSTRAPPER_RESTART_PROMPT, - BOOTSTRAPPER_RESTART_AUTOMATIC, - BOOTSTRAPPER_RESTART_ALWAYS, -}; - -enum BOOTSTRAPPER_RESUME_TYPE -{ - BOOTSTRAPPER_RESUME_TYPE_NONE, - BOOTSTRAPPER_RESUME_TYPE_INVALID, // resume information is present but invalid - BOOTSTRAPPER_RESUME_TYPE_INTERRUPTED, // relaunched after an unexpected interruption - BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING, // reboot has not taken place yet - BOOTSTRAPPER_RESUME_TYPE_REBOOT, // relaunched after reboot - BOOTSTRAPPER_RESUME_TYPE_SUSPEND, // relaunched after suspend - BOOTSTRAPPER_RESUME_TYPE_ARP, // launched from ARP -}; - -enum BOOTSTRAPPER_ERROR_TYPE -{ - BOOTSTRAPPER_ERROR_TYPE_ELEVATE, // error occurred trying to elevate. - BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER, // error came from windows installer. - BOOTSTRAPPER_ERROR_TYPE_EXE_PACKAGE, // error came from an exe package. - BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER, // error occurred trying to authenticate with HTTP server. - BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY, // error occurred trying to authenticate with HTTP proxy. - BOOTSTRAPPER_ERROR_TYPE_APPLY, // error occurred during apply. -}; - -enum BOOTSTRAPPER_RELATED_OPERATION -{ - BOOTSTRAPPER_RELATED_OPERATION_NONE, - BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE, - BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE, - BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE, - BOOTSTRAPPER_RELATED_OPERATION_REMOVE, - BOOTSTRAPPER_RELATED_OPERATION_INSTALL, - BOOTSTRAPPER_RELATED_OPERATION_REPAIR, -}; - -enum BOOTSTRAPPER_CACHE_OPERATION -{ - // There is no source available. - BOOTSTRAPPER_CACHE_OPERATION_NONE, - // Copy the payload or container from the chosen local source. - BOOTSTRAPPER_CACHE_OPERATION_COPY, - // Download the payload or container using the download URL. - BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD, - // Extract the payload from the container. - BOOTSTRAPPER_CACHE_OPERATION_EXTRACT, -}; - -enum BOOTSTRAPPER_CACHE_RESOLVE_OPERATION -{ - // There is no source available. - BOOTSTRAPPER_CACHE_RESOLVE_NONE, - // Copy the payload or container from the chosen local source. - BOOTSTRAPPER_CACHE_RESOLVE_LOCAL, - // Download the payload or container from the download URL. - BOOTSTRAPPER_CACHE_RESOLVE_DOWNLOAD, - // Extract the payload from the container. - BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER, - // Look again for the payload or container locally. - BOOTSTRAPPER_CACHE_RESOLVE_RETRY, -}; - -enum BOOTSTRAPPER_CACHE_VERIFY_STEP -{ - BOOTSTRAPPER_CACHE_VERIFY_STEP_STAGE, - BOOTSTRAPPER_CACHE_VERIFY_STEP_HASH, - BOOTSTRAPPER_CACHE_VERIFY_STEP_FINALIZE, -}; - -enum BOOTSTRAPPER_APPLY_RESTART -{ - BOOTSTRAPPER_APPLY_RESTART_NONE, - BOOTSTRAPPER_APPLY_RESTART_REQUIRED, - BOOTSTRAPPER_APPLY_RESTART_INITIATED, -}; - -enum BOOTSTRAPPER_RELATION_TYPE -{ - BOOTSTRAPPER_RELATION_NONE, - BOOTSTRAPPER_RELATION_DETECT, - BOOTSTRAPPER_RELATION_UPGRADE, - BOOTSTRAPPER_RELATION_ADDON, - BOOTSTRAPPER_RELATION_PATCH, - BOOTSTRAPPER_RELATION_DEPENDENT, - BOOTSTRAPPER_RELATION_UPDATE, -}; - -enum BOOTSTRAPPER_CACHE_TYPE -{ - BOOTSTRAPPER_CACHE_TYPE_REMOVE, - BOOTSTRAPPER_CACHE_TYPE_KEEP, - BOOTSTRAPPER_CACHE_TYPE_FORCE, -}; - -enum BOOTSTRAPPER_PACKAGE_CONDITION_RESULT -{ - BOOTSTRAPPER_PACKAGE_CONDITION_DEFAULT, - BOOTSTRAPPER_PACKAGE_CONDITION_FALSE, - BOOTSTRAPPER_PACKAGE_CONDITION_TRUE, -}; - -enum BOOTSTRAPPER_APPLICATION_MESSAGE -{ - BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONSTARTUP, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONSHUTDOWN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMSHUTDOWN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTFORWARDCOMPATIBLEBUNDLE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATEBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATECOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGEBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDMSIPACKAGE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPATCHTARGET, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTMSIFEATURE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGECOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGEBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPATCHTARGET, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIFEATURE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGECOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATEBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATECOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONPROGRESS, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONERROR, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERCOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGEBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREPROGRESS, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRERESOLVING, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRECOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYCOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGECOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGEBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPATCHTARGET, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROGRESS, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEMSIMESSAGE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEFILESINUSE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGECOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTECOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERCOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYCOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXEBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXECOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIPACKAGE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONBEGINMSITRANSACTIONBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONBEGINMSITRANSACTIONCOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONCOMMITMSITRANSACTIONBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONCOMMITMSITRANSACTIONCOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONROLLBACKMSITRANSACTIONBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONROLLBACKMSITRANSACTIONCOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONPAUSEAUTOMATICUPDATESBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONPAUSEAUTOMATICUPDATESCOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMRESTOREPOINTBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMRESTOREPOINTCOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDPACKAGE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANFORWARDCOMPATIBLEBUNDLE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYPROGRESS, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECONTAINERORPAYLOADVERIFYBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECONTAINERORPAYLOADVERIFYCOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECONTAINERORPAYLOADVERIFYPROGRESS, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPAYLOADEXTRACTBEGIN, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPAYLOADEXTRACTCOMPLETE, - BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPAYLOADEXTRACTPROGRESS, -}; - -enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION -{ - BOOTSTRAPPER_APPLYCOMPLETE_ACTION_NONE, - // Instructs the engine to restart. - // The engine will not launch again after the machine is rebooted. - // Ignored if reboot was already initiated by OnExecutePackageComplete(). - BOOTSTRAPPER_APPLYCOMPLETE_ACTION_RESTART, -}; - -enum BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION -{ - BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_NONE, - // Instructs the engine to try the acquisition of the payload again. - // Ignored if hrStatus is a success. - BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_RETRY, -}; - -enum BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION -{ - BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE, - // Instructs the engine to ignore non-vital package failures and - // continue with the caching. - // Ignored if hrStatus is a success or the package is vital. - BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE, - // Instructs the engine to try the acquisition and verification of the package again. - // Ignored if hrStatus is a success. - BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_RETRY, -}; - -enum BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION -{ - BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE, - // Ignored if hrStatus is a success. - BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION, - // Ignored if hrStatus is a success. - BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION, -}; - -enum BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION -{ - BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_NONE, - // Instructs the engine to ignore non-vital package failures and - // continue with the install. - // Ignored if hrStatus is a success or the package is vital. - BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE, - // Instructs the engine to try the execution of the package again. - // Ignored if hrStatus is a success. - BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RETRY, - // Instructs the engine to stop processing the chain and restart. - // The engine will launch again after the machine is restarted. - BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RESTART, - // Instructs the engine to stop processing the chain and - // suspend the current state. - BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_SUSPEND, -}; - -enum BOOTSTRAPPER_SHUTDOWN_ACTION -{ - BOOTSTRAPPER_SHUTDOWN_ACTION_NONE, - // Instructs the engine to restart. - // The engine will not launch again after the machine is rebooted. - // Ignored if reboot was already initiated by OnExecutePackageComplete(). - BOOTSTRAPPER_SHUTDOWN_ACTION_RESTART, - // Instructs the engine to unload the bootstrapper application and - // restart the engine which will load the bootstrapper application again. - // Typically used to switch from a native bootstrapper application to a managed one. - BOOTSTRAPPER_SHUTDOWN_ACTION_RELOAD_BOOTSTRAPPER, - // Opts out of the engine behavior of trying to uninstall itself - // when no non-permanent packages are installed. - BOOTSTRAPPER_SHUTDOWN_ACTION_SKIP_CLEANUP, -}; - -enum BURN_MSI_PROPERTY -{ - BURN_MSI_PROPERTY_NONE, // no property added - BURN_MSI_PROPERTY_INSTALL, // add BURNMSIINSTALL=1 - BURN_MSI_PROPERTY_MODIFY, // add BURNMSIMODIFY=1 - BURN_MSI_PROPERTY_REPAIR, // add BURNMSIREPAIR=1 - BURN_MSI_PROPERTY_UNINSTALL,// add BURNMSIUNINSTALL=1 -}; - -struct BOOTSTRAPPER_COMMAND -{ - DWORD cbSize; - BOOTSTRAPPER_ACTION action; - BOOTSTRAPPER_DISPLAY display; - BOOTSTRAPPER_RESTART restart; - - LPWSTR wzCommandLine; - int nCmdShow; - - BOOTSTRAPPER_RESUME_TYPE resumeType; - HWND hwndSplashScreen; - - // If this was run from a related bundle, specifies the relation type - BOOTSTRAPPER_RELATION_TYPE relationType; - BOOL fPassthrough; - - LPWSTR wzLayoutDirectory; - LPWSTR wzBootstrapperWorkingFolder; - LPWSTR wzBootstrapperApplicationDataPath; -}; - -struct BA_ONAPPLYBEGIN_ARGS -{ - DWORD cbSize; - DWORD dwPhaseCount; -}; - -struct BA_ONAPPLYBEGIN_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONAPPLYCOMPLETE_ARGS -{ - DWORD cbSize; - HRESULT hrStatus; - // Indicates whether any package required a reboot or initiated the reboot already. - BOOTSTRAPPER_APPLY_RESTART restart; - BOOTSTRAPPER_APPLYCOMPLETE_ACTION recommendation; -}; - -struct BA_ONAPPLYCOMPLETE_RESULTS -{ - DWORD cbSize; - BOOTSTRAPPER_APPLYCOMPLETE_ACTION action; -}; - -struct BA_ONBEGINMSITRANSACTIONBEGIN_ARGS -{ - DWORD cbSize; - LPCWSTR wzTransactionId; -}; - -struct BA_ONBEGINMSITRANSACTIONBEGIN_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONBEGINMSITRANSACTIONCOMPLETE_ARGS -{ - DWORD cbSize; - LPCWSTR wzTransactionId; - HRESULT hrStatus; -}; - -struct BA_ONBEGINMSITRANSACTIONCOMPLETE_RESULTS -{ - DWORD cbSize; -}; - -struct BA_ONCACHEACQUIREBEGIN_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageOrContainerId; - LPCWSTR wzPayloadId; - LPCWSTR wzSource; - LPCWSTR wzDownloadUrl; - LPCWSTR wzPayloadContainerId; - BOOTSTRAPPER_CACHE_OPERATION recommendation; -}; - -struct BA_ONCACHEACQUIREBEGIN_RESULTS -{ - DWORD cbSize; - BOOL fCancel; - BOOTSTRAPPER_CACHE_OPERATION action; -}; - -struct BA_ONCACHEACQUIRECOMPLETE_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageOrContainerId; - LPCWSTR wzPayloadId; - HRESULT hrStatus; - BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION recommendation; -}; - -struct BA_ONCACHEACQUIRECOMPLETE_RESULTS -{ - DWORD cbSize; - BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION action; -}; - -struct BA_ONCACHEACQUIREPROGRESS_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageOrContainerId; - LPCWSTR wzPayloadId; - DWORD64 dw64Progress; - DWORD64 dw64Total; - DWORD dwOverallPercentage; -}; - -struct BA_ONCACHEACQUIREPROGRESS_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONCACHEACQUIRERESOLVING_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageOrContainerId; - LPCWSTR wzPayloadId; - LPCWSTR* rgSearchPaths; - DWORD cSearchPaths; - BOOL fFoundLocal; - DWORD dwRecommendedSearchPath; - LPCWSTR wzDownloadUrl; - LPCWSTR wzPayloadContainerId; - BOOTSTRAPPER_CACHE_RESOLVE_OPERATION recommendation; -}; - -struct BA_ONCACHEACQUIRERESOLVING_RESULTS -{ - DWORD cbSize; - DWORD dwChosenSearchPath; - BOOTSTRAPPER_CACHE_RESOLVE_OPERATION action; - BOOL fCancel; -}; - -struct BA_ONCACHEBEGIN_ARGS -{ - DWORD cbSize; -}; - -struct BA_ONCACHEBEGIN_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONCACHECOMPLETE_ARGS -{ - DWORD cbSize; - HRESULT hrStatus; -}; - -struct BA_ONCACHECOMPLETE_RESULTS -{ - DWORD cbSize; -}; - -struct BA_ONCACHECONTAINERORPAYLOADVERIFYBEGIN_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageOrContainerId; - LPCWSTR wzPayloadId; -}; - -struct BA_ONCACHECONTAINERORPAYLOADVERIFYBEGIN_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONCACHECONTAINERORPAYLOADVERIFYCOMPLETE_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageOrContainerId; - LPCWSTR wzPayloadId; - HRESULT hrStatus; -}; - -struct BA_ONCACHECONTAINERORPAYLOADVERIFYCOMPLETE_RESULTS -{ - DWORD cbSize; -}; - -struct BA_ONCACHECONTAINERORPAYLOADVERIFYPROGRESS_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageOrContainerId; - LPCWSTR wzPayloadId; - DWORD64 dw64Progress; - DWORD64 dw64Total; - DWORD dwOverallPercentage; -}; - -struct BA_ONCACHECONTAINERORPAYLOADVERIFYPROGRESS_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONCACHEPACKAGEBEGIN_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; - DWORD cCachePayloads; - DWORD64 dw64PackageCacheSize; -}; - -struct BA_ONCACHEPACKAGEBEGIN_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONCACHEPACKAGECOMPLETE_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; - HRESULT hrStatus; - BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION recommendation; -}; - -struct BA_ONCACHEPACKAGECOMPLETE_RESULTS -{ - DWORD cbSize; - BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION action; -}; - -struct BA_ONCACHEPAYLOADEXTRACTBEGIN_ARGS -{ - DWORD cbSize; - LPCWSTR wzContainerId; - LPCWSTR wzPayloadId; -}; - -struct BA_ONCACHEPAYLOADEXTRACTBEGIN_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONCACHEPAYLOADEXTRACTCOMPLETE_ARGS -{ - DWORD cbSize; - LPCWSTR wzContainerId; - LPCWSTR wzPayloadId; - HRESULT hrStatus; -}; - -struct BA_ONCACHEPAYLOADEXTRACTCOMPLETE_RESULTS -{ - DWORD cbSize; -}; - -struct BA_ONCACHEPAYLOADEXTRACTPROGRESS_ARGS -{ - DWORD cbSize; - LPCWSTR wzContainerId; - LPCWSTR wzPayloadId; - DWORD64 dw64Progress; - DWORD64 dw64Total; - DWORD dwOverallPercentage; -}; - -struct BA_ONCACHEPAYLOADEXTRACTPROGRESS_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONCACHEVERIFYBEGIN_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageOrContainerId; - LPCWSTR wzPayloadId; -}; - -struct BA_ONCACHEVERIFYBEGIN_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONCACHEVERIFYCOMPLETE_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageOrContainerId; - LPCWSTR wzPayloadId; - HRESULT hrStatus; - BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION recommendation; -}; - -struct BA_ONCACHEVERIFYCOMPLETE_RESULTS -{ - DWORD cbSize; - BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action; -}; - -struct BA_ONCACHEVERIFYPROGRESS_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageOrContainerId; - LPCWSTR wzPayloadId; - DWORD64 dw64Progress; - DWORD64 dw64Total; - DWORD dwOverallPercentage; - BOOTSTRAPPER_CACHE_VERIFY_STEP verifyStep; -}; - -struct BA_ONCACHEVERIFYPROGRESS_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONCOMMITMSITRANSACTIONBEGIN_ARGS -{ - DWORD cbSize; - LPCWSTR wzTransactionId; -}; - -struct BA_ONCOMMITMSITRANSACTIONBEGIN_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONCOMMITMSITRANSACTIONCOMPLETE_ARGS -{ - DWORD cbSize; - LPCWSTR wzTransactionId; - HRESULT hrStatus; -}; - -struct BA_ONCOMMITMSITRANSACTIONCOMPLETE_RESULTS -{ - DWORD cbSize; -}; - -struct BA_ONDETECTBEGIN_ARGS -{ - DWORD cbSize; - BOOL fInstalled; - DWORD cPackages; - BOOL fCached; -}; - -struct BA_ONDETECTBEGIN_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONDETECTCOMPLETE_ARGS -{ - DWORD cbSize; - HRESULT hrStatus; - BOOL fEligibleForCleanup; -}; - -struct BA_ONDETECTCOMPLETE_RESULTS -{ - DWORD cbSize; -}; - -struct BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_ARGS -{ - DWORD cbSize; - LPCWSTR wzBundleId; - BOOTSTRAPPER_RELATION_TYPE relationType; - LPCWSTR wzBundleTag; - BOOL fPerMachine; - LPCWSTR wzVersion; - BOOL fMissingFromCache; -}; - -struct BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONDETECTMSIFEATURE_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; - LPCWSTR wzFeatureId; - BOOTSTRAPPER_FEATURE_STATE state; -}; - -struct BA_ONDETECTMSIFEATURE_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONDETECTPACKAGEBEGIN_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; -}; - -struct BA_ONDETECTPACKAGEBEGIN_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONDETECTPACKAGECOMPLETE_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; - HRESULT hrStatus; - BOOTSTRAPPER_PACKAGE_STATE state; - BOOL fCached; -}; - -struct BA_ONDETECTPACKAGECOMPLETE_RESULTS -{ - DWORD cbSize; -}; - -struct BA_ONDETECTRELATEDBUNDLE_ARGS -{ - DWORD cbSize; - LPCWSTR wzBundleId; - BOOTSTRAPPER_RELATION_TYPE relationType; - LPCWSTR wzBundleTag; - BOOL fPerMachine; - LPCWSTR wzVersion; - BOOTSTRAPPER_RELATED_OPERATION operation; - BOOL fMissingFromCache; -}; - -struct BA_ONDETECTRELATEDBUNDLE_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONDETECTRELATEDMSIPACKAGE_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; - LPCWSTR wzUpgradeCode; - LPCWSTR wzProductCode; - BOOL fPerMachine; - LPCWSTR wzVersion; - BOOTSTRAPPER_RELATED_OPERATION operation; -}; - -struct BA_ONDETECTRELATEDMSIPACKAGE_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONDETECTPATCHTARGET_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; - LPCWSTR wzProductCode; - BOOTSTRAPPER_PACKAGE_STATE patchState; -}; - -struct BA_ONDETECTPATCHTARGET_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONDETECTUPDATE_ARGS -{ - DWORD cbSize; - LPCWSTR wzUpdateLocation; - DWORD64 dw64Size; - LPCWSTR wzVersion; - LPCWSTR wzTitle; - LPCWSTR wzSummary; - LPCWSTR wzContentType; - LPCWSTR wzContent; -}; - -struct BA_ONDETECTUPDATE_RESULTS -{ - DWORD cbSize; - BOOL fCancel; - BOOL fStopProcessingUpdates; -}; - -struct BA_ONDETECTUPDATEBEGIN_ARGS -{ - DWORD cbSize; - LPCWSTR wzUpdateLocation; -}; - -struct BA_ONDETECTUPDATEBEGIN_RESULTS -{ - DWORD cbSize; - BOOL fCancel; - BOOL fSkip; -}; - -struct BA_ONDETECTUPDATECOMPLETE_ARGS -{ - DWORD cbSize; - HRESULT hrStatus; -}; - -struct BA_ONDETECTUPDATECOMPLETE_RESULTS -{ - DWORD cbSize; - BOOL fIgnoreError; -}; - -struct BA_ONELEVATEBEGIN_ARGS -{ - DWORD cbSize; -}; - -struct BA_ONELEVATEBEGIN_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONELEVATECOMPLETE_ARGS -{ - DWORD cbSize; - HRESULT hrStatus; -}; - -struct BA_ONELEVATECOMPLETE_RESULTS -{ - DWORD cbSize; -}; - -struct BA_ONERROR_ARGS -{ - DWORD cbSize; - BOOTSTRAPPER_ERROR_TYPE errorType; - LPCWSTR wzPackageId; - DWORD dwCode; - LPCWSTR wzError; - DWORD dwUIHint; - DWORD cData; - LPCWSTR* rgwzData; - int nRecommendation; -}; - -struct BA_ONERROR_RESULTS -{ - DWORD cbSize; - int nResult; -}; - -struct BA_ONEXECUTEBEGIN_ARGS -{ - DWORD cbSize; - DWORD cExecutingPackages; -}; - -struct BA_ONEXECUTEBEGIN_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONEXECUTECOMPLETE_ARGS -{ - DWORD cbSize; - HRESULT hrStatus; -}; - -struct BA_ONEXECUTECOMPLETE_RESULTS -{ - DWORD cbSize; -}; - -struct BA_ONEXECUTEFILESINUSE_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; - DWORD cFiles; - LPCWSTR* rgwzFiles; - int nRecommendation; -}; - -struct BA_ONEXECUTEFILESINUSE_RESULTS -{ - DWORD cbSize; - int nResult; -}; - -struct BA_ONEXECUTEMSIMESSAGE_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; - INSTALLMESSAGE messageType; - DWORD dwUIHint; - LPCWSTR wzMessage; - DWORD cData; - LPCWSTR* rgwzData; - int nRecommendation; -}; - -struct BA_ONEXECUTEMSIMESSAGE_RESULTS -{ - DWORD cbSize; - int nResult; -}; - -struct BA_ONEXECUTEPACKAGEBEGIN_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; - BOOL fExecute; // false means rollback. - BOOTSTRAPPER_ACTION_STATE action; - INSTALLUILEVEL uiLevel; - BOOL fDisableExternalUiHandler; -}; - -struct BA_ONEXECUTEPACKAGEBEGIN_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONEXECUTEPACKAGECOMPLETE_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; - HRESULT hrStatus; - // Indicates whether this package requires a reboot or initiated the reboot already. - BOOTSTRAPPER_APPLY_RESTART restart; - BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION recommendation; -}; - -struct BA_ONEXECUTEPACKAGECOMPLETE_RESULTS -{ - DWORD cbSize; - BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION action; -}; - -struct BA_ONEXECUTEPATCHTARGET_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; - LPCWSTR wzTargetProductCode; -}; - -struct BA_ONEXECUTEPATCHTARGET_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONEXECUTEPROGRESS_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; - DWORD dwProgressPercentage; - DWORD dwOverallPercentage; -}; - -struct BA_ONEXECUTEPROGRESS_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONLAUNCHAPPROVEDEXEBEGIN_ARGS -{ - DWORD cbSize; -}; - -struct BA_ONLAUNCHAPPROVEDEXEBEGIN_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONLAUNCHAPPROVEDEXECOMPLETE_ARGS -{ - DWORD cbSize; - HRESULT hrStatus; - // Only valid if the operation succeeded. - DWORD dwProcessId; -}; - -struct BA_ONLAUNCHAPPROVEDEXECOMPLETE_RESULTS -{ - DWORD cbSize; -}; - -struct BA_ONPAUSEAUTOMATICUPDATESBEGIN_ARGS -{ - DWORD cbSize; -}; - -struct BA_ONPAUSEAUTOMATICUPDATESBEGIN_RESULTS -{ - DWORD cbSize; -}; - -struct BA_ONPAUSEAUTOMATICUPDATESCOMPLETE_ARGS -{ - DWORD cbSize; - HRESULT hrStatus; -}; - -struct BA_ONPAUSEAUTOMATICUPDATESCOMPLETE_RESULTS -{ - DWORD cbSize; -}; - -struct BA_ONPLANBEGIN_ARGS -{ - DWORD cbSize; - DWORD cPackages; -}; - -struct BA_ONPLANBEGIN_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONPLANCOMPLETE_ARGS -{ - DWORD cbSize; - HRESULT hrStatus; -}; - -struct BA_ONPLANCOMPLETE_RESULTS -{ - DWORD cbSize; -}; - -struct BA_ONPLANFORWARDCOMPATIBLEBUNDLE_ARGS -{ - DWORD cbSize; - LPCWSTR wzBundleId; - BOOTSTRAPPER_RELATION_TYPE relationType; - LPCWSTR wzBundleTag; - BOOL fPerMachine; - LPCWSTR wzVersion; - BOOL fRecommendedIgnoreBundle; -}; - -struct BA_ONPLANFORWARDCOMPATIBLEBUNDLE_RESULTS -{ - DWORD cbSize; - BOOL fCancel; - BOOL fIgnoreBundle; -}; - -struct BA_ONPLANMSIFEATURE_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; - LPCWSTR wzFeatureId; - BOOTSTRAPPER_FEATURE_STATE recommendedState; -}; - -struct BA_ONPLANMSIFEATURE_RESULTS -{ - DWORD cbSize; - BOOTSTRAPPER_FEATURE_STATE requestedState; - BOOL fCancel; -}; - -struct BA_ONPLANMSIPACKAGE_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; - BOOL fExecute; // false means rollback. - BOOTSTRAPPER_ACTION_STATE action; -}; - -struct BA_ONPLANMSIPACKAGE_RESULTS -{ - DWORD cbSize; - BOOL fCancel; - BURN_MSI_PROPERTY actionMsiProperty; - INSTALLUILEVEL uiLevel; - BOOL fDisableExternalUiHandler; -}; - -struct BA_ONPLANNEDPACKAGE_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; - BOOTSTRAPPER_ACTION_STATE execute; - BOOTSTRAPPER_ACTION_STATE rollback; - BOOL fPlannedCache; - BOOL fPlannedUncache; -}; - -struct BA_ONPLANNEDPACKAGE_RESULTS -{ - DWORD cbSize; -}; - -struct BA_ONPLANPACKAGEBEGIN_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; - BOOTSTRAPPER_PACKAGE_STATE state; - BOOL fCached; - BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition; - BOOTSTRAPPER_REQUEST_STATE recommendedState; - BOOTSTRAPPER_CACHE_TYPE recommendedCacheType; -}; - -struct BA_ONPLANPACKAGEBEGIN_RESULTS -{ - DWORD cbSize; - BOOL fCancel; - BOOTSTRAPPER_REQUEST_STATE requestedState; - BOOTSTRAPPER_CACHE_TYPE requestedCacheType; -}; - -struct BA_ONPLANPACKAGECOMPLETE_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; - HRESULT hrStatus; - BOOTSTRAPPER_REQUEST_STATE requested; -}; - -struct BA_ONPLANPACKAGECOMPLETE_RESULTS -{ - DWORD cbSize; -}; - -struct BA_ONPLANRELATEDBUNDLE_ARGS -{ - DWORD cbSize; - LPCWSTR wzBundleId; - BOOTSTRAPPER_REQUEST_STATE recommendedState; -}; - -struct BA_ONPLANRELATEDBUNDLE_RESULTS -{ - DWORD cbSize; - BOOL fCancel; - BOOTSTRAPPER_REQUEST_STATE requestedState; -}; - -struct BA_ONPLANPATCHTARGET_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageId; - LPCWSTR wzProductCode; - BOOTSTRAPPER_REQUEST_STATE recommendedState; -}; - -struct BA_ONPLANPATCHTARGET_RESULTS -{ - DWORD cbSize; - BOOTSTRAPPER_REQUEST_STATE requestedState; - BOOL fCancel; -}; - -struct BA_ONPROGRESS_ARGS -{ - DWORD cbSize; - DWORD dwProgressPercentage; - DWORD dwOverallPercentage; -}; - -struct BA_ONPROGRESS_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONREGISTERBEGIN_ARGS -{ - DWORD cbSize; -}; - -struct BA_ONREGISTERBEGIN_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONREGISTERCOMPLETE_ARGS -{ - DWORD cbSize; - HRESULT hrStatus; -}; - -struct BA_ONREGISTERCOMPLETE_RESULTS -{ - DWORD cbSize; -}; - -struct BA_ONROLLBACKMSITRANSACTIONBEGIN_ARGS -{ - DWORD cbSize; - LPCWSTR wzTransactionId; -}; - -struct BA_ONROLLBACKMSITRANSACTIONBEGIN_RESULTS -{ - DWORD cbSize; -}; - -struct BA_ONROLLBACKMSITRANSACTIONCOMPLETE_ARGS -{ - DWORD cbSize; - LPCWSTR wzTransactionId; - HRESULT hrStatus; -}; - -struct BA_ONROLLBACKMSITRANSACTIONCOMPLETE_RESULTS -{ - DWORD cbSize; -}; - -struct BA_ONSHUTDOWN_ARGS -{ - DWORD cbSize; -}; - -struct BA_ONSHUTDOWN_RESULTS -{ - DWORD cbSize; - BOOTSTRAPPER_SHUTDOWN_ACTION action; -}; - -struct BA_ONSTARTUP_ARGS -{ - DWORD cbSize; -}; - -struct BA_ONSTARTUP_RESULTS -{ - DWORD cbSize; -}; - -struct BA_ONSYSTEMRESTOREPOINTBEGIN_ARGS -{ - DWORD cbSize; -}; - -struct BA_ONSYSTEMRESTOREPOINTBEGIN_RESULTS -{ - DWORD cbSize; -}; - -struct BA_ONSYSTEMRESTOREPOINTCOMPLETE_ARGS -{ - DWORD cbSize; - HRESULT hrStatus; -}; - -struct BA_ONSYSTEMRESTOREPOINTCOMPLETE_RESULTS -{ - DWORD cbSize; -}; - -struct BA_ONSYSTEMSHUTDOWN_ARGS -{ - DWORD cbSize; - DWORD dwEndSession; -}; - -struct BA_ONSYSTEMSHUTDOWN_RESULTS -{ - DWORD cbSize; - BOOL fCancel; -}; - -struct BA_ONUNREGISTERBEGIN_ARGS -{ - DWORD cbSize; - BOOL fKeepRegistration; -}; - -struct BA_ONUNREGISTERBEGIN_RESULTS -{ - DWORD cbSize; - BOOL fForceKeepRegistration; -}; - -struct BA_ONUNREGISTERCOMPLETE_ARGS -{ - DWORD cbSize; - HRESULT hrStatus; -}; - -struct BA_ONUNREGISTERCOMPLETE_RESULTS -{ - DWORD cbSize; -}; - - - -extern "C" typedef HRESULT(WINAPI *PFN_BOOTSTRAPPER_APPLICATION_PROC)( - __in BOOTSTRAPPER_APPLICATION_MESSAGE message, - __in const LPVOID pvArgs, - __inout LPVOID pvResults, - __in_opt LPVOID pvContext - ); - -extern "C" typedef void (WINAPI *PFN_BOOTSTRAPPER_APPLICATION_DESTROY)(); - - - -struct BOOTSTRAPPER_CREATE_ARGS -{ - DWORD cbSize; - DWORD64 qwEngineAPIVersion; - PFN_BOOTSTRAPPER_ENGINE_PROC pfnBootstrapperEngineProc; - LPVOID pvBootstrapperEngineProcContext; - BOOTSTRAPPER_COMMAND* pCommand; -}; - -struct BOOTSTRAPPER_CREATE_RESULTS -{ - DWORD cbSize; - PFN_BOOTSTRAPPER_APPLICATION_PROC pfnBootstrapperApplicationProc; - LPVOID pvBootstrapperApplicationProcContext; - BOOL fDisableUnloading; // indicates the BA dll must not be unloaded after BootstrapperApplicationDestroy. -}; - -extern "C" typedef HRESULT(WINAPI *PFN_BOOTSTRAPPER_APPLICATION_CREATE)( - __in const BOOTSTRAPPER_CREATE_ARGS* pArgs, - __inout BOOTSTRAPPER_CREATE_RESULTS* pResults - ); diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h deleted file mode 100644 index 9c9b38a5..00000000 --- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h +++ /dev/null @@ -1,442 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - -#define IDERROR -1 -#define IDNOACTION 0 - -#ifndef FACILITY_WIX -#define FACILITY_WIX 500 -#endif - -static const HRESULT E_SUSPECTED_AV_INTERFERENCE = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIX, 2000); - -// Note that ordering of the enumeration values is important. -// Some code paths use < or > comparisions and simply reording values will break those comparisons. -enum BOOTSTRAPPER_ACTION -{ - BOOTSTRAPPER_ACTION_UNKNOWN, - BOOTSTRAPPER_ACTION_HELP, - BOOTSTRAPPER_ACTION_LAYOUT, - BOOTSTRAPPER_ACTION_UNINSTALL, - BOOTSTRAPPER_ACTION_CACHE, - BOOTSTRAPPER_ACTION_INSTALL, - BOOTSTRAPPER_ACTION_MODIFY, - BOOTSTRAPPER_ACTION_REPAIR, - BOOTSTRAPPER_ACTION_UPDATE_REPLACE, - BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED, -}; - -enum BOOTSTRAPPER_ACTION_STATE -{ - BOOTSTRAPPER_ACTION_STATE_NONE, - BOOTSTRAPPER_ACTION_STATE_UNINSTALL, - BOOTSTRAPPER_ACTION_STATE_INSTALL, - BOOTSTRAPPER_ACTION_STATE_MODIFY, - BOOTSTRAPPER_ACTION_STATE_MEND, - BOOTSTRAPPER_ACTION_STATE_REPAIR, - BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE, -}; - -enum BOOTSTRAPPER_PACKAGE_STATE -{ - BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN, - BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE, - BOOTSTRAPPER_PACKAGE_STATE_ABSENT, - BOOTSTRAPPER_PACKAGE_STATE_PRESENT, - BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED, -}; - -enum BOOTSTRAPPER_REQUEST_STATE -{ - BOOTSTRAPPER_REQUEST_STATE_NONE, - BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT, - BOOTSTRAPPER_REQUEST_STATE_ABSENT, - BOOTSTRAPPER_REQUEST_STATE_CACHE, - BOOTSTRAPPER_REQUEST_STATE_PRESENT, - BOOTSTRAPPER_REQUEST_STATE_MEND, - BOOTSTRAPPER_REQUEST_STATE_REPAIR, -}; - -enum BOOTSTRAPPER_FEATURE_STATE -{ - BOOTSTRAPPER_FEATURE_STATE_UNKNOWN, - BOOTSTRAPPER_FEATURE_STATE_ABSENT, - BOOTSTRAPPER_FEATURE_STATE_ADVERTISED, - BOOTSTRAPPER_FEATURE_STATE_LOCAL, - BOOTSTRAPPER_FEATURE_STATE_SOURCE, -}; - -enum BOOTSTRAPPER_LOG_LEVEL -{ - BOOTSTRAPPER_LOG_LEVEL_NONE, // turns off report (only valid for XXXSetLevel()) - BOOTSTRAPPER_LOG_LEVEL_STANDARD, // written if reporting is on - BOOTSTRAPPER_LOG_LEVEL_VERBOSE, // written only if verbose reporting is on - BOOTSTRAPPER_LOG_LEVEL_DEBUG, // reporting useful when debugging code - BOOTSTRAPPER_LOG_LEVEL_ERROR, // always gets reported, but can never be specified -}; - -enum BOOTSTRAPPER_UPDATE_HASH_TYPE -{ - BOOTSTRAPPER_UPDATE_HASH_TYPE_NONE, - BOOTSTRAPPER_UPDATE_HASH_TYPE_SHA512, -}; - -enum BOOTSTRAPPER_ENGINE_MESSAGE -{ - BOOTSTRAPPER_ENGINE_MESSAGE_GETPACKAGECOUNT, - BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLENUMERIC, - BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLESTRING, - BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLEVERSION, - BOOTSTRAPPER_ENGINE_MESSAGE_FORMATSTRING, - BOOTSTRAPPER_ENGINE_MESSAGE_ESCAPESTRING, - BOOTSTRAPPER_ENGINE_MESSAGE_EVALUATECONDITION, - BOOTSTRAPPER_ENGINE_MESSAGE_LOG, - BOOTSTRAPPER_ENGINE_MESSAGE_SENDEMBEDDEDERROR, - BOOTSTRAPPER_ENGINE_MESSAGE_SENDEMBEDDEDPROGRESS, - BOOTSTRAPPER_ENGINE_MESSAGE_SETUPDATE, - BOOTSTRAPPER_ENGINE_MESSAGE_SETLOCALSOURCE, - BOOTSTRAPPER_ENGINE_MESSAGE_SETDOWNLOADSOURCE, - BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLENUMERIC, - BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLESTRING, - BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLEVERSION, - BOOTSTRAPPER_ENGINE_MESSAGE_CLOSESPLASHSCREEN, - BOOTSTRAPPER_ENGINE_MESSAGE_DETECT, - BOOTSTRAPPER_ENGINE_MESSAGE_PLAN, - BOOTSTRAPPER_ENGINE_MESSAGE_ELEVATE, - BOOTSTRAPPER_ENGINE_MESSAGE_APPLY, - BOOTSTRAPPER_ENGINE_MESSAGE_QUIT, - BOOTSTRAPPER_ENGINE_MESSAGE_LAUNCHAPPROVEDEXE, - BOOTSTRAPPER_ENGINE_MESSAGE_SETUPDATESOURCE, - BOOTSTRAPPER_ENGINE_MESSAGE_COMPAREVERSIONS, -}; - -typedef struct _BAENGINE_APPLY_ARGS -{ - DWORD cbSize; - HWND hwndParent; -} BAENGINE_APPLY_ARGS; - -typedef struct _BAENGINE_APPLY_RESULTS -{ - DWORD cbSize; -} BAENGINE_APPLY_RESULTS; - -typedef struct _BAENGINE_CLOSESPLASHSCREEN_ARGS -{ - DWORD cbSize; -} BAENGINE_CLOSESPLASHSCREEN_ARGS; - -typedef struct _BAENGINE_CLOSESPLASHSCREEN_RESULTS -{ - DWORD cbSize; -} BAENGINE_CLOSESPLASHSCREEN_RESULTS; - -typedef struct _BAENGINE_COMPAREVERSIONS_ARGS -{ - DWORD cbSize; - LPCWSTR wzVersion1; - LPCWSTR wzVersion2; -} BAENGINE_COMPAREVERSIONS_ARGS; - -typedef struct _BAENGINE_COMPAREVERSIONS_RESULTS -{ - DWORD cbSize; - int nResult; -} BAENGINE_COMPAREVERSIONS_RESULTS; - -typedef struct _BAENGINE_DETECT_ARGS -{ - DWORD cbSize; - HWND hwndParent; -} BAENGINE_DETECT_ARGS; - -typedef struct _BAENGINE_DETECT_RESULTS -{ - DWORD cbSize; -} BAENGINE_DETECT_RESULTS; - -typedef struct _BAENGINE_ELEVATE_ARGS -{ - DWORD cbSize; - HWND hwndParent; -} BAENGINE_ELEVATE_ARGS; - -typedef struct _BAENGINE_ELEVATE_RESULTS -{ - DWORD cbSize; -} BAENGINE_ELEVATE_RESULTS; - -typedef struct _BAENGINE_ESCAPESTRING_ARGS -{ - DWORD cbSize; - LPCWSTR wzIn; -} BAENGINE_ESCAPESTRING_ARGS; - -typedef struct _BAENGINE_ESCAPESTRING_RESULTS -{ - DWORD cbSize; - LPWSTR wzOut; - // Should be initialized to the size of wzOut. - SIZE_T cchOut; -} BAENGINE_ESCAPESTRING_RESULTS; - -typedef struct _BAENGINE_EVALUATECONDITION_ARGS -{ - DWORD cbSize; - LPCWSTR wzCondition; -} BAENGINE_EVALUATECONDITION_ARGS; - -typedef struct _BAENGINE_EVALUATECONDITION_RESULTS -{ - DWORD cbSize; - BOOL f; -} BAENGINE_EVALUATECONDITION_RESULTS; - -typedef struct _BAENGINE_FORMATSTRING_ARGS -{ - DWORD cbSize; - LPCWSTR wzIn; -} BAENGINE_FORMATSTRING_ARGS; - -typedef struct _BAENGINE_FORMATSTRING_RESULTS -{ - DWORD cbSize; - LPWSTR wzOut; - // Should be initialized to the size of wzOut. - SIZE_T cchOut; -} BAENGINE_FORMATSTRING_RESULTS; - -typedef struct _BAENGINE_GETPACKAGECOUNT_ARGS -{ - DWORD cbSize; -} BAENGINE_GETPACKAGECOUNT_ARGS; - -typedef struct _BAENGINE_GETPACKAGECOUNT_RESULTS -{ - DWORD cbSize; - DWORD cPackages; -} BAENGINE_GETPACKAGECOUNT_RESULTS; - -typedef struct _BAENGINE_GETVARIABLENUMERIC_ARGS -{ - DWORD cbSize; - LPCWSTR wzVariable; -} BAENGINE_GETVARIABLENUMERIC_ARGS; - -typedef struct _BAENGINE_GETVARIABLENUMERIC_RESULTS -{ - DWORD cbSize; - LONGLONG llValue; -} BAENGINE_GETVARIABLENUMERIC_RESULTS; - -typedef struct _BAENGINE_GETVARIABLESTRING_ARGS -{ - DWORD cbSize; - LPCWSTR wzVariable; -} BAENGINE_GETVARIABLESTRING_ARGS; - -typedef struct _BAENGINE_GETVARIABLESTRING_RESULTS -{ - DWORD cbSize; - LPWSTR wzValue; - // Should be initialized to the size of wzValue. - SIZE_T cchValue; -} BAENGINE_GETVARIABLESTRING_RESULTS; - -typedef struct _BAENGINE_GETVARIABLEVERSION_ARGS -{ - DWORD cbSize; - LPCWSTR wzVariable; -} BAENGINE_GETVARIABLEVERSION_ARGS; - -typedef struct _BAENGINE_GETVARIABLEVERSION_RESULTS -{ - DWORD cbSize; - LPWSTR wzValue; - // Should be initialized to the size of wzValue. - SIZE_T cchValue; -} BAENGINE_GETVARIABLEVERSION_RESULTS; - -typedef struct _BAENGINE_LAUNCHAPPROVEDEXE_ARGS -{ - DWORD cbSize; - HWND hwndParent; - LPCWSTR wzApprovedExeForElevationId; - LPCWSTR wzArguments; - DWORD dwWaitForInputIdleTimeout; -} BAENGINE_LAUNCHAPPROVEDEXE_ARGS; - -typedef struct _BAENGINE_LAUNCHAPPROVEDEXE_RESULTS -{ - DWORD cbSize; -} BAENGINE_LAUNCHAPPROVEDEXE_RESULTS; - -typedef struct _BAENGINE_SETUPDATESOURCE_ARGS -{ - DWORD cbSize; - LPCWSTR wzUrl; -} BAENGINE_SETUPDATESOURCE_ARGS; - -typedef struct _BAENGINE_SETUPDATESOURCE_RESULTS -{ - DWORD cbSize; -} BAENGINE_SETUPDATESOURCE_RESULTS; - -typedef struct _BAENGINE_LOG_ARGS -{ - DWORD cbSize; - BOOTSTRAPPER_LOG_LEVEL level; - LPCWSTR wzMessage; -} BAENGINE_LOG_ARGS; - -typedef struct _BAENGINE_LOG_RESULTS -{ - DWORD cbSize; -} BAENGINE_LOG_RESULTS; - -typedef struct _BAENGINE_PLAN_ARGS -{ - DWORD cbSize; - BOOTSTRAPPER_ACTION action; -} BAENGINE_PLAN_ARGS; - -typedef struct _BAENGINE_PLAN_RESULTS -{ - DWORD cbSize; -} BAENGINE_PLAN_RESULTS; - -typedef struct _BAENGINE_QUIT_ARGS -{ - DWORD cbSize; - DWORD dwExitCode; -} BAENGINE_QUIT_ARGS; - -typedef struct _BAENGINE_QUIT_RESULTS -{ - DWORD cbSize; -} BAENGINE_QUIT_RESULTS; - -typedef struct _BAENGINE_SENDEMBEDDEDERROR_ARGS -{ - DWORD cbSize; - DWORD dwErrorCode; - LPCWSTR wzMessage; - DWORD dwUIHint; -} BAENGINE_SENDEMBEDDEDERROR_ARGS; - -typedef struct _BAENGINE_SENDEMBEDDEDERROR_RESULTS -{ - DWORD cbSize; - int nResult; -} BAENGINE_SENDEMBEDDEDERROR_RESULTS; - -typedef struct _BAENGINE_SENDEMBEDDEDPROGRESS_ARGS -{ - DWORD cbSize; - DWORD dwProgressPercentage; - DWORD dwOverallProgressPercentage; -} BAENGINE_SENDEMBEDDEDPROGRESS_ARGS; - -typedef struct _BAENGINE_SENDEMBEDDEDPROGRESS_RESULTS -{ - DWORD cbSize; - int nResult; -} BAENGINE_SENDEMBEDDEDPROGRESS_RESULTS; - -typedef struct _BAENGINE_SETDOWNLOADSOURCE_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageOrContainerId; - LPCWSTR wzPayloadId; - LPCWSTR wzUrl; - LPCWSTR wzUser; - LPCWSTR wzPassword; -} BAENGINE_SETDOWNLOADSOURCE_ARGS; - -typedef struct _BAENGINE_SETDOWNLOADSOURCE_RESULTS -{ - DWORD cbSize; -} BAENGINE_SETDOWNLOADSOURCE_RESULTS; - -typedef struct _BAENGINE_SETLOCALSOURCE_ARGS -{ - DWORD cbSize; - LPCWSTR wzPackageOrContainerId; - LPCWSTR wzPayloadId; - LPCWSTR wzPath; -} BAENGINE_SETLOCALSOURCE_ARGS; - -typedef struct _BAENGINE_SETLOCALSOURCE_RESULTS -{ - DWORD cbSize; -} BAENGINE_SETLOCALSOURCE_RESULTS; - -typedef struct _BAENGINE_SETUPDATE_ARGS -{ - DWORD cbSize; - LPCWSTR wzLocalSource; - LPCWSTR wzDownloadSource; - DWORD64 qwSize; - BOOTSTRAPPER_UPDATE_HASH_TYPE hashType; - BYTE* rgbHash; - DWORD cbHash; -} BAENGINE_SETUPDATE_ARGS; - -typedef struct _BAENGINE_SETUPDATE_RESULTS -{ - DWORD cbSize; -} BAENGINE_SETUPDATE_RESULTS; - -typedef struct _BAENGINE_SETVARIABLENUMERIC_ARGS -{ - DWORD cbSize; - LPCWSTR wzVariable; - LONGLONG llValue; -} BAENGINE_SETVARIABLENUMERIC_ARGS; - -typedef struct _BAENGINE_SETVARIABLENUMERIC_RESULTS -{ - DWORD cbSize; -} BAENGINE_SETVARIABLENUMERIC_RESULTS; - -typedef struct _BAENGINE_SETVARIABLESTRING_ARGS -{ - DWORD cbSize; - LPCWSTR wzVariable; - LPCWSTR wzValue; - BOOL fFormatted; -} BAENGINE_SETVARIABLESTRING_ARGS; - -typedef struct _BAENGINE_SETVARIABLESTRING_RESULTS -{ - DWORD cbSize; -} BAENGINE_SETVARIABLESTRING_RESULTS; - -typedef struct _BAENGINE_SETVARIABLEVERSION_ARGS -{ - DWORD cbSize; - LPCWSTR wzVariable; - LPCWSTR wzValue; -} BAENGINE_SETVARIABLEVERSION_ARGS; - -typedef struct _BAENGINE_SETVARIABLEVERSION_RESULTS -{ - DWORD cbSize; -} BAENGINE_SETVARIABLEVERSION_RESULTS; - - -extern "C" typedef HRESULT(WINAPI *PFN_BOOTSTRAPPER_ENGINE_PROC)( - __in BOOTSTRAPPER_ENGINE_MESSAGE message, - __in const LPVOID pvArgs, - __inout LPVOID pvResults, - __in_opt LPVOID pvContext - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BundleExtension.h b/src/WixToolset.BootstrapperCore.Native/inc/BundleExtension.h deleted file mode 100644 index be76a1a5..00000000 --- a/src/WixToolset.BootstrapperCore.Native/inc/BundleExtension.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - -enum BUNDLE_EXTENSION_MESSAGE -{ - BUNDLE_EXTENSION_MESSAGE_SEARCH, -}; - -typedef struct _BUNDLE_EXTENSION_SEARCH_ARGS -{ - DWORD cbSize; - LPCWSTR wzId; - LPCWSTR wzVariable; -} BUNDLE_EXTENSION_SEARCH_ARGS; - -typedef struct _BUNDLE_EXTENSION_SEARCH_RESULTS -{ - DWORD cbSize; -} BUNDLE_EXTENSION_SEARCH_RESULTS; - -extern "C" typedef HRESULT(WINAPI *PFN_BUNDLE_EXTENSION_PROC)( - __in BUNDLE_EXTENSION_MESSAGE message, - __in const LPVOID pvArgs, - __inout LPVOID pvResults, - __in_opt LPVOID pvContext - ); - -typedef struct _BUNDLE_EXTENSION_CREATE_ARGS -{ - DWORD cbSize; - DWORD64 qwEngineAPIVersion; - PFN_BUNDLE_EXTENSION_ENGINE_PROC pfnBundleExtensionEngineProc; - LPVOID pvBundleExtensionEngineProcContext; - LPCWSTR wzBootstrapperWorkingFolder; - LPCWSTR wzBundleExtensionDataPath; - LPCWSTR wzExtensionId; -} BUNDLE_EXTENSION_CREATE_ARGS; - -typedef struct _BUNDLE_EXTENSION_CREATE_RESULTS -{ - DWORD cbSize; - PFN_BUNDLE_EXTENSION_PROC pfnBundleExtensionProc; - LPVOID pvBundleExtensionProcContext; -} BUNDLE_EXTENSION_CREATE_RESULTS; - -extern "C" typedef HRESULT(WINAPI *PFN_BUNDLE_EXTENSION_CREATE)( - __in const BUNDLE_EXTENSION_CREATE_ARGS* pArgs, - __inout BUNDLE_EXTENSION_CREATE_RESULTS* pResults - ); - -extern "C" typedef void (WINAPI *PFN_BUNDLE_EXTENSION_DESTROY)(); - -#if defined(__cplusplus) -} -#endif diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h b/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h deleted file mode 100644 index b397ec16..00000000 --- a/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h +++ /dev/null @@ -1,184 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - -enum BUNDLE_EXTENSION_LOG_LEVEL -{ - BUNDLE_EXTENSION_LOG_LEVEL_NONE, // turns off report (only valid for XXXSetLevel()) - BUNDLE_EXTENSION_LOG_LEVEL_STANDARD, // written if reporting is on - BUNDLE_EXTENSION_LOG_LEVEL_VERBOSE, // written only if verbose reporting is on - BUNDLE_EXTENSION_LOG_LEVEL_DEBUG, // reporting useful when debugging code - BUNDLE_EXTENSION_LOG_LEVEL_ERROR, // always gets reported, but can never be specified -}; - -enum BUNDLE_EXTENSION_ENGINE_MESSAGE -{ - BUNDLE_EXTENSION_ENGINE_MESSAGE_ESCAPESTRING, - BUNDLE_EXTENSION_ENGINE_MESSAGE_EVALUATECONDITION, - BUNDLE_EXTENSION_ENGINE_MESSAGE_FORMATSTRING, - BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLENUMERIC, - BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLESTRING, - BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLEVERSION, - BUNDLE_EXTENSION_ENGINE_MESSAGE_LOG, - BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLENUMERIC, - BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLESTRING, - BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLEVERSION, - BUNDLE_EXTENSION_ENGINE_MESSAGE_COMPAREVERSIONS, -}; - -typedef struct _BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_ARGS -{ - DWORD cbSize; - LPCWSTR wzVersion1; - LPCWSTR wzVersion2; -} BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_ARGS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_RESULTS -{ - DWORD cbSize; - int nResult; -} BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_RESULTS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_ARGS -{ - DWORD cbSize; - LPCWSTR wzIn; -} BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_ARGS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_RESULTS -{ - DWORD cbSize; - LPWSTR wzOut; - // Should be initialized to the size of wzOut. - SIZE_T cchOut; -} BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_RESULTS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_ARGS -{ - DWORD cbSize; - LPCWSTR wzCondition; -} BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_ARGS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_RESULTS -{ - DWORD cbSize; - BOOL f; -} BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_RESULTS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_FORMATSTRING_ARGS -{ - DWORD cbSize; - LPCWSTR wzIn; -} BUNDLE_EXTENSION_ENGINE_FORMATSTRING_ARGS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_FORMATSTRING_RESULTS -{ - DWORD cbSize; - LPWSTR wzOut; - // Should be initialized to the size of wzOut. - SIZE_T cchOut; -} BUNDLE_EXTENSION_ENGINE_FORMATSTRING_RESULTS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_ARGS -{ - DWORD cbSize; - LPCWSTR wzVariable; -} BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_ARGS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_RESULTS -{ - DWORD cbSize; - LONGLONG llValue; -} BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_RESULTS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_ARGS -{ - DWORD cbSize; - LPCWSTR wzVariable; -} BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_ARGS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_RESULTS -{ - DWORD cbSize; - LPWSTR wzValue; - // Should be initialized to the size of wzValue. - SIZE_T cchValue; -} BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_RESULTS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_ARGS -{ - DWORD cbSize; - LPCWSTR wzVariable; -} BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_ARGS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_RESULTS -{ - DWORD cbSize; - LPWSTR wzValue; - // Should be initialized to the size of wzValue. - SIZE_T cchValue; -} BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_RESULTS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_LOG_ARGS -{ - DWORD cbSize; - BUNDLE_EXTENSION_LOG_LEVEL level; - LPCWSTR wzMessage; -} BUNDLE_EXTENSION_ENGINE_LOG_ARGS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_LOG_RESULTS -{ - DWORD cbSize; -} BUNDLE_EXTENSION_ENGINE_LOG_RESULTS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_ARGS -{ - DWORD cbSize; - LPCWSTR wzVariable; - LONGLONG llValue; -} BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_ARGS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_RESULTS -{ - DWORD cbSize; -} BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_RESULTS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_ARGS -{ - DWORD cbSize; - LPCWSTR wzVariable; - LPCWSTR wzValue; - BOOL fFormatted; -} BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_ARGS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_RESULTS -{ - DWORD cbSize; -} BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_RESULTS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_ARGS -{ - DWORD cbSize; - LPCWSTR wzVariable; - LPCWSTR wzValue; -} BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_ARGS; - -typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_RESULTS -{ - DWORD cbSize; -} BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_RESULTS; - -extern "C" typedef HRESULT(WINAPI *PFN_BUNDLE_EXTENSION_ENGINE_PROC)( - __in BUNDLE_EXTENSION_ENGINE_MESSAGE message, - __in const LPVOID pvArgs, - __inout LPVOID pvResults, - __in_opt LPVOID pvContext - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj index 9e90ee19..b3a0f81b 100644 --- a/src/engine/engine.vcxproj +++ b/src/engine/engine.vcxproj @@ -1,7 +1,10 @@ + - + + + Debug @@ -28,6 +31,7 @@ ARM64 + {8119537D-E1D9-6591-D51A-49768A2F9C37} StaticLibrary @@ -36,12 +40,19 @@ Unicode Native component of WixToolset.Burn + + + + $(ProjectDir)..\..\..\balutil\src\WixToolset.BootstrapperCore.Native\inc;$(ProjectAdditionalIncludeDirectories) + + + @@ -136,6 +147,7 @@ + Compiling message file... @@ -144,6 +156,7 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" $(IntDir)engine.messages.h;$(IntDir)engine.messages.rc;$(OutDir)engine.res + $(MajorMinorVersion.Split(`.`)[0]) @@ -153,6 +166,7 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" $(rmj).$(rmm).$(rup).$(rpr) rmj=$(rmj);rmm=$(rmm);rup=$(rup);rpr=$(rpr);szVerMajorMinorBuild="$(szVerMajorMinorBuild)" + $(wixver);%(PreprocessorDefinitions) @@ -166,6 +180,7 @@ rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + + - \ No newline at end of file + diff --git a/src/engine/precomp.h b/src/engine/precomp.h index 4ab636fc..11b594da 100644 --- a/src/engine/precomp.h +++ b/src/engine/precomp.h @@ -55,10 +55,10 @@ #include #include -#include "..\WixToolset.BootstrapperCore.Native\inc\BootstrapperEngine.h" -#include "..\WixToolset.BootstrapperCore.Native\inc\BootstrapperApplication.h" -#include "..\WixToolset.BootstrapperCore.Native\inc\BundleExtensionEngine.h" -#include "..\WixToolset.BootstrapperCore.Native\inc\BundleExtension.h" +#include "BootstrapperEngine.h" +#include "BootstrapperApplication.h" +#include "BundleExtensionEngine.h" +#include "BundleExtension.h" #include "platform.h" #include "variant.h" diff --git a/src/stub/WixToolset.Burn.nuspec b/src/stub/WixToolset.Burn.nuspec deleted file mode 100644 index f314a8a4..00000000 --- a/src/stub/WixToolset.Burn.nuspec +++ /dev/null @@ -1,24 +0,0 @@ - - - - $id$ - $version$ - $authors$ - $authors$ - MS-RL - https://github.com/wixtoolset/burn - false - $description$ - $copyright$ - - - - - - - - - - - - diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj index 99db505d..33c8ed6c 100644 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -1,10 +1,10 @@ - - - + + + Debug @@ -23,6 +23,7 @@ Win32 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942} {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67} @@ -31,13 +32,18 @@ DynamicLibrary Unicode true + false + + - ..\..\engine;..\..\WixToolset.BootstrapperCore.Native\inc + $(ProjectDir)..\..\..\..\balutil\src\WixToolset.BootstrapperCore.Native\inc + $(ProjectAdditionalIncludeDirectories);..\..\engine cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib + @@ -86,7 +92,7 @@ - {8119537D-E1D9-6591-D51A-49768A2F9C37} + {8119537D-E1D9-6591-D51A-49770A2F9C37} @@ -95,8 +101,9 @@ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - + + + + - \ No newline at end of file + diff --git a/src/test/BurnUnitTest/packages.config b/src/test/BurnUnitTest/packages.config index 1eb30932..1d36c387 100644 --- a/src/test/BurnUnitTest/packages.config +++ b/src/test/BurnUnitTest/packages.config @@ -1,9 +1,10 @@  - - - + + + + -- cgit v1.2.3-55-g6feb From 9c2aed97299fb96aeee3f1471ce40225437aaecf Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Thu, 22 Apr 2021 16:58:03 -0700 Subject: Integrate new build scripts with signing support --- .gitignore | 43 +++++++++---- appveyor.cmd | 12 ++-- appveyor.yml | 2 + nuget.config | 3 +- signing.json | 13 ++++ src/Directory.Build.props | 4 +- src/Directory.Build.targets | 87 +++++++++++++++++--------- src/Directory.Packages.props | 20 ++++++ src/Directory.csproj.props | 13 ++++ src/Directory.vcxproj.props | 118 +++++++++++++++++++++++++++++++++++ src/NativeMultiTargeting.Build.props | 10 +++ src/engine/container.cpp | 4 ++ src/engine/container.h | 2 +- src/stub/stub.nuspec | 25 ++++++++ src/stub/stub.vcxproj | 8 +-- src/wix.snk | Bin 0 -> 596 bytes 16 files changed, 307 insertions(+), 57 deletions(-) create mode 100644 signing.json create mode 100644 src/Directory.Packages.props create mode 100644 src/Directory.csproj.props create mode 100644 src/Directory.vcxproj.props create mode 100644 src/NativeMultiTargeting.Build.props create mode 100644 src/stub/stub.nuspec create mode 100644 src/wix.snk diff --git a/.gitignore b/.gitignore index 3e8a1553..1ee53850 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,9 @@ # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs +# Mono auto generated files +mono_crash.* + # Build results [Dd]ebug/ [Dd]ebugPublic/ @@ -20,12 +23,14 @@ [Rr]eleases/ x64/ x86/ +[Ww][Ii][Nn]32/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ +[Ll]ogs/ # Visual Studio 2015/2017 cache/options directory .vs/ @@ -39,9 +44,10 @@ Generated\ Files/ [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* -# NUNIT +# NUnit *.VisualState.xml TestResult.xml +nunit-*.xml # Build Results of an ATL Project [Dd]ebugPS/ @@ -56,6 +62,9 @@ project.lock.json project.fragment.lock.json artifacts/ +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + # StyleCop StyleCopReport.xml @@ -122,9 +131,6 @@ _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user -# JustCode is a .NET coding add-in -.JustCode - # TeamCity is a build add-in _TeamCity* @@ -135,6 +141,11 @@ _TeamCity* .axoCover/* !.axoCover/settings.json +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + # Visual Studio code coverage results *.coverage *.coveragexml @@ -182,6 +193,8 @@ PublishScripts/ # NuGet Packages *.nupkg +# NuGet Symbol Packages +*.snupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. @@ -206,6 +219,8 @@ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx +*.appxbundle +*.appxupload # Visual Studio cache files # files ending in .cache can be ignored @@ -231,8 +246,6 @@ orleans.codegen.cs # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ -# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true -**/wwwroot/lib/ # RIA/Silverlight projects Generated_Code/ @@ -257,6 +270,9 @@ ServiceFabricBackup/ *.bim.layout *.bim_*.settings *.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl # Microsoft Fakes FakesAssemblies/ @@ -292,10 +308,6 @@ paket-files/ # FAKE - F# Make .fake/ -# JetBrains Rider -.idea/ -*.sln.iml - # CodeRush personal settings .cr/personal @@ -337,5 +349,14 @@ ASALocalRun/ # Local History for Visual Studio .localhistory/ -# BeatPulse healthcheck temp database +# BeatPulse healthcheck temp database healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd diff --git a/appveyor.cmd b/appveyor.cmd index 4f18fbee..a35e10d5 100644 --- a/appveyor.cmd +++ b/appveyor.cmd @@ -1,15 +1,17 @@ @setlocal @pushd %~dp0 +@set _C=Release +@if /i "%1"=="debug" set _C=Debug nuget restore || exit /b -msbuild -t:Test -p:Configuration=Release src\test\BurnUnitTest || exit /b +msbuild -t:Test -p:Configuration=%_C% src\test\BurnUnitTest || exit /b -msbuild -p:Configuration=Release;Platform=x86 || exit /b -msbuild -p:Configuration=Release;Platform=x64 || exit /b -msbuild -p:Configuration=Release;Platform=arm64 || exit /b +msbuild -p:Configuration=%_C%;Platform=x86 || exit /b +msbuild -p:Configuration=%_C%;Platform=x64 || exit /b +msbuild -p:Configuration=%_C%;Platform=arm64 || exit /b -msbuild -p:Configuration=Release -t:Pack src\stub\stub.vcxproj || exit /b +msbuild -p:Configuration=%_C% -t:PackNative src\stub\stub.vcxproj || exit /b @popd @endlocal \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index e4d25586..364569cf 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -35,6 +35,8 @@ skip_tags: true artifacts: - path: build\Release\**\*.nupkg name: nuget +- path: build\Release\**\*.snupkg + name: snupkg notifications: - provider: Slack diff --git a/nuget.config b/nuget.config index 405367b4..237c522e 100644 --- a/nuget.config +++ b/nuget.config @@ -4,6 +4,7 @@ + - \ No newline at end of file + diff --git a/signing.json b/signing.json new file mode 100644 index 00000000..fe1c8c9b --- /dev/null +++ b/signing.json @@ -0,0 +1,13 @@ +{ + "SignClient": { + "AzureAd": { + "AADInstance": "https://login.microsoftonline.com/", + "ClientId": "c248d68a-ba6f-4aa9-8a68-71fe872063f8", + "TenantId": "16076fdc-fcc1-4a15-b1ca-32c9a255900e" + }, + "Service": { + "Url": "https://codesign.dotnetfoundation.org/", + "ResourceId": "https://SignService/3c30251f-36f3-490b-a955-520addb85001" + } + } +} diff --git a/src/Directory.Build.props b/src/Directory.Build.props index a22f4470..fb34d54e 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -8,7 +8,6 @@ Debug false - MSB3246 $(MSBuildProjectName) $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\)) @@ -22,7 +21,6 @@ WiX Toolset - - + diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index dac7452a..44701fb6 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -4,45 +4,70 @@ Do NOT modify this file. Update the canonical version in Home\repo-template\src\Directory.Build.targets then update all of the repos. --> - - true - $(SolutionPath) - $(NCrunchOriginalSolutionPath) + $(BaseOutputPath)obj\.tools + $(SigningToolFolder)\SignClient.exe + $(SigningToolFolder)\empty-filelist.txt + $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), signing.json))\signing.json + + + + false + $(OutputPath)\$(AssemblyName).xml - - + + + $(PrivateRepositoryUrl.Replace('.git','')) + + $(MSBuildProjectName).nuspec + $(MSBuildProjectDirectory) + $(NuspecProperties);Id=$(PackageId);Authors="$(Authors)";Configuration=$(Configuration);Copyright="$(Copyright)";Description="$(Description)";Title="$(Title)" + $(NuspecProperties);Version=$(NPMPackageVersion);RepositoryCommit=$(SourceRevisionId);RepositoryType=$(RepositoryType);RepositoryUrl=$(PrivateRepositoryUrl);ProjectFolder=$(MSBuildProjectDirectory)\;ProjectUrl=$(ProjectUrl) + true + snupkg + + + + + + + + + + + + + + + - - $([System.IO.File]::ReadAllText($(TheSolutionPath))) - $([System.IO.Path]::GetDirectoryName( $(TheSolutionPath) )) - (?<="[PackageName]", ")(.*)(?=", ") - + + - - - - %(Identity) - $(SolutionFileContent.Contains('\%(Identity).csproj')) - + - - - $(RegexPattern.Replace('[PackageName]','%(PackageName)') ) - $([System.Text.RegularExpressions.Regex]::Match('$(SolutionFileContent)', '%(Pattern)')) - + + - + + + + - - - + + - - + + diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props new file mode 100644 index 00000000..369c51e7 --- /dev/null +++ b/src/Directory.Packages.props @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/Directory.csproj.props b/src/Directory.csproj.props new file mode 100644 index 00000000..81d24ad1 --- /dev/null +++ b/src/Directory.csproj.props @@ -0,0 +1,13 @@ + + + + + true + true + $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)wix.snk)) + false + + diff --git a/src/Directory.vcxproj.props b/src/Directory.vcxproj.props new file mode 100644 index 00000000..63d73b36 --- /dev/null +++ b/src/Directory.vcxproj.props @@ -0,0 +1,118 @@ + + + + + + Win32 + $(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\ + $(OutputPath)$(Platform)\ + + + $(Company) + $(Copyright) + + win-x86;win-x64;win-arm64 + native,Version=v0.0 + + + + $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) + + + + $(MSBuildThisFileDirectory)CustomizedNativeRecommendedRules.ruleset + + + + + $(DisableSpecificCompilerWarnings) + Level4 + $(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0600;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) + Use + precomp.h + StdCall + true + false + Guard + -YlprecompDefine + /Zc:threadSafeInit- %(AdditionalOptions) + true + + + $(ArmPreprocessorDefinitions);%(PreprocessorDefinitions) + $(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories) + + + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories) + + + $(ProjectSubSystem) + $(ProjectModuleDefinitionFile) + $(ResourceOnlyDll) + true + $(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies) + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories) + /IGNORE:4099 %(AdditionalOptions) + + + + + + NoExtensions + + + + + CDecl + + + + + OldStyle + true + true + + + + + Disabled + EnableFastChecks + _DEBUG;DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebug + + + + + + + MultiThreadedDebugDll + + + + + MinSpace + NDEBUG;%(PreprocessorDefinitions) + true + true + MultiThreaded + + + true + true + + + + + + + MultiThreadedDll + + + + + $(LinkKeyFile) + $(LinkDelaySign) + + + diff --git a/src/NativeMultiTargeting.Build.props b/src/NativeMultiTargeting.Build.props new file mode 100644 index 00000000..1ff46559 --- /dev/null +++ b/src/NativeMultiTargeting.Build.props @@ -0,0 +1,10 @@ + + + + + + + $(BaseIntermediateOutputPath)$(Configuration)\$(PlatformToolset)\$(PlatformTarget)\ + $(OutputPath)$(PlatformToolset)\$(PlatformTarget)\ + + diff --git a/src/engine/container.cpp b/src/engine/container.cpp index 2db2412c..0cce3131 100644 --- a/src/engine/container.cpp +++ b/src/engine/container.cpp @@ -318,6 +318,10 @@ extern "C" HRESULT ContainerStreamToBuffer( case BURN_CONTAINER_TYPE_CABINET: hr = CabExtractStreamToBuffer(pContext, ppbBuffer, pcbBuffer); break; + + default: + *ppbBuffer = NULL; + *pcbBuffer = 0; } //LExit: diff --git a/src/engine/container.h b/src/engine/container.h index 88fe48f1..c2c1c9a8 100644 --- a/src/engine/container.h +++ b/src/engine/container.h @@ -161,7 +161,7 @@ HRESULT ContainerOpen( __in_z LPCWSTR wzFilePath ); HRESULT ContainerNextStream( - __inout BURN_CONTAINER_CONTEXT* pContext, + __in BURN_CONTAINER_CONTEXT* pContext, __inout_z LPWSTR* psczStreamName ); HRESULT ContainerStreamToFile( diff --git a/src/stub/stub.nuspec b/src/stub/stub.nuspec new file mode 100644 index 00000000..968feff3 --- /dev/null +++ b/src/stub/stub.nuspec @@ -0,0 +1,25 @@ + + + + $id$ + $version$ + $title$ + $description$ + $authors$ + MS-RL + false + $copyright$ + $projectUrl$ + + + + + + + + + + + + + diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj index 38710865..97972848 100644 --- a/src/stub/stub.vcxproj +++ b/src/stub/stub.vcxproj @@ -46,6 +46,8 @@ 1033 Burn + WixToolset.Burn + false @@ -101,10 +103,6 @@ - - - - @@ -119,4 +117,4 @@ - \ No newline at end of file + diff --git a/src/wix.snk b/src/wix.snk new file mode 100644 index 00000000..3908a66a Binary files /dev/null and b/src/wix.snk differ -- cgit v1.2.3-55-g6feb From af10c45d7b3a44af0b461a557847fe03263dcc10 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Thu, 22 Apr 2021 17:06:54 -0700 Subject: Move burn into burn --- .editorconfig | 37 - README.md | 2 - appveyor.cmd | 17 - appveyor.yml | 44 - burn.sln | 61 - nuget.config | 10 - signing.json | 13 - src/.editorconfig | 37 + src/CustomizedNativeRecommendedRules.ruleset | 8 - src/Directory.Build.props | 26 - src/Directory.Build.targets | 73 - src/Directory.Packages.props | 20 - src/Directory.csproj.props | 13 - src/Directory.vcxproj.props | 118 - src/NativeMultiTargeting.Build.props | 10 - .../ManagedBundleRunner/BundleErrorEventArgs.cs | 33 - .../ManagedBundleRunner/BundleProgressEventArgs.cs | 23 - src/Samples/ManagedBundleRunner/BundleResult.cs | 24 - src/Samples/ManagedBundleRunner/BundleRunner.cs | 212 -- src/Samples/runbundle/AssemblyInfo.cs | 12 - src/Samples/runbundle/Program.cs | 47 - src/burn/CustomizedNativeRecommendedRules.ruleset | 8 + src/burn/Directory.Build.props | 26 + src/burn/Directory.Build.targets | 73 + src/burn/Directory.Packages.props | 20 + src/burn/Directory.csproj.props | 13 + src/burn/Directory.vcxproj.props | 118 + src/burn/NativeMultiTargeting.Build.props | 10 + src/burn/README.md | 2 + src/burn/appveyor.cmd | 17 + src/burn/appveyor.yml | 44 + src/burn/burn.sln | 61 + src/burn/engine/EngineForApplication.cpp | 529 ++++ src/burn/engine/EngineForApplication.h | 44 + src/burn/engine/EngineForExtension.cpp | 263 ++ src/burn/engine/EngineForExtension.h | 27 + src/burn/engine/apply.cpp | 3096 +++++++++++++++++++ src/burn/engine/apply.h | 104 + src/burn/engine/approvedexe.cpp | 262 ++ src/burn/engine/approvedexe.h | 67 + src/burn/engine/burnextension.cpp | 264 ++ src/burn/engine/burnextension.h | 61 + src/burn/engine/cabextract.cpp | 974 ++++++ src/burn/engine/cabextract.h | 40 + src/burn/engine/cache.cpp | 2052 +++++++++++++ src/burn/engine/cache.h | 216 ++ src/burn/engine/condition.cpp | 1057 +++++++ src/burn/engine/condition.h | 39 + src/burn/engine/container.cpp | 398 +++ src/burn/engine/container.h | 191 ++ src/burn/engine/core.cpp | 1856 +++++++++++ src/burn/engine/core.h | 218 ++ src/burn/engine/dependency.cpp | 1312 ++++++++ src/burn/engine/dependency.h | 168 + src/burn/engine/detect.cpp | 469 +++ src/burn/engine/detect.h | 44 + src/burn/engine/elevation.cpp | 3239 ++++++++++++++++++++ src/burn/engine/elevation.h | 176 ++ src/burn/engine/embedded.cpp | 197 ++ src/burn/engine/embedded.h | 27 + src/burn/engine/engine.cpp | 992 ++++++ src/burn/engine/engine.mc | 1090 +++++++ src/burn/engine/engine.vcxproj | 186 ++ src/burn/engine/exeengine.cpp | 816 +++++ src/burn/engine/exeengine.h | 50 + src/burn/engine/externalengine.cpp | 805 +++++ src/burn/engine/externalengine.h | 181 ++ src/burn/engine/inc/burnsources.h | 4 + src/burn/engine/inc/engine.h | 27 + src/burn/engine/logging.cpp | 754 +++++ src/burn/engine/logging.h | 153 + src/burn/engine/manifest.cpp | 164 + src/burn/engine/manifest.h | 28 + src/burn/engine/msiengine.cpp | 2035 ++++++++++++ src/burn/engine/msiengine.h | 104 + src/burn/engine/mspengine.cpp | 1197 ++++++++ src/burn/engine/mspengine.h | 84 + src/burn/engine/msuengine.cpp | 529 ++++ src/burn/engine/msuengine.h | 50 + src/burn/engine/netfxchainer.cpp | 418 +++ src/burn/engine/netfxchainer.h | 98 + src/burn/engine/package.cpp | 692 +++++ src/burn/engine/package.h | 380 +++ src/burn/engine/packages.config | 5 + src/burn/engine/payload.cpp | 314 ++ src/burn/engine/payload.h | 107 + src/burn/engine/pipe.cpp | 821 +++++ src/burn/engine/pipe.h | 113 + src/burn/engine/plan.cpp | 2699 ++++++++++++++++ src/burn/engine/plan.h | 456 +++ src/burn/engine/platform.cpp | 16 + src/burn/engine/platform.h | 34 + src/burn/engine/precomp.cpp | 3 + src/burn/engine/precomp.h | 102 + src/burn/engine/pseudobundle.cpp | 241 ++ src/burn/engine/pseudobundle.h | 40 + src/burn/engine/registration.cpp | 1702 ++++++++++ src/burn/engine/registration.h | 225 ++ src/burn/engine/relatedbundle.cpp | 483 +++ src/burn/engine/relatedbundle.h | 20 + src/burn/engine/search.cpp | 1303 ++++++++ src/burn/engine/search.h | 163 + src/burn/engine/section.cpp | 399 +++ src/burn/engine/section.h | 54 + src/burn/engine/splashscreen.cpp | 355 +++ src/burn/engine/splashscreen.h | 31 + src/burn/engine/uithread.cpp | 222 ++ src/burn/engine/uithread.h | 23 + src/burn/engine/update.cpp | 44 + src/burn/engine/update.h | 33 + src/burn/engine/userexperience.cpp | 2653 ++++++++++++++++ src/burn/engine/userexperience.h | 545 ++++ src/burn/engine/variable.cpp | 2323 ++++++++++++++ src/burn/engine/variable.h | 185 ++ src/burn/engine/variant.cpp | 321 ++ src/burn/engine/variant.h | 100 + src/burn/nuget.config | 10 + src/burn/stub/StubSection.cpp | 23 + src/burn/stub/WixToolset.Burn.props | 13 + src/burn/stub/packages.config | 8 + src/burn/stub/precomp.cpp | 3 + src/burn/stub/precomp.h | 17 + src/burn/stub/stub.cpp | 106 + src/burn/stub/stub.ico | Bin 0 -> 2238 bytes src/burn/stub/stub.nuspec | 25 + src/burn/stub/stub.rc | 3 + src/burn/stub/stub.vcxproj | 120 + src/burn/test/BurnUnitTest/AssemblyInfo.cpp | 12 + src/burn/test/BurnUnitTest/BurnTestException.h | 93 + src/burn/test/BurnUnitTest/BurnTestFixture.h | 75 + src/burn/test/BurnUnitTest/BurnUnitTest.h | 48 + src/burn/test/BurnUnitTest/BurnUnitTest.rc | 6 + src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj | 109 + .../test/BurnUnitTest/BurnUnitTest.vcxproj.filters | 80 + src/burn/test/BurnUnitTest/CacheTest.cpp | 119 + src/burn/test/BurnUnitTest/ElevationTest.cpp | 221 ++ src/burn/test/BurnUnitTest/ManifestHelpers.cpp | 41 + src/burn/test/BurnUnitTest/ManifestHelpers.h | 24 + src/burn/test/BurnUnitTest/ManifestTest.cpp | 62 + src/burn/test/BurnUnitTest/PlanTest.cpp | 1473 +++++++++ src/burn/test/BurnUnitTest/RegistrationTest.cpp | 772 +++++ src/burn/test/BurnUnitTest/SearchTest.cpp | 815 +++++ .../TestData/CacheTest/CacheSignatureTest.File | 1 + .../BasicFunctionality_BundleA_manifest.xml | 1 + .../PlanTest/MsiTransaction_BundleAv1_manifest.xml | 1 + .../PlanTest/Slipstream_BundleA_manifest.xml | 1 + src/burn/test/BurnUnitTest/VariableHelpers.cpp | 217 ++ src/burn/test/BurnUnitTest/VariableHelpers.h | 36 + src/burn/test/BurnUnitTest/VariableTest.cpp | 532 ++++ src/burn/test/BurnUnitTest/VariantTest.cpp | 221 ++ src/burn/test/BurnUnitTest/packages.config | 15 + src/burn/test/BurnUnitTest/precomp.cpp | 3 + src/burn/test/BurnUnitTest/precomp.h | 79 + src/engine/EngineForApplication.cpp | 529 ---- src/engine/EngineForApplication.h | 44 - src/engine/EngineForExtension.cpp | 263 -- src/engine/EngineForExtension.h | 27 - src/engine/apply.cpp | 3096 ------------------- src/engine/apply.h | 104 - src/engine/approvedexe.cpp | 262 -- src/engine/approvedexe.h | 67 - src/engine/burnextension.cpp | 264 -- src/engine/burnextension.h | 61 - src/engine/cabextract.cpp | 974 ------ src/engine/cabextract.h | 40 - src/engine/cache.cpp | 2052 ------------- src/engine/cache.h | 216 -- src/engine/condition.cpp | 1057 ------- src/engine/condition.h | 39 - src/engine/container.cpp | 398 --- src/engine/container.h | 191 -- src/engine/core.cpp | 1856 ----------- src/engine/core.h | 218 -- src/engine/dependency.cpp | 1312 -------- src/engine/dependency.h | 168 - src/engine/detect.cpp | 469 --- src/engine/detect.h | 44 - src/engine/elevation.cpp | 3239 -------------------- src/engine/elevation.h | 176 -- src/engine/embedded.cpp | 197 -- src/engine/embedded.h | 27 - src/engine/engine.cpp | 992 ------ src/engine/engine.mc | 1090 ------- src/engine/engine.vcxproj | 186 -- src/engine/exeengine.cpp | 816 ----- src/engine/exeengine.h | 50 - src/engine/externalengine.cpp | 805 ----- src/engine/externalengine.h | 181 -- src/engine/inc/burnsources.h | 4 - src/engine/inc/engine.h | 27 - src/engine/logging.cpp | 754 ----- src/engine/logging.h | 153 - src/engine/manifest.cpp | 164 - src/engine/manifest.h | 28 - src/engine/msiengine.cpp | 2035 ------------ src/engine/msiengine.h | 104 - src/engine/mspengine.cpp | 1197 -------- src/engine/mspengine.h | 84 - src/engine/msuengine.cpp | 529 ---- src/engine/msuengine.h | 50 - src/engine/netfxchainer.cpp | 418 --- src/engine/netfxchainer.h | 98 - src/engine/package.cpp | 692 ----- src/engine/package.h | 380 --- src/engine/packages.config | 5 - src/engine/payload.cpp | 314 -- src/engine/payload.h | 107 - src/engine/pipe.cpp | 821 ----- src/engine/pipe.h | 113 - src/engine/plan.cpp | 2699 ---------------- src/engine/plan.h | 456 --- src/engine/platform.cpp | 16 - src/engine/platform.h | 34 - src/engine/precomp.cpp | 3 - src/engine/precomp.h | 102 - src/engine/pseudobundle.cpp | 241 -- src/engine/pseudobundle.h | 40 - src/engine/registration.cpp | 1702 ---------- src/engine/registration.h | 225 -- src/engine/relatedbundle.cpp | 483 --- src/engine/relatedbundle.h | 20 - src/engine/search.cpp | 1303 -------- src/engine/search.h | 163 - src/engine/section.cpp | 399 --- src/engine/section.h | 54 - src/engine/splashscreen.cpp | 355 --- src/engine/splashscreen.h | 31 - src/engine/uithread.cpp | 222 -- src/engine/uithread.h | 23 - src/engine/update.cpp | 44 - src/engine/update.h | 33 - src/engine/userexperience.cpp | 2653 ---------------- src/engine/userexperience.h | 545 ---- src/engine/variable.cpp | 2323 -------------- src/engine/variable.h | 185 -- src/engine/variant.cpp | 321 -- src/engine/variant.h | 100 - .../ManagedBundleRunner/BundleErrorEventArgs.cs | 33 + .../ManagedBundleRunner/BundleProgressEventArgs.cs | 23 + .../burn/ManagedBundleRunner/BundleResult.cs | 24 + .../burn/ManagedBundleRunner/BundleRunner.cs | 212 ++ src/samples/burn/runbundle/AssemblyInfo.cs | 12 + src/samples/burn/runbundle/Program.cs | 47 + src/signing.json | 13 + src/stub/StubSection.cpp | 23 - src/stub/WixToolset.Burn.props | 13 - src/stub/packages.config | 8 - src/stub/precomp.cpp | 3 - src/stub/precomp.h | 17 - src/stub/stub.cpp | 106 - src/stub/stub.ico | Bin 2238 -> 0 bytes src/stub/stub.nuspec | 25 - src/stub/stub.rc | 3 - src/stub/stub.vcxproj | 120 - src/test/BurnUnitTest/AssemblyInfo.cpp | 12 - src/test/BurnUnitTest/BurnTestException.h | 93 - src/test/BurnUnitTest/BurnTestFixture.h | 75 - src/test/BurnUnitTest/BurnUnitTest.h | 48 - src/test/BurnUnitTest/BurnUnitTest.rc | 6 - src/test/BurnUnitTest/BurnUnitTest.vcxproj | 109 - src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters | 80 - src/test/BurnUnitTest/CacheTest.cpp | 119 - src/test/BurnUnitTest/ElevationTest.cpp | 221 -- src/test/BurnUnitTest/ManifestHelpers.cpp | 41 - src/test/BurnUnitTest/ManifestHelpers.h | 24 - src/test/BurnUnitTest/ManifestTest.cpp | 62 - src/test/BurnUnitTest/PlanTest.cpp | 1473 --------- src/test/BurnUnitTest/RegistrationTest.cpp | 772 ----- src/test/BurnUnitTest/SearchTest.cpp | 815 ----- .../TestData/CacheTest/CacheSignatureTest.File | 1 - .../BasicFunctionality_BundleA_manifest.xml | 1 - .../PlanTest/MsiTransaction_BundleAv1_manifest.xml | 1 - .../PlanTest/Slipstream_BundleA_manifest.xml | 1 - src/test/BurnUnitTest/VariableHelpers.cpp | 217 -- src/test/BurnUnitTest/VariableHelpers.h | 36 - src/test/BurnUnitTest/VariableTest.cpp | 532 ---- src/test/BurnUnitTest/VariantTest.cpp | 221 -- src/test/BurnUnitTest/packages.config | 15 - src/test/BurnUnitTest/precomp.cpp | 3 - src/test/BurnUnitTest/precomp.h | 79 - src/version.json | 11 + version.json | 11 - 282 files changed, 50551 insertions(+), 50551 deletions(-) delete mode 100644 .editorconfig delete mode 100644 README.md delete mode 100644 appveyor.cmd delete mode 100644 appveyor.yml delete mode 100644 burn.sln delete mode 100644 nuget.config delete mode 100644 signing.json create mode 100644 src/.editorconfig delete mode 100644 src/CustomizedNativeRecommendedRules.ruleset delete mode 100644 src/Directory.Build.props delete mode 100644 src/Directory.Build.targets delete mode 100644 src/Directory.Packages.props delete mode 100644 src/Directory.csproj.props delete mode 100644 src/Directory.vcxproj.props delete mode 100644 src/NativeMultiTargeting.Build.props delete mode 100644 src/Samples/ManagedBundleRunner/BundleErrorEventArgs.cs delete mode 100644 src/Samples/ManagedBundleRunner/BundleProgressEventArgs.cs delete mode 100644 src/Samples/ManagedBundleRunner/BundleResult.cs delete mode 100644 src/Samples/ManagedBundleRunner/BundleRunner.cs delete mode 100644 src/Samples/runbundle/AssemblyInfo.cs delete mode 100644 src/Samples/runbundle/Program.cs create mode 100644 src/burn/CustomizedNativeRecommendedRules.ruleset create mode 100644 src/burn/Directory.Build.props create mode 100644 src/burn/Directory.Build.targets create mode 100644 src/burn/Directory.Packages.props create mode 100644 src/burn/Directory.csproj.props create mode 100644 src/burn/Directory.vcxproj.props create mode 100644 src/burn/NativeMultiTargeting.Build.props create mode 100644 src/burn/README.md create mode 100644 src/burn/appveyor.cmd create mode 100644 src/burn/appveyor.yml create mode 100644 src/burn/burn.sln create mode 100644 src/burn/engine/EngineForApplication.cpp create mode 100644 src/burn/engine/EngineForApplication.h create mode 100644 src/burn/engine/EngineForExtension.cpp create mode 100644 src/burn/engine/EngineForExtension.h create mode 100644 src/burn/engine/apply.cpp create mode 100644 src/burn/engine/apply.h create mode 100644 src/burn/engine/approvedexe.cpp create mode 100644 src/burn/engine/approvedexe.h create mode 100644 src/burn/engine/burnextension.cpp create mode 100644 src/burn/engine/burnextension.h create mode 100644 src/burn/engine/cabextract.cpp create mode 100644 src/burn/engine/cabextract.h create mode 100644 src/burn/engine/cache.cpp create mode 100644 src/burn/engine/cache.h create mode 100644 src/burn/engine/condition.cpp create mode 100644 src/burn/engine/condition.h create mode 100644 src/burn/engine/container.cpp create mode 100644 src/burn/engine/container.h create mode 100644 src/burn/engine/core.cpp create mode 100644 src/burn/engine/core.h create mode 100644 src/burn/engine/dependency.cpp create mode 100644 src/burn/engine/dependency.h create mode 100644 src/burn/engine/detect.cpp create mode 100644 src/burn/engine/detect.h create mode 100644 src/burn/engine/elevation.cpp create mode 100644 src/burn/engine/elevation.h create mode 100644 src/burn/engine/embedded.cpp create mode 100644 src/burn/engine/embedded.h create mode 100644 src/burn/engine/engine.cpp create mode 100644 src/burn/engine/engine.mc create mode 100644 src/burn/engine/engine.vcxproj create mode 100644 src/burn/engine/exeengine.cpp create mode 100644 src/burn/engine/exeengine.h create mode 100644 src/burn/engine/externalengine.cpp create mode 100644 src/burn/engine/externalengine.h create mode 100644 src/burn/engine/inc/burnsources.h create mode 100644 src/burn/engine/inc/engine.h create mode 100644 src/burn/engine/logging.cpp create mode 100644 src/burn/engine/logging.h create mode 100644 src/burn/engine/manifest.cpp create mode 100644 src/burn/engine/manifest.h create mode 100644 src/burn/engine/msiengine.cpp create mode 100644 src/burn/engine/msiengine.h create mode 100644 src/burn/engine/mspengine.cpp create mode 100644 src/burn/engine/mspengine.h create mode 100644 src/burn/engine/msuengine.cpp create mode 100644 src/burn/engine/msuengine.h create mode 100644 src/burn/engine/netfxchainer.cpp create mode 100644 src/burn/engine/netfxchainer.h create mode 100644 src/burn/engine/package.cpp create mode 100644 src/burn/engine/package.h create mode 100644 src/burn/engine/packages.config create mode 100644 src/burn/engine/payload.cpp create mode 100644 src/burn/engine/payload.h create mode 100644 src/burn/engine/pipe.cpp create mode 100644 src/burn/engine/pipe.h create mode 100644 src/burn/engine/plan.cpp create mode 100644 src/burn/engine/plan.h create mode 100644 src/burn/engine/platform.cpp create mode 100644 src/burn/engine/platform.h create mode 100644 src/burn/engine/precomp.cpp create mode 100644 src/burn/engine/precomp.h create mode 100644 src/burn/engine/pseudobundle.cpp create mode 100644 src/burn/engine/pseudobundle.h create mode 100644 src/burn/engine/registration.cpp create mode 100644 src/burn/engine/registration.h create mode 100644 src/burn/engine/relatedbundle.cpp create mode 100644 src/burn/engine/relatedbundle.h create mode 100644 src/burn/engine/search.cpp create mode 100644 src/burn/engine/search.h create mode 100644 src/burn/engine/section.cpp create mode 100644 src/burn/engine/section.h create mode 100644 src/burn/engine/splashscreen.cpp create mode 100644 src/burn/engine/splashscreen.h create mode 100644 src/burn/engine/uithread.cpp create mode 100644 src/burn/engine/uithread.h create mode 100644 src/burn/engine/update.cpp create mode 100644 src/burn/engine/update.h create mode 100644 src/burn/engine/userexperience.cpp create mode 100644 src/burn/engine/userexperience.h create mode 100644 src/burn/engine/variable.cpp create mode 100644 src/burn/engine/variable.h create mode 100644 src/burn/engine/variant.cpp create mode 100644 src/burn/engine/variant.h create mode 100644 src/burn/nuget.config create mode 100644 src/burn/stub/StubSection.cpp create mode 100644 src/burn/stub/WixToolset.Burn.props create mode 100644 src/burn/stub/packages.config create mode 100644 src/burn/stub/precomp.cpp create mode 100644 src/burn/stub/precomp.h create mode 100644 src/burn/stub/stub.cpp create mode 100644 src/burn/stub/stub.ico create mode 100644 src/burn/stub/stub.nuspec create mode 100644 src/burn/stub/stub.rc create mode 100644 src/burn/stub/stub.vcxproj create mode 100644 src/burn/test/BurnUnitTest/AssemblyInfo.cpp create mode 100644 src/burn/test/BurnUnitTest/BurnTestException.h create mode 100644 src/burn/test/BurnUnitTest/BurnTestFixture.h create mode 100644 src/burn/test/BurnUnitTest/BurnUnitTest.h create mode 100644 src/burn/test/BurnUnitTest/BurnUnitTest.rc create mode 100644 src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj create mode 100644 src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters create mode 100644 src/burn/test/BurnUnitTest/CacheTest.cpp create mode 100644 src/burn/test/BurnUnitTest/ElevationTest.cpp create mode 100644 src/burn/test/BurnUnitTest/ManifestHelpers.cpp create mode 100644 src/burn/test/BurnUnitTest/ManifestHelpers.h create mode 100644 src/burn/test/BurnUnitTest/ManifestTest.cpp create mode 100644 src/burn/test/BurnUnitTest/PlanTest.cpp create mode 100644 src/burn/test/BurnUnitTest/RegistrationTest.cpp create mode 100644 src/burn/test/BurnUnitTest/SearchTest.cpp create mode 100644 src/burn/test/BurnUnitTest/TestData/CacheTest/CacheSignatureTest.File create mode 100644 src/burn/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml create mode 100644 src/burn/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml create mode 100644 src/burn/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml create mode 100644 src/burn/test/BurnUnitTest/VariableHelpers.cpp create mode 100644 src/burn/test/BurnUnitTest/VariableHelpers.h create mode 100644 src/burn/test/BurnUnitTest/VariableTest.cpp create mode 100644 src/burn/test/BurnUnitTest/VariantTest.cpp create mode 100644 src/burn/test/BurnUnitTest/packages.config create mode 100644 src/burn/test/BurnUnitTest/precomp.cpp create mode 100644 src/burn/test/BurnUnitTest/precomp.h delete mode 100644 src/engine/EngineForApplication.cpp delete mode 100644 src/engine/EngineForApplication.h delete mode 100644 src/engine/EngineForExtension.cpp delete mode 100644 src/engine/EngineForExtension.h delete mode 100644 src/engine/apply.cpp delete mode 100644 src/engine/apply.h delete mode 100644 src/engine/approvedexe.cpp delete mode 100644 src/engine/approvedexe.h delete mode 100644 src/engine/burnextension.cpp delete mode 100644 src/engine/burnextension.h delete mode 100644 src/engine/cabextract.cpp delete mode 100644 src/engine/cabextract.h delete mode 100644 src/engine/cache.cpp delete mode 100644 src/engine/cache.h delete mode 100644 src/engine/condition.cpp delete mode 100644 src/engine/condition.h delete mode 100644 src/engine/container.cpp delete mode 100644 src/engine/container.h delete mode 100644 src/engine/core.cpp delete mode 100644 src/engine/core.h delete mode 100644 src/engine/dependency.cpp delete mode 100644 src/engine/dependency.h delete mode 100644 src/engine/detect.cpp delete mode 100644 src/engine/detect.h delete mode 100644 src/engine/elevation.cpp delete mode 100644 src/engine/elevation.h delete mode 100644 src/engine/embedded.cpp delete mode 100644 src/engine/embedded.h delete mode 100644 src/engine/engine.cpp delete mode 100644 src/engine/engine.mc delete mode 100644 src/engine/engine.vcxproj delete mode 100644 src/engine/exeengine.cpp delete mode 100644 src/engine/exeengine.h delete mode 100644 src/engine/externalengine.cpp delete mode 100644 src/engine/externalengine.h delete mode 100644 src/engine/inc/burnsources.h delete mode 100644 src/engine/inc/engine.h delete mode 100644 src/engine/logging.cpp delete mode 100644 src/engine/logging.h delete mode 100644 src/engine/manifest.cpp delete mode 100644 src/engine/manifest.h delete mode 100644 src/engine/msiengine.cpp delete mode 100644 src/engine/msiengine.h delete mode 100644 src/engine/mspengine.cpp delete mode 100644 src/engine/mspengine.h delete mode 100644 src/engine/msuengine.cpp delete mode 100644 src/engine/msuengine.h delete mode 100644 src/engine/netfxchainer.cpp delete mode 100644 src/engine/netfxchainer.h delete mode 100644 src/engine/package.cpp delete mode 100644 src/engine/package.h delete mode 100644 src/engine/packages.config delete mode 100644 src/engine/payload.cpp delete mode 100644 src/engine/payload.h delete mode 100644 src/engine/pipe.cpp delete mode 100644 src/engine/pipe.h delete mode 100644 src/engine/plan.cpp delete mode 100644 src/engine/plan.h delete mode 100644 src/engine/platform.cpp delete mode 100644 src/engine/platform.h delete mode 100644 src/engine/precomp.cpp delete mode 100644 src/engine/precomp.h delete mode 100644 src/engine/pseudobundle.cpp delete mode 100644 src/engine/pseudobundle.h delete mode 100644 src/engine/registration.cpp delete mode 100644 src/engine/registration.h delete mode 100644 src/engine/relatedbundle.cpp delete mode 100644 src/engine/relatedbundle.h delete mode 100644 src/engine/search.cpp delete mode 100644 src/engine/search.h delete mode 100644 src/engine/section.cpp delete mode 100644 src/engine/section.h delete mode 100644 src/engine/splashscreen.cpp delete mode 100644 src/engine/splashscreen.h delete mode 100644 src/engine/uithread.cpp delete mode 100644 src/engine/uithread.h delete mode 100644 src/engine/update.cpp delete mode 100644 src/engine/update.h delete mode 100644 src/engine/userexperience.cpp delete mode 100644 src/engine/userexperience.h delete mode 100644 src/engine/variable.cpp delete mode 100644 src/engine/variable.h delete mode 100644 src/engine/variant.cpp delete mode 100644 src/engine/variant.h create mode 100644 src/samples/burn/ManagedBundleRunner/BundleErrorEventArgs.cs create mode 100644 src/samples/burn/ManagedBundleRunner/BundleProgressEventArgs.cs create mode 100644 src/samples/burn/ManagedBundleRunner/BundleResult.cs create mode 100644 src/samples/burn/ManagedBundleRunner/BundleRunner.cs create mode 100644 src/samples/burn/runbundle/AssemblyInfo.cs create mode 100644 src/samples/burn/runbundle/Program.cs create mode 100644 src/signing.json delete mode 100644 src/stub/StubSection.cpp delete mode 100644 src/stub/WixToolset.Burn.props delete mode 100644 src/stub/packages.config delete mode 100644 src/stub/precomp.cpp delete mode 100644 src/stub/precomp.h delete mode 100644 src/stub/stub.cpp delete mode 100644 src/stub/stub.ico delete mode 100644 src/stub/stub.nuspec delete mode 100644 src/stub/stub.rc delete mode 100644 src/stub/stub.vcxproj delete mode 100644 src/test/BurnUnitTest/AssemblyInfo.cpp delete mode 100644 src/test/BurnUnitTest/BurnTestException.h delete mode 100644 src/test/BurnUnitTest/BurnTestFixture.h delete mode 100644 src/test/BurnUnitTest/BurnUnitTest.h delete mode 100644 src/test/BurnUnitTest/BurnUnitTest.rc delete mode 100644 src/test/BurnUnitTest/BurnUnitTest.vcxproj delete mode 100644 src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters delete mode 100644 src/test/BurnUnitTest/CacheTest.cpp delete mode 100644 src/test/BurnUnitTest/ElevationTest.cpp delete mode 100644 src/test/BurnUnitTest/ManifestHelpers.cpp delete mode 100644 src/test/BurnUnitTest/ManifestHelpers.h delete mode 100644 src/test/BurnUnitTest/ManifestTest.cpp delete mode 100644 src/test/BurnUnitTest/PlanTest.cpp delete mode 100644 src/test/BurnUnitTest/RegistrationTest.cpp delete mode 100644 src/test/BurnUnitTest/SearchTest.cpp delete mode 100644 src/test/BurnUnitTest/TestData/CacheTest/CacheSignatureTest.File delete mode 100644 src/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml delete mode 100644 src/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml delete mode 100644 src/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml delete mode 100644 src/test/BurnUnitTest/VariableHelpers.cpp delete mode 100644 src/test/BurnUnitTest/VariableHelpers.h delete mode 100644 src/test/BurnUnitTest/VariableTest.cpp delete mode 100644 src/test/BurnUnitTest/VariantTest.cpp delete mode 100644 src/test/BurnUnitTest/packages.config delete mode 100644 src/test/BurnUnitTest/precomp.cpp delete mode 100644 src/test/BurnUnitTest/precomp.h create mode 100644 src/version.json delete mode 100644 version.json diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 1d72e683..00000000 --- a/.editorconfig +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -# -# Do NOT modify this file. Update the canonical version in Home\repo-template\src\.editorconfig -# then update all of the repos. - -root = true - -[*] -charset = utf-8 -indent_style = space -indent_size = 4 -trim_trailing_whitespace = true - -[*.{cs,vb}] -dotnet_sort_system_directives_first = true - -[*.cs] -csharp_indent_case_contents = true : error -csharp_indent_switch_labels = true : error -csharp_new_line_before_open_brace = all -csharp_prefer_braces = true : error -csharp_style_expression_bodied_methods = when_on_single_line : suggestion -csharp_style_expression_bodied_constructors = when_on_single_line : suggestion -csharp_style_expression_bodied_operators = when_on_single_line : suggestion -csharp_style_expression_bodied_properties = when_on_single_line : suggestion -csharp_style_expression_bodied_indexers = when_on_single_line : suggestion -csharp_style_expression_bodied_accessors = when_on_single_line : suggestion -csharp_style_var_elsewhere = true : suggestion -csharp_style_var_for_built_in_types = true : suggestion -csharp_style_var_when_type_is_apparent = true : suggestion -dotnet_style_qualification_for_event = true : error -dotnet_style_qualification_for_field = true : error -dotnet_style_qualification_for_method = true : error -dotnet_style_qualification_for_property = true : error - -[*.targets] -indent_size = 2 diff --git a/README.md b/README.md deleted file mode 100644 index a564d971..00000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# burn -burn.lib - Burn engine diff --git a/appveyor.cmd b/appveyor.cmd deleted file mode 100644 index a35e10d5..00000000 --- a/appveyor.cmd +++ /dev/null @@ -1,17 +0,0 @@ -@setlocal -@pushd %~dp0 -@set _C=Release -@if /i "%1"=="debug" set _C=Debug - -nuget restore || exit /b - -msbuild -t:Test -p:Configuration=%_C% src\test\BurnUnitTest || exit /b - -msbuild -p:Configuration=%_C%;Platform=x86 || exit /b -msbuild -p:Configuration=%_C%;Platform=x64 || exit /b -msbuild -p:Configuration=%_C%;Platform=arm64 || exit /b - -msbuild -p:Configuration=%_C% -t:PackNative src\stub\stub.vcxproj || exit /b - -@popd -@endlocal \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 364569cf..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -# -# Do NOT modify this file. Update the canonical version in Home\repo-template\src\appveyor.yml -# then update all of the repos. - -branches: - only: - - master - - develop - -image: Visual Studio 2019 - -version: 0.0.0.{build} -configuration: Release - -environment: - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true - DOTNET_CLI_TELEMETRY_OPTOUT: 1 - NUGET_XMLDOC_MODE: skip - -build_script: - - appveyor.cmd - -test: off - -pull_requests: - do_not_increment_build_number: true - -nuget: - disable_publish_on_pr: true - -skip_branch_with_pr: true -skip_tags: true - -artifacts: -- path: build\Release\**\*.nupkg - name: nuget -- path: build\Release\**\*.snupkg - name: snupkg - -notifications: -- provider: Slack - incoming_webhook: - secure: p5xuu+4x2JHfwGDMDe5KcG1k7gZxqYc4jWVwvyNZv5cvkubPD2waJs5yXMAXZNN7Z63/3PWHb7q4KoY/99AjauYa1nZ4c5qYqRPFRBKTHfA= diff --git a/burn.sln b/burn.sln deleted file mode 100644 index 6a64b8f0..00000000 --- a/burn.sln +++ /dev/null @@ -1,61 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30711.63 -MinimumVisualStudioVersion = 15.0.26124.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "engine", "src\engine\engine.vcxproj", "{8119537D-E1D9-6591-D51A-49768A2F9C37}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stub", "src\stub\stub.vcxproj", "{C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BurnUnitTest", "src\test\BurnUnitTest\BurnUnitTest.vcxproj", "{9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|ARM64 = Debug|ARM64 - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|ARM64 = Release|ARM64 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|ARM64.Build.0 = Debug|ARM64 - {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|x64.ActiveCfg = Debug|x64 - {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|x64.Build.0 = Debug|x64 - {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|x86.ActiveCfg = Debug|Win32 - {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|x86.Build.0 = Debug|Win32 - {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|ARM64.ActiveCfg = Release|ARM64 - {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|ARM64.Build.0 = Release|ARM64 - {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|x64.ActiveCfg = Release|x64 - {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|x64.Build.0 = Release|x64 - {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|x86.ActiveCfg = Release|Win32 - {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|x86.Build.0 = Release|Win32 - {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|ARM64.Build.0 = Debug|ARM64 - {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|x64.ActiveCfg = Debug|x64 - {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|x64.Build.0 = Debug|x64 - {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|x86.ActiveCfg = Debug|Win32 - {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|x86.Build.0 = Debug|Win32 - {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|ARM64.ActiveCfg = Release|ARM64 - {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|ARM64.Build.0 = Release|ARM64 - {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x64.ActiveCfg = Release|x64 - {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x64.Build.0 = Release|x64 - {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x86.ActiveCfg = Release|Win32 - {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x86.Build.0 = Release|Win32 - {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Debug|x64.ActiveCfg = Debug|Win32 - {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Debug|x86.ActiveCfg = Debug|Win32 - {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Debug|x86.Build.0 = Debug|Win32 - {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Release|ARM64.ActiveCfg = Release|ARM64 - {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Release|x64.ActiveCfg = Release|Win32 - {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Release|x86.ActiveCfg = Release|Win32 - {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {A35910C5-8A89-473E-9578-E084172DD3C9} - EndGlobalSection -EndGlobal diff --git a/nuget.config b/nuget.config deleted file mode 100644 index 237c522e..00000000 --- a/nuget.config +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/signing.json b/signing.json deleted file mode 100644 index fe1c8c9b..00000000 --- a/signing.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "SignClient": { - "AzureAd": { - "AADInstance": "https://login.microsoftonline.com/", - "ClientId": "c248d68a-ba6f-4aa9-8a68-71fe872063f8", - "TenantId": "16076fdc-fcc1-4a15-b1ca-32c9a255900e" - }, - "Service": { - "Url": "https://codesign.dotnetfoundation.org/", - "ResourceId": "https://SignService/3c30251f-36f3-490b-a955-520addb85001" - } - } -} diff --git a/src/.editorconfig b/src/.editorconfig new file mode 100644 index 00000000..1d72e683 --- /dev/null +++ b/src/.editorconfig @@ -0,0 +1,37 @@ +# Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. +# +# Do NOT modify this file. Update the canonical version in Home\repo-template\src\.editorconfig +# then update all of the repos. + +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.{cs,vb}] +dotnet_sort_system_directives_first = true + +[*.cs] +csharp_indent_case_contents = true : error +csharp_indent_switch_labels = true : error +csharp_new_line_before_open_brace = all +csharp_prefer_braces = true : error +csharp_style_expression_bodied_methods = when_on_single_line : suggestion +csharp_style_expression_bodied_constructors = when_on_single_line : suggestion +csharp_style_expression_bodied_operators = when_on_single_line : suggestion +csharp_style_expression_bodied_properties = when_on_single_line : suggestion +csharp_style_expression_bodied_indexers = when_on_single_line : suggestion +csharp_style_expression_bodied_accessors = when_on_single_line : suggestion +csharp_style_var_elsewhere = true : suggestion +csharp_style_var_for_built_in_types = true : suggestion +csharp_style_var_when_type_is_apparent = true : suggestion +dotnet_style_qualification_for_event = true : error +dotnet_style_qualification_for_field = true : error +dotnet_style_qualification_for_method = true : error +dotnet_style_qualification_for_property = true : error + +[*.targets] +indent_size = 2 diff --git a/src/CustomizedNativeRecommendedRules.ruleset b/src/CustomizedNativeRecommendedRules.ruleset deleted file mode 100644 index 142b141c..00000000 --- a/src/CustomizedNativeRecommendedRules.ruleset +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/Directory.Build.props b/src/Directory.Build.props deleted file mode 100644 index fb34d54e..00000000 --- a/src/Directory.Build.props +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - Debug - false - - $(MSBuildProjectName) - $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\)) - $(BaseOutputPath)obj\$(ProjectName)\ - $(BaseOutputPath)$(Configuration)\ - - WiX Toolset Team - WiX Toolset - Copyright (c) .NET Foundation and contributors. All rights reserved. - MS-RL - WiX Toolset - - - - - diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets deleted file mode 100644 index 44701fb6..00000000 --- a/src/Directory.Build.targets +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - $(BaseOutputPath)obj\.tools - $(SigningToolFolder)\SignClient.exe - $(SigningToolFolder)\empty-filelist.txt - $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), signing.json))\signing.json - - - - false - $(OutputPath)\$(AssemblyName).xml - - - - - $(PrivateRepositoryUrl.Replace('.git','')) - - $(MSBuildProjectName).nuspec - $(MSBuildProjectDirectory) - $(NuspecProperties);Id=$(PackageId);Authors="$(Authors)";Configuration=$(Configuration);Copyright="$(Copyright)";Description="$(Description)";Title="$(Title)" - $(NuspecProperties);Version=$(NPMPackageVersion);RepositoryCommit=$(SourceRevisionId);RepositoryType=$(RepositoryType);RepositoryUrl=$(PrivateRepositoryUrl);ProjectFolder=$(MSBuildProjectDirectory)\;ProjectUrl=$(ProjectUrl) - true - snupkg - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props deleted file mode 100644 index 369c51e7..00000000 --- a/src/Directory.Packages.props +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/src/Directory.csproj.props b/src/Directory.csproj.props deleted file mode 100644 index 81d24ad1..00000000 --- a/src/Directory.csproj.props +++ /dev/null @@ -1,13 +0,0 @@ - - - - - true - true - $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)wix.snk)) - false - - diff --git a/src/Directory.vcxproj.props b/src/Directory.vcxproj.props deleted file mode 100644 index 63d73b36..00000000 --- a/src/Directory.vcxproj.props +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - Win32 - $(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\ - $(OutputPath)$(Platform)\ - - - $(Company) - $(Copyright) - - win-x86;win-x64;win-arm64 - native,Version=v0.0 - - - - $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) - - - - $(MSBuildThisFileDirectory)CustomizedNativeRecommendedRules.ruleset - - - - - $(DisableSpecificCompilerWarnings) - Level4 - $(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0600;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) - Use - precomp.h - StdCall - true - false - Guard - -YlprecompDefine - /Zc:threadSafeInit- %(AdditionalOptions) - true - - - $(ArmPreprocessorDefinitions);%(PreprocessorDefinitions) - $(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories) - - - $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories) - - - $(ProjectSubSystem) - $(ProjectModuleDefinitionFile) - $(ResourceOnlyDll) - true - $(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies) - $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories) - /IGNORE:4099 %(AdditionalOptions) - - - - - - NoExtensions - - - - - CDecl - - - - - OldStyle - true - true - - - - - Disabled - EnableFastChecks - _DEBUG;DEBUG;%(PreprocessorDefinitions) - MultiThreadedDebug - - - - - - - MultiThreadedDebugDll - - - - - MinSpace - NDEBUG;%(PreprocessorDefinitions) - true - true - MultiThreaded - - - true - true - - - - - - - MultiThreadedDll - - - - - $(LinkKeyFile) - $(LinkDelaySign) - - - diff --git a/src/NativeMultiTargeting.Build.props b/src/NativeMultiTargeting.Build.props deleted file mode 100644 index 1ff46559..00000000 --- a/src/NativeMultiTargeting.Build.props +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - $(BaseIntermediateOutputPath)$(Configuration)\$(PlatformToolset)\$(PlatformTarget)\ - $(OutputPath)$(PlatformToolset)\$(PlatformTarget)\ - - diff --git a/src/Samples/ManagedBundleRunner/BundleErrorEventArgs.cs b/src/Samples/ManagedBundleRunner/BundleErrorEventArgs.cs deleted file mode 100644 index 2c377326..00000000 --- a/src/Samples/ManagedBundleRunner/BundleErrorEventArgs.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace Wix.Samples -{ - using System; - - /// - /// Arguments provided when bundle encounters an error. - /// - [Serializable] - public class BundleErrorEventArgs : EventArgs - { - /// - /// Gets the error code. - /// - public int Code { get; set; } - - /// - /// Gets the error message. - /// - public string Message { get; set; } - - /// - /// Gets the recommended display flags for an error dialog. - /// - public int UIHint { get; set; } - - /// - /// Gets or sets the of the operation. This is passed back to the bundle. - /// - public BundleResult Result { get; set; } - } -} diff --git a/src/Samples/ManagedBundleRunner/BundleProgressEventArgs.cs b/src/Samples/ManagedBundleRunner/BundleProgressEventArgs.cs deleted file mode 100644 index ed42b5b1..00000000 --- a/src/Samples/ManagedBundleRunner/BundleProgressEventArgs.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace Wix.Samples -{ - using System; - - /// - /// Arguments provided when bundle progress is updated. - /// - [Serializable] - public class BundleProgressEventArgs : EventArgs - { - /// - /// Gets the percentage from 0 to 100 completed for a bundle. - /// - public int Progress { get; set; } - - /// - /// Gets or sets the of the operation. This is passed back to the bundle. - /// - public BundleResult Result { get; set; } - } -} diff --git a/src/Samples/ManagedBundleRunner/BundleResult.cs b/src/Samples/ManagedBundleRunner/BundleResult.cs deleted file mode 100644 index c32644f4..00000000 --- a/src/Samples/ManagedBundleRunner/BundleResult.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace Wix.Samples -{ - /// - /// Result codes. - /// - public enum BundleResult - { - Error = -1, - None, - Ok, - Cancel, - Abort, - Retry, - Ignore, - Yes, - No, - Close, - Help, - TryAgain, - Continue, - } -} diff --git a/src/Samples/ManagedBundleRunner/BundleRunner.cs b/src/Samples/ManagedBundleRunner/BundleRunner.cs deleted file mode 100644 index e2089787..00000000 --- a/src/Samples/ManagedBundleRunner/BundleRunner.cs +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace Wix.Samples -{ - using System; - using System.Diagnostics; - using System.IO.Pipes; - using System.Text; - using System.Threading; - - /// - /// Runs a bundle with provided command-line. - /// - public class BundleRunner - { - /// - /// Creates a runner for the provided bundle. - /// - /// Path to the bundle to run. - public BundleRunner(string bundle) - { - this.Path = bundle; - } - - /// - /// Fired when the bundle encounters an error. - /// - public event EventHandler Error; - - /// - /// Fired when the bundle progress is udpated. - /// - public event EventHandler Progress; - - /// - /// Gets the path to the bundle to run. - /// - public string Path { get; private set; } - - /// - /// Runs the bundle with the provided command-line. - /// - /// Optional command-line to pass to the bundle. - /// Exit code from the bundle. - public int Run(string commandLine = null) - { - WaitHandle[] waits = new WaitHandle[] { new ManualResetEvent(false), new ManualResetEvent(false) }; - int returnCode = 0; - int pid = Process.GetCurrentProcess().Id; - string pipeName = String.Concat("bpe_", pid); - string pipeSecret = Guid.NewGuid().ToString("N"); - - using (NamedPipeServerStream pipe = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1)) - { - using (Process bundleProcess = new Process()) - { - bundleProcess.StartInfo.FileName = this.Path; - bundleProcess.StartInfo.Arguments = String.Format("{0} -burn.embedded {1} {2} {3}", commandLine ?? String.Empty, pipeName, pipeSecret, pid); - bundleProcess.StartInfo.UseShellExecute = false; - bundleProcess.StartInfo.CreateNoWindow = true; - bundleProcess.Start(); - - Connect(pipe, pipeSecret, pid, bundleProcess.Id); - - PumpMessages(pipe); - - bundleProcess.WaitForExit(); - returnCode = bundleProcess.ExitCode; - } - } - - return returnCode; - } - - /// - /// Called when bundle encounters an error. - /// - /// Additional arguments for this event. - protected virtual void OnError(BundleErrorEventArgs e) - { - EventHandler handler = this.Error; - if (handler != null) - { - handler(this, e); - } - } - - /// - /// Called when bundle progress is updated. - /// - /// Additional arguments for this event. - protected virtual void OnProgress(BundleProgressEventArgs e) - { - EventHandler handler = this.Progress; - if (handler != null) - { - handler(this, e); - } - } - - private void Connect(NamedPipeServerStream pipe, string pipeSecret, int pid, int childPid) - { - pipe.WaitForConnection(); - - WriteSecretToPipe(pipe, pipeSecret); - - WriteNumberToPipe(pipe, (uint)pid); - - uint ack = ReadNumberFromPipe(pipe); - // This is not true when bundle is run under a debugger - //if (ack != childPid) - //{ - // throw new ApplicationException("Incorrect child process."); - //} - } - - private void PumpMessages(NamedPipeServerStream pipe) - { - uint messageId; - while (TryReadNumberFromPipe(pipe, out messageId)) - { - uint messageSize = ReadNumberFromPipe(pipe); - - BundleResult result = BundleResult.None; - switch (messageId) - { - case 1: //error - result = ProcessErrorMessage(pipe); - break; - - case 2: // progress - result = ProcessProgressMessage(pipe); - break; - - default: // unknown message, do nothing. - break; - } - - CompleteMessage(pipe, result); - } - } - - private BundleResult ProcessErrorMessage(NamedPipeServerStream pipe) - { - BundleErrorEventArgs e = new BundleErrorEventArgs(); - e.Code = (int)ReadNumberFromPipe(pipe); - e.Message = ReadStringFromPipe(pipe); - e.UIHint = (int)ReadNumberFromPipe(pipe); - - this.OnError(e); - - return e.Result; - } - - private BundleResult ProcessProgressMessage(NamedPipeServerStream pipe) - { - ReadNumberFromPipe(pipe); // eat the first progress number because it is always zero. - - BundleProgressEventArgs e = new BundleProgressEventArgs(); - e.Progress = (int)ReadNumberFromPipe(pipe); - - this.OnProgress(e); - - return e.Result; - } - - private void CompleteMessage(NamedPipeServerStream pipe, BundleResult result) - { - uint complete = 0xF0000002; - WriteNumberToPipe(pipe, complete); - WriteNumberToPipe(pipe, 4); // size of message data - WriteNumberToPipe(pipe, (uint)result); - } - - private uint ReadNumberFromPipe(NamedPipeServerStream pipe) - { - byte[] buffer = new byte[4]; - pipe.Read(buffer, 0, buffer.Length); - return BitConverter.ToUInt32(buffer, 0); - } - - private string ReadStringFromPipe(NamedPipeServerStream pipe) - { - uint length = ReadNumberFromPipe(pipe); - - byte[] buffer = new byte[length * 2]; - pipe.Read(buffer, 0, buffer.Length); - - return Encoding.Unicode.GetString(buffer); - } - - private bool TryReadNumberFromPipe(NamedPipeServerStream pipe, out uint value) - { - value = ReadNumberFromPipe(pipe); // reading will not block and return zero if pipe is not connected. - return pipe.IsConnected; - } - - private void WriteNumberToPipe(NamedPipeServerStream pipe, uint value) - { - byte[] buffer = BitConverter.GetBytes(value); - pipe.Write(buffer, 0, buffer.Length); - } - - private void WriteSecretToPipe(NamedPipeServerStream pipe, string secret) - { - byte[] buffer = Encoding.Unicode.GetBytes(secret); - - WriteNumberToPipe(pipe, (uint)buffer.Length); - pipe.Write(buffer, 0, buffer.Length); - } - } -} diff --git a/src/Samples/runbundle/AssemblyInfo.cs b/src/Samples/runbundle/AssemblyInfo.cs deleted file mode 100644 index 3a66d5e3..00000000 --- a/src/Samples/runbundle/AssemblyInfo.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("Executable to demonstrate Bundle Runner Sample")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyCulture("")] -[assembly: CLSCompliant(true)] -[assembly: ComVisible(false)] diff --git a/src/Samples/runbundle/Program.cs b/src/Samples/runbundle/Program.cs deleted file mode 100644 index 8edca5dc..00000000 --- a/src/Samples/runbundle/Program.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace Wix.Samples -{ - using System; - using System.Linq; - using Wix.Samples; - - /// - /// Example executable that installs then immediately uninstalls a bundle showing progress. - /// - class Program - { - static int Main(string[] args) - { - if (args.Length == 0) - { - Console.WriteLine("Must provide the path to the bundle to install then uninstall."); - return -1; - } - - BundleRunner runner = new BundleRunner(args[0]); - runner.Error += Program.OnError; - runner.Progress += Program.OnProgress; - - Console.WriteLine("Installing: {0}", runner.Path); - int exitCode = runner.Run(String.Join(" ", args.Skip(1).ToArray())); - if (0 == exitCode) - { - Console.WriteLine("\r\nUninstalling: {0}", runner.Path); - exitCode = runner.Run("-uninstall"); - } - - return exitCode; - } - - static void OnError(object sender, BundleErrorEventArgs e) - { - Console.WriteLine("error: {0}, uiHint: {1}, message: {2}", e.Code, e.UIHint, e.Message); - } - - static void OnProgress(object sender, BundleProgressEventArgs e) - { - Console.WriteLine("progresss: {0}%", e.Progress); - } - } -} diff --git a/src/burn/CustomizedNativeRecommendedRules.ruleset b/src/burn/CustomizedNativeRecommendedRules.ruleset new file mode 100644 index 00000000..142b141c --- /dev/null +++ b/src/burn/CustomizedNativeRecommendedRules.ruleset @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/burn/Directory.Build.props b/src/burn/Directory.Build.props new file mode 100644 index 00000000..fb34d54e --- /dev/null +++ b/src/burn/Directory.Build.props @@ -0,0 +1,26 @@ + + + + + + Debug + false + + $(MSBuildProjectName) + $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\)) + $(BaseOutputPath)obj\$(ProjectName)\ + $(BaseOutputPath)$(Configuration)\ + + WiX Toolset Team + WiX Toolset + Copyright (c) .NET Foundation and contributors. All rights reserved. + MS-RL + WiX Toolset + + + + + diff --git a/src/burn/Directory.Build.targets b/src/burn/Directory.Build.targets new file mode 100644 index 00000000..44701fb6 --- /dev/null +++ b/src/burn/Directory.Build.targets @@ -0,0 +1,73 @@ + + + + + + $(BaseOutputPath)obj\.tools + $(SigningToolFolder)\SignClient.exe + $(SigningToolFolder)\empty-filelist.txt + $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), signing.json))\signing.json + + + + false + $(OutputPath)\$(AssemblyName).xml + + + + + $(PrivateRepositoryUrl.Replace('.git','')) + + $(MSBuildProjectName).nuspec + $(MSBuildProjectDirectory) + $(NuspecProperties);Id=$(PackageId);Authors="$(Authors)";Configuration=$(Configuration);Copyright="$(Copyright)";Description="$(Description)";Title="$(Title)" + $(NuspecProperties);Version=$(NPMPackageVersion);RepositoryCommit=$(SourceRevisionId);RepositoryType=$(RepositoryType);RepositoryUrl=$(PrivateRepositoryUrl);ProjectFolder=$(MSBuildProjectDirectory)\;ProjectUrl=$(ProjectUrl) + true + snupkg + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/burn/Directory.Packages.props b/src/burn/Directory.Packages.props new file mode 100644 index 00000000..369c51e7 --- /dev/null +++ b/src/burn/Directory.Packages.props @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/burn/Directory.csproj.props b/src/burn/Directory.csproj.props new file mode 100644 index 00000000..81d24ad1 --- /dev/null +++ b/src/burn/Directory.csproj.props @@ -0,0 +1,13 @@ + + + + + true + true + $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)wix.snk)) + false + + diff --git a/src/burn/Directory.vcxproj.props b/src/burn/Directory.vcxproj.props new file mode 100644 index 00000000..63d73b36 --- /dev/null +++ b/src/burn/Directory.vcxproj.props @@ -0,0 +1,118 @@ + + + + + + Win32 + $(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\ + $(OutputPath)$(Platform)\ + + + $(Company) + $(Copyright) + + win-x86;win-x64;win-arm64 + native,Version=v0.0 + + + + $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) + + + + $(MSBuildThisFileDirectory)CustomizedNativeRecommendedRules.ruleset + + + + + $(DisableSpecificCompilerWarnings) + Level4 + $(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0600;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) + Use + precomp.h + StdCall + true + false + Guard + -YlprecompDefine + /Zc:threadSafeInit- %(AdditionalOptions) + true + + + $(ArmPreprocessorDefinitions);%(PreprocessorDefinitions) + $(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories) + + + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories) + + + $(ProjectSubSystem) + $(ProjectModuleDefinitionFile) + $(ResourceOnlyDll) + true + $(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies) + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories) + /IGNORE:4099 %(AdditionalOptions) + + + + + + NoExtensions + + + + + CDecl + + + + + OldStyle + true + true + + + + + Disabled + EnableFastChecks + _DEBUG;DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebug + + + + + + + MultiThreadedDebugDll + + + + + MinSpace + NDEBUG;%(PreprocessorDefinitions) + true + true + MultiThreaded + + + true + true + + + + + + + MultiThreadedDll + + + + + $(LinkKeyFile) + $(LinkDelaySign) + + + diff --git a/src/burn/NativeMultiTargeting.Build.props b/src/burn/NativeMultiTargeting.Build.props new file mode 100644 index 00000000..1ff46559 --- /dev/null +++ b/src/burn/NativeMultiTargeting.Build.props @@ -0,0 +1,10 @@ + + + + + + + $(BaseIntermediateOutputPath)$(Configuration)\$(PlatformToolset)\$(PlatformTarget)\ + $(OutputPath)$(PlatformToolset)\$(PlatformTarget)\ + + diff --git a/src/burn/README.md b/src/burn/README.md new file mode 100644 index 00000000..a564d971 --- /dev/null +++ b/src/burn/README.md @@ -0,0 +1,2 @@ +# burn +burn.lib - Burn engine diff --git a/src/burn/appveyor.cmd b/src/burn/appveyor.cmd new file mode 100644 index 00000000..a35e10d5 --- /dev/null +++ b/src/burn/appveyor.cmd @@ -0,0 +1,17 @@ +@setlocal +@pushd %~dp0 +@set _C=Release +@if /i "%1"=="debug" set _C=Debug + +nuget restore || exit /b + +msbuild -t:Test -p:Configuration=%_C% src\test\BurnUnitTest || exit /b + +msbuild -p:Configuration=%_C%;Platform=x86 || exit /b +msbuild -p:Configuration=%_C%;Platform=x64 || exit /b +msbuild -p:Configuration=%_C%;Platform=arm64 || exit /b + +msbuild -p:Configuration=%_C% -t:PackNative src\stub\stub.vcxproj || exit /b + +@popd +@endlocal \ No newline at end of file diff --git a/src/burn/appveyor.yml b/src/burn/appveyor.yml new file mode 100644 index 00000000..364569cf --- /dev/null +++ b/src/burn/appveyor.yml @@ -0,0 +1,44 @@ +# Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. +# +# Do NOT modify this file. Update the canonical version in Home\repo-template\src\appveyor.yml +# then update all of the repos. + +branches: + only: + - master + - develop + +image: Visual Studio 2019 + +version: 0.0.0.{build} +configuration: Release + +environment: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + NUGET_XMLDOC_MODE: skip + +build_script: + - appveyor.cmd + +test: off + +pull_requests: + do_not_increment_build_number: true + +nuget: + disable_publish_on_pr: true + +skip_branch_with_pr: true +skip_tags: true + +artifacts: +- path: build\Release\**\*.nupkg + name: nuget +- path: build\Release\**\*.snupkg + name: snupkg + +notifications: +- provider: Slack + incoming_webhook: + secure: p5xuu+4x2JHfwGDMDe5KcG1k7gZxqYc4jWVwvyNZv5cvkubPD2waJs5yXMAXZNN7Z63/3PWHb7q4KoY/99AjauYa1nZ4c5qYqRPFRBKTHfA= diff --git a/src/burn/burn.sln b/src/burn/burn.sln new file mode 100644 index 00000000..6a64b8f0 --- /dev/null +++ b/src/burn/burn.sln @@ -0,0 +1,61 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30711.63 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "engine", "src\engine\engine.vcxproj", "{8119537D-E1D9-6591-D51A-49768A2F9C37}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stub", "src\stub\stub.vcxproj", "{C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BurnUnitTest", "src\test\BurnUnitTest\BurnUnitTest.vcxproj", "{9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|ARM64.Build.0 = Debug|ARM64 + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|x64.ActiveCfg = Debug|x64 + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|x64.Build.0 = Debug|x64 + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|x86.ActiveCfg = Debug|Win32 + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|x86.Build.0 = Debug|Win32 + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|ARM64.ActiveCfg = Release|ARM64 + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|ARM64.Build.0 = Release|ARM64 + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|x64.ActiveCfg = Release|x64 + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|x64.Build.0 = Release|x64 + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|x86.ActiveCfg = Release|Win32 + {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|x86.Build.0 = Release|Win32 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|ARM64.Build.0 = Debug|ARM64 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|x64.ActiveCfg = Debug|x64 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|x64.Build.0 = Debug|x64 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|x86.ActiveCfg = Debug|Win32 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|x86.Build.0 = Debug|Win32 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|ARM64.ActiveCfg = Release|ARM64 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|ARM64.Build.0 = Release|ARM64 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x64.ActiveCfg = Release|x64 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x64.Build.0 = Release|x64 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x86.ActiveCfg = Release|Win32 + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x86.Build.0 = Release|Win32 + {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Debug|x64.ActiveCfg = Debug|Win32 + {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Debug|x86.ActiveCfg = Debug|Win32 + {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Debug|x86.Build.0 = Debug|Win32 + {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Release|ARM64.ActiveCfg = Release|ARM64 + {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Release|x64.ActiveCfg = Release|Win32 + {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Release|x86.ActiveCfg = Release|Win32 + {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A35910C5-8A89-473E-9578-E084172DD3C9} + EndGlobalSection +EndGlobal diff --git a/src/burn/engine/EngineForApplication.cpp b/src/burn/engine/EngineForApplication.cpp new file mode 100644 index 00000000..83d88ba1 --- /dev/null +++ b/src/burn/engine/EngineForApplication.cpp @@ -0,0 +1,529 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +static HRESULT BAEngineGetPackageCount( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_GETPACKAGECOUNT_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_GETPACKAGECOUNT_RESULTS, pResults); + + ExternalEngineGetPackageCount(pContext->pEngineState, &pResults->cPackages); + +LExit: + return hr; +} + +static HRESULT BAEngineGetVariableNumeric( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_GETVARIABLENUMERIC_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_GETVARIABLENUMERIC_RESULTS, pResults); + + hr = ExternalEngineGetVariableNumeric(pContext->pEngineState, pArgs->wzVariable, &pResults->llValue); + +LExit: + return hr; +} + +static HRESULT BAEngineGetVariableString( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_GETVARIABLESTRING_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_GETVARIABLESTRING_RESULTS, pResults); + + hr = ExternalEngineGetVariableString(pContext->pEngineState, pArgs->wzVariable, pResults->wzValue, &pResults->cchValue); + +LExit: + return hr; +} + +static HRESULT BAEngineGetVariableVersion( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_GETVARIABLEVERSION_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_GETVARIABLEVERSION_RESULTS, pResults); + + hr = ExternalEngineGetVariableVersion(pContext->pEngineState, pArgs->wzVariable, pResults->wzValue, &pResults->cchValue); + +LExit: + return hr; +} + +static HRESULT BAEngineFormatString( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_FORMATSTRING_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_FORMATSTRING_RESULTS, pResults); + + hr = ExternalEngineFormatString(pContext->pEngineState, pArgs->wzIn, pResults->wzOut, &pResults->cchOut); + +LExit: + return hr; +} + +static HRESULT BAEngineEscapeString( + __in BOOTSTRAPPER_ENGINE_CONTEXT* /*pContext*/, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_ESCAPESTRING_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_ESCAPESTRING_RESULTS, pResults); + + hr = ExternalEngineEscapeString(pArgs->wzIn, pResults->wzOut, &pResults->cchOut); + +LExit: + return hr; +} + +static HRESULT BAEngineEvaluateCondition( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_EVALUATECONDITION_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_EVALUATECONDITION_RESULTS, pResults); + + hr = ExternalEngineEvaluateCondition(pContext->pEngineState, pArgs->wzCondition, &pResults->f); + +LExit: + return hr; +} + +static HRESULT BAEngineLog( + __in BOOTSTRAPPER_ENGINE_CONTEXT* /*pContext*/, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + REPORT_LEVEL rl = REPORT_NONE; + ValidateMessageArgs(hr, pvArgs, BAENGINE_LOG_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_LOG_RESULTS, pResults); + + switch (pArgs->level) + { + case BOOTSTRAPPER_LOG_LEVEL_STANDARD: + rl = REPORT_STANDARD; + break; + + case BOOTSTRAPPER_LOG_LEVEL_VERBOSE: + rl = REPORT_VERBOSE; + break; + + case BOOTSTRAPPER_LOG_LEVEL_DEBUG: + rl = REPORT_DEBUG; + break; + + case BOOTSTRAPPER_LOG_LEVEL_ERROR: + rl = REPORT_ERROR; + break; + + default: + ExitFunction1(hr = E_INVALIDARG); + } + + hr = ExternalEngineLog(rl, pArgs->wzMessage); + ExitOnFailure(hr, "Failed to log BA message."); + +LExit: + return hr; +} + +static HRESULT BAEngineSendEmbeddedError( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_SENDEMBEDDEDERROR_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_SENDEMBEDDEDERROR_RESULTS, pResults); + + hr = ExternalEngineSendEmbeddedError(pContext->pEngineState, pArgs->dwErrorCode, pArgs->wzMessage, pArgs->dwUIHint, &pResults->nResult); + +LExit: + return hr; +} + +static HRESULT BAEngineSendEmbeddedProgress( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_SENDEMBEDDEDPROGRESS_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_SENDEMBEDDEDPROGRESS_RESULTS, pResults); + + hr = ExternalEngineSendEmbeddedProgress(pContext->pEngineState, pArgs->dwProgressPercentage, pArgs->dwOverallProgressPercentage, &pResults->nResult); + +LExit: + return hr; +} + +static HRESULT BAEngineSetUpdate( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_SETUPDATE_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_SETUPDATE_RESULTS, pResults); + + hr = ExternalEngineSetUpdate(pContext->pEngineState, pArgs->wzLocalSource, pArgs->wzDownloadSource, pArgs->qwSize, pArgs->hashType, pArgs->rgbHash, pArgs->cbHash); + +LExit: + return hr; +} + +static HRESULT BAEngineSetLocalSource( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_SETLOCALSOURCE_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_SETLOCALSOURCE_RESULTS, pResults); + + hr = ExternalEngineSetLocalSource(pContext->pEngineState, pArgs->wzPackageOrContainerId, pArgs->wzPayloadId, pArgs->wzPath); + +LExit: + return hr; +} + +static HRESULT BAEngineSetDownloadSource( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_SETDOWNLOADSOURCE_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_SETDOWNLOADSOURCE_RESULTS, pResults); + + hr = ExternalEngineSetDownloadSource(pContext->pEngineState, pArgs->wzPackageOrContainerId, pArgs->wzPayloadId, pArgs->wzUrl, pArgs->wzUser, pArgs->wzPassword); + +LExit: + return hr; +} + +static HRESULT BAEngineSetVariableNumeric( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_SETVARIABLENUMERIC_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_SETVARIABLENUMERIC_RESULTS, pResults); + + hr = ExternalEngineSetVariableNumeric(pContext->pEngineState, pArgs->wzVariable, pArgs->llValue); + +LExit: + return hr; +} + +static HRESULT BAEngineSetVariableString( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_SETVARIABLESTRING_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_SETVARIABLESTRING_RESULTS, pResults); + + hr = ExternalEngineSetVariableString(pContext->pEngineState, pArgs->wzVariable, pArgs->wzValue, pArgs->fFormatted); + +LExit: + return hr; +} + +static HRESULT BAEngineSetVariableVersion( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_SETVARIABLEVERSION_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_SETVARIABLEVERSION_RESULTS, pResults); + + hr = ExternalEngineSetVariableVersion(pContext->pEngineState, pArgs->wzVariable, pArgs->wzValue); + +LExit: + return hr; +} + +static HRESULT BAEngineCloseSplashScreen( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_CLOSESPLASHSCREEN_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_CLOSESPLASHSCREEN_RESULTS, pResults); + + ExternalEngineCloseSplashScreen(pContext->pEngineState); + +LExit: + return hr; +} + +static HRESULT BAEngineCompareVersions( + __in BOOTSTRAPPER_ENGINE_CONTEXT* /*pContext*/, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_COMPAREVERSIONS_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_COMPAREVERSIONS_RESULTS, pResults); + + hr = ExternalEngineCompareVersions(pArgs->wzVersion1, pArgs->wzVersion2, &pResults->nResult); + +LExit: + return hr; +} + +static HRESULT BAEngineDetect( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_DETECT_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_DETECT_RESULTS, pResults); + + hr = ExternalEngineDetect(pContext->dwThreadId, pArgs->hwndParent); + +LExit: + return hr; +} + +static HRESULT BAEnginePlan( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_PLAN_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_PLAN_RESULTS, pResults); + + hr = ExternalEnginePlan(pContext->dwThreadId, pArgs->action); + +LExit: + return hr; +} + +static HRESULT BAEngineElevate( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_ELEVATE_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_ELEVATE_RESULTS, pResults); + + hr = ExternalEngineElevate(pContext->pEngineState, pContext->dwThreadId, pArgs->hwndParent); + +LExit: + return hr; +} + +static HRESULT BAEngineApply( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_APPLY_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_APPLY_RESULTS, pResults); + + hr = ExternalEngineApply(pContext->dwThreadId, pArgs->hwndParent); + +LExit: + return hr; +} + +static HRESULT BAEngineQuit( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_QUIT_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_QUIT_RESULTS, pResults); + + hr = ExternalEngineQuit(pContext->dwThreadId, pArgs->dwExitCode); + +LExit: + return hr; +} + +static HRESULT BAEngineLaunchApprovedExe( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_LAUNCHAPPROVEDEXE_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_LAUNCHAPPROVEDEXE_RESULTS, pResults); + + hr = ExternalEngineLaunchApprovedExe(pContext->pEngineState, pContext->dwThreadId, pArgs->hwndParent, pArgs->wzApprovedExeForElevationId, pArgs->wzArguments, pArgs->dwWaitForInputIdleTimeout); + +LExit: + return hr; +} + +static HRESULT BAEngineSetUpdateSource( + __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BAENGINE_SETUPDATESOURCE_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BAENGINE_SETUPDATESOURCE_RESULTS, pResults); + + hr = ExternalEngineSetUpdateSource(pContext->pEngineState, pArgs->wzUrl); + +LExit: + return hr; +} + +HRESULT WINAPI EngineForApplicationProc( + __in BOOTSTRAPPER_ENGINE_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults, + __in_opt LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + BOOTSTRAPPER_ENGINE_CONTEXT* pContext = reinterpret_cast(pvContext); + + if (!pContext || !pvArgs || !pvResults) + { + ExitFunction1(hr = E_INVALIDARG); + } + + switch (message) + { + case BOOTSTRAPPER_ENGINE_MESSAGE_GETPACKAGECOUNT: + hr = BAEngineGetPackageCount(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLENUMERIC: + hr = BAEngineGetVariableNumeric(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLESTRING: + hr = BAEngineGetVariableString(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLEVERSION: + hr = BAEngineGetVariableVersion(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_FORMATSTRING: + hr = BAEngineFormatString(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_ESCAPESTRING: + hr = BAEngineEscapeString(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_EVALUATECONDITION: + hr = BAEngineEvaluateCondition(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_LOG: + hr = BAEngineLog(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_SENDEMBEDDEDERROR: + hr = BAEngineSendEmbeddedError(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_SENDEMBEDDEDPROGRESS: + hr = BAEngineSendEmbeddedProgress(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_SETUPDATE: + hr = BAEngineSetUpdate(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_SETLOCALSOURCE: + hr = BAEngineSetLocalSource(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_SETDOWNLOADSOURCE: + hr = BAEngineSetDownloadSource(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLENUMERIC: + hr = BAEngineSetVariableNumeric(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLESTRING: + hr = BAEngineSetVariableString(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLEVERSION: + hr = BAEngineSetVariableVersion(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_CLOSESPLASHSCREEN: + hr = BAEngineCloseSplashScreen(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_DETECT: + hr = BAEngineDetect(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_PLAN: + hr = BAEnginePlan(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_ELEVATE: + hr = BAEngineElevate(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_APPLY: + hr = BAEngineApply(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_QUIT: + hr = BAEngineQuit(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_LAUNCHAPPROVEDEXE: + hr = BAEngineLaunchApprovedExe(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_SETUPDATESOURCE: + hr = BAEngineSetUpdateSource(pContext, pvArgs, pvResults); + break; + case BOOTSTRAPPER_ENGINE_MESSAGE_COMPAREVERSIONS: + hr = BAEngineCompareVersions(pContext, pvArgs, pvResults); + break; + default: + hr = E_NOTIMPL; + break; + } + +LExit: + return hr; +} diff --git a/src/burn/engine/EngineForApplication.h b/src/burn/engine/EngineForApplication.h new file mode 100644 index 00000000..e5e8f6d7 --- /dev/null +++ b/src/burn/engine/EngineForApplication.h @@ -0,0 +1,44 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +// constants + +enum WM_BURN +{ + WM_BURN_FIRST = WM_APP + 0xFFF, // this enum value must always be first. + + WM_BURN_DETECT, + WM_BURN_PLAN, + WM_BURN_ELEVATE, + WM_BURN_APPLY, + WM_BURN_LAUNCH_APPROVED_EXE, + WM_BURN_QUIT, + + WM_BURN_LAST, // this enum value must always be last. +}; + +// structs + +typedef struct _BOOTSTRAPPER_ENGINE_CONTEXT +{ + BURN_ENGINE_STATE* pEngineState; + DWORD dwThreadId; +} BOOTSTRAPPER_ENGINE_CONTEXT; + +// function declarations + +HRESULT WINAPI EngineForApplicationProc( + __in BOOTSTRAPPER_ENGINE_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults, + __in_opt LPVOID pvContext + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/EngineForExtension.cpp b/src/burn/engine/EngineForExtension.cpp new file mode 100644 index 00000000..2e1c98fd --- /dev/null +++ b/src/burn/engine/EngineForExtension.cpp @@ -0,0 +1,263 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +static HRESULT BEEngineEscapeString( + __in BURN_EXTENSION_ENGINE_CONTEXT* /*pContext*/, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_RESULTS, pResults); + + hr = ExternalEngineEscapeString(pArgs->wzIn, pResults->wzOut, &pResults->cchOut); + +LExit: + return hr; +} + +static HRESULT BEEngineEvaluateCondition( + __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_RESULTS, pResults); + + hr = ExternalEngineEvaluateCondition(pContext->pEngineState, pArgs->wzCondition, &pResults->f); + +LExit: + return hr; +} + +static HRESULT BEEngineFormatString( + __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_FORMATSTRING_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_FORMATSTRING_RESULTS, pResults); + + hr = ExternalEngineFormatString(pContext->pEngineState, pArgs->wzIn, pResults->wzOut, &pResults->cchOut); + +LExit: + return hr; +} + +static HRESULT BEEngineGetVariableNumeric( + __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_RESULTS, pResults); + + hr = ExternalEngineGetVariableNumeric(pContext->pEngineState, pArgs->wzVariable, &pResults->llValue); + +LExit: + return hr; +} + +static HRESULT BEEngineGetVariableString( + __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_RESULTS, pResults); + + hr = ExternalEngineGetVariableString(pContext->pEngineState, pArgs->wzVariable, pResults->wzValue, &pResults->cchValue); + +LExit: + return hr; +} + +static HRESULT BEEngineGetVariableVersion( + __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_RESULTS, pResults); + + hr = ExternalEngineGetVariableVersion(pContext->pEngineState, pArgs->wzVariable, pResults->wzValue, &pResults->cchValue); + +LExit: + return hr; +} + +static HRESULT BEEngineLog( + __in BURN_EXTENSION_ENGINE_CONTEXT* /*pContext*/, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + REPORT_LEVEL rl = REPORT_NONE; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_LOG_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_LOG_RESULTS, pResults); + + switch (pArgs->level) + { + case BUNDLE_EXTENSION_LOG_LEVEL_STANDARD: + rl = REPORT_STANDARD; + break; + + case BUNDLE_EXTENSION_LOG_LEVEL_VERBOSE: + rl = REPORT_VERBOSE; + break; + + case BUNDLE_EXTENSION_LOG_LEVEL_DEBUG: + rl = REPORT_DEBUG; + break; + + case BUNDLE_EXTENSION_LOG_LEVEL_ERROR: + rl = REPORT_ERROR; + break; + + default: + ExitFunction1(hr = E_INVALIDARG); + } + + hr = ExternalEngineLog(rl, pArgs->wzMessage); + ExitOnFailure(hr, "Failed to log Bundle Extension message."); + +LExit: + return hr; +} + +static HRESULT BEEngineSetVariableNumeric( + __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_RESULTS, pResults); + + hr = ExternalEngineSetVariableNumeric(pContext->pEngineState, pArgs->wzVariable, pArgs->llValue); + +LExit: + return hr; +} + +static HRESULT BEEngineSetVariableString( + __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_RESULTS, pResults); + + hr = ExternalEngineSetVariableString(pContext->pEngineState, pArgs->wzVariable, pArgs->wzValue, pArgs->fFormatted); + +LExit: + return hr; +} + +static HRESULT BEEngineSetVariableVersion( + __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_RESULTS, pResults); + + hr = ExternalEngineSetVariableVersion(pContext->pEngineState, pArgs->wzVariable, pArgs->wzValue); + +LExit: + return hr; +} + +static HRESULT BEEngineCompareVersions( + __in BURN_EXTENSION_ENGINE_CONTEXT* /*pContext*/, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_ARGS, pArgs); + ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_RESULTS, pResults); + + hr = ExternalEngineCompareVersions(pArgs->wzVersion1, pArgs->wzVersion2, &pResults->nResult); + +LExit: + return hr; +} + +HRESULT WINAPI EngineForExtensionProc( + __in BUNDLE_EXTENSION_ENGINE_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults, + __in_opt LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + BURN_EXTENSION_ENGINE_CONTEXT* pContext = reinterpret_cast(pvContext); + + if (!pContext || !pvArgs || !pvResults) + { + ExitFunction1(hr = E_INVALIDARG); + } + + switch (message) + { + case BUNDLE_EXTENSION_ENGINE_MESSAGE_ESCAPESTRING: + hr = BEEngineEscapeString(pContext, pvArgs, pvResults); + break; + case BUNDLE_EXTENSION_ENGINE_MESSAGE_EVALUATECONDITION: + hr = BEEngineEvaluateCondition(pContext, pvArgs, pvResults); + break; + case BUNDLE_EXTENSION_ENGINE_MESSAGE_FORMATSTRING: + hr = BEEngineFormatString(pContext, pvArgs, pvResults); + break; + case BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLENUMERIC: + hr = BEEngineGetVariableNumeric(pContext, pvArgs, pvResults); + break; + case BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLESTRING: + hr = BEEngineGetVariableString(pContext, pvArgs, pvResults); + break; + case BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLEVERSION: + hr = BEEngineGetVariableVersion(pContext, pvArgs, pvResults); + break; + case BUNDLE_EXTENSION_ENGINE_MESSAGE_LOG: + hr = BEEngineLog(pContext, pvArgs, pvResults); + break; + case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLENUMERIC: + hr = BEEngineSetVariableNumeric(pContext, pvArgs, pvResults); + break; + case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLESTRING: + hr = BEEngineSetVariableString(pContext, pvArgs, pvResults); + break; + case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLEVERSION: + hr = BEEngineSetVariableVersion(pContext, pvArgs, pvResults); + break; + case BUNDLE_EXTENSION_ENGINE_MESSAGE_COMPAREVERSIONS: + hr = BEEngineCompareVersions(pContext, pvArgs, pvResults); + break; + default: + hr = E_NOTIMPL; + break; + } + +LExit: + return hr; +} diff --git a/src/burn/engine/EngineForExtension.h b/src/burn/engine/EngineForExtension.h new file mode 100644 index 00000000..bad5f08a --- /dev/null +++ b/src/burn/engine/EngineForExtension.h @@ -0,0 +1,27 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +// structs + +typedef struct _BURN_EXTENSION_ENGINE_CONTEXT +{ + BURN_ENGINE_STATE* pEngineState; +} BURN_EXTENSION_ENGINE_CONTEXT; + +// function declarations + +HRESULT WINAPI EngineForExtensionProc( + __in BUNDLE_EXTENSION_ENGINE_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults, + __in_opt LPVOID pvContext + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/apply.cpp b/src/burn/engine/apply.cpp new file mode 100644 index 00000000..58d41b28 --- /dev/null +++ b/src/burn/engine/apply.cpp @@ -0,0 +1,3096 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +#ifdef DEBUG + #define IgnoreRollbackError(x, f, ...) if (FAILED(x)) { TraceError(x, f, __VA_ARGS__); } +#else + #define IgnoreRollbackError(x, f, ...) +#endif + +const DWORD BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS = 2; + +enum BURN_CACHE_PROGRESS_TYPE +{ + BURN_CACHE_PROGRESS_TYPE_ACQUIRE, + BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY, + BURN_CACHE_PROGRESS_TYPE_EXTRACT, + BURN_CACHE_PROGRESS_TYPE_FINALIZE, + BURN_CACHE_PROGRESS_TYPE_HASH, + BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY, + BURN_CACHE_PROGRESS_TYPE_STAGE, +}; + +// structs + +typedef struct _BURN_CACHE_CONTEXT +{ + BURN_USER_EXPERIENCE* pUX; + BURN_VARIABLES* pVariables; + BURN_PAYLOADS* pPayloads; + HANDLE hPipe; + HANDLE hSourceEngineFile; + DWORD64 qwTotalCacheSize; + DWORD64 qwSuccessfulCacheProgress; + LPCWSTR wzLayoutDirectory; + LPWSTR* rgSearchPaths; + DWORD cSearchPaths; + DWORD cSearchPathsMax; + LPWSTR sczLastUsedFolderCandidate; +} BURN_CACHE_CONTEXT; + +typedef struct _BURN_CACHE_PROGRESS_CONTEXT +{ + BURN_CACHE_CONTEXT* pCacheContext; + BURN_CACHE_PROGRESS_TYPE type; + BURN_CONTAINER* pContainer; + BURN_PACKAGE* pPackage; + BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem; + BURN_PAYLOAD* pPayload; + + BOOL fCancel; + HRESULT hrError; +} BURN_CACHE_PROGRESS_CONTEXT; + +typedef struct _BURN_EXECUTE_CONTEXT +{ + BURN_USER_EXPERIENCE* pUX; + BOOL fRollback; + BURN_PACKAGE* pExecutingPackage; + DWORD cExecutedPackages; + DWORD cExecutePackagesTotal; + DWORD* pcOverallProgressTicks; +} BURN_EXECUTE_CONTEXT; + + +// internal function declarations +static HRESULT WINAPI AuthenticationRequired( + __in LPVOID pData, + __in HINTERNET hUrl, + __in long lHttpCode, + __out BOOL* pfRetrySend, + __out BOOL* pfRetry + ); + +static void CalculateKeepRegistration( + __in BURN_ENGINE_STATE* pEngineState, + __inout BOOL* pfKeepRegistration + ); +static HRESULT ExecuteDependentRegistrationActions( + __in HANDLE hPipe, + __in const BURN_REGISTRATION* pRegistration, + __in_ecount(cActions) const BURN_DEPENDENT_REGISTRATION_ACTION* rgActions, + __in DWORD cActions + ); +static HRESULT ApplyCachePackage( + __in BURN_CACHE_CONTEXT* pContext, + __in BURN_PACKAGE* pPackage + ); +static HRESULT ApplyExtractContainer( + __in BURN_CACHE_CONTEXT* pContext, + __in BURN_CONTAINER* pContainer + ); +static HRESULT ApplyLayoutBundle( + __in BURN_CACHE_CONTEXT* pContext, + __in BURN_PAYLOAD_GROUP* pPayloads, + __in_z LPCWSTR wzExecutableName, + __in_z LPCWSTR wzUnverifiedPath, + __in DWORD64 qwBundleSize + ); +static HRESULT ApplyLayoutContainer( + __in BURN_CACHE_CONTEXT* pContext, + __in BURN_CONTAINER* pContainer + ); +static HRESULT ApplyProcessPayload( + __in BURN_CACHE_CONTEXT* pContext, + __in_opt BURN_PACKAGE* pPackage, + __in BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem + ); +static HRESULT ApplyCacheVerifyContainerOrPayload( + __in BURN_CACHE_CONTEXT* pContext, + __in_opt BURN_CONTAINER* pContainer, + __in_opt BURN_PACKAGE* pPackage, + __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem + ); +static HRESULT ExtractContainer( + __in BURN_CACHE_CONTEXT* pContext, + __in BURN_CONTAINER* pContainer + ); +static HRESULT LayoutBundle( + __in BURN_CACHE_CONTEXT* pContext, + __in_z LPCWSTR wzExecutableName, + __in_z LPCWSTR wzUnverifiedPath, + __in DWORD64 qwBundleSize + ); +static HRESULT ApplyAcquireContainerOrPayload( + __in BURN_CACHE_CONTEXT* pContext, + __in_opt BURN_CONTAINER* pContainer, + __in_opt BURN_PACKAGE* pPackage, + __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem + ); +static HRESULT AcquireContainerOrPayload( + __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, + __out BOOL* pfRetry + ); +static BOOL IsValidLocalFile( + __in_z LPCWSTR wzFilePath, + __in DWORD64 qwFileSize, + __in BOOL fMinimumFileSize + ); +static HRESULT LayoutOrCacheContainerOrPayload( + __in BURN_CACHE_CONTEXT* pContext, + __in_opt BURN_CONTAINER* pContainer, + __in_opt BURN_PACKAGE* pPackage, + __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem, + __in DWORD cTryAgainAttempts, + __out BOOL* pfRetry + ); +static HRESULT PreparePayloadDestinationPath( + __in_z LPCWSTR wzDestinationPath + ); +static HRESULT CopyPayload( + __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, + __in HANDLE hSourceFile, + __in_z LPCWSTR wzSourcePath, + __in_z LPCWSTR wzDestinationPath + ); +static HRESULT DownloadPayload( + __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, + __in_z LPCWSTR wzDestinationPath + ); +static HRESULT CALLBACK CacheMessageHandler( + __in BURN_CACHE_MESSAGE* pMessage, + __in LPVOID pvContext + ); +static HRESULT CompleteCacheProgress( + __in BURN_CACHE_PROGRESS_CONTEXT* pContext, + __in DWORD64 qwFileSize + ); +static DWORD CALLBACK CacheProgressRoutine( + __in LARGE_INTEGER TotalFileSize, + __in LARGE_INTEGER TotalBytesTransferred, + __in LARGE_INTEGER StreamSize, + __in LARGE_INTEGER StreamBytesTransferred, + __in DWORD dwStreamNumber, + __in DWORD dwCallbackReason, + __in HANDLE hSourceFile, + __in HANDLE hDestinationFile, + __in_opt LPVOID lpData + ); +static void DoRollbackCache( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PLAN* pPlan, + __in HANDLE hPipe, + __in DWORD dwCheckpoint + ); +static HRESULT DoExecuteAction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in_opt HANDLE hCacheThread, + __in BURN_EXECUTE_CONTEXT* pContext, + __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, + __inout BURN_EXECUTE_ACTION_CHECKPOINT** ppCheckpoint, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +static HRESULT DoRollbackActions( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_CONTEXT* pContext, + __in DWORD dwCheckpoint, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +static HRESULT ExecuteExePackage( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_EXECUTE_CONTEXT* pContext, + __in BOOL fRollback, + __out BOOL* pfRetry, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +static HRESULT ExecuteMsiPackage( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_EXECUTE_CONTEXT* pContext, + __in BOOL fInsideMsiTransaction, + __in BOOL fRollback, + __out BOOL* pfRetry, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +static HRESULT ExecuteMspPackage( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_EXECUTE_CONTEXT* pContext, + __in BOOL fInsideMsiTransaction, + __in BOOL fRollback, + __out BOOL* pfRetry, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +static HRESULT ExecuteMsuPackage( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_EXECUTE_CONTEXT* pContext, + __in BOOL fRollback, + __in BOOL fStopWusaService, + __out BOOL* pfRetry, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +static HRESULT ExecutePackageProviderAction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pAction, + __in BURN_EXECUTE_CONTEXT* pContext + ); +static HRESULT ExecuteDependencyAction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pAction, + __in BURN_EXECUTE_CONTEXT* pContext + ); +static HRESULT ExecuteMsiBeginTransaction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, + __in BURN_EXECUTE_CONTEXT* pContext + ); +static HRESULT ExecuteMsiCommitTransaction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, + __in BURN_EXECUTE_CONTEXT* pContext + ); +static HRESULT ExecuteMsiRollbackTransaction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, + __in BURN_EXECUTE_CONTEXT* pContext + ); +static void ResetTransactionRegistrationState( + __in BURN_ENGINE_STATE* pEngineState, + __in BOOL fCommit + ); +static HRESULT CleanPackage( + __in HANDLE hElevatedPipe, + __in BURN_PACKAGE* pPackage + ); +static int GenericExecuteMessageHandler( + __in GENERIC_EXECUTE_MESSAGE* pMessage, + __in LPVOID pvContext + ); +static int MsiExecuteMessageHandler( + __in WIU_MSI_EXECUTE_MESSAGE* pMessage, + __in_opt LPVOID pvContext + ); +static HRESULT ReportOverallProgressTicks( + __in BURN_USER_EXPERIENCE* pUX, + __in BOOL fRollback, + __in DWORD cOverallProgressTicksTotal, + __in DWORD cOverallProgressTicks + ); +static HRESULT ExecutePackageComplete( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_VARIABLES* pVariables, + __in BURN_PACKAGE* pPackage, + __in HRESULT hrOverall, + __in HRESULT hrExecute, + __in BOOL fRollback, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart, + __out BOOL* pfRetry, + __out BOOL* pfSuspend + ); + + +// function definitions + +extern "C" void ApplyInitialize() +{ + // Prevent the system from sleeping. + ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED); +} + +extern "C" void ApplyUninitialize() +{ + ::SetThreadExecutionState(ES_CONTINUOUS); +} + +extern "C" HRESULT ApplySetVariables( + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + + hr = VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, NULL, TRUE, FALSE); + ExitOnFailure(hr, "Failed to set the bundle forced restart package built-in variable."); + +LExit: + return hr; +} + +extern "C" void ApplyReset( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PACKAGES* pPackages + ) +{ + UserExperienceExecuteReset(pUX); + + for (DWORD i = 0; i < pPackages->cPackages; ++i) + { + BURN_PACKAGE* pPackage = pPackages->rgPackages + i; + pPackage->hrCacheResult = S_OK; + pPackage->transactionRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; + } +} + +extern "C" HRESULT ApplyLock( + __in BOOL /*fPerMachine*/, + __out HANDLE* phLock + ) +{ + HRESULT hr = S_OK; + *phLock = NULL; + +#if 0 // eventually figure out the correct way to support this. In its current form, embedded bundles (including related bundles) are hosed. + DWORD er = ERROR_SUCCESS; + HANDLE hLock = NULL; + + hLock = ::CreateMutexW(NULL, TRUE, fPerMachine ? L"Global\\WixBurnExecutionLock" : L"Local\\WixBurnExecutionLock"); + ExitOnNullWithLastError(hLock, hr, "Failed to create lock."); + + er = ::GetLastError(); + if (ERROR_ALREADY_EXISTS == er) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSTALL_ALREADY_RUNNING)); + } + + *phLock = hLock; + hLock = NULL; + +LExit: + ReleaseHandle(hLock); +#endif + return hr; +} + +extern "C" HRESULT ApplyRegister( + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + LPWSTR sczEngineWorkingPath = NULL; + + hr = UserExperienceOnRegisterBegin(&pEngineState->userExperience); + ExitOnRootFailure(hr, "BA aborted register begin."); + + // If we have a resume mode that suggests the bundle is on the machine. + if (BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING < pEngineState->command.resumeType) + { + // resume previous session + if (pEngineState->registration.fPerMachine) + { + hr = ElevationSessionResume(pEngineState->companionConnection.hPipe, pEngineState->registration.sczResumeCommandLine, pEngineState->registration.fDisableResume, &pEngineState->variables); + ExitOnFailure(hr, "Failed to resume registration session in per-machine process."); + } + else + { + hr = RegistrationSessionResume(&pEngineState->registration, &pEngineState->variables); + ExitOnFailure(hr, "Failed to resume registration session."); + } + } + else // need to complete registration on the machine. + { + hr = CacheCalculateBundleWorkingPath(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &sczEngineWorkingPath); + ExitOnFailure(hr, "Failed to calculate working path for engine."); + + // begin new session + if (pEngineState->registration.fPerMachine) + { + hr = ElevationSessionBegin(pEngineState->companionConnection.hPipe, sczEngineWorkingPath, pEngineState->registration.sczResumeCommandLine, pEngineState->registration.fDisableResume, &pEngineState->variables, pEngineState->plan.dwRegistrationOperations, pEngineState->plan.dependencyRegistrationAction, pEngineState->plan.qwEstimatedSize); + ExitOnFailure(hr, "Failed to begin registration session in per-machine process."); + } + else + { + hr = RegistrationSessionBegin(sczEngineWorkingPath, &pEngineState->registration, &pEngineState->variables, pEngineState->plan.dwRegistrationOperations, pEngineState->plan.dependencyRegistrationAction, pEngineState->plan.qwEstimatedSize); + ExitOnFailure(hr, "Failed to begin registration session."); + } + } + + // Apply any registration actions. + HRESULT hrExecuteRegistration = ExecuteDependentRegistrationActions(pEngineState->companionConnection.hPipe, &pEngineState->registration, pEngineState->plan.rgRegistrationActions, pEngineState->plan.cRegistrationActions); + UNREFERENCED_PARAMETER(hrExecuteRegistration); + + // Try to save engine state. + hr = CoreSaveEngineState(pEngineState); + if (FAILED(hr)) + { + LogErrorId(hr, MSG_STATE_NOT_SAVED); + hr = S_OK; + } + +LExit: + UserExperienceOnRegisterComplete(&pEngineState->userExperience, hr); + ReleaseStr(sczEngineWorkingPath); + + return hr; +} + +extern "C" HRESULT ApplyUnregister( + __in BURN_ENGINE_STATE* pEngineState, + __in BOOL fFailedOrRollback, + __in BOOL fSuspend, + __in BOOTSTRAPPER_APPLY_RESTART restart + ) +{ + HRESULT hr = S_OK; + BURN_RESUME_MODE resumeMode = BURN_RESUME_MODE_NONE; + BOOL fKeepRegistration = pEngineState->plan.fDisallowRemoval; + + CalculateKeepRegistration(pEngineState, &fKeepRegistration); + + hr = UserExperienceOnUnregisterBegin(&pEngineState->userExperience, &fKeepRegistration); + ExitOnRootFailure(hr, "BA aborted unregister begin."); + + // Calculate the correct resume mode. If a restart has been initiated, that trumps all other + // modes. If the user chose to suspend the install then we'll use that as the resume mode. + // Barring those special cases, if it was determined that we should keep the registration then + // do that, otherwise the resume mode was initialized to none and registration will be removed. + if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart) + { + resumeMode = BURN_RESUME_MODE_REBOOT_PENDING; + } + else if (fSuspend) + { + resumeMode = BURN_RESUME_MODE_SUSPEND; + } + else if (fKeepRegistration) + { + resumeMode = BURN_RESUME_MODE_ARP; + } + + // If apply failed in any way and we're going to be keeping the bundle registered then + // execute any rollback dependency registration actions. + if (fFailedOrRollback && fKeepRegistration) + { + // Execute any rollback registration actions. + HRESULT hrRegistrationRollback = ExecuteDependentRegistrationActions(pEngineState->companionConnection.hPipe, &pEngineState->registration, pEngineState->plan.rgRollbackRegistrationActions, pEngineState->plan.cRollbackRegistrationActions); + UNREFERENCED_PARAMETER(hrRegistrationRollback); + } + + if (pEngineState->registration.fPerMachine) + { + hr = ElevationSessionEnd(pEngineState->companionConnection.hPipe, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction); + ExitOnFailure(hr, "Failed to end session in per-machine process."); + } + else + { + hr = RegistrationSessionEnd(&pEngineState->registration, &pEngineState->variables, &pEngineState->packages, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction); + ExitOnFailure(hr, "Failed to end session in per-user process."); + } + + pEngineState->resumeMode = resumeMode; + +LExit: + UserExperienceOnUnregisterComplete(&pEngineState->userExperience, hr); + + return hr; +} + +extern "C" HRESULT ApplyCache( + __in HANDLE hSourceEngineFile, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_VARIABLES* pVariables, + __in BURN_PLAN* pPlan, + __in HANDLE hPipe, + __inout DWORD* pcOverallProgressTicks, + __inout BOOL* pfRollback + ) +{ + HRESULT hr = S_OK; + DWORD dwCheckpoint = 0; + BURN_CACHE_CONTEXT cacheContext = { }; + BURN_PACKAGE* pPackage = NULL; + + *pfRollback = FALSE; + + hr = UserExperienceOnCacheBegin(pUX); + ExitOnRootFailure(hr, "BA aborted cache."); + + cacheContext.hSourceEngineFile = hSourceEngineFile; + cacheContext.pPayloads = pPlan->pPayloads; + cacheContext.pUX = pUX; + cacheContext.pVariables = pVariables; + cacheContext.qwTotalCacheSize = pPlan->qwCacheSizeTotal; + cacheContext.wzLayoutDirectory = pPlan->sczLayoutDirectory; + + hr = MemAllocArray(reinterpret_cast(&cacheContext.rgSearchPaths), sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); + ExitOnNull(cacheContext.rgSearchPaths, hr, E_OUTOFMEMORY, "Failed to allocate cache search paths array."); + + for (DWORD i = 0; i < pPlan->cCacheActions; ++i) + { + BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + i; + cacheContext.hPipe = hPipe; + pPackage = NULL; + + switch (pCacheAction->type) + { + case BURN_CACHE_ACTION_TYPE_CHECKPOINT: + dwCheckpoint = pCacheAction->checkpoint.dwId; + break; + + case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: + hr = ApplyLayoutBundle(&cacheContext, pCacheAction->bundleLayout.pPayloadGroup, pCacheAction->bundleLayout.sczExecutableName, pCacheAction->bundleLayout.sczUnverifiedPath, pCacheAction->bundleLayout.qwBundleSize); + ExitOnFailure(hr, "Failed cache action: %ls", L"layout bundle"); + + ++(*pcOverallProgressTicks); + + hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks); + LogExitOnFailure(hr, MSG_USER_CANCELED, "Cancel during cache: %ls", L"layout bundle"); + + break; + + case BURN_CACHE_ACTION_TYPE_PACKAGE: + pPackage = pCacheAction->package.pPackage; + + if (!pPackage->fPerMachine && !cacheContext.wzLayoutDirectory) + { + hr = CacheGetCompletedPath(FALSE, pPackage->sczCacheId, &pPackage->sczCacheFolder); + ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", pPackage->sczCacheId); + + cacheContext.hPipe = INVALID_HANDLE_VALUE; + } + + hr = ApplyCachePackage(&cacheContext, pPackage); + ExitOnFailure(hr, "Failed cache action: %ls", L"cache package"); + + ++(*pcOverallProgressTicks); + + hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks); + LogExitOnFailure(hr, MSG_USER_CANCELED, "Cancel during cache: %ls", L"cache package"); + + break; + + case BURN_CACHE_ACTION_TYPE_CONTAINER: + Assert(pPlan->sczLayoutDirectory); + hr = ApplyLayoutContainer(&cacheContext, pCacheAction->container.pContainer); + ExitOnFailure(hr, "Failed cache action: %ls", L"layout container"); + + break; + + case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT: + if (!::SetEvent(pCacheAction->syncpoint.hEvent)) + { + ExitWithLastError(hr, "Failed to set syncpoint event."); + } + break; + + default: + AssertSz(FALSE, "Unknown cache action."); + break; + } + } + +LExit: + if (FAILED(hr)) + { + DoRollbackCache(pUX, pPlan, hPipe, dwCheckpoint); + *pfRollback = TRUE; + } + + // Clean up any remanents in the cache. + if (INVALID_HANDLE_VALUE != hPipe) + { + ElevationCacheCleanup(hPipe); + } + + CacheCleanup(FALSE, pPlan->wzBundleId); + + for (DWORD i = 0; i < cacheContext.cSearchPathsMax; ++i) + { + ReleaseNullStr(cacheContext.rgSearchPaths[i]); + } + ReleaseMem(cacheContext.rgSearchPaths); + ReleaseStr(cacheContext.sczLastUsedFolderCandidate); + + UserExperienceOnCacheComplete(pUX, hr); + return hr; +} + +extern "C" HRESULT ApplyExecute( + __in BURN_ENGINE_STATE* pEngineState, + __in_opt HANDLE hCacheThread, + __inout DWORD* pcOverallProgressTicks, + __out BOOL* pfRollback, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + HRESULT hrRollback = S_OK; + BURN_EXECUTE_ACTION_CHECKPOINT* pCheckpoint = NULL; + BURN_EXECUTE_CONTEXT context = { }; + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; + BOOL fSeekNextRollbackBoundary = FALSE; + + context.pUX = &pEngineState->userExperience; + context.cExecutePackagesTotal = pEngineState->plan.cExecutePackagesTotal; + context.pcOverallProgressTicks = pcOverallProgressTicks; + + *pfRollback = FALSE; + *pfSuspend = FALSE; + + // Send execute begin to BA. + hr = UserExperienceOnExecuteBegin(&pEngineState->userExperience, pEngineState->plan.cExecutePackagesTotal); + ExitOnRootFailure(hr, "BA aborted execute begin."); + + // Do execute actions. + for (DWORD i = 0; i < pEngineState->plan.cExecuteActions; ++i) + { + BURN_EXECUTE_ACTION* pExecuteAction = &pEngineState->plan.rgExecuteActions[i]; + if (pExecuteAction->fDeleted) + { + continue; + } + + // If we are seeking the next rollback boundary, skip if this action wasn't it. + if (fSeekNextRollbackBoundary) + { + if (BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY == pExecuteAction->type) + { + continue; + } + else + { + fSeekNextRollbackBoundary = FALSE; + } + } + + // Execute the action. + hr = DoExecuteAction(pEngineState, pExecuteAction, hCacheThread, &context, &pRollbackBoundary, &pCheckpoint, pfSuspend, pRestart); + + if (*pfSuspend || BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart) + { + if (pCheckpoint && pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction) + { + hr = E_INVALIDSTATE; + LogId(REPORT_ERROR, MSG_RESTART_REQUEST_DURING_MSI_TRANSACTION, pCheckpoint->pActiveRollbackBoundary->sczId); + } + else + { + ExitFunction(); + } + } + + if (FAILED(hr)) + { + // If rollback is disabled, keep what we have and always end execution here. + if (pEngineState->plan.fDisableRollback) + { + LogId(REPORT_WARNING, MSG_PLAN_ROLLBACK_DISABLED); + + if (pCheckpoint && pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction) + { + hrRollback = ExecuteMsiCommitTransaction(pEngineState, pCheckpoint->pActiveRollbackBoundary, &context); + IgnoreRollbackError(hrRollback, "Failed commit transaction from disable rollback"); + } + + *pfRollback = TRUE; + break; + } + + if (pCheckpoint) + { + // If inside a MSI transaction, roll it back. + if (pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction) + { + hrRollback = ExecuteMsiRollbackTransaction(pEngineState, pCheckpoint->pActiveRollbackBoundary, &context); + IgnoreRollbackError(hrRollback, "Failed rolling back transaction"); + } + + // The action failed, roll back to previous rollback boundary. + hrRollback = DoRollbackActions(pEngineState, &context, pCheckpoint->dwId, pRestart); + IgnoreRollbackError(hrRollback, "Failed rollback actions"); + } + + // If the rollback boundary is vital, end execution here. + if (pRollbackBoundary && pRollbackBoundary->fVital) + { + *pfRollback = TRUE; + break; + } + + // Move forward to next rollback boundary. + fSeekNextRollbackBoundary = TRUE; + } + } + +LExit: + // Send execute complete to BA. + UserExperienceOnExecuteComplete(&pEngineState->userExperience, hr); + + return hr; +} + +extern "C" void ApplyClean( + __in BURN_USER_EXPERIENCE* /*pUX*/, + __in BURN_PLAN* pPlan, + __in HANDLE hPipe + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < pPlan->cCleanActions; ++i) + { + BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + i; + BURN_PACKAGE* pPackage = pCleanAction->pPackage; + + hr = CleanPackage(hPipe, pPackage); + } +} + + +// internal helper functions + +static void CalculateKeepRegistration( + __in BURN_ENGINE_STATE* pEngineState, + __inout BOOL* pfKeepRegistration + ) +{ + LogId(REPORT_STANDARD, MSG_POST_APPLY_CALCULATE_REGISTRATION); + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + + if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + MspEngineFinalizeInstallRegistrationState(pPackage); + } + + LogId(REPORT_STANDARD, MSG_POST_APPLY_PACKAGE, pPackage->sczId, LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->installRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->cacheRegistrationState)); + + if (!pPackage->fCanAffectRegistration) + { + continue; + } + + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState || + BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState) + { + *pfKeepRegistration = TRUE; + } + } +} + +static HRESULT ExecuteDependentRegistrationActions( + __in HANDLE hPipe, + __in const BURN_REGISTRATION* pRegistration, + __in_ecount(cActions) const BURN_DEPENDENT_REGISTRATION_ACTION* rgActions, + __in DWORD cActions + ) +{ + HRESULT hr = S_OK; + + for (DWORD iAction = 0; iAction < cActions; ++iAction) + { + const BURN_DEPENDENT_REGISTRATION_ACTION* pAction = rgActions + iAction; + + if (pRegistration->fPerMachine) + { + hr = ElevationProcessDependentRegistration(hPipe, pAction); + ExitOnFailure(hr, "Failed to execute dependent registration action."); + } + else + { + hr = DependencyProcessDependentRegistration(pRegistration, pAction); + ExitOnFailure(hr, "Failed to process dependency registration action."); + } + } + +LExit: + return hr; +} + +static HRESULT ApplyCachePackage( + __in BURN_CACHE_CONTEXT* pContext, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + BOOL fCanceledBegin = FALSE; + BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION cachePackageCompleteAction = BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE; + + for (;;) + { + fCanceledBegin = FALSE; + + hr = UserExperienceOnCachePackageBegin(pContext->pUX, pPackage->sczId, pPackage->payloads.cItems, pPackage->payloads.qwTotalSize); + if (FAILED(hr)) + { + fCanceledBegin = TRUE; + } + else + { + for (DWORD i = 0; i < pPackage->payloads.cItems; ++i) + { + BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem = pPackage->payloads.rgItems + i; + + hr = ApplyProcessPayload(pContext, pPackage, pPayloadGroupItem); + if (FAILED(hr)) + { + break; + } + } + } + + pPackage->hrCacheResult = hr; + cachePackageCompleteAction = SUCCEEDED(hr) || pPackage->fVital || fCanceledBegin ? BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE; + UserExperienceOnCachePackageComplete(pContext->pUX, pPackage->sczId, hr, &cachePackageCompleteAction); + + if (SUCCEEDED(hr)) + { + break; + } + + if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_RETRY == cachePackageCompleteAction) + { + for (DWORD i = 0; i < pPackage->payloads.cItems; ++i) + { + BURN_PAYLOAD_GROUP_ITEM* pItem = pPackage->payloads.rgItems + i; + if (pItem->fCached) + { + pItem->pPayload->cRemainingInstances += 1; + pItem->fCached = FALSE; + } + + if (pItem->qwCommittedCacheProgress) + { + pContext->qwSuccessfulCacheProgress -= pItem->qwCommittedCacheProgress; + pItem->qwCommittedCacheProgress = 0; + } + } + + LogErrorId(hr, MSG_CACHE_RETRYING_PACKAGE, pPackage->sczId, NULL, NULL); + + continue; + } + else if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE == cachePackageCompleteAction && !pPackage->fVital) // ignore non-vital download failures. + { + LogId(REPORT_STANDARD, MSG_CACHE_CONTINUING_NONVITAL_PACKAGE, pPackage->sczId, hr); + hr = S_OK; + } + else if (fCanceledBegin) + { + LogExitOnFailure(hr, MSG_USER_CANCELED, "Cancel during cache: %ls: %ls", L"begin cache package", pPackage->sczId); + } + + break; + } + +LExit: + return hr; +} + +static HRESULT ApplyExtractContainer( + __in BURN_CACHE_CONTEXT* pContext, + __in BURN_CONTAINER* pContainer + ) +{ + HRESULT hr = S_OK; + + if (pContainer->qwCommittedCacheProgress) + { + pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedCacheProgress; + pContainer->qwCommittedCacheProgress = 0; + } + + if (pContainer->qwCommittedExtractProgress) + { + pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedExtractProgress; + pContainer->qwCommittedExtractProgress = 0; + } + + if (!pContainer->fActuallyAttached) + { + hr = ApplyAcquireContainerOrPayload(pContext, pContainer, NULL, NULL); + LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_CONTAINER, "Failed to acquire container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath); + } + + hr = ExtractContainer(pContext, pContainer); + LogExitOnFailure(hr, MSG_FAILED_EXTRACT_CONTAINER, "Failed to extract payloads from container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath); + + if (pContext->sczLastUsedFolderCandidate) + { + // We successfully copied from a source location, set that as the last used source. + CacheSetLastUsedSource(pContext->pVariables, pContext->sczLastUsedFolderCandidate, pContainer->sczFilePath); + } + + if (pContainer->qwExtractSizeTotal < pContainer->qwCommittedExtractProgress) + { + AssertSz(FALSE, "Container extracted more than planned."); + pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedExtractProgress; + pContext->qwSuccessfulCacheProgress += pContainer->qwExtractSizeTotal; + } + else + { + pContext->qwSuccessfulCacheProgress += pContainer->qwExtractSizeTotal - pContainer->qwCommittedExtractProgress; + } + + pContainer->qwCommittedExtractProgress = pContainer->qwExtractSizeTotal; + +LExit: + ReleaseNullStr(pContext->sczLastUsedFolderCandidate); + + return hr; +} + +static HRESULT ApplyLayoutBundle( + __in BURN_CACHE_CONTEXT* pContext, + __in BURN_PAYLOAD_GROUP* pPayloads, + __in_z LPCWSTR wzExecutableName, + __in_z LPCWSTR wzUnverifiedPath, + __in DWORD64 qwBundleSize + ) +{ + HRESULT hr = S_OK; + + hr = LayoutBundle(pContext, wzExecutableName, wzUnverifiedPath, qwBundleSize); + ExitOnFailure(hr, "Failed to layout bundle."); + + for (DWORD i = 0; i < pPayloads->cItems; ++i) + { + BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem = pPayloads->rgItems + i; + + hr = ApplyProcessPayload(pContext, NULL, pPayloadGroupItem); + ExitOnFailure(hr, "Failed to layout bundle payload: %ls", pPayloadGroupItem->pPayload->sczKey); + } + +LExit: + return hr; +} + +static HRESULT ApplyLayoutContainer( + __in BURN_CACHE_CONTEXT* pContext, + __in BURN_CONTAINER* pContainer + ) +{ + HRESULT hr = S_OK; + DWORD cTryAgainAttempts = 0; + BOOL fRetry = FALSE; + + Assert(!pContainer->fAttached); + + hr = ApplyCacheVerifyContainerOrPayload(pContext, pContainer, NULL, NULL); + if (SUCCEEDED(hr)) + { + ExitFunction(); + } + + for (;;) + { + fRetry = FALSE; + + hr = ApplyAcquireContainerOrPayload(pContext, pContainer, NULL, NULL); + LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_CONTAINER, "Failed to acquire container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath); + + hr = LayoutOrCacheContainerOrPayload(pContext, pContainer, NULL, NULL, cTryAgainAttempts, &fRetry); + if (SUCCEEDED(hr)) + { + break; + } + else + { + LogErrorId(hr, MSG_FAILED_LAYOUT_CONTAINER, pContainer->sczId, pContext->wzLayoutDirectory, pContainer->sczUnverifiedPath); + + if (!fRetry) + { + ExitFunction(); + } + + ++cTryAgainAttempts; + pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedCacheProgress; + pContainer->qwCommittedCacheProgress = 0; + ReleaseNullStr(pContext->sczLastUsedFolderCandidate); + LogErrorId(hr, MSG_CACHE_RETRYING_CONTAINER, pContainer->sczId, NULL, NULL); + } + } + +LExit: + ReleaseNullStr(pContext->sczLastUsedFolderCandidate); + + return hr; +} + +static HRESULT ApplyProcessPayload( + __in BURN_CACHE_CONTEXT* pContext, + __in_opt BURN_PACKAGE* pPackage, + __in BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem + ) +{ + HRESULT hr = S_OK; + DWORD cTryAgainAttempts = 0; + BOOL fRetry = FALSE; + BURN_PAYLOAD* pPayload = pPayloadGroupItem->pPayload; + + Assert(pContext->pPayloads && pPackage || pContext->wzLayoutDirectory); + + if (pPayload->pContainer && pContext->wzLayoutDirectory) + { + ExitFunction(); + } + + hr = ApplyCacheVerifyContainerOrPayload(pContext, NULL, pPackage, pPayloadGroupItem); + if (SUCCEEDED(hr)) + { + ExitFunction(); + } + + for (;;) + { + fRetry = FALSE; + + hr = ApplyAcquireContainerOrPayload(pContext, NULL, pPackage, pPayloadGroupItem); + LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_PAYLOAD, "Failed to acquire payload: %ls to working path: %ls", pPayload->sczKey, pPayload->sczUnverifiedPath); + + hr = LayoutOrCacheContainerOrPayload(pContext, NULL, pPackage, pPayloadGroupItem, cTryAgainAttempts, &fRetry); + if (SUCCEEDED(hr)) + { + break; + } + else + { + LogErrorId(hr, pContext->wzLayoutDirectory ? MSG_FAILED_LAYOUT_PAYLOAD : MSG_FAILED_CACHE_PAYLOAD, pPayload->sczKey, pContext->wzLayoutDirectory, pPayload->sczUnverifiedPath); + + if (!fRetry) + { + ExitFunction(); + } + + ++cTryAgainAttempts; + pContext->qwSuccessfulCacheProgress -= pPayloadGroupItem->qwCommittedCacheProgress; + pPayloadGroupItem->qwCommittedCacheProgress = 0; + ReleaseNullStr(pContext->sczLastUsedFolderCandidate); + LogErrorId(hr, MSG_CACHE_RETRYING_PAYLOAD, pPayload->sczKey, NULL, NULL); + } + } + +LExit: + ReleaseNullStr(pContext->sczLastUsedFolderCandidate); + + return hr; +} + +static HRESULT ApplyCacheVerifyContainerOrPayload( + __in BURN_CACHE_CONTEXT* pContext, + __in_opt BURN_CONTAINER* pContainer, + __in_opt BURN_PACKAGE* pPackage, + __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem + ) +{ + AssertSz(pContainer || pPayloadGroupItem, "Must provide a container or a payload."); + + HRESULT hr = S_OK; + BURN_CACHE_PROGRESS_CONTEXT progress = { }; + + progress.pCacheContext = pContext; + progress.pContainer = pContainer; + progress.pPackage = pPackage; + progress.pPayloadGroupItem = pPayloadGroupItem; + + if (pContainer) + { + hr = CacheVerifyContainer(pContainer, pContext->wzLayoutDirectory, CacheMessageHandler, CacheProgressRoutine, &progress); + } + else if (!pContext->wzLayoutDirectory && INVALID_HANDLE_VALUE != pContext->hPipe) + { + hr = ElevationCacheVerifyPayload(pContext->hPipe, pPackage, pPayloadGroupItem->pPayload, CacheMessageHandler, CacheProgressRoutine, &progress); + } + else + { + hr = CacheVerifyPayload(pPayloadGroupItem->pPayload, pContext->wzLayoutDirectory ? pContext->wzLayoutDirectory : pPackage->sczCacheFolder, CacheMessageHandler, CacheProgressRoutine, &progress); + } + + return hr; +} + +static HRESULT ExtractContainer( + __in BURN_CACHE_CONTEXT* pContext, + __in BURN_CONTAINER* pContainer + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER_CONTEXT context = { }; + HANDLE hContainerHandle = INVALID_HANDLE_VALUE; + LPWSTR sczStreamName = NULL; + BURN_PAYLOAD* pExtract = NULL; + BURN_CACHE_PROGRESS_CONTEXT progress = { }; + + progress.pCacheContext = pContext; + progress.pContainer = pContainer; + progress.type = BURN_CACHE_PROGRESS_TYPE_EXTRACT; + + // If the container is actually attached, then it was planned to be acquired through hSourceEngineFile. + if (pContainer->fActuallyAttached) + { + hContainerHandle = pContext->hSourceEngineFile; + } + + hr = ContainerOpen(&context, pContainer, hContainerHandle, pContainer->sczUnverifiedPath); + ExitOnFailure(hr, "Failed to open container: %ls.", pContainer->sczId); + + while (S_OK == (hr = ContainerNextStream(&context, &sczStreamName))) + { + BOOL fExtracted = FALSE; + + hr = PayloadFindEmbeddedBySourcePath(pContext->pPayloads, sczStreamName, &pExtract); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to find embedded payload by source path: %ls container: %ls", sczStreamName, pContainer->sczId); + + // Skip payloads that weren't planned or have already been cached. + if (pExtract->sczUnverifiedPath && pExtract->cRemainingInstances) + { + progress.pPayload = pExtract; + + hr = PreparePayloadDestinationPath(pExtract->sczUnverifiedPath); + ExitOnFailure(hr, "Failed to prepare payload destination path: %ls", pExtract->sczUnverifiedPath); + + hr = UserExperienceOnCachePayloadExtractBegin(pContext->pUX, pContainer->sczId, pExtract->sczKey); + if (FAILED(hr)) + { + UserExperienceOnCachePayloadExtractComplete(pContext->pUX, pContainer->sczId, pExtract->sczKey, hr); + ExitOnRootFailure(hr, "BA aborted cache payload extract begin."); + } + + // TODO: Send progress when extracting stream to file. + hr = ContainerStreamToFile(&context, pExtract->sczUnverifiedPath); + // Error handling happens after sending complete message to BA. + + // If succeeded, send 100% complete here to make sure progress was sent to the BA. + if (SUCCEEDED(hr)) + { + hr = CompleteCacheProgress(&progress, pExtract->qwFileSize); + } + + UserExperienceOnCachePayloadExtractComplete(pContext->pUX, pContainer->sczId, pExtract->sczKey, hr); + ExitOnFailure(hr, "Failed to extract payload: %ls from container: %ls", sczStreamName, pContainer->sczId); + + fExtracted = TRUE; + } + } + + if (!fExtracted) + { + hr = ContainerSkipStream(&context); + ExitOnFailure(hr, "Failed to skip the extraction of payload: %ls from container: %ls", sczStreamName, pContainer->sczId); + } + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to extract all payloads from container: %ls", pContainer->sczId); + +LExit: + ReleaseStr(sczStreamName); + ContainerClose(&context); + + return hr; +} + +static HRESULT LayoutBundle( + __in BURN_CACHE_CONTEXT* pContext, + __in_z LPCWSTR wzExecutableName, + __in_z LPCWSTR wzUnverifiedPath, + __in DWORD64 qwBundleSize + ) +{ + HRESULT hr = S_OK; + LPWSTR sczBundlePath = NULL; + LPWSTR sczBundleDownloadUrl = NULL; + LPWSTR sczDestinationPath = NULL; + int nEquivalentPaths = 0; + BOOTSTRAPPER_CACHE_OPERATION cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE; + BURN_CACHE_PROGRESS_CONTEXT progress = { }; + BOOL fRetry = FALSE; + BOOL fRetryAcquire = FALSE; + BOOL fCanceledBegin = FALSE; + + progress.pCacheContext = pContext; + + hr = VariableGetString(pContext->pVariables, BURN_BUNDLE_SOURCE_PROCESS_PATH, &sczBundlePath); + if (FAILED(hr)) + { + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get path to bundle source process path to layout."); + } + + hr = PathForCurrentProcess(&sczBundlePath, NULL); + ExitOnFailure(hr, "Failed to get path to bundle to layout."); + } + + hr = PathConcat(pContext->wzLayoutDirectory, wzExecutableName, &sczDestinationPath); + ExitOnFailure(hr, "Failed to concat layout path for bundle."); + + // If the destination path is the currently running bundle, bail. + hr = PathCompare(sczBundlePath, sczDestinationPath, &nEquivalentPaths); + ExitOnFailure(hr, "Failed to determine if layout bundle path was equivalent with current process path."); + + if (CSTR_EQUAL == nEquivalentPaths && FileExistsEx(sczDestinationPath, NULL)) + { + hr = UserExperienceOnCacheContainerOrPayloadVerifyBegin(pContext->pUX, NULL, NULL); + if (FAILED(hr)) + { + UserExperienceOnCacheContainerOrPayloadVerifyComplete(pContext->pUX, NULL, NULL, hr); + ExitOnRootFailure(hr, "BA aborted cache payload verify begin."); + } + + progress.type = BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY; + hr = CompleteCacheProgress(&progress, qwBundleSize); + + UserExperienceOnCacheContainerOrPayloadVerifyComplete(pContext->pUX, NULL, NULL, hr); + + ExitFunction(); + } + + do + { + hr = S_OK; + fRetry = FALSE; + progress.type = BURN_CACHE_PROGRESS_TYPE_ACQUIRE; + + for (;;) + { + fRetryAcquire = FALSE; + progress.fCancel = FALSE; + fCanceledBegin = FALSE; + + hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, NULL, NULL, &sczBundlePath, &sczBundleDownloadUrl, NULL, &cacheOperation); + + if (FAILED(hr)) + { + fCanceledBegin = TRUE; + } + else + { + hr = CopyPayload(&progress, pContext->hSourceEngineFile, sczBundlePath, wzUnverifiedPath); + // Error handling happens after sending complete message to BA. + + // If succeeded, send 100% complete here to make sure progress was sent to the BA. + if (SUCCEEDED(hr)) + { + hr = CompleteCacheProgress(&progress, qwBundleSize); + } + } + + UserExperienceOnCacheAcquireComplete(pContext->pUX, NULL, NULL, hr, &fRetryAcquire); + if (fRetryAcquire) + { + continue; + } + else if (fCanceledBegin) + { + ExitOnRootFailure(hr, "BA aborted cache acquire begin."); + } + + ExitOnFailure(hr, "Failed to copy bundle from: '%ls' to: '%ls'", sczBundlePath, wzUnverifiedPath); + break; + } + + do + { + fCanceledBegin = FALSE; + + hr = UserExperienceOnCacheVerifyBegin(pContext->pUX, NULL, NULL); + + if (FAILED(hr)) + { + fCanceledBegin = TRUE; + } + else + { + hr = CacheLayoutBundle(wzExecutableName, pContext->wzLayoutDirectory, wzUnverifiedPath, qwBundleSize, CacheMessageHandler, CacheProgressRoutine, &progress); + } + + BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE; + UserExperienceOnCacheVerifyComplete(pContext->pUX, NULL, NULL, hr, &action); + if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action) + { + hr = S_FALSE; // retry verify. + } + else if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION == action) + { + fRetry = TRUE; // go back and retry acquire. + } + else if (fCanceledBegin) + { + ExitOnRootFailure(hr, "BA aborted cache verify begin."); + } + } while (S_FALSE == hr); + + if (fRetry) + { + pContext->qwSuccessfulCacheProgress -= qwBundleSize; // Acquire + } + } while (fRetry); + LogExitOnFailure(hr, MSG_FAILED_LAYOUT_BUNDLE, "Failed to layout bundle: %ls to layout directory: %ls", sczBundlePath, pContext->wzLayoutDirectory); + +LExit: + ReleaseStr(sczDestinationPath); + ReleaseStr(sczBundleDownloadUrl); + ReleaseStr(sczBundlePath); + + return hr; +} + +static HRESULT ApplyAcquireContainerOrPayload( + __in BURN_CACHE_CONTEXT* pContext, + __in_opt BURN_CONTAINER* pContainer, + __in_opt BURN_PACKAGE* pPackage, + __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem + ) +{ + AssertSz(pContainer || pPayloadGroupItem, "Must provide a container or a payload."); + + HRESULT hr = S_OK; + BURN_CACHE_PROGRESS_CONTEXT progress = { }; + BOOL fRetry = FALSE; + + progress.pCacheContext = pContext; + progress.type = BURN_CACHE_PROGRESS_TYPE_ACQUIRE; + progress.pContainer = pContainer; + progress.pPackage = pPackage; + progress.pPayloadGroupItem = pPayloadGroupItem; + + do + { + hr = AcquireContainerOrPayload(&progress, &fRetry); + + if (fRetry) + { + LogErrorId(hr, pContainer ? MSG_APPLY_RETRYING_ACQUIRE_CONTAINER : MSG_APPLY_RETRYING_ACQUIRE_PAYLOAD, pContainer ? pContainer->sczId : pPayloadGroupItem->pPayload->sczKey, NULL, NULL); + hr = S_OK; + } + + ExitOnFailure(hr, "Failed to acquire %hs: %ls", pContainer ? "container" : "payload", pContainer ? pContainer->sczId : pPayloadGroupItem->pPayload->sczKey); + } while (fRetry); + +LExit: + return hr; +} + +static HRESULT AcquireContainerOrPayload( + __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, + __out BOOL* pfRetry + ) +{ + BURN_CACHE_CONTEXT* pContext = pProgress->pCacheContext; + BURN_CONTAINER* pContainer = pProgress->pContainer; + BURN_PACKAGE* pPackage = pProgress->pPackage; + BURN_PAYLOAD* pPayload = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload : NULL; + AssertSz(pContainer || pPayload, "Must provide a container or a payload."); + + HRESULT hr = S_OK; + int nEquivalentPaths = 0; + LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : NULL; + LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : NULL; + LPCWSTR wzPayloadContainerId = pPayload && pPayload->pContainer ? pPayload->pContainer->sczId : NULL; + LPCWSTR wzDestinationPath = pContainer ? pContainer->sczUnverifiedPath: pPayload->sczUnverifiedPath; + LPCWSTR wzRelativePath = pContainer ? pContainer->sczFilePath : pPayload->sczFilePath; + DWORD dwChosenSearchPath = 0; + DWORD dwDestinationSearchPath = 0; + BOOTSTRAPPER_CACHE_OPERATION cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE; + BOOTSTRAPPER_CACHE_RESOLVE_OPERATION resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_NONE; + LPWSTR* pwzDownloadUrl = pContainer ? &pContainer->downloadSource.sczUrl : &pPayload->downloadSource.sczUrl; + LPWSTR* pwzSourcePath = pContainer ? &pContainer->sczSourcePath : &pPayload->sczSourcePath; + BOOL fFoundLocal = FALSE; + BOOL fPreferExtract = FALSE; + DWORD64 qwFileSize = 0; + BOOL fMinimumFileSize = FALSE; + + if (pContainer) + { + if (pContainer->fAttached) + { + fMinimumFileSize = TRUE; + qwFileSize = pContainer->qwAttachedOffset + pContainer->qwFileSize; + } + else if (pContainer->pbHash && pContext->wzLayoutDirectory) + { + qwFileSize = pContainer->qwFileSize; + } + } + else if (pPayload->pbHash) + { + qwFileSize = pPayload->qwFileSize; + } + + pContext->cSearchPaths = 0; + *pfRetry = FALSE; + pProgress->fCancel = FALSE; + + hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId, pwzSourcePath, pwzDownloadUrl, wzPayloadContainerId, &cacheOperation); + ExitOnRootFailure(hr, "BA aborted cache acquire begin."); + + // Skip the Resolving event and probing local paths if the BA already knew it wanted to download or extract. + if (BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD != cacheOperation && + BOOTSTRAPPER_CACHE_OPERATION_EXTRACT != cacheOperation) + { + do + { + fFoundLocal = FALSE; + fPreferExtract = FALSE; + resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_NONE; + dwChosenSearchPath = 0; + dwDestinationSearchPath = 0; + + hr = CacheGetLocalSourcePaths(wzRelativePath, *pwzSourcePath, wzDestinationPath, pContext->wzLayoutDirectory, pContext->pVariables, &pContext->rgSearchPaths, &pContext->cSearchPaths, &dwChosenSearchPath, &dwDestinationSearchPath); + ExitOnFailure(hr, "Failed to search local source."); + + if (wzPayloadContainerId) + { + // When a payload comes from a container, the container has the highest chance of being correct. + // But we want to avoid extracting the container multiple times. + // So only consider the destination path, which means the container was already extracted. + if (IsValidLocalFile(pContext->rgSearchPaths[dwDestinationSearchPath], qwFileSize, fMinimumFileSize)) + { + fFoundLocal = TRUE; + dwChosenSearchPath = dwDestinationSearchPath; + } + else // don't prefer the container if extracting it already failed. + { + fPreferExtract = SUCCEEDED(pPayload->pContainer->hrExtract); + } + } + + if (!fFoundLocal) + { + for (DWORD i = 0; i < pContext->cSearchPaths; ++i) + { + // If the file exists locally with the correct size, choose it. + if (IsValidLocalFile(pContext->rgSearchPaths[i], qwFileSize, fMinimumFileSize)) + { + dwChosenSearchPath = i; + + fFoundLocal = TRUE; + break; + } + } + } + + if (BOOTSTRAPPER_CACHE_OPERATION_COPY == cacheOperation) + { + if (fFoundLocal) + { + resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_LOCAL; + } + } + else + { + if (fPreferExtract) // the file comes from a container which hasn't been extracted yet, so extract it. + { + resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER; + } + else if (fFoundLocal) // the file exists locally, so copy it. + { + resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_LOCAL; + } + else if (*pwzDownloadUrl && **pwzDownloadUrl) + { + resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_DOWNLOAD; + } + else if (wzPayloadContainerId) + { + resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER; + } + } + + // Let the BA have a chance to override the source. + hr = UserExperienceOnCacheAcquireResolving(pContext->pUX, wzPackageOrContainerId, wzPayloadId, pContext->rgSearchPaths, pContext->cSearchPaths, fFoundLocal, &dwChosenSearchPath, pwzDownloadUrl, wzPayloadContainerId, &resolveOperation); + ExitOnRootFailure(hr, "BA aborted cache acquire resolving."); + + switch (resolveOperation) + { + case BOOTSTRAPPER_CACHE_RESOLVE_LOCAL: + cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_COPY; + break; + case BOOTSTRAPPER_CACHE_RESOLVE_DOWNLOAD: + cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD; + break; + case BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER: + cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_EXTRACT; + break; + case BOOTSTRAPPER_CACHE_RESOLVE_RETRY: + pContext->cSearchPathsMax = max(pContext->cSearchPaths, pContext->cSearchPathsMax); + break; + } + } while (BOOTSTRAPPER_CACHE_RESOLVE_RETRY == resolveOperation); + } + + switch (cacheOperation) + { + case BOOTSTRAPPER_CACHE_OPERATION_COPY: + // If the source path and destination path are different, do the copy (otherwise there's no point). + hr = PathCompare(pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath, &nEquivalentPaths); + ExitOnFailure(hr, "Failed to determine if payload paths were equivalent, source: %ls, destination: %ls.", pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath); + + if (CSTR_EQUAL != nEquivalentPaths) + { + hr = CopyPayload(pProgress, INVALID_HANDLE_VALUE, pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath); + ExitOnFailure(hr, "Failed to copy payload: %ls", wzPayloadId); + + // Store the source path so it can be used as the LastUsedFolder if it passes verification. + pContext->sczLastUsedFolderCandidate = pContext->rgSearchPaths[dwChosenSearchPath]; + pContext->rgSearchPaths[dwChosenSearchPath] = NULL; + } + + break; + case BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD: + hr = DownloadPayload(pProgress, wzDestinationPath); + ExitOnFailure(hr, "Failed to download payload: %ls", wzPayloadId); + + break; + case BOOTSTRAPPER_CACHE_OPERATION_EXTRACT: + Assert(pPayload && pPayload->pContainer); + + hr = ApplyExtractContainer(pContext, pPayload->pContainer); + ExitOnFailure(hr, "Failed to extract container for payload: %ls", wzPayloadId); + + break; + default: + hr = E_FILENOTFOUND; + LogExitOnFailure(hr, MSG_RESOLVE_SOURCE_FAILED, "Failed to resolve source, payload: %ls, package: %ls, container: %ls", wzPayloadId, pPackage ? pPackage->sczId : NULL, pContainer ? pContainer->sczId : NULL); + } + + // Send 100% complete here. This is sometimes the only progress sent to the BA. + hr = CompleteCacheProgress(pProgress, pContainer ? pContainer->qwFileSize : pPayload->qwFileSize); + +LExit: + if (BOOTSTRAPPER_CACHE_OPERATION_EXTRACT == cacheOperation) + { + if (FAILED(hr) && SUCCEEDED(pPayload->pContainer->hrExtract) && + (fFoundLocal || pPayload->downloadSource.sczUrl && *pPayload->downloadSource.sczUrl)) + { + *pfRetry = TRUE; + } + pPayload->pContainer->hrExtract = hr; + } + UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, pfRetry); + + pContext->cSearchPathsMax = max(pContext->cSearchPaths, pContext->cSearchPathsMax); + + return hr; +} + +static BOOL IsValidLocalFile( + __in_z LPCWSTR wzFilePath, + __in DWORD64 qwFileSize, + __in BOOL fMinimumFileSize + ) +{ + LONGLONG llFileSize = 0; + + if (!qwFileSize) + { + return FileExistsEx(wzFilePath, NULL); + } + else + { + return SUCCEEDED(FileSize(wzFilePath, &llFileSize)) && + (static_cast(llFileSize) == qwFileSize || + fMinimumFileSize && static_cast(llFileSize) > qwFileSize); + } +} + +static HRESULT LayoutOrCacheContainerOrPayload( + __in BURN_CACHE_CONTEXT* pContext, + __in_opt BURN_CONTAINER* pContainer, + __in_opt BURN_PACKAGE* pPackage, + __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem, + __in DWORD cTryAgainAttempts, + __out BOOL* pfRetry + ) +{ + HRESULT hr = S_OK; + BURN_PAYLOAD* pPayload = pPayloadGroupItem ? pPayloadGroupItem->pPayload : NULL; + LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : L""; + LPCWSTR wzUnverifiedPath = pContainer ? pContainer->sczUnverifiedPath : pPayload->sczUnverifiedPath; + LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : L""; + BOOL fCanAffectRegistration = FALSE; + BURN_CACHE_PROGRESS_CONTEXT progress = { }; + BOOL fMove = !pPayload || 1 == pPayload->cRemainingInstances; + BOOL fCanceledBegin = FALSE; + + if (pContainer) + { + Assert(!pPayloadGroupItem); + } + else + { + Assert(pPayload); + AssertSz(0 < pPayload->cRemainingInstances, "Laying out payload more times than planned."); + AssertSz(!pPayloadGroupItem->fCached, "Laying out payload group item that was already cached."); + } + + if (!pContext->wzLayoutDirectory) + { + Assert(!pContainer); + Assert(pPackage); + + fCanAffectRegistration = pPackage->fCanAffectRegistration; + } + + *pfRetry = FALSE; + progress.pCacheContext = pContext; + progress.pContainer = pContainer; + progress.pPackage = pPackage; + progress.pPayloadGroupItem = pPayloadGroupItem; + + do + { + fCanceledBegin = FALSE; + + hr = UserExperienceOnCacheVerifyBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId); + + if (FAILED(hr)) + { + fCanceledBegin = TRUE; + } + else + { + if (pContext->wzLayoutDirectory) // layout the container or payload. + { + if (pContainer) + { + hr = CacheLayoutContainer(pContainer, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress); + } + else + { + hr = CacheLayoutPayload(pPayload, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress); + } + } + else if (INVALID_HANDLE_VALUE != pContext->hPipe) // pass the decision off to the elevated process. + { + hr = ElevationCacheCompletePayload(pContext->hPipe, pPackage, pPayload, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress); + } + else // complete the payload. + { + hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress); + } + } + + if (SUCCEEDED(hr) && fCanAffectRegistration) + { + pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + + BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = FAILED(hr) && !fCanceledBegin && cTryAgainAttempts < BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS ? BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION : BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE; + UserExperienceOnCacheVerifyComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &action); + if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action) + { + hr = S_FALSE; // retry verify. + } + else if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION == action) + { + *pfRetry = TRUE; // go back and retry acquire. + } + else if (fCanceledBegin) + { + ExitOnRootFailure(hr, "BA aborted cache verify begin."); + } + } while (S_FALSE == hr); + + if (SUCCEEDED(hr) && pPayloadGroupItem) + { + pPayload->cRemainingInstances -= 1; + pPayloadGroupItem->fCached = TRUE; + } + +LExit: + return hr; +} + +static HRESULT PreparePayloadDestinationPath( + __in_z LPCWSTR wzDestinationPath + ) +{ + HRESULT hr = S_OK; + DWORD dwFileAttributes = 0; + + // If the destination file already exists, clear the readonly bit to avoid E_ACCESSDENIED. + if (FileExistsEx(wzDestinationPath, &dwFileAttributes)) + { + if (FILE_ATTRIBUTE_READONLY & dwFileAttributes) + { + dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY; + if (!::SetFileAttributes(wzDestinationPath, dwFileAttributes)) + { + ExitWithLastError(hr, "Failed to clear readonly bit on payload destination path: %ls", wzDestinationPath); + } + } + } + +LExit: + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + hr = S_OK; + } + + return hr; +} + +static HRESULT CopyPayload( + __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, + __in HANDLE hSourceFile, + __in_z LPCWSTR wzSourcePath, + __in_z LPCWSTR wzDestinationPath + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L""; + LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : L""; + HANDLE hDestinationFile = INVALID_HANDLE_VALUE; + HANDLE hSourceOpenedFile = INVALID_HANDLE_VALUE; + + DWORD dwLogId = pProgress->pContainer ? MSG_ACQUIRE_CONTAINER : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; + LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "copy", wzSourcePath); + + hr = PreparePayloadDestinationPath(wzDestinationPath); + ExitOnFailure(hr, "Failed to prepare payload destination path: %ls", wzDestinationPath); + + if (INVALID_HANDLE_VALUE == hSourceFile) + { + hSourceOpenedFile = ::CreateFileW(wzSourcePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hSourceOpenedFile) + { + ExitWithLastError(hr, "Failed to open source file to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); + } + + hSourceFile = hSourceOpenedFile; + } + else + { + hr = FileSetPointer(hSourceFile, 0, NULL, FILE_BEGIN); + ExitOnRootFailure(hr, "Failed to read from start of source file to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); + } + + hDestinationFile = ::CreateFileW(wzDestinationPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hDestinationFile) + { + ExitWithLastError(hr, "Failed to open destination file to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); + } + + hr = FileCopyUsingHandlesWithProgress(hSourceFile, hDestinationFile, 0, CacheProgressRoutine, pProgress); + if (FAILED(hr)) + { + if (pProgress->fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + ExitOnRootFailure(hr, "BA aborted copy of payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); + } + else + { + ExitOnRootFailure(hr, "Failed attempt to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); + } + } + +LExit: + ReleaseFileHandle(hDestinationFile); + ReleaseFileHandle(hSourceOpenedFile); + + return hr; +} + +static HRESULT DownloadPayload( + __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, + __in_z LPCWSTR wzDestinationPath + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L""; + LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : L""; + DOWNLOAD_SOURCE* pDownloadSource = pProgress->pContainer ? &pProgress->pContainer->downloadSource : &pProgress->pPayloadGroupItem->pPayload->downloadSource; + DWORD64 qwDownloadSize = pProgress->pContainer ? pProgress->pContainer->qwFileSize : pProgress->pPayloadGroupItem->pPayload->qwFileSize; + DOWNLOAD_CACHE_CALLBACK cacheCallback = { }; + DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { }; + APPLY_AUTHENTICATION_REQUIRED_DATA authenticationData = { }; + + DWORD dwLogId = pProgress->pContainer ? MSG_ACQUIRE_CONTAINER : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; + LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "download", pDownloadSource->sczUrl); + + hr = PreparePayloadDestinationPath(wzDestinationPath); + ExitOnFailure(hr, "Failed to prepare payload destination path: %ls", wzDestinationPath); + + cacheCallback.pfnProgress = CacheProgressRoutine; + cacheCallback.pfnCancel = NULL; // TODO: set this + cacheCallback.pv = pProgress; + + authenticationData.pUX = pProgress->pCacheContext->pUX; + authenticationData.wzPackageOrContainerId = wzPackageOrContainerId; + authenticationData.wzPayloadId = wzPayloadId; + authenticationCallback.pv = static_cast(&authenticationData); + authenticationCallback.pfnAuthenticate = &AuthenticationRequired; + + hr = DownloadUrl(pDownloadSource, qwDownloadSize, wzDestinationPath, &cacheCallback, &authenticationCallback); + ExitOnFailure(hr, "Failed attempt to download URL: '%ls' to: '%ls'", pDownloadSource->sczUrl, wzDestinationPath); + +LExit: + return hr; +} + +static HRESULT WINAPI AuthenticationRequired( + __in LPVOID pData, + __in HINTERNET hUrl, + __in long lHttpCode, + __out BOOL* pfRetrySend, + __out BOOL* pfRetry + ) +{ + Assert(401 == lHttpCode || 407 == lHttpCode); + + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + BOOTSTRAPPER_ERROR_TYPE errorType = (401 == lHttpCode) ? BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER : BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY; + LPWSTR sczError = NULL; + int nResult = IDNOACTION; + + *pfRetrySend = FALSE; + *pfRetry = FALSE; + + hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL); + ExitOnFailure(hr, "Failed to allocation error string."); + + APPLY_AUTHENTICATION_REQUIRED_DATA* authenticationData = reinterpret_cast(pData); + + UserExperienceOnError(authenticationData->pUX, errorType, authenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value; + nResult = UserExperienceCheckExecuteResult(authenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult); + if (IDTRYAGAIN == nResult && authenticationData->pUX->hwndApply) + { + er = ::InternetErrorDlg(authenticationData->pUX->hwndApply, hUrl, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL); + if (ERROR_SUCCESS == er || ERROR_CANCELLED == er) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + else if (ERROR_INTERNET_FORCE_RETRY == er) + { + *pfRetrySend = TRUE; + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); + } + } + else if (IDRETRY == nResult) + { + *pfRetry = TRUE; + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); + } + +LExit: + ReleaseStr(sczError); + + return hr; +} + +static HRESULT CALLBACK CacheMessageHandler( + __in BURN_CACHE_MESSAGE* pMessage, + __in LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + BURN_CACHE_PROGRESS_CONTEXT* pProgress = static_cast(pvContext); + LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : NULL; + LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : pProgress->pPayload ? pProgress->pPayload->sczKey : NULL; + + switch (pMessage->type) + { + case BURN_CACHE_MESSAGE_BEGIN: + switch (pMessage->begin.cacheStep) + { + case BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE: + pProgress->type = BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY; + hr = UserExperienceOnCacheContainerOrPayloadVerifyBegin(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId); + break; + case BURN_CACHE_STEP_HASH_TO_SKIP_VERIFY: + pProgress->type = BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY; + break; + case BURN_CACHE_STEP_STAGE: + pProgress->type = BURN_CACHE_PROGRESS_TYPE_STAGE; + break; + case BURN_CACHE_STEP_HASH: + pProgress->type = BURN_CACHE_PROGRESS_TYPE_HASH; + break; + case BURN_CACHE_STEP_FINALIZE: + pProgress->type = BURN_CACHE_PROGRESS_TYPE_FINALIZE; + break; + } + break; + case BURN_CACHE_MESSAGE_SUCCESS: + hr = CompleteCacheProgress(pProgress, pMessage->success.qwFileSize); + break; + case BURN_CACHE_MESSAGE_COMPLETE: + switch (pProgress->type) + { + case BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY: + hr = UserExperienceOnCacheContainerOrPayloadVerifyComplete(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, hr); + break; + } + } + + return hr; +} + +static HRESULT CompleteCacheProgress( + __in BURN_CACHE_PROGRESS_CONTEXT* pContext, + __in DWORD64 qwFileSize + ) +{ + HRESULT hr = S_OK; + LARGE_INTEGER liContainerOrPayloadSize = { }; + LARGE_INTEGER liZero = { }; + DWORD dwResult = 0; + DWORD64 qwCommitSize = 0; + + liContainerOrPayloadSize.QuadPart = qwFileSize; + + // Need to commit the steps that were skipped. + if (BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY == pContext->type || BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY == pContext->type) + { + Assert(!pContext->pPayload); + + qwCommitSize = qwFileSize * (pContext->pCacheContext->wzLayoutDirectory ? 2 : 3); // Acquire (+ Stage) + Hash + Finalize - 1 (that's added later) + + pContext->pCacheContext->qwSuccessfulCacheProgress += qwCommitSize; + + if (pContext->pContainer) + { + pContext->pContainer->qwCommittedCacheProgress += qwCommitSize; + } + else if (pContext->pPayloadGroupItem) + { + pContext->pPayloadGroupItem->qwCommittedCacheProgress += qwCommitSize; + } + } + + dwResult = CacheProgressRoutine(liContainerOrPayloadSize, liContainerOrPayloadSize, liZero, liZero, 0, 0, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, pContext); + + if (PROGRESS_CONTINUE == dwResult) + { + pContext->pCacheContext->qwSuccessfulCacheProgress += qwFileSize; + + if (pContext->pPayload) + { + pContext->pContainer->qwCommittedExtractProgress += qwFileSize; + } + else if (pContext->pContainer) + { + pContext->pContainer->qwCommittedCacheProgress += qwFileSize; + } + else if (pContext->pPayloadGroupItem) + { + pContext->pPayloadGroupItem->qwCommittedCacheProgress += qwFileSize; + } + + if (BURN_CACHE_PROGRESS_TYPE_FINALIZE == pContext->type && pContext->pCacheContext->sczLastUsedFolderCandidate) + { + // We successfully copied from a source location, set that as the last used source. + CacheSetLastUsedSource(pContext->pCacheContext->pVariables, pContext->pCacheContext->sczLastUsedFolderCandidate, pContext->pContainer ? pContext->pContainer->sczFilePath : pContext->pPayloadGroupItem->pPayload->sczFilePath); + } + } + else if (PROGRESS_CANCEL == dwResult) + { + if (pContext->fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + else + { + hr = pContext->hrError; + } + + if (qwCommitSize) + { + pContext->pCacheContext->qwSuccessfulCacheProgress -= qwCommitSize; + + if (pContext->pContainer) + { + pContext->pContainer->qwCommittedCacheProgress -= qwCommitSize; + } + else if (pContext->pPayloadGroupItem) + { + pContext->pPayloadGroupItem->qwCommittedCacheProgress -= qwCommitSize; + } + } + } + + return hr; +} + +static DWORD CALLBACK CacheProgressRoutine( + __in LARGE_INTEGER TotalFileSize, + __in LARGE_INTEGER TotalBytesTransferred, + __in LARGE_INTEGER /*StreamSize*/, + __in LARGE_INTEGER /*StreamBytesTransferred*/, + __in DWORD /*dwStreamNumber*/, + __in DWORD /*dwCallbackReason*/, + __in HANDLE /*hSourceFile*/, + __in HANDLE /*hDestinationFile*/, + __in_opt LPVOID lpData + ) +{ + HRESULT hr = S_OK; + DWORD dwResult = PROGRESS_CONTINUE; + BURN_CACHE_PROGRESS_CONTEXT* pProgress = static_cast(lpData); + LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : NULL; + LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : pProgress->pPayload ? pProgress->pPayload->sczKey : NULL; + DWORD64 qwCacheProgress = pProgress->pCacheContext->qwSuccessfulCacheProgress + TotalBytesTransferred.QuadPart; + if (qwCacheProgress > pProgress->pCacheContext->qwTotalCacheSize) + { + //AssertSz(FALSE, "Apply has cached more than Plan envisioned."); + qwCacheProgress = pProgress->pCacheContext->qwTotalCacheSize; + } + DWORD dwOverallPercentage = pProgress->pCacheContext->qwTotalCacheSize ? static_cast(qwCacheProgress * 100 / pProgress->pCacheContext->qwTotalCacheSize) : 0; + + switch (pProgress->type) + { + case BURN_CACHE_PROGRESS_TYPE_ACQUIRE: + hr = UserExperienceOnCacheAcquireProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); + ExitOnRootFailure(hr, "BA aborted acquire of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); + break; + case BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY: + hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_HASH); + ExitOnRootFailure(hr, "BA aborted payload verify step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); + break; + case BURN_CACHE_PROGRESS_TYPE_STAGE: + hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_STAGE); + ExitOnRootFailure(hr, "BA aborted stage step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); + break; + case BURN_CACHE_PROGRESS_TYPE_HASH: + hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_HASH); + ExitOnRootFailure(hr, "BA aborted hash step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); + break; + case BURN_CACHE_PROGRESS_TYPE_FINALIZE: + hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_FINALIZE); + ExitOnRootFailure(hr, "BA aborted finalize step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); + break; + case BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY: + hr = UserExperienceOnCacheContainerOrPayloadVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); + ExitOnRootFailure(hr, "BA aborted container or payload verify: %ls", wzPayloadId); + break; + case BURN_CACHE_PROGRESS_TYPE_EXTRACT: + hr = UserExperienceOnCachePayloadExtractProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); + ExitOnRootFailure(hr, "BA aborted extract container: %ls, payload: %ls", wzPackageOrContainerId, wzPayloadId); + break; + } + +LExit: + if (HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr) + { + dwResult = PROGRESS_CANCEL; + pProgress->fCancel = TRUE; + } + else if (FAILED(hr)) + { + dwResult = PROGRESS_CANCEL; + pProgress->hrError = hr; + } + else + { + dwResult = PROGRESS_CONTINUE; + } + + return dwResult; +} + +static void DoRollbackCache( + __in BURN_USER_EXPERIENCE* /*pUX*/, + __in BURN_PLAN* pPlan, + __in HANDLE hPipe, + __in DWORD dwCheckpoint + ) +{ + HRESULT hr = S_OK; + DWORD iCheckpoint = 0; + + // Scan to last checkpoint. + for (DWORD i = 0; i < pPlan->cRollbackCacheActions; ++i) + { + BURN_CACHE_ACTION* pRollbackCacheAction = &pPlan->rgRollbackCacheActions[i]; + + if (BURN_CACHE_ACTION_TYPE_CHECKPOINT == pRollbackCacheAction->type && pRollbackCacheAction->checkpoint.dwId == dwCheckpoint) + { + iCheckpoint = i; + break; + } + } + + // Rollback cache actions. + if (iCheckpoint) + { + // i has to be a signed integer so it doesn't get decremented to 0xFFFFFFFF. + for (int i = iCheckpoint - 1; i >= 0; --i) + { + BURN_CACHE_ACTION* pRollbackCacheAction = &pPlan->rgRollbackCacheActions[i]; + + switch (pRollbackCacheAction->type) + { + case BURN_CACHE_ACTION_TYPE_CHECKPOINT: + break; + + case BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE: + hr = CleanPackage(hPipe, pRollbackCacheAction->rollbackPackage.pPackage); + break; + + default: + AssertSz(FALSE, "Invalid rollback cache action."); + break; + } + } + } +} + +static HRESULT DoExecuteAction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in_opt HANDLE hCacheThread, + __in BURN_EXECUTE_CONTEXT* pContext, + __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, + __inout BURN_EXECUTE_ACTION_CHECKPOINT** ppCheckpoint, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + Assert(!pExecuteAction->fDeleted); + + HRESULT hr = S_OK; + HANDLE rghWait[2] = { }; + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; + BOOL fRetry = FALSE; + BOOL fStopWusaService = FALSE; + BOOL fInsideMsiTransaction = FALSE; + + pContext->fRollback = FALSE; + + do + { + fInsideMsiTransaction = *ppRollbackBoundary && (*ppRollbackBoundary)->fActiveTransaction; + + switch (pExecuteAction->type) + { + case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT: + *ppCheckpoint = &pExecuteAction->checkpoint; + break; + + case BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT: + // wait for cache sync-point + rghWait[0] = pExecuteAction->syncpoint.hEvent; + rghWait[1] = hCacheThread; + switch (::WaitForMultipleObjects(rghWait[1] ? 2 : 1, rghWait, FALSE, INFINITE)) + { + case WAIT_OBJECT_0: + break; + + case WAIT_OBJECT_0 + 1: + if (!::GetExitCodeThread(hCacheThread, (DWORD*)&hr)) + { + ExitWithLastError(hr, "Failed to get cache thread exit code."); + } + + if (SUCCEEDED(hr)) + { + hr = E_UNEXPECTED; + } + ExitOnFailure(hr, "Cache thread exited unexpectedly."); + + case WAIT_FAILED: __fallthrough; + default: + ExitWithLastError(hr, "Failed to wait for cache check-point."); + } + break; + + case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: + hr = ExecuteExePackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart); + ExitOnFailure(hr, "Failed to execute EXE package."); + break; + + case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: + hr = ExecuteMsiPackage(pEngineState, pExecuteAction, pContext, fInsideMsiTransaction, FALSE, &fRetry, pfSuspend, &restart); + ExitOnFailure(hr, "Failed to execute MSI package."); + break; + + case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: + hr = ExecuteMspPackage(pEngineState, pExecuteAction, pContext, fInsideMsiTransaction, FALSE, &fRetry, pfSuspend, &restart); + ExitOnFailure(hr, "Failed to execute MSP package."); + break; + + case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: + hr = ExecuteMsuPackage(pEngineState, pExecuteAction, pContext, FALSE, fStopWusaService, &fRetry, pfSuspend, &restart); + fStopWusaService = fRetry; + ExitOnFailure(hr, "Failed to execute MSU package."); + break; + + case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER: + hr = ExecutePackageProviderAction(pEngineState, pExecuteAction, pContext); + ExitOnFailure(hr, "Failed to execute package provider registration action."); + break; + + case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: + hr = ExecuteDependencyAction(pEngineState, pExecuteAction, pContext); + ExitOnFailure(hr, "Failed to execute dependency action."); + break; + + break; + + case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY: + *ppRollbackBoundary = pExecuteAction->rollbackBoundary.pRollbackBoundary; + break; + + case BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION: + hr = ExecuteMsiBeginTransaction(pEngineState, pExecuteAction->msiTransaction.pRollbackBoundary, pContext); + ExitOnFailure(hr, "Failed to execute begin MSI transaction action."); + break; + + case BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION: + hr = ExecuteMsiCommitTransaction(pEngineState, pExecuteAction->msiTransaction.pRollbackBoundary, pContext); + ExitOnFailure(hr, "Failed to execute commit MSI transaction action."); + break; + + default: + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Invalid execute action."); + } + + if (*pRestart < restart) + { + *pRestart = restart; + } + } while (fRetry && *pRestart < BOOTSTRAPPER_APPLY_RESTART_INITIATED); + +LExit: + return hr; +} + +static HRESULT DoRollbackActions( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_CONTEXT* pContext, + __in DWORD dwCheckpoint, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + DWORD iCheckpoint = 0; + BOOL fRetryIgnored = FALSE; + BOOL fSuspendIgnored = FALSE; + + pContext->fRollback = TRUE; + + // scan to last checkpoint + for (DWORD i = 0; i < pEngineState->plan.cRollbackActions; ++i) + { + BURN_EXECUTE_ACTION* pRollbackAction = &pEngineState->plan.rgRollbackActions[i]; + if (pRollbackAction->fDeleted) + { + continue; + } + + if (BURN_EXECUTE_ACTION_TYPE_CHECKPOINT == pRollbackAction->type) + { + if (pRollbackAction->checkpoint.dwId == dwCheckpoint) + { + iCheckpoint = i; + break; + } + } + } + + // execute rollback actions + if (iCheckpoint) + { + // i has to be a signed integer so it doesn't get decremented to 0xFFFFFFFF. + for (int i = iCheckpoint - 1; i >= 0; --i) + { + BURN_EXECUTE_ACTION* pRollbackAction = &pEngineState->plan.rgRollbackActions[i]; + if (pRollbackAction->fDeleted) + { + continue; + } + + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; + switch (pRollbackAction->type) + { + case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT: + break; + + case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: + hr = ExecuteExePackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); + IgnoreRollbackError(hr, "Failed to rollback EXE package."); + break; + + case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: + hr = ExecuteMsiPackage(pEngineState, pRollbackAction, pContext, FALSE, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); + IgnoreRollbackError(hr, "Failed to rollback MSI package."); + break; + + case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: + hr = ExecuteMspPackage(pEngineState, pRollbackAction, pContext, FALSE, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); + IgnoreRollbackError(hr, "Failed to rollback MSP package."); + break; + + case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: + hr = ExecuteMsuPackage(pEngineState, pRollbackAction, pContext, TRUE, FALSE, &fRetryIgnored, &fSuspendIgnored, &restart); + IgnoreRollbackError(hr, "Failed to rollback MSU package."); + break; + + case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER: + hr = ExecutePackageProviderAction(pEngineState, pRollbackAction, pContext); + IgnoreRollbackError(hr, "Failed to rollback package provider action."); + break; + + case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: + hr = ExecuteDependencyAction(pEngineState, pRollbackAction, pContext); + IgnoreRollbackError(hr, "Failed to rollback dependency action."); + break; + + case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY: + ExitFunction1(hr = S_OK); + + case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE: + // TODO: This used to be skipped if the package was already cached. + // Need to figure out new logic for when (if?) to skip it. + hr = CleanPackage(pEngineState->companionConnection.hPipe, pRollbackAction->uncachePackage.pPackage); + IgnoreRollbackError(hr, "Failed to uncache package for rollback."); + break; + + default: + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Invalid rollback action: %d.", pRollbackAction->type); + } + + if (*pRestart < restart) + { + *pRestart = restart; + } + } + } + +LExit: + return hr; +} + +static HRESULT ExecuteExePackage( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_EXECUTE_CONTEXT* pContext, + __in BOOL fRollback, + __out BOOL* pfRetry, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + HRESULT hrExecute = S_OK; + GENERIC_EXECUTE_MESSAGE message = { }; + int nResult = 0; + BOOL fBeginCalled = FALSE; + BOOL fExecuted = FALSE; + BURN_PACKAGE* pPackage = pExecuteAction->exePackage.pPackage; + + if (FAILED(pPackage->hrCacheResult)) + { + LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult); + ExitFunction1(hr = S_OK); + } + + Assert(pContext->fRollback == fRollback); + pContext->pExecutingPackage = pPackage; + fBeginCalled = TRUE; + + // Send package execute begin to BA. + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->exePackage.action, INSTALLUILEVEL_NOCHANGE, FALSE); + ExitOnRootFailure(hr, "BA aborted execute EXE package begin."); + + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; + message.dwAllowedResults = MB_OKCANCEL; + message.progress.dwPercentage = fRollback ? 100 : 0; + nResult = GenericExecuteMessageHandler(&message, pContext); + hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); + ExitOnRootFailure(hr, "BA aborted EXE progress."); + + fExecuted = TRUE; + + // Execute package. + if (pPackage->fPerMachine) + { + hrExecute = ElevationExecuteExePackage(pEngineState->companionConnection.hPipe, pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart); + ExitOnFailure(hrExecute, "Failed to configure per-machine EXE package."); + } + else + { + hrExecute = ExeEngineExecutePackage(pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart); + ExitOnFailure(hrExecute, "Failed to configure per-user EXE package."); + } + + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; + message.dwAllowedResults = MB_OKCANCEL; + message.progress.dwPercentage = fRollback ? 0 : 100; + nResult = GenericExecuteMessageHandler(&message, pContext); + hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); + ExitOnRootFailure(hr, "BA aborted EXE progress."); + + pContext->cExecutedPackages += fRollback ? -1 : 1; + (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; + + hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); + ExitOnRootFailure(hr, "BA aborted EXE package execute progress."); + +LExit: + if (fExecuted) + { + ExeEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute); + } + + if (fBeginCalled) + { + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); + } + + return hr; +} + +static HRESULT ExecuteMsiPackage( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_EXECUTE_CONTEXT* pContext, + __in BOOL fInsideMsiTransaction, + __in BOOL fRollback, + __out BOOL* pfRetry, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + HRESULT hrExecute = S_OK; + BOOL fBeginCalled = FALSE; + BOOL fExecuted = FALSE; + BURN_PACKAGE* pPackage = pExecuteAction->msiPackage.pPackage; + + if (FAILED(pPackage->hrCacheResult)) + { + LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult); + ExitFunction1(hr = S_OK); + } + + Assert(pContext->fRollback == fRollback); + pContext->pExecutingPackage = pPackage; + fBeginCalled = TRUE; + + // Send package execute begin to BA. + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->msiPackage.action, pExecuteAction->msiPackage.uiLevel, pExecuteAction->msiPackage.fDisableExternalUiHandler); + ExitOnRootFailure(hr, "BA aborted execute MSI package begin."); + + fExecuted = TRUE; + + // execute package + if (pPackage->fPerMachine) + { + hrExecute = ElevationExecuteMsiPackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); + ExitOnFailure(hrExecute, "Failed to configure per-machine MSI package."); + } + else + { + hrExecute = MsiEngineExecutePackage(pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); + ExitOnFailure(hrExecute, "Failed to configure per-user MSI package."); + } + + pContext->cExecutedPackages += fRollback ? -1 : 1; + (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; + + hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); + ExitOnRootFailure(hr, "BA aborted MSI package execute progress."); + +LExit: + if (fExecuted) + { + MsiEngineUpdateInstallRegistrationState(pExecuteAction, fRollback, hrExecute, fInsideMsiTransaction); + } + + if (fBeginCalled) + { + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); + } + + return hr; +} + +static HRESULT ExecuteMspPackage( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_EXECUTE_CONTEXT* pContext, + __in BOOL fInsideMsiTransaction, + __in BOOL fRollback, + __out BOOL* pfRetry, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + HRESULT hrExecute = S_OK; + BOOL fBeginCalled = FALSE; + BOOL fExecuted = FALSE; + BURN_PACKAGE* pPackage = pExecuteAction->mspTarget.pPackage; + + if (FAILED(pPackage->hrCacheResult)) + { + LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult); + ExitFunction1(hr = S_OK); + } + + Assert(pContext->fRollback == fRollback); + pContext->pExecutingPackage = pPackage; + fBeginCalled = TRUE; + + // Send package execute begin to BA. + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->mspTarget.action, pExecuteAction->mspTarget.uiLevel, pExecuteAction->mspTarget.fDisableExternalUiHandler); + ExitOnRootFailure(hr, "BA aborted execute MSP package begin."); + + // Now send all the patches that target this product code. + for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i) + { + BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage; + + hr = UserExperienceOnExecutePatchTarget(&pEngineState->userExperience, pMspPackage->sczId, pExecuteAction->mspTarget.sczTargetProductCode); + ExitOnRootFailure(hr, "BA aborted execute MSP target."); + } + + fExecuted = TRUE; + + // execute package + if (pExecuteAction->mspTarget.fPerMachineTarget) + { + hrExecute = ElevationExecuteMspPackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); + ExitOnFailure(hrExecute, "Failed to configure per-machine MSP package."); + } + else + { + hrExecute = MspEngineExecutePackage(pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); + ExitOnFailure(hrExecute, "Failed to configure per-user MSP package."); + } + + pContext->cExecutedPackages += fRollback ? -1 : 1; + (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; + + hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); + ExitOnRootFailure(hr, "BA aborted MSP package execute progress."); + +LExit: + if (fExecuted) + { + MspEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute, fInsideMsiTransaction); + } + + if (fBeginCalled) + { + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); + } + + return hr; +} + +static HRESULT ExecuteMsuPackage( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_EXECUTE_CONTEXT* pContext, + __in BOOL fRollback, + __in BOOL fStopWusaService, + __out BOOL* pfRetry, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + HRESULT hrExecute = S_OK; + GENERIC_EXECUTE_MESSAGE message = { }; + int nResult = 0; + BOOL fBeginCalled = FALSE; + BOOL fExecuted = FALSE; + BURN_PACKAGE* pPackage = pExecuteAction->msuPackage.pPackage; + + if (FAILED(pPackage->hrCacheResult)) + { + LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult); + ExitFunction1(hr = S_OK); + } + + Assert(pContext->fRollback == fRollback); + pContext->pExecutingPackage = pPackage; + fBeginCalled = TRUE; + + // Send package execute begin to BA. + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->msuPackage.action, INSTALLUILEVEL_NOCHANGE, FALSE); + ExitOnRootFailure(hr, "BA aborted execute MSU package begin."); + + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; + message.dwAllowedResults = MB_OKCANCEL; + message.progress.dwPercentage = fRollback ? 100 : 0; + nResult = GenericExecuteMessageHandler(&message, pContext); + hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); + ExitOnRootFailure(hr, "BA aborted MSU progress."); + + fExecuted = TRUE; + + // execute package + if (pPackage->fPerMachine) + { + hrExecute = ElevationExecuteMsuPackage(pEngineState->companionConnection.hPipe, pExecuteAction, fRollback, fStopWusaService, GenericExecuteMessageHandler, pContext, pRestart); + ExitOnFailure(hrExecute, "Failed to configure per-machine MSU package."); + } + else + { + hrExecute = E_UNEXPECTED; + ExitOnFailure(hr, "MSU packages cannot be per-user."); + } + + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; + message.dwAllowedResults = MB_OKCANCEL; + message.progress.dwPercentage = fRollback ? 0 : 100; + nResult = GenericExecuteMessageHandler(&message, pContext); + hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); + ExitOnRootFailure(hr, "BA aborted MSU progress."); + + pContext->cExecutedPackages += fRollback ? -1 : 1; + (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; + + hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); + ExitOnRootFailure(hr, "BA aborted MSU package execute progress."); + +LExit: + if (fExecuted) + { + MsuEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute); + } + + if (fBeginCalled) + { + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); + } + + return hr; +} + +static HRESULT ExecutePackageProviderAction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pAction, + __in BURN_EXECUTE_CONTEXT* /*pContext*/ + ) +{ + HRESULT hr = S_OK; + + if (pAction->packageProvider.pPackage->fPerMachine) + { + hr = ElevationExecutePackageProviderAction(pEngineState->companionConnection.hPipe, pAction); + ExitOnFailure(hr, "Failed to register the package provider on per-machine package."); + } + else + { + hr = DependencyExecutePackageProviderAction(pAction); + ExitOnFailure(hr, "Failed to register the package provider on per-user package."); + } + +LExit: + return hr; +} + +static HRESULT ExecuteDependencyAction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_EXECUTE_ACTION* pAction, + __in BURN_EXECUTE_CONTEXT* /*pContext*/ + ) +{ + HRESULT hr = S_OK; + + if (pAction->packageDependency.pPackage->fPerMachine) + { + hr = ElevationExecutePackageDependencyAction(pEngineState->companionConnection.hPipe, pAction); + ExitOnFailure(hr, "Failed to register the dependency on per-machine package."); + } + else + { + hr = DependencyExecutePackageDependencyAction(FALSE, pAction); + ExitOnFailure(hr, "Failed to register the dependency on per-user package."); + } + + if (pAction->packageDependency.pPackage->fCanAffectRegistration) + { + if (BURN_DEPENDENCY_ACTION_REGISTER == pAction->packageDependency.action) + { + if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pAction->packageDependency.pPackage->cacheRegistrationState) + { + pAction->packageDependency.pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + + if (BURN_PACKAGE_TYPE_MSP == pAction->packageDependency.pPackage->type) + { + for (DWORD i = 0; i < pAction->packageDependency.pPackage->Msp.cTargetProductCodes; ++i) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pAction->packageDependency.pPackage->Msp.rgTargetProducts + i; + + if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pTargetProduct->registrationState) + { + pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + } + } + else if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pAction->packageDependency.pPackage->installRegistrationState) + { + pAction->packageDependency.pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + } + else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pAction->packageDependency.action) + { + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pAction->packageDependency.pPackage->cacheRegistrationState) + { + pAction->packageDependency.pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + + if (BURN_PACKAGE_TYPE_MSP == pAction->packageDependency.pPackage->type) + { + for (DWORD i = 0; i < pAction->packageDependency.pPackage->Msp.cTargetProductCodes; ++i) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pAction->packageDependency.pPackage->Msp.rgTargetProducts + i; + + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pTargetProduct->registrationState) + { + pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + } + } + else if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pAction->packageDependency.pPackage->installRegistrationState) + { + pAction->packageDependency.pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + } + } + +LExit: + return hr; +} + +static HRESULT ExecuteMsiBeginTransaction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, + __in BURN_EXECUTE_CONTEXT* /*pContext*/ + ) +{ + HRESULT hr = S_OK; + BOOL fBeginCalled = FALSE; + + if (pRollbackBoundary->fActiveTransaction) + { + ExitFunction1(hr = E_INVALIDSTATE); + } + + fBeginCalled = TRUE; + hr = UserExperienceOnBeginMsiTransactionBegin(&pEngineState->userExperience, pRollbackBoundary->sczId); + ExitOnRootFailure(hr, "BA aborted execute begin MSI transaction."); + + if (pEngineState->plan.fPerMachine) + { + hr = ElevationMsiBeginTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary); + ExitOnFailure(hr, "Failed to begin an elevated MSI transaction."); + } + else + { + hr = MsiEngineBeginTransaction(pRollbackBoundary); + } + + if (SUCCEEDED(hr)) + { + pRollbackBoundary->fActiveTransaction = TRUE; + + ResetTransactionRegistrationState(pEngineState, FALSE); + } + +LExit: + if (fBeginCalled) + { + UserExperienceOnBeginMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr); + } + + return hr; +} + +static HRESULT ExecuteMsiCommitTransaction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, + __in BURN_EXECUTE_CONTEXT* /*pContext*/ + ) +{ + HRESULT hr = S_OK; + BOOL fCommitBeginCalled = FALSE; + + if (!pRollbackBoundary->fActiveTransaction) + { + ExitFunction1(hr = E_INVALIDSTATE); + } + + fCommitBeginCalled = TRUE; + hr = UserExperienceOnCommitMsiTransactionBegin(&pEngineState->userExperience, pRollbackBoundary->sczId); + ExitOnRootFailure(hr, "BA aborted execute commit MSI transaction."); + + if (pEngineState->plan.fPerMachine) + { + hr = ElevationMsiCommitTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary); + ExitOnFailure(hr, "Failed to commit an elevated MSI transaction."); + } + else + { + hr = MsiEngineCommitTransaction(pRollbackBoundary); + } + + if (SUCCEEDED(hr)) + { + pRollbackBoundary->fActiveTransaction = FALSE; + + ResetTransactionRegistrationState(pEngineState, TRUE); + } + +LExit: + if (fCommitBeginCalled) + { + UserExperienceOnCommitMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr); + } + + return hr; +} + +static HRESULT ExecuteMsiRollbackTransaction( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, + __in BURN_EXECUTE_CONTEXT* /*pContext*/ + ) +{ + HRESULT hr = S_OK; + BOOL fRollbackBeginCalled = FALSE; + + if (!pRollbackBoundary->fActiveTransaction) + { + ExitFunction(); + } + + fRollbackBeginCalled = TRUE; + UserExperienceOnRollbackMsiTransactionBegin(&pEngineState->userExperience, pRollbackBoundary->sczId); + + if (pEngineState->plan.fPerMachine) + { + hr = ElevationMsiRollbackTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary); + ExitOnFailure(hr, "Failed to rollback an elevated MSI transaction."); + } + else + { + hr = MsiEngineRollbackTransaction(pRollbackBoundary); + } + +LExit: + pRollbackBoundary->fActiveTransaction = FALSE; + + ResetTransactionRegistrationState(pEngineState, FALSE); + + if (fRollbackBeginCalled) + { + UserExperienceOnRollbackMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr); + } + + return hr; +} + +static void ResetTransactionRegistrationState( + __in BURN_ENGINE_STATE* pEngineState, + __in BOOL fCommit + ) +{ + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + + if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + j; + + if (fCommit && BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN != pTargetProduct->transactionRegistrationState) + { + pTargetProduct->registrationState = pTargetProduct->transactionRegistrationState; + } + + pTargetProduct->transactionRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; + } + } + else if (fCommit && BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN != pPackage->transactionRegistrationState) + { + pPackage->installRegistrationState = pPackage->transactionRegistrationState; + } + + pPackage->transactionRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; + } +} + +static HRESULT CleanPackage( + __in HANDLE hElevatedPipe, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + + if (pPackage->fPerMachine) + { + hr = ElevationCleanPackage(hElevatedPipe, pPackage); + } + else + { + hr = CacheRemovePackage(FALSE, pPackage->sczId, pPackage->sczCacheId); + } + + if (pPackage->fCanAffectRegistration) + { + pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + + return hr; +} + +static int GenericExecuteMessageHandler( + __in GENERIC_EXECUTE_MESSAGE* pMessage, + __in LPVOID pvContext + ) +{ + BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext; + int nResult = IDNOACTION; + + switch (pMessage->type) + { + case GENERIC_EXECUTE_MESSAGE_PROGRESS: + { + DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0; + UserExperienceOnExecuteProgress(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value. + } + break; + + case GENERIC_EXECUTE_MESSAGE_ERROR: + UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_EXE_PACKAGE, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwAllowedResults, 0, NULL, &nResult); // ignore return value. + break; + + case GENERIC_EXECUTE_MESSAGE_FILES_IN_USE: + UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->filesInUse.cFiles, pMessage->filesInUse.rgwzFiles, &nResult); // ignore return value. + break; + } + + nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult); + return nResult; +} + +static int MsiExecuteMessageHandler( + __in WIU_MSI_EXECUTE_MESSAGE* pMessage, + __in_opt LPVOID pvContext + ) +{ + BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext; + int nResult = IDNOACTION; + + switch (pMessage->type) + { + case WIU_MSI_EXECUTE_MESSAGE_PROGRESS: + { + DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0; + UserExperienceOnExecuteProgress(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value. + } + break; + + case WIU_MSI_EXECUTE_MESSAGE_ERROR: + nResult = pMessage->nResultRecommendation; + UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwAllowedResults, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value. + break; + + case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE: + nResult = pMessage->nResultRecommendation; + UserExperienceOnExecuteMsiMessage(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiMessage.mt, pMessage->dwAllowedResults, pMessage->msiMessage.wzMessage, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value. + break; + + case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE: + UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiFilesInUse.cFiles, pMessage->msiFilesInUse.rgwzFiles, &nResult); // ignore return value. + break; + } + + nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult); + return nResult; +} + +static HRESULT ReportOverallProgressTicks( + __in BURN_USER_EXPERIENCE* pUX, + __in BOOL fRollback, + __in DWORD cOverallProgressTicksTotal, + __in DWORD cOverallProgressTicks + ) +{ + HRESULT hr = S_OK; + DWORD dwProgress = cOverallProgressTicksTotal ? (cOverallProgressTicks * 100 / cOverallProgressTicksTotal) : 0; + + // TODO: consider sending different progress numbers in the future. + hr = UserExperienceOnProgress(pUX, fRollback, dwProgress, dwProgress); + + return hr; +} + +static HRESULT ExecutePackageComplete( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_VARIABLES* pVariables, + __in BURN_PACKAGE* pPackage, + __in HRESULT hrOverall, + __in HRESULT hrExecute, + __in BOOL fRollback, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart, + __out BOOL* pfRetry, + __out BOOL* pfSuspend + ) +{ + HRESULT hr = FAILED(hrOverall) ? hrOverall : hrExecute; // if the overall function failed use that otherwise use the execution result. + BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION executePackageCompleteAction = FAILED(hrOverall) || SUCCEEDED(hrExecute) || pPackage->fVital ? BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE; + + // Send package execute complete to BA. + UserExperienceOnExecutePackageComplete(pUX, pPackage->sczId, hr, *pRestart, &executePackageCompleteAction); + if (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RESTART == executePackageCompleteAction) + { + *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; + } + *pfRetry = (FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RETRY == executePackageCompleteAction); // allow retry only on failures. + *pfSuspend = (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_SUSPEND == executePackageCompleteAction); + + // Remember this package as the package that initiated the forced restart. + if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart) + { + // Best effort to set the forced restart package variable. + VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, pPackage->sczId, TRUE, FALSE); + } + + // If we're retrying, leave a message in the log file and say everything is okay. + if (*pfRetry) + { + LogId(REPORT_STANDARD, MSG_APPLY_RETRYING_PACKAGE, pPackage->sczId, hrExecute); + hr = S_OK; + } + else if (SUCCEEDED(hrOverall) && FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE == executePackageCompleteAction && !pPackage->fVital) // If we *only* failed to execute and the BA ignored this *not-vital* package, say everything is okay. + { + LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, pPackage->sczId, hrExecute); + hr = S_OK; + } + else + { + LogId(REPORT_STANDARD, MSG_APPLY_COMPLETED_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, hr, LoggingRestartToString(*pRestart)); + } + + return hr; +} diff --git a/src/burn/engine/apply.h b/src/burn/engine/apply.h new file mode 100644 index 00000000..548e147d --- /dev/null +++ b/src/burn/engine/apply.h @@ -0,0 +1,104 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + + +enum GENERIC_EXECUTE_MESSAGE_TYPE +{ + GENERIC_EXECUTE_MESSAGE_NONE, + GENERIC_EXECUTE_MESSAGE_ERROR, + GENERIC_EXECUTE_MESSAGE_PROGRESS, + GENERIC_EXECUTE_MESSAGE_FILES_IN_USE, +}; + +typedef struct _APPLY_AUTHENTICATION_REQUIRED_DATA +{ + BURN_USER_EXPERIENCE* pUX; + LPCWSTR wzPackageOrContainerId; + LPCWSTR wzPayloadId; +} APPLY_AUTHENTICATION_REQUIRED_DATA; + +typedef struct _GENERIC_EXECUTE_MESSAGE +{ + GENERIC_EXECUTE_MESSAGE_TYPE type; + DWORD dwAllowedResults; + + union + { + struct + { + DWORD dwErrorCode; + LPCWSTR wzMessage; + } error; + struct + { + DWORD dwPercentage; + } progress; + struct + { + DWORD cFiles; + LPCWSTR* rgwzFiles; + } filesInUse; + }; +} GENERIC_EXECUTE_MESSAGE; + + +typedef int (*PFN_GENERICMESSAGEHANDLER)( + __in GENERIC_EXECUTE_MESSAGE* pMessage, + __in LPVOID pvContext + ); + + +void ApplyInitialize(); +void ApplyUninitialize(); +HRESULT ApplySetVariables( + __in BURN_VARIABLES* pVariables + ); +void ApplyReset( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PACKAGES* pPackages + ); +HRESULT ApplyLock( + __in BOOL fPerMachine, + __out HANDLE* phLock + ); +HRESULT ApplyRegister( + __in BURN_ENGINE_STATE* pEngineState + ); +HRESULT ApplyUnregister( + __in BURN_ENGINE_STATE* pEngineState, + __in BOOL fFailedOrRollback, + __in BOOL fSuspend, + __in BOOTSTRAPPER_APPLY_RESTART restart + ); +HRESULT ApplyCache( + __in HANDLE hSourceEngineFile, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_VARIABLES* pVariables, + __in BURN_PLAN* pPlan, + __in HANDLE hPipe, + __inout DWORD* pcOverallProgressTicks, + __inout BOOL* pfRollback + ); +HRESULT ApplyExecute( + __in BURN_ENGINE_STATE* pEngineState, + __in_opt HANDLE hCacheThread, + __inout DWORD* pcOverallProgressTicks, + __out BOOL* pfRollback, + __out BOOL* pfSuspend, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +void ApplyClean( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PLAN* pPlan, + __in HANDLE hPipe + ); + + +#ifdef __cplusplus +} +#endif diff --git a/src/burn/engine/approvedexe.cpp b/src/burn/engine/approvedexe.cpp new file mode 100644 index 00000000..55518519 --- /dev/null +++ b/src/burn/engine/approvedexe.cpp @@ -0,0 +1,262 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// function definitions + +extern "C" HRESULT ApprovedExesParseFromXml( + __in BURN_APPROVED_EXES* pApprovedExes, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + LPWSTR scz = NULL; + + // select approved exe nodes + hr = XmlSelectNodes(pixnBundle, L"ApprovedExeForElevation", &pixnNodes); + ExitOnFailure(hr, "Failed to select approved exe nodes."); + + // get approved exe node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get approved exe node count."); + + if (!cNodes) + { + ExitFunction(); + } + + // allocate memory for approved exes + pApprovedExes->rgApprovedExes = (BURN_APPROVED_EXE*)MemAlloc(sizeof(BURN_APPROVED_EXE) * cNodes, TRUE); + ExitOnNull(pApprovedExes->rgApprovedExes, hr, E_OUTOFMEMORY, "Failed to allocate memory for approved exe structs."); + + pApprovedExes->cApprovedExes = cNodes; + + // parse approved exe elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_APPROVED_EXE* pApprovedExe = &pApprovedExes->rgApprovedExes[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &pApprovedExe->sczId); + ExitOnFailure(hr, "Failed to get @Id."); + + // @Key + hr = XmlGetAttributeEx(pixnNode, L"Key", &pApprovedExe->sczKey); + ExitOnFailure(hr, "Failed to get @Key."); + + // @ValueName + hr = XmlGetAttributeEx(pixnNode, L"ValueName", &pApprovedExe->sczValueName); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @ValueName."); + } + + // @Win64 + hr = XmlGetYesNoAttribute(pixnNode, L"Win64", &pApprovedExe->fWin64); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Win64."); + } + + // prepare next iteration + ReleaseNullObject(pixnNode); + ReleaseNullStr(scz); + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseStr(scz); + return hr; +} + +extern "C" void ApprovedExesUninitialize( + __in BURN_APPROVED_EXES* pApprovedExes + ) +{ + if (pApprovedExes->rgApprovedExes) + { + for (DWORD i = 0; i < pApprovedExes->cApprovedExes; ++i) + { + BURN_APPROVED_EXE* pApprovedExe = &pApprovedExes->rgApprovedExes[i]; + + ReleaseStr(pApprovedExe->sczId); + ReleaseStr(pApprovedExe->sczKey); + ReleaseStr(pApprovedExe->sczValueName); + } + MemFree(pApprovedExes->rgApprovedExes); + } +} + +extern "C" void ApprovedExesUninitializeLaunch( + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe + ) +{ + if (pLaunchApprovedExe) + { + ReleaseStr(pLaunchApprovedExe->sczArguments); + ReleaseStr(pLaunchApprovedExe->sczExecutablePath); + ReleaseStr(pLaunchApprovedExe->sczId); + MemFree(pLaunchApprovedExe); + } +} + +extern "C" HRESULT ApprovedExesFindById( + __in BURN_APPROVED_EXES* pApprovedExes, + __in_z LPCWSTR wzId, + __out BURN_APPROVED_EXE** ppApprovedExe + ) +{ + HRESULT hr = S_OK; + BURN_APPROVED_EXE* pApprovedExe = NULL; + + for (DWORD i = 0; i < pApprovedExes->cApprovedExes; ++i) + { + pApprovedExe = &pApprovedExes->rgApprovedExes[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pApprovedExe->sczId, -1, wzId, -1)) + { + *ppApprovedExe = pApprovedExe; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + +extern "C" HRESULT ApprovedExesLaunch( + __in BURN_VARIABLES* pVariables, + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe, + __out DWORD* pdwProcessId + ) +{ + HRESULT hr = S_OK; + LPWSTR sczArgumentsFormatted = NULL; + LPWSTR sczArgumentsObfuscated = NULL; + LPWSTR sczCommand = NULL; + LPWSTR sczCommandObfuscated = NULL; + LPWSTR sczExecutableDirectory = NULL; + STARTUPINFOW si = { }; + PROCESS_INFORMATION pi = { }; + + // build command + if (pLaunchApprovedExe->sczArguments && *pLaunchApprovedExe->sczArguments) + { + hr = VariableFormatString(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsFormatted, NULL); + ExitOnFailure(hr, "Failed to format argument string."); + + hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsFormatted); + ExitOnFailure(hr, "Failed to create executable command."); + + hr = VariableFormatStringObfuscated(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsObfuscated, NULL); + ExitOnFailure(hr, "Failed to format obfuscated argument string."); + + hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsObfuscated); + } + else + { + hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath); + ExitOnFailure(hr, "Failed to create executable command."); + + hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath); + } + ExitOnFailure(hr, "Failed to create obfuscated executable command."); + + // Try to get the directory of the executable so we can set the current directory of the process to help those executables + // that expect stuff to be relative to them. Best effort only. + hr = PathGetDirectory(pLaunchApprovedExe->sczExecutablePath, &sczExecutableDirectory); + if (FAILED(hr)) + { + ReleaseNullStr(sczExecutableDirectory); + } + + LogId(REPORT_STANDARD, MSG_LAUNCHING_APPROVED_EXE, pLaunchApprovedExe->sczExecutablePath, sczCommandObfuscated); + + si.cb = sizeof(si); + if (!::CreateProcessW(pLaunchApprovedExe->sczExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NEW_PROCESS_GROUP, NULL, sczExecutableDirectory, &si, &pi)) + { + ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", pLaunchApprovedExe->sczExecutablePath); + } + + *pdwProcessId = pi.dwProcessId; + + if (pLaunchApprovedExe->dwWaitForInputIdleTimeout) + { + ::WaitForInputIdle(pi.hProcess, pLaunchApprovedExe->dwWaitForInputIdleTimeout); + } + +LExit: + StrSecureZeroFreeString(sczArgumentsFormatted); + ReleaseStr(sczArgumentsObfuscated); + StrSecureZeroFreeString(sczCommand); + ReleaseStr(sczCommandObfuscated); + ReleaseStr(sczExecutableDirectory); + + ReleaseHandle(pi.hThread); + ReleaseHandle(pi.hProcess); + + return hr; +} + +extern "C" HRESULT ApprovedExesVerifySecureLocation( + __in BURN_VARIABLES* pVariables, + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe + ) +{ + HRESULT hr = S_OK; + LPWSTR scz = NULL; + + const LPCWSTR vrgSecureFolderVariables[] = { + L"ProgramFiles64Folder", + L"ProgramFilesFolder", + }; + + for (DWORD i = 0; i < countof(vrgSecureFolderVariables); ++i) + { + LPCWSTR wzSecureFolderVariable = vrgSecureFolderVariables[i]; + + hr = VariableGetString(pVariables, wzSecureFolderVariable, &scz); + if (SUCCEEDED(hr)) + { + hr = PathDirectoryContainsPath(scz, pLaunchApprovedExe->sczExecutablePath); + if (S_OK == hr) + { + ExitFunction(); + } + } + else if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get the variable: %ls", wzSecureFolderVariable); + } + } + + // The problem with using a Variable for the root package cache folder is that it might not have been secured yet. + // Getting it through CacheGetRootCompletedPath makes sure it has been secured. + hr = CacheGetRootCompletedPath(TRUE, TRUE, &scz); + ExitOnFailure(hr, "Failed to get the root package cache folder."); + + hr = PathDirectoryContainsPath(scz, pLaunchApprovedExe->sczExecutablePath); + if (S_OK == hr) + { + ExitFunction(); + } + + hr = S_FALSE; + +LExit: + ReleaseStr(scz); + + return hr; +} diff --git a/src/burn/engine/approvedexe.h b/src/burn/engine/approvedexe.h new file mode 100644 index 00000000..23f3b1bb --- /dev/null +++ b/src/burn/engine/approvedexe.h @@ -0,0 +1,67 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// structs + +typedef struct _BURN_APPROVED_EXE +{ + LPWSTR sczId; + LPWSTR sczKey; + LPWSTR sczValueName; + BOOL fWin64; +} BURN_APPROVED_EXE; + +typedef struct _BURN_APPROVED_EXES +{ + BURN_APPROVED_EXE* rgApprovedExes; + DWORD cApprovedExes; +} BURN_APPROVED_EXES; + +typedef struct _BURN_LAUNCH_APPROVED_EXE +{ + HWND hwndParent; + LPWSTR sczId; + LPWSTR sczExecutablePath; + LPWSTR sczArguments; + DWORD dwWaitForInputIdleTimeout; +} BURN_LAUNCH_APPROVED_EXE; + + +// function declarations + +HRESULT ApprovedExesParseFromXml( + __in BURN_APPROVED_EXES* pApprovedExes, + __in IXMLDOMNode* pixnBundle + ); + +void ApprovedExesUninitialize( + __in BURN_APPROVED_EXES* pApprovedExes + ); +void ApprovedExesUninitializeLaunch( + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe + ); +HRESULT ApprovedExesFindById( + __in BURN_APPROVED_EXES* pApprovedExes, + __in_z LPCWSTR wzId, + __out BURN_APPROVED_EXE** ppApprovedExe + ); +HRESULT ApprovedExesLaunch( + __in BURN_VARIABLES* pVariables, + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe, + __out DWORD* pdwProcessId + ); +HRESULT ApprovedExesVerifySecureLocation( + __in BURN_VARIABLES* pVariables, + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/burnextension.cpp b/src/burn/engine/burnextension.cpp new file mode 100644 index 00000000..475df1c5 --- /dev/null +++ b/src/burn/engine/burnextension.cpp @@ -0,0 +1,264 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +static HRESULT SendRequiredBextMessage( + __in BURN_EXTENSION* pExtension, + __in BUNDLE_EXTENSION_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ); + +// function definitions + +/******************************************************************* + BurnExtensionParseFromXml - + +*******************************************************************/ +EXTERN_C HRESULT BurnExtensionParseFromXml( + __in BURN_EXTENSIONS* pBurnExtensions, + __in BURN_PAYLOADS* pBaPayloads, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + + // Select BundleExtension nodes. + hr = XmlSelectNodes(pixnBundle, L"BundleExtension", &pixnNodes); + ExitOnFailure(hr, "Failed to select BundleExtension nodes."); + + // Get BundleExtension node count. + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get BundleExtension node count."); + + if (!cNodes) + { + ExitFunction(); + } + + // Allocate memory for BundleExtensions. + pBurnExtensions->rgExtensions = (BURN_EXTENSION*)MemAlloc(sizeof(BURN_EXTENSION) * cNodes, TRUE); + ExitOnNull(pBurnExtensions->rgExtensions, hr, E_OUTOFMEMORY, "Failed to allocate memory for BundleExtension structs."); + + pBurnExtensions->cExtensions = cNodes; + + // parse search elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_EXTENSION* pExtension = &pBurnExtensions->rgExtensions[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &pExtension->sczId); + ExitOnFailure(hr, "Failed to get @Id."); + + // @EntryPayloadId + hr = XmlGetAttributeEx(pixnNode, L"EntryPayloadId", &pExtension->sczEntryPayloadId); + ExitOnFailure(hr, "Failed to get @EntryPayloadId."); + + hr = PayloadFindById(pBaPayloads, pExtension->sczEntryPayloadId, &pExtension->pEntryPayload); + ExitOnFailure(hr, "Failed to find BundleExtension EntryPayload '%ls'.", pExtension->sczEntryPayloadId); + + // prepare next iteration + ReleaseNullObject(pixnNode); + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNode); + ReleaseObject(pixnNodes); + + return hr; +} + +/******************************************************************* + BurnExtensionUninitialize - + +*******************************************************************/ +EXTERN_C void BurnExtensionUninitialize( + __in BURN_EXTENSIONS* pBurnExtensions + ) +{ + if (pBurnExtensions->rgExtensions) + { + for (DWORD i = 0; i < pBurnExtensions->cExtensions; ++i) + { + BURN_EXTENSION* pExtension = &pBurnExtensions->rgExtensions[i]; + + ReleaseStr(pExtension->sczEntryPayloadId); + ReleaseStr(pExtension->sczId); + } + MemFree(pBurnExtensions->rgExtensions); + } + + // clear struct + memset(pBurnExtensions, 0, sizeof(BURN_EXTENSIONS)); +} + +/******************************************************************* + BurnExtensionLoad - + +*******************************************************************/ +EXTERN_C HRESULT BurnExtensionLoad( + __in BURN_EXTENSIONS * pBurnExtensions, + __in BURN_EXTENSION_ENGINE_CONTEXT* pEngineContext + ) +{ + HRESULT hr = S_OK; + LPWSTR sczBundleExtensionDataPath = NULL; + BUNDLE_EXTENSION_CREATE_ARGS args = { }; + BUNDLE_EXTENSION_CREATE_RESULTS results = { }; + + if (!pBurnExtensions->rgExtensions || !pBurnExtensions->cExtensions) + { + ExitFunction(); + } + + hr = PathConcat(pEngineContext->pEngineState->userExperience.sczTempDirectory, L"BundleExtensionData.xml", &sczBundleExtensionDataPath); + ExitOnFailure(hr, "Failed to get BundleExtensionDataPath."); + + for (DWORD i = 0; i < pBurnExtensions->cExtensions; ++i) + { + BURN_EXTENSION* pExtension = &pBurnExtensions->rgExtensions[i]; + + memset(&args, 0, sizeof(BUNDLE_EXTENSION_CREATE_ARGS)); + memset(&results, 0, sizeof(BUNDLE_EXTENSION_CREATE_RESULTS)); + + args.cbSize = sizeof(BUNDLE_EXTENSION_CREATE_ARGS); + args.pfnBundleExtensionEngineProc = EngineForExtensionProc; + args.pvBundleExtensionEngineProcContext = pEngineContext; + args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 4, 27, 0); + args.wzBootstrapperWorkingFolder = pEngineContext->pEngineState->userExperience.sczTempDirectory; + args.wzBundleExtensionDataPath = sczBundleExtensionDataPath; + args.wzExtensionId = pExtension->sczId; + + results.cbSize = sizeof(BUNDLE_EXTENSION_CREATE_RESULTS); + + // Load BundleExtension DLL. + pExtension->hBextModule = ::LoadLibraryExW(pExtension->pEntryPayload->sczLocalFilePath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + ExitOnNullWithLastError(pExtension->hBextModule, hr, "Failed to load BundleExtension DLL '%ls': '%ls'.", pExtension->sczId, pExtension->pEntryPayload->sczLocalFilePath); + + // Get BundleExtensionCreate entry-point. + PFN_BUNDLE_EXTENSION_CREATE pfnCreate = (PFN_BUNDLE_EXTENSION_CREATE)::GetProcAddress(pExtension->hBextModule, "BundleExtensionCreate"); + ExitOnNullWithLastError(pfnCreate, hr, "Failed to get BundleExtensionCreate entry-point '%ls'.", pExtension->sczId); + + // Create BundleExtension. + hr = pfnCreate(&args, &results); + ExitOnFailure(hr, "Failed to create BundleExtension '%ls'.", pExtension->sczId); + + pExtension->pfnBurnExtensionProc = results.pfnBundleExtensionProc; + pExtension->pvBurnExtensionProcContext = results.pvBundleExtensionProcContext; + } + +LExit: + ReleaseStr(sczBundleExtensionDataPath); + + return hr; +} + +/******************************************************************* + BurnExtensionUnload - + +*******************************************************************/ +EXTERN_C void BurnExtensionUnload( + __in BURN_EXTENSIONS * pBurnExtensions + ) +{ + HRESULT hr = S_OK; + + if (pBurnExtensions->rgExtensions) + { + for (DWORD i = 0; i < pBurnExtensions->cExtensions; ++i) + { + BURN_EXTENSION* pExtension = &pBurnExtensions->rgExtensions[i]; + + if (pExtension->hBextModule) + { + // Get BundleExtensionDestroy entry-point and call it if it exists. + PFN_BUNDLE_EXTENSION_DESTROY pfnDestroy = (PFN_BUNDLE_EXTENSION_DESTROY)::GetProcAddress(pExtension->hBextModule, "BundleExtensionDestroy"); + if (pfnDestroy) + { + pfnDestroy(); + } + + // Free BundleExtension DLL. + if (!::FreeLibrary(pExtension->hBextModule)) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + TraceError(hr, "Failed to unload BundleExtension DLL."); + } + pExtension->hBextModule = NULL; + } + } + } +} + +EXTERN_C HRESULT BurnExtensionFindById( + __in BURN_EXTENSIONS* pBurnExtensions, + __in_z LPCWSTR wzId, + __out BURN_EXTENSION** ppExtension + ) +{ + HRESULT hr = S_OK; + BURN_EXTENSION* pExtension = NULL; + + for (DWORD i = 0; i < pBurnExtensions->cExtensions; ++i) + { + pExtension = &pBurnExtensions->rgExtensions[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pExtension->sczId, -1, wzId, -1)) + { + *ppExtension = pExtension; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + +EXTERN_C BEEAPI BurnExtensionPerformSearch( + __in BURN_EXTENSION* pExtension, + __in LPWSTR wzSearchId, + __in LPWSTR wzVariable + ) +{ + HRESULT hr = S_OK; + BUNDLE_EXTENSION_SEARCH_ARGS args = { }; + BUNDLE_EXTENSION_SEARCH_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzId = wzSearchId; + args.wzVariable = wzVariable; + + results.cbSize = sizeof(results); + + hr = SendRequiredBextMessage(pExtension, BUNDLE_EXTENSION_MESSAGE_SEARCH, &args, &results); + ExitOnFailure(hr, "BundleExtension '%ls' Search '%ls' failed.", pExtension->sczId, wzSearchId); + +LExit: + return hr; +} + +static HRESULT SendRequiredBextMessage( + __in BURN_EXTENSION* pExtension, + __in BUNDLE_EXTENSION_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + + hr = pExtension->pfnBurnExtensionProc(message, pvArgs, pvResults, pExtension->pvBurnExtensionProcContext); + + return hr; +} diff --git a/src/burn/engine/burnextension.h b/src/burn/engine/burnextension.h new file mode 100644 index 00000000..370ddd2d --- /dev/null +++ b/src/burn/engine/burnextension.h @@ -0,0 +1,61 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#define BEEAPI HRESULT __stdcall + +#if defined(__cplusplus) +extern "C" { +#endif + +// structs + +typedef struct _BURN_EXTENSION_ENGINE_CONTEXT BURN_EXTENSION_ENGINE_CONTEXT; + +typedef struct _BURN_EXTENSION +{ + LPWSTR sczEntryPayloadId; + LPWSTR sczId; + + BURN_PAYLOAD* pEntryPayload; + + HMODULE hBextModule; + PFN_BUNDLE_EXTENSION_PROC pfnBurnExtensionProc; + LPVOID pvBurnExtensionProcContext; +} BURN_EXTENSION; + +typedef struct _BURN_EXTENSIONS +{ + BURN_EXTENSION* rgExtensions; + DWORD cExtensions; +} BURN_EXTENSIONS; + +// functions + +HRESULT BurnExtensionParseFromXml( + __in BURN_EXTENSIONS* pBurnExtensions, + __in BURN_PAYLOADS* pBaPayloads, + __in IXMLDOMNode* pixnBundle + ); +void BurnExtensionUninitialize( + __in BURN_EXTENSIONS* pBurnExtensions + ); +HRESULT BurnExtensionLoad( + __in BURN_EXTENSIONS* pBurnExtensions, + __in BURN_EXTENSION_ENGINE_CONTEXT* pEngineContext + ); +void BurnExtensionUnload( + __in BURN_EXTENSIONS* pBurnExtensions + ); +HRESULT BurnExtensionFindById( + __in BURN_EXTENSIONS* pBurnExtensions, + __in_z LPCWSTR wzId, + __out BURN_EXTENSION** ppExtension + ); +BEEAPI BurnExtensionPerformSearch( + __in BURN_EXTENSION* pExtension, + __in LPWSTR wzSearchId, + __in LPWSTR wzVariable + ); +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/cabextract.cpp b/src/burn/engine/cabextract.cpp new file mode 100644 index 00000000..5a02ff8a --- /dev/null +++ b/src/burn/engine/cabextract.cpp @@ -0,0 +1,974 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#include + +#define ARRAY_GROWTH_SIZE 2 + +const LPSTR INVALID_CAB_NAME = ".cab"; + +// structs + +typedef struct _BURN_CAB_CONTEXT +{ + HANDLE hFile; + DWORD64 qwOffset; + DWORD64 qwSize; + + HANDLE hThread; + HANDLE hBeginOperationEvent; + HANDLE hOperationCompleteEvent; + + BURN_CAB_OPERATION operation; + HRESULT hrError; + + LPWSTR* psczStreamName; + LPCWSTR wzTargetFile; + HANDLE hTargetFile; + BYTE* pbTargetBuffer; + DWORD cbTargetBuffer; + DWORD iTargetBuffer; +} BURN_CAB_CONTEXT; + + +// internal function declarations + +static HRESULT BeginAndWaitForOperation( + __in BURN_CONTAINER_CONTEXT* pContext + ); +static HRESULT WaitForOperation( + __in BURN_CONTAINER_CONTEXT* pContext + ); +static DWORD WINAPI ExtractThreadProc( + __in LPVOID lpThreadParameter + ); +static INT_PTR DIAMONDAPI CabNotifyCallback( + __in FDINOTIFICATIONTYPE iNotification, + __inout FDINOTIFICATION *pFDINotify + ); +static INT_PTR CopyFileCallback( + __in BURN_CONTAINER_CONTEXT* pContext, + __inout FDINOTIFICATION *pFDINotify + ); +static INT_PTR CloseFileInfoCallback( + __in BURN_CONTAINER_CONTEXT* pContext, + __inout FDINOTIFICATION *pFDINotify + ); +static LPVOID DIAMONDAPI CabAlloc( + __in DWORD dwSize + ); +static void DIAMONDAPI CabFree( + __in LPVOID pvData + ); +static INT_PTR FAR DIAMONDAPI CabOpen( + __in char FAR *pszFile, + __in int /* oflag */, + __in int /* pmode */ + ); +static UINT FAR DIAMONDAPI CabRead( + __in INT_PTR hf, + __out void FAR *pv, + __in UINT cb + ); +static UINT FAR DIAMONDAPI CabWrite( + __in INT_PTR hf, + __in void FAR *pv, + __in UINT cb + ); +static long FAR DIAMONDAPI CabSeek( + __in INT_PTR hf, + __in long dist, + __in int seektype + ); +static int FAR DIAMONDAPI CabClose( + __in INT_PTR hf + ); +static HRESULT AddVirtualFilePointer( + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, + __in HANDLE hFile, + __in LONGLONG llInitialFilePointer + ); +static HRESULT ReadIfVirtualFilePointer( + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, + __in HANDLE hFile, + __in DWORD cbRead + ); +static BOOL SetIfVirtualFilePointer( + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, + __in HANDLE hFile, + __in LONGLONG llDistance, + __out LONGLONG* pllNewPostion, + __in DWORD dwSeekType + ); +static HRESULT CloseIfVirturalFilePointer( + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, + __in HANDLE hFile + ); +static BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* GetVirtualFilePointer( + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, + __in HANDLE hFile + ); + + +// internal variables + +__declspec(thread) static BURN_CONTAINER_CONTEXT* vpContext; + + +// function definitions + +extern "C" void CabExtractInitialize() +{ +} + +extern "C" HRESULT CabExtractOpen( + __in BURN_CONTAINER_CONTEXT* pContext, + __in LPCWSTR wzFilePath + ) +{ + HRESULT hr = S_OK; + + // initialize context + pContext->Cabinet.hTargetFile = INVALID_HANDLE_VALUE; + + hr = StrAllocString(&pContext->Cabinet.sczFile, wzFilePath, 0); + ExitOnFailure(hr, "Failed to copy file name."); + + // create events + pContext->Cabinet.hBeginOperationEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL); + ExitOnNullWithLastError(pContext->Cabinet.hBeginOperationEvent, hr, "Failed to create begin operation event."); + + pContext->Cabinet.hOperationCompleteEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL); + ExitOnNullWithLastError(pContext->Cabinet.hOperationCompleteEvent, hr, "Failed to create operation complete event."); + + // create extraction thread + pContext->Cabinet.hThread = ::CreateThread(NULL, 0, ExtractThreadProc, pContext, 0, NULL); + ExitOnNullWithLastError(pContext->Cabinet.hThread, hr, "Failed to create extraction thread."); + + // wait for operation to complete + hr = WaitForOperation(pContext); + ExitOnFailure(hr, "Failed to wait for operation complete."); + +LExit: + return hr; +} + +extern "C" HRESULT CabExtractNextStream( + __in BURN_CONTAINER_CONTEXT* pContext, + __inout_z LPWSTR* psczStreamName + ) +{ + HRESULT hr = S_OK; + + // set operation to move to next stream + pContext->Cabinet.operation = BURN_CAB_OPERATION_NEXT_STREAM; + pContext->Cabinet.psczStreamName = psczStreamName; + + // begin operation and wait + hr = BeginAndWaitForOperation(pContext); + if (E_ABORT != hr && E_NOMOREITEMS != hr) + { + ExitOnFailure(hr, "Failed to begin and wait for operation."); + } + +LExit: + return hr; +} + +extern "C" HRESULT CabExtractStreamToFile( + __in BURN_CONTAINER_CONTEXT* pContext, + __in_z LPCWSTR wzFileName + ) +{ + HRESULT hr = S_OK; + + // set operation to move to next stream + pContext->Cabinet.operation = BURN_CAB_OPERATION_STREAM_TO_FILE; + pContext->Cabinet.wzTargetFile = wzFileName; + + // begin operation and wait + hr = BeginAndWaitForOperation(pContext); + ExitOnFailure(hr, "Failed to begin and wait for operation."); + + // clear file name + pContext->Cabinet.wzTargetFile = NULL; + +LExit: + return hr; +} + +extern "C" HRESULT CabExtractStreamToBuffer( + __in BURN_CONTAINER_CONTEXT* pContext, + __out BYTE** ppbBuffer, + __out SIZE_T* pcbBuffer + ) +{ + HRESULT hr = S_OK; + + // set operation to move to next stream + pContext->Cabinet.operation = BURN_CAB_OPERATION_STREAM_TO_BUFFER; + + // begin operation and wait + hr = BeginAndWaitForOperation(pContext); + ExitOnFailure(hr, "Failed to begin and wait for operation."); + + // return values + *ppbBuffer = pContext->Cabinet.pbTargetBuffer; + *pcbBuffer = pContext->Cabinet.cbTargetBuffer; + + // clear buffer variables + pContext->Cabinet.pbTargetBuffer = NULL; + pContext->Cabinet.cbTargetBuffer = 0; + pContext->Cabinet.iTargetBuffer = 0; + +LExit: + return hr; +} + +extern "C" HRESULT CabExtractSkipStream( + __in BURN_CONTAINER_CONTEXT* pContext + ) +{ + HRESULT hr = S_OK; + + // set operation to move to next stream + pContext->Cabinet.operation = BURN_CAB_OPERATION_SKIP_STREAM; + + // begin operation and wait + hr = BeginAndWaitForOperation(pContext); + ExitOnFailure(hr, "Failed to begin and wait for operation."); + +LExit: + return hr; +} + +extern "C" HRESULT CabExtractClose( + __in BURN_CONTAINER_CONTEXT* pContext + ) +{ + HRESULT hr = S_OK; + + // terminate worker thread + if (pContext->Cabinet.hThread) + { + // set operation to move to close + pContext->Cabinet.operation = BURN_CAB_OPERATION_CLOSE; + + // set begin operation event + if (!::SetEvent(pContext->Cabinet.hBeginOperationEvent)) + { + ExitWithLastError(hr, "Failed to set begin operation event."); + } + + // wait for thread to terminate + if (WAIT_OBJECT_0 != ::WaitForSingleObject(pContext->Cabinet.hThread, INFINITE)) + { + ExitWithLastError(hr, "Failed to wait for thread to terminate."); + } + } + +LExit: + ReleaseHandle(pContext->Cabinet.hThread); + ReleaseHandle(pContext->Cabinet.hBeginOperationEvent); + ReleaseHandle(pContext->Cabinet.hOperationCompleteEvent); + ReleaseMem(pContext->Cabinet.rgVirtualFilePointers); + ReleaseStr(pContext->Cabinet.sczFile); + + return hr; +} + + +// internal helper functions + +static HRESULT BeginAndWaitForOperation( + __in BURN_CONTAINER_CONTEXT* pContext + ) +{ + HRESULT hr = S_OK; + + // set begin operation event + if (!::SetEvent(pContext->Cabinet.hBeginOperationEvent)) + { + ExitWithLastError(hr, "Failed to set begin operation event."); + } + + // wait for operation to complete + hr = WaitForOperation(pContext); + +LExit: + return hr; +} + +static HRESULT WaitForOperation( + __in BURN_CONTAINER_CONTEXT* pContext + ) +{ + HRESULT hr = S_OK; + HANDLE rghWait[2] = { }; + + // wait for operation complete event + rghWait[0] = pContext->Cabinet.hOperationCompleteEvent; + rghWait[1] = pContext->Cabinet.hThread; + switch (::WaitForMultipleObjects(countof(rghWait), rghWait, FALSE, INFINITE)) + { + case WAIT_OBJECT_0: + if (!::ResetEvent(pContext->Cabinet.hOperationCompleteEvent)) + { + ExitWithLastError(hr, "Failed to reset operation complete event."); + } + break; + + case WAIT_OBJECT_0 + 1: + if (!::GetExitCodeThread(pContext->Cabinet.hThread, (DWORD*)&hr)) + { + ExitWithLastError(hr, "Failed to get extraction thread exit code."); + } + ExitFunction(); + + case WAIT_FAILED: __fallthrough; + default: + ExitWithLastError(hr, "Failed to wait for operation complete event."); + } + + // clear operation + pContext->Cabinet.operation = BURN_CAB_OPERATION_NONE; + +LExit: + return hr; +} + +static DWORD WINAPI ExtractThreadProc( + __in LPVOID lpThreadParameter + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER_CONTEXT* pContext = (BURN_CONTAINER_CONTEXT*)lpThreadParameter; + BOOL fComInitialized = FALSE; + HFDI hfdi = NULL; + ERF erf = { }; + + // initialize COM + hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); + ExitOnFailure(hr, "Failed to initialize COM."); + fComInitialized = TRUE; + + // save context in TLS storage + vpContext = pContext; + + // create FDI context + hfdi = ::FDICreate(CabAlloc, CabFree, CabOpen, CabRead, CabWrite, CabClose, CabSeek, cpuUNKNOWN, &erf); + ExitOnNull(hfdi, hr, E_FAIL, "Failed to initialize cabinet.dll."); + + // begin CAB extraction + if (!::FDICopy(hfdi, INVALID_CAB_NAME, "", 0, CabNotifyCallback, NULL, NULL)) + { + hr = pContext->Cabinet.hrError; + if (E_ABORT == hr || E_NOMOREITEMS == hr) + { + ExitFunction(); + } + else if (SUCCEEDED(hr)) + { + if (ERROR_SUCCESS != erf.erfType) + { + hr = HRESULT_FROM_WIN32(erf.erfType); + } + else + { + switch (erf.erfOper) + { + case FDIERROR_NONE: + hr = E_UNEXPECTED; + break; + case FDIERROR_CABINET_NOT_FOUND: + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + break; + case FDIERROR_NOT_A_CABINET: + hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); + break; + case FDIERROR_UNKNOWN_CABINET_VERSION: + hr = HRESULT_FROM_WIN32(ERROR_VERSION_PARSE_ERROR); + break; + case FDIERROR_CORRUPT_CABINET: + hr = HRESULT_FROM_WIN32(ERROR_FILE_CORRUPT); + break; + case FDIERROR_ALLOC_FAIL: + hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); + break; + case FDIERROR_BAD_COMPR_TYPE: + hr = HRESULT_FROM_WIN32(ERROR_UNSUPPORTED_COMPRESSION); + break; + case FDIERROR_MDI_FAIL: + hr = HRESULT_FROM_WIN32(ERROR_BAD_COMPRESSION_BUFFER); + break; + case FDIERROR_TARGET_FILE: + hr = HRESULT_FROM_WIN32(ERROR_WRITE_FAULT); + break; + case FDIERROR_RESERVE_MISMATCH: + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + break; + case FDIERROR_WRONG_CABINET: + hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH); + break; + case FDIERROR_USER_ABORT: + hr = E_ABORT; + break; + default: + hr = E_FAIL; + break; + } + } + } + ExitOnFailure(hr, "Failed to extract all files from container, erf: %d:%X:%d", erf.fError, erf.erfOper, erf.erfType); + } + + // set operation complete event + if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent)) + { + ExitWithLastError(hr, "Failed to set operation complete event."); + } + + // wait for begin operation event + if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE)) + { + ExitWithLastError(hr, "Failed to wait for begin operation event."); + } + + if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent)) + { + ExitWithLastError(hr, "Failed to reset begin operation event."); + } + + // read operation + switch (pContext->Cabinet.operation) + { + case BURN_CAB_OPERATION_NEXT_STREAM: + ExitFunction1(hr = E_NOMOREITEMS); + break; + + case BURN_CAB_OPERATION_CLOSE: + ExitFunction1(hr = S_OK); + + default: + hr = E_INVALIDSTATE; + ExitOnRootFailure(hr, "Invalid operation for this state."); + } + +LExit: + if (hfdi) + { + ::FDIDestroy(hfdi); + } + if (fComInitialized) + { + ::CoUninitialize(); + } + + return (DWORD)hr; +} + +static INT_PTR DIAMONDAPI CabNotifyCallback( + __in FDINOTIFICATIONTYPE iNotification, + __inout FDINOTIFICATION *pFDINotify + ) +{ + BURN_CONTAINER_CONTEXT* pContext = vpContext; + INT_PTR ipResult = 0; // result to return on success + + switch (iNotification) + { + case fdintCOPY_FILE: + ipResult = CopyFileCallback(pContext, pFDINotify); + break; + + case fdintCLOSE_FILE_INFO: // resource extraction complete + ipResult = CloseFileInfoCallback(pContext, pFDINotify); + break; + + case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages + case fdintNEXT_CABINET: __fallthrough; + case fdintENUMERATE: __fallthrough; + case fdintCABINET_INFO: + break; + + default: + AssertSz(FALSE, "CabExtractCallback() - unknown FDI notification command"); + }; + +//LExit: + return ipResult; +} + +static INT_PTR CopyFileCallback( + __in BURN_CONTAINER_CONTEXT* pContext, + __inout FDINOTIFICATION* pFDINotify + ) +{ + HRESULT hr = S_OK; + INT_PTR ipResult = 1; // result to return on success + LPWSTR pwzPath = NULL; + LARGE_INTEGER li = { }; + + // set operation complete event + if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent)) + { + ExitWithLastError(hr, "Failed to set operation complete event."); + } + + // wait for begin operation event + if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE)) + { + ExitWithLastError(hr, "Failed to wait for begin operation event."); + } + + if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent)) + { + ExitWithLastError(hr, "Failed to reset begin operation event."); + } + + // read operation + switch (pContext->Cabinet.operation) + { + case BURN_CAB_OPERATION_NEXT_STREAM: + break; + + case BURN_CAB_OPERATION_CLOSE: + ExitFunction1(hr = E_ABORT); + + default: + hr = E_INVALIDSTATE; + ExitOnRootFailure(hr, "Invalid operation for this state."); + } + + // copy stream name + hr = StrAllocStringAnsi(pContext->Cabinet.psczStreamName, pFDINotify->psz1, 0, CP_UTF8); + ExitOnFailure(hr, "Failed to copy stream name: %hs", pFDINotify->psz1); + + // set operation complete event + if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent)) + { + ExitWithLastError(hr, "Failed to set operation complete event."); + } + + // wait for begin operation event + if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE)) + { + ExitWithLastError(hr, "Failed to wait for begin operation event."); + } + + if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent)) + { + ExitWithLastError(hr, "Failed to reset begin operation event."); + } + + // read operation + switch (pContext->Cabinet.operation) + { + case BURN_CAB_OPERATION_STREAM_TO_FILE: + // create file + pContext->Cabinet.hTargetFile = ::CreateFileW(pContext->Cabinet.wzTargetFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == pContext->Cabinet.hTargetFile) + { + ExitWithLastError(hr, "Failed to create file: %ls", pContext->Cabinet.wzTargetFile); + } + + // set file size + li.QuadPart = pFDINotify->cb; + if (!::SetFilePointerEx(pContext->Cabinet.hTargetFile, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to set file pointer to end of file."); + } + + if (!::SetEndOfFile(pContext->Cabinet.hTargetFile)) + { + ExitWithLastError(hr, "Failed to set end of file."); + } + + li.QuadPart = 0; + if (!::SetFilePointerEx(pContext->Cabinet.hTargetFile, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to set file pointer to beginning of file."); + } + + break; + + case BURN_CAB_OPERATION_STREAM_TO_BUFFER: + // allocate buffer for stream + pContext->Cabinet.pbTargetBuffer = (BYTE*)MemAlloc(pFDINotify->cb, TRUE); + ExitOnNull(pContext->Cabinet.pbTargetBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for stream."); + + // set buffer size and write position + pContext->Cabinet.cbTargetBuffer = pFDINotify->cb; + pContext->Cabinet.iTargetBuffer = 0; + + break; + + case BURN_CAB_OPERATION_SKIP_STREAM: + ipResult = 0; + break; + + case BURN_CAB_OPERATION_CLOSE: + ExitFunction1(hr = E_ABORT); + + default: + hr = E_INVALIDSTATE; + ExitOnRootFailure(hr, "Invalid operation for this state."); + } + +LExit: + ReleaseStr(pwzPath); + + pContext->Cabinet.hrError = hr; + return SUCCEEDED(hr) ? ipResult : -1; +} + +static INT_PTR CloseFileInfoCallback( + __in BURN_CONTAINER_CONTEXT* pContext, + __inout FDINOTIFICATION *pFDINotify + ) +{ + HRESULT hr = S_OK; + INT_PTR ipResult = 1; // result to return on success + FILETIME ftLocal = { }; + FILETIME ft = { }; + + // read operation + switch (pContext->Cabinet.operation) + { + case BURN_CAB_OPERATION_STREAM_TO_FILE: + // Make a best effort to set the time on the new file before + // we close it. + if (::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal)) + { + if (::LocalFileTimeToFileTime(&ftLocal, &ft)) + { + ::SetFileTime(pContext->Cabinet.hTargetFile, &ft, &ft, &ft); + } + } + + // close file + ReleaseFile(pContext->Cabinet.hTargetFile); + break; + + case BURN_CAB_OPERATION_STREAM_TO_BUFFER: + break; + + case BURN_CAB_OPERATION_CLOSE: + ExitFunction1(hr = E_ABORT); + + default: + hr = E_INVALIDSTATE; + ExitOnRootFailure(hr, "Invalid operation for this state."); + } + + //if (pContext->pfnProgress) + //{ + // hr = StrAllocFormatted(&pwzPath, L"%s%ls", pContext->wzRootPath, pFDINotify->psz1); + // ExitOnFailure(hr, "Failed to calculate file path from: %ls and %s", pContext->wzRootPath, pFDINotify->psz1); + // if (SUCCEEDED(hr)) + // { + // hr = pContext->pfnProgress(BOX_PROGRESS_DECOMPRESSION_END, pwzPath, 0, pContext->pvContext); + // if (S_OK != hr) + // { + // pContext->hrError = hr; + // ExitFunction(); + // } + // } + //} + +LExit: + pContext->Cabinet.hrError = hr; + return SUCCEEDED(hr) ? ipResult : -1; +} + +static LPVOID DIAMONDAPI CabAlloc( + __in DWORD dwSize + ) +{ + return MemAlloc(dwSize, FALSE); +} + +static void DIAMONDAPI CabFree( + __in LPVOID pvData + ) +{ + MemFree(pvData); +} + +static INT_PTR FAR DIAMONDAPI CabOpen( + __in char FAR * pszFile, + __in int /* oflag */, + __in int /* pmode */ + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER_CONTEXT* pContext = vpContext; + HANDLE hFile = INVALID_HANDLE_VALUE; + + // If this is the invalid cab name, use our file handle. + if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, INVALID_CAB_NAME, -1, pszFile, -1)) + { + if (!::DuplicateHandle(::GetCurrentProcess(), pContext->hFile, ::GetCurrentProcess(), &hFile, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + ExitWithLastError(hr, "Failed to duplicate handle to cab container."); + } + + // Use a virtual file pointer since duplicated file handles share their file pointer. Seek to container offset + // to start. + hr = AddVirtualFilePointer(&pContext->Cabinet, hFile, pContext->qwOffset); + ExitOnFailure(hr, "Failed to add virtual file pointer for cab container."); + } + else // open file requested. This is used in the rare cases where the CAB API wants to create a temp file. + { + hFile = ::CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open cabinet file: %hs", pszFile); + } + +LExit: + pContext->Cabinet.hrError = hr; + return FAILED(hr) ? -1 : (INT_PTR)hFile; +} + +static UINT FAR DIAMONDAPI CabRead( + __in INT_PTR hf, + __out void FAR *pv, + __in UINT cb + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER_CONTEXT* pContext = vpContext; + HANDLE hFile = (HANDLE)hf; + DWORD cbRead = 0; + + ReadIfVirtualFilePointer(&pContext->Cabinet, hFile, cb); + + if (!::ReadFile(hFile, pv, cb, &cbRead, NULL)) + { + ExitWithLastError(hr, "Failed to read during cabinet extraction."); + } + +LExit: + pContext->Cabinet.hrError = hr; + return FAILED(hr) ? -1 : cbRead; +} + +static UINT FAR DIAMONDAPI CabWrite( + __in INT_PTR /* hf */, + __in void FAR *pv, + __in UINT cb + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER_CONTEXT* pContext = vpContext; + DWORD cbWrite = 0; + + switch (pContext->Cabinet.operation) + { + case BURN_CAB_OPERATION_STREAM_TO_FILE: + // write file + if (!::WriteFile(pContext->Cabinet.hTargetFile, pv, cb, &cbWrite, NULL)) + { + ExitWithLastError(hr, "Failed to write during cabinet extraction."); + } + break; + + case BURN_CAB_OPERATION_STREAM_TO_BUFFER: + // copy to target buffer + memcpy_s(pContext->Cabinet.pbTargetBuffer + pContext->Cabinet.iTargetBuffer, pContext->Cabinet.cbTargetBuffer - pContext->Cabinet.iTargetBuffer, pv, cb); + pContext->Cabinet.iTargetBuffer += cb; + + cbWrite = cb; + break; + + default: + hr = E_INVALIDSTATE; + ExitOnFailure(hr, "Unexpected call to CabWrite()."); + } + +LExit: + pContext->Cabinet.hrError = hr; + return FAILED(hr) ? -1 : cbWrite; +} + +static long FAR DIAMONDAPI CabSeek( + __in INT_PTR hf, + __in long dist, + __in int seektype + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER_CONTEXT* pContext = vpContext; + HANDLE hFile = (HANDLE)hf; + LARGE_INTEGER liDistance = { }; + LARGE_INTEGER liNewPointer = { }; + DWORD dwSeekType = 0; + + // We assume that CabSeek() will only be called to seek the + // cabinet itself so we have to offset the seek operations to + // where the internal cabinet starts. + switch (seektype) + { + case FILE_BEGIN: + liDistance.QuadPart = pContext->qwOffset + dist; + dwSeekType = FILE_BEGIN; + break; + + case FILE_CURRENT: + liDistance.QuadPart = dist; + dwSeekType = FILE_CURRENT; + break; + + case FILE_END: + liDistance.QuadPart = pContext->qwOffset + pContext->qwSize + dist; + dwSeekType = FILE_BEGIN; + break; + + default: + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid seek type.");; + } + + if (SetIfVirtualFilePointer(&pContext->Cabinet, hFile, liDistance.QuadPart, &liNewPointer.QuadPart, seektype)) + { + // set file pointer + if (!::SetFilePointerEx(hFile, liDistance, &liNewPointer, seektype)) + { + ExitWithLastError(hr, "Failed to move file pointer 0x%x bytes.", dist); + } + } + + liNewPointer.QuadPart -= pContext->qwOffset; + +LExit: + pContext->Cabinet.hrError = hr; + return FAILED(hr) ? -1 : liNewPointer.LowPart; +} + +static int FAR DIAMONDAPI CabClose( + __in INT_PTR hf + ) +{ + BURN_CONTAINER_CONTEXT* pContext = vpContext; + HANDLE hFile = (HANDLE)hf; + + CloseIfVirturalFilePointer(&pContext->Cabinet, hFile); + ReleaseFileHandle(hFile); + + return 0; +} + +static HRESULT AddVirtualFilePointer( + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, + __in HANDLE hFile, + __in LONGLONG llInitialFilePointer + ) +{ + HRESULT hr = S_OK; + + hr = MemEnsureArraySize(reinterpret_cast(&pCabinetContext->rgVirtualFilePointers), pCabinetContext->cVirtualFilePointers, sizeof(BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER), ARRAY_GROWTH_SIZE); + ExitOnFailure(hr, "Failed to allocate memory for the virtual file pointer array."); + + pCabinetContext->rgVirtualFilePointers[pCabinetContext->cVirtualFilePointers].hFile = hFile; + pCabinetContext->rgVirtualFilePointers[pCabinetContext->cVirtualFilePointers].liPosition.QuadPart = llInitialFilePointer; + ++pCabinetContext->cVirtualFilePointers; + +LExit: + return hr; +} + +static HRESULT ReadIfVirtualFilePointer( + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, + __in HANDLE hFile, + __in DWORD cbRead + ) +{ + HRESULT hr = E_NOTFOUND; + + BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile); + if (pVfp) + { + // Set the file handle to the virtual file pointer. + if (!::SetFilePointerEx(hFile, pVfp->liPosition, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to move to virtual file pointer."); + } + + pVfp->liPosition.QuadPart += cbRead; // add the amount that will be read to advance the pointer. + hr = S_OK; + } + +LExit: + return hr; +} + +static BOOL SetIfVirtualFilePointer( + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, + __in HANDLE hFile, + __in LONGLONG llDistance, + __out LONGLONG* pllNewPostion, + __in DWORD dwSeekType + ) +{ + BOOL fFound = FALSE; + + BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile); + if (pVfp) + { + switch (dwSeekType) + { + case FILE_BEGIN: + pVfp->liPosition.QuadPart = llDistance; + break; + + case FILE_CURRENT: + pVfp->liPosition.QuadPart += llDistance; + break; + + case FILE_END: __fallthrough; + default: + AssertSz(FALSE, "Unsupported seek type."); + break; + } + + *pllNewPostion = pVfp->liPosition.QuadPart; + fFound = TRUE; + } + + return fFound; +} + +static HRESULT CloseIfVirturalFilePointer( + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, + __in HANDLE hFile + ) +{ + HRESULT hr = E_NOTFOUND; + + BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile); + if (pVfp) + { + pVfp->hFile = INVALID_HANDLE_VALUE; + pVfp->liPosition.QuadPart = 0; + hr = S_OK; + } + + return hr; +} + +static BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* GetVirtualFilePointer( + __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, + __in HANDLE hFile + ) +{ + for (DWORD i = 0; i < pCabinetContext->cVirtualFilePointers; ++i) + { + BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = pCabinetContext->rgVirtualFilePointers + i; + if (pVfp->hFile == hFile) + { + return pVfp; + } + } + + return NULL; +} diff --git a/src/burn/engine/cabextract.h b/src/burn/engine/cabextract.h new file mode 100644 index 00000000..31667f2b --- /dev/null +++ b/src/burn/engine/cabextract.h @@ -0,0 +1,40 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// function declarations + +void CabExtractInitialize(); +HRESULT CabExtractOpen( + __in BURN_CONTAINER_CONTEXT* pContext, + __in LPCWSTR wzFilePath + ); +HRESULT CabExtractNextStream( + __in BURN_CONTAINER_CONTEXT* pContext, + __inout_z LPWSTR* psczStreamName + ); +HRESULT CabExtractStreamToFile( + __in BURN_CONTAINER_CONTEXT* pContext, + __in_z LPCWSTR wzFileName + ); +HRESULT CabExtractStreamToBuffer( + __in BURN_CONTAINER_CONTEXT* pContext, + __out BYTE** ppbBuffer, + __out SIZE_T* pcbBuffer + ); +HRESULT CabExtractSkipStream( + __in BURN_CONTAINER_CONTEXT* pContext + ); +HRESULT CabExtractClose( + __in BURN_CONTAINER_CONTEXT* pContext + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/cache.cpp b/src/burn/engine/cache.cpp new file mode 100644 index 00000000..59daf139 --- /dev/null +++ b/src/burn/engine/cache.cpp @@ -0,0 +1,2052 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static const LPCWSTR BUNDLE_CLEAN_ROOM_WORKING_FOLDER_NAME = L".cr"; +static const LPCWSTR BUNDLE_WORKING_FOLDER_NAME = L".be"; +static const LPCWSTR UNVERIFIED_CACHE_FOLDER_NAME = L".unverified"; +static const LPCWSTR PACKAGE_CACHE_FOLDER_NAME = L"Package Cache"; +static const DWORD FILE_OPERATION_RETRY_COUNT = 3; +static const DWORD FILE_OPERATION_RETRY_WAIT = 2000; + +static BOOL vfInitializedCache = FALSE; +static BOOL vfRunningFromCache = FALSE; +static LPWSTR vsczSourceProcessFolder = NULL; +static LPWSTR vsczWorkingFolder = NULL; +static LPWSTR vsczDefaultUserPackageCache = NULL; +static LPWSTR vsczDefaultMachinePackageCache = NULL; +static LPWSTR vsczCurrentMachinePackageCache = NULL; + +static HRESULT CalculateWorkingFolder( + __in_z LPCWSTR wzBundleId, + __deref_out_z LPWSTR* psczWorkingFolder + ); +static HRESULT GetLastUsedSourceFolder( + __in BURN_VARIABLES* pVariables, + __out_z LPWSTR* psczLastSource + ); +static HRESULT CreateCompletedPath( + __in BOOL fPerMachine, + __in LPCWSTR wzCacheId, + __out LPWSTR* psczCacheDirectory + ); +static HRESULT CreateUnverifiedPath( + __in BOOL fPerMachine, + __in_z LPCWSTR wzPayloadId, + __out_z LPWSTR* psczUnverifiedPayloadPath + ); +static HRESULT GetRootPath( + __in BOOL fPerMachine, + __in BOOL fAllowRedirect, + __deref_out_z LPWSTR* psczRootPath + ); +static HRESULT VerifyThenTransferContainer( + __in BURN_CONTAINER* pContainer, + __in_z LPCWSTR wzCachedPath, + __in_z LPCWSTR wzUnverifiedContainerPath, + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ); +static HRESULT VerifyThenTransferPayload( + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzCachedPath, + __in_z LPCWSTR wzUnverifiedPayloadPath, + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ); +static HRESULT CacheTransferFileWithRetry( + __in_z LPCWSTR wzSourcePath, + __in_z LPCWSTR wzDestinationPath, + __in BOOL fMove, + __in BURN_CACHE_STEP cacheStep, + __in DWORD64 qwFileSize, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ); +static HRESULT VerifyFileAgainstContainer( + __in BURN_CONTAINER* pContainer, + __in_z LPCWSTR wzVerifyPath, + __in BOOL fAlreadyCached, + __in BURN_CACHE_STEP cacheStep, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ); +static HRESULT VerifyFileAgainstPayload( + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzVerifyPath, + __in BOOL fAlreadyCached, + __in BURN_CACHE_STEP cacheStep, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ); +static HRESULT ResetPathPermissions( + __in BOOL fPerMachine, + __in_z LPCWSTR wzPath + ); +static HRESULT SecurePath( + __in LPCWSTR wzPath + ); +static HRESULT CopyEngineToWorkingFolder( + __in_z LPCWSTR wzSourcePath, + __in_z LPCWSTR wzWorkingFolderName, + __in_z LPCWSTR wzExecutableName, + __in BURN_SECTION* pSection, + __deref_out_z_opt LPWSTR* psczEngineWorkingPath + ); +static HRESULT CopyEngineWithSignatureFixup( + __in HANDLE hEngineFile, + __in_z LPCWSTR wzEnginePath, + __in_z LPCWSTR wzTargetPath, + __in BURN_SECTION* pSection + ); +static HRESULT RemoveBundleOrPackage( + __in BOOL fBundle, + __in BOOL fPerMachine, + __in_z LPCWSTR wzBundleOrPackageId, + __in_z LPCWSTR wzCacheId + ); +static HRESULT VerifyHash( + __in BYTE* pbHash, + __in DWORD cbHash, + __in DWORD64 qwFileSize, + __in_z LPCWSTR wzUnverifiedPayloadPath, + __in HANDLE hFile, + __in BURN_CACHE_STEP cacheStep, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ); +static HRESULT SendCacheBeginMessage( + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPVOID pContext, + __in BURN_CACHE_STEP cacheStep + ); +static HRESULT SendCacheSuccessMessage( + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPVOID pContext, + __in DWORD64 qwFileSize + ); +static HRESULT SendCacheCompleteMessage( + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPVOID pContext, + __in HRESULT hrStatus + ); + + +extern "C" HRESULT CacheInitialize( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in_z_opt LPCWSTR wzSourceProcessPath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCurrentPath = NULL; + LPWSTR sczCompletedFolder = NULL; + LPWSTR sczCompletedPath = NULL; + LPWSTR sczOriginalSource = NULL; + LPWSTR sczOriginalSourceFolder = NULL; + int nCompare = 0; + + if (!vfInitializedCache) + { + hr = PathForCurrentProcess(&sczCurrentPath, NULL); + ExitOnFailure(hr, "Failed to get current process path."); + + // Determine if we are running from the package cache or not. + hr = CacheGetCompletedPath(pRegistration->fPerMachine, pRegistration->sczId, &sczCompletedFolder); + ExitOnFailure(hr, "Failed to get completed path for bundle."); + + hr = PathConcat(sczCompletedFolder, pRegistration->sczExecutableName, &sczCompletedPath); + ExitOnFailure(hr, "Failed to combine working path with engine file name."); + + hr = PathCompare(sczCurrentPath, sczCompletedPath, &nCompare); + ExitOnFailure(hr, "Failed to compare current path for bundle: %ls", sczCurrentPath); + + vfRunningFromCache = (CSTR_EQUAL == nCompare); + + // If a source process path was not provided (e.g. we are not being + // run in a clean room) then use the current process path as the + // source process path. + if (!wzSourceProcessPath) + { + wzSourceProcessPath = sczCurrentPath; + } + + hr = PathGetDirectory(wzSourceProcessPath, &vsczSourceProcessFolder); + ExitOnFailure(hr, "Failed to initialize cache source folder."); + + // If we're not running from the cache, ensure the original source is set. + if (!vfRunningFromCache) + { + // If the original source has not been set already then set it where the bundle is + // running from right now. This value will be persisted and we'll use it when launched + // from the clean room or package cache since none of our packages will be relative to + // those locations. + hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource); + if (E_NOTFOUND == hr) + { + hr = VariableSetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, wzSourceProcessPath, FALSE, FALSE); + ExitOnFailure(hr, "Failed to set original source variable."); + + hr = StrAllocString(&sczOriginalSource, wzSourceProcessPath, 0); + ExitOnFailure(hr, "Failed to copy current path to original source."); + } + + hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, &sczOriginalSourceFolder); + if (E_NOTFOUND == hr) + { + hr = PathGetDirectory(sczOriginalSource, &sczOriginalSourceFolder); + ExitOnFailure(hr, "Failed to get directory from original source path."); + + hr = VariableSetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, sczOriginalSourceFolder, FALSE, FALSE); + ExitOnFailure(hr, "Failed to set original source directory variable."); + } + } + + vfInitializedCache = TRUE; + } + +LExit: + ReleaseStr(sczCurrentPath); + ReleaseStr(sczCompletedFolder); + ReleaseStr(sczCompletedPath); + ReleaseStr(sczOriginalSource); + ReleaseStr(sczOriginalSourceFolder); + + return hr; +} + +extern "C" HRESULT CacheEnsureWorkingFolder( + __in_z_opt LPCWSTR wzBundleId, + __deref_out_z_opt LPWSTR* psczWorkingFolder + ) +{ + HRESULT hr = S_OK; + LPWSTR sczWorkingFolder = NULL; + + hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder); + ExitOnFailure(hr, "Failed to calculate working folder to ensure it exists."); + + hr = DirEnsureExists(sczWorkingFolder, NULL); + ExitOnFailure(hr, "Failed create working folder."); + + // Best effort to ensure our working folder is not encrypted. + ::DecryptFileW(sczWorkingFolder, 0); + + if (psczWorkingFolder) + { + hr = StrAllocString(psczWorkingFolder, sczWorkingFolder, 0); + ExitOnFailure(hr, "Failed to copy working folder."); + } + +LExit: + ReleaseStr(sczWorkingFolder); + + return hr; +} + +extern "C" HRESULT CacheCalculateBundleWorkingPath( + __in_z LPCWSTR wzBundleId, + __in LPCWSTR wzExecutableName, + __deref_out_z LPWSTR* psczWorkingPath + ) +{ + Assert(vfInitializedCache); + + HRESULT hr = S_OK; + LPWSTR sczWorkingFolder = NULL; + + // If the bundle is running out of the package cache then we use that as the + // working folder since we feel safe in the package cache. + if (vfRunningFromCache) + { + hr = PathForCurrentProcess(psczWorkingPath, NULL); + ExitOnFailure(hr, "Failed to get current process path."); + } + else // Otherwise, use the real working folder. + { + hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder); + ExitOnFailure(hr, "Failed to get working folder for bundle."); + + hr = StrAllocFormatted(psczWorkingPath, L"%ls%ls\\%ls", sczWorkingFolder, BUNDLE_WORKING_FOLDER_NAME, wzExecutableName); + ExitOnFailure(hr, "Failed to calculate the bundle working path."); + } + +LExit: + ReleaseStr(sczWorkingFolder); + + return hr; +} + +extern "C" HRESULT CacheCalculateBundleLayoutWorkingPath( + __in_z LPCWSTR wzBundleId, + __deref_out_z LPWSTR* psczWorkingPath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczWorkingFolder = NULL; + + hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath); + ExitOnFailure(hr, "Failed to get working folder for bundle layout."); + + hr = StrAllocConcat(psczWorkingPath, wzBundleId, 0); + ExitOnFailure(hr, "Failed to append bundle id for bundle layout working path."); + +LExit: + ReleaseStr(sczWorkingFolder); + + return hr; +} + +extern "C" HRESULT CacheCalculatePayloadWorkingPath( + __in_z LPCWSTR wzBundleId, + __in BURN_PAYLOAD* pPayload, + __deref_out_z LPWSTR* psczWorkingPath + ) +{ + HRESULT hr = S_OK; + + hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath); + ExitOnFailure(hr, "Failed to get working folder for payload."); + + hr = StrAllocConcat(psczWorkingPath, pPayload->sczKey, 0); + ExitOnFailure(hr, "Failed to append Id as payload unverified path."); + +LExit: + return hr; +} + +extern "C" HRESULT CacheCalculateContainerWorkingPath( + __in_z LPCWSTR wzBundleId, + __in BURN_CONTAINER* pContainer, + __deref_out_z LPWSTR* psczWorkingPath + ) +{ + HRESULT hr = S_OK; + + hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath); + ExitOnFailure(hr, "Failed to get working folder for container."); + + hr = StrAllocConcat(psczWorkingPath, pContainer->sczHash, 0); + ExitOnFailure(hr, "Failed to append hash as container unverified path."); + +LExit: + return hr; +} + +extern "C" HRESULT CacheGetRootCompletedPath( + __in BOOL fPerMachine, + __in BOOL fForceInitialize, + __deref_out_z LPWSTR* psczRootCompletedPath + ) +{ + HRESULT hr = S_OK; + + if (fForceInitialize) + { + hr = CreateCompletedPath(fPerMachine, L"", psczRootCompletedPath); + } + else + { + hr = GetRootPath(fPerMachine, TRUE, psczRootCompletedPath); + } + + return hr; +} + +extern "C" HRESULT CacheGetCompletedPath( + __in BOOL fPerMachine, + __in_z LPCWSTR wzCacheId, + __deref_out_z LPWSTR* psczCompletedPath + ) +{ + HRESULT hr = S_OK; + BOOL fRedirected = FALSE; + LPWSTR sczRootPath = NULL; + LPWSTR sczCurrentCompletedPath = NULL; + LPWSTR sczDefaultCompletedPath = NULL; + + hr = GetRootPath(fPerMachine, TRUE, &sczRootPath); + ExitOnFailure(hr, "Failed to get %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user"); + + // GetRootPath returns S_FALSE if the package cache is redirected elsewhere. + fRedirected = S_FALSE == hr; + + hr = PathConcat(sczRootPath, wzCacheId, &sczCurrentCompletedPath); + ExitOnFailure(hr, "Failed to construct cache path."); + + hr = PathBackslashTerminate(&sczCurrentCompletedPath); + ExitOnFailure(hr, "Failed to ensure cache path was backslash terminated."); + + // Return the old package cache directory if the new directory does not exist but the old directory does. + // If neither package cache directory exists return the (possibly) redirected package cache directory. + if (fRedirected && !DirExists(sczCurrentCompletedPath, NULL)) + { + hr = GetRootPath(fPerMachine, FALSE, &sczRootPath); + ExitOnFailure(hr, "Failed to get old %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user"); + + hr = PathConcat(sczRootPath, wzCacheId, &sczDefaultCompletedPath); + ExitOnFailure(hr, "Failed to construct cache path."); + + hr = PathBackslashTerminate(&sczDefaultCompletedPath); + ExitOnFailure(hr, "Failed to ensure cache path was backslash terminated."); + + if (DirExists(sczDefaultCompletedPath, NULL)) + { + *psczCompletedPath = sczDefaultCompletedPath; + sczDefaultCompletedPath = NULL; + + ExitFunction(); + } + } + + *psczCompletedPath = sczCurrentCompletedPath; + sczCurrentCompletedPath = NULL; + +LExit: + ReleaseNullStr(sczDefaultCompletedPath); + ReleaseNullStr(sczCurrentCompletedPath); + ReleaseNullStr(sczRootPath); + + return hr; +} + +extern "C" HRESULT CacheGetResumePath( + __in_z LPCWSTR wzPayloadWorkingPath, + __deref_out_z LPWSTR* psczResumePath + ) +{ + HRESULT hr = S_OK; + + hr = StrAllocFormatted(psczResumePath, L"%ls.R", wzPayloadWorkingPath); + ExitOnFailure(hr, "Failed to create resume path."); + +LExit: + return hr; +} + +extern "C" HRESULT CacheGetLocalSourcePaths( + __in_z LPCWSTR wzRelativePath, + __in_z LPCWSTR wzSourcePath, + __in_z LPCWSTR wzDestinationPath, + __in_z_opt LPCWSTR wzLayoutDirectory, + __in BURN_VARIABLES* pVariables, + __inout LPWSTR** prgSearchPaths, + __out DWORD* pcSearchPaths, + __out DWORD* pdwLikelySearchPath, + __out DWORD* pdwDestinationSearchPath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCurrentPath = NULL; + LPWSTR sczLastSourceFolder = NULL; + LPWSTR* psczPath = NULL; + BOOL fPreferSourcePathLocation = FALSE; + BOOL fTryLastFolder = FALSE; + BOOL fTryRelativePath = FALSE; + BOOL fSourceIsAbsolute = FALSE; + DWORD cSearchPaths = 0; + DWORD dwLikelySearchPath = 0; + DWORD dwDestinationSearchPath = 0; + + AssertSz(vfInitializedCache, "Cache wasn't initialized"); + + hr = GetLastUsedSourceFolder(pVariables, &sczLastSourceFolder); + fPreferSourcePathLocation = !vfRunningFromCache || FAILED(hr); + fTryLastFolder = SUCCEEDED(hr) && sczLastSourceFolder && *sczLastSourceFolder && CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, vsczSourceProcessFolder, -1, sczLastSourceFolder, -1); + fTryRelativePath = CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzSourcePath, -1, wzRelativePath, -1); + fSourceIsAbsolute = PathIsAbsolute(wzSourcePath); + + // If the source path provided is a full path, try that first. + if (fSourceIsAbsolute) + { + hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); + ExitOnFailure(hr, "Failed to ensure size for search paths array."); + + psczPath = *prgSearchPaths + cSearchPaths; + ++cSearchPaths; + + hr = StrAllocString(psczPath, wzSourcePath, 0); + ExitOnFailure(hr, "Failed to copy absolute source path."); + } + else + { + // If none of the paths exist, then most BAs will want to prompt the user with a possible path. + // The destination path is a temporary location and so not really a possible path. + dwLikelySearchPath = 1; + } + + // Try the destination path next. + hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); + ExitOnFailure(hr, "Failed to ensure size for search paths array."); + + dwDestinationSearchPath = cSearchPaths; + psczPath = *prgSearchPaths + cSearchPaths; + ++cSearchPaths; + + hr = StrAllocString(psczPath, wzDestinationPath, 0); + ExitOnFailure(hr, "Failed to copy absolute source path."); + + if (!fSourceIsAbsolute) + { + // Calculate the source path location. + // In the case where we are in the bundle's package cache and + // couldn't find a last used source that will be the package cache path + // which isn't likely to have what we are looking for. + hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); + ExitOnFailure(hr, "Failed to ensure size for search paths array."); + + hr = PathConcat(vsczSourceProcessFolder, wzSourcePath, &sczCurrentPath); + ExitOnFailure(hr, "Failed to combine source process folder with source."); + + // If we're not running from cache or we couldn't get the last source, + // try the source path location next. + if (fPreferSourcePathLocation) + { + (*prgSearchPaths)[cSearchPaths] = sczCurrentPath; + ++cSearchPaths; + sczCurrentPath = NULL; + } + + // If we have a last used source and it is not the source path location, + // add the last used source to the search path next. + if (fTryLastFolder) + { + hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); + ExitOnFailure(hr, "Failed to ensure size for search paths array."); + + psczPath = *prgSearchPaths + cSearchPaths; + ++cSearchPaths; + + hr = PathConcat(sczLastSourceFolder, wzSourcePath, psczPath); + ExitOnFailure(hr, "Failed to combine last source with source."); + } + + if (!fPreferSourcePathLocation) + { + (*prgSearchPaths)[cSearchPaths] = sczCurrentPath; + ++cSearchPaths; + sczCurrentPath = NULL; + } + + // Also consider the layout directory if doing Layout. + if (wzLayoutDirectory) + { + hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); + ExitOnFailure(hr, "Failed to ensure size for search paths array."); + + psczPath = *prgSearchPaths + cSearchPaths; + ++cSearchPaths; + + hr = PathConcat(wzLayoutDirectory, wzSourcePath, psczPath); + ExitOnFailure(hr, "Failed to combine layout source with source."); + } + } + + if (fTryRelativePath) + { + hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); + ExitOnFailure(hr, "Failed to ensure size for search paths array."); + + hr = PathConcat(vsczSourceProcessFolder, wzRelativePath, &sczCurrentPath); + ExitOnFailure(hr, "Failed to combine source process folder with relative."); + + if (fPreferSourcePathLocation) + { + (*prgSearchPaths)[cSearchPaths] = sczCurrentPath; + ++cSearchPaths; + sczCurrentPath = NULL; + } + + if (fTryLastFolder) + { + hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); + ExitOnFailure(hr, "Failed to ensure size for search paths array."); + + psczPath = *prgSearchPaths + cSearchPaths; + ++cSearchPaths; + + hr = PathConcat(sczLastSourceFolder, wzRelativePath, psczPath); + ExitOnFailure(hr, "Failed to combine last source with relative."); + } + + if (!fPreferSourcePathLocation) + { + (*prgSearchPaths)[cSearchPaths] = sczCurrentPath; + ++cSearchPaths; + sczCurrentPath = NULL; + } + + if (wzLayoutDirectory) + { + hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); + ExitOnFailure(hr, "Failed to ensure size for search paths array."); + + psczPath = *prgSearchPaths + cSearchPaths; + ++cSearchPaths; + + hr = PathConcat(wzLayoutDirectory, wzSourcePath, psczPath); + ExitOnFailure(hr, "Failed to combine layout source with relative."); + } + } + +LExit: + ReleaseStr(sczCurrentPath); + ReleaseStr(sczLastSourceFolder); + + AssertSz(cSearchPaths <= BURN_CACHE_MAX_SEARCH_PATHS, "Got more than BURN_CACHE_MAX_SEARCH_PATHS search paths"); + *pcSearchPaths = cSearchPaths; + *pdwLikelySearchPath = dwLikelySearchPath; + *pdwDestinationSearchPath = dwDestinationSearchPath; + + return hr; +} + +extern "C" HRESULT CacheSetLastUsedSource( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzSourcePath, + __in_z LPCWSTR wzRelativePath + ) +{ + HRESULT hr = S_OK; + size_t cchSourcePath = 0; + size_t cchRelativePath = 0; + size_t iSourceRelativePath = 0; + LPWSTR sczSourceFolder = NULL; + LPWSTR sczLastSourceFolder = NULL; + int nCompare = 0; + + hr = ::StringCchLengthW(wzSourcePath, STRSAFE_MAX_CCH, &cchSourcePath); + ExitOnFailure(hr, "Failed to determine length of source path."); + + hr = ::StringCchLengthW(wzRelativePath, STRSAFE_MAX_CCH, &cchRelativePath); + ExitOnFailure(hr, "Failed to determine length of relative path."); + + // If the source path is smaller than the relative path (plus space for "X:\") then we know they + // are not relative to each other. + if (cchSourcePath < cchRelativePath + 3) + { + ExitFunction(); + } + + // If the source path ends with the relative path then this source could be a new path. + iSourceRelativePath = cchSourcePath - cchRelativePath; + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzSourcePath + iSourceRelativePath, -1, wzRelativePath, -1)) + { + hr = StrAllocString(&sczSourceFolder, wzSourcePath, iSourceRelativePath); + ExitOnFailure(hr, "Failed to trim source folder."); + + hr = VariableGetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, &sczLastSourceFolder); + if (SUCCEEDED(hr)) + { + nCompare = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczSourceFolder, -1, sczLastSourceFolder, -1); + } + else if (E_NOTFOUND == hr) + { + nCompare = CSTR_GREATER_THAN; + hr = S_OK; + } + + if (CSTR_EQUAL != nCompare) + { + hr = VariableSetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, sczSourceFolder, FALSE, FALSE); + ExitOnFailure(hr, "Failed to set last source."); + } + } + +LExit: + ReleaseStr(sczLastSourceFolder); + ReleaseStr(sczSourceFolder); + + return hr; +} + +extern "C" HRESULT CacheSendProgressCallback( + __in DOWNLOAD_CACHE_CALLBACK* pCallback, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in HANDLE hDestinationFile + ) +{ + static LARGE_INTEGER LARGE_INTEGER_ZERO = { }; + + HRESULT hr = S_OK; + DWORD dwResult = PROGRESS_CONTINUE; + LARGE_INTEGER liTotalSize = { }; + LARGE_INTEGER liTotalTransferred = { }; + + if (pCallback->pfnProgress) + { + liTotalSize.QuadPart = dw64Total; + liTotalTransferred.QuadPart = dw64Progress; + + dwResult = (*pCallback->pfnProgress)(liTotalSize, liTotalTransferred, LARGE_INTEGER_ZERO, LARGE_INTEGER_ZERO, 1, CALLBACK_CHUNK_FINISHED, INVALID_HANDLE_VALUE, hDestinationFile, pCallback->pv); + switch (dwResult) + { + case PROGRESS_CONTINUE: + hr = S_OK; + break; + + case PROGRESS_CANCEL: __fallthrough; // TODO: should cancel and stop be treated differently? + case PROGRESS_STOP: + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + ExitOnRootFailure(hr, "UX aborted on download progress."); + + case PROGRESS_QUIET: // Not actually an error, just an indication to the caller to stop requesting progress. + pCallback->pfnProgress = NULL; + hr = S_OK; + break; + + default: + hr = E_UNEXPECTED; + ExitOnRootFailure(hr, "Invalid return code from progress routine."); + } + } + +LExit: + return hr; +} + +extern "C" void CacheSendErrorCallback( + __in DOWNLOAD_CACHE_CALLBACK* pCallback, + __in HRESULT hrError, + __in_z_opt LPCWSTR wzError, + __out_opt BOOL* pfRetry + ) +{ + if (pfRetry) + { + *pfRetry = FALSE; + } + + if (pCallback->pfnCancel) + { + int nResult = (*pCallback->pfnCancel)(hrError, wzError, pfRetry != NULL, pCallback->pv); + if (pfRetry && IDRETRY == nResult) + { + *pfRetry = TRUE; + } + } +} + +extern "C" BOOL CacheBundleRunningFromCache() +{ + return vfRunningFromCache; +} + +extern "C" HRESULT CacheBundleToCleanRoom( + __in BURN_SECTION* pSection, + __deref_out_z_opt LPWSTR* psczCleanRoomBundlePath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczSourcePath = NULL; + LPWSTR wzExecutableName = NULL; + + hr = PathForCurrentProcess(&sczSourcePath, NULL); + ExitOnFailure(hr, "Failed to get current path for process to cache to clean room."); + + wzExecutableName = PathFile(sczSourcePath); + + hr = CopyEngineToWorkingFolder(sczSourcePath, BUNDLE_CLEAN_ROOM_WORKING_FOLDER_NAME, wzExecutableName, pSection, psczCleanRoomBundlePath); + ExitOnFailure(hr, "Failed to cache bundle to clean room."); + +LExit: + ReleaseStr(sczSourcePath); + + return hr; +} + +extern "C" HRESULT CacheBundleToWorkingDirectory( + __in_z LPCWSTR /*wzBundleId*/, + __in_z LPCWSTR wzExecutableName, + __in BURN_SECTION* pSection, + __deref_out_z_opt LPWSTR* psczEngineWorkingPath + ) +{ + Assert(vfInitializedCache); + + HRESULT hr = S_OK; + LPWSTR sczSourcePath = NULL; + + // Initialize the source. + hr = PathForCurrentProcess(&sczSourcePath, NULL); + ExitOnFailure(hr, "Failed to get current process path."); + + // If the bundle is running out of the package cache then we don't need to copy it to + // the working folder since we feel safe in the package cache and will run from there. + if (vfRunningFromCache) + { + hr = StrAllocString(psczEngineWorkingPath, sczSourcePath, 0); + ExitOnFailure(hr, "Failed to use current process path as target path."); + } + else // otherwise, carry on putting the bundle in the working folder. + { + hr = CopyEngineToWorkingFolder(sczSourcePath, BUNDLE_WORKING_FOLDER_NAME, wzExecutableName, pSection, psczEngineWorkingPath); + ExitOnFailure(hr, "Failed to copy engine to working folder."); + } + +LExit: + ReleaseStr(sczSourcePath); + + return hr; +} + +extern "C" HRESULT CacheLayoutBundle( + __in_z LPCWSTR wzExecutableName, + __in_z LPCWSTR wzLayoutDirectory, + __in_z LPCWSTR wzSourceBundlePath, + __in DWORD64 qwBundleSize, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ) +{ + HRESULT hr = S_OK; + LPWSTR sczTargetPath = NULL; + + hr = PathConcat(wzLayoutDirectory, wzExecutableName, &sczTargetPath); + ExitOnFailure(hr, "Failed to combine completed path with engine file name for layout."); + + LogStringLine(REPORT_STANDARD, "Layout bundle from: '%ls' to: '%ls'", wzSourceBundlePath, sczTargetPath); + + hr = CacheTransferFileWithRetry(wzSourceBundlePath, sczTargetPath, TRUE, BURN_CACHE_STEP_FINALIZE, qwBundleSize, pfnCacheMessageHandler, pfnProgress, pContext); + ExitOnFailure(hr, "Failed to layout bundle from: '%ls' to '%ls'", wzSourceBundlePath, sczTargetPath); + +LExit: + ReleaseStr(sczTargetPath); + + return hr; +} + +extern "C" HRESULT CacheCompleteBundle( + __in BOOL fPerMachine, + __in_z LPCWSTR wzExecutableName, + __in_z LPCWSTR wzBundleId, + __in_z LPCWSTR wzSourceBundlePath +#ifdef DEBUG + , __in_z LPCWSTR wzExecutablePath +#endif + ) +{ + HRESULT hr = S_OK; + int nCompare = 0; + LPWSTR sczTargetDirectory = NULL; + LPWSTR sczTargetPath = NULL; + LPWSTR sczSourceDirectory = NULL; + LPWSTR sczPayloadSourcePath = NULL; + + hr = CreateCompletedPath(fPerMachine, wzBundleId, &sczTargetDirectory); + ExitOnFailure(hr, "Failed to create completed cache path for bundle."); + + hr = PathConcat(sczTargetDirectory, wzExecutableName, &sczTargetPath); + ExitOnFailure(hr, "Failed to combine completed path with engine file name."); + + // We can't just use wzExecutablePath because we needed to call CreateCompletedPath to ensure that the destination was secured. + Assert(CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzExecutablePath, -1, sczTargetPath, -1)); + + // If the bundle is running out of the package cache then we don't need to copy it there + // (and don't want to since it'll be in use) so bail. + hr = PathCompare(wzSourceBundlePath, sczTargetPath, &nCompare); + ExitOnFailure(hr, "Failed to compare completed cache path for bundle: %ls", wzSourceBundlePath); + + if (CSTR_EQUAL == nCompare) + { + ExitFunction(); + } + + // Otherwise, carry on putting the bundle in the cache. + LogStringLine(REPORT_STANDARD, "Caching bundle from: '%ls' to: '%ls'", wzSourceBundlePath, sczTargetPath); + + FileRemoveFromPendingRename(sczTargetPath); // best effort to ensure bundle is not deleted from cache post restart. + + hr = FileEnsureCopyWithRetry(wzSourceBundlePath, sczTargetPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); + ExitOnFailure(hr, "Failed to cache bundle from: '%ls' to '%ls'", wzSourceBundlePath, sczTargetPath); + + // Reset the path permissions in the cache. + hr = ResetPathPermissions(fPerMachine, sczTargetPath); + ExitOnFailure(hr, "Failed to reset permissions on cached bundle: '%ls'", sczTargetPath); + + hr = PathGetDirectory(wzSourceBundlePath, &sczSourceDirectory); + ExitOnFailure(hr, "Failed to get directory from engine working path: %ls", wzSourceBundlePath); + +LExit: + ReleaseStr(sczPayloadSourcePath); + ReleaseStr(sczSourceDirectory); + ReleaseStr(sczTargetPath); + ReleaseStr(sczTargetDirectory); + + return hr; +} + +extern "C" HRESULT CacheLayoutContainer( + __in BURN_CONTAINER* pContainer, + __in_z_opt LPCWSTR wzLayoutDirectory, + __in_z LPCWSTR wzUnverifiedContainerPath, + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCachedPath = NULL; + + hr = PathConcat(wzLayoutDirectory, pContainer->sczFilePath, &sczCachedPath); + ExitOnFailure(hr, "Failed to concat complete cached path."); + + hr = VerifyThenTransferContainer(pContainer, sczCachedPath, wzUnverifiedContainerPath, fMove, pfnCacheMessageHandler, pfnProgress, pContext); + ExitOnFailure(hr, "Failed to layout container from cached path: %ls", sczCachedPath); + +LExit: + ReleaseStr(sczCachedPath); + + return hr; +} + +extern "C" HRESULT CacheLayoutPayload( + __in BURN_PAYLOAD* pPayload, + __in_z_opt LPCWSTR wzLayoutDirectory, + __in_z LPCWSTR wzUnverifiedPayloadPath, + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCachedPath = NULL; + + hr = PathConcat(wzLayoutDirectory, pPayload->sczFilePath, &sczCachedPath); + ExitOnFailure(hr, "Failed to concat complete cached path."); + + hr = VerifyThenTransferPayload(pPayload, sczCachedPath, wzUnverifiedPayloadPath, fMove, pfnCacheMessageHandler, pfnProgress, pContext); + ExitOnFailure(hr, "Failed to layout payload from cached payload: %ls", sczCachedPath); + +LExit: + ReleaseStr(sczCachedPath); + + return hr; +} + +extern "C" HRESULT CacheCompletePayload( + __in BOOL fPerMachine, + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzCacheId, + __in_z LPCWSTR wzWorkingPayloadPath, + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCachedDirectory = NULL; + LPWSTR sczCachedPath = NULL; + LPWSTR sczUnverifiedPayloadPath = NULL; + + hr = CreateCompletedPath(fPerMachine, wzCacheId, &sczCachedDirectory); + ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", wzCacheId); + + hr = PathConcat(sczCachedDirectory, pPayload->sczFilePath, &sczCachedPath); + ExitOnFailure(hr, "Failed to concat complete cached path."); + + // If the cached file matches what we expected, we're good. + hr = VerifyFileAgainstPayload(pPayload, sczCachedPath, TRUE, BURN_CACHE_STEP_HASH_TO_SKIP_VERIFY, pfnCacheMessageHandler, pfnProgress, pContext); + if (SUCCEEDED(hr)) + { + ExitFunction(); + } + + hr = CreateUnverifiedPath(fPerMachine, pPayload->sczKey, &sczUnverifiedPayloadPath); + ExitOnFailure(hr, "Failed to create unverified path."); + + // If the working path exists, let's get it into the unverified path so we can reset the ACLs and verify the file. + if (FileExistsEx(wzWorkingPayloadPath, NULL)) + { + hr = CacheTransferFileWithRetry(wzWorkingPayloadPath, sczUnverifiedPayloadPath, fMove, BURN_CACHE_STEP_STAGE, pPayload->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext); + ExitOnFailure(hr, "Failed to transfer working path to unverified path for payload: %ls.", pPayload->sczKey); + } + else if (FileExistsEx(sczUnverifiedPayloadPath, NULL)) + { + // Make sure the staging progress is sent even though there was nothing to do. + hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, BURN_CACHE_STEP_STAGE); + if (SUCCEEDED(hr)) + { + hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, pPayload->qwFileSize); + } + SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr); + ExitOnFailure(hr, "Aborted transferring working path to unverified path for payload: %ls.", pPayload->sczKey); + } + else // if the working path and unverified path do not exist, nothing we can do. + { + hr = E_FILENOTFOUND; + ExitOnFailure(hr, "Failed to find payload: %ls in working path: %ls and unverified path: %ls", pPayload->sczKey, wzWorkingPayloadPath, sczUnverifiedPayloadPath); + } + + hr = ResetPathPermissions(fPerMachine, sczUnverifiedPayloadPath); + ExitOnFailure(hr, "Failed to reset permissions on unverified cached payload: %ls", pPayload->sczKey); + + hr = VerifyFileAgainstPayload(pPayload, sczUnverifiedPayloadPath, FALSE, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext); + LogExitOnFailure(hr, MSG_FAILED_VERIFY_PAYLOAD, "Failed to verify payload: %ls at path: %ls", pPayload->sczKey, sczUnverifiedPayloadPath, NULL); + + LogId(REPORT_STANDARD, MSG_VERIFIED_ACQUIRED_PAYLOAD, pPayload->sczKey, sczUnverifiedPayloadPath, fMove ? "moving" : "copying", sczCachedPath); + + hr = CacheTransferFileWithRetry(sczUnverifiedPayloadPath, sczCachedPath, TRUE, BURN_CACHE_STEP_FINALIZE, pPayload->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext); + ExitOnFailure(hr, "Failed to move verified file to complete payload path: %ls", sczCachedPath); + + ::DecryptFileW(sczCachedPath, 0); // Let's try to make sure it's not encrypted. + +LExit: + ReleaseStr(sczUnverifiedPayloadPath); + ReleaseStr(sczCachedPath); + ReleaseStr(sczCachedDirectory); + + return hr; +} + +extern "C" HRESULT CacheVerifyContainer( + __in BURN_CONTAINER* pContainer, + __in_z LPCWSTR wzCachedDirectory, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCachedPath = NULL; + + hr = PathConcat(wzCachedDirectory, pContainer->sczFilePath, &sczCachedPath); + ExitOnFailure(hr, "Failed to concat complete cached path."); + + hr = VerifyFileAgainstContainer(pContainer, sczCachedPath, TRUE, BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE, pfnCacheMessageHandler, pfnProgress, pContext); + +LExit: + ReleaseStr(sczCachedPath); + + return hr; +} + +extern "C" HRESULT CacheVerifyPayload( + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzCachedDirectory, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCachedPath = NULL; + + hr = PathConcat(wzCachedDirectory, pPayload->sczFilePath, &sczCachedPath); + ExitOnFailure(hr, "Failed to concat complete cached path."); + + hr = VerifyFileAgainstPayload(pPayload, sczCachedPath, TRUE, BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE, pfnCacheMessageHandler, pfnProgress, pContext); + +LExit: + ReleaseStr(sczCachedPath); + + return hr; +} + +extern "C" HRESULT CacheRemoveWorkingFolder( + __in_z_opt LPCWSTR wzBundleId + ) +{ + HRESULT hr = S_OK; + LPWSTR sczWorkingFolder = NULL; + + if (vfInitializedCache) + { + hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder); + ExitOnFailure(hr, "Failed to calculate the working folder to remove it."); + + // Try to clean out everything in the working folder. + hr = DirEnsureDeleteEx(sczWorkingFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE); + TraceError(hr, "Could not delete bundle engine working folder."); + } + +LExit: + ReleaseStr(sczWorkingFolder); + + return hr; +} + +extern "C" HRESULT CacheRemoveBundle( + __in BOOL fPerMachine, + __in_z LPCWSTR wzBundleId + ) +{ + HRESULT hr = S_OK; + + hr = RemoveBundleOrPackage(TRUE, fPerMachine, wzBundleId, wzBundleId); + ExitOnFailure(hr, "Failed to remove bundle id: %ls.", wzBundleId); + +LExit: + return hr; +} + +extern "C" HRESULT CacheRemovePackage( + __in BOOL fPerMachine, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzCacheId + ) +{ + HRESULT hr = S_OK; + + hr = RemoveBundleOrPackage(FALSE, fPerMachine, wzPackageId, wzCacheId); + ExitOnFailure(hr, "Failed to remove package id: %ls.", wzPackageId); + +LExit: + return hr; +} + +extern "C" void CacheCleanup( + __in BOOL fPerMachine, + __in_z LPCWSTR wzBundleId + ) +{ + HRESULT hr = S_OK; + LPWSTR sczFolder = NULL; + LPWSTR sczFiles = NULL; + LPWSTR sczDelete = NULL; + HANDLE hFind = INVALID_HANDLE_VALUE; + WIN32_FIND_DATAW wfd = { }; + size_t cchFileName = 0; + + hr = CacheGetCompletedPath(fPerMachine, UNVERIFIED_CACHE_FOLDER_NAME, &sczFolder); + if (SUCCEEDED(hr)) + { + hr = DirEnsureDeleteEx(sczFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE); + } + + if (!fPerMachine) + { + hr = CalculateWorkingFolder(wzBundleId, &sczFolder); + if (SUCCEEDED(hr)) + { + hr = PathConcat(sczFolder, L"*.*", &sczFiles); + if (SUCCEEDED(hr)) + { + hFind = ::FindFirstFileW(sczFiles, &wfd); + if (INVALID_HANDLE_VALUE != hFind) + { + do + { + // Skip directories. + if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + continue; + } + + // Skip resume files (they end with ".R"). + hr = ::StringCchLengthW(wfd.cFileName, MAX_PATH, &cchFileName); + if (FAILED(hr) || + 2 < cchFileName && L'.' == wfd.cFileName[cchFileName - 2] && (L'R' == wfd.cFileName[cchFileName - 1] || L'r' == wfd.cFileName[cchFileName - 1])) + { + continue; + } + + hr = PathConcatCch(sczFolder, 0, wfd.cFileName, cchFileName, &sczDelete); + if (SUCCEEDED(hr)) + { + hr = FileEnsureDelete(sczDelete); + } + } while (::FindNextFileW(hFind, &wfd)); + } + } + } + } + + if (INVALID_HANDLE_VALUE != hFind) + { + ::FindClose(hFind); + } + + ReleaseStr(sczDelete); + ReleaseStr(sczFiles); + ReleaseStr(sczFolder); +} + +extern "C" void CacheUninitialize() +{ + ReleaseNullStr(vsczCurrentMachinePackageCache); + ReleaseNullStr(vsczDefaultMachinePackageCache); + ReleaseNullStr(vsczDefaultUserPackageCache); + ReleaseNullStr(vsczWorkingFolder); + ReleaseNullStr(vsczSourceProcessFolder); + + vfRunningFromCache = FALSE; + vfInitializedCache = FALSE; +} + +// Internal functions. + +static HRESULT CalculateWorkingFolder( + __in_z_opt LPCWSTR /*wzBundleId*/, + __deref_out_z LPWSTR* psczWorkingFolder + ) +{ + HRESULT hr = S_OK; + RPC_STATUS rs = RPC_S_OK; + BOOL fElevated = FALSE; + WCHAR wzTempPath[MAX_PATH] = { }; + UUID guid = {}; + WCHAR wzGuid[39]; + + if (!vsczWorkingFolder) + { + ProcElevated(::GetCurrentProcess(), &fElevated); + + if (fElevated) + { + if (!::GetWindowsDirectoryW(wzTempPath, countof(wzTempPath))) + { + ExitWithLastError(hr, "Failed to get windows path for working folder."); + } + + hr = PathFixedBackslashTerminate(wzTempPath, countof(wzTempPath)); + ExitOnFailure(hr, "Failed to ensure windows path for working folder ended in backslash."); + + hr = ::StringCchCatW(wzTempPath, countof(wzTempPath), L"Temp\\"); + ExitOnFailure(hr, "Failed to concat Temp directory on windows path for working folder."); + } + else if (0 == ::GetTempPathW(countof(wzTempPath), wzTempPath)) + { + ExitWithLastError(hr, "Failed to get temp path for working folder."); + } + + rs = ::UuidCreate(&guid); + hr = HRESULT_FROM_RPC(rs); + ExitOnFailure(hr, "Failed to create working folder guid."); + + if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid))) + { + hr = E_OUTOFMEMORY; + ExitOnRootFailure(hr, "Failed to convert working folder guid into string."); + } + + hr = StrAllocFormatted(&vsczWorkingFolder, L"%ls%ls\\", wzTempPath, wzGuid); + ExitOnFailure(hr, "Failed to append bundle id on to temp path for working folder."); + } + + hr = StrAllocString(psczWorkingFolder, vsczWorkingFolder, 0); + ExitOnFailure(hr, "Failed to copy working folder path."); + +LExit: + return hr; +} + +static HRESULT GetRootPath( + __in BOOL fPerMachine, + __in BOOL fAllowRedirect, + __deref_out_z LPWSTR* psczRootPath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczAppData = NULL; + int nCompare = 0; + + // Cache paths are initialized once so they cannot be changed while the engine is caching payloads. + if (fPerMachine) + { + // Always construct the default machine package cache path so we can determine if we're redirected. + if (!vsczDefaultMachinePackageCache) + { + hr = PathGetKnownFolder(CSIDL_COMMON_APPDATA, &sczAppData); + ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-machine"); + + hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &vsczDefaultMachinePackageCache); + ExitOnFailure(hr, "Failed to construct %hs package cache directory name.", "per-machine"); + + hr = PathBackslashTerminate(&vsczDefaultMachinePackageCache); + ExitOnFailure(hr, "Failed to backslash terminate default %hs package cache directory name.", "per-machine"); + } + + if (!vsczCurrentMachinePackageCache) + { + hr = PolcReadString(POLICY_BURN_REGISTRY_PATH, L"PackageCache", NULL, &vsczCurrentMachinePackageCache); + ExitOnFailure(hr, "Failed to read PackageCache policy directory."); + + if (vsczCurrentMachinePackageCache) + { + hr = PathBackslashTerminate(&vsczCurrentMachinePackageCache); + ExitOnFailure(hr, "Failed to backslash terminate redirected per-machine package cache directory name."); + } + else + { + hr = StrAllocString(&vsczCurrentMachinePackageCache, vsczDefaultMachinePackageCache, 0); + ExitOnFailure(hr, "Failed to copy default package cache directory to current package cache directory."); + } + } + + hr = StrAllocString(psczRootPath, fAllowRedirect ? vsczCurrentMachinePackageCache : vsczDefaultMachinePackageCache, 0); + ExitOnFailure(hr, "Failed to copy %hs package cache root directory.", "per-machine"); + + hr = PathCompare(vsczDefaultMachinePackageCache, *psczRootPath, &nCompare); + ExitOnFailure(hr, "Failed to compare default and current package cache directories."); + + // Return S_FALSE if the current location is not the default location (redirected). + hr = CSTR_EQUAL == nCompare ? S_OK : S_FALSE; + } + else + { + if (!vsczDefaultUserPackageCache) + { + hr = PathGetKnownFolder(CSIDL_LOCAL_APPDATA, &sczAppData); + ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-user"); + + hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &vsczDefaultUserPackageCache); + ExitOnFailure(hr, "Failed to construct %hs package cache directory name.", "per-user"); + + hr = PathBackslashTerminate(&vsczDefaultUserPackageCache); + ExitOnFailure(hr, "Failed to backslash terminate default %hs package cache directory name.", "per-user"); + } + + hr = StrAllocString(psczRootPath, vsczDefaultUserPackageCache, 0); + ExitOnFailure(hr, "Failed to copy %hs package cache root directory.", "per-user"); + } + +LExit: + ReleaseStr(sczAppData); + + return hr; +} + +static HRESULT GetLastUsedSourceFolder( + __in BURN_VARIABLES* pVariables, + __out_z LPWSTR* psczLastSource + ) +{ + HRESULT hr = S_OK; + + hr = VariableGetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, psczLastSource); + if (E_NOTFOUND == hr) + { + // Try the original source folder. + hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, psczLastSource); + } + + return hr; +} + +static HRESULT CreateCompletedPath( + __in BOOL fPerMachine, + __in LPCWSTR wzId, + __out LPWSTR* psczCacheDirectory + ) +{ + static BOOL fPerMachineCacheRootVerified = FALSE; + + HRESULT hr = S_OK; + LPWSTR sczCacheDirectory = NULL; + + // If we are doing a permachine install but have not yet verified that the root cache folder + // was created with the correct ACLs yet, do that now. + if (fPerMachine && !fPerMachineCacheRootVerified) + { + hr = GetRootPath(fPerMachine, TRUE, &sczCacheDirectory); + ExitOnFailure(hr, "Failed to get cache directory."); + + hr = DirEnsureExists(sczCacheDirectory, NULL); + ExitOnFailure(hr, "Failed to create cache directory: %ls", sczCacheDirectory); + + hr = SecurePath(sczCacheDirectory); + ExitOnFailure(hr, "Failed to secure cache directory: %ls", sczCacheDirectory); + + fPerMachineCacheRootVerified = TRUE; + } + + // Get the cache completed path, ensure it exists, and reset any permissions people + // might have tried to set on the directory so we inherit the (correct!) security + // permissions from the parent directory. + hr = CacheGetCompletedPath(fPerMachine, wzId, &sczCacheDirectory); + ExitOnFailure(hr, "Failed to get cache directory."); + + hr = DirEnsureExists(sczCacheDirectory, NULL); + ExitOnFailure(hr, "Failed to create cache directory: %ls", sczCacheDirectory); + + ResetPathPermissions(fPerMachine, sczCacheDirectory); + + *psczCacheDirectory = sczCacheDirectory; + sczCacheDirectory = NULL; + +LExit: + ReleaseStr(sczCacheDirectory); + return hr; +} + +static HRESULT CreateUnverifiedPath( + __in BOOL fPerMachine, + __in_z LPCWSTR wzPayloadId, + __out_z LPWSTR* psczUnverifiedPayloadPath + ) +{ + static BOOL fUnverifiedCacheFolderCreated = FALSE; + + HRESULT hr = S_OK; + LPWSTR sczUnverifiedCacheFolder = NULL; + + hr = CacheGetCompletedPath(fPerMachine, UNVERIFIED_CACHE_FOLDER_NAME, &sczUnverifiedCacheFolder); + ExitOnFailure(hr, "Failed to get cache directory."); + + if (!fUnverifiedCacheFolderCreated) + { + hr = DirEnsureExists(sczUnverifiedCacheFolder, NULL); + ExitOnFailure(hr, "Failed to create unverified cache directory: %ls", sczUnverifiedCacheFolder); + + ResetPathPermissions(fPerMachine, sczUnverifiedCacheFolder); + } + + hr = PathConcat(sczUnverifiedCacheFolder, wzPayloadId, psczUnverifiedPayloadPath); + ExitOnFailure(hr, "Failed to concat payload id to unverified folder path."); + +LExit: + ReleaseStr(sczUnverifiedCacheFolder); + + return hr; +} + +static HRESULT VerifyThenTransferContainer( + __in BURN_CONTAINER* pContainer, + __in_z LPCWSTR wzCachedPath, + __in_z LPCWSTR wzUnverifiedContainerPath, + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + + // Get the container on disk actual hash. + hFile = ::CreateFileW(wzUnverifiedContainerPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + ExitWithLastError(hr, "Failed to open container in working path: %ls", wzUnverifiedContainerPath); + } + + // Container should have a hash we can use to verify with. + if (pContainer->pbHash) + { + hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, pContainer->qwFileSize, wzUnverifiedContainerPath, hFile, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext); + ExitOnFailure(hr, "Failed to verify container hash: %ls", wzCachedPath); + } + + LogStringLine(REPORT_STANDARD, "%ls container from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedContainerPath, wzCachedPath); + + hr = CacheTransferFileWithRetry(wzUnverifiedContainerPath, wzCachedPath, fMove, BURN_CACHE_STEP_FINALIZE, pContainer->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext); + +LExit: + ReleaseFileHandle(hFile); + + return hr; +} + +static HRESULT VerifyThenTransferPayload( + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzCachedPath, + __in_z LPCWSTR wzUnverifiedPayloadPath, + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + + // Get the payload on disk actual hash. + hFile = ::CreateFileW(wzUnverifiedPayloadPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + ExitWithLastError(hr, "Failed to open payload in working path: %ls", wzUnverifiedPayloadPath); + } + + if (pPayload->pbHash) // the payload should have a hash we can use to verify it. + { + hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, wzUnverifiedPayloadPath, hFile, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext); + ExitOnFailure(hr, "Failed to verify payload hash: %ls", wzCachedPath); + } + + LogStringLine(REPORT_STANDARD, "%ls payload from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedPayloadPath, wzCachedPath); + + hr = CacheTransferFileWithRetry(wzUnverifiedPayloadPath, wzCachedPath, fMove, BURN_CACHE_STEP_FINALIZE, pPayload->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext); + +LExit: + ReleaseFileHandle(hFile); + + return hr; +} + +static HRESULT CacheTransferFileWithRetry( + __in_z LPCWSTR wzSourcePath, + __in_z LPCWSTR wzDestinationPath, + __in BOOL fMove, + __in BURN_CACHE_STEP cacheStep, + __in DWORD64 qwFileSize, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE /*pfnProgress*/, + __in LPVOID pContext + ) +{ + HRESULT hr = S_OK; + + hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, cacheStep); + ExitOnFailure(hr, "Aborted cache file transfer begin."); + + // TODO: send progress during the file transfer. + if (fMove) + { + hr = FileEnsureMoveWithRetry(wzSourcePath, wzDestinationPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); + ExitOnFailure(hr, "Failed to move %ls to %ls", wzSourcePath, wzDestinationPath); + } + else + { + hr = FileEnsureCopyWithRetry(wzSourcePath, wzDestinationPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); + ExitOnFailure(hr, "Failed to copy %ls to %ls", wzSourcePath, wzDestinationPath); + } + + hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, qwFileSize); + +LExit: + SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr); + + return hr; +} + +static HRESULT VerifyFileAgainstContainer( + __in BURN_CONTAINER* pContainer, + __in_z LPCWSTR wzVerifyPath, + __in BOOL fAlreadyCached, + __in BURN_CACHE_STEP cacheStep, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + + // Get the container on disk actual hash. + hFile = ::CreateFileW(wzVerifyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (E_PATHNOTFOUND == hr || E_FILENOTFOUND == hr) + { + ExitFunction(); // do not log error when the file was not found. + } + ExitOnRootFailure(hr, "Failed to open container at path: %ls", wzVerifyPath); + } + + if (pContainer->pbHash) // the container should have a hash we can use to verify it. + { + hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, pContainer->qwFileSize, wzVerifyPath, hFile, cacheStep, pfnCacheMessageHandler, pfnProgress, pContext); + ExitOnFailure(hr, "Failed to verify hash of container: %ls", pContainer->sczId); + } + + if (fAlreadyCached) + { + LogId(REPORT_STANDARD, MSG_VERIFIED_EXISTING_CONTAINER, pContainer->sczId, wzVerifyPath); + ::DecryptFileW(wzVerifyPath, 0); // Let's try to make sure it's not encrypted. + } + +LExit: + ReleaseFileHandle(hFile); + + if (FAILED(hr) && E_PATHNOTFOUND != hr && E_FILENOTFOUND != hr) + { + if (fAlreadyCached) + { + LogErrorId(hr, MSG_FAILED_VERIFY_CONTAINER, pContainer->sczId, wzVerifyPath, NULL); + } + + FileEnsureDelete(wzVerifyPath); // if the file existed but did not verify correctly, make it go away. + } + + return hr; +} + +static HRESULT VerifyFileAgainstPayload( + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzVerifyPath, + __in BOOL fAlreadyCached, + __in BURN_CACHE_STEP cacheStep, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ) +{ + HRESULT hr = S_OK; + HANDLE hFile = INVALID_HANDLE_VALUE; + + // Get the payload on disk actual hash. + hFile = ::CreateFileW(wzVerifyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (E_PATHNOTFOUND == hr || E_FILENOTFOUND == hr) + { + ExitFunction(); // do not log error when the file was not found. + } + ExitOnRootFailure(hr, "Failed to open payload at path: %ls", wzVerifyPath); + } + + if (pPayload->pbHash) // the payload should have a hash we can use to verify it. + { + hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, wzVerifyPath, hFile, cacheStep, pfnCacheMessageHandler, pfnProgress, pContext); + ExitOnFailure(hr, "Failed to verify hash of payload: %ls", pPayload->sczKey); + } + + if (fAlreadyCached) + { + LogId(REPORT_STANDARD, MSG_VERIFIED_EXISTING_PAYLOAD, pPayload->sczKey, wzVerifyPath); + ::DecryptFileW(wzVerifyPath, 0); // Let's try to make sure it's not encrypted. + } + +LExit: + ReleaseFileHandle(hFile); + + if (FAILED(hr) && E_PATHNOTFOUND != hr && E_FILENOTFOUND != hr) + { + if (fAlreadyCached) + { + LogErrorId(hr, MSG_FAILED_VERIFY_PAYLOAD, pPayload->sczKey, wzVerifyPath, NULL); + } + + FileEnsureDelete(wzVerifyPath); // if the file existed but did not verify correctly, make it go away. + } + + return hr; +} + +static HRESULT AllocateSid( + __in WELL_KNOWN_SID_TYPE type, + __out PSID* ppSid + ) +{ + HRESULT hr = S_OK; + PSID pAllocSid = NULL; + DWORD cbSid = SECURITY_MAX_SID_SIZE; + + pAllocSid = static_cast(MemAlloc(cbSid, TRUE)); + ExitOnNull(pAllocSid, hr, E_OUTOFMEMORY, "Failed to allocate memory for well known SID."); + + if (!::CreateWellKnownSid(type, NULL, pAllocSid, &cbSid)) + { + ExitWithLastError(hr, "Failed to create well known SID."); + } + + *ppSid = pAllocSid; + pAllocSid = NULL; + +LExit: + ReleaseMem(pAllocSid); + return hr; +} + + +static HRESULT ResetPathPermissions( + __in BOOL fPerMachine, + __in_z LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD dwSetSecurity = DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION; + ACL acl = { }; + PSID pSid = NULL; + + if (fPerMachine) + { + hr = AllocateSid(WinBuiltinAdministratorsSid, &pSid); + ExitOnFailure(hr, "Failed to allocate administrator SID."); + + // Create an empty (not NULL!) ACL to reset the permissions on the file to purely inherit from parent. + if (!::InitializeAcl(&acl, sizeof(acl), ACL_REVISION)) + { + ExitWithLastError(hr, "Failed to initialize ACL."); + } + + dwSetSecurity |= OWNER_SECURITY_INFORMATION; + } + + hr = AclSetSecurityWithRetry(wzPath, SE_FILE_OBJECT, dwSetSecurity, pSid, NULL, &acl, NULL, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); + ExitOnWin32Error(er, hr, "Failed to reset the ACL on cached file: %ls", wzPath); + + ::SetFileAttributesW(wzPath, FILE_ATTRIBUTE_NORMAL); // Let's try to reset any possible read-only/system bits. + +LExit: + ReleaseMem(pSid); + return hr; +} + + +static HRESULT GrantAccessAndAllocateSid( + __in WELL_KNOWN_SID_TYPE type, + __in DWORD dwGrantAccess, + __in EXPLICIT_ACCESS* pAccess + ) +{ + HRESULT hr = S_OK; + + hr = AllocateSid(type, reinterpret_cast(&pAccess->Trustee.ptstrName)); + ExitOnFailure(hr, "Failed to allocate SID to grate access."); + + pAccess->grfAccessMode = GRANT_ACCESS; + pAccess->grfAccessPermissions = dwGrantAccess; + pAccess->grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; + pAccess->Trustee.TrusteeForm = TRUSTEE_IS_SID; + pAccess->Trustee.TrusteeType = TRUSTEE_IS_GROUP; + +LExit: + return hr; +} + + +static HRESULT SecurePath( + __in LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + EXPLICIT_ACCESSW access[4] = { }; + PACL pAcl = NULL; + + // Administrators must be the first one in the array so we can reuse the allocated SID below. + hr = GrantAccessAndAllocateSid(WinBuiltinAdministratorsSid, FILE_ALL_ACCESS, &access[0]); + ExitOnFailure(hr, "Failed to allocate access for Administrators group to path: %ls", wzPath); + + hr = GrantAccessAndAllocateSid(WinLocalSystemSid, FILE_ALL_ACCESS, &access[1]); + ExitOnFailure(hr, "Failed to allocate access for SYSTEM group to path: %ls", wzPath); + + hr = GrantAccessAndAllocateSid(WinWorldSid, GENERIC_READ | GENERIC_EXECUTE, &access[2]); + ExitOnFailure(hr, "Failed to allocate access for Everyone group to path: %ls", wzPath); + + hr = GrantAccessAndAllocateSid(WinBuiltinUsersSid, GENERIC_READ | GENERIC_EXECUTE, &access[3]); + ExitOnFailure(hr, "Failed to allocate access for Users group to path: %ls", wzPath); + + er = ::SetEntriesInAclW(countof(access), access, NULL, &pAcl); + ExitOnWin32Error(er, hr, "Failed to create ACL to secure cache path: %ls", wzPath); + + // Set the ACL and ensure the Administrators group ends up the owner + hr = AclSetSecurityWithRetry(wzPath, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION, + reinterpret_cast(access[0].Trustee.ptstrName), NULL, pAcl, NULL, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); + ExitOnFailure(hr, "Failed to secure cache path: %ls", wzPath); + +LExit: + if (pAcl) + { + ::LocalFree(pAcl); + } + + for (DWORD i = 0; i < countof(access); ++i) + { + ReleaseMem(access[i].Trustee.ptstrName); + } + + return hr; +} + + +static HRESULT CopyEngineToWorkingFolder( + __in_z LPCWSTR wzSourcePath, + __in_z LPCWSTR wzWorkingFolderName, + __in_z LPCWSTR wzExecutableName, + __in BURN_SECTION* pSection, + __deref_out_z_opt LPWSTR* psczEngineWorkingPath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczWorkingFolder = NULL; + LPWSTR sczTargetDirectory = NULL; + LPWSTR sczTargetPath = NULL; + LPWSTR sczSourceDirectory = NULL; + LPWSTR sczPayloadSourcePath = NULL; + LPWSTR sczPayloadTargetPath = NULL; + + hr = CacheEnsureWorkingFolder(NULL, &sczWorkingFolder); + ExitOnFailure(hr, "Failed to create working path to copy engine."); + + hr = PathConcat(sczWorkingFolder, wzWorkingFolderName, &sczTargetDirectory); + ExitOnFailure(hr, "Failed to calculate the bundle working folder target name."); + + hr = DirEnsureExists(sczTargetDirectory, NULL); + ExitOnFailure(hr, "Failed create bundle working folder."); + + hr = PathConcat(sczTargetDirectory, wzExecutableName, &sczTargetPath); + ExitOnFailure(hr, "Failed to combine working path with engine file name."); + + // Copy the engine without any attached containers to the working path. + hr = CopyEngineWithSignatureFixup(pSection->hEngineFile, wzSourcePath, sczTargetPath, pSection); + ExitOnFailure(hr, "Failed to copy engine: '%ls' to working path: %ls", wzSourcePath, sczTargetPath); + + if (psczEngineWorkingPath) + { + hr = StrAllocString(psczEngineWorkingPath, sczTargetPath, 0); + ExitOnFailure(hr, "Failed to copy target path for engine working path."); + } + +LExit: + ReleaseStr(sczPayloadTargetPath); + ReleaseStr(sczPayloadSourcePath); + ReleaseStr(sczSourceDirectory); + ReleaseStr(sczTargetPath); + ReleaseStr(sczTargetDirectory); + ReleaseStr(sczWorkingFolder); + + return hr; +} + + +static HRESULT CopyEngineWithSignatureFixup( + __in HANDLE hEngineFile, + __in_z LPCWSTR wzEnginePath, + __in_z LPCWSTR wzTargetPath, + __in BURN_SECTION* pSection + ) +{ + HRESULT hr = S_OK; + HANDLE hTarget = INVALID_HANDLE_VALUE; + LARGE_INTEGER li = { }; + DWORD dwZeroOriginals[3] = { }; + + hTarget = ::CreateFileW(wzTargetPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hTarget) + { + ExitWithLastError(hr, "Failed to create engine file at path: %ls", wzTargetPath); + } + + hr = FileSetPointer(hEngineFile, 0, NULL, FILE_BEGIN); + ExitOnFailure(hr, "Failed to seek to beginning of engine file: %ls", wzEnginePath); + + hr = FileCopyUsingHandles(hEngineFile, hTarget, pSection->cbEngineSize, NULL); + ExitOnFailure(hr, "Failed to copy engine from: %ls to: %ls", wzEnginePath, wzTargetPath); + + // If the original executable was signed, let's put back the checksum and signature. + if (pSection->dwOriginalSignatureOffset) + { + // Fix up the checksum. + li.QuadPart = pSection->dwChecksumOffset; + if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to seek to checksum in exe header."); + } + + hr = FileWriteHandle(hTarget, reinterpret_cast(&pSection->dwOriginalChecksum), sizeof(pSection->dwOriginalChecksum)); + ExitOnFailure(hr, "Failed to update signature offset."); + + // Fix up the signature information. + li.QuadPart = pSection->dwCertificateTableOffset; + if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to seek to signature table in exe header."); + } + + hr = FileWriteHandle(hTarget, reinterpret_cast(&pSection->dwOriginalSignatureOffset), sizeof(pSection->dwOriginalSignatureOffset)); + ExitOnFailure(hr, "Failed to update signature offset."); + + hr = FileWriteHandle(hTarget, reinterpret_cast(&pSection->dwOriginalSignatureSize), sizeof(pSection->dwOriginalSignatureSize)); + ExitOnFailure(hr, "Failed to update signature offset."); + + // Zero out the original information since that is how it was when the file was originally signed. + li.QuadPart = pSection->dwOriginalChecksumAndSignatureOffset; + if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to seek to original data in exe burn section header."); + } + + hr = FileWriteHandle(hTarget, reinterpret_cast(&dwZeroOriginals), sizeof(dwZeroOriginals)); + ExitOnFailure(hr, "Failed to zero out original data offset."); + } + +LExit: + ReleaseFileHandle(hTarget); + + return hr; +} + + +static HRESULT RemoveBundleOrPackage( + __in BOOL fBundle, + __in BOOL fPerMachine, + __in_z LPCWSTR wzBundleOrPackageId, + __in_z LPCWSTR wzCacheId + ) +{ + HRESULT hr = S_OK; + LPWSTR sczRootCacheDirectory = NULL; + LPWSTR sczDirectory = NULL; + + hr = CacheGetCompletedPath(fPerMachine, wzCacheId, &sczDirectory); + ExitOnFailure(hr, "Failed to calculate cache path."); + + LogId(REPORT_STANDARD, fBundle ? MSG_UNCACHE_BUNDLE : MSG_UNCACHE_PACKAGE, wzBundleOrPackageId, sczDirectory); + + // Try really hard to remove the cache directory. + hr = E_FAIL; + for (DWORD iRetry = 0; FAILED(hr) && iRetry < FILE_OPERATION_RETRY_COUNT; ++iRetry) + { + if (0 < iRetry) + { + ::Sleep(FILE_OPERATION_RETRY_WAIT); + } + + hr = DirEnsureDeleteEx(sczDirectory, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE); + if (E_PATHNOTFOUND == hr) + { + break; + } + } + + if (E_PATHNOTFOUND != hr && FAILED(hr)) + { + LogId(REPORT_STANDARD, fBundle ? MSG_UNABLE_UNCACHE_BUNDLE : MSG_UNABLE_UNCACHE_PACKAGE, wzBundleOrPackageId, sczDirectory, hr); + hr = S_OK; + } + else + { + // Try to remove root package cache in the off chance it is now empty. + hr = GetRootPath(fPerMachine, TRUE, &sczRootCacheDirectory); + ExitOnFailure(hr, "Failed to get %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user"); + DirEnsureDeleteEx(sczRootCacheDirectory, DIR_DELETE_SCHEDULE); + + // GetRootPath returns S_FALSE if the package cache is redirected elsewhere. + if (S_FALSE == hr) + { + hr = GetRootPath(fPerMachine, FALSE, &sczRootCacheDirectory); + ExitOnFailure(hr, "Failed to get old %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user"); + DirEnsureDeleteEx(sczRootCacheDirectory, DIR_DELETE_SCHEDULE); + } + } + +LExit: + ReleaseStr(sczDirectory); + ReleaseStr(sczRootCacheDirectory); + + return hr; +} + +static HRESULT VerifyHash( + __in BYTE* pbHash, + __in DWORD cbHash, + __in DWORD64 qwFileSize, + __in_z LPCWSTR wzUnverifiedPayloadPath, + __in HANDLE hFile, + __in BURN_CACHE_STEP cacheStep, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE /*pfnProgress*/, + __in LPVOID pContext + ) +{ + UNREFERENCED_PARAMETER(wzUnverifiedPayloadPath); + + HRESULT hr = S_OK; + BYTE rgbActualHash[SHA512_HASH_LEN] = { }; + DWORD64 qwHashedBytes = 0; + LONGLONG llSize = 0; + LPWSTR pszExpected = NULL; + LPWSTR pszActual = NULL; + + hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, cacheStep); + ExitOnFailure(hr, "Aborted cache verify hash begin."); + + hr = FileSizeByHandle(hFile, &llSize); + ExitOnFailure(hr, "Failed to get file size for path: %ls", wzUnverifiedPayloadPath); + + if (static_cast(llSize) != qwFileSize) + { + ExitOnFailure(hr = ERROR_FILE_CORRUPT, "File size mismatch for path: %ls, expected: %llu, actual: %lld", wzUnverifiedPayloadPath, qwFileSize, llSize); + } + + // TODO: create a cryp hash file that sends progress. + hr = CrypHashFileHandle(hFile, PROV_RSA_AES, CALG_SHA_512, rgbActualHash, sizeof(rgbActualHash), &qwHashedBytes); + ExitOnFailure(hr, "Failed to calculate hash for path: %ls", wzUnverifiedPayloadPath); + + // Compare hashes. + if (cbHash != sizeof(rgbActualHash) || 0 != memcmp(pbHash, rgbActualHash, sizeof(rgbActualHash))) + { + hr = CRYPT_E_HASH_VALUE; + + // Best effort to log the expected and actual hash value strings. + if (SUCCEEDED(StrAllocHexEncode(pbHash, cbHash, &pszExpected)) && + SUCCEEDED(StrAllocHexEncode(rgbActualHash, sizeof(rgbActualHash), &pszActual))) + { + ExitOnFailure(hr, "Hash mismatch for path: %ls, expected: %ls, actual: %ls", wzUnverifiedPayloadPath, pszExpected, pszActual); + } + else + { + ExitOnFailure(hr, "Hash mismatch for path: %ls", wzUnverifiedPayloadPath); + } + } + + hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, qwFileSize); + +LExit: + SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr); + + ReleaseStr(pszActual); + ReleaseStr(pszExpected); + + return hr; +} + +static HRESULT SendCacheBeginMessage( + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPVOID pContext, + __in BURN_CACHE_STEP cacheStep + ) +{ + HRESULT hr = S_OK; + BURN_CACHE_MESSAGE message = { }; + + message.type = BURN_CACHE_MESSAGE_BEGIN; + message.begin.cacheStep = cacheStep; + + hr = pfnCacheMessageHandler(&message, pContext); + + return hr; +} + +static HRESULT SendCacheSuccessMessage( + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPVOID pContext, + __in DWORD64 qwFileSize + ) +{ + HRESULT hr = S_OK; + BURN_CACHE_MESSAGE message = { }; + + message.type = BURN_CACHE_MESSAGE_SUCCESS; + message.success.qwFileSize = qwFileSize; + + hr = pfnCacheMessageHandler(&message, pContext); + + return hr; +} + +static HRESULT SendCacheCompleteMessage( + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPVOID pContext, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BURN_CACHE_MESSAGE message = { }; + + message.type = BURN_CACHE_MESSAGE_COMPLETE; + message.complete.hrStatus = hrStatus; + + hr = pfnCacheMessageHandler(&message, pContext); + + return hr; +} diff --git a/src/burn/engine/cache.h b/src/burn/engine/cache.h new file mode 100644 index 00000000..0152d33b --- /dev/null +++ b/src/burn/engine/cache.h @@ -0,0 +1,216 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#define BURN_CACHE_MAX_SEARCH_PATHS 7 + +#ifdef __cplusplus +extern "C" { +#endif + + +enum BURN_CACHE_MESSAGE_TYPE +{ + BURN_CACHE_MESSAGE_BEGIN, + BURN_CACHE_MESSAGE_SUCCESS, + BURN_CACHE_MESSAGE_COMPLETE, +}; + +enum BURN_CACHE_STEP +{ + BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE, + BURN_CACHE_STEP_HASH_TO_SKIP_VERIFY, + BURN_CACHE_STEP_STAGE, + BURN_CACHE_STEP_HASH, + BURN_CACHE_STEP_FINALIZE, +}; + +typedef struct _BURN_CACHE_MESSAGE +{ + BURN_CACHE_MESSAGE_TYPE type; + + union + { + struct + { + BURN_CACHE_STEP cacheStep; + } begin; + struct + { + DWORD64 qwFileSize; + } success; + struct + { + HRESULT hrStatus; + } complete; + }; +} BURN_CACHE_MESSAGE; + +typedef HRESULT(CALLBACK* PFN_BURNCACHEMESSAGEHANDLER)( + __in BURN_CACHE_MESSAGE* pMessage, + __in LPVOID pvContext + ); + +// functions + +HRESULT CacheInitialize( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in_z_opt LPCWSTR wzSourceProcessPath + ); +HRESULT CacheEnsureWorkingFolder( + __in_z_opt LPCWSTR wzBundleId, + __deref_out_z_opt LPWSTR* psczWorkingFolder + ); +HRESULT CacheCalculateBundleWorkingPath( + __in_z LPCWSTR wzBundleId, + __in LPCWSTR wzExecutableName, + __deref_out_z LPWSTR* psczWorkingPath + ); +HRESULT CacheCalculateBundleLayoutWorkingPath( + __in_z LPCWSTR wzBundleId, + __deref_out_z LPWSTR* psczWorkingPath + ); +HRESULT CacheCalculatePayloadWorkingPath( + __in_z LPCWSTR wzBundleId, + __in BURN_PAYLOAD* pPayload, + __deref_out_z LPWSTR* psczWorkingPath + ); +HRESULT CacheCalculateContainerWorkingPath( + __in_z LPCWSTR wzBundleId, + __in BURN_CONTAINER* pContainer, + __deref_out_z LPWSTR* psczWorkingPath + ); +HRESULT CacheGetRootCompletedPath( + __in BOOL fPerMachine, + __in BOOL fForceInitialize, + __deref_out_z LPWSTR* psczRootCompletedPath + ); +HRESULT CacheGetCompletedPath( + __in BOOL fPerMachine, + __in_z LPCWSTR wzCacheId, + __deref_out_z LPWSTR* psczCompletedPath + ); +HRESULT CacheGetResumePath( + __in_z LPCWSTR wzPayloadWorkingPath, + __deref_out_z LPWSTR* psczResumePath + ); +HRESULT CacheGetLocalSourcePaths( + __in_z LPCWSTR wzRelativePath, + __in_z LPCWSTR wzSourcePath, + __in_z LPCWSTR wzDestinationPath, + __in_z_opt LPCWSTR wzLayoutDirectory, + __in BURN_VARIABLES* pVariables, + __inout LPWSTR** prgSearchPaths, + __out DWORD* pcSearchPaths, + __out DWORD* pdwLikelySearchPath, + __out DWORD* pdwDestinationSearchPath + ); +HRESULT CacheSetLastUsedSource( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzSourcePath, + __in_z LPCWSTR wzRelativePath + ); +HRESULT CacheSendProgressCallback( + __in DOWNLOAD_CACHE_CALLBACK* pCallback, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in HANDLE hDestinationFile + ); +void CacheSendErrorCallback( + __in DOWNLOAD_CACHE_CALLBACK* pCallback, + __in HRESULT hrError, + __in_z_opt LPCWSTR wzError, + __out_opt BOOL* pfRetry + ); +BOOL CacheBundleRunningFromCache(); +HRESULT CacheBundleToCleanRoom( + __in BURN_SECTION* pSection, + __deref_out_z_opt LPWSTR* psczCleanRoomBundlePath + ); +HRESULT CacheBundleToWorkingDirectory( + __in_z LPCWSTR wzBundleId, + __in_z LPCWSTR wzExecutableName, + __in BURN_SECTION* pSection, + __deref_out_z_opt LPWSTR* psczEngineWorkingPath + ); +HRESULT CacheLayoutBundle( + __in_z LPCWSTR wzExecutableName, + __in_z LPCWSTR wzLayoutDirectory, + __in_z LPCWSTR wzSourceBundlePath, + __in DWORD64 qwBundleSize, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ); +HRESULT CacheCompleteBundle( + __in BOOL fPerMachine, + __in_z LPCWSTR wzExecutableName, + __in_z LPCWSTR wzBundleId, + __in_z LPCWSTR wzSourceBundlePath +#ifdef DEBUG + , __in_z LPCWSTR wzExecutablePath +#endif + ); +HRESULT CacheLayoutContainer( + __in BURN_CONTAINER* pContainer, + __in_z_opt LPCWSTR wzLayoutDirectory, + __in_z LPCWSTR wzUnverifiedContainerPath, + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ); +HRESULT CacheLayoutPayload( + __in BURN_PAYLOAD* pPayload, + __in_z_opt LPCWSTR wzLayoutDirectory, + __in_z LPCWSTR wzUnverifiedPayloadPath, + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ); +HRESULT CacheCompletePayload( + __in BOOL fPerMachine, + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzCacheId, + __in_z LPCWSTR wzUnverifiedPayloadPath, + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ); +HRESULT CacheVerifyContainer( + __in BURN_CONTAINER* pContainer, + __in_z LPCWSTR wzCachedDirectory, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ); +HRESULT CacheVerifyPayload( + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzCachedDirectory, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ); +HRESULT CacheRemoveWorkingFolder( + __in_z_opt LPCWSTR wzBundleId + ); +HRESULT CacheRemoveBundle( + __in BOOL fPerMachine, + __in_z LPCWSTR wzPackageId + ); +HRESULT CacheRemovePackage( + __in BOOL fPerMachine, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzCacheId + ); +void CacheCleanup( + __in BOOL fPerMachine, + __in_z LPCWSTR wzBundleId + ); +void CacheUninitialize(); + +#ifdef __cplusplus +} +#endif diff --git a/src/burn/engine/condition.cpp b/src/burn/engine/condition.cpp new file mode 100644 index 00000000..b7cd7413 --- /dev/null +++ b/src/burn/engine/condition.cpp @@ -0,0 +1,1057 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// +// parse rules +// +// value variable | literal | integer | version +// comparison-operator < | > | <= | >= | = | <> | >< | << | >> +// term value | value comparison-operator value | ( expression ) +// boolean-factor term | NOT term +// boolean-term boolean-factor | boolean-factor AND boolean-term +// expression boolean-term | boolean-term OR expression +// + + +// constants + +#define COMPARISON 0x00010000 +#define INSENSITIVE 0x00020000 + +enum BURN_SYMBOL_TYPE +{ + // terminals + BURN_SYMBOL_TYPE_NONE = 0, + BURN_SYMBOL_TYPE_END = 1, + BURN_SYMBOL_TYPE_OR = 2, // OR + BURN_SYMBOL_TYPE_AND = 3, // AND + BURN_SYMBOL_TYPE_NOT = 4, // NOT + BURN_SYMBOL_TYPE_LT = 5 | COMPARISON, // < + BURN_SYMBOL_TYPE_GT = 6 | COMPARISON, // > + BURN_SYMBOL_TYPE_LE = 7 | COMPARISON, // <= + BURN_SYMBOL_TYPE_GE = 8 | COMPARISON, // >= + BURN_SYMBOL_TYPE_EQ = 9 | COMPARISON, // = + BURN_SYMBOL_TYPE_NE = 10 | COMPARISON, // <> + BURN_SYMBOL_TYPE_BAND = 11 | COMPARISON, // >< + BURN_SYMBOL_TYPE_HIEQ = 12 | COMPARISON, // << + BURN_SYMBOL_TYPE_LOEQ = 13 | COMPARISON, // >> + BURN_SYMBOL_TYPE_LT_I = 5 | COMPARISON | INSENSITIVE, // ~< + BURN_SYMBOL_TYPE_GT_I = 6 | COMPARISON | INSENSITIVE, // ~> + BURN_SYMBOL_TYPE_LE_I = 7 | COMPARISON | INSENSITIVE, // ~<= + BURN_SYMBOL_TYPE_GE_I = 8 | COMPARISON | INSENSITIVE, // ~>= + BURN_SYMBOL_TYPE_EQ_I = 9 | COMPARISON | INSENSITIVE, // ~= + BURN_SYMBOL_TYPE_NE_I = 10 | COMPARISON | INSENSITIVE, // ~<> + BURN_SYMBOL_TYPE_BAND_I = 11 | COMPARISON | INSENSITIVE, // ~>< + BURN_SYMBOL_TYPE_HIEQ_I = 12 | COMPARISON | INSENSITIVE, // ~<< + BURN_SYMBOL_TYPE_LOEQ_I = 13 | COMPARISON | INSENSITIVE, // ~>> + BURN_SYMBOL_TYPE_LPAREN = 14, // ( + BURN_SYMBOL_TYPE_RPAREN = 15, // ) + BURN_SYMBOL_TYPE_NUMBER = 16, + BURN_SYMBOL_TYPE_IDENTIFIER = 17, + BURN_SYMBOL_TYPE_LITERAL = 18, + BURN_SYMBOL_TYPE_VERSION = 19, +}; + + +// structs + +struct BURN_SYMBOL +{ + BURN_SYMBOL_TYPE Type; + DWORD iPosition; + BURN_VARIANT Value; +}; + +struct BURN_CONDITION_PARSE_CONTEXT +{ + BURN_VARIABLES* pVariables; + LPCWSTR wzCondition; + LPCWSTR wzRead; + BURN_SYMBOL NextSymbol; + BOOL fError; +}; + +struct BURN_CONDITION_OPERAND +{ + BOOL fHidden; + BURN_VARIANT Value; +}; + + +// internal function declarations + +static HRESULT ParseExpression( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __out BOOL* pf + ); +static HRESULT ParseBooleanTerm( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __out BOOL* pf + ); +static HRESULT ParseBooleanFactor( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __out BOOL* pf + ); +static HRESULT ParseTerm( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __out BOOL* pf + ); +static HRESULT ParseOperand( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __out BURN_CONDITION_OPERAND* pOperand + ); +static HRESULT Expect( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __in BURN_SYMBOL_TYPE symbolType + ); +static HRESULT NextSymbol( + __in BURN_CONDITION_PARSE_CONTEXT* pContext + ); +static HRESULT CompareOperands( + __in BURN_SYMBOL_TYPE comparison, + __in BURN_CONDITION_OPERAND* pLeftOperand, + __in BURN_CONDITION_OPERAND* pRightOperand, + __out BOOL* pfResult + ); +static HRESULT CompareStringValues( + __in BURN_SYMBOL_TYPE comparison, + __in_z LPCWSTR wzLeftOperand, + __in_z LPCWSTR wzRightOperand, + __out BOOL* pfResult + ); +static HRESULT CompareIntegerValues( + __in BURN_SYMBOL_TYPE comparison, + __in LONGLONG llLeftOperand, + __in LONGLONG llRightOperand, + __out BOOL* pfResult + ); +static HRESULT CompareVersionValues( + __in BURN_SYMBOL_TYPE comparison, + __in VERUTIL_VERSION* pLeftOperand, + __in VERUTIL_VERSION* pRightOperand, + __out BOOL* pfResult + ); + + +// function definitions + +extern "C" HRESULT ConditionEvaluate( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzCondition, + __out BOOL* pf + ) +{ + HRESULT hr = S_OK; + BURN_CONDITION_PARSE_CONTEXT context = { }; + BOOL f = FALSE; + + context.pVariables = pVariables; + context.wzCondition = wzCondition; + context.wzRead = wzCondition; + + hr = NextSymbol(&context); + ExitOnFailure(hr, "Failed to read next symbol."); + + hr = ParseExpression(&context, &f); + ExitOnFailure(hr, "Failed to parse expression."); + + hr = Expect(&context, BURN_SYMBOL_TYPE_END); + ExitOnFailure(hr, "Failed to expect end symbol."); + + LogId(REPORT_VERBOSE, MSG_CONDITION_RESULT, wzCondition, LoggingTrueFalseToString(f)); + + *pf = f; + hr = S_OK; + +LExit: + if (context.fError) + { + Assert(FAILED(hr)); + LogErrorId(hr, MSG_FAILED_PARSE_CONDITION, wzCondition, NULL, NULL); + } + + return hr; +} + +extern "C" HRESULT ConditionGlobalCheck( + __in BURN_VARIABLES* pVariables, + __in BURN_CONDITION* pCondition, + __in BOOTSTRAPPER_DISPLAY display, + __in_z LPCWSTR wzBundleName, + __out DWORD *pdwExitCode, + __out BOOL *pfContinueExecution + ) +{ + HRESULT hr = S_OK; + BOOL fSuccess = TRUE; + HRESULT hrError = HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION); + + // Only run on Windows Vista SP2 or newer, or Windows Server 2008 SP2 or newer. + if (!::IsWindowsVistaSP2OrGreater()) + { + fSuccess = FALSE; + } + else + { + if (NULL != pCondition->sczConditionString) + { + hr = ConditionEvaluate(pVariables, pCondition->sczConditionString, &fSuccess); + ExitOnFailure(hr, "Failed to evaluate condition: %ls", pCondition->sczConditionString); + } + } + + if (!fSuccess) + { + // Display the error messagebox, as long as we're in an appropriate display mode + hr = SplashScreenDisplayError(display, wzBundleName, hrError); + ExitOnFailure(hr, "Failed to display error dialog"); + + *pdwExitCode = static_cast(hrError); + *pfContinueExecution = FALSE; + } + +LExit: + return hr; +} + +HRESULT ConditionGlobalParseFromXml( + __in BURN_CONDITION* pCondition, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNode* pixnNode = NULL; + BSTR bstrExpression = NULL; + + // select variable nodes + hr = XmlSelectSingleNode(pixnBundle, L"Condition", &pixnNode); + if (S_FALSE == hr) + { + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to select condition node."); + + // @Condition + hr = XmlGetText(pixnNode, &bstrExpression); + ExitOnFailure(hr, "Failed to get Condition inner text."); + + hr = StrAllocString(&pCondition->sczConditionString, bstrExpression, 0); + ExitOnFailure(hr, "Failed to copy condition string from BSTR"); + +LExit: + ReleaseBSTR(bstrExpression); + ReleaseObject(pixnNode); + + return hr; +} + + +// internal function definitions + +static HRESULT ParseExpression( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __out BOOL* pf + ) +{ + HRESULT hr = S_OK; + BOOL fFirst = FALSE; + BOOL fSecond = FALSE; + + hr = ParseBooleanTerm(pContext, &fFirst); + ExitOnFailure(hr, "Failed to parse boolean-term."); + + if (BURN_SYMBOL_TYPE_OR == pContext->NextSymbol.Type) + { + hr = NextSymbol(pContext); + ExitOnFailure(hr, "Failed to read next symbol."); + + hr = ParseExpression(pContext, &fSecond); + ExitOnFailure(hr, "Failed to parse expression."); + + *pf = fFirst || fSecond; + } + else + { + *pf = fFirst; + } + +LExit: + return hr; +} + +static HRESULT ParseBooleanTerm( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __out BOOL* pf + ) +{ + HRESULT hr = S_OK; + BOOL fFirst = FALSE; + BOOL fSecond = FALSE; + + hr = ParseBooleanFactor(pContext, &fFirst); + ExitOnFailure(hr, "Failed to parse boolean-factor."); + + if (BURN_SYMBOL_TYPE_AND == pContext->NextSymbol.Type) + { + hr = NextSymbol(pContext); + ExitOnFailure(hr, "Failed to read next symbol."); + + hr = ParseBooleanTerm(pContext, &fSecond); + ExitOnFailure(hr, "Failed to parse boolean-term."); + + *pf = fFirst && fSecond; + } + else + { + *pf = fFirst; + } + +LExit: + return hr; +} + +static HRESULT ParseBooleanFactor( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __out BOOL* pf + ) +{ + HRESULT hr = S_OK; + BOOL fNot = FALSE; + BOOL f = FALSE; + + if (BURN_SYMBOL_TYPE_NOT == pContext->NextSymbol.Type) + { + hr = NextSymbol(pContext); + ExitOnFailure(hr, "Failed to read next symbol."); + + fNot = TRUE; + } + + hr = ParseTerm(pContext, &f); + ExitOnFailure(hr, "Failed to parse term."); + + *pf = fNot ? !f : f; + +LExit: + return hr; +} + +static HRESULT ParseTerm( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __out BOOL* pf + ) +{ + HRESULT hr = S_OK; + BURN_CONDITION_OPERAND firstOperand = { }; + BURN_CONDITION_OPERAND secondOperand = { }; + + if (BURN_SYMBOL_TYPE_LPAREN == pContext->NextSymbol.Type) + { + hr = NextSymbol(pContext); + ExitOnFailure(hr, "Failed to read next symbol."); + + hr = ParseExpression(pContext, pf); + ExitOnFailure(hr, "Failed to parse expression."); + + hr = Expect(pContext, BURN_SYMBOL_TYPE_RPAREN); + ExitOnFailure(hr, "Failed to expect right parenthesis."); + + ExitFunction1(hr = S_OK); + } + + hr = ParseOperand(pContext, &firstOperand); + ExitOnFailure(hr, "Failed to parse operand."); + + if (COMPARISON & pContext->NextSymbol.Type) + { + BURN_SYMBOL_TYPE comparison = pContext->NextSymbol.Type; + + hr = NextSymbol(pContext); + ExitOnFailure(hr, "Failed to read next symbol."); + + hr = ParseOperand(pContext, &secondOperand); + ExitOnFailure(hr, "Failed to parse operand."); + + hr = CompareOperands(comparison, &firstOperand, &secondOperand, pf); + ExitOnFailure(hr, "Failed to compare operands."); + } + else + { + LONGLONG llValue = 0; + LPWSTR sczValue = NULL; + VERUTIL_VERSION* pVersion = NULL; + switch (firstOperand.Value.Type) + { + case BURN_VARIANT_TYPE_NONE: + *pf = FALSE; + break; + case BURN_VARIANT_TYPE_STRING: + hr = BVariantGetString(&firstOperand.Value, &sczValue); + if (SUCCEEDED(hr)) + { + *pf = sczValue && *sczValue; + } + StrSecureZeroFreeString(sczValue); + break; + case BURN_VARIANT_TYPE_NUMERIC: + hr = BVariantGetNumeric(&firstOperand.Value, &llValue); + if (SUCCEEDED(hr)) + { + *pf = 0 != llValue; + } + SecureZeroMemory(&llValue, sizeof(llValue)); + break; + case BURN_VARIANT_TYPE_VERSION: + hr = BVariantGetVersionHidden(&firstOperand.Value, firstOperand.fHidden, &pVersion); + if (SUCCEEDED(hr)) + { + *pf = 0 != *pVersion->sczVersion; + } + ReleaseVerutilVersion(pVersion); + break; + default: + ExitFunction1(hr = E_UNEXPECTED); + } + } + +LExit: + BVariantUninitialize(&firstOperand.Value); + BVariantUninitialize(&secondOperand.Value); + return hr; +} + +static HRESULT ParseOperand( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __out BURN_CONDITION_OPERAND* pOperand + ) +{ + HRESULT hr = S_OK; + LPWSTR sczFormatted = NULL; + + switch (pContext->NextSymbol.Type) + { + case BURN_SYMBOL_TYPE_IDENTIFIER: + Assert(BURN_VARIANT_TYPE_STRING == pContext->NextSymbol.Value.Type); + + // find variable + hr = VariableGetVariant(pContext->pVariables, pContext->NextSymbol.Value.sczValue, &pOperand->Value); + if (E_NOTFOUND != hr) + { + ExitOnRootFailure(hr, "Failed to find variable."); + + hr = VariableIsHidden(pContext->pVariables, pContext->NextSymbol.Value.sczValue, &pOperand->fHidden); + ExitOnRootFailure(hr, "Failed to get if variable is hidden."); + } + + if (BURN_VARIANT_TYPE_FORMATTED == pOperand->Value.Type) + { + hr = VariableGetFormatted(pContext->pVariables, pContext->NextSymbol.Value.sczValue, &sczFormatted, &pOperand->fHidden); + ExitOnRootFailure(hr, "Failed to format variable '%ls' for condition '%ls'", pContext->NextSymbol.Value.sczValue, pContext->wzCondition); + + hr = BVariantSetString(&pOperand->Value, sczFormatted, 0, FALSE); + ExitOnRootFailure(hr, "Failed to store formatted value for variable '%ls' for condition '%ls'", pContext->NextSymbol.Value.sczValue, pContext->wzCondition); + } + break; + + case BURN_SYMBOL_TYPE_NUMBER: __fallthrough; + case BURN_SYMBOL_TYPE_LITERAL: __fallthrough; + case BURN_SYMBOL_TYPE_VERSION: + pOperand->fHidden = FALSE; + // steal value of symbol + memcpy_s(&pOperand->Value, sizeof(BURN_VARIANT), &pContext->NextSymbol.Value, sizeof(BURN_VARIANT)); + memset(&pContext->NextSymbol.Value, 0, sizeof(BURN_VARIANT)); + break; + + default: + pContext->fError = TRUE; + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Failed to parse condition '%ls' at position: %u", pContext->wzCondition, pContext->NextSymbol.iPosition); + } + + // get next symbol + hr = NextSymbol(pContext); + ExitOnFailure(hr, "Failed to read next symbol."); + +LExit: + StrSecureZeroFreeString(sczFormatted); + + return hr; +} + +// +// Expect - expects a symbol. +// +static HRESULT Expect( + __in BURN_CONDITION_PARSE_CONTEXT* pContext, + __in BURN_SYMBOL_TYPE symbolType + ) +{ + HRESULT hr = S_OK; + + if (pContext->NextSymbol.Type != symbolType) + { + pContext->fError = TRUE; + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Failed to parse condition '%ls' at position: %u", pContext->wzCondition, pContext->NextSymbol.iPosition); + } + + hr = NextSymbol(pContext); + ExitOnFailure(hr, "Failed to read next symbol."); + +LExit: + return hr; +} + +// +// NextSymbol - finds the next symbol in an expression string. +// +static HRESULT NextSymbol( + __in BURN_CONDITION_PARSE_CONTEXT* pContext + ) +{ + HRESULT hr = S_OK; + WORD charType = 0; + ptrdiff_t cchPosition = 0; + DWORD iPosition = 0; + DWORD n = 0; + + // free existing symbol + BVariantUninitialize(&pContext->NextSymbol.Value); + memset(&pContext->NextSymbol, 0, sizeof(BURN_SYMBOL)); + + // skip past blanks + while (L'\0' != pContext->wzRead[0]) + { + ::GetStringTypeW(CT_CTYPE1, pContext->wzRead, 1, &charType); + if (0 == (C1_BLANK & charType)) + { + break; // no blank, done + } + ++pContext->wzRead; + } + + cchPosition = pContext->wzRead - pContext->wzCondition; + if (DWORD_MAX < cchPosition || 0 > cchPosition) + { + ExitOnFailure(hr = E_INVALIDARG, "Symbol was too long: %ls", pContext->wzCondition); + } + iPosition = (DWORD)cchPosition; + + // read depending on first character type + switch (pContext->wzRead[0]) + { + case L'\0': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_END; + break; + case L'~': + switch (pContext->wzRead[1]) + { + case L'=': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_EQ_I; + n = 2; + break; + case L'>': + switch (pContext->wzRead[2]) + { + case '=': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GE_I; + n = 3; + break; + case L'>': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LOEQ_I; + n = 3; + break; + case L'<': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_BAND_I; + n = 3; + break; + default: + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GT_I; + n = 2; + } + break; + case L'<': + switch (pContext->wzRead[2]) + { + case '=': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LE_I; + n = 3; + break; + case L'<': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_HIEQ_I; + n = 3; + break; + case '>': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NE_I; + n = 3; + break; + default: + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LT_I; + n = 2; + } + break; + default: + // error + pContext->fError = TRUE; + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unexpected '~' operator at position %d.", pContext->wzCondition, iPosition); + } + break; + case L'>': + switch (pContext->wzRead[1]) + { + case L'=': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GE; + n = 2; + break; + case L'>': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LOEQ; + n = 2; + break; + case L'<': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_BAND; + n = 2; + break; + default: + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GT; + n = 1; + } + break; + case L'<': + switch (pContext->wzRead[1]) + { + case L'=': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LE; + n = 2; + break; + case L'<': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_HIEQ; + n = 2; + break; + case L'>': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NE; + n = 2; + break; + default: + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LT; + n = 1; + } + break; + case L'=': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_EQ; + n = 1; + break; + case L'(': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LPAREN; + n = 1; + break; + case L')': + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_RPAREN; + n = 1; + break; + case L'"': // literal + do + { + ++n; + if (L'\0' == pContext->wzRead[n]) + { + // error + pContext->fError = TRUE; + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unterminated literal at position %d.", pContext->wzCondition, iPosition); + } + } while (L'"' != pContext->wzRead[n]); + ++n; // terminating '"' + + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LITERAL; + hr = BVariantSetString(&pContext->NextSymbol.Value, &pContext->wzRead[1], n - 2, FALSE); + ExitOnFailure(hr, "Failed to set symbol value."); + break; + default: + if (C1_DIGIT & charType || L'-' == pContext->wzRead[0]) + { + do + { + ++n; + ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType); + if (C1_ALPHA & charType || L'_' == pContext->wzRead[n]) + { + // error, identifier cannot start with a digit + pContext->fError = TRUE; + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Identifier cannot start at a digit, at position %d.", pContext->wzCondition, iPosition); + } + } while (C1_DIGIT & charType); + + // number + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NUMBER; + + LONGLONG ll = 0; + hr = StrStringToInt64(pContext->wzRead, n, &ll); + if (FAILED(hr)) + { + pContext->fError = TRUE; + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Constant too big, at position %d.", pContext->wzCondition, iPosition); + } + + hr = BVariantSetNumeric(&pContext->NextSymbol.Value, ll); + ExitOnFailure(hr, "Failed to set symbol value."); + } + else if (C1_ALPHA & charType || L'_' == pContext->wzRead[0]) + { + ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[1], 1, &charType); + if (L'v' == pContext->wzRead[0] && C1_DIGIT & charType) + { + // version + do + { + ++n; + } while (pContext->wzRead[n] >= L'0' && pContext->wzRead[n] <= L'9' || + pContext->wzRead[n] >= L'A' && pContext->wzRead[n] <= L'Z' || + pContext->wzRead[n] >= L'a' && pContext->wzRead[n] <= L'z' || + pContext->wzRead[n] == L'_' || + pContext->wzRead[n] == L'+' || + pContext->wzRead[n] == L'-' || + pContext->wzRead[n] == L'.'); + + hr = VerParseVersion(&pContext->wzRead[1], n - 1, FALSE, &pContext->NextSymbol.Value.pValue); + if (FAILED(hr)) + { + pContext->fError = TRUE; + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Invalid version format, at position %d.", pContext->wzCondition, iPosition); + } + else if (pContext->NextSymbol.Value.pValue->fInvalid) + { + LogId(REPORT_WARNING, MSG_CONDITION_INVALID_VERSION, pContext->wzCondition, pContext->NextSymbol.Value.pValue->sczVersion); + } + + pContext->NextSymbol.Value.Type = BURN_VARIANT_TYPE_VERSION; + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_VERSION; + } + else + { + do + { + ++n; + ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType); + } while (C1_ALPHA & charType || C1_DIGIT & charType || L'_' == pContext->wzRead[n]); + + if (2 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 2, L"OR", 2)) + { + // OR + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_OR; + } + else if (3 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 3, L"AND", 3)) + { + // AND + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_AND; + } + else if (3 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 3, L"NOT", 3)) + { + // NOT + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NOT; + } + else + { + // identifier + pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_IDENTIFIER; + hr = BVariantSetString(&pContext->NextSymbol.Value, pContext->wzRead, n, FALSE); + ExitOnFailure(hr, "Failed to set symbol value."); + } + } + } + else + { + // error, unexpected character + pContext->fError = TRUE; + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unexpected character at position %d.", pContext->wzCondition, iPosition); + } + } + pContext->NextSymbol.iPosition = iPosition; + pContext->wzRead += n; + +LExit: + return hr; +} + +// +// CompareOperands - compares two variant values using a given comparison. +// +static HRESULT CompareOperands( + __in BURN_SYMBOL_TYPE comparison, + __in BURN_CONDITION_OPERAND* pLeftOperand, + __in BURN_CONDITION_OPERAND* pRightOperand, + __out BOOL* pfResult + ) +{ + HRESULT hr = S_OK; + LONGLONG llLeft = 0; + VERUTIL_VERSION* pVersionLeft = 0; + LPWSTR sczLeft = NULL; + LONGLONG llRight = 0; + VERUTIL_VERSION* pVersionRight = 0; + LPWSTR sczRight = NULL; + BURN_VARIANT* pLeftValue = &pLeftOperand->Value; + BURN_VARIANT* pRightValue = &pRightOperand->Value; + + // get values to compare based on type + if (BURN_VARIANT_TYPE_STRING == pLeftValue->Type && BURN_VARIANT_TYPE_STRING == pRightValue->Type) + { + hr = BVariantGetString(pLeftValue, &sczLeft); + ExitOnFailure(hr, "Failed to get the left string"); + hr = BVariantGetString(pRightValue, &sczRight); + ExitOnFailure(hr, "Failed to get the right string"); + hr = CompareStringValues(comparison, sczLeft, sczRight, pfResult); + } + else if (BURN_VARIANT_TYPE_NUMERIC == pLeftValue->Type && BURN_VARIANT_TYPE_NUMERIC == pRightValue->Type) + { + hr = BVariantGetNumeric(pLeftValue, &llLeft); + ExitOnFailure(hr, "Failed to get the left numeric"); + hr = BVariantGetNumeric(pRightValue, &llRight); + ExitOnFailure(hr, "Failed to get the right numeric"); + hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult); + } + else if (BURN_VARIANT_TYPE_VERSION == pLeftValue->Type && BURN_VARIANT_TYPE_VERSION == pRightValue->Type) + { + hr = BVariantGetVersionHidden(pLeftValue, pLeftOperand->fHidden, &pVersionLeft); + ExitOnFailure(hr, "Failed to get the left version"); + hr = BVariantGetVersionHidden(pRightValue, pRightOperand->fHidden, &pVersionRight); + ExitOnFailure(hr, "Failed to get the right version"); + hr = CompareVersionValues(comparison, pVersionLeft, pVersionRight, pfResult); + } + else if (BURN_VARIANT_TYPE_VERSION == pLeftValue->Type && BURN_VARIANT_TYPE_STRING == pRightValue->Type) + { + hr = BVariantGetVersionHidden(pLeftValue, pLeftOperand->fHidden, &pVersionLeft); + ExitOnFailure(hr, "Failed to get the left version"); + hr = BVariantGetVersionHidden(pRightValue, pRightOperand->fHidden, &pVersionRight); + if (FAILED(hr)) + { + if (DISP_E_TYPEMISMATCH != hr) + { + ExitOnFailure(hr, "Failed to get the right version"); + } + *pfResult = (BURN_SYMBOL_TYPE_NE == comparison); + hr = S_OK; + } + else + { + hr = CompareVersionValues(comparison, pVersionLeft, pVersionRight, pfResult); + } + } + else if (BURN_VARIANT_TYPE_STRING == pLeftValue->Type && BURN_VARIANT_TYPE_VERSION == pRightValue->Type) + { + hr = BVariantGetVersionHidden(pRightValue, pRightOperand->fHidden, &pVersionRight); + ExitOnFailure(hr, "Failed to get the right version"); + hr = BVariantGetVersionHidden(pLeftValue, pLeftOperand->fHidden, &pVersionLeft); + if (FAILED(hr)) + { + if (DISP_E_TYPEMISMATCH != hr) + { + ExitOnFailure(hr, "Failed to get the left version"); + } + *pfResult = (BURN_SYMBOL_TYPE_NE == comparison); + hr = S_OK; + } + else + { + hr = CompareVersionValues(comparison, pVersionLeft, pVersionRight, pfResult); + } + } + else if (BURN_VARIANT_TYPE_NUMERIC == pLeftValue->Type && BURN_VARIANT_TYPE_STRING == pRightValue->Type) + { + hr = BVariantGetNumeric(pLeftValue, &llLeft); + ExitOnFailure(hr, "Failed to get the left numeric"); + hr = BVariantGetNumeric(pRightValue, &llRight); + if (FAILED(hr)) + { + if (DISP_E_TYPEMISMATCH != hr) + { + ExitOnFailure(hr, "Failed to get the right numeric"); + } + *pfResult = (BURN_SYMBOL_TYPE_NE == comparison); + hr = S_OK; + } + else + { + hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult); + } + } + else if (BURN_VARIANT_TYPE_STRING == pLeftValue->Type && BURN_VARIANT_TYPE_NUMERIC == pRightValue->Type) + { + hr = BVariantGetNumeric(pRightValue, &llRight); + ExitOnFailure(hr, "Failed to get the right numeric"); + hr = BVariantGetNumeric(pLeftValue, &llLeft); + if (FAILED(hr)) + { + if (DISP_E_TYPEMISMATCH != hr) + { + ExitOnFailure(hr, "Failed to get the left numeric"); + } + *pfResult = (BURN_SYMBOL_TYPE_NE == comparison); + hr = S_OK; + } + else + { + hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult); + } + } + else + { + // not a combination that can be compared + *pfResult = (BURN_SYMBOL_TYPE_NE == comparison || BURN_SYMBOL_TYPE_NE_I == comparison); + } + +LExit: + ReleaseVerutilVersion(pVersionLeft); + SecureZeroMemory(&llLeft, sizeof(LONGLONG)); + StrSecureZeroFreeString(sczLeft); + ReleaseVerutilVersion(pVersionRight); + SecureZeroMemory(&llRight, sizeof(LONGLONG)); + StrSecureZeroFreeString(sczRight); + + return hr; +} + +// +// CompareStringValues - compares two string values using a given comparison. +// +static HRESULT CompareStringValues( + __in BURN_SYMBOL_TYPE comparison, + __in_z LPCWSTR wzLeftOperand, + __in_z LPCWSTR wzRightOperand, + __out BOOL* pfResult + ) +{ + HRESULT hr = S_OK; + DWORD dwCompareString = (comparison & INSENSITIVE) ? NORM_IGNORECASE : 0; + size_t cchLeftSize = 0; + size_t cchRightSize = 0; + int cchLeft = 0; + int cchRight = 0; + + hr = ::StringCchLengthW(wzLeftOperand, STRSAFE_MAX_CCH, &cchLeftSize); + ExitOnRootFailure(hr, "Failed to get length of left string: %ls", wzLeftOperand); + + hr = ::StringCchLengthW(wzRightOperand, STRSAFE_MAX_CCH, &cchRightSize); + ExitOnRootFailure(hr, "Failed to get length of right string: %ls", wzRightOperand); + + cchLeft = static_cast(cchLeftSize); + cchRight = static_cast(cchRightSize); + + switch (comparison) + { + case BURN_SYMBOL_TYPE_LT: + case BURN_SYMBOL_TYPE_GT: + case BURN_SYMBOL_TYPE_LE: + case BURN_SYMBOL_TYPE_GE: + case BURN_SYMBOL_TYPE_EQ: + case BURN_SYMBOL_TYPE_NE: + case BURN_SYMBOL_TYPE_LT_I: + case BURN_SYMBOL_TYPE_GT_I: + case BURN_SYMBOL_TYPE_LE_I: + case BURN_SYMBOL_TYPE_GE_I: + case BURN_SYMBOL_TYPE_EQ_I: + case BURN_SYMBOL_TYPE_NE_I: + { + int i = ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand, cchLeft, wzRightOperand, cchRight); + hr = CompareIntegerValues(comparison, i, CSTR_EQUAL, pfResult); + } + break; + case BURN_SYMBOL_TYPE_BAND: + case BURN_SYMBOL_TYPE_BAND_I: + // test if left string contains right string + for (int i = 0; (i + cchRight) <= cchLeft; ++i) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand + i, cchRight, wzRightOperand, cchRight)) + { + *pfResult = TRUE; + ExitFunction(); + } + } + *pfResult = FALSE; + break; + case BURN_SYMBOL_TYPE_HIEQ: + case BURN_SYMBOL_TYPE_HIEQ_I: + // test if left string starts with right string + *pfResult = cchLeft >= cchRight && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand, cchRight, wzRightOperand, cchRight); + break; + case BURN_SYMBOL_TYPE_LOEQ: + case BURN_SYMBOL_TYPE_LOEQ_I: + // test if left string ends with right string + *pfResult = cchLeft >= cchRight && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand + (cchLeft - cchRight), cchRight, wzRightOperand, cchRight); + break; + default: + ExitFunction1(hr = E_INVALIDARG); + } + +LExit: + return hr; +} + +// +// CompareIntegerValues - compares two integer values using a given comparison. +// +static HRESULT CompareIntegerValues( + __in BURN_SYMBOL_TYPE comparison, + __in LONGLONG llLeftOperand, + __in LONGLONG llRightOperand, + __out BOOL* pfResult + ) +{ + HRESULT hr = S_OK; + + switch (comparison) + { + case BURN_SYMBOL_TYPE_LT: case BURN_SYMBOL_TYPE_LT_I: *pfResult = llLeftOperand < llRightOperand; break; + case BURN_SYMBOL_TYPE_GT: case BURN_SYMBOL_TYPE_GT_I: *pfResult = llLeftOperand > llRightOperand; break; + case BURN_SYMBOL_TYPE_LE: case BURN_SYMBOL_TYPE_LE_I: *pfResult = llLeftOperand <= llRightOperand; break; + case BURN_SYMBOL_TYPE_GE: case BURN_SYMBOL_TYPE_GE_I: *pfResult = llLeftOperand >= llRightOperand; break; + case BURN_SYMBOL_TYPE_EQ: case BURN_SYMBOL_TYPE_EQ_I: *pfResult = llLeftOperand == llRightOperand; break; + case BURN_SYMBOL_TYPE_NE: case BURN_SYMBOL_TYPE_NE_I: *pfResult = llLeftOperand != llRightOperand; break; + case BURN_SYMBOL_TYPE_BAND: case BURN_SYMBOL_TYPE_BAND_I: *pfResult = (llLeftOperand & llRightOperand) ? TRUE : FALSE; break; + case BURN_SYMBOL_TYPE_HIEQ: case BURN_SYMBOL_TYPE_HIEQ_I: *pfResult = ((llLeftOperand >> 16) & 0xFFFF) == llRightOperand; break; + case BURN_SYMBOL_TYPE_LOEQ: case BURN_SYMBOL_TYPE_LOEQ_I: *pfResult = (llLeftOperand & 0xFFFF) == llRightOperand; break; + default: + ExitFunction1(hr = E_INVALIDARG); + } + +LExit: + return hr; +} + +// +// CompareVersionValues - compares two quad-word version values using a given comparison. +// +static HRESULT CompareVersionValues( + __in BURN_SYMBOL_TYPE comparison, + __in VERUTIL_VERSION* pLeftOperand, + __in VERUTIL_VERSION* pRightOperand, + __out BOOL* pfResult + ) +{ + HRESULT hr = S_OK; + int nResult = 0; + + hr = VerCompareParsedVersions(pLeftOperand, pRightOperand, &nResult); + ExitOnFailure(hr, "Failed to compare condition versions: '%ls', '%ls'", pLeftOperand->sczVersion, pRightOperand->sczVersion); + + switch (comparison) + { + case BURN_SYMBOL_TYPE_LT: *pfResult = nResult < 0; break; + case BURN_SYMBOL_TYPE_GT: *pfResult = nResult > 0; break; + case BURN_SYMBOL_TYPE_LE: *pfResult = nResult <= 0; break; + case BURN_SYMBOL_TYPE_GE: *pfResult = nResult >= 0; break; + case BURN_SYMBOL_TYPE_EQ: *pfResult = nResult == 0; break; + case BURN_SYMBOL_TYPE_NE: *pfResult = nResult != 0; break; + default: + ExitFunction1(hr = E_INVALIDARG); + } + +LExit: + return hr; +} diff --git a/src/burn/engine/condition.h b/src/burn/engine/condition.h new file mode 100644 index 00000000..91627f3c --- /dev/null +++ b/src/burn/engine/condition.h @@ -0,0 +1,39 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +typedef struct _BURN_CONDITION +{ + // The is an expression a condition string to fire the built-in "need newer OS" message + LPWSTR sczConditionString; +} BURN_CONDITION; + + +// function declarations + +HRESULT ConditionEvaluate( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzCondition, + __out BOOL* pf + ); +HRESULT ConditionGlobalCheck( + __in BURN_VARIABLES* pVariables, + __in BURN_CONDITION* pBlock, + __in BOOTSTRAPPER_DISPLAY display, + __in_z LPCWSTR wzBundleName, + __out DWORD *pdwExitCode, + __out BOOL *pfContinueExecution + ); +HRESULT ConditionGlobalParseFromXml( + __in BURN_CONDITION* pBlock, + __in IXMLDOMNode* pixnBundle + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/container.cpp b/src/burn/engine/container.cpp new file mode 100644 index 00000000..0cce3131 --- /dev/null +++ b/src/burn/engine/container.cpp @@ -0,0 +1,398 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// function definitions + +extern "C" HRESULT ContainersParseFromXml( + __in BURN_CONTAINERS* pContainers, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + LPWSTR scz = NULL; + + // select container nodes + hr = XmlSelectNodes(pixnBundle, L"Container", &pixnNodes); + ExitOnFailure(hr, "Failed to select container nodes."); + + // get container node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get container node count."); + + if (!cNodes) + { + ExitFunction(); + } + + // allocate memory for searches + pContainers->rgContainers = (BURN_CONTAINER*)MemAlloc(sizeof(BURN_CONTAINER) * cNodes, TRUE); + ExitOnNull(pContainers->rgContainers, hr, E_OUTOFMEMORY, "Failed to allocate memory for container structs."); + + pContainers->cContainers = cNodes; + + // parse search elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_CONTAINER* pContainer = &pContainers->rgContainers[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // TODO: Read type from manifest. Today only CABINET is supported. + pContainer->type = BURN_CONTAINER_TYPE_CABINET; + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &pContainer->sczId); + ExitOnFailure(hr, "Failed to get @Id."); + + // @Primary + hr = XmlGetYesNoAttribute(pixnNode, L"Primary", &pContainer->fPrimary); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Primary."); + } + + // @Attached + hr = XmlGetYesNoAttribute(pixnNode, L"Attached", &pContainer->fAttached); + if (E_NOTFOUND != hr || pContainer->fPrimary) // if it is a primary container, it has to be attached + { + ExitOnFailure(hr, "Failed to get @Attached."); + } + + // @AttachedIndex + hr = XmlGetAttributeNumber(pixnNode, L"AttachedIndex", &pContainer->dwAttachedIndex); + if (E_NOTFOUND != hr || pContainer->fAttached) // if it is an attached container it must have an index + { + ExitOnFailure(hr, "Failed to get @AttachedIndex."); + } + + // Attached containers are always found attached to the current process, so use the current proccess's + // name instead of what may be in the manifest. + if (pContainer->fAttached) + { + hr = PathForCurrentProcess(&scz, NULL); + ExitOnFailure(hr, "Failed to get path to current process for attached container."); + + LPCWSTR wzFileName = PathFile(scz); + + hr = StrAllocString(&pContainer->sczFilePath, wzFileName, 0); + ExitOnFailure(hr, "Failed to set attached container file path."); + } + else + { + // @FilePath + hr = XmlGetAttributeEx(pixnNode, L"FilePath", &pContainer->sczFilePath); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @FilePath."); + } + } + + // The source path starts as the file path. + hr = StrAllocString(&pContainer->sczSourcePath, pContainer->sczFilePath, 0); + ExitOnFailure(hr, "Failed to copy @FilePath"); + + // @DownloadUrl + hr = XmlGetAttributeEx(pixnNode, L"DownloadUrl", &pContainer->downloadSource.sczUrl); + if (E_NOTFOUND != hr || (!pContainer->fPrimary && !pContainer->sczSourcePath)) // if the package is not a primary package, it must have a source path or a download url + { + ExitOnFailure(hr, "Failed to get @DownloadUrl. Either @SourcePath or @DownloadUrl needs to be provided."); + } + + // @Hash + hr = XmlGetAttributeEx(pixnNode, L"Hash", &pContainer->sczHash); + if (SUCCEEDED(hr)) + { + hr = StrAllocHexDecode(pContainer->sczHash, &pContainer->pbHash, &pContainer->cbHash); + ExitOnFailure(hr, "Failed to hex decode the Container/@Hash."); + } + else if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Hash."); + } + + // prepare next iteration + ReleaseNullObject(pixnNode); + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseStr(scz); + + return hr; +} + +extern "C" HRESULT ContainersInitialize( + __in BURN_CONTAINERS* pContainers, + __in BURN_SECTION* pSection + ) +{ + HRESULT hr = S_OK; + + if (pContainers->rgContainers) + { + for (DWORD i = 0; i < pContainers->cContainers; ++i) + { + BURN_CONTAINER* pContainer = &pContainers->rgContainers[i]; + + // If the container is attached, make sure the information in the section matches what the + // manifest contained and get the offset to the container. + if (pContainer->fAttached) + { + hr = SectionGetAttachedContainerInfo(pSection, pContainer->dwAttachedIndex, pContainer->type, &pContainer->qwAttachedOffset, &pContainer->qwFileSize, &pContainer->fActuallyAttached); + ExitOnFailure(hr, "Failed to get attached container information."); + } + } + } + +LExit: + return hr; +} + +extern "C" void ContainersUninitialize( + __in BURN_CONTAINERS* pContainers + ) +{ + if (pContainers->rgContainers) + { + for (DWORD i = 0; i < pContainers->cContainers; ++i) + { + BURN_CONTAINER* pContainer = &pContainers->rgContainers[i]; + + ReleaseStr(pContainer->sczId); + ReleaseStr(pContainer->sczHash); + ReleaseStr(pContainer->sczSourcePath); + ReleaseStr(pContainer->sczFilePath); + ReleaseMem(pContainer->pbHash); + ReleaseStr(pContainer->downloadSource.sczUrl); + ReleaseStr(pContainer->downloadSource.sczUser); + ReleaseStr(pContainer->downloadSource.sczPassword); + ReleaseStr(pContainer->sczUnverifiedPath); + } + MemFree(pContainers->rgContainers); + } + + // clear struct + memset(pContainers, 0, sizeof(BURN_CONTAINERS)); +} + +extern "C" HRESULT ContainerOpenUX( + __in BURN_SECTION* pSection, + __in BURN_CONTAINER_CONTEXT* pContext + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER container = { }; + LPWSTR sczExecutablePath = NULL; + + // open attached container + container.type = BURN_CONTAINER_TYPE_CABINET; + container.fPrimary = TRUE; + container.fAttached = TRUE; + container.dwAttachedIndex = 0; + + hr = SectionGetAttachedContainerInfo(pSection, container.dwAttachedIndex, container.type, &container.qwAttachedOffset, &container.qwFileSize, &container.fActuallyAttached); + ExitOnFailure(hr, "Failed to get container information for UX container."); + + AssertSz(container.fActuallyAttached, "The BA container must always be found attached."); + + hr = PathForCurrentProcess(&sczExecutablePath, NULL); + ExitOnFailure(hr, "Failed to get path for executing module."); + + hr = ContainerOpen(pContext, &container, pSection->hEngineFile, sczExecutablePath); + ExitOnFailure(hr, "Failed to open attached container."); + +LExit: + ReleaseStr(sczExecutablePath); + + return hr; +} + +extern "C" HRESULT ContainerOpen( + __in BURN_CONTAINER_CONTEXT* pContext, + __in BURN_CONTAINER* pContainer, + __in HANDLE hContainerFile, + __in_z LPCWSTR wzFilePath + ) +{ + HRESULT hr = S_OK; + LARGE_INTEGER li = { }; + + // initialize context + pContext->type = pContainer->type; + pContext->qwSize = pContainer->qwFileSize; + pContext->qwOffset = pContainer->qwAttachedOffset; + + // If the handle to the container is not open already, open container file + if (INVALID_HANDLE_VALUE == hContainerFile) + { + pContext->hFile = ::CreateFileW(wzFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + ExitOnInvalidHandleWithLastError(pContext->hFile, hr, "Failed to open file: %ls", wzFilePath); + } + else // use the container file handle. + { + if (!::DuplicateHandle(::GetCurrentProcess(), hContainerFile, ::GetCurrentProcess(), &pContext->hFile, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + ExitWithLastError(hr, "Failed to duplicate handle to container: %ls", wzFilePath); + } + } + + // If it is a container attached to an executable, seek to the container offset. + if (pContainer->fAttached) + { + li.QuadPart = (LONGLONG)pContext->qwOffset; + } + + if (!::SetFilePointerEx(pContext->hFile, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to move file pointer to container offset."); + } + + // open the archive + switch (pContext->type) + { + case BURN_CONTAINER_TYPE_CABINET: + hr = CabExtractOpen(pContext, wzFilePath); + break; + } + ExitOnFailure(hr, "Failed to open container."); + +LExit: + return hr; +} + +extern "C" HRESULT ContainerNextStream( + __in BURN_CONTAINER_CONTEXT* pContext, + __inout_z LPWSTR* psczStreamName + ) +{ + HRESULT hr = S_OK; + + switch (pContext->type) + { + case BURN_CONTAINER_TYPE_CABINET: + hr = CabExtractNextStream(pContext, psczStreamName); + break; + } + +//LExit: + return hr; +} + +extern "C" HRESULT ContainerStreamToFile( + __in BURN_CONTAINER_CONTEXT* pContext, + __in_z LPCWSTR wzFileName + ) +{ + HRESULT hr = S_OK; + + switch (pContext->type) + { + case BURN_CONTAINER_TYPE_CABINET: + hr = CabExtractStreamToFile(pContext, wzFileName); + break; + } + +//LExit: + return hr; +} + +extern "C" HRESULT ContainerStreamToBuffer( + __in BURN_CONTAINER_CONTEXT* pContext, + __out BYTE** ppbBuffer, + __out SIZE_T* pcbBuffer + ) +{ + HRESULT hr = S_OK; + + switch (pContext->type) + { + case BURN_CONTAINER_TYPE_CABINET: + hr = CabExtractStreamToBuffer(pContext, ppbBuffer, pcbBuffer); + break; + + default: + *ppbBuffer = NULL; + *pcbBuffer = 0; + } + +//LExit: + return hr; +} + +extern "C" HRESULT ContainerSkipStream( + __in BURN_CONTAINER_CONTEXT* pContext + ) +{ + HRESULT hr = S_OK; + + switch (pContext->type) + { + case BURN_CONTAINER_TYPE_CABINET: + hr = CabExtractSkipStream(pContext); + break; + } + +//LExit: + return hr; +} + +extern "C" HRESULT ContainerClose( + __in BURN_CONTAINER_CONTEXT* pContext + ) +{ + HRESULT hr = S_OK; + + // close container + switch (pContext->type) + { + case BURN_CONTAINER_TYPE_CABINET: + hr = CabExtractClose(pContext); + ExitOnFailure(hr, "Failed to close cabinet."); + break; + } + +LExit: + ReleaseFile(pContext->hFile); + + if (SUCCEEDED(hr)) + { + memset(pContext, 0, sizeof(BURN_CONTAINER_CONTEXT)); + } + + return hr; +} + +extern "C" HRESULT ContainerFindById( + __in BURN_CONTAINERS* pContainers, + __in_z LPCWSTR wzId, + __out BURN_CONTAINER** ppContainer + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER* pContainer = NULL; + + for (DWORD i = 0; i < pContainers->cContainers; ++i) + { + pContainer = &pContainers->rgContainers[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pContainer->sczId, -1, wzId, -1)) + { + *ppContainer = pContainer; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} diff --git a/src/burn/engine/container.h b/src/burn/engine/container.h new file mode 100644 index 00000000..c2c1c9a8 --- /dev/null +++ b/src/burn/engine/container.h @@ -0,0 +1,191 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// typedefs + +//typedef HRESULT (*PFN_EXTRACTOPEN)( +// __in HANDLE hFile, +// __in DWORD64 qwOffset, +// __in DWORD64 qwSize, +// __out void** ppCookie +// ); +//typedef HRESULT (*PFN_EXTRACTNEXTSTREAM)( +// __in void* pCookie, +// __inout_z LPWSTR* psczStreamName +// ); +//typedef HRESULT (*PFN_EXTRACTSTREAMTOFILE)( +// __in void* pCookie, +// __in_z LPCWSTR wzFileName +// ); +//typedef HRESULT (*PFN_EXTRACTSTREAMTOBUFFER)( +// __in void* pCookie, +// __out BYTE** ppbBuffer, +// __out SIZE_T* pcbBuffer +// ); +//typedef HRESULT (*PFN_EXTRACTCLOSE)( +// __in void* pCookie +// ); + + +// constants + +enum BURN_CONTAINER_TYPE +{ + BURN_CONTAINER_TYPE_NONE, + BURN_CONTAINER_TYPE_CABINET, + BURN_CONTAINER_TYPE_SEVENZIP, +}; + +enum BURN_CAB_OPERATION +{ + BURN_CAB_OPERATION_NONE, + BURN_CAB_OPERATION_NEXT_STREAM, + BURN_CAB_OPERATION_STREAM_TO_FILE, + BURN_CAB_OPERATION_STREAM_TO_BUFFER, + BURN_CAB_OPERATION_SKIP_STREAM, + BURN_CAB_OPERATION_CLOSE, +}; + + +// structs + +typedef struct _BURN_CONTAINER +{ + LPWSTR sczId; + BURN_CONTAINER_TYPE type; + BOOL fPrimary; + BOOL fAttached; + DWORD dwAttachedIndex; + DWORD64 qwFileSize; + LPWSTR sczHash; + LPWSTR sczFilePath; // relative path to container. + DOWNLOAD_SOURCE downloadSource; + + BYTE* pbHash; + DWORD cbHash; + DWORD64 qwAttachedOffset; + BOOL fActuallyAttached; // indicates whether an attached container is attached or missing. + + // mutable members + BOOL fPlanned; + LPWSTR sczSourcePath; + LPWSTR sczUnverifiedPath; + DWORD64 qwExtractSizeTotal; + DWORD64 qwCommittedCacheProgress; + DWORD64 qwCommittedExtractProgress; + HRESULT hrExtract; +} BURN_CONTAINER; + +typedef struct _BURN_CONTAINERS +{ + BURN_CONTAINER* rgContainers; + DWORD cContainers; +} BURN_CONTAINERS; + +typedef struct _BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER +{ + HANDLE hFile; + LARGE_INTEGER liPosition; +} BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER; + +typedef struct _BURN_CONTAINER_CONTEXT_CABINET +{ + LPWSTR sczFile; + + HANDLE hThread; + HANDLE hBeginOperationEvent; + HANDLE hOperationCompleteEvent; + + BURN_CAB_OPERATION operation; + HRESULT hrError; + + LPWSTR* psczStreamName; + LPCWSTR wzTargetFile; + HANDLE hTargetFile; + BYTE* pbTargetBuffer; + DWORD cbTargetBuffer; + DWORD iTargetBuffer; + + BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* rgVirtualFilePointers; + DWORD cVirtualFilePointers; +} BURN_CONTAINER_CONTEXT_CABINET; + +typedef struct _BURN_CONTAINER_CONTEXT +{ + HANDLE hFile; + DWORD64 qwOffset; + DWORD64 qwSize; + + //PFN_EXTRACTOPEN pfnExtractOpen; + //PFN_EXTRACTNEXTSTREAM pfnExtractNextStream; + //PFN_EXTRACTSTREAMTOFILE pfnExtractStreamToFile; + //PFN_EXTRACTSTREAMTOBUFFER pfnExtractStreamToBuffer; + //PFN_EXTRACTCLOSE pfnExtractClose; + //void* pCookie; + BURN_CONTAINER_TYPE type; + union + { + BURN_CONTAINER_CONTEXT_CABINET Cabinet; + }; + +} BURN_CONTAINER_CONTEXT; + + +// functions + +HRESULT ContainersParseFromXml( + __in BURN_CONTAINERS* pContainers, + __in IXMLDOMNode* pixnBundle + ); +HRESULT ContainersInitialize( + __in BURN_CONTAINERS* pContainers, + __in BURN_SECTION* pSection + ); +void ContainersUninitialize( + __in BURN_CONTAINERS* pContainers + ); +HRESULT ContainerOpenUX( + __in BURN_SECTION* pSection, + __in BURN_CONTAINER_CONTEXT* pContext + ); +HRESULT ContainerOpen( + __in BURN_CONTAINER_CONTEXT* pContext, + __in BURN_CONTAINER* pContainer, + __in HANDLE hContainerFile, + __in_z LPCWSTR wzFilePath + ); +HRESULT ContainerNextStream( + __in BURN_CONTAINER_CONTEXT* pContext, + __inout_z LPWSTR* psczStreamName + ); +HRESULT ContainerStreamToFile( + __in BURN_CONTAINER_CONTEXT* pContext, + __in_z LPCWSTR wzFileName + ); +HRESULT ContainerStreamToBuffer( + __in BURN_CONTAINER_CONTEXT* pContext, + __out BYTE** ppbBuffer, + __out SIZE_T* pcbBuffer + ); +HRESULT ContainerSkipStream( + __in BURN_CONTAINER_CONTEXT* pContext + ); +HRESULT ContainerClose( + __in BURN_CONTAINER_CONTEXT* pContext + ); +HRESULT ContainerFindById( + __in BURN_CONTAINERS* pContainers, + __in_z LPCWSTR wzId, + __out BURN_CONTAINER** ppContainer + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/core.cpp b/src/burn/engine/core.cpp new file mode 100644 index 00000000..535043af --- /dev/null +++ b/src/burn/engine/core.cpp @@ -0,0 +1,1856 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// structs + +struct BURN_CACHE_THREAD_CONTEXT +{ + BURN_ENGINE_STATE* pEngineState; + DWORD* pcOverallProgressTicks; + BOOL* pfRollback; +}; + + +// internal function declarations + +static HRESULT ParseCommandLine( + __in int argc, + __in LPWSTR* argv, + __in BOOTSTRAPPER_COMMAND* pCommand, + __in BURN_PIPE_CONNECTION* pCompanionConnection, + __in BURN_PIPE_CONNECTION* pEmbeddedConnection, + __in BURN_VARIABLES* pVariables, + __out BURN_MODE* pMode, + __out BURN_AU_PAUSE_ACTION* pAutomaticUpdates, + __out BOOL* pfDisableSystemRestore, + __out_z LPWSTR* psczSourceProcessPath, + __out_z LPWSTR* psczOriginalSource, + __out BOOL* pfDisableUnelevate, + __out DWORD *pdwLoggingAttributes, + __out_z LPWSTR* psczLogFile, + __out_z LPWSTR* psczActiveParent, + __out_z LPWSTR* psczIgnoreDependencies, + __out_z LPWSTR* psczAncestors, + __out_z LPWSTR* psczSanitizedCommandLine + ); +static HRESULT ParsePipeConnection( + __in_ecount(3) LPWSTR* rgArgs, + __in BURN_PIPE_CONNECTION* pConnection + ); +static HRESULT DetectPackage( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_PACKAGE* pPackage + ); +static HRESULT DetectPackagePayloadsCached( + __in BURN_PACKAGE* pPackage + ); +static DWORD WINAPI CacheThreadProc( + __in LPVOID lpThreadParameter + ); +static HRESULT WaitForCacheThread( + __in HANDLE hCacheThread + ); +static void LogPackages( + __in_opt const BURN_PACKAGE* pUpgradeBundlePackage, + __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage, + __in const BURN_PACKAGES* pPackages, + __in const BURN_RELATED_BUNDLES* pRelatedBundles, + __in const BOOTSTRAPPER_ACTION action + ); +static void LogRelatedBundles( + __in const BURN_RELATED_BUNDLES* pRelatedBundles, + __in BOOL fReverse + ); + + +// function definitions + +extern "C" HRESULT CoreInitialize( + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + LPWSTR sczSanitizedCommandLine = NULL; + LPWSTR sczStreamName = NULL; + BYTE* pbBuffer = NULL; + SIZE_T cbBuffer = 0; + BURN_CONTAINER_CONTEXT containerContext = { }; + BOOL fElevated = FALSE; + LPWSTR sczSourceProcessPath = NULL; + LPWSTR sczSourceProcessFolder = NULL; + LPWSTR sczOriginalSource = NULL; + + // Initialize variables. + hr = VariableInitialize(&pEngineState->variables); + ExitOnFailure(hr, "Failed to initialize variables."); + + // Open attached UX container. + hr = ContainerOpenUX(&pEngineState->section, &containerContext); + ExitOnFailure(hr, "Failed to open attached UX container."); + + // Load manifest. + hr = ContainerNextStream(&containerContext, &sczStreamName); + ExitOnFailure(hr, "Failed to open manifest stream."); + + hr = ContainerStreamToBuffer(&containerContext, &pbBuffer, &cbBuffer); + ExitOnFailure(hr, "Failed to get manifest stream from container."); + + hr = ManifestLoadXmlFromBuffer(pbBuffer, cbBuffer, pEngineState); + ExitOnFailure(hr, "Failed to load manifest."); + + hr = ContainersInitialize(&pEngineState->containers, &pEngineState->section); + ExitOnFailure(hr, "Failed to initialize containers."); + + // Parse command line. + hr = ParseCommandLine(pEngineState->argc, pEngineState->argv, &pEngineState->command, &pEngineState->companionConnection, &pEngineState->embeddedConnection, &pEngineState->variables, &pEngineState->mode, &pEngineState->automaticUpdates, &pEngineState->fDisableSystemRestore, &sczSourceProcessPath, &sczOriginalSource, &pEngineState->fDisableUnelevate, &pEngineState->log.dwAttributes, &pEngineState->log.sczPath, &pEngineState->registration.sczActiveParent, &pEngineState->sczIgnoreDependencies, &pEngineState->registration.sczAncestors, &sczSanitizedCommandLine); + ExitOnFailure(hr, "Failed to parse command line."); + + LogId(REPORT_STANDARD, MSG_BURN_COMMAND_LINE, sczSanitizedCommandLine ? sczSanitizedCommandLine : L""); + + hr = CoreInitializeConstants(pEngineState); + ExitOnFailure(hr, "Failed to initialize contants."); + + // Retain whether bundle was initially run elevated. + ProcElevated(::GetCurrentProcess(), &fElevated); + + hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, fElevated, TRUE); + ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_ELEVATED); + + hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_UILEVEL, pEngineState->command.display, TRUE); + ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_UILEVEL); + + if (sczSourceProcessPath) + { + hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_PATH, sczSourceProcessPath, TRUE, FALSE); + ExitOnFailure(hr, "Failed to set source process path variable."); + + hr = PathGetDirectory(sczSourceProcessPath, &sczSourceProcessFolder); + ExitOnFailure(hr, "Failed to get source process folder from path."); + + hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, sczSourceProcessFolder, TRUE, FALSE); + ExitOnFailure(hr, "Failed to set source process folder variable."); + } + + // Set BURN_BUNDLE_ORIGINAL_SOURCE, if it was passed in on the command line. + // Needs to be done after ManifestLoadXmlFromBuffer. + if (sczOriginalSource) + { + hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_ORIGINAL_SOURCE, sczOriginalSource, FALSE, FALSE); + ExitOnFailure(hr, "Failed to set original source variable."); + } + + if (BURN_MODE_UNTRUSTED == pEngineState->mode || BURN_MODE_NORMAL == pEngineState->mode || BURN_MODE_EMBEDDED == pEngineState->mode) + { + hr = CacheInitialize(&pEngineState->registration, &pEngineState->variables, sczSourceProcessPath); + ExitOnFailure(hr, "Failed to initialize internal cache functionality."); + } + + // If we're not elevated then we'll be loading the bootstrapper application, so extract + // the payloads from the BA container. + if (BURN_MODE_NORMAL == pEngineState->mode || BURN_MODE_EMBEDDED == pEngineState->mode) + { + // Extract all UX payloads to working folder. + hr = UserExperienceEnsureWorkingFolder(pEngineState->registration.sczId, &pEngineState->userExperience.sczTempDirectory); + ExitOnFailure(hr, "Failed to get unique temporary folder for bootstrapper application."); + + hr = PayloadExtractUXContainer(&pEngineState->userExperience.payloads, &containerContext, pEngineState->userExperience.sczTempDirectory); + ExitOnFailure(hr, "Failed to extract bootstrapper application payloads."); + + hr = PathConcat(pEngineState->userExperience.sczTempDirectory, L"BootstrapperApplicationData.xml", &pEngineState->command.wzBootstrapperApplicationDataPath); + ExitOnFailure(hr, "Failed to get BootstrapperApplicationDataPath."); + + hr = StrAllocString(&pEngineState->command.wzBootstrapperWorkingFolder, pEngineState->userExperience.sczTempDirectory, 0); + ExitOnFailure(hr, "Failed to copy sczBootstrapperWorkingFolder."); + } + +LExit: + ReleaseStr(sczOriginalSource); + ReleaseStr(sczSourceProcessFolder); + ReleaseStr(sczSourceProcessPath); + ContainerClose(&containerContext); + ReleaseStr(sczStreamName); + ReleaseStr(sczSanitizedCommandLine); + ReleaseMem(pbBuffer); + + return hr; +} + +extern "C" HRESULT CoreInitializeConstants( + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + BURN_REGISTRATION* pRegistration = &pEngineState->registration; + + hr = DependencyInitialize(pRegistration, pEngineState->sczIgnoreDependencies); + ExitOnFailure(hr, "Failed to initialize dependency data."); + + // Support passing Ancestors to embedded burn bundles. + if (pRegistration->sczAncestors && *pRegistration->sczAncestors) + { + hr = StrAllocFormatted(&pRegistration->sczBundlePackageAncestors, L"%ls;%ls", pRegistration->sczAncestors, pRegistration->sczId); + ExitOnFailure(hr, "Failed to copy ancestors and self to bundle package ancestors."); + } + else + { + hr = StrAllocString(&pRegistration->sczBundlePackageAncestors, pRegistration->sczId, 0); + ExitOnFailure(hr, "Failed to copy self to bundle package ancestors."); + } + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + + if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) // TODO: Don't assume exePackages with burn protocol are bundles. + { + // Pass along any ancestors and ourself to prevent infinite loops. + pPackage->Exe.wzAncestors = pRegistration->sczBundlePackageAncestors; + } + } + +LExit: + return hr; +} + +extern "C" HRESULT CoreSerializeEngineState( + __in BURN_ENGINE_STATE* pEngineState, + __inout BYTE** ppbBuffer, + __inout SIZE_T* piBuffer + ) +{ + HRESULT hr = S_OK; + + hr = VariableSerialize(&pEngineState->variables, TRUE, ppbBuffer, piBuffer); + ExitOnFailure(hr, "Failed to serialize variables."); + +LExit: + return hr; +} + +extern "C" HRESULT CoreQueryRegistration( + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + BYTE* pbBuffer = NULL; + SIZE_T cbBuffer = 0; + SIZE_T iBuffer = 0; + + // Detect if bundle is already installed. + hr = RegistrationDetectInstalled(&pEngineState->registration); + ExitOnFailure(hr, "Failed to detect bundle install state."); + + // detect resume type + hr = RegistrationDetectResumeType(&pEngineState->registration, &pEngineState->command.resumeType); + ExitOnFailure(hr, "Failed to detect resume type."); + + // If we have a resume mode that suggests the bundle might already be present, try to load any + // previously stored state. + if (BOOTSTRAPPER_RESUME_TYPE_INVALID < pEngineState->command.resumeType) + { + // load resume state + hr = RegistrationLoadState(&pEngineState->registration, &pbBuffer, &cbBuffer); + if (SUCCEEDED(hr)) + { + hr = VariableDeserialize(&pEngineState->variables, TRUE, pbBuffer, cbBuffer, &iBuffer); + } + + // Log any failures and continue. + if (FAILED(hr)) + { + LogId(REPORT_STANDARD, MSG_CANNOT_LOAD_STATE_FILE, hr, pEngineState->registration.sczStateFile); + hr = S_OK; + } + } + +LExit: + ReleaseBuffer(pbBuffer); + + return hr; +} + +extern "C" HRESULT CoreDetect( + __in BURN_ENGINE_STATE* pEngineState, + __in_opt HWND hwndParent + ) +{ + HRESULT hr = S_OK; + BOOL fDetectBegan = FALSE; + BURN_PACKAGE* pPackage = NULL; + HRESULT hrFirstPackageFailure = S_OK; + + LogId(REPORT_STANDARD, MSG_DETECT_BEGIN, pEngineState->packages.cPackages); + + // Always reset the detect state which means the plan should be reset too. + pEngineState->fDetected = FALSE; + pEngineState->fPlanned = FALSE; + DetectReset(&pEngineState->registration, &pEngineState->packages); + PlanReset(&pEngineState->plan, &pEngineState->containers, &pEngineState->packages, &pEngineState->layoutPayloads); + + // Detect if bundle installed state has changed since start up. This + // only happens if Apply() changed the state of bundle (installed or + // uninstalled). In that case, Detect() can be used here to reset + // the installed state. + hr = RegistrationDetectInstalled(&pEngineState->registration); + ExitOnFailure(hr, "Failed to detect bundle install state."); + + if (pEngineState->registration.fInstalled) + { + hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_INSTALLED, 1, TRUE); + ExitOnFailure(hr, "Failed to set the bundle installed built-in variable."); + } + else + { + hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_INSTALLED, NULL, TRUE, FALSE); + ExitOnFailure(hr, "Failed to unset the bundle installed built-in variable."); + } + + fDetectBegan = TRUE; + hr = UserExperienceOnDetectBegin(&pEngineState->userExperience, pEngineState->registration.fCached, pEngineState->registration.fInstalled, pEngineState->packages.cPackages); + ExitOnRootFailure(hr, "UX aborted detect begin."); + + pEngineState->userExperience.hwndDetect = hwndParent; + + hr = SearchesExecute(&pEngineState->searches, &pEngineState->variables); + ExitOnFailure(hr, "Failed to execute searches."); + + // Load all of the related bundles. + hr = RegistrationDetectRelatedBundles(&pEngineState->registration); + ExitOnFailure(hr, "Failed to detect related bundles."); + + hr = DependencyDetectProviderKeyBundleId(&pEngineState->registration); + if (SUCCEEDED(hr)) + { + hr = DetectForwardCompatibleBundles(&pEngineState->userExperience, &pEngineState->registration); + ExitOnFailure(hr, "Failed to detect forward compatible bundle."); + } + else if (E_NOTFOUND == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to detect provider key bundle id."); + + // Report the related bundles. + hr = DetectReportRelatedBundles(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, pEngineState->command.action, &pEngineState->registration.fEligibleForCleanup); + ExitOnFailure(hr, "Failed to report detected related bundles."); + + // Do update detection. + hr = DetectUpdate(pEngineState->registration.sczId, &pEngineState->userExperience, &pEngineState->update); + ExitOnFailure(hr, "Failed to detect update."); + + // Detecting MSPs requires special initialization before processing each package but + // only do the detection if there are actually patch packages to detect because it + // can be expensive. + if (pEngineState->packages.cPatchInfo) + { + hr = MspEngineDetectInitialize(&pEngineState->packages); + ExitOnFailure(hr, "Failed to initialize MSP engine detection."); + + hr = MsiEngineDetectInitialize(&pEngineState->packages); + ExitOnFailure(hr, "Failed to initialize MSI engine detection."); + } + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + pPackage = pEngineState->packages.rgPackages + i; + + hr = DetectPackage(pEngineState, pPackage); + + // If the package detection failed, ensure the package state is set to unknown. + if (FAILED(hr)) + { + if (SUCCEEDED(hrFirstPackageFailure)) + { + hrFirstPackageFailure = hr; + } + + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; + pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; + } + } + + hr = DependencyDetect(pEngineState); + ExitOnFailure(hr, "Failed to detect the dependencies."); + + // Log the detected states. + for (DWORD iPackage = 0; iPackage < pEngineState->packages.cPackages; ++iPackage) + { + pPackage = pEngineState->packages.rgPackages + iPackage; + + // If any packages that can affect registration are present, then the bundle should not automatically be uninstalled. + if (pEngineState->registration.fEligibleForCleanup && pPackage->fCanAffectRegistration && + (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState || + BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState)) + { + pEngineState->registration.fEligibleForCleanup = FALSE; + } + + LogId(REPORT_STANDARD, MSG_DETECTED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingBoolToString(pPackage->fCached), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->installRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->cacheRegistrationState)); + + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + { + for (DWORD iFeature = 0; iFeature < pPackage->Msi.cFeatures; ++iFeature) + { + const BURN_MSIFEATURE* pFeature = pPackage->Msi.rgFeatures + iFeature; + LogId(REPORT_STANDARD, MSG_DETECTED_MSI_FEATURE, pPackage->sczId, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState)); + } + } + else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + for (DWORD iTargetProduct = 0; iTargetProduct < pPackage->Msp.cTargetProductCodes; ++iTargetProduct) + { + const BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + iTargetProduct; + LogId(REPORT_STANDARD, MSG_DETECTED_MSP_TARGET, pPackage->sczId, pTargetProduct->wzTargetProductCode, LoggingPackageStateToString(pTargetProduct->patchPackageState)); + } + } + } + +LExit: + if (SUCCEEDED(hr)) + { + hr = hrFirstPackageFailure; + } + + if (SUCCEEDED(hr)) + { + pEngineState->fDetected = TRUE; + } + + if (fDetectBegan) + { + UserExperienceOnDetectComplete(&pEngineState->userExperience, hr, pEngineState->registration.fEligibleForCleanup); + } + + pEngineState->userExperience.hwndDetect = NULL; + + LogId(REPORT_STANDARD, MSG_DETECT_COMPLETE, hr, !fDetectBegan ? "(failed)" : LoggingBoolToString(pEngineState->registration.fInstalled), !fDetectBegan ? "(failed)" : LoggingBoolToString(pEngineState->registration.fCached), FAILED(hr) ? "(failed)" : LoggingBoolToString(pEngineState->registration.fEligibleForCleanup)); + + return hr; +} + +extern "C" HRESULT CorePlan( + __in BURN_ENGINE_STATE* pEngineState, + __in BOOTSTRAPPER_ACTION action + ) +{ + HRESULT hr = S_OK; + BOOL fPlanBegan = FALSE; + BURN_PACKAGE* pUpgradeBundlePackage = NULL; + BURN_PACKAGE* pForwardCompatibleBundlePackage = NULL; + BOOL fContinuePlanning = TRUE; // assume we won't skip planning due to dependencies. + + LogId(REPORT_STANDARD, MSG_PLAN_BEGIN, pEngineState->packages.cPackages, LoggingBurnActionToString(action)); + + fPlanBegan = TRUE; + hr = UserExperienceOnPlanBegin(&pEngineState->userExperience, pEngineState->packages.cPackages); + ExitOnRootFailure(hr, "BA aborted plan begin."); + + if (!pEngineState->fDetected) + { + ExitOnFailure(hr = E_INVALIDSTATE, "Plan cannot be done without a successful Detect."); + } + else if (pEngineState->plan.fAffectedMachineState) + { + ExitOnFailure(hr = E_INVALIDSTATE, "Plan requires a new successful Detect after calling Apply."); + } + + // Always reset the plan. + pEngineState->fPlanned = FALSE; + PlanReset(&pEngineState->plan, &pEngineState->containers, &pEngineState->packages, &pEngineState->layoutPayloads); + + // Remember the overall action state in the plan since it shapes the changes + // we make everywhere. + pEngineState->plan.action = action; + pEngineState->plan.pPayloads = &pEngineState->payloads; + pEngineState->plan.wzBundleId = pEngineState->registration.sczId; + pEngineState->plan.wzBundleProviderKey = pEngineState->registration.sczId; + pEngineState->plan.fDisableRollback = pEngineState->fDisableRollback; + + hr = PlanSetVariables(action, &pEngineState->variables); + ExitOnFailure(hr, "Failed to update action."); + + // Set resume commandline + hr = PlanSetResumeCommand(&pEngineState->registration, action, &pEngineState->command, &pEngineState->log); + ExitOnFailure(hr, "Failed to set resume command"); + + hr = DependencyPlanInitialize(&pEngineState->registration, &pEngineState->plan); + ExitOnFailure(hr, "Failed to initialize the dependencies for the plan."); + + if (BOOTSTRAPPER_ACTION_LAYOUT == action) + { + Assert(!pEngineState->plan.fPerMachine); + + // Plan the bundle's layout. + hr = PlanLayoutBundle(&pEngineState->plan, pEngineState->registration.sczExecutableName, pEngineState->section.qwBundleSize, &pEngineState->variables, &pEngineState->layoutPayloads); + ExitOnFailure(hr, "Failed to plan the layout of the bundle."); + + // Plan the packages' layout. + hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType); + ExitOnFailure(hr, "Failed to plan packages."); + } + else if (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == action || BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED == action) + { + Assert(!pEngineState->plan.fPerMachine); + + pUpgradeBundlePackage = &pEngineState->update.package; + + hr = PlanUpdateBundle(&pEngineState->userExperience, pUpgradeBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType); + ExitOnFailure(hr, "Failed to plan update."); + } + else + { + hr = PlanForwardCompatibleBundles(&pEngineState->userExperience, &pEngineState->command, &pEngineState->plan, &pEngineState->registration, action); + ExitOnFailure(hr, "Failed to plan forward compatible bundles."); + + if (pEngineState->plan.fEnabledForwardCompatibleBundle) + { + Assert(!pEngineState->plan.fPerMachine); + + pForwardCompatibleBundlePackage = &pEngineState->plan.forwardCompatibleBundle; + + hr = PlanPassThroughBundle(&pEngineState->userExperience, pForwardCompatibleBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType); + ExitOnFailure(hr, "Failed to plan passthrough."); + } + else // doing an action that modifies the machine state. + { + pEngineState->plan.fPerMachine = pEngineState->registration.fPerMachine; // default the scope of the plan to the per-machine state of the bundle. + + hr = PlanRegistration(&pEngineState->plan, &pEngineState->registration, pEngineState->command.resumeType, pEngineState->command.relationType, &fContinuePlanning); + ExitOnFailure(hr, "Failed to plan registration."); + + if (fContinuePlanning) + { + // Remember the early index, because we want to be able to insert some related bundles + // into the plan before other executed packages. This particularly occurs for uninstallation + // of addons and patches, which should be uninstalled before the main product. + DWORD dwExecuteActionEarlyIndex = pEngineState->plan.cExecuteActions; + + // Plan the related bundles first to support downgrades with ref-counting. + hr = PlanRelatedBundlesBegin(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->plan); + ExitOnFailure(hr, "Failed to plan related bundles."); + + hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType); + ExitOnFailure(hr, "Failed to plan packages."); + + // Schedule the update of related bundles last. + hr = PlanRelatedBundlesComplete(&pEngineState->registration, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, dwExecuteActionEarlyIndex); + ExitOnFailure(hr, "Failed to schedule related bundles."); + } + } + } + + if (fContinuePlanning) + { + // Finally, display all packages and related bundles in the log. + LogPackages(pUpgradeBundlePackage, pForwardCompatibleBundlePackage, &pEngineState->packages, &pEngineState->registration.relatedBundles, action); + } + + PlanDump(&pEngineState->plan); + +LExit: + if (SUCCEEDED(hr)) + { + pEngineState->fPlanned = TRUE; + } + + if (fPlanBegan) + { + UserExperienceOnPlanComplete(&pEngineState->userExperience, hr); + } + + LogId(REPORT_STANDARD, MSG_PLAN_COMPLETE, hr); + + return hr; +} + +extern "C" HRESULT CoreElevate( + __in BURN_ENGINE_STATE* pEngineState, + __in_opt HWND hwndParent + ) +{ + HRESULT hr = S_OK; + DWORD cAVRetryAttempts = 0; + + while (INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe) + { + // If the elevated companion pipe isn't created yet, let's make that happen. + if (!pEngineState->sczBundleEngineWorkingPath) + { + hr = CacheBundleToWorkingDirectory(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath); + ExitOnFailure(hr, "Failed to cache engine to working directory."); + } + + hr = ElevationElevate(pEngineState, hwndParent); + if (E_SUSPECTED_AV_INTERFERENCE == hr && 1 > cAVRetryAttempts) + { + ++cAVRetryAttempts; + continue; + } + ExitOnFailure(hr, "Failed to actually elevate."); + + hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, TRUE, TRUE); + ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_ELEVATED); + } + +LExit: + return hr; +} + +extern "C" HRESULT CoreApply( + __in BURN_ENGINE_STATE* pEngineState, + __in_opt HWND hwndParent + ) +{ + HRESULT hr = S_OK; + HANDLE hLock = NULL; + DWORD cOverallProgressTicks = 0; + HANDLE hCacheThread = NULL; + BOOL fApplyInitialize = FALSE; + BOOL fElevated = FALSE; + BOOL fRegistered = FALSE; + BOOL fRollback = FALSE; + BOOL fSuspend = FALSE; + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; + BURN_CACHE_THREAD_CONTEXT cacheThreadContext = { }; + DWORD dwPhaseCount = 0; + BOOTSTRAPPER_APPLYCOMPLETE_ACTION applyCompleteAction = BOOTSTRAPPER_APPLYCOMPLETE_ACTION_NONE; + + LogId(REPORT_STANDARD, MSG_APPLY_BEGIN); + + if (!pEngineState->fPlanned) + { + ExitOnFailure(hr = E_INVALIDSTATE, "Apply cannot be done without a successful Plan."); + } + else if (pEngineState->plan.fAffectedMachineState) + { + ExitOnFailure(hr = E_INVALIDSTATE, "Plans cannot be applied multiple times."); + } + + // Ensure any previous attempts to execute are reset. + ApplyReset(&pEngineState->userExperience, &pEngineState->packages); + + if (pEngineState->plan.cCacheActions) + { + ++dwPhaseCount; + } + if (pEngineState->plan.cExecuteActions) + { + ++dwPhaseCount; + } + + hr = UserExperienceOnApplyBegin(&pEngineState->userExperience, dwPhaseCount); + ExitOnRootFailure(hr, "BA aborted apply begin."); + + pEngineState->plan.fAffectedMachineState = pEngineState->plan.fCanAffectMachineState; + + // Abort if this bundle already requires a restart. + if (BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING == pEngineState->command.resumeType) + { + restart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; + + hr = HRESULT_FROM_WIN32(ERROR_FAIL_NOACTION_REBOOT); + UserExperienceSendError(&pEngineState->userExperience, BOOTSTRAPPER_ERROR_TYPE_APPLY, NULL, hr, NULL, MB_ICONERROR | MB_OK, IDNOACTION); // ignore return value. + ExitFunction(); + } + + hr = ApplyLock(FALSE, &hLock); + ExitOnFailure(hr, "Another per-user setup is already executing."); + + // Initialize only after getting a lock. + fApplyInitialize = TRUE; + ApplyInitialize(); + + pEngineState->userExperience.hwndApply = hwndParent; + + hr = ApplySetVariables(&pEngineState->variables); + ExitOnFailure(hr, "Failed to set initial apply variables."); + + // If the plan is empty of work to do, skip everything. + if (!(pEngineState->plan.cRegistrationActions || pEngineState->plan.cCacheActions || pEngineState->plan.cExecuteActions || pEngineState->plan.cCleanActions)) + { + LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED); + ExitFunction(); + } + + // Ensure the engine is cached to the working path. + if (!pEngineState->sczBundleEngineWorkingPath) + { + hr = CacheBundleToWorkingDirectory(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath); + ExitOnFailure(hr, "Failed to cache engine to working directory."); + } + + // Elevate. + if (pEngineState->plan.fPerMachine) + { + hr = CoreElevate(pEngineState, pEngineState->userExperience.hwndApply); + ExitOnFailure(hr, "Failed to elevate."); + + hr = ElevationApplyInitialize(pEngineState->companionConnection.hPipe, &pEngineState->userExperience, &pEngineState->variables, pEngineState->plan.action, pEngineState->automaticUpdates, !pEngineState->fDisableSystemRestore); + ExitOnFailure(hr, "Failed to initialize apply in elevated process."); + + fElevated = TRUE; + } + + // Register. + if (pEngineState->plan.fCanAffectMachineState) + { + fRegistered = TRUE; + hr = ApplyRegister(pEngineState); + ExitOnFailure(hr, "Failed to register bundle."); + } + + // Cache. + if (pEngineState->plan.cCacheActions) + { + // Launch the cache thread. + cacheThreadContext.pEngineState = pEngineState; + cacheThreadContext.pcOverallProgressTicks = &cOverallProgressTicks; + cacheThreadContext.pfRollback = &fRollback; + + hCacheThread = ::CreateThread(NULL, 0, CacheThreadProc, &cacheThreadContext, 0, NULL); + ExitOnNullWithLastError(hCacheThread, hr, "Failed to create cache thread."); + + // If we're not caching in parallel, wait for the cache thread to terminate. + if (!pEngineState->fParallelCacheAndExecute) + { + hr = WaitForCacheThread(hCacheThread); + ExitOnFailure(hr, "Failed while caching, aborting execution."); + + ReleaseHandle(hCacheThread); + } + } + + // Execute. + if (pEngineState->plan.cExecuteActions) + { + hr = ApplyExecute(pEngineState, hCacheThread, &cOverallProgressTicks, &fRollback, &fSuspend, &restart); + UserExperienceExecutePhaseComplete(&pEngineState->userExperience, hr); // signal that execute completed. + } + + // Wait for cache thread to terminate, this should return immediately unless we're waiting for layout to complete. + if (hCacheThread) + { + HRESULT hrCached = WaitForCacheThread(hCacheThread); + if (SUCCEEDED(hr)) + { + hr = hrCached; + } + } + + // If something went wrong or force restarted, skip cleaning. + if (FAILED(hr) || fRollback || fSuspend || BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart) + { + ExitFunction(); + } + + // Clean. + if (pEngineState->plan.cCleanActions) + { + ApplyClean(&pEngineState->userExperience, &pEngineState->plan, pEngineState->companionConnection.hPipe); + } + +LExit: + // Unregister. + if (fRegistered) + { + ApplyUnregister(pEngineState, FAILED(hr) || fRollback, fSuspend, restart); + } + + if (fElevated) + { + ElevationApplyUninitialize(pEngineState->companionConnection.hPipe); + } + + pEngineState->userExperience.hwndApply = NULL; + + if (fApplyInitialize) + { + ApplyUninitialize(); + } + + if (hLock) + { + ::ReleaseMutex(hLock); + ::CloseHandle(hLock); + } + + ReleaseHandle(hCacheThread); + + UserExperienceOnApplyComplete(&pEngineState->userExperience, hr, restart, &applyCompleteAction); + if (BOOTSTRAPPER_APPLYCOMPLETE_ACTION_RESTART == applyCompleteAction) + { + pEngineState->fRestart = TRUE; + } + + LogId(REPORT_STANDARD, MSG_APPLY_COMPLETE, hr, LoggingRestartToString(restart), LoggingBoolToString(pEngineState->fRestart)); + + return hr; +} + +extern "C" HRESULT CoreLaunchApprovedExe( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe + ) +{ + HRESULT hr = S_OK; + DWORD dwProcessId = 0; + + LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_BEGIN, pLaunchApprovedExe->sczId); + + hr = UserExperienceOnLaunchApprovedExeBegin(&pEngineState->userExperience); + ExitOnRootFailure(hr, "BA aborted LaunchApprovedExe begin."); + + // Elevate. + hr = CoreElevate(pEngineState, pLaunchApprovedExe->hwndParent); + ExitOnFailure(hr, "Failed to elevate."); + + // Launch. + hr = ElevationLaunchApprovedExe(pEngineState->companionConnection.hPipe, pLaunchApprovedExe, &dwProcessId); + +LExit: + UserExperienceOnLaunchApprovedExeComplete(&pEngineState->userExperience, hr, dwProcessId); + + LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_COMPLETE, hr, dwProcessId); + + ApprovedExesUninitializeLaunch(pLaunchApprovedExe); + + return hr; +} + +extern "C" HRESULT CoreQuit( + __in BURN_ENGINE_STATE* pEngineState, + __in int nExitCode + ) +{ + HRESULT hr = S_OK; + + // Save engine state if resume mode is unequal to "none". + if (BURN_RESUME_MODE_NONE != pEngineState->resumeMode) + { + hr = CoreSaveEngineState(pEngineState); + if (FAILED(hr)) + { + LogErrorId(hr, MSG_STATE_NOT_SAVED); + hr = S_OK; + } + } + + LogId(REPORT_STANDARD, MSG_QUIT, nExitCode); + + pEngineState->fQuit = TRUE; + + ::PostQuitMessage(nExitCode); // go bye-bye. + + return hr; +} + +extern "C" HRESULT CoreSaveEngineState( + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + BYTE* pbBuffer = NULL; + SIZE_T cbBuffer = 0; + + // serialize engine state + hr = CoreSerializeEngineState(pEngineState, &pbBuffer, &cbBuffer); + ExitOnFailure(hr, "Failed to serialize engine state."); + + // write to registration store + if (pEngineState->registration.fPerMachine) + { + hr = ElevationSaveState(pEngineState->companionConnection.hPipe, pbBuffer, cbBuffer); + ExitOnFailure(hr, "Failed to save engine state in per-machine process."); + } + else + { + hr = RegistrationSaveState(&pEngineState->registration, pbBuffer, cbBuffer); + ExitOnFailure(hr, "Failed to save engine state."); + } + +LExit: + ReleaseBuffer(pbBuffer); + + return hr; +} + +extern "C" LPCWSTR CoreRelationTypeToCommandLineString( + __in BOOTSTRAPPER_RELATION_TYPE relationType + ) +{ + LPCWSTR wzRelationTypeCommandLine = NULL; + switch (relationType) + { + case BOOTSTRAPPER_RELATION_DETECT: + wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_DETECT; + break; + case BOOTSTRAPPER_RELATION_UPGRADE: + wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE; + break; + case BOOTSTRAPPER_RELATION_ADDON: + wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_ADDON; + break; + case BOOTSTRAPPER_RELATION_PATCH: + wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_PATCH; + break; + case BOOTSTRAPPER_RELATION_UPDATE: + wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_UPDATE; + break; + case BOOTSTRAPPER_RELATION_DEPENDENT: + break; + case BOOTSTRAPPER_RELATION_NONE: __fallthrough; + default: + wzRelationTypeCommandLine = NULL; + break; + } + + return wzRelationTypeCommandLine; +} + +extern "C" HRESULT CoreRecreateCommandLine( + __deref_inout_z LPWSTR* psczCommandLine, + __in BOOTSTRAPPER_ACTION action, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RESTART restart, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in BOOL fPassthrough, + __in_z_opt LPCWSTR wzActiveParent, + __in_z_opt LPCWSTR wzAncestors, + __in_z_opt LPCWSTR wzAppendLogPath, + __in_z_opt LPCWSTR wzAdditionalCommandLineArguments + ) +{ + HRESULT hr = S_OK; + LPWSTR scz = NULL; + LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType); + + hr = StrAllocString(psczCommandLine, L"", 0); + ExitOnFailure(hr, "Failed to empty command line."); + + switch (display) + { + case BOOTSTRAPPER_DISPLAY_NONE: + hr = StrAllocConcat(psczCommandLine, L" /quiet", 0); + break; + case BOOTSTRAPPER_DISPLAY_PASSIVE: + hr = StrAllocConcat(psczCommandLine, L" /passive", 0); + break; + } + ExitOnFailure(hr, "Failed to append display state to command-line"); + + switch (action) + { + case BOOTSTRAPPER_ACTION_MODIFY: + hr = StrAllocConcat(psczCommandLine, L" /modify", 0); + break; + case BOOTSTRAPPER_ACTION_REPAIR: + hr = StrAllocConcat(psczCommandLine, L" /repair", 0); + break; + case BOOTSTRAPPER_ACTION_UNINSTALL: + hr = StrAllocConcat(psczCommandLine, L" /uninstall", 0); + break; + } + ExitOnFailure(hr, "Failed to append action state to command-line"); + + switch (restart) + { + case BOOTSTRAPPER_RESTART_ALWAYS: + hr = StrAllocConcat(psczCommandLine, L" /forcerestart", 0); + break; + case BOOTSTRAPPER_RESTART_NEVER: + hr = StrAllocConcat(psczCommandLine, L" /norestart", 0); + break; + } + ExitOnFailure(hr, "Failed to append restart state to command-line"); + + if (wzActiveParent) + { + if (*wzActiveParent) + { + hr = StrAllocFormatted(&scz, L" /%ls \"%ls\"", BURN_COMMANDLINE_SWITCH_PARENT, wzActiveParent); + ExitOnFailure(hr, "Failed to format active parent command-line for command-line."); + } + else + { + hr = StrAllocFormatted(&scz, L" /%ls", BURN_COMMANDLINE_SWITCH_PARENT_NONE); + ExitOnFailure(hr, "Failed to format parent:none command-line for command-line."); + } + + hr = StrAllocConcat(psczCommandLine, scz, 0); + ExitOnFailure(hr, "Failed to append active parent command-line to command-line."); + } + + if (wzAncestors) + { + hr = StrAllocFormatted(&scz, L" /%ls=%ls", BURN_COMMANDLINE_SWITCH_ANCESTORS, wzAncestors); + ExitOnFailure(hr, "Failed to format ancestors for command-line."); + + hr = StrAllocConcat(psczCommandLine, scz, 0); + ExitOnFailure(hr, "Failed to append ancestors to command-line."); + } + + if (wzRelationTypeCommandLine) + { + hr = StrAllocFormatted(&scz, L" /%ls", wzRelationTypeCommandLine); + ExitOnFailure(hr, "Failed to format relation type for command-line."); + + hr = StrAllocConcat(psczCommandLine, scz, 0); + ExitOnFailure(hr, "Failed to append relation type to command-line."); + } + + if (fPassthrough) + { + hr = StrAllocFormatted(&scz, L" /%ls", BURN_COMMANDLINE_SWITCH_PASSTHROUGH); + ExitOnFailure(hr, "Failed to format passthrough for command-line."); + + hr = StrAllocConcat(psczCommandLine, scz, 0); + ExitOnFailure(hr, "Failed to append passthrough to command-line."); + } + + if (wzAppendLogPath && *wzAppendLogPath) + { + hr = StrAllocFormatted(&scz, L" /%ls \"%ls\"", BURN_COMMANDLINE_SWITCH_LOG_APPEND, wzAppendLogPath); + ExitOnFailure(hr, "Failed to format append log command-line for command-line."); + + hr = StrAllocConcat(psczCommandLine, scz, 0); + ExitOnFailure(hr, "Failed to append log command-line to command-line"); + } + + if (wzAdditionalCommandLineArguments && *wzAdditionalCommandLineArguments) + { + hr = StrAllocConcat(psczCommandLine, L" ", 0); + ExitOnFailure(hr, "Failed to append space to command-line."); + + hr = StrAllocConcat(psczCommandLine, wzAdditionalCommandLineArguments, 0); + ExitOnFailure(hr, "Failed to append command-line to command-line."); + } + +LExit: + ReleaseStr(scz); + + return hr; +} + +extern "C" HRESULT CoreAppendFileHandleAttachedToCommandLine( + __in HANDLE hFileWithAttachedContainer, + __out HANDLE* phExecutableFile, + __deref_inout_z LPWSTR* psczCommandLine + ) +{ + HRESULT hr = S_OK; + HANDLE hExecutableFile = INVALID_HANDLE_VALUE; + + *phExecutableFile = INVALID_HANDLE_VALUE; + + if (!::DuplicateHandle(::GetCurrentProcess(), hFileWithAttachedContainer, ::GetCurrentProcess(), &hExecutableFile, 0, TRUE, DUPLICATE_SAME_ACCESS)) + { + ExitWithLastError(hr, "Failed to duplicate file handle for attached container."); + } + + hr = StrAllocFormattedSecure(psczCommandLine, L"%ls -%ls=%Iu", *psczCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, reinterpret_cast(hExecutableFile)); + ExitOnFailure(hr, "Failed to append the file handle to the command line."); + + *phExecutableFile = hExecutableFile; + hExecutableFile = INVALID_HANDLE_VALUE; + +LExit: + ReleaseFileHandle(hExecutableFile); + + return hr; +} + +extern "C" HRESULT CoreAppendFileHandleSelfToCommandLine( + __in LPCWSTR wzExecutablePath, + __out HANDLE* phExecutableFile, + __deref_inout_z LPWSTR* psczCommandLine, + __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine + ) +{ + HRESULT hr = S_OK; + HANDLE hExecutableFile = INVALID_HANDLE_VALUE; + SECURITY_ATTRIBUTES securityAttributes = { }; + securityAttributes.bInheritHandle = TRUE; + *phExecutableFile = INVALID_HANDLE_VALUE; + + hExecutableFile = ::CreateFileW(wzExecutablePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, &securityAttributes, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE != hExecutableFile) + { + hr = StrAllocFormattedSecure(psczCommandLine, L"%ls -%ls=%Iu", *psczCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, reinterpret_cast(hExecutableFile)); + ExitOnFailure(hr, "Failed to append the file handle to the command line."); + + if (psczObfuscatedCommandLine) + { + hr = StrAllocFormatted(psczObfuscatedCommandLine, L"%ls -%ls=%Iu", *psczObfuscatedCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, reinterpret_cast(hExecutableFile)); + ExitOnFailure(hr, "Failed to append the file handle to the obfuscated command line."); + } + + *phExecutableFile = hExecutableFile; + hExecutableFile = INVALID_HANDLE_VALUE; + } + +LExit: + ReleaseFileHandle(hExecutableFile); + + return hr; +} + +extern "C" void CoreCleanup( + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + LONGLONG llValue = 0; + BOOL fNeedsElevation = pEngineState->registration.fPerMachine && INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe; + + LogId(REPORT_STANDARD, MSG_CLEANUP_BEGIN); + + if (pEngineState->plan.fAffectedMachineState) + { + LogId(REPORT_STANDARD, MSG_CLEANUP_SKIPPED_APPLY); + ExitFunction(); + } + + if (fNeedsElevation) + { + hr = VariableGetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, &llValue); + ExitOnFailure(hr, "Failed to get value of WixBundleElevated variable during cleanup"); + + if (llValue) + { + fNeedsElevation = FALSE; + } + else + { + LogId(REPORT_STANDARD, MSG_CLEANUP_SKIPPED_ELEVATION_REQUIRED); + ExitFunction(); + } + } + + if (!pEngineState->fDetected) + { + hr = CoreDetect(pEngineState, pEngineState->hMessageWindow); + ExitOnFailure(hr, "Detect during cleanup failed"); + } + + if (!pEngineState->registration.fEligibleForCleanup) + { + ExitFunction(); + } + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); + ExitOnFailure(hr, "Plan during cleanup failed"); + + hr = CoreApply(pEngineState, pEngineState->hMessageWindow); + ExitOnFailure(hr, "Apply during cleanup failed"); + +LExit: + LogId(REPORT_STANDARD, MSG_CLEANUP_COMPLETE, hr); +} + +// internal helper functions + +static HRESULT ParseCommandLine( + __in int argc, + __in LPWSTR* argv, + __in BOOTSTRAPPER_COMMAND* pCommand, + __in BURN_PIPE_CONNECTION* pCompanionConnection, + __in BURN_PIPE_CONNECTION* pEmbeddedConnection, + __in BURN_VARIABLES* pVariables, + __out BURN_MODE* pMode, + __out BURN_AU_PAUSE_ACTION* pAutomaticUpdates, + __out BOOL* pfDisableSystemRestore, + __out_z LPWSTR* psczSourceProcessPath, + __out_z LPWSTR* psczOriginalSource, + __out BOOL* pfDisableUnelevate, + __out DWORD *pdwLoggingAttributes, + __out_z LPWSTR* psczLogFile, + __out_z LPWSTR* psczActiveParent, + __out_z LPWSTR* psczIgnoreDependencies, + __out_z LPWSTR* psczAncestors, + __out_z LPWSTR* psczSanitizedCommandLine + ) +{ + HRESULT hr = S_OK; + BOOL fUnknownArg = FALSE; + BOOL fHidden = FALSE; + LPWSTR sczCommandLine = NULL; + LPWSTR sczSanitizedArgument = NULL; + LPWSTR sczVariableName = NULL; + + for (int i = 0; i < argc; ++i) + { + fUnknownArg = FALSE; + int originalIndex = i; + ReleaseNullStr(sczSanitizedArgument); + + if (argv[i][0] == L'-' || argv[i][0] == L'/') + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"l", -1) || + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"log", -1) || + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"xlog", -1)) + { + *pdwLoggingAttributes &= ~BURN_LOGGING_ATTRIBUTE_APPEND; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], 1, L"x", 1)) + { + *pdwLoggingAttributes |= BURN_LOGGING_ATTRIBUTE_VERBOSE | BURN_LOGGING_ATTRIBUTE_EXTRADEBUG; + } + + if (i + 1 >= argc) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for log."); + } + + ++i; + + hr = StrAllocString(psczLogFile, argv[i], 0); + ExitOnFailure(hr, "Failed to copy log file path."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"?", -1) || + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"h", -1) || + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"help", -1)) + { + pCommand->action = BOOTSTRAPPER_ACTION_HELP; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"q", -1) || + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"quiet", -1) || + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"s", -1) || + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"silent", -1)) + { + pCommand->display = BOOTSTRAPPER_DISPLAY_NONE; + + if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart) + { + pCommand->restart = BOOTSTRAPPER_RESTART_AUTOMATIC; + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"passive", -1)) + { + pCommand->display = BOOTSTRAPPER_DISPLAY_PASSIVE; + + if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart) + { + pCommand->restart = BOOTSTRAPPER_RESTART_AUTOMATIC; + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"norestart", -1)) + { + pCommand->restart = BOOTSTRAPPER_RESTART_NEVER; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"forcerestart", -1)) + { + pCommand->restart = BOOTSTRAPPER_RESTART_ALWAYS; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"promptrestart", -1)) + { + pCommand->restart = BOOTSTRAPPER_RESTART_PROMPT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"layout", -1)) + { + if (BOOTSTRAPPER_ACTION_HELP != pCommand->action) + { + pCommand->action = BOOTSTRAPPER_ACTION_LAYOUT; + } + + // If there is another command line argument and it is not a switch, use that as the layout directory. + if (i + 1 < argc && argv[i + 1][0] != L'-' && argv[i + 1][0] != L'/') + { + ++i; + + hr = PathExpand(&pCommand->wzLayoutDirectory, argv[i], PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); + ExitOnFailure(hr, "Failed to copy path for layout directory."); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"uninstall", -1)) + { + if (BOOTSTRAPPER_ACTION_HELP != pCommand->action) + { + pCommand->action = BOOTSTRAPPER_ACTION_UNINSTALL; + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"repair", -1)) + { + if (BOOTSTRAPPER_ACTION_HELP != pCommand->action) + { + pCommand->action = BOOTSTRAPPER_ACTION_REPAIR; + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"modify", -1)) + { + if (BOOTSTRAPPER_ACTION_HELP != pCommand->action) + { + pCommand->action = BOOTSTRAPPER_ACTION_MODIFY; + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"package", -1) || + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"update", -1)) + { + if (BOOTSTRAPPER_ACTION_UNKNOWN == pCommand->action) + { + pCommand->action = BOOTSTRAPPER_ACTION_INSTALL; + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"noaupause", -1)) + { + *pAutomaticUpdates = BURN_AU_PAUSE_ACTION_NONE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"keepaupaused", -1)) + { + // Switch /noaupause takes precedence. + if (BURN_AU_PAUSE_ACTION_NONE != *pAutomaticUpdates) + { + *pAutomaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME; + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"disablesystemrestore", -1)) + { + *pfDisableSystemRestore = TRUE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"originalsource", -1)) + { + if (i + 1 >= argc) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for original source."); + } + + ++i; + hr = StrAllocString(psczOriginalSource, argv[i], 0); + ExitOnFailure(hr, "Failed to copy last used source."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PARENT, -1)) + { + if (i + 1 >= argc) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a value for parent."); + } + + ++i; + + hr = StrAllocString(psczActiveParent, argv[i], 0); + ExitOnFailure(hr, "Failed to copy parent."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PARENT_NONE, -1)) + { + hr = StrAllocString(psczActiveParent, L"", 0); + ExitOnFailure(hr, "Failed to initialize parent to none."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_LOG_APPEND, -1)) + { + if (i + 1 >= argc) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for append log."); + } + + ++i; + + hr = StrAllocString(psczLogFile, argv[i], 0); + ExitOnFailure(hr, "Failed to copy append log file path."); + + *pdwLoggingAttributes |= BURN_LOGGING_ATTRIBUTE_APPEND; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_ELEVATED, -1)) + { + if (i + 3 >= argc) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Must specify the elevated name, token and parent process id."); + } + + if (BURN_MODE_UNTRUSTED != *pMode) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided."); + } + + *pMode = BURN_MODE_ELEVATED; + + ++i; + + hr = ParsePipeConnection(argv + i, pCompanionConnection); + ExitOnFailure(hr, "Failed to parse elevated connection."); + + i += 2; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM), BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM))) + { + // Get a pointer to the next character after the switch. + LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM)]; + if (L'=' != wzParam[0] || L'\0' == wzParam[1]) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_CLEAN_ROOM); + } + + if (BURN_MODE_UNTRUSTED != *pMode) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided."); + } + + *pMode = BURN_MODE_NORMAL; + + hr = StrAllocString(psczSourceProcessPath, wzParam + 1, 0); + ExitOnFailure(hr, "Failed to copy source process path."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_EMBEDDED, -1)) + { + if (i + 3 >= argc) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Must specify the embedded name, token and parent process id."); + } + + switch (*pMode) + { + case BURN_MODE_UNTRUSTED: + // Leave mode as UNTRUSTED to launch the clean room process. + break; + case BURN_MODE_NORMAL: + // The initialization code already assumes that the + // clean room switch is at the beginning of the command line, + // so it's safe to assume that the mode is NORMAL in the clean room. + *pMode = BURN_MODE_EMBEDDED; + break; + default: + ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided."); + } + + ++i; + + hr = ParsePipeConnection(argv + i, pEmbeddedConnection); + ExitOnFailure(hr, "Failed to parse embedded connection."); + + i += 2; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_DETECT, -1)) + { + pCommand->relationType = BOOTSTRAPPER_RELATION_DETECT; + + LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType)); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE, -1)) + { + pCommand->relationType = BOOTSTRAPPER_RELATION_UPGRADE; + + LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType)); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_ADDON, -1)) + { + pCommand->relationType = BOOTSTRAPPER_RELATION_ADDON; + + LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType)); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_PATCH, -1)) + { + pCommand->relationType = BOOTSTRAPPER_RELATION_PATCH; + + LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType)); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_UPDATE, -1)) + { + pCommand->relationType = BOOTSTRAPPER_RELATION_UPDATE; + + LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType)); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PASSTHROUGH, -1)) + { + pCommand->fPassthrough = TRUE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_DISABLE_UNELEVATE, -1)) + { + *pfDisableUnelevate = TRUE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RUNONCE, -1)) + { + if (BURN_MODE_UNTRUSTED != *pMode) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided."); + } + + *pMode = BURN_MODE_RUNONCE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES), BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES))) + { + // Get a pointer to the next character after the switch. + LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES)]; + if (L'=' != wzParam[0] || L'\0' == wzParam[1]) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES); + } + + hr = StrAllocString(psczIgnoreDependencies, &wzParam[1], 0); + ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS), BURN_COMMANDLINE_SWITCH_ANCESTORS, lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS))) + { + // Get a pointer to the next character after the switch. + LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS)]; + if (L'=' != wzParam[0] || L'\0' == wzParam[1]) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_ANCESTORS); + } + + hr = StrAllocString(psczAncestors, &wzParam[1], 0); + ExitOnFailure(hr, "Failed to allocate the list of ancestors."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED), BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED))) + { + // Already processed in InitializeEngineState. + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF), BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF))) + { + // Already processed in InitializeEngineState. + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX), BURN_COMMANDLINE_SWITCH_PREFIX, lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX))) + { + // Skip (but log) any other private burn switches we don't recognize, so that + // adding future private variables doesn't break old bundles + LogId(REPORT_STANDARD, MSG_BURN_UNKNOWN_PRIVATE_SWITCH, &argv[i][1]); + } + else + { + fUnknownArg = TRUE; + } + } + else + { + fUnknownArg = TRUE; + + const wchar_t* pwc = wcschr(argv[i], L'='); + if (pwc) + { + hr = StrAllocString(&sczVariableName, argv[i], pwc - argv[i]); + ExitOnFailure(hr, "Failed to copy variable name."); + + hr = VariableIsHidden(pVariables, sczVariableName, &fHidden); + ExitOnFailure(hr, "Failed to determine whether variable is hidden."); + + if (fHidden) + { + hr = StrAllocFormatted(&sczSanitizedArgument, L"%ls=*****", sczVariableName); + ExitOnFailure(hr, "Failed to copy sanitized argument."); + } + } + } + + // Remember command-line switch to pass off to UX. + if (fUnknownArg) + { + PathCommandLineAppend(&pCommand->wzCommandLine, argv[i]); + } + + if (sczSanitizedArgument) + { + PathCommandLineAppend(psczSanitizedCommandLine, sczSanitizedArgument); + } + else + { + for (; originalIndex <= i; ++originalIndex) + { + PathCommandLineAppend(psczSanitizedCommandLine, argv[originalIndex]); + } + } + } + + // If embedded, ensure the display goes embedded as well. + if (BURN_MODE_EMBEDDED == *pMode) + { + pCommand->display = BOOTSTRAPPER_DISPLAY_EMBEDDED; + } + + // Set the defaults if nothing was set above. + if (BOOTSTRAPPER_ACTION_UNKNOWN == pCommand->action) + { + pCommand->action = BOOTSTRAPPER_ACTION_INSTALL; + } + + if (BOOTSTRAPPER_DISPLAY_UNKNOWN == pCommand->display) + { + pCommand->display = BOOTSTRAPPER_DISPLAY_FULL; + } + + if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart) + { + pCommand->restart = BOOTSTRAPPER_RESTART_PROMPT; + } + +LExit: + ReleaseStr(sczVariableName); + ReleaseStr(sczSanitizedArgument); + ReleaseStr(sczCommandLine); + + return hr; +} + +static HRESULT ParsePipeConnection( + __in_ecount(3) LPWSTR* rgArgs, + __in BURN_PIPE_CONNECTION* pConnection + ) +{ + HRESULT hr = S_OK; + + hr = StrAllocString(&pConnection->sczName, rgArgs[0], 0); + ExitOnFailure(hr, "Failed to copy connection name from command line."); + + hr = StrAllocString(&pConnection->sczSecret, rgArgs[1], 0); + ExitOnFailure(hr, "Failed to copy connection secret from command line."); + + hr = StrStringToUInt32(rgArgs[2], 0, reinterpret_cast(&pConnection->dwProcessId)); + ExitOnFailure(hr, "Failed to copy parent process id from command line."); + +LExit: + return hr; +} + +static HRESULT DetectPackage( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + BOOL fBegan = FALSE; + + fBegan = TRUE; + hr = UserExperienceOnDetectPackageBegin(&pEngineState->userExperience, pPackage->sczId); + ExitOnRootFailure(hr, "BA aborted detect package begin."); + + // Detect the cache state of the package. + hr = DetectPackagePayloadsCached(pPackage); + ExitOnFailure(hr, "Failed to detect if payloads are all cached for package: %ls", pPackage->sczId); + + // Use the correct engine to detect the package. + switch (pPackage->type) + { + case BURN_PACKAGE_TYPE_EXE: + hr = ExeEngineDetectPackage(pPackage, &pEngineState->variables); + break; + + case BURN_PACKAGE_TYPE_MSI: + hr = MsiEngineDetectPackage(pPackage, &pEngineState->userExperience); + break; + + case BURN_PACKAGE_TYPE_MSP: + hr = MspEngineDetectPackage(pPackage, &pEngineState->userExperience); + break; + + case BURN_PACKAGE_TYPE_MSU: + hr = MsuEngineDetectPackage(pPackage, &pEngineState->variables); + break; + + default: + hr = E_NOTIMPL; + ExitOnRootFailure(hr, "Package type not supported by detect yet."); + } + +LExit: + if (FAILED(hr)) + { + LogErrorId(hr, MSG_FAILED_DETECT_PACKAGE, pPackage->sczId, NULL, NULL); + } + + if (fBegan) + { + UserExperienceOnDetectPackageComplete(&pEngineState->userExperience, pPackage->sczId, hr, pPackage->currentState, pPackage->fCached); + } + + return hr; +} + +static HRESULT DetectPackagePayloadsCached( + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCachePath = NULL; + BOOL fCached = FALSE; // assume the package is not cached. + LPWSTR sczPayloadCachePath = NULL; + + if (pPackage->sczCacheId && *pPackage->sczCacheId) + { + hr = CacheGetCompletedPath(pPackage->fPerMachine, pPackage->sczCacheId, &sczCachePath); + ExitOnFailure(hr, "Failed to get completed cache path."); + + // If the cached directory exists, we have something. + if (DirExists(sczCachePath, NULL)) + { + // Check all payloads to see if any exist. + for (DWORD i = 0; i < pPackage->payloads.cItems; ++i) + { + BURN_PAYLOAD* pPayload = pPackage->payloads.rgItems[i].pPayload; + + hr = PathConcat(sczCachePath, pPayload->sczFilePath, &sczPayloadCachePath); + ExitOnFailure(hr, "Failed to concat payload cache path."); + + if (FileExistsEx(sczPayloadCachePath, NULL)) + { + fCached = TRUE; + break; + } + else + { + LogId(REPORT_STANDARD, MSG_DETECT_PACKAGE_NOT_FULLY_CACHED, pPackage->sczId, pPayload->sczKey); + } + } + } + } + + pPackage->fCached = fCached; + + if (pPackage->fCanAffectRegistration) + { + pPackage->cacheRegistrationState = pPackage->fCached ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + +LExit: + ReleaseStr(sczPayloadCachePath); + ReleaseStr(sczCachePath); + return hr; +} + +static DWORD WINAPI CacheThreadProc( + __in LPVOID lpThreadParameter + ) +{ + HRESULT hr = S_OK; + BURN_CACHE_THREAD_CONTEXT* pContext = reinterpret_cast(lpThreadParameter); + BURN_ENGINE_STATE* pEngineState = pContext->pEngineState; + DWORD* pcOverallProgressTicks = pContext->pcOverallProgressTicks; + BOOL* pfRollback = pContext->pfRollback; + BOOL fComInitialized = FALSE; + + // initialize COM + hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); + ExitOnFailure(hr, "Failed to initialize COM on cache thread."); + fComInitialized = TRUE; + + // cache packages + hr = ApplyCache(pEngineState->section.hSourceEngineFile, &pEngineState->userExperience, &pEngineState->variables, &pEngineState->plan, pEngineState->companionConnection.hCachePipe, pcOverallProgressTicks, pfRollback); + +LExit: + UserExperienceExecutePhaseComplete(&pEngineState->userExperience, hr); // signal that cache completed. + + if (fComInitialized) + { + ::CoUninitialize(); + } + + return (DWORD)hr; +} + +static HRESULT WaitForCacheThread( + __in HANDLE hCacheThread + ) +{ + HRESULT hr = S_OK; + + if (WAIT_OBJECT_0 != ::WaitForSingleObject(hCacheThread, INFINITE)) + { + ExitWithLastError(hr, "Failed to wait for cache thread to terminate."); + } + + if (!::GetExitCodeThread(hCacheThread, (DWORD*)&hr)) + { + ExitWithLastError(hr, "Failed to get cache thread exit code."); + } + +LExit: + return hr; +} + +static void LogPackages( + __in_opt const BURN_PACKAGE* pUpgradeBundlePackage, + __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage, + __in const BURN_PACKAGES* pPackages, + __in const BURN_RELATED_BUNDLES* pRelatedBundles, + __in const BOOTSTRAPPER_ACTION action + ) +{ + if (pUpgradeBundlePackage) + { + LogId(REPORT_STANDARD, MSG_PLANNED_UPGRADE_BUNDLE, pUpgradeBundlePackage->sczId, LoggingRequestStateToString(pUpgradeBundlePackage->defaultRequested), LoggingRequestStateToString(pUpgradeBundlePackage->requested), LoggingActionStateToString(pUpgradeBundlePackage->execute), LoggingActionStateToString(pUpgradeBundlePackage->rollback), LoggingDependencyActionToString(pUpgradeBundlePackage->dependencyExecute)); + } + else if (pForwardCompatibleBundlePackage) + { + LogId(REPORT_STANDARD, MSG_PLANNED_FORWARD_COMPATIBLE_BUNDLE, pForwardCompatibleBundlePackage->sczId, LoggingRequestStateToString(pForwardCompatibleBundlePackage->defaultRequested), LoggingRequestStateToString(pForwardCompatibleBundlePackage->requested), LoggingActionStateToString(pForwardCompatibleBundlePackage->execute), LoggingActionStateToString(pForwardCompatibleBundlePackage->rollback), LoggingDependencyActionToString(pForwardCompatibleBundlePackage->dependencyExecute)); + } + else + { + // Display related bundles first if uninstalling. + if (BOOTSTRAPPER_ACTION_UNINSTALL == action) + { + LogRelatedBundles(pRelatedBundles, TRUE); + } + + // Display all the packages in the log. + for (DWORD i = 0; i < pPackages->cPackages; ++i) + { + const DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? pPackages->cPackages - 1 - i : i; + const BURN_PACKAGE* pPackage = &pPackages->rgPackages[iPackage]; + + LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingBoolToString(pPackage->fPlannedCache), LoggingBoolToString(pPackage->fPlannedUncache), LoggingDependencyActionToString(pPackage->dependencyExecute), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedInstallRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedCacheRegistrationState)); + + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + { + if (pPackage->Msi.cFeatures) + { + LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURES, pPackage->Msi.cFeatures, pPackage->sczId); + + for (DWORD j = 0; j < pPackage->Msi.cFeatures; ++j) + { + const BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[j]; + + LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURE, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState), LoggingMsiFeatureStateToString(pFeature->defaultRequested), LoggingMsiFeatureStateToString(pFeature->requested), LoggingMsiFeatureActionToString(pFeature->execute), LoggingMsiFeatureActionToString(pFeature->rollback)); + } + } + + if (pPackage->Msi.cSlipstreamMspPackages) + { + LogId(REPORT_STANDARD, MSG_PLANNED_SLIPSTREAMED_MSP_TARGETS, pPackage->Msi.cSlipstreamMspPackages, pPackage->sczId); + + for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) + { + const BURN_SLIPSTREAM_MSP* pSlipstreamMsp = &pPackage->Msi.rgSlipstreamMsps[j]; + + LogId(REPORT_STANDARD, MSG_PLANNED_SLIPSTREAMED_MSP_TARGET, pSlipstreamMsp->pMspPackage->sczId, LoggingActionStateToString(pSlipstreamMsp->execute), LoggingActionStateToString(pSlipstreamMsp->rollback)); + } + } + } + else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.cTargetProductCodes) + { + LogId(REPORT_STANDARD, MSG_PLANNED_MSP_TARGETS, pPackage->Msp.cTargetProductCodes, pPackage->sczId); + + for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j) + { + const BURN_MSPTARGETPRODUCT* pTargetProduct = &pPackage->Msp.rgTargetProducts[j]; + + LogId(REPORT_STANDARD, MSG_PLANNED_MSP_TARGET, pTargetProduct->wzTargetProductCode, LoggingPackageStateToString(pTargetProduct->patchPackageState), LoggingRequestStateToString(pTargetProduct->defaultRequested), LoggingRequestStateToString(pTargetProduct->requested), LoggingMspTargetActionToString(pTargetProduct->execute, pTargetProduct->executeSkip), LoggingMspTargetActionToString(pTargetProduct->rollback, pTargetProduct->rollbackSkip)); + } + } + } + + // Display related bundles last if caching, installing, modifying, or repairing. + if (BOOTSTRAPPER_ACTION_UNINSTALL < action) + { + LogRelatedBundles(pRelatedBundles, FALSE); + } + } +} + +static void LogRelatedBundles( + __in const BURN_RELATED_BUNDLES* pRelatedBundles, + __in BOOL fReverse + ) +{ + if (0 < pRelatedBundles->cRelatedBundles) + { + for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i) + { + const DWORD iRelatedBundle = fReverse ? pRelatedBundles->cRelatedBundles - 1 - i : i; + const BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + iRelatedBundle; + const BURN_PACKAGE* pPackage = &pRelatedBundle->package; + + if (pRelatedBundle->fPlannable) + { + LogId(REPORT_STANDARD, MSG_PLANNED_RELATED_BUNDLE, pPackage->sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingDependencyActionToString(pPackage->dependencyExecute)); + } + } + } +} diff --git a/src/burn/engine/core.h b/src/burn/engine/core.h new file mode 100644 index 00000000..e96440bb --- /dev/null +++ b/src/burn/engine/core.h @@ -0,0 +1,218 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + +const LPCWSTR BURN_POLICY_REGISTRY_PATH = L"WiX\\Burn"; + +const LPCWSTR BURN_COMMANDLINE_SWITCH_PARENT = L"parent"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_PARENT_NONE = L"parent:none"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_CLEAN_ROOM = L"burn.clean.room"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_ELEVATED = L"burn.elevated"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_EMBEDDED = L"burn.embedded"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_RUNONCE = L"burn.runonce"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_LOG_APPEND = L"burn.log.append"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_DETECT = L"burn.related.detect"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE = L"burn.related.upgrade"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_ADDON = L"burn.related.addon"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_PATCH = L"burn.related.patch"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPDATE = L"burn.related.update"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_PASSTHROUGH = L"burn.passthrough"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_DISABLE_UNELEVATE = L"burn.disable.unelevate"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES = L"burn.ignoredependencies"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_ANCESTORS = L"burn.ancestors"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED = L"burn.filehandle.attached"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF = L"burn.filehandle.self"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_PREFIX = L"burn."; + +const LPCWSTR BURN_BUNDLE_LAYOUT_DIRECTORY = L"WixBundleLayoutDirectory"; +const LPCWSTR BURN_BUNDLE_ACTION = L"WixBundleAction"; +const LPCWSTR BURN_BUNDLE_ACTIVE_PARENT = L"WixBundleActiveParent"; +const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER = L"WixBundleExecutePackageCacheFolder"; +const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_ACTION = L"WixBundleExecutePackageAction"; +const LPCWSTR BURN_BUNDLE_FORCED_RESTART_PACKAGE = L"WixBundleForcedRestartPackage"; +const LPCWSTR BURN_BUNDLE_INSTALLED = L"WixBundleInstalled"; +const LPCWSTR BURN_BUNDLE_ELEVATED = L"WixBundleElevated"; +const LPCWSTR BURN_BUNDLE_PROVIDER_KEY = L"WixBundleProviderKey"; +const LPCWSTR BURN_BUNDLE_MANUFACTURER = L"WixBundleManufacturer"; +const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_PATH = L"WixBundleSourceProcessPath"; +const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_FOLDER = L"WixBundleSourceProcessFolder"; +const LPCWSTR BURN_BUNDLE_TAG = L"WixBundleTag"; +const LPCWSTR BURN_BUNDLE_UILEVEL = L"WixBundleUILevel"; +const LPCWSTR BURN_BUNDLE_VERSION = L"WixBundleVersion"; +const LPCWSTR BURN_REBOOT_PENDING = L"RebootPending"; + +// The following constants must stay in sync with src\wix\Binder.cs +const LPCWSTR BURN_BUNDLE_NAME = L"WixBundleName"; +const LPCWSTR BURN_BUNDLE_ORIGINAL_SOURCE = L"WixBundleOriginalSource"; +const LPCWSTR BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER = L"WixBundleOriginalSourceFolder"; +const LPCWSTR BURN_BUNDLE_LAST_USED_SOURCE = L"WixBundleLastUsedSource"; + + +// enums + +enum BURN_MODE +{ + BURN_MODE_UNTRUSTED, + BURN_MODE_NORMAL, + BURN_MODE_ELEVATED, + BURN_MODE_EMBEDDED, + BURN_MODE_RUNONCE, +}; + +enum BURN_AU_PAUSE_ACTION +{ + BURN_AU_PAUSE_ACTION_NONE, + BURN_AU_PAUSE_ACTION_IFELEVATED, + BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME, +}; + + +// structs + +typedef struct _BURN_ENGINE_STATE +{ + // UX flow control + BOOL fDetected; + BOOL fPlanned; + BOOL fQuit; + //BOOL fSuspend; // Is TRUE when UX made Suspend() call on core. + //BOOL fForcedReboot; // Is TRUE when UX made Reboot() call on core. + //BOOL fCancelled; // Is TRUE when UX return cancel on UX OnXXX() methods. + //BOOL fReboot; // Is TRUE when UX confirms OnRestartRequried(). + BOOL fRestart; // Set TRUE when UX returns IDRESTART during Apply(). + + // engine data + BOOTSTRAPPER_COMMAND command; + BURN_SECTION section; + BURN_VARIABLES variables; + BURN_CONDITION condition; + BURN_SEARCHES searches; + BURN_USER_EXPERIENCE userExperience; + BURN_REGISTRATION registration; + BURN_CONTAINERS containers; + BURN_PAYLOADS payloads; + BURN_PACKAGES packages; + BURN_UPDATE update; + BURN_APPROVED_EXES approvedExes; + BURN_EXTENSIONS extensions; + + HWND hMessageWindow; + HANDLE hMessageWindowThread; + + BOOL fDisableRollback; + BOOL fDisableSystemRestore; + BOOL fParallelCacheAndExecute; + + BURN_LOGGING log; + + BURN_PAYLOAD_GROUP layoutPayloads; + + BURN_PLAN plan; + + BURN_MODE mode; + BURN_AU_PAUSE_ACTION automaticUpdates; + + DWORD dwElevatedLoggingTlsId; + + LPWSTR sczBundleEngineWorkingPath; + BURN_PIPE_CONNECTION companionConnection; + BURN_PIPE_CONNECTION embeddedConnection; + + BURN_RESUME_MODE resumeMode; + BOOL fDisableUnelevate; + + LPWSTR sczIgnoreDependencies; + + int argc; + LPWSTR* argv; +} BURN_ENGINE_STATE; + + +// function declarations + +HRESULT CoreInitialize( + __in BURN_ENGINE_STATE* pEngineState + ); +HRESULT CoreInitializeConstants( + __in BURN_ENGINE_STATE* pEngineState + ); +HRESULT CoreSerializeEngineState( + __in BURN_ENGINE_STATE* pEngineState, + __inout BYTE** ppbBuffer, + __inout SIZE_T* piBuffer + ); +HRESULT CoreQueryRegistration( + __in BURN_ENGINE_STATE* pEngineState + ); +//HRESULT CoreDeserializeEngineState( +// __in BURN_ENGINE_STATE* pEngineState, +// __in_bcount(cbBuffer) BYTE* pbBuffer, +// __in SIZE_T cbBuffer +// ); +HRESULT CoreDetect( + __in BURN_ENGINE_STATE* pEngineState, + __in_opt HWND hwndParent + ); +HRESULT CorePlan( + __in BURN_ENGINE_STATE* pEngineState, + __in BOOTSTRAPPER_ACTION action + ); +HRESULT CoreElevate( + __in BURN_ENGINE_STATE* pEngineState, + __in_opt HWND hwndParent + ); +HRESULT CoreApply( + __in BURN_ENGINE_STATE* pEngineState, + __in_opt HWND hwndParent + ); +HRESULT CoreLaunchApprovedExe( + __in BURN_ENGINE_STATE* pEngineState, + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe + ); +HRESULT CoreQuit( + __in BURN_ENGINE_STATE* pEngineState, + __in int nExitCode + ); +HRESULT CoreSaveEngineState( + __in BURN_ENGINE_STATE* pEngineState + ); +LPCWSTR CoreRelationTypeToCommandLineString( + __in BOOTSTRAPPER_RELATION_TYPE relationType + ); +HRESULT CoreRecreateCommandLine( + __deref_inout_z LPWSTR* psczCommandLine, + __in BOOTSTRAPPER_ACTION action, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RESTART restart, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in BOOL fPassthrough, + __in_z_opt LPCWSTR wzActiveParent, + __in_z_opt LPCWSTR wzAncestors, + __in_z_opt LPCWSTR wzAppendLogPath, + __in_z_opt LPCWSTR wzAdditionalCommandLineArguments + ); +HRESULT CoreAppendFileHandleAttachedToCommandLine( + __in HANDLE hFileWithAttachedContainer, + __out HANDLE* phExecutableFile, + __deref_inout_z LPWSTR* psczCommandLine + ); +HRESULT CoreAppendFileHandleSelfToCommandLine( + __in LPCWSTR wzExecutablePath, + __out HANDLE* phExecutableFile, + __deref_inout_z LPWSTR* psczCommandLine, + __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine + ); +void CoreCleanup( + __in BURN_ENGINE_STATE* pEngineState + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/dependency.cpp b/src/burn/engine/dependency.cpp new file mode 100644 index 00000000..876cd8b3 --- /dev/null +++ b/src/burn/engine/dependency.cpp @@ -0,0 +1,1312 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// constants + +#define INITIAL_STRINGDICT_SIZE 48 +const LPCWSTR vcszIgnoreDependenciesDelim = L";"; + + +// internal function declarations + +static HRESULT DetectPackageDependents( + __in BURN_PACKAGE* pPackage, + __in STRINGDICT_HANDLE sdIgnoredDependents, + __in const BURN_REGISTRATION* pRegistration + ); + +static HRESULT SplitIgnoreDependencies( + __in_z LPCWSTR wzIgnoreDependencies, + __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, + __inout LPUINT pcDependencies, + __out BOOL* pfIgnoreAll + ); + +static HRESULT JoinIgnoreDependencies( + __out_z LPWSTR* psczIgnoreDependencies, + __in_ecount(cDependencies) const DEPENDENCY* rgDependencies, + __in UINT cDependencies + ); + +static HRESULT GetIgnoredDependents( + __in const BURN_PACKAGE* pPackage, + __in const BURN_PLAN* pPlan, + __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents + ); + +static BOOL GetProviderExists( + __in HKEY hkRoot, + __in_z LPCWSTR wzProviderKey + ); + +static void CalculateDependencyActionStates( + __in const BURN_PACKAGE* pPackage, + __in const BOOTSTRAPPER_ACTION action, + __out BURN_DEPENDENCY_ACTION* pDependencyExecuteAction, + __out BURN_DEPENDENCY_ACTION* pDependencyRollbackAction + ); + +static HRESULT AddPackageDependencyActions( + __in_opt DWORD *pdwInsertSequence, + __in const BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in const BURN_DEPENDENCY_ACTION dependencyExecuteAction, + __in const BURN_DEPENDENCY_ACTION dependencyRollbackAction + ); + +static HRESULT RegisterPackageProvider( + __in const BURN_PACKAGE* pPackage + ); + +static void UnregisterPackageProvider( + __in const BURN_PACKAGE* pPackage + ); + +static HRESULT RegisterPackageDependency( + __in BOOL fPerMachine, + __in const BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzDependentProviderKey + ); + +static void UnregisterPackageDependency( + __in BOOL fPerMachine, + __in const BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzDependentProviderKey + ); + + +// functions + +extern "C" void DependencyUninitializeProvider( + __in BURN_DEPENDENCY_PROVIDER* pProvider + ) +{ + ReleaseStr(pProvider->sczKey); + ReleaseStr(pProvider->sczVersion); + ReleaseStr(pProvider->sczDisplayName); + ReleaseDependencyArray(pProvider->rgDependents, pProvider->cDependents); + + memset(pProvider, 0, sizeof(BURN_DEPENDENCY_PROVIDER)); +} + +extern "C" HRESULT DependencyParseProvidersFromXml( + __in BURN_PACKAGE* pPackage, + __in IXMLDOMNode* pixnPackage + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + DWORD cNodes = 0; + IXMLDOMNode* pixnNode = NULL; + + // Select dependency provider nodes. + hr = XmlSelectNodes(pixnPackage, L"Provides", &pixnNodes); + ExitOnFailure(hr, "Failed to select dependency provider nodes."); + + // Get dependency provider node count. + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get the dependency provider node count."); + + if (!cNodes) + { + ExitFunction1(hr = S_OK); + } + + // Allocate memory for dependency provider pointers. + pPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER) * cNodes, TRUE); + ExitOnNull(pPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers."); + + pPackage->cDependencyProviders = cNodes; + + // Parse dependency provider elements. + for (DWORD i = 0; i < cNodes; i++) + { + BURN_DEPENDENCY_PROVIDER* pDependencyProvider = &pPackage->rgDependencyProviders[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get the next dependency provider node."); + + // @Key + hr = XmlGetAttributeEx(pixnNode, L"Key", &pDependencyProvider->sczKey); + ExitOnFailure(hr, "Failed to get the Key attribute."); + + // @Version + hr = XmlGetAttributeEx(pixnNode, L"Version", &pDependencyProvider->sczVersion); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get the Version attribute."); + } + + // @DisplayName + hr = XmlGetAttributeEx(pixnNode, L"DisplayName", &pDependencyProvider->sczDisplayName); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get the DisplayName attribute."); + } + + // @Imported + hr = XmlGetYesNoAttribute(pixnNode, L"Imported", &pDependencyProvider->fImported); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get the Imported attribute."); + } + else + { + pDependencyProvider->fImported = FALSE; + hr = S_OK; + } + + // Prepare next iteration. + ReleaseNullObject(pixnNode); + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNode); + ReleaseObject(pixnNodes); + + return hr; +} + +extern "C" HRESULT DependencyInitialize( + __in BURN_REGISTRATION* pRegistration, + __in_z_opt LPCWSTR wzIgnoreDependencies + ) +{ + HRESULT hr = S_OK; + + // If no parent was specified at all, use the bundle id as the self dependent. + if (!pRegistration->sczActiveParent) + { + pRegistration->wzSelfDependent = pRegistration->sczId; + } + else if (*pRegistration->sczActiveParent) // if parent was specified use that as the self dependent. + { + pRegistration->wzSelfDependent = pRegistration->sczActiveParent; + } + // else parent:none was used which means we should not register a dependency on ourself. + + // The current bundle provider key should always be ignored for dependency checks. + hr = DepDependencyArrayAlloc(&pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies, pRegistration->sczProviderKey, NULL); + ExitOnFailure(hr, "Failed to add the bundle provider key to the list of dependencies to ignore."); + + // Add the list of dependencies to ignore. + if (wzIgnoreDependencies) + { + hr = SplitIgnoreDependencies(wzIgnoreDependencies, &pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies, &pRegistration->fIgnoreAllDependents); + ExitOnFailure(hr, "Failed to split the list of dependencies to ignore."); + } + +LExit: + return hr; +} + +extern "C" HRESULT DependencyDetectProviderKeyBundleId( + __in BURN_REGISTRATION* pRegistration + ) +{ + HRESULT hr = S_OK; + + hr = DepGetProviderInformation(pRegistration->hkRoot, pRegistration->sczProviderKey, &pRegistration->sczDetectedProviderKeyBundleId, NULL, NULL); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get provider key bundle id."); + + // If a bundle id was not explicitly set, default the provider key bundle id to this bundle's provider key. + if (!pRegistration->sczDetectedProviderKeyBundleId || !*pRegistration->sczDetectedProviderKeyBundleId) + { + hr = StrAllocString(&pRegistration->sczDetectedProviderKeyBundleId, pRegistration->sczProviderKey, 0); + ExitOnFailure(hr, "Failed to initialize provider key bundle id."); + } + +LExit: + return hr; +} + +extern "C" HRESULT DependencyDetect( + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + BURN_REGISTRATION* pRegistration = &pEngineState->registration; + STRINGDICT_HANDLE sdIgnoredDependents = NULL; + BURN_PACKAGE* pPackage = NULL; + BOOL fSelfDependent = NULL != pRegistration->wzSelfDependent; + BOOL fActiveParent = NULL != pRegistration->sczActiveParent && NULL != *pRegistration->sczActiveParent; + + // Always leave this empty so that all dependents get detected. Plan will ignore dependents based on its own logic. + hr = DictCreateStringList(&sdIgnoredDependents, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create the string dictionary."); + + hr = DepCheckDependents(pRegistration->hkRoot, pRegistration->sczProviderKey, 0, sdIgnoredDependents, &pRegistration->rgDependents, &pRegistration->cDependents); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed dependents check on bundle"); + } + else + { + hr = S_OK; + } + + for (DWORD iPackage = 0; iPackage < pEngineState->packages.cPackages; ++iPackage) + { + pPackage = pEngineState->packages.rgPackages + iPackage; + hr = DetectPackageDependents(pPackage, sdIgnoredDependents, pRegistration); + ExitOnFailure(hr, "Failed to detect dependents for package '%ls'", pPackage->sczId); + } + + for (DWORD iRelatedBundle = 0; iRelatedBundle < pEngineState->registration.relatedBundles.cRelatedBundles; ++iRelatedBundle) + { + BURN_RELATED_BUNDLE* pRelatedBundle = pEngineState->registration.relatedBundles.rgRelatedBundles + iRelatedBundle; + if (!pRelatedBundle->fPlannable) + { + continue; + } + + pPackage = &pRelatedBundle->package; + hr = DetectPackageDependents(pPackage, sdIgnoredDependents, pRegistration); + ExitOnFailure(hr, "Failed to detect dependents for related bundle '%ls'", pPackage->sczId); + } + + if (fSelfDependent || fActiveParent) + { + for (DWORD i = 0; i < pRegistration->cDependents; ++i) + { + DEPENDENCY* pDependent = pRegistration->rgDependents + i; + + if (fActiveParent && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczActiveParent, -1, pDependent->sczKey, -1)) + { + pRegistration->fParentRegisteredAsDependent = TRUE; + } + + if (fSelfDependent && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->wzSelfDependent, -1, pDependent->sczKey, -1)) + { + pRegistration->fSelfRegisteredAsDependent = TRUE; + } + } + } + +LExit: + ReleaseDict(sdIgnoredDependents); + + return hr; +} + +extern "C" HRESULT DependencyPlanInitialize( + __in const BURN_REGISTRATION* pRegistration, + __in BURN_PLAN* pPlan + ) +{ + HRESULT hr = S_OK; + + // TODO: After adding enumeration to STRINGDICT, a single STRINGDICT_HANDLE can be used everywhere. + for (DWORD i = 0; i < pRegistration->cIgnoredDependencies; ++i) + { + DEPENDENCY* pDependency = pRegistration->rgIgnoredDependencies + i; + + hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pDependency->sczKey, pDependency->sczName); + ExitOnFailure(hr, "Failed to add the detected provider to the list of dependencies to ignore."); + } + +LExit: + return hr; +} + +extern "C" HRESULT DependencyAllocIgnoreDependencies( + __in const BURN_PLAN *pPlan, + __out_z LPWSTR* psczIgnoreDependencies + ) +{ + HRESULT hr = S_OK; + + // Join the list of dependencies to ignore for each related bundle. + if (0 < pPlan->cPlannedProviders) + { + hr = JoinIgnoreDependencies(psczIgnoreDependencies, pPlan->rgPlannedProviders, pPlan->cPlannedProviders); + ExitOnFailure(hr, "Failed to join the list of dependencies to ignore."); + } + +LExit: + return hr; +} + +extern "C" HRESULT DependencyAddIgnoreDependencies( + __in STRINGDICT_HANDLE sdIgnoreDependencies, + __in_z LPCWSTR wzAddIgnoreDependencies + ) +{ + HRESULT hr = S_OK; + LPWSTR wzContext = NULL; + + // Parse through the semicolon-delimited tokens and add to the array. + for (LPCWSTR wzToken = ::wcstok_s(const_cast(wzAddIgnoreDependencies), vcszIgnoreDependenciesDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, vcszIgnoreDependenciesDelim, &wzContext)) + { + hr = DictKeyExists(sdIgnoreDependencies, wzToken); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies."); + } + else + { + hr = DictAddKey(sdIgnoreDependencies, wzToken); + ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzToken); + } + } + +LExit: + return hr; +} + +extern "C" HRESULT DependencyPlanPackageBegin( + __in BOOL fPerMachine, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan + ) +{ + HRESULT hr = S_OK; + STRINGDICT_HANDLE sdIgnoredDependents = NULL; + BURN_DEPENDENCY_ACTION dependencyExecuteAction = BURN_DEPENDENCY_ACTION_NONE; + BURN_DEPENDENCY_ACTION dependencyRollbackAction = BURN_DEPENDENCY_ACTION_NONE; + + pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE; + pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE; + + // Make sure the package defines at least one provider. + if (0 == pPackage->cDependencyProviders) + { + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_SKIP_NOPROVIDERS, pPackage->sczId); + ExitFunction1(hr = S_OK); + } + + // Make sure the package is in the same scope as the bundle. + if (fPerMachine != pPackage->fPerMachine) + { + LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine)); + ExitFunction1(hr = S_OK); + } + + // If we're uninstalling the package, check if any dependents are registered. + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) + { + // Build up a list of dependents to ignore, including the current bundle. + hr = GetIgnoredDependents(pPackage, pPlan, &sdIgnoredDependents); + ExitOnFailure(hr, "Failed to build the list of ignored dependents."); + + // Skip the dependency check if "ALL" was authored for IGNOREDEPENDENCIES. + hr = DictKeyExists(sdIgnoredDependents, L"ALL"); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to check if \"ALL\" was set in IGNOREDEPENDENCIES."); + } + else + { + hr = S_OK; + + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + i; + + for (DWORD j = 0; j < pProvider->cDependents; ++j) + { + const DEPENDENCY* pDependency = pProvider->rgDependents + j; + + hr = DictKeyExists(sdIgnoredDependents, pDependency->sczKey); + if (E_NOTFOUND == hr) + { + hr = S_OK; + + if (!pPackage->fDependencyManagerWasHere) + { + pPackage->fDependencyManagerWasHere = TRUE; + + LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS, pPackage->sczId); + } + + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_DEPENDENT, pDependency->sczKey, LoggingStringOrUnknownIfNull(pDependency->sczName)); + } + ExitOnFailure(hr, "Failed to check the dictionary of ignored dependents."); + } + } + } + } + + // Calculate the dependency actions before the package itself is planned. + CalculateDependencyActionStates(pPackage, pPlan->action, &dependencyExecuteAction, &dependencyRollbackAction); + + // If dependents were found, change the action to not uninstall the package. + if (pPackage->fDependencyManagerWasHere) + { + pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; + pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + } + else + { + // Use the calculated dependency actions as the provider actions if there + // are any non-imported providers that need to be registered and the package + // is current (not obsolete). + if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE != pPackage->currentState) + { + BOOL fAllImportedProviders = TRUE; // assume all providers were imported. + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + if (!pProvider->fImported) + { + fAllImportedProviders = FALSE; + break; + } + } + + if (!fAllImportedProviders) + { + pPackage->providerExecute = dependencyExecuteAction; + pPackage->providerRollback = dependencyRollbackAction; + } + } + + // If the package will be removed, add its providers to the growing list in the plan. + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) + { + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + + hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, NULL); + ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey); + } + } + } + + pPackage->dependencyExecute = dependencyExecuteAction; + pPackage->dependencyRollback = dependencyRollbackAction; + +LExit: + ReleaseDict(sdIgnoredDependents); + + return hr; +} + +extern "C" HRESULT DependencyPlanPackage( + __in_opt DWORD *pdwInsertSequence, + __in const BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* pAction = NULL; + + // If the dependency execution action is to unregister, add the dependency actions to the plan + // *before* the provider key is potentially removed. + if (BURN_DEPENDENCY_ACTION_UNREGISTER == pPackage->dependencyExecute) + { + hr = AddPackageDependencyActions(pdwInsertSequence, pPackage, pPlan, pPackage->dependencyExecute, pPackage->dependencyRollback); + ExitOnFailure(hr, "Failed to plan the dependency actions for package: %ls", pPackage->sczId); + } + + // Add the provider rollback plan. + if (BURN_DEPENDENCY_ACTION_NONE != pPackage->providerRollback) + { + hr = PlanAppendRollbackAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append provider rollback action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER; + pAction->packageProvider.pPackage = const_cast(pPackage); + pAction->packageProvider.action = pPackage->providerRollback; + + // Put a checkpoint before the execute action so that rollback happens + // if execute fails. + hr = PlanExecuteCheckpoint(pPlan); + ExitOnFailure(hr, "Failed to plan provider checkpoint action."); + } + + // Add the provider execute plan. This comes after rollback so if something goes wrong + // rollback will try to clean up after us. + if (BURN_DEPENDENCY_ACTION_NONE != pPackage->providerExecute) + { + if (NULL != pdwInsertSequence) + { + hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction); + ExitOnFailure(hr, "Failed to insert provider execute action."); + + // Always move the sequence after this dependency action so the provider registration + // stays in front of the inserted actions. + ++(*pdwInsertSequence); + } + else + { + hr = PlanAppendExecuteAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append provider execute action."); + } + + pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER; + pAction->packageProvider.pPackage = const_cast(pPackage); + pAction->packageProvider.action = pPackage->providerExecute; + } + +LExit: + return hr; +} + +extern "C" HRESULT DependencyPlanPackageComplete( + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan + ) +{ + HRESULT hr = S_OK; + + // Registration of dependencies happens here, after the package is planned to be + // installed and all that good stuff. + if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute) + { + // Recalculate the dependency actions in case other operations may have changed + // the package execution state. + CalculateDependencyActionStates(pPackage, pPlan->action, &pPackage->dependencyExecute, &pPackage->dependencyRollback); + + // If the dependency execution action is *still* to register, add the dependency actions to the plan. + if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute) + { + hr = AddPackageDependencyActions(NULL, pPackage, pPlan, pPackage->dependencyExecute, pPackage->dependencyRollback); + ExitOnFailure(hr, "Failed to plan the dependency actions for package: %ls", pPackage->sczId); + } + } + +LExit: + return hr; +} + +extern "C" HRESULT DependencyExecutePackageProviderAction( + __in const BURN_EXECUTE_ACTION* pAction + ) +{ + AssertSz(BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER == pAction->type, "Execute action type not supported by this function."); + + HRESULT hr = S_OK; + const BURN_PACKAGE* pPackage = pAction->packageProvider.pPackage; + + // Register or unregister the package provider(s). + if (BURN_DEPENDENCY_ACTION_REGISTER == pAction->packageProvider.action) + { + hr = RegisterPackageProvider(pPackage); + ExitOnFailure(hr, "Failed to register the package providers."); + } + else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pAction->packageProvider.action) + { + UnregisterPackageProvider(pPackage); + } + +LExit: + if (!pPackage->fVital) + { + hr = S_OK; + } + + return hr; +} + +extern "C" HRESULT DependencyExecutePackageDependencyAction( + __in BOOL fPerMachine, + __in const BURN_EXECUTE_ACTION* pAction + ) +{ + AssertSz(BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY == pAction->type, "Execute action type not supported by this function."); + + HRESULT hr = S_OK; + const BURN_PACKAGE* pPackage = pAction->packageDependency.pPackage; + + // Register or unregister the bundle as a dependent of each package dependency provider. + if (BURN_DEPENDENCY_ACTION_REGISTER == pAction->packageDependency.action) + { + hr = RegisterPackageDependency(fPerMachine, pPackage, pAction->packageDependency.sczBundleProviderKey); + ExitOnFailure(hr, "Failed to register the dependency on the package provider."); + } + else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pAction->packageDependency.action) + { + UnregisterPackageDependency(fPerMachine, pPackage, pAction->packageDependency.sczBundleProviderKey); + } + +LExit: + if (!pPackage->fVital) + { + hr = S_OK; + } + + return hr; +} + +extern "C" HRESULT DependencyRegisterBundle( + __in const BURN_REGISTRATION* pRegistration + ) +{ + HRESULT hr = S_OK; + + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_REGISTER, pRegistration->sczProviderKey, pRegistration->pVersion->sczVersion); + + // Register the bundle provider key. + hr = DepRegisterDependency(pRegistration->hkRoot, pRegistration->sczProviderKey, pRegistration->pVersion->sczVersion, pRegistration->sczDisplayName, pRegistration->sczId, 0); + ExitOnFailure(hr, "Failed to register the bundle dependency provider."); + +LExit: + return hr; +} + +extern "C" HRESULT DependencyProcessDependentRegistration( + __in const BURN_REGISTRATION* pRegistration, + __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction + ) +{ + HRESULT hr = S_OK; + + switch (pAction->type) + { + case BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER: + hr = DepRegisterDependent(pRegistration->hkRoot, pRegistration->sczProviderKey, pAction->sczDependentProviderKey, NULL, NULL, 0); + ExitOnFailure(hr, "Failed to register dependent: %ls", pAction->sczDependentProviderKey); + break; + + case BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER: + hr = DepUnregisterDependent(pRegistration->hkRoot, pRegistration->sczProviderKey, pAction->sczDependentProviderKey); + ExitOnFailure(hr, "Failed to unregister dependent: %ls", pAction->sczDependentProviderKey); + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Unrecognized registration action type: %d", pAction->type); + } + +LExit: + return hr; +} + +extern "C" void DependencyUnregisterBundle( + __in const BURN_REGISTRATION* pRegistration, + __in const BURN_PACKAGES* pPackages + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzDependentProviderKey = pRegistration->sczId; + + // Remove the bundle provider key. + hr = DepUnregisterDependency(pRegistration->hkRoot, pRegistration->sczProviderKey); + if (SUCCEEDED(hr)) + { + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_UNREGISTERED, pRegistration->sczProviderKey); + } + else if (FAILED(hr) && E_FILENOTFOUND != hr) + { + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_UNREGISTERED_FAILED, pRegistration->sczProviderKey, hr); + } + + // Best effort to make sure this bundle is not registered as a dependent for anything. + for (DWORD i = 0; i < pPackages->cPackages; ++i) + { + const BURN_PACKAGE* pPackage = pPackages->rgPackages + i; + UnregisterPackageDependency(pPackage->fPerMachine, pPackage, wzDependentProviderKey); + } + + for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) + { + const BURN_PACKAGE* pPackage = &pRegistration->relatedBundles.rgRelatedBundles[i].package; + UnregisterPackageDependency(pPackage->fPerMachine, pPackage, wzDependentProviderKey); + } +} + +// internal functions + + +static HRESULT DetectPackageDependents( + __in BURN_PACKAGE* pPackage, + __in STRINGDICT_HANDLE sdIgnoredDependents, + __in const BURN_REGISTRATION* pRegistration + ) +{ + HRESULT hr = S_OK; + HKEY hkHive = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + BOOL fCanIgnorePresence = pPackage->fCanAffectRegistration && 0 < pPackage->cDependencyProviders && + (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState || BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState); + BOOL fBundleRegisteredAsDependent = FALSE; + + // There's currently no point in getting the dependents if the scope doesn't match, + // because they will just get ignored. + if (pRegistration->fPerMachine != pPackage->fPerMachine) + { + ExitFunction(); + } + + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + + hr = DepCheckDependents(hkHive, pProvider->sczKey, 0, sdIgnoredDependents, &pProvider->rgDependents, &pProvider->cDependents); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed dependents check on package provider: %ls", pProvider->sczKey); + + if (!pPackage->fPackageProviderExists && (0 < pProvider->cDependents || GetProviderExists(hkHive, pProvider->sczKey))) + { + pPackage->fPackageProviderExists = TRUE; + } + + if (fCanIgnorePresence && !fBundleRegisteredAsDependent) + { + for (DWORD iDependent = 0; iDependent < pProvider->cDependents; ++iDependent) + { + DEPENDENCY* pDependent = pProvider->rgDependents + iDependent; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczId, -1, pDependent->sczKey, -1)) + { + fBundleRegisteredAsDependent = TRUE; + break; + } + } + } + } + else + { + hr = S_OK; + + if (!pPackage->fPackageProviderExists && GetProviderExists(hkHive, pProvider->sczKey)) + { + pPackage->fPackageProviderExists = TRUE; + } + } + } + + // Older bundles may not have written the id so try the default. + if (!pPackage->fPackageProviderExists && BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.sczProductCode && GetProviderExists(hkHive, pPackage->Msi.sczProductCode)) + { + pPackage->fPackageProviderExists = TRUE; + } + + if (fCanIgnorePresence && !fBundleRegisteredAsDependent) + { + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState) + { + pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState) + { + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; + + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pTargetProduct->registrationState) + { + pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + } + } + } + +LExit: + return hr; +} + +/******************************************************************** + SplitIgnoreDependencies - Splits a semicolon-delimited + string into a list of unique dependencies to ignore. + +*********************************************************************/ +static HRESULT SplitIgnoreDependencies( + __in_z LPCWSTR wzIgnoreDependencies, + __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, + __inout LPUINT pcDependencies, + __out BOOL* pfIgnoreAll + ) +{ + HRESULT hr = S_OK; + LPWSTR wzContext = NULL; + STRINGDICT_HANDLE sdIgnoreDependencies = NULL; + *pfIgnoreAll = FALSE; + + // Create a dictionary to hold unique dependencies. + hr = DictCreateStringList(&sdIgnoreDependencies, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create the string dictionary."); + + // Parse through the semicolon-delimited tokens and add to the array. + for (LPCWSTR wzToken = ::wcstok_s(const_cast(wzIgnoreDependencies), vcszIgnoreDependenciesDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, vcszIgnoreDependenciesDelim, &wzContext)) + { + hr = DictKeyExists(sdIgnoreDependencies, wzToken); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies."); + } + else + { + hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzToken, NULL); + ExitOnFailure(hr, "Failed to add \"%ls\" to the list of dependencies to ignore.", wzToken); + + hr = DictAddKey(sdIgnoreDependencies, wzToken); + ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzToken); + + if (!*pfIgnoreAll && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, L"ALL", -1, wzToken, -1)) + { + *pfIgnoreAll = TRUE; + } + } + } + +LExit: + ReleaseDict(sdIgnoreDependencies); + + return hr; +} + +/******************************************************************** + JoinIgnoreDependencies - Joins a list of dependencies + to ignore into a semicolon-delimited string of unique values. + +*********************************************************************/ +static HRESULT JoinIgnoreDependencies( + __out_z LPWSTR* psczIgnoreDependencies, + __in_ecount(cDependencies) const DEPENDENCY* rgDependencies, + __in UINT cDependencies + ) +{ + HRESULT hr = S_OK; + STRINGDICT_HANDLE sdIgnoreDependencies = NULL; + + // Make sure we pass back an empty string if there are no dependencies. + if (0 == cDependencies) + { + ExitFunction1(hr = S_OK); + } + + // Create a dictionary to hold unique dependencies. + hr = DictCreateStringList(&sdIgnoreDependencies, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create the string dictionary."); + + for (UINT i = 0; i < cDependencies; ++i) + { + const DEPENDENCY* pDependency = &rgDependencies[i]; + + hr = DictKeyExists(sdIgnoreDependencies, pDependency->sczKey); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies."); + } + else + { + if (0 < i) + { + hr = StrAllocConcat(psczIgnoreDependencies, vcszIgnoreDependenciesDelim, 1); + ExitOnFailure(hr, "Failed to append the string delimiter."); + } + + hr = StrAllocConcat(psczIgnoreDependencies, pDependency->sczKey, 0); + ExitOnFailure(hr, "Failed to append the key \"%ls\".", pDependency->sczKey); + + hr = DictAddKey(sdIgnoreDependencies, pDependency->sczKey); + ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", pDependency->sczKey); + } + } + +LExit: + ReleaseDict(sdIgnoreDependencies); + + return hr; +} + +/******************************************************************** + GetIgnoredDependents - Combines the current bundle's + provider key, packages' provider keys that are being uninstalled, + and any ignored dependencies authored for packages into a string + list to pass to deputil. + +*********************************************************************/ +static HRESULT GetIgnoredDependents( + __in const BURN_PACKAGE* pPackage, + __in const BURN_PLAN* pPlan, + __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents + ) +{ + HRESULT hr = S_OK; + LPWSTR sczIgnoreDependencies = NULL; + + // Create the dictionary and add the bundle provider key initially. + hr = DictCreateStringList(psdIgnoredDependents, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create the string dictionary."); + + hr = DictAddKey(*psdIgnoredDependents, pPlan->wzBundleProviderKey); + ExitOnFailure(hr, "Failed to add the bundle provider key \"%ls\" to the list of ignored dependencies.", pPlan->wzBundleProviderKey); + + // Add previously planned package providers to the dictionary. + for (DWORD i = 0; i < pPlan->cPlannedProviders; ++i) + { + const DEPENDENCY* pDependency = &pPlan->rgPlannedProviders[i]; + + hr = DictAddKey(*psdIgnoredDependents, pDependency->sczKey); + ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the list of ignored dependencies.", pDependency->sczKey); + } + + // Get the IGNOREDEPENDENCIES property if defined. + hr = PackageGetProperty(pPackage, DEPENDENCY_IGNOREDEPENDENCIES, &sczIgnoreDependencies); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get the package property: %ls", DEPENDENCY_IGNOREDEPENDENCIES); + + hr = DependencyAddIgnoreDependencies(*psdIgnoredDependents, sczIgnoreDependencies); + ExitOnFailure(hr, "Failed to add the authored ignored dependencies to the cumulative list of ignored dependencies."); + } + else + { + hr = S_OK; + } + +LExit: + ReleaseStr(sczIgnoreDependencies); + + return hr; +} + +/******************************************************************** + GetProviderExists - Gets whether the provider key is registered. + +*********************************************************************/ +static BOOL GetProviderExists( + __in HKEY hkRoot, + __in_z LPCWSTR wzProviderKey + ) +{ + HRESULT hr = DepGetProviderInformation(hkRoot, wzProviderKey, NULL, NULL, NULL); + return SUCCEEDED(hr); +} + +/******************************************************************** + CalculateDependencyActionStates - Calculates the dependency execute and + rollback actions for a package. + +*********************************************************************/ +static void CalculateDependencyActionStates( + __in const BURN_PACKAGE* pPackage, + __in const BOOTSTRAPPER_ACTION action, + __out BURN_DEPENDENCY_ACTION* pDependencyExecuteAction, + __out BURN_DEPENDENCY_ACTION* pDependencyRollbackAction + ) +{ + switch (action) + { + case BOOTSTRAPPER_ACTION_UNINSTALL: + // Always remove the dependency when uninstalling a bundle even if the package is absent. + *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_UNREGISTER; + break; + case BOOTSTRAPPER_ACTION_INSTALL: __fallthrough; + case BOOTSTRAPPER_ACTION_CACHE: + // Always remove the dependency during rollback when installing a bundle. + *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_UNREGISTER; + __fallthrough; + case BOOTSTRAPPER_ACTION_MODIFY: __fallthrough; + case BOOTSTRAPPER_ACTION_REPAIR: + switch (pPackage->execute) + { + case BOOTSTRAPPER_ACTION_STATE_NONE: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_NONE: + // Register if a newer, compatible package is already installed. + switch (pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: + if (!pPackage->fPackageProviderExists) + { + break; + } + __fallthrough; + case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: + *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER; + break; + } + break; + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_MEND: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + // Register if the package is requested but already installed. + switch (pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: + if (!pPackage->fPackageProviderExists) + { + break; + } + __fallthrough; + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: + *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER; + break; + } + break; + } + break; + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: + *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_UNREGISTER; + break; + case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough; + case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough; + case BOOTSTRAPPER_ACTION_STATE_MEND: __fallthrough; + case BOOTSTRAPPER_ACTION_STATE_REPAIR: __fallthrough; + case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: __fallthrough; + *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER; + break; + } + break; + } + + switch (*pDependencyExecuteAction) + { + case BURN_DEPENDENCY_ACTION_REGISTER: + switch (pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough; + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough; + *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_UNREGISTER; + break; + } + break; + case BURN_DEPENDENCY_ACTION_UNREGISTER: + switch (pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: + *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_REGISTER; + break; + } + break; + } +} + +/******************************************************************** + AddPackageDependencyActions - Adds the dependency execute and rollback + actions to the plan. + +*********************************************************************/ +static HRESULT AddPackageDependencyActions( + __in_opt DWORD *pdwInsertSequence, + __in const BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in const BURN_DEPENDENCY_ACTION dependencyExecuteAction, + __in const BURN_DEPENDENCY_ACTION dependencyRollbackAction + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* pAction = NULL; + + // Add the rollback plan. + if (BURN_DEPENDENCY_ACTION_NONE != dependencyRollbackAction) + { + hr = PlanAppendRollbackAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append rollback action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY; + pAction->packageDependency.pPackage = const_cast(pPackage); + pAction->packageDependency.action = dependencyRollbackAction; + + hr = StrAllocString(&pAction->packageDependency.sczBundleProviderKey, pPlan->wzBundleProviderKey, 0); + ExitOnFailure(hr, "Failed to copy the bundle dependency provider."); + + // Put a checkpoint before the execute action so that rollback happens + // if execute fails. + hr = PlanExecuteCheckpoint(pPlan); + ExitOnFailure(hr, "Failed to plan dependency checkpoint action."); + } + + // Add the execute plan. This comes after rollback so if something goes wrong + // rollback will try to clean up after us correctly. + if (BURN_DEPENDENCY_ACTION_NONE != dependencyExecuteAction) + { + if (NULL != pdwInsertSequence) + { + hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction); + ExitOnFailure(hr, "Failed to insert execute action."); + + // Always move the sequence after this dependency action so the dependency registration + // stays in front of the inserted actions. + ++(*pdwInsertSequence); + } + else + { + hr = PlanAppendExecuteAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append execute action."); + } + + pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY; + pAction->packageDependency.pPackage = const_cast(pPackage); + pAction->packageDependency.action = dependencyExecuteAction; + + hr = StrAllocString(&pAction->packageDependency.sczBundleProviderKey, pPlan->wzBundleProviderKey, 0); + ExitOnFailure(hr, "Failed to copy the bundle dependency provider."); + } + +LExit: + return hr; +} + +static HRESULT RegisterPackageProvider( + __in const BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + LPWSTR wzId = NULL; + HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + + if (pPackage->rgDependencyProviders) + { + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + { + wzId = pPackage->Msi.sczProductCode; + } + else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + wzId = pPackage->Msp.sczPatchCode; + } + + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + + if (!pProvider->fImported) + { + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_REGISTER, pProvider->sczKey, pProvider->sczVersion, pPackage->sczId); + + hr = DepRegisterDependency(hkRoot, pProvider->sczKey, pProvider->sczVersion, pProvider->sczDisplayName, wzId, 0); + ExitOnFailure(hr, "Failed to register the package dependency provider: %ls", pProvider->sczKey); + } + } + } + +LExit: + if (!pPackage->fVital) + { + hr = S_OK; + } + + return hr; +} + +/******************************************************************** + UnregisterPackageProvider - Removes each dependency provider + for the package (if not imported from the package itself). + + Note: Does not check for existing dependents before removing the key. +*********************************************************************/ +static void UnregisterPackageProvider( + __in const BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + + if (pPackage->rgDependencyProviders) + { + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + + if (!pProvider->fImported) + { + hr = DepUnregisterDependency(hkRoot, pProvider->sczKey); + if (SUCCEEDED(hr)) + { + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED, pProvider->sczKey, pPackage->sczId); + } + else if (FAILED(hr) && E_FILENOTFOUND != hr) + { + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_FAILED, pProvider->sczKey, pPackage->sczId, hr); + } + } + } + } +} + +/******************************************************************** + RegisterPackageDependency - Registers the provider key + as a dependent of a package. + +*********************************************************************/ +static HRESULT RegisterPackageDependency( + __in BOOL fPerMachine, + __in const BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzDependentProviderKey + ) +{ + HRESULT hr = S_OK; + HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + + // Do not register a dependency on a package in a different install context. + if (fPerMachine != pPackage->fPerMachine) + { + LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine)); + ExitFunction1(hr = S_OK); + } + + if (pPackage->rgDependencyProviders) + { + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_REGISTER_DEPENDENCY, wzDependentProviderKey, pProvider->sczKey, pPackage->sczId); + + hr = DepRegisterDependent(hkRoot, pProvider->sczKey, wzDependentProviderKey, NULL, NULL, 0); + if (E_FILENOTFOUND != hr || pPackage->fVital) + { + ExitOnFailure(hr, "Failed to register the dependency on package dependency provider: %ls", pProvider->sczKey); + } + else + { + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_SKIP_MISSING, pProvider->sczKey, pPackage->sczId); + hr = S_OK; + } + } + } + +LExit: + return hr; +} + +/******************************************************************** + UnregisterPackageDependency - Unregisters the provider key + as a dependent of a package. + +*********************************************************************/ +static void UnregisterPackageDependency( + __in BOOL fPerMachine, + __in const BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzDependentProviderKey + ) +{ + HRESULT hr = S_OK; + HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + + // Should be no registration to remove since we don't write keys across contexts. + if (fPerMachine != pPackage->fPerMachine) + { + LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine)); + return; + } + + // Loop through each package provider and remove the bundle dependency key. + if (pPackage->rgDependencyProviders) + { + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; + + hr = DepUnregisterDependent(hkRoot, pProvider->sczKey, wzDependentProviderKey); + if (SUCCEEDED(hr)) + { + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY, wzDependentProviderKey, pProvider->sczKey, pPackage->sczId); + } + else if (FAILED(hr) && E_FILENOTFOUND != hr) + { + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY_FAILED, wzDependentProviderKey, pProvider->sczKey, pPackage->sczId, hr); + } + } + } +} diff --git a/src/burn/engine/dependency.h b/src/burn/engine/dependency.h new file mode 100644 index 00000000..06a01a20 --- /dev/null +++ b/src/burn/engine/dependency.h @@ -0,0 +1,168 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +// constants + +const LPCWSTR DEPENDENCY_IGNOREDEPENDENCIES = L"IGNOREDEPENDENCIES"; + + +// function declarations + +/******************************************************************** + DependencyUninitializeProvider - Frees and zeros memory allocated in + the dependency provider. + +*********************************************************************/ +void DependencyUninitializeProvider( + __in BURN_DEPENDENCY_PROVIDER* pProvider + ); + +/******************************************************************** + DependencyParseProvidersFromXml - Parses dependency information + from the manifest for the specified package. + +*********************************************************************/ +HRESULT DependencyParseProvidersFromXml( + __in BURN_PACKAGE* pPackage, + __in IXMLDOMNode* pixnPackage + ); + +HRESULT DependencyInitialize( + __in BURN_REGISTRATION* pRegistration, + __in_z_opt LPCWSTR wzIgnoreDependencies + ); + +/******************************************************************** + DependencyDetectProviderKeyBundleId - Detect if the provider key is + registered and if so what bundle is registered. + + Note: Returns E_NOTFOUND if the provider key is not registered. +*********************************************************************/ +HRESULT DependencyDetectProviderKeyBundleId( + __in BURN_REGISTRATION* pRegistration + ); + +/******************************************************************** + DependencyDetect - Detects dependency information. + +*********************************************************************/ +HRESULT DependencyDetect( + __in BURN_ENGINE_STATE* pEngineState + ); + +/******************************************************************** + DependencyPlanInitialize - Initializes the plan. + +*********************************************************************/ +HRESULT DependencyPlanInitialize( + __in const BURN_REGISTRATION* pRegistration, + __in BURN_PLAN* pPlan + ); + +/******************************************************************** + DependencyAllocIgnoreDependencies - Allocates the dependencies to + ignore as a semicolon-delimited string. + +*********************************************************************/ +HRESULT DependencyAllocIgnoreDependencies( + __in const BURN_PLAN *pPlan, + __out_z LPWSTR* psczIgnoreDependencies + ); + +/******************************************************************** + DependencyAddIgnoreDependencies - Populates the ignore dependency + names. + +*********************************************************************/ +HRESULT DependencyAddIgnoreDependencies( + __in STRINGDICT_HANDLE sdIgnoreDependencies, + __in_z LPCWSTR wzAddIgnoreDependencies + ); + +/******************************************************************** + DependencyPlanPackageBegin - Updates the dependency registration + action depending on the calculated state for the package. + +*********************************************************************/ +HRESULT DependencyPlanPackageBegin( + __in BOOL fPerMachine, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan + ); + +/******************************************************************** + DependencyPlanPackage - adds dependency related actions to the plan + for this package. + +*********************************************************************/ +HRESULT DependencyPlanPackage( + __in_opt DWORD *pdwInsertSequence, + __in const BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan + ); + +/******************************************************************** + DependencyPlanPackageComplete - Updates the dependency registration + action depending on the planned action for the package. + +*********************************************************************/ +HRESULT DependencyPlanPackageComplete( + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan + ); + +/******************************************************************** + DependencyExecutePackageProviderAction - Registers or unregisters + provider information for the package contained within the action. + +*********************************************************************/ +HRESULT DependencyExecutePackageProviderAction( + __in const BURN_EXECUTE_ACTION* pAction + ); + +/******************************************************************** + DependencyExecutePackageDependencyAction - Registers or unregisters + dependency information for the package contained within the action. + +*********************************************************************/ +HRESULT DependencyExecutePackageDependencyAction( + __in BOOL fPerMachine, + __in const BURN_EXECUTE_ACTION* pAction + ); + +/******************************************************************** + DependencyRegisterBundle - Registers the bundle dependency provider. + +*********************************************************************/ +HRESULT DependencyRegisterBundle( + __in const BURN_REGISTRATION* pRegistration + ); + +/******************************************************************** + DependencyProcessDependentRegistration - Registers or unregisters dependents + on the bundle based on the action. + +*********************************************************************/ +HRESULT DependencyProcessDependentRegistration( + __in const BURN_REGISTRATION* pRegistration, + __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction + ); + +/******************************************************************** + DependencyUnregisterBundle - Removes the bundle dependency provider. + + Note: Does not check for existing dependents before removing the key. +*********************************************************************/ +void DependencyUnregisterBundle( + __in const BURN_REGISTRATION* pRegistration, + __in const BURN_PACKAGES* pPackages + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/detect.cpp b/src/burn/engine/detect.cpp new file mode 100644 index 00000000..dc35e747 --- /dev/null +++ b/src/burn/engine/detect.cpp @@ -0,0 +1,469 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +typedef struct _DETECT_AUTHENTICATION_REQUIRED_DATA +{ + BURN_USER_EXPERIENCE* pUX; + LPCWSTR wzPackageOrContainerId; +} DETECT_AUTHENTICATION_REQUIRED_DATA; + +// internal function definitions +static HRESULT WINAPI AuthenticationRequired( + __in LPVOID pData, + __in HINTERNET hUrl, + __in long lHttpCode, + __out BOOL* pfRetrySend, + __out BOOL* pfRetry + ); + +static HRESULT DetectAtomFeedUpdate( + __in_z LPCWSTR wzBundleId, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_UPDATE* pUpdate + ); + +static HRESULT DownloadUpdateFeed( + __in_z LPCWSTR wzBundleId, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_UPDATE* pUpdate, + __deref_inout_z LPWSTR* psczTempFile + ); + +// function definitions + +extern "C" void DetectReset( + __in BURN_REGISTRATION* pRegistration, + __in BURN_PACKAGES* pPackages + ) +{ + RelatedBundlesUninitialize(&pRegistration->relatedBundles); + ReleaseNullStr(pRegistration->sczDetectedProviderKeyBundleId); + pRegistration->fSelfRegisteredAsDependent = FALSE; + pRegistration->fParentRegisteredAsDependent = FALSE; + pRegistration->fForwardCompatibleBundleExists = FALSE; + pRegistration->fEligibleForCleanup = FALSE; + + if (pRegistration->rgIgnoredDependencies) + { + ReleaseDependencyArray(pRegistration->rgIgnoredDependencies, pRegistration->cIgnoredDependencies); + } + pRegistration->rgIgnoredDependencies = NULL; + pRegistration->cIgnoredDependencies = 0; + + if (pRegistration->rgDependents) + { + ReleaseDependencyArray(pRegistration->rgDependents, pRegistration->cDependents); + } + pRegistration->rgDependents = NULL; + pRegistration->cDependents = 0; + + for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage) + { + BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage; + + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; + pPackage->fPackageProviderExists = FALSE; + pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; + + pPackage->fCached = FALSE; + + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + { + for (DWORD iFeature = 0; iFeature < pPackage->Msi.cFeatures; ++iFeature) + { + BURN_MSIFEATURE* pFeature = pPackage->Msi.rgFeatures + iFeature; + + pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; + } + + for (DWORD iSlipstreamMsp = 0; iSlipstreamMsp < pPackage->Msi.cSlipstreamMspPackages; ++iSlipstreamMsp) + { + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + iSlipstreamMsp; + + pSlipstreamMsp->dwMsiChainedPatchIndex = BURN_PACKAGE_INVALID_PATCH_INDEX; + } + + ReleaseNullMem(pPackage->Msi.rgChainedPatches); + pPackage->Msi.cChainedPatches = 0; + } + else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + ReleaseNullMem(pPackage->Msp.rgTargetProducts); + pPackage->Msp.cTargetProductCodes = 0; + } + + for (DWORD iProvider = 0; iProvider < pPackage->cDependencyProviders; ++iProvider) + { + BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + iProvider; + + if (pProvider->rgDependents) + { + ReleaseDependencyArray(pProvider->rgDependents, pProvider->cDependents); + } + pProvider->rgDependents = NULL; + pProvider->cDependents = 0; + } + } + + for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo) + { + MSIPATCHSEQUENCEINFOW* pPatchInfo = pPackages->rgPatchInfo + iPatchInfo; + pPatchInfo->dwOrder = 0; + pPatchInfo->uStatus = 0; + } +} + +extern "C" HRESULT DetectForwardCompatibleBundles( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_REGISTRATION* pRegistration + ) +{ + HRESULT hr = S_OK; + int nCompareResult = 0; + + if (pRegistration->sczDetectedProviderKeyBundleId && + CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRegistration->sczId, -1)) + { + for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) + { + BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle; + + if (BOOTSTRAPPER_RELATION_UPGRADE == pRelatedBundle->relationType && + CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRelatedBundle->package.sczId, -1)) + { + hr = VerCompareParsedVersions(pRegistration->pVersion, pRelatedBundle->pVersion, &nCompareResult); + ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistration->pVersion->sczVersion, pRelatedBundle->pVersion->sczVersion); + + if (nCompareResult <= 0) + { + if (pRelatedBundle->fPlannable) + { + pRelatedBundle->fForwardCompatible = TRUE; + pRegistration->fForwardCompatibleBundleExists = TRUE; + } + + hr = UserExperienceOnDetectForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, !pRelatedBundle->package.fCached); + ExitOnRootFailure(hr, "BA aborted detect forward compatible bundle."); + + LogId(REPORT_STANDARD, MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingBoolToString(pRelatedBundle->package.fCached)); + } + } + } + } + +LExit: + return hr; +} + +extern "C" HRESULT DetectReportRelatedBundles( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_REGISTRATION* pRegistration, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in BOOTSTRAPPER_ACTION action, + __out BOOL* pfEligibleForCleanup + ) +{ + HRESULT hr = S_OK; + int nCompareResult = 0; + BOOTSTRAPPER_REQUEST_STATE uninstallRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; + *pfEligibleForCleanup = pRegistration->fInstalled || pRegistration->fCached; + + for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) + { + const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle; + BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE; + + switch (pRelatedBundle->relationType) + { + case BOOTSTRAPPER_RELATION_UPGRADE: + if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < action) + { + hr = VerCompareParsedVersions(pRegistration->pVersion, pRelatedBundle->pVersion, &nCompareResult); + ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistration->pVersion->sczVersion, pRelatedBundle->pVersion->sczVersion); + + if (nCompareResult < 0) + { + operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; + } + else + { + operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE; + } + } + break; + + case BOOTSTRAPPER_RELATION_PATCH: __fallthrough; + case BOOTSTRAPPER_RELATION_ADDON: + if (BOOTSTRAPPER_ACTION_UNINSTALL == action) + { + operation = BOOTSTRAPPER_RELATED_OPERATION_REMOVE; + } + else if (BOOTSTRAPPER_ACTION_INSTALL == action || BOOTSTRAPPER_ACTION_MODIFY == action) + { + operation = BOOTSTRAPPER_RELATED_OPERATION_INSTALL; + } + else if (BOOTSTRAPPER_ACTION_REPAIR == action) + { + operation = BOOTSTRAPPER_RELATED_OPERATION_REPAIR; + } + break; + + case BOOTSTRAPPER_RELATION_DETECT: __fallthrough; + case BOOTSTRAPPER_RELATION_DEPENDENT: + break; + + default: + hr = E_FAIL; + ExitOnRootFailure(hr, "Unexpected relation type encountered: %d", pRelatedBundle->relationType); + break; + } + + LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingRelatedOperationToString(operation), LoggingBoolToString(pRelatedBundle->package.fCached)); + + hr = UserExperienceOnDetectRelatedBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, operation, !pRelatedBundle->package.fCached); + ExitOnRootFailure(hr, "BA aborted detect related bundle."); + + // For now, if any related bundles will be executed during uninstall by default then never automatically clean up the bundle. + if (*pfEligibleForCleanup && pRelatedBundle->fPlannable) + { + uninstallRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; + hr = PlanDefaultRelatedBundleRequestState(relationType, pRelatedBundle->relationType, BOOTSTRAPPER_ACTION_UNINSTALL, pRegistration->pVersion, pRelatedBundle->pVersion, &uninstallRequestState); + ExitOnFailure(hr, "Failed to get the default request state for related bundle for calculating fEligibleForCleanup"); + + if (BOOTSTRAPPER_REQUEST_STATE_NONE != uninstallRequestState) + { + *pfEligibleForCleanup = FALSE; + } + } + } + +LExit: + return hr; +} + +extern "C" HRESULT DetectUpdate( + __in_z LPCWSTR wzBundleId, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_UPDATE* pUpdate + ) +{ + HRESULT hr = S_OK; + BOOL fBeginCalled = FALSE; + BOOL fSkip = TRUE; + BOOL fIgnoreError = FALSE; + LPWSTR sczOriginalSource = NULL; + + // If no update source was specified, skip update detection. + if (!pUpdate->sczUpdateSource || !*pUpdate->sczUpdateSource) + { + ExitFunction(); + } + + fBeginCalled = TRUE; + + hr = StrAllocString(&sczOriginalSource, pUpdate->sczUpdateSource, 0); + ExitOnFailure(hr, "Failed to duplicate update feed source."); + + hr = UserExperienceOnDetectUpdateBegin(pUX, sczOriginalSource, &fSkip); + ExitOnRootFailure(hr, "BA aborted detect update begin."); + + if (!fSkip) + { + hr = DetectAtomFeedUpdate(wzBundleId, pUX, pUpdate); + ExitOnFailure(hr, "Failed to detect atom feed update."); + } + +LExit: + ReleaseStr(sczOriginalSource); + + if (fBeginCalled) + { + UserExperienceOnDetectUpdateComplete(pUX, hr, &fIgnoreError); + if (fIgnoreError) + { + hr = S_OK; + } + } + + return hr; +} + +static HRESULT WINAPI AuthenticationRequired( + __in LPVOID pData, + __in HINTERNET hUrl, + __in long lHttpCode, + __out BOOL* pfRetrySend, + __out BOOL* pfRetry + ) +{ + Assert(401 == lHttpCode || 407 == lHttpCode); + + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + BOOTSTRAPPER_ERROR_TYPE errorType = (401 == lHttpCode) ? BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER : BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY; + LPWSTR sczError = NULL; + DETECT_AUTHENTICATION_REQUIRED_DATA* pAuthenticationData = reinterpret_cast(pData); + int nResult = IDNOACTION; + + *pfRetrySend = FALSE; + *pfRetry = FALSE; + + hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL); + ExitOnFailure(hr, "Failed to allocation error string."); + + UserExperienceOnError(pAuthenticationData->pUX, errorType, pAuthenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value. + nResult = UserExperienceCheckExecuteResult(pAuthenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult); + if (IDTRYAGAIN == nResult && pAuthenticationData->pUX->hwndDetect) + { + er = ::InternetErrorDlg(pAuthenticationData->pUX->hwndDetect, hUrl, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL); + if (ERROR_SUCCESS == er || ERROR_CANCELLED == er) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + else if (ERROR_INTERNET_FORCE_RETRY == er) + { + *pfRetrySend = TRUE; + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); + } + } + else if (IDRETRY == nResult) + { + *pfRetry = TRUE; + hr = S_OK; + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); + } + +LExit: + ReleaseStr(sczError); + + return hr; +} + +static HRESULT DownloadUpdateFeed( + __in_z LPCWSTR wzBundleId, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_UPDATE* pUpdate, + __deref_inout_z LPWSTR* psczTempFile + ) +{ + HRESULT hr = S_OK; + DOWNLOAD_SOURCE downloadSource = { }; + DOWNLOAD_CACHE_CALLBACK cacheCallback = { }; + DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { }; + DETECT_AUTHENTICATION_REQUIRED_DATA authenticationData = { }; + LPWSTR sczUpdateId = NULL; + LPWSTR sczError = NULL; + DWORD64 qwDownloadSize = 0; + + // Always do our work in the working folder, even if cached. + hr = PathCreateTimeBasedTempFile(NULL, L"UpdateFeed", NULL, L"xml", psczTempFile, NULL); + ExitOnFailure(hr, "Failed to create UpdateFeed based on current system time."); + + // Do we need a means of the BA to pass in a user name and password? If so, we should copy it to downloadSource here + hr = StrAllocString(&downloadSource.sczUrl, pUpdate->sczUpdateSource, 0); + ExitOnFailure(hr, "Failed to copy update url."); + + cacheCallback.pfnProgress = NULL; //UpdateProgressRoutine; + cacheCallback.pfnCancel = NULL; // TODO: set this + cacheCallback.pv = NULL; //pProgress; + + authenticationData.pUX = pUX; + authenticationData.wzPackageOrContainerId = wzBundleId; + + authenticationCallback.pv = static_cast(&authenticationData); + authenticationCallback.pfnAuthenticate = &AuthenticationRequired; + + hr = DownloadUrl(&downloadSource, qwDownloadSize, *psczTempFile, &cacheCallback, &authenticationCallback); + ExitOnFailure(hr, "Failed attempt to download update feed from URL: '%ls' to: '%ls'", downloadSource.sczUrl, *psczTempFile); + +LExit: + if (FAILED(hr)) + { + if (*psczTempFile) + { + FileEnsureDelete(*psczTempFile); + } + + ReleaseNullStr(*psczTempFile); + } + + ReleaseStr(downloadSource.sczUrl); + ReleaseStr(downloadSource.sczUser); + ReleaseStr(downloadSource.sczPassword); + ReleaseStr(sczUpdateId); + ReleaseStr(sczError); + return hr; +} + + +static HRESULT DetectAtomFeedUpdate( + __in_z LPCWSTR wzBundleId, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_UPDATE* pUpdate + ) +{ + Assert(pUpdate && pUpdate->sczUpdateSource && *pUpdate->sczUpdateSource); +#ifdef DEBUG + LogStringLine(REPORT_STANDARD, "DetectAtomFeedUpdate() - update location: %ls", pUpdate->sczUpdateSource); +#endif + + + HRESULT hr = S_OK; + LPWSTR sczUpdateFeedTempFile = NULL; + ATOM_FEED* pAtomFeed = NULL; + APPLICATION_UPDATE_CHAIN* pApupChain = NULL; + BOOL fStopProcessingUpdates = FALSE; + + hr = AtomInitialize(); + ExitOnFailure(hr, "Failed to initialize Atom."); + + hr = DownloadUpdateFeed(wzBundleId, pUX, pUpdate, &sczUpdateFeedTempFile); + ExitOnFailure(hr, "Failed to download update feed."); + + hr = AtomParseFromFile(sczUpdateFeedTempFile, &pAtomFeed); + ExitOnFailure(hr, "Failed to parse update atom feed: %ls.", sczUpdateFeedTempFile); + + hr = ApupAllocChainFromAtom(pAtomFeed, &pApupChain); + ExitOnFailure(hr, "Failed to allocate update chain from atom feed."); + + if (0 < pApupChain->cEntries) + { + for (DWORD i = 0; i < pApupChain->cEntries; ++i) + { + APPLICATION_UPDATE_ENTRY* pAppUpdateEntry = &pApupChain->rgEntries[i]; + + hr = UserExperienceOnDetectUpdate(pUX, pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->wzUrl : NULL, + pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->dw64Size : 0, + pAppUpdateEntry->pVersion, pAppUpdateEntry->wzTitle, + pAppUpdateEntry->wzSummary, pAppUpdateEntry->wzContentType, pAppUpdateEntry->wzContent, &fStopProcessingUpdates); + ExitOnRootFailure(hr, "BA aborted detect update."); + + if (fStopProcessingUpdates) + { + break; + } + } + } + +LExit: + if (sczUpdateFeedTempFile && *sczUpdateFeedTempFile) + { + FileEnsureDelete(sczUpdateFeedTempFile); + } + + ApupFreeChain(pApupChain); + AtomFreeFeed(pAtomFeed); + ReleaseStr(sczUpdateFeedTempFile); + AtomUninitialize(); + + return hr; +} diff --git a/src/burn/engine/detect.h b/src/burn/engine/detect.h new file mode 100644 index 00000000..9bc34882 --- /dev/null +++ b/src/burn/engine/detect.h @@ -0,0 +1,44 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + + +// structs + + +// functions + +void DetectReset( + __in BURN_REGISTRATION* pRegistration, + __in BURN_PACKAGES* pPackages + ); + +HRESULT DetectForwardCompatibleBundles( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_REGISTRATION* pRegistration + ); + +HRESULT DetectReportRelatedBundles( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_REGISTRATION* pRegistration, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in BOOTSTRAPPER_ACTION action, + __out BOOL* pfEligibleForCleanup + ); + +HRESULT DetectUpdate( + __in_z LPCWSTR wzBundleId, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_UPDATE* pUpdate + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/elevation.cpp b/src/burn/engine/elevation.cpp new file mode 100644 index 00000000..9d1b8fc7 --- /dev/null +++ b/src/burn/engine/elevation.cpp @@ -0,0 +1,3239 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +const DWORD BURN_TIMEOUT = 5 * 60 * 1000; // TODO: is 5 minutes good? + +typedef enum _BURN_ELEVATION_MESSAGE_TYPE +{ + BURN_ELEVATION_MESSAGE_TYPE_UNKNOWN, + BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE, + BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE, + BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN, + BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME, + BURN_ELEVATION_MESSAGE_TYPE_SESSION_END, + BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE, + BURN_ELEVATION_MESSAGE_TYPE_CACHE_COMPLETE_PAYLOAD, + BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_PAYLOAD, + BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP, + BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION, + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE, + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE, + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE, + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE, + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER, + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY, + BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_EMBEDDED_CHILD, + BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE, + BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE, + BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION, + BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION, + BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION, + + BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_BEGIN, + BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_COMPLETE, + BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_BEGIN, + BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_COMPLETE, + BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_BEGIN, + BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_COMPLETE, + BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_SUCCESS, + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS, + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR, + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE, + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE, + BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID, + BURN_ELEVATION_MESSAGE_TYPE_PROGRESS_ROUTINE, +} BURN_ELEVATION_MESSAGE_TYPE; + + +// struct + +typedef struct _BURN_ELEVATION_APPLY_INITIALIZE_MESSAGE_CONTEXT +{ + BURN_USER_EXPERIENCE* pBA; + BOOL fPauseCompleteNeeded; + BOOL fSrpCompleteNeeded; +} BURN_ELEVATION_APPLY_INITIALIZE_MESSAGE_CONTEXT; + +typedef struct _BURN_ELEVATION_CACHE_MESSAGE_CONTEXT +{ + PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler; + LPPROGRESS_ROUTINE pfnProgress; + LPVOID pvContext; +} BURN_ELEVATION_CACHE_MESSAGE_CONTEXT; + +typedef struct _BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT +{ + PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler; + LPVOID pvContext; +} BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT; + +typedef struct _BURN_ELEVATION_MSI_MESSAGE_CONTEXT +{ + PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler; + LPVOID pvContext; +} BURN_ELEVATION_MSI_MESSAGE_CONTEXT; + +typedef struct _BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT +{ + DWORD dwProcessId; +} BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT; + +typedef struct _BURN_ELEVATION_CHILD_MESSAGE_CONTEXT +{ + DWORD dwLoggingTlsId; + HANDLE hPipe; + HANDLE* phLock; + BOOL* pfDisabledAutomaticUpdates; + BURN_APPROVED_EXES* pApprovedExes; + BURN_CONTAINERS* pContainers; + BURN_PACKAGES* pPackages; + BURN_PAYLOADS* pPayloads; + BURN_VARIABLES* pVariables; + BURN_REGISTRATION* pRegistration; + BURN_USER_EXPERIENCE* pUserExperience; +} BURN_ELEVATION_CHILD_MESSAGE_CONTEXT; + + +// internal function declarations + +static DWORD WINAPI ElevatedChildCacheThreadProc( + __in LPVOID lpThreadParameter + ); +static HRESULT WaitForElevatedChildCacheThread( + __in HANDLE hCacheThread, + __in DWORD dwExpectedExitCode + ); +static HRESULT ProcessApplyInitializeMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); +static HRESULT ProcessBurnCacheMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in LPVOID pvContext, + __out DWORD* pdwResult + ); +static HRESULT ProcessGenericExecuteMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in LPVOID pvContext, + __out DWORD* pdwResult + ); +static HRESULT ProcessMsiPackageMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); +static HRESULT ProcessLaunchApprovedExeMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); +static HRESULT ProcessProgressRoutineMessage( + __in BURN_PIPE_MESSAGE* pMsg, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pvContext, + __out DWORD* pdwResult + ); +static HRESULT ProcessElevatedChildMessage( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); +static HRESULT ProcessElevatedChildCacheMessage( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); +static HRESULT ProcessResult( + __in DWORD dwResult, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +static HRESULT OnApplyInitialize( + __in HANDLE hPipe, + __in BURN_VARIABLES* pVariables, + __in BURN_REGISTRATION* pRegistration, + __in HANDLE* phLock, + __in BOOL* pfDisabledWindowsUpdate, + __in BYTE* pbData, + __in SIZE_T cbData + ); +static HRESULT OnApplyUninitialize( + __in HANDLE* phLock + ); +static HRESULT OnSessionBegin( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in SIZE_T cbData + ); +static HRESULT OnSessionResume( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in SIZE_T cbData + ); +static HRESULT OnSessionEnd( + __in BURN_PACKAGES* pPackages, + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in SIZE_T cbData + ); +static HRESULT OnSaveState( + __in BURN_REGISTRATION* pRegistration, + __in BYTE* pbData, + __in SIZE_T cbData + ); +static HRESULT OnCacheCompletePayload( + __in HANDLE hPipe, + __in BURN_PACKAGES* pPackages, + __in BURN_PAYLOADS* pPayloads, + __in BYTE* pbData, + __in SIZE_T cbData + ); +static HRESULT OnCacheVerifyPayload( + __in HANDLE hPipe, + __in BURN_PACKAGES* pPackages, + __in BURN_PAYLOADS* pPayloads, + __in BYTE* pbData, + __in SIZE_T cbData + ); +static void OnCacheCleanup( + __in_z LPCWSTR wzBundleId + ); +static HRESULT OnProcessDependentRegistration( + __in const BURN_REGISTRATION* pRegistration, + __in BYTE* pbData, + __in SIZE_T cbData + ); +static HRESULT OnExecuteExePackage( + __in HANDLE hPipe, + __in BURN_PACKAGES* pPackages, + __in BURN_RELATED_BUNDLES* pRelatedBundles, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in SIZE_T cbData + ); +static HRESULT OnExecuteMsiPackage( + __in HANDLE hPipe, + __in BURN_PACKAGES* pPackages, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in SIZE_T cbData + ); +static HRESULT OnExecuteMspPackage( + __in HANDLE hPipe, + __in BURN_PACKAGES* pPackages, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in SIZE_T cbData + ); +static HRESULT OnExecuteMsuPackage( + __in HANDLE hPipe, + __in BURN_PACKAGES* pPackages, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in SIZE_T cbData + ); +static HRESULT OnExecutePackageProviderAction( + __in BURN_PACKAGES* pPackages, + __in BURN_RELATED_BUNDLES* pRelatedBundles, + __in BYTE* pbData, + __in SIZE_T cbData + ); +static HRESULT OnExecutePackageDependencyAction( + __in BURN_PACKAGES* pPackages, + __in BURN_RELATED_BUNDLES* pRelatedBundles, + __in BYTE* pbData, + __in SIZE_T cbData + ); +static HRESULT CALLBACK BurnCacheMessageHandler( + __in BURN_CACHE_MESSAGE* pMessage, + __in LPVOID pvContext + ); +static DWORD CALLBACK ElevatedProgressRoutine( + __in LARGE_INTEGER TotalFileSize, + __in LARGE_INTEGER TotalBytesTransferred, + __in LARGE_INTEGER StreamSize, + __in LARGE_INTEGER StreamBytesTransferred, + __in DWORD dwStreamNumber, + __in DWORD dwCallbackReason, + __in HANDLE hSourceFile, + __in HANDLE hDestinationFile, + __in_opt LPVOID lpData + ); +static int GenericExecuteMessageHandler( + __in GENERIC_EXECUTE_MESSAGE* pMessage, + __in LPVOID pvContext + ); +static int MsiExecuteMessageHandler( + __in WIU_MSI_EXECUTE_MESSAGE* pMessage, + __in_opt LPVOID pvContext + ); +static HRESULT OnCleanPackage( + __in BURN_PACKAGES* pPackages, + __in BYTE* pbData, + __in SIZE_T cbData + ); +static HRESULT OnLaunchApprovedExe( + __in HANDLE hPipe, + __in BURN_APPROVED_EXES* pApprovedExes, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in SIZE_T cbData + ); +static HRESULT OnMsiBeginTransaction( + __in BURN_PACKAGES* pPackages, + __in BYTE* pbData, + __in SIZE_T cbData + ); +static HRESULT OnMsiCommitTransaction( + __in BURN_PACKAGES* pPackages, + __in BYTE* pbData, + __in SIZE_T cbData + ); +static HRESULT OnMsiRollbackTransaction( + __in BURN_PACKAGES* pPackages, + __in BYTE* pbData, + __in SIZE_T cbData + ); +static HRESULT ElevatedOnPauseAUBegin( + __in HANDLE hPipe + ); +static HRESULT ElevatedOnPauseAUComplete( + __in HANDLE hPipe, + __in HRESULT hrStatus + ); +static HRESULT ElevatedOnSystemRestorePointBegin( + __in HANDLE hPipe + ); +static HRESULT ElevatedOnSystemRestorePointComplete( + __in HANDLE hPipe, + __in HRESULT hrStatus + ); + + +// function definitions + +extern "C" HRESULT ElevationElevate( + __in BURN_ENGINE_STATE* pEngineState, + __in_opt HWND hwndParent + ) +{ + Assert(BURN_MODE_ELEVATED != pEngineState->mode); + Assert(!pEngineState->companionConnection.sczName); + Assert(!pEngineState->companionConnection.sczSecret); + Assert(!pEngineState->companionConnection.hProcess); + Assert(!pEngineState->companionConnection.dwProcessId); + Assert(INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe); + Assert(INVALID_HANDLE_VALUE == pEngineState->companionConnection.hCachePipe); + + HRESULT hr = S_OK; + int nResult = IDOK; + HANDLE hPipesCreatedEvent = INVALID_HANDLE_VALUE; + + hr = UserExperienceOnElevateBegin(&pEngineState->userExperience); + ExitOnRootFailure(hr, "BA aborted elevation requirement."); + + hr = PipeCreateNameAndSecret(&pEngineState->companionConnection.sczName, &pEngineState->companionConnection.sczSecret); + ExitOnFailure(hr, "Failed to create pipe name and client token."); + + hr = PipeCreatePipes(&pEngineState->companionConnection, TRUE, &hPipesCreatedEvent); + ExitOnFailure(hr, "Failed to create pipe and cache pipe."); + + LogId(REPORT_STANDARD, MSG_LAUNCH_ELEVATED_ENGINE_STARTING); + + do + { + nResult = IDOK; + + // Create the elevated process and if successful, wait for it to connect. + hr = PipeLaunchChildProcess(pEngineState->sczBundleEngineWorkingPath, &pEngineState->companionConnection, TRUE, hwndParent); + if (SUCCEEDED(hr)) + { + LogId(REPORT_STANDARD, MSG_LAUNCH_ELEVATED_ENGINE_SUCCESS); + + hr = PipeWaitForChildConnect(&pEngineState->companionConnection); + if (HRESULT_FROM_WIN32(ERROR_NO_DATA) == hr) + { + hr = E_SUSPECTED_AV_INTERFERENCE; + } + ExitOnFailure(hr, "Failed to connect to elevated child process."); + + LogId(REPORT_STANDARD, MSG_CONNECT_TO_ELEVATED_ENGINE_SUCCESS); + } + else if (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr) + { + // The user clicked "Cancel" on the elevation prompt or the elevation prompt timed out, provide the notification with the option to retry. + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + nResult = UserExperienceSendError(&pEngineState->userExperience, BOOTSTRAPPER_ERROR_TYPE_ELEVATE, NULL, hr, NULL, MB_ICONERROR | MB_RETRYCANCEL, IDNOACTION); + } + } while (IDRETRY == nResult); + ExitOnFailure(hr, "Failed to elevate."); + +LExit: + ReleaseHandle(hPipesCreatedEvent); + + if (FAILED(hr)) + { + PipeConnectionUninitialize(&pEngineState->companionConnection); + } + + UserExperienceOnElevateComplete(&pEngineState->userExperience, hr); + + return hr; +} + +extern "C" HRESULT ElevationApplyInitialize( + __in HANDLE hPipe, + __in BURN_USER_EXPERIENCE* pBA, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_ACTION action, + __in BURN_AU_PAUSE_ACTION auAction, + __in BOOL fTakeSystemRestorePoint + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + BURN_ELEVATION_APPLY_INITIALIZE_MESSAGE_CONTEXT context = { }; + + context.pBA = pBA; + + // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)action); + ExitOnFailure(hr, "Failed to write action to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)auAction); + ExitOnFailure(hr, "Failed to write update action to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fTakeSystemRestorePoint); + ExitOnFailure(hr, "Failed to write system restore point action to message buffer."); + + hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); + ExitOnFailure(hr, "Failed to write variables."); + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE, pbData, cbData, ProcessApplyInitializeMessages, &context, &dwResult); + ExitOnFailure(hr, "Failed to send message to per-machine process."); + + hr = (HRESULT)dwResult; + + // Best effort to keep the sequence of BA events sane. + if (context.fPauseCompleteNeeded) + { + UserExperienceOnPauseAUComplete(pBA, hr); + } + if (context.fSrpCompleteNeeded) + { + UserExperienceOnSystemRestorePointComplete(pBA, hr); + } + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +extern "C" HRESULT ElevationApplyUninitialize( + __in HANDLE hPipe + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationSessionBegin - + +*******************************************************************/ +extern "C" HRESULT ElevationSessionBegin( + __in HANDLE hPipe, + __in_z LPCWSTR wzEngineWorkingPath, + __in_z LPCWSTR wzResumeCommandLine, + __in BOOL fDisableResume, + __in BURN_VARIABLES* pVariables, + __in DWORD dwRegistrationOperations, + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction, + __in DWORD64 qwEstimatedSize + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + + // serialize message data + hr = BuffWriteString(&pbData, &cbData, wzEngineWorkingPath); + ExitOnFailure(hr, "Failed to write engine working path to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, wzResumeCommandLine); + ExitOnFailure(hr, "Failed to write resume command line to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, fDisableResume); + ExitOnFailure(hr, "Failed to write resume flag."); + + hr = BuffWriteNumber(&pbData, &cbData, dwRegistrationOperations); + ExitOnFailure(hr, "Failed to write registration operations to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)dependencyRegistrationAction); + ExitOnFailure(hr, "Failed to write dependency registration action to message buffer."); + + hr = BuffWriteNumber64(&pbData, &cbData, qwEstimatedSize); + ExitOnFailure(hr, "Failed to write estimated size to message buffer."); + + hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); + ExitOnFailure(hr, "Failed to write variables."); + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationSessionResume - + +*******************************************************************/ +extern "C" HRESULT ElevationSessionResume( + __in HANDLE hPipe, + __in_z LPCWSTR wzResumeCommandLine, + __in BOOL fDisableResume, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + + // serialize message data + hr = BuffWriteString(&pbData, &cbData, wzResumeCommandLine); + ExitOnFailure(hr, "Failed to write resume command line to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, fDisableResume); + ExitOnFailure(hr, "Failed to write resume flag."); + + hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); + ExitOnFailure(hr, "Failed to write variables."); + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationSessionEnd - + +*******************************************************************/ +extern "C" HRESULT ElevationSessionEnd( + __in HANDLE hPipe, + __in BURN_RESUME_MODE resumeMode, + __in BOOTSTRAPPER_APPLY_RESTART restart, + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + + // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)resumeMode); + ExitOnFailure(hr, "Failed to write resume mode to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)restart); + ExitOnFailure(hr, "Failed to write restart enum to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)dependencyRegistrationAction); + ExitOnFailure(hr, "Failed to write dependency registration action to message buffer."); + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SESSION_END, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationSaveState - + +*******************************************************************/ +HRESULT ElevationSaveState( + __in HANDLE hPipe, + __in_bcount(cbBuffer) BYTE* pbBuffer, + __in SIZE_T cbBuffer + ) +{ + HRESULT hr = S_OK; + DWORD dwResult = 0; + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE, pbBuffer, cbBuffer, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + return hr; +} + +/******************************************************************* + ElevationCacheCompletePayload - + +*******************************************************************/ +extern "C" HRESULT ElevationCacheCompletePayload( + __in HANDLE hPipe, + __in BURN_PACKAGE* pPackage, + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzUnverifiedPath, + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + BURN_ELEVATION_CACHE_MESSAGE_CONTEXT context = { }; + + context.pfnCacheMessageHandler = pfnCacheMessageHandler; + context.pfnProgress = pfnProgress; + context.pvContext = pContext; + + // serialize message data + hr = BuffWriteString(&pbData, &cbData, pPackage->sczId); + ExitOnFailure(hr, "Failed to write package id to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pPayload->sczKey); + ExitOnFailure(hr, "Failed to write payload id to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, wzUnverifiedPath); + ExitOnFailure(hr, "Failed to write unverified path to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fMove); + ExitOnFailure(hr, "Failed to write move flag to message buffer."); + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_COMPLETE_PAYLOAD, pbData, cbData, ProcessBurnCacheMessages, &context, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_COMPLETE_PAYLOAD message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +extern "C" HRESULT ElevationCacheVerifyPayload( + __in HANDLE hPipe, + __in BURN_PACKAGE* pPackage, + __in BURN_PAYLOAD* pPayload, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + BURN_ELEVATION_CACHE_MESSAGE_CONTEXT context = { }; + + context.pfnCacheMessageHandler = pfnCacheMessageHandler; + context.pfnProgress = pfnProgress; + context.pvContext = pContext; + + // serialize message data + hr = BuffWriteString(&pbData, &cbData, pPackage->sczId); + ExitOnFailure(hr, "Failed to write package id to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pPayload->sczKey); + ExitOnFailure(hr, "Failed to write payload id to message buffer."); + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_PAYLOAD, pbData, cbData, ProcessBurnCacheMessages, &context, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_PAYLOAD message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationCacheCleanup - + +*******************************************************************/ +extern "C" HRESULT ElevationCacheCleanup( + __in HANDLE hPipe + ) +{ + HRESULT hr = S_OK; + DWORD dwResult = 0; + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP, NULL, 0, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + return hr; +} + +extern "C" HRESULT ElevationProcessDependentRegistration( + __in HANDLE hPipe, + __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + + // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, pAction->type); + ExitOnFailure(hr, "Failed to write action type to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pAction->sczBundleId); + ExitOnFailure(hr, "Failed to write bundle id to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pAction->sczDependentProviderKey); + ExitOnFailure(hr, "Failed to write dependent provider key to message buffer."); + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationExecuteExePackage - + +*******************************************************************/ +extern "C" HRESULT ElevationExecuteExePackage( + __in HANDLE hPipe, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT context = { }; + DWORD dwResult = 0; + + // serialize message data + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.pPackage->sczId); + ExitOnFailure(hr, "Failed to write package id to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->exePackage.action); + ExitOnFailure(hr, "Failed to write action to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, fRollback); + ExitOnFailure(hr, "Failed to write rollback."); + + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.sczIgnoreDependencies); + ExitOnFailure(hr, "Failed to write the list of dependencies to ignore to the message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.sczAncestors); + ExitOnFailure(hr, "Failed to write the list of ancestors to the message buffer."); + + hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); + ExitOnFailure(hr, "Failed to write variables."); + + // send message + context.pfnGenericMessageHandler = pfnGenericMessageHandler; + context.pvContext = pvContext; + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE, pbData, cbData, ProcessGenericExecuteMessages, &context, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE message to per-machine process."); + + hr = ProcessResult(dwResult, pRestart); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +extern "C" HRESULT ElevationMsiBeginTransaction( + __in HANDLE hPipe, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = ERROR_SUCCESS; + + // serialize message data + hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczId); + ExitOnFailure(hr, "Failed to write transaction name to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczLogPath); + ExitOnFailure(hr, "Failed to write transaction log path to message buffer."); + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION message to per-machine process."); + + hr = static_cast(dwResult); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +extern "C" HRESULT ElevationMsiCommitTransaction( + __in HANDLE hPipe, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = ERROR_SUCCESS; + + // serialize message data + hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczId); + ExitOnFailure(hr, "Failed to write transaction name to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczLogPath); + ExitOnFailure(hr, "Failed to write transaction log path to message buffer."); + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION message to per-machine process."); + + hr = static_cast(dwResult); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +extern "C" HRESULT ElevationMsiRollbackTransaction( + __in HANDLE hPipe, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = ERROR_SUCCESS; + + // serialize message data + hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczId); + ExitOnFailure(hr, "Failed to write transaction name to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczLogPath); + ExitOnFailure(hr, "Failed to write transaction log path to message buffer."); + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION message to per-machine process."); + + hr = static_cast(dwResult); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + + + +/******************************************************************* + ElevationExecuteMsiPackage - + +*******************************************************************/ +extern "C" HRESULT ElevationExecuteMsiPackage( + __in HANDLE hPipe, + __in_opt HWND hwndParent, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = { }; + DWORD dwResult = 0; + + // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fRollback); + ExitOnFailure(hr, "Failed to write rollback flag to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msiPackage.pPackage->sczId); + ExitOnFailure(hr, "Failed to write package id to message buffer."); + + hr = BuffWritePointer(&pbData, &cbData, (DWORD_PTR)hwndParent); + ExitOnFailure(hr, "Failed to write parent hwnd to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msiPackage.sczLogPath); + ExitOnFailure(hr, "Failed to write package log to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.actionMsiProperty); + ExitOnFailure(hr, "Failed to write actionMsiProperty to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.uiLevel); + ExitOnFailure(hr, "Failed to write UI level to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.fDisableExternalUiHandler); + ExitOnFailure(hr, "Failed to write fDisableExternalUiHandler to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.action); + ExitOnFailure(hr, "Failed to write action to message buffer."); + + // Feature actions. + for (DWORD i = 0; i < pExecuteAction->msiPackage.pPackage->Msi.cFeatures; ++i) + { + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.rgFeatures[i]); + ExitOnFailure(hr, "Failed to write feature action to message buffer."); + } + + // Slipstream patches actions. + for (DWORD i = 0; i < pExecuteAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++i) + { + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pExecuteAction->msiPackage.pPackage->Msi.rgSlipstreamMsps + i; + BOOTSTRAPPER_ACTION_STATE* pAction = fRollback ? &pSlipstreamMsp->rollback : &pSlipstreamMsp->execute; + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)*pAction); + ExitOnFailure(hr, "Failed to write slipstream patch action to message buffer."); + } + + hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); + ExitOnFailure(hr, "Failed to write variables."); + + + // send message + context.pfnMessageHandler = pfnMessageHandler; + context.pvContext = pvContext; + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE, pbData, cbData, ProcessMsiPackageMessages, &context, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process."); + + hr = ProcessResult(dwResult, pRestart); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationExecuteMspPackage - + +*******************************************************************/ +extern "C" HRESULT ElevationExecuteMspPackage( + __in HANDLE hPipe, + __in_opt HWND hwndParent, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = { }; + DWORD dwResult = 0; + + // serialize message data + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.pPackage->sczId); + ExitOnFailure(hr, "Failed to write package id to message buffer."); + + hr = BuffWritePointer(&pbData, &cbData, (DWORD_PTR)hwndParent); + ExitOnFailure(hr, "Failed to write parent hwnd to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.sczTargetProductCode); + ExitOnFailure(hr, "Failed to write target product code to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.sczLogPath); + ExitOnFailure(hr, "Failed to write package log to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.actionMsiProperty); + ExitOnFailure(hr, "Failed to write actionMsiProperty to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.uiLevel); + ExitOnFailure(hr, "Failed to write UI level to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.fDisableExternalUiHandler); + ExitOnFailure(hr, "Failed to write fDisableExternalUiHandler to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.action); + ExitOnFailure(hr, "Failed to write action to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->mspTarget.cOrderedPatches); + ExitOnFailure(hr, "Failed to write count of ordered patches to message buffer."); + + for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i) + { + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage->sczId); + ExitOnFailure(hr, "Failed to write ordered patch id to message buffer."); + } + + hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); + ExitOnFailure(hr, "Failed to write variables."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fRollback); + ExitOnFailure(hr, "Failed to write rollback flag to message buffer."); + + // send message + context.pfnMessageHandler = pfnMessageHandler; + context.pvContext = pvContext; + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE, pbData, cbData, ProcessMsiPackageMessages, &context, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE message to per-machine process."); + + hr = ProcessResult(dwResult, pRestart); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationExecuteMsuPackage - + +*******************************************************************/ +extern "C" HRESULT ElevationExecuteMsuPackage( + __in HANDLE hPipe, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BOOL fRollback, + __in BOOL fStopWusaService, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT context = { }; + DWORD dwResult = 0; + + // serialize message data + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msuPackage.pPackage->sczId); + ExitOnFailure(hr, "Failed to write package id to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msuPackage.sczLogPath); + ExitOnFailure(hr, "Failed to write package log to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msuPackage.action); + ExitOnFailure(hr, "Failed to write action to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, fRollback); + ExitOnFailure(hr, "Failed to write rollback."); + + hr = BuffWriteNumber(&pbData, &cbData, fStopWusaService); + ExitOnFailure(hr, "Failed to write StopWusaService."); + + // send message + context.pfnGenericMessageHandler = pfnGenericMessageHandler; + context.pvContext = pvContext; + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE, pbData, cbData, ProcessGenericExecuteMessages, &context, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE message to per-machine process."); + + hr = ProcessResult(dwResult, pRestart); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +extern "C" HRESULT ElevationExecutePackageProviderAction( + __in HANDLE hPipe, + __in BURN_EXECUTE_ACTION* pExecuteAction + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; + + // Serialize the message data. + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->packageProvider.pPackage->sczId); + ExitOnFailure(hr, "Failed to write package id to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->packageProvider.action); + ExitOnFailure(hr, "Failed to write action to message buffer."); + + // Send the message. + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER message to per-machine process."); + + // Ignore the restart since this action only results in registry writes. + hr = ProcessResult(dwResult, &restart); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +extern "C" HRESULT ElevationExecutePackageDependencyAction( + __in HANDLE hPipe, + __in BURN_EXECUTE_ACTION* pExecuteAction + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; + + // Serialize the message data. + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->packageDependency.pPackage->sczId); + ExitOnFailure(hr, "Failed to write package id to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->packageDependency.sczBundleProviderKey); + ExitOnFailure(hr, "Failed to write bundle dependency key to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->packageDependency.action); + ExitOnFailure(hr, "Failed to write action to message buffer."); + + // Send the message. + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY message to per-machine process."); + + // Ignore the restart since this action only results in registry writes. + hr = ProcessResult(dwResult, &restart); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationCleanPackage - + +*******************************************************************/ +extern "C" HRESULT ElevationCleanPackage( + __in HANDLE hPipe, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + + // serialize message data + hr = BuffWriteString(&pbData, &cbData, pPackage->sczId); + ExitOnFailure(hr, "Failed to write clean package id to message buffer."); + + // send message + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE message to per-machine process."); + + hr = (HRESULT)dwResult; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +extern "C" HRESULT ElevationLaunchApprovedExe( + __in HANDLE hPipe, + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe, + __out DWORD* pdwProcessId + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT context = { }; + + // Serialize message data. + hr = BuffWriteString(&pbData, &cbData, pLaunchApprovedExe->sczId); + ExitOnFailure(hr, "Failed to write approved exe id to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pLaunchApprovedExe->sczArguments); + ExitOnFailure(hr, "Failed to write approved exe arguments to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, pLaunchApprovedExe->dwWaitForInputIdleTimeout); + ExitOnFailure(hr, "Failed to write approved exe WaitForInputIdle timeout to message buffer."); + + // Send the message. + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE, pbData, cbData, ProcessLaunchApprovedExeMessages, &context, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE message to per-machine process."); + + hr = (HRESULT)dwResult; + *pdwProcessId = context.dwProcessId; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + ElevationChildPumpMessages - + +*******************************************************************/ +extern "C" HRESULT ElevationChildPumpMessages( + __in DWORD dwLoggingTlsId, + __in HANDLE hPipe, + __in HANDLE hCachePipe, + __in BURN_APPROVED_EXES* pApprovedExes, + __in BURN_CONTAINERS* pContainers, + __in BURN_PACKAGES* pPackages, + __in BURN_PAYLOADS* pPayloads, + __in BURN_VARIABLES* pVariables, + __in BURN_REGISTRATION* pRegistration, + __in BURN_USER_EXPERIENCE* pUserExperience, + __out HANDLE* phLock, + __out BOOL* pfDisabledAutomaticUpdates, + __out DWORD* pdwChildExitCode, + __out BOOL* pfRestart + ) +{ + HRESULT hr = S_OK; + BURN_ELEVATION_CHILD_MESSAGE_CONTEXT cacheContext = { }; + BURN_ELEVATION_CHILD_MESSAGE_CONTEXT context = { }; + HANDLE hCacheThread = NULL; + BURN_PIPE_RESULT result = { }; + + cacheContext.dwLoggingTlsId = dwLoggingTlsId; + cacheContext.hPipe = hCachePipe; + cacheContext.pContainers = pContainers; + cacheContext.pPackages = pPackages; + cacheContext.pPayloads = pPayloads; + cacheContext.pVariables = pVariables; + cacheContext.pRegistration = pRegistration; + cacheContext.pUserExperience = pUserExperience; + + context.dwLoggingTlsId = dwLoggingTlsId; + context.hPipe = hPipe; + context.phLock = phLock; + context.pfDisabledAutomaticUpdates = pfDisabledAutomaticUpdates; + context.pApprovedExes = pApprovedExes; + context.pContainers = pContainers; + context.pPackages = pPackages; + context.pPayloads = pPayloads; + context.pVariables = pVariables; + context.pRegistration = pRegistration; + context.pUserExperience = pUserExperience; + + hCacheThread = ::CreateThread(NULL, 0, ElevatedChildCacheThreadProc, &cacheContext, 0, NULL); + ExitOnNullWithLastError(hCacheThread, hr, "Failed to create elevated cache thread."); + + hr = PipePumpMessages(hPipe, ProcessElevatedChildMessage, &context, &result); + ExitOnFailure(hr, "Failed to pump messages in child process."); + + // Wait for the cache thread and verify it gets the right result but don't fail if things + // don't work out. + WaitForElevatedChildCacheThread(hCacheThread, result.dwResult); + + *pdwChildExitCode = result.dwResult; + *pfRestart = result.fRestart; + +LExit: + ReleaseHandle(hCacheThread); + + return hr; +} + +extern "C" HRESULT ElevationChildResumeAutomaticUpdates() +{ + HRESULT hr = S_OK; + + LogId(REPORT_STANDARD, MSG_RESUME_AU_STARTING); + + hr = WuaResumeAutomaticUpdates(); + ExitOnFailure(hr, "Failed to resume automatic updates after pausing them, continuing..."); + + LogId(REPORT_STANDARD, MSG_RESUME_AU_SUCCEEDED); + +LExit: + return hr; +} + +// internal function definitions + +static DWORD WINAPI ElevatedChildCacheThreadProc( + __in LPVOID lpThreadParameter + ) +{ + HRESULT hr = S_OK; + BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = reinterpret_cast(lpThreadParameter); + BOOL fComInitialized = FALSE; + BURN_PIPE_RESULT result = { }; + + if (!::TlsSetValue(pContext->dwLoggingTlsId, pContext->hPipe)) + { + ExitWithLastError(hr, "Failed to set elevated cache pipe into thread local storage for logging."); + } + + // initialize COM + hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); + ExitOnFailure(hr, "Failed to initialize COM."); + fComInitialized = TRUE; + + hr = PipePumpMessages(pContext->hPipe, ProcessElevatedChildCacheMessage, pContext, &result); + ExitOnFailure(hr, "Failed to pump messages in child process."); + + hr = (HRESULT)result.dwResult; + +LExit: + if (fComInitialized) + { + ::CoUninitialize(); + } + + return (DWORD)hr; +} + +static HRESULT WaitForElevatedChildCacheThread( + __in HANDLE hCacheThread, + __in DWORD dwExpectedExitCode + ) +{ + UNREFERENCED_PARAMETER(dwExpectedExitCode); + + HRESULT hr = S_OK; + DWORD dwExitCode = ERROR_SUCCESS; + + if (WAIT_OBJECT_0 != ::WaitForSingleObject(hCacheThread, BURN_TIMEOUT)) + { + ExitWithLastError(hr, "Failed to wait for cache thread to terminate."); + } + + if (!::GetExitCodeThread(hCacheThread, &dwExitCode)) + { + ExitWithLastError(hr, "Failed to get cache thread exit code."); + } + + AssertSz(dwExitCode == dwExpectedExitCode, "Cache thread should have exited with the expected exit code."); + +LExit: + return hr; +} + +static HRESULT ProcessApplyInitializeMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + BURN_ELEVATION_APPLY_INITIALIZE_MESSAGE_CONTEXT* pContext = static_cast(pvContext); + BYTE* pbData = (BYTE*)pMsg->pvData; + SIZE_T iData = 0; + HRESULT hrStatus = S_OK; + HRESULT hrBA = S_OK; + + // Process the message. + switch (pMsg->dwMessage) + { + case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_BEGIN: + pContext->fPauseCompleteNeeded = TRUE; + hrBA = UserExperienceOnPauseAUBegin(pContext->pBA); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_COMPLETE: + // read hrStatus + hr = BuffReadNumber(pbData, pMsg->cbData, &iData, reinterpret_cast(&hrStatus)); + ExitOnFailure(hr, "Failed to read pause AU hrStatus."); + + pContext->fPauseCompleteNeeded = FALSE; + hrBA = UserExperienceOnPauseAUComplete(pContext->pBA, hrStatus); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_BEGIN: + if (pContext->fPauseCompleteNeeded) + { + pContext->fPauseCompleteNeeded = FALSE; + hrBA = UserExperienceOnPauseAUComplete(pContext->pBA, E_INVALIDSTATE); + } + + pContext->fSrpCompleteNeeded = TRUE; + hrBA = UserExperienceOnSystemRestorePointBegin(pContext->pBA); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_COMPLETE: + // read hrStatus + hr = BuffReadNumber(pbData, pMsg->cbData, &iData, reinterpret_cast(&hrStatus)); + ExitOnFailure(hr, "Failed to read system restore point hrStatus."); + + pContext->fSrpCompleteNeeded = FALSE; + hrBA = UserExperienceOnSystemRestorePointComplete(pContext->pBA, hrStatus); + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid apply initialize message."); + break; + } + + *pdwResult = static_cast(hrBA); + +LExit: + return hr; +} + +static HRESULT ProcessBurnCacheMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + BURN_ELEVATION_CACHE_MESSAGE_CONTEXT* pContext = static_cast(pvContext); + BURN_CACHE_MESSAGE message = { }; + BOOL fProgressRoutine = FALSE; + + // Process the message. + switch (pMsg->dwMessage) + { + case BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_BEGIN: + // read message parameters + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, reinterpret_cast(&message.begin.cacheStep)); + ExitOnFailure(hr, "Failed to read begin cache step."); + + message.type = BURN_CACHE_MESSAGE_BEGIN; + break; + + case BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_COMPLETE: + // read message parameters + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, reinterpret_cast(&message.complete.hrStatus)); + ExitOnFailure(hr, "Failed to read complete hresult."); + + message.type = BURN_CACHE_MESSAGE_COMPLETE; + break; + + case BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_SUCCESS: + // read message parameters + hr = BuffReadNumber64((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.success.qwFileSize); + ExitOnFailure(hr, "Failed to read begin cache step."); + + message.type = BURN_CACHE_MESSAGE_SUCCESS; + break; + + case BURN_ELEVATION_MESSAGE_TYPE_PROGRESS_ROUTINE: + fProgressRoutine = TRUE; + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid burn cache message."); + break; + } + + if (fProgressRoutine) + { + hr = ProcessProgressRoutineMessage(pMsg, pContext->pfnProgress, pContext->pvContext, pdwResult); + } + else + { + hr = pContext->pfnCacheMessageHandler(&message, pContext->pvContext); + *pdwResult = static_cast(hr); + } + +LExit: + return hr; +} + +static HRESULT ProcessGenericExecuteMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT* pContext = static_cast(pvContext); + LPWSTR sczMessage = NULL; + DWORD cFiles = 0; + LPWSTR* rgwzFiles = NULL; + GENERIC_EXECUTE_MESSAGE message = { }; + + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.dwAllowedResults); + ExitOnFailure(hr, "Failed to allowed results."); + + // Process the message. + switch (pMsg->dwMessage) + { + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS: + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; + + // read message parameters + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.progress.dwPercentage); + ExitOnFailure(hr, "Failed to progress."); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR: + message.type = GENERIC_EXECUTE_MESSAGE_ERROR; + + // read message parameters + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.error.dwErrorCode); + ExitOnFailure(hr, "Failed to read error code."); + + hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &sczMessage); + ExitOnFailure(hr, "Failed to read message."); + + message.error.wzMessage = sczMessage; + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE: + message.type = GENERIC_EXECUTE_MESSAGE_FILES_IN_USE; + + // read message parameters + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &cFiles); + ExitOnFailure(hr, "Failed to read file count."); + + rgwzFiles = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cFiles, TRUE); + ExitOnNull(rgwzFiles, hr, E_OUTOFMEMORY, "Failed to allocate buffer for files in use."); + + for (DWORD i = 0; i < cFiles; ++i) + { + hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &rgwzFiles[i]); + ExitOnFailure(hr, "Failed to read file name: %u", i); + } + + message.filesInUse.cFiles = cFiles; + message.filesInUse.rgwzFiles = (LPCWSTR*)rgwzFiles; + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid package message."); + break; + } + + // send message + *pdwResult = (DWORD)pContext->pfnGenericMessageHandler(&message, pContext->pvContext); + +LExit: + ReleaseStr(sczMessage); + + if (rgwzFiles) + { + for (DWORD i = 0; i < cFiles; ++i) + { + ReleaseStr(rgwzFiles[i]); + } + MemFree(rgwzFiles); + } + return hr; +} + +static HRESULT ProcessMsiPackageMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + WIU_MSI_EXECUTE_MESSAGE message = { }; + DWORD cMsiData = 0; + LPWSTR* rgwzMsiData = NULL; + BURN_ELEVATION_MSI_MESSAGE_CONTEXT* pContext = static_cast(pvContext); + LPWSTR sczMessage = NULL; + + // Read MSI extended message data. + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &cMsiData); + ExitOnFailure(hr, "Failed to read MSI data count."); + + if (cMsiData) + { + rgwzMsiData = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cMsiData, TRUE); + ExitOnNull(rgwzMsiData, hr, E_OUTOFMEMORY, "Failed to allocate buffer to read MSI data."); + + for (DWORD i = 0; i < cMsiData; ++i) + { + hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &rgwzMsiData[i]); + ExitOnFailure(hr, "Failed to read MSI data: %u", i); + } + + message.cData = cMsiData; + message.rgwzData = (LPCWSTR*)rgwzMsiData; + } + + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.dwAllowedResults); + ExitOnFailure(hr, "Failed to read UI flags."); + + // Process the rest of the message. + switch (pMsg->dwMessage) + { + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS: + // read message parameters + message.type = WIU_MSI_EXECUTE_MESSAGE_PROGRESS; + + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.progress.dwPercentage); + ExitOnFailure(hr, "Failed to read progress."); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR: + // read message parameters + message.type = WIU_MSI_EXECUTE_MESSAGE_ERROR; + + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.error.dwErrorCode); + ExitOnFailure(hr, "Failed to read error code."); + + hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &sczMessage); + ExitOnFailure(hr, "Failed to read message."); + message.error.wzMessage = sczMessage; + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE: + // read message parameters + message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE; + + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, (DWORD*)&message.msiMessage.mt); + ExitOnFailure(hr, "Failed to read message type."); + + hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &sczMessage); + ExitOnFailure(hr, "Failed to read message."); + message.msiMessage.wzMessage = sczMessage; + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE: + message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE; + message.msiFilesInUse.cFiles = cMsiData; + message.msiFilesInUse.rgwzFiles = (LPCWSTR*)rgwzMsiData; + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid package message."); + break; + } + + // send message + *pdwResult = (DWORD)pContext->pfnMessageHandler(&message, pContext->pvContext); + +LExit: + ReleaseStr(sczMessage); + + if (rgwzMsiData) + { + for (DWORD i = 0; i < cMsiData; ++i) + { + ReleaseStr(rgwzMsiData[i]); + } + + MemFree(rgwzMsiData); + } + + return hr; +} + +static HRESULT ProcessLaunchApprovedExeMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT* pContext = static_cast(pvContext); + DWORD dwProcessId = 0; + + // Process the message. + switch (pMsg->dwMessage) + { + case BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID: + // read message parameters + hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &dwProcessId); + ExitOnFailure(hr, "Failed to read approved exe process id."); + pContext->dwProcessId = dwProcessId; + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid launch approved exe message."); + break; + } + + *pdwResult = static_cast(hr); + +LExit: + return hr; +} + +static HRESULT ProcessProgressRoutineMessage( + __in BURN_PIPE_MESSAGE* pMsg, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LARGE_INTEGER liTotalFileSize = { }; + LARGE_INTEGER liTotalBytesTransferred = { }; + LARGE_INTEGER liStreamSize = { }; + LARGE_INTEGER liStreamBytesTransferred = { }; + DWORD dwStreamNumber = 0; + DWORD dwCallbackReason = CALLBACK_CHUNK_FINISHED; + HANDLE hSourceFile = INVALID_HANDLE_VALUE; + HANDLE hDestinationFile = INVALID_HANDLE_VALUE; + + hr = BuffReadNumber64((BYTE*)pMsg->pvData, pMsg->cbData, &iData, reinterpret_cast(&liTotalFileSize.QuadPart)); + ExitOnFailure(hr, "Failed to read total file size for progress."); + + hr = BuffReadNumber64((BYTE*)pMsg->pvData, pMsg->cbData, &iData, reinterpret_cast(&liTotalBytesTransferred.QuadPart)); + ExitOnFailure(hr, "Failed to read total bytes transferred for progress."); + + *pdwResult = pfnProgress(liTotalFileSize, liTotalBytesTransferred, liStreamSize, liStreamBytesTransferred, dwStreamNumber, dwCallbackReason, hSourceFile, hDestinationFile, pvContext); + +LExit: + return hr; +} + +static HRESULT ProcessElevatedChildMessage( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = static_cast(pvContext); + HRESULT hrResult = S_OK; + DWORD dwPid = 0; + + switch (pMsg->dwMessage) + { + case BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION: + hrResult = OnMsiBeginTransaction(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION: + hrResult = OnMsiCommitTransaction(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION: + hrResult = OnMsiRollbackTransaction(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE: + hrResult = OnApplyInitialize(pContext->hPipe, pContext->pVariables, pContext->pRegistration, pContext->phLock, pContext->pfDisabledAutomaticUpdates, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE: + hrResult = OnApplyUninitialize(pContext->phLock); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN: + hrResult = OnSessionBegin(pContext->pRegistration, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME: + hrResult = OnSessionResume(pContext->pRegistration, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_SESSION_END: + hrResult = OnSessionEnd(pContext->pPackages, pContext->pRegistration, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE: + hrResult = OnSaveState(pContext->pRegistration, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION: + hrResult = OnProcessDependentRegistration(pContext->pRegistration, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE: + hrResult = OnExecuteExePackage(pContext->hPipe, pContext->pPackages, &pContext->pRegistration->relatedBundles, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE: + hrResult = OnExecuteMsiPackage(pContext->hPipe, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE: + hrResult = OnExecuteMspPackage(pContext->hPipe, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE: + hrResult = OnExecuteMsuPackage(pContext->hPipe, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER: + hrResult = OnExecutePackageProviderAction(pContext->pPackages, &pContext->pRegistration->relatedBundles, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY: + hrResult = OnExecutePackageDependencyAction(pContext->pPackages, &pContext->pRegistration->relatedBundles, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE: + hrResult = OnCleanPackage(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE: + hrResult = OnLaunchApprovedExe(pContext->hPipe, pContext->pApprovedExes, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Unexpected elevated message sent to child process, msg: %u", pMsg->dwMessage); + } + + *pdwResult = dwPid ? dwPid : (DWORD)hrResult; + +LExit: + return hr; +} + +static HRESULT ProcessElevatedChildCacheMessage( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = static_cast(pvContext); + HRESULT hrResult = S_OK; + + switch (pMsg->dwMessage) + { + case BURN_ELEVATION_MESSAGE_TYPE_CACHE_COMPLETE_PAYLOAD: + hrResult = OnCacheCompletePayload(pContext->hPipe, pContext->pPackages, pContext->pPayloads, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_PAYLOAD: + hrResult = OnCacheVerifyPayload(pContext->hPipe, pContext->pPackages, pContext->pPayloads, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + case BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP: + OnCacheCleanup(pContext->pRegistration->sczId); + hrResult = S_OK; + break; + + case BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE: + hrResult = OnCleanPackage(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Unexpected elevated cache message sent to child process, msg: %u", pMsg->dwMessage); + } + + *pdwResult = (DWORD)hrResult; + +LExit: + return hr; +} + +static HRESULT ProcessResult( + __in DWORD dwResult, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = static_cast(dwResult); + if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == hr) + { + *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; + hr = S_OK; + } + else if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED) == hr) + { + *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; + hr = S_OK; + } + + return hr; +} + +static HRESULT OnApplyInitialize( + __in HANDLE hPipe, + __in BURN_VARIABLES* pVariables, + __in BURN_REGISTRATION* pRegistration, + __in HANDLE* phLock, + __in BOOL* pfDisabledWindowsUpdate, + __in BYTE* pbData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + DWORD dwAction = 0; + DWORD dwAUAction = 0; + DWORD dwTakeSystemRestorePoint = 0; + LPWSTR sczBundleName = NULL; + HRESULT hrStatus = S_OK; + + // Deserialize message data. + hr = BuffReadNumber(pbData, cbData, &iData, &dwAction); + ExitOnFailure(hr, "Failed to read action."); + + hr = BuffReadNumber(pbData, cbData, &iData, &dwAUAction); + ExitOnFailure(hr, "Failed to read update action."); + + hr = BuffReadNumber(pbData, cbData, &iData, &dwTakeSystemRestorePoint); + ExitOnFailure(hr, "Failed to read system restore point action."); + + hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); + ExitOnFailure(hr, "Failed to read variables."); + + // Initialize. + hr = ApplyLock(TRUE, phLock); + ExitOnFailure(hr, "Failed to acquire lock due to setup in other session."); + + // Reset and reload the related bundles. + RelatedBundlesUninitialize(&pRegistration->relatedBundles); + + hr = RelatedBundlesInitializeForScope(TRUE, pRegistration, &pRegistration->relatedBundles); + ExitOnFailure(hr, "Failed to initialize per-machine related bundles."); + + // Attempt to pause AU with best effort. + if (BURN_AU_PAUSE_ACTION_IFELEVATED == dwAUAction || BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME == dwAUAction) + { + hr = ElevatedOnPauseAUBegin(hPipe); + ExitOnFailure(hr, "ElevatedOnPauseAUBegin failed."); + + LogId(REPORT_STANDARD, MSG_PAUSE_AU_STARTING); + + hrStatus = hr = WuaPauseAutomaticUpdates(); + if (FAILED(hr)) + { + LogId(REPORT_STANDARD, MSG_FAILED_PAUSE_AU, hr); + hr = S_OK; + } + else + { + LogId(REPORT_STANDARD, MSG_PAUSE_AU_SUCCEEDED); + if (BURN_AU_PAUSE_ACTION_IFELEVATED == dwAUAction) + { + *pfDisabledWindowsUpdate = TRUE; + } + } + + hr = ElevatedOnPauseAUComplete(hPipe, hrStatus); + ExitOnFailure(hr, "ElevatedOnPauseAUComplete failed."); + } + + if (dwTakeSystemRestorePoint) + { + hr = VariableGetString(pVariables, BURN_BUNDLE_NAME, &sczBundleName); + if (FAILED(hr)) + { + hr = S_OK; + ExitFunction(); + } + + hr = ElevatedOnSystemRestorePointBegin(hPipe); + ExitOnFailure(hr, "ElevatedOnSystemRestorePointBegin failed."); + + LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_STARTING); + + BOOTSTRAPPER_ACTION action = static_cast(dwAction); + SRP_ACTION restoreAction = (BOOTSTRAPPER_ACTION_INSTALL == action) ? SRP_ACTION_INSTALL : (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? SRP_ACTION_UNINSTALL : SRP_ACTION_MODIFY; + hrStatus = hr = SrpCreateRestorePoint(sczBundleName, restoreAction); + if (SUCCEEDED(hr)) + { + LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_SUCCEEDED); + } + else if (E_NOTIMPL == hr) + { + LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_DISABLED); + hr = S_OK; + } + else + { + LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_FAILED, hr); + hr = S_OK; + } + + hr = ElevatedOnSystemRestorePointComplete(hPipe, hrStatus); + ExitOnFailure(hr, "ElevatedOnSystemRestorePointComplete failed."); + } + +LExit: + ReleaseStr(sczBundleName); + return hr; +} + +static HRESULT OnApplyUninitialize( + __in HANDLE* phLock + ) +{ + Assert(phLock); + + // TODO: end system restore point. + + if (*phLock) + { + ::ReleaseMutex(*phLock); + ::CloseHandle(*phLock); + *phLock = NULL; + } + + return S_OK; +} + +static HRESULT OnSessionBegin( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczEngineWorkingPath = NULL; + DWORD dwRegistrationOperations = 0; + DWORD dwDependencyRegistrationAction = 0; + DWORD64 qwEstimatedSize = 0; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &sczEngineWorkingPath); + ExitOnFailure(hr, "Failed to read engine working path."); + + hr = BuffReadString(pbData, cbData, &iData, &pRegistration->sczResumeCommandLine); + ExitOnFailure(hr, "Failed to read resume command line."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&pRegistration->fDisableResume); + ExitOnFailure(hr, "Failed to read resume flag."); + + hr = BuffReadNumber(pbData, cbData, &iData, &dwRegistrationOperations); + ExitOnFailure(hr, "Failed to read registration operations."); + + hr = BuffReadNumber(pbData, cbData, &iData, &dwDependencyRegistrationAction); + ExitOnFailure(hr, "Failed to read dependency registration action."); + + hr = BuffReadNumber64(pbData, cbData, &iData, &qwEstimatedSize); + ExitOnFailure(hr, "Failed to read estimated size."); + + hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); + ExitOnFailure(hr, "Failed to read variables."); + + // Begin session in per-machine process. + hr = RegistrationSessionBegin(sczEngineWorkingPath, pRegistration, pVariables, dwRegistrationOperations, (BURN_DEPENDENCY_REGISTRATION_ACTION)dwDependencyRegistrationAction, qwEstimatedSize); + ExitOnFailure(hr, "Failed to begin registration session."); + +LExit: + ReleaseStr(sczEngineWorkingPath); + + return hr; +} + +static HRESULT OnSessionResume( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &pRegistration->sczResumeCommandLine); + ExitOnFailure(hr, "Failed to read resume command line."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&pRegistration->fDisableResume); + ExitOnFailure(hr, "Failed to read resume flag."); + + hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); + ExitOnFailure(hr, "Failed to read variables."); + + // resume session in per-machine process + hr = RegistrationSessionResume(pRegistration, pVariables); + ExitOnFailure(hr, "Failed to resume registration session."); + +LExit: + return hr; +} + +static HRESULT OnSessionEnd( + __in BURN_PACKAGES* pPackages, + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + DWORD dwResumeMode = 0; + DWORD dwRestart = 0; + DWORD dwDependencyRegistrationAction = 0; + + // Deserialize message data. + hr = BuffReadNumber(pbData, cbData, &iData, &dwResumeMode); + ExitOnFailure(hr, "Failed to read resume mode enum."); + + hr = BuffReadNumber(pbData, cbData, &iData, &dwRestart); + ExitOnFailure(hr, "Failed to read restart enum."); + + hr = BuffReadNumber(pbData, cbData, &iData, &dwDependencyRegistrationAction); + ExitOnFailure(hr, "Failed to read dependency registration action."); + + // suspend session in per-machine process + hr = RegistrationSessionEnd(pRegistration, pVariables, pPackages, (BURN_RESUME_MODE)dwResumeMode, (BOOTSTRAPPER_APPLY_RESTART)dwRestart, (BURN_DEPENDENCY_REGISTRATION_ACTION)dwDependencyRegistrationAction); + ExitOnFailure(hr, "Failed to suspend registration session."); + +LExit: + return hr; +} + +static HRESULT OnSaveState( + __in BURN_REGISTRATION* pRegistration, + __in BYTE* pbData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + + // save state in per-machine process + hr = RegistrationSaveState(pRegistration, pbData, cbData); + ExitOnFailure(hr, "Failed to save state."); + +LExit: + return hr; +} + +static HRESULT OnCacheCompletePayload( + __in HANDLE hPipe, + __in BURN_PACKAGES* pPackages, + __in BURN_PAYLOADS* pPayloads, + __in BYTE* pbData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR scz = NULL; + BURN_PACKAGE* pPackage = NULL; + BURN_PAYLOAD* pPayload = NULL; + LPWSTR sczUnverifiedPath = NULL; + BOOL fMove = FALSE; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &scz); + ExitOnFailure(hr, "Failed to read package id."); + + if (scz && *scz) + { + hr = PackageFindById(pPackages, scz, &pPackage); + ExitOnFailure(hr, "Failed to find package: %ls", scz); + } + + hr = BuffReadString(pbData, cbData, &iData, &scz); + ExitOnFailure(hr, "Failed to read payload id."); + + if (scz && *scz) + { + hr = PayloadFindById(pPayloads, scz, &pPayload); + ExitOnFailure(hr, "Failed to find payload: %ls", scz); + } + + hr = BuffReadString(pbData, cbData, &iData, &sczUnverifiedPath); + ExitOnFailure(hr, "Failed to read unverified path."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fMove); + ExitOnFailure(hr, "Failed to read move flag."); + + if (pPackage && pPayload) // complete payload. + { + hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, sczUnverifiedPath, fMove, BurnCacheMessageHandler, ElevatedProgressRoutine, hPipe); + ExitOnFailure(hr, "Failed to cache payload: %ls", pPayload->sczKey); + } + else + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid data passed to cache complete payload."); + } + +LExit: + ReleaseStr(sczUnverifiedPath); + ReleaseStr(scz); + + return hr; +} + +static HRESULT OnCacheVerifyPayload( + __in HANDLE hPipe, + __in BURN_PACKAGES* pPackages, + __in BURN_PAYLOADS* pPayloads, + __in BYTE* pbData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR scz = NULL; + BURN_PACKAGE* pPackage = NULL; + BURN_PAYLOAD* pPayload = NULL; + LPWSTR sczCacheDirectory = NULL; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &scz); + ExitOnFailure(hr, "Failed to read package id."); + + if (scz && *scz) + { + hr = PackageFindById(pPackages, scz, &pPackage); + ExitOnFailure(hr, "Failed to find package: %ls", scz); + } + + hr = BuffReadString(pbData, cbData, &iData, &scz); + ExitOnFailure(hr, "Failed to read payload id."); + + if (scz && *scz) + { + hr = PayloadFindById(pPayloads, scz, &pPayload); + ExitOnFailure(hr, "Failed to find payload: %ls", scz); + } + + if (pPackage && pPayload) + { + hr = CacheGetCompletedPath(TRUE, pPackage->sczCacheId, &sczCacheDirectory); + ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", pPackage->sczCacheId); + + hr = CacheVerifyPayload(pPayload, sczCacheDirectory, BurnCacheMessageHandler, ElevatedProgressRoutine, hPipe); + } + else + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid data passed to cache verify payload."); + } + // Nothing should be logged on failure. + +LExit: + ReleaseStr(sczCacheDirectory); + ReleaseStr(scz); + + return hr; +} + +static void OnCacheCleanup( + __in_z LPCWSTR wzBundleId + ) +{ + CacheCleanup(TRUE, wzBundleId); +} + +static HRESULT OnProcessDependentRegistration( + __in const BURN_REGISTRATION* pRegistration, + __in BYTE* pbData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + BURN_DEPENDENT_REGISTRATION_ACTION action = { }; + + // Deserialize message data. + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&action.type); + ExitOnFailure(hr, "Failed to read action type."); + + hr = BuffReadString(pbData, cbData, &iData, &action.sczBundleId); + ExitOnFailure(hr, "Failed to read bundle id."); + + hr = BuffReadString(pbData, cbData, &iData, &action.sczDependentProviderKey); + ExitOnFailure(hr, "Failed to read dependent provider key."); + + // Execute the registration action. + hr = DependencyProcessDependentRegistration(pRegistration, &action); + ExitOnFailure(hr, "Failed to execute dependent registration action for provider key: %ls", action.sczDependentProviderKey); + +LExit: + // TODO: do the right thing here. + //DependencyUninitializeRegistrationAction(&action); + ReleaseStr(action.sczDependentProviderKey); + ReleaseStr(action.sczBundleId) + + return hr; +} + +static HRESULT OnExecuteExePackage( + __in HANDLE hPipe, + __in BURN_PACKAGES* pPackages, + __in BURN_RELATED_BUNDLES* pRelatedBundles, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczPackage = NULL; + DWORD dwRollback = 0; + BURN_EXECUTE_ACTION executeAction = { }; + LPWSTR sczIgnoreDependencies = NULL; + LPWSTR sczAncestors = NULL; + BOOTSTRAPPER_APPLY_RESTART exeRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; + + executeAction.type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &sczPackage); + ExitOnFailure(hr, "Failed to read EXE package id."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.exePackage.action); + ExitOnFailure(hr, "Failed to read action."); + + hr = BuffReadNumber(pbData, cbData, &iData, &dwRollback); + ExitOnFailure(hr, "Failed to read rollback."); + + hr = BuffReadString(pbData, cbData, &iData, &sczIgnoreDependencies); + ExitOnFailure(hr, "Failed to read the list of dependencies to ignore."); + + hr = BuffReadString(pbData, cbData, &iData, &sczAncestors); + ExitOnFailure(hr, "Failed to read the list of ancestors."); + + hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); + ExitOnFailure(hr, "Failed to read variables."); + + hr = PackageFindById(pPackages, sczPackage, &executeAction.exePackage.pPackage); + if (E_NOTFOUND == hr) + { + hr = PackageFindRelatedById(pRelatedBundles, sczPackage, &executeAction.exePackage.pPackage); + } + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); + + // Pass the list of dependencies to ignore, if any, to the related bundle. + if (sczIgnoreDependencies && *sczIgnoreDependencies) + { + hr = StrAllocString(&executeAction.exePackage.sczIgnoreDependencies, sczIgnoreDependencies, 0); + ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore."); + } + + // Pass the list of ancestors, if any, to the related bundle. + if (sczAncestors && *sczAncestors) + { + hr = StrAllocString(&executeAction.exePackage.sczAncestors, sczAncestors, 0); + ExitOnFailure(hr, "Failed to allocate the list of ancestors."); + } + + // Execute EXE package. + hr = ExeEngineExecutePackage(&executeAction, pVariables, static_cast(dwRollback), GenericExecuteMessageHandler, hPipe, &exeRestart); + ExitOnFailure(hr, "Failed to execute EXE package."); + +LExit: + ReleaseStr(sczAncestors); + ReleaseStr(sczIgnoreDependencies); + ReleaseStr(sczPackage); + PlanUninitializeExecuteAction(&executeAction); + + if (SUCCEEDED(hr)) + { + if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == exeRestart) + { + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); + } + else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == exeRestart) + { + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED); + } + } + + return hr; +} + +static HRESULT OnExecuteMsiPackage( + __in HANDLE hPipe, + __in BURN_PACKAGES* pPackages, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczPackage = NULL; + HWND hwndParent = NULL; + BOOL fRollback = 0; + BURN_EXECUTE_ACTION executeAction = { }; + BOOTSTRAPPER_APPLY_RESTART msiRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; + + executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE; + + // Deserialize message data. + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback); + ExitOnFailure(hr, "Failed to read rollback flag."); + + hr = BuffReadString(pbData, cbData, &iData, &sczPackage); + ExitOnFailure(hr, "Failed to read MSI package id."); + + hr = PackageFindById(pPackages, sczPackage, &executeAction.msiPackage.pPackage); + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); + + hr = BuffReadPointer(pbData, cbData, &iData, (DWORD_PTR*)&hwndParent); + ExitOnFailure(hr, "Failed to read parent hwnd."); + + hr = BuffReadString(pbData, cbData, &iData, &executeAction.msiPackage.sczLogPath); + ExitOnFailure(hr, "Failed to read package log."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.actionMsiProperty); + ExitOnFailure(hr, "Failed to read actionMsiProperty."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.uiLevel); + ExitOnFailure(hr, "Failed to read UI level."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.fDisableExternalUiHandler); + ExitOnFailure(hr, "Failed to read fDisableExternalUiHandler."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.action); + ExitOnFailure(hr, "Failed to read action."); + + // Read feature actions. + if (executeAction.msiPackage.pPackage->Msi.cFeatures) + { + executeAction.msiPackage.rgFeatures = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(executeAction.msiPackage.pPackage->Msi.cFeatures * sizeof(BOOTSTRAPPER_FEATURE_ACTION), TRUE); + ExitOnNull(executeAction.msiPackage.rgFeatures, hr, E_OUTOFMEMORY, "Failed to allocate memory for feature actions."); + + for (DWORD i = 0; i < executeAction.msiPackage.pPackage->Msi.cFeatures; ++i) + { + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.rgFeatures[i]); + ExitOnFailure(hr, "Failed to read feature action."); + } + } + + // Read slipstream patches actions. + if (executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages) + { + for (DWORD i = 0; i < executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++i) + { + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = executeAction.msiPackage.pPackage->Msi.rgSlipstreamMsps + i; + BOOTSTRAPPER_ACTION_STATE* pAction = fRollback ? &pSlipstreamMsp->rollback : &pSlipstreamMsp->execute; + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)pAction); + ExitOnFailure(hr, "Failed to read slipstream action."); + } + } + + hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); + ExitOnFailure(hr, "Failed to read variables."); + + // Execute MSI package. + hr = MsiEngineExecutePackage(hwndParent, &executeAction, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &msiRestart); + ExitOnFailure(hr, "Failed to execute MSI package."); + +LExit: + ReleaseStr(sczPackage); + PlanUninitializeExecuteAction(&executeAction); + + if (SUCCEEDED(hr)) + { + if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == msiRestart) + { + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); + } + else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == msiRestart) + { + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED); + } + } + + return hr; +} + +static HRESULT OnExecuteMspPackage( + __in HANDLE hPipe, + __in BURN_PACKAGES* pPackages, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczPackage = NULL; + HWND hwndParent = NULL; + BOOL fRollback = 0; + BURN_EXECUTE_ACTION executeAction = { }; + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; + + executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSP_TARGET; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &sczPackage); + ExitOnFailure(hr, "Failed to read MSP package id."); + + hr = PackageFindById(pPackages, sczPackage, &executeAction.mspTarget.pPackage); + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); + + hr = BuffReadPointer(pbData, cbData, &iData, (DWORD_PTR*)&hwndParent); + ExitOnFailure(hr, "Failed to read parent hwnd."); + + executeAction.mspTarget.fPerMachineTarget = TRUE; // we're in the elevated process, clearly we're targeting a per-machine product. + + hr = BuffReadString(pbData, cbData, &iData, &executeAction.mspTarget.sczTargetProductCode); + ExitOnFailure(hr, "Failed to read target product code."); + + hr = BuffReadString(pbData, cbData, &iData, &executeAction.mspTarget.sczLogPath); + ExitOnFailure(hr, "Failed to read package log."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.actionMsiProperty); + ExitOnFailure(hr, "Failed to read actionMsiProperty."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.uiLevel); + ExitOnFailure(hr, "Failed to read UI level."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.fDisableExternalUiHandler); + ExitOnFailure(hr, "Failed to read fDisableExternalUiHandler."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.action); + ExitOnFailure(hr, "Failed to read action."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.cOrderedPatches); + ExitOnFailure(hr, "Failed to read count of ordered patches."); + + if (executeAction.mspTarget.cOrderedPatches) + { + executeAction.mspTarget.rgOrderedPatches = (BURN_ORDERED_PATCHES*)MemAlloc(executeAction.mspTarget.cOrderedPatches * sizeof(BURN_ORDERED_PATCHES), TRUE); + ExitOnNull(executeAction.mspTarget.rgOrderedPatches, hr, E_OUTOFMEMORY, "Failed to allocate memory for ordered patches."); + + for (DWORD i = 0; i < executeAction.mspTarget.cOrderedPatches; ++i) + { + hr = BuffReadString(pbData, cbData, &iData, &sczPackage); + ExitOnFailure(hr, "Failed to read ordered patch package id."); + + hr = PackageFindById(pPackages, sczPackage, &executeAction.mspTarget.rgOrderedPatches[i].pPackage); + ExitOnFailure(hr, "Failed to find ordered patch package: %ls", sczPackage); + } + } + + hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); + ExitOnFailure(hr, "Failed to read variables."); + + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback); + ExitOnFailure(hr, "Failed to read rollback flag."); + + // Execute MSP package. + hr = MspEngineExecutePackage(hwndParent, &executeAction, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &restart); + ExitOnFailure(hr, "Failed to execute MSP package."); + +LExit: + ReleaseStr(sczPackage); + PlanUninitializeExecuteAction(&executeAction); + + if (SUCCEEDED(hr)) + { + if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == restart) + { + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); + } + else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart) + { + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED); + } + } + + return hr; +} + +static HRESULT OnExecuteMsuPackage( + __in HANDLE hPipe, + __in BURN_PACKAGES* pPackages, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczPackage = NULL; + DWORD dwRollback = 0; + DWORD dwStopWusaService = 0; + BURN_EXECUTE_ACTION executeAction = { }; + BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; + + executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &sczPackage); + ExitOnFailure(hr, "Failed to read MSU package id."); + + hr = BuffReadString(pbData, cbData, &iData, &executeAction.msuPackage.sczLogPath); + ExitOnFailure(hr, "Failed to read package log."); + + hr = BuffReadNumber(pbData, cbData, &iData, reinterpret_cast(&executeAction.msuPackage.action)); + ExitOnFailure(hr, "Failed to read action."); + + hr = BuffReadNumber(pbData, cbData, &iData, &dwRollback); + ExitOnFailure(hr, "Failed to read rollback."); + + hr = BuffReadNumber(pbData, cbData, &iData, &dwStopWusaService); + ExitOnFailure(hr, "Failed to read StopWusaService."); + + hr = PackageFindById(pPackages, sczPackage, &executeAction.msuPackage.pPackage); + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); + + // execute MSU package + hr = MsuEngineExecutePackage(&executeAction, pVariables, static_cast(dwRollback), static_cast(dwStopWusaService), GenericExecuteMessageHandler, hPipe, &restart); + ExitOnFailure(hr, "Failed to execute MSU package."); + +LExit: + ReleaseStr(sczPackage); + PlanUninitializeExecuteAction(&executeAction); + + if (SUCCEEDED(hr)) + { + if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == restart) + { + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); + } + else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart) + { + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED); + } + } + + return hr; +} + +static HRESULT OnExecutePackageProviderAction( + __in BURN_PACKAGES* pPackages, + __in BURN_RELATED_BUNDLES* pRelatedBundles, + __in BYTE* pbData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczPackage = NULL; + BURN_EXECUTE_ACTION executeAction = { }; + + executeAction.type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER; + + // Deserialize the message data. + hr = BuffReadString(pbData, cbData, &iData, &sczPackage); + ExitOnFailure(hr, "Failed to read package id from message buffer."); + + hr = BuffReadNumber(pbData, cbData, &iData, reinterpret_cast(&executeAction.packageProvider.action)); + ExitOnFailure(hr, "Failed to read action."); + + // Find the package again. + hr = PackageFindById(pPackages, sczPackage, &executeAction.packageProvider.pPackage); + if (E_NOTFOUND == hr) + { + hr = PackageFindRelatedById(pRelatedBundles, sczPackage, &executeAction.packageProvider.pPackage); + } + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); + + // Execute the package provider action. + hr = DependencyExecutePackageProviderAction(&executeAction); + ExitOnFailure(hr, "Failed to execute package provider action."); + +LExit: + ReleaseStr(sczPackage); + PlanUninitializeExecuteAction(&executeAction); + + return hr; +} + +static HRESULT OnExecutePackageDependencyAction( + __in BURN_PACKAGES* pPackages, + __in BURN_RELATED_BUNDLES* pRelatedBundles, + __in BYTE* pbData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczPackage = NULL; + BURN_EXECUTE_ACTION executeAction = { }; + + executeAction.type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY; + + // Deserialize the message data. + hr = BuffReadString(pbData, cbData, &iData, &sczPackage); + ExitOnFailure(hr, "Failed to read package id from message buffer."); + + hr = BuffReadString(pbData, cbData, &iData, &executeAction.packageDependency.sczBundleProviderKey); + ExitOnFailure(hr, "Failed to read bundle dependency key from message buffer."); + + hr = BuffReadNumber(pbData, cbData, &iData, reinterpret_cast(&executeAction.packageDependency.action)); + ExitOnFailure(hr, "Failed to read action."); + + // Find the package again. + hr = PackageFindById(pPackages, sczPackage, &executeAction.packageDependency.pPackage); + if (E_NOTFOUND == hr) + { + hr = PackageFindRelatedById(pRelatedBundles, sczPackage, &executeAction.packageDependency.pPackage); + } + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); + + // Execute the package dependency action. + hr = DependencyExecutePackageDependencyAction(TRUE, &executeAction); + ExitOnFailure(hr, "Failed to execute package dependency action."); + +LExit: + ReleaseStr(sczPackage); + PlanUninitializeExecuteAction(&executeAction); + + return hr; +} + +static HRESULT CALLBACK BurnCacheMessageHandler( + __in BURN_CACHE_MESSAGE* pMessage, + __in LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + DWORD dwResult = 0; + HANDLE hPipe = (HANDLE)pvContext; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwMessage = 0; + + switch (pMessage->type) + { + case BURN_CACHE_MESSAGE_BEGIN: + // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, pMessage->begin.cacheStep); + ExitOnFailure(hr, "Failed to write progress percentage to message buffer."); + + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_BEGIN; + break; + + case BURN_CACHE_MESSAGE_COMPLETE: + // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, pMessage->complete.hrStatus); + ExitOnFailure(hr, "Failed to write error code to message buffer."); + + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_COMPLETE; + break; + + case BURN_CACHE_MESSAGE_SUCCESS: + hr = BuffWriteNumber64(&pbData, &cbData, pMessage->success.qwFileSize); + ExitOnFailure(hr, "Failed to count of files in use to message buffer."); + + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_SUCCESS; + break; + } + + // send message + hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send burn cache message to per-user process."); + + hr = dwResult; + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +static DWORD CALLBACK ElevatedProgressRoutine( + __in LARGE_INTEGER TotalFileSize, + __in LARGE_INTEGER TotalBytesTransferred, + __in LARGE_INTEGER /*StreamSize*/, + __in LARGE_INTEGER /*StreamBytesTransferred*/, + __in DWORD /*dwStreamNumber*/, + __in DWORD /*dwCallbackReason*/, + __in HANDLE /*hSourceFile*/, + __in HANDLE /*hDestinationFile*/, + __in_opt LPVOID lpData + ) +{ + HRESULT hr = S_OK; + DWORD dwResult = 0; + HANDLE hPipe = (HANDLE)lpData; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwMessage = BURN_ELEVATION_MESSAGE_TYPE_PROGRESS_ROUTINE; + + hr = BuffWriteNumber64(&pbData, &cbData, TotalFileSize.QuadPart); + ExitOnFailure(hr, "Failed to write total file size progress to message buffer."); + + hr = BuffWriteNumber64(&pbData, &cbData, TotalBytesTransferred.QuadPart); + ExitOnFailure(hr, "Failed to write total bytes transferred progress to message buffer."); + + // send message + hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send progress routine message to per-user process."); + +LExit: + ReleaseBuffer(pbData); + + return dwResult; +} + +static int GenericExecuteMessageHandler( + __in GENERIC_EXECUTE_MESSAGE* pMessage, + __in LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + int nResult = IDOK; + HANDLE hPipe = (HANDLE)pvContext; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwMessage = 0; + + hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwAllowedResults); + ExitOnFailure(hr, "Failed to write UI flags."); + + switch(pMessage->type) + { + case GENERIC_EXECUTE_MESSAGE_PROGRESS: + // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, pMessage->progress.dwPercentage); + ExitOnFailure(hr, "Failed to write progress percentage to message buffer."); + + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS; + break; + + case GENERIC_EXECUTE_MESSAGE_ERROR: + // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, pMessage->error.dwErrorCode); + ExitOnFailure(hr, "Failed to write error code to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pMessage->error.wzMessage); + ExitOnFailure(hr, "Failed to write message to message buffer."); + + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR; + break; + + case GENERIC_EXECUTE_MESSAGE_FILES_IN_USE: + hr = BuffWriteNumber(&pbData, &cbData, pMessage->filesInUse.cFiles); + ExitOnFailure(hr, "Failed to count of files in use to message buffer."); + + for (DWORD i = 0; i < pMessage->filesInUse.cFiles; ++i) + { + hr = BuffWriteString(&pbData, &cbData, pMessage->filesInUse.rgwzFiles[i]); + ExitOnFailure(hr, "Failed to write file in use to message buffer."); + } + + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE; + break; + } + + // send message + hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, reinterpret_cast(&nResult)); + ExitOnFailure(hr, "Failed to send message to per-user process."); + +LExit: + ReleaseBuffer(pbData); + + return nResult; +} + +static int MsiExecuteMessageHandler( + __in WIU_MSI_EXECUTE_MESSAGE* pMessage, + __in_opt LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + int nResult = IDOK; + HANDLE hPipe = (HANDLE)pvContext; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwMessage = 0; + + // Always send any extra data via the struct first. + hr = BuffWriteNumber(&pbData, &cbData, pMessage->cData); + ExitOnFailure(hr, "Failed to write MSI data count to message buffer."); + + for (DWORD i = 0; i < pMessage->cData; ++i) + { + hr = BuffWriteString(&pbData, &cbData, pMessage->rgwzData[i]); + ExitOnFailure(hr, "Failed to write MSI data to message buffer."); + } + + hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwAllowedResults); + ExitOnFailure(hr, "Failed to write UI flags."); + + switch (pMessage->type) + { + case WIU_MSI_EXECUTE_MESSAGE_PROGRESS: + // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, pMessage->progress.dwPercentage); + ExitOnFailure(hr, "Failed to write progress percentage to message buffer."); + + // set message id + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS; + break; + + case WIU_MSI_EXECUTE_MESSAGE_ERROR: + // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, pMessage->error.dwErrorCode); + ExitOnFailure(hr, "Failed to write error code to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pMessage->error.wzMessage); + ExitOnFailure(hr, "Failed to write message to message buffer."); + + // set message id + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR; + break; + + case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE: + // serialize message data + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pMessage->msiMessage.mt); + ExitOnFailure(hr, "Failed to write MSI message type to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pMessage->msiMessage.wzMessage); + ExitOnFailure(hr, "Failed to write message to message buffer."); + + // set message id + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE; + break; + + case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE: + // NOTE: we do not serialize other message data here because all the "files in use" are in the data above. + + // set message id + dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE; + break; + + default: + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Invalid message type: %d", pMessage->type); + } + + // send message + hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, (DWORD*)&nResult); + ExitOnFailure(hr, "Failed to send msi message to per-user process."); + +LExit: + ReleaseBuffer(pbData); + + return nResult; +} + +static HRESULT OnCleanPackage( + __in BURN_PACKAGES* pPackages, + __in BYTE* pbData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczPackage = NULL; + BURN_PACKAGE* pPackage = NULL; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &sczPackage); + ExitOnFailure(hr, "Failed to read package id."); + + hr = PackageFindById(pPackages, sczPackage, &pPackage); + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); + + // Remove the package from the cache. + hr = CacheRemovePackage(TRUE, pPackage->sczId, pPackage->sczCacheId); + ExitOnFailure(hr, "Failed to remove from cache package: %ls", pPackage->sczId); + +LExit: + ReleaseStr(sczPackage); + return hr; +} + +static HRESULT OnLaunchApprovedExe( + __in HANDLE hPipe, + __in BURN_APPROVED_EXES* pApprovedExes, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe = NULL; + BURN_APPROVED_EXE* pApprovedExe = NULL; + REGSAM samDesired = KEY_QUERY_VALUE; + HKEY hKey = NULL; + DWORD dwProcessId = 0; + BYTE* pbSendData = NULL; + SIZE_T cbSendData = 0; + DWORD dwResult = 0; + + pLaunchApprovedExe = (BURN_LAUNCH_APPROVED_EXE*)MemAlloc(sizeof(BURN_LAUNCH_APPROVED_EXE), TRUE); + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &pLaunchApprovedExe->sczId); + ExitOnFailure(hr, "Failed to read approved exe id."); + + hr = BuffReadString(pbData, cbData, &iData, &pLaunchApprovedExe->sczArguments); + ExitOnFailure(hr, "Failed to read approved exe arguments."); + + hr = BuffReadNumber(pbData, cbData, &iData, &pLaunchApprovedExe->dwWaitForInputIdleTimeout); + ExitOnFailure(hr, "Failed to read approved exe WaitForInputIdle timeout."); + + hr = ApprovedExesFindById(pApprovedExes, pLaunchApprovedExe->sczId, &pApprovedExe); + ExitOnFailure(hr, "The per-user process requested unknown approved exe with id: %ls", pLaunchApprovedExe->sczId); + + LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_SEARCH, pApprovedExe->sczKey, pApprovedExe->sczValueName ? pApprovedExe->sczValueName : L"", pApprovedExe->fWin64 ? L"yes" : L"no"); + + if (pApprovedExe->fWin64) + { + samDesired |= KEY_WOW64_64KEY; + } + + hr = RegOpen(HKEY_LOCAL_MACHINE, pApprovedExe->sczKey, samDesired, &hKey); + ExitOnFailure(hr, "Failed to open the registry key for the approved exe path."); + + hr = RegReadString(hKey, pApprovedExe->sczValueName, &pLaunchApprovedExe->sczExecutablePath); + ExitOnFailure(hr, "Failed to read the value for the approved exe path."); + + hr = ApprovedExesVerifySecureLocation(pVariables, pLaunchApprovedExe); + ExitOnFailure(hr, "Failed to verify the executable path is in a secure location: %ls", pLaunchApprovedExe->sczExecutablePath); + if (S_FALSE == hr) + { + LogStringLine(REPORT_STANDARD, "The executable path is not in a secure location: %ls", pLaunchApprovedExe->sczExecutablePath); + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)); + } + + hr = ApprovedExesLaunch(pVariables, pLaunchApprovedExe, &dwProcessId); + ExitOnFailure(hr, "Failed to launch approved exe: %ls", pLaunchApprovedExe->sczExecutablePath); + + //send process id over pipe + hr = BuffWriteNumber(&pbSendData, &cbSendData, dwProcessId); + ExitOnFailure(hr, "Failed to write the approved exe process id to message buffer."); + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID, pbSendData, cbSendData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID message to per-user process."); + +LExit: + ReleaseBuffer(pbSendData); + ApprovedExesUninitializeLaunch(pLaunchApprovedExe); + return hr; +} + +static HRESULT OnMsiBeginTransaction( + __in BURN_PACKAGES* pPackages, + __in BYTE* pbData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczId = NULL; + LPWSTR sczLogPath = NULL; + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &sczId); + ExitOnFailure(hr, "Failed to read rollback boundary id."); + + hr = BuffReadString(pbData, cbData, &iData, &sczLogPath); + ExitOnFailure(hr, "Failed to read transaction log path."); + + hr = PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary); + ExitOnFailure(hr, "Failed to find rollback boundary: %ls", sczId); + + pRollbackBoundary->sczLogPath = sczLogPath; + + hr = MsiEngineBeginTransaction(pRollbackBoundary); + +LExit: + ReleaseStr(sczId); + ReleaseStr(sczLogPath); + + if (pRollbackBoundary) + { + pRollbackBoundary->sczLogPath = NULL; + } + + return hr; +} + +static HRESULT OnMsiCommitTransaction( + __in BURN_PACKAGES* pPackages, + __in BYTE* pbData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczId = NULL; + LPWSTR sczLogPath = NULL; + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &sczId); + ExitOnFailure(hr, "Failed to read rollback boundary id."); + + hr = BuffReadString(pbData, cbData, &iData, &sczLogPath); + ExitOnFailure(hr, "Failed to read transaction log path."); + + hr = PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary); + ExitOnFailure(hr, "Failed to find rollback boundary: %ls", sczId); + + pRollbackBoundary->sczLogPath = sczLogPath; + + hr = MsiEngineCommitTransaction(pRollbackBoundary); + +LExit: + ReleaseStr(sczId); + ReleaseStr(sczLogPath); + + if (pRollbackBoundary) + { + pRollbackBoundary->sczLogPath = NULL; + } + + return hr; +} + +static HRESULT OnMsiRollbackTransaction( + __in BURN_PACKAGES* pPackages, + __in BYTE* pbData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + LPWSTR sczId = NULL; + LPWSTR sczLogPath = NULL; + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; + + // Deserialize message data. + hr = BuffReadString(pbData, cbData, &iData, &sczId); + ExitOnFailure(hr, "Failed to read rollback boundary id."); + + hr = BuffReadString(pbData, cbData, &iData, &sczLogPath); + ExitOnFailure(hr, "Failed to read transaction log path."); + + hr = PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary); + ExitOnFailure(hr, "Failed to find rollback boundary: %ls", sczId); + + pRollbackBoundary->sczLogPath = sczLogPath; + + hr = MsiEngineRollbackTransaction(pRollbackBoundary); + +LExit: + ReleaseStr(sczId); + ReleaseStr(sczLogPath); + + if (pRollbackBoundary) + { + pRollbackBoundary->sczLogPath = NULL; + } + + return hr; +} + +static HRESULT ElevatedOnPauseAUBegin( + __in HANDLE hPipe + ) +{ + HRESULT hr = S_OK; + DWORD dwResult = 0; + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_BEGIN, NULL, 0, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_BEGIN message to per-user process."); + +LExit: + return hr; +} + +static HRESULT ElevatedOnPauseAUComplete( + __in HANDLE hPipe, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BYTE* pbSendData = NULL; + SIZE_T cbSendData = 0; + DWORD dwResult = 0; + + hr = BuffWriteNumber(&pbSendData, &cbSendData, hrStatus); + ExitOnFailure(hr, "Failed to write the pause au status to message buffer."); + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_COMPLETE, pbSendData, cbSendData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_COMPLETE message to per-user process."); + +LExit: + ReleaseBuffer(pbSendData); + + return hr; +} + +static HRESULT ElevatedOnSystemRestorePointBegin( + __in HANDLE hPipe + ) +{ + HRESULT hr = S_OK; + DWORD dwResult = 0; + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_BEGIN, NULL, 0, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_BEGIN message to per-user process."); + +LExit: + return hr; +} + +static HRESULT ElevatedOnSystemRestorePointComplete( + __in HANDLE hPipe, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BYTE* pbSendData = NULL; + SIZE_T cbSendData = 0; + DWORD dwResult = 0; + + hr = BuffWriteNumber(&pbSendData, &cbSendData, hrStatus); + ExitOnFailure(hr, "Failed to write the system restore point status to message buffer."); + + hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_COMPLETE, pbSendData, cbSendData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_COMPLETE message to per-user process."); + +LExit: + ReleaseBuffer(pbSendData); + + return hr; +} diff --git a/src/burn/engine/elevation.h b/src/burn/engine/elevation.h new file mode 100644 index 00000000..9244f36c --- /dev/null +++ b/src/burn/engine/elevation.h @@ -0,0 +1,176 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + + +// Parent (per-user process) side functions. +HRESULT ElevationElevate( + __in BURN_ENGINE_STATE* pEngineState, + __in_opt HWND hwndParent + ); +HRESULT ElevationApplyInitialize( + __in HANDLE hPipe, + __in BURN_USER_EXPERIENCE* pBA, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_ACTION action, + __in BURN_AU_PAUSE_ACTION auAction, + __in BOOL fTakeSystemRestorePoint + ); +HRESULT ElevationApplyUninitialize( + __in HANDLE hPipe + ); +HRESULT ElevationSessionBegin( + __in HANDLE hPipe, + __in_z LPCWSTR wzEngineWorkingPath, + __in_z LPCWSTR wzResumeCommandLine, + __in BOOL fDisableResume, + __in BURN_VARIABLES* pVariables, + __in DWORD dwRegistrationOperations, + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction, + __in DWORD64 qwEstimatedSize + ); +HRESULT ElevationSessionResume( + __in HANDLE hPipe, + __in_z LPCWSTR wzResumeCommandLine, + __in BOOL fDisableResume, + __in BURN_VARIABLES* pVariables + ); +HRESULT ElevationSessionEnd( + __in HANDLE hPipe, + __in BURN_RESUME_MODE resumeMode, + __in BOOTSTRAPPER_APPLY_RESTART restart, + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction + ); +HRESULT ElevationSaveState( + __in HANDLE hPipe, + __in_bcount(cbBuffer) BYTE* pbBuffer, + __in SIZE_T cbBuffer + ); +HRESULT ElevationCacheCompletePayload( + __in HANDLE hPipe, + __in BURN_PACKAGE* pPackage, + __in BURN_PAYLOAD* pPayload, + __in_z LPCWSTR wzUnverifiedPath, + __in BOOL fMove, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ); +HRESULT ElevationCacheVerifyPayload( + __in HANDLE hPipe, + __in BURN_PACKAGE* pPackage, + __in BURN_PAYLOAD* pPayload, + __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, + __in LPPROGRESS_ROUTINE pfnProgress, + __in LPVOID pContext + ); +HRESULT ElevationCacheCleanup( + __in HANDLE hPipe + ); +HRESULT ElevationProcessDependentRegistration( + __in HANDLE hPipe, + __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction + ); +HRESULT ElevationExecuteExePackage( + __in HANDLE hPipe, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +HRESULT ElevationExecuteMsiPackage( + __in HANDLE hPipe, + __in_opt HWND hwndParent, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +HRESULT ElevationExecuteMspPackage( + __in HANDLE hPipe, + __in_opt HWND hwndParent, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +HRESULT ElevationExecuteMsuPackage( + __in HANDLE hPipe, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BOOL fRollback, + __in BOOL fStopWusaService, + __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +HRESULT ElevationExecutePackageProviderAction( + __in HANDLE hPipe, + __in BURN_EXECUTE_ACTION* pExecuteAction + ); +HRESULT ElevationExecutePackageDependencyAction( + __in HANDLE hPipe, + __in BURN_EXECUTE_ACTION* pExecuteAction + ); +HRESULT ElevationLaunchElevatedChild( + __in HANDLE hPipe, + __in BURN_PACKAGE* pPackage, + __in LPCWSTR wzPipeName, + __in LPCWSTR wzPipeToken, + __out DWORD* pdwChildPid + ); +HRESULT ElevationCleanPackage( + __in HANDLE hPipe, + __in BURN_PACKAGE* pPackage + ); +HRESULT ElevationLaunchApprovedExe( + __in HANDLE hPipe, + __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe, + __out DWORD* pdwProcessId + ); + +// Child (per-machine process) side functions. +HRESULT ElevationChildPumpMessages( + __in DWORD dwLoggingTlsId, + __in HANDLE hPipe, + __in HANDLE hCachePipe, + __in BURN_APPROVED_EXES* pApprovedExes, + __in BURN_CONTAINERS* pContainers, + __in BURN_PACKAGES* pPackages, + __in BURN_PAYLOADS* pPayloads, + __in BURN_VARIABLES* pVariables, + __in BURN_REGISTRATION* pRegistration, + __in BURN_USER_EXPERIENCE* pUserExperience, + __out HANDLE* phLock, + __out BOOL* pfDisabledAutomaticUpdates, + __out DWORD* pdwChildExitCode, + __out BOOL* pfRestart + ); +HRESULT ElevationChildResumeAutomaticUpdates(); + + +HRESULT ElevationMsiBeginTransaction( + __in HANDLE hPipe, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary + ); +HRESULT ElevationMsiCommitTransaction( + __in HANDLE hPipe, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary + ); +HRESULT ElevationMsiRollbackTransaction( + __in HANDLE hPipe, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/burn/engine/embedded.cpp b/src/burn/engine/embedded.cpp new file mode 100644 index 00000000..03898ebd --- /dev/null +++ b/src/burn/engine/embedded.cpp @@ -0,0 +1,197 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// struct + +struct BURN_EMBEDDED_CALLBACK_CONTEXT +{ + PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler; + LPVOID pvContext; +}; + +// internal function declarations + +static HRESULT ProcessEmbeddedMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); +static HRESULT OnEmbeddedErrorMessage( + __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __in_bcount(cbData) BYTE* pbData, + __in SIZE_T cbData, + __out DWORD* pdwResult + ); +static HRESULT OnEmbeddedProgress( + __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __in_bcount(cbData) BYTE* pbData, + __in SIZE_T cbData, + __out DWORD* pdwResult + ); + +// function definitions + +/******************************************************************* + EmbeddedLaunchChildProcess - + +*******************************************************************/ +extern "C" HRESULT EmbeddedRunBundle( + __in LPCWSTR wzExecutablePath, + __in LPCWSTR wzArguments, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __out DWORD* pdwExitCode + ) +{ + HRESULT hr = S_OK; + DWORD dwCurrentProcessId = ::GetCurrentProcessId(); + HANDLE hCreatedPipesEvent = NULL; + LPWSTR sczCommand = NULL; + STARTUPINFOW si = { }; + PROCESS_INFORMATION pi = { }; + BURN_PIPE_RESULT result = { }; + + BURN_PIPE_CONNECTION connection = { }; + PipeConnectionInitialize(&connection); + + BURN_EMBEDDED_CALLBACK_CONTEXT context = { }; + context.pfnGenericMessageHandler = pfnGenericMessageHandler; + context.pvContext = pvContext; + + hr = PipeCreateNameAndSecret(&connection.sczName, &connection.sczSecret); + ExitOnFailure(hr, "Failed to create embedded pipe name and client token."); + + hr = PipeCreatePipes(&connection, FALSE, &hCreatedPipesEvent); + ExitOnFailure(hr, "Failed to create embedded pipe."); + + hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls %ls %ls %u", wzArguments, BURN_COMMANDLINE_SWITCH_EMBEDDED, connection.sczName, connection.sczSecret, dwCurrentProcessId); + ExitOnFailure(hr, "Failed to allocate embedded command."); + + if (!::CreateProcessW(wzExecutablePath, sczCommand, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) + { + ExitWithLastError(hr, "Failed to create embedded process at path: %ls", wzExecutablePath); + } + + connection.dwProcessId = ::GetProcessId(pi.hProcess); + connection.hProcess = pi.hProcess; + pi.hProcess = NULL; + + hr = PipeWaitForChildConnect(&connection); + ExitOnFailure(hr, "Failed to wait for embedded process to connect to pipe."); + + hr = PipePumpMessages(connection.hPipe, ProcessEmbeddedMessages, &context, &result); + ExitOnFailure(hr, "Failed to process messages from embedded message."); + + // Get the return code from the embedded process. + hr = ProcWaitForCompletion(connection.hProcess, INFINITE, pdwExitCode); + ExitOnFailure(hr, "Failed to wait for embedded executable: %ls", wzExecutablePath); + +LExit: + ReleaseHandle(pi.hThread); + ReleaseHandle(pi.hProcess); + + StrSecureZeroFreeString(sczCommand); + ReleaseHandle(hCreatedPipesEvent); + PipeConnectionUninitialize(&connection); + + return hr; +} + + +// internal function definitions + +static HRESULT ProcessEmbeddedMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + BURN_EMBEDDED_CALLBACK_CONTEXT* pContext = static_cast(pvContext); + DWORD dwResult = 0; + + // Process the message. + switch (pMsg->dwMessage) + { + case BURN_EMBEDDED_MESSAGE_TYPE_ERROR: + hr = OnEmbeddedErrorMessage(pContext->pfnGenericMessageHandler, pContext->pvContext, static_cast(pMsg->pvData), pMsg->cbData, &dwResult); + ExitOnFailure(hr, "Failed to process embedded error message."); + break; + + case BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS: + hr = OnEmbeddedProgress(pContext->pfnGenericMessageHandler, pContext->pvContext, static_cast(pMsg->pvData), pMsg->cbData, &dwResult); + ExitOnFailure(hr, "Failed to process embedded progress message."); + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Unexpected embedded message sent to child process, msg: %u", pMsg->dwMessage); + } + + *pdwResult = dwResult; + +LExit: + return hr; +} + +static HRESULT OnEmbeddedErrorMessage( + __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __in_bcount(cbData) BYTE* pbData, + __in SIZE_T cbData, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + GENERIC_EXECUTE_MESSAGE message = { }; + LPWSTR sczMessage = NULL; + + message.type = GENERIC_EXECUTE_MESSAGE_ERROR; + + hr = BuffReadNumber(pbData, cbData, &iData, &message.error.dwErrorCode); + ExitOnFailure(hr, "Failed to read error code from buffer."); + + hr = BuffReadString(pbData, cbData, &iData, &sczMessage); + ExitOnFailure(hr, "Failed to read error message from buffer."); + + message.error.wzMessage = sczMessage; + + hr = BuffReadNumber(pbData, cbData, &iData, &message.dwAllowedResults); + ExitOnFailure(hr, "Failed to read UI hint from buffer."); + + *pdwResult = (DWORD)pfnMessageHandler(&message, pvContext); + +LExit: + ReleaseStr(sczMessage); + + return hr; +} + +static HRESULT OnEmbeddedProgress( + __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __in_bcount(cbData) BYTE* pbData, + __in SIZE_T cbData, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + SIZE_T iData = 0; + GENERIC_EXECUTE_MESSAGE message = { }; + + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; + message.dwAllowedResults = MB_OKCANCEL; + + hr = BuffReadNumber(pbData, cbData, &iData, &message.progress.dwPercentage); + ExitOnFailure(hr, "Failed to read progress from buffer."); + + *pdwResult = (DWORD)pfnMessageHandler(&message, pvContext); + +LExit: + return hr; +} diff --git a/src/burn/engine/embedded.h b/src/burn/engine/embedded.h new file mode 100644 index 00000000..08adeae0 --- /dev/null +++ b/src/burn/engine/embedded.h @@ -0,0 +1,27 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum _BURN_EMBEDDED_MESSAGE_TYPE +{ + BURN_EMBEDDED_MESSAGE_TYPE_UNKNOWN, + BURN_EMBEDDED_MESSAGE_TYPE_ERROR, + BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS, +} BURN_EMBEDDED_MESSAGE_TYPE; + + +HRESULT EmbeddedRunBundle( + __in LPCWSTR wzExecutablePath, + __in LPCWSTR wzArguments, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __out DWORD* pdwExitCode + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/burn/engine/engine.cpp b/src/burn/engine/engine.cpp new file mode 100644 index 00000000..8f024e98 --- /dev/null +++ b/src/burn/engine/engine.cpp @@ -0,0 +1,992 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// constants + +const DWORD RESTART_RETRIES = 10; + +// internal function declarations + +static HRESULT InitializeEngineState( + __in BURN_ENGINE_STATE* pEngineState, + __in HANDLE hEngineFile + ); +static void UninitializeEngineState( + __in BURN_ENGINE_STATE* pEngineState + ); +static HRESULT RunUntrusted( + __in LPCWSTR wzCommandLine, + __in BURN_ENGINE_STATE* pEngineState + ); +static HRESULT RunNormal( + __in HINSTANCE hInstance, + __in BURN_ENGINE_STATE* pEngineState + ); +static HRESULT RunElevated( + __in HINSTANCE hInstance, + __in LPCWSTR wzCommandLine, + __in BURN_ENGINE_STATE* pEngineState + ); +static HRESULT RunEmbedded( + __in HINSTANCE hInstance, + __in BURN_ENGINE_STATE* pEngineState + ); +static HRESULT RunRunOnce( + __in const BURN_REGISTRATION* pRegistration, + __in int nCmdShow + ); +static HRESULT RunApplication( + __in BURN_ENGINE_STATE* pEngineState, + __out BOOL* pfReloadApp, + __out BOOL* pfSkipCleanup + ); +static HRESULT ProcessMessage( + __in BURN_ENGINE_STATE* pEngineState, + __in const MSG* pmsg + ); +static HRESULT DAPI RedirectLoggingOverPipe( + __in_z LPCSTR szString, + __in_opt LPVOID pvContext + ); +static HRESULT Restart(); + + +// function definitions + +extern "C" BOOL EngineInCleanRoom( + __in_z_opt LPCWSTR wzCommandLine + ) +{ + // Be very careful with the functions you call from here. + // This function will be called before ::SetDefaultDllDirectories() + // has been called so dependencies outside of kernel32.dll are + // very likely to introduce DLL hijacking opportunities. + + static DWORD cchCleanRoomSwitch = lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM); + + // This check is wholly dependent on the clean room command line switch being + // present at the beginning of the command line. Since Burn is the only thing + // that should be setting this command line option, that is in our control. + BOOL fInCleanRoom = (wzCommandLine && + (wzCommandLine[0] == L'-' || wzCommandLine[0] == L'/') && + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzCommandLine + 1, cchCleanRoomSwitch, BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, cchCleanRoomSwitch) && + wzCommandLine[1 + cchCleanRoomSwitch] == L'=' + ); + + return fInCleanRoom; +} + +extern "C" HRESULT EngineRun( + __in HINSTANCE hInstance, + __in HANDLE hEngineFile, + __in_z_opt LPCWSTR wzCommandLine, + __in int nCmdShow, + __out DWORD* pdwExitCode + ) +{ + HRESULT hr = S_OK; + BOOL fComInitialized = FALSE; + BOOL fLogInitialized = FALSE; + BOOL fCrypInitialized = FALSE; + BOOL fDpiuInitialized = FALSE; + BOOL fRegInitialized = FALSE; + BOOL fWiuInitialized = FALSE; + BOOL fXmlInitialized = FALSE; + SYSTEM_INFO si = { }; + RTL_OSVERSIONINFOEXW ovix = { }; + LPWSTR sczExePath = NULL; + BOOL fRunNormal = FALSE; + BOOL fRestart = FALSE; + + BURN_ENGINE_STATE engineState = { }; + engineState.command.cbSize = sizeof(BOOTSTRAPPER_COMMAND); + + // Always initialize logging first + LogInitialize(::GetModuleHandleW(NULL)); + fLogInitialized = TRUE; + + // Ensure that log contains approriate level of information +#ifdef _DEBUG + LogSetLevel(REPORT_DEBUG, FALSE); +#else + LogSetLevel(REPORT_VERBOSE, FALSE); // FALSE means don't write an additional text line to the log saying the level changed +#endif + + hr = AppParseCommandLine(wzCommandLine, &engineState.argc, &engineState.argv); + ExitOnFailure(hr, "Failed to parse command line."); + + hr = InitializeEngineState(&engineState, hEngineFile); + ExitOnFailure(hr, "Failed to initialize engine state."); + + engineState.command.nCmdShow = nCmdShow; + + // initialize platform layer + PlatformInitialize(); + + // initialize COM + hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); + ExitOnFailure(hr, "Failed to initialize COM."); + fComInitialized = TRUE; + + // Initialize dutil. + hr = CrypInitialize(); + ExitOnFailure(hr, "Failed to initialize Cryputil."); + fCrypInitialized = TRUE; + + DpiuInitialize(); + fDpiuInitialized = TRUE; + + hr = RegInitialize(); + ExitOnFailure(hr, "Failed to initialize Regutil."); + fRegInitialized = TRUE; + + hr = WiuInitialize(); + ExitOnFailure(hr, "Failed to initialize Wiutil."); + fWiuInitialized = TRUE; + + hr = XmlInitialize(); + ExitOnFailure(hr, "Failed to initialize XML util."); + fXmlInitialized = TRUE; + + hr = OsRtlGetVersion(&ovix); + ExitOnFailure(hr, "Failed to get OS info."); + +#if defined(_M_ARM64) + LPCSTR szBurnPlatform = "ARM64"; +#elif defined(_M_AMD64) + LPCSTR szBurnPlatform = "x64"; +#else + LPCSTR szBurnPlatform = "x86"; +#endif + + LPCSTR szMachinePlatform = "unknown architecture"; + ::GetNativeSystemInfo(&si); + switch (si.wProcessorArchitecture) + { + case PROCESSOR_ARCHITECTURE_AMD64: + szMachinePlatform = "x64"; + break; + case PROCESSOR_ARCHITECTURE_ARM: + szMachinePlatform = "ARM"; + break; + case PROCESSOR_ARCHITECTURE_ARM64: + szMachinePlatform = "ARM64"; + break; + case PROCESSOR_ARCHITECTURE_INTEL: + szMachinePlatform = "x86"; + break; + } + + PathForCurrentProcess(&sczExePath, NULL); // Ignore failure. + LogId(REPORT_STANDARD, MSG_BURN_INFO, szVerMajorMinorBuild, ovix.dwMajorVersion, ovix.dwMinorVersion, ovix.dwBuildNumber, ovix.wServicePackMajor, sczExePath, szBurnPlatform, szMachinePlatform); + ReleaseNullStr(sczExePath); + + // initialize core + hr = CoreInitialize(&engineState); + ExitOnFailure(hr, "Failed to initialize core."); + + // Select run mode. + switch (engineState.mode) + { + case BURN_MODE_UNTRUSTED: + hr = RunUntrusted(wzCommandLine, &engineState); + ExitOnFailure(hr, "Failed to run untrusted mode."); + break; + + case BURN_MODE_NORMAL: + fRunNormal = TRUE; + + hr = RunNormal(hInstance, &engineState); + ExitOnFailure(hr, "Failed to run per-user mode."); + break; + + case BURN_MODE_ELEVATED: + hr = RunElevated(hInstance, wzCommandLine, &engineState); + ExitOnFailure(hr, "Failed to run per-machine mode."); + break; + + case BURN_MODE_EMBEDDED: + fRunNormal = TRUE; + + hr = RunEmbedded(hInstance, &engineState); + ExitOnFailure(hr, "Failed to run embedded mode."); + break; + + case BURN_MODE_RUNONCE: + hr = RunRunOnce(&engineState.registration, nCmdShow); + ExitOnFailure(hr, "Failed to run RunOnce mode."); + break; + + default: + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Invalid run mode."); + } + + // set exit code and remember if we are supposed to restart. + *pdwExitCode = engineState.userExperience.dwExitCode; + fRestart = engineState.fRestart; + +LExit: + ReleaseStr(sczExePath); + + // If anything went wrong but the log was never open, try to open a "failure" log + // and that will dump anything captured in the log memory buffer to the log. + if (FAILED(hr) && BURN_LOGGING_STATE_CLOSED == engineState.log.state) + { + LoggingOpenFailed(); + } + + UserExperienceRemove(&engineState.userExperience); + + CacheRemoveWorkingFolder(engineState.registration.sczId); + CacheUninitialize(); + + // If this is a related bundle (but not an update) suppress restart and return the standard restart error code. + if (fRestart && BOOTSTRAPPER_RELATION_NONE != engineState.command.relationType && BOOTSTRAPPER_RELATION_UPDATE != engineState.command.relationType) + { + LogId(REPORT_STANDARD, MSG_RESTART_ABORTED, LoggingRelationTypeToString(engineState.command.relationType)); + + fRestart = FALSE; + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); + } + + UninitializeEngineState(&engineState); + + if (fXmlInitialized) + { + XmlUninitialize(); + } + + if (fWiuInitialized) + { + WiuUninitialize(); + } + + if (fRegInitialized) + { + RegUninitialize(); + } + + if (fDpiuInitialized) + { + DpiuUninitialize(); + } + + if (fCrypInitialized) + { + CrypUninitialize(); + } + + if (fComInitialized) + { + ::CoUninitialize(); + } + + if (fRunNormal) + { + LogId(REPORT_STANDARD, MSG_EXITING, FAILED(hr) ? (int)hr : *pdwExitCode, LoggingBoolToString(fRestart)); + + if (fRestart) + { + LogId(REPORT_STANDARD, MSG_RESTARTING); + } + } + + if (fLogInitialized) + { + LogClose(FALSE); + } + + if (fRestart) + { + Restart(); + } + + if (fLogInitialized) + { + LogUninitialize(FALSE); + } + + return hr; +} + + +// internal function definitions + +static HRESULT InitializeEngineState( + __in BURN_ENGINE_STATE* pEngineState, + __in HANDLE hEngineFile + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzParam = NULL; + HANDLE hSectionFile = hEngineFile; + HANDLE hSourceEngineFile = INVALID_HANDLE_VALUE; + DWORD64 qw = 0; + + pEngineState->automaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED; + pEngineState->dwElevatedLoggingTlsId = TLS_OUT_OF_INDEXES; + ::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive); + PipeConnectionInitialize(&pEngineState->companionConnection); + PipeConnectionInitialize(&pEngineState->embeddedConnection); + + for (int i = 0; i < pEngineState->argc; ++i) + { + if (pEngineState->argv[i][0] == L'-') + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &pEngineState->argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED), BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED))) + { + wzParam = &pEngineState->argv[i][2 + lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED)]; + if (L'=' != wzParam[-1] || L'\0' == wzParam[0]) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED); + } + + hr = StrStringToUInt64(wzParam, 0, &qw); + ExitOnFailure(hr, "Failed to parse file handle: '%ls'", (wzParam)); + + hSourceEngineFile = (HANDLE)qw; + } + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &pEngineState->argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF), BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF))) + { + wzParam = &pEngineState->argv[i][2 + lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF)]; + if (L'=' != wzParam[-1] || L'\0' == wzParam[0]) + { + ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF); + } + + hr = StrStringToUInt64(wzParam, 0, &qw); + ExitOnFailure(hr, "Failed to parse file handle: '%ls'", (wzParam)); + + hSectionFile = (HANDLE)qw; + } + } + } + + hr = SectionInitialize(&pEngineState->section, hSectionFile, hSourceEngineFile); + ExitOnFailure(hr, "Failed to initialize engine section."); + +LExit: + return hr; +} + +static void UninitializeEngineState( + __in BURN_ENGINE_STATE* pEngineState + ) +{ + if (pEngineState->argv) + { + AppFreeCommandLineArgs(pEngineState->argv); + } + + ReleaseStr(pEngineState->sczIgnoreDependencies); + + PipeConnectionUninitialize(&pEngineState->embeddedConnection); + PipeConnectionUninitialize(&pEngineState->companionConnection); + ReleaseStr(pEngineState->sczBundleEngineWorkingPath) + + ReleaseHandle(pEngineState->hMessageWindowThread); + + BurnExtensionUninitialize(&pEngineState->extensions); + + ::DeleteCriticalSection(&pEngineState->userExperience.csEngineActive); + UserExperienceUninitialize(&pEngineState->userExperience); + + ApprovedExesUninitialize(&pEngineState->approvedExes); + UpdateUninitialize(&pEngineState->update); + VariablesUninitialize(&pEngineState->variables); + SearchesUninitialize(&pEngineState->searches); + RegistrationUninitialize(&pEngineState->registration); + PayloadsUninitialize(&pEngineState->payloads); + PackagesUninitialize(&pEngineState->packages); + SectionUninitialize(&pEngineState->section); + ContainersUninitialize(&pEngineState->containers); + + ReleaseStr(pEngineState->command.wzBootstrapperApplicationDataPath); + ReleaseStr(pEngineState->command.wzBootstrapperWorkingFolder); + ReleaseStr(pEngineState->command.wzLayoutDirectory); + ReleaseStr(pEngineState->command.wzCommandLine); + + ReleaseStr(pEngineState->log.sczExtension); + ReleaseStr(pEngineState->log.sczPrefix); + ReleaseStr(pEngineState->log.sczPath); + ReleaseStr(pEngineState->log.sczPathVariable); + + if (TLS_OUT_OF_INDEXES != pEngineState->dwElevatedLoggingTlsId) + { + ::TlsFree(pEngineState->dwElevatedLoggingTlsId); + } + + // clear struct + memset(pEngineState, 0, sizeof(BURN_ENGINE_STATE)); +} + +static HRESULT RunUntrusted( + __in LPCWSTR wzCommandLine, + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCurrentProcessPath = NULL; + LPWSTR wzCleanRoomBundlePath = NULL; + LPWSTR sczCachedCleanRoomBundlePath = NULL; + LPWSTR sczParameters = NULL; + LPWSTR sczFullCommandLine = NULL; + STARTUPINFOW si = { }; + PROCESS_INFORMATION pi = { }; + HANDLE hFileAttached = NULL; + HANDLE hFileSelf = NULL; + HANDLE hProcess = NULL; + + hr = PathForCurrentProcess(&sczCurrentProcessPath, NULL); + ExitOnFailure(hr, "Failed to get path for current process."); + + BOOL fRunningFromCache = CacheBundleRunningFromCache(); + + // If we're running from the package cache, we're in a secure + // folder (DLLs cannot be inserted here for hijacking purposes) + // so just launch the current process's path as the clean room + // process. Technically speaking, we'd be able to skip creating + // a clean room process at all (since we're already running from + // a secure folder) but it makes the code that only wants to run + // in clean room more complicated if we don't launch an explicit + // clean room process. + if (fRunningFromCache) + { + wzCleanRoomBundlePath = sczCurrentProcessPath; + } + else + { + hr = CacheBundleToCleanRoom(&pEngineState->section, &sczCachedCleanRoomBundlePath); + ExitOnFailure(hr, "Failed to cache to clean room."); + + wzCleanRoomBundlePath = sczCachedCleanRoomBundlePath; + } + + // The clean room switch must always be at the front of the command line so + // the EngineInCleanRoom function will operate correctly. + hr = StrAllocFormatted(&sczParameters, L"-%ls=\"%ls\"", BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, sczCurrentProcessPath); + ExitOnFailure(hr, "Failed to allocate parameters for unelevated process."); + + // Send a file handle for the child Burn process to access the attached container. + hr = CoreAppendFileHandleAttachedToCommandLine(pEngineState->section.hEngineFile, &hFileAttached, &sczParameters); + ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED); + + // Grab a file handle for the child Burn process. + hr = CoreAppendFileHandleSelfToCommandLine(wzCleanRoomBundlePath, &hFileSelf, &sczParameters, NULL); + ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF); + + hr = StrAllocFormattedSecure(&sczParameters, L"%ls %ls", sczParameters, wzCommandLine); + ExitOnFailure(hr, "Failed to append original command line."); + +#ifdef ENABLE_UNELEVATE + // TODO: Pass file handle to unelevated process if this ever gets reenabled. + if (!pEngineState->fDisableUnelevate) + { + // Try to launch unelevated and if that fails for any reason, we'll launch our process normally (even though that may make it elevated). + hr = ProcExecuteAsInteractiveUser(wzCleanRoomBundlePath, sczParameters, &hProcess); + } +#endif + + if (!hProcess) + { + hr = StrAllocFormattedSecure(&sczFullCommandLine, L"\"%ls\" %ls", wzCleanRoomBundlePath, sczParameters); + ExitOnFailure(hr, "Failed to allocate full command-line."); + + si.cb = sizeof(si); + si.wShowWindow = static_cast(pEngineState->command.nCmdShow); + if (!::CreateProcessW(wzCleanRoomBundlePath, sczFullCommandLine, NULL, NULL, TRUE, 0, 0, NULL, &si, &pi)) + { + ExitWithLastError(hr, "Failed to launch clean room process: %ls", sczFullCommandLine); + } + + hProcess = pi.hProcess; + pi.hProcess = NULL; + } + + hr = ProcWaitForCompletion(hProcess, INFINITE, &pEngineState->userExperience.dwExitCode); + ExitOnFailure(hr, "Failed to wait for clean room process: %ls", wzCleanRoomBundlePath); + +LExit: + ReleaseHandle(pi.hThread); + ReleaseFileHandle(hFileSelf); + ReleaseFileHandle(hFileAttached); + ReleaseHandle(hProcess); + StrSecureZeroFreeString(sczFullCommandLine); + StrSecureZeroFreeString(sczParameters); + ReleaseStr(sczCachedCleanRoomBundlePath); + ReleaseStr(sczCurrentProcessPath); + + return hr; +} + +static HRESULT RunNormal( + __in HINSTANCE hInstance, + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + LPWSTR sczOriginalSource = NULL; + LPWSTR sczCopiedOriginalSource = NULL; + BOOL fContinueExecution = TRUE; + BOOL fReloadApp = FALSE; + BOOL fSkipCleanup = FALSE; + BURN_EXTENSION_ENGINE_CONTEXT extensionEngineContext = { }; + + // Initialize logging. + hr = LoggingOpen(&pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->registration.sczDisplayName); + ExitOnFailure(hr, "Failed to open log."); + + // Ensure we're on a supported operating system. + hr = ConditionGlobalCheck(&pEngineState->variables, &pEngineState->condition, pEngineState->command.display, pEngineState->registration.sczDisplayName, &pEngineState->userExperience.dwExitCode, &fContinueExecution); + ExitOnFailure(hr, "Failed to check global conditions"); + + if (!fContinueExecution) + { + LogId(REPORT_STANDARD, MSG_FAILED_CONDITION_CHECK); + + // If the block told us to abort, abort! + ExitFunction1(hr = S_OK); + } + + if (pEngineState->userExperience.fSplashScreen && BOOTSTRAPPER_DISPLAY_NONE < pEngineState->command.display) + { + SplashScreenCreate(hInstance, NULL, &pEngineState->command.hwndSplashScreen); + } + + // Create a top-level window to handle system messages. + hr = UiCreateMessageWindow(hInstance, pEngineState); + ExitOnFailure(hr, "Failed to create the message window."); + + // Query registration state. + hr = CoreQueryRegistration(pEngineState); + ExitOnFailure(hr, "Failed to query registration."); + + // Best effort to set the source of attached containers to BURN_BUNDLE_ORIGINAL_SOURCE. + hr = VariableGetString(&pEngineState->variables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource); + if (SUCCEEDED(hr)) + { + for (DWORD i = 0; i < pEngineState->containers.cContainers; ++i) + { + BURN_CONTAINER* pContainer = pEngineState->containers.rgContainers + i; + if (pContainer->fAttached) + { + hr = StrAllocString(&sczCopiedOriginalSource, sczOriginalSource, 0); + if (SUCCEEDED(hr)) + { + ReleaseNullStr(pContainer->sczSourcePath); + pContainer->sczSourcePath = sczCopiedOriginalSource; + sczCopiedOriginalSource = NULL; + } + } + } + } + hr = S_OK; + + // Set some built-in variables before loading the BA. + hr = PlanSetVariables(pEngineState->command.action, &pEngineState->variables); + ExitOnFailure(hr, "Failed to set action variables."); + + hr = RegistrationSetVariables(&pEngineState->registration, &pEngineState->variables); + ExitOnFailure(hr, "Failed to set registration variables."); + + // If a layout directory was specified on the command-line, set it as a well-known variable. + if (pEngineState->command.wzLayoutDirectory && *pEngineState->command.wzLayoutDirectory) + { + hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_LAYOUT_DIRECTORY, pEngineState->command.wzLayoutDirectory, FALSE, FALSE); + ExitOnFailure(hr, "Failed to set layout directory variable to value provided from command-line."); + } + + // Setup the extension engine. + extensionEngineContext.pEngineState = pEngineState; + + // Load the extensions. + hr = BurnExtensionLoad(&pEngineState->extensions, &extensionEngineContext); + ExitOnFailure(hr, "Failed to load BundleExtensions."); + + do + { + fReloadApp = FALSE; + pEngineState->fQuit = FALSE; + + hr = RunApplication(pEngineState, &fReloadApp, &fSkipCleanup); + ExitOnFailure(hr, "Failed while running "); + } while (fReloadApp); + +LExit: + if (!fSkipCleanup) + { + CoreCleanup(pEngineState); + } + + BurnExtensionUnload(&pEngineState->extensions); + + // If the message window is still around, close it. + UiCloseMessageWindow(pEngineState); + + VariablesDump(&pEngineState->variables); + + // end per-machine process if running + if (INVALID_HANDLE_VALUE != pEngineState->companionConnection.hPipe) + { + PipeTerminateChildProcess(&pEngineState->companionConnection, pEngineState->userExperience.dwExitCode, FALSE); + } + + // If the splash screen is still around, close it. + if (::IsWindow(pEngineState->command.hwndSplashScreen)) + { + ::PostMessageW(pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0); + } + + ReleaseStr(sczOriginalSource); + ReleaseStr(sczCopiedOriginalSource); + + return hr; +} + +static HRESULT RunElevated( + __in HINSTANCE hInstance, + __in LPCWSTR /*wzCommandLine*/, + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + HANDLE hLock = NULL; + BOOL fDisabledAutomaticUpdates = FALSE; + + // connect to per-user process + hr = PipeChildConnect(&pEngineState->companionConnection, TRUE); + ExitOnFailure(hr, "Failed to connect to unelevated process."); + + // Set up the thread local storage to store the correct pipe to communicate logging then + // override logging to write over the pipe. + pEngineState->dwElevatedLoggingTlsId = ::TlsAlloc(); + if (TLS_OUT_OF_INDEXES == pEngineState->dwElevatedLoggingTlsId) + { + ExitWithLastError(hr, "Failed to allocate thread local storage for logging."); + } + + if (!::TlsSetValue(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe)) + { + ExitWithLastError(hr, "Failed to set elevated pipe into thread local storage for logging."); + } + + LogRedirect(RedirectLoggingOverPipe, pEngineState); + + // Create a top-level window to prevent shutting down the elevated process. + hr = UiCreateMessageWindow(hInstance, pEngineState); + ExitOnFailure(hr, "Failed to create the message window."); + + SrpInitialize(TRUE); + + // Pump messages from parent process. + hr = ElevationChildPumpMessages(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe, pEngineState->companionConnection.hCachePipe, &pEngineState->approvedExes, &pEngineState->containers, &pEngineState->packages, &pEngineState->payloads, &pEngineState->variables, &pEngineState->registration, &pEngineState->userExperience, &hLock, &fDisabledAutomaticUpdates, &pEngineState->userExperience.dwExitCode, &pEngineState->fRestart); + LogRedirect(NULL, NULL); // reset logging so the next failure gets written to "log buffer" for the failure log. + ExitOnFailure(hr, "Failed to pump messages from parent process."); + +LExit: + LogRedirect(NULL, NULL); // we're done talking to the child so always reset logging now. + + // If the message window is still around, close it. + UiCloseMessageWindow(pEngineState); + + if (fDisabledAutomaticUpdates) + { + ElevationChildResumeAutomaticUpdates(); + } + + if (hLock) + { + ::ReleaseMutex(hLock); + ::CloseHandle(hLock); + } + + return hr; +} + +static HRESULT RunEmbedded( + __in HINSTANCE hInstance, + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + + // Disable system restore since the parent bundle may have done it. + pEngineState->fDisableSystemRestore = TRUE; + + // Connect to parent process. + hr = PipeChildConnect(&pEngineState->embeddedConnection, FALSE); + ExitOnFailure(hr, "Failed to connect to parent of embedded process."); + + // Do not register the bundle to automatically restart if embedded. + if (BOOTSTRAPPER_DISPLAY_EMBEDDED == pEngineState->command.display) + { + pEngineState->registration.fDisableResume = TRUE; + } + + // Now run the application like normal. + hr = RunNormal(hInstance, pEngineState); + ExitOnFailure(hr, "Failed to run bootstrapper application embedded."); + +LExit: + return hr; +} + +static HRESULT RunRunOnce( + __in const BURN_REGISTRATION* pRegistration, + __in int nCmdShow + ) +{ + HRESULT hr = S_OK; + LPWSTR sczNewCommandLine = NULL; + LPWSTR sczBurnPath = NULL; + HANDLE hProcess = NULL; + + hr = RegistrationGetResumeCommandLine(pRegistration, &sczNewCommandLine); + ExitOnFailure(hr, "Unable to get resume command line from the registry"); + + // and re-launch + hr = PathForCurrentProcess(&sczBurnPath, NULL); + ExitOnFailure(hr, "Failed to get current process path."); + + hr = ProcExec(sczBurnPath, 0 < sczNewCommandLine ? sczNewCommandLine : L"", nCmdShow, &hProcess); + ExitOnFailure(hr, "Failed to re-launch bundle process after RunOnce: %ls", sczBurnPath); + +LExit: + ReleaseHandle(hProcess); + ReleaseStr(sczNewCommandLine); + ReleaseStr(sczBurnPath); + + return hr; +} + +static HRESULT RunApplication( + __in BURN_ENGINE_STATE* pEngineState, + __out BOOL* pfReloadApp, + __out BOOL* pfSkipCleanup + ) +{ + HRESULT hr = S_OK; + BOOTSTRAPPER_ENGINE_CONTEXT engineContext = { }; + BOOL fStartupCalled = FALSE; + BOOL fRet = FALSE; + MSG msg = { }; + BOOTSTRAPPER_SHUTDOWN_ACTION shutdownAction = BOOTSTRAPPER_SHUTDOWN_ACTION_NONE; + + ::PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + + // Setup the bootstrapper engine. + engineContext.dwThreadId = ::GetCurrentThreadId(); + engineContext.pEngineState = pEngineState; + + // Load the bootstrapper application. + hr = UserExperienceLoad(&pEngineState->userExperience, &engineContext, &pEngineState->command); + ExitOnFailure(hr, "Failed to load BA."); + + fStartupCalled = TRUE; + hr = UserExperienceOnStartup(&pEngineState->userExperience); + ExitOnFailure(hr, "Failed to start bootstrapper application."); + + // Enter the message pump. + while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) + { + if (-1 == fRet) + { + hr = E_UNEXPECTED; + ExitOnRootFailure(hr, "Unexpected return value from message pump."); + } + else + { + // When the BA makes a request from its own thread, it's common for the PostThreadMessage in externalengine.cpp + // to block until this thread waits on something. It's also common for Detect and Plan to never wait on something. + // In the extreme case, the engine could be elevating in Apply before the Detect call returned to the BA. + // This helps to avoid that situation, which could be blocking a UI thread. + ::Sleep(0); + + ProcessMessage(pEngineState, &msg); + } + } + + // Get exit code. + pEngineState->userExperience.dwExitCode = (DWORD)msg.wParam; + +LExit: + if (fStartupCalled) + { + UserExperienceOnShutdown(&pEngineState->userExperience, &shutdownAction); + if (BOOTSTRAPPER_SHUTDOWN_ACTION_RESTART == shutdownAction) + { + LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RESTART, LoggingBoolToString(pEngineState->fRestart)); + pEngineState->fRestart = TRUE; + } + else if (BOOTSTRAPPER_SHUTDOWN_ACTION_RELOAD_BOOTSTRAPPER == shutdownAction) + { + LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RELOAD); + *pfReloadApp = TRUE; + } + else if (BOOTSTRAPPER_SHUTDOWN_ACTION_SKIP_CLEANUP == shutdownAction) + { + LogId(REPORT_STANDARD, MSG_BA_REQUESTED_SKIP_CLEANUP); + *pfSkipCleanup = TRUE; + } + } + + // Unload BA. + UserExperienceUnload(&pEngineState->userExperience); + + return hr; +} + +static HRESULT ProcessMessage( + __in BURN_ENGINE_STATE* pEngineState, + __in const MSG* pmsg + ) +{ + HRESULT hr = S_OK; + + UserExperienceActivateEngine(&pEngineState->userExperience); + + if (pEngineState->fQuit) + { + LogId(REPORT_WARNING, MSG_IGNORE_OPERATION_AFTER_QUIT, LoggingBurnMessageToString(pmsg->message)); + ExitFunction1(hr = E_INVALIDSTATE); + } + + switch (pmsg->message) + { + case WM_BURN_DETECT: + hr = CoreDetect(pEngineState, reinterpret_cast(pmsg->lParam)); + break; + + case WM_BURN_PLAN: + hr = CorePlan(pEngineState, static_cast(pmsg->lParam)); + break; + + case WM_BURN_ELEVATE: + hr = CoreElevate(pEngineState, reinterpret_cast(pmsg->lParam)); + break; + + case WM_BURN_APPLY: + hr = CoreApply(pEngineState, reinterpret_cast(pmsg->lParam)); + break; + + case WM_BURN_LAUNCH_APPROVED_EXE: + hr = CoreLaunchApprovedExe(pEngineState, reinterpret_cast(pmsg->lParam)); + break; + + case WM_BURN_QUIT: + hr = CoreQuit(pEngineState, static_cast(pmsg->wParam)); + break; + } + +LExit: + UserExperienceDeactivateEngine(&pEngineState->userExperience); + + return hr; +} + +static HRESULT DAPI RedirectLoggingOverPipe( + __in_z LPCSTR szString, + __in_opt LPVOID pvContext + ) +{ + static BOOL s_fCurrentlyLoggingToPipe = FALSE; + + HRESULT hr = S_OK; + BURN_ENGINE_STATE* pEngineState = static_cast(pvContext); + BOOL fStartedLogging = FALSE; + HANDLE hPipe = INVALID_HANDLE_VALUE; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = 0; + + // Prevent this function from being called recursively. + if (s_fCurrentlyLoggingToPipe) + { + ExitFunction(); + } + + s_fCurrentlyLoggingToPipe = TRUE; + fStartedLogging = TRUE; + + // Make sure the current thread set the pipe in TLS. + hPipe = ::TlsGetValue(pEngineState->dwElevatedLoggingTlsId); + if (!hPipe || INVALID_HANDLE_VALUE == hPipe) + { + hr = HRESULT_FROM_WIN32(ERROR_PIPE_NOT_CONNECTED); + ExitFunction(); + } + + // Do not log or use ExitOnFailure() macro here because they will be discarded + // by the recursive block at the top of this function. + hr = BuffWriteStringAnsi(&pbData, &cbData, szString); + if (SUCCEEDED(hr)) + { + hr = PipeSendMessage(hPipe, static_cast(BURN_PIPE_MESSAGE_TYPE_LOG), pbData, cbData, NULL, NULL, &dwResult); + if (SUCCEEDED(hr)) + { + hr = (HRESULT)dwResult; + } + } + +LExit: + ReleaseBuffer(pbData); + + // We started logging so remember to say we are no longer logging. + if (fStartedLogging) + { + s_fCurrentlyLoggingToPipe = FALSE; + } + + return hr; +} + +static HRESULT Restart() +{ + HRESULT hr = S_OK; + HANDLE hProcessToken = NULL; + TOKEN_PRIVILEGES priv = { }; + DWORD dwRetries = 0; + + if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken)) + { + ExitWithLastError(hr, "Failed to get process token."); + } + + priv.PrivilegeCount = 1; + priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + if (!::LookupPrivilegeValueW(NULL, L"SeShutdownPrivilege", &priv.Privileges[0].Luid)) + { + ExitWithLastError(hr, "Failed to get shutdown privilege LUID."); + } + + if (!::AdjustTokenPrivileges(hProcessToken, FALSE, &priv, sizeof(TOKEN_PRIVILEGES), NULL, 0)) + { + ExitWithLastError(hr, "Failed to adjust token to add shutdown privileges."); + } + + do + { + hr = S_OK; + + // Wait a second to let the companion process (assuming we did an elevated install) to get to the + // point where it too is thinking about restarting the computer. Only one will schedule the restart + // but both will have their log files closed and otherwise be ready to exit. + // + // On retry, we'll also wait a second to let the OS try to get to a place where the restart can + // be initiated. + ::Sleep(1000); + + if (!vpfnInitiateSystemShutdownExW(NULL, NULL, 0, FALSE, TRUE, SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_INSTALLATION | SHTDN_REASON_FLAG_PLANNED)) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + } + } while (dwRetries++ < RESTART_RETRIES && (HRESULT_FROM_WIN32(ERROR_MACHINE_LOCKED) == hr || HRESULT_FROM_WIN32(ERROR_NOT_READY) == hr)); + ExitOnRootFailure(hr, "Failed to schedule restart."); + +LExit: + ReleaseHandle(hProcessToken); + return hr; +} diff --git a/src/burn/engine/engine.mc b/src/burn/engine/engine.mc new file mode 100644 index 00000000..25d5b4e4 --- /dev/null +++ b/src/burn/engine/engine.mc @@ -0,0 +1,1090 @@ +; // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +MessageIdTypedef=DWORD + +LanguageNames=(English=0x409:MSG00409) + + +; // message definitions + +; // MessageId=# +; // Severity=Success +; // SymbolicName=MSG_SUCCESS +; // Language=English +; // Success %1. +; // . +; +; // MessageId=# +; // Severity=Warning +; // SymbolicName=MSG_WARNING +; // Language=English +; // Warning %1. +; // . +; +; // MessageId=# +; // Severity=Error +; // SymbolicName=MSG_ERROR +; // Language=English +; // Error %1. +; // . + +MessageId=1 +Severity=Success +SymbolicName=MSG_BURN_INFO +Language=English +Burn %7!hs! v%1!hs!, Windows v%2!d!.%3!d! %8!hs! (Build %4!d!: Service Pack %5!d!), path: %6!ls! +. + +MessageId=2 +Severity=Warning +SymbolicName=MSG_BURN_UNKNOWN_PRIVATE_SWITCH +Language=English +Unknown burn internal command-line switch encountered: '%1!ls!'. +. + +MessageId=3 +Severity=Success +SymbolicName=MSG_BURN_RUN_BY_RELATED_BUNDLE +Language=English +This bundle is being run by a related bundle as type '%1!hs!'. +. + +MessageId=4 +Severity=Success +SymbolicName=MSG_BA_REQUESTED_RESTART +Language=English +Bootstrapper application requested restart at shutdown. Planned to restart already: %1!hs!. +. + +MessageId=5 +Severity=Warning +SymbolicName=MSG_RESTARTING +Language=English +Restarting computer... +======================================= +. + +MessageId=6 +Severity=Success +SymbolicName=MSG_BA_REQUESTED_RELOAD +Language=English +Bootstrapper application requested to be reloaded. +. + +MessageId=7 +Severity=Success +SymbolicName=MSG_EXITING +Language=English +Exit code: 0x%1!x!, restarting: %2!hs! +. + +MessageId=8 +Severity=Warning +SymbolicName=MSG_RESTART_ABORTED +Language=English +Preventing requested restart because bundle is related: '%1!hs!'. Returning restart requested to parent bundle. +. + +MessageId=9 +Severity=Success +SymbolicName=MSG_BURN_COMMAND_LINE +Language=English +Command Line: '%1!ls!' +. + +MessageId=10 +Severity=Success +SymbolicName=MSG_LAUNCH_ELEVATED_ENGINE_STARTING +Language=English +Launching elevated engine process. +. + +MessageId=11 +Severity=Success +SymbolicName=MSG_LAUNCH_ELEVATED_ENGINE_SUCCESS +Language=English +Launched elevated engine process. +. + +MessageId=12 +Severity=Success +SymbolicName=MSG_CONNECT_TO_ELEVATED_ENGINE_SUCCESS +Language=English +Connected to elevated engine. +. + +MessageId=13 +Severity=Warning +SymbolicName=MSG_MANIFEST_INVALID_VERSION +Language=English +The manifest contains an invalid version string: '%1!ls!' +. + +MessageId=14 +Severity=Success +SymbolicName=MSG_BA_REQUESTED_SKIP_CLEANUP +Language=English +Bootstrapper application opted out of any engine behavior to automatically uninstall the bundle during shutdown. +. + +MessageId=51 +Severity=Error +SymbolicName=MSG_FAILED_PARSE_CONDITION +Language=English +Error %1!hs!. Failed to parse condition %2!ls!. Unexpected symbol at position %3!hs! +. + +MessageId=52 +Severity=Success +SymbolicName=MSG_CONDITION_RESULT +Language=English +Condition '%1!ls!' evaluates to %2!hs!. +. + +MessageId=53 +Severity=Error +SymbolicName=MSG_FAILED_CONDITION_CHECK +Language=English +Bundle global condition check didn't succeed - aborting without loading application. +. + +MessageId=54 +Severity=Error +SymbolicName=MSG_RESOLVE_SOURCE_FAILED +Language=English +Failed to resolve source for payload: %2!ls!, package: %3!ls!, container: %4!ls!, error: %1!ls!. +. + +MessageId=55 +Severity=Warning +SymbolicName=MSG_CANNOT_LOAD_STATE_FILE +Language=English +Could not load or read state file: %2!ls!, error: 0x%1!x!. +. + +MessageId=56 +Severity=Error +SymbolicName=MSG_USER_CANCELED +Language=English +Application canceled operation: %2!ls!, error: %1!ls! +. + +MessageId=57 +Severity=Warning +SymbolicName=MSG_CONDITION_INVALID_VERSION +Language=English +Condition '%1!ls!' contains invalid version string '%2!ls!'. +. + +MessageId=58 +Severity=Warning +SymbolicName=MSG_IGNORE_OPERATION_AFTER_QUIT +Language=English +Bootstrapper application already requested to quit, ignoring request: '%1!hs!'. +. + +MessageId=100 +Severity=Success +SymbolicName=MSG_DETECT_BEGIN +Language=English +Detect begin, %1!u! packages +. + +MessageId=101 +Severity=Success +SymbolicName=MSG_DETECTED_PACKAGE +Language=English +Detected package: %1!ls!, state: %2!hs!, cached: %3!hs!, install registration state: %4!hs!, cache registration state: %5!hs! +. + +MessageId=102 +Severity=Success +SymbolicName=MSG_DETECTED_RELATED_BUNDLE +Language=English +Detected related bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!ls!, operation: %5!hs!, cached: %6!hs! +. + +MessageId=103 +Severity=Success +SymbolicName=MSG_DETECTED_RELATED_PACKAGE +Language=English +Detected related package: %1!ls!, scope: %2!hs!, version: %3!ls!, language: %4!u! operation: %5!hs! +. + +MessageId=104 +Severity=Success +SymbolicName=MSG_DETECTED_MSI_FEATURE +Language=English +Detected package: %1!ls!, feature: %2!ls!, state: %3!hs! +. + +MessageId=105 +Severity=Success +SymbolicName=MSG_DETECTED_MSP_TARGET +Language=English +Detected package: %1!ls! target: %2!ls!, state: %3!hs! +. + +MessageId=106 +Severity=Success +SymbolicName=MSG_DETECT_CALCULATE_PATCH_APPLICABILITY +Language=English +Calculating patch applicability for target product code: %1!ls!, context: %2!hs! +. + +MessageId=107 +Severity=Success +SymbolicName=MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE +Language=English +Detected forward compatible bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!ls!, cached: %5!hs! +. + +MessageId=108 +Severity=Warning +SymbolicName=MSG_DETECT_RELATED_BUNDLE_NOT_CACHED +Language=English +Detected related bundle missing from cache: %1!ls!, cache path: %2!ls! +. + +MessageId=120 +Severity=Warning +SymbolicName=MSG_DETECT_PACKAGE_NOT_FULLY_CACHED +Language=English +Detected partially cached package: %1!ls!, missing payload: %2!ls! +. + +MessageId=121 +Severity=Warning +SymbolicName=MSG_DETECT_FAILED_CALCULATE_PATCH_APPLICABILITY +Language=English +Could not calculate patch applicability for target product code: %1!ls!, context: %2!hs!, reason: 0x%3!x! +. + +MessageId=122 +Severity=Warning +SymbolicName=MSG_RELATED_PACKAGE_INVALID_VERSION +Language=English +Related package: '%1!ls!' has invalid version: %2!ls! +. + +MessageId=123 +Severity=Warning +SymbolicName=MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION +Language=English +Detected msi package with invalid version, product code: '%1!ls!', version: '%2!ls!' +. + +MessageId=151 +Severity=Error +SymbolicName=MSG_FAILED_DETECT_PACKAGE +Language=English +Detect failed for package: %2!ls!, error: %1!ls! +. + +MessageId=152 +Severity=Error +SymbolicName=MSG_FAILED_READ_RELATED_PACKAGE_LANGUAGE +Language=English +Detected related package: %2!ls!, but failed to read language: %3!hs!, error: 0x%1!x! +. + +MessageId=170 +Severity=Warning +SymbolicName=MSG_DETECT_BAD_PRODUCT_CONFIGURATION +Language=English +Detected bad configuration for product: %1!ls! +. + +MessageId=199 +Severity=Success +SymbolicName=MSG_DETECT_COMPLETE +Language=English +Detect complete, result: 0x%1!x!, installed: %2!hs!, cached: %3!hs!, eligible for cleanup: %4!hs! +. + +MessageId=200 +Severity=Success +SymbolicName=MSG_PLAN_BEGIN +Language=English +Plan begin, %1!u! packages, action: %2!hs! +. + +MessageId=201 +Severity=Success +SymbolicName=MSG_PLANNED_PACKAGE +Language=English +Planned package: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, cache: %7!hs!, uncache: %8!hs!, dependency: %9!hs!, expected install registration state: %10!hs!, expected cache registration state: %11!hs! +. + +MessageId=202 +Severity=Success +SymbolicName=MSG_PLANNED_BUNDLE_UX_CHANGED_REQUEST +Language=English +Planned bundle: %1!ls!, ba requested state: %2!hs! over default: %3!hs! +. + +MessageId=203 +Severity=Success +SymbolicName=MSG_PLANNED_MSI_FEATURE +Language=English + Planned feature: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute action: %5!hs!, rollback action: %6!hs! +. + +MessageId=204 +Severity=Success +SymbolicName=MSG_PLANNED_MSI_FEATURES +Language=English + Plan %1!u! msi features for package: %2!ls! +. + +MessageId=205 +Severity=Warning +SymbolicName=MSG_PLAN_SKIP_PATCH_ACTION +Language=English +Plan %5!hs! skipped patch: %1!ls!, action: %2!hs! because chained target package: %3!ls! being uninstalled +. + +MessageId=206 +Severity=Warning +SymbolicName=MSG_PLAN_SKIP_SLIPSTREAM_ACTION +Language=English +Plan %5!hs! skipped patch: %1!ls!, action: %2!hs! because slipstreamed into chained target package: %3!ls!, action: %4!hs! +. + +MessageId=207 +Severity=Success +SymbolicName=MSG_PLANNED_RELATED_BUNDLE +Language=English +Planned related bundle: %1!ls!, type: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, dependency: %7!hs! +. + +MessageId=208 +Severity=Warning +SymbolicName=MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE +Language=English +Plan disabled rollback due to incomplete cache for package: %1!ls!, original rollback action: %2!hs! +. + +MessageId=209 +Severity=Warning +SymbolicName=MSG_PLAN_SKIPPED_PROVIDER_KEY_REMOVAL +Language=English +Plan skipped removal of provider key: %1!ls! because it is registered to a different bundle: %2!ls! +. + +MessageId=210 +Severity=Warning +SymbolicName=MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS +Language=English +Plan skipped due to remaining dependents: +. + +MessageId=211 +Severity=Success +SymbolicName=MSG_PLANNED_UPGRADE_BUNDLE +Language=English +Planned upgrade bundle: %1!ls!, default requested: %2!hs!, ba requested: %3!hs!, execute: %4!hs!, rollback: %5!hs!, dependency: %6!hs! +. + +MessageId=212 +Severity=Success +SymbolicName=MSG_PLANNED_FORWARD_COMPATIBLE_BUNDLE +Language=English +Planned forward compatible bundle: %1!ls!, default requested: %2!hs!, ba requested: %3!hs!, execute: %4!hs!, rollback: %5!hs!, dependency: %6!hs! +. + +MessageId=213 +Severity=Success +SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_DEPENDENT +Language=English +Plan skipped related bundle: %1!ls!, type: %2!hs!, because it was dependent and the current bundle is being executed as type: %3!hs!. +. + +MessageId=214 +Severity=Success +SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_SCHEDULED +Language=English +Plan skipped related bundle: %1!ls!, type: %2!hs!, because it was previously scheduled. +. + +MessageId=216 +Severity=Success +SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER +Language=English +Plan skipped related bundle: %1!ls!, type: %2!hs!, provider key: %3!ls!, because an embedded bundle with the same provider key is being installed. +. + +MessageId=217 +Severity=Success +SymbolicName=MSG_PLAN_SKIPPED_DEPENDENT_BUNDLE_REPAIR +Language=English +Plan skipped dependent bundle repair: %1!ls!, type: %2!hs!, because no packages are being executed during this uninstall operation. +. + +MessageId=218 +Severity=Success +SymbolicName=MSG_PLANNED_MSP_TARGETS +Language=English + Plan %1!u! patch targets for package: %2!ls! +. + +MessageId=219 +Severity=Success +SymbolicName=MSG_PLANNED_MSP_TARGET +Language=English + Planned patch target: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs! +. + +MessageId=220 +Severity=Success +SymbolicName=MSG_PLANNED_SLIPSTREAMED_MSP_TARGETS +Language=English + Plan %1!u! slipstream patches for package: %2!ls! +. + +MessageId=221 +Severity=Success +SymbolicName=MSG_PLANNED_SLIPSTREAMED_MSP_TARGET +Language=English + Planned slipstreamed patch: %1!ls!, execute: %2!hs!, rollback: %3!hs! +. + +MessageId=299 +Severity=Success +SymbolicName=MSG_PLAN_COMPLETE +Language=English +Plan complete, result: 0x%1!x! +. + +MessageId=300 +Severity=Success +SymbolicName=MSG_APPLY_BEGIN +Language=English +Apply begin +. + +MessageId=301 +Severity=Success +SymbolicName=MSG_APPLYING_PACKAGE +Language=English +Applying %1!hs! package: %2!ls!, action: %3!hs!, path: %4!ls!, arguments: '%5!ls!' +. + +MessageId=302 +Severity=Success +SymbolicName=MSG_ACQUIRED_PAYLOAD +Language=English +Acquired payload: %1!ls! to working path: %2!ls! from: %4!ls!. +. + +MessageId=303 +Severity=Success +SymbolicName=MSG_VERIFIED_EXISTING_CONTAINER +Language=English +Verified existing container: %1!ls! at path: %2!ls!. +. + +MessageId=304 +Severity=Success +SymbolicName=MSG_VERIFIED_EXISTING_PAYLOAD +Language=English +Verified existing payload: %1!ls! at path: %2!ls!. +. + +MessageId=305 +Severity=Success +SymbolicName=MSG_VERIFIED_ACQUIRED_PAYLOAD +Language=English +Verified acquired payload: %1!ls! at path: %2!ls!, %3!hs! to: %4!ls!. +. + +MessageId=306 +Severity=Success +SymbolicName=MSG_APPLYING_PATCH_PACKAGE +Language=English +Applying package: %1!ls!, target: %5!ls!, action: %2!hs!, path: %3!ls!, arguments: '%4!ls!' +. + +MessageId=307 +Severity=Warning +SymbolicName=MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE +Language=English +Attempted to uninstall absent package: %1!ls!. Continuing... +. + +MessageId=308 +Severity=Warning +SymbolicName=MSG_FAILED_PAUSE_AU +Language=English +Automatic updates could not be paused due to error: 0x%1!x!. Continuing... +. + +MessageId=309 +Severity=Warning +SymbolicName=MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE +Language=English +Skipping apply of package: %1!ls! due to cache error: 0x%2!x!. Continuing... +. + +MessageId=310 +Severity=Error +SymbolicName=MSG_FAILED_VERIFY_PAYLOAD +Language=English +Failed to verify payload: %2!ls! at path: %3!ls!, error: %1!ls!. Deleting file. +. + +MessageId=311 +Severity=Error +SymbolicName=MSG_FAILED_ACQUIRE_CONTAINER +Language=English +Failed to acquire container: %2!ls! to working path: %3!ls!, error: %1!ls!. +. + +MessageId=312 +Severity=Error +SymbolicName=MSG_FAILED_EXTRACT_CONTAINER +Language=English +Failed to extract payloads from container: %2!ls! to working path: %3!ls!, error: %1!ls!. +. + +MessageId=313 +Severity=Error +SymbolicName=MSG_FAILED_ACQUIRE_PAYLOAD +Language=English +Failed to acquire payload: %2!ls! to working path: %3!ls!, error: %1!ls!. +. + +MessageId=314 +Severity=Error +SymbolicName=MSG_FAILED_CACHE_PAYLOAD +Language=English +Failed to cache payload: %2!ls! from working path: %4!ls!, error: %1!ls!. +. + +MessageId=315 +Severity=Error +SymbolicName=MSG_FAILED_LAYOUT_BUNDLE +Language=English +Failed to layout bundle: %2!ls! to layout directory: %3!ls!, error: %1!ls!. +. + +MessageId=316 +Severity=Error +SymbolicName=MSG_FAILED_LAYOUT_CONTAINER +Language=English +Failed to layout container: %2!ls! to layout directory: %3!ls!, error: %1!ls!. +. + + +MessageId=317 +Severity=Error +SymbolicName=MSG_FAILED_LAYOUT_PAYLOAD +Language=English +Failed to layout payload: %2!ls! from working path: %4!ls! to layout directory: %3!ls!, error: %1!ls!. +. + +MessageId=318 +Severity=Success +SymbolicName=MSG_ROLLBACK_PACKAGE_SKIPPED +Language=English +Skipped rollback of package: %1!ls!, action: %2!hs!, already: %3!hs! +. + +MessageId=319 +Severity=Success +SymbolicName=MSG_APPLY_COMPLETED_PACKAGE +Language=English +Applied %1!hs! package: %2!ls!, result: 0x%3!x!, restart: %4!hs! +. + +MessageId=320 +Severity=Success +SymbolicName=MSG_DEPENDENCY_BUNDLE_REGISTER +Language=English +Registering bundle dependency provider: %1!ls!, version: %2!ls! +. + +MessageId=321 +Severity=Warning +SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_NOPROVIDERS +Language=English +Skipping dependency registration on package with no dependency providers: %1!ls! +. + +MessageId=322 +Severity=Warning +SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE +Language=English +Skipping cross-scope dependency registration on package: %1!ls!, bundle scope: %2!hs!, package scope: %3!hs! +. + +MessageId=323 +Severity=Success +SymbolicName=MSG_DEPENDENCY_PACKAGE_REGISTER +Language=English +Registering package dependency provider: %1!ls!, version: %2!ls!, package: %3!ls! +. + +MessageId=324 +Severity=Warning +SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_MISSING +Language=English +Skipping dependency registration on missing package provider: %1!ls!, package: %2!ls! +. + +MessageId=325 +Severity=Success +SymbolicName=MSG_DEPENDENCY_PACKAGE_REGISTER_DEPENDENCY +Language=English +Registering dependency: %1!ls! on package provider: %2!ls!, package: %3!ls! +. + +MessageId=326 +Severity=Success +SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY +Language=English +Removed dependency: %1!ls! on package provider: %2!ls!, package %3!ls! +. + +MessageId=327 +Severity=Warning +SymbolicName=MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS +Language=English +Will not uninstall package: %1!ls!, found dependents: +. + +MessageId=328 +Severity=Warning +SymbolicName=MSG_DEPENDENCY_PACKAGE_DEPENDENT +Language=English +Found dependent: %1!ls!, name: %2!ls! +. + +MessageId=329 +Severity=Success +SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED +Language=English +Removed package dependency provider: %1!ls!, package: %2!ls! +. + +MessageId=330 +Severity=Success +SymbolicName=MSG_DEPENDENCY_BUNDLE_UNREGISTERED +Language=English +Removed bundle dependency provider: %1!ls! +. + +MessageId=331 +Severity=Warning +SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY_FAILED +Language=English +Could not remove dependency: %1!ls! on package provider: %2!ls!, package %3!ls!, error: 0x%4!x! +. + +MessageId=332 +Severity=Warning +SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_FAILED +Language=English +Could not remove package dependency provider: %1!ls!, package: %2!ls!, error: 0x%3!x! +. + +MessageId=333 +Severity=Warning +SymbolicName=MSG_DEPENDENCY_BUNDLE_UNREGISTERED_FAILED +Language=English +Could not remove bundle dependency provider: %1!ls!, error: 0x%2!x! +. + +MessageId=334 +Severity=Warning +SymbolicName=MSG_DEPENDENCY_BUNDLE_DEPENDENT +Language=English +Found dependent: %1!ls!, name: %2!ls! +. + +MessageId=335 +Severity=Success +SymbolicName=MSG_ACQUIRE_BUNDLE_PAYLOAD +Language=English +Acquiring bundle payload: %2!ls!, %3!hs! from: %4!ls! +. + +MessageId=336 +Severity=Success +SymbolicName=MSG_ACQUIRE_CONTAINER +Language=English +Acquiring container: %1!ls!, %3!hs! from: %4!ls! +. + +MessageId=338 +Severity=Success +SymbolicName=MSG_ACQUIRE_PACKAGE_PAYLOAD +Language=English +Acquiring package: %1!ls!, payload: %2!ls!, %3!hs! from: %4!ls! +. + +MessageId=339 +Severity=Error +SymbolicName=MSG_FAILED_VERIFY_CONTAINER +Language=English +Failed to verify container: %2!ls! at path: %3!ls!, error: %1!ls!. Deleting file. +. + +MessageId=340 +Severity=Warning +SymbolicName=MSG_CACHE_CONTINUING_NONVITAL_PACKAGE +Language=English +Cached non-vital package: %1!ls!, encountered error: 0x%2!x!. Continuing... +. + +MessageId=346 +Severity=Warning +SymbolicName=MSG_CACHE_RETRYING_PACKAGE +Language=English +Application requested retry of caching package: %1!ls!, encountered error: 0x%2!x!. Retrying... +. + +MessageId=347 +Severity=Warning +SymbolicName=MSG_CACHE_RETRYING_CONTAINER +Language=English +Application requested retry of caching container: %2!ls!, encountered error: %1!ls!. Retrying... +. + +MessageId=348 +Severity=Warning +SymbolicName=MSG_APPLY_RETRYING_PACKAGE +Language=English +Application requested retry of executing package: %1!ls!, encountered error: 0x%2!x!. Retrying... +. + +MessageId=349 +Severity=Warning +SymbolicName=MSG_CACHE_RETRYING_PAYLOAD +Language=English +Application requested retry of caching payload: %2!ls!, encountered error: %1!ls!. Retrying... +. + +MessageId=350 +Severity=Warning +SymbolicName=MSG_APPLY_CONTINUING_NONVITAL_PACKAGE +Language=English +Applied non-vital package: %1!ls!, encountered error: 0x%2!x!. Continuing... +. + +MessageId=351 +Severity=Success +SymbolicName=MSG_UNCACHE_PACKAGE +Language=English +Removing cached package: %1!ls!, from path: %2!ls! +. + +MessageId=352 +Severity=Success +SymbolicName=MSG_UNCACHE_BUNDLE +Language=English +Removing cached bundle: %1!ls!, from path: %2!ls! +. + +MessageId=353 +Severity=Warning +SymbolicName=MSG_UNABLE_UNCACHE_PACKAGE +Language=English +Unable to remove cached package: %1!ls!, from path: %2!ls!, reason: 0x%3!x!. Continuing... +. + +MessageId=354 +Severity=Warning +SymbolicName=MSG_UNABLE_UNCACHE_BUNDLE +Language=English +Unable to remove cached bundle: %1!ls!, from path: %2!ls!, reason: 0x%3!x!. Continuing... +. + +MessageId=355 +Severity=Warning +SymbolicName=MSG_SOURCELIST_REGISTER +Language=English +Unable to register source directory: %1!ls!, product: %2!ls!, reason: 0x%3!x!. Continuing... +. + +MessageId=356 +Severity=Warning +SymbolicName=MSG_APPLY_RETRYING_ACQUIRE_CONTAINER +Language=English +Application requested retry acquire of container: %2!ls!, encountered error: %1!ls!. Retrying... +. + +MessageId=357 +Severity=Warning +SymbolicName=MSG_APPLY_RETRYING_ACQUIRE_PAYLOAD +Language=English +Application requested retry acquire of payload: %2!ls!, encountered error: %1!ls!. Retrying... +. + +MessageId=358 +Severity=Success +SymbolicName=MSG_PAUSE_AU_STARTING +Language=English +Pausing automatic updates. +. + +MessageId=359 +Severity=Success +SymbolicName=MSG_PAUSE_AU_SUCCEEDED +Language=English +Paused automatic updates. +. + +MessageId=360 +Severity=Success +SymbolicName=MSG_SYSTEM_RESTORE_POINT_STARTING +Language=English +Creating a system restore point. +. + +MessageId=361 +Severity=Success +SymbolicName=MSG_SYSTEM_RESTORE_POINT_SUCCEEDED +Language=English +Created a system restore point. +. + +MessageId=362 +Severity=Success +SymbolicName=MSG_SYSTEM_RESTORE_POINT_DISABLED +Language=English +System restore disabled, system restore point not created. +. + +MessageId=363 +Severity=Warning +SymbolicName=MSG_SYSTEM_RESTORE_POINT_FAILED +Language=English +Could not create system restore point, error: 0x%1!x!. Continuing... +. + +MessageId=370 +Severity=Success +SymbolicName=MSG_SESSION_BEGIN +Language=English +Session begin, registration key: %1!ls!, options: 0x%2!x!, disable resume: %3!hs! +. + +MessageId=371 +Severity=Success +SymbolicName=MSG_SESSION_UPDATE +Language=English +Updating session, registration key: %1!ls!, resume: %2!hs!, restart initiated: %3!hs!, disable resume: %4!hs! +. + +MessageId=372 +Severity=Success +SymbolicName=MSG_SESSION_END +Language=English +Session end, registration key: %1!ls!, resume: %2!hs!, restart: %3!hs!, disable resume: %4!hs! +. + +MessageId=373 +Severity=Success +SymbolicName=MSG_POST_APPLY_CALCULATE_REGISTRATION +Language=English +Calculating whether to keep registration +. + + +MessageId=374 +Severity=Success +SymbolicName=MSG_POST_APPLY_PACKAGE +Language=English + package: %1!ls!, install registration state: %2!hs!, cache registration state: %3!hs! +. + +MessageId=380 +Severity=Warning +SymbolicName=MSG_APPLY_SKIPPED +Language=English +Apply skipped, no planned actions +. + +MessageId=381 +Severity=Warning +SymbolicName=MSG_APPLY_CANCEL_IGNORED_DURING_ROLLBACK +Language=English +Ignoring application request to cancel from %1!ls! during rollback. +. + +MessageId=382 +Severity=Warning +SymbolicName=MSG_PLAN_ROLLBACK_DISABLED +Language=English +Rollback is disabled for this bundle. +. + +MessageId=383 +Severity=Error +SymbolicName=MSG_MSI_TRANSACTIONS_DISABLED +Language=English +Windows Installer rollback is disabled on this computer. It must be enabled for this bundle to proceed. +. + +MessageId=384 +Severity=Success +SymbolicName=MSG_MSI_TRANSACTION_BEGIN +Language=English +Starting a new MSI transaction, id: %1!ls! +. + +MessageId=385 +Severity=Success +SymbolicName=MSG_MSI_TRANSACTION_COMMIT +Language=English +Committing MSI transaction, id: %1!ls! +. + +MessageId=386 +Severity=Warning +SymbolicName=MSG_MSI_TRANSACTION_ROLLBACK +Language=English +Rolling back MSI transaction, id: %1!ls! +. + +MessageId=387 +Severity=Error +SymbolicName=MSG_RESTART_REQUEST_DURING_MSI_TRANSACTION +Language=English +Illegal state: Reboot requested within an MSI transaction, id: %1!ls! +. + +MessageId=399 +Severity=Success +SymbolicName=MSG_APPLY_COMPLETE +Language=English +Apply complete, result: 0x%1!x!, restart: %2!hs!, ba requested restart: %3!hs! +. + +MessageId=400 +Severity=Success +SymbolicName=MSG_SYSTEM_SHUTDOWN +Language=English +Received system request to shut down the process: critical: %1!hs!, elevated: %2!hs!, allowed: %3!hs! +. + +MessageId=410 +Severity=Success +SymbolicName=MSG_VARIABLE_DUMP +Language=English +Variable: %1!ls! +. + +MessageId=411 +Severity=Warning +SymbolicName=MSG_VARIABLE_INVALID_VERSION +Language=English +The variable '%1!ls!' is being set with an invalid version string. +. + +MessageId=412 +Severity=Warning +SymbolicName=MSG_INVALID_VERSION_COERSION +Language=English +The string '%1!ls!' could not be coerced to a valid version. +. + +MessageId=420 +Severity=Success +SymbolicName=MSG_RESUME_AU_STARTING +Language=English +Resuming automatic updates. +. + +MessageId=421 +Severity=Success +SymbolicName=MSG_RESUME_AU_SUCCEEDED +Language=English +Resumed automatic updates. +. + +MessageId=500 +Severity=Success +SymbolicName=MSG_QUIT +Language=English +Shutting down, exit code: 0x%1!x! +. + +MessageId=501 +Severity=Warning +SymbolicName=MSG_STATE_NOT_SAVED +Language=English +The state file could not be saved, error: %1!ls!. Continuing... +. + +MessageId=502 +Severity=Success +SymbolicName=MSG_CLEANUP_BEGIN +Language=English +Cleanup begin. +. + +MessageId=503 +Severity=Success +SymbolicName=MSG_CLEANUP_SKIPPED_APPLY +Language=English +Cleanup not required due to running Apply. +. + +MessageId=504 +Severity=Success +SymbolicName=MSG_CLEANUP_SKIPPED_ELEVATION_REQUIRED +Language=English +Cleanup check skipped since this per-machine bundle would require elevation. +. + +MessageId=599 +Severity=Success +SymbolicName=MSG_CLEANUP_COMPLETE +Language=English +Cleanup complete, result: 0x%1!x! +. + +MessageId=600 +Severity=Success +SymbolicName=MSG_LAUNCH_APPROVED_EXE_BEGIN +Language=English +LaunchApprovedExe begin, id: %1!ls! +. + +MessageId=601 +Severity=Success +SymbolicName=MSG_LAUNCH_APPROVED_EXE_SEARCH +Language=English +Searching registry for approved exe path, key: %1!ls!, value: '%2!ls!', win64: %3!ls! +. + +MessageId=602 +Severity=Success +SymbolicName=MSG_LAUNCHING_APPROVED_EXE +Language=English +Launching approved exe, path: '%1!ls!', 'command: %2!ls!' +. + +MessageId=699 +Severity=Success +SymbolicName=MSG_LAUNCH_APPROVED_EXE_COMPLETE +Language=English +LaunchApprovedExe complete, result: 0x%1!x!, processId: %2!lu! +. + +MessageId=700 +Severity=Success +SymbolicName=MSG_MSI_PROPERTY_CONDITION_FAILED +Language=English +Skipping MSI property '%1!ls!' because condition '%2!ls!' evaluates to %3!hs!. +. + +MessageId=701 +Severity=Warning +SymbolicName=MSG_PENDING_REBOOT_DETECTED +Language=English +A reboot is pending from a prior execution of this bundle: %1!ls!. Apply will be blocked. Continuing... +. diff --git a/src/burn/engine/engine.vcxproj b/src/burn/engine/engine.vcxproj new file mode 100644 index 00000000..b3a0f81b --- /dev/null +++ b/src/burn/engine/engine.vcxproj @@ -0,0 +1,186 @@ + + + + + + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + Debug + ARM64 + + + Release + ARM64 + + + + + {8119537D-E1D9-6591-D51A-49768A2F9C37} + StaticLibrary + engine + v142 + Unicode + Native component of WixToolset.Burn + + + + + + + $(ProjectDir)..\..\..\balutil\src\WixToolset.BootstrapperCore.Native\inc;$(ProjectAdditionalIncludeDirectories) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Compiling message file... + mc.exe -h "$(IntDir)." -r "$(IntDir)." -A -c -z engine.messages "$(InputDir)engine.mc" +rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" + $(IntDir)engine.messages.h;$(IntDir)engine.messages.rc;$(OutDir)engine.res + + + + + + $(MajorMinorVersion.Split(`.`)[0]) + $(MajorMinorVersion.Split(`.`)[1]) + 0 + $(BuildNumber) + $(rmj).$(rmm).$(rup).$(rpr) + rmj=$(rmj);rmm=$(rmm);rup=$(rup);rpr=$(rpr);szVerMajorMinorBuild="$(szVerMajorMinorBuild)" + + + + + $(wixver);%(PreprocessorDefinitions) + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + diff --git a/src/burn/engine/exeengine.cpp b/src/burn/engine/exeengine.cpp new file mode 100644 index 00000000..c0ba93e0 --- /dev/null +++ b/src/burn/engine/exeengine.cpp @@ -0,0 +1,816 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// internal function declarations + +static HRESULT HandleExitCode( + __in BURN_PACKAGE* pPackage, + __in DWORD dwExitCode, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +static HRESULT ParseCommandLineArgumentsFromXml( + __in IXMLDOMNode* pixnExePackage, + __in BURN_PACKAGE* pPackage + ); +static HRESULT ParseExitCodesFromXml( + __in IXMLDOMNode* pixnExePackage, + __in BURN_PACKAGE* pPackage + ); + + +// function definitions + +extern "C" HRESULT ExeEngineParsePackageFromXml( + __in IXMLDOMNode* pixnExePackage, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + LPWSTR scz = NULL; + + // @DetectCondition + hr = XmlGetAttributeEx(pixnExePackage, L"DetectCondition", &pPackage->Exe.sczDetectCondition); + ExitOnFailure(hr, "Failed to get @DetectCondition."); + + // @InstallArguments + hr = XmlGetAttributeEx(pixnExePackage, L"InstallArguments", &pPackage->Exe.sczInstallArguments); + ExitOnFailure(hr, "Failed to get @InstallArguments."); + + // @UninstallArguments + hr = XmlGetAttributeEx(pixnExePackage, L"UninstallArguments", &pPackage->Exe.sczUninstallArguments); + ExitOnFailure(hr, "Failed to get @UninstallArguments."); + + // @RepairArguments + hr = XmlGetAttributeEx(pixnExePackage, L"RepairArguments", &pPackage->Exe.sczRepairArguments); + ExitOnFailure(hr, "Failed to get @RepairArguments."); + + // @Repairable + hr = XmlGetYesNoAttribute(pixnExePackage, L"Repairable", &pPackage->Exe.fRepairable); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Repairable."); + } + + // @Protocol + hr = XmlGetAttributeEx(pixnExePackage, L"Protocol", &scz); + if (SUCCEEDED(hr)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"burn", -1)) + { + pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_BURN; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"netfx4", -1)) + { + pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_NETFX4; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"none", -1)) + { + pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_NONE; + } + else + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Invalid protocol type: %ls", scz); + } + } + else if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Protocol."); + } + + hr = ParseExitCodesFromXml(pixnExePackage, pPackage); + ExitOnFailure(hr, "Failed to parse exit codes."); + + hr = ParseCommandLineArgumentsFromXml(pixnExePackage, pPackage); + ExitOnFailure(hr, "Failed to parse command lines."); + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseStr(scz); + + return hr; +} + +extern "C" void ExeEnginePackageUninitialize( + __in BURN_PACKAGE* pPackage + ) +{ + ReleaseStr(pPackage->Exe.sczDetectCondition); + ReleaseStr(pPackage->Exe.sczInstallArguments); + ReleaseStr(pPackage->Exe.sczRepairArguments); + ReleaseStr(pPackage->Exe.sczUninstallArguments); + ReleaseStr(pPackage->Exe.sczIgnoreDependencies); + //ReleaseStr(pPackage->Exe.sczProgressSwitch); + ReleaseMem(pPackage->Exe.rgExitCodes); + + // free command-line arguments + if (pPackage->Exe.rgCommandLineArguments) + { + for (DWORD i = 0; i < pPackage->Exe.cCommandLineArguments; ++i) + { + BURN_EXE_COMMAND_LINE_ARGUMENT* pCommandLineArgument = &pPackage->Exe.rgCommandLineArguments[i]; + ReleaseStr(pCommandLineArgument->sczInstallArgument); + ReleaseStr(pCommandLineArgument->sczUninstallArgument); + ReleaseStr(pCommandLineArgument->sczRepairArgument); + ReleaseStr(pCommandLineArgument->sczCondition); + } + MemFree(pPackage->Exe.rgCommandLineArguments); + } + + // clear struct + memset(&pPackage->Exe, 0, sizeof(pPackage->Exe)); +} + +extern "C" HRESULT ExeEngineDetectPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + BOOL fDetected = FALSE; + + // evaluate detect condition + if (pPackage->Exe.sczDetectCondition && *pPackage->Exe.sczDetectCondition) + { + hr = ConditionEvaluate(pVariables, pPackage->Exe.sczDetectCondition, &fDetected); + ExitOnFailure(hr, "Failed to evaluate executable package detect condition."); + } + + // update detect state + pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + + if (pPackage->fCanAffectRegistration) + { + pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + +LExit: + return hr; +} + +// +// PlanCalculate - calculates the execute and rollback state for the requested package state. +// +extern "C" HRESULT ExeEnginePlanCalculatePackage( + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; + BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + + // execute action + switch (pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: + execute = pPackage->Exe.fPseudoBundle ? BOOTSTRAPPER_ACTION_STATE_INSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; + break; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + execute = pPackage->Exe.fRepairable ? BOOTSTRAPPER_ACTION_STATE_REPAIR : BOOTSTRAPPER_ACTION_STATE_NONE; + break; + case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_CACHE: + execute = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; + break; + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: + execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; + break; + default: + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; + break; + default: + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid package current state: %d.", pPackage->currentState); + } + + // Calculate the rollback action if there is an execute action. + if (BOOTSTRAPPER_ACTION_STATE_NONE != execute) + { + switch (pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_ABSENT: + rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL; + break; + default: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + rollback = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; + break; + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_ABSENT: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + default: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid package expected state."); + } + } + + // return values + pPackage->execute = execute; + pPackage->rollback = rollback; + +LExit: + return hr; +} + +// +// PlanAdd - adds the calculated execute and rollback actions for the package. +// +extern "C" HRESULT ExeEnginePlanAddPackage( + __in_opt DWORD *pdwInsertSequence, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in_opt HANDLE hCacheEvent + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* pAction = NULL; + + // add wait for cache + if (hCacheEvent) + { + hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent); + ExitOnFailure(hr, "Failed to plan package cache syncpoint"); + } + + hr = DependencyPlanPackage(pdwInsertSequence, pPackage, pPlan); + ExitOnFailure(hr, "Failed to plan package dependency actions."); + + // add execute action + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute) + { + if (NULL != pdwInsertSequence) + { + hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction); + ExitOnFailure(hr, "Failed to insert execute action."); + } + else + { + hr = PlanAppendExecuteAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append execute action."); + } + + pAction->type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE; + pAction->exePackage.pPackage = pPackage; + pAction->exePackage.fFireAndForget = (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == pPlan->action); + pAction->exePackage.action = pPackage->execute; + + if (pPackage->Exe.sczIgnoreDependencies) + { + hr = StrAllocString(&pAction->exePackage.sczIgnoreDependencies, pPackage->Exe.sczIgnoreDependencies, 0); + ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore."); + } + + if (pPackage->Exe.wzAncestors) + { + hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.wzAncestors, 0); + ExitOnFailure(hr, "Failed to allocate the list of ancestors."); + } + + LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, NULL); // ignore errors. + } + + // add rollback action + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback) + { + hr = PlanAppendRollbackAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append rollback action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE; + pAction->exePackage.pPackage = pPackage; + pAction->exePackage.action = pPackage->rollback; + + if (pPackage->Exe.sczIgnoreDependencies) + { + hr = StrAllocString(&pAction->exePackage.sczIgnoreDependencies, pPackage->Exe.sczIgnoreDependencies, 0); + ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore."); + } + + if (pPackage->Exe.wzAncestors) + { + hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.wzAncestors, 0); + ExitOnFailure(hr, "Failed to allocate the list of ancestors."); + } + + LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, NULL); // ignore errors. + } + +LExit: + return hr; +} + +extern "C" HRESULT ExeEngineExecutePackage( + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + int nResult = IDNOACTION; + LPCWSTR wzArguments = NULL; + LPWSTR sczArguments = NULL; + LPWSTR sczArgumentsFormatted = NULL; + LPWSTR sczArgumentsObfuscated = NULL; + LPWSTR sczCachedDirectory = NULL; + LPWSTR sczExecutablePath = NULL; + LPWSTR sczCommand = NULL; + LPWSTR sczCommandObfuscated = NULL; + HANDLE hExecutableFile = INVALID_HANDLE_VALUE; + STARTUPINFOW si = { }; + PROCESS_INFORMATION pi = { }; + DWORD dwExitCode = 0; + GENERIC_EXECUTE_MESSAGE message = { }; + BURN_PACKAGE* pPackage = pExecuteAction->exePackage.pPackage; + BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload; + + // get cached executable path + hr = CacheGetCompletedPath(pPackage->fPerMachine, pPackage->sczCacheId, &sczCachedDirectory); + ExitOnFailure(hr, "Failed to get cached path for package: %ls", pPackage->sczId); + + // Best effort to set the execute package cache folder and action variables. + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE); + VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->exePackage.action, TRUE); + + hr = PathConcat(sczCachedDirectory, pPackagePayload->sczFilePath, &sczExecutablePath); + ExitOnFailure(hr, "Failed to build executable path."); + + // pick arguments + switch (pExecuteAction->exePackage.action) + { + case BOOTSTRAPPER_ACTION_STATE_INSTALL: + wzArguments = pPackage->Exe.sczInstallArguments; + break; + + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: + wzArguments = pPackage->Exe.sczUninstallArguments; + break; + + case BOOTSTRAPPER_ACTION_STATE_REPAIR: + wzArguments = pPackage->Exe.sczRepairArguments; + break; + + default: + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action); + } + + // now add optional arguments + hr = StrAllocString(&sczArguments, wzArguments && *wzArguments ? wzArguments : L"", 0); + ExitOnFailure(hr, "Failed to copy package arguments."); + + for (DWORD i = 0; i < pPackage->Exe.cCommandLineArguments; ++i) + { + BURN_EXE_COMMAND_LINE_ARGUMENT* commandLineArgument = &pPackage->Exe.rgCommandLineArguments[i]; + BOOL fCondition = FALSE; + + hr = ConditionEvaluate(pVariables, commandLineArgument->sczCondition, &fCondition); + ExitOnFailure(hr, "Failed to evaluate executable package command-line condition."); + + if (fCondition) + { + hr = StrAllocConcat(&sczArguments, L" ", 0); + ExitOnFailure(hr, "Failed to separate command-line arguments."); + + switch (pExecuteAction->exePackage.action) + { + case BOOTSTRAPPER_ACTION_STATE_INSTALL: + hr = StrAllocConcat(&sczArguments, commandLineArgument->sczInstallArgument, 0); + ExitOnFailure(hr, "Failed to get command-line argument for install."); + break; + + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: + hr = StrAllocConcat(&sczArguments, commandLineArgument->sczUninstallArgument, 0); + ExitOnFailure(hr, "Failed to get command-line argument for uninstall."); + break; + + case BOOTSTRAPPER_ACTION_STATE_REPAIR: + hr = StrAllocConcat(&sczArguments, commandLineArgument->sczRepairArgument, 0); + ExitOnFailure(hr, "Failed to get command-line argument for repair."); + break; + + default: + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action); + } + } + } + + // build command + if (*sczArguments) + { + hr = VariableFormatString(pVariables, sczArguments, &sczArgumentsFormatted, NULL); + ExitOnFailure(hr, "Failed to format argument string."); + + hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", sczExecutablePath, sczArgumentsFormatted); + ExitOnFailure(hr, "Failed to create executable command."); + + hr = VariableFormatStringObfuscated(pVariables, sczArguments, &sczArgumentsObfuscated, NULL); + ExitOnFailure(hr, "Failed to format obfuscated argument string."); + + hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", sczExecutablePath, sczArgumentsObfuscated); + } + else + { + hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", sczExecutablePath); + ExitOnFailure(hr, "Failed to create executable command."); + + hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", sczExecutablePath); + } + ExitOnFailure(hr, "Failed to create obfuscated executable command."); + + if (pPackage->Exe.fSupportsAncestors) + { + // Add the list of dependencies to ignore, if any, to the burn command line. + if (pExecuteAction->exePackage.sczIgnoreDependencies && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) + { + hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies); + ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line."); + + hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies); + ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the obfuscated command line."); + } + + // Add the list of ancestors, if any, to the burn command line. + if (pExecuteAction->exePackage.sczAncestors) + { + hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors); + ExitOnFailure(hr, "Failed to append the list of ancestors to the command line."); + + hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors); + ExitOnFailure(hr, "Failed to append the list of ancestors to the obfuscated command line."); + } + } + + if (BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) + { + hr = CoreAppendFileHandleSelfToCommandLine(sczExecutablePath, &hExecutableFile, &sczCommand, &sczCommandObfuscated); + ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF); + } + + // Log before we add the secret pipe name and client token for embedded processes. + LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, LoggingActionStateToString(pExecuteAction->exePackage.action), sczExecutablePath, sczCommandObfuscated); + + if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) + { + hr = EmbeddedRunBundle(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode); + ExitOnFailure(hr, "Failed to run bundle as embedded from path: %ls", sczExecutablePath); + } + else if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_NETFX4 == pPackage->Exe.protocol) + { + hr = NetFxRunChainer(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode); + ExitOnFailure(hr, "Failed to run netfx chainer: %ls", sczExecutablePath); + } + else // create and wait for the executable process while sending fake progress to allow cancel. + { + // Make the cache location of the executable the current directory to help those executables + // that expect stuff to be relative to them. + si.cb = sizeof(si); + if (!::CreateProcessW(sczExecutablePath, sczCommand, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, sczCachedDirectory, &si, &pi)) + { + ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", sczExecutablePath); + } + + if (pExecuteAction->exePackage.fFireAndForget) + { + ::WaitForInputIdle(pi.hProcess, 5000); + ExitFunction(); + } + + do + { + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; + message.dwAllowedResults = MB_OKCANCEL; + message.progress.dwPercentage = 50; + nResult = pfnGenericMessageHandler(&message, pvContext); + hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); + ExitOnRootFailure(hr, "Bootstrapper application aborted during EXE progress."); + + hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode); + if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr) + { + ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", sczExecutablePath); + } + } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr); + } + + hr = HandleExitCode(pPackage, dwExitCode, pRestart); + ExitOnRootFailure(hr, "Process returned error: 0x%x", dwExitCode); + +LExit: + StrSecureZeroFreeString(sczArguments); + StrSecureZeroFreeString(sczArgumentsFormatted); + ReleaseStr(sczArgumentsObfuscated); + ReleaseStr(sczCachedDirectory); + ReleaseStr(sczExecutablePath); + StrSecureZeroFreeString(sczCommand); + ReleaseStr(sczCommandObfuscated); + + ReleaseHandle(pi.hThread); + ReleaseHandle(pi.hProcess); + ReleaseFileHandle(hExecutableFile); + + // Best effort to clear the execute package cache folder and action variables. + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE); + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE, FALSE); + + return hr; +} + +extern "C" void ExeEngineUpdateInstallRegistrationState( + __in BURN_EXECUTE_ACTION* pAction, + __in HRESULT hrExecute + ) +{ + BURN_PACKAGE* pPackage = pAction->exePackage.pPackage; + + if (FAILED(hrExecute) || !pPackage->fCanAffectRegistration) + { + ExitFunction(); + } + + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->exePackage.action) + { + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + else + { + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + +LExit: + return; +} + + +// internal helper functions + +static HRESULT ParseExitCodesFromXml( + __in IXMLDOMNode* pixnExePackage, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + LPWSTR scz = NULL; + + // select exit code nodes + hr = XmlSelectNodes(pixnExePackage, L"ExitCode", &pixnNodes); + ExitOnFailure(hr, "Failed to select exit code nodes."); + + // get exit code node count + hr = pixnNodes->get_length((long*) &cNodes); + ExitOnFailure(hr, "Failed to get exit code node count."); + + if (cNodes) + { + // allocate memory for exit codes + pPackage->Exe.rgExitCodes = (BURN_EXE_EXIT_CODE*) MemAlloc(sizeof(BURN_EXE_EXIT_CODE) * cNodes, TRUE); + ExitOnNull(pPackage->Exe.rgExitCodes, hr, E_OUTOFMEMORY, "Failed to allocate memory for exit code structs."); + + pPackage->Exe.cExitCodes = cNodes; + + // parse package elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_EXE_EXIT_CODE* pExitCode = &pPackage->Exe.rgExitCodes[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // @Type + hr = XmlGetAttributeNumber(pixnNode, L"Type", (DWORD*)&pExitCode->type); + ExitOnFailure(hr, "Failed to get @Type."); + + // @Code + hr = XmlGetAttributeEx(pixnNode, L"Code", &scz); + ExitOnFailure(hr, "Failed to get @Code."); + + if (L'*' == scz[0]) + { + pExitCode->fWildcard = TRUE; + } + else + { + hr = StrStringToUInt32(scz, 0, (UINT*) &pExitCode->dwCode); + ExitOnFailure(hr, "Failed to parse @Code value: %ls", scz); + } + + // prepare next iteration + ReleaseNullObject(pixnNode); + } + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseStr(scz); + + return hr; +} + +static HRESULT ParseCommandLineArgumentsFromXml( + __in IXMLDOMNode* pixnExePackage, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + LPWSTR scz = NULL; + + // Select command-line argument nodes. + hr = XmlSelectNodes(pixnExePackage, L"CommandLine", &pixnNodes); + ExitOnFailure(hr, "Failed to select command-line argument nodes."); + + // Get command-line argument node count. + hr = pixnNodes->get_length((long*) &cNodes); + ExitOnFailure(hr, "Failed to get command-line argument count."); + + if (cNodes) + { + pPackage->Exe.rgCommandLineArguments = (BURN_EXE_COMMAND_LINE_ARGUMENT*) MemAlloc(sizeof(BURN_EXE_COMMAND_LINE_ARGUMENT) * cNodes, TRUE); + ExitOnNull(pPackage->Exe.rgCommandLineArguments, hr, E_OUTOFMEMORY, "Failed to allocate memory for command-line argument structs."); + + pPackage->Exe.cCommandLineArguments = cNodes; + + // Parse command-line argument elements. + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_EXE_COMMAND_LINE_ARGUMENT* pCommandLineArgument = &pPackage->Exe.rgCommandLineArguments[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next command-line argument node."); + + // @InstallArgument + hr = XmlGetAttributeEx(pixnNode, L"InstallArgument", &pCommandLineArgument->sczInstallArgument); + ExitOnFailure(hr, "Failed to get @InstallArgument."); + + // @UninstallArgument + hr = XmlGetAttributeEx(pixnNode, L"UninstallArgument", &pCommandLineArgument->sczUninstallArgument); + ExitOnFailure(hr, "Failed to get @UninstallArgument."); + + // @RepairArgument + hr = XmlGetAttributeEx(pixnNode, L"RepairArgument", &pCommandLineArgument->sczRepairArgument); + ExitOnFailure(hr, "Failed to get @RepairArgument."); + + // @Condition + hr = XmlGetAttributeEx(pixnNode, L"Condition", &pCommandLineArgument->sczCondition); + ExitOnFailure(hr, "Failed to get @Condition."); + + // Prepare next iteration. + ReleaseNullObject(pixnNode); + } + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseStr(scz); + + return hr; +} + +static HRESULT HandleExitCode( + __in BURN_PACKAGE* pPackage, + __in DWORD dwExitCode, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + BURN_EXE_EXIT_CODE_TYPE typeCode = BURN_EXE_EXIT_CODE_TYPE_NONE; + + for (DWORD i = 0; i < pPackage->Exe.cExitCodes; ++i) + { + BURN_EXE_EXIT_CODE* pExitCode = &pPackage->Exe.rgExitCodes[i]; + + // If this is a wildcard, use the last one we come across. + if (pExitCode->fWildcard) + { + typeCode = pExitCode->type; + } + else if (dwExitCode == pExitCode->dwCode) // If we have an exact match on the error code use that and stop looking. + { + typeCode = pExitCode->type; + break; + } + } + + // If we didn't find a matching code then treat 0 as success, the standard restarts codes as restarts + // and everything else as an error. + if (BURN_EXE_EXIT_CODE_TYPE_NONE == typeCode) + { + if (0 == dwExitCode) + { + typeCode = BURN_EXE_EXIT_CODE_TYPE_SUCCESS; + } + else if (ERROR_SUCCESS_REBOOT_REQUIRED == dwExitCode || + HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast(dwExitCode) || + ERROR_SUCCESS_RESTART_REQUIRED == dwExitCode || + HRESULT_FROM_WIN32(ERROR_SUCCESS_RESTART_REQUIRED) == static_cast(dwExitCode)) + { + typeCode = BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT; + } + else if (ERROR_SUCCESS_REBOOT_INITIATED == dwExitCode || + HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED) == static_cast(dwExitCode)) + { + typeCode = BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT; + } + else + { + typeCode = BURN_EXE_EXIT_CODE_TYPE_ERROR; + } + } + + switch (typeCode) + { + case BURN_EXE_EXIT_CODE_TYPE_SUCCESS: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; + hr = S_OK; + break; + + case BURN_EXE_EXIT_CODE_TYPE_ERROR: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; + hr = HRESULT_FROM_WIN32(dwExitCode); + if (SUCCEEDED(hr)) + { + hr = E_FAIL; + } + break; + + case BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; + hr = S_OK; + break; + + case BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; + hr = S_OK; + break; + + default: + hr = E_UNEXPECTED; + break; + } + +//LExit: + return hr; +} diff --git a/src/burn/engine/exeengine.h b/src/burn/engine/exeengine.h new file mode 100644 index 00000000..e032ea01 --- /dev/null +++ b/src/burn/engine/exeengine.h @@ -0,0 +1,50 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// function declarations + +HRESULT ExeEngineParsePackageFromXml( + __in IXMLDOMNode* pixnExePackage, + __in BURN_PACKAGE* pPackage + ); +void ExeEnginePackageUninitialize( + __in BURN_PACKAGE* pPackage + ); +HRESULT ExeEngineDetectPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_VARIABLES* pVariables + ); +HRESULT ExeEnginePlanCalculatePackage( + __in BURN_PACKAGE* pPackage + ); +HRESULT ExeEnginePlanAddPackage( + __in_opt DWORD *pdwInsertSequence, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in_opt HANDLE hCacheEvent + ); +HRESULT ExeEngineExecutePackage( + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +void ExeEngineUpdateInstallRegistrationState( + __in BURN_EXECUTE_ACTION* pAction, + __in HRESULT hrExecute + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/externalengine.cpp b/src/burn/engine/externalengine.cpp new file mode 100644 index 00000000..409353e4 --- /dev/null +++ b/src/burn/engine/externalengine.cpp @@ -0,0 +1,805 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +static HRESULT CopyStringToExternal( + __in_z LPWSTR wzValue, + __in_z_opt LPWSTR wzBuffer, + __inout SIZE_T* pcchBuffer + ); + +// function definitions + +void ExternalEngineGetPackageCount( + __in BURN_ENGINE_STATE* pEngineState, + __out DWORD* pcPackages + ) +{ + *pcPackages = pEngineState->packages.cPackages; +} + +HRESULT ExternalEngineGetVariableNumeric( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __out LONGLONG* pllValue + ) +{ + HRESULT hr = S_OK; + + if (wzVariable && *wzVariable) + { + hr = VariableGetNumeric(&pEngineState->variables, wzVariable, pllValue); + } + else + { + *pllValue = 0; + hr = E_INVALIDARG; + } + + return hr; +} + +HRESULT ExternalEngineGetVariableString( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __out_ecount_opt(*pcchValue) LPWSTR wzValue, + __inout SIZE_T* pcchValue + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + if (wzVariable && *wzVariable) + { + hr = VariableGetString(&pEngineState->variables, wzVariable, &sczValue); + if (SUCCEEDED(hr)) + { + hr = CopyStringToExternal(sczValue, wzValue, pcchValue); + } + } + else + { + hr = E_INVALIDARG; + } + + StrSecureZeroFreeString(sczValue); + + return hr; +} + +HRESULT ExternalEngineGetVariableVersion( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __out_ecount_opt(*pcchValue) LPWSTR wzValue, + __inout SIZE_T* pcchValue + ) +{ + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion = NULL; + + if (wzVariable && *wzVariable) + { + hr = VariableGetVersion(&pEngineState->variables, wzVariable, &pVersion); + if (SUCCEEDED(hr)) + { + hr = CopyStringToExternal(pVersion->sczVersion, wzValue, pcchValue); + } + } + else + { + hr = E_INVALIDARG; + } + + ReleaseVerutilVersion(pVersion); + + return hr; +} + +HRESULT ExternalEngineFormatString( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzIn, + __out_ecount_opt(*pcchOut) LPWSTR wzOut, + __inout SIZE_T* pcchOut + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + if (wzIn && *wzIn) + { + hr = VariableFormatString(&pEngineState->variables, wzIn, &sczValue, NULL); + if (SUCCEEDED(hr)) + { + hr = CopyStringToExternal(sczValue, wzOut, pcchOut); + } + } + else + { + hr = E_INVALIDARG; + } + + StrSecureZeroFreeString(sczValue); + + return hr; +} + +HRESULT ExternalEngineEscapeString( + __in_z LPCWSTR wzIn, + __out_ecount_opt(*pcchOut) LPWSTR wzOut, + __inout SIZE_T* pcchOut + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + if (wzIn && *wzIn) + { + hr = VariableEscapeString(wzIn, &sczValue); + if (SUCCEEDED(hr)) + { + hr = CopyStringToExternal(sczValue, wzOut, pcchOut); + } + } + else + { + hr = E_INVALIDARG; + } + + StrSecureZeroFreeString(sczValue); + + return hr; +} + +HRESULT ExternalEngineEvaluateCondition( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzCondition, + __out BOOL* pf + ) +{ + HRESULT hr = S_OK; + + if (wzCondition && *wzCondition) + { + hr = ConditionEvaluate(&pEngineState->variables, wzCondition, pf); + } + else + { + *pf = FALSE; + hr = E_INVALIDARG; + } + + return hr; +} + +HRESULT ExternalEngineLog( + __in REPORT_LEVEL rl, + __in_z LPCWSTR wzMessage + ) +{ + HRESULT hr = S_OK; + + hr = LogStringLine(rl, "%ls", wzMessage); + + return hr; +} + +HRESULT ExternalEngineSendEmbeddedError( + __in BURN_ENGINE_STATE* pEngineState, + __in const DWORD dwErrorCode, + __in_z LPCWSTR wzMessage, + __in const DWORD dwUIHint, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = *pnResult = 0; + + if (BURN_MODE_EMBEDDED != pEngineState->mode) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE); + ExitOnRootFailure(hr, "BA requested to send embedded message when not in embedded mode."); + } + + hr = BuffWriteNumber(&pbData, &cbData, dwErrorCode); + ExitOnFailure(hr, "Failed to write error code to message buffer."); + + hr = BuffWriteString(&pbData, &cbData, wzMessage ? wzMessage : L""); + ExitOnFailure(hr, "Failed to write message string to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, dwUIHint); + ExitOnFailure(hr, "Failed to write UI hint to message buffer."); + + hr = PipeSendMessage(pEngineState->embeddedConnection.hPipe, BURN_EMBEDDED_MESSAGE_TYPE_ERROR, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send embedded message over pipe."); + + *pnResult = static_cast(dwResult); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +HRESULT ExternalEngineSendEmbeddedProgress( + __in BURN_ENGINE_STATE* pEngineState, + __in const DWORD dwProgressPercentage, + __in const DWORD dwOverallProgressPercentage, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + DWORD dwResult = *pnResult = 0; + + if (BURN_MODE_EMBEDDED != pEngineState->mode) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE); + ExitOnRootFailure(hr, "BA requested to send embedded progress message when not in embedded mode."); + } + + hr = BuffWriteNumber(&pbData, &cbData, dwProgressPercentage); + ExitOnFailure(hr, "Failed to write progress percentage to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, dwOverallProgressPercentage); + ExitOnFailure(hr, "Failed to write overall progress percentage to message buffer."); + + hr = PipeSendMessage(pEngineState->embeddedConnection.hPipe, BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS, pbData, cbData, NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send embedded progress message over pipe."); + + *pnResult = static_cast(dwResult); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +HRESULT ExternalEngineSetUpdate( + __in BURN_ENGINE_STATE* pEngineState, + __in_z_opt LPCWSTR wzLocalSource, + __in_z_opt LPCWSTR wzDownloadSource, + __in const DWORD64 qwSize, + __in const BOOTSTRAPPER_UPDATE_HASH_TYPE hashType, + __in_opt const BYTE* rgbHash, + __in const DWORD cbHash + ) +{ + HRESULT hr = S_OK; + LPWSTR sczFilePath = NULL; + LPWSTR sczCommandline = NULL; + UUID guid = { }; + WCHAR wzGuid[39]; + RPC_STATUS rs = RPC_S_OK; + + ::EnterCriticalSection(&pEngineState->userExperience.csEngineActive); + hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience); + ExitOnFailure(hr, "Engine is active, cannot change engine state."); + + if ((!wzLocalSource || !*wzLocalSource) && (!wzDownloadSource || !*wzDownloadSource)) + { + UpdateUninitialize(&pEngineState->update); + } + else if (BOOTSTRAPPER_UPDATE_HASH_TYPE_NONE == hashType && (0 != cbHash || rgbHash)) + { + hr = E_INVALIDARG; + } + else if (BOOTSTRAPPER_UPDATE_HASH_TYPE_SHA512 == hashType && (SHA512_HASH_LEN != cbHash || !rgbHash)) + { + hr = E_INVALIDARG; + } + else + { + UpdateUninitialize(&pEngineState->update); + + hr = CoreRecreateCommandLine(&sczCommandline, BOOTSTRAPPER_ACTION_INSTALL, pEngineState->command.display, pEngineState->command.restart, BOOTSTRAPPER_RELATION_NONE, FALSE, pEngineState->registration.sczActiveParent, pEngineState->registration.sczAncestors, NULL, pEngineState->command.wzCommandLine); + ExitOnFailure(hr, "Failed to recreate command-line for update bundle."); + + // Bundles would fail to use the downloaded update bundle, as the running bundle would be one of the search paths. + // Here I am generating a random guid, but in the future it would be nice if the feed would provide the ID of the update. + rs = ::UuidCreate(&guid); + hr = HRESULT_FROM_RPC(rs); + ExitOnFailure(hr, "Failed to create bundle update guid."); + + if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid))) + { + hr = E_OUTOFMEMORY; + ExitOnRootFailure(hr, "Failed to convert bundle update guid into string."); + } + + hr = StrAllocFormatted(&sczFilePath, L"%ls\\%ls", wzGuid, pEngineState->registration.sczExecutableName); + ExitOnFailure(hr, "Failed to build bundle update file path."); + + if (!wzLocalSource || !*wzLocalSource) + { + wzLocalSource = sczFilePath; + } + + hr = PseudoBundleInitialize(FILEMAKEVERSION(rmj, rmm, rup, rpr), &pEngineState->update.package, FALSE, pEngineState->registration.sczId, BOOTSTRAPPER_RELATION_UPDATE, BOOTSTRAPPER_PACKAGE_STATE_ABSENT, FALSE, sczFilePath, wzLocalSource, wzDownloadSource, qwSize, TRUE, sczCommandline, NULL, NULL, NULL, rgbHash, cbHash); + ExitOnFailure(hr, "Failed to set update bundle."); + + pEngineState->update.fUpdateAvailable = TRUE; + } + +LExit: + ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive); + + ReleaseStr(sczCommandline); + ReleaseStr(sczFilePath); + + return hr; +} + +HRESULT ExternalEngineSetLocalSource( + __in BURN_ENGINE_STATE* pEngineState, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in_z LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER* pContainer = NULL; + BURN_PAYLOAD* pPayload = NULL; + + ::EnterCriticalSection(&pEngineState->userExperience.csEngineActive); + hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience); + ExitOnFailure(hr, "Engine is active, cannot change engine state."); + + if (!wzPath || !*wzPath) + { + hr = E_INVALIDARG; + } + else if (wzPayloadId && *wzPayloadId) + { + hr = PayloadFindById(&pEngineState->payloads, wzPayloadId, &pPayload); + ExitOnFailure(hr, "BA requested unknown payload with id: %ls", wzPayloadId); + + hr = StrAllocString(&pPayload->sczSourcePath, wzPath, 0); + ExitOnFailure(hr, "Failed to set source path for payload."); + } + else if (wzPackageOrContainerId && *wzPackageOrContainerId) + { + hr = ContainerFindById(&pEngineState->containers, wzPackageOrContainerId, &pContainer); + ExitOnFailure(hr, "BA requested unknown container with id: %ls", wzPackageOrContainerId); + + hr = StrAllocString(&pContainer->sczSourcePath, wzPath, 0); + ExitOnFailure(hr, "Failed to set source path for container."); + } + else + { + hr = E_INVALIDARG; + } + +LExit: + ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive); + + return hr; +} + +HRESULT ExternalEngineSetDownloadSource( + __in BURN_ENGINE_STATE* pEngineState, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in_z_opt LPCWSTR wzUrl, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword + ) +{ + HRESULT hr = S_OK; + BURN_CONTAINER* pContainer = NULL; + BURN_PAYLOAD* pPayload = NULL; + DOWNLOAD_SOURCE* pDownloadSource = NULL; + + ::EnterCriticalSection(&pEngineState->userExperience.csEngineActive); + hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience); + ExitOnFailure(hr, "Engine is active, cannot change engine state."); + + if (wzPayloadId && *wzPayloadId) + { + hr = PayloadFindById(&pEngineState->payloads, wzPayloadId, &pPayload); + ExitOnFailure(hr, "BA requested unknown payload with id: %ls", wzPayloadId); + + pDownloadSource = &pPayload->downloadSource; + } + else if (wzPackageOrContainerId && *wzPackageOrContainerId) + { + hr = ContainerFindById(&pEngineState->containers, wzPackageOrContainerId, &pContainer); + ExitOnFailure(hr, "BA requested unknown container with id: %ls", wzPackageOrContainerId); + + pDownloadSource = &pContainer->downloadSource; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "BA did not provide container or payload id."); + } + + if (wzUrl && *wzUrl) + { + hr = StrAllocString(&pDownloadSource->sczUrl, wzUrl, 0); + ExitOnFailure(hr, "Failed to set download URL."); + + if (wzUser && *wzUser) + { + hr = StrAllocString(&pDownloadSource->sczUser, wzUser, 0); + ExitOnFailure(hr, "Failed to set download user."); + + if (wzPassword && *wzPassword) + { + hr = StrAllocString(&pDownloadSource->sczPassword, wzPassword, 0); + ExitOnFailure(hr, "Failed to set download password."); + } + else // no password. + { + ReleaseNullStr(pDownloadSource->sczPassword); + } + } + else // no user means no password either. + { + ReleaseNullStr(pDownloadSource->sczUser); + ReleaseNullStr(pDownloadSource->sczPassword); + } + } + else // no URL provided means clear out the whole download source. + { + ReleaseNullStr(pDownloadSource->sczUrl); + ReleaseNullStr(pDownloadSource->sczUser); + ReleaseNullStr(pDownloadSource->sczPassword); + } + +LExit: + ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive); + + return hr; +} + +HRESULT ExternalEngineSetVariableNumeric( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __in const LONGLONG llValue + ) +{ + HRESULT hr = S_OK; + + if (wzVariable && *wzVariable) + { + hr = VariableSetNumeric(&pEngineState->variables, wzVariable, llValue, FALSE); + ExitOnFailure(hr, "Failed to set numeric variable."); + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "SetVariableNumeric did not provide variable name."); + } + +LExit: + return hr; +} + +HRESULT ExternalEngineSetVariableString( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __in_z_opt LPCWSTR wzValue, + __in const BOOL fFormatted + ) +{ + HRESULT hr = S_OK; + + if (wzVariable && *wzVariable) + { + hr = VariableSetString(&pEngineState->variables, wzVariable, wzValue, FALSE, fFormatted); + ExitOnFailure(hr, "Failed to set string variable."); + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "SetVariableString did not provide variable name."); + } + +LExit: + return hr; +} + +HRESULT ExternalEngineSetVariableVersion( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __in_z_opt LPCWSTR wzValue + ) +{ + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion = NULL; + + if (wzVariable && *wzVariable) + { + if (wzValue) + { + hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); + ExitOnFailure(hr, "Failed to parse new version value."); + } + + hr = VariableSetVersion(&pEngineState->variables, wzVariable, pVersion, FALSE); + ExitOnFailure(hr, "Failed to set version variable."); + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "SetVariableVersion did not provide variable name."); + } + +LExit: + ReleaseVerutilVersion(pVersion); + + return hr; +} + +void ExternalEngineCloseSplashScreen( + __in BURN_ENGINE_STATE* pEngineState + ) +{ + // If the splash screen is still around, close it. + if (::IsWindow(pEngineState->command.hwndSplashScreen)) + { + ::PostMessageW(pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0); + } +} + +HRESULT ExternalEngineCompareVersions( + __in_z LPCWSTR wzVersion1, + __in_z LPCWSTR wzVersion2, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + + hr = VerCompareStringVersions(wzVersion1, wzVersion2, FALSE, pnResult); + + return hr; +} + +HRESULT ExternalEngineDetect( + __in const DWORD dwThreadId, + __in_opt const HWND hwndParent + ) +{ + HRESULT hr = S_OK; + + if (!::PostThreadMessageW(dwThreadId, WM_BURN_DETECT, 0, reinterpret_cast(hwndParent))) + { + ExitWithLastError(hr, "Failed to post detect message."); + } + +LExit: + return hr; +} + +HRESULT ExternalEnginePlan( + __in const DWORD dwThreadId, + __in const BOOTSTRAPPER_ACTION action + ) +{ + HRESULT hr = S_OK; + + if (BOOTSTRAPPER_ACTION_LAYOUT > action || BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED < action) + { + ExitOnRootFailure(hr = E_INVALIDARG, "BA passed invalid action to Plan: %u.", action); + } + + if (!::PostThreadMessageW(dwThreadId, WM_BURN_PLAN, 0, action)) + { + ExitWithLastError(hr, "Failed to post plan message."); + } + +LExit: + return hr; +} + +HRESULT ExternalEngineElevate( + __in BURN_ENGINE_STATE* pEngineState, + __in const DWORD dwThreadId, + __in_opt const HWND hwndParent + ) +{ + HRESULT hr = S_OK; + + if (INVALID_HANDLE_VALUE != pEngineState->companionConnection.hPipe) + { + hr = HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED); + } + else if (!::PostThreadMessageW(dwThreadId, WM_BURN_ELEVATE, 0, reinterpret_cast(hwndParent))) + { + ExitWithLastError(hr, "Failed to post elevate message."); + } + +LExit: + return hr; +} + +HRESULT ExternalEngineApply( + __in const DWORD dwThreadId, + __in_opt const HWND hwndParent + ) +{ + HRESULT hr = S_OK; + + ExitOnNull(hwndParent, hr, E_INVALIDARG, "BA passed NULL hwndParent to Apply."); + if (!::IsWindow(hwndParent)) + { + ExitOnRootFailure(hr = E_INVALIDARG, "BA passed invalid hwndParent to Apply."); + } + + if (!::PostThreadMessageW(dwThreadId, WM_BURN_APPLY, 0, reinterpret_cast(hwndParent))) + { + ExitWithLastError(hr, "Failed to post apply message."); + } + +LExit: + return hr; +} + +HRESULT ExternalEngineQuit( + __in const DWORD dwThreadId, + __in const DWORD dwExitCode + ) +{ + HRESULT hr = S_OK; + + if (!::PostThreadMessageW(dwThreadId, WM_BURN_QUIT, static_cast(dwExitCode), 0)) + { + ExitWithLastError(hr, "Failed to post shutdown message."); + } + +LExit: + return hr; +} + +HRESULT ExternalEngineLaunchApprovedExe( + __in BURN_ENGINE_STATE* pEngineState, + __in const DWORD dwThreadId, + __in_opt const HWND hwndParent, + __in_z LPCWSTR wzApprovedExeForElevationId, + __in_z_opt LPCWSTR wzArguments, + __in const DWORD dwWaitForInputIdleTimeout + ) +{ + HRESULT hr = S_OK; + BURN_APPROVED_EXE* pApprovedExe = NULL; + BOOL fLeaveCriticalSection = FALSE; + BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe = NULL; + + pLaunchApprovedExe = (BURN_LAUNCH_APPROVED_EXE*)MemAlloc(sizeof(BURN_LAUNCH_APPROVED_EXE), TRUE); + ExitOnNull(pLaunchApprovedExe, hr, E_OUTOFMEMORY, "Failed to alloc BURN_LAUNCH_APPROVED_EXE"); + + ::EnterCriticalSection(&pEngineState->userExperience.csEngineActive); + fLeaveCriticalSection = TRUE; + hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience); + ExitOnFailure(hr, "Engine is active, cannot change engine state."); + + if (!wzApprovedExeForElevationId || !*wzApprovedExeForElevationId) + { + ExitFunction1(hr = E_INVALIDARG); + } + + hr = ApprovedExesFindById(&pEngineState->approvedExes, wzApprovedExeForElevationId, &pApprovedExe); + ExitOnFailure(hr, "BA requested unknown approved exe with id: %ls", wzApprovedExeForElevationId); + + hr = StrAllocString(&pLaunchApprovedExe->sczId, wzApprovedExeForElevationId, NULL); + ExitOnFailure(hr, "Failed to copy the id."); + + if (wzArguments) + { + hr = StrAllocString(&pLaunchApprovedExe->sczArguments, wzArguments, NULL); + ExitOnFailure(hr, "Failed to copy the arguments."); + } + + pLaunchApprovedExe->dwWaitForInputIdleTimeout = dwWaitForInputIdleTimeout; + + pLaunchApprovedExe->hwndParent = hwndParent; + + if (!::PostThreadMessageW(dwThreadId, WM_BURN_LAUNCH_APPROVED_EXE, 0, reinterpret_cast(pLaunchApprovedExe))) + { + ExitWithLastError(hr, "Failed to post launch approved exe message."); + } + +LExit: + if (fLeaveCriticalSection) + { + ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive); + } + + if (FAILED(hr)) + { + ApprovedExesUninitializeLaunch(pLaunchApprovedExe); + } + + return hr; +} + +HRESULT ExternalEngineSetUpdateSource( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzUrl + ) +{ + HRESULT hr = S_OK; + BOOL fLeaveCriticalSection = FALSE; + + ::EnterCriticalSection(&pEngineState->userExperience.csEngineActive); + fLeaveCriticalSection = TRUE; + hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience); + ExitOnFailure(hr, "Engine is active, cannot change engine state."); + + if (wzUrl && *wzUrl) + { + hr = StrAllocString(&pEngineState->update.sczUpdateSource, wzUrl, 0); + ExitOnFailure(hr, "Failed to set feed download URL."); + } + else // no URL provided means clear out the whole download source. + { + ReleaseNullStr(pEngineState->update.sczUpdateSource); + } + +LExit: + if (fLeaveCriticalSection) + { + ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive); + } + + return hr; +} + +// TODO: callers need to provide the original size (at the time of first public release) of the struct instead of the current size. +HRESULT WINAPI ExternalEngineValidateMessageParameter( + __in_opt const LPVOID pv, + __in SIZE_T cbSizeOffset, + __in DWORD dwMinimumSize + ) +{ + HRESULT hr = S_OK; + + if (!pv) + { + ExitFunction1(hr = E_INVALIDARG); + } + + DWORD cbSize = *(DWORD*)((BYTE*)pv + cbSizeOffset); + if (dwMinimumSize < cbSize) + { + ExitFunction1(hr = E_INVALIDARG); + } + +LExit: + return hr; +} + +static HRESULT CopyStringToExternal( + __in_z LPWSTR wzValue, + __in_z_opt LPWSTR wzBuffer, + __inout SIZE_T* pcchBuffer + ) +{ + HRESULT hr = S_OK; + BOOL fTooSmall = !wzBuffer; + + if (!fTooSmall) + { + hr = ::StringCchCopyExW(wzBuffer, *pcchBuffer, wzValue, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + fTooSmall = TRUE; + } + } + + if (fTooSmall) + { + hr = ::StringCchLengthW(wzValue, STRSAFE_MAX_LENGTH, reinterpret_cast(pcchBuffer)); + if (SUCCEEDED(hr)) + { + hr = E_MOREDATA; + *pcchBuffer += 1; // null terminator. + } + } + + return hr; +} diff --git a/src/burn/engine/externalengine.h b/src/burn/engine/externalengine.h new file mode 100644 index 00000000..2903615d --- /dev/null +++ b/src/burn/engine/externalengine.h @@ -0,0 +1,181 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#define ValidateMessageParameter(x, pv, type) { x = ExternalEngineValidateMessageParameter(pv, offsetof(type, cbSize), sizeof(type)); if (FAILED(x)) { goto LExit; }} +#define ValidateMessageArgs(x, pv, type, identifier) ValidateMessageParameter(x, pv, type); const type* identifier = reinterpret_cast(pv); UNREFERENCED_PARAMETER(identifier) +#define ValidateMessageResults(x, pv, type, identifier) ValidateMessageParameter(x, pv, type); type* identifier = reinterpret_cast(pv); UNREFERENCED_PARAMETER(identifier) + + +#if defined(__cplusplus) +extern "C" { +#endif + +void ExternalEngineGetPackageCount( + __in BURN_ENGINE_STATE* pEngineState, + __out DWORD* pcPackages + ); + +HRESULT ExternalEngineGetVariableNumeric( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __out LONGLONG* pllValue + ); + +HRESULT ExternalEngineGetVariableString( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __out_ecount_opt(*pcchValue) LPWSTR wzValue, + __inout SIZE_T* pcchValue + ); + +HRESULT ExternalEngineGetVariableVersion( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __out_ecount_opt(*pcchValue) LPWSTR wzValue, + __inout SIZE_T* pcchValue + ); + +HRESULT ExternalEngineFormatString( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzIn, + __out_ecount_opt(*pcchOut) LPWSTR wzOut, + __inout SIZE_T* pcchOut + ); + +HRESULT ExternalEngineEscapeString( + __in_z LPCWSTR wzIn, + __out_ecount_opt(*pcchOut) LPWSTR wzOut, + __inout SIZE_T* pcchOut + ); + +HRESULT ExternalEngineEvaluateCondition( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzCondition, + __out BOOL* pf + ); + +HRESULT ExternalEngineLog( + __in REPORT_LEVEL rl, + __in_z LPCWSTR wzMessage + ); + +HRESULT ExternalEngineSendEmbeddedError( + __in BURN_ENGINE_STATE* pEngineState, + __in const DWORD dwErrorCode, + __in_z LPCWSTR wzMessage, + __in const DWORD dwUIHint, + __out int* pnResult + ); + +HRESULT ExternalEngineSendEmbeddedProgress( + __in BURN_ENGINE_STATE* pEngineState, + __in const DWORD dwProgressPercentage, + __in const DWORD dwOverallProgressPercentage, + __out int* pnResult + ); + +HRESULT ExternalEngineSetUpdate( + __in BURN_ENGINE_STATE* pEngineState, + __in_z_opt LPCWSTR wzLocalSource, + __in_z_opt LPCWSTR wzDownloadSource, + __in const DWORD64 qwSize, + __in const BOOTSTRAPPER_UPDATE_HASH_TYPE hashType, + __in_opt const BYTE* rgbHash, + __in const DWORD cbHash + ); + +HRESULT ExternalEngineSetLocalSource( + __in BURN_ENGINE_STATE* pEngineState, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in_z LPCWSTR wzPath + ); + +HRESULT ExternalEngineSetDownloadSource( + __in BURN_ENGINE_STATE* pEngineState, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in_z_opt LPCWSTR wzUrl, + __in_z_opt LPCWSTR wzUser, + __in_z_opt LPCWSTR wzPassword + ); + +HRESULT ExternalEngineSetVariableNumeric( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __in const LONGLONG llValue + ); + +HRESULT ExternalEngineSetVariableString( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __in_z_opt LPCWSTR wzValue, + __in const BOOL fFormatted + ); + +HRESULT ExternalEngineSetVariableVersion( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzVariable, + __in_z_opt LPCWSTR wzValue + ); + +void ExternalEngineCloseSplashScreen( + __in BURN_ENGINE_STATE* pEngineState + ); + +HRESULT ExternalEngineCompareVersions( + __in_z LPCWSTR wzVersion1, + __in_z LPCWSTR wzVersion2, + __out int* pnResult + ); + +HRESULT ExternalEngineDetect( + __in const DWORD dwThreadId, + __in_opt const HWND hwndParent + ); + +HRESULT ExternalEnginePlan( + __in const DWORD dwThreadId, + __in const BOOTSTRAPPER_ACTION action + ); + +HRESULT ExternalEngineElevate( + __in BURN_ENGINE_STATE* pEngineState, + __in const DWORD dwThreadId, + __in_opt const HWND hwndParent + ); + +HRESULT ExternalEngineApply( + __in const DWORD dwThreadId, + __in_opt const HWND hwndParent + ); + +HRESULT ExternalEngineQuit( + __in const DWORD dwThreadId, + __in const DWORD dwExitCode + ); + +HRESULT ExternalEngineLaunchApprovedExe( + __in BURN_ENGINE_STATE* pEngineState, + __in const DWORD dwThreadId, + __in_opt const HWND hwndParent, + __in_z LPCWSTR wzApprovedExeForElevationId, + __in_z_opt LPCWSTR wzArguments, + __in const DWORD dwWaitForInputIdleTimeout + ); + +HRESULT ExternalEngineSetUpdateSource( + __in BURN_ENGINE_STATE* pEngineState, + __in_z LPCWSTR wzUrl + ); + +HRESULT WINAPI ExternalEngineValidateMessageParameter( + __in_opt const LPVOID pv, + __in SIZE_T cbSizeOffset, + __in DWORD dwMinimumSize + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/inc/burnsources.h b/src/burn/engine/inc/burnsources.h new file mode 100644 index 00000000..bff79ed5 --- /dev/null +++ b/src/burn/engine/inc/burnsources.h @@ -0,0 +1,4 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_EXTERNAL diff --git a/src/burn/engine/inc/engine.h b/src/burn/engine/inc/engine.h new file mode 100644 index 00000000..808bb91a --- /dev/null +++ b/src/burn/engine/inc/engine.h @@ -0,0 +1,27 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// function declarations + +BOOL EngineInCleanRoom( + __in_z_opt LPCWSTR wzCommandLine + ); + +HRESULT EngineRun( + __in HINSTANCE hInstance, + __in HANDLE hEngineFile, + __in_z_opt LPCWSTR wzCommandLine, + __in int nCmdShow, + __out DWORD* pdwExitCode + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/logging.cpp b/src/burn/engine/logging.cpp new file mode 100644 index 00000000..065ef907 --- /dev/null +++ b/src/burn/engine/logging.cpp @@ -0,0 +1,754 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +static DWORD vdwPackageSequence = 0; +static const DWORD LOG_OPEN_RETRY_COUNT = 3; +static const DWORD LOG_OPEN_RETRY_WAIT = 2000; +static CONST LPWSTR LOG_FAILED_EVENT_LOG_MESSAGE = L"Burn Engine Fatal Error: failed to open log file."; + +// structs + + + +// internal function declarations + +static void CheckLoggingPolicy( + __out DWORD *pdwAttributes + ); +static HRESULT GetNonSessionSpecificTempFolder( + __deref_out_z LPWSTR* psczNonSessionTempFolder + ); + + +// function definitions + +extern "C" HRESULT LoggingOpen( + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __in_z LPCWSTR wzBundleName + ) +{ + HRESULT hr = S_OK; + LPWSTR sczLoggingBaseFolder = NULL; + LPWSTR sczPrefixFormatted = NULL; + + // Check if the logging policy is set and configure the logging appropriately. + CheckLoggingPolicy(&pLog->dwAttributes); + + if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_VERBOSE || pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG) + { + if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG) + { + LogSetLevel(REPORT_DEBUG, FALSE); + } + else if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_VERBOSE) + { + LogSetLevel(REPORT_VERBOSE, FALSE); + } + + if ((!pLog->sczPath || !*pLog->sczPath) && (!pLog->sczPrefix || !*pLog->sczPrefix)) + { + PathCreateTimeBasedTempFile(NULL, L"Setup", NULL, L"log", &pLog->sczPath, NULL); + } + } + + // Open the log approriately. + if (pLog->sczPath && *pLog->sczPath) + { + DWORD cRetry = 0; + + hr = DirGetCurrent(&sczLoggingBaseFolder); + ExitOnFailure(hr, "Failed to get current directory."); + + // Try pretty hard to open the log file when appending. + do + { + if (0 < cRetry) + { + ::Sleep(LOG_OPEN_RETRY_WAIT); + } + + hr = LogOpen(sczLoggingBaseFolder, pLog->sczPath, NULL, NULL, pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND, FALSE, &pLog->sczPath); + if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND && HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) == hr) + { + ++cRetry; + } + } while (cRetry > 0 && cRetry <= LOG_OPEN_RETRY_COUNT); + + if (FAILED(hr)) + { + // Log is not open, so note that. + LogDisable(); + pLog->state = BURN_LOGGING_STATE_DISABLED; + + if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND) + { + // If appending, ignore the failure and continue. + hr = S_OK; + } + else // specifically tried to create a log file so show an error if appropriate and bail. + { + HRESULT hrOriginal = hr; + + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_LOG_FAILURE); + SplashScreenDisplayError(display, wzBundleName, hr); + + ExitOnFailure(hrOriginal, "Failed to open log: %ls", pLog->sczPath); + } + } + else + { + pLog->state = BURN_LOGGING_STATE_OPEN; + } + } + else + { + if (pLog->sczPrefix && *pLog->sczPrefix) + { + hr = VariableFormatString(pVariables, pLog->sczPrefix, &sczPrefixFormatted, NULL); + } + + if (sczPrefixFormatted && *sczPrefixFormatted) + { + LPCWSTR wzPrefix = sczPrefixFormatted; + + // Best effort to open default logging. + if (PathIsAbsolute(sczPrefixFormatted)) + { + hr = PathGetDirectory(sczPrefixFormatted, &sczLoggingBaseFolder); + ExitOnFailure(hr, "Failed to get parent directory from '%ls'.", sczPrefixFormatted); + + wzPrefix = PathFile(sczPrefixFormatted); + } + else + { + hr = GetNonSessionSpecificTempFolder(&sczLoggingBaseFolder); + ExitOnFailure(hr, "Failed to get non-session specific TEMP folder."); + } + + hr = LogOpen(sczLoggingBaseFolder, wzPrefix, NULL, pLog->sczExtension, FALSE, FALSE, &pLog->sczPath); + if (FAILED(hr)) + { + LogDisable(); + pLog->state = BURN_LOGGING_STATE_DISABLED; + + hr = S_OK; + } + else + { + pLog->state = BURN_LOGGING_STATE_OPEN; + } + } + else // no logging enabled. + { + LogDisable(); + pLog->state = BURN_LOGGING_STATE_DISABLED; + } + } + + // If the log was opened, write the header info and update the prefix and extension to match + // the log name so future logs are opened with the same pattern. + if (BURN_LOGGING_STATE_OPEN == pLog->state) + { + LPCWSTR wzExtension = PathExtension(pLog->sczPath); + if (wzExtension && *wzExtension) + { + hr = StrAllocString(&pLog->sczPrefix, pLog->sczPath, wzExtension - pLog->sczPath); + ExitOnFailure(hr, "Failed to copy log path to prefix."); + + hr = StrAllocString(&pLog->sczExtension, wzExtension + 1, 0); + ExitOnFailure(hr, "Failed to copy log extension to extension."); + } + else + { + hr = StrAllocString(&pLog->sczPrefix, pLog->sczPath, 0); + ExitOnFailure(hr, "Failed to copy full log path to prefix."); + } + + if (pLog->sczPathVariable && *pLog->sczPathVariable) + { + VariableSetString(pVariables, pLog->sczPathVariable, pLog->sczPath, FALSE, FALSE); // Ignore failure. + } + } + +LExit: + ReleaseStr(sczLoggingBaseFolder); + StrSecureZeroFreeString(sczPrefixFormatted); + + return hr; +} + +extern "C" void LoggingOpenFailed() +{ + HRESULT hr = S_OK; + HANDLE hEventLog = NULL; + LPCWSTR* lpStrings = const_cast(&LOG_FAILED_EVENT_LOG_MESSAGE); + WORD wNumStrings = 1; + + hr = LogOpen(NULL, L"Setup", L"_Failed", L"txt", FALSE, FALSE, NULL); + if (SUCCEEDED(hr)) + { + ExitFunction(); + } + + // If opening the "failure" log failed, then attempt to record that in the Application event log. + hEventLog = ::OpenEventLogW(NULL, L"Application"); + ExitOnNullWithLastError(hEventLog, hr, "Failed to open Application event log"); + + hr = ::ReportEventW(hEventLog, EVENTLOG_ERROR_TYPE, 1, 1, NULL, wNumStrings, 0, lpStrings, NULL); + ExitOnNullWithLastError(hEventLog, hr, "Failed to write event log entry"); + +LExit: + if (hEventLog) + { + ::CloseEventLog(hEventLog); + } +} + +extern "C" void LoggingIncrementPackageSequence() +{ + ++vdwPackageSequence; +} + +extern "C" HRESULT LoggingSetPackageVariable( + __in BURN_PACKAGE* pPackage, + __in_z_opt LPCWSTR wzSuffix, + __in BOOL fRollback, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __out_opt LPWSTR* psczLogPath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczLogPath = NULL; + + // Make sure that no package log files are created when logging has been disabled via Log element. + if (BURN_LOGGING_STATE_DISABLED == pLog->state) + { + if (psczLogPath) + { + *psczLogPath = NULL; + } + + ExitFunction(); + } + + if ((!fRollback && pPackage->sczLogPathVariable && *pPackage->sczLogPathVariable) || + (fRollback && pPackage->sczRollbackLogPathVariable && *pPackage->sczRollbackLogPathVariable)) + { + hr = StrAllocFormatted(&sczLogPath, L"%ls%hs%ls_%03u_%ls%ls.%ls", pLog->sczPrefix, wzSuffix && *wzSuffix ? "_" : "", wzSuffix && *wzSuffix ? wzSuffix : L"", vdwPackageSequence, pPackage->sczId, fRollback ? L"_rollback" : L"", pLog->sczExtension); + ExitOnFailure(hr, "Failed to allocate path for package log."); + + hr = VariableSetString(pVariables, fRollback ? pPackage->sczRollbackLogPathVariable : pPackage->sczLogPathVariable, sczLogPath, FALSE, FALSE); + ExitOnFailure(hr, "Failed to set log path into variable."); + + if (psczLogPath) + { + hr = StrAllocString(psczLogPath, sczLogPath, 0); + ExitOnFailure(hr, "Failed to copy package log path."); + } + } + +LExit: + ReleaseStr(sczLogPath); + + return hr; +} + +extern "C" LPCSTR LoggingBurnActionToString( + __in BOOTSTRAPPER_ACTION action + ) +{ + switch (action) + { + case BOOTSTRAPPER_ACTION_UNKNOWN: + return "Unknown"; + case BOOTSTRAPPER_ACTION_HELP: + return "Help"; + case BOOTSTRAPPER_ACTION_LAYOUT: + return "Layout"; + case BOOTSTRAPPER_ACTION_CACHE: + return "Cache"; + case BOOTSTRAPPER_ACTION_UNINSTALL: + return "Uninstall"; + case BOOTSTRAPPER_ACTION_INSTALL: + return "Install"; + case BOOTSTRAPPER_ACTION_MODIFY: + return "Modify"; + case BOOTSTRAPPER_ACTION_REPAIR: + return "Repair"; + case BOOTSTRAPPER_ACTION_UPDATE_REPLACE: + return "UpdateReplace"; + case BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED: + return "UpdateReplaceEmbedded"; + default: + return "Invalid"; + } +} + +LPCSTR LoggingBurnMessageToString( + __in UINT message + ) +{ + switch (message) + { + case WM_BURN_APPLY: + return "Apply"; + case WM_BURN_DETECT: + return "Detect"; + case WM_BURN_ELEVATE: + return "Elevate"; + case WM_BURN_LAUNCH_APPROVED_EXE: + return "LaunchApprovedExe"; + case WM_BURN_PLAN: + return "Plan"; + case WM_BURN_QUIT: + return "Quit"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingActionStateToString( + __in BOOTSTRAPPER_ACTION_STATE actionState + ) +{ + switch (actionState) + { + case BOOTSTRAPPER_ACTION_STATE_NONE: + return "None"; + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: + return "Uninstall"; + case BOOTSTRAPPER_ACTION_STATE_INSTALL: + return "Install"; + case BOOTSTRAPPER_ACTION_STATE_MODIFY: + return "Modify"; + case BOOTSTRAPPER_ACTION_STATE_MEND: + return "Mend"; + case BOOTSTRAPPER_ACTION_STATE_REPAIR: + return "Repair"; + case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: + return "MinorUpgrade"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingDependencyActionToString( + BURN_DEPENDENCY_ACTION action + ) +{ + switch (action) + { + case BURN_DEPENDENCY_ACTION_NONE: + return "None"; + case BURN_DEPENDENCY_ACTION_REGISTER: + return "Register"; + case BURN_DEPENDENCY_ACTION_UNREGISTER: + return "Unregister"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingBoolToString( + __in BOOL f + ) +{ + if (f) + { + return "Yes"; + } + + return "No"; +} + +extern "C" LPCSTR LoggingTrueFalseToString( + __in BOOL f + ) +{ + if (f) + { + return "true"; + } + + return "false"; +} + +extern "C" LPCSTR LoggingPackageStateToString( + __in BOOTSTRAPPER_PACKAGE_STATE packageState + ) +{ + switch (packageState) + { + case BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN: + return "Unknown"; + case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: + return "Obsolete"; + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: + return "Absent"; + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: + return "Present"; + case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: + return "Superseded"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingPackageRegistrationStateToString( + __in BOOL fCanAffectRegistration, + __in BURN_PACKAGE_REGISTRATION_STATE registrationState + ) +{ + if (!fCanAffectRegistration) + { + return "(permanent)"; + } + + switch (registrationState) + { + case BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN: + return "Unknown"; + case BURN_PACKAGE_REGISTRATION_STATE_IGNORED: + return "Ignored"; + case BURN_PACKAGE_REGISTRATION_STATE_ABSENT: + return "Absent"; + case BURN_PACKAGE_REGISTRATION_STATE_PRESENT: + return "Present"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingMsiFeatureStateToString( + __in BOOTSTRAPPER_FEATURE_STATE featureState + ) +{ + switch (featureState) + { + case BOOTSTRAPPER_FEATURE_STATE_UNKNOWN: + return "Unknown"; + case BOOTSTRAPPER_FEATURE_STATE_ABSENT: + return "Absent"; + case BOOTSTRAPPER_FEATURE_STATE_ADVERTISED: + return "Advertised"; + case BOOTSTRAPPER_FEATURE_STATE_LOCAL: + return "Local"; + case BOOTSTRAPPER_FEATURE_STATE_SOURCE: + return "Source"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingMsiFeatureActionToString( + __in BOOTSTRAPPER_FEATURE_ACTION featureAction + ) +{ + switch (featureAction) + { + case BOOTSTRAPPER_FEATURE_ACTION_NONE: + return "None"; + case BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL: + return "AddLocal"; + case BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE: + return "AddSource"; + case BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT: + return "AddDefault"; + case BOOTSTRAPPER_FEATURE_ACTION_REINSTALL: + return "Reinstall"; + case BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE: + return "Advertise"; + case BOOTSTRAPPER_FEATURE_ACTION_REMOVE: + return "Remove"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingMsiInstallContext( + __in MSIINSTALLCONTEXT context + ) +{ + switch (context) + { + case MSIINSTALLCONTEXT_ALL: + return "All"; + case MSIINSTALLCONTEXT_ALLUSERMANAGED: + return "AllUserManaged"; + case MSIINSTALLCONTEXT_MACHINE: + return "Machine"; + case MSIINSTALLCONTEXT_NONE: + return "None"; + case MSIINSTALLCONTEXT_USERMANAGED: + return "UserManaged"; + case MSIINSTALLCONTEXT_USERUNMANAGED: + return "UserUnmanaged"; + default: + return "Invalid"; + } +} + +extern "C" LPCWSTR LoggingBurnMsiPropertyToString( + __in BURN_MSI_PROPERTY burnMsiProperty + ) +{ + switch (burnMsiProperty) + { + case BURN_MSI_PROPERTY_INSTALL: + return BURNMSIINSTALL_PROPERTY_NAME; + case BURN_MSI_PROPERTY_MODIFY: + return BURNMSIMODIFY_PROPERTY_NAME; + case BURN_MSI_PROPERTY_NONE: + return L"(none)"; + case BURN_MSI_PROPERTY_REPAIR: + return BURNMSIREPAIR_PROPERTY_NAME; + case BURN_MSI_PROPERTY_UNINSTALL: + return BURNMSIUNINSTALL_PROPERTY_NAME; + default: + return L"Invalid"; + } +} + +extern "C" LPCSTR LoggingMspTargetActionToString( + __in BOOTSTRAPPER_ACTION_STATE action, + __in BURN_PATCH_SKIP_STATE skipState + ) +{ + switch (skipState) + { + case BURN_PATCH_SKIP_STATE_NONE: + return LoggingActionStateToString(action); + case BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL: + return "Skipped (target uninstall)"; + case BURN_PATCH_SKIP_STATE_SLIPSTREAM: + return "Skipped (slipstream)"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingPerMachineToString( + __in BOOL fPerMachine + ) +{ + if (fPerMachine) + { + return "PerMachine"; + } + + return "PerUser"; +} + +extern "C" LPCSTR LoggingRestartToString( + __in BOOTSTRAPPER_APPLY_RESTART restart + ) +{ + switch (restart) + { + case BOOTSTRAPPER_APPLY_RESTART_NONE: + return "None"; + case BOOTSTRAPPER_APPLY_RESTART_REQUIRED: + return "Required"; + case BOOTSTRAPPER_APPLY_RESTART_INITIATED: + return "Initiated"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingResumeModeToString( + __in BURN_RESUME_MODE resumeMode + ) +{ + switch (resumeMode) + { + case BURN_RESUME_MODE_NONE: + return "None"; + case BURN_RESUME_MODE_ACTIVE: + return "Active"; + case BURN_RESUME_MODE_SUSPEND: + return "Suspend"; + case BURN_RESUME_MODE_ARP: + return "ARP"; + case BURN_RESUME_MODE_REBOOT_PENDING: + return "Reboot Pending"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingRelationTypeToString( + __in BOOTSTRAPPER_RELATION_TYPE type + ) +{ + switch (type) + { + case BOOTSTRAPPER_RELATION_NONE: + return "None"; + case BOOTSTRAPPER_RELATION_DETECT: + return "Detect"; + case BOOTSTRAPPER_RELATION_UPGRADE: + return "Upgrade"; + case BOOTSTRAPPER_RELATION_ADDON: + return "Addon"; + case BOOTSTRAPPER_RELATION_PATCH: + return "Patch"; + case BOOTSTRAPPER_RELATION_DEPENDENT: + return "Dependent"; + case BOOTSTRAPPER_RELATION_UPDATE: + return "Update"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingRelatedOperationToString( + __in BOOTSTRAPPER_RELATED_OPERATION operation + ) +{ + switch (operation) + { + case BOOTSTRAPPER_RELATED_OPERATION_NONE: + return "None"; + case BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE: + return "Downgrade"; + case BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE: + return "MinorUpdate"; + case BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE: + return "MajorUpgrade"; + case BOOTSTRAPPER_RELATED_OPERATION_REMOVE: + return "Remove"; + case BOOTSTRAPPER_RELATED_OPERATION_INSTALL: + return "Install"; + case BOOTSTRAPPER_RELATED_OPERATION_REPAIR: + return "Repair"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingRequestStateToString( + __in BOOTSTRAPPER_REQUEST_STATE requestState + ) +{ + switch (requestState) + { + case BOOTSTRAPPER_REQUEST_STATE_NONE: + return "None"; + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: + return "ForceAbsent"; + case BOOTSTRAPPER_REQUEST_STATE_ABSENT: + return "Absent"; + case BOOTSTRAPPER_REQUEST_STATE_CACHE: + return "Cache"; + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: + return "Present"; + case BOOTSTRAPPER_REQUEST_STATE_MEND: + return "Mend"; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + return "Repair"; + default: + return "Invalid"; + } +} + +extern "C" LPCSTR LoggingRollbackOrExecute( + __in BOOL fRollback + ) +{ + return fRollback ? "rollback" : "execute"; +} + +extern "C" LPWSTR LoggingStringOrUnknownIfNull( + __in LPCWSTR wz + ) +{ + return wz ? wz : L"Unknown"; +} + + +// internal function declarations + +static void CheckLoggingPolicy( + __out DWORD *pdwAttributes + ) +{ + HRESULT hr = S_OK; + HKEY hk = NULL; + LPWSTR sczLoggingPolicy = NULL; + + hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Policies\\Microsoft\\Windows\\Installer", KEY_READ, &hk); + if (SUCCEEDED(hr)) + { + hr = RegReadString(hk, L"Logging", &sczLoggingPolicy); + if (SUCCEEDED(hr)) + { + LPCWSTR wz = sczLoggingPolicy; + while (*wz) + { + if (L'v' == *wz || L'V' == *wz) + { + *pdwAttributes |= BURN_LOGGING_ATTRIBUTE_VERBOSE; + } + else if (L'x' == *wz || L'X' == *wz) + { + *pdwAttributes |= BURN_LOGGING_ATTRIBUTE_EXTRADEBUG; + } + + ++wz; + } + } + } + + ReleaseStr(sczLoggingPolicy); + ReleaseRegKey(hk); +} + +static HRESULT GetNonSessionSpecificTempFolder( + __deref_out_z LPWSTR* psczNonSessionTempFolder + ) +{ + HRESULT hr = S_OK; + WCHAR wzTempFolder[MAX_PATH] = { }; + SIZE_T cchTempFolder = 0; + DWORD dwSessionId = 0; + LPWSTR sczSessionId = 0; + SIZE_T cchSessionId = 0; + + if (!::GetTempPathW(countof(wzTempFolder), wzTempFolder)) + { + ExitWithLastError(hr, "Failed to get temp folder."); + } + + hr = ::StringCchLengthW(wzTempFolder, countof(wzTempFolder), reinterpret_cast(&cchTempFolder)); + ExitOnFailure(hr, "Failed to get length of temp folder."); + + // If our session id is in the TEMP path then remove that part so we get the non-session + // specific temporary folder. + if (::ProcessIdToSessionId(::GetCurrentProcessId(), &dwSessionId)) + { + hr = StrAllocFormatted(&sczSessionId, L"%u\\", dwSessionId); + ExitOnFailure(hr, "Failed to format session id as a string."); + + hr = ::StringCchLengthW(sczSessionId, STRSAFE_MAX_CCH, reinterpret_cast(&cchSessionId)); + ExitOnFailure(hr, "Failed to get length of session id string."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzTempFolder + cchTempFolder - cchSessionId, static_cast(cchSessionId), sczSessionId, static_cast(cchSessionId))) + { + cchTempFolder -= cchSessionId; + } + } + + hr = StrAllocString(psczNonSessionTempFolder, wzTempFolder, cchTempFolder); + ExitOnFailure(hr, "Failed to copy temp folder."); + +LExit: + ReleaseStr(sczSessionId); + + return hr; +} diff --git a/src/burn/engine/logging.h b/src/burn/engine/logging.h new file mode 100644 index 00000000..601039f9 --- /dev/null +++ b/src/burn/engine/logging.h @@ -0,0 +1,153 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + +enum BURN_LOGGING_STATE +{ + BURN_LOGGING_STATE_CLOSED, + BURN_LOGGING_STATE_OPEN, + BURN_LOGGING_STATE_DISABLED, +}; + +enum BURN_LOGGING_ATTRIBUTE +{ + BURN_LOGGING_ATTRIBUTE_APPEND = 0x1, + BURN_LOGGING_ATTRIBUTE_VERBOSE = 0x2, + BURN_LOGGING_ATTRIBUTE_EXTRADEBUG = 0x4, +}; + + +// structs + +typedef struct _BURN_LOGGING +{ + BURN_LOGGING_STATE state; + LPWSTR sczPathVariable; + + DWORD dwAttributes; + LPWSTR sczPath; + LPWSTR sczPrefix; + LPWSTR sczExtension; +} BURN_LOGGING; + + + +// function declarations + +HRESULT LoggingOpen( + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __in_z LPCWSTR wzBundleName + ); + +void LoggingOpenFailed(); + +void LoggingIncrementPackageSequence(); + +HRESULT LoggingSetPackageVariable( + __in BURN_PACKAGE* pPackage, + __in_z_opt LPCWSTR wzSuffix, + __in BOOL fRollback, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __out_opt LPWSTR* psczLogPath + ); + +LPCSTR LoggingBurnActionToString( + __in BOOTSTRAPPER_ACTION action + ); + +LPCSTR LoggingBurnMessageToString( + __in UINT message + ); + +LPCSTR LoggingActionStateToString( + __in BOOTSTRAPPER_ACTION_STATE actionState + ); + +LPCSTR LoggingDependencyActionToString( + BURN_DEPENDENCY_ACTION action + ); + +LPCSTR LoggingBoolToString( + __in BOOL f + ); + +LPCSTR LoggingTrueFalseToString( + __in BOOL f + ); + +LPCSTR LoggingPackageStateToString( + __in BOOTSTRAPPER_PACKAGE_STATE packageState + ); + +LPCSTR LoggingPackageRegistrationStateToString( + __in BOOL fCanAffectRegistration, + __in BURN_PACKAGE_REGISTRATION_STATE registrationState + ); + +LPCSTR LoggingMsiFeatureStateToString( + __in BOOTSTRAPPER_FEATURE_STATE featureState + ); + +LPCSTR LoggingMsiFeatureActionToString( + __in BOOTSTRAPPER_FEATURE_ACTION featureAction + ); + +LPCSTR LoggingMsiInstallContext( + __in MSIINSTALLCONTEXT context + ); + +LPCWSTR LoggingBurnMsiPropertyToString( + __in BURN_MSI_PROPERTY burnMsiProperty + ); + +LPCSTR LoggingMspTargetActionToString( + __in BOOTSTRAPPER_ACTION_STATE action, + __in BURN_PATCH_SKIP_STATE skipState + ); + +LPCSTR LoggingPerMachineToString( + __in BOOL fPerMachine + ); + +LPCSTR LoggingRestartToString( + __in BOOTSTRAPPER_APPLY_RESTART restart + ); + +LPCSTR LoggingResumeModeToString( + __in BURN_RESUME_MODE resumeMode + ); + +LPCSTR LoggingRelationTypeToString( + __in BOOTSTRAPPER_RELATION_TYPE type + ); + +LPCSTR LoggingRelatedOperationToString( + __in BOOTSTRAPPER_RELATED_OPERATION operation + ); + +LPCSTR LoggingRequestStateToString( + __in BOOTSTRAPPER_REQUEST_STATE requestState + ); + +LPCSTR LoggingRollbackOrExecute( + __in BOOL fRollback + ); + +LPWSTR LoggingStringOrUnknownIfNull( + __in LPCWSTR wz + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/manifest.cpp b/src/burn/engine/manifest.cpp new file mode 100644 index 00000000..b1740083 --- /dev/null +++ b/src/burn/engine/manifest.cpp @@ -0,0 +1,164 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +static HRESULT ParseFromXml( + __in IXMLDOMDocument* pixdDocument, + __in BURN_ENGINE_STATE* pEngineState + ); + +// function definitions + +extern "C" HRESULT ManifestLoadXmlFromFile( + __in LPCWSTR wzPath, + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + IXMLDOMDocument* pixdDocument = NULL; + + // load xml document + hr = XmlLoadDocumentFromFile(wzPath, &pixdDocument); + ExitOnFailure(hr, "Failed to load manifest as XML document."); + + hr = ParseFromXml(pixdDocument, pEngineState); + +LExit: + ReleaseObject(pixdDocument); + + return hr; +} + +extern "C" HRESULT ManifestLoadXmlFromBuffer( + __in_bcount(cbBuffer) BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + IXMLDOMDocument* pixdDocument = NULL; + + // load xml document + hr = XmlLoadDocumentFromBuffer(pbBuffer, cbBuffer, &pixdDocument); + ExitOnFailure(hr, "Failed to load manifest as XML document."); + + hr = ParseFromXml(pixdDocument, pEngineState); + +LExit: + ReleaseObject(pixdDocument); + + return hr; +} + +static HRESULT ParseFromXml( + __in IXMLDOMDocument* pixdDocument, + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + IXMLDOMNode* pixnLog = NULL; + IXMLDOMNode* pixnChain = NULL; + + // get bundle element + hr = pixdDocument->get_documentElement(&pixeBundle); + ExitOnFailure(hr, "Failed to get bundle element."); + + // parse the log element, if present. + hr = XmlSelectSingleNode(pixeBundle, L"Log", &pixnLog); + ExitOnFailure(hr, "Failed to get Log element."); + + if (S_OK == hr) + { + hr = XmlGetAttributeEx(pixnLog, L"PathVariable", &pEngineState->log.sczPathVariable); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get Log/@PathVariable."); + } + + hr = XmlGetAttributeEx(pixnLog, L"Prefix", &pEngineState->log.sczPrefix); + ExitOnFailure(hr, "Failed to get Log/@Prefix attribute."); + + hr = XmlGetAttributeEx(pixnLog, L"Extension", &pEngineState->log.sczExtension); + ExitOnFailure(hr, "Failed to get Log/@Extension attribute."); + } + + // get the chain element + hr = XmlSelectSingleNode(pixeBundle, L"Chain", &pixnChain); + ExitOnFailure(hr, "Failed to get chain element."); + + if (S_OK == hr) + { + // parse disable rollback + hr = XmlGetYesNoAttribute(pixnChain, L"DisableRollback", &pEngineState->fDisableRollback); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get Chain/@DisableRollback"); + } + + // parse disable system restore + hr = XmlGetYesNoAttribute(pixnChain, L"DisableSystemRestore", &pEngineState->fDisableSystemRestore); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get Chain/@DisableSystemRestore"); + } + + // parse parallel cache + hr = XmlGetYesNoAttribute(pixnChain, L"ParallelCache", &pEngineState->fParallelCacheAndExecute); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get Chain/@ParallelCache"); + } + } + + // parse built-in condition + hr = ConditionGlobalParseFromXml(&pEngineState->condition, pixeBundle); + ExitOnFailure(hr, "Failed to parse global condition."); + + // parse variables + hr = VariablesParseFromXml(&pEngineState->variables, pixeBundle); + ExitOnFailure(hr, "Failed to parse variables."); + + // parse user experience + hr = UserExperienceParseFromXml(&pEngineState->userExperience, pixeBundle); + ExitOnFailure(hr, "Failed to parse user experience."); + + // parse extensions + hr = BurnExtensionParseFromXml(&pEngineState->extensions, &pEngineState->userExperience.payloads, pixeBundle); + ExitOnFailure(hr, "Failed to parse extensions."); + + // parse searches + hr = SearchesParseFromXml(&pEngineState->searches, &pEngineState->extensions, pixeBundle); + ExitOnFailure(hr, "Failed to parse searches."); + + // parse registration + hr = RegistrationParseFromXml(&pEngineState->registration, pixeBundle); + ExitOnFailure(hr, "Failed to parse registration."); + + // parse update + hr = UpdateParseFromXml(&pEngineState->update, pixeBundle); + ExitOnFailure(hr, "Failed to parse update."); + + // parse containers + hr = ContainersParseFromXml(&pEngineState->containers, pixeBundle); + ExitOnFailure(hr, "Failed to parse containers."); + + // parse payloads + hr = PayloadsParseFromXml(&pEngineState->payloads, &pEngineState->containers, &pEngineState->layoutPayloads, pixeBundle); + ExitOnFailure(hr, "Failed to parse payloads."); + + // parse packages + hr = PackagesParseFromXml(&pEngineState->packages, &pEngineState->payloads, pixeBundle); + ExitOnFailure(hr, "Failed to parse packages."); + + // parse approved exes for elevation + hr = ApprovedExesParseFromXml(&pEngineState->approvedExes, pixeBundle); + ExitOnFailure(hr, "Failed to parse approved exes."); + +LExit: + ReleaseObject(pixnChain); + ReleaseObject(pixnLog); + ReleaseObject(pixeBundle); + return hr; +} diff --git a/src/burn/engine/manifest.h b/src/burn/engine/manifest.h new file mode 100644 index 00000000..8c527279 --- /dev/null +++ b/src/burn/engine/manifest.h @@ -0,0 +1,28 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +interface IBurnPayload; // forward declare. + +#if defined(__cplusplus) +extern "C" { +#endif + + +// function declarations + +HRESULT ManifestLoadXmlFromFile( + __in LPCWSTR wzPath, + __in BURN_ENGINE_STATE* pEngineState + ); + +HRESULT ManifestLoadXmlFromBuffer( + __in_bcount(cbBuffer) BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __in BURN_ENGINE_STATE* pEngineState + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/msiengine.cpp b/src/burn/engine/msiengine.cpp new file mode 100644 index 00000000..3e96e5f9 --- /dev/null +++ b/src/burn/engine/msiengine.cpp @@ -0,0 +1,2035 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// constants + + +// structs + + + +// internal function declarations + +static HRESULT ParseRelatedMsiFromXml( + __in IXMLDOMNode* pixnRelatedMsi, + __in BURN_RELATED_MSI* pRelatedMsi + ); +static HRESULT EvaluateActionStateConditions( + __in BURN_VARIABLES* pVariables, + __in_z_opt LPCWSTR sczAddLocalCondition, + __in_z_opt LPCWSTR sczAddSourceCondition, + __in_z_opt LPCWSTR sczAdvertiseCondition, + __out BOOTSTRAPPER_FEATURE_STATE* pState + ); +static HRESULT CalculateFeatureAction( + __in BOOTSTRAPPER_FEATURE_STATE currentState, + __in BOOTSTRAPPER_FEATURE_STATE requestedState, + __in BOOL fRepair, + __out BOOTSTRAPPER_FEATURE_ACTION* pFeatureAction, + __inout BOOL* pfDelta + ); +static HRESULT EscapePropertyArgumentString( + __in LPCWSTR wzProperty, + __inout_z LPWSTR* psczEscapedValue, + __in BOOL fZeroOnRealloc + ); +static HRESULT ConcatFeatureActionProperties( + __in BURN_PACKAGE* pPackage, + __in BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions, + __inout_z LPWSTR* psczArguments + ); +static HRESULT ConcatPatchProperty( + __in BURN_PACKAGE* pPackage, + __in BOOL fRollback, + __inout_z LPWSTR* psczArguments + ); +static void RegisterSourceDirectory( + __in BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzCacheDirectory + ); + + +// function definitions + +extern "C" HRESULT MsiEngineParsePackageFromXml( + __in IXMLDOMNode* pixnMsiPackage, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + LPWSTR scz = NULL; + + // @ProductCode + hr = XmlGetAttributeEx(pixnMsiPackage, L"ProductCode", &pPackage->Msi.sczProductCode); + ExitOnFailure(hr, "Failed to get @ProductCode."); + + // @Language + hr = XmlGetAttributeNumber(pixnMsiPackage, L"Language", &pPackage->Msi.dwLanguage); + ExitOnFailure(hr, "Failed to get @Language."); + + // @Version + hr = XmlGetAttributeEx(pixnMsiPackage, L"Version", &scz); + ExitOnFailure(hr, "Failed to get @Version."); + + hr = VerParseVersion(scz, 0, FALSE, &pPackage->Msi.pVersion); + ExitOnFailure(hr, "Failed to parse @Version: %ls", scz); + + if (pPackage->Msi.pVersion->fInvalid) + { + LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz); + } + + // @UpgradeCode + hr = XmlGetAttributeEx(pixnMsiPackage, L"UpgradeCode", &pPackage->Msi.sczUpgradeCode); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @UpgradeCode."); + } + + // select feature nodes + hr = XmlSelectNodes(pixnMsiPackage, L"MsiFeature", &pixnNodes); + ExitOnFailure(hr, "Failed to select feature nodes."); + + // get feature node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get feature node count."); + + if (cNodes) + { + // allocate memory for features + pPackage->Msi.rgFeatures = (BURN_MSIFEATURE*)MemAlloc(sizeof(BURN_MSIFEATURE) * cNodes, TRUE); + ExitOnNull(pPackage->Msi.rgFeatures, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSI feature structs."); + + pPackage->Msi.cFeatures = cNodes; + + // parse feature elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &pFeature->sczId); + ExitOnFailure(hr, "Failed to get @Id."); + + // @AddLocalCondition + hr = XmlGetAttributeEx(pixnNode, L"AddLocalCondition", &pFeature->sczAddLocalCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @AddLocalCondition."); + } + + // @AddSourceCondition + hr = XmlGetAttributeEx(pixnNode, L"AddSourceCondition", &pFeature->sczAddSourceCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @AddSourceCondition."); + } + + // @AdvertiseCondition + hr = XmlGetAttributeEx(pixnNode, L"AdvertiseCondition", &pFeature->sczAdvertiseCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @AdvertiseCondition."); + } + + // @RollbackAddLocalCondition + hr = XmlGetAttributeEx(pixnNode, L"RollbackAddLocalCondition", &pFeature->sczRollbackAddLocalCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @RollbackAddLocalCondition."); + } + + // @RollbackAddSourceCondition + hr = XmlGetAttributeEx(pixnNode, L"RollbackAddSourceCondition", &pFeature->sczRollbackAddSourceCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @RollbackAddSourceCondition."); + } + + // @RollbackAdvertiseCondition + hr = XmlGetAttributeEx(pixnNode, L"RollbackAdvertiseCondition", &pFeature->sczRollbackAdvertiseCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @RollbackAdvertiseCondition."); + } + + // prepare next iteration + ReleaseNullObject(pixnNode); + } + } + + ReleaseNullObject(pixnNodes); // done with the MsiFeature elements. + + hr = MsiEngineParsePropertiesFromXml(pixnMsiPackage, &pPackage->Msi.rgProperties, &pPackage->Msi.cProperties); + ExitOnFailure(hr, "Failed to parse properties from XML."); + + // select related MSI nodes + hr = XmlSelectNodes(pixnMsiPackage, L"RelatedPackage", &pixnNodes); + ExitOnFailure(hr, "Failed to select related MSI nodes."); + + // get related MSI node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get related MSI node count."); + + if (cNodes) + { + // allocate memory for related MSIs + pPackage->Msi.rgRelatedMsis = (BURN_RELATED_MSI*)MemAlloc(sizeof(BURN_RELATED_MSI) * cNodes, TRUE); + ExitOnNull(pPackage->Msi.rgRelatedMsis, hr, E_OUTOFMEMORY, "Failed to allocate memory for related MSI structs."); + + pPackage->Msi.cRelatedMsis = cNodes; + + // parse related MSI elements + for (DWORD i = 0; i < cNodes; ++i) + { + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // parse related MSI element + hr = ParseRelatedMsiFromXml(pixnNode, &pPackage->Msi.rgRelatedMsis[i]); + ExitOnFailure(hr, "Failed to parse related MSI element."); + + // prepare next iteration + ReleaseNullObject(pixnNode); + } + } + + ReleaseNullObject(pixnNodes); // done with the RelatedPackage elements. + + // Select slipstream MSP nodes. + hr = XmlSelectNodes(pixnMsiPackage, L"SlipstreamMsp", &pixnNodes); + ExitOnFailure(hr, "Failed to select related MSI nodes."); + + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get related MSI node count."); + + if (cNodes) + { + pPackage->Msi.rgSlipstreamMsps = reinterpret_cast(MemAlloc(sizeof(BURN_SLIPSTREAM_MSP) * cNodes, TRUE)); + ExitOnNull(pPackage->Msi.rgSlipstreamMsps, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP packages."); + + pPackage->Msi.rgsczSlipstreamMspPackageIds = reinterpret_cast(MemAlloc(sizeof(LPWSTR*) * cNodes, TRUE)); + ExitOnNull(pPackage->Msi.rgsczSlipstreamMspPackageIds, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP ids."); + + pPackage->Msi.cSlipstreamMspPackages = cNodes; + + // Parse slipstream MSP Ids. + for (DWORD i = 0; i < cNodes; ++i) + { + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next slipstream MSP node."); + + hr = XmlGetAttributeEx(pixnNode, L"Id", pPackage->Msi.rgsczSlipstreamMspPackageIds + i); + ExitOnFailure(hr, "Failed to parse slipstream MSP ids."); + + ReleaseNullObject(pixnNode); + } + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseStr(scz); + + return hr; +} + +extern "C" HRESULT MsiEngineParsePropertiesFromXml( + __in IXMLDOMNode* pixnPackage, + __out BURN_MSIPROPERTY** prgProperties, + __out DWORD* pcProperties + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + + BURN_MSIPROPERTY* pProperties = NULL; + + // select property nodes + hr = XmlSelectNodes(pixnPackage, L"MsiProperty", &pixnNodes); + ExitOnFailure(hr, "Failed to select property nodes."); + + // get property node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get property node count."); + + if (cNodes) + { + // allocate memory for properties + pProperties = (BURN_MSIPROPERTY*)MemAlloc(sizeof(BURN_MSIPROPERTY) * cNodes, TRUE); + ExitOnNull(pProperties, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSI property structs."); + + // parse property elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_MSIPROPERTY* pProperty = &pProperties[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &pProperty->sczId); + ExitOnFailure(hr, "Failed to get @Id."); + + // @Value + hr = XmlGetAttributeEx(pixnNode, L"Value", &pProperty->sczValue); + ExitOnFailure(hr, "Failed to get @Value."); + + // @RollbackValue + hr = XmlGetAttributeEx(pixnNode, L"RollbackValue", &pProperty->sczRollbackValue); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @RollbackValue."); + } + + // @Condition + hr = XmlGetAttributeEx(pixnNode, L"Condition", &pProperty->sczCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Condition."); + } + + // prepare next iteration + ReleaseNullObject(pixnNode); + } + } + + *pcProperties = cNodes; + *prgProperties = pProperties; + pProperties = NULL; + + hr = S_OK; + +LExit: + ReleaseNullObject(pixnNodes); + ReleaseMem(pProperties); + + return hr; +} + +extern "C" void MsiEnginePackageUninitialize( + __in BURN_PACKAGE* pPackage + ) +{ + ReleaseStr(pPackage->Msi.sczProductCode); + ReleaseStr(pPackage->Msi.sczUpgradeCode); + + // free features + if (pPackage->Msi.rgFeatures) + { + for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) + { + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; + + ReleaseStr(pFeature->sczId); + ReleaseStr(pFeature->sczAddLocalCondition); + ReleaseStr(pFeature->sczAddSourceCondition); + ReleaseStr(pFeature->sczAdvertiseCondition); + ReleaseStr(pFeature->sczRollbackAddLocalCondition); + ReleaseStr(pFeature->sczRollbackAddSourceCondition); + ReleaseStr(pFeature->sczRollbackAdvertiseCondition); + } + MemFree(pPackage->Msi.rgFeatures); + } + + // free properties + if (pPackage->Msi.rgProperties) + { + for (DWORD i = 0; i < pPackage->Msi.cProperties; ++i) + { + BURN_MSIPROPERTY* pProperty = &pPackage->Msi.rgProperties[i]; + + ReleaseStr(pProperty->sczId); + ReleaseStr(pProperty->sczValue); + ReleaseStr(pProperty->sczRollbackValue); + ReleaseStr(pProperty->sczCondition); + } + MemFree(pPackage->Msi.rgProperties); + } + + // free related MSIs + if (pPackage->Msi.rgRelatedMsis) + { + for (DWORD i = 0; i < pPackage->Msi.cRelatedMsis; ++i) + { + BURN_RELATED_MSI* pRelatedMsi = &pPackage->Msi.rgRelatedMsis[i]; + + ReleaseStr(pRelatedMsi->sczUpgradeCode); + ReleaseMem(pRelatedMsi->rgdwLanguages); + } + MemFree(pPackage->Msi.rgRelatedMsis); + } + + // free slipstream MSPs + if (pPackage->Msi.rgsczSlipstreamMspPackageIds) + { + for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) + { + ReleaseStr(pPackage->Msi.rgsczSlipstreamMspPackageIds[i]); + } + + MemFree(pPackage->Msi.rgsczSlipstreamMspPackageIds); + } + + if (pPackage->Msi.rgSlipstreamMsps) + { + MemFree(pPackage->Msi.rgSlipstreamMsps); + } + + if (pPackage->Msi.rgChainedPatches) + { + MemFree(pPackage->Msi.rgChainedPatches); + } + + // clear struct + memset(&pPackage->Msi, 0, sizeof(pPackage->Msi)); +} + +extern "C" HRESULT MsiEngineDetectInitialize( + __in BURN_PACKAGES* pPackages + ) +{ + AssertSz(pPackages->cPatchInfo, "MsiEngineDetectInitialize() should only be called if there are MSP packages."); + + HRESULT hr = S_OK; + + // Add target products for slipstream MSIs that weren't detected. + for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage) + { + BURN_PACKAGE* pMsiPackage = pPackages->rgPackages + iPackage; + if (BURN_PACKAGE_TYPE_MSI == pMsiPackage->type) + { + for (DWORD j = 0; j < pMsiPackage->Msi.cSlipstreamMspPackages; ++j) + { + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pMsiPackage->Msi.rgSlipstreamMsps + j; + Assert(pSlipstreamMsp->pMspPackage && BURN_PACKAGE_TYPE_MSP == pSlipstreamMsp->pMspPackage->type); + + if (pSlipstreamMsp->pMspPackage && BURN_PACKAGE_INVALID_PATCH_INDEX == pSlipstreamMsp->dwMsiChainedPatchIndex) + { + hr = MspEngineAddMissingSlipstreamTarget(pMsiPackage, pSlipstreamMsp); + ExitOnFailure(hr, "Failed to add slipstreamed target product code to package: %ls", pSlipstreamMsp->pMspPackage->sczId); + } + } + } + } + +LExit: + return hr; +} + +extern "C" HRESULT MsiEngineDetectPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + Trace(REPORT_STANDARD, "Detecting MSI package 0x%p", pPackage); + + HRESULT hr = S_OK; + int nCompareResult = 0; + LPWSTR sczInstalledVersion = NULL; + LPWSTR sczInstalledLanguage = NULL; + INSTALLSTATE installState = INSTALLSTATE_UNKNOWN; + BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE; + BOOTSTRAPPER_RELATED_OPERATION relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE; + WCHAR wzProductCode[MAX_GUID_CHARS + 1] = { }; + VERUTIL_VERSION* pVersion = NULL; + UINT uLcid = 0; + BOOL fPerMachine = FALSE; + + // detect self by product code + // TODO: what to do about MSIINSTALLCONTEXT_USERMANAGED? + hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); + if (SUCCEEDED(hr)) + { + hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pPackage->Msi.pInstalledVersion); + ExitOnFailure(hr, "Failed to parse installed version: '%ls' for ProductCode: %ls", sczInstalledVersion, pPackage->Msi.sczProductCode); + + if (pPackage->Msi.pInstalledVersion->fInvalid) + { + LogId(REPORT_WARNING, MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION, pPackage->Msi.sczProductCode, sczInstalledVersion); + } + + // compare versions + hr = VerCompareParsedVersions(pPackage->Msi.pVersion, pPackage->Msi.pInstalledVersion, &nCompareResult); + ExitOnFailure(hr, "Failed to compare version '%ls' to installed version: '%ls'", pPackage->Msi.pVersion->sczVersion, pPackage->Msi.pInstalledVersion->sczVersion); + + if (nCompareResult < 0) + { + operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; + } + else + { + if (nCompareResult > 0) + { + operation = BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE; + } + + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; + } + + // Report related MSI package to BA. + if (BOOTSTRAPPER_RELATED_OPERATION_NONE != operation) + { + LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, pPackage->Msi.sczProductCode, LoggingPerMachineToString(pPackage->fPerMachine), pPackage->Msi.pInstalledVersion->sczVersion, pPackage->Msi.dwLanguage, LoggingRelatedOperationToString(operation)); + + hr = UserExperienceOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pPackage->Msi.sczUpgradeCode, pPackage->Msi.sczProductCode, pPackage->fPerMachine, pPackage->Msi.pInstalledVersion, operation); + ExitOnRootFailure(hr, "BA aborted detect related MSI package."); + } + } + else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr || HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr) // package not present. + { + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to get product information for ProductCode: %ls", pPackage->Msi.sczProductCode); + } + + // detect related packages by upgrade code + for (DWORD i = 0; i < pPackage->Msi.cRelatedMsis; ++i) + { + BURN_RELATED_MSI* pRelatedMsi = &pPackage->Msi.rgRelatedMsis[i]; + + for (DWORD iProduct = 0; ; ++iProduct) + { + // get product + hr = WiuEnumRelatedProducts(pRelatedMsi->sczUpgradeCode, iProduct, wzProductCode); + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + break; + } + ExitOnFailure(hr, "Failed to enum related products."); + + // If we found ourselves, skip because saying that a package is related to itself is nonsensical. + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pPackage->Msi.sczProductCode, -1, wzProductCode, -1)) + { + continue; + } + + // get product version + hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr) + { + ExitOnFailure(hr, "Failed to get version for product in machine context: %ls", wzProductCode); + fPerMachine = TRUE; + } + else + { + hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr) + { + ExitOnFailure(hr, "Failed to get version for product in user unmanaged context: %ls", wzProductCode); + fPerMachine = FALSE; + } + else + { + hr = S_OK; + continue; + } + } + + hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pVersion); + ExitOnFailure(hr, "Failed to parse related installed version: '%ls' for ProductCode: %ls", sczInstalledVersion, wzProductCode); + + if (pVersion->fInvalid) + { + LogId(REPORT_WARNING, MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION, wzProductCode, sczInstalledVersion); + } + + // compare versions + if (pRelatedMsi->fMinProvided) + { + hr = VerCompareParsedVersions(pVersion, pRelatedMsi->pMinVersion, &nCompareResult); + ExitOnFailure(hr, "Failed to compare related installed version '%ls' to related min version: '%ls'", pVersion->sczVersion, pRelatedMsi->pMinVersion->sczVersion); + + if (pRelatedMsi->fMinInclusive ? (nCompareResult < 0) : (nCompareResult <= 0)) + { + continue; + } + } + + if (pRelatedMsi->fMaxProvided) + { + hr = VerCompareParsedVersions(pVersion, pRelatedMsi->pMaxVersion, &nCompareResult); + ExitOnFailure(hr, "Failed to compare related installed version '%ls' to related max version: '%ls'", pVersion->sczVersion, pRelatedMsi->pMaxVersion->sczVersion); + + if (pRelatedMsi->fMaxInclusive ? (nCompareResult > 0) : (nCompareResult >= 0)) + { + continue; + } + } + + // Filter by language if necessary. + uLcid = 0; // always reset the found language. + if (pRelatedMsi->cLanguages) + { + // If there is a language to get, convert it into an LCID. + hr = WiuGetProductInfoEx(wzProductCode, NULL, fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_LANGUAGE, &sczInstalledLanguage); + if (SUCCEEDED(hr)) + { + hr = StrStringToUInt32(sczInstalledLanguage, 0, &uLcid); + } + + // Ignore related product where we can't read the language. + if (FAILED(hr)) + { + LogErrorId(hr, MSG_FAILED_READ_RELATED_PACKAGE_LANGUAGE, wzProductCode, sczInstalledLanguage, NULL); + + hr = S_OK; + continue; + } + + BOOL fMatchedLcid = FALSE; + for (DWORD iLanguage = 0; iLanguage < pRelatedMsi->cLanguages; ++iLanguage) + { + if (uLcid == pRelatedMsi->rgdwLanguages[iLanguage]) + { + fMatchedLcid = TRUE; + break; + } + } + + // Skip the product if the language did not meet the inclusive/exclusive criteria. + if ((pRelatedMsi->fLangInclusive && !fMatchedLcid) || (!pRelatedMsi->fLangInclusive && fMatchedLcid)) + { + continue; + } + } + + // If this is a detect-only related package and we're not installed yet, then we'll assume a downgrade + // would take place since that is the overwhelmingly common use of detect-only related packages. If + // not detect-only then it's easy; we're clearly doing a major upgrade. + if (pRelatedMsi->fOnlyDetect) + { + // If we've already detected a major upgrade that trumps any guesses that the detect is a downgrade + // or even something else. + if (BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE == operation) + { + relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE; + } + // It can't be a downgrade if the upgrade codes aren't the same. + else if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == pPackage->currentState && + pPackage->Msi.sczUpgradeCode && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pPackage->Msi.sczUpgradeCode, -1, pRelatedMsi->sczUpgradeCode, -1)) + { + relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; + operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE; + } + else // we're already on the machine so the detect-only *must* be for detection purposes only. + { + relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE; + } + } + else + { + relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE; + operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE; + } + + LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, wzProductCode, LoggingPerMachineToString(fPerMachine), pVersion->sczVersion, uLcid, LoggingRelatedOperationToString(relatedMsiOperation)); + + // Pass to BA. + hr = UserExperienceOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pRelatedMsi->sczUpgradeCode, wzProductCode, fPerMachine, pVersion, relatedMsiOperation); + ExitOnRootFailure(hr, "BA aborted detect related MSI package."); + } + } + + // detect features + if (pPackage->Msi.cFeatures) + { + for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) + { + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; + + // Try to detect features state if the product is present on the machine. + if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT <= pPackage->currentState) + { + hr = WiuQueryFeatureState(pPackage->Msi.sczProductCode, pFeature->sczId, &installState); + ExitOnFailure(hr, "Failed to query feature state."); + + if (INSTALLSTATE_UNKNOWN == installState) // in case of an upgrade a feature could be removed. + { + installState = INSTALLSTATE_ABSENT; + } + } + else // MSI not installed then the features can't be either. + { + installState = INSTALLSTATE_ABSENT; + } + + // set current state + switch (installState) + { + case INSTALLSTATE_ABSENT: + pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_ABSENT; + break; + case INSTALLSTATE_ADVERTISED: + pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_ADVERTISED; + break; + case INSTALLSTATE_LOCAL: + pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_LOCAL; + break; + case INSTALLSTATE_SOURCE: + pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_SOURCE; + break; + default: + hr = E_UNEXPECTED; + ExitOnRootFailure(hr, "Invalid state value."); + } + + // Pass to BA. + hr = UserExperienceOnDetectMsiFeature(pUserExperience, pPackage->sczId, pFeature->sczId, pFeature->currentState); + ExitOnRootFailure(hr, "BA aborted detect MSI feature."); + } + } + + if (pPackage->fCanAffectRegistration) + { + pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + +LExit: + ReleaseStr(sczInstalledLanguage); + ReleaseStr(sczInstalledVersion); + ReleaseVerutilVersion(pVersion); + + return hr; +} + +extern "C" HRESULT MsiEnginePlanInitializePackage( + __in BURN_PACKAGE* pPackage, + __in BURN_VARIABLES* pVariables, + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + + if (pPackage->Msi.cFeatures) + { + // get feature request states + for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) + { + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; + + // Evaluate feature conditions. + hr = EvaluateActionStateConditions(pVariables, pFeature->sczAddLocalCondition, pFeature->sczAddSourceCondition, pFeature->sczAdvertiseCondition, &pFeature->defaultRequested); + ExitOnFailure(hr, "Failed to evaluate requested state conditions."); + + hr = EvaluateActionStateConditions(pVariables, pFeature->sczRollbackAddLocalCondition, pFeature->sczRollbackAddSourceCondition, pFeature->sczRollbackAdvertiseCondition, &pFeature->expectedState); + ExitOnFailure(hr, "Failed to evaluate expected state conditions."); + + // Remember the default feature requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it. + pFeature->requested = pFeature->defaultRequested; + + // Send plan MSI feature message to BA. + hr = UserExperienceOnPlanMsiFeature(pUserExperience, pPackage->sczId, pFeature->sczId, &pFeature->requested); + ExitOnRootFailure(hr, "BA aborted plan MSI feature."); + } + } + +LExit: + return hr; +} + +// +// PlanCalculate - calculates the execute and rollback state for the requested package state. +// +extern "C" HRESULT MsiEnginePlanCalculatePackage( + __in BURN_PACKAGE* pPackage, + __in BOOL fInsideMsiTransaction + ) +{ + Trace(REPORT_STANDARD, "Planning MSI package 0x%p", pPackage); + + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion = pPackage->Msi.pVersion; + VERUTIL_VERSION* pInstalledVersion = pPackage->Msi.pInstalledVersion; + int nCompareResult = 0; + BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; + BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + BOOL fFeatureActionDelta = FALSE; + BOOL fRollbackFeatureActionDelta = FALSE; + + if (pPackage->Msi.cFeatures) + { + // If the package is present and we're repairing it. + BOOL fRepairingPackage = (BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested); + + // plan features + for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) + { + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; + + // Calculate feature actions. + hr = CalculateFeatureAction(pFeature->currentState, pFeature->requested, fRepairingPackage, &pFeature->execute, &fFeatureActionDelta); + ExitOnFailure(hr, "Failed to calculate execute feature state."); + + hr = CalculateFeatureAction(pFeature->requested, BOOTSTRAPPER_FEATURE_ACTION_NONE == pFeature->execute ? pFeature->expectedState : pFeature->currentState, FALSE, &pFeature->rollback, &fRollbackFeatureActionDelta); + ExitOnFailure(hr, "Failed to calculate rollback feature state."); + } + } + + // execute action + switch (pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: + if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_MEND == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) + { + hr = VerCompareParsedVersions(pVersion, pInstalledVersion, &nCompareResult); + ExitOnFailure(hr, "Failed to compare '%ls' to '%ls' for planning.", pVersion->sczVersion, pInstalledVersion->sczVersion); + + // Take a look at the version and determine if this is a potential + // minor upgrade (same ProductCode newer ProductVersion), otherwise, + // there is a newer version so no work necessary. + if (nCompareResult > 0) + { + execute = BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE; + } + else if (BOOTSTRAPPER_REQUEST_STATE_MEND == pPackage->requested) + { + execute = BOOTSTRAPPER_ACTION_STATE_MEND; + } + else if (BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) + { + execute = BOOTSTRAPPER_ACTION_STATE_REPAIR; + } + else + { + execute = fFeatureActionDelta ? BOOTSTRAPPER_ACTION_STATE_MODIFY : BOOTSTRAPPER_ACTION_STATE_NONE; + } + } + else if ((BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_CACHE == pPackage->requested) && + pPackage->fUninstallable) // removing a package that can be removed. + { + execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; + } + else if (BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested) + { + execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; + } + else + { + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + } + break; + + case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough; + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_MEND: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; + break; + + default: + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid package current state result encountered during plan: %d", pPackage->currentState); + } + + // Calculate the rollback action if there is an execute action. + if (BOOTSTRAPPER_ACTION_STATE_NONE != execute && !fInsideMsiTransaction) + { + switch (pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: + rollback = fRollbackFeatureActionDelta ? BOOTSTRAPPER_ACTION_STATE_MODIFY : BOOTSTRAPPER_ACTION_STATE_NONE; + break; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_ABSENT: + rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL; + break; + default: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough; + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough; + // If the package is uninstallable and we requested to put the package on the machine then + // remove the package during rollback. + if (pPackage->fUninstallable && + (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || + BOOTSTRAPPER_REQUEST_STATE_MEND == pPackage->requested || + BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested)) + { + rollback = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; + } + else + { + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + } + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid package detection result encountered."); + } + } + + // return values + pPackage->execute = execute; + pPackage->rollback = rollback; + +LExit: + return hr; +} + +// +// PlanAdd - adds the calculated execute and rollback actions for the package. +// +extern "C" HRESULT MsiEnginePlanAddPackage( + __in BOOTSTRAPPER_DISPLAY display, + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in_opt HANDLE hCacheEvent + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* pAction = NULL; + BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions = NULL; + BOOTSTRAPPER_FEATURE_ACTION* rgRollbackFeatureActions = NULL; + + if (pPackage->Msi.cFeatures) + { + // Allocate and populate array for feature actions. + rgFeatureActions = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(sizeof(BOOTSTRAPPER_FEATURE_ACTION) * pPackage->Msi.cFeatures, TRUE); + ExitOnNull(rgFeatureActions, hr, E_OUTOFMEMORY, "Failed to allocate memory for feature actions."); + + rgRollbackFeatureActions = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(sizeof(BOOTSTRAPPER_FEATURE_ACTION) * pPackage->Msi.cFeatures, TRUE); + ExitOnNull(rgRollbackFeatureActions, hr, E_OUTOFMEMORY, "Failed to allocate memory for rollback feature actions."); + + for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) + { + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; + + // calculate feature actions + rgFeatureActions[i] = pFeature->execute; + rgRollbackFeatureActions[i] = pFeature->rollback; + } + } + + // add wait for cache + if (hCacheEvent) + { + hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent); + ExitOnFailure(hr, "Failed to plan package cache syncpoint"); + } + + hr = DependencyPlanPackage(NULL, pPackage, pPlan); + ExitOnFailure(hr, "Failed to plan package dependency actions."); + + // add rollback action + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback) + { + hr = PlanAppendRollbackAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append rollback action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE; + pAction->msiPackage.pPackage = pPackage; + pAction->msiPackage.action = pPackage->rollback; + pAction->msiPackage.rgFeatures = rgRollbackFeatureActions; + rgRollbackFeatureActions = NULL; + + hr = MsiEngineCalculateInstallUiLevel(display, pUserExperience, pPackage->sczId, FALSE, pAction->msiPackage.action, + &pAction->msiPackage.actionMsiProperty, &pAction->msiPackage.uiLevel, &pAction->msiPackage.fDisableExternalUiHandler); + ExitOnFailure(hr, "Failed to get msi ui options."); + + LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors. + pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes; + + // Plan a checkpoint between rollback and execute so that we always attempt + // rollback in the case that the MSI was not able to rollback itself (e.g. + // user pushes cancel after InstallFinalize). + hr = PlanExecuteCheckpoint(pPlan); + ExitOnFailure(hr, "Failed to append execute checkpoint."); + } + + // add execute action + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute) + { + hr = PlanAppendExecuteAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append execute action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE; + pAction->msiPackage.pPackage = pPackage; + pAction->msiPackage.action = pPackage->execute; + pAction->msiPackage.rgFeatures = rgFeatureActions; + rgFeatureActions = NULL; + + hr = MsiEngineCalculateInstallUiLevel(display, pUserExperience, pPackage->sczId, TRUE, pAction->msiPackage.action, + &pAction->msiPackage.actionMsiProperty, &pAction->msiPackage.uiLevel, &pAction->msiPackage.fDisableExternalUiHandler); + ExitOnFailure(hr, "Failed to get msi ui options."); + + LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors. + pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes; + } + +LExit: + ReleaseMem(rgFeatureActions); + ReleaseMem(rgRollbackFeatureActions); + + return hr; +} + +extern "C" HRESULT MsiEngineBeginTransaction( + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary + ) +{ + HRESULT hr = S_OK; + MSIHANDLE hTransactionHandle = NULL; + HANDLE hChangeOfOwnerEvent = NULL; + + LogId(REPORT_STANDARD, MSG_MSI_TRANSACTION_BEGIN, pRollbackBoundary->sczId); + + hr = WiuBeginTransaction(pRollbackBoundary->sczId, 0, &hTransactionHandle, &hChangeOfOwnerEvent, WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE, pRollbackBoundary->sczLogPath); + + if (HRESULT_FROM_WIN32(ERROR_ROLLBACK_DISABLED) == hr) + { + LogId(REPORT_ERROR, MSG_MSI_TRANSACTIONS_DISABLED); + } + + ExitOnFailure(hr, "Failed to begin an MSI transaction"); + +LExit: + ReleaseMsi(hTransactionHandle); + ReleaseHandle(hChangeOfOwnerEvent); + + return hr; +} + +extern "C" HRESULT MsiEngineCommitTransaction( + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary + ) +{ + HRESULT hr = S_OK; + + LogId(REPORT_STANDARD, MSG_MSI_TRANSACTION_COMMIT, pRollbackBoundary->sczId); + + hr = WiuEndTransaction(MSITRANSACTIONSTATE_COMMIT, WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE, pRollbackBoundary->sczLogPath); + ExitOnFailure(hr, "Failed to commit the MSI transaction"); + +LExit: + + return hr; +} + +extern "C" HRESULT MsiEngineRollbackTransaction( + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary + ) +{ + HRESULT hr = S_OK; + + LogId(REPORT_WARNING, MSG_MSI_TRANSACTION_ROLLBACK, pRollbackBoundary->sczId); + + hr = WiuEndTransaction(MSITRANSACTIONSTATE_ROLLBACK, WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE, pRollbackBoundary->sczLogPath); + ExitOnFailure(hr, "Failed to rollback the MSI transaction"); + +LExit: + + return hr; +} + +extern "C" HRESULT MsiEngineExecutePackage( + __in_opt HWND hwndParent, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + WIU_MSI_EXECUTE_CONTEXT context = { }; + WIU_RESTART restart = WIU_RESTART_NONE; + + LPWSTR sczInstalledVersion = NULL; + LPWSTR sczCachedDirectory = NULL; + LPWSTR sczMsiPath = NULL; + LPWSTR sczProperties = NULL; + LPWSTR sczObfuscatedProperties = NULL; + BURN_PACKAGE* pPackage = pExecuteAction->msiPackage.pPackage; + BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload; + + // During rollback, if the package is already in the rollback state we expect don't + // touch it again. + if (fRollback) + { + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->msiPackage.action) + { + hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); + if (FAILED(hr)) // package not present. + { + LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_ABSENT)); + + hr = S_OK; + ExitFunction(); + } + } + else if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->msiPackage.action) + { + hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); + if (SUCCEEDED(hr)) // package present. + { + LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_PRESENT)); + + hr = S_OK; + ExitFunction(); + } + + hr = S_OK; + } + } + + // Default to "verbose" logging and set extra debug mode only if explicitly required. + DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE; + + if (pExecuteAction->msiPackage.dwLoggingAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG) + { + dwLogMode |= INSTALLLOGMODE_EXTRADEBUG; + } + + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL != pExecuteAction->msiPackage.action) + { + // get cached MSI path + hr = CacheGetCompletedPath(pPackage->fPerMachine, pPackage->sczCacheId, &sczCachedDirectory); + ExitOnFailure(hr, "Failed to get cached path for package: %ls", pPackage->sczId); + + // Best effort to set the execute package cache folder variable. + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE); + + hr = PathConcat(sczCachedDirectory, pPackagePayload->sczFilePath, &sczMsiPath); + ExitOnFailure(hr, "Failed to build MSI path."); + } + + // Best effort to set the execute package action variable. + VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->msiPackage.action, TRUE); + + // Wire up the external UI handler and logging. + if (pExecuteAction->msiPackage.fDisableExternalUiHandler) + { + hr = WiuInitializeInternalUI(pExecuteAction->msiPackage.uiLevel, hwndParent, &context); + ExitOnFailure(hr, "Failed to initialize internal UI for MSI package."); + } + else + { + hr = WiuInitializeExternalUI(pfnMessageHandler, pExecuteAction->msiPackage.uiLevel, hwndParent, pvContext, fRollback, &context); + ExitOnFailure(hr, "Failed to initialize external UI handler."); + } + + if (pExecuteAction->msiPackage.sczLogPath && *pExecuteAction->msiPackage.sczLogPath) + { + hr = WiuEnableLog(dwLogMode, pExecuteAction->msiPackage.sczLogPath, 0); + ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pPackage->sczId, pExecuteAction->msiPackage.sczLogPath); + } + + // set up properties + hr = MsiEngineConcatProperties(pPackage->Msi.rgProperties, pPackage->Msi.cProperties, pVariables, fRollback, &sczProperties, FALSE); + ExitOnFailure(hr, "Failed to add properties to argument string."); + + hr = MsiEngineConcatProperties(pPackage->Msi.rgProperties, pPackage->Msi.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE); + ExitOnFailure(hr, "Failed to add obfuscated properties to argument string."); + + // add feature action properties + hr = ConcatFeatureActionProperties(pPackage, pExecuteAction->msiPackage.rgFeatures, &sczProperties); + ExitOnFailure(hr, "Failed to add feature action properties to argument string."); + + hr = ConcatFeatureActionProperties(pPackage, pExecuteAction->msiPackage.rgFeatures, &sczObfuscatedProperties); + ExitOnFailure(hr, "Failed to add feature action properties to obfuscated argument string."); + + // add slipstream patch properties + hr = ConcatPatchProperty(pPackage, fRollback, &sczProperties); + ExitOnFailure(hr, "Failed to add patch properties to argument string."); + + hr = ConcatPatchProperty(pPackage, fRollback, &sczObfuscatedProperties); + ExitOnFailure(hr, "Failed to add patch properties to obfuscated argument string."); + + hr = MsiEngineConcatActionProperty(pExecuteAction->msiPackage.actionMsiProperty, &sczProperties); + ExitOnFailure(hr, "Failed to add action property to argument string."); + + hr = MsiEngineConcatActionProperty(pExecuteAction->msiPackage.actionMsiProperty, &sczObfuscatedProperties); + ExitOnFailure(hr, "Failed to add action property to obfuscated argument string."); + + LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), sczMsiPath, sczObfuscatedProperties ? sczObfuscatedProperties : L""); + + // + // Do the actual action. + // + switch (pExecuteAction->msiPackage.action) + { + case BOOTSTRAPPER_ACTION_STATE_INSTALL: + hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0); + ExitOnFailure(hr, "Failed to add reboot suppression property on install."); + + hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart); + ExitOnFailure(hr, "Failed to install MSI package."); + + RegisterSourceDirectory(pPackage, sczMsiPath); + break; + + case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: + // If feature selection is not enabled, then reinstall the existing features to ensure they get + // updated. + if (0 == pPackage->Msi.cFeatures) + { + hr = StrAllocConcatSecure(&sczProperties, L" REINSTALL=ALL", 0); + ExitOnFailure(hr, "Failed to add reinstall all property on minor upgrade."); + } + + hr = StrAllocConcatSecure(&sczProperties, L" REINSTALLMODE=\"vomus\" REBOOT=ReallySuppress", 0); + ExitOnFailure(hr, "Failed to add reinstall mode and reboot suppression properties on minor upgrade."); + + hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart); + ExitOnFailure(hr, "Failed to perform minor upgrade of MSI package."); + + RegisterSourceDirectory(pPackage, sczMsiPath); + break; + + case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough; + case BOOTSTRAPPER_ACTION_STATE_MEND: __fallthrough; + case BOOTSTRAPPER_ACTION_STATE_REPAIR: + { + LPCWSTR wzReinstallAll = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action || + pPackage->Msi.cFeatures) ? L"" : L" REINSTALL=ALL"; + LPCWSTR wzReinstallMode = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action || BOOTSTRAPPER_ACTION_STATE_MEND == pExecuteAction->msiPackage.action) ? L"o" : L"e"; + + hr = StrAllocFormattedSecure(&sczProperties, L"%ls%ls REINSTALLMODE=\"cmus%ls\" REBOOT=ReallySuppress", sczProperties ? sczProperties : L"", wzReinstallAll, wzReinstallMode); + ExitOnFailure(hr, "Failed to add reinstall mode and reboot suppression properties on repair."); + } + + // Ignore all dependencies, since the Burn engine already performed the check. + hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES); + ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties."); + + hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart); + ExitOnFailure(hr, "Failed to run maintenance mode for MSI package."); + break; + + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: + hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0); + ExitOnFailure(hr, "Failed to add reboot suppression property on uninstall."); + + // Ignore all dependencies, since the Burn engine already performed the check. + hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES); + ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties."); + + hr = WiuConfigureProductEx(pPackage->Msi.sczProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, sczProperties, &restart); + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr) + { + LogId(REPORT_STANDARD, MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE, pPackage->sczId); + hr = S_OK; + } + ExitOnFailure(hr, "Failed to uninstall MSI package."); + break; + } + +LExit: + WiuUninitializeExternalUI(&context); + + StrSecureZeroFreeString(sczProperties); + ReleaseStr(sczObfuscatedProperties); + ReleaseStr(sczMsiPath); + ReleaseStr(sczCachedDirectory); + ReleaseStr(sczInstalledVersion); + + switch (restart) + { + case WIU_RESTART_NONE: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; + break; + + case WIU_RESTART_REQUIRED: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; + break; + + case WIU_RESTART_INITIATED: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; + break; + } + + // Best effort to clear the execute package cache folder and action variables. + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE); + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE, FALSE); + + return hr; +} + +extern "C" HRESULT MsiEngineConcatActionProperty( + __in BURN_MSI_PROPERTY actionMsiProperty, + __deref_out_z LPWSTR* psczProperties + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzPropertyName = NULL; + + switch (actionMsiProperty) + { + case BURN_MSI_PROPERTY_INSTALL: + wzPropertyName = BURNMSIINSTALL_PROPERTY_NAME; + break; + case BURN_MSI_PROPERTY_MODIFY: + wzPropertyName = BURNMSIMODIFY_PROPERTY_NAME; + break; + case BURN_MSI_PROPERTY_REPAIR: + wzPropertyName = BURNMSIREPAIR_PROPERTY_NAME; + break; + case BURN_MSI_PROPERTY_UNINSTALL: + wzPropertyName = BURNMSIUNINSTALL_PROPERTY_NAME; + break; + } + + if (wzPropertyName) + { + hr = StrAllocConcatFormattedSecure(psczProperties, L" %ls=1", wzPropertyName); + ExitOnFailure(hr, "Failed to add burn action property."); + } + +LExit: + return hr; +} + +extern "C" HRESULT MsiEngineConcatProperties( + __in_ecount(cProperties) BURN_MSIPROPERTY* rgProperties, + __in DWORD cProperties, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __deref_out_z LPWSTR* psczProperties, + __in BOOL fObfuscateHiddenVariables + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + LPWSTR sczEscapedValue = NULL; + LPWSTR sczProperty = NULL; + + for (DWORD i = 0; i < cProperties; ++i) + { + BURN_MSIPROPERTY* pProperty = &rgProperties[i]; + + if (pProperty->sczCondition && *pProperty->sczCondition) + { + BOOL fCondition = FALSE; + + hr = ConditionEvaluate(pVariables, pProperty->sczCondition, &fCondition); + if (FAILED(hr) || !fCondition) + { + LogId(REPORT_VERBOSE, MSG_MSI_PROPERTY_CONDITION_FAILED, pProperty->sczId, pProperty->sczCondition, LoggingTrueFalseToString(fCondition)); + continue; + } + } + + // format property value + if (fObfuscateHiddenVariables) + { + hr = VariableFormatStringObfuscated(pVariables, (fRollback && pProperty->sczRollbackValue) ? pProperty->sczRollbackValue : pProperty->sczValue, &sczValue, NULL); + } + else + { + hr = VariableFormatString(pVariables, (fRollback && pProperty->sczRollbackValue) ? pProperty->sczRollbackValue : pProperty->sczValue, &sczValue, NULL); + ExitOnFailure(hr, "Failed to format property value."); + } + ExitOnFailure(hr, "Failed to format property value."); + + // escape property value + hr = EscapePropertyArgumentString(sczValue, &sczEscapedValue, !fObfuscateHiddenVariables); + ExitOnFailure(hr, "Failed to escape string."); + + // build part + hr = VariableStrAllocFormatted(!fObfuscateHiddenVariables, &sczProperty, L" %s%=\"%s\"", pProperty->sczId, sczEscapedValue); + ExitOnFailure(hr, "Failed to format property string part."); + + // append to property string + hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, psczProperties, sczProperty, 0); + ExitOnFailure(hr, "Failed to append property string part."); + } + +LExit: + StrSecureZeroFreeString(sczValue); + StrSecureZeroFreeString(sczEscapedValue); + StrSecureZeroFreeString(sczProperty); + return hr; +} + +extern "C" HRESULT MsiEngineCalculateInstallUiLevel( + __in BOOTSTRAPPER_DISPLAY display, + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzPackageId, + __in BOOL fExecute, + __in BOOTSTRAPPER_ACTION_STATE actionState, + __out BURN_MSI_PROPERTY* pActionMsiProperty, + __out INSTALLUILEVEL* pUiLevel, + __out BOOL* pfDisableExternalUiHandler + ) +{ + *pUiLevel = INSTALLUILEVEL_NONE; + *pfDisableExternalUiHandler = FALSE; + + if (BOOTSTRAPPER_DISPLAY_FULL == display || + BOOTSTRAPPER_DISPLAY_PASSIVE == display) + { + *pUiLevel = static_cast(*pUiLevel | INSTALLUILEVEL_SOURCERESONLY); + } + + switch (actionState) + { + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: + *pActionMsiProperty = BURN_MSI_PROPERTY_UNINSTALL; + break; + case BOOTSTRAPPER_ACTION_STATE_REPAIR: + *pActionMsiProperty = BURN_MSI_PROPERTY_REPAIR; + break; + case BOOTSTRAPPER_ACTION_STATE_MODIFY: + *pActionMsiProperty = BURN_MSI_PROPERTY_MODIFY; + break; + default: + *pActionMsiProperty = BURN_MSI_PROPERTY_INSTALL; + break; + } + + return UserExperienceOnPlanMsiPackage(pUserExperience, wzPackageId, fExecute, actionState, pActionMsiProperty, pUiLevel, pfDisableExternalUiHandler); +} + +extern "C" void MsiEngineUpdateInstallRegistrationState( + __in BURN_EXECUTE_ACTION* pAction, + __in BOOL fRollback, + __in HRESULT hrExecute, + __in BOOL fInsideMsiTransaction + ) +{ + BURN_PACKAGE_REGISTRATION_STATE newState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; + BURN_PACKAGE* pPackage = pAction->msiPackage.pPackage; + + if (FAILED(hrExecute) || !pPackage->fCanAffectRegistration) + { + ExitFunction(); + } + + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->msiPackage.action) + { + newState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + else + { + newState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + + if (fInsideMsiTransaction) + { + pPackage->transactionRegistrationState = newState; + } + else + { + pPackage->installRegistrationState = newState; + } + + if (BURN_PACKAGE_REGISTRATION_STATE_ABSENT == newState) + { + for (DWORD i = 0; i < pPackage->Msi.cChainedPatches; ++i) + { + BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + i; + BURN_MSPTARGETPRODUCT* pTargetProduct = pChainedPatch->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex; + + if (fInsideMsiTransaction) + { + pTargetProduct->transactionRegistrationState = newState; + } + else + { + pTargetProduct->registrationState = newState; + } + } + } + else + { + for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) + { + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + i; + BOOTSTRAPPER_ACTION_STATE patchExecuteAction = fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute; + + if (BOOTSTRAPPER_ACTION_STATE_INSTALL > patchExecuteAction) + { + continue; + } + + BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + pSlipstreamMsp->dwMsiChainedPatchIndex; + BURN_MSPTARGETPRODUCT* pTargetProduct = pChainedPatch->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex; + + if (fInsideMsiTransaction) + { + pTargetProduct->transactionRegistrationState = newState; + } + else + { + pTargetProduct->registrationState = newState; + } + } + } + +LExit: + return; +} + + +// internal helper functions + +static HRESULT ParseRelatedMsiFromXml( + __in IXMLDOMNode* pixnRelatedMsi, + __in BURN_RELATED_MSI* pRelatedMsi + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + LPWSTR scz = NULL; + + // @Id + hr = XmlGetAttributeEx(pixnRelatedMsi, L"Id", &pRelatedMsi->sczUpgradeCode); + ExitOnFailure(hr, "Failed to get @Id."); + + // @MinVersion + hr = XmlGetAttributeEx(pixnRelatedMsi, L"MinVersion", &scz); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @MinVersion."); + + hr = VerParseVersion(scz, 0, FALSE, &pRelatedMsi->pMinVersion); + ExitOnFailure(hr, "Failed to parse @MinVersion: %ls", scz); + + if (pRelatedMsi->pMinVersion->fInvalid) + { + LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz); + } + + // flag that we have a min version + pRelatedMsi->fMinProvided = TRUE; + + // @MinInclusive + hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"MinInclusive", &pRelatedMsi->fMinInclusive); + ExitOnFailure(hr, "Failed to get @MinInclusive."); + } + + // @MaxVersion + hr = XmlGetAttributeEx(pixnRelatedMsi, L"MaxVersion", &scz); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @MaxVersion."); + + hr = VerParseVersion(scz, 0, FALSE, &pRelatedMsi->pMaxVersion); + ExitOnFailure(hr, "Failed to parse @MaxVersion: %ls", scz); + + if (pRelatedMsi->pMaxVersion->fInvalid) + { + LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz); + } + + // flag that we have a max version + pRelatedMsi->fMaxProvided = TRUE; + + // @MaxInclusive + hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"MaxInclusive", &pRelatedMsi->fMaxInclusive); + ExitOnFailure(hr, "Failed to get @MaxInclusive."); + } + + // @OnlyDetect + hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"OnlyDetect", &pRelatedMsi->fOnlyDetect); + ExitOnFailure(hr, "Failed to get @OnlyDetect."); + + // select language nodes + hr = XmlSelectNodes(pixnRelatedMsi, L"Language", &pixnNodes); + ExitOnFailure(hr, "Failed to select language nodes."); + + // get language node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get language node count."); + + if (cNodes) + { + // @LangInclusive + hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"LangInclusive", &pRelatedMsi->fLangInclusive); + ExitOnFailure(hr, "Failed to get @LangInclusive."); + + // allocate memory for language IDs + pRelatedMsi->rgdwLanguages = (DWORD*)MemAlloc(sizeof(DWORD) * cNodes, TRUE); + ExitOnNull(pRelatedMsi->rgdwLanguages, hr, E_OUTOFMEMORY, "Failed to allocate memory for language IDs."); + + pRelatedMsi->cLanguages = cNodes; + + // parse language elements + for (DWORD i = 0; i < cNodes; ++i) + { + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeNumber(pixnNode, L"Id", &pRelatedMsi->rgdwLanguages[i]); + ExitOnFailure(hr, "Failed to get Language/@Id."); + + // prepare next iteration + ReleaseNullObject(pixnNode); + } + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseStr(scz); + + return hr; +} + +static HRESULT EvaluateActionStateConditions( + __in BURN_VARIABLES* pVariables, + __in_z_opt LPCWSTR sczAddLocalCondition, + __in_z_opt LPCWSTR sczAddSourceCondition, + __in_z_opt LPCWSTR sczAdvertiseCondition, + __out BOOTSTRAPPER_FEATURE_STATE* pState + ) +{ + HRESULT hr = S_OK; + BOOL fCondition = FALSE; + + // if no condition was set, return no feature state + if (!sczAddLocalCondition && !sczAddSourceCondition && !sczAdvertiseCondition) + { + *pState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; + ExitFunction(); + } + + if (sczAddLocalCondition) + { + hr = ConditionEvaluate(pVariables, sczAddLocalCondition, &fCondition); + ExitOnFailure(hr, "Failed to evaluate add local condition."); + + if (fCondition) + { + *pState = BOOTSTRAPPER_FEATURE_STATE_LOCAL; + ExitFunction(); + } + } + + if (sczAddSourceCondition) + { + hr = ConditionEvaluate(pVariables, sczAddSourceCondition, &fCondition); + ExitOnFailure(hr, "Failed to evaluate add source condition."); + + if (fCondition) + { + *pState = BOOTSTRAPPER_FEATURE_STATE_SOURCE; + ExitFunction(); + } + } + + if (sczAdvertiseCondition) + { + hr = ConditionEvaluate(pVariables, sczAdvertiseCondition, &fCondition); + ExitOnFailure(hr, "Failed to evaluate advertise condition."); + + if (fCondition) + { + *pState = BOOTSTRAPPER_FEATURE_STATE_ADVERTISED; + ExitFunction(); + } + } + + // if no condition was true, set to absent + *pState = BOOTSTRAPPER_FEATURE_STATE_ABSENT; + +LExit: + return hr; +} + +static HRESULT CalculateFeatureAction( + __in BOOTSTRAPPER_FEATURE_STATE currentState, + __in BOOTSTRAPPER_FEATURE_STATE requestedState, + __in BOOL fRepair, + __out BOOTSTRAPPER_FEATURE_ACTION* pFeatureAction, + __inout BOOL* pfDelta + ) +{ + HRESULT hr = S_OK; + + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_NONE; + switch (requestedState) + { + case BOOTSTRAPPER_FEATURE_STATE_UNKNOWN: + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_NONE; + break; + + case BOOTSTRAPPER_FEATURE_STATE_ABSENT: + if (BOOTSTRAPPER_FEATURE_STATE_ABSENT != currentState) + { + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REMOVE; + } + break; + + case BOOTSTRAPPER_FEATURE_STATE_ADVERTISED: + if (BOOTSTRAPPER_FEATURE_STATE_ADVERTISED != currentState) + { + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE; + } + else if (fRepair) + { + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL; + } + break; + + case BOOTSTRAPPER_FEATURE_STATE_LOCAL: + if (BOOTSTRAPPER_FEATURE_STATE_LOCAL != currentState) + { + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL; + } + else if (fRepair) + { + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL; + } + break; + + case BOOTSTRAPPER_FEATURE_STATE_SOURCE: + if (BOOTSTRAPPER_FEATURE_STATE_SOURCE != currentState) + { + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE; + } + else if (fRepair) + { + *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL; + } + break; + + default: + hr = E_UNEXPECTED; + ExitOnRootFailure(hr, "Invalid state value."); + } + + if (BOOTSTRAPPER_FEATURE_ACTION_NONE != *pFeatureAction) + { + *pfDelta = TRUE; + } + +LExit: + return hr; +} + +static HRESULT EscapePropertyArgumentString( + __in LPCWSTR wzProperty, + __inout_z LPWSTR* psczEscapedValue, + __in BOOL fZeroOnRealloc + ) +{ + HRESULT hr = S_OK; + DWORD cch = 0; + DWORD cchEscape = 0; + LPCWSTR wzSource = NULL; + LPWSTR wzTarget = NULL; + + // count characters to escape + wzSource = wzProperty; + while (*wzSource) + { + ++cch; + if (L'\"' == *wzSource) + { + ++cchEscape; + } + ++wzSource; + } + + // allocate target buffer + hr = VariableStrAlloc(fZeroOnRealloc, psczEscapedValue, cch + cchEscape + 1); // character count, plus escape character count, plus null terminator + ExitOnFailure(hr, "Failed to allocate string buffer."); + + // write to target buffer + wzSource = wzProperty; + wzTarget = *psczEscapedValue; + while (*wzSource) + { + *wzTarget = *wzSource; + if (L'\"' == *wzTarget) + { + ++wzTarget; + *wzTarget = L'\"'; + } + + ++wzSource; + ++wzTarget; + } + + *wzTarget = L'\0'; // add null terminator + +LExit: + return hr; +} + +static HRESULT ConcatFeatureActionProperties( + __in BURN_PACKAGE* pPackage, + __in BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions, + __inout_z LPWSTR* psczArguments + ) +{ + HRESULT hr = S_OK; + LPWSTR scz = NULL; + LPWSTR sczAddLocal = NULL; + LPWSTR sczAddSource = NULL; + LPWSTR sczAddDefault = NULL; + LPWSTR sczReinstall = NULL; + LPWSTR sczAdvertise = NULL; + LPWSTR sczRemove = NULL; + + // features + for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) + { + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; + + switch (rgFeatureActions[i]) + { + case BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL: + if (sczAddLocal) + { + hr = StrAllocConcat(&sczAddLocal, L",", 0); + ExitOnFailure(hr, "Failed to concat separator."); + } + hr = StrAllocConcat(&sczAddLocal, pFeature->sczId, 0); + ExitOnFailure(hr, "Failed to concat feature."); + break; + + case BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE: + if (sczAddSource) + { + hr = StrAllocConcat(&sczAddSource, L",", 0); + ExitOnFailure(hr, "Failed to concat separator."); + } + hr = StrAllocConcat(&sczAddSource, pFeature->sczId, 0); + ExitOnFailure(hr, "Failed to concat feature."); + break; + + case BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT: + if (sczAddDefault) + { + hr = StrAllocConcat(&sczAddDefault, L",", 0); + ExitOnFailure(hr, "Failed to concat separator."); + } + hr = StrAllocConcat(&sczAddDefault, pFeature->sczId, 0); + ExitOnFailure(hr, "Failed to concat feature."); + break; + + case BOOTSTRAPPER_FEATURE_ACTION_REINSTALL: + if (sczReinstall) + { + hr = StrAllocConcat(&sczReinstall, L",", 0); + ExitOnFailure(hr, "Failed to concat separator."); + } + hr = StrAllocConcat(&sczReinstall, pFeature->sczId, 0); + ExitOnFailure(hr, "Failed to concat feature."); + break; + + case BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE: + if (sczAdvertise) + { + hr = StrAllocConcat(&sczAdvertise, L",", 0); + ExitOnFailure(hr, "Failed to concat separator."); + } + hr = StrAllocConcat(&sczAdvertise, pFeature->sczId, 0); + ExitOnFailure(hr, "Failed to concat feature."); + break; + + case BOOTSTRAPPER_FEATURE_ACTION_REMOVE: + if (sczRemove) + { + hr = StrAllocConcat(&sczRemove, L",", 0); + ExitOnFailure(hr, "Failed to concat separator."); + } + hr = StrAllocConcat(&sczRemove, pFeature->sczId, 0); + ExitOnFailure(hr, "Failed to concat feature."); + break; + } + } + + if (sczAddLocal) + { + hr = StrAllocFormatted(&scz, L" ADDLOCAL=\"%s\"", sczAddLocal, 0); + ExitOnFailure(hr, "Failed to format ADDLOCAL string."); + + hr = StrAllocConcatSecure(psczArguments, scz, 0); + ExitOnFailure(hr, "Failed to concat argument string."); + } + + if (sczAddSource) + { + hr = StrAllocFormatted(&scz, L" ADDSOURCE=\"%s\"", sczAddSource, 0); + ExitOnFailure(hr, "Failed to format ADDSOURCE string."); + + hr = StrAllocConcatSecure(psczArguments, scz, 0); + ExitOnFailure(hr, "Failed to concat argument string."); + } + + if (sczAddDefault) + { + hr = StrAllocFormatted(&scz, L" ADDDEFAULT=\"%s\"", sczAddDefault, 0); + ExitOnFailure(hr, "Failed to format ADDDEFAULT string."); + + hr = StrAllocConcatSecure(psczArguments, scz, 0); + ExitOnFailure(hr, "Failed to concat argument string."); + } + + if (sczReinstall) + { + hr = StrAllocFormatted(&scz, L" REINSTALL=\"%s\"", sczReinstall, 0); + ExitOnFailure(hr, "Failed to format REINSTALL string."); + + hr = StrAllocConcatSecure(psczArguments, scz, 0); + ExitOnFailure(hr, "Failed to concat argument string."); + } + + if (sczAdvertise) + { + hr = StrAllocFormatted(&scz, L" ADVERTISE=\"%s\"", sczAdvertise, 0); + ExitOnFailure(hr, "Failed to format ADVERTISE string."); + + hr = StrAllocConcatSecure(psczArguments, scz, 0); + ExitOnFailure(hr, "Failed to concat argument string."); + } + + if (sczRemove) + { + hr = StrAllocFormatted(&scz, L" REMOVE=\"%s\"", sczRemove, 0); + ExitOnFailure(hr, "Failed to format REMOVE string."); + + hr = StrAllocConcatSecure(psczArguments, scz, 0); + ExitOnFailure(hr, "Failed to concat argument string."); + } + +LExit: + ReleaseStr(scz); + ReleaseStr(sczAddLocal); + ReleaseStr(sczAddSource); + ReleaseStr(sczAddDefault); + ReleaseStr(sczReinstall); + ReleaseStr(sczAdvertise); + ReleaseStr(sczRemove); + + return hr; +} + +static HRESULT ConcatPatchProperty( + __in BURN_PACKAGE* pPackage, + __in BOOL fRollback, + __inout_z LPWSTR* psczArguments + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCachedDirectory = NULL; + LPWSTR sczMspPath = NULL; + LPWSTR sczPatches = NULL; + + // If there are slipstream patch actions, build up their patch action. + if (pPackage->Msi.cSlipstreamMspPackages) + { + for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) + { + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + i; + BURN_PACKAGE* pMspPackage = pSlipstreamMsp->pMspPackage; + BURN_PAYLOAD* pMspPackagePayload = pMspPackage->payloads.rgItems[0].pPayload; + BOOTSTRAPPER_ACTION_STATE patchExecuteAction = fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute; + + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < patchExecuteAction) + { + hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory); + ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId); + + hr = PathConcat(sczCachedDirectory, pMspPackagePayload->sczFilePath, &sczMspPath); + ExitOnFailure(hr, "Failed to build MSP path."); + + if (!sczPatches) + { + hr = StrAllocConcat(&sczPatches, L" PATCH=\"", 0); + ExitOnFailure(hr, "Failed to prefix with PATCH property."); + } + else + { + hr = StrAllocConcat(&sczPatches, L";", 0); + ExitOnFailure(hr, "Failed to semi-colon delimit patches."); + } + + hr = StrAllocConcat(&sczPatches, sczMspPath, 0); + ExitOnFailure(hr, "Failed to append patch path."); + } + } + + if (sczPatches) + { + hr = StrAllocConcat(&sczPatches, L"\"", 0); + ExitOnFailure(hr, "Failed to close the quoted PATCH property."); + + hr = StrAllocConcatSecure(psczArguments, sczPatches, 0); + ExitOnFailure(hr, "Failed to append PATCH property."); + } + } + +LExit: + ReleaseStr(sczMspPath); + ReleaseStr(sczCachedDirectory); + ReleaseStr(sczPatches); + return hr; +} + +static void RegisterSourceDirectory( + __in BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzMsiPath + ) +{ + HRESULT hr = S_OK; + LPWSTR sczMsiDirectory = NULL; + MSIINSTALLCONTEXT dwContext = pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED; + + hr = PathGetDirectory(wzMsiPath, &sczMsiDirectory); + ExitOnFailure(hr, "Failed to get directory for path: %ls", wzMsiPath); + + hr = WiuSourceListAddSourceEx(pPackage->Msi.sczProductCode, NULL, dwContext, MSICODE_PRODUCT, sczMsiDirectory, 1); + if (FAILED(hr)) + { + LogId(REPORT_VERBOSE, MSG_SOURCELIST_REGISTER, sczMsiDirectory, pPackage->Msi.sczProductCode, hr); + ExitFunction(); + } + +LExit: + ReleaseStr(sczMsiDirectory); + + return; +} diff --git a/src/burn/engine/msiengine.h b/src/burn/engine/msiengine.h new file mode 100644 index 00000000..8b5bcdd0 --- /dev/null +++ b/src/burn/engine/msiengine.h @@ -0,0 +1,104 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +// constants +#define BURNMSIINSTALL_PROPERTY_NAME L"BURNMSIINSTALL" +#define BURNMSIMODIFY_PROPERTY_NAME L"BURNMSIMODIFY" +#define BURNMSIREPAIR_PROPERTY_NAME L"BURNMSIREPAIR" +#define BURNMSIUNINSTALL_PROPERTY_NAME L"BURNMSIUNINSTALL" + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// function declarations + +HRESULT MsiEngineParsePackageFromXml( + __in IXMLDOMNode* pixnBundle, + __in BURN_PACKAGE* pPackage + ); +HRESULT MsiEngineParsePropertiesFromXml( + __in IXMLDOMNode* pixnPackage, + __out BURN_MSIPROPERTY** prgProperties, + __out DWORD* pcProperties + ); +void MsiEnginePackageUninitialize( + __in BURN_PACKAGE* pPackage + ); +HRESULT MsiEngineDetectInitialize( + __in BURN_PACKAGES* pPackages + ); +HRESULT MsiEngineDetectPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_USER_EXPERIENCE* pUserExperience + ); +HRESULT MsiEnginePlanInitializePackage( + __in BURN_PACKAGE* pPackage, + __in BURN_VARIABLES* pVariables, + __in BURN_USER_EXPERIENCE* pUserExperience + ); +HRESULT MsiEnginePlanCalculatePackage( + __in BURN_PACKAGE* pPackage, + __in BOOL fInsideMsiTransaction + ); +HRESULT MsiEnginePlanAddPackage( + __in BOOTSTRAPPER_DISPLAY display, + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in_opt HANDLE hCacheEvent + ); +HRESULT MsiEngineBeginTransaction( + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary + ); +HRESULT MsiEngineCommitTransaction( + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary + ); +HRESULT MsiEngineRollbackTransaction( + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary + ); +HRESULT MsiEngineExecutePackage( + __in_opt HWND hwndParent, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +HRESULT MsiEngineConcatActionProperty( + __in BURN_MSI_PROPERTY actionMsiProperty, + __deref_out_z LPWSTR* psczProperties + ); +HRESULT MsiEngineConcatProperties( + __in_ecount(cProperties) BURN_MSIPROPERTY* rgProperties, + __in DWORD cProperties, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __deref_out_z LPWSTR* psczProperties, + __in BOOL fObfuscateHiddenVariables + ); +HRESULT MsiEngineCalculateInstallUiLevel( + __in BOOTSTRAPPER_DISPLAY display, + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzPackageId, + __in BOOL fExecute, + __in BOOTSTRAPPER_ACTION_STATE actionState, + __out BURN_MSI_PROPERTY* pActionMsiProperty, + __out INSTALLUILEVEL* pUiLevel, + __out BOOL* pfDisableExternalUiHandler + ); +void MsiEngineUpdateInstallRegistrationState( + __in BURN_EXECUTE_ACTION* pAction, + __in BOOL fRollback, + __in HRESULT hrExecute, + __in BOOL fInsideMsiTransaction + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/mspengine.cpp b/src/burn/engine/mspengine.cpp new file mode 100644 index 00000000..6d58d324 --- /dev/null +++ b/src/burn/engine/mspengine.cpp @@ -0,0 +1,1197 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// constants + + +// structs + +struct POSSIBLE_TARGETPRODUCT +{ + WCHAR wzProductCode[39]; + LPWSTR pszLocalPackage; + MSIINSTALLCONTEXT context; +}; + +// internal function declarations + +static HRESULT GetPossibleTargetProductCodes( + __in BURN_PACKAGES* pPackages, + __deref_inout_ecount_opt(*pcPossibleTargetProductCodes) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProductCodes, + __inout DWORD* pcPossibleTargetProductCodes + ); +static HRESULT AddPossibleTargetProduct( + __in STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes, + __in_z LPCWSTR wzPossibleTargetProductCode, + __in MSIINSTALLCONTEXT context, + __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts, + __inout DWORD* pcPossibleTargetProducts + ); +static HRESULT AddDetectedTargetProduct( + __in BURN_PACKAGE* pPackage, + __in DWORD dwOrder, + __in_z LPCWSTR wzProductCode, + __in MSIINSTALLCONTEXT context, + __out DWORD* pdwTargetProductIndex + ); +static HRESULT AddMsiChainedPatch( + __in BURN_PACKAGE* pPackage, + __in BURN_PACKAGE* pMspPackage, + __in DWORD dwMspTargetProductIndex, + __out DWORD* pdwChainedPatchIndex + ); +static HRESULT DeterminePatchChainedTarget( + __in BURN_PACKAGES* pPackages, + __in BURN_PACKAGE* pMspPackage, + __in LPCWSTR wzTargetProductCode, + __in DWORD dwMspTargetProductIndex + ); +static HRESULT PlanTargetProduct( + __in BOOTSTRAPPER_DISPLAY display, + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fRollback, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_ACTION_STATE actionState, + __in BURN_PACKAGE* pPackage, + __in BURN_MSPTARGETPRODUCT* pTargetProduct, + __in_opt HANDLE hCacheEvent + ); + + +// function definitions + +extern "C" HRESULT MspEngineParsePackageFromXml( + __in IXMLDOMNode* pixnMspPackage, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + + // @PatchCode + hr = XmlGetAttributeEx(pixnMspPackage, L"PatchCode", &pPackage->Msp.sczPatchCode); + ExitOnFailure(hr, "Failed to get @PatchCode."); + + // @PatchXml + hr = XmlGetAttributeEx(pixnMspPackage, L"PatchXml", &pPackage->Msp.sczApplicabilityXml); + ExitOnFailure(hr, "Failed to get @PatchXml."); + + // Read properties. + hr = MsiEngineParsePropertiesFromXml(pixnMspPackage, &pPackage->Msp.rgProperties, &pPackage->Msp.cProperties); + ExitOnFailure(hr, "Failed to parse properties from XML."); + +LExit: + + return hr; +} + +extern "C" void MspEnginePackageUninitialize( + __in BURN_PACKAGE* pPackage + ) +{ + ReleaseStr(pPackage->Msp.sczPatchCode); + ReleaseStr(pPackage->Msp.sczApplicabilityXml); + + // free properties + if (pPackage->Msp.rgProperties) + { + for (DWORD i = 0; i < pPackage->Msp.cProperties; ++i) + { + BURN_MSIPROPERTY* pProperty = &pPackage->Msp.rgProperties[i]; + + ReleaseStr(pProperty->sczId); + ReleaseStr(pProperty->sczValue); + ReleaseStr(pProperty->sczRollbackValue); + } + MemFree(pPackage->Msp.rgProperties); + } + + // free target products + ReleaseMem(pPackage->Msp.rgTargetProducts); + + // clear struct + memset(&pPackage->Msp, 0, sizeof(pPackage->Msp)); +} + +extern "C" HRESULT MspEngineDetectInitialize( + __in BURN_PACKAGES* pPackages + ) +{ + AssertSz(pPackages->cPatchInfo, "MspEngineDetectInitialize() should only be called if there are MSP packages."); + + HRESULT hr = S_OK; + POSSIBLE_TARGETPRODUCT* rgPossibleTargetProducts = NULL; + DWORD cPossibleTargetProducts = 0; + +#ifdef DEBUG + // All patch info should be initialized to zero. + for (DWORD i = 0; i < pPackages->cPatchInfo; ++i) + { + BURN_PACKAGE* pPackage = pPackages->rgPatchInfoToPackage[i]; + Assert(!pPackage->Msp.cTargetProductCodes); + Assert(!pPackage->Msp.rgTargetProducts); + } +#endif + + // Figure out which product codes to target on the machine. In the worst case all products on the machine + // will be returned. + hr = GetPossibleTargetProductCodes(pPackages, &rgPossibleTargetProducts, &cPossibleTargetProducts); + ExitOnFailure(hr, "Failed to get possible target product codes."); + + // Loop through possible target products, testing the collective patch applicability against each product in + // the appropriate context. Store the result with the appropriate patch package. + for (DWORD iSearch = 0; iSearch < cPossibleTargetProducts; ++iSearch) + { + const POSSIBLE_TARGETPRODUCT* pPossibleTargetProduct = rgPossibleTargetProducts + iSearch; + + LogId(REPORT_STANDARD, MSG_DETECT_CALCULATE_PATCH_APPLICABILITY, pPossibleTargetProduct->wzProductCode, LoggingMsiInstallContext(pPossibleTargetProduct->context)); + + if (pPossibleTargetProduct->pszLocalPackage) + { + // Ignores current machine state to determine just patch applicability. + // Superseded and obsolesced patches will be planned separately. + hr = WiuDetermineApplicablePatches(pPossibleTargetProduct->pszLocalPackage, pPackages->rgPatchInfo, pPackages->cPatchInfo); + } + else + { + hr = WiuDeterminePatchSequence(pPossibleTargetProduct->wzProductCode, NULL, pPossibleTargetProduct->context, pPackages->rgPatchInfo, pPackages->cPatchInfo); + } + + if (SUCCEEDED(hr)) + { + for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo) + { + hr = HRESULT_FROM_WIN32(pPackages->rgPatchInfo[iPatchInfo].uStatus); + BURN_PACKAGE* pMspPackage = pPackages->rgPatchInfoToPackage[iPatchInfo]; + Assert(BURN_PACKAGE_TYPE_MSP == pMspPackage->type); + + if (S_OK == hr) + { + // Note that we do add superseded and obsolete MSP packages. Package Detect and Plan will sort them out later. + hr = MspEngineAddDetectedTargetProduct(pPackages, pMspPackage, pPackages->rgPatchInfo[iPatchInfo].dwOrder, pPossibleTargetProduct->wzProductCode, pPossibleTargetProduct->context); + ExitOnFailure(hr, "Failed to add target product code to package: %ls", pMspPackage->sczId); + } + else + { + LogStringLine(REPORT_DEBUG, " 0x%x: Patch applicability failed for package: %ls", hr, pMspPackage->sczId); + } + } + } + else + { + LogId(REPORT_STANDARD, MSG_DETECT_FAILED_CALCULATE_PATCH_APPLICABILITY, pPossibleTargetProduct->wzProductCode, LoggingMsiInstallContext(pPossibleTargetProduct->context), hr); + } + + hr = S_OK; // always reset so we test all possible target products. + } + +LExit: + if (rgPossibleTargetProducts) + { + for (DWORD i = 0; i < cPossibleTargetProducts; ++i) + { + ReleaseStr(rgPossibleTargetProducts[i].pszLocalPackage); + } + MemFree(rgPossibleTargetProducts); + } + + return hr; +} + +extern "C" HRESULT MspEngineAddDetectedTargetProduct( + __in BURN_PACKAGES* pPackages, + __in BURN_PACKAGE* pPackage, + __in DWORD dwOrder, + __in_z LPCWSTR wzProductCode, + __in MSIINSTALLCONTEXT context + ) +{ + HRESULT hr = S_OK; + DWORD dwTargetProductIndex = 0; + + hr = AddDetectedTargetProduct(pPackage, dwOrder, wzProductCode, context, &dwTargetProductIndex); + ExitOnFailure(hr, "Failed to add detected target product."); + + hr = DeterminePatchChainedTarget(pPackages, pPackage, wzProductCode, dwTargetProductIndex); + ExitOnFailure(hr, "Failed to determine patch chained target."); + +LExit: + return hr; +} + +extern "C" HRESULT MspEngineAddMissingSlipstreamTarget( + __in BURN_PACKAGE* pMsiPackage, + __in BURN_SLIPSTREAM_MSP* pSlipstreamMsp + ) +{ + HRESULT hr = S_OK; + DWORD dwTargetProductIndex = 0; + BURN_MSPTARGETPRODUCT* pTargetProduct = NULL; + DWORD dwChainedPatchIndex = 0; + + hr = AddDetectedTargetProduct(pSlipstreamMsp->pMspPackage, 0, pMsiPackage->Msi.sczProductCode, pMsiPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, &dwTargetProductIndex); + ExitOnFailure(hr, "Failed to add missing slipstream target."); + + pTargetProduct = pSlipstreamMsp->pMspPackage->Msp.rgTargetProducts + dwTargetProductIndex; + pTargetProduct->fSlipstream = TRUE; + pTargetProduct->fSlipstreamRequired = TRUE; + pTargetProduct->pChainedTargetPackage = pMsiPackage; + + hr = AddMsiChainedPatch(pMsiPackage, pSlipstreamMsp->pMspPackage, dwTargetProductIndex, &dwChainedPatchIndex); + ExitOnFailure(hr, "Failed to add chained patch."); + + pSlipstreamMsp->dwMsiChainedPatchIndex = dwChainedPatchIndex; + +LExit: + return hr; +} + +extern "C" HRESULT MspEngineDetectPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + LPWSTR sczState = NULL; + + if (pPackage->fCanAffectRegistration) + { + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + + if (0 == pPackage->Msp.cTargetProductCodes) + { + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + } + else + { + // Start the package state at the highest state then loop through all the + // target product codes and end up setting the current state to the lowest + // package state applied to the target product codes. + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; + + for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; + + hr = WiuGetPatchInfoEx(pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode, NULL, pTargetProduct->context, INSTALLPROPERTY_PATCHSTATE, &sczState); + if (SUCCEEDED(hr)) + { + switch (*sczState) + { + case '1': + pTargetProduct->fInstalled = TRUE; + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; + break; + + case '2': + pTargetProduct->fInstalled = TRUE; + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; + break; + + case '4': + pTargetProduct->fInstalled = TRUE; + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE; + break; + + default: + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + break; + } + } + else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PATCH) == hr || HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr) + { + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get patch information for patch code: %ls, target product code: %ls", pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode); + + if (pPackage->currentState > pTargetProduct->patchPackageState) + { + pPackage->currentState = pTargetProduct->patchPackageState; + } + + if (pPackage->fCanAffectRegistration) + { + pTargetProduct->registrationState = pTargetProduct->fInstalled ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + + if (pTargetProduct->fInstalled) + { + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + } + + hr = UserExperienceOnDetectPatchTarget(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, pTargetProduct->patchPackageState); + ExitOnRootFailure(hr, "BA aborted detect patch target."); + } + } + +LExit: + ReleaseStr(sczState); + + return hr; +} + +extern "C" HRESULT MspEnginePlanInitializePackage( + __in BURN_PACKAGE* pPackage, + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; + + if (!pTargetProduct->fInstalled && pTargetProduct->fSlipstreamRequired && BOOTSTRAPPER_REQUEST_STATE_PRESENT > pTargetProduct->pChainedTargetPackage->requested) + { + // There's no way to apply the patch if the target isn't installed. + pTargetProduct->defaultRequested = pTargetProduct->requested = BOOTSTRAPPER_REQUEST_STATE_NONE; + continue; + } + + pTargetProduct->defaultRequested = pTargetProduct->requested = pPackage->requested; + + hr = UserExperienceOnPlanPatchTarget(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, &pTargetProduct->requested); + ExitOnRootFailure(hr, "BA aborted plan patch target."); + } + +LExit: + return hr; +} + +// +// PlanCalculate - calculates the execute and rollback state for the requested package state. +// +extern "C" HRESULT MspEnginePlanCalculatePackage( + __in BURN_PACKAGE* pPackage, + __in BOOL fInsideMsiTransaction + ) +{ + HRESULT hr = S_OK; + BOOL fWillUninstallAll = TRUE; + + for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; + + BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; + BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + + // Calculate the execute action. + switch (pTargetProduct->patchPackageState) + { + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: + switch (pTargetProduct->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + execute = BOOTSTRAPPER_ACTION_STATE_REPAIR; + fWillUninstallAll = FALSE; + break; + + case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_CACHE: + execute = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; + break; + + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: + execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; + break; + + default: + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + fWillUninstallAll = FALSE; + break; + } + break; + + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: + switch (pTargetProduct->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; + fWillUninstallAll = FALSE; + break; + + default: + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + default: + if (pTargetProduct->fInstalled) + { + fWillUninstallAll = FALSE; + } + break; + } + + // Calculate the rollback action if there is an execute action. + if (BOOTSTRAPPER_ACTION_STATE_NONE != execute && !fInsideMsiTransaction) + { + switch (pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: + switch (pTargetProduct->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_ABSENT: + rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL; + break; + + default: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough; + switch (pTargetProduct->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + rollback = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; + break; + + default: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + default: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + } + + pTargetProduct->execute = execute; + pTargetProduct->rollback = rollback; + + // The highest aggregate action state found will be returned. + if (pPackage->execute < execute) + { + pPackage->execute = execute; + } + + if (pPackage->rollback < rollback) + { + pPackage->rollback = rollback; + } + } + + // The dependency manager will do the wrong thing if the package level action is UNINSTALL + // when the patch will still be applied to at least one product. + if (!fWillUninstallAll && BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) + { + pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; + } + + return hr; +} + +// +// PlanAdd - adds the calculated execute and rollback actions for the package. +// +extern "C" HRESULT MspEnginePlanAddPackage( + __in BOOTSTRAPPER_DISPLAY display, + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in_opt HANDLE hCacheEvent + ) +{ + HRESULT hr = S_OK; + + // TODO: need to handle the case where this patch adds itself to an earlier patch's list of target products. That would + // essentially bump this patch earlier in the plan and we need to make sure this patch is downloaded. + // add wait for cache + if (hCacheEvent) + { + hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent); + ExitOnFailure(hr, "Failed to plan package cache syncpoint"); + } + + hr = DependencyPlanPackage(NULL, pPackage, pPlan); + ExitOnFailure(hr, "Failed to plan package dependency actions."); + + // Plan the actions for each target product code. + for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; + + // If the dependency manager changed the action state for the patch, change the target product actions. + if (pPackage->fDependencyManagerWasHere) + { + pTargetProduct->execute = pPackage->execute; + pTargetProduct->rollback = pPackage->rollback; + } + + if (BOOTSTRAPPER_ACTION_STATE_NONE != pTargetProduct->execute) + { + hr = PlanTargetProduct(display, pUserExperience, FALSE, pPlan, pLog, pVariables, pTargetProduct->execute, pPackage, pTargetProduct, hCacheEvent); + ExitOnFailure(hr, "Failed to plan target product."); + } + + if (BOOTSTRAPPER_ACTION_STATE_NONE != pTargetProduct->rollback) + { + hr = PlanTargetProduct(display, pUserExperience, TRUE, pPlan, pLog, pVariables, pTargetProduct->rollback, pPackage, pTargetProduct, hCacheEvent); + ExitOnFailure(hr, "Failed to plan rollback target product."); + } + } + +LExit: + + return hr; +} + +extern "C" HRESULT MspEngineExecutePackage( + __in_opt HWND hwndParent, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + WIU_MSI_EXECUTE_CONTEXT context = { }; + WIU_RESTART restart = WIU_RESTART_NONE; + + LPWSTR sczCachedDirectory = NULL; + LPWSTR sczMspPath = NULL; + LPWSTR sczPatches = NULL; + LPWSTR sczProperties = NULL; + LPWSTR sczObfuscatedProperties = NULL; + + // default to "verbose" logging + DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE; + + // get cached MSP paths + for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i) + { + LPCWSTR wzAppend = NULL; + BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage; + BURN_PAYLOAD* pMspPackagePayload = pMspPackage->payloads.rgItems[0].pPayload; + AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Invalid package type added to ordered patches."); + + if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->mspTarget.action) + { + hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory); + ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId); + + // TODO: Figure out if this makes sense -- the variable is set to the last patch's path only + // Best effort to set the execute package cache folder variable. + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE); + + hr = PathConcat(sczCachedDirectory, pMspPackagePayload->sczFilePath, &sczMspPath); + ExitOnFailure(hr, "Failed to build MSP path."); + + wzAppend = sczMspPath; + } + else // uninstall + { + wzAppend = pMspPackage->Msp.sczPatchCode; + } + + if (NULL != sczPatches) + { + hr = StrAllocConcat(&sczPatches, L";", 0); + ExitOnFailure(hr, "Failed to semi-colon delimit patches."); + } + + hr = StrAllocConcat(&sczPatches, wzAppend, 0); + ExitOnFailure(hr, "Failed to append patch."); + } + + // Best effort to set the execute package action variable. + VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->mspTarget.action, TRUE); + + // Wire up the external UI handler and logging. + if (pExecuteAction->mspTarget.fDisableExternalUiHandler) + { + hr = WiuInitializeInternalUI(pExecuteAction->mspTarget.uiLevel, hwndParent, &context); + ExitOnFailure(hr, "Failed to initialize internal UI for MSP package."); + } + else + { + hr = WiuInitializeExternalUI(pfnMessageHandler, pExecuteAction->mspTarget.uiLevel, hwndParent, pvContext, fRollback, &context); + ExitOnFailure(hr, "Failed to initialize external UI handler."); + } + + //if (BURN_LOGGING_LEVEL_DEBUG == logLevel) + //{ + // dwLogMode | INSTALLLOGMODE_EXTRADEBUG; + //} + + if (pExecuteAction->mspTarget.sczLogPath && *pExecuteAction->mspTarget.sczLogPath) + { + hr = WiuEnableLog(dwLogMode, pExecuteAction->mspTarget.sczLogPath, 0); + ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pExecuteAction->mspTarget.pPackage->sczId, pExecuteAction->mspTarget.sczLogPath); + } + + // set up properties + hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczProperties, FALSE); + ExitOnFailure(hr, "Failed to add properties to argument string."); + + hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE); + ExitOnFailure(hr, "Failed to add properties to obfuscated argument string."); + + hr = MsiEngineConcatActionProperty(pExecuteAction->mspTarget.actionMsiProperty, &sczProperties); + ExitOnFailure(hr, "Failed to add action property to argument string."); + + hr = MsiEngineConcatActionProperty(pExecuteAction->mspTarget.actionMsiProperty, &sczObfuscatedProperties); + ExitOnFailure(hr, "Failed to add action property to obfuscated argument string."); + + LogId(REPORT_STANDARD, MSG_APPLYING_PATCH_PACKAGE, pExecuteAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pExecuteAction->mspTarget.action), sczPatches, sczObfuscatedProperties, pExecuteAction->mspTarget.sczTargetProductCode); + + // + // Do the actual action. + // + switch (pExecuteAction->mspTarget.action) + { + case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough; + case BOOTSTRAPPER_ACTION_STATE_REPAIR: + hr = StrAllocConcatSecure(&sczProperties, L" PATCH=\"", 0); + ExitOnFailure(hr, "Failed to add PATCH property on install."); + + hr = StrAllocConcatSecure(&sczProperties, sczPatches, 0); + ExitOnFailure(hr, "Failed to add patches to PATCH property on install."); + + hr = StrAllocConcatSecure(&sczProperties, L"\" REBOOT=ReallySuppress", 0); + ExitOnFailure(hr, "Failed to add reboot suppression property on install."); + + hr = WiuConfigureProductEx(pExecuteAction->mspTarget.sczTargetProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, sczProperties, &restart); + ExitOnFailure(hr, "Failed to install MSP package."); + break; + + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: + hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0); + ExitOnFailure(hr, "Failed to add reboot suppression property on uninstall."); + + // Ignore all dependencies, since the Burn engine already performed the check. + hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES); + ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties."); + + hr = WiuRemovePatches(sczPatches, pExecuteAction->mspTarget.sczTargetProductCode, sczProperties, &restart); + ExitOnFailure(hr, "Failed to uninstall MSP package."); + break; + } + +LExit: + WiuUninitializeExternalUI(&context); + + ReleaseStr(sczCachedDirectory); + ReleaseStr(sczMspPath); + StrSecureZeroFreeString(sczProperties); + ReleaseStr(sczObfuscatedProperties); + ReleaseStr(sczPatches); + + switch (restart) + { + case WIU_RESTART_NONE: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; + break; + + case WIU_RESTART_REQUIRED: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; + break; + + case WIU_RESTART_INITIATED: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; + break; + } + + // Best effort to clear the execute package cache folder and action variables. + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE); + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE, FALSE); + + return hr; +} + +extern "C" void MspEngineUpdateInstallRegistrationState( + __in BURN_EXECUTE_ACTION* pAction, + __in HRESULT hrExecute, + __in BOOL fInsideMsiTransaction + ) +{ + BURN_PACKAGE_REGISTRATION_STATE newState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; + + if (FAILED(hrExecute)) + { + ExitFunction(); + } + + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->mspTarget.action) + { + newState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + else + { + newState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + + for (DWORD i = 0; i < pAction->mspTarget.cOrderedPatches; ++i) + { + BURN_ORDERED_PATCHES* pOrderedPatches = pAction->mspTarget.rgOrderedPatches + i; + BURN_PACKAGE* pPackage = pOrderedPatches->pPackage; + BURN_MSPTARGETPRODUCT* pTargetProduct = NULL; + + Assert(BURN_PACKAGE_TYPE_MSP == pPackage->type); + + if (!pPackage->fCanAffectRegistration) + { + continue; + } + + for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j) + { + pTargetProduct = pPackage->Msp.rgTargetProducts + j; + if (pAction->mspTarget.fPerMachineTarget == (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context) && + CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pAction->mspTarget.sczTargetProductCode, -1, pTargetProduct->wzTargetProductCode, -1)) + { + break; + } + + pTargetProduct = NULL; + } + + if (!pTargetProduct) + { + AssertSz(pTargetProduct, "Ordered patch didn't have corresponding target product"); + continue; + } + + if (fInsideMsiTransaction) + { + pTargetProduct->transactionRegistrationState = newState; + } + else + { + pTargetProduct->registrationState = newState; + } + } + +LExit: + return; +} + +extern "C" void MspEngineFinalizeInstallRegistrationState( + __in BURN_PACKAGE* pPackage + ) +{ + if (!pPackage->fCanAffectRegistration) + { + ExitFunction(); + } + + if (!pPackage->Msp.cTargetProductCodes) + { + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + else + { + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; + + for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; + + if (pPackage->installRegistrationState < pTargetProduct->registrationState) + { + pPackage->installRegistrationState = pTargetProduct->registrationState; + } + } + } + +LExit: + return; +} + + +// internal helper functions + +static HRESULT GetPossibleTargetProductCodes( + __in BURN_PACKAGES* pPackages, + __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts, + __inout DWORD* pcPossibleTargetProducts + ) +{ + HRESULT hr = S_OK; + STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes = NULL; + BOOL fCheckAll = FALSE; + WCHAR wzPossibleTargetProductCode[MAX_GUID_CHARS + 1]; + + // Use a dictionary to ensure we capture unique product codes. Otherwise, we could end up + // doing patch applicability for the same product code multiple times and that would confuse + // everything down stream. + hr = DictCreateStringList(&sdUniquePossibleTargetProductCodes, 5, DICT_FLAG_NONE); + ExitOnFailure(hr, "Failed to create unique target product codes."); + + // If the patches target a specific set of product/upgrade codes, search only those. This + // should be much faster than searching all packages on the machine. + if (pPackages->rgPatchTargetCodes) + { + for (DWORD i = 0; i < pPackages->cPatchTargetCodes; ++i) + { + BURN_PATCH_TARGETCODE* pTargetCode = pPackages->rgPatchTargetCodes + i; + + // If targeting a product, add the unique product code to the list. + if (BURN_PATCH_TARGETCODE_TYPE_PRODUCT == pTargetCode->type) + { + hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, pTargetCode->sczTargetCode, MSIINSTALLCONTEXT_NONE, prgPossibleTargetProducts, pcPossibleTargetProducts); + ExitOnFailure(hr, "Failed to add product code to possible target product codes."); + } + else if (BURN_PATCH_TARGETCODE_TYPE_UPGRADE == pTargetCode->type) + { + // Enumerate all unique related products to the target upgrade code. + for (DWORD iProduct = 0; SUCCEEDED(hr); ++iProduct) + { + hr = WiuEnumRelatedProducts(pTargetCode->sczTargetCode, iProduct, wzPossibleTargetProductCode); + if (SUCCEEDED(hr)) + { + hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode, MSIINSTALLCONTEXT_NONE, prgPossibleTargetProducts, pcPossibleTargetProducts); + ExitOnFailure(hr, "Failed to add upgrade product code to possible target product codes."); + } + else if (E_BADCONFIGURATION == hr) + { + // Skip product's with bad configuration and continue. + LogId(REPORT_STANDARD, MSG_DETECT_BAD_PRODUCT_CONFIGURATION, wzPossibleTargetProductCode); + + hr = S_OK; + } + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to enumerate all products to patch related to upgrade code: %ls", pTargetCode->sczTargetCode); + } + else + { + // The element does not target a specific product. + fCheckAll = TRUE; + + break; + } + } + } + else + { + fCheckAll = TRUE; + } + + // One or more of the patches do not target a specific product so search everything on the machine. + if (fCheckAll) + { + for (DWORD iProduct = 0; SUCCEEDED(hr); ++iProduct) + { + MSIINSTALLCONTEXT context = MSIINSTALLCONTEXT_NONE; + + hr = WiuEnumProductsEx(NULL, NULL, MSIINSTALLCONTEXT_ALL, iProduct, wzPossibleTargetProductCode, &context, NULL, NULL); + if (SUCCEEDED(hr)) + { + hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode, context, prgPossibleTargetProducts, pcPossibleTargetProducts); + ExitOnFailure(hr, "Failed to add product code to search product codes."); + } + else if (E_BADCONFIGURATION == hr) + { + // Skip products with bad configuration and continue. + LogId(REPORT_STANDARD, MSG_DETECT_BAD_PRODUCT_CONFIGURATION, wzPossibleTargetProductCode); + + hr = S_OK; + } + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to enumerate all products on the machine for patches applicability."); + } + +LExit: + ReleaseDict(sdUniquePossibleTargetProductCodes); + + return hr; +} + +static HRESULT AddPossibleTargetProduct( + __in STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes, + __in_z LPCWSTR wzPossibleTargetProductCode, + __in MSIINSTALLCONTEXT context, + __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts, + __inout DWORD* pcPossibleTargetProducts + ) +{ + HRESULT hr = S_OK; + LPWSTR pszLocalPackage = NULL; + + // Only add this possible target code if we haven't queried for it already. + if (E_NOTFOUND == DictKeyExists(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode)) + { + // If the install context is not known, ask the Windows Installer for it. If we can't get the context + // then bail. + if (MSIINSTALLCONTEXT_NONE == context) + { + hr = WiuEnumProductsEx(wzPossibleTargetProductCode, NULL, MSIINSTALLCONTEXT_ALL, 0, NULL, &context, NULL, NULL); + if (FAILED(hr)) + { + ExitFunction1(hr = S_OK); + } + } + + hr = DictAddKey(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode); + ExitOnFailure(hr, "Failed to add possible target code to unique product codes."); + + hr = MemEnsureArraySize(reinterpret_cast(prgPossibleTargetProducts), *pcPossibleTargetProducts + 1, sizeof(POSSIBLE_TARGETPRODUCT), 3); + ExitOnFailure(hr, "Failed to grow array of possible target products."); + + POSSIBLE_TARGETPRODUCT *const pPossibleTargetProduct = *prgPossibleTargetProducts + *pcPossibleTargetProducts; + + hr = ::StringCchCopyW(pPossibleTargetProduct->wzProductCode, countof(pPossibleTargetProduct->wzProductCode), wzPossibleTargetProductCode); + ExitOnFailure(hr, "Failed to copy possible target product code."); + + // Attempt to get the local package path so we can more quickly determine patch applicability later. + hr = WiuGetProductInfoEx(wzPossibleTargetProductCode, NULL, context, INSTALLPROPERTY_LOCALPACKAGE, &pszLocalPackage); + if (SUCCEEDED(hr)) + { + pPossibleTargetProduct->pszLocalPackage = pszLocalPackage; + pszLocalPackage = NULL; + } + else + { + // Will instead call MsiDeterminePatchSequence later. + hr = S_OK; + } + + pPossibleTargetProduct->context = context; + + ++(*pcPossibleTargetProducts); + } + +LExit: + ReleaseStr(pszLocalPackage); + + return hr; +} + +static HRESULT AddDetectedTargetProduct( + __in BURN_PACKAGE* pPackage, + __in DWORD dwOrder, + __in_z LPCWSTR wzProductCode, + __in MSIINSTALLCONTEXT context, + __out DWORD* pdwTargetProductIndex + ) +{ + HRESULT hr = S_OK; + BURN_MSPTARGETPRODUCT* pTargetProduct = NULL; + + *pdwTargetProductIndex = BURN_PACKAGE_INVALID_PATCH_INDEX; + + hr = MemEnsureArraySize(reinterpret_cast(&pPackage->Msp.rgTargetProducts), pPackage->Msp.cTargetProductCodes + 1, sizeof(BURN_MSPTARGETPRODUCT), 5); + ExitOnFailure(hr, "Failed to ensure enough target product codes were allocated."); + + pTargetProduct = pPackage->Msp.rgTargetProducts + pPackage->Msp.cTargetProductCodes; + + hr = ::StringCchCopyW(pTargetProduct->wzTargetProductCode, countof(pTargetProduct->wzTargetProductCode), wzProductCode); + ExitOnFailure(hr, "Failed to copy target product code."); + + pTargetProduct->context = context; + pTargetProduct->dwOrder = dwOrder; + + *pdwTargetProductIndex = pPackage->Msp.cTargetProductCodes; + ++pPackage->Msp.cTargetProductCodes; + +LExit: + return hr; +} + +static HRESULT AddMsiChainedPatch( + __in BURN_PACKAGE* pPackage, + __in BURN_PACKAGE* pMspPackage, + __in DWORD dwMspTargetProductIndex, + __out DWORD* pdwChainedPatchIndex + ) +{ + HRESULT hr = S_OK; + + hr = MemEnsureArraySize(reinterpret_cast(&pPackage->Msi.rgChainedPatches), pPackage->Msi.cChainedPatches + 1, sizeof(BURN_CHAINED_PATCH), 5); + ExitOnFailure(hr, "Failed to ensure enough chained patches were allocated."); + + BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + pPackage->Msi.cChainedPatches; + pChainedPatch->pMspPackage = pMspPackage; + pChainedPatch->dwMspTargetProductIndex = dwMspTargetProductIndex; + + *pdwChainedPatchIndex = pPackage->Msi.cChainedPatches; + ++pPackage->Msi.cChainedPatches; +LExit: + return hr; +} + +static HRESULT DeterminePatchChainedTarget( + __in BURN_PACKAGES* pPackages, + __in BURN_PACKAGE* pMspPackage, + __in LPCWSTR wzTargetProductCode, + __in DWORD dwMspTargetProductIndex + ) +{ + HRESULT hr = S_OK; + DWORD dwChainedPatchIndex = 0; + BURN_MSPTARGETPRODUCT* pTargetProduct = pMspPackage->Msp.rgTargetProducts + dwMspTargetProductIndex; + + for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage) + { + BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage; + + if (BURN_PACKAGE_TYPE_MSI == pPackage->type && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzTargetProductCode, -1, pPackage->Msi.sczProductCode, -1)) + { + pTargetProduct->pChainedTargetPackage = pPackage; + + hr = AddMsiChainedPatch(pPackage, pMspPackage, dwMspTargetProductIndex, &dwChainedPatchIndex); + ExitOnFailure(hr, "Failed to add chained patch."); + + for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) + { + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + j; + if (pSlipstreamMsp->pMspPackage == pMspPackage) + { + AssertSz(BURN_PACKAGE_INVALID_PATCH_INDEX == pSlipstreamMsp->dwMsiChainedPatchIndex, "An MSP should only show up as a slipstreamed patch in an MSI once."); + pTargetProduct->fSlipstream = TRUE; + pSlipstreamMsp->dwMsiChainedPatchIndex = dwChainedPatchIndex; + break; + } + } + + break; + } + } + +LExit: + return hr; +} + +static HRESULT PlanTargetProduct( + __in BOOTSTRAPPER_DISPLAY display, + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fRollback, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_ACTION_STATE actionState, + __in BURN_PACKAGE* pPackage, + __in BURN_MSPTARGETPRODUCT* pTargetProduct, + __in_opt HANDLE hCacheEvent + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* rgActions = fRollback ? pPlan->rgRollbackActions : pPlan->rgExecuteActions; + DWORD cActions = fRollback ? pPlan->cRollbackActions : pPlan->cExecuteActions; + BURN_EXECUTE_ACTION* pAction = NULL; + DWORD dwInsertSequence = 0; + + // Try to find another MSP action with the exact same action (install or uninstall) targeting + // the same product in the same machine context (per-user or per-machine). + for (DWORD i = 0; i < cActions; ++i) + { + pAction = rgActions + i; + + if (BURN_EXECUTE_ACTION_TYPE_MSP_TARGET == pAction->type && + pAction->mspTarget.action == actionState && + pAction->mspTarget.fPerMachineTarget == (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context) && + CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pAction->mspTarget.sczTargetProductCode, -1, pTargetProduct->wzTargetProductCode, -1)) + { + dwInsertSequence = i; + break; + } + + pAction = NULL; + } + + // If we didn't find an MSP target action already updating the product, create a new action. + if (!pAction) + { + if (fRollback) + { + hr = PlanAppendRollbackAction(pPlan, &pAction); + } + else + { + hr = PlanAppendExecuteAction(pPlan, &pAction); + } + ExitOnFailure(hr, "Failed to plan action for target product."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_MSP_TARGET; + pAction->mspTarget.action = actionState; + pAction->mspTarget.pPackage = pPackage; + pAction->mspTarget.fPerMachineTarget = (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context); + pAction->mspTarget.pChainedTargetPackage = pTargetProduct->pChainedTargetPackage; + pAction->mspTarget.fSlipstream = pTargetProduct->fSlipstream; + hr = StrAllocString(&pAction->mspTarget.sczTargetProductCode, pTargetProduct->wzTargetProductCode, 0); + ExitOnFailure(hr, "Failed to copy target product code."); + + hr = MsiEngineCalculateInstallUiLevel(display, pUserExperience, pPackage->sczId, !fRollback, pAction->mspTarget.action, + &pAction->mspTarget.actionMsiProperty, &pAction->mspTarget.uiLevel, &pAction->mspTarget.fDisableExternalUiHandler); + ExitOnFailure(hr, "Failed to get msp ui options."); + + // If this is a per-machine target product, then the plan needs to be per-machine as well. + if (pAction->mspTarget.fPerMachineTarget) + { + pPlan->fPerMachine = TRUE; + } + + LoggingSetPackageVariable(pPackage, pAction->mspTarget.sczTargetProductCode, fRollback, pLog, pVariables, &pAction->mspTarget.sczLogPath); // ignore errors. + } + else + { + if (!fRollback && hCacheEvent) + { + // Since a previouse MSP target action is being updated with the new MSP, + // insert a wait syncpoint to before this action since we need to cache the current MSI before using it. + BURN_EXECUTE_ACTION* pWaitSyncPointAction = NULL; + hr = PlanInsertExecuteAction(dwInsertSequence, pPlan, &pWaitSyncPointAction); + ExitOnFailure(hr, "Failed to insert execute action."); + + pWaitSyncPointAction->type = BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT; + pWaitSyncPointAction->syncpoint.hEvent = hCacheEvent; + + // Since we inserted an action before the MSP target action that we will be updating, need to update the pointer. + pAction = pPlan->rgExecuteActions + (dwInsertSequence + 1); + } + } + + // Add our target product to the array and sort based on their order determined during detection. + hr = MemEnsureArraySize(reinterpret_cast(&pAction->mspTarget.rgOrderedPatches), pAction->mspTarget.cOrderedPatches + 1, sizeof(BURN_ORDERED_PATCHES), 2); + ExitOnFailure(hr, "Failed grow array of ordered patches."); + + pAction->mspTarget.rgOrderedPatches[pAction->mspTarget.cOrderedPatches].pTargetProduct = pTargetProduct; + pAction->mspTarget.rgOrderedPatches[pAction->mspTarget.cOrderedPatches].pPackage = pPackage; + ++pAction->mspTarget.cOrderedPatches; + + // Insertion sort to keep the patches ordered. + for (DWORD i = pAction->mspTarget.cOrderedPatches - 1; i > 0; --i) + { + if (pAction->mspTarget.rgOrderedPatches[i].pTargetProduct->dwOrder < pAction->mspTarget.rgOrderedPatches[i - 1].pTargetProduct->dwOrder) + { + BURN_ORDERED_PATCHES temp = pAction->mspTarget.rgOrderedPatches[i - 1]; + pAction->mspTarget.rgOrderedPatches[i - 1] = pAction->mspTarget.rgOrderedPatches[i]; + pAction->mspTarget.rgOrderedPatches[i] = temp; + } + else // no swap necessary, we're done. + { + break; + } + } + +LExit: + return hr; +} diff --git a/src/burn/engine/mspengine.h b/src/burn/engine/mspengine.h new file mode 100644 index 00000000..79998030 --- /dev/null +++ b/src/burn/engine/mspengine.h @@ -0,0 +1,84 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + + +// structures + + +// typedefs + + +// function declarations + +HRESULT MspEngineParsePackageFromXml( + __in IXMLDOMNode* pixnBundle, + __in BURN_PACKAGE* pPackage + ); +void MspEnginePackageUninitialize( + __in BURN_PACKAGE* pPackage + ); +HRESULT MspEngineDetectInitialize( + __in BURN_PACKAGES* pPackages + ); +HRESULT MspEngineAddDetectedTargetProduct( + __in BURN_PACKAGES* pPackages, + __in BURN_PACKAGE* pPackage, + __in DWORD dwOrder, + __in_z LPCWSTR wzProductCode, + __in MSIINSTALLCONTEXT context + ); +HRESULT MspEngineAddMissingSlipstreamTarget( + __in BURN_PACKAGE* pMsiPackage, + __in BURN_SLIPSTREAM_MSP* pSlipstreamMsp + ); +HRESULT MspEngineDetectPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_USER_EXPERIENCE* pUserExperience + ); +HRESULT MspEnginePlanInitializePackage( + __in BURN_PACKAGE* pPackage, + __in BURN_USER_EXPERIENCE* pUserExperience + ); +HRESULT MspEnginePlanCalculatePackage( + __in BURN_PACKAGE* pPackage, + __in BOOL fInsideMsiTransaction + ); +HRESULT MspEnginePlanAddPackage( + __in BOOTSTRAPPER_DISPLAY display, + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in_opt HANDLE hCacheEvent + ); +HRESULT MspEngineExecutePackage( + __in_opt HWND hwndParent, + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +void MspEngineUpdateInstallRegistrationState( + __in BURN_EXECUTE_ACTION* pAction, + __in HRESULT hrExecute, + __in BOOL fInsideMsiTransaction + ); +void MspEngineFinalizeInstallRegistrationState( + __in BURN_PACKAGE* pPackage + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/msuengine.cpp b/src/burn/engine/msuengine.cpp new file mode 100644 index 00000000..6003123b --- /dev/null +++ b/src/burn/engine/msuengine.cpp @@ -0,0 +1,529 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// constants + +#define WU_S_REBOOT_REQUIRED 0x00240005L +#define WU_S_ALREADY_INSTALLED 0x00240006L + + +// function definitions +static HRESULT EnsureWUServiceEnabled( + __in BOOL fStopWusaService, + __out SC_HANDLE* pschWu, + __out BOOL* pfPreviouslyDisabled + ); +static HRESULT SetServiceStartType( + __in SC_HANDLE sch, + __in DWORD stratType + ); +static HRESULT StopWUService( + __in SC_HANDLE schWu + ); + + +extern "C" HRESULT MsuEngineParsePackageFromXml( + __in IXMLDOMNode* pixnMsuPackage, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + + // @KB + hr = XmlGetAttributeEx(pixnMsuPackage, L"KB", &pPackage->Msu.sczKB); + ExitOnFailure(hr, "Failed to get @KB."); + + // @DetectCondition + hr = XmlGetAttributeEx(pixnMsuPackage, L"DetectCondition", &pPackage->Msu.sczDetectCondition); + ExitOnFailure(hr, "Failed to get @DetectCondition."); + +LExit: + return hr; +} + +extern "C" void MsuEnginePackageUninitialize( + __in BURN_PACKAGE* pPackage + ) +{ + ReleaseNullStr(pPackage->Msu.sczKB); + ReleaseNullStr(pPackage->Msu.sczDetectCondition); +} + +extern "C" HRESULT MsuEngineDetectPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + BOOL fDetected = FALSE; + + // evaluate detect condition + if (pPackage->Msu.sczDetectCondition && *pPackage->Msu.sczDetectCondition) + { + hr = ConditionEvaluate(pVariables, pPackage->Msu.sczDetectCondition, &fDetected); + ExitOnFailure(hr, "Failed to evaluate MSU package detect condition."); + } + + // update detect state + pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + + if (pPackage->fCanAffectRegistration) + { + pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + +LExit: + return hr; +} + +// +// PlanCalculate - calculates the execute and rollback state for the requested package state. +// +extern "C" HRESULT MsuEnginePlanCalculatePackage( + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; + BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + BOOL fAllowUninstall = FALSE; + + // We can only uninstall MSU packages if they have a KB and we are on Win7 or newer. + fAllowUninstall = pPackage->Msu.sczKB && *pPackage->Msu.sczKB && ::IsWindows7OrGreater(); + + // execute action + switch (pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + + case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_CACHE: + execute = fAllowUninstall && pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; + break; + + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: + execute = fAllowUninstall ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; + break; + + default: + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; + break; + + default: + execute = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid package state."); + } + + // Calculate the rollback action if there is an execute action. + if (BOOTSTRAPPER_ACTION_STATE_NONE != execute) + { + switch (pPackage->currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_ABSENT: + rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL; + break; + + default: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: + switch (pPackage->requested) + { + case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_REPAIR: + rollback = fAllowUninstall ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; + break; + + default: + rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + break; + } + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid package expected state."); + } + } + + // return values + pPackage->execute = execute; + pPackage->rollback = rollback; + +LExit: + return hr; +} + +// +// PlanAdd - adds the calculated execute and rollback actions for the package. +// +extern "C" HRESULT MsuEnginePlanAddPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in HANDLE hCacheEvent + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* pAction = NULL; + + // add wait for cache + if (hCacheEvent) + { + hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent); + ExitOnFailure(hr, "Failed to plan package cache syncpoint"); + } + + hr = DependencyPlanPackage(NULL, pPackage, pPlan); + ExitOnFailure(hr, "Failed to plan package dependency actions."); + + // add execute action + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute) + { + hr = PlanAppendExecuteAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append execute action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE; + pAction->msuPackage.pPackage = pPackage; + pAction->msuPackage.action = pPackage->execute; + + LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors. + } + + // add rollback action + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback) + { + hr = PlanAppendRollbackAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append rollback action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE; + pAction->msuPackage.pPackage = pPackage; + pAction->msuPackage.action = pPackage->rollback; + + LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors. + } + +LExit: + return hr; +} + +extern "C" HRESULT MsuEngineExecutePackage( + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in BOOL fStopWusaService, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + HRESULT hr = S_OK; + int nResult = IDNOACTION; + LPWSTR sczCachedDirectory = NULL; + LPWSTR sczMsuPath = NULL; + LPWSTR sczWindowsPath = NULL; + LPWSTR sczSystemPath = NULL; + LPWSTR sczWusaPath = NULL; + LPWSTR sczCommand = NULL; + SC_HANDLE schWu = NULL; + BOOL fWuWasDisabled = FALSE; + STARTUPINFOW si = { }; + PROCESS_INFORMATION pi = { }; + GENERIC_EXECUTE_MESSAGE message = { }; + DWORD dwExitCode = 0; + BOOL fUseSysNativePath = FALSE; + BURN_PACKAGE* pPackage = pExecuteAction->msuPackage.pPackage; + BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload; + +#if !defined(_WIN64) + hr = ProcWow64(::GetCurrentProcess(), &fUseSysNativePath); + ExitOnFailure(hr, "Failed to determine WOW64 status."); +#endif + + *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; + + // get wusa.exe path + if (fUseSysNativePath) + { + hr = PathGetKnownFolder(CSIDL_WINDOWS, &sczWindowsPath); + ExitOnFailure(hr, "Failed to find Windows directory."); + + hr = PathConcat(sczWindowsPath, L"SysNative\\", &sczSystemPath); + ExitOnFailure(hr, "Failed to append SysNative directory."); + } + else + { + hr = PathGetKnownFolder(CSIDL_SYSTEM, &sczSystemPath); + ExitOnFailure(hr, "Failed to find System32 directory."); + } + + hr = PathConcat(sczSystemPath, L"wusa.exe", &sczWusaPath); + ExitOnFailure(hr, "Failed to allocate WUSA.exe path."); + + // build command + switch (pExecuteAction->msuPackage.action) + { + case BOOTSTRAPPER_ACTION_STATE_INSTALL: + // get cached MSU path + hr = CacheGetCompletedPath(TRUE, pPackage->sczCacheId, &sczCachedDirectory); + ExitOnFailure(hr, "Failed to get cached path for package: %ls", pPackage->sczId); + + // Best effort to set the execute package cache folder variable. + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE); + + hr = PathConcat(sczCachedDirectory, pPackagePayload->sczFilePath, &sczMsuPath); + ExitOnFailure(hr, "Failed to build MSU path."); + + // format command + hr = StrAllocFormatted(&sczCommand, L"\"%ls\" \"%ls\" /quiet /norestart", sczWusaPath, sczMsuPath); + ExitOnFailure(hr, "Failed to format MSU install command."); + break; + + case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: + // format command + hr = StrAllocFormatted(&sczCommand, L"\"%ls\" /uninstall /kb:%ls /quiet /norestart", sczWusaPath, pPackage->Msu.sczKB); + ExitOnFailure(hr, "Failed to format MSU uninstall command."); + break; + + default: + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Failed to get action arguments for MSU package."); + } + + if (pExecuteAction->msuPackage.sczLogPath && *pExecuteAction->msuPackage.sczLogPath) + { + hr = StrAllocConcat(&sczCommand, L" /log:", 0); + ExitOnFailure(hr, "Failed to append log switch to MSU command-line."); + + hr = StrAllocConcat(&sczCommand, pExecuteAction->msuPackage.sczLogPath, 0); + ExitOnFailure(hr, "Failed to append log path to MSU command-line."); + } + + LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, LoggingActionStateToString(pExecuteAction->msuPackage.action), sczMsuPath ? sczMsuPath : pPackage->Msu.sczKB, sczCommand); + + hr = EnsureWUServiceEnabled(fStopWusaService, &schWu, &fWuWasDisabled); + ExitOnFailure(hr, "Failed to ensure WU service was enabled to install MSU package."); + + // create process + si.cb = sizeof(si); + if (!::CreateProcessW(sczWusaPath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) + { + ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", sczWusaPath); + } + + do + { + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; + message.dwAllowedResults = MB_OKCANCEL; + message.progress.dwPercentage = 50; + nResult = pfnGenericMessageHandler(&message, pvContext); + hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); + ExitOnRootFailure(hr, "Bootstrapper application aborted during MSU progress."); + + // wait for process to terminate + hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode); + if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr) + { + ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", sczWusaPath); + } + } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr); + + // get process exit code + if (!::GetExitCodeProcess(pi.hProcess, &dwExitCode)) + { + ExitWithLastError(hr, "Failed to get process exit code."); + } + + // We'll normalize the restart required error code from wusa.exe just in case. Most likely + // that on reboot we'll actually get WU_S_REBOOT_REQUIRED. + if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast(dwExitCode)) + { + dwExitCode = ERROR_SUCCESS_REBOOT_REQUIRED; + } + + // handle exit code + switch (dwExitCode) + { + case S_OK: __fallthrough; + case S_FALSE: __fallthrough; + case WU_S_ALREADY_INSTALLED: + hr = S_OK; + break; + + case ERROR_SUCCESS_REBOOT_REQUIRED: __fallthrough; + case WU_S_REBOOT_REQUIRED: + *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; + hr = S_OK; + break; + + default: + hr = static_cast(dwExitCode); + break; + } + +LExit: + ReleaseStr(sczCachedDirectory); + ReleaseStr(sczMsuPath); + ReleaseStr(sczSystemPath); + ReleaseStr(sczWindowsPath); + ReleaseStr(sczWusaPath); + ReleaseStr(sczCommand); + + ReleaseHandle(pi.hProcess); + ReleaseHandle(pi.hThread); + + if (fWuWasDisabled) + { + SetServiceStartType(schWu, SERVICE_DISABLED); + } + + // Best effort to clear the execute package cache folder variable. + VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE); + + return hr; +} + +extern "C" void MsuEngineUpdateInstallRegistrationState( + __in BURN_EXECUTE_ACTION* pAction, + __in HRESULT hrExecute + ) +{ + BURN_PACKAGE* pPackage = pAction->msuPackage.pPackage; + + if (FAILED(hrExecute) || !pPackage->fCanAffectRegistration) + { + ExitFunction(); + } + + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->msuPackage.action) + { + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + else + { + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + +LExit: + return; +} + +static HRESULT EnsureWUServiceEnabled( + __in BOOL fStopWusaService, + __out SC_HANDLE* pschWu, + __out BOOL* pfPreviouslyDisabled + ) +{ + HRESULT hr = S_OK; + SC_HANDLE schSCM = NULL; + SC_HANDLE schWu = NULL; + SERVICE_STATUS serviceStatus = { }; + QUERY_SERVICE_CONFIGW* pConfig = NULL; + + schSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); + ExitOnNullWithLastError(schSCM, hr, "Failed to open service control manager."); + + schWu = ::OpenServiceW(schSCM, L"wuauserv", SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_STOP ); + ExitOnNullWithLastError(schWu, hr, "Failed to open WU service."); + + if (!::QueryServiceStatus(schWu, &serviceStatus) ) + { + ExitWithLastError(hr, "Failed to query status of WU service."); + } + + // Stop service if requested to. + if (SERVICE_STOPPED != serviceStatus.dwCurrentState && fStopWusaService) + { + hr = StopWUService(schWu); + } + + // If the service is not running then it might be disabled so let's check. + if (SERVICE_RUNNING != serviceStatus.dwCurrentState) + { + hr = SvcQueryConfig(schWu, &pConfig); + ExitOnFailure(hr, "Failed to read configuration for WU service."); + + // If WU is disabled, change it to a demand start service (but touch nothing else). + if (SERVICE_DISABLED == pConfig->dwStartType) + { + hr = SetServiceStartType(schWu, SERVICE_DEMAND_START); + ExitOnFailure(hr, "Failed to mark WU service to start on demand."); + + *pfPreviouslyDisabled = TRUE; + } + } + + *pschWu = schWu; + schWu = NULL; + +LExit: + ReleaseMem(pConfig); + ReleaseServiceHandle(schWu); + ReleaseServiceHandle(schSCM); + + return hr; +} + +static HRESULT SetServiceStartType( + __in SC_HANDLE sch, + __in DWORD startType + ) +{ + HRESULT hr = S_OK; + + if (!::ChangeServiceConfigW(sch, SERVICE_NO_CHANGE, startType, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) + { + ExitWithLastError(hr, "Failed to set service start type."); + } + +LExit: + return hr; +} + +static HRESULT StopWUService( + __in SC_HANDLE schWu + ) +{ + HRESULT hr = S_OK; + SERVICE_STATUS serviceStatus = { }; + + if(!::ControlService(schWu, SERVICE_CONTROL_STOP, &serviceStatus)) + { + ExitWithLastError(hr, "Failed to stop wusa service."); + } + +LExit: + return hr; +} diff --git a/src/burn/engine/msuengine.h b/src/burn/engine/msuengine.h new file mode 100644 index 00000000..fda7a5ab --- /dev/null +++ b/src/burn/engine/msuengine.h @@ -0,0 +1,50 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// function declarations + +HRESULT MsuEngineParsePackageFromXml( + __in IXMLDOMNode* pixnMsiPackage, + __in BURN_PACKAGE* pPackage + ); +void MsuEnginePackageUninitialize( + __in BURN_PACKAGE* pPackage + ); +HRESULT MsuEngineDetectPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_VARIABLES* pVariables + ); +HRESULT MsuEnginePlanCalculatePackage( + __in BURN_PACKAGE* pPackage + ); +HRESULT MsuEnginePlanAddPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in HANDLE hCacheEvent + ); +HRESULT MsuEngineExecutePackage( + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in BOOL fStopWusaService, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); +void MsuEngineUpdateInstallRegistrationState( + __in BURN_EXECUTE_ACTION* pAction, + __in HRESULT hrExecute + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/netfxchainer.cpp b/src/burn/engine/netfxchainer.cpp new file mode 100644 index 00000000..4e7a7720 --- /dev/null +++ b/src/burn/engine/netfxchainer.cpp @@ -0,0 +1,418 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static VOID DestroyNetFxChainer( + __in NetFxChainer* pChainer + ) +{ + if (pChainer) + { + ReleaseHandle(pChainer->hSection); + ReleaseHandle(pChainer->hEventChaineeSend); + ReleaseHandle(pChainer->hEventChainerSend); + ReleaseHandle(pChainer->hMutex); + + if (pChainer->pData) + { + ::UnmapViewOfFile(pChainer->pData); + } + + MemFree(pChainer); + } +} + +static HRESULT CreateNetFxChainer( + __in LPCWSTR wzSectionName, + __in LPCWSTR wzEventName, + __out NetFxChainer** ppChainer + ) +{ + HRESULT hr = S_OK; + LPWSTR sczName = NULL; + NetFxChainer* pChainer = NULL; + + pChainer = (NetFxChainer*)MemAlloc(sizeof(NetFxChainer), TRUE); + ExitOnNull(pChainer, hr, E_OUTOFMEMORY, "Failed to allocate memory for NetFxChainer struct."); + + pChainer->hEventChaineeSend = ::CreateEvent(NULL, FALSE, FALSE, wzEventName); + ExitOnNullWithLastError(pChainer->hEventChaineeSend, hr, "Failed to create event: %ls", wzEventName); + + hr = StrAllocFormatted(&sczName, L"%ls_send", wzEventName); + ExitOnFailure(hr, "failed to allocate memory for event name"); + + pChainer->hEventChainerSend = ::CreateEvent(NULL, FALSE, FALSE, sczName); + ExitOnNullWithLastError(pChainer->hEventChainerSend, hr, "Failed to create event: %ls", sczName); + + hr = StrAllocFormatted(&sczName, L"%ls_mutex", wzEventName); + ExitOnFailure(hr, "failed to allocate memory for mutex name"); + + // Create the mutex, we initially own + pChainer->hMutex = ::CreateMutex(NULL, TRUE, sczName); + ExitOnNullWithLastError(pChainer->hMutex, hr, "Failed to create mutex: %ls", sczName); + + pChainer->hSection = ::CreateFileMapping(INVALID_HANDLE_VALUE, + NULL, // security attributes + PAGE_READWRITE, + 0, // high-order DWORD of maximum size + NETFXDATA_SIZE, // low-order DWORD of maximum size + wzSectionName); + ExitOnNullWithLastError(pChainer->hSection, hr, "Failed to memory map cabinet file: %ls", wzSectionName); + + pChainer->pData = reinterpret_cast(::MapViewOfFile(pChainer->hSection, + FILE_MAP_WRITE, + 0, 0, // offsets + 0 // map entire file + )); + ExitOnNullWithLastError(pChainer->pData, hr, "Failed to MapViewOfFile for %ls.", wzSectionName); + + // Initialize the shared memory + hr = ::StringCchCopyW(pChainer->pData->szEventName, countof(pChainer->pData->szEventName), wzEventName); + ExitOnFailure(hr, "failed to copy event name to shared memory structure."); + pChainer->pData->downloadFinished = false; + pChainer->pData->downloadSoFar = 0; + pChainer->pData->hrDownloadFinished = E_PENDING; + pChainer->pData->downloadAbort = false; + pChainer->pData->installFinished = false; + pChainer->pData->installSoFar = 0; + pChainer->pData->hrInstallFinished = E_PENDING; + pChainer->pData->installAbort = false; + pChainer->pData->hrInternalError = S_OK; + pChainer->pData->version = NETFXDATA_VERSION; + pChainer->pData->messageCode = 0; + pChainer->pData->messageResponse = 0; + pChainer->pData->messageDataLength = 0; + + // Done with initialization, allow others to access. + ::ReleaseMutex(pChainer->hMutex); + + *ppChainer = pChainer; + pChainer = NULL; + +LExit: + ReleaseStr(sczName); + + if (pChainer) + { + // Something failed, release the mutex and destroy the object + if (pChainer->hMutex) + { + ::ReleaseMutex(pChainer->hMutex); + } + + DestroyNetFxChainer(pChainer); + } + + return hr; +} + + +static VOID NetFxAbort( + __in NetFxChainer* pChainer + ) +{ + ::WaitForSingleObject(pChainer->hMutex, INFINITE); + + pChainer->pData->downloadAbort = true; + pChainer->pData->installAbort = true; + + ::ReleaseMutex(pChainer->hMutex); + + ::SetEvent(pChainer->hEventChainerSend); +} + +static BYTE NetFxGetProgress( + __in NetFxChainer* pChainer + ) +{ + BYTE bProgress = 0; + ::WaitForSingleObject(pChainer->hMutex, INFINITE); + + bProgress = (pChainer->pData->installSoFar + pChainer->pData->downloadSoFar) / 2; + + ::ReleaseMutex(pChainer->hMutex); + + return bProgress; +} + +static HRESULT NetFxGetMessage( + __in NetFxChainer* pChainer, + __out DWORD* pdwMessage, + __out LPVOID* ppBuffer, + __out DWORD* pdwBufferSize + ) +{ + HRESULT hr = S_OK; + ::WaitForSingleObject(pChainer->hMutex, INFINITE); + + *pdwMessage = pChainer->pData->messageCode; + *ppBuffer = NULL; + *pdwBufferSize = 0; + + if (NETFX_NO_MESSAGE != *pdwMessage) + { + *ppBuffer = MemAlloc(pChainer->pData->messageDataLength, TRUE); + ExitOnNull(*ppBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for message data"); + + memcpy(*ppBuffer, pChainer->pData->messageData, pChainer->pData->messageDataLength); + *pdwBufferSize = pChainer->pData->messageDataLength; + } + +LExit: + ::ReleaseMutex(pChainer->hMutex); + + return hr; +} + +static void NetFxRespond( + __in NetFxChainer* pChainer, + __in DWORD dwResponse + ) +{ + ::WaitForSingleObject(pChainer->hMutex, INFINITE); + + pChainer->pData->messageCode = NETFX_NO_MESSAGE; + pChainer->pData->messageResponse = dwResponse; + if (IDCANCEL == dwResponse) + { + pChainer->pData->downloadAbort = true; + pChainer->pData->installAbort = true; + } + + ::ReleaseMutex(pChainer->hMutex); + + ::SetEvent(pChainer->hEventChainerSend); +} + +static HRESULT NetFxGetResult( + __in NetFxChainer* pChainer, + __out HRESULT* phrInternalError + ) +{ + HRESULT hr = S_OK; + ::WaitForSingleObject(pChainer->hMutex, INFINITE); + + hr = pChainer->pData->hrInstallFinished; + + if (FAILED(pChainer->pData->hrDownloadFinished) && // Download failed + (S_OK == hr || E_ABORT == hr)) // Install succeeded or was aborted + { + hr = pChainer->pData->hrDownloadFinished; + } + + if (phrInternalError) + { + *phrInternalError = pChainer->pData->hrInternalError; + } + + ::ReleaseMutex(pChainer->hMutex); + + return hr; +} + +static HRESULT OnNetFxFilesInUse( + __in NetFxChainer* pNetfxChainer, + __in NetFxCloseApplications* pCloseApps, + __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + DWORD cFiles = 0; + LPWSTR* rgwzFiles = NULL; + GENERIC_EXECUTE_MESSAGE message = { }; + DWORD dwResponse = 0; + + cFiles = pCloseApps->dwApplicationsSize; + rgwzFiles = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cFiles, TRUE); + ExitOnNull(rgwzFiles, hr, E_OUTOFMEMORY, "Failed to allocate buffer."); + + for (DWORD i = 0; i < pCloseApps->dwApplicationsSize; ++i) + { + rgwzFiles[i] = pCloseApps->applications[i].szName; + } + + // send message + message.type = GENERIC_EXECUTE_MESSAGE_FILES_IN_USE; + message.dwAllowedResults = MB_ABORTRETRYIGNORE; + message.filesInUse.cFiles = cFiles; + message.filesInUse.rgwzFiles = (LPCWSTR*)rgwzFiles; + dwResponse = (DWORD)pfnMessageHandler(&message, pvContext); + + NetFxRespond(pNetfxChainer, dwResponse); + +LExit: + ReleaseMem(rgwzFiles); + + return hr; +} + +static HRESULT OnNetFxProgress( + __in NetFxChainer* pNetfxChainer, + __in BYTE bProgress, + __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext + ) +{ + GENERIC_EXECUTE_MESSAGE message = { }; + DWORD dwResponse = 0; + + // send message + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; + message.dwAllowedResults = MB_OKCANCEL; + message.progress.dwPercentage = 100 * (DWORD)bProgress / BYTE_MAX; + dwResponse = (DWORD)pfnMessageHandler(&message, pvContext); + + if (IDCANCEL == dwResponse) + { + NetFxAbort(pNetfxChainer); + } + + return S_OK; +} + +static HRESULT OnNetFxError( + __in NetFxChainer* /*pNetfxChainer*/, + __in HRESULT hrError, + __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, + __in LPVOID pvContext + ) +{ + GENERIC_EXECUTE_MESSAGE message = { }; + DWORD dwResponse = 0; + + // send message + message.type = GENERIC_EXECUTE_MESSAGE_ERROR; + message.dwAllowedResults = MB_OK; + message.error.dwErrorCode = hrError; + message.error.wzMessage = NULL; + dwResponse = (DWORD)pfnMessageHandler(&message, pvContext); + + return S_OK; +} + +static HRESULT ProcessNetFxMessage( + __in NetFxChainer* pNetfxChainer, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + DWORD dwMessage = NETFX_NO_MESSAGE; + DWORD dwBufferSize = 0; + LPVOID pBuffer = NULL; + + // send progress + hr = OnNetFxProgress(pNetfxChainer, NetFxGetProgress(pNetfxChainer), pfnGenericMessageHandler, pvContext); + ExitOnFailure(hr, "Failed to send progress from netfx chainer."); + + // Check for message + hr = NetFxGetMessage(pNetfxChainer, &dwMessage, &pBuffer, &dwBufferSize); + ExitOnFailure(hr, "Failed to get message from netfx chainer."); + + switch(dwMessage) + { + case NETFX_CLOSE_APPS: + hr = OnNetFxFilesInUse(pNetfxChainer, (NetFxCloseApplications*)pBuffer, pfnGenericMessageHandler, pvContext); + ExitOnFailure(hr, "Failed to send files in use message from netfx chainer."); + break; + + default: + // No message we understand. + break; + } + +LExit: + ReleaseMem(pBuffer); + + return hr; +} + +extern "C" HRESULT NetFxRunChainer( + __in LPCWSTR wzExecutablePath, + __in LPCWSTR wzArguments, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __out DWORD* pdwExitCode + ) +{ + HRESULT hr = S_OK; + DWORD er = 0; + WCHAR wzGuid[GUID_STRING_LENGTH]; + LPWSTR sczEventName = NULL; + LPWSTR sczSectionName = NULL; + LPWSTR sczCommand = NULL; + NetFxChainer* pNetfxChainer = NULL; + STARTUPINFOW si = { }; + PROCESS_INFORMATION pi = { }; + HRESULT hrInternalError = 0; + + // Create the unique name suffix. + hr = GuidFixedCreate(wzGuid); + ExitOnRootFailure(hr, "Failed to create netfx chainer guid."); + + hr = StrAllocFormatted(&sczSectionName, L"NetFxSection.%ls", wzGuid); + ExitOnFailure(hr, "Failed to allocate section name."); + + hr = StrAllocFormatted(&sczEventName, L"NetFxEvent.%ls", wzGuid); + ExitOnFailure(hr, "Failed to allocate event name."); + + hr = CreateNetFxChainer(sczSectionName, sczEventName, &pNetfxChainer); + ExitOnFailure(hr, "Failed to create netfx chainer."); + + hr = StrAllocFormattedSecure(&sczCommand, L"%ls /pipe %ls", wzArguments, sczSectionName); + ExitOnFailure(hr, "Failed to allocate netfx chainer arguments."); + + si.cb = sizeof(si); + if (!::CreateProcessW(wzExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) + { + ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", wzExecutablePath); + } + + HANDLE handles[2] = { pi.hProcess, pNetfxChainer->hEventChaineeSend }; + + for (;;) + { + er = ::WaitForMultipleObjects(2, handles, FALSE, 100); + if (WAIT_OBJECT_0 == er) + { + // Process has exited + *pdwExitCode = NetFxGetResult(pNetfxChainer, &hrInternalError); + if (E_PENDING == *pdwExitCode) + { + if (!::GetExitCodeProcess(pi.hProcess, pdwExitCode)) + { + ExitWithLastError(hr, "Failed to get netfx return code."); + } + } + else if (FAILED(hrInternalError)) + { + // push internal error message + OnNetFxError(pNetfxChainer, hrInternalError, pfnGenericMessageHandler, pvContext); + ExitOnFailure(hr, "Failed to send internal error message from netfx chainer."); + } + + break; + } + else if (WAIT_OBJECT_0 + 1 == er) + { + // Chainee has notified us of a change. + hr = ProcessNetFxMessage(pNetfxChainer, pfnGenericMessageHandler, pvContext); + ExitOnFailure(hr, "Failed to process netfx chainer message."); + } + else if (WAIT_FAILED == er) + { + ExitWithLastError(hr, "Failed to wait for netfx chainer process to complete"); + } + } + +LExit: + ReleaseStr(sczSectionName); + ReleaseStr(sczEventName); + StrSecureZeroFreeString(sczCommand); + DestroyNetFxChainer(pNetfxChainer); + ReleaseHandle(pi.hThread); + ReleaseHandle(pi.hProcess); + + return hr; +} diff --git a/src/burn/engine/netfxchainer.h b/src/burn/engine/netfxchainer.h new file mode 100644 index 00000000..7d3aff1c --- /dev/null +++ b/src/burn/engine/netfxchainer.h @@ -0,0 +1,98 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +struct NetFxDataStructure +{ + bool downloadFinished; // download done yet? + bool installFinished; // install done yet? + bool downloadAbort; // set downloader to abort + bool installAbort; // set installer to abort + HRESULT hrDownloadFinished; // resultant HRESULT for download + HRESULT hrInstallFinished; // resultant HRESULT for install + HRESULT hrInternalError; + WCHAR szCurrentItemStep[MAX_PATH]; + BYTE downloadSoFar; // download progress 0 - 255 (0 to 100% done) + BYTE installSoFar; // install progress 0 - 255 (0 to 100% done) + WCHAR szEventName[MAX_PATH]; // event that chainer 'creates' and chainee 'opens'to sync communications + + BYTE version; // version of the data structure, set by chainer. + + DWORD messageCode; // current message being sent by the chainee, 0 if no message is active + DWORD messageResponse; // chainer's response to current message, 0 if not yet handled + DWORD messageDataLength; // length of the m_messageData field in bytes + BYTE messageData[1]; // variable length buffer, content depends on m_messageCode +}; + +struct NetFxChainer +{ + HANDLE hSection; + + HANDLE hEventChaineeSend; + HANDLE hEventChainerSend; + HANDLE hMutex; + + NetFxDataStructure* pData; + DWORD dwDataSize; +}; + +#define NETFXDATA_SIZE 65536 + +#define NETFXDATA_VERSION 1 + +#define NETFX_MESSAGE(version, defaultResponse, messageCode) \ + ((((DWORD)version & 0xFF) << 24) | (((DWORD)defaultResponse & 0xFF) << 16) | ((DWORD)messageCode & 0xFFFF)) +#define NETFX_MESSAGE_CODE(messageId) \ + (messageId & 0xFFFF) +#define NETFX_MESSAGE_DEFAULT_RESPONSE(messageId) \ + ((messageId >> 16) & 0xFF) +#define NETFX_MESSAGE_VERSION(messageId) \ + ((messageId >>24) & 0xFF) + +#define NETFX_NO_MESSAGE 0 + + +//------------------------------------------------------------------------------ +// NETFX_CLOSE_APPS +// +// Sent by the chainee when it detects that applications are holding files in +// use. Respond to this message in order to tell the chainee to close the +// applications to prevent a reboot. +// +// pData : NetFxCloseApplications : The list of applications +// Acceptable responses: +// IDYES : Indicates that the chainee should attempt to shutdown the apps. +// If all apps do not successfully close the message may be sent again. +// IDNO : Indicates that the chainee should not attempt to close apps. +// IDRETRY : Indicates that the chainee should refresh the list of apps. +// Another NETFX_CLOSE_APPS message will be sent asynchronously with +// the new list of apps. +//------------------------------------------------------------------------------ +#define NETFX_CLOSE_APPS NETFX_MESSAGE(NETFXDATA_VERSION, IDNO, 1) + +struct NetFxApplication +{ + WCHAR szName[MAX_PATH]; + DWORD dwPid; +}; + +struct NetFxCloseApplications +{ + DWORD dwApplicationsSize; + NetFxApplication applications[1]; +}; + +HRESULT NetFxRunChainer( + __in LPCWSTR wzExecutablePath, + __in LPCWSTR wzArguments, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __out DWORD* pdwExitCode + ); +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/package.cpp b/src/burn/engine/package.cpp new file mode 100644 index 00000000..3f8c8b0f --- /dev/null +++ b/src/burn/engine/package.cpp @@ -0,0 +1,692 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// internal function declarations + +static HRESULT ParsePayloadRefsFromXml( + __in BURN_PACKAGE* pPackage, + __in BURN_PAYLOADS* pPayloads, + __in IXMLDOMNode* pixnPackage + ); +static HRESULT ParsePatchTargetCode( + __in BURN_PACKAGES* pPackages, + __in IXMLDOMNode* pixnBundle + ); +static HRESULT FindRollbackBoundaryById( + __in BURN_PACKAGES* pPackages, + __in_z LPCWSTR wzId, + __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary + ); + + +// function definitions + +extern "C" HRESULT PackagesParseFromXml( + __in BURN_PACKAGES* pPackages, + __in BURN_PAYLOADS* pPayloads, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + BSTR bstrNodeName = NULL; + DWORD cMspPackages = 0; + LPWSTR scz = NULL; + + // select rollback boundary nodes + hr = XmlSelectNodes(pixnBundle, L"RollbackBoundary", &pixnNodes); + ExitOnFailure(hr, "Failed to select rollback boundary nodes."); + + // get rollback boundary node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get rollback bundary node count."); + + if (cNodes) + { + // allocate memory for rollback boundaries + pPackages->rgRollbackBoundaries = (BURN_ROLLBACK_BOUNDARY*)MemAlloc(sizeof(BURN_ROLLBACK_BOUNDARY) * cNodes, TRUE); + ExitOnNull(pPackages->rgRollbackBoundaries, hr, E_OUTOFMEMORY, "Failed to allocate memory for rollback boundary structs."); + + pPackages->cRollbackBoundaries = cNodes; + + // parse rollback boundary elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = &pPackages->rgRollbackBoundaries[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &pRollbackBoundary->sczId); + ExitOnFailure(hr, "Failed to get @Id."); + + // @Vital + hr = XmlGetYesNoAttribute(pixnNode, L"Vital", &pRollbackBoundary->fVital); + ExitOnFailure(hr, "Failed to get @Vital."); + + // @Transaction + hr = XmlGetYesNoAttribute(pixnNode, L"Transaction", &pRollbackBoundary->fTransaction); + ExitOnFailure(hr, "Failed to get @Transaction."); + + // prepare next iteration + ReleaseNullObject(pixnNode); + ReleaseNullBSTR(bstrNodeName); + } + } + + ReleaseNullObject(pixnNodes); // done with the RollbackBoundary elements. + + // select package nodes + hr = XmlSelectNodes(pixnBundle, L"Chain/ExePackage|Chain/MsiPackage|Chain/MspPackage|Chain/MsuPackage", &pixnNodes); + ExitOnFailure(hr, "Failed to select package nodes."); + + // get package node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get package node count."); + + if (!cNodes) + { + ExitFunction1(hr = S_OK); + } + + // allocate memory for packages + pPackages->rgPackages = (BURN_PACKAGE*)MemAlloc(sizeof(BURN_PACKAGE) * cNodes, TRUE); + ExitOnNull(pPackages->rgPackages, hr, E_OUTOFMEMORY, "Failed to allocate memory for package structs."); + + pPackages->cPackages = cNodes; + + // parse package elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_PACKAGE* pPackage = &pPackages->rgPackages[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &pPackage->sczId); + ExitOnFailure(hr, "Failed to get @Id."); + + // @Cache + hr = XmlGetAttributeEx(pixnNode, L"Cache", &scz); + if (SUCCEEDED(hr)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"remove", -1)) + { + pPackage->authoredCacheType = BOOTSTRAPPER_CACHE_TYPE_REMOVE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"keep", -1)) + { + pPackage->authoredCacheType = BOOTSTRAPPER_CACHE_TYPE_KEEP; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"force", -1)) + { + pPackage->authoredCacheType = BOOTSTRAPPER_CACHE_TYPE_FORCE; + } + else + { + hr = E_UNEXPECTED; + ExitOnRootFailure(hr, "Invalid cache type: %ls", scz); + } + } + ExitOnFailure(hr, "Failed to get @Cache."); + + // @CacheId + hr = XmlGetAttributeEx(pixnNode, L"CacheId", &pPackage->sczCacheId); + ExitOnFailure(hr, "Failed to get @CacheId."); + + // @Size + hr = XmlGetAttributeLargeNumber(pixnNode, L"Size", &pPackage->qwSize); + ExitOnFailure(hr, "Failed to get @Size."); + + // @InstallSize + hr = XmlGetAttributeLargeNumber(pixnNode, L"InstallSize", &pPackage->qwInstallSize); + ExitOnFailure(hr, "Failed to get @InstallSize."); + + // @PerMachine + hr = XmlGetYesNoAttribute(pixnNode, L"PerMachine", &pPackage->fPerMachine); + ExitOnFailure(hr, "Failed to get @PerMachine."); + + // @Permanent + hr = XmlGetYesNoAttribute(pixnNode, L"Permanent", &pPackage->fUninstallable); + ExitOnFailure(hr, "Failed to get @Permanent."); + pPackage->fUninstallable = !pPackage->fUninstallable; // TODO: change "Uninstallable" variable name to permanent, until then Uninstallable is the opposite of Permanent so fix the variable. + pPackage->fCanAffectRegistration = pPackage->fUninstallable; + + // @Vital + hr = XmlGetYesNoAttribute(pixnNode, L"Vital", &pPackage->fVital); + ExitOnFailure(hr, "Failed to get @Vital."); + + // @LogPathVariable + hr = XmlGetAttributeEx(pixnNode, L"LogPathVariable", &pPackage->sczLogPathVariable); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @LogPathVariable."); + } + + // @RollbackLogPathVariable + hr = XmlGetAttributeEx(pixnNode, L"RollbackLogPathVariable", &pPackage->sczRollbackLogPathVariable); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @RollbackLogPathVariable."); + } + + // @InstallCondition + hr = XmlGetAttributeEx(pixnNode, L"InstallCondition", &pPackage->sczInstallCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @InstallCondition."); + } + + // @RollbackBoundaryForward + hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryForward", &scz); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @RollbackBoundaryForward."); + + hr = FindRollbackBoundaryById(pPackages, scz, &pPackage->pRollbackBoundaryForward); + ExitOnFailure(hr, "Failed to find forward transaction boundary: %ls", scz); + } + + // @RollbackBoundaryBackward + hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryBackward", &scz); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @RollbackBoundaryBackward."); + + hr = FindRollbackBoundaryById(pPackages, scz, &pPackage->pRollbackBoundaryBackward); + ExitOnFailure(hr, "Failed to find backward transaction boundary: %ls", scz); + } + + // read type specific attributes + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"ExePackage", -1)) + { + pPackage->type = BURN_PACKAGE_TYPE_EXE; + + hr = ExeEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization + ExitOnFailure(hr, "Failed to parse EXE package."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiPackage", -1)) + { + pPackage->type = BURN_PACKAGE_TYPE_MSI; + + hr = MsiEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization + ExitOnFailure(hr, "Failed to parse MSI package."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MspPackage", -1)) + { + pPackage->type = BURN_PACKAGE_TYPE_MSP; + + hr = MspEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization + ExitOnFailure(hr, "Failed to parse MSP package."); + + ++cMspPackages; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsuPackage", -1)) + { + pPackage->type = BURN_PACKAGE_TYPE_MSU; + + hr = MsuEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization + ExitOnFailure(hr, "Failed to parse MSU package."); + } + else + { + // ignore other package types for now + } + + // parse payload references + hr = ParsePayloadRefsFromXml(pPackage, pPayloads, pixnNode); + ExitOnFailure(hr, "Failed to parse payload references."); + + // parse dependency providers + hr = DependencyParseProvidersFromXml(pPackage, pixnNode); + ExitOnFailure(hr, "Failed to parse dependency providers."); + + // prepare next iteration + ReleaseNullObject(pixnNode); + ReleaseNullBSTR(bstrNodeName); + } + + if (cMspPackages) + { + pPackages->rgPatchInfo = static_cast(MemAlloc(sizeof(MSIPATCHSEQUENCEINFOW) * cMspPackages, TRUE)); + ExitOnNull(pPackages->rgPatchInfo, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSP patch sequence information."); + + pPackages->rgPatchInfoToPackage = static_cast(MemAlloc(sizeof(BURN_PACKAGE*) * cMspPackages, TRUE)); + ExitOnNull(pPackages->rgPatchInfoToPackage, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch sequence information to package lookup."); + + for (DWORD i = 0; i < pPackages->cPackages; ++i) + { + BURN_PACKAGE* pPackage = &pPackages->rgPackages[i]; + + if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + pPackages->rgPatchInfo[pPackages->cPatchInfo].szPatchData = pPackage->Msp.sczApplicabilityXml; + pPackages->rgPatchInfo[pPackages->cPatchInfo].ePatchDataType = MSIPATCH_DATATYPE_XMLBLOB; + pPackages->rgPatchInfoToPackage[pPackages->cPatchInfo] = pPackage; + ++pPackages->cPatchInfo; + + // Loop through all MSI packages seeing if any of them slipstream this MSP. + for (DWORD j = 0; j < pPackages->cPackages; ++j) + { + BURN_PACKAGE* pMsiPackage = &pPackages->rgPackages[j]; + + if (BURN_PACKAGE_TYPE_MSI == pMsiPackage->type) + { + for (DWORD k = 0; k < pMsiPackage->Msi.cSlipstreamMspPackages; ++k) + { + if (pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k] && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k], -1)) + { + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pMsiPackage->Msi.rgSlipstreamMsps + k; + pSlipstreamMsp->pMspPackage = pPackage; + pSlipstreamMsp->dwMsiChainedPatchIndex = BURN_PACKAGE_INVALID_PATCH_INDEX; + + ReleaseNullStr(pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k]); // we don't need the slipstream package id any longer so free it. + } + } + } + } + } + } + } + + AssertSz(pPackages->cPatchInfo == cMspPackages, "Count of packages patch info should be equal to the number of MSP packages."); + +#if DEBUG + // Loop through all MSI packages seeing if any of them are missing their slipstream MSP. + for (DWORD i = 0; i < pPackages->cPackages; ++i) + { + BURN_PACKAGE* pPackage = &pPackages->rgPackages[i]; + + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + { + for (DWORD k = 0; k < pPackage->Msi.cSlipstreamMspPackages; ++k) + { + if (pPackage->Msi.rgsczSlipstreamMspPackageIds[k]) + { + AssertSz(FALSE, "MSI slipstream MSP package doesn't exist."); + } + } + } + } +#endif + + hr = ParsePatchTargetCode(pPackages, pixnBundle); + ExitOnFailure(hr, "Failed to parse target product codes."); + + hr = S_OK; + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseBSTR(bstrNodeName); + ReleaseStr(scz); + + return hr; +} + +extern "C" void PackageUninitialize( + __in BURN_PACKAGE* pPackage + ) +{ + ReleaseStr(pPackage->sczId); + ReleaseStr(pPackage->sczLogPathVariable); + ReleaseStr(pPackage->sczRollbackLogPathVariable); + ReleaseStr(pPackage->sczInstallCondition); + ReleaseStr(pPackage->sczCacheId); + + if (pPackage->rgDependencyProviders) + { + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + DependencyUninitializeProvider(pPackage->rgDependencyProviders + i); + } + MemFree(pPackage->rgDependencyProviders); + } + + ReleaseMem(pPackage->payloads.rgItems); + + switch (pPackage->type) + { + case BURN_PACKAGE_TYPE_EXE: + ExeEnginePackageUninitialize(pPackage); // TODO: Modularization + break; + case BURN_PACKAGE_TYPE_MSI: + MsiEnginePackageUninitialize(pPackage); // TODO: Modularization + break; + case BURN_PACKAGE_TYPE_MSP: + MspEnginePackageUninitialize(pPackage); // TODO: Modularization + break; + case BURN_PACKAGE_TYPE_MSU: + MsuEnginePackageUninitialize(pPackage); // TODO: Modularization + break; + } +} + +extern "C" void PackagesUninitialize( + __in BURN_PACKAGES* pPackages + ) +{ + if (pPackages->rgRollbackBoundaries) + { + for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i) + { + ReleaseStr(pPackages->rgRollbackBoundaries[i].sczId); + ReleaseStr(pPackages->rgRollbackBoundaries[i].sczLogPath); + } + MemFree(pPackages->rgRollbackBoundaries); + } + + if (pPackages->rgPackages) + { + for (DWORD i = 0; i < pPackages->cPackages; ++i) + { + PackageUninitialize(pPackages->rgPackages + i); + } + MemFree(pPackages->rgPackages); + } + + if (pPackages->rgPatchTargetCodes) + { + for (DWORD i = 0; i < pPackages->cPatchTargetCodes; ++i) + { + ReleaseStr(pPackages->rgPatchTargetCodes[i].sczTargetCode); + } + MemFree(pPackages->rgPatchTargetCodes); + } + + ReleaseMem(pPackages->rgPatchInfo); + ReleaseMem(pPackages->rgPatchInfoToPackage); + + // clear struct + memset(pPackages, 0, sizeof(BURN_PACKAGES)); +} + +extern "C" HRESULT PackageFindById( + __in BURN_PACKAGES* pPackages, + __in_z LPCWSTR wzId, + __out BURN_PACKAGE** ppPackage + ) +{ + HRESULT hr = S_OK; + BURN_PACKAGE* pPackage = NULL; + + for (DWORD i = 0; i < pPackages->cPackages; ++i) + { + pPackage = &pPackages->rgPackages[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, wzId, -1)) + { + *ppPackage = pPackage; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + + +extern "C" HRESULT PackageFindRelatedById( + __in BURN_RELATED_BUNDLES* pRelatedBundles, + __in_z LPCWSTR wzId, + __out BURN_PACKAGE** ppPackage + ) +{ + HRESULT hr = S_OK; + BURN_PACKAGE* pPackage = NULL; + + for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i) + { + pPackage = &pRelatedBundles->rgRelatedBundles[i].package; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, wzId, -1)) + { + *ppPackage = pPackage; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + +/******************************************************************** + PackageGetProperty - Determines if the property is defined + and optionally copies the property value. + + Note: The caller must free psczValue if requested. + + Note: Returns E_NOTFOUND if the property was not defined or if the + package does not support properties. + +*********************************************************************/ +extern "C" HRESULT PackageGetProperty( + __in const BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzProperty, + __out_z_opt LPWSTR* psczValue + ) +{ + HRESULT hr = E_NOTFOUND; + BURN_MSIPROPERTY* rgProperties = NULL; + DWORD cProperties = 0; + + // For MSIs and MSPs, enumerate the properties looking for wzProperty. + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + { + rgProperties = pPackage->Msi.rgProperties; + cProperties = pPackage->Msi.cProperties; + } + else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + rgProperties = pPackage->Msp.rgProperties; + cProperties = pPackage->Msp.cProperties; + } + + for (DWORD i = 0; i < cProperties; ++i) + { + const BURN_MSIPROPERTY* pProperty = &rgProperties[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pProperty->sczId, -1, wzProperty, -1)) + { + if (psczValue) + { + hr = StrAllocString(psczValue, pProperty->sczValue, 0); + ExitOnFailure(hr, "Failed to copy the property value."); + } + + ExitFunction1(hr = S_OK); + } + } + +LExit: + return hr; +} + +extern "C" HRESULT PackageFindRollbackBoundaryById( + __in BURN_PACKAGES* pPackages, + __in_z LPCWSTR wzId, + __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary + ) +{ + HRESULT hr = S_OK; + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; + + for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i) + { + pRollbackBoundary = &pPackages->rgRollbackBoundaries[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pRollbackBoundary->sczId, -1, wzId, -1)) + { + *ppRollbackBoundary = pRollbackBoundary; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + + +// internal function declarations + +static HRESULT ParsePayloadRefsFromXml( + __in BURN_PACKAGE* pPackage, + __in BURN_PAYLOADS* pPayloads, + __in IXMLDOMNode* pixnPackage + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + LPWSTR sczId = NULL; + + // select package nodes + hr = XmlSelectNodes(pixnPackage, L"PayloadRef", &pixnNodes); + ExitOnFailure(hr, "Failed to select package nodes."); + + // get package node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get package node count."); + + if (!cNodes) + { + ExitFunction1(hr = S_OK); + } + + // allocate memory for payload pointers + pPackage->payloads.rgItems = (BURN_PAYLOAD_GROUP_ITEM*)MemAlloc(sizeof(BURN_PAYLOAD_GROUP_ITEM) * cNodes, TRUE); + ExitOnNull(pPackage->payloads.rgItems, hr, E_OUTOFMEMORY, "Failed to allocate memory for package payloads."); + + pPackage->payloads.cItems = cNodes; + + // parse package elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_PAYLOAD_GROUP_ITEM* pPackagePayload = pPackage->payloads.rgItems + i; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &sczId); + ExitOnFailure(hr, "Failed to get Id attribute."); + + // find payload + hr = PayloadFindById(pPayloads, sczId, &pPackagePayload->pPayload); + ExitOnFailure(hr, "Failed to find payload."); + + pPackage->payloads.qwTotalSize += pPackagePayload->pPayload->qwFileSize; + + // prepare next iteration + ReleaseNullObject(pixnNode); + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseStr(sczId); + + return hr; +} + +static HRESULT ParsePatchTargetCode( + __in BURN_PACKAGES* pPackages, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + BSTR bstrNodeText = NULL; + BOOL fProduct; + + hr = XmlSelectNodes(pixnBundle, L"PatchTargetCode", &pixnNodes); + ExitOnFailure(hr, "Failed to select PatchTargetCode nodes."); + + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get PatchTargetCode node count."); + + if (!cNodes) + { + ExitFunction1(hr = S_OK); + } + + pPackages->rgPatchTargetCodes = (BURN_PATCH_TARGETCODE*)MemAlloc(sizeof(BURN_PATCH_TARGETCODE) * cNodes, TRUE); + ExitOnNull(pPackages->rgPatchTargetCodes, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch targetcodes."); + + pPackages->cPatchTargetCodes = cNodes; + + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_PATCH_TARGETCODE* pTargetCode = pPackages->rgPatchTargetCodes + i; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + hr = XmlGetAttributeEx(pixnNode, L"TargetCode", &pTargetCode->sczTargetCode); + ExitOnFailure(hr, "Failed to get @TargetCode attribute."); + + hr = XmlGetYesNoAttribute(pixnNode, L"Product", &fProduct); + if (E_NOTFOUND == hr) + { + fProduct = FALSE; + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get @Product."); + + pTargetCode->type = fProduct ? BURN_PATCH_TARGETCODE_TYPE_PRODUCT : BURN_PATCH_TARGETCODE_TYPE_UPGRADE; + + // prepare next iteration + ReleaseNullBSTR(bstrNodeText); + ReleaseNullObject(pixnNode); + } + +LExit: + ReleaseBSTR(bstrNodeText); + ReleaseObject(pixnNode); + ReleaseObject(pixnNodes); + + return hr; +} + +static HRESULT FindRollbackBoundaryById( + __in BURN_PACKAGES* pPackages, + __in_z LPCWSTR wzId, + __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary + ) +{ + HRESULT hr = S_OK; + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; + + for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i) + { + pRollbackBoundary = &pPackages->rgRollbackBoundaries[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pRollbackBoundary->sczId, -1, wzId, -1)) + { + *ppRollbackBoundary = pRollbackBoundary; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} diff --git a/src/burn/engine/package.h b/src/burn/engine/package.h new file mode 100644 index 00000000..89a3d6e9 --- /dev/null +++ b/src/burn/engine/package.h @@ -0,0 +1,380 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +struct _BURN_RELATED_BUNDLES; +typedef _BURN_RELATED_BUNDLES BURN_RELATED_BUNDLES; + +struct _BURN_PACKAGE; +typedef _BURN_PACKAGE BURN_PACKAGE; + +// constants + +const DWORD BURN_PACKAGE_INVALID_PATCH_INDEX = 0x80000000; + +enum BURN_EXE_EXIT_CODE_TYPE +{ + BURN_EXE_EXIT_CODE_TYPE_NONE, + BURN_EXE_EXIT_CODE_TYPE_SUCCESS, + BURN_EXE_EXIT_CODE_TYPE_ERROR, + BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT, + BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT, +}; + +enum BURN_EXE_PROTOCOL_TYPE +{ + BURN_EXE_PROTOCOL_TYPE_NONE, + BURN_EXE_PROTOCOL_TYPE_BURN, + BURN_EXE_PROTOCOL_TYPE_NETFX4, +}; + +enum BURN_PACKAGE_TYPE +{ + BURN_PACKAGE_TYPE_NONE, + BURN_PACKAGE_TYPE_EXE, + BURN_PACKAGE_TYPE_MSI, + BURN_PACKAGE_TYPE_MSP, + BURN_PACKAGE_TYPE_MSU, +}; + +enum BURN_DEPENDENCY_ACTION +{ + BURN_DEPENDENCY_ACTION_NONE, + BURN_DEPENDENCY_ACTION_REGISTER, + BURN_DEPENDENCY_ACTION_UNREGISTER, +}; + +enum BURN_PATCH_TARGETCODE_TYPE +{ + BURN_PATCH_TARGETCODE_TYPE_UNKNOWN, + BURN_PATCH_TARGETCODE_TYPE_PRODUCT, + BURN_PATCH_TARGETCODE_TYPE_UPGRADE, +}; + +enum BOOTSTRAPPER_FEATURE_ACTION +{ + BOOTSTRAPPER_FEATURE_ACTION_NONE, + BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL, + BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE, + BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT, + BOOTSTRAPPER_FEATURE_ACTION_REINSTALL, + BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE, + BOOTSTRAPPER_FEATURE_ACTION_REMOVE, +}; + +enum BURN_PACKAGE_REGISTRATION_STATE +{ + BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, + BURN_PACKAGE_REGISTRATION_STATE_ABSENT, + BURN_PACKAGE_REGISTRATION_STATE_IGNORED, + BURN_PACKAGE_REGISTRATION_STATE_PRESENT, +}; + +enum BURN_PATCH_SKIP_STATE +{ + BURN_PATCH_SKIP_STATE_NONE, + BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL, + BURN_PATCH_SKIP_STATE_SLIPSTREAM, +}; + +// structs + +typedef struct _BURN_EXE_EXIT_CODE +{ + BURN_EXE_EXIT_CODE_TYPE type; + DWORD dwCode; + BOOL fWildcard; +} BURN_EXE_EXIT_CODE; + +typedef struct _BURN_EXE_COMMAND_LINE_ARGUMENT +{ + LPWSTR sczInstallArgument; + LPWSTR sczUninstallArgument; + LPWSTR sczRepairArgument; + LPWSTR sczCondition; +} BURN_EXE_COMMAND_LINE_ARGUMENT; + +typedef struct _BURN_MSPTARGETPRODUCT +{ + MSIINSTALLCONTEXT context; + DWORD dwOrder; + WCHAR wzTargetProductCode[39]; + BURN_PACKAGE* pChainedTargetPackage; + BOOL fInstalled; + BOOL fSlipstream; + BOOL fSlipstreamRequired; // this means the target product is not present on the machine, but is available in the chain as a slipstream target. + + BOOTSTRAPPER_PACKAGE_STATE patchPackageState; // only valid after Detect. + BOOTSTRAPPER_REQUEST_STATE defaultRequested; // only valid during Plan. + BOOTSTRAPPER_REQUEST_STATE requested; // only valid during Plan. + BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan. + BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan. + BURN_PATCH_SKIP_STATE executeSkip; // only valid during Plan. + BURN_PATCH_SKIP_STATE rollbackSkip; // only valid during Plan. + + BURN_PACKAGE_REGISTRATION_STATE registrationState; // initialized during Detect, updated during Apply. + BURN_PACKAGE_REGISTRATION_STATE transactionRegistrationState;// only valid during Apply inside an MSI transaction. +} BURN_MSPTARGETPRODUCT; + +typedef struct _BURN_MSIPROPERTY +{ + LPWSTR sczId; + LPWSTR sczValue; // used during forward execution + LPWSTR sczRollbackValue; // used during rollback + LPWSTR sczCondition; +} BURN_MSIPROPERTY; + +typedef struct _BURN_MSIFEATURE +{ + LPWSTR sczId; + LPWSTR sczAddLocalCondition; + LPWSTR sczAddSourceCondition; + LPWSTR sczAdvertiseCondition; + LPWSTR sczRollbackAddLocalCondition; + LPWSTR sczRollbackAddSourceCondition; + LPWSTR sczRollbackAdvertiseCondition; + + BOOTSTRAPPER_FEATURE_STATE currentState; // only valid after Detect. + BOOTSTRAPPER_FEATURE_STATE expectedState; // only valid during Plan. + BOOTSTRAPPER_FEATURE_STATE defaultRequested; // only valid during Plan. + BOOTSTRAPPER_FEATURE_STATE requested; // only valid during Plan. + BOOTSTRAPPER_FEATURE_ACTION execute; // only valid during Plan. + BOOTSTRAPPER_FEATURE_ACTION rollback; // only valid during Plan. +} BURN_MSIFEATURE; + +typedef struct _BURN_RELATED_MSI +{ + LPWSTR sczUpgradeCode; + VERUTIL_VERSION* pMinVersion; + VERUTIL_VERSION* pMaxVersion; + BOOL fMinProvided; + BOOL fMaxProvided; + BOOL fMinInclusive; + BOOL fMaxInclusive; + BOOL fOnlyDetect; + BOOL fLangInclusive; + + DWORD* rgdwLanguages; + DWORD cLanguages; +} BURN_RELATED_MSI; + +typedef struct _BURN_CHAINED_PATCH +{ + BURN_PACKAGE* pMspPackage; + DWORD dwMspTargetProductIndex; // index into the Msp.rgTargetProducts +} BURN_CHAINED_PATCH; + +typedef struct _BURN_SLIPSTREAM_MSP +{ + BURN_PACKAGE* pMspPackage; + DWORD dwMsiChainedPatchIndex; // index into the Msi.rgChainedPatches + + BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan. + BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan. +} BURN_SLIPSTREAM_MSP; + +typedef struct _BURN_DEPENDENCY_PROVIDER +{ + LPWSTR sczKey; + LPWSTR sczVersion; + LPWSTR sczDisplayName; + BOOL fImported; + + DEPENDENCY* rgDependents; // only valid after Detect. + UINT cDependents; // only valid after Detect. +} BURN_DEPENDENCY_PROVIDER; + +typedef struct _BURN_ROLLBACK_BOUNDARY +{ + LPWSTR sczId; + BOOL fVital; + BOOL fTransaction; + BOOL fActiveTransaction; // only valid during Apply. + LPWSTR sczLogPath; +} BURN_ROLLBACK_BOUNDARY; + +typedef struct _BURN_PATCH_TARGETCODE +{ + LPWSTR sczTargetCode; + BURN_PATCH_TARGETCODE_TYPE type; +} BURN_PATCH_TARGETCODE; + +typedef struct _BURN_PACKAGE +{ + LPWSTR sczId; + + LPWSTR sczLogPathVariable; // name of the variable that will be set to the log path. + LPWSTR sczRollbackLogPathVariable; // name of the variable that will be set to the rollback path. + + LPWSTR sczInstallCondition; + BOOL fPerMachine; + BOOL fUninstallable; + BOOL fVital; + BOOL fCanAffectRegistration; + + BOOTSTRAPPER_CACHE_TYPE authoredCacheType; + LPWSTR sczCacheId; + + DWORD64 qwInstallSize; + DWORD64 qwSize; + + BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryForward; // used during install and repair. + BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryBackward; // used during uninstall. + + BOOTSTRAPPER_PACKAGE_STATE currentState; // only valid after Detect. + BOOL fCached; // only valid after Detect. + BOOL fPackageProviderExists; // only valid after Detect. + BOOTSTRAPPER_CACHE_TYPE cacheType; // only valid during Plan. + BOOTSTRAPPER_REQUEST_STATE defaultRequested;// only valid during Plan. + BOOTSTRAPPER_REQUEST_STATE requested; // only valid during Plan. + BOOL fPlannedCache; // only valid during Plan. + BOOL fPlannedUncache; // only valid during Plan. + BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan. + BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan. + BURN_DEPENDENCY_ACTION providerExecute; // only valid during Plan. + BURN_DEPENDENCY_ACTION providerRollback; // only valid during Plan. + BURN_DEPENDENCY_ACTION dependencyExecute; // only valid during Plan. + BURN_DEPENDENCY_ACTION dependencyRollback; // only valid during Plan. + BOOL fDependencyManagerWasHere; // only valid during Plan. + LPWSTR sczCacheFolder; // only valid during Apply. + HRESULT hrCacheResult; // only valid during Apply. + + BURN_PACKAGE_REGISTRATION_STATE cacheRegistrationState; // initialized during Detect, updated during Apply. + BURN_PACKAGE_REGISTRATION_STATE installRegistrationState; // initialized during Detect, updated during Apply. + BURN_PACKAGE_REGISTRATION_STATE expectedCacheRegistrationState; // only valid after Plan. + BURN_PACKAGE_REGISTRATION_STATE expectedInstallRegistrationState;// only valid after Plan. + BURN_PACKAGE_REGISTRATION_STATE transactionRegistrationState; // only valid during Apply inside an MSI transaction. + + BURN_PAYLOAD_GROUP payloads; + + BURN_DEPENDENCY_PROVIDER* rgDependencyProviders; + DWORD cDependencyProviders; + + BURN_PACKAGE_TYPE type; + union + { + struct + { + LPWSTR sczDetectCondition; + LPWSTR sczInstallArguments; + LPWSTR sczRepairArguments; + LPWSTR sczUninstallArguments; + LPWSTR sczIgnoreDependencies; + LPCWSTR wzAncestors; // points directly into engine state. + + BOOL fPseudoBundle; + + BOOL fRepairable; + BURN_EXE_PROTOCOL_TYPE protocol; + + BOOL fSupportsAncestors; + + BURN_EXE_EXIT_CODE* rgExitCodes; + DWORD cExitCodes; + + BURN_EXE_COMMAND_LINE_ARGUMENT* rgCommandLineArguments; + DWORD cCommandLineArguments; + } Exe; + struct + { + LPWSTR sczProductCode; + DWORD dwLanguage; + VERUTIL_VERSION* pVersion; + VERUTIL_VERSION* pInstalledVersion; + LPWSTR sczUpgradeCode; + + BURN_MSIPROPERTY* rgProperties; + DWORD cProperties; + + BURN_MSIFEATURE* rgFeatures; + DWORD cFeatures; + + BURN_RELATED_MSI* rgRelatedMsis; + DWORD cRelatedMsis; + + BURN_SLIPSTREAM_MSP* rgSlipstreamMsps; + LPWSTR* rgsczSlipstreamMspPackageIds; + DWORD cSlipstreamMspPackages; + + BURN_CHAINED_PATCH* rgChainedPatches; + DWORD cChainedPatches; + } Msi; + struct + { + LPWSTR sczPatchCode; + LPWSTR sczApplicabilityXml; + + BURN_MSIPROPERTY* rgProperties; + DWORD cProperties; + + BURN_MSPTARGETPRODUCT* rgTargetProducts; + DWORD cTargetProductCodes; + } Msp; + struct + { + LPWSTR sczDetectCondition; + LPWSTR sczKB; + } Msu; + }; +} BURN_PACKAGE; + +typedef struct _BURN_PACKAGES +{ + BURN_ROLLBACK_BOUNDARY* rgRollbackBoundaries; + DWORD cRollbackBoundaries; + + BURN_PACKAGE* rgPackages; + DWORD cPackages; + + BURN_PATCH_TARGETCODE* rgPatchTargetCodes; + DWORD cPatchTargetCodes; + + MSIPATCHSEQUENCEINFOW* rgPatchInfo; + BURN_PACKAGE** rgPatchInfoToPackage; // direct lookup from patch information to the (MSP) package it describes. + // Thus this array is the exact same size as rgPatchInfo. + DWORD cPatchInfo; +} BURN_PACKAGES; + + +// function declarations + +HRESULT PackagesParseFromXml( + __in BURN_PACKAGES* pPackages, + __in BURN_PAYLOADS* pPayloads, + __in IXMLDOMNode* pixnBundle + ); +void PackageUninitialize( + __in BURN_PACKAGE* pPackage + ); +void PackagesUninitialize( + __in BURN_PACKAGES* pPackages + ); +HRESULT PackageFindById( + __in BURN_PACKAGES* pPackages, + __in_z LPCWSTR wzId, + __out BURN_PACKAGE** ppPackage + ); +HRESULT PackageFindRelatedById( + __in BURN_RELATED_BUNDLES* pRelatedBundles, + __in_z LPCWSTR wzId, + __out BURN_PACKAGE** ppPackage + ); +HRESULT PackageGetProperty( + __in const BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzProperty, + __out_z_opt LPWSTR* psczValue + ); +HRESULT PackageFindRollbackBoundaryById( + __in BURN_PACKAGES* pPackages, + __in_z LPCWSTR wzId, + __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/packages.config b/src/burn/engine/packages.config new file mode 100644 index 00000000..7219a3da --- /dev/null +++ b/src/burn/engine/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/burn/engine/payload.cpp b/src/burn/engine/payload.cpp new file mode 100644 index 00000000..72eb3476 --- /dev/null +++ b/src/burn/engine/payload.cpp @@ -0,0 +1,314 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// internal function declarations + +static HRESULT FindEmbeddedBySourcePath( + __in BURN_PAYLOADS* pPayloads, + __in_opt BURN_CONTAINER* pContainer, + __in_z LPCWSTR wzStreamName, + __out BURN_PAYLOAD** ppPayload + ); + + +// function definitions + +extern "C" HRESULT PayloadsParseFromXml( + __in BURN_PAYLOADS* pPayloads, + __in_opt BURN_CONTAINERS* pContainers, + __in_opt BURN_PAYLOAD_GROUP* pLayoutPayloads, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + LPWSTR scz = NULL; + + // select payload nodes + hr = XmlSelectNodes(pixnBundle, L"Payload", &pixnNodes); + ExitOnFailure(hr, "Failed to select payload nodes."); + + // get payload node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get payload node count."); + + if (!cNodes) + { + ExitFunction(); + } + + // allocate memory for payloads + pPayloads->rgPayloads = (BURN_PAYLOAD*)MemAlloc(sizeof(BURN_PAYLOAD) * cNodes, TRUE); + ExitOnNull(pPayloads->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate memory for payload structs."); + + pPayloads->cPayloads = cNodes; + + // parse search elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_PAYLOAD* pPayload = &pPayloads->rgPayloads[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &pPayload->sczKey); + ExitOnFailure(hr, "Failed to get @Id."); + + // @FilePath + hr = XmlGetAttributeEx(pixnNode, L"FilePath", &pPayload->sczFilePath); + ExitOnFailure(hr, "Failed to get @FilePath."); + + // @Packaging + hr = XmlGetAttributeEx(pixnNode, L"Packaging", &scz); + ExitOnFailure(hr, "Failed to get @Packaging."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"embedded", -1)) + { + pPayload->packaging = BURN_PAYLOAD_PACKAGING_EMBEDDED; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"external", -1)) + { + pPayload->packaging = BURN_PAYLOAD_PACKAGING_EXTERNAL; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Packaging: %ls", scz); + } + + // @Container + if (pContainers) + { + hr = XmlGetAttributeEx(pixnNode, L"Container", &scz); + if (E_NOTFOUND != hr || BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging) + { + ExitOnFailure(hr, "Failed to get @Container."); + + // find container + hr = ContainerFindById(pContainers, scz, &pPayload->pContainer); + ExitOnFailure(hr, "Failed to to find container: %ls", scz); + } + } + + // @LayoutOnly + hr = XmlGetYesNoAttribute(pixnNode, L"LayoutOnly", &pPayload->fLayoutOnly); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @LayoutOnly."); + } + + // @SourcePath + hr = XmlGetAttributeEx(pixnNode, L"SourcePath", &pPayload->sczSourcePath); + ExitOnFailure(hr, "Failed to get @SourcePath."); + + // @DownloadUrl + hr = XmlGetAttributeEx(pixnNode, L"DownloadUrl", &pPayload->downloadSource.sczUrl); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @DownloadUrl."); + } + + // @FileSize + hr = XmlGetAttributeEx(pixnNode, L"FileSize", &scz); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @FileSize."); + + hr = StrStringToUInt64(scz, 0, &pPayload->qwFileSize); + ExitOnFailure(hr, "Failed to parse @FileSize."); + } + + // @Hash + hr = XmlGetAttributeEx(pixnNode, L"Hash", &scz); + ExitOnFailure(hr, "Failed to get @Hash."); + + hr = StrAllocHexDecode(scz, &pPayload->pbHash, &pPayload->cbHash); + ExitOnFailure(hr, "Failed to hex decode the Payload/@Hash."); + + if (pPayload->fLayoutOnly && pLayoutPayloads) + { + hr = MemEnsureArraySize(reinterpret_cast(&pLayoutPayloads->rgItems), pLayoutPayloads->cItems + 1, sizeof(BURN_PAYLOAD_GROUP_ITEM), 5); + ExitOnFailure(hr, "Failed to allocate memory for layout payloads."); + + pLayoutPayloads->rgItems[pLayoutPayloads->cItems].pPayload = pPayload; + ++pLayoutPayloads->cItems; + + pLayoutPayloads->qwTotalSize += pPayload->qwFileSize; + } + + // prepare next iteration + ReleaseNullObject(pixnNode); + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseStr(scz); + + return hr; +} + +extern "C" void PayloadUninitialize( + __in BURN_PAYLOAD* pPayload + ) +{ + if (pPayload) + { + ReleaseStr(pPayload->sczKey); + ReleaseStr(pPayload->sczFilePath); + ReleaseMem(pPayload->pbHash); + ReleaseStr(pPayload->sczSourcePath); + ReleaseStr(pPayload->sczLocalFilePath); + ReleaseStr(pPayload->downloadSource.sczUrl); + ReleaseStr(pPayload->downloadSource.sczUser); + ReleaseStr(pPayload->downloadSource.sczPassword); + ReleaseStr(pPayload->sczUnverifiedPath); + } +} + +extern "C" void PayloadsUninitialize( + __in BURN_PAYLOADS* pPayloads + ) +{ + if (pPayloads->rgPayloads) + { + for (DWORD i = 0; i < pPayloads->cPayloads; ++i) + { + PayloadUninitialize(pPayloads->rgPayloads + i); + } + MemFree(pPayloads->rgPayloads); + } + + // clear struct + memset(pPayloads, 0, sizeof(BURN_PAYLOADS)); +} + +extern "C" HRESULT PayloadExtractUXContainer( + __in BURN_PAYLOADS* pPayloads, + __in BURN_CONTAINER_CONTEXT* pContainerContext, + __in_z LPCWSTR wzTargetDir + ) +{ + HRESULT hr = S_OK; + LPWSTR sczStreamName = NULL; + LPWSTR sczDirectory = NULL; + BURN_PAYLOAD* pPayload = NULL; + + // extract all payloads + for (;;) + { + // get next stream + hr = ContainerNextStream(pContainerContext, &sczStreamName); + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + break; + } + ExitOnFailure(hr, "Failed to get next stream."); + + // find payload by stream name + hr = PayloadFindEmbeddedBySourcePath(pPayloads, sczStreamName, &pPayload); + ExitOnFailure(hr, "Failed to find embedded payload: %ls", sczStreamName); + + // make file path + hr = PathConcat(wzTargetDir, pPayload->sczFilePath, &pPayload->sczLocalFilePath); + ExitOnFailure(hr, "Failed to concat file paths."); + + // extract file + hr = PathGetDirectory(pPayload->sczLocalFilePath, &sczDirectory); + ExitOnFailure(hr, "Failed to get directory portion of local file path"); + + hr = DirEnsureExists(sczDirectory, NULL); + ExitOnFailure(hr, "Failed to ensure directory exists"); + + hr = ContainerStreamToFile(pContainerContext, pPayload->sczLocalFilePath); + ExitOnFailure(hr, "Failed to extract file."); + + // flag that the payload has been acquired + pPayload->state = BURN_PAYLOAD_STATE_ACQUIRED; + } + + // locate any payloads that were not extracted + for (DWORD i = 0; i < pPayloads->cPayloads; ++i) + { + pPayload = &pPayloads->rgPayloads[i]; + + // if the payload has not been acquired + if (BURN_PAYLOAD_STATE_ACQUIRED > pPayload->state) + { + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Payload was not found in container: %ls", pPayload->sczKey); + } + } + +LExit: + ReleaseStr(sczStreamName); + ReleaseStr(sczDirectory); + + return hr; +} + +extern "C" HRESULT PayloadFindById( + __in BURN_PAYLOADS* pPayloads, + __in_z LPCWSTR wzId, + __out BURN_PAYLOAD** ppPayload + ) +{ + HRESULT hr = S_OK; + BURN_PAYLOAD* pPayload = NULL; + + for (DWORD i = 0; i < pPayloads->cPayloads; ++i) + { + pPayload = &pPayloads->rgPayloads[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPayload->sczKey, -1, wzId, -1)) + { + *ppPayload = pPayload; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + +extern "C" HRESULT PayloadFindEmbeddedBySourcePath( + __in BURN_PAYLOADS* pPayloads, + __in_z LPCWSTR wzStreamName, + __out BURN_PAYLOAD** ppPayload + ) +{ + HRESULT hr = S_OK; + BURN_PAYLOAD* pPayload = NULL; + + for (DWORD i = 0; i < pPayloads->cPayloads; ++i) + { + pPayload = &pPayloads->rgPayloads[i]; + + if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPayload->sczSourcePath, -1, wzStreamName, -1)) + { + *ppPayload = pPayload; + ExitFunction1(hr = S_OK); + } + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} + + +// internal function definitions diff --git a/src/burn/engine/payload.h b/src/burn/engine/payload.h new file mode 100644 index 00000000..f28b437f --- /dev/null +++ b/src/burn/engine/payload.h @@ -0,0 +1,107 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + +enum BURN_PAYLOAD_PACKAGING +{ + BURN_PAYLOAD_PACKAGING_NONE, + BURN_PAYLOAD_PACKAGING_EMBEDDED, + BURN_PAYLOAD_PACKAGING_EXTERNAL, +}; + +enum BURN_PAYLOAD_STATE +{ + BURN_PAYLOAD_STATE_NONE, + BURN_PAYLOAD_STATE_ACQUIRED, + BURN_PAYLOAD_STATE_CACHED, +}; + + +// structs + +typedef struct _BURN_PAYLOAD +{ + LPWSTR sczKey; + BURN_PAYLOAD_PACKAGING packaging; + BOOL fLayoutOnly; + DWORD64 qwFileSize; + LPWSTR sczFilePath; // file path relative to the execute location + + BYTE* pbHash; + DWORD cbHash; + + LPWSTR sczSourcePath; + BURN_CONTAINER* pContainer; + DOWNLOAD_SOURCE downloadSource; + + // mutable members + BURN_PAYLOAD_STATE state; + LPWSTR sczLocalFilePath; // location of extracted or downloaded copy + + LPWSTR sczUnverifiedPath; + DWORD cRemainingInstances; +} BURN_PAYLOAD; + +typedef struct _BURN_PAYLOADS +{ + BURN_PAYLOAD* rgPayloads; + DWORD cPayloads; +} BURN_PAYLOADS; + +typedef struct _BURN_PAYLOAD_GROUP_ITEM +{ + BURN_PAYLOAD* pPayload; + + // mutable members + BOOL fCached; + DWORD64 qwCommittedCacheProgress; +} BURN_PAYLOAD_GROUP_ITEM; + +typedef struct _BURN_PAYLOAD_GROUP +{ + BURN_PAYLOAD_GROUP_ITEM* rgItems; + DWORD cItems; + DWORD64 qwTotalSize; +} BURN_PAYLOAD_GROUP; + +// functions + +HRESULT PayloadsParseFromXml( + __in BURN_PAYLOADS* pPayloads, + __in_opt BURN_CONTAINERS* pContainers, + __in_opt BURN_PAYLOAD_GROUP* pLayoutPayloads, + __in IXMLDOMNode* pixnBundle + ); +void PayloadUninitialize( + __in BURN_PAYLOAD* pPayload + ); +void PayloadsUninitialize( + __in BURN_PAYLOADS* pPayloads + ); +HRESULT PayloadExtractUXContainer( + __in BURN_PAYLOADS* pPayloads, + __in BURN_CONTAINER_CONTEXT* pContainerContext, + __in_z LPCWSTR wzTargetDir + ); +HRESULT PayloadFindById( + __in BURN_PAYLOADS* pPayloads, + __in_z LPCWSTR wzId, + __out BURN_PAYLOAD** ppPayload + ); +HRESULT PayloadFindEmbeddedBySourcePath( + __in BURN_PAYLOADS* pPayloads, + __in_z LPCWSTR wzStreamName, + __out BURN_PAYLOAD** ppPayload + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/pipe.cpp b/src/burn/engine/pipe.cpp new file mode 100644 index 00000000..a9fd24e8 --- /dev/null +++ b/src/burn/engine/pipe.cpp @@ -0,0 +1,821 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static const DWORD PIPE_64KB = 64 * 1024; +static const DWORD PIPE_WAIT_FOR_CONNECTION = 100; // wait a 10th of a second, +static const DWORD PIPE_RETRY_FOR_CONNECTION = 1800; // for up to 3 minutes. + +static const LPCWSTR PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls"; +static const LPCWSTR CACHE_PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls.Cache"; + +static HRESULT AllocatePipeMessage( + __in DWORD dwMessage, + __in_bcount_opt(cbData) LPVOID pvData, + __in SIZE_T cbData, + __out_bcount(cb) LPVOID* ppvMessage, + __out SIZE_T* cbMessage + ); +static void FreePipeMessage( + __in BURN_PIPE_MESSAGE *pMsg + ); +static HRESULT WritePipeMessage( + __in HANDLE hPipe, + __in DWORD dwMessage, + __in_bcount_opt(cbData) LPVOID pvData, + __in SIZE_T cbData + ); +static HRESULT GetPipeMessage( + __in HANDLE hPipe, + __in BURN_PIPE_MESSAGE* pMsg + ); +static HRESULT ChildPipeConnected( + __in HANDLE hPipe, + __in_z LPCWSTR wzSecret, + __inout DWORD* pdwProcessId + ); + + + +/******************************************************************* + PipeConnectionInitialize - initialize pipe connection data. + +*******************************************************************/ +void PipeConnectionInitialize( + __in BURN_PIPE_CONNECTION* pConnection + ) +{ + memset(pConnection, 0, sizeof(BURN_PIPE_CONNECTION)); + pConnection->hPipe = INVALID_HANDLE_VALUE; + pConnection->hCachePipe = INVALID_HANDLE_VALUE; +} + +/******************************************************************* + PipeConnectionUninitialize - free data in a pipe connection. + +*******************************************************************/ +void PipeConnectionUninitialize( + __in BURN_PIPE_CONNECTION* pConnection + ) +{ + ReleaseFileHandle(pConnection->hCachePipe); + ReleaseFileHandle(pConnection->hPipe); + ReleaseHandle(pConnection->hProcess); + ReleaseStr(pConnection->sczSecret); + ReleaseStr(pConnection->sczName); + + memset(pConnection, 0, sizeof(BURN_PIPE_CONNECTION)); + pConnection->hPipe = INVALID_HANDLE_VALUE; + pConnection->hCachePipe = INVALID_HANDLE_VALUE; +} + +/******************************************************************* + PipeSendMessage - + +*******************************************************************/ +extern "C" HRESULT PipeSendMessage( + __in HANDLE hPipe, + __in DWORD dwMessage, + __in_bcount_opt(cbData) LPVOID pvData, + __in SIZE_T cbData, + __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + BURN_PIPE_RESULT result = { }; + + hr = WritePipeMessage(hPipe, dwMessage, pvData, cbData); + ExitOnFailure(hr, "Failed to write send message to pipe."); + + hr = PipePumpMessages(hPipe, pfnCallback, pvContext, &result); + ExitOnFailure(hr, "Failed to pump messages during send message to pipe."); + + *pdwResult = result.dwResult; + +LExit: + return hr; +} + +/******************************************************************* + PipePumpMessages - + +*******************************************************************/ +extern "C" HRESULT PipePumpMessages( + __in HANDLE hPipe, + __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, + __in_opt LPVOID pvContext, + __in BURN_PIPE_RESULT* pResult + ) +{ + HRESULT hr = S_OK; + BURN_PIPE_MESSAGE msg = { }; + SIZE_T iData = 0; + LPSTR sczMessage = NULL; + DWORD dwResult = 0; + + // Pump messages from child process. + while (S_OK == (hr = GetPipeMessage(hPipe, &msg))) + { + switch (msg.dwMessage) + { + case BURN_PIPE_MESSAGE_TYPE_LOG: + iData = 0; + + hr = BuffReadStringAnsi((BYTE*)msg.pvData, msg.cbData, &iData, &sczMessage); + ExitOnFailure(hr, "Failed to read log message."); + + hr = LogStringWorkRaw(sczMessage); + ExitOnFailure(hr, "Failed to write log message:'%hs'.", sczMessage); + + dwResult = static_cast(hr); + break; + + case BURN_PIPE_MESSAGE_TYPE_COMPLETE: + if (!msg.pvData || sizeof(DWORD) != msg.cbData) + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "No status returned to PipePumpMessages()"); + } + + pResult->dwResult = *static_cast(msg.pvData); + ExitFunction1(hr = S_OK); // exit loop. + + case BURN_PIPE_MESSAGE_TYPE_TERMINATE: + iData = 0; + + hr = BuffReadNumber(static_cast(msg.pvData), msg.cbData, &iData, &pResult->dwResult); + ExitOnFailure(hr, "Failed to read returned result to PipePumpMessages()"); + + if (sizeof(DWORD) * 2 == msg.cbData) + { + hr = BuffReadNumber(static_cast(msg.pvData), msg.cbData, &iData, (DWORD*)&pResult->fRestart); + ExitOnFailure(hr, "Failed to read returned restart to PipePumpMessages()"); + } + + ExitFunction1(hr = S_OK); // exit loop. + + default: + if (pfnCallback) + { + hr = pfnCallback(&msg, pvContext, &dwResult); + } + else + { + hr = E_INVALIDARG; + } + ExitOnFailure(hr, "Failed to process message: %u", msg.dwMessage); + break; + } + + // post result + hr = WritePipeMessage(hPipe, static_cast(BURN_PIPE_MESSAGE_TYPE_COMPLETE), &dwResult, sizeof(dwResult)); + ExitOnFailure(hr, "Failed to post result to child process."); + + FreePipeMessage(&msg); + } + ExitOnFailure(hr, "Failed to get message over pipe"); + + if (S_FALSE == hr) + { + hr = S_OK; + } + +LExit: + ReleaseStr(sczMessage); + FreePipeMessage(&msg); + + return hr; +} + +/******************************************************************* + PipeCreateNameAndSecret - + +*******************************************************************/ +extern "C" HRESULT PipeCreateNameAndSecret( + __out_z LPWSTR *psczConnectionName, + __out_z LPWSTR *psczSecret + ) +{ + HRESULT hr = S_OK; + WCHAR wzGuid[GUID_STRING_LENGTH]; + LPWSTR sczConnectionName = NULL; + LPWSTR sczSecret = NULL; + + // Create the unique pipe name. + hr = GuidFixedCreate(wzGuid); + ExitOnRootFailure(hr, "Failed to create pipe guid."); + + hr = StrAllocFormatted(&sczConnectionName, L"BurnPipe.%s", wzGuid); + ExitOnFailure(hr, "Failed to allocate pipe name."); + + // Create the unique client secret. + hr = GuidFixedCreate(wzGuid); + ExitOnRootFailure(hr, "Failed to create pipe secret."); + + hr = StrAllocString(&sczSecret, wzGuid, 0); + ExitOnFailure(hr, "Failed to allocate pipe secret."); + + *psczConnectionName = sczConnectionName; + sczConnectionName = NULL; + *psczSecret = sczSecret; + sczSecret = NULL; + +LExit: + ReleaseStr(sczSecret); + ReleaseStr(sczConnectionName); + + return hr; +} + +/******************************************************************* + PipeCreatePipes - create the pipes and event to signal child process. + +*******************************************************************/ +extern "C" HRESULT PipeCreatePipes( + __in BURN_PIPE_CONNECTION* pConnection, + __in BOOL fCreateCachePipe, + __out HANDLE* phEvent + ) +{ + Assert(pConnection->sczName); + Assert(INVALID_HANDLE_VALUE == pConnection->hPipe); + Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe); + + HRESULT hr = S_OK; + PSECURITY_DESCRIPTOR psd = NULL; + SECURITY_ATTRIBUTES sa = { }; + LPWSTR sczFullPipeName = NULL; + HANDLE hPipe = INVALID_HANDLE_VALUE; + HANDLE hCachePipe = INVALID_HANDLE_VALUE; + + // Only the grant special rights when the pipe is being used for "embedded" + // scenarios (aka: there is no cache pipe). + if (!fCreateCachePipe) + { + // Create the security descriptor that grants read/write/sync access to Everyone. + // TODO: consider locking down "WD" to LogonIds (logon session) + LPCWSTR wzSddl = L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW0x00100000;;;WD)"; + if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(wzSddl, SDDL_REVISION_1, &psd, NULL)) + { + ExitWithLastError(hr, "Failed to create the security descriptor for the connection event and pipe."); + } + + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = psd; + sa.bInheritHandle = FALSE; + } + + // Create the pipe. + hr = StrAllocFormatted(&sczFullPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName); + ExitOnFailure(hr, "Failed to allocate full name of pipe: %ls", pConnection->sczName); + + // TODO: consider using overlapped IO to do waits on the pipe and still be able to cancel and such. + hPipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, psd ? &sa : NULL); + if (INVALID_HANDLE_VALUE == hPipe) + { + ExitWithLastError(hr, "Failed to create pipe: %ls", sczFullPipeName); + } + + if (fCreateCachePipe) + { + // Create the cache pipe. + hr = StrAllocFormatted(&sczFullPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName); + ExitOnFailure(hr, "Failed to allocate full name of cache pipe: %ls", pConnection->sczName); + + hCachePipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, NULL); + if (INVALID_HANDLE_VALUE == hCachePipe) + { + ExitWithLastError(hr, "Failed to create pipe: %ls", sczFullPipeName); + } + } + + pConnection->hCachePipe = hCachePipe; + hCachePipe = INVALID_HANDLE_VALUE; + + pConnection->hPipe = hPipe; + hPipe = INVALID_HANDLE_VALUE; + + // TODO: remove the following + *phEvent = NULL; + +LExit: + ReleaseFileHandle(hCachePipe); + ReleaseFileHandle(hPipe); + ReleaseStr(sczFullPipeName); + + if (psd) + { + ::LocalFree(psd); + } + + return hr; +} + +/******************************************************************* + PipeLaunchParentProcess - Called from the per-machine process to create + a per-user process and set up the + communication pipe. + +*******************************************************************/ +const LPCWSTR BURN_COMMANDLINE_SWITCH_UNELEVATED = L"burn.unelevated"; +HRESULT PipeLaunchParentProcess( + __in_z LPCWSTR wzCommandLine, + __in int nCmdShow, + __in_z LPWSTR sczConnectionName, + __in_z LPWSTR sczSecret, + __in BOOL /*fDisableUnelevate*/ + ) +{ + HRESULT hr = S_OK; + DWORD dwProcessId = 0; + LPWSTR sczBurnPath = NULL; + LPWSTR sczParameters = NULL; + HANDLE hProcess = NULL; + + dwProcessId = ::GetCurrentProcessId(); + + hr = PathForCurrentProcess(&sczBurnPath, NULL); + ExitOnFailure(hr, "Failed to get current process path."); + + hr = StrAllocFormatted(&sczParameters, L"-%ls %ls %ls %u %ls", BURN_COMMANDLINE_SWITCH_UNELEVATED, sczConnectionName, sczSecret, dwProcessId, wzCommandLine); + ExitOnFailure(hr, "Failed to allocate parameters for unelevated process."); + +#ifdef ENABLE_UNELEVATE + if (fDisableUnelevate) + { + hr = ProcExec(sczBurnPath, sczParameters, nCmdShow, &hProcess); + ExitOnFailure(hr, "Failed to launch parent process with unelevate disabled: %ls", sczBurnPath); + } + else + { + // Try to launch unelevated and if that fails for any reason, try launch our process normally (even though that may make it elevated). + hr = ProcExecuteAsInteractiveUser(sczBurnPath, sczParameters, &hProcess); + if (FAILED(hr)) + { + hr = ShelExecUnelevated(sczBurnPath, sczParameters, L"open", NULL, nCmdShow); + if (FAILED(hr)) + { + hr = ShelExec(sczBurnPath, sczParameters, L"open", NULL, nCmdShow, NULL, NULL); + ExitOnFailure(hr, "Failed to launch parent process: %ls", sczBurnPath); + } + } + } +#else + hr = ProcExec(sczBurnPath, sczParameters, nCmdShow, &hProcess); + ExitOnFailure(hr, "Failed to launch parent process with unelevate disabled: %ls", sczBurnPath); +#endif + +LExit: + ReleaseHandle(hProcess); + ReleaseStr(sczParameters); + ReleaseStr(sczBurnPath); + + return hr; +} + +/******************************************************************* + PipeLaunchChildProcess - Called from the per-user process to create + the per-machine process and set up the + communication pipe. + +*******************************************************************/ +extern "C" HRESULT PipeLaunchChildProcess( + __in_z LPCWSTR wzExecutablePath, + __in BURN_PIPE_CONNECTION* pConnection, + __in BOOL fElevate, + __in_opt HWND hwndParent + ) +{ + HRESULT hr = S_OK; + DWORD dwCurrentProcessId = ::GetCurrentProcessId(); + LPWSTR sczParameters = NULL; + LPCWSTR wzVerb = NULL; + HANDLE hProcess = NULL; + + hr = StrAllocFormatted(&sczParameters, L"-q -%ls %ls %ls %u", BURN_COMMANDLINE_SWITCH_ELEVATED, pConnection->sczName, pConnection->sczSecret, dwCurrentProcessId); + ExitOnFailure(hr, "Failed to allocate parameters for elevated process."); + + wzVerb = !fElevate ? L"open" : L"runas"; + + // Since ShellExecuteEx doesn't support passing inherited handles, don't bother with CoreAppendFileHandleSelfToCommandLine. + // We could fallback to using ::DuplicateHandle to inject the file handle later if necessary. + hr = ShelExec(wzExecutablePath, sczParameters, wzVerb, NULL, SW_SHOWNA, hwndParent, &hProcess); + ExitOnFailure(hr, "Failed to launch elevated child process: %ls", wzExecutablePath); + + pConnection->dwProcessId = ::GetProcessId(hProcess); + pConnection->hProcess = hProcess; + hProcess = NULL; + +LExit: + ReleaseHandle(hProcess); + ReleaseStr(sczParameters); + + return hr; +} + +/******************************************************************* + PipeWaitForChildConnect - + +*******************************************************************/ +extern "C" HRESULT PipeWaitForChildConnect( + __in BURN_PIPE_CONNECTION* pConnection + ) +{ + HRESULT hr = S_OK; + HANDLE hPipes[2] = { pConnection->hPipe, pConnection->hCachePipe}; + LPCWSTR wzSecret = pConnection->sczSecret; + DWORD cbSecret = lstrlenW(wzSecret) * sizeof(WCHAR); + DWORD dwCurrentProcessId = ::GetCurrentProcessId(); + DWORD dwAck = 0; + + for (DWORD i = 0; i < countof(hPipes) && INVALID_HANDLE_VALUE != hPipes[i]; ++i) + { + HANDLE hPipe = hPipes[i]; + DWORD dwPipeState = PIPE_READMODE_BYTE | PIPE_NOWAIT; + + // Temporarily make the pipe non-blocking so we will not get stuck in ::ConnectNamedPipe() forever + // if the child decides not to show up. + if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL)) + { + ExitWithLastError(hr, "Failed to set pipe to non-blocking."); + } + + // Loop for a while waiting for a connection from child process. + DWORD cRetry = 0; + do + { + if (!::ConnectNamedPipe(hPipe, NULL)) + { + DWORD er = ::GetLastError(); + if (ERROR_PIPE_CONNECTED == er) + { + hr = S_OK; + break; + } + else if (ERROR_PIPE_LISTENING == er) + { + if (cRetry < PIPE_RETRY_FOR_CONNECTION) + { + hr = HRESULT_FROM_WIN32(er); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); + break; + } + + ++cRetry; + ::Sleep(PIPE_WAIT_FOR_CONNECTION); + } + else + { + hr = HRESULT_FROM_WIN32(er); + break; + } + } + } while (HRESULT_FROM_WIN32(ERROR_PIPE_LISTENING) == hr); + ExitOnRootFailure(hr, "Failed to wait for child to connect to pipe."); + + // Put the pipe back in blocking mode. + dwPipeState = PIPE_READMODE_BYTE | PIPE_WAIT; + if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL)) + { + ExitWithLastError(hr, "Failed to reset pipe to blocking."); + } + + // Prove we are the one that created the elevated process by passing the secret. + hr = FileWriteHandle(hPipe, reinterpret_cast(&cbSecret), sizeof(cbSecret)); + ExitOnFailure(hr, "Failed to write secret length to pipe."); + + hr = FileWriteHandle(hPipe, reinterpret_cast(wzSecret), cbSecret); + ExitOnFailure(hr, "Failed to write secret to pipe."); + + hr = FileWriteHandle(hPipe, reinterpret_cast(&dwCurrentProcessId), sizeof(dwCurrentProcessId)); + ExitOnFailure(hr, "Failed to write our process id to pipe."); + + // Wait until the elevated process responds that it is ready to go. + hr = FileReadHandle(hPipe, reinterpret_cast(&dwAck), sizeof(dwAck)); + ExitOnFailure(hr, "Failed to read ACK from pipe."); + + // The ACK should match out expected child process id. + //if (pConnection->dwProcessId != dwAck) + //{ + // hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + // ExitOnRootFailure(hr, "Incorrect ACK from elevated pipe: %u", dwAck); + //} + } + +LExit: + return hr; +} + +/******************************************************************* + PipeTerminateChildProcess - + +*******************************************************************/ +extern "C" HRESULT PipeTerminateChildProcess( + __in BURN_PIPE_CONNECTION* pConnection, + __in DWORD dwParentExitCode, + __in BOOL fRestart + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + + // Prepare the exit message. + hr = BuffWriteNumber(&pbData, &cbData, dwParentExitCode); + ExitOnFailure(hr, "Failed to write exit code to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, fRestart); + ExitOnFailure(hr, "Failed to write restart to message buffer."); + + // Send the messages. + if (INVALID_HANDLE_VALUE != pConnection->hCachePipe) + { + hr = WritePipeMessage(pConnection->hCachePipe, static_cast(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData); + ExitOnFailure(hr, "Failed to post terminate message to child process cache thread."); + } + + hr = WritePipeMessage(pConnection->hPipe, static_cast(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData); + ExitOnFailure(hr, "Failed to post terminate message to child process."); + + // If we were able to get a handle to the other process, wait for it to exit. + if (pConnection->hProcess) + { + if (WAIT_FAILED == ::WaitForSingleObject(pConnection->hProcess, PIPE_WAIT_FOR_CONNECTION * PIPE_RETRY_FOR_CONNECTION)) + { + ExitWithLastError(hr, "Failed to wait for child process exit."); + } + +#ifdef DEBUG + DWORD dwChildExitCode = 0; + DWORD dwErrorCode = ERROR_SUCCESS; + BOOL fReturnedExitCode = ::GetExitCodeProcess(pConnection->hProcess, &dwChildExitCode); + if (!fReturnedExitCode) + { + dwErrorCode = ::GetLastError(); // if the other process is elevated and we are not, then we'll get ERROR_ACCESS_DENIED. + + // The unit test use a thread instead of a process so try to get the exit code from + // the thread because we failed to get it from the process. + if (ERROR_INVALID_HANDLE == dwErrorCode) + { + fReturnedExitCode = ::GetExitCodeThread(pConnection->hProcess, &dwChildExitCode); + } + } + AssertSz((fReturnedExitCode && dwChildExitCode == dwParentExitCode) || + (!fReturnedExitCode && ERROR_ACCESS_DENIED == dwErrorCode), + "Child elevated process did not return matching exit code to parent process."); +#endif + } + +LExit: + return hr; +} + +/******************************************************************* + PipeChildConnect - Called from the child process to connect back + to the pipe provided by the parent process. + +*******************************************************************/ +extern "C" HRESULT PipeChildConnect( + __in BURN_PIPE_CONNECTION* pConnection, + __in BOOL fConnectCachePipe + ) +{ + Assert(pConnection->sczName); + Assert(pConnection->sczSecret); + Assert(!pConnection->hProcess); + Assert(INVALID_HANDLE_VALUE == pConnection->hPipe); + Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe); + + HRESULT hr = S_OK; + LPWSTR sczPipeName = NULL; + + // Try to connect to the parent. + hr = StrAllocFormatted(&sczPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName); + ExitOnFailure(hr, "Failed to allocate name of parent pipe."); + + hr = E_UNEXPECTED; + for (DWORD cRetry = 0; FAILED(hr) && cRetry < PIPE_RETRY_FOR_CONNECTION; ++cRetry) + { + pConnection->hPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (INVALID_HANDLE_VALUE == pConnection->hPipe) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (E_FILENOTFOUND == hr) // if the pipe isn't created, call it a timeout waiting on the parent. + { + hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); + } + + ::Sleep(PIPE_WAIT_FOR_CONNECTION); + } + else // we have a connection, go with it. + { + hr = S_OK; + } + } + ExitOnRootFailure(hr, "Failed to open parent pipe: %ls", sczPipeName) + + // Verify the parent and notify it that the child connected. + hr = ChildPipeConnected(pConnection->hPipe, pConnection->sczSecret, &pConnection->dwProcessId); + ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName); + + if (fConnectCachePipe) + { + // Connect to the parent for the cache pipe. + hr = StrAllocFormatted(&sczPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName); + ExitOnFailure(hr, "Failed to allocate name of parent cache pipe."); + + pConnection->hCachePipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (INVALID_HANDLE_VALUE == pConnection->hCachePipe) + { + ExitWithLastError(hr, "Failed to open parent pipe: %ls", sczPipeName) + } + + // Verify the parent and notify it that the child connected. + hr = ChildPipeConnected(pConnection->hCachePipe, pConnection->sczSecret, &pConnection->dwProcessId); + ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName); + } + + pConnection->hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, pConnection->dwProcessId); + ExitOnNullWithLastError(pConnection->hProcess, hr, "Failed to open companion process with PID: %u", pConnection->dwProcessId); + +LExit: + ReleaseStr(sczPipeName); + + return hr; +} + + +static HRESULT AllocatePipeMessage( + __in DWORD dwMessage, + __in_bcount_opt(cbData) LPVOID pvData, + __in SIZE_T cbData, + __out_bcount(cb) LPVOID* ppvMessage, + __out SIZE_T* cbMessage + ) +{ + HRESULT hr = S_OK; + LPVOID pv = NULL; + SIZE_T cb = 0; + + // If no data was provided, ensure the count of bytes is zero. + if (!pvData) + { + cbData = 0; + } + + // Allocate the message. + cb = sizeof(dwMessage) + sizeof(cbData) + cbData; + pv = MemAlloc(cb, FALSE); + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for message."); + + memcpy_s(pv, cb, &dwMessage, sizeof(dwMessage)); + memcpy_s(static_cast(pv) + sizeof(dwMessage), cb - sizeof(dwMessage), &cbData, sizeof(cbData)); + if (cbData) + { + memcpy_s(static_cast(pv) + sizeof(dwMessage) + sizeof(cbData), cb - sizeof(dwMessage) - sizeof(cbData), pvData, cbData); + } + + *cbMessage = cb; + *ppvMessage = pv; + pv = NULL; + +LExit: + ReleaseMem(pv); + return hr; +} + +static void FreePipeMessage( + __in BURN_PIPE_MESSAGE *pMsg + ) +{ + if (pMsg->fAllocatedData) + { + ReleaseNullMem(pMsg->pvData); + pMsg->fAllocatedData = FALSE; + } +} + +static HRESULT WritePipeMessage( + __in HANDLE hPipe, + __in DWORD dwMessage, + __in_bcount_opt(cbData) LPVOID pvData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + LPVOID pv = NULL; + SIZE_T cb = 0; + + hr = AllocatePipeMessage(dwMessage, pvData, cbData, &pv, &cb); + ExitOnFailure(hr, "Failed to allocate message to write."); + + // Write the message. + hr = FileWriteHandle(hPipe, reinterpret_cast(pv), cb); + ExitOnFailure(hr, "Failed to write message type to pipe."); + +LExit: + ReleaseMem(pv); + return hr; +} + +static HRESULT GetPipeMessage( + __in HANDLE hPipe, + __in BURN_PIPE_MESSAGE* pMsg + ) +{ + HRESULT hr = S_OK; + BYTE pbMessageAndByteCount[sizeof(DWORD) + sizeof(SIZE_T)] = { }; + + hr = FileReadHandle(hPipe, pbMessageAndByteCount, sizeof(pbMessageAndByteCount)); + if (HRESULT_FROM_WIN32(ERROR_BROKEN_PIPE) == hr) + { + memset(pbMessageAndByteCount, 0, sizeof(pbMessageAndByteCount)); + hr = S_FALSE; + } + ExitOnFailure(hr, "Failed to read message from pipe."); + + pMsg->dwMessage = *(DWORD*)(pbMessageAndByteCount); + pMsg->cbData = *(SIZE_T*)(pbMessageAndByteCount + sizeof(DWORD)); + if (pMsg->cbData) + { + pMsg->pvData = MemAlloc(pMsg->cbData, FALSE); + ExitOnNull(pMsg->pvData, hr, E_OUTOFMEMORY, "Failed to allocate data for message."); + + hr = FileReadHandle(hPipe, reinterpret_cast(pMsg->pvData), pMsg->cbData); + ExitOnFailure(hr, "Failed to read data for message."); + + pMsg->fAllocatedData = TRUE; + } + +LExit: + if (!pMsg->fAllocatedData && pMsg->pvData) + { + MemFree(pMsg->pvData); + } + + return hr; +} + +static HRESULT ChildPipeConnected( + __in HANDLE hPipe, + __in_z LPCWSTR wzSecret, + __inout DWORD* pdwProcessId + ) +{ + HRESULT hr = S_OK; + LPWSTR sczVerificationSecret = NULL; + DWORD cbVerificationSecret = 0; + DWORD dwVerificationProcessId = 0; + DWORD dwAck = ::GetCurrentProcessId(); // send our process id as the ACK. + + // Read the verification secret. + hr = FileReadHandle(hPipe, reinterpret_cast(&cbVerificationSecret), sizeof(cbVerificationSecret)); + ExitOnFailure(hr, "Failed to read size of verification secret from parent pipe."); + + if (255 < cbVerificationSecret / sizeof(WCHAR)) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Verification secret from parent is too big."); + } + + hr = StrAlloc(&sczVerificationSecret, cbVerificationSecret / sizeof(WCHAR) + 1); + ExitOnFailure(hr, "Failed to allocate buffer for verification secret."); + + FileReadHandle(hPipe, reinterpret_cast(sczVerificationSecret), cbVerificationSecret); + ExitOnFailure(hr, "Failed to read verification secret from parent pipe."); + + // Verify the secrets match. + if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, 0, sczVerificationSecret, -1, wzSecret, -1)) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Verification secret from parent does not match."); + } + + // Read the verification process id. + hr = FileReadHandle(hPipe, reinterpret_cast(&dwVerificationProcessId), sizeof(dwVerificationProcessId)); + ExitOnFailure(hr, "Failed to read verification process id from parent pipe."); + + // If a process id was not provided, we'll trust the process id from the parent. + if (*pdwProcessId == 0) + { + *pdwProcessId = dwVerificationProcessId; + } + else if (*pdwProcessId != dwVerificationProcessId) // verify the ids match. + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Verification process id from parent does not match."); + } + + // All is well, tell the parent process. + hr = FileWriteHandle(hPipe, reinterpret_cast(&dwAck), sizeof(dwAck)); + ExitOnFailure(hr, "Failed to inform parent process that child is running."); + +LExit: + ReleaseStr(sczVerificationSecret); + return hr; +} diff --git a/src/burn/engine/pipe.h b/src/burn/engine/pipe.h new file mode 100644 index 00000000..429cd824 --- /dev/null +++ b/src/burn/engine/pipe.h @@ -0,0 +1,113 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _BURN_PIPE_CONNECTION +{ + LPWSTR sczName; + LPWSTR sczSecret; + DWORD dwProcessId; + + HANDLE hProcess; + HANDLE hPipe; + HANDLE hCachePipe; +} BURN_PIPE_CONNECTION; + +typedef enum _BURN_PIPE_MESSAGE_TYPE : DWORD +{ + BURN_PIPE_MESSAGE_TYPE_LOG = 0xF0000001, + BURN_PIPE_MESSAGE_TYPE_COMPLETE = 0xF0000002, + BURN_PIPE_MESSAGE_TYPE_TERMINATE = 0xF0000003, +} BURN_PIPE_MESSAGE_TYPE; + +typedef struct _BURN_PIPE_MESSAGE +{ + DWORD dwMessage; + SIZE_T cbData; + + BOOL fAllocatedData; + LPVOID pvData; +} BURN_PIPE_MESSAGE; + +typedef struct _BURN_PIPE_RESULT +{ + DWORD dwResult; + BOOL fRestart; +} BURN_PIPE_RESULT; + + +typedef HRESULT (*PFN_PIPE_MESSAGE_CALLBACK)( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); + + +// Common functions. +void PipeConnectionInitialize( + __in BURN_PIPE_CONNECTION* pConnection + ); +void PipeConnectionUninitialize( + __in BURN_PIPE_CONNECTION* pConnection + ); +HRESULT PipeSendMessage( + __in HANDLE hPipe, + __in DWORD dwMessage, + __in_bcount_opt(cbData) LPVOID pvData, + __in SIZE_T cbData, + __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); +HRESULT PipePumpMessages( + __in HANDLE hPipe, + __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, + __in_opt LPVOID pvContext, + __in BURN_PIPE_RESULT* pResult + ); + +// Parent functions. +HRESULT PipeCreateNameAndSecret( + __out_z LPWSTR *psczConnectionName, + __out_z LPWSTR *psczSecret + ); +HRESULT PipeCreatePipes( + __in BURN_PIPE_CONNECTION* pConnection, + __in BOOL fCreateCachePipe, + __out HANDLE* phEvent + ); +HRESULT PipeLaunchParentProcess( + __in LPCWSTR wzCommandLine, + __in int nCmdShow, + __in_z LPWSTR sczConnectionName, + __in_z LPWSTR sczSecret, + __in BOOL fDisableUnelevate + ); +HRESULT PipeLaunchChildProcess( + __in_z LPCWSTR wzExecutablePath, + __in BURN_PIPE_CONNECTION* pConnection, + __in BOOL fElevate, + __in_opt HWND hwndParent + ); +HRESULT PipeWaitForChildConnect( + __in BURN_PIPE_CONNECTION* pConnection + ); +HRESULT PipeTerminateChildProcess( + __in BURN_PIPE_CONNECTION* pConnection, + __in DWORD dwParentExitCode, + __in BOOL fRestart + ); + +// Child functions. +HRESULT PipeChildConnect( + __in BURN_PIPE_CONNECTION* pConnection, + __in BOOL fConnectCachePipe + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/burn/engine/plan.cpp b/src/burn/engine/plan.cpp new file mode 100644 index 00000000..9a4aa5f1 --- /dev/null +++ b/src/burn/engine/plan.cpp @@ -0,0 +1,2699 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#define PlanDumpLevel REPORT_DEBUG + +// internal struct definitions + + +// internal function definitions + +static void UninitializeRegistrationAction( + __in BURN_DEPENDENT_REGISTRATION_ACTION* pAction + ); +static void UninitializeCacheAction( + __in BURN_CACHE_ACTION* pCacheAction + ); +static void ResetPlannedContainerState( + __in BURN_CONTAINER* pContainer + ); +static void ResetPlannedPayloadsState( + __in BURN_PAYLOADS* pPayloads + ); +static void ResetPlannedPayloadGroupState( + __in BURN_PAYLOAD_GROUP* pPayloadGroup + ); +static void ResetPlannedPackageState( + __in BURN_PACKAGE* pPackage + ); +static void ResetPlannedRollbackBoundaryState( + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary + ); +static HRESULT PlanPackagesHelper( + __in BURN_PACKAGE* rgPackages, + __in DWORD cPackages, + __in BOOL fPlanCleanPackages, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RELATION_TYPE relationType + ); +static HRESULT InitializePackage( + __in BURN_PLAN* pPlan, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_VARIABLES* pVariables, + __in BURN_PACKAGE* pPackage, + __in BOOTSTRAPPER_RELATION_TYPE relationType + ); +static HRESULT ProcessPackage( + __in BOOL fBundlePerMachine, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __inout HANDLE* phSyncpointEvent, + __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary + ); +static HRESULT ProcessPackageRollbackBoundary( + __in BURN_PLAN* pPlan, + __in_opt BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary, + __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary + ); +static HRESULT GetActionDefaultRequestState( + __in BOOTSTRAPPER_ACTION action, + __in BOOL fPermanent, + __in BOOTSTRAPPER_PACKAGE_STATE currentState, + __out BOOTSTRAPPER_REQUEST_STATE* pRequestState + ); +static HRESULT AddRegistrationAction( + __in BURN_PLAN* pPlan, + __in BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type, + __in_z LPCWSTR wzDependentProviderKey, + __in_z LPCWSTR wzOwnerBundleId + ); +static HRESULT AddCachePackage( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __out HANDLE* phSyncpointEvent + ); +static HRESULT AddCachePackageHelper( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __out HANDLE* phSyncpointEvent + ); +static HRESULT AddCacheSlipstreamMsps( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage + ); +static BOOL AlreadyPlannedCachePackage( + __in BURN_PLAN* pPlan, + __in_z LPCWSTR wzPackageId, + __out HANDLE* phSyncpointEvent + ); +static DWORD GetNextCheckpointId( + __in BURN_PLAN* pPlan + ); +static HRESULT AppendCacheAction( + __in BURN_PLAN* pPlan, + __out BURN_CACHE_ACTION** ppCacheAction + ); +static HRESULT AppendRollbackCacheAction( + __in BURN_PLAN* pPlan, + __out BURN_CACHE_ACTION** ppCacheAction + ); +static HRESULT ProcessPayloadGroup( + __in BURN_PLAN* pPlan, + __in BURN_PAYLOAD_GROUP* pPayloadGroup + ); +static void RemoveUnnecessaryActions( + __in BOOL fExecute, + __in BURN_EXECUTE_ACTION* rgActions, + __in DWORD cActions + ); +static void FinalizePatchActions( + __in BOOL fExecute, + __in BURN_EXECUTE_ACTION* rgActions, + __in DWORD cActions + ); +static void CalculateExpectedRegistrationStates( + __in BURN_PACKAGE* rgPackages, + __in DWORD cPackages + ); +static HRESULT PlanDependencyActions( + __in BOOL fBundlePerMachine, + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage + ); +static HRESULT CalculateExecuteActions( + __in BURN_PACKAGE* pPackage, + __in_opt BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary + ); +static BOOL NeedsCache( + __in BURN_PACKAGE* pPackage, + __in BOOL fExecute + ); +static BOOL ForceCache( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage + ); + +// function definitions + +extern "C" void PlanReset( + __in BURN_PLAN* pPlan, + __in BURN_CONTAINERS* pContainers, + __in BURN_PACKAGES* pPackages, + __in BURN_PAYLOAD_GROUP* pLayoutPayloads + ) +{ + ReleaseNullStr(pPlan->sczLayoutDirectory); + PackageUninitialize(&pPlan->forwardCompatibleBundle); + + if (pPlan->rgRegistrationActions) + { + for (DWORD i = 0; i < pPlan->cRegistrationActions; ++i) + { + UninitializeRegistrationAction(&pPlan->rgRegistrationActions[i]); + } + MemFree(pPlan->rgRegistrationActions); + } + + if (pPlan->rgRollbackRegistrationActions) + { + for (DWORD i = 0; i < pPlan->cRollbackRegistrationActions; ++i) + { + UninitializeRegistrationAction(&pPlan->rgRollbackRegistrationActions[i]); + } + MemFree(pPlan->rgRollbackRegistrationActions); + } + + if (pPlan->rgCacheActions) + { + for (DWORD i = 0; i < pPlan->cCacheActions; ++i) + { + UninitializeCacheAction(&pPlan->rgCacheActions[i]); + } + MemFree(pPlan->rgCacheActions); + } + + if (pPlan->rgExecuteActions) + { + for (DWORD i = 0; i < pPlan->cExecuteActions; ++i) + { + PlanUninitializeExecuteAction(&pPlan->rgExecuteActions[i]); + } + MemFree(pPlan->rgExecuteActions); + } + + if (pPlan->rgRollbackActions) + { + for (DWORD i = 0; i < pPlan->cRollbackActions; ++i) + { + PlanUninitializeExecuteAction(&pPlan->rgRollbackActions[i]); + } + MemFree(pPlan->rgRollbackActions); + } + + if (pPlan->rgCleanActions) + { + // Nothing needs to be freed inside clean actions today. + MemFree(pPlan->rgCleanActions); + } + + if (pPlan->rgPlannedProviders) + { + ReleaseDependencyArray(pPlan->rgPlannedProviders, pPlan->cPlannedProviders); + } + + if (pPlan->rgContainerProgress) + { + MemFree(pPlan->rgContainerProgress); + } + + if (pPlan->shContainerProgress) + { + ReleaseDict(pPlan->shContainerProgress); + } + + if (pPlan->rgPayloadProgress) + { + MemFree(pPlan->rgPayloadProgress); + } + + if (pPlan->shPayloadProgress) + { + ReleaseDict(pPlan->shPayloadProgress); + } + + if (pPlan->pPayloads) + { + ResetPlannedPayloadsState(pPlan->pPayloads); + } + + memset(pPlan, 0, sizeof(BURN_PLAN)); + + if (pContainers->rgContainers) + { + for (DWORD i = 0; i < pContainers->cContainers; ++i) + { + ResetPlannedContainerState(&pContainers->rgContainers[i]); + } + } + + // Reset the planned actions for each package. + if (pPackages->rgPackages) + { + for (DWORD i = 0; i < pPackages->cPackages; ++i) + { + ResetPlannedPackageState(&pPackages->rgPackages[i]); + } + } + + ResetPlannedPayloadGroupState(pLayoutPayloads); + + // Reset the planned state for each rollback boundary. + if (pPackages->rgRollbackBoundaries) + { + for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i) + { + ResetPlannedRollbackBoundaryState(&pPackages->rgRollbackBoundaries[i]); + } + } +} + +extern "C" void PlanUninitializeExecuteAction( + __in BURN_EXECUTE_ACTION* pExecuteAction + ) +{ + switch (pExecuteAction->type) + { + case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: + ReleaseStr(pExecuteAction->exePackage.sczIgnoreDependencies); + ReleaseStr(pExecuteAction->exePackage.sczAncestors); + break; + + case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: + ReleaseStr(pExecuteAction->msiPackage.sczLogPath); + ReleaseMem(pExecuteAction->msiPackage.rgFeatures); + break; + + case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: + ReleaseStr(pExecuteAction->mspTarget.sczTargetProductCode); + ReleaseStr(pExecuteAction->mspTarget.sczLogPath); + ReleaseMem(pExecuteAction->mspTarget.rgOrderedPatches); + break; + + case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: + ReleaseStr(pExecuteAction->msuPackage.sczLogPath); + break; + + case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: + ReleaseStr(pExecuteAction->packageDependency.sczBundleProviderKey); + break; + } +} + +extern "C" HRESULT PlanSetVariables( + __in BOOTSTRAPPER_ACTION action, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + + hr = VariableSetNumeric(pVariables, BURN_BUNDLE_ACTION, action, TRUE); + ExitOnFailure(hr, "Failed to set the bundle action built-in variable."); + +LExit: + return hr; +} + +extern "C" HRESULT PlanDefaultPackageRequestState( + __in BURN_PACKAGE_TYPE packageType, + __in BOOTSTRAPPER_PACKAGE_STATE currentState, + __in BOOL fPermanent, + __in BOOTSTRAPPER_ACTION action, + __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __out BOOTSTRAPPER_REQUEST_STATE* pRequestState + ) +{ + HRESULT hr = S_OK; + BOOTSTRAPPER_REQUEST_STATE defaultRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; + + // If doing layout, then always default to requesting the package be cached. + if (BOOTSTRAPPER_ACTION_LAYOUT == action) + { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE; + } + else if (BOOTSTRAPPER_RELATION_PATCH == relationType && BURN_PACKAGE_TYPE_MSP == packageType) + { + // For patch related bundles, only install a patch if currently absent during install, modify, or repair. + if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == currentState && BOOTSTRAPPER_ACTION_INSTALL <= action) + { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; + } + else + { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; + } + } + else if (BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED == currentState && BOOTSTRAPPER_ACTION_UNINSTALL != action) + { + // Superseded means the package is on the machine but not active, so only uninstall operations are allowed. + // All other operations do nothing. + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; + } + else if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == currentState && !(BOOTSTRAPPER_ACTION_UNINSTALL == action && BURN_PACKAGE_TYPE_MSP == packageType)) + { + // Obsolete means the package is not on the machine and should not be installed, *except* patches can be obsolete + // and present so allow them to be removed during uninstall. Everyone else, gets nothing. + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; + } + else // pick the best option for the action state and install condition. + { + hr = GetActionDefaultRequestState(action, fPermanent, currentState, &defaultRequestState); + ExitOnFailure(hr, "Failed to get default request state for action."); + + // If we're doing an install, use the install condition + // to determine whether to use the default request state or make the package absent. + if (BOOTSTRAPPER_ACTION_UNINSTALL != action && BOOTSTRAPPER_PACKAGE_CONDITION_FALSE == installCondition) + { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT; + } + else // just set the package to the default request state. + { + *pRequestState = defaultRequestState; + } + } + +LExit: + return hr; +} + +extern "C" HRESULT PlanLayoutBundle( + __in BURN_PLAN* pPlan, + __in_z LPCWSTR wzExecutableName, + __in DWORD64 qwBundleSize, + __in BURN_VARIABLES* pVariables, + __in BURN_PAYLOAD_GROUP* pLayoutPayloads + ) +{ + HRESULT hr = S_OK; + BURN_CACHE_ACTION* pCacheAction = NULL; + LPWSTR sczExecutablePath = NULL; + + // Get the layout directory. + hr = VariableGetString(pVariables, BURN_BUNDLE_LAYOUT_DIRECTORY, &pPlan->sczLayoutDirectory); + if (E_NOTFOUND == hr) // if not set, use the current directory as the layout directory. + { + hr = VariableGetString(pVariables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, &pPlan->sczLayoutDirectory); + if (E_NOTFOUND == hr) // if not set, use the current directory as the layout directory. + { + hr = PathForCurrentProcess(&sczExecutablePath, NULL); + ExitOnFailure(hr, "Failed to get path for current executing process as layout directory."); + + hr = PathGetDirectory(sczExecutablePath, &pPlan->sczLayoutDirectory); + ExitOnFailure(hr, "Failed to get executing process as layout directory."); + } + } + ExitOnFailure(hr, "Failed to get bundle layout directory property."); + + hr = PathBackslashTerminate(&pPlan->sczLayoutDirectory); + ExitOnFailure(hr, "Failed to ensure layout directory is backslash terminated."); + + hr = ProcessPayloadGroup(pPlan, pLayoutPayloads); + ExitOnFailure(hr, "Failed to process payload group for bundle."); + + // Plan the layout of the bundle engine itself. + hr = AppendCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to append bundle start action."); + + pCacheAction->type = BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE; + + hr = StrAllocString(&pCacheAction->bundleLayout.sczExecutableName, wzExecutableName, 0); + ExitOnFailure(hr, "Failed to to copy executable name for bundle."); + + hr = CacheCalculateBundleLayoutWorkingPath(pPlan->wzBundleId, &pCacheAction->bundleLayout.sczUnverifiedPath); + ExitOnFailure(hr, "Failed to calculate bundle layout working path."); + + pCacheAction->bundleLayout.qwBundleSize = qwBundleSize; + pCacheAction->bundleLayout.pPayloadGroup = pLayoutPayloads; + + // Acquire + Verify + Finalize + pPlan->qwCacheSizeTotal += 3 * qwBundleSize; + + ++pPlan->cOverallProgressTicksTotal; + +LExit: + ReleaseStr(sczExecutablePath); + + return hr; +} + +extern "C" HRESULT PlanForwardCompatibleBundles( + __in BURN_USER_EXPERIENCE* pUX, + __in BOOTSTRAPPER_COMMAND* pCommand, + __in BURN_PLAN* pPlan, + __in BURN_REGISTRATION* pRegistration, + __in BOOTSTRAPPER_ACTION action + ) +{ + HRESULT hr = S_OK; + BOOL fRecommendIgnore = TRUE; + BOOL fIgnoreBundle = FALSE; + + if (!pRegistration->fForwardCompatibleBundleExists) + { + ExitFunction(); + } + + // Only change the recommendation if an active parent was provided. + if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent) + { + // On install, recommend running the forward compatible bundle because there is an active parent. This + // will essentially register the parent with the forward compatible bundle. + if (BOOTSTRAPPER_ACTION_INSTALL == action) + { + fRecommendIgnore = FALSE; + } + else if (BOOTSTRAPPER_ACTION_UNINSTALL == action || + BOOTSTRAPPER_ACTION_MODIFY == action || + BOOTSTRAPPER_ACTION_REPAIR == action) + { + // When modifying the bundle, only recommend running the forward compatible bundle if the parent + // is already registered as a dependent of the provider key. + if (pRegistration->fParentRegisteredAsDependent) + { + fRecommendIgnore = FALSE; + } + } + } + + for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) + { + BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle; + if (!pRelatedBundle->fForwardCompatible) + { + continue; + } + + fIgnoreBundle = fRecommendIgnore; + + hr = UserExperienceOnPlanForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, &fIgnoreBundle); + ExitOnRootFailure(hr, "BA aborted plan forward compatible bundle."); + + if (!fIgnoreBundle) + { + hr = PseudoBundleInitializePassthrough(&pPlan->forwardCompatibleBundle, pCommand, NULL, pRegistration->sczActiveParent, pRegistration->sczAncestors, &pRelatedBundle->package); + ExitOnFailure(hr, "Failed to initialize pass through bundle."); + + pPlan->fEnabledForwardCompatibleBundle = TRUE; + break; + } + } + +LExit: + return hr; +} + +extern "C" HRESULT PlanPackages( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PACKAGES* pPackages, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RELATION_TYPE relationType + ) +{ + HRESULT hr = S_OK; + + hr = PlanPackagesHelper(pPackages->rgPackages, pPackages->cPackages, TRUE, pUX, pPlan, pLog, pVariables, display, relationType); + + return hr; +} + +extern "C" HRESULT PlanRegistration( + __in BURN_PLAN* pPlan, + __in BURN_REGISTRATION* pRegistration, + __in BOOTSTRAPPER_RESUME_TYPE /*resumeType*/, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __inout BOOL* pfContinuePlanning + ) +{ + HRESULT hr = S_OK; + STRINGDICT_HANDLE sdBundleDependents = NULL; + STRINGDICT_HANDLE sdIgnoreDependents = NULL; + + pPlan->fCanAffectMachineState = TRUE; // register the bundle since we're modifying machine state. + pPlan->fDisallowRemoval = FALSE; // by default the bundle can be planned to be removed + pPlan->fIgnoreAllDependents = pRegistration->fIgnoreAllDependents; + + // Ensure the bundle is cached if not running from the cache. + if (!CacheBundleRunningFromCache()) + { + pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE; + } + + // Always write registration since things may have changed or it just needs to be "fixed up". + pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION; + + // Always update our estimated size registration when installing/modify/repair since things + // may have been added or removed or it just needs to be "fixed up". + pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE; + + if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) + { + // If our provider key was detected and it points to our current bundle then we can + // unregister the bundle dependency. + if (pRegistration->sczDetectedProviderKeyBundleId && + CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczId, -1, pRegistration->sczDetectedProviderKeyBundleId, -1)) + { + pPlan->dependencyRegistrationAction = BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER; + } + else // log that another bundle already owned our registration, hopefully this only happens when a newer version + { // of a bundle installed and is in the process of upgrading us. + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_PROVIDER_KEY_REMOVAL, pRegistration->sczProviderKey, pRegistration->sczDetectedProviderKeyBundleId); + } + + // Create the dictionary of dependents that should be ignored. + hr = DictCreateStringList(&sdIgnoreDependents, 5, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create the string dictionary."); + + // If the self-dependent dependent exists, plan its removal. If we did not do this, we + // would prevent self-removal. + if (pRegistration->fSelfRegisteredAsDependent) + { + hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER, pRegistration->wzSelfDependent, pRegistration->sczId); + ExitOnFailure(hr, "Failed to allocate registration action."); + + hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, pRegistration->wzSelfDependent); + ExitOnFailure(hr, "Failed to add self-dependent to ignore dependents."); + } + + if (!pPlan->fIgnoreAllDependents) + { + // If we are not doing an upgrade, we check to see if there are still dependents on us and if so we skip planning. + // However, when being upgraded, we always execute our uninstall because a newer version of us is probably + // already on the machine and we need to clean up the stuff specific to this bundle. + if (BOOTSTRAPPER_RELATION_UPGRADE != relationType) + { + // If there were other dependencies to ignore, add them. + for (DWORD iDependency = 0; iDependency < pRegistration->cIgnoredDependencies; ++iDependency) + { + DEPENDENCY* pDependency = pRegistration->rgIgnoredDependencies + iDependency; + + hr = DictKeyExists(sdIgnoreDependents, pDependency->sczKey); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to check the dictionary of ignored dependents."); + } + else + { + hr = DictAddKey(sdIgnoreDependents, pDependency->sczKey); + ExitOnFailure(hr, "Failed to add dependent key to ignored dependents."); + } + } + + // For addon or patch bundles, dependent related bundles should be ignored. This allows + // that addon or patch to be removed even though bundles it targets still are registered. + for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) + { + const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i; + + if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType) + { + for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j; + + hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, pProvider->sczKey); + ExitOnFailure(hr, "Failed to add dependent bundle provider key to ignore dependents."); + } + } + } + + // If there are any (non-ignored and not-planned-to-be-removed) dependents left, skip planning. + for (DWORD iDependent = 0; iDependent < pRegistration->cDependents; ++iDependent) + { + DEPENDENCY* pDependent = pRegistration->rgDependents + iDependent; + + hr = DictKeyExists(sdIgnoreDependents, pDependent->sczKey); + if (E_NOTFOUND == hr) + { + hr = S_OK; + + // TODO: callback to the BA and let it have the option to ignore this dependent? + if (!pPlan->fDisallowRemoval) + { + pPlan->fDisallowRemoval = TRUE; // ensure the registration stays + *pfContinuePlanning = FALSE; // skip the rest of planning. + + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS); + } + + LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_DEPENDENT, pDependent->sczKey, LoggingStringOrUnknownIfNull(pDependent->sczName)); + } + ExitOnFailure(hr, "Failed to check for remaining dependents during planning."); + } + } + } + } + else + { + BOOL fAddonOrPatchBundle = (pRegistration->cAddonCodes || pRegistration->cPatchCodes); + + // Always plan to write our provider key registration when installing/modify/repair to "fix it" + // if broken. + pPlan->dependencyRegistrationAction = BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER; + + // Create the dictionary of bundle dependents. + hr = DictCreateStringList(&sdBundleDependents, 5, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create the string dictionary."); + + for (DWORD iDependent = 0; iDependent < pRegistration->cDependents; ++iDependent) + { + DEPENDENCY* pDependent = pRegistration->rgDependents + iDependent; + + hr = DictKeyExists(sdBundleDependents, pDependent->sczKey); + if (E_NOTFOUND == hr) + { + hr = DictAddKey(sdBundleDependents, pDependent->sczKey); + ExitOnFailure(hr, "Failed to add dependent key to bundle dependents."); + } + ExitOnFailure(hr, "Failed to check the dictionary of bundle dependents."); + } + + // Register each dependent related bundle. The ensures that addons and patches are reference + // counted and stick around until the last targeted bundle is removed. + for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) + { + const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i; + + if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType) + { + for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j) + { + const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j; + + hr = DictKeyExists(sdBundleDependents, pProvider->sczKey); + if (E_NOTFOUND == hr) + { + hr = DictAddKey(sdBundleDependents, pProvider->sczKey); + ExitOnFailure(hr, "Failed to add new dependent key to bundle dependents."); + + hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, pProvider->sczKey, pRelatedBundle->package.sczId); + ExitOnFailure(hr, "Failed to add registration action for dependent related bundle."); + } + ExitOnFailure(hr, "Failed to check the dictionary of bundle dependents."); + } + } + } + + // Only do the following if we decided there was a dependent self to register. If so and and an explicit parent was + // provided, register dependent self. Otherwise, if this bundle is not an addon or patch bundle then self-regisiter + // as our own dependent. + if (pRegistration->wzSelfDependent && !pRegistration->fSelfRegisteredAsDependent && (pRegistration->sczActiveParent || !fAddonOrPatchBundle)) + { + hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, pRegistration->wzSelfDependent, pRegistration->sczId); + ExitOnFailure(hr, "Failed to add registration action for self dependent."); + } + } + +LExit: + ReleaseDict(sdBundleDependents); + ReleaseDict(sdIgnoreDependents); + + return hr; +} + +extern "C" HRESULT PlanPassThroughBundle( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RELATION_TYPE relationType + ) +{ + HRESULT hr = S_OK; + + // Plan passthrough package. + // Passthrough packages are never cleaned up by the calling bundle (they delete themselves when appropriate) + // so we don't need to plan clean up. + hr = PlanPackagesHelper(pPackage, 1, FALSE, pUX, pPlan, pLog, pVariables, display, relationType); + ExitOnFailure(hr, "Failed to process passthrough package."); + +LExit: + return hr; +} + +extern "C" HRESULT PlanUpdateBundle( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RELATION_TYPE relationType + ) +{ + HRESULT hr = S_OK; + + // Plan update package. + hr = PlanPackagesHelper(pPackage, 1, TRUE, pUX, pPlan, pLog, pVariables, display, relationType); + ExitOnFailure(hr, "Failed to process update package."); + +LExit: + return hr; +} + +static HRESULT PlanPackagesHelper( + __in BURN_PACKAGE* rgPackages, + __in DWORD cPackages, + __in BOOL fPlanCleanPackages, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RELATION_TYPE relationType + ) +{ + HRESULT hr = S_OK; + BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine. + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; + HANDLE hSyncpointEvent = NULL; + + // Initialize the packages. + for (DWORD i = 0; i < cPackages; ++i) + { + DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i; + BURN_PACKAGE* pPackage = rgPackages + iPackage; + + hr = InitializePackage(pPlan, pUX, pVariables, pPackage, relationType); + ExitOnFailure(hr, "Failed to initialize package."); + } + + // Initialize the patch targets after all packages, since they could rely on the requested state of packages that are after the patch's package in the chain. + for (DWORD i = 0; i < cPackages; ++i) + { + DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i; + BURN_PACKAGE* pPackage = rgPackages + iPackage; + + if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + hr = MspEnginePlanInitializePackage(pPackage, pUX); + ExitOnFailure(hr, "Failed to initialize plan package: %ls", pPackage->sczId); + } + } + + // Plan the packages. + for (DWORD i = 0; i < cPackages; ++i) + { + DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i; + BURN_PACKAGE* pPackage = rgPackages + iPackage; + + hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, &hSyncpointEvent, &pRollbackBoundary); + ExitOnFailure(hr, "Failed to process package."); + } + + // If we still have an open rollback boundary, complete it. + if (pRollbackBoundary) + { + hr = PlanRollbackBoundaryComplete(pPlan); + ExitOnFailure(hr, "Failed to plan final rollback boundary complete."); + + pRollbackBoundary = NULL; + } + + if (fPlanCleanPackages) + { + // Plan clean up of packages. + for (DWORD i = 0; i < cPackages; ++i) + { + DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i; + BURN_PACKAGE* pPackage = rgPackages + iPackage; + + hr = PlanCleanPackage(pPlan, pPackage); + ExitOnFailure(hr, "Failed to plan clean package."); + } + } + + // Remove unnecessary actions. + hr = PlanFinalizeActions(pPlan); + ExitOnFailure(hr, "Failed to remove unnecessary actions from plan."); + + CalculateExpectedRegistrationStates(rgPackages, cPackages); + + // Let the BA know the actions that were planned. + for (DWORD i = 0; i < cPackages; ++i) + { + DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i; + BURN_PACKAGE* pPackage = rgPackages + iPackage; + + UserExperienceOnPlannedPackage(pUX, pPackage->sczId, pPackage->execute, pPackage->rollback, pPackage->fPlannedCache, pPackage->fPlannedUncache); + } + +LExit: + return hr; +} + +static HRESULT InitializePackage( + __in BURN_PLAN* pPlan, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_VARIABLES* pVariables, + __in BURN_PACKAGE* pPackage, + __in BOOTSTRAPPER_RELATION_TYPE relationType + ) +{ + HRESULT hr = S_OK; + BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition = BOOTSTRAPPER_PACKAGE_CONDITION_DEFAULT; + BOOL fInstallCondition = FALSE; + BOOL fBeginCalled = FALSE; + + if (pPackage->fCanAffectRegistration) + { + pPackage->expectedCacheRegistrationState = pPackage->cacheRegistrationState; + pPackage->expectedInstallRegistrationState = pPackage->installRegistrationState; + } + + if (pPackage->sczInstallCondition && *pPackage->sczInstallCondition) + { + hr = ConditionEvaluate(pVariables, pPackage->sczInstallCondition, &fInstallCondition); + ExitOnFailure(hr, "Failed to evaluate install condition."); + + installCondition = fInstallCondition ? BOOTSTRAPPER_PACKAGE_CONDITION_TRUE : BOOTSTRAPPER_PACKAGE_CONDITION_FALSE; + } + + // Remember the default requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it. + hr = PlanDefaultPackageRequestState(pPackage->type, pPackage->currentState, !pPackage->fUninstallable, pPlan->action, installCondition, relationType, &pPackage->defaultRequested); + ExitOnFailure(hr, "Failed to set default package state."); + + pPackage->requested = pPackage->defaultRequested; + fBeginCalled = TRUE; + + hr = UserExperienceOnPlanPackageBegin(pUX, pPackage->sczId, pPackage->currentState, pPackage->fCached, installCondition, &pPackage->requested, &pPackage->cacheType); + ExitOnRootFailure(hr, "BA aborted plan package begin."); + + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + { + hr = MsiEnginePlanInitializePackage(pPackage, pVariables, pUX); + ExitOnFailure(hr, "Failed to initialize plan package: %ls", pPackage->sczId); + } + +LExit: + if (fBeginCalled) + { + UserExperienceOnPlanPackageComplete(pUX, pPackage->sczId, hr, pPackage->requested); + } + + return hr; +} + +static HRESULT ProcessPackage( + __in BOOL fBundlePerMachine, + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __inout HANDLE* phSyncpointEvent, + __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary + ) +{ + HRESULT hr = S_OK; + BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary = NULL; + + pEffectiveRollbackBoundary = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackage->pRollbackBoundaryBackward : pPackage->pRollbackBoundaryForward; + hr = ProcessPackageRollbackBoundary(pPlan, pEffectiveRollbackBoundary, ppRollbackBoundary); + ExitOnFailure(hr, "Failed to process package rollback boundary."); + + if (BOOTSTRAPPER_ACTION_LAYOUT == pPlan->action) + { + if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested) + { + hr = PlanLayoutPackage(pPlan, pPackage); + ExitOnFailure(hr, "Failed to plan layout package."); + } + } + else + { + if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested) + { + // If the package is in a requested state, plan it. + hr = PlanExecutePackage(fBundlePerMachine, display, pUX, pPlan, pPackage, pLog, pVariables, phSyncpointEvent); + ExitOnFailure(hr, "Failed to plan execute package."); + } + else + { + if (ForceCache(pPlan, pPackage)) + { + hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent); + ExitOnFailure(hr, "Failed to plan cache package."); + + if (pPackage->fPerMachine) + { + pPlan->fPerMachine = TRUE; + } + } + + // Make sure the package is properly ref-counted even if no plan is requested. + hr = PlanDependencyActions(fBundlePerMachine, pPlan, pPackage); + ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId); + } + } + + // Add the checkpoint after each package and dependency registration action. + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute || BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback || BURN_DEPENDENCY_ACTION_NONE != pPackage->dependencyExecute) + { + hr = PlanExecuteCheckpoint(pPlan); + ExitOnFailure(hr, "Failed to append execute checkpoint."); + } + +LExit: + return hr; +} + +static HRESULT ProcessPackageRollbackBoundary( + __in BURN_PLAN* pPlan, + __in_opt BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary, + __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary + ) +{ + HRESULT hr = S_OK; + + // If the package marks the start of a rollback boundary, start a new one. + if (pEffectiveRollbackBoundary) + { + // Complete previous rollback boundary. + if (*ppRollbackBoundary) + { + hr = PlanRollbackBoundaryComplete(pPlan); + ExitOnFailure(hr, "Failed to plan rollback boundary complete."); + } + + // Start new rollback boundary. + hr = PlanRollbackBoundaryBegin(pPlan, pEffectiveRollbackBoundary); + ExitOnFailure(hr, "Failed to plan rollback boundary begin."); + + *ppRollbackBoundary = pEffectiveRollbackBoundary; + } + +LExit: + return hr; +} + +extern "C" HRESULT PlanLayoutContainer( + __in BURN_PLAN* pPlan, + __in BURN_CONTAINER* pContainer + ) +{ + HRESULT hr = S_OK; + BURN_CACHE_ACTION* pCacheAction = NULL; + + Assert(!pContainer->fPlanned); + pContainer->fPlanned = TRUE; + + if (pPlan->sczLayoutDirectory) + { + if (!pContainer->fAttached) + { + hr = AppendCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to append package start action."); + + pCacheAction->type = BURN_CACHE_ACTION_TYPE_CONTAINER; + pCacheAction->container.pContainer = pContainer; + + // Acquire + Verify + Finalize + pPlan->qwCacheSizeTotal += 3 * pContainer->qwFileSize; + } + } + else + { + if (!pContainer->fActuallyAttached) + { + // Acquire + pPlan->qwCacheSizeTotal += pContainer->qwFileSize; + } + } + + if (!pContainer->sczUnverifiedPath) + { + if (pContainer->fActuallyAttached) + { + hr = PathForCurrentProcess(&pContainer->sczUnverifiedPath, NULL); + ExitOnFailure(hr, "Failed to get path for executing module as attached container working path."); + } + else + { + hr = CacheCalculateContainerWorkingPath(pPlan->wzBundleId, pContainer, &pContainer->sczUnverifiedPath); + ExitOnFailure(hr, "Failed to calculate unverified path for container."); + } + } + +LExit: + return hr; +} + +extern "C" HRESULT PlanLayoutPackage( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + BURN_CACHE_ACTION* pCacheAction = NULL; + + hr = ProcessPayloadGroup(pPlan, &pPackage->payloads); + ExitOnFailure(hr, "Failed to process payload group for package: %ls.", pPackage->sczId); + + hr = AppendCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to append package start action."); + + pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE; + pCacheAction->package.pPackage = pPackage; + + ++pPlan->cOverallProgressTicksTotal; + +LExit: + return hr; +} + +extern "C" HRESULT PlanExecutePackage( + __in BOOL fPerMachine, + __in BOOTSTRAPPER_DISPLAY display, + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __inout HANDLE* phSyncpointEvent + ) +{ + HRESULT hr = S_OK; + BOOL fRequestedCache = BOOTSTRAPPER_REQUEST_STATE_CACHE == pPackage->requested || ForceCache(pPlan, pPackage); + + hr = CalculateExecuteActions(pPackage, pPlan->pActiveRollbackBoundary); + ExitOnFailure(hr, "Failed to calculate plan actions for package: %ls", pPackage->sczId); + + // Calculate package states based on reference count and plan certain dependency actions prior to planning the package execute action. + hr = DependencyPlanPackageBegin(fPerMachine, pPackage, pPlan); + ExitOnFailure(hr, "Failed to begin plan dependency actions for package: %ls", pPackage->sczId); + + if (fRequestedCache || NeedsCache(pPackage, TRUE)) + { + hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent); + ExitOnFailure(hr, "Failed to plan cache package."); + } + else if (!pPackage->fCached && NeedsCache(pPackage, FALSE)) + { + // TODO: this decision should be made during apply instead of plan based on whether the package is actually cached. + // If the package is not in the cache, disable any rollback that would require the package from the cache. + LogId(REPORT_STANDARD, MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE, pPackage->sczId, LoggingActionStateToString(pPackage->rollback)); + pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + } + + // Add the cache and install size to estimated size if it will be on the machine at the end of the install + if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || + fRequestedCache || + (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_ABSENT < pPackage->requested) + ) + { + // If the package will remain in the cache, add the package size to the estimated size + if (BOOTSTRAPPER_CACHE_TYPE_REMOVE < pPackage->cacheType) + { + pPlan->qwEstimatedSize += pPackage->qwSize; + } + + // If the package will end up installed on the machine, add the install size to the estimated size. + if (BOOTSTRAPPER_REQUEST_STATE_CACHE < pPackage->requested) + { + // MSP packages get cached automatically by windows installer with any embedded cabs, so include that in the size as well + if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + pPlan->qwEstimatedSize += pPackage->qwSize; + } + + pPlan->qwEstimatedSize += pPackage->qwInstallSize; + } + } + + // Add execute actions. + switch (pPackage->type) + { + case BURN_PACKAGE_TYPE_EXE: + hr = ExeEnginePlanAddPackage(NULL, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent); + break; + + case BURN_PACKAGE_TYPE_MSI: + hr = MsiEnginePlanAddPackage(display, pUserExperience, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent); + break; + + case BURN_PACKAGE_TYPE_MSP: + hr = MspEnginePlanAddPackage(display, pUserExperience, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent); + break; + + case BURN_PACKAGE_TYPE_MSU: + hr = MsuEnginePlanAddPackage(pPackage, pPlan, pLog, pVariables, *phSyncpointEvent); + break; + + default: + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Invalid package type."); + } + ExitOnFailure(hr, "Failed to add plan actions for package: %ls", pPackage->sczId); + + // Plan certain dependency actions after planning the package execute action. + hr = DependencyPlanPackageComplete(pPackage, pPlan); + ExitOnFailure(hr, "Failed to complete plan dependency actions for package: %ls", pPackage->sczId); + + // If we are going to take any action on this package, add progress for it. + if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute || BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback) + { + LoggingIncrementPackageSequence(); + + ++pPlan->cExecutePackagesTotal; + ++pPlan->cOverallProgressTicksTotal; + + // If package is per-machine and is being executed, flag the plan to be per-machine as well. + if (pPackage->fPerMachine) + { + pPlan->fPerMachine = TRUE; + } + } + +LExit: + return hr; +} + +extern "C" HRESULT PlanDefaultRelatedBundleRequestState( + __in BOOTSTRAPPER_RELATION_TYPE commandRelationType, + __in BOOTSTRAPPER_RELATION_TYPE relatedBundleRelationType, + __in BOOTSTRAPPER_ACTION action, + __in VERUTIL_VERSION* pRegistrationVersion, + __in VERUTIL_VERSION* pRelatedBundleVersion, + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestState + ) +{ + HRESULT hr = S_OK; + int nCompareResult = 0; + + // Never touch related bundles during Cache. + if (BOOTSTRAPPER_ACTION_CACHE == action) + { + ExitFunction1(*pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE); + } + + switch (relatedBundleRelationType) + { + case BOOTSTRAPPER_RELATION_UPGRADE: + if (BOOTSTRAPPER_RELATION_UPGRADE != commandRelationType && BOOTSTRAPPER_ACTION_UNINSTALL < action) + { + hr = VerCompareParsedVersions(pRegistrationVersion, pRelatedBundleVersion, &nCompareResult); + ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistrationVersion ? pRegistrationVersion->sczVersion : NULL, pRelatedBundleVersion ? pRelatedBundleVersion->sczVersion : NULL); + + *pRequestState = (nCompareResult < 0) ? BOOTSTRAPPER_REQUEST_STATE_NONE : BOOTSTRAPPER_REQUEST_STATE_ABSENT; + } + break; + case BOOTSTRAPPER_RELATION_PATCH: __fallthrough; + case BOOTSTRAPPER_RELATION_ADDON: + if (BOOTSTRAPPER_ACTION_UNINSTALL == action) + { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT; + } + else if (BOOTSTRAPPER_ACTION_INSTALL == action || BOOTSTRAPPER_ACTION_MODIFY == action) + { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; + } + else if (BOOTSTRAPPER_ACTION_REPAIR == action) + { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_REPAIR; + } + break; + case BOOTSTRAPPER_RELATION_DEPENDENT: + // Automatically repair dependent bundles to restore missing + // packages after uninstall unless we're being upgraded with the + // assumption that upgrades are cumulative (as intended). + if (BOOTSTRAPPER_RELATION_UPGRADE != commandRelationType && BOOTSTRAPPER_ACTION_UNINSTALL == action) + { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_REPAIR; + } + break; + case BOOTSTRAPPER_RELATION_DETECT: + break; + default: + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Unexpected relation type encountered during plan: %d", relatedBundleRelationType); + break; + } + +LExit: + return hr; +} + +extern "C" HRESULT PlanRelatedBundlesBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BURN_REGISTRATION* pRegistration, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in BURN_PLAN* pPlan + ) +{ + HRESULT hr = S_OK; + LPWSTR* rgsczAncestors = NULL; + UINT cAncestors = 0; + STRINGDICT_HANDLE sdAncestors = NULL; + + if (pRegistration->sczAncestors) + { + hr = StrSplitAllocArray(&rgsczAncestors, &cAncestors, pRegistration->sczAncestors, L";"); + ExitOnFailure(hr, "Failed to create string array from ancestors."); + + hr = DictCreateStringListFromArray(&sdAncestors, rgsczAncestors, cAncestors, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create dictionary from ancestors array."); + } + + for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) + { + BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i; + + if (!pRelatedBundle->fPlannable) + { + continue; + } + + pRelatedBundle->package.defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE; + pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE; + + // Do not execute the same bundle twice. + if (sdAncestors) + { + hr = DictKeyExists(sdAncestors, pRelatedBundle->package.sczId); + if (SUCCEEDED(hr)) + { + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_SCHEDULED, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType)); + continue; + } + else if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to lookup the bundle ID in the ancestors dictionary."); + } + } + else if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_RELATION_NONE != relationType) + { + // Avoid repair loops for older bundles that do not handle ancestors. + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_DEPENDENT, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRelationTypeToString(relationType)); + continue; + } + + // Pass along any ancestors and ourself to prevent infinite loops. + pRelatedBundle->package.Exe.wzAncestors = pRegistration->sczBundlePackageAncestors; + + hr = PlanDefaultRelatedBundleRequestState(relationType, pRelatedBundle->relationType, pPlan->action, pRegistration->pVersion, pRelatedBundle->pVersion, &pRelatedBundle->package.requested); + ExitOnFailure(hr, "Failed to get default request state for related bundle."); + + pRelatedBundle->package.defaultRequested = pRelatedBundle->package.requested; + + hr = UserExperienceOnPlanRelatedBundle(pUserExperience, pRelatedBundle->package.sczId, &pRelatedBundle->package.requested); + ExitOnRootFailure(hr, "BA aborted plan related bundle."); + + // Log when the BA changed the bundle state so the engine doesn't get blamed for planning the wrong thing. + if (pRelatedBundle->package.requested != pRelatedBundle->package.defaultRequested) + { + LogId(REPORT_STANDARD, MSG_PLANNED_BUNDLE_UX_CHANGED_REQUEST, pRelatedBundle->package.sczId, LoggingRequestStateToString(pRelatedBundle->package.requested), LoggingRequestStateToString(pRelatedBundle->package.defaultRequested)); + } + + // If uninstalling and the dependent related bundle may be executed, ignore its provider key to allow for downgrades with ref-counting. + if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested) + { + if (0 < pRelatedBundle->package.cDependencyProviders) + { + // Bundles only support a single provider key. + const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders; + + hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, pProvider->sczDisplayName); + ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey); + } + } + } + +LExit: + ReleaseDict(sdAncestors); + ReleaseStrArray(rgsczAncestors, cAncestors); + + return hr; +} + +extern "C" HRESULT PlanRelatedBundlesComplete( + __in BURN_REGISTRATION* pRegistration, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in DWORD dwExecuteActionEarlyIndex + ) +{ + HRESULT hr = S_OK; + LPWSTR sczIgnoreDependencies = NULL; + STRINGDICT_HANDLE sdProviderKeys = NULL; + + // Get the list of dependencies to ignore to pass to related bundles. + hr = DependencyAllocIgnoreDependencies(pPlan, &sczIgnoreDependencies); + ExitOnFailure(hr, "Failed to get the list of dependencies to ignore."); + + hr = DictCreateStringList(&sdProviderKeys, pPlan->cExecuteActions, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create dictionary for planned packages."); + + BOOL fExecutingAnyPackage = FALSE; + + for (DWORD i = 0; i < pPlan->cExecuteActions; ++i) + { + if (BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE == pPlan->rgExecuteActions[i].type && BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].exePackage.action) + { + fExecutingAnyPackage = TRUE; + + BURN_PACKAGE* pPackage = pPlan->rgExecuteActions[i].packageProvider.pPackage; + if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) + { + if (0 < pPackage->cDependencyProviders) + { + // Bundles only support a single provider key. + const BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders; + DictAddKey(sdProviderKeys, pProvider->sczKey); + } + } + } + else + { + switch (pPlan->rgExecuteActions[i].type) + { + case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: + fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].msiPackage.action); + break; + + case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: + fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].mspTarget.action); + break; + + case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: + fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].msuPackage.action); + break; + } + } + } + + for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) + { + DWORD *pdwInsertIndex = NULL; + BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i; + + if (!pRelatedBundle->fPlannable) + { + continue; + } + + // Do not execute if a major upgrade to the related bundle is an embedded bundle (Provider keys are the same) + if (0 < pRelatedBundle->package.cDependencyProviders) + { + // Bundles only support a single provider key. + const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders; + hr = DictKeyExists(sdProviderKeys, pProvider->sczKey); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to check the dictionary for a related bundle provider key: \"%ls\".", pProvider->sczKey); + // Key found, so there is an embedded bundle with the same provider key that will be executed. So this related bundle should not be added to the plan + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), pProvider->sczKey); + continue; + } + else + { + hr = S_OK; + } + } + + // For an uninstall, there is no need to repair dependent bundles if no packages are executing. + if (!fExecutingAnyPackage && BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pRelatedBundle->package.requested && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) + { + pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE; + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DEPENDENT_BUNDLE_REPAIR, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType)); + } + + if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType) + { + // Addon and patch bundles will be passed a list of dependencies to ignore for planning. + hr = StrAllocString(&pRelatedBundle->package.Exe.sczIgnoreDependencies, sczIgnoreDependencies, 0); + ExitOnFailure(hr, "Failed to copy the list of dependencies to ignore."); + + // Uninstall addons and patches early in the chain, before other packages are uninstalled. + if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) + { + pdwInsertIndex = &dwExecuteActionEarlyIndex; + } + } + + if (BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested) + { + hr = ExeEnginePlanCalculatePackage(&pRelatedBundle->package); + ExitOnFailure(hr, "Failed to calcuate plan for related bundle: %ls", pRelatedBundle->package.sczId); + + // Calculate package states based on reference count for addon and patch related bundles. + if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType) + { + hr = DependencyPlanPackageBegin(pRegistration->fPerMachine, &pRelatedBundle->package, pPlan); + ExitOnFailure(hr, "Failed to begin plan dependency actions to package: %ls", pRelatedBundle->package.sczId); + + // If uninstalling a related bundle, make sure the bundle is uninstalled after removing registration. + if (pdwInsertIndex && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) + { + ++(*pdwInsertIndex); + } + } + + hr = ExeEnginePlanAddPackage(pdwInsertIndex, &pRelatedBundle->package, pPlan, pLog, pVariables, NULL); + ExitOnFailure(hr, "Failed to add to plan related bundle: %ls", pRelatedBundle->package.sczId); + + // Calculate package states based on reference count for addon and patch related bundles. + if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType) + { + hr = DependencyPlanPackageComplete(&pRelatedBundle->package, pPlan); + ExitOnFailure(hr, "Failed to complete plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId); + } + + // If we are going to take any action on this package, add progress for it. + if (BOOTSTRAPPER_ACTION_STATE_NONE != pRelatedBundle->package.execute || BOOTSTRAPPER_ACTION_STATE_NONE != pRelatedBundle->package.rollback) + { + LoggingIncrementPackageSequence(); + + ++pPlan->cExecutePackagesTotal; + ++pPlan->cOverallProgressTicksTotal; + } + + // If package is per-machine and is being executed, flag the plan to be per-machine as well. + if (pRelatedBundle->package.fPerMachine) + { + pPlan->fPerMachine = TRUE; + } + } + else if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType) + { + // Make sure the package is properly ref-counted even if no plan is requested. + hr = DependencyPlanPackageBegin(pRegistration->fPerMachine, &pRelatedBundle->package, pPlan); + ExitOnFailure(hr, "Failed to begin plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId); + + hr = DependencyPlanPackage(pdwInsertIndex, &pRelatedBundle->package, pPlan); + ExitOnFailure(hr, "Failed to plan related bundle package provider actions."); + + hr = DependencyPlanPackageComplete(&pRelatedBundle->package, pPlan); + ExitOnFailure(hr, "Failed to complete plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId); + } + } + +LExit: + ReleaseDict(sdProviderKeys); + ReleaseStr(sczIgnoreDependencies); + + return hr; +} + +extern "C" HRESULT PlanFinalizeActions( + __in BURN_PLAN* pPlan + ) +{ + HRESULT hr = S_OK; + + FinalizePatchActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions); + + FinalizePatchActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions); + + RemoveUnnecessaryActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions); + + RemoveUnnecessaryActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions); + + return hr; +} + +extern "C" HRESULT PlanCleanPackage( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + BOOL fPlanCleanPackage = FALSE; + BURN_CLEAN_ACTION* pCleanAction = NULL; + + // The following is a complex set of logic that determines when a package should be cleaned from the cache. + if (BOOTSTRAPPER_CACHE_TYPE_FORCE > pPackage->cacheType || BOOTSTRAPPER_ACTION_CACHE > pPlan->action) + { + // The following are all different reasons why the package should be cleaned from the cache. + // The else-ifs are used to make the conditions easier to see (rather than have them combined + // in one huge condition). + if (BOOTSTRAPPER_CACHE_TYPE_KEEP > pPackage->cacheType) // easy, package is not supposed to stay cached. + { + fPlanCleanPackage = TRUE; + } + else if ((BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested || + BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested) && // requested to be removed and + BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) // actually being removed. + { + fPlanCleanPackage = TRUE; + } + else if ((BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested || + BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested) && // requested to be removed but + BOOTSTRAPPER_ACTION_STATE_NONE == pPackage->execute && // execute is do nothing and + !pPackage->fDependencyManagerWasHere && // dependency manager didn't change execute and + BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState) // currently not installed. + { + fPlanCleanPackage = TRUE; + } + else if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && // uninstalling and + BOOTSTRAPPER_REQUEST_STATE_NONE == pPackage->requested && // requested do nothing (aka: default) and + BOOTSTRAPPER_ACTION_STATE_NONE == pPackage->execute && // execute is still do nothing and + !pPackage->fDependencyManagerWasHere && // dependency manager didn't change execute and + BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState) // currently not installed. + { + fPlanCleanPackage = TRUE; + } + } + + if (fPlanCleanPackage) + { + hr = MemEnsureArraySize(reinterpret_cast(&pPlan->rgCleanActions), pPlan->cCleanActions + 1, sizeof(BURN_CLEAN_ACTION), 5); + ExitOnFailure(hr, "Failed to grow plan's array of clean actions."); + + pCleanAction = pPlan->rgCleanActions + pPlan->cCleanActions; + ++pPlan->cCleanActions; + + pCleanAction->pPackage = pPackage; + + pPackage->fPlannedUncache = TRUE; + + if (pPackage->fCanAffectRegistration) + { + pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + } + +LExit: + return hr; +} + +extern "C" HRESULT PlanExecuteCacheSyncAndRollback( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __in HANDLE hCacheEvent + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* pAction = NULL; + + hr = PlanAppendExecuteAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append wait action for caching."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT; + pAction->syncpoint.hEvent = hCacheEvent; + + hr = PlanAppendRollbackAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append rollback action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE; + pAction->uncachePackage.pPackage = pPackage; + + hr = PlanExecuteCheckpoint(pPlan); + ExitOnFailure(hr, "Failed to append execute checkpoint for cache rollback."); + +LExit: + return hr; +} + +extern "C" HRESULT PlanExecuteCheckpoint( + __in BURN_PLAN* pPlan + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* pAction = NULL; + DWORD dwCheckpointId = GetNextCheckpointId(pPlan); + + // execute checkpoint + hr = PlanAppendExecuteAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append execute action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT; + pAction->checkpoint.dwId = dwCheckpointId; + pAction->checkpoint.pActiveRollbackBoundary = pPlan->pActiveRollbackBoundary; + + // rollback checkpoint + hr = PlanAppendRollbackAction(pPlan, &pAction); + ExitOnFailure(hr, "Failed to append rollback action."); + + pAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT; + pAction->checkpoint.dwId = dwCheckpointId; + pAction->checkpoint.pActiveRollbackBoundary = pPlan->pActiveRollbackBoundary; + +LExit: + return hr; +} + +extern "C" HRESULT PlanInsertExecuteAction( + __in DWORD dwIndex, + __in BURN_PLAN* pPlan, + __out BURN_EXECUTE_ACTION** ppExecuteAction + ) +{ + HRESULT hr = S_OK; + + hr = MemInsertIntoArray((void**)&pPlan->rgExecuteActions, dwIndex, 1, pPlan->cExecuteActions + 1, sizeof(BURN_EXECUTE_ACTION), 5); + ExitOnFailure(hr, "Failed to grow plan's array of execute actions."); + + *ppExecuteAction = pPlan->rgExecuteActions + dwIndex; + ++pPlan->cExecuteActions; + +LExit: + return hr; +} + +extern "C" HRESULT PlanInsertRollbackAction( + __in DWORD dwIndex, + __in BURN_PLAN* pPlan, + __out BURN_EXECUTE_ACTION** ppRollbackAction + ) +{ + HRESULT hr = S_OK; + + hr = MemInsertIntoArray((void**)&pPlan->rgRollbackActions, dwIndex, 1, pPlan->cRollbackActions + 1, sizeof(BURN_EXECUTE_ACTION), 5); + ExitOnFailure(hr, "Failed to grow plan's array of rollback actions."); + + *ppRollbackAction = pPlan->rgRollbackActions + dwIndex; + ++pPlan->cRollbackActions; + +LExit: + return hr; +} + +extern "C" HRESULT PlanAppendExecuteAction( + __in BURN_PLAN* pPlan, + __out BURN_EXECUTE_ACTION** ppExecuteAction + ) +{ + HRESULT hr = S_OK; + + hr = MemEnsureArraySize((void**)&pPlan->rgExecuteActions, pPlan->cExecuteActions + 1, sizeof(BURN_EXECUTE_ACTION), 5); + ExitOnFailure(hr, "Failed to grow plan's array of execute actions."); + + *ppExecuteAction = pPlan->rgExecuteActions + pPlan->cExecuteActions; + ++pPlan->cExecuteActions; + +LExit: + return hr; +} + +extern "C" HRESULT PlanAppendRollbackAction( + __in BURN_PLAN* pPlan, + __out BURN_EXECUTE_ACTION** ppRollbackAction + ) +{ + HRESULT hr = S_OK; + + hr = MemEnsureArraySize((void**)&pPlan->rgRollbackActions, pPlan->cRollbackActions + 1, sizeof(BURN_EXECUTE_ACTION), 5); + ExitOnFailure(hr, "Failed to grow plan's array of rollback actions."); + + *ppRollbackAction = pPlan->rgRollbackActions + pPlan->cRollbackActions; + ++pPlan->cRollbackActions; + +LExit: + return hr; +} + +extern "C" HRESULT PlanRollbackBoundaryBegin( + __in BURN_PLAN* pPlan, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* pExecuteAction = NULL; + + AssertSz(!pPlan->pActiveRollbackBoundary, "PlanRollbackBoundaryBegin called without completing previous RollbackBoundary"); + pPlan->pActiveRollbackBoundary = pRollbackBoundary; + + // Add begin rollback boundary to execute plan. + hr = PlanAppendExecuteAction(pPlan, &pExecuteAction); + ExitOnFailure(hr, "Failed to append rollback boundary begin action."); + + pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY; + pExecuteAction->rollbackBoundary.pRollbackBoundary = pRollbackBoundary; + + // Add begin rollback boundary to rollback plan. + hr = PlanAppendRollbackAction(pPlan, &pExecuteAction); + ExitOnFailure(hr, "Failed to append rollback boundary begin action."); + + pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY; + pExecuteAction->rollbackBoundary.pRollbackBoundary = pRollbackBoundary; + + // Add begin MSI transaction to execute plan. + if (pRollbackBoundary->fTransaction) + { + hr = PlanExecuteCheckpoint(pPlan); + ExitOnFailure(hr, "Failed to append checkpoint before MSI transaction begin action."); + + hr = PlanAppendExecuteAction(pPlan, &pExecuteAction); + ExitOnFailure(hr, "Failed to append MSI transaction begin action."); + + pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION; + pExecuteAction->msiTransaction.pRollbackBoundary = pRollbackBoundary; + } + +LExit: + return hr; +} + +extern "C" HRESULT PlanRollbackBoundaryComplete( + __in BURN_PLAN* pPlan + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* pExecuteAction = NULL; + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = pPlan->pActiveRollbackBoundary; + + AssertSz(pRollbackBoundary, "PlanRollbackBoundaryComplete called without an active RollbackBoundary"); + + if (pRollbackBoundary && pRollbackBoundary->fTransaction) + { + // Add commit MSI transaction to execute plan. + hr = PlanAppendExecuteAction(pPlan, &pExecuteAction); + ExitOnFailure(hr, "Failed to append MSI transaction commit action."); + + pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION; + pExecuteAction->msiTransaction.pRollbackBoundary = pRollbackBoundary; + } + + pPlan->pActiveRollbackBoundary = NULL; + + // Add checkpoints. + hr = PlanExecuteCheckpoint(pPlan); + +LExit: + return hr; +} + +/******************************************************************* + PlanSetResumeCommand - Initializes resume command string + +*******************************************************************/ +extern "C" HRESULT PlanSetResumeCommand( + __in BURN_REGISTRATION* pRegistration, + __in BOOTSTRAPPER_ACTION action, + __in BOOTSTRAPPER_COMMAND* pCommand, + __in BURN_LOGGING* pLog + ) +{ + HRESULT hr = S_OK; + + // build the resume command-line. + hr = CoreRecreateCommandLine(&pRegistration->sczResumeCommandLine, action, pCommand->display, pCommand->restart, pCommand->relationType, pCommand->fPassthrough, pRegistration->sczActiveParent, pRegistration->sczAncestors, pLog->sczPath, pCommand->wzCommandLine); + ExitOnFailure(hr, "Failed to recreate resume command-line."); + +LExit: + return hr; +} + + +// internal function definitions + +static void UninitializeRegistrationAction( + __in BURN_DEPENDENT_REGISTRATION_ACTION* pAction + ) +{ + ReleaseStr(pAction->sczDependentProviderKey); + ReleaseStr(pAction->sczBundleId); + memset(pAction, 0, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION)); +} + +static void UninitializeCacheAction( + __in BURN_CACHE_ACTION* pCacheAction + ) +{ + switch (pCacheAction->type) + { + case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT: + ReleaseHandle(pCacheAction->syncpoint.hEvent); + break; + + case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: + ReleaseStr(pCacheAction->bundleLayout.sczExecutableName); + ReleaseStr(pCacheAction->bundleLayout.sczUnverifiedPath); + break; + } +} + +static void ResetPlannedContainerState( + __in BURN_CONTAINER* pContainer + ) +{ + pContainer->fPlanned = FALSE; + pContainer->qwExtractSizeTotal = 0; + pContainer->qwCommittedCacheProgress = 0; + pContainer->qwCommittedExtractProgress = 0; + pContainer->hrExtract = S_OK; +} + +static void ResetPlannedPayloadsState( + __in BURN_PAYLOADS* pPayloads + ) +{ + for (DWORD i = 0; i < pPayloads->cPayloads; ++i) + { + BURN_PAYLOAD* pPayload = pPayloads->rgPayloads + i; + + pPayload->cRemainingInstances = 0; + pPayload->state = BURN_PAYLOAD_STATE_NONE; + ReleaseNullStr(pPayload->sczLocalFilePath); + } +} + +static void ResetPlannedPayloadGroupState( + __in BURN_PAYLOAD_GROUP* pPayloadGroup + ) +{ + for (DWORD i = 0; i < pPayloadGroup->cItems; ++i) + { + BURN_PAYLOAD_GROUP_ITEM* pItem = pPayloadGroup->rgItems + i; + + pItem->fCached = FALSE; + pItem->qwCommittedCacheProgress = 0; + } +} + +static void ResetPlannedPackageState( + __in BURN_PACKAGE* pPackage + ) +{ + // Reset package state that is a result of planning. + pPackage->cacheType = pPackage->authoredCacheType; + pPackage->defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE; + pPackage->requested = BOOTSTRAPPER_REQUEST_STATE_NONE; + pPackage->fPlannedCache = FALSE; + pPackage->fPlannedUncache = FALSE; + pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; + pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + pPackage->providerExecute = BURN_DEPENDENCY_ACTION_NONE; + pPackage->providerRollback = BURN_DEPENDENCY_ACTION_NONE; + pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE; + pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE; + pPackage->fDependencyManagerWasHere = FALSE; + pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; + pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; + + ReleaseNullStr(pPackage->sczCacheFolder); + + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + { + for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) + { + BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; + + pFeature->expectedState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; + pFeature->defaultRequested = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; + pFeature->requested = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; + pFeature->execute = BOOTSTRAPPER_FEATURE_ACTION_NONE; + pFeature->rollback = BOOTSTRAPPER_FEATURE_ACTION_NONE; + } + + for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) + { + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = &pPackage->Msi.rgSlipstreamMsps[i]; + + pSlipstreamMsp->execute = BOOTSTRAPPER_ACTION_STATE_NONE; + pSlipstreamMsp->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + } + } + else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.rgTargetProducts) + { + for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = &pPackage->Msp.rgTargetProducts[i]; + + pTargetProduct->defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE; + pTargetProduct->requested = BOOTSTRAPPER_REQUEST_STATE_NONE; + pTargetProduct->execute = BOOTSTRAPPER_ACTION_STATE_NONE; + pTargetProduct->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + pTargetProduct->executeSkip = BURN_PATCH_SKIP_STATE_NONE; + pTargetProduct->rollbackSkip = BURN_PATCH_SKIP_STATE_NONE; + } + } + + ResetPlannedPayloadGroupState(&pPackage->payloads); +} + +static void ResetPlannedRollbackBoundaryState( + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary + ) +{ + pRollbackBoundary->fActiveTransaction = FALSE; + ReleaseNullStr(pRollbackBoundary->sczLogPath); +} + +static HRESULT GetActionDefaultRequestState( + __in BOOTSTRAPPER_ACTION action, + __in BOOL fPermanent, + __in BOOTSTRAPPER_PACKAGE_STATE currentState, + __out BOOTSTRAPPER_REQUEST_STATE* pRequestState + ) +{ + HRESULT hr = S_OK; + + switch (action) + { + case BOOTSTRAPPER_ACTION_CACHE: + switch (currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; + break; + + default: + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE; + break; + } + break; + + case BOOTSTRAPPER_ACTION_INSTALL: __fallthrough; + case BOOTSTRAPPER_ACTION_UPDATE_REPLACE: __fallthrough; + case BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED: + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; + break; + + case BOOTSTRAPPER_ACTION_REPAIR: + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_REPAIR; + break; + + case BOOTSTRAPPER_ACTION_UNINSTALL: + *pRequestState = fPermanent ? BOOTSTRAPPER_REQUEST_STATE_NONE : BOOTSTRAPPER_REQUEST_STATE_ABSENT; + break; + + case BOOTSTRAPPER_ACTION_MODIFY: + switch (currentState) + { + case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT; + break; + + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; + break; + + default: + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; + break; + } + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Invalid action state."); + } + +LExit: + return hr; +} + +static HRESULT AddRegistrationAction( + __in BURN_PLAN* pPlan, + __in BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type, + __in_z LPCWSTR wzDependentProviderKey, + __in_z LPCWSTR wzOwnerBundleId + ) +{ + HRESULT hr = S_OK; + BURN_DEPENDENT_REGISTRATION_ACTION_TYPE rollbackType = (BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER == type) ? BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER : BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER; + BURN_DEPENDENT_REGISTRATION_ACTION* pAction = NULL; + + // Create forward registration action. + hr = MemEnsureArraySize((void**)&pPlan->rgRegistrationActions, pPlan->cRegistrationActions + 1, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION), 5); + ExitOnFailure(hr, "Failed to grow plan's array of registration actions."); + + pAction = pPlan->rgRegistrationActions + pPlan->cRegistrationActions; + ++pPlan->cRegistrationActions; + + pAction->type = type; + + hr = StrAllocString(&pAction->sczBundleId, wzOwnerBundleId, 0); + ExitOnFailure(hr, "Failed to copy owner bundle to registration action."); + + hr = StrAllocString(&pAction->sczDependentProviderKey, wzDependentProviderKey, 0); + ExitOnFailure(hr, "Failed to copy dependent provider key to registration action."); + + // Create rollback registration action. + hr = MemEnsureArraySize((void**)&pPlan->rgRollbackRegistrationActions, pPlan->cRollbackRegistrationActions + 1, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION), 5); + ExitOnFailure(hr, "Failed to grow plan's array of rollback registration actions."); + + pAction = pPlan->rgRollbackRegistrationActions + pPlan->cRollbackRegistrationActions; + ++pPlan->cRollbackRegistrationActions; + + pAction->type = rollbackType; + + hr = StrAllocString(&pAction->sczBundleId, wzOwnerBundleId, 0); + ExitOnFailure(hr, "Failed to copy owner bundle to registration action."); + + hr = StrAllocString(&pAction->sczDependentProviderKey, wzDependentProviderKey, 0); + ExitOnFailure(hr, "Failed to copy dependent provider key to rollback registration action."); + +LExit: + return hr; +} + +static HRESULT AddCachePackage( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __out HANDLE* phSyncpointEvent + ) +{ + HRESULT hr = S_OK; + + // If this is an MSI package with slipstream MSPs, ensure the MSPs are cached first. + if (BURN_PACKAGE_TYPE_MSI == pPackage->type && 0 < pPackage->Msi.cSlipstreamMspPackages) + { + hr = AddCacheSlipstreamMsps(pPlan, pPackage); + ExitOnFailure(hr, "Failed to plan slipstream patches for package."); + } + + hr = AddCachePackageHelper(pPlan, pPackage, phSyncpointEvent); + ExitOnFailure(hr, "Failed to plan cache package."); + +LExit: + return hr; +} + +static HRESULT AddCachePackageHelper( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __out HANDLE* phSyncpointEvent + ) +{ + AssertSz(pPackage->sczCacheId && *pPackage->sczCacheId, "AddCachePackageHelper() expects the package to have a cache id."); + + HRESULT hr = S_OK; + BURN_CACHE_ACTION* pCacheAction = NULL; + DWORD dwCheckpoint = 0; + + BOOL fPlanned = AlreadyPlannedCachePackage(pPlan, pPackage->sczId, phSyncpointEvent); + if (fPlanned) + { + ExitFunction(); + } + + // Cache checkpoints happen before the package is cached because downloading packages' + // payloads will not roll themselves back the way installation packages rollback on + // failure automatically. + dwCheckpoint = GetNextCheckpointId(pPlan); + + hr = AppendCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to append checkpoint before package start action."); + + pCacheAction->type = BURN_CACHE_ACTION_TYPE_CHECKPOINT; + pCacheAction->checkpoint.dwId = dwCheckpoint; + + // Only plan the cache rollback if the package is also going to be uninstalled; + // otherwise, future operations like repair will not be able to locate the cached package. + BOOL fPlanCacheRollback = (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->rollback); + + if (fPlanCacheRollback) + { + hr = AppendRollbackCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to append rollback cache action."); + + pCacheAction->type = BURN_CACHE_ACTION_TYPE_CHECKPOINT; + pCacheAction->checkpoint.dwId = dwCheckpoint; + } + + hr = PlanLayoutPackage(pPlan, pPackage); + ExitOnFailure(hr, "Failed to plan cache for package."); + + // Create syncpoint action. + hr = AppendCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to append cache action."); + + pCacheAction->type = BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT; + pCacheAction->syncpoint.hEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL); + ExitOnNullWithLastError(pCacheAction->syncpoint.hEvent, hr, "Failed to create syncpoint event."); + + *phSyncpointEvent = pCacheAction->syncpoint.hEvent; + + pPackage->fPlannedCache = TRUE; + if (pPackage->fCanAffectRegistration) + { + pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + +LExit: + return hr; +} + +static HRESULT AddCacheSlipstreamMsps( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + HANDLE hIgnored = NULL; + + AssertSz(BURN_PACKAGE_TYPE_MSI == pPackage->type, "Only MSI packages can have slipstream patches."); + + for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) + { + BURN_PACKAGE* pMspPackage = pPackage->Msi.rgSlipstreamMsps[i].pMspPackage; + AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches."); + + hr = AddCachePackageHelper(pPlan, pMspPackage, &hIgnored); + ExitOnFailure(hr, "Failed to plan slipstream MSP: %ls", pMspPackage->sczId); + } + +LExit: + return hr; +} + +static BOOL AlreadyPlannedCachePackage( + __in BURN_PLAN* pPlan, + __in_z LPCWSTR wzPackageId, + __out HANDLE* phSyncpointEvent + ) +{ + BOOL fPlanned = FALSE; + + for (DWORD iCacheAction = 0; iCacheAction < pPlan->cCacheActions; ++iCacheAction) + { + BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + iCacheAction; + + if (BURN_CACHE_ACTION_TYPE_PACKAGE == pCacheAction->type) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pCacheAction->package.pPackage->sczId, -1, wzPackageId, -1)) + { + if (iCacheAction + 1 < pPlan->cCacheActions && BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT == pPlan->rgCacheActions[iCacheAction + 1].type) + { + *phSyncpointEvent = pPlan->rgCacheActions[iCacheAction + 1].syncpoint.hEvent; + } + + fPlanned = TRUE; + break; + } + } + } + + return fPlanned; +} + +static DWORD GetNextCheckpointId( + __in BURN_PLAN* pPlan + ) +{ + return ++pPlan->dwNextCheckpointId; +} + +static HRESULT AppendCacheAction( + __in BURN_PLAN* pPlan, + __out BURN_CACHE_ACTION** ppCacheAction + ) +{ + HRESULT hr = S_OK; + + hr = MemEnsureArraySize(reinterpret_cast(&pPlan->rgCacheActions), pPlan->cCacheActions + 1, sizeof(BURN_CACHE_ACTION), 5); + ExitOnFailure(hr, "Failed to grow plan's array of cache actions."); + + *ppCacheAction = pPlan->rgCacheActions + pPlan->cCacheActions; + ++pPlan->cCacheActions; + +LExit: + return hr; +} + +static HRESULT AppendRollbackCacheAction( + __in BURN_PLAN* pPlan, + __out BURN_CACHE_ACTION** ppCacheAction + ) +{ + HRESULT hr = S_OK; + + hr = MemEnsureArraySize(reinterpret_cast(&pPlan->rgRollbackCacheActions), pPlan->cRollbackCacheActions + 1, sizeof(BURN_CACHE_ACTION), 5); + ExitOnFailure(hr, "Failed to grow plan's array of rollback cache actions."); + + *ppCacheAction = pPlan->rgRollbackCacheActions + pPlan->cRollbackCacheActions; + ++pPlan->cRollbackCacheActions; + +LExit: + return hr; +} + +static HRESULT ProcessPayloadGroup( + __in BURN_PLAN* pPlan, + __in BURN_PAYLOAD_GROUP* pPayloadGroup + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < pPayloadGroup->cItems; ++i) + { + BURN_PAYLOAD_GROUP_ITEM* pItem = pPayloadGroup->rgItems + i; + BURN_PAYLOAD* pPayload = pItem->pPayload; + + pPayload->cRemainingInstances += 1; + + if (pPayload->pContainer && !pPayload->pContainer->fPlanned) + { + hr = PlanLayoutContainer(pPlan, pPayload->pContainer); + ExitOnFailure(hr, "Failed to plan container: %ls", pPayload->pContainer->sczId); + } + + if (!pPlan->sczLayoutDirectory || !pPayload->pContainer) + { + // Acquire + Verify + Finalize + pPlan->qwCacheSizeTotal += 3 * pPayload->qwFileSize; + + if (!pPlan->sczLayoutDirectory) + { + // Staging + pPlan->qwCacheSizeTotal += pPayload->qwFileSize; + } + } + + if (!pPlan->sczLayoutDirectory && pPayload->pContainer && 1 == pPayload->cRemainingInstances) + { + // Extract + pPlan->qwCacheSizeTotal += pPayload->qwFileSize; + pPayload->pContainer->qwExtractSizeTotal += pPayload->qwFileSize; + } + + if (!pPayload->sczUnverifiedPath) + { + hr = CacheCalculatePayloadWorkingPath(pPlan->wzBundleId, pPayload, &pPayload->sczUnverifiedPath); + ExitOnFailure(hr, "Failed to calculate unverified path for payload."); + } + } + +LExit: + return hr; +} + +static void RemoveUnnecessaryActions( + __in BOOL fExecute, + __in BURN_EXECUTE_ACTION* rgActions, + __in DWORD cActions + ) +{ + LPCSTR szExecuteOrRollback = fExecute ? "execute" : "rollback"; + + for (DWORD i = 0; i < cActions; ++i) + { + BURN_EXECUTE_ACTION* pAction = rgActions + i; + + if (BURN_EXECUTE_ACTION_TYPE_MSP_TARGET == pAction->type && pAction->mspTarget.pChainedTargetPackage) + { + BURN_MSPTARGETPRODUCT* pFirstTargetProduct = pAction->mspTarget.rgOrderedPatches->pTargetProduct; + BURN_PATCH_SKIP_STATE skipState = fExecute ? pFirstTargetProduct->executeSkip : pFirstTargetProduct->rollbackSkip; + BOOTSTRAPPER_ACTION_STATE chainedTargetPackageAction = fExecute ? pAction->mspTarget.pChainedTargetPackage->execute : pAction->mspTarget.pChainedTargetPackage->rollback; + + switch (skipState) + { + case BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL: + pAction->fDeleted = TRUE; + LogId(REPORT_STANDARD, MSG_PLAN_SKIP_PATCH_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback); + break; + case BURN_PATCH_SKIP_STATE_SLIPSTREAM: + pAction->fDeleted = TRUE; + LogId(REPORT_STANDARD, MSG_PLAN_SKIP_SLIPSTREAM_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback); + break; + } + } + } +} + +static void FinalizePatchActions( + __in BOOL fExecute, + __in BURN_EXECUTE_ACTION* rgActions, + __in DWORD cActions + ) +{ + for (DWORD i = 0; i < cActions; ++i) + { + BURN_EXECUTE_ACTION* pAction = rgActions + i; + + if (BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE == pAction->type) + { + BURN_PACKAGE* pPackage = pAction->msiPackage.pPackage; + AssertSz(BOOTSTRAPPER_ACTION_STATE_NONE < pAction->msiPackage.action, "Planned execute MSI action to do nothing"); + + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->msiPackage.action) + { + // If we are uninstalling the MSI, we must skip all the patches. + for (DWORD j = 0; j < pPackage->Msi.cChainedPatches; ++j) + { + BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + j; + BURN_MSPTARGETPRODUCT* pTargetProduct = pChainedPatch->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex; + + if (fExecute) + { + pTargetProduct->execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; + pTargetProduct->executeSkip = BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL; + } + else + { + pTargetProduct->rollback = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; + pTargetProduct->rollbackSkip = BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL; + } + } + } + else + { + // If the slipstream target is being installed or upgraded (not uninstalled or repaired) then we will slipstream so skip + // the patch's standalone action. Also, if the slipstream target is being repaired and the patch is being + // repaired, skip this operation since it will be redundant. + // + // The primary goal here is to ensure that a slipstream patch that is yet not installed is installed even if the MSI + // is already on the machine. The slipstream must be installed standalone if the MSI is being repaired. + for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) + { + BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + j; + BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + pSlipstreamMsp->dwMsiChainedPatchIndex; + BURN_MSPTARGETPRODUCT* pTargetProduct = pSlipstreamMsp->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex; + BOOTSTRAPPER_ACTION_STATE action = fExecute ? pTargetProduct->execute : pTargetProduct->rollback; + BOOL fSlipstream = BOOTSTRAPPER_ACTION_STATE_UNINSTALL < action && + (BOOTSTRAPPER_ACTION_STATE_REPAIR != pAction->msiPackage.action || BOOTSTRAPPER_ACTION_STATE_REPAIR == action); + + if (fSlipstream) + { + if (fExecute) + { + pSlipstreamMsp->execute = action; + pTargetProduct->executeSkip = BURN_PATCH_SKIP_STATE_SLIPSTREAM; + } + else + { + pSlipstreamMsp->rollback = action; + pTargetProduct->rollbackSkip = BURN_PATCH_SKIP_STATE_SLIPSTREAM; + } + } + } + } + } + } +} + +static void CalculateExpectedRegistrationStates( + __in BURN_PACKAGE* rgPackages, + __in DWORD cPackages + ) +{ + for (DWORD i = 0; i < cPackages; ++i) + { + BURN_PACKAGE* pPackage = rgPackages + i; + + // MspPackages can have actions throughout the plan, so the plan needed to be finalized before anything could be calculated. + if (BURN_PACKAGE_TYPE_MSP == pPackage->type && !pPackage->fDependencyManagerWasHere) + { + pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; + pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; + + for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + j; + + // The highest aggregate action state found will be used. + if (pPackage->execute < pTargetProduct->execute) + { + pPackage->execute = pTargetProduct->execute; + } + + if (pPackage->rollback < pTargetProduct->rollback) + { + pPackage->rollback = pTargetProduct->rollback; + } + } + } + + if (pPackage->fCanAffectRegistration) + { + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pPackage->execute) + { + pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + else if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) + { + pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + + if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute) + { + if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pPackage->expectedCacheRegistrationState) + { + pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pPackage->expectedInstallRegistrationState) + { + pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + } + else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pPackage->dependencyExecute) + { + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->expectedCacheRegistrationState) + { + pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->expectedInstallRegistrationState) + { + pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; + } + } + } + } +} + +static HRESULT PlanDependencyActions( + __in BOOL fBundlePerMachine, + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + + hr = DependencyPlanPackageBegin(fBundlePerMachine, pPackage, pPlan); + ExitOnFailure(hr, "Failed to begin plan dependency actions for package: %ls", pPackage->sczId); + + hr = DependencyPlanPackage(NULL, pPackage, pPlan); + ExitOnFailure(hr, "Failed to plan package dependency actions."); + + hr = DependencyPlanPackageComplete(pPackage, pPlan); + ExitOnFailure(hr, "Failed to complete plan dependency actions for package: %ls", pPackage->sczId); + +LExit: + return hr; +} + +static HRESULT CalculateExecuteActions( + __in BURN_PACKAGE* pPackage, + __in_opt BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary + ) +{ + HRESULT hr = S_OK; + BOOL fInsideMsiTransaction = pActiveRollbackBoundary && pActiveRollbackBoundary->fTransaction; + + // Calculate execute actions. + switch (pPackage->type) + { + case BURN_PACKAGE_TYPE_EXE: + hr = ExeEnginePlanCalculatePackage(pPackage); + break; + + case BURN_PACKAGE_TYPE_MSI: + hr = MsiEnginePlanCalculatePackage(pPackage, fInsideMsiTransaction); + break; + + case BURN_PACKAGE_TYPE_MSP: + hr = MspEnginePlanCalculatePackage(pPackage, fInsideMsiTransaction); + break; + + case BURN_PACKAGE_TYPE_MSU: + hr = MsuEnginePlanCalculatePackage(pPackage); + break; + + default: + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Invalid package type."); + } + +LExit: + return hr; +} + +static BOOL NeedsCache( + __in BURN_PACKAGE* pPackage, + __in BOOL fExecute + ) +{ + BOOTSTRAPPER_ACTION_STATE action = fExecute ? pPackage->execute : pPackage->rollback; + if (BURN_PACKAGE_TYPE_EXE == pPackage->type) // Exe packages require the package for all operations (even uninstall). + { + return BOOTSTRAPPER_ACTION_STATE_NONE != action; + } + else // The other package types can uninstall without the original package. + { + return BOOTSTRAPPER_ACTION_STATE_UNINSTALL < action; + } +} + +static BOOL ForceCache( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage + ) +{ + // All packages that have cacheType set to force should be cached if the bundle is going to be present. + return BOOTSTRAPPER_CACHE_TYPE_FORCE == pPackage->cacheType && BOOTSTRAPPER_ACTION_UNINSTALL < pPlan->action; +} + +static void CacheActionLog( + __in DWORD iAction, + __in BURN_CACHE_ACTION* pAction, + __in BOOL fRollback + ) +{ + LPCWSTR wzBase = fRollback ? L" Rollback cache" : L" Cache"; + switch (pAction->type) + { + case BURN_CACHE_ACTION_TYPE_CHECKPOINT: + LogStringLine(PlanDumpLevel, "%ls action[%u]: CHECKPOINT id: %u", wzBase, iAction, pAction->checkpoint.dwId); + break; + + case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: + LogStringLine(PlanDumpLevel, "%ls action[%u]: LAYOUT_BUNDLE working path: %ls, exe name: %ls", wzBase, iAction, pAction->bundleLayout.sczUnverifiedPath, pAction->bundleLayout.sczExecutableName); + break; + + case BURN_CACHE_ACTION_TYPE_CONTAINER: + LogStringLine(PlanDumpLevel, "%ls action[%u]: CONTAINER container id: %ls, working path: %ls", wzBase, iAction, pAction->container.pContainer->sczId, pAction->container.pContainer->sczUnverifiedPath); + break; + + case BURN_CACHE_ACTION_TYPE_PACKAGE: + LogStringLine(PlanDumpLevel, "%ls action[%u]: PACKAGE id: %ls", wzBase, iAction, pAction->package.pPackage->sczId); + break; + + case BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE: + LogStringLine(PlanDumpLevel, "%ls action[%u]: ROLLBACK_PACKAGE id: %ls", wzBase, iAction, pAction->rollbackPackage.pPackage->sczId); + break; + + case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT: + LogStringLine(PlanDumpLevel, "%ls action[%u]: SIGNAL_SYNCPOINT event handle: 0x%p", wzBase, iAction, pAction->syncpoint.hEvent); + break; + + default: + AssertSz(FALSE, "Unknown cache action type."); + break; + } +} + +static void ExecuteActionLog( + __in DWORD iAction, + __in BURN_EXECUTE_ACTION* pAction, + __in BOOL fRollback + ) +{ + LPCWSTR wzBase = fRollback ? L" Rollback" : L" Execute"; + switch (pAction->type) + { + case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT: + LogStringLine(PlanDumpLevel, "%ls action[%u]: CHECKPOINT id: %u, msi transaction id: %ls", wzBase, iAction, pAction->checkpoint.dwId, pAction->checkpoint.pActiveRollbackBoundary && pAction->checkpoint.pActiveRollbackBoundary->fTransaction ? pAction->checkpoint.pActiveRollbackBoundary->sczId : L"(none)"); + break; + + case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER: + LogStringLine(PlanDumpLevel, "%ls action[%u]: PACKAGE_PROVIDER package id: %ls, action: %hs", wzBase, iAction, pAction->packageProvider.pPackage->sczId, LoggingDependencyActionToString(pAction->packageProvider.action)); + break; + + case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: + LogStringLine(PlanDumpLevel, "%ls action[%u]: PACKAGE_DEPENDENCY package id: %ls, bundle provider key: %ls, action: %hs", wzBase, iAction, pAction->packageDependency.pPackage->sczId, pAction->packageDependency.sczBundleProviderKey, LoggingDependencyActionToString(pAction->packageDependency.action)); + break; + + case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: + LogStringLine(PlanDumpLevel, "%ls action[%u]: EXE_PACKAGE package id: %ls, action: %hs, ignore dependencies: %ls", wzBase, iAction, pAction->exePackage.pPackage->sczId, LoggingActionStateToString(pAction->exePackage.action), pAction->exePackage.sczIgnoreDependencies); + break; + + case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: + LogStringLine(PlanDumpLevel, "%ls action[%u]: MSI_PACKAGE package id: %ls, action: %hs, action msi property: %ls, ui level: %u, disable externaluihandler: %ls, log path: %ls, logging attrib: %u", wzBase, iAction, pAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pAction->msiPackage.action), LoggingBurnMsiPropertyToString(pAction->msiPackage.actionMsiProperty), pAction->msiPackage.uiLevel, pAction->msiPackage.fDisableExternalUiHandler ? L"yes" : L"no", pAction->msiPackage.sczLogPath, pAction->msiPackage.dwLoggingAttributes); + for (DWORD j = 0; j < pAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++j) + { + const BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pAction->msiPackage.pPackage->Msi.rgSlipstreamMsps + j; + LogStringLine(PlanDumpLevel, " Patch[%u]: msp package id: %ls, action: %hs", j, pSlipstreamMsp->pMspPackage->sczId, LoggingActionStateToString(fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute)); + } + break; + + case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: + LogStringLine(PlanDumpLevel, "%ls action[%u]: MSP_TARGET package id: %ls, action: %hs, target product code: %ls, target per-machine: %ls, action msi property: %ls, ui level: %u, disable externaluihandler: %ls, log path: %ls", wzBase, iAction, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.sczTargetProductCode, pAction->mspTarget.fPerMachineTarget ? L"yes" : L"no", LoggingBurnMsiPropertyToString(pAction->mspTarget.actionMsiProperty), pAction->mspTarget.uiLevel, pAction->mspTarget.fDisableExternalUiHandler ? L"yes" : L"no", pAction->mspTarget.sczLogPath); + for (DWORD j = 0; j < pAction->mspTarget.cOrderedPatches; ++j) + { + LogStringLine(PlanDumpLevel, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->mspTarget.rgOrderedPatches[j].pTargetProduct->dwOrder, pAction->mspTarget.rgOrderedPatches[j].pPackage->sczId); + } + break; + + case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: + LogStringLine(PlanDumpLevel, "%ls action[%u]: MSU_PACKAGE package id: %ls, action: %hs, log path: %ls", wzBase, iAction, pAction->msuPackage.pPackage->sczId, LoggingActionStateToString(pAction->msuPackage.action), pAction->msuPackage.sczLogPath); + break; + + case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY: + LogStringLine(PlanDumpLevel, "%ls action[%u]: ROLLBACK_BOUNDARY id: %ls, vital: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no"); + break; + + case BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT: + LogStringLine(PlanDumpLevel, "%ls action[%u]: WAIT_SYNCPOINT event handle: 0x%p", wzBase, iAction, pAction->syncpoint.hEvent); + break; + + case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE: + LogStringLine(PlanDumpLevel, "%ls action[%u]: UNCACHE_PACKAGE id: %ls", wzBase, iAction, pAction->uncachePackage.pPackage->sczId); + break; + + case BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION: + LogStringLine(PlanDumpLevel, "%ls action[%u]: BEGIN_MSI_TRANSACTION id: %ls", wzBase, iAction, pAction->msiTransaction.pRollbackBoundary->sczId); + break; + + case BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION: + LogStringLine(PlanDumpLevel, "%ls action[%u]: COMMIT_MSI_TRANSACTION id: %ls", wzBase, iAction, pAction->msiTransaction.pRollbackBoundary->sczId); + break; + + default: + AssertSz(FALSE, "Unknown execute action type."); + break; + } + + if (pAction->fDeleted) + { + LogStringLine(PlanDumpLevel, " (deleted action)"); + } +} + +extern "C" void PlanDump( + __in BURN_PLAN* pPlan + ) +{ + LogStringLine(PlanDumpLevel, "--- Begin plan dump ---"); + + LogStringLine(PlanDumpLevel, "Plan action: %hs", LoggingBurnActionToString(pPlan->action)); + LogStringLine(PlanDumpLevel, " per-machine: %hs", LoggingTrueFalseToString(pPlan->fPerMachine)); + LogStringLine(PlanDumpLevel, " disable-rollback: %hs", LoggingTrueFalseToString(pPlan->fDisableRollback)); + LogStringLine(PlanDumpLevel, " estimated size: %llu", pPlan->qwEstimatedSize); + if (pPlan->sczLayoutDirectory) + { + LogStringLine(PlanDumpLevel, " layout directory: %ls", pPlan->sczLayoutDirectory); + } + + LogStringLine(PlanDumpLevel, "Plan cache size: %llu", pPlan->qwCacheSizeTotal); + for (DWORD i = 0; i < pPlan->cCacheActions; ++i) + { + CacheActionLog(i, pPlan->rgCacheActions + i, FALSE); + } + + for (DWORD i = 0; i < pPlan->cRollbackCacheActions; ++i) + { + CacheActionLog(i, pPlan->rgRollbackCacheActions + i, TRUE); + } + + LogStringLine(PlanDumpLevel, "Plan execute package count: %u", pPlan->cExecutePackagesTotal); + LogStringLine(PlanDumpLevel, " overall progress ticks: %u", pPlan->cOverallProgressTicksTotal); + for (DWORD i = 0; i < pPlan->cExecuteActions; ++i) + { + ExecuteActionLog(i, pPlan->rgExecuteActions + i, FALSE); + } + + for (DWORD i = 0; i < pPlan->cRollbackActions; ++i) + { + ExecuteActionLog(i, pPlan->rgRollbackActions + i, TRUE); + } + + for (DWORD i = 0; i < pPlan->cCleanActions; ++i) + { + LogStringLine(PlanDumpLevel, " Clean action[%u]: CLEAN_PACKAGE package id: %ls", i, pPlan->rgCleanActions[i].pPackage->sczId); + } + + for (DWORD i = 0; i < pPlan->cPlannedProviders; ++i) + { + LogStringLine(PlanDumpLevel, " Dependency action[%u]: PLANNED_PROVIDER key: %ls, name: %ls", i, pPlan->rgPlannedProviders[i].sczKey, pPlan->rgPlannedProviders[i].sczName); + } + + LogStringLine(PlanDumpLevel, "--- End plan dump ---"); +} diff --git a/src/burn/engine/plan.h b/src/burn/engine/plan.h new file mode 100644 index 00000000..00ab5516 --- /dev/null +++ b/src/burn/engine/plan.h @@ -0,0 +1,456 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + +const DWORD BURN_PLAN_INVALID_ACTION_INDEX = 0x80000000; + +enum BURN_REGISTRATION_ACTION_OPERATIONS +{ + BURN_REGISTRATION_ACTION_OPERATIONS_NONE = 0x0, + BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE = 0x1, + BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION = 0x2, + BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE = 0x4, +}; + +enum BURN_DEPENDENCY_REGISTRATION_ACTION +{ + BURN_DEPENDENCY_REGISTRATION_ACTION_NONE, + BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, + BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, +}; + +enum BURN_DEPENDENT_REGISTRATION_ACTION_TYPE +{ + BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_NONE, + BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, + BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER, +}; + +enum BURN_CACHE_ACTION_TYPE +{ + BURN_CACHE_ACTION_TYPE_NONE, + BURN_CACHE_ACTION_TYPE_CHECKPOINT, + BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE, + BURN_CACHE_ACTION_TYPE_PACKAGE, + BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE, + BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT, + BURN_CACHE_ACTION_TYPE_CONTAINER, +}; + +enum BURN_EXECUTE_ACTION_TYPE +{ + BURN_EXECUTE_ACTION_TYPE_NONE, + BURN_EXECUTE_ACTION_TYPE_CHECKPOINT, + BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT, + BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE, + BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE, + BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE, + BURN_EXECUTE_ACTION_TYPE_MSP_TARGET, + BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE, + BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER, + BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY, + BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY, + BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION, + BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION, +}; + +enum BURN_CLEAN_ACTION_TYPE +{ + BURN_CLEAN_ACTION_TYPE_NONE, + BURN_CLEAN_ACTION_TYPE_BUNDLE, + BURN_CLEAN_ACTION_TYPE_PACKAGE, +}; + + +// structs + +typedef struct _BURN_DEPENDENT_REGISTRATION_ACTION +{ + BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type; + LPWSTR sczBundleId; + LPWSTR sczDependentProviderKey; +} BURN_DEPENDENT_REGISTRATION_ACTION; + +typedef struct _BURN_CACHE_CONTAINER_PROGRESS +{ + LPWSTR wzId; + DWORD iIndex; + BOOL fCachedDuringApply; + BURN_CONTAINER* pContainer; +} BURN_CACHE_CONTAINER_PROGRESS; + +typedef struct _BURN_CACHE_PAYLOAD_PROGRESS +{ + LPWSTR wzId; + DWORD iIndex; + BOOL fCachedDuringApply; + BURN_PAYLOAD* pPayload; +} BURN_CACHE_PAYLOAD_PROGRESS; + +typedef struct _BURN_CACHE_ACTION +{ + BURN_CACHE_ACTION_TYPE type; + union + { + struct + { + DWORD dwId; + } checkpoint; + struct + { + LPWSTR sczExecutableName; + LPWSTR sczUnverifiedPath; + DWORD64 qwBundleSize; + BURN_PAYLOAD_GROUP* pPayloadGroup; + } bundleLayout; + struct + { + BURN_PACKAGE* pPackage; + } package; + struct + { + BURN_PACKAGE* pPackage; + } rollbackPackage; + struct + { + HANDLE hEvent; + } syncpoint; + struct + { + BURN_CONTAINER* pContainer; + } container; + }; +} BURN_CACHE_ACTION; + +typedef struct _BURN_ORDERED_PATCHES +{ + BURN_PACKAGE* pPackage; + + BURN_MSPTARGETPRODUCT* pTargetProduct; // only valid in the unelevated engine. +} BURN_ORDERED_PATCHES; + +typedef struct _BURN_EXECUTE_ACTION_CHECKPOINT +{ + DWORD dwId; + BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary; +} BURN_EXECUTE_ACTION_CHECKPOINT; + +typedef struct _BURN_EXECUTE_ACTION +{ + BURN_EXECUTE_ACTION_TYPE type; + BOOL fDeleted; // used to skip an action after it was planned since deleting actions out of the plan is too hard. + union + { + BURN_EXECUTE_ACTION_CHECKPOINT checkpoint; + struct + { + HANDLE hEvent; + } syncpoint; + struct + { + BURN_PACKAGE* pPackage; + } uncachePackage; + struct + { + BURN_PACKAGE* pPackage; + BOOL fFireAndForget; + BOOTSTRAPPER_ACTION_STATE action; + LPWSTR sczIgnoreDependencies; + LPWSTR sczAncestors; + } exePackage; + struct + { + BURN_PACKAGE* pPackage; + LPWSTR sczLogPath; + DWORD dwLoggingAttributes; + BURN_MSI_PROPERTY actionMsiProperty; + INSTALLUILEVEL uiLevel; + BOOL fDisableExternalUiHandler; + BOOTSTRAPPER_ACTION_STATE action; + + BOOTSTRAPPER_FEATURE_ACTION* rgFeatures; + } msiPackage; + struct + { + BURN_PACKAGE* pPackage; + LPWSTR sczTargetProductCode; + BURN_PACKAGE* pChainedTargetPackage; + BOOL fSlipstream; + BOOL fPerMachineTarget; + LPWSTR sczLogPath; + BURN_MSI_PROPERTY actionMsiProperty; + INSTALLUILEVEL uiLevel; + BOOL fDisableExternalUiHandler; + BOOTSTRAPPER_ACTION_STATE action; + + BURN_ORDERED_PATCHES* rgOrderedPatches; + DWORD cOrderedPatches; + } mspTarget; + struct + { + BURN_PACKAGE* pPackage; + LPWSTR sczLogPath; + BOOTSTRAPPER_ACTION_STATE action; + } msuPackage; + struct + { + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary; + } rollbackBoundary; + struct + { + BURN_PACKAGE* pPackage; + BURN_DEPENDENCY_ACTION action; + } packageProvider; + struct + { + BURN_PACKAGE* pPackage; + LPWSTR sczBundleProviderKey; + BURN_DEPENDENCY_ACTION action; + } packageDependency; + struct + { + BURN_ROLLBACK_BOUNDARY* pRollbackBoundary; + } msiTransaction; + }; +} BURN_EXECUTE_ACTION; + +typedef struct _BURN_CLEAN_ACTION +{ + BURN_PACKAGE* pPackage; +} BURN_CLEAN_ACTION; + +typedef struct _BURN_PLAN +{ + BOOTSTRAPPER_ACTION action; + BURN_PAYLOADS* pPayloads; // points directly into parent the ENGINE_STATE. + LPWSTR wzBundleId; // points directly into parent the ENGINE_STATE. + LPWSTR wzBundleProviderKey; // points directly into parent the ENGINE_STATE. + BOOL fPerMachine; + BOOL fCanAffectMachineState; + DWORD dwRegistrationOperations; + BOOL fDisallowRemoval; + BOOL fDisableRollback; + BOOL fAffectedMachineState; + BOOL fIgnoreAllDependents; + LPWSTR sczLayoutDirectory; + + DWORD64 qwCacheSizeTotal; + + DWORD64 qwEstimatedSize; + + DWORD cExecutePackagesTotal; + DWORD cOverallProgressTicksTotal; + + BOOL fEnabledForwardCompatibleBundle; + BURN_PACKAGE forwardCompatibleBundle; + + BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction; + + BURN_DEPENDENT_REGISTRATION_ACTION* rgRegistrationActions; + DWORD cRegistrationActions; + + BURN_DEPENDENT_REGISTRATION_ACTION* rgRollbackRegistrationActions; + DWORD cRollbackRegistrationActions; + + BURN_CACHE_ACTION* rgCacheActions; + DWORD cCacheActions; + + BURN_CACHE_ACTION* rgRollbackCacheActions; + DWORD cRollbackCacheActions; + + BURN_EXECUTE_ACTION* rgExecuteActions; + DWORD cExecuteActions; + + BURN_EXECUTE_ACTION* rgRollbackActions; + DWORD cRollbackActions; + + BURN_CLEAN_ACTION* rgCleanActions; + DWORD cCleanActions; + + DEPENDENCY* rgPlannedProviders; + UINT cPlannedProviders; + + BURN_CACHE_CONTAINER_PROGRESS* rgContainerProgress; + DWORD cContainerProgress; + STRINGDICT_HANDLE shContainerProgress; + + BURN_CACHE_PAYLOAD_PROGRESS* rgPayloadProgress; + DWORD cPayloadProgress; + STRINGDICT_HANDLE shPayloadProgress; + + DWORD dwNextCheckpointId; // for plan internal use + BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary; // for plan internal use +} BURN_PLAN; + + +// functions + +void PlanReset( + __in BURN_PLAN* pPlan, + __in BURN_CONTAINERS* pContainers, + __in BURN_PACKAGES* pPackages, + __in BURN_PAYLOAD_GROUP* pLayoutPayloads + ); +void PlanUninitializeExecuteAction( + __in BURN_EXECUTE_ACTION* pExecuteAction + ); +HRESULT PlanSetVariables( + __in BOOTSTRAPPER_ACTION action, + __in BURN_VARIABLES* pVariables + ); +HRESULT PlanDefaultPackageRequestState( + __in BURN_PACKAGE_TYPE packageType, + __in BOOTSTRAPPER_PACKAGE_STATE currentState, + __in BOOL fPermanent, + __in BOOTSTRAPPER_ACTION action, + __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __out BOOTSTRAPPER_REQUEST_STATE* pRequestState + ); +HRESULT PlanLayoutBundle( + __in BURN_PLAN* pPlan, + __in_z LPCWSTR wzExecutableName, + __in DWORD64 qwBundleSize, + __in BURN_VARIABLES* pVariables, + __in BURN_PAYLOAD_GROUP* pLayoutPayloads + ); +HRESULT PlanForwardCompatibleBundles( + __in BURN_USER_EXPERIENCE* pUX, + __in BOOTSTRAPPER_COMMAND* pCommand, + __in BURN_PLAN* pPlan, + __in BURN_REGISTRATION* pRegistration, + __in BOOTSTRAPPER_ACTION action + ); +HRESULT PlanPackages( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PACKAGES* pPackages, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RELATION_TYPE relationType + ); +HRESULT PlanRegistration( + __in BURN_PLAN* pPlan, + __in BURN_REGISTRATION* pRegistration, + __in BOOTSTRAPPER_RESUME_TYPE resumeType, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __inout BOOL* pfContinuePlanning + ); +HRESULT PlanPassThroughBundle( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RELATION_TYPE relationType + ); +HRESULT PlanUpdateBundle( + __in BURN_USER_EXPERIENCE* pUX, + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in BOOTSTRAPPER_DISPLAY display, + __in BOOTSTRAPPER_RELATION_TYPE relationType + ); +HRESULT PlanLayoutContainer( + __in BURN_PLAN* pPlan, + __in BURN_CONTAINER* pContainer + ); +HRESULT PlanLayoutPackage( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage + ); +HRESULT PlanExecutePackage( + __in BOOL fPerMachine, + __in BOOTSTRAPPER_DISPLAY display, + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __inout HANDLE* phSyncpointEvent + ); +HRESULT PlanDefaultRelatedBundleRequestState( + __in BOOTSTRAPPER_RELATION_TYPE commandRelationType, + __in BOOTSTRAPPER_RELATION_TYPE relatedBundleRelationType, + __in BOOTSTRAPPER_ACTION action, + __in VERUTIL_VERSION* pRegistrationVersion, + __in VERUTIL_VERSION* pRelatedBundleVersion, + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestState + ); +HRESULT PlanRelatedBundlesBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BURN_REGISTRATION* pRegistration, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in BURN_PLAN* pPlan + ); +HRESULT PlanRelatedBundlesComplete( + __in BURN_REGISTRATION* pRegistration, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables, + __in DWORD dwExecuteActionEarlyIndex + ); +HRESULT PlanFinalizeActions( + __in BURN_PLAN* pPlan + ); +HRESULT PlanCleanPackage( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage + ); +HRESULT PlanExecuteCacheSyncAndRollback( + __in BURN_PLAN* pPlan, + __in BURN_PACKAGE* pPackage, + __in HANDLE hCacheEvent + ); +HRESULT PlanExecuteCheckpoint( + __in BURN_PLAN* pPlan + ); +HRESULT PlanInsertExecuteAction( + __in DWORD dwIndex, + __in BURN_PLAN* pPlan, + __out BURN_EXECUTE_ACTION** ppExecuteAction + ); +HRESULT PlanInsertRollbackAction( + __in DWORD dwIndex, + __in BURN_PLAN* pPlan, + __out BURN_EXECUTE_ACTION** ppRollbackAction + ); +HRESULT PlanAppendExecuteAction( + __in BURN_PLAN* pPlan, + __out BURN_EXECUTE_ACTION** ppExecuteAction + ); +HRESULT PlanAppendRollbackAction( + __in BURN_PLAN* pPlan, + __out BURN_EXECUTE_ACTION** ppExecuteAction + ); +HRESULT PlanRollbackBoundaryBegin( + __in BURN_PLAN* pPlan, + __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary + ); +HRESULT PlanRollbackBoundaryComplete( + __in BURN_PLAN* pPlan + ); +HRESULT PlanSetResumeCommand( + __in BURN_REGISTRATION* pRegistration, + __in BOOTSTRAPPER_ACTION action, + __in BOOTSTRAPPER_COMMAND* pCommand, + __in BURN_LOGGING* pLog + ); +void PlanDump( + __in BURN_PLAN* pPlan + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/platform.cpp b/src/burn/engine/platform.cpp new file mode 100644 index 00000000..9469ff49 --- /dev/null +++ b/src/burn/engine/platform.cpp @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// variables + +PFN_INITIATESYSTEMSHUTDOWNEXW vpfnInitiateSystemShutdownExW; + + +// function definitions + +extern "C" void PlatformInitialize() +{ + vpfnInitiateSystemShutdownExW = ::InitiateSystemShutdownExW; +} diff --git a/src/burn/engine/platform.h b/src/burn/engine/platform.h new file mode 100644 index 00000000..3681f248 --- /dev/null +++ b/src/burn/engine/platform.h @@ -0,0 +1,34 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// typedefs + +typedef BOOL (WINAPI *PFN_INITIATESYSTEMSHUTDOWNEXW)( + __in_opt LPWSTR lpMachineName, + __in_opt LPWSTR lpMessage, + __in DWORD dwTimeout, + __in BOOL bForceAppsClosed, + __in BOOL bRebootAfterShutdown, + __in DWORD dwReason + ); + + +// variable declarations + +extern PFN_INITIATESYSTEMSHUTDOWNEXW vpfnInitiateSystemShutdownExW; + + +// function declarations + +void PlatformInitialize(); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/precomp.cpp b/src/burn/engine/precomp.cpp new file mode 100644 index 00000000..37664a1c --- /dev/null +++ b/src/burn/engine/precomp.cpp @@ -0,0 +1,3 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" diff --git a/src/burn/engine/precomp.h b/src/burn/engine/precomp.h new file mode 100644 index 00000000..11b594da --- /dev/null +++ b/src/burn/engine/precomp.h @@ -0,0 +1,102 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "BootstrapperEngine.h" +#include "BootstrapperApplication.h" +#include "BundleExtensionEngine.h" +#include "BundleExtension.h" + +#include "platform.h" +#include "variant.h" +#include "variable.h" +#include "condition.h" +#include "section.h" +#include "approvedexe.h" +#include "container.h" +#include "payload.h" +#include "cabextract.h" +#include "burnextension.h" +#include "search.h" +#include "userexperience.h" +#include "package.h" +#include "update.h" +#include "pseudobundle.h" +#include "registration.h" +#include "relatedbundle.h" +#include "detect.h" +#include "plan.h" +#include "logging.h" +#include "pipe.h" +#include "core.h" +#include "cache.h" +#include "apply.h" +#include "exeengine.h" +#include "msiengine.h" +#include "mspengine.h" +#include "msuengine.h" +#include "dependency.h" +#include "elevation.h" +#include "embedded.h" +#include "manifest.h" +#include "splashscreen.h" +#include "uithread.h" +#include "netfxchainer.h" + +#include "externalengine.h" +#include "EngineForApplication.h" +#include "EngineForExtension.h" +#include "engine.messages.h" diff --git a/src/burn/engine/pseudobundle.cpp b/src/burn/engine/pseudobundle.cpp new file mode 100644 index 00000000..180cc621 --- /dev/null +++ b/src/burn/engine/pseudobundle.cpp @@ -0,0 +1,241 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +extern "C" HRESULT PseudoBundleInitialize( + __in DWORD64 qwEngineVersion, + __in BURN_PACKAGE* pPackage, + __in BOOL fPerMachine, + __in_z LPCWSTR wzId, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in BOOTSTRAPPER_PACKAGE_STATE state, + __in BOOL fCached, + __in_z LPCWSTR wzFilePath, + __in_z LPCWSTR wzLocalSource, + __in_z_opt LPCWSTR wzDownloadSource, + __in DWORD64 qwSize, + __in BOOL fVital, + __in_z_opt LPCWSTR wzInstallArguments, + __in_z_opt LPCWSTR wzRepairArguments, + __in_z_opt LPCWSTR wzUninstallArguments, + __in_opt BURN_DEPENDENCY_PROVIDER* pDependencyProvider, + __in_opt const BYTE* pbHash, + __in const DWORD cbHash + ) +{ + HRESULT hr = S_OK; + LPWSTR sczRelationTypeCommandLineSwitch = NULL; + BURN_PAYLOAD* pPayload = NULL; + + LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType); + if (wzRelationTypeCommandLine) + { + hr = StrAllocFormatted(&sczRelationTypeCommandLineSwitch, L" -%ls", wzRelationTypeCommandLine); + } + + // Initialize the single payload, and fill out all the necessary fields + pPackage->payloads.rgItems = (BURN_PAYLOAD_GROUP_ITEM*)MemAlloc(sizeof(BURN_PAYLOAD_GROUP_ITEM), TRUE); + ExitOnNull(pPackage->payloads.rgItems, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload group inside of related bundle struct"); + pPackage->payloads.cItems = 1; + + pPayload = (BURN_PAYLOAD*)MemAlloc(sizeof(BURN_PAYLOAD), TRUE); + ExitOnNull(pPayload, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload inside of related bundle struct"); + pPackage->payloads.rgItems[0].pPayload = pPayload; + pPayload->packaging = BURN_PAYLOAD_PACKAGING_EXTERNAL; + pPayload->qwFileSize = qwSize; + + hr = StrAllocString(&pPayload->sczKey, wzId, 0); + ExitOnFailure(hr, "Failed to copy key for pseudo bundle payload."); + + hr = StrAllocString(&pPayload->sczFilePath, wzFilePath, 0); + ExitOnFailure(hr, "Failed to copy filename for pseudo bundle."); + + hr = StrAllocString(&pPayload->sczSourcePath, wzLocalSource, 0); + ExitOnFailure(hr, "Failed to copy local source path for pseudo bundle."); + + if (wzDownloadSource && *wzDownloadSource) + { + hr = StrAllocString(&pPayload->downloadSource.sczUrl, wzDownloadSource, 0); + ExitOnFailure(hr, "Failed to copy download source for pseudo bundle."); + } + + if (pbHash) + { + pPayload->pbHash = static_cast(MemAlloc(cbHash, FALSE)); + ExitOnNull(pPayload->pbHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for pseudo bundle payload hash."); + + pPayload->cbHash = cbHash; + memcpy_s(pPayload->pbHash, pPayload->cbHash, pbHash, cbHash); + } + + pPackage->Exe.fPseudoBundle = TRUE; + + pPackage->type = BURN_PACKAGE_TYPE_EXE; + pPackage->fPerMachine = fPerMachine; + pPackage->currentState = state; + pPackage->fCached = fCached; + pPackage->qwInstallSize = qwSize; + pPackage->qwSize = qwSize; + pPackage->fVital = fVital; + + hr = StrAllocString(&pPackage->sczId, wzId, 0); + ExitOnFailure(hr, "Failed to copy key for pseudo bundle."); + + hr = StrAllocString(&pPackage->sczCacheId, wzId, 0); + ExitOnFailure(hr, "Failed to copy cache id for pseudo bundle."); + + // If we are a self updating bundle, we don't have to have Install arguments. + if (wzInstallArguments) + { + hr = StrAllocString(&pPackage->Exe.sczInstallArguments, wzInstallArguments, 0); + ExitOnFailure(hr, "Failed to copy install arguments for related bundle package"); + } + + if (sczRelationTypeCommandLineSwitch) + { + hr = StrAllocConcat(&pPackage->Exe.sczInstallArguments, sczRelationTypeCommandLineSwitch, 0); + ExitOnFailure(hr, "Failed to append relation type to install arguments for related bundle package"); + } + + if (wzRepairArguments) + { + hr = StrAllocString(&pPackage->Exe.sczRepairArguments, wzRepairArguments, 0); + ExitOnFailure(hr, "Failed to copy repair arguments for related bundle package"); + + if (sczRelationTypeCommandLineSwitch) + { + hr = StrAllocConcat(&pPackage->Exe.sczRepairArguments, sczRelationTypeCommandLineSwitch, 0); + ExitOnFailure(hr, "Failed to append relation type to repair arguments for related bundle package"); + } + + pPackage->Exe.fRepairable = TRUE; + } + + if (wzUninstallArguments) + { + hr = StrAllocString(&pPackage->Exe.sczUninstallArguments, wzUninstallArguments, 0); + ExitOnFailure(hr, "Failed to copy uninstall arguments for related bundle package"); + + if (sczRelationTypeCommandLineSwitch) + { + hr = StrAllocConcat(&pPackage->Exe.sczUninstallArguments, sczRelationTypeCommandLineSwitch, 0); + ExitOnFailure(hr, "Failed to append relation type to uninstall arguments for related bundle package"); + } + + pPackage->fUninstallable = TRUE; + } + + // Only support progress from engines that are compatible (aka: version greater than or equal to last protocol breaking change *and* versions that are older or the same as this engine). + pPackage->Exe.protocol = (FILEMAKEVERSION(3, 6, 2221, 0) <= qwEngineVersion && qwEngineVersion <= FILEMAKEVERSION(rmj, rmm, rup, rpr)) ? BURN_EXE_PROTOCOL_TYPE_BURN : BURN_EXE_PROTOCOL_TYPE_NONE; + + // All versions of Burn past v3.9 RTM support suppressing ancestors. + pPackage->Exe.fSupportsAncestors = FILEMAKEVERSION(3, 9, 1006, 0) <= qwEngineVersion; + + if (pDependencyProvider) + { + pPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER), TRUE); + ExitOnNull(pPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers."); + pPackage->cDependencyProviders = 1; + + pPackage->rgDependencyProviders[0].fImported = pDependencyProvider->fImported; + + hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczKey, pDependencyProvider->sczKey, 0); + ExitOnFailure(hr, "Failed to copy key for pseudo bundle."); + + hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczVersion, pDependencyProvider->sczVersion, 0); + ExitOnFailure(hr, "Failed to copy version for pseudo bundle."); + + hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczDisplayName, pDependencyProvider->sczDisplayName, 0); + ExitOnFailure(hr, "Failed to copy display name for pseudo bundle."); + } + +LExit: + ReleaseStr(sczRelationTypeCommandLineSwitch); + + return hr; +} + +extern "C" HRESULT PseudoBundleInitializePassthrough( + __in BURN_PACKAGE* pPassthroughPackage, + __in BOOTSTRAPPER_COMMAND* pCommand, + __in_z_opt LPCWSTR wzAppendLogPath, + __in_z_opt LPCWSTR wzActiveParent, + __in_z_opt LPCWSTR wzAncestors, + __in BURN_PACKAGE* pPackage + ) +{ + Assert(BURN_PACKAGE_TYPE_EXE == pPackage->type); + + HRESULT hr = S_OK; + LPWSTR sczArguments = NULL; + + // Initialize the payloads, and copy the necessary fields. + pPassthroughPackage->payloads.rgItems = (BURN_PAYLOAD_GROUP_ITEM*)MemAlloc(sizeof(BURN_PAYLOAD_GROUP_ITEM) * pPackage->payloads.cItems, TRUE); + ExitOnNull(pPassthroughPackage->payloads.rgItems, hr, E_OUTOFMEMORY, "Failed to allocate space for burn package payload inside of passthrough bundle."); + pPassthroughPackage->payloads.cItems = pPackage->payloads.cItems; + + for (DWORD iPayload = 0; iPayload < pPackage->payloads.cItems; ++iPayload) + { + pPassthroughPackage->payloads.rgItems[iPayload].pPayload = pPackage->payloads.rgItems[iPayload].pPayload; + } + + pPassthroughPackage->Exe.fPseudoBundle = TRUE; + + pPassthroughPackage->fPerMachine = FALSE; // passthrough bundles are always launched per-user. + pPassthroughPackage->type = pPackage->type; + pPassthroughPackage->currentState = pPackage->currentState; + pPassthroughPackage->fCached = pPackage->fCached; + pPassthroughPackage->qwInstallSize = pPackage->qwInstallSize; + pPassthroughPackage->qwSize = pPackage->qwSize; + pPassthroughPackage->fVital = pPackage->fVital; + + hr = StrAllocString(&pPassthroughPackage->sczId, pPackage->sczId, 0); + ExitOnFailure(hr, "Failed to copy key for passthrough pseudo bundle."); + + hr = StrAllocString(&pPassthroughPackage->sczCacheId, pPackage->sczCacheId, 0); + ExitOnFailure(hr, "Failed to copy cache id for passthrough pseudo bundle."); + + pPassthroughPackage->Exe.protocol = pPackage->Exe.protocol; + + // No matter the operation, we're passing the same command-line. That's what makes + // this a passthrough bundle. + hr = CoreRecreateCommandLine(&sczArguments, pCommand->action, pCommand->display, pCommand->restart, pCommand->relationType, TRUE, wzActiveParent, wzAncestors, wzAppendLogPath, pCommand->wzCommandLine); + ExitOnFailure(hr, "Failed to recreate command-line arguments."); + + hr = StrAllocString(&pPassthroughPackage->Exe.sczInstallArguments, sczArguments, 0); + ExitOnFailure(hr, "Failed to copy install arguments for passthrough bundle package"); + + hr = StrAllocString(&pPassthroughPackage->Exe.sczRepairArguments, sczArguments, 0); + ExitOnFailure(hr, "Failed to copy related arguments for passthrough bundle package"); + + pPassthroughPackage->Exe.fRepairable = TRUE; + + hr = StrAllocString(&pPassthroughPackage->Exe.sczUninstallArguments, sczArguments, 0); + ExitOnFailure(hr, "Failed to copy uninstall arguments for passthrough bundle package"); + + pPassthroughPackage->fUninstallable = TRUE; + + // TODO: consider bringing this back in the near future. + //if (pDependencyProvider) + //{ + // pPassthroughPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER), TRUE); + // ExitOnNull(pPassthroughPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers."); + // pPassthroughPackage->cDependencyProviders = 1; + + // pPassthroughPackage->rgDependencyProviders[0].fImported = pDependencyProvider->fImported; + + // hr = StrAllocString(&pPassthroughPackage->rgDependencyProviders[0].sczKey, pDependencyProvider->sczKey, 0); + // ExitOnFailure(hr, "Failed to copy key for pseudo bundle."); + + // hr = StrAllocString(&pPassthroughPackage->rgDependencyProviders[0].sczVersion, pDependencyProvider->sczVersion, 0); + // ExitOnFailure(hr, "Failed to copy version for pseudo bundle."); + + // hr = StrAllocString(&pPassthroughPackage->rgDependencyProviders[0].sczDisplayName, pDependencyProvider->sczDisplayName, 0); + // ExitOnFailure(hr, "Failed to copy display name for pseudo bundle."); + //} + +LExit: + ReleaseStr(sczArguments); + return hr; +} diff --git a/src/burn/engine/pseudobundle.h b/src/burn/engine/pseudobundle.h new file mode 100644 index 00000000..9fb530aa --- /dev/null +++ b/src/burn/engine/pseudobundle.h @@ -0,0 +1,40 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +HRESULT PseudoBundleInitialize( + __in DWORD64 qwEngineVersion, + __in BURN_PACKAGE* pPackage, + __in BOOL fPerMachine, + __in_z LPCWSTR wzId, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in BOOTSTRAPPER_PACKAGE_STATE state, + __in BOOL fCached, + __in_z LPCWSTR wzFilePath, + __in_z LPCWSTR wzLocalSource, + __in_z_opt LPCWSTR wzDownloadSource, + __in DWORD64 qwSize, + __in BOOL fVital, + __in_z_opt LPCWSTR wzInstallArguments, + __in_z_opt LPCWSTR wzRepairArguments, + __in_z_opt LPCWSTR wzUninstallArguments, + __in_opt BURN_DEPENDENCY_PROVIDER* pDependencyProvider, + __in_opt const BYTE* pbHash, + __in const DWORD cbHash + ); +HRESULT PseudoBundleInitializePassthrough( + __in BURN_PACKAGE* pPassthroughPackage, + __in BOOTSTRAPPER_COMMAND* pCommand, + __in_z_opt LPCWSTR wzAppendLogPath, + __in_z_opt LPCWSTR wzActiveParent, + __in_z_opt LPCWSTR wzAncestors, + __in BURN_PACKAGE* pPackage + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/registration.cpp b/src/burn/engine/registration.cpp new file mode 100644 index 00000000..19da543c --- /dev/null +++ b/src/burn/engine/registration.cpp @@ -0,0 +1,1702 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// constants + +const LPCWSTR REGISTRY_RUN_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"; +const LPCWSTR REGISTRY_RUN_ONCE_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"; +const LPCWSTR REGISTRY_REBOOT_PENDING_FORMAT = L"%ls.RebootRequired"; +const LPCWSTR REGISTRY_BUNDLE_INSTALLED = L"Installed"; +const LPCWSTR REGISTRY_BUNDLE_DISPLAY_ICON = L"DisplayIcon"; +const LPCWSTR REGISTRY_BUNDLE_DISPLAY_VERSION = L"DisplayVersion"; +const LPCWSTR REGISTRY_BUNDLE_ESTIMATED_SIZE = L"EstimatedSize"; +const LPCWSTR REGISTRY_BUNDLE_PUBLISHER = L"Publisher"; +const LPCWSTR REGISTRY_BUNDLE_HELP_LINK = L"HelpLink"; +const LPCWSTR REGISTRY_BUNDLE_HELP_TELEPHONE = L"HelpTelephone"; +const LPCWSTR REGISTRY_BUNDLE_URL_INFO_ABOUT = L"URLInfoAbout"; +const LPCWSTR REGISTRY_BUNDLE_URL_UPDATE_INFO = L"URLUpdateInfo"; +const LPCWSTR REGISTRY_BUNDLE_PARENT_DISPLAY_NAME = L"ParentDisplayName"; +const LPCWSTR REGISTRY_BUNDLE_PARENT_KEY_NAME = L"ParentKeyName"; +const LPCWSTR REGISTRY_BUNDLE_COMMENTS = L"Comments"; +const LPCWSTR REGISTRY_BUNDLE_CONTACT = L"Contact"; +const LPCWSTR REGISTRY_BUNDLE_NO_MODIFY = L"NoModify"; +const LPCWSTR REGISTRY_BUNDLE_MODIFY_PATH = L"ModifyPath"; +const LPCWSTR REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY = L"NoElevateOnModify"; +const LPCWSTR REGISTRY_BUNDLE_NO_REMOVE = L"NoRemove"; +const LPCWSTR REGISTRY_BUNDLE_SYSTEM_COMPONENT = L"SystemComponent"; +const LPCWSTR REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING = L"QuietUninstallString"; +const LPCWSTR REGISTRY_BUNDLE_UNINSTALL_STRING = L"UninstallString"; +const LPCWSTR REGISTRY_BUNDLE_RESUME_COMMAND_LINE = L"BundleResumeCommandLine"; +const LPCWSTR REGISTRY_BUNDLE_VERSION_MAJOR = L"VersionMajor"; +const LPCWSTR REGISTRY_BUNDLE_VERSION_MINOR = L"VersionMinor"; +const LPCWSTR SWIDTAG_FOLDER = L"swidtag"; + +// internal function declarations + +static HRESULT ParseSoftwareTagsFromXml( + __in IXMLDOMNode* pixnRegistrationNode, + __out BURN_SOFTWARE_TAG** prgSoftwareTags, + __out DWORD* pcSoftwareTags + ); +static HRESULT SetPaths( + __in BURN_REGISTRATION* pRegistration + ); +static HRESULT GetBundleManufacturer( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __out LPWSTR* psczBundleManufacturer + ); +static HRESULT GetBundleName( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __out LPWSTR* psczBundleName + ); +static HRESULT UpdateResumeMode( + __in BURN_REGISTRATION* pRegistration, + __in HKEY hkRegistration, + __in BURN_RESUME_MODE resumeMode, + __in BOOL fRestartInitiated + ); +static HRESULT ParseRelatedCodes( + __in BURN_REGISTRATION* pRegistration, + __in IXMLDOMNode* pixnBundle + ); +static HRESULT FormatUpdateRegistrationKey( + __in BURN_REGISTRATION* pRegistration, + __out_z LPWSTR* psczKey + ); +static HRESULT WriteSoftwareTags( + __in BURN_VARIABLES* pVariables, + __in BURN_SOFTWARE_TAGS* pSoftwareTags + ); +static HRESULT RemoveSoftwareTags( + __in BURN_VARIABLES* pVariables, + __in BURN_SOFTWARE_TAGS* pSoftwareTags + ); +static HRESULT WriteUpdateRegistration( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables + ); +static HRESULT RemoveUpdateRegistration( + __in BURN_REGISTRATION* pRegistration + ); +static HRESULT RegWriteStringVariable( + __in HKEY hkKey, + __in BURN_VARIABLES* pVariables, + __in LPCWSTR wzVariable, + __in LPCWSTR wzName + ); +static HRESULT UpdateBundleNameRegistration( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in HKEY hkRegistration + ); +static BOOL IsWuRebootPending(); +static BOOL IsBundleRebootPending( + __in BURN_REGISTRATION* pRegistration +); +static BOOL IsRegistryRebootPending(); + +// function definitions + +/******************************************************************* + RegistrationParseFromXml - Parses registration information from manifest. + +*******************************************************************/ +extern "C" HRESULT RegistrationParseFromXml( + __in BURN_REGISTRATION* pRegistration, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNode* pixnRegistrationNode = NULL; + IXMLDOMNode* pixnArpNode = NULL; + IXMLDOMNode* pixnUpdateNode = NULL; + LPWSTR scz = NULL; + + // select registration node + hr = XmlSelectSingleNode(pixnBundle, L"Registration", &pixnRegistrationNode); + if (S_FALSE == hr) + { + hr = E_NOTFOUND; + } + ExitOnFailure(hr, "Failed to select registration node."); + + // @Id + hr = XmlGetAttributeEx(pixnRegistrationNode, L"Id", &pRegistration->sczId); + ExitOnFailure(hr, "Failed to get @Id."); + + // @Tag + hr = XmlGetAttributeEx(pixnRegistrationNode, L"Tag", &pRegistration->sczTag); + ExitOnFailure(hr, "Failed to get @Tag."); + + hr = ParseRelatedCodes(pRegistration, pixnBundle); + ExitOnFailure(hr, "Failed to parse related bundles"); + + // @Version + hr = XmlGetAttributeEx(pixnRegistrationNode, L"Version", &scz); + ExitOnFailure(hr, "Failed to get @Version."); + + hr = VerParseVersion(scz, 0, FALSE, &pRegistration->pVersion); + ExitOnFailure(hr, "Failed to parse @Version: %ls", scz); + + if (pRegistration->pVersion->fInvalid) + { + LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz); + } + + // @ProviderKey + hr = XmlGetAttributeEx(pixnRegistrationNode, L"ProviderKey", &pRegistration->sczProviderKey); + ExitOnFailure(hr, "Failed to get @ProviderKey."); + + // @ExecutableName + hr = XmlGetAttributeEx(pixnRegistrationNode, L"ExecutableName", &pRegistration->sczExecutableName); + ExitOnFailure(hr, "Failed to get @ExecutableName."); + + // @PerMachine + hr = XmlGetYesNoAttribute(pixnRegistrationNode, L"PerMachine", &pRegistration->fPerMachine); + ExitOnFailure(hr, "Failed to get @PerMachine."); + + // select ARP node + hr = XmlSelectSingleNode(pixnRegistrationNode, L"Arp", &pixnArpNode); + if (S_FALSE != hr) + { + ExitOnFailure(hr, "Failed to select ARP node."); + + // @Register + hr = XmlGetYesNoAttribute(pixnArpNode, L"Register", &pRegistration->fRegisterArp); + ExitOnFailure(hr, "Failed to get @Register."); + + // @DisplayName + hr = XmlGetAttributeEx(pixnArpNode, L"DisplayName", &pRegistration->sczDisplayName); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @DisplayName."); + } + + // @DisplayVersion + hr = XmlGetAttributeEx(pixnArpNode, L"DisplayVersion", &pRegistration->sczDisplayVersion); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @DisplayVersion."); + } + + // @Publisher + hr = XmlGetAttributeEx(pixnArpNode, L"Publisher", &pRegistration->sczPublisher); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Publisher."); + } + + // @HelpLink + hr = XmlGetAttributeEx(pixnArpNode, L"HelpLink", &pRegistration->sczHelpLink); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @HelpLink."); + } + + // @HelpTelephone + hr = XmlGetAttributeEx(pixnArpNode, L"HelpTelephone", &pRegistration->sczHelpTelephone); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @HelpTelephone."); + } + + // @AboutUrl + hr = XmlGetAttributeEx(pixnArpNode, L"AboutUrl", &pRegistration->sczAboutUrl); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @AboutUrl."); + } + + // @UpdateUrl + hr = XmlGetAttributeEx(pixnArpNode, L"UpdateUrl", &pRegistration->sczUpdateUrl); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @UpdateUrl."); + } + + // @ParentDisplayName + hr = XmlGetAttributeEx(pixnArpNode, L"ParentDisplayName", &pRegistration->sczParentDisplayName); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @ParentDisplayName."); + } + + // @Comments + hr = XmlGetAttributeEx(pixnArpNode, L"Comments", &pRegistration->sczComments); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Comments."); + } + + // @Contact + hr = XmlGetAttributeEx(pixnArpNode, L"Contact", &pRegistration->sczContact); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Contact."); + } + + // @DisableModify + hr = XmlGetAttributeEx(pixnArpNode, L"DisableModify", &scz); + if (SUCCEEDED(hr)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"button", -1)) + { + pRegistration->modify = BURN_REGISTRATION_MODIFY_DISABLE_BUTTON; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"yes", -1)) + { + pRegistration->modify = BURN_REGISTRATION_MODIFY_DISABLE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"no", -1)) + { + pRegistration->modify = BURN_REGISTRATION_MODIFY_ENABLED; + } + else + { + hr = E_UNEXPECTED; + ExitOnRootFailure(hr, "Invalid modify disabled type: %ls", scz); + } + } + else if (E_NOTFOUND == hr) + { + pRegistration->modify = BURN_REGISTRATION_MODIFY_ENABLED; + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get @DisableModify."); + + // @DisableRemove + hr = XmlGetYesNoAttribute(pixnArpNode, L"DisableRemove", &pRegistration->fNoRemove); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @DisableRemove."); + pRegistration->fNoRemoveDefined = TRUE; + } + } + + hr = ParseSoftwareTagsFromXml(pixnRegistrationNode, &pRegistration->softwareTags.rgSoftwareTags, &pRegistration->softwareTags.cSoftwareTags); + ExitOnFailure(hr, "Failed to parse software tag."); + + // select Update node + hr = XmlSelectSingleNode(pixnRegistrationNode, L"Update", &pixnUpdateNode); + if (S_FALSE != hr) + { + ExitOnFailure(hr, "Failed to select Update node."); + + pRegistration->update.fRegisterUpdate = TRUE; + + // @Manufacturer + hr = XmlGetAttributeEx(pixnUpdateNode, L"Manufacturer", &pRegistration->update.sczManufacturer); + ExitOnFailure(hr, "Failed to get @Manufacturer."); + + // @Department + hr = XmlGetAttributeEx(pixnUpdateNode, L"Department", &pRegistration->update.sczDepartment); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Department."); + } + + // @ProductFamily + hr = XmlGetAttributeEx(pixnUpdateNode, L"ProductFamily", &pRegistration->update.sczProductFamily); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @ProductFamily."); + } + + // @Name + hr = XmlGetAttributeEx(pixnUpdateNode, L"Name", &pRegistration->update.sczName); + ExitOnFailure(hr, "Failed to get @Name."); + + // @Classification + hr = XmlGetAttributeEx(pixnUpdateNode, L"Classification", &pRegistration->update.sczClassification); + ExitOnFailure(hr, "Failed to get @Classification."); + } + + hr = SetPaths(pRegistration); + ExitOnFailure(hr, "Failed to set registration paths."); + +LExit: + ReleaseObject(pixnRegistrationNode); + ReleaseObject(pixnArpNode); + ReleaseObject(pixnUpdateNode); + ReleaseStr(scz); + + return hr; +} + +/******************************************************************* + RegistrationUninitialize - + +*******************************************************************/ +extern "C" void RegistrationUninitialize( + __in BURN_REGISTRATION* pRegistration + ) +{ + ReleaseStr(pRegistration->sczId); + ReleaseStr(pRegistration->sczTag); + + for (DWORD i = 0; i < pRegistration->cDetectCodes; ++i) + { + ReleaseStr(pRegistration->rgsczDetectCodes[i]); + } + ReleaseMem(pRegistration->rgsczDetectCodes); + + for (DWORD i = 0; i < pRegistration->cUpgradeCodes; ++i) + { + ReleaseStr(pRegistration->rgsczUpgradeCodes[i]); + } + ReleaseMem(pRegistration->rgsczUpgradeCodes); + + for (DWORD i = 0; i < pRegistration->cAddonCodes; ++i) + { + ReleaseStr(pRegistration->rgsczAddonCodes[i]); + } + ReleaseMem(pRegistration->rgsczAddonCodes); + + for (DWORD i = 0; i < pRegistration->cPatchCodes; ++i) + { + ReleaseStr(pRegistration->rgsczPatchCodes[i]); + } + ReleaseMem(pRegistration->rgsczPatchCodes); + + ReleaseStr(pRegistration->sczProviderKey); + ReleaseStr(pRegistration->sczActiveParent); + ReleaseStr(pRegistration->sczExecutableName); + + ReleaseStr(pRegistration->sczRegistrationKey); + ReleaseStr(pRegistration->sczCacheExecutablePath); + ReleaseStr(pRegistration->sczResumeCommandLine); + ReleaseStr(pRegistration->sczStateFile); + + ReleaseStr(pRegistration->sczDisplayName); + ReleaseStr(pRegistration->sczDisplayVersion); + ReleaseStr(pRegistration->sczPublisher); + ReleaseStr(pRegistration->sczHelpLink); + ReleaseStr(pRegistration->sczHelpTelephone); + ReleaseStr(pRegistration->sczAboutUrl); + ReleaseStr(pRegistration->sczUpdateUrl); + ReleaseStr(pRegistration->sczParentDisplayName); + ReleaseStr(pRegistration->sczComments); + ReleaseStr(pRegistration->sczContact); + + ReleaseStr(pRegistration->update.sczManufacturer); + ReleaseStr(pRegistration->update.sczDepartment); + ReleaseStr(pRegistration->update.sczProductFamily); + ReleaseStr(pRegistration->update.sczName); + ReleaseStr(pRegistration->update.sczClassification); + + if (pRegistration->softwareTags.rgSoftwareTags) + { + for (DWORD i = 0; i < pRegistration->softwareTags.cSoftwareTags; ++i) + { + ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczFilename); + ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczRegid); + ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczPath); + ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczTag); + } + + MemFree(pRegistration->softwareTags.rgSoftwareTags); + } + + ReleaseStr(pRegistration->sczDetectedProviderKeyBundleId); + ReleaseStr(pRegistration->sczAncestors); + ReleaseStr(pRegistration->sczBundlePackageAncestors); + RelatedBundlesUninitialize(&pRegistration->relatedBundles); + + // clear struct + memset(pRegistration, 0, sizeof(BURN_REGISTRATION)); +} + +/******************************************************************* + RegistrationSetVariables - Initializes bundle variables that map to + registration entities. + +*******************************************************************/ +extern "C" HRESULT RegistrationSetVariables( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + LPWSTR sczBundleManufacturer = NULL; + LPWSTR sczBundleName = NULL; + + if (pRegistration->fInstalled) + { + hr = VariableSetNumeric(pVariables, BURN_BUNDLE_INSTALLED, 1, TRUE); + ExitOnFailure(hr, "Failed to set the bundle installed built-in variable."); + } + + // Ensure the registration bundle name is updated. + hr = GetBundleName(pRegistration, pVariables, &sczBundleName); + ExitOnFailure(hr, "Failed to initialize bundle name."); + + hr = GetBundleManufacturer(pRegistration, pVariables, &sczBundleName); + ExitOnFailure(hr, "Failed to initialize bundle manufacturer."); + + if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent) + { + hr = VariableSetString(pVariables, BURN_BUNDLE_ACTIVE_PARENT, pRegistration->sczActiveParent, TRUE, FALSE); + ExitOnFailure(hr, "Failed to overwrite the bundle active parent built-in variable."); + } + + hr = VariableSetString(pVariables, BURN_BUNDLE_PROVIDER_KEY, pRegistration->sczProviderKey, TRUE, FALSE); + ExitOnFailure(hr, "Failed to overwrite the bundle provider key built-in variable."); + + hr = VariableSetString(pVariables, BURN_BUNDLE_TAG, pRegistration->sczTag, TRUE, FALSE); + ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable."); + + hr = VariableSetVersion(pVariables, BURN_BUNDLE_VERSION, pRegistration->pVersion, TRUE); + ExitOnFailure(hr, "Failed to overwrite the bundle version built-in variable."); + + hr = VariableSetNumeric(pVariables, BURN_REBOOT_PENDING, IsBundleRebootPending(pRegistration) || IsWuRebootPending() || IsRegistryRebootPending(), TRUE); + ExitOnFailure(hr, "Failed to overwrite the bundle reboot-pending built-in variable."); + +LExit: + ReleaseStr(sczBundleManufacturer); + ReleaseStr(sczBundleName); + + return hr; +} + +extern "C" HRESULT RegistrationDetectInstalled( + __in BURN_REGISTRATION* pRegistration + ) +{ + HRESULT hr = S_OK; + HKEY hkRegistration = NULL; + DWORD dwInstalled = 0; + + pRegistration->fCached = FileExistsEx(pRegistration->sczCacheExecutablePath, NULL); + + // open registration key + hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration); + if (SUCCEEDED(hr)) + { + hr = RegReadNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, &dwInstalled); + } + + // Not finding the key or value is okay. + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + hr = S_OK; + } + + pRegistration->fInstalled = (1 == dwInstalled); + + ReleaseRegKey(hkRegistration); + return hr; +} + +/******************************************************************* + RegistrationDetectResumeMode - Detects registration information on the system + to determine if a resume is taking place. + +*******************************************************************/ +extern "C" HRESULT RegistrationDetectResumeType( + __in BURN_REGISTRATION* pRegistration, + __out BOOTSTRAPPER_RESUME_TYPE* pResumeType + ) +{ + HRESULT hr = S_OK; + HKEY hkRegistration = NULL; + DWORD dwResume = 0; + + if (IsBundleRebootPending(pRegistration)) + { + LogId(REPORT_STANDARD, MSG_PENDING_REBOOT_DETECTED, pRegistration->sczRegistrationKey); + + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING; + ExitFunction1(hr = S_OK); + } + + // open registration key + hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_NONE; + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to open registration key."); + + // read Resume value + hr = RegReadNumber(hkRegistration, L"Resume", &dwResume); + if (E_FILENOTFOUND == hr) + { + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INVALID; + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to read Resume value."); + + switch (dwResume) + { + case BURN_RESUME_MODE_ACTIVE: + // a previous run was interrupted + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INTERRUPTED; + break; + + case BURN_RESUME_MODE_SUSPEND: + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_SUSPEND; + break; + + case BURN_RESUME_MODE_ARP: + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_ARP; + break; + + case BURN_RESUME_MODE_REBOOT_PENDING: + // The volatile pending registry doesn't exist (checked above) which means + // the system was successfully restarted. + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_REBOOT; + break; + + default: + // the value stored in the registry is not valid + *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INVALID; + break; + } + +LExit: + ReleaseRegKey(hkRegistration); + + return hr; +} + +/******************************************************************* + RegistrationDetectRelatedBundles - finds the bundles with same + upgrade/detect/addon/patch codes. + +*******************************************************************/ +extern "C" HRESULT RegistrationDetectRelatedBundles( + __in BURN_REGISTRATION* pRegistration + ) +{ + HRESULT hr = S_OK; + + hr = RelatedBundlesInitializeForScope(TRUE, pRegistration, &pRegistration->relatedBundles); + ExitOnFailure(hr, "Failed to initialize per-machine related bundles."); + + hr = RelatedBundlesInitializeForScope(FALSE, pRegistration, &pRegistration->relatedBundles); + ExitOnFailure(hr, "Failed to initialize per-user related bundles."); + +LExit: + return hr; +} + +/******************************************************************* + RegistrationSessionBegin - Registers a run session on the system. + +*******************************************************************/ +extern "C" HRESULT RegistrationSessionBegin( + __in_z LPCWSTR wzEngineWorkingPath, + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in DWORD dwRegistrationOptions, + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction, + __in DWORD64 qwEstimatedSize + ) +{ + HRESULT hr = S_OK; + DWORD dwSize = 0; + HKEY hkRegistration = NULL; + LPWSTR sczPublisher = NULL; + + LogId(REPORT_VERBOSE, MSG_SESSION_BEGIN, pRegistration->sczRegistrationKey, dwRegistrationOptions, LoggingBoolToString(pRegistration->fDisableResume)); + + // Cache bundle executable. + if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE) + { + hr = CacheCompleteBundle(pRegistration->fPerMachine, pRegistration->sczExecutableName, pRegistration->sczId, wzEngineWorkingPath +#ifdef DEBUG + , pRegistration->sczCacheExecutablePath +#endif + ); + ExitOnFailure(hr, "Failed to cache bundle from path: %ls", wzEngineWorkingPath); + } + + // create registration key + hr = RegCreate(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration); + ExitOnFailure(hr, "Failed to create registration key."); + + // Write any ARP values and software tags. + if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION) + { + // Upgrade information + hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, pRegistration->sczCacheExecutablePath); + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH); + + hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, pRegistration->rgsczUpgradeCodes, pRegistration->cUpgradeCodes); + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE); + + hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, pRegistration->rgsczAddonCodes, pRegistration->cAddonCodes); + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE); + + hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, pRegistration->rgsczDetectCodes, pRegistration->cDetectCodes); + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE); + + hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, pRegistration->rgsczPatchCodes, pRegistration->cPatchCodes); + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE); + + hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, pRegistration->pVersion->sczVersion); + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION); + + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_VERSION_MAJOR, pRegistration->pVersion->dwMajor); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_VERSION_MAJOR); + + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_VERSION_MINOR, pRegistration->pVersion->dwMinor); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_VERSION_MINOR); + + if (pRegistration->sczProviderKey) + { + hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY, pRegistration->sczProviderKey); + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY); + } + + if (pRegistration->sczTag) + { + hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_TAG, pRegistration->sczTag); + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_TAG); + } + + hr = RegWriteStringFormatted(hkRegistration, BURN_REGISTRATION_REGISTRY_ENGINE_VERSION, L"%hs", szVerMajorMinorBuild); + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_ENGINE_VERSION); + + // DisplayIcon: [path to exe] and ",0" to refer to the first icon in the executable. + hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_DISPLAY_ICON, L"%s,0", pRegistration->sczCacheExecutablePath); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_DISPLAY_ICON); + + // update display name + hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration); + ExitOnFailure(hr, "Failed to update name and publisher."); + + // DisplayVersion: provided by UI + if (pRegistration->sczDisplayVersion) + { + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_DISPLAY_VERSION, pRegistration->sczDisplayVersion); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_DISPLAY_VERSION); + } + + // Publisher: provided by UI + hr = GetBundleManufacturer(pRegistration, pVariables, &sczPublisher); + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PUBLISHER, SUCCEEDED(hr) ? sczPublisher : pRegistration->sczPublisher); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PUBLISHER); + + // HelpLink: provided by UI + if (pRegistration->sczHelpLink) + { + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_HELP_LINK, pRegistration->sczHelpLink); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_HELP_LINK); + } + + // HelpTelephone: provided by UI + if (pRegistration->sczHelpTelephone) + { + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_HELP_TELEPHONE, pRegistration->sczHelpTelephone); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_HELP_TELEPHONE); + } + + // URLInfoAbout, provided by UI + if (pRegistration->sczAboutUrl) + { + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_URL_INFO_ABOUT, pRegistration->sczAboutUrl); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_URL_INFO_ABOUT); + } + + // URLUpdateInfo, provided by UI + if (pRegistration->sczUpdateUrl) + { + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_URL_UPDATE_INFO, pRegistration->sczUpdateUrl); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_URL_UPDATE_INFO); + } + + // ParentDisplayName + if (pRegistration->sczParentDisplayName) + { + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PARENT_DISPLAY_NAME, pRegistration->sczParentDisplayName); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PARENT_DISPLAY_NAME); + + // Need to write the ParentKeyName but can be set to anything. + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PARENT_KEY_NAME, pRegistration->sczParentDisplayName); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PARENT_KEY_NAME); + } + + // Comments, provided by UI + if (pRegistration->sczComments) + { + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_COMMENTS, pRegistration->sczComments); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_COMMENTS); + } + + // Contact, provided by UI + if (pRegistration->sczContact) + { + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_CONTACT, pRegistration->sczContact); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_CONTACT); + } + + // InstallLocation: provided by UI + // TODO: need to figure out what "InstallLocation" means in a chainer. + + // NoModify + if (BURN_REGISTRATION_MODIFY_DISABLE == pRegistration->modify) + { + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_MODIFY, 1); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_MODIFY); + } + else if (BURN_REGISTRATION_MODIFY_DISABLE_BUTTON != pRegistration->modify) // if support modify (aka: did not disable anything) + { + // ModifyPath: [path to exe] /modify + hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_MODIFY_PATH, L"\"%ls\" /modify", pRegistration->sczCacheExecutablePath); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_MODIFY_PATH); + + // NoElevateOnModify: 1 + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY, 1); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY); + } + + // NoRemove: should this be allowed? + if (pRegistration->fNoRemoveDefined) + { + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_REMOVE, (DWORD)pRegistration->fNoRemove); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_REMOVE); + } + + // Conditionally hide the ARP entry. + if (!pRegistration->fRegisterArp) + { + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_SYSTEM_COMPONENT, 1); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_SYSTEM_COMPONENT); + } + + // QuietUninstallString: [path to exe] /uninstall /quiet + hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING, L"\"%ls\" /uninstall /quiet", pRegistration->sczCacheExecutablePath); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING); + + // UninstallString, [path to exe] + // If the modify button is to be disabled, we'll add "/modify" to the uninstall string because the button is "Uninstall/Change". Otherwise, + // it's just the "Uninstall" button so we add "/uninstall" to make the program just go away. + LPCWSTR wzUninstallParameters = (BURN_REGISTRATION_MODIFY_DISABLE_BUTTON == pRegistration->modify) ? L"/modify" : L" /uninstall"; + hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_UNINSTALL_STRING, L"\"%ls\" %ls", pRegistration->sczCacheExecutablePath, wzUninstallParameters); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_UNINSTALL_STRING); + + if (pRegistration->softwareTags.cSoftwareTags) + { + hr = WriteSoftwareTags(pVariables, &pRegistration->softwareTags); + ExitOnFailure(hr, "Failed to write software tags."); + } + + // Update registration. + if (pRegistration->update.fRegisterUpdate) + { + hr = WriteUpdateRegistration(pRegistration, pVariables); + ExitOnFailure(hr, "Failed to write update registration."); + } + } + + // Update estimated size. + if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE) + { + qwEstimatedSize /= 1024; // Convert bytes to KB + if (0 < qwEstimatedSize) + { + if (DWORD_MAX < qwEstimatedSize) + { + // ARP doesn't support QWORDs here + dwSize = DWORD_MAX; + } + else + { + dwSize = static_cast(qwEstimatedSize); + } + + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_ESTIMATED_SIZE, dwSize); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_ESTIMATED_SIZE); + } + } + + // Register the bundle dependency key. + if (BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER == dependencyRegistrationAction) + { + hr = DependencyRegisterBundle(pRegistration); + ExitOnFailure(hr, "Failed to register the bundle dependency key."); + } + + // update resume mode + hr = UpdateResumeMode(pRegistration, hkRegistration, BURN_RESUME_MODE_ACTIVE, FALSE); + ExitOnFailure(hr, "Failed to update resume mode."); + +LExit: + ReleaseStr(sczPublisher); + ReleaseRegKey(hkRegistration); + + return hr; +} + + +/******************************************************************* + RegistrationSessionResume - Resumes a previous run session. + +*******************************************************************/ +extern "C" HRESULT RegistrationSessionResume( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + HKEY hkRegistration = NULL; + + // open registration key + hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration); + ExitOnFailure(hr, "Failed to open registration key."); + + // update resume mode + hr = UpdateResumeMode(pRegistration, hkRegistration, BURN_RESUME_MODE_ACTIVE, FALSE); + ExitOnFailure(hr, "Failed to update resume mode."); + + // update display name + hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration); + ExitOnFailure(hr, "Failed to update name and publisher."); + +LExit: + ReleaseRegKey(hkRegistration); + + return hr; +} + + +/******************************************************************* + RegistrationSessionEnd - Unregisters a run session from the system. + + *******************************************************************/ +extern "C" HRESULT RegistrationSessionEnd( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in BURN_PACKAGES* pPackages, + __in BURN_RESUME_MODE resumeMode, + __in BOOTSTRAPPER_APPLY_RESTART restart, + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction + ) +{ + HRESULT hr = S_OK; + LPWSTR sczRebootRequiredKey = NULL; + HKEY hkRebootRequired = NULL; + HKEY hkRegistration = NULL; + + LogId(REPORT_STANDARD, MSG_SESSION_END, pRegistration->sczRegistrationKey, LoggingResumeModeToString(resumeMode), LoggingRestartToString(restart), LoggingBoolToString(pRegistration->fDisableResume)); + + // If a restart is required for any reason, write a volatile registry key to track of + // of that fact until the reboot has taken place. + if (BOOTSTRAPPER_APPLY_RESTART_NONE != restart) + { + // We'll write the volatile registry key right next to the bundle ARP registry key + // because that's easy. This is all best effort since the worst case just means in + // the rare case the user launches the same install again before taking the restart + // the BA won't know a restart was still required. + hr = StrAllocFormatted(&sczRebootRequiredKey, REGISTRY_REBOOT_PENDING_FORMAT, pRegistration->sczRegistrationKey); + if (SUCCEEDED(hr)) + { + hr = RegCreateEx(pRegistration->hkRoot, sczRebootRequiredKey, KEY_WRITE, TRUE, NULL, &hkRebootRequired, NULL); + } + + if (FAILED(hr)) + { + ExitTraceSource(DUTIL_SOURCE_DEFAULT, hr, "Failed to write volatile reboot required registry key."); + hr = S_OK; + } + } + + // If no resume mode, then remove the bundle registration. + if (BURN_RESUME_MODE_NONE == resumeMode) + { + // If we just registered the bundle dependency but something went wrong and caused us to not + // keep the bundle registration (like rollback) or we are supposed to unregister the bundle + // dependency when unregistering the bundle, do so. + if (BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER == dependencyRegistrationAction || + BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER == dependencyRegistrationAction) + { + // Remove the bundle dependency key. + DependencyUnregisterBundle(pRegistration, pPackages); + } + + // Delete update registration key. + if (pRegistration->update.fRegisterUpdate) + { + RemoveUpdateRegistration(pRegistration); + } + + RemoveSoftwareTags(pVariables, &pRegistration->softwareTags); + + // Delete registration key. + hr = RegDelete(pRegistration->hkRoot, pRegistration->sczRegistrationKey, REG_KEY_DEFAULT, FALSE); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to delete registration key: %ls", pRegistration->sczRegistrationKey); + } + + CacheRemoveBundle(pRegistration->fPerMachine, pRegistration->sczId); + } + else // the mode needs to be updated so open the registration key. + { + // Open registration key. + hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration); + ExitOnFailure(hr, "Failed to open registration key."); + } + + // Update resume mode. + hr = UpdateResumeMode(pRegistration, hkRegistration, resumeMode, BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart); + ExitOnFailure(hr, "Failed to update resume mode."); + +LExit: + ReleaseRegKey(hkRegistration); + ReleaseRegKey(hkRebootRequired); + ReleaseStr(sczRebootRequiredKey); + + return hr; +} + +/******************************************************************* + RegistrationSaveState - Saves an engine state BLOB for retreval after a resume. + +*******************************************************************/ +extern "C" HRESULT RegistrationSaveState( + __in BURN_REGISTRATION* pRegistration, + __in_bcount(cbBuffer) BYTE* pbBuffer, + __in SIZE_T cbBuffer + ) +{ + HRESULT hr = S_OK; + + // write data to file + hr = FileWrite(pRegistration->sczStateFile, FILE_ATTRIBUTE_NORMAL, pbBuffer, cbBuffer, NULL); + if (E_PATHNOTFOUND == hr) + { + // TODO: should we log that the bundle's cache folder was not present so the state file wasn't created either? + hr = S_OK; + } + ExitOnFailure(hr, "Failed to write state to file: %ls", pRegistration->sczStateFile); + +LExit: + return hr; +} + +/******************************************************************* + RegistrationLoadState - Loads a previously stored engine state BLOB. + +*******************************************************************/ +extern "C" HRESULT RegistrationLoadState( + __in BURN_REGISTRATION* pRegistration, + __out_bcount(*pcbBuffer) BYTE** ppbBuffer, + __out SIZE_T* pcbBuffer + ) +{ + // read data from file + HRESULT hr = FileRead(ppbBuffer, pcbBuffer, pRegistration->sczStateFile); + return hr; +} + +/******************************************************************* +RegistrationGetResumeCommandLine - Gets the resume command line from the registry + +*******************************************************************/ +extern "C" HRESULT RegistrationGetResumeCommandLine( + __in const BURN_REGISTRATION* pRegistration, + __deref_out_z LPWSTR* psczResumeCommandLine + ) +{ + HRESULT hr = S_OK; + HKEY hkRegistration = NULL; + + // open registration key + hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration); + if (SUCCEEDED(hr)) + { + hr = RegReadString(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE, psczResumeCommandLine); + } + + // Not finding the key or value is okay. + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + hr = S_OK; + } + + ReleaseRegKey(hkRegistration); + + return hr; +} + + +// internal helper functions + +static HRESULT ParseSoftwareTagsFromXml( + __in IXMLDOMNode* pixnRegistrationNode, + __out BURN_SOFTWARE_TAG** prgSoftwareTags, + __out DWORD* pcSoftwareTags + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + + BURN_SOFTWARE_TAG* pSoftwareTags = NULL; + BSTR bstrTagXml = NULL; + + // select tag nodes + hr = XmlSelectNodes(pixnRegistrationNode, L"SoftwareTag", &pixnNodes); + ExitOnFailure(hr, "Failed to select software tag nodes."); + + // get tag node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get software tag count."); + + if (cNodes) + { + pSoftwareTags = (BURN_SOFTWARE_TAG*)MemAlloc(sizeof(BURN_SOFTWARE_TAG) * cNodes, TRUE); + ExitOnNull(pSoftwareTags, hr, E_OUTOFMEMORY, "Failed to allocate memory for software tag structs."); + + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_SOFTWARE_TAG* pSoftwareTag = &pSoftwareTags[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + hr = XmlGetAttributeEx(pixnNode, L"Filename", &pSoftwareTag->sczFilename); + ExitOnFailure(hr, "Failed to get @Filename."); + + hr = XmlGetAttributeEx(pixnNode, L"Regid", &pSoftwareTag->sczRegid); + ExitOnFailure(hr, "Failed to get @Regid."); + + hr = XmlGetAttributeEx(pixnNode, L"Path", &pSoftwareTag->sczPath); + ExitOnFailure(hr, "Failed to get @Path."); + + hr = XmlGetText(pixnNode, &bstrTagXml); + ExitOnFailure(hr, "Failed to get SoftwareTag text."); + + hr = StrAnsiAllocString(&pSoftwareTag->sczTag, bstrTagXml, 0, CP_UTF8); + ExitOnFailure(hr, "Failed to convert SoftwareTag text to UTF-8"); + + // prepare next iteration + ReleaseNullBSTR(bstrTagXml); + ReleaseNullObject(pixnNode); + } + } + + *pcSoftwareTags = cNodes; + *prgSoftwareTags = pSoftwareTags; + pSoftwareTags = NULL; + + hr = S_OK; + +LExit: + ReleaseBSTR(bstrTagXml); + ReleaseObject(pixnNode); + ReleaseObject(pixnNodes); + ReleaseMem(pSoftwareTags); + + return hr; +} + +static HRESULT SetPaths( + __in BURN_REGISTRATION* pRegistration + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCacheDirectory = NULL; + + // save registration key root + pRegistration->hkRoot = pRegistration->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + + // build uninstall registry key path + hr = StrAllocFormatted(&pRegistration->sczRegistrationKey, L"%s\\%s", BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, pRegistration->sczId); + ExitOnFailure(hr, "Failed to build uninstall registry key path."); + + // build cache directory + hr = CacheGetCompletedPath(pRegistration->fPerMachine, pRegistration->sczId, &sczCacheDirectory); + ExitOnFailure(hr, "Failed to build cache directory."); + + // build cached executable path + hr = PathConcat(sczCacheDirectory, pRegistration->sczExecutableName, &pRegistration->sczCacheExecutablePath); + ExitOnFailure(hr, "Failed to build cached executable path."); + + // build state file path + hr = StrAllocFormatted(&pRegistration->sczStateFile, L"%s\\state.rsm", sczCacheDirectory); + ExitOnFailure(hr, "Failed to build state file path."); + +LExit: + ReleaseStr(sczCacheDirectory); + return hr; +} + +static HRESULT GetBundleManufacturer( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __out LPWSTR* psczBundleManufacturer + ) +{ + HRESULT hr = S_OK; + + hr = VariableGetString(pVariables, BURN_BUNDLE_MANUFACTURER, psczBundleManufacturer); + if (E_NOTFOUND == hr) + { + hr = VariableSetString(pVariables, BURN_BUNDLE_MANUFACTURER, pRegistration->sczPublisher, FALSE, FALSE); + ExitOnFailure(hr, "Failed to set bundle manufacturer."); + + hr = StrAllocString(psczBundleManufacturer, pRegistration->sczPublisher, 0); + } + ExitOnFailure(hr, "Failed to get bundle manufacturer."); + +LExit: + return hr; +} + +static HRESULT GetBundleName( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __out LPWSTR* psczBundleName + ) +{ + HRESULT hr = S_OK; + + hr = VariableGetString(pVariables, BURN_BUNDLE_NAME, psczBundleName); + if (E_NOTFOUND == hr) + { + hr = VariableSetString(pVariables, BURN_BUNDLE_NAME, pRegistration->sczDisplayName, FALSE, FALSE); + ExitOnFailure(hr, "Failed to set bundle name."); + + hr = StrAllocString(psczBundleName, pRegistration->sczDisplayName, 0); + } + ExitOnFailure(hr, "Failed to get bundle name."); + +LExit: + return hr; +} + +static HRESULT UpdateResumeMode( + __in BURN_REGISTRATION* pRegistration, + __in HKEY hkRegistration, + __in BURN_RESUME_MODE resumeMode, + __in BOOL fRestartInitiated + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + HKEY hkRebootRequired = NULL; + HKEY hkRun = NULL; + LPWSTR sczResumeCommandLine = NULL; + LPCWSTR sczResumeKey = REGISTRY_RUN_ONCE_KEY; + + LogId(REPORT_STANDARD, MSG_SESSION_UPDATE, pRegistration->sczRegistrationKey, LoggingResumeModeToString(resumeMode), LoggingBoolToString(fRestartInitiated), LoggingBoolToString(pRegistration->fDisableResume)); + + // write resume information + if (hkRegistration) + { + // write Resume value + hr = RegWriteNumber(hkRegistration, L"Resume", (DWORD)resumeMode); + ExitOnFailure(hr, "Failed to write Resume value."); + + // Write the Installed value *only* when the mode is ARP. This will tell us + // that the bundle considers itself "installed" on the machine. Note that we + // never change the value to "0" after that. The bundle will be considered + // "uninstalled" when all of the registration is removed. + if (BURN_RESUME_MODE_ARP == resumeMode) + { + // Write Installed value. + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, 1); + ExitOnFailure(hr, "Failed to write Installed value."); + } + } + + // If the engine is active write the run key so we resume if there is an unexpected + // power loss. Also, if a restart was initiated in the middle of the chain then + // ensure the run key exists (it should since going active would have written it). + // Do not write the run key when embedded since the containing bundle + // is expected to detect for and restart the embedded bundle. + if ((BURN_RESUME_MODE_ACTIVE == resumeMode || fRestartInitiated) && !pRegistration->fDisableResume) + { + // append RunOnce switch + hr = StrAllocFormatted(&sczResumeCommandLine, L"\"%ls\" /%ls", pRegistration->sczCacheExecutablePath, BURN_COMMANDLINE_SWITCH_RUNONCE); + ExitOnFailure(hr, "Failed to format resume command line for RunOnce."); + + // write run key + hr = RegCreate(pRegistration->hkRoot, sczResumeKey, KEY_WRITE, &hkRun); + ExitOnFailure(hr, "Failed to create run key."); + + hr = RegWriteString(hkRun, pRegistration->sczId, sczResumeCommandLine); + ExitOnFailure(hr, "Failed to write run key value."); + + hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE, pRegistration->sczResumeCommandLine); + ExitOnFailure(hr, "Failed to write resume command line value."); + } + else // delete run key value + { + hr = RegOpen(pRegistration->hkRoot, sczResumeKey, KEY_WRITE, &hkRun); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + hr = S_OK; + } + else + { + ExitOnWin32Error(er, hr, "Failed to open run key."); + + er = ::RegDeleteValueW(hkRun, pRegistration->sczId); + if (ERROR_FILE_NOT_FOUND == er) + { + er = ERROR_SUCCESS; + } + ExitOnWin32Error(er, hr, "Failed to delete run key value."); + } + + if (hkRegistration) + { + er = ::RegDeleteValueW(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE); + if (ERROR_FILE_NOT_FOUND == er) + { + er = ERROR_SUCCESS; + } + ExitOnWin32Error(er, hr, "Failed to delete resume command line value."); + } + } + +LExit: + ReleaseStr(sczResumeCommandLine); + ReleaseRegKey(hkRebootRequired); + ReleaseRegKey(hkRun); + + return hr; +} + +static HRESULT ParseRelatedCodes( + __in BURN_REGISTRATION* pRegistration, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnElement = NULL; + LPWSTR sczAction = NULL; + LPWSTR sczId = NULL; + DWORD cElements = 0; + + hr = XmlSelectNodes(pixnBundle, L"RelatedBundle", &pixnNodes); + ExitOnFailure(hr, "Failed to get RelatedBundle nodes"); + + hr = pixnNodes->get_length((long*)&cElements); + ExitOnFailure(hr, "Failed to get RelatedBundle element count."); + + for (DWORD i = 0; i < cElements; ++i) + { + hr = XmlNextElement(pixnNodes, &pixnElement, NULL); + ExitOnFailure(hr, "Failed to get next RelatedBundle element."); + + hr = XmlGetAttributeEx(pixnElement, L"Action", &sczAction); + ExitOnFailure(hr, "Failed to get @Action."); + + hr = XmlGetAttributeEx(pixnElement, L"Id", &sczId); + ExitOnFailure(hr, "Failed to get @Id."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Detect", -1)) + { + hr = MemEnsureArraySize(reinterpret_cast(&pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes + 1, sizeof(LPWSTR), 5); + ExitOnFailure(hr, "Failed to resize Detect code array in registration"); + + pRegistration->rgsczDetectCodes[pRegistration->cDetectCodes] = sczId; + sczId = NULL; + ++pRegistration->cDetectCodes; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Upgrade", -1)) + { + hr = MemEnsureArraySize(reinterpret_cast(&pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes + 1, sizeof(LPWSTR), 5); + ExitOnFailure(hr, "Failed to resize Upgrade code array in registration"); + + pRegistration->rgsczUpgradeCodes[pRegistration->cUpgradeCodes] = sczId; + sczId = NULL; + ++pRegistration->cUpgradeCodes; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Addon", -1)) + { + hr = MemEnsureArraySize(reinterpret_cast(&pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes + 1, sizeof(LPWSTR), 5); + ExitOnFailure(hr, "Failed to resize Addon code array in registration"); + + pRegistration->rgsczAddonCodes[pRegistration->cAddonCodes] = sczId; + sczId = NULL; + ++pRegistration->cAddonCodes; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Patch", -1)) + { + hr = MemEnsureArraySize(reinterpret_cast(&pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes + 1, sizeof(LPWSTR), 5); + ExitOnFailure(hr, "Failed to resize Patch code array in registration"); + + pRegistration->rgsczPatchCodes[pRegistration->cPatchCodes] = sczId; + sczId = NULL; + ++pRegistration->cPatchCodes; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Action: %ls", sczAction); + } + } + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnElement); + ReleaseStr(sczAction); + ReleaseStr(sczId); + + return hr; +} + +static HRESULT FormatUpdateRegistrationKey( + __in BURN_REGISTRATION* pRegistration, + __out_z LPWSTR* psczKey + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + + hr = StrAllocFormatted(&sczKey, L"SOFTWARE\\%ls\\Updates\\", pRegistration->update.sczManufacturer); + ExitOnFailure(hr, "Failed to format the key path for update registration."); + + if (pRegistration->update.sczProductFamily) + { + hr = StrAllocFormatted(&sczKey, L"%ls%ls\\", sczKey, pRegistration->update.sczProductFamily); + ExitOnFailure(hr, "Failed to format the key path for update registration."); + } + + hr = StrAllocConcat(&sczKey, pRegistration->update.sczName, 0); + ExitOnFailure(hr, "Failed to format the key path for update registration."); + + *psczKey = sczKey; + sczKey = NULL; + +LExit: + ReleaseStr(sczKey); + + return hr; +} + +static HRESULT WriteSoftwareTags( + __in BURN_VARIABLES* pVariables, + __in BURN_SOFTWARE_TAGS* pSoftwareTags + ) +{ + HRESULT hr = S_OK; + LPWSTR sczRootFolder = NULL; + LPWSTR sczTagFolder = NULL; + LPWSTR sczPath = NULL; + + for (DWORD iTag = 0; iTag < pSoftwareTags->cSoftwareTags; ++iTag) + { + BURN_SOFTWARE_TAG* pSoftwareTag = pSoftwareTags->rgSoftwareTags + iTag; + + hr = VariableFormatString(pVariables, pSoftwareTag->sczPath, &sczRootFolder, NULL); + ExitOnFailure(hr, "Failed to format tag folder path."); + + hr = PathConcat(sczRootFolder, SWIDTAG_FOLDER, &sczTagFolder); + ExitOnFailure(hr, "Failed to allocate regid folder path."); + + hr = PathConcat(sczTagFolder, pSoftwareTag->sczFilename, &sczPath); + ExitOnFailure(hr, "Failed to allocate regid file path."); + + hr = DirEnsureExists(sczTagFolder, NULL); + ExitOnFailure(hr, "Failed to create regid folder: %ls", sczTagFolder); + + hr = FileWrite(sczPath, FILE_ATTRIBUTE_NORMAL, reinterpret_cast(pSoftwareTag->sczTag), lstrlenA(pSoftwareTag->sczTag), NULL); + ExitOnFailure(hr, "Failed to write tag xml to file: %ls", sczPath); + } + +LExit: + ReleaseStr(sczPath); + ReleaseStr(sczTagFolder); + ReleaseStr(sczRootFolder); + + return hr; +} + +static HRESULT RemoveSoftwareTags( + __in BURN_VARIABLES* pVariables, + __in BURN_SOFTWARE_TAGS* pSoftwareTags + ) +{ + HRESULT hr = S_OK; + LPWSTR sczRootFolder = NULL; + LPWSTR sczTagFolder = NULL; + LPWSTR sczPath = NULL; + + for (DWORD iTag = 0; iTag < pSoftwareTags->cSoftwareTags; ++iTag) + { + BURN_SOFTWARE_TAG* pSoftwareTag = pSoftwareTags->rgSoftwareTags + iTag; + + hr = VariableFormatString(pVariables, pSoftwareTag->sczPath, &sczRootFolder, NULL); + ExitOnFailure(hr, "Failed to format tag folder path."); + + hr = PathConcat(sczRootFolder, SWIDTAG_FOLDER, &sczTagFolder); + ExitOnFailure(hr, "Failed to allocate regid folder path."); + + hr = PathConcat(sczTagFolder, pSoftwareTag->sczFilename, &sczPath); + ExitOnFailure(hr, "Failed to allocate regid file path."); + + // Best effort to delete the software tag file and the regid folder. + FileEnsureDelete(sczPath); + + DirDeleteEmptyDirectoriesToRoot(sczTagFolder, 0); + } + +LExit: + ReleaseStr(sczPath); + ReleaseStr(sczTagFolder); + ReleaseStr(sczRootFolder); + + return hr; +} + +static HRESULT WriteUpdateRegistration( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + HKEY hkKey = NULL; + + hr = FormatUpdateRegistrationKey(pRegistration, &sczKey); + ExitOnFailure(hr, "Failed to get the formatted key path for update registration."); + + hr = RegCreate(pRegistration->hkRoot, sczKey, KEY_WRITE, &hkKey); + ExitOnFailure(hr, "Failed to create the key for update registration."); + + hr = RegWriteString(hkKey, L"ThisVersionInstalled", L"Y"); + ExitOnFailure(hr, "Failed to write %ls value.", L"ThisVersionInstalled"); + + hr = RegWriteString(hkKey, L"PackageName", pRegistration->sczDisplayName); + ExitOnFailure(hr, "Failed to write %ls value.", L"PackageName"); + + hr = RegWriteString(hkKey, L"PackageVersion", pRegistration->sczDisplayVersion); + ExitOnFailure(hr, "Failed to write %ls value.", L"PackageVersion"); + + hr = RegWriteString(hkKey, L"Publisher", pRegistration->sczPublisher); + ExitOnFailure(hr, "Failed to write %ls value.", L"Publisher"); + + if (pRegistration->update.sczDepartment) + { + hr = RegWriteString(hkKey, L"PublishingGroup", pRegistration->update.sczDepartment); + ExitOnFailure(hr, "Failed to write %ls value.", L"PublishingGroup"); + } + + hr = RegWriteString(hkKey, L"ReleaseType", pRegistration->update.sczClassification); + ExitOnFailure(hr, "Failed to write %ls value.", L"ReleaseType"); + + hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_LOGONUSER, L"InstalledBy"); + ExitOnFailure(hr, "Failed to write %ls value.", L"InstalledBy"); + + hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_DATE, L"InstalledDate"); + ExitOnFailure(hr, "Failed to write %ls value.", L"InstalledDate"); + + hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_INSTALLERNAME, L"InstallerName"); + ExitOnFailure(hr, "Failed to write %ls value.", L"InstallerName"); + + hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_INSTALLERVERSION, L"InstallerVersion"); + ExitOnFailure(hr, "Failed to write %ls value.", L"InstallerVersion"); + +LExit: + ReleaseRegKey(hkKey); + ReleaseStr(sczKey); + + return hr; +} + +static HRESULT RemoveUpdateRegistration( + __in BURN_REGISTRATION* pRegistration + ) +{ + HRESULT hr = S_OK; + LPWSTR sczKey = NULL; + LPWSTR sczPackageVersion = NULL; + HKEY hkKey = NULL; + BOOL fDeleteRegKey = TRUE; + + hr = FormatUpdateRegistrationKey(pRegistration, &sczKey); + ExitOnFailure(hr, "Failed to format key for update registration."); + + // Only delete if the uninstalling bundle's PackageVersion is the same as the + // PackageVersion in the update registration key. + // This is to support build to build upgrades + hr = RegOpen(pRegistration->hkRoot, sczKey, KEY_QUERY_VALUE, &hkKey); + if (SUCCEEDED(hr)) + { + hr = RegReadString(hkKey, L"PackageVersion", &sczPackageVersion); + if (SUCCEEDED(hr)) + { + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczPackageVersion, -1, pRegistration->sczDisplayVersion, -1)) + { + fDeleteRegKey = FALSE; + } + } + ReleaseRegKey(hkKey); + } + + // Unable to open the key or read the value is okay. + hr = S_OK; + + if (fDeleteRegKey) + { + hr = RegDelete(pRegistration->hkRoot, sczKey, REG_KEY_DEFAULT, FALSE); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to remove update registration key: %ls", sczKey); + } + } + +LExit: + ReleaseStr(sczPackageVersion); + ReleaseStr(sczKey); + + return hr; +} + +static HRESULT RegWriteStringVariable( + __in HKEY hk, + __in BURN_VARIABLES* pVariables, + __in LPCWSTR wzVariable, + __in LPCWSTR wzName + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + hr = VariableGetString(pVariables, wzVariable, &sczValue); + ExitOnFailure(hr, "Failed to get the %ls variable.", wzVariable); + + hr = RegWriteString(hk, wzName, sczValue); + ExitOnFailure(hr, "Failed to write %ls value.", wzName); + +LExit: + StrSecureZeroFreeString(sczValue); + + return hr; +} + +static HRESULT UpdateBundleNameRegistration( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in HKEY hkRegistration + ) +{ + HRESULT hr = S_OK; + LPWSTR sczDisplayName = NULL; + + // DisplayName: provided by UI + hr = GetBundleName(pRegistration, pVariables, &sczDisplayName); + hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME, SUCCEEDED(hr) ? sczDisplayName : pRegistration->sczDisplayName); + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME); + +LExit: + ReleaseStr(sczDisplayName); + + return hr; +} + +static BOOL IsWuRebootPending() +{ + HRESULT hr = S_OK; + BOOL fRebootPending = FALSE; + + // Do a best effort to ask WU if a reboot is required. If anything goes + // wrong then let's pretend a reboot is not required. + hr = ::CoInitialize(NULL); + if (SUCCEEDED(hr) || RPC_E_CHANGED_MODE == hr) + { + hr = WuaRestartRequired(&fRebootPending); + if (FAILED(hr)) + { + fRebootPending = FALSE; + } + + ::CoUninitialize(); + } + + return fRebootPending; +} + +static BOOL IsBundleRebootPending(BURN_REGISTRATION* pRegistration) +{ + HRESULT hr = S_OK; + LPWSTR sczRebootRequiredKey = NULL; + HKEY hkRebootRequired = NULL; + BOOL fBundleRebootPending = FALSE; + + // Check to see if a restart is pending for this bundle. + hr = StrAllocFormatted(&sczRebootRequiredKey, REGISTRY_REBOOT_PENDING_FORMAT, pRegistration->sczRegistrationKey); + ExitOnFailure(hr, "Failed to format pending restart registry key to read."); + + hr = RegOpen(pRegistration->hkRoot, sczRebootRequiredKey, KEY_QUERY_VALUE, &hkRebootRequired); + fBundleRebootPending = SUCCEEDED(hr); + +LExit: + ReleaseStr(sczRebootRequiredKey); + ReleaseRegKey(hkRebootRequired); + + return fBundleRebootPending; +} + +static BOOL IsRegistryRebootPending() +{ + HRESULT hr = S_OK; + DWORD dwValue; + HKEY hk = NULL; + BOOL fRebootPending = FALSE; + + hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\ServerManager", L"CurrentRebootAttempts", TRUE, &dwValue); + fRebootPending = SUCCEEDED(hr) && 0 < dwValue; + + if (!fRebootPending) + { + hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Updates", L"UpdateExeVolatile", TRUE, &dwValue); + fRebootPending = SUCCEEDED(hr) && 0 < dwValue; + + if (!fRebootPending) + { + fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootPending", NULL, TRUE); + + if (!fRebootPending) + { + fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootInProgress", NULL, TRUE); + + if (!fRebootPending) + { + hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update", L"AUState", TRUE, &dwValue); + fRebootPending = SUCCEEDED(hr) && 8 == dwValue; + + if (!fRebootPending) + { + fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager", L"PendingFileRenameOperations", TRUE); + + if (!fRebootPending) + { + fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager", L"PendingFileRenameOperations2", TRUE); + + if (!fRebootPending) + { + hr = RegOpen(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\FileRenameOperations", KEY_READ | KEY_WOW64_64KEY, &hk); + if (SUCCEEDED(hr)) + { + DWORD cSubKeys = 0; + DWORD cValues = 0; + hr = RegQueryKey(hk, &cSubKeys, &cValues); + fRebootPending = SUCCEEDED(hr) && (0 < cSubKeys || 0 < cValues); + } + } + } + } + } + } + } + } + + ReleaseRegKey(hk); + + return fRebootPending; +} diff --git a/src/burn/engine/registration.h b/src/burn/engine/registration.h new file mode 100644 index 00000000..6d8a6d2a --- /dev/null +++ b/src/burn/engine/registration.h @@ -0,0 +1,225 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +enum BURN_MODE; +enum BURN_DEPENDENCY_REGISTRATION_ACTION; +struct _BURN_LOGGING; +typedef _BURN_LOGGING BURN_LOGGING; + +// constants + +const LPCWSTR BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH = L"BundleCachePath"; +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE = L"BundleAddonCode"; +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE = L"BundleDetectCode"; +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE = L"BundlePatchCode"; +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode"; +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME = L"DisplayName"; +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION = L"BundleVersion"; +const LPCWSTR BURN_REGISTRATION_REGISTRY_ENGINE_VERSION = L"EngineVersion"; +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey"; +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_TAG = L"BundleTag"; + +enum BURN_RESUME_MODE +{ + BURN_RESUME_MODE_NONE, + BURN_RESUME_MODE_ACTIVE, + BURN_RESUME_MODE_SUSPEND, + BURN_RESUME_MODE_ARP, + BURN_RESUME_MODE_REBOOT_PENDING, +}; + +enum BURN_REGISTRATION_MODIFY_TYPE +{ + BURN_REGISTRATION_MODIFY_ENABLED, + BURN_REGISTRATION_MODIFY_DISABLE, + BURN_REGISTRATION_MODIFY_DISABLE_BUTTON, +}; + + +// structs + +typedef struct _BURN_UPDATE_REGISTRATION +{ + BOOL fRegisterUpdate; + LPWSTR sczManufacturer; + LPWSTR sczDepartment; + LPWSTR sczProductFamily; + LPWSTR sczName; + LPWSTR sczClassification; +} BURN_UPDATE_REGISTRATION; + +typedef struct _BURN_RELATED_BUNDLE +{ + BOOTSTRAPPER_RELATION_TYPE relationType; + BOOL fForwardCompatible; + + VERUTIL_VERSION* pVersion; + LPWSTR sczTag; + BOOL fPlannable; + + BURN_PACKAGE package; +} BURN_RELATED_BUNDLE; + +typedef struct _BURN_RELATED_BUNDLES +{ + BURN_RELATED_BUNDLE* rgRelatedBundles; + DWORD cRelatedBundles; +} BURN_RELATED_BUNDLES; + +typedef struct _BURN_SOFTWARE_TAG +{ + LPWSTR sczFilename; + LPWSTR sczRegid; + LPWSTR sczPath; + LPSTR sczTag; +} BURN_SOFTWARE_TAG; + +typedef struct _BURN_SOFTWARE_TAGS +{ + BURN_SOFTWARE_TAG* rgSoftwareTags; + DWORD cSoftwareTags; +} BURN_SOFTWARE_TAGS; + +typedef struct _BURN_REGISTRATION +{ + BOOL fPerMachine; + BOOL fRegisterArp; + BOOL fDisableResume; + BOOL fCached; + BOOL fInstalled; + LPWSTR sczId; + LPWSTR sczTag; + + LPWSTR *rgsczDetectCodes; + DWORD cDetectCodes; + + LPWSTR *rgsczUpgradeCodes; + DWORD cUpgradeCodes; + + LPWSTR *rgsczAddonCodes; + DWORD cAddonCodes; + + LPWSTR *rgsczPatchCodes; + DWORD cPatchCodes; + + VERUTIL_VERSION* pVersion; + LPWSTR sczActiveParent; + LPWSTR sczProviderKey; + LPWSTR sczExecutableName; + + // paths + HKEY hkRoot; + LPWSTR sczRegistrationKey; + LPWSTR sczCacheExecutablePath; + LPWSTR sczResumeCommandLine; + LPWSTR sczStateFile; + + // ARP registration + LPWSTR sczDisplayName; + LPWSTR sczDisplayVersion; + LPWSTR sczPublisher; + LPWSTR sczHelpLink; + LPWSTR sczHelpTelephone; + LPWSTR sczAboutUrl; + LPWSTR sczUpdateUrl; + LPWSTR sczParentDisplayName; + LPWSTR sczComments; + //LPWSTR sczReadme; // TODO: this would be a file path + LPWSTR sczContact; + //DWORD64 qwEstimatedSize; // TODO: size should come from disk cost calculation + BURN_REGISTRATION_MODIFY_TYPE modify; + BOOL fNoRemoveDefined; + BOOL fNoRemove; + + BURN_SOFTWARE_TAGS softwareTags; + + // Update registration + BURN_UPDATE_REGISTRATION update; + + BURN_RELATED_BUNDLES relatedBundles; // Only valid after detect. + DEPENDENCY* rgIgnoredDependencies; // Only valid after detect. + UINT cIgnoredDependencies; // Only valid after detect. + DEPENDENCY* rgDependents; // Only valid after detect. + UINT cDependents; // Only valid after detect. + BOOL fIgnoreAllDependents; // Only valid after detect. + LPCWSTR wzSelfDependent; // Only valid after detect. + BOOL fSelfRegisteredAsDependent; // Only valid after detect. + BOOL fParentRegisteredAsDependent; // Only valid after detect. + BOOL fForwardCompatibleBundleExists; // Only valid after detect. + BOOL fEligibleForCleanup; // Only valid after detect. + + LPWSTR sczDetectedProviderKeyBundleId; + LPWSTR sczAncestors; + LPWSTR sczBundlePackageAncestors; +} BURN_REGISTRATION; + + +// functions + +HRESULT RegistrationParseFromXml( + __in BURN_REGISTRATION* pRegistration, + __in IXMLDOMNode* pixnBundle + ); +void RegistrationUninitialize( + __in BURN_REGISTRATION* pRegistration + ); +HRESULT RegistrationSetVariables( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables + ); +HRESULT RegistrationDetectInstalled( + __in BURN_REGISTRATION* pRegistration + ); +HRESULT RegistrationDetectResumeType( + __in BURN_REGISTRATION* pRegistration, + __out BOOTSTRAPPER_RESUME_TYPE* pResumeType + ); +HRESULT RegistrationDetectRelatedBundles( + __in BURN_REGISTRATION* pRegistration + ); +HRESULT RegistrationSessionBegin( + __in_z LPCWSTR wzEngineWorkingPath, + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in DWORD dwRegistrationOptions, + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction, + __in DWORD64 qwEstimatedSize + ); +HRESULT RegistrationSessionResume( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables + ); +HRESULT RegistrationSessionEnd( + __in BURN_REGISTRATION* pRegistration, + __in BURN_VARIABLES* pVariables, + __in BURN_PACKAGES* pPackages, + __in BURN_RESUME_MODE resumeMode, + __in BOOTSTRAPPER_APPLY_RESTART restart, + __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction + ); +HRESULT RegistrationSaveState( + __in BURN_REGISTRATION* pRegistration, + __in_bcount_opt(cbBuffer) BYTE* pbBuffer, + __in_opt SIZE_T cbBuffer + ); +HRESULT RegistrationLoadState( + __in BURN_REGISTRATION* pRegistration, + __out_bcount(*pcbBuffer) BYTE** ppbBuffer, + __out SIZE_T* pcbBuffer + ); +HRESULT RegistrationGetResumeCommandLine( + __in const BURN_REGISTRATION* pRegistration, + __deref_out_z LPWSTR* psczResumeCommandLine + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/relatedbundle.cpp b/src/burn/engine/relatedbundle.cpp new file mode 100644 index 00000000..d3c856a6 --- /dev/null +++ b/src/burn/engine/relatedbundle.cpp @@ -0,0 +1,483 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// internal function declarations + +static HRESULT LoadIfRelatedBundle( + __in BOOL fPerMachine, + __in HKEY hkUninstallKey, + __in_z LPCWSTR sczRelatedBundleId, + __in BURN_REGISTRATION* pRegistration, + __in BURN_RELATED_BUNDLES* pRelatedBundles + ); +static HRESULT DetermineRelationType( + __in HKEY hkBundleId, + __in BURN_REGISTRATION* pRegistration, + __out BOOTSTRAPPER_RELATION_TYPE* pRelationType + ); +static HRESULT LoadRelatedBundleFromKey( + __in_z LPCWSTR wzRelatedBundleId, + __in HKEY hkBundleId, + __in BOOL fPerMachine, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __inout BURN_RELATED_BUNDLE *pRelatedBundle + ); + + +// function definitions + +extern "C" HRESULT RelatedBundlesInitializeForScope( + __in BOOL fPerMachine, + __in BURN_REGISTRATION* pRegistration, + __in BURN_RELATED_BUNDLES* pRelatedBundles + ) +{ + HRESULT hr = S_OK; + HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + HKEY hkUninstallKey = NULL; + LPWSTR sczRelatedBundleId = NULL; + + hr = RegOpen(hkRoot, BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, &hkUninstallKey); + if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) + { + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to open uninstall registry key."); + + for (DWORD dwIndex = 0; /* exit via break below */; ++dwIndex) + { + hr = RegKeyEnum(hkUninstallKey, dwIndex, &sczRelatedBundleId); + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + break; + } + ExitOnFailure(hr, "Failed to enumerate uninstall key for related bundles."); + + // If we did not find our bundle id, try to load the subkey as a related bundle. + if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczRelatedBundleId, -1, pRegistration->sczId, -1)) + { + // Ignore failures here since we'll often find products that aren't actually + // related bundles (or even bundles at all). + HRESULT hrRelatedBundle = LoadIfRelatedBundle(fPerMachine, hkUninstallKey, sczRelatedBundleId, pRegistration, pRelatedBundles); + UNREFERENCED_PARAMETER(hrRelatedBundle); + } + } + +LExit: + ReleaseStr(sczRelatedBundleId); + ReleaseRegKey(hkUninstallKey); + + return hr; +} + +extern "C" void RelatedBundlesUninitialize( + __in BURN_RELATED_BUNDLES* pRelatedBundles + ) +{ + if (pRelatedBundles->rgRelatedBundles) + { + for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i) + { + BURN_PACKAGE* pPackage = &pRelatedBundles->rgRelatedBundles[i].package; + + for (DWORD j = 0; j < pPackage->payloads.cItems; ++j) + { + PayloadUninitialize(pPackage->payloads.rgItems[j].pPayload); + } + + PackageUninitialize(pPackage); + ReleaseStr(pRelatedBundles->rgRelatedBundles[i].sczTag); + } + + MemFree(pRelatedBundles->rgRelatedBundles); + } + + memset(pRelatedBundles, 0, sizeof(BURN_RELATED_BUNDLES)); +} + + +// internal helper functions + +static HRESULT LoadIfRelatedBundle( + __in BOOL fPerMachine, + __in HKEY hkUninstallKey, + __in_z LPCWSTR sczRelatedBundleId, + __in BURN_REGISTRATION* pRegistration, + __in BURN_RELATED_BUNDLES* pRelatedBundles + ) +{ + HRESULT hr = S_OK; + HKEY hkBundleId = NULL; + BOOTSTRAPPER_RELATION_TYPE relationType = BOOTSTRAPPER_RELATION_NONE; + + hr = RegOpen(hkUninstallKey, sczRelatedBundleId, KEY_READ, &hkBundleId); + ExitOnFailure(hr, "Failed to open uninstall key for potential related bundle: %ls", sczRelatedBundleId); + + hr = DetermineRelationType(hkBundleId, pRegistration, &relationType); + if (FAILED(hr) || BOOTSTRAPPER_RELATION_NONE == relationType) + { + // Must not be a related bundle. + hr = E_NOTFOUND; + } + else // load the related bundle. + { + hr = MemEnsureArraySize(reinterpret_cast(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5); + ExitOnFailure(hr, "Failed to ensure there is space for related bundles."); + + BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles; + + hr = LoadRelatedBundleFromKey(sczRelatedBundleId, hkBundleId, fPerMachine, relationType, pRelatedBundle); + ExitOnFailure(hr, "Failed to initialize package from related bundle id: %ls", sczRelatedBundleId); + + ++pRelatedBundles->cRelatedBundles; + } + +LExit: + ReleaseRegKey(hkBundleId); + + return hr; +} + +static HRESULT DetermineRelationType( + __in HKEY hkBundleId, + __in BURN_REGISTRATION* pRegistration, + __out BOOTSTRAPPER_RELATION_TYPE* pRelationType + ) +{ + HRESULT hr = S_OK; + LPWSTR* rgsczUpgradeCodes = NULL; + DWORD cUpgradeCodes = 0; + STRINGDICT_HANDLE sdUpgradeCodes = NULL; + LPWSTR* rgsczAddonCodes = NULL; + DWORD cAddonCodes = 0; + STRINGDICT_HANDLE sdAddonCodes = NULL; + LPWSTR* rgsczDetectCodes = NULL; + DWORD cDetectCodes = 0; + STRINGDICT_HANDLE sdDetectCodes = NULL; + LPWSTR* rgsczPatchCodes = NULL; + DWORD cPatchCodes = 0; + STRINGDICT_HANDLE sdPatchCodes = NULL; + + *pRelationType = BOOTSTRAPPER_RELATION_NONE; + + // All remaining operations should treat all related bundles as non-vital. + hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes, &cUpgradeCodes); + if (HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE) == hr) + { + TraceError(hr, "Failed to read upgrade codes as REG_MULTI_SZ. Trying again as REG_SZ in case of older bundles."); + + rgsczUpgradeCodes = reinterpret_cast(MemAlloc(sizeof(LPWSTR), TRUE)); + ExitOnNull(rgsczUpgradeCodes, hr, E_OUTOFMEMORY, "Failed to allocate list for a single upgrade code from older bundle."); + + hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes[0]); + if (SUCCEEDED(hr)) + { + cUpgradeCodes = 1; + } + } + + // Compare upgrade codes. + if (SUCCEEDED(hr)) + { + hr = DictCreateStringListFromArray(&sdUpgradeCodes, rgsczUpgradeCodes, cUpgradeCodes, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "upgrade codes"); + + // Upgrade relationship: when their upgrade codes match our upgrade codes. + hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for upgrade code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_UPGRADE; + ExitFunction(); + } + + // Detect relationship: when their upgrade codes match our detect codes. + hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for detect code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_DETECT; + ExitFunction(); + } + + // Dependent relationship: when their upgrade codes match our addon codes. + hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for addon code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT; + ExitFunction(); + } + + // Dependent relationship: when their upgrade codes match our patch codes. + hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for addon code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT; + ExitFunction(); + } + + ReleaseNullDict(sdUpgradeCodes); + ReleaseNullStrArray(rgsczUpgradeCodes, cUpgradeCodes); + } + + // Compare addon codes. + hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, &rgsczAddonCodes, &cAddonCodes); + if (SUCCEEDED(hr)) + { + hr = DictCreateStringListFromArray(&sdAddonCodes, rgsczAddonCodes, cAddonCodes, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "addon codes"); + + // Addon relationship: when their addon codes match our detect codes. + hr = DictCompareStringListToArray(sdAddonCodes, const_cast(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for addon code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_ADDON; + ExitFunction(); + } + + // Addon relationship: when their addon codes match our upgrade codes. + hr = DictCompareStringListToArray(sdAddonCodes, const_cast(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for addon code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_ADDON; + ExitFunction(); + } + + ReleaseNullDict(sdAddonCodes); + ReleaseNullStrArray(rgsczAddonCodes, cAddonCodes); + } + + // Compare patch codes. + hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, &rgsczPatchCodes, &cPatchCodes); + if (SUCCEEDED(hr)) + { + hr = DictCreateStringListFromArray(&sdPatchCodes, rgsczPatchCodes, cPatchCodes, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "patch codes"); + + // Patch relationship: when their patch codes match our detect codes. + hr = DictCompareStringListToArray(sdPatchCodes, const_cast(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for patch code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_PATCH; + ExitFunction(); + } + + // Patch relationship: when their patch codes match our upgrade codes. + hr = DictCompareStringListToArray(sdPatchCodes, const_cast(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for patch code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_PATCH; + ExitFunction(); + } + + ReleaseNullDict(sdPatchCodes); + ReleaseNullStrArray(rgsczPatchCodes, cPatchCodes); + } + + // Compare detect codes. + hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, &rgsczDetectCodes, &cDetectCodes); + if (SUCCEEDED(hr)) + { + hr = DictCreateStringListFromArray(&sdDetectCodes, rgsczDetectCodes, cDetectCodes, DICT_FLAG_CASEINSENSITIVE); + ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "detect codes"); + + // Detect relationship: when their detect codes match our detect codes. + hr = DictCompareStringListToArray(sdDetectCodes, const_cast(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for detect code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_DETECT; + ExitFunction(); + } + + // Dependent relationship: when their detect codes match our addon codes. + hr = DictCompareStringListToArray(sdDetectCodes, const_cast(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for addon code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT; + ExitFunction(); + } + + // Dependent relationship: when their detect codes match our patch codes. + hr = DictCompareStringListToArray(sdDetectCodes, const_cast(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes); + if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) + { + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to do array search for addon code match."); + + *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT; + ExitFunction(); + } + + ReleaseNullDict(sdDetectCodes); + ReleaseNullStrArray(rgsczDetectCodes, cDetectCodes); + } + +LExit: + if (SUCCEEDED(hr) && BOOTSTRAPPER_RELATION_NONE == *pRelationType) + { + hr = E_NOTFOUND; + } + + ReleaseDict(sdUpgradeCodes); + ReleaseStrArray(rgsczUpgradeCodes, cUpgradeCodes); + ReleaseDict(sdAddonCodes); + ReleaseStrArray(rgsczAddonCodes, cAddonCodes); + ReleaseDict(sdDetectCodes); + ReleaseStrArray(rgsczDetectCodes, cDetectCodes); + ReleaseDict(sdPatchCodes); + ReleaseStrArray(rgsczPatchCodes, cPatchCodes); + + return hr; +} + +static HRESULT LoadRelatedBundleFromKey( + __in_z LPCWSTR wzRelatedBundleId, + __in HKEY hkBundleId, + __in BOOL fPerMachine, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __inout BURN_RELATED_BUNDLE* pRelatedBundle + ) +{ + HRESULT hr = S_OK; + DWORD64 qwEngineVersion = 0; + LPWSTR sczBundleVersion = NULL; + LPWSTR sczCachePath = NULL; + BOOL fCached = FALSE; + DWORD64 qwFileSize = 0; + BURN_DEPENDENCY_PROVIDER dependencyProvider = { }; + + hr = RegReadVersion(hkBundleId, BURN_REGISTRATION_REGISTRY_ENGINE_VERSION, &qwEngineVersion); + if (FAILED(hr)) + { + qwEngineVersion = 0; + hr = S_OK; + } + + hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, &sczBundleVersion); + ExitOnFailure(hr, "Failed to read version from registry for bundle: %ls", wzRelatedBundleId); + + hr = VerParseVersion(sczBundleVersion, 0, FALSE, &pRelatedBundle->pVersion); + ExitOnFailure(hr, "Failed to parse pseudo bundle version: %ls", sczBundleVersion); + + if (pRelatedBundle->pVersion->fInvalid) + { + LogId(REPORT_WARNING, MSG_RELATED_PACKAGE_INVALID_VERSION, wzRelatedBundleId, sczBundleVersion); + } + + hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, &sczCachePath); + ExitOnFailure(hr, "Failed to read cache path from registry for bundle: %ls", wzRelatedBundleId); + + if (FileExistsEx(sczCachePath, NULL)) + { + fCached = TRUE; + } + else + { + LogId(REPORT_STANDARD, MSG_DETECT_RELATED_BUNDLE_NOT_CACHED, wzRelatedBundleId, sczCachePath); + } + + pRelatedBundle->fPlannable = fCached; + + hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY, &dependencyProvider.sczKey); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to read provider key from registry for bundle: %ls", wzRelatedBundleId); + + dependencyProvider.fImported = TRUE; + + hr = StrAllocString(&dependencyProvider.sczVersion, pRelatedBundle->pVersion->sczVersion, 0); + ExitOnFailure(hr, "Failed to copy version for bundle: %ls", wzRelatedBundleId); + + hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME, &dependencyProvider.sczDisplayName); + if (E_FILENOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to copy display name for bundle: %ls", wzRelatedBundleId); + } + } + + hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_TAG, &pRelatedBundle->sczTag); + if (E_FILENOTFOUND == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to read tag from registry for bundle: %ls", wzRelatedBundleId); + + pRelatedBundle->relationType = relationType; + + hr = PseudoBundleInitialize(qwEngineVersion, &pRelatedBundle->package, fPerMachine, wzRelatedBundleId, pRelatedBundle->relationType, + BOOTSTRAPPER_PACKAGE_STATE_PRESENT, fCached, sczCachePath, sczCachePath, NULL, qwFileSize, FALSE, + L"-quiet", L"-repair -quiet", L"-uninstall -quiet", + (dependencyProvider.sczKey && *dependencyProvider.sczKey) ? &dependencyProvider : NULL, + NULL, 0); + ExitOnFailure(hr, "Failed to initialize related bundle to represent bundle: %ls", wzRelatedBundleId); + +LExit: + DependencyUninitializeProvider(&dependencyProvider); + ReleaseStr(sczCachePath); + ReleaseStr(sczBundleVersion); + + return hr; +} diff --git a/src/burn/engine/relatedbundle.h b/src/burn/engine/relatedbundle.h new file mode 100644 index 00000000..01691c25 --- /dev/null +++ b/src/burn/engine/relatedbundle.h @@ -0,0 +1,20 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + +HRESULT RelatedBundlesInitializeForScope( + __in BOOL fPerMachine, + __in BURN_REGISTRATION* pRegistration, + __in BURN_RELATED_BUNDLES* pRelatedBundles + ); +void RelatedBundlesUninitialize( + __in BURN_RELATED_BUNDLES* pRelatedBundles + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/search.cpp b/src/burn/engine/search.cpp new file mode 100644 index 00000000..6d5f8d49 --- /dev/null +++ b/src/burn/engine/search.cpp @@ -0,0 +1,1303 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// internal function declarations + +static HRESULT DirectorySearchExists( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT DirectorySearchPath( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT FileSearchExists( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT FileSearchVersion( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT FileSearchPath( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT RegistrySearchExists( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT RegistrySearchValue( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT MsiComponentSearch( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT MsiProductSearch( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT MsiFeatureSearch( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT PerformExtensionSearch( + __in BURN_SEARCH* pSearch + ); +static HRESULT PerformSetVariable( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables +); + + +// function definitions + +extern "C" HRESULT SearchesParseFromXml( + __in BURN_SEARCHES* pSearches, + __in BURN_EXTENSIONS* pBurnExtensions, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + BSTR bstrNodeName = NULL; + LPWSTR scz = NULL; + BURN_VARIANT_TYPE valueType = BURN_VARIANT_TYPE_NONE; + + // select search nodes + hr = XmlSelectNodes(pixnBundle, L"DirectorySearch|FileSearch|RegistrySearch|MsiComponentSearch|MsiProductSearch|MsiFeatureSearch|ExtensionSearch|SetVariable", &pixnNodes); + ExitOnFailure(hr, "Failed to select search nodes."); + + // get search node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get search node count."); + + if (!cNodes) + { + ExitFunction(); + } + + // allocate memory for searches + pSearches->rgSearches = (BURN_SEARCH*)MemAlloc(sizeof(BURN_SEARCH) * cNodes, TRUE); + ExitOnNull(pSearches->rgSearches, hr, E_OUTOFMEMORY, "Failed to allocate memory for search structs."); + + pSearches->cSearches = cNodes; + + // parse search elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_SEARCH* pSearch = &pSearches->rgSearches[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &pSearch->sczKey); + ExitOnFailure(hr, "Failed to get @Id."); + + // @Variable + hr = XmlGetAttributeEx(pixnNode, L"Variable", &pSearch->sczVariable); + ExitOnFailure(hr, "Failed to get @Variable."); + + // @Condition + hr = XmlGetAttributeEx(pixnNode, L"Condition", &pSearch->sczCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Condition."); + } + + // read type specific attributes + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"DirectorySearch", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_DIRECTORY; + + // @Path + hr = XmlGetAttributeEx(pixnNode, L"Path", &pSearch->DirectorySearch.sczPath); + ExitOnFailure(hr, "Failed to get @Path."); + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1)) + { + pSearch->DirectorySearch.Type = BURN_DIRECTORY_SEARCH_TYPE_EXISTS; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"path", -1)) + { + pSearch->DirectorySearch.Type = BURN_DIRECTORY_SEARCH_TYPE_PATH; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"FileSearch", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_FILE; + + // @Path + hr = XmlGetAttributeEx(pixnNode, L"Path", &pSearch->FileSearch.sczPath); + ExitOnFailure(hr, "Failed to get @Path."); + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1)) + { + pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_EXISTS; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) + { + pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_VERSION; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"path", -1)) + { + pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_PATH; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"RegistrySearch", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_REGISTRY; + + // @Root + hr = XmlGetAttributeEx(pixnNode, L"Root", &scz); + ExitOnFailure(hr, "Failed to get @Root."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKCR", -1)) + { + pSearch->RegistrySearch.hRoot = HKEY_CLASSES_ROOT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKCU", -1)) + { + pSearch->RegistrySearch.hRoot = HKEY_CURRENT_USER; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKLM", -1)) + { + pSearch->RegistrySearch.hRoot = HKEY_LOCAL_MACHINE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKU", -1)) + { + pSearch->RegistrySearch.hRoot = HKEY_USERS; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Root: %ls", scz); + } + + // @Key + hr = XmlGetAttributeEx(pixnNode, L"Key", &pSearch->RegistrySearch.sczKey); + ExitOnFailure(hr, "Failed to get Key attribute."); + + // @Value + hr = XmlGetAttributeEx(pixnNode, L"Value", &pSearch->RegistrySearch.sczValue); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get Value attribute."); + } + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + hr = XmlGetYesNoAttribute(pixnNode, L"Win64", &pSearch->RegistrySearch.fWin64); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get Win64 attribute."); + } + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1)) + { + pSearch->RegistrySearch.Type = BURN_REGISTRY_SEARCH_TYPE_EXISTS; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"value", -1)) + { + pSearch->RegistrySearch.Type = BURN_REGISTRY_SEARCH_TYPE_VALUE; + + // @ExpandEnvironment + hr = XmlGetYesNoAttribute(pixnNode, L"ExpandEnvironment", &pSearch->RegistrySearch.fExpandEnvironment); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @ExpandEnvironment."); + } + + // @VariableType + hr = XmlGetAttributeEx(pixnNode, L"VariableType", &scz); + ExitOnFailure(hr, "Failed to get @VariableType."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"formatted", -1)) + { + pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_FORMATTED; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) + { + pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_NUMERIC; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1)) + { + pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_STRING; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) + { + pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_VERSION; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @VariableType: %ls", scz); + } + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiComponentSearch", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_MSI_COMPONENT; + + // @ProductCode + hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiComponentSearch.sczProductCode); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @ProductCode."); + } + + // @ComponentId + hr = XmlGetAttributeEx(pixnNode, L"ComponentId", &pSearch->MsiComponentSearch.sczComponentId); + ExitOnFailure(hr, "Failed to get @ComponentId."); + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"keyPath", -1)) + { + pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1)) + { + pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_STATE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"directory", -1)) + { + pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiProductSearch", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_MSI_PRODUCT; + pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE; + + // @ProductCode (if we don't find a product code then look for an upgrade code) + hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiProductSearch.sczGuid); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @ProductCode."); + pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_PRODUCTCODE; + } + else + { + // @UpgradeCode + hr = XmlGetAttributeEx(pixnNode, L"UpgradeCode", &pSearch->MsiProductSearch.sczGuid); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @UpgradeCode."); + pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE; + } + } + + // make sure we found either a product or upgrade code + if (BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE == pSearch->MsiProductSearch.GuidType) + { + hr = E_NOTFOUND; + ExitOnFailure(hr, "Failed to get @ProductCode or @UpgradeCode."); + } + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) + { + pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"language", -1)) + { + pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1)) + { + pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_STATE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"assignment", -1)) + { + pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiFeatureSearch", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_MSI_FEATURE; + + // @ProductCode + hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiFeatureSearch.sczProductCode); + ExitOnFailure(hr, "Failed to get @ProductCode."); + + // @FeatureId + hr = XmlGetAttributeEx(pixnNode, L"FeatureId", &pSearch->MsiFeatureSearch.sczFeatureId); + ExitOnFailure(hr, "Failed to get @FeatureId."); + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1)) + { + pSearch->MsiFeatureSearch.Type = BURN_MSI_FEATURE_SEARCH_TYPE_STATE; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"ExtensionSearch", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_EXTENSION; + + // @ExtensionId + hr = XmlGetAttributeEx(pixnNode, L"ExtensionId", &scz); + ExitOnFailure(hr, "Failed to get @ExtensionId."); + + hr = BurnExtensionFindById(pBurnExtensions, scz, &pSearch->ExtensionSearch.pExtension); + ExitOnFailure(hr, "Failed to find extension '%ls' for search '%ls'", scz, pSearch->sczKey); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"SetVariable", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_SET_VARIABLE; + + // @Value + hr = XmlGetAttributeEx(pixnNode, L"Value", &scz); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Value."); + + hr = BVariantSetString(&pSearch->SetVariable.value, scz, 0, FALSE); + ExitOnFailure(hr, "Failed to set variant value."); + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"formatted", -1)) + { + valueType = BURN_VARIANT_TYPE_FORMATTED; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) + { + valueType = BURN_VARIANT_TYPE_NUMERIC; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1)) + { + valueType = BURN_VARIANT_TYPE_STRING; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) + { + valueType = BURN_VARIANT_TYPE_VERSION; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else + { + valueType = BURN_VARIANT_TYPE_NONE; + } + + // change value variant to correct type + hr = BVariantChangeType(&pSearch->SetVariable.value, valueType); + ExitOnFailure(hr, "Failed to change variant type."); + } + else + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Unexpected element name: %ls", bstrNodeName); + } + + // prepare next iteration + ReleaseNullObject(pixnNode); + ReleaseNullBSTR(bstrNodeName); + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseBSTR(bstrNodeName); + ReleaseStr(scz); + return hr; +} + +extern "C" HRESULT SearchesExecute( + __in BURN_SEARCHES* pSearches, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + BOOL f = FALSE; + + for (DWORD i = 0; i < pSearches->cSearches; ++i) + { + BURN_SEARCH* pSearch = &pSearches->rgSearches[i]; + + // evaluate condition + if (pSearch->sczCondition && *pSearch->sczCondition) + { + hr = ConditionEvaluate(pVariables, pSearch->sczCondition, &f); + if (E_INVALIDDATA == hr) + { + TraceError(hr, "Failed to parse search condition. Id = '%ls', Condition = '%ls'", pSearch->sczKey, pSearch->sczCondition); + hr = S_OK; + continue; + } + ExitOnFailure(hr, "Failed to evaluate search condition. Id = '%ls', Condition = '%ls'", pSearch->sczKey, pSearch->sczCondition); + + if (!f) + { + continue; // condition evaluated to false, skip + } + } + + switch (pSearch->Type) + { + case BURN_SEARCH_TYPE_DIRECTORY: + switch (pSearch->DirectorySearch.Type) + { + case BURN_DIRECTORY_SEARCH_TYPE_EXISTS: + hr = DirectorySearchExists(pSearch, pVariables); + break; + case BURN_DIRECTORY_SEARCH_TYPE_PATH: + hr = DirectorySearchPath(pSearch, pVariables); + break; + default: + hr = E_UNEXPECTED; + } + break; + case BURN_SEARCH_TYPE_FILE: + switch (pSearch->FileSearch.Type) + { + case BURN_FILE_SEARCH_TYPE_EXISTS: + hr = FileSearchExists(pSearch, pVariables); + break; + case BURN_FILE_SEARCH_TYPE_VERSION: + hr = FileSearchVersion(pSearch, pVariables); + break; + case BURN_FILE_SEARCH_TYPE_PATH: + hr = FileSearchPath(pSearch, pVariables); + break; + default: + hr = E_UNEXPECTED; + } + break; + case BURN_SEARCH_TYPE_REGISTRY: + switch (pSearch->RegistrySearch.Type) + { + case BURN_REGISTRY_SEARCH_TYPE_EXISTS: + hr = RegistrySearchExists(pSearch, pVariables); + break; + case BURN_REGISTRY_SEARCH_TYPE_VALUE: + hr = RegistrySearchValue(pSearch, pVariables); + break; + default: + hr = E_UNEXPECTED; + } + break; + case BURN_SEARCH_TYPE_MSI_COMPONENT: + hr = MsiComponentSearch(pSearch, pVariables); + break; + case BURN_SEARCH_TYPE_MSI_PRODUCT: + hr = MsiProductSearch(pSearch, pVariables); + break; + case BURN_SEARCH_TYPE_MSI_FEATURE: + hr = MsiFeatureSearch(pSearch, pVariables); + break; + case BURN_SEARCH_TYPE_EXTENSION: + hr = PerformExtensionSearch(pSearch); + break; + case BURN_SEARCH_TYPE_SET_VARIABLE: + hr = PerformSetVariable(pSearch, pVariables); + break; + default: + hr = E_UNEXPECTED; + } + + if (FAILED(hr)) + { + TraceError(hr, "Search failed. Id = '%ls'", pSearch->sczKey); + continue; + } + } + + hr = S_OK; + +LExit: + return hr; +} + +extern "C" void SearchesUninitialize( + __in BURN_SEARCHES* pSearches + ) +{ + if (pSearches->rgSearches) + { + for (DWORD i = 0; i < pSearches->cSearches; ++i) + { + BURN_SEARCH* pSearch = &pSearches->rgSearches[i]; + + ReleaseStr(pSearch->sczKey); + ReleaseStr(pSearch->sczVariable); + ReleaseStr(pSearch->sczCondition); + + switch (pSearch->Type) + { + case BURN_SEARCH_TYPE_DIRECTORY: + ReleaseStr(pSearch->DirectorySearch.sczPath); + break; + case BURN_SEARCH_TYPE_FILE: + ReleaseStr(pSearch->FileSearch.sczPath); + break; + case BURN_SEARCH_TYPE_REGISTRY: + ReleaseStr(pSearch->RegistrySearch.sczKey); + ReleaseStr(pSearch->RegistrySearch.sczValue); + break; + case BURN_SEARCH_TYPE_MSI_COMPONENT: + ReleaseStr(pSearch->MsiComponentSearch.sczProductCode); + ReleaseStr(pSearch->MsiComponentSearch.sczComponentId); + break; + case BURN_SEARCH_TYPE_MSI_PRODUCT: + ReleaseStr(pSearch->MsiProductSearch.sczGuid); + break; + case BURN_SEARCH_TYPE_MSI_FEATURE: + ReleaseStr(pSearch->MsiFeatureSearch.sczProductCode); + ReleaseStr(pSearch->MsiFeatureSearch.sczFeatureId); + break; + case BURN_SEARCH_TYPE_SET_VARIABLE: + BVariantUninitialize(&pSearch->SetVariable.value); + break; + } + } + MemFree(pSearches->rgSearches); + } +} + + +// internal function definitions + +static HRESULT DirectorySearchExists( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + BOOL fExists = FALSE; + + // format path + hr = VariableFormatString(pVariables, pSearch->DirectorySearch.sczPath, &sczPath, NULL); + ExitOnFailure(hr, "Failed to format variable string."); + + DWORD dwAttributes = ::GetFileAttributesW(sczPath); + if (INVALID_FILE_ATTRIBUTES == dwAttributes) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + hr = S_OK; // didn't find file, fExists still is false. + } + } + else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + fExists = TRUE; + } + + // else must have found a file. + // What if there is a hidden variable in sczPath? + ExitOnFailure(hr, "Failed while searching directory search: %ls, for path: %ls", pSearch->sczKey, sczPath); + + // set variable + hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE); + ExitOnFailure(hr, "Failed to set variable."); + +LExit: + StrSecureZeroFreeString(sczPath); + + return hr; +} + +static HRESULT DirectorySearchPath( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + + // format path + hr = VariableFormatString(pVariables, pSearch->DirectorySearch.sczPath, &sczPath, NULL); + ExitOnFailure(hr, "Failed to format variable string."); + + DWORD dwAttributes = ::GetFileAttributesW(sczPath); + if (INVALID_FILE_ATTRIBUTES == dwAttributes) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + } + else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE); + ExitOnFailure(hr, "Failed to set directory search path variable."); + } + else // must have found a file. + { + hr = E_PATHNOTFOUND; + } + + // What if there is a hidden variable in sczPath? + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + LogStringLine(REPORT_STANDARD, "Directory search: %ls, did not find path: %ls, reason: 0x%x", pSearch->sczKey, sczPath, hr); + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed while searching directory search: %ls, for path: %ls", pSearch->sczKey, sczPath); + +LExit: + StrSecureZeroFreeString(sczPath); + + return hr; +} + +static HRESULT FileSearchExists( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + LPWSTR sczPath = NULL; + BOOL fExists = FALSE; + + // format path + hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL); + ExitOnFailure(hr, "Failed to format variable string."); + + // find file + DWORD dwAttributes = ::GetFileAttributesW(sczPath); + if (INVALID_FILE_ATTRIBUTES == dwAttributes) + { + er = ::GetLastError(); + if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er) + { + // What if there is a hidden variable in sczPath? + LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath); + } + else + { + ExitOnWin32Error(er, hr, "Failed get to file attributes. '%ls'", pSearch->DirectorySearch.sczPath); + } + } + else if (FILE_ATTRIBUTE_DIRECTORY != (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + fExists = TRUE; + } + + // set variable + hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE); + ExitOnFailure(hr, "Failed to set variable."); + +LExit: + StrSecureZeroFreeString(sczPath); + return hr; +} + +static HRESULT FileSearchVersion( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + ULARGE_INTEGER uliVersion = { }; + LPWSTR sczPath = NULL; + VERUTIL_VERSION* pVersion = NULL; + + // format path + hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL); + ExitOnFailure(hr, "Failed to format path string."); + + // get file version + hr = FileVersion(sczPath, &uliVersion.HighPart, &uliVersion.LowPart); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + // What if there is a hidden variable in sczPath? + LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath); + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to get file version."); + + hr = VerVersionFromQword(uliVersion.QuadPart, &pVersion); + ExitOnFailure(hr, "Failed to create version from file version."); + + // set variable + hr = VariableSetVersion(pVariables, pSearch->sczVariable, pVersion, FALSE); + ExitOnFailure(hr, "Failed to set variable."); + +LExit: + StrSecureZeroFreeString(sczPath); + ReleaseVerutilVersion(pVersion); + return hr; +} + +static HRESULT FileSearchPath( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + + // format path + hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL); + ExitOnFailure(hr, "Failed to format variable string."); + + DWORD dwAttributes = ::GetFileAttributesW(sczPath); + if (INVALID_FILE_ATTRIBUTES == dwAttributes) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + } + else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) // found a directory. + { + hr = E_FILENOTFOUND; + } + else // found our file. + { + hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE); + ExitOnFailure(hr, "Failed to set variable to file search path."); + } + + // What if there is a hidden variable in sczPath? + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath); + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed while searching file search: %ls, for path: %ls", pSearch->sczKey, sczPath); + +LExit: + StrSecureZeroFreeString(sczPath); + + return hr; +} + +static HRESULT RegistrySearchExists( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + LPWSTR sczKey = NULL; + LPWSTR sczValue = NULL; + HKEY hKey = NULL; + DWORD dwType = 0; + BOOL fExists = FALSE; + REGSAM samDesired = KEY_QUERY_VALUE; + + if (pSearch->RegistrySearch.fWin64) + { + samDesired = samDesired | KEY_WOW64_64KEY; + } + + // format key string + hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczKey, &sczKey, NULL); + ExitOnFailure(hr, "Failed to format key string."); + + // open key + hr = RegOpen(pSearch->RegistrySearch.hRoot, sczKey, samDesired, &hKey); + if (SUCCEEDED(hr)) + { + fExists = TRUE; + } + else if (E_FILENOTFOUND == hr) + { + // What if there is a hidden variable in sczKey? + LogStringLine(REPORT_STANDARD, "Registry key not found. Key = '%ls'", sczKey); + fExists = FALSE; + hr = S_OK; + } + else + { + // What if there is a hidden variable in sczKey? + ExitOnFailure(hr, "Failed to open registry key. Key = '%ls'", sczKey); + } + + if (fExists && pSearch->RegistrySearch.sczValue) + { + // format value string + hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczValue, &sczValue, NULL); + ExitOnFailure(hr, "Failed to format value string."); + + // query value + er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, NULL, NULL); + switch (er) + { + case ERROR_SUCCESS: + fExists = TRUE; + break; + case ERROR_FILE_NOT_FOUND: + // What if there is a hidden variable in sczKey or sczValue? + LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue); + fExists = FALSE; + break; + default: + ExitOnWin32Error(er, hr, "Failed to query registry key value."); + } + } + + // set variable + hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE); + ExitOnFailure(hr, "Failed to set variable."); + +LExit: + if (FAILED(hr)) + { + // What if there is a hidden variable in sczKey? + LogStringLine(REPORT_STANDARD, "RegistrySearchExists failed: ID '%ls', HRESULT 0x%x", sczKey, hr); + } + + StrSecureZeroFreeString(sczKey); + StrSecureZeroFreeString(sczValue); + ReleaseRegKey(hKey); + + return hr; +} + +static HRESULT RegistrySearchValue( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + LPWSTR sczKey = NULL; + LPWSTR sczValue = NULL; + HKEY hKey = NULL; + DWORD dwType = 0; + DWORD cbData = 0; + LPBYTE pData = NULL; + DWORD cch = 0; + BURN_VARIANT value = { }; + REGSAM samDesired = KEY_QUERY_VALUE; + + if (pSearch->RegistrySearch.fWin64) + { + samDesired = samDesired | KEY_WOW64_64KEY; + } + + // format key string + hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczKey, &sczKey, NULL); + ExitOnFailure(hr, "Failed to format key string."); + + // format value string + if (pSearch->RegistrySearch.sczValue) + { + hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczValue, &sczValue, NULL); + ExitOnFailure(hr, "Failed to format value string."); + } + + // open key + hr = RegOpen(pSearch->RegistrySearch.hRoot, sczKey, samDesired, &hKey); + if (E_FILENOTFOUND == hr) + { + // What if there is a hidden variable in sczKey? + LogStringLine(REPORT_STANDARD, "Registry key not found. Key = '%ls'", sczKey); + + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to open registry key."); + + // get value + er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, NULL, &cbData); + if (ERROR_FILE_NOT_FOUND == er) + { + // What if there is a hidden variable in sczKey or sczValue? + LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue); + + ExitFunction1(hr = S_OK); + } + ExitOnWin32Error(er, hr, "Failed to query registry key value size."); + + pData = (LPBYTE)MemAlloc(cbData + sizeof(WCHAR), TRUE); // + sizeof(WCHAR) here to ensure that we always have a null terminator for REG_SZ + ExitOnNull(pData, hr, E_OUTOFMEMORY, "Failed to allocate memory registry value."); + + er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, pData, &cbData); + ExitOnWin32Error(er, hr, "Failed to query registry key value."); + + switch (dwType) + { + case REG_DWORD: + if (sizeof(LONG) != cbData) + { + ExitFunction1(hr = E_UNEXPECTED); + } + hr = BVariantSetNumeric(&value, *((LONG*)pData)); + break; + case REG_QWORD: + if (sizeof(LONGLONG) != cbData) + { + ExitFunction1(hr = E_UNEXPECTED); + } + hr = BVariantSetNumeric(&value, *((LONGLONG*)pData)); + break; + case REG_EXPAND_SZ: + if (pSearch->RegistrySearch.fExpandEnvironment) + { + hr = StrAlloc(&value.sczValue, cbData); + ExitOnFailure(hr, "Failed to allocate string buffer."); + value.Type = BURN_VARIANT_TYPE_STRING; + + cch = ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cbData); + if (cch > cbData) + { + hr = StrAlloc(&value.sczValue, cch); + ExitOnFailure(hr, "Failed to allocate string buffer."); + + if (cch != ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cch)) + { + ExitWithLastError(hr, "Failed to get expand environment string."); + } + } + break; + } + __fallthrough; + case REG_SZ: + hr = BVariantSetString(&value, (LPCWSTR)pData, 0, FALSE); + break; + default: + ExitOnFailure(hr = E_NOTIMPL, "Unsupported registry key value type. Type = '%u'", dwType); + } + ExitOnFailure(hr, "Failed to read registry value."); + + // change value to requested type + hr = BVariantChangeType(&value, pSearch->RegistrySearch.VariableType); + ExitOnFailure(hr, "Failed to change value type."); + + // Set variable. + hr = VariableSetVariant(pVariables, pSearch->sczVariable, &value); + ExitOnFailure(hr, "Failed to set variable."); + +LExit: + if (FAILED(hr)) + { + // What if there is a hidden variable in sczKey? + LogStringLine(REPORT_STANDARD, "RegistrySearchValue failed: ID '%ls', HRESULT 0x%x", sczKey, hr); + } + + StrSecureZeroFreeString(sczKey); + StrSecureZeroFreeString(sczValue); + ReleaseRegKey(hKey); + ReleaseMem(pData); + BVariantUninitialize(&value); + + return hr; +} + +static HRESULT MsiComponentSearch( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + INSTALLSTATE is = INSTALLSTATE_BROKEN; + LPWSTR sczComponentId = NULL; + LPWSTR sczProductCode = NULL; + LPWSTR sczPath = NULL; + + // format component id string + hr = VariableFormatString(pVariables, pSearch->MsiComponentSearch.sczComponentId, &sczComponentId, NULL); + ExitOnFailure(hr, "Failed to format component id string."); + + if (pSearch->MsiComponentSearch.sczProductCode) + { + // format product code string + hr = VariableFormatString(pVariables, pSearch->MsiComponentSearch.sczProductCode, &sczProductCode, NULL); + ExitOnFailure(hr, "Failed to format product code string."); + } + + if (sczProductCode) + { + hr = WiuGetComponentPath(sczProductCode, sczComponentId, &is, &sczPath); + } + else + { + hr = WiuLocateComponent(sczComponentId, &is, &sczPath); + } + + if (INSTALLSTATE_SOURCEABSENT == is) + { + is = INSTALLSTATE_SOURCE; + } + else if (INSTALLSTATE_UNKNOWN == is || INSTALLSTATE_NOTUSED == is) + { + is = INSTALLSTATE_ABSENT; + } + else if (INSTALLSTATE_ABSENT != is && INSTALLSTATE_LOCAL != is && INSTALLSTATE_SOURCE != is) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Failed to get component path: %d", is); + } + + // set variable + switch (pSearch->MsiComponentSearch.Type) + { + case BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH: + if (INSTALLSTATE_ABSENT == is || INSTALLSTATE_LOCAL == is || INSTALLSTATE_SOURCE == is) + { + hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE); + } + break; + case BURN_MSI_COMPONENT_SEARCH_TYPE_STATE: + hr = VariableSetNumeric(pVariables, pSearch->sczVariable, is, FALSE); + break; + case BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY: + if (INSTALLSTATE_ABSENT == is || INSTALLSTATE_LOCAL == is || INSTALLSTATE_SOURCE == is) + { + // remove file part from path, if any + LPWSTR wz = wcsrchr(sczPath, L'\\'); + if (wz) + { + wz[1] = L'\0'; + } + + hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE); + } + break; + } + ExitOnFailure(hr, "Failed to set variable."); + +LExit: + if (FAILED(hr)) + { + LogStringLine(REPORT_STANDARD, "MsiComponentSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr); + } + + StrSecureZeroFreeString(sczComponentId); + StrSecureZeroFreeString(sczProductCode); + ReleaseStr(sczPath); + return hr; +} + +static HRESULT MsiProductSearch( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + LPWSTR sczGuid = NULL; + LPCWSTR wzProperty = NULL; + LPWSTR *rgsczRelatedProductCodes = NULL; + DWORD dwRelatedProducts = 0; + BURN_VARIANT_TYPE type = BURN_VARIANT_TYPE_NONE; + BURN_VARIANT value = { }; + + switch (pSearch->MsiProductSearch.Type) + { + case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION: + wzProperty = INSTALLPROPERTY_VERSIONSTRING; + break; + case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE: + wzProperty = INSTALLPROPERTY_LANGUAGE; + break; + case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE: + wzProperty = INSTALLPROPERTY_PRODUCTSTATE; + break; + case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT: + wzProperty = INSTALLPROPERTY_ASSIGNMENTTYPE; + break; + default: + ExitOnFailure(hr = E_NOTIMPL, "Unsupported product search type: %u", pSearch->MsiProductSearch.Type); + } + + // format guid string + hr = VariableFormatString(pVariables, pSearch->MsiProductSearch.sczGuid, &sczGuid, NULL); + ExitOnFailure(hr, "Failed to format GUID string."); + + // get product info + value.Type = BURN_VARIANT_TYPE_STRING; + + // if this is an upgrade code then get the product code of the highest versioned related product + if (BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE == pSearch->MsiProductSearch.GuidType) + { + // WiuEnumRelatedProductCodes will log sczGuid on errors, what if there's a hidden variable in there? + hr = WiuEnumRelatedProductCodes(sczGuid, &rgsczRelatedProductCodes, &dwRelatedProducts, TRUE); + ExitOnFailure(hr, "Failed to enumerate related products for upgrade code."); + + // if we actually found a related product then use its upgrade code for the rest of the search + if (1 == dwRelatedProducts) + { + hr = StrAllocStringSecure(&sczGuid, rgsczRelatedProductCodes[0], 0); + ExitOnFailure(hr, "Failed to copy upgrade code."); + } + else + { + // set this here so we have a way of knowing that we don't need to bother + // querying for the product information below + hr = HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT); + } + } + + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr) + { + hr = WiuGetProductInfo(sczGuid, wzProperty, &value.sczValue); + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr) + { + // product state is available only through MsiGetProductInfoEx + // What if there is a hidden variable in sczGuid? + LogStringLine(REPORT_VERBOSE, "Trying per-machine extended info for property '%ls' for product: %ls", wzProperty, sczGuid); + hr = WiuGetProductInfoEx(sczGuid, NULL, MSIINSTALLCONTEXT_MACHINE, wzProperty, &value.sczValue); + + // if not in per-machine context, try per-user (unmanaged) + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr) + { + // What if there is a hidden variable in sczGuid? + LogStringLine(REPORT_STANDARD, "Trying per-user extended info for property '%ls' for product: %ls", wzProperty, sczGuid); + hr = WiuGetProductInfoEx(sczGuid, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, wzProperty, &value.sczValue); + } + } + } + + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr) + { + // What if there is a hidden variable in sczGuid? + LogStringLine(REPORT_STANDARD, "Product or related product not found: %ls", sczGuid); + + // set value to indicate absent + switch (pSearch->MsiProductSearch.Type) + { + case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT: __fallthrough; + case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION: + value.Type = BURN_VARIANT_TYPE_NUMERIC; + value.llValue = 0; + break; + case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE: + // is supposed to remain empty + break; + case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE: + value.Type = BURN_VARIANT_TYPE_NUMERIC; + value.llValue = INSTALLSTATE_ABSENT; + break; + } + + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get product info."); + + // change value type + switch (pSearch->MsiProductSearch.Type) + { + case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION: + type = BURN_VARIANT_TYPE_VERSION; + break; + case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE: + type = BURN_VARIANT_TYPE_STRING; + break; + case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE: __fallthrough; + case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT: + type = BURN_VARIANT_TYPE_NUMERIC; + break; + } + hr = BVariantChangeType(&value, type); + ExitOnFailure(hr, "Failed to change value type."); + + // Set variable. + hr = VariableSetVariant(pVariables, pSearch->sczVariable, &value); + ExitOnFailure(hr, "Failed to set variable."); + +LExit: + if (FAILED(hr)) + { + LogStringLine(REPORT_STANDARD, "MsiProductSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr); + } + + StrSecureZeroFreeString(sczGuid); + ReleaseStrArray(rgsczRelatedProductCodes, dwRelatedProducts); + BVariantUninitialize(&value); + + return hr; +} + +static HRESULT MsiFeatureSearch( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* /*pVariables*/ + ) +{ + HRESULT hr = E_NOTIMPL; + +//LExit: + if (FAILED(hr)) + { + LogStringLine(REPORT_STANDARD, "MsiFeatureSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr); + } + + return hr; +} + +static HRESULT PerformExtensionSearch( + __in BURN_SEARCH* pSearch + ) +{ + HRESULT hr = S_OK; + + hr = BurnExtensionPerformSearch(pSearch->ExtensionSearch.pExtension, pSearch->sczKey, pSearch->sczVariable); + + return hr; +} + +static HRESULT PerformSetVariable( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + + hr = VariableSetVariant(pVariables, pSearch->sczVariable, &pSearch->SetVariable.value); + ExitOnFailure(hr, "Failed to set variable: %ls", pSearch->sczVariable); + +LExit: + return hr; +} diff --git a/src/burn/engine/search.h b/src/burn/engine/search.h new file mode 100644 index 00000000..c699c97c --- /dev/null +++ b/src/burn/engine/search.h @@ -0,0 +1,163 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + +enum BURN_SEARCH_TYPE +{ + BURN_SEARCH_TYPE_NONE, + BURN_SEARCH_TYPE_DIRECTORY, + BURN_SEARCH_TYPE_FILE, + BURN_SEARCH_TYPE_REGISTRY, + BURN_SEARCH_TYPE_MSI_COMPONENT, + BURN_SEARCH_TYPE_MSI_PRODUCT, + BURN_SEARCH_TYPE_MSI_FEATURE, + BURN_SEARCH_TYPE_EXTENSION, + BURN_SEARCH_TYPE_SET_VARIABLE, +}; + +enum BURN_DIRECTORY_SEARCH_TYPE +{ + BURN_DIRECTORY_SEARCH_TYPE_NONE, + BURN_DIRECTORY_SEARCH_TYPE_EXISTS, + BURN_DIRECTORY_SEARCH_TYPE_PATH, +}; + +enum BURN_FILE_SEARCH_TYPE +{ + BURN_FILE_SEARCH_TYPE_NONE, + BURN_FILE_SEARCH_TYPE_EXISTS, + BURN_FILE_SEARCH_TYPE_VERSION, + BURN_FILE_SEARCH_TYPE_PATH, +}; + +enum BURN_REGISTRY_SEARCH_TYPE +{ + BURN_REGISTRY_SEARCH_TYPE_NONE, + BURN_REGISTRY_SEARCH_TYPE_EXISTS, + BURN_REGISTRY_SEARCH_TYPE_VALUE, +}; + +enum BURN_MSI_COMPONENT_SEARCH_TYPE +{ + BURN_MSI_COMPONENT_SEARCH_TYPE_NONE, + BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH, + BURN_MSI_COMPONENT_SEARCH_TYPE_STATE, + BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY, +}; + +enum BURN_MSI_PRODUCT_SEARCH_TYPE +{ + BURN_MSI_PRODUCT_SEARCH_TYPE_NONE, + BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION, + BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE, + BURN_MSI_PRODUCT_SEARCH_TYPE_STATE, + BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT, +}; + +enum BURN_MSI_PRODUCT_SEARCH_GUID_TYPE +{ + BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE, + BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_PRODUCTCODE, + BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE +}; + +enum BURN_MSI_FEATURE_SEARCH_TYPE +{ + BURN_MSI_FEATURE_SEARCH_TYPE_NONE, + BURN_MSI_FEATURE_SEARCH_TYPE_STATE, +}; + + +// structs + +typedef struct _BURN_SEARCH +{ + LPWSTR sczKey; + LPWSTR sczVariable; + LPWSTR sczCondition; + + BURN_SEARCH_TYPE Type; + union + { + struct + { + BURN_DIRECTORY_SEARCH_TYPE Type; + LPWSTR sczPath; + } DirectorySearch; + struct + { + BURN_FILE_SEARCH_TYPE Type; + LPWSTR sczPath; + } FileSearch; + struct + { + BURN_REGISTRY_SEARCH_TYPE Type; + BURN_VARIANT_TYPE VariableType; + HKEY hRoot; + LPWSTR sczKey; + LPWSTR sczValue; + BOOL fWin64; + BOOL fExpandEnvironment; + } RegistrySearch; + struct + { + BURN_MSI_COMPONENT_SEARCH_TYPE Type; + LPWSTR sczProductCode; + LPWSTR sczComponentId; + } MsiComponentSearch; + struct + { + BURN_MSI_PRODUCT_SEARCH_TYPE Type; + BURN_MSI_PRODUCT_SEARCH_GUID_TYPE GuidType; + LPWSTR sczGuid; + } MsiProductSearch; + struct + { + BURN_MSI_FEATURE_SEARCH_TYPE Type; + LPWSTR sczProductCode; + LPWSTR sczFeatureId; + } MsiFeatureSearch; + struct + { + BURN_EXTENSION* pExtension; + } ExtensionSearch; + struct + { + BURN_VARIANT value; + } SetVariable; + }; +} BURN_SEARCH; + +typedef struct _BURN_SEARCHES +{ + BURN_SEARCH* rgSearches; + DWORD cSearches; +} BURN_SEARCHES; + + +// function declarations + +HRESULT SearchesParseFromXml( + __in BURN_SEARCHES* pSearches, + __in BURN_EXTENSIONS* pBurnExtensions, + __in IXMLDOMNode* pixnBundle + ); +HRESULT SearchesExecute( + __in BURN_SEARCHES* pSearches, + __in BURN_VARIABLES* pVariables + ); +void SearchesUninitialize( + __in BURN_SEARCHES* pSearches + ); + + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/section.cpp b/src/burn/engine/section.cpp new file mode 100644 index 00000000..3720155c --- /dev/null +++ b/src/burn/engine/section.cpp @@ -0,0 +1,399 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// constants + +// If these defaults ever change, be sure to update constants in burn\stub\StubSection.cpp as well. +#define BURN_SECTION_NAME ".wixburn" +#define BURN_SECTION_MAGIC 0x00f14300 +#define BURN_SECTION_VERSION 0x00000002 +#define MANIFEST_CABINET_TOKEN L"0" + +// structs +typedef struct _BURN_SECTION_HEADER +{ + DWORD dwMagic; + DWORD dwVersion; + + GUID guidBundleId; + + DWORD dwStubSize; + DWORD dwOriginalChecksum; + DWORD dwOriginalSignatureOffset; + DWORD dwOriginalSignatureSize; + + DWORD dwFormat; + DWORD cContainers; + DWORD rgcbContainers[1]; +} BURN_SECTION_HEADER; + +static HRESULT VerifySectionMatchesMemoryPEHeader( + __in REFGUID pSection + ); + + +extern "C" HRESULT SectionInitialize( + __in BURN_SECTION* pSection, + __in HANDLE hEngineFile, + __in HANDLE hSourceEngineFile + ) +{ + HRESULT hr = S_OK; + DWORD cbRead = 0; + LARGE_INTEGER li = { }; + LONGLONG llSize = 0; + IMAGE_DOS_HEADER dosHeader = { }; + IMAGE_NT_HEADERS ntHeader = { }; + DWORD dwChecksumOffset = 0; + DWORD dwCertificateTableOffset = 0; + DWORD dwSignatureOffset = 0; + DWORD cbSignature = 0; + IMAGE_SECTION_HEADER sectionHeader = { }; + DWORD_PTR dwOriginalChecksumAndSignatureOffset = 0; + BURN_SECTION_HEADER* pBurnSectionHeader = NULL; + + pSection->hEngineFile = hEngineFile; + ExitOnInvalidHandleWithLastError(pSection->hEngineFile, hr, "Failed to open handle to engine process path."); + + pSection->hSourceEngineFile = INVALID_HANDLE_VALUE == hSourceEngineFile ? hEngineFile : hSourceEngineFile; + + // + // First, make sure we have a valid DOS signature. + // + if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to seek to start of file."); + } + + // read DOS header + if (!::ReadFile(pSection->hEngineFile, &dosHeader, sizeof(IMAGE_DOS_HEADER), &cbRead, NULL)) + { + ExitWithLastError(hr, "Failed to read DOS header."); + } + else if (sizeof(IMAGE_DOS_HEADER) > cbRead || IMAGE_DOS_SIGNATURE != dosHeader.e_magic) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find valid DOS image header in buffer."); + } + + // + // Now, make sure we have a valid NT signature. + // + + // seek to new header + li.QuadPart = dosHeader.e_lfanew; + if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to seek to NT header."); + } + + // read NT header + if (!::ReadFile(pSection->hEngineFile, &ntHeader, sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER), &cbRead, NULL)) + { + ExitWithLastError(hr, "Failed to read NT header."); + } + else if ((sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER)) > cbRead || IMAGE_NT_SIGNATURE != ntHeader.Signature) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find valid NT image header in buffer."); + } + + // Get the table offsets. + dwChecksumOffset = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + (sizeof(DWORD) * 16); + dwCertificateTableOffset = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - (sizeof(IMAGE_DATA_DIRECTORY) * (IMAGE_NUMBEROF_DIRECTORY_ENTRIES - IMAGE_DIRECTORY_ENTRY_SECURITY)); + + // Seek into the certificate table to get the signature size. + li.QuadPart = dwCertificateTableOffset; + if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to seek to section info."); + } + + if (!::ReadFile(pSection->hEngineFile, &dwSignatureOffset, sizeof(dwSignatureOffset), &cbRead, NULL)) + { + ExitWithLastError(hr, "Failed to read signature offset."); + } + + if (!::ReadFile(pSection->hEngineFile, &cbSignature, sizeof(cbSignature), &cbRead, NULL)) + { + ExitWithLastError(hr, "Failed to read signature size."); + } + + // + // Finally, get into the section table and look for the Burn section info. + // + + // seek past optional headers + li.QuadPart = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + ntHeader.FileHeader.SizeOfOptionalHeader; + if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to seek past optional headers."); + } + + // read sections one by one until we find our section + for (DWORD i = 0; ; ++i) + { + // read section + if (!::ReadFile(pSection->hEngineFile, §ionHeader, sizeof(IMAGE_SECTION_HEADER), &cbRead, NULL)) + { + ExitWithLastError(hr, "Failed to read image section header, index: %u", i); + } + if (sizeof(IMAGE_SECTION_HEADER) > cbRead) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to read complete image section header, index: %u", i); + } + + // compare header name + C_ASSERT(sizeof(sectionHeader.Name) == sizeof(BURN_SECTION_NAME) - 1); + if (0 == memcmp(sectionHeader.Name, BURN_SECTION_NAME, sizeof(sectionHeader.Name))) + { + break; + } + + // fail if we hit the end + if (i + 1 >= ntHeader.FileHeader.NumberOfSections) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find Burn section."); + } + } + + // + // We've arrived at the section info. + // + + // check size of section + if (sizeof(BURN_SECTION_HEADER) > sectionHeader.SizeOfRawData) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to read section info, data to short: %u", sectionHeader.SizeOfRawData); + } + + // allocate buffer for section info + pBurnSectionHeader = (BURN_SECTION_HEADER*)MemAlloc(sectionHeader.SizeOfRawData, TRUE); + ExitOnNull(pBurnSectionHeader, hr, E_OUTOFMEMORY, "Failed to allocate buffer for section info."); + + // seek to section info + li.QuadPart = sectionHeader.PointerToRawData; + if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN)) + { + ExitWithLastError(hr, "Failed to seek to section info."); + } + + // Note the location of original checksum and signature information in the burn section header. + dwOriginalChecksumAndSignatureOffset = sectionHeader.PointerToRawData + (reinterpret_cast(&pBurnSectionHeader->dwOriginalChecksum) - reinterpret_cast(pBurnSectionHeader)); + + // read section info + if (!::ReadFile(pSection->hEngineFile, pBurnSectionHeader, sectionHeader.SizeOfRawData, &cbRead, NULL)) + { + ExitWithLastError(hr, "Failed to read section info."); + } + else if (sectionHeader.SizeOfRawData > cbRead) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to read complete section info."); + } + + // validate version of section info + if (BURN_SECTION_VERSION != pBurnSectionHeader->dwVersion) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to read section info, unsupported version: %08x", pBurnSectionHeader->dwVersion); + } + + hr = FileSizeByHandle(pSection->hSourceEngineFile, &llSize); + ExitOnFailure(hr, "Failed to get total size of bundle."); + + pSection->cbStub = pBurnSectionHeader->dwStubSize; + + // If there is an original signature use that to determine the engine size. + if (pBurnSectionHeader->dwOriginalSignatureOffset) + { + pSection->cbEngineSize = pBurnSectionHeader->dwOriginalSignatureOffset + pBurnSectionHeader->dwOriginalSignatureSize; + } + else if (dwSignatureOffset) // if there is a signature, use it. + { + pSection->cbEngineSize = dwSignatureOffset + cbSignature; + } + else // just use the stub and UX container as the size of the engine. + { + pSection->cbEngineSize = pSection->cbStub + pBurnSectionHeader->rgcbContainers[0]; + } + + pSection->qwBundleSize = static_cast(llSize); + + pSection->dwChecksumOffset = dwChecksumOffset; + pSection->dwCertificateTableOffset = dwCertificateTableOffset; + pSection->dwOriginalChecksumAndSignatureOffset = dwOriginalChecksumAndSignatureOffset; + + pSection->dwOriginalChecksum = pBurnSectionHeader->dwOriginalChecksum; + pSection->dwOriginalSignatureOffset = pBurnSectionHeader->dwOriginalSignatureOffset; + pSection->dwOriginalSignatureSize = pBurnSectionHeader->dwOriginalSignatureSize; + + pSection->dwFormat = pBurnSectionHeader->dwFormat; + pSection->cContainers = pBurnSectionHeader->cContainers; + pSection->rgcbContainers = (DWORD*)MemAlloc(sizeof(DWORD) * pSection->cContainers, TRUE); + ExitOnNull(pSection->rgcbContainers, hr, E_OUTOFMEMORY, "Failed to allocate memory for container sizes."); + + memcpy(pSection->rgcbContainers, pBurnSectionHeader->rgcbContainers, sizeof(DWORD) * pSection->cContainers); + + // TODO: verify more than just the GUID. + hr = VerifySectionMatchesMemoryPEHeader(pBurnSectionHeader->guidBundleId); + ExitOnRootFailure(hr, "PE Header from file didn't match PE Header in memory."); + +LExit: + ReleaseMem(pBurnSectionHeader); + + return hr; +} + +extern "C" void SectionUninitialize( + __out BURN_SECTION* pSection + ) +{ + ReleaseMem(pSection->rgcbContainers); + memset(pSection, 0, sizeof(BURN_SECTION)); +} + +extern "C" HRESULT SectionGetAttachedContainerInfo( + __in BURN_SECTION* pSection, + __in DWORD iContainerIndex, + __in DWORD dwExpectedType, + __out DWORD64* pqwOffset, + __out DWORD64* pqwSize, + __out BOOL* pfPresent + ) +{ + HRESULT hr = S_OK; + + // validate container info + if (iContainerIndex >= pSection->cContainers) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find container info, too few elements: %u", pSection->cContainers); + } + else if (dwExpectedType != pSection->dwFormat) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Unexpected container format."); + } + + // If we are asking for the UX container, find it right after the stub. + if (0 == iContainerIndex) + { + *pqwOffset = pSection->cbStub; + } + else // attached containers start after the whole engine. + { + *pqwOffset = pSection->cbEngineSize; + for (DWORD i = 1; i < iContainerIndex; ++i) + { + *pqwOffset += pSection->rgcbContainers[i]; + } + } + + *pqwSize = pSection->rgcbContainers[iContainerIndex]; + *pfPresent = (*pqwOffset + *pqwSize) <= pSection->qwBundleSize; + + AssertSz(*pfPresent || pSection->qwBundleSize <= *pqwOffset, "An attached container should either be present or completely absent from the bundle. Found a case where the attached container is partially present which is wrong."); + +LExit: + return hr; +} + +HRESULT VerifySectionMatchesMemoryPEHeader( + __in REFGUID pBundleId + ) +{ + HRESULT hr = S_OK; + BYTE* pbPEHeader = NULL; + PIMAGE_DOS_HEADER pDosHeader = NULL; + PIMAGE_NT_HEADERS pNtHeader = NULL; + PIMAGE_SECTION_HEADER pSections = NULL; + PIMAGE_SECTION_HEADER pSectionHeader = NULL; + BURN_SECTION_HEADER* pBurnSectionHeader = NULL; + + pbPEHeader = reinterpret_cast(::GetModuleHandleW(NULL)); + ExitOnNullWithLastError(pbPEHeader, hr, "Failed to get module handle to process."); + + // + // First, make sure we have a valid DOS signature. + // + + pDosHeader = reinterpret_cast(pbPEHeader); + if (IMAGE_DOS_SIGNATURE != pDosHeader->e_magic) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find valid DOS image header in buffer."); + } + + // + // Now, make sure we have a valid NT signature. + // + + pNtHeader = reinterpret_cast(pbPEHeader + pDosHeader->e_lfanew); + if (IMAGE_NT_SIGNATURE != pNtHeader->Signature) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find valid NT image header in buffer."); + } + + // + // Finally, get into the section table and look for the Burn section info. + // + + pSections = reinterpret_cast(pbPEHeader + pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + pNtHeader->FileHeader.SizeOfOptionalHeader); + + // Read sections one by one until we find our section. + for (DWORD i = 0; ; ++i) + { + pSectionHeader = pSections + i; + + // Compare header name. + C_ASSERT(sizeof(pSectionHeader->Name) == sizeof(BURN_SECTION_NAME) - 1); + if (0 == memcmp(pSectionHeader->Name, BURN_SECTION_NAME, sizeof(pSectionHeader->Name))) + { + break; + } + + // Fail if we hit the end. + if (i + 1 >= pNtHeader->FileHeader.NumberOfSections) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to find Burn section."); + } + } + + // + // We've arrived at the section info. + // + + // Check size of section. + if (sizeof(BURN_SECTION_HEADER) > pSectionHeader->SizeOfRawData) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to read section info, data to short: %u", pSectionHeader->SizeOfRawData); + } + + // Get Burn section info. + pBurnSectionHeader = reinterpret_cast(pbPEHeader + pSectionHeader->VirtualAddress); + + // Validate version of section info. + if (BURN_SECTION_VERSION != pBurnSectionHeader->dwVersion) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Failed to read section info, unsupported version: %08x", pBurnSectionHeader->dwVersion); + } + + if (!::IsEqualGUID(pBundleId, pBurnSectionHeader->guidBundleId)) + { + hr = E_INVALIDDATA; + ExitOnRootFailure(hr, "Bundle guid didn't match the guid in the PE Header in memory."); + } + +LExit: + return hr; +} diff --git a/src/burn/engine/section.h b/src/burn/engine/section.h new file mode 100644 index 00000000..6c62ba44 --- /dev/null +++ b/src/burn/engine/section.h @@ -0,0 +1,54 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// structs + +typedef struct _BURN_SECTION +{ + HANDLE hEngineFile; + HANDLE hSourceEngineFile; + + DWORD cbStub; + DWORD cbEngineSize; // stub + UX container + original certficiate + DWORD64 qwBundleSize; // stub + UX container + original certificate [+ attached containers* + final certificate] + + DWORD dwChecksumOffset; + DWORD dwCertificateTableOffset; + DWORD_PTR dwOriginalChecksumAndSignatureOffset; + + DWORD dwOriginalChecksum; + DWORD dwOriginalSignatureOffset; + DWORD dwOriginalSignatureSize; + + DWORD dwFormat; + DWORD cContainers; + DWORD* rgcbContainers; +} BURN_SECTION; + + +HRESULT SectionInitialize( + __in BURN_SECTION* pSection, + __in HANDLE hEngineFile, + __in HANDLE hSourceEngineFile + ); +void SectionUninitialize( + __in BURN_SECTION* pSection + ); +HRESULT SectionGetAttachedContainerInfo( + __in BURN_SECTION* pSection, + __in DWORD iContainerIndex, + __in DWORD dwExpectedType, + __out DWORD64* pqwOffset, + __out DWORD64* pqwSize, + __out BOOL* pfPresent + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/splashscreen.cpp b/src/burn/engine/splashscreen.cpp new file mode 100644 index 00000000..90bd5203 --- /dev/null +++ b/src/burn/engine/splashscreen.cpp @@ -0,0 +1,355 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#define BURN_SPLASHSCREEN_CLASS_WINDOW L"WixBurnSplashScreen" +#define IDB_SPLASHSCREEN 1 + +// struct + +struct SPLASHSCREEN_INFO +{ + HBITMAP hBitmap; + SIZE defaultDpiSize; + SIZE size; + UINT nDpi; + HWND hWnd; +}; + +struct SPLASHSCREEN_CONTEXT +{ + HANDLE hInitializedEvent; + HINSTANCE hInstance; + LPCWSTR wzCaption; + + HWND* pHwnd; +}; + +// internal function definitions + +static DWORD WINAPI ThreadProc( + __in LPVOID pvContext + ); +static LRESULT CALLBACK WndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ); +static HRESULT LoadSplashScreen( + __in SPLASHSCREEN_CONTEXT* pContext, + __in SPLASHSCREEN_INFO* pSplashScreen + ); +static BOOL OnDpiChanged( + __in SPLASHSCREEN_INFO* pSplashScreen, + __in WPARAM wParam, + __in LPARAM lParam + ); +static void OnEraseBkgnd( + __in SPLASHSCREEN_INFO* pSplashScreen, + __in WPARAM wParam + ); +static void OnNcCreate( + __in HWND hWnd, + __in LPARAM lParam + ); +static void ScaleSplashScreen( + __in SPLASHSCREEN_INFO* pSplashScreen, + __in UINT nDpi, + __in int x, + __in int y + ); + + +// function definitions + +extern "C" void SplashScreenCreate( + __in HINSTANCE hInstance, + __in_z_opt LPCWSTR wzCaption, + __out HWND* pHwnd + ) +{ + HRESULT hr = S_OK; + SPLASHSCREEN_CONTEXT context = { }; + HANDLE rgSplashScreenEvents[2] = { }; + DWORD dwSplashScreenThreadId = 0; + + rgSplashScreenEvents[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL); + ExitOnNullWithLastError(rgSplashScreenEvents[0], hr, "Failed to create modal event."); + + // create splash screen thread. + context.hInitializedEvent = rgSplashScreenEvents[0]; + context.hInstance = hInstance; + context.wzCaption = wzCaption; + context.pHwnd = pHwnd; + + rgSplashScreenEvents[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, &dwSplashScreenThreadId); + ExitOnNullWithLastError(rgSplashScreenEvents[1], hr, "Failed to create UI thread."); + + // It doesn't really matter if the thread gets initialized (WAIT_OBJECT_0) or fails and exits + // prematurely (WAIT_OBJECT_0 + 1), we just want to wait long enough for one of those two + // events to happen. + ::WaitForMultipleObjects(countof(rgSplashScreenEvents), rgSplashScreenEvents, FALSE, INFINITE); + +LExit: + ReleaseHandle(rgSplashScreenEvents[1]); + ReleaseHandle(rgSplashScreenEvents[0]); +} + +extern "C" HRESULT SplashScreenDisplayError( + __in BOOTSTRAPPER_DISPLAY display, + __in_z LPCWSTR wzBundleName, + __in HRESULT hrError + ) +{ + HRESULT hr = S_OK; + LPWSTR sczDisplayString = NULL; + + hr = StrAllocFromError(&sczDisplayString, hrError, NULL); + ExitOnFailure(hr, "Failed to allocate string to display error message"); + + Trace(REPORT_STANDARD, "Error message displayed because: %ls", sczDisplayString); + + if (BOOTSTRAPPER_DISPLAY_NONE == display || BOOTSTRAPPER_DISPLAY_PASSIVE == display || BOOTSTRAPPER_DISPLAY_EMBEDDED == display) + { + // Don't display the error dialog in these modes + ExitFunction1(hr = S_OK); + } + + ::MessageBoxW(NULL, sczDisplayString, wzBundleName, MB_OK | MB_ICONERROR | MB_SYSTEMMODAL); + +LExit: + ReleaseStr(sczDisplayString); + + return hr; +} + + +static DWORD WINAPI ThreadProc( + __in LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + SPLASHSCREEN_CONTEXT* pContext = static_cast(pvContext); + + SPLASHSCREEN_INFO splashScreenInfo = { }; + + WNDCLASSW wc = { }; + BOOL fRegistered = TRUE; + + BOOL fRet = FALSE; + MSG msg = { }; + + // Register the window class. + wc.lpfnWndProc = WndProc; + wc.hInstance = pContext->hInstance; + wc.hCursor = ::LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); + wc.lpszClassName = BURN_SPLASHSCREEN_CLASS_WINDOW; + if (!::RegisterClassW(&wc)) + { + ExitWithLastError(hr, "Failed to register window."); + } + + fRegistered = TRUE; + + hr = LoadSplashScreen(pContext, &splashScreenInfo); + ExitOnFailure(hr, "Failed to load splash screen."); + + // Return the splash screen window and free the main thread waiting for us to be initialized. + *pContext->pHwnd = splashScreenInfo.hWnd; + ::SetEvent(pContext->hInitializedEvent); + + // Pump messages until the bootstrapper application destroys the window. + while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) + { + if (-1 == fRet) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Unexpected return value from message pump."); + } + else if (!::IsDialogMessageW(splashScreenInfo.hWnd, &msg)) + { + ::TranslateMessage(&msg); + ::DispatchMessageW(&msg); + } + } + +LExit: + if (fRegistered) + { + ::UnregisterClassW(BURN_SPLASHSCREEN_CLASS_WINDOW, pContext->hInstance); + } + + if (splashScreenInfo.hBitmap) + { + ::DeleteObject(splashScreenInfo.hBitmap); + } + + return hr; +} + +static LRESULT CALLBACK WndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + LRESULT lres = 0; + SPLASHSCREEN_INFO* pSplashScreen = reinterpret_cast(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); + + switch (uMsg) + { + case WM_NCCREATE: + OnNcCreate(hWnd, lParam); + break; + + case WM_NCDESTROY: + lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam); + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); + ::PostQuitMessage(0); + return lres; + + case WM_NCHITTEST: + return HTCAPTION; // allow window to be moved by grabbing any pixel. + + case WM_DPICHANGED: + if (OnDpiChanged(pSplashScreen, wParam, lParam)) + { + return 0; + } + break; + + case WM_ERASEBKGND: + OnEraseBkgnd(pSplashScreen, wParam); + return 1; + } + + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + +static HRESULT LoadSplashScreen( + __in SPLASHSCREEN_CONTEXT* pContext, + __in SPLASHSCREEN_INFO* pSplashScreen + ) +{ + HRESULT hr = S_OK; + BITMAP bmp = { }; + POINT pt = { }; + int x = 0; + int y = 0; + DPIU_MONITOR_CONTEXT* pMonitorContext = NULL; + RECT* pMonitorRect = NULL; + + pSplashScreen->nDpi = USER_DEFAULT_SCREEN_DPI; + pSplashScreen->hBitmap = ::LoadBitmapW(pContext->hInstance, MAKEINTRESOURCEW(IDB_SPLASHSCREEN)); + ExitOnNullWithLastError(pSplashScreen->hBitmap, hr, "Failed to load splash screen bitmap."); + + ::GetObject(pSplashScreen->hBitmap, sizeof(bmp), static_cast(&bmp)); + pSplashScreen->defaultDpiSize.cx = pSplashScreen->size.cx = bmp.bmWidth; + pSplashScreen->defaultDpiSize.cy = pSplashScreen->size.cy = bmp.bmHeight; + + // Try to default to the monitor with the mouse, otherwise default to the primary monitor. + if (!::GetCursorPos(&pt)) + { + pt.x = 0; + pt.y = 0; + } + + // Try to center the window on the chosen monitor. + hr = DpiuGetMonitorContextFromPoint(&pt, &pMonitorContext); + if (SUCCEEDED(hr)) + { + pMonitorRect = &pMonitorContext->mi.rcWork; + if (pMonitorContext->nDpi != pSplashScreen->nDpi) + { + ScaleSplashScreen(pSplashScreen, pMonitorContext->nDpi, pMonitorRect->left, pMonitorRect->top); + } + + x = pMonitorRect->left + (pMonitorRect->right - pMonitorRect->left - pSplashScreen->size.cx) / 2; + y = pMonitorRect->top + (pMonitorRect->bottom - pMonitorRect->top - pSplashScreen->size.cy) / 2; + } + else + { + hr = S_OK; + x = CW_USEDEFAULT; + y = CW_USEDEFAULT; + } + + pSplashScreen->hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, BURN_SPLASHSCREEN_CLASS_WINDOW, pContext->wzCaption, WS_POPUP | WS_VISIBLE, x, y, pSplashScreen->size.cx, pSplashScreen->size.cy, HWND_DESKTOP, NULL, pContext->hInstance, pSplashScreen); + ExitOnNullWithLastError(pSplashScreen->hWnd, hr, "Failed to create window."); + +LExit: + MemFree(pMonitorContext); + + return hr; +} + +static BOOL OnDpiChanged( + __in SPLASHSCREEN_INFO* pSplashScreen, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + UINT nDpi = HIWORD(wParam); + RECT* pRect = reinterpret_cast(lParam); + BOOL fDpiChanged = pSplashScreen->nDpi != nDpi; + + if (fDpiChanged) + { + ScaleSplashScreen(pSplashScreen, nDpi, pRect->left, pRect->top); + } + + return fDpiChanged; +} + +static void OnEraseBkgnd( + __in SPLASHSCREEN_INFO* pSplashScreen, + __in WPARAM wParam + ) +{ + HDC hdc = reinterpret_cast(wParam); + HDC hdcMem = ::CreateCompatibleDC(hdc); + HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pSplashScreen->hBitmap)); + ::StretchBlt(hdc, 0, 0, pSplashScreen->size.cx, pSplashScreen->size.cy, hdcMem, 0, 0, pSplashScreen->defaultDpiSize.cx, pSplashScreen->defaultDpiSize.cy, SRCCOPY); + ::SelectObject(hdcMem, hDefaultBitmap); + ::DeleteDC(hdcMem); +} + +static void OnNcCreate( + __in HWND hWnd, + __in LPARAM lParam + ) +{ + DPIU_WINDOW_CONTEXT windowContext = { }; + CREATESTRUCTW* pCreateStruct = reinterpret_cast(lParam); + SPLASHSCREEN_INFO* pSplashScreen = reinterpret_cast(pCreateStruct->lpCreateParams); + + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(pSplashScreen)); + pSplashScreen->hWnd = hWnd; + + DpiuGetWindowContext(pSplashScreen->hWnd, &windowContext); + + if (windowContext.nDpi != pSplashScreen->nDpi) + { + ScaleSplashScreen(pSplashScreen, windowContext.nDpi, pCreateStruct->x, pCreateStruct->y); + } +} + +static void ScaleSplashScreen( + __in SPLASHSCREEN_INFO* pSplashScreen, + __in UINT nDpi, + __in int x, + __in int y + ) +{ + pSplashScreen->nDpi = nDpi; + + pSplashScreen->size.cx = DpiuScaleValue(pSplashScreen->defaultDpiSize.cx, pSplashScreen->nDpi); + pSplashScreen->size.cy = DpiuScaleValue(pSplashScreen->defaultDpiSize.cy, pSplashScreen->nDpi); + + if (pSplashScreen->hWnd) + { + ::SetWindowPos(pSplashScreen->hWnd, NULL, x, y, pSplashScreen->size.cx, pSplashScreen->size.cy, SWP_NOACTIVATE | SWP_NOZORDER); + } +} diff --git a/src/burn/engine/splashscreen.h b/src/burn/engine/splashscreen.h new file mode 100644 index 00000000..8f8817c7 --- /dev/null +++ b/src/burn/engine/splashscreen.h @@ -0,0 +1,31 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + + +// structs + + +// functions + +void SplashScreenCreate( + __in HINSTANCE hInstance, + __in_z_opt LPCWSTR wzCaption, + __out HWND* pHwnd + ); +HRESULT SplashScreenDisplayError( + __in BOOTSTRAPPER_DISPLAY display, + __in_z LPCWSTR wzBundleName, + __in HRESULT hrError + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/uithread.cpp b/src/burn/engine/uithread.cpp new file mode 100644 index 00000000..433cb171 --- /dev/null +++ b/src/burn/engine/uithread.cpp @@ -0,0 +1,222 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#define BURN_UITHREAD_CLASS_WINDOW L"WixBurnMessageWindow" + + +// structs + +struct UITHREAD_CONTEXT +{ + HANDLE hInitializedEvent; + HINSTANCE hInstance; + BURN_ENGINE_STATE* pEngineState; +}; + +struct UITHREAD_INFO +{ + BOOL fElevated; + BURN_USER_EXPERIENCE* pUserExperience; +}; + + +// internal function declarations + +static DWORD WINAPI ThreadProc( + __in LPVOID pvContext + ); + +static LRESULT CALLBACK WndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ); + + +// function definitions + +HRESULT UiCreateMessageWindow( + __in HINSTANCE hInstance, + __in BURN_ENGINE_STATE* pEngineState + ) +{ + HRESULT hr = S_OK; + HANDLE rgWaitHandles[2] = { }; + UITHREAD_CONTEXT context = { }; + + // Create event to signal after the UI thread / window is initialized. + rgWaitHandles[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL); + ExitOnNullWithLastError(rgWaitHandles[0], hr, "Failed to create initialization event."); + + // Pass necessary information to create the window. + context.hInitializedEvent = rgWaitHandles[0]; + context.hInstance = hInstance; + context.pEngineState = pEngineState; + + // Create our separate UI thread. + rgWaitHandles[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, NULL); + ExitOnNullWithLastError(rgWaitHandles[1], hr, "Failed to create the UI thread."); + + // Wait for either the thread to be initialized or the window to exit / fail prematurely. + ::WaitForMultipleObjects(countof(rgWaitHandles), rgWaitHandles, FALSE, INFINITE); + + pEngineState->hMessageWindowThread = rgWaitHandles[1]; + rgWaitHandles[1] = NULL; + +LExit: + ReleaseHandle(rgWaitHandles[1]); + ReleaseHandle(rgWaitHandles[0]); + + return hr; +} + +void UiCloseMessageWindow( + __in BURN_ENGINE_STATE* pEngineState + ) +{ + if (::IsWindow(pEngineState->hMessageWindow)) + { + ::PostMessageW(pEngineState->hMessageWindow, WM_CLOSE, 0, 0); + + // Give the window 15 seconds to close because if it stays open it can prevent + // the engine from starting a reboot (should a reboot actually be necessary). + ::WaitForSingleObject(pEngineState->hMessageWindowThread, 15 * 1000); + } +} + + +// internal function definitions + +static DWORD WINAPI ThreadProc( + __in LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + UITHREAD_CONTEXT* pContext = static_cast(pvContext); + UITHREAD_INFO info = { }; + + WNDCLASSW wc = { }; + BOOL fRegistered = TRUE; + HWND hWnd = NULL; + + BOOL fRet = FALSE; + MSG msg = { }; + + BURN_ENGINE_STATE* pEngineState = pContext->pEngineState; + BOOL fElevated = BURN_MODE_ELEVATED == pContext->pEngineState->mode; + + // If elevated, set up the thread local storage to store the correct pipe to communicate logging. + if (fElevated) + { + Assert(TLS_OUT_OF_INDEXES != pEngineState->dwElevatedLoggingTlsId); + + if (!::TlsSetValue(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe)) + { + // If the function failed we cannot write to the pipe so just terminate. + ExitFunction1(hr = E_INVALIDSTATE); + } + } + + wc.lpfnWndProc = WndProc; + wc.hInstance = pContext->hInstance; + wc.lpszClassName = BURN_UITHREAD_CLASS_WINDOW; + + if (!::RegisterClassW(&wc)) + { + ExitWithLastError(hr, "Failed to register window."); + } + + fRegistered = TRUE; + + info.fElevated = fElevated; + info.pUserExperience = &pEngineState->userExperience; + + // Create the window to handle reboots without activating it. + hWnd = ::CreateWindowExW(WS_EX_NOACTIVATE, wc.lpszClassName, NULL, WS_POPUP, 0, 0, 0, 0, HWND_DESKTOP, NULL, pContext->hInstance, &info); + ExitOnNullWithLastError(hWnd, hr, "Failed to create window."); + + ::ShowWindow(hWnd, SW_SHOWNA); + + // Persist the window handle and let the caller know we've initialized. + pEngineState->hMessageWindow = hWnd; + ::SetEvent(pContext->hInitializedEvent); + + // Pump messages until the window is closed. + while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) + { + if (-1 == fRet) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Unexpected return value from message pump."); + } + else if (!::IsDialogMessageW(msg.hwnd, &msg)) + { + ::TranslateMessage(&msg); + ::DispatchMessageW(&msg); + } + } + +LExit: + if (fRegistered) + { + ::UnregisterClassW(BURN_UITHREAD_CLASS_WINDOW, pContext->hInstance); + } + + return hr; +} + +static LRESULT CALLBACK WndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_NCCREATE: + { + LPCREATESTRUCTW lpcs = reinterpret_cast(lParam); + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(lpcs->lpCreateParams)); + break; + } + + case WM_NCDESTROY: + { + LRESULT lRes = ::DefWindowProcW(hWnd, uMsg, wParam, lParam); + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); + return lRes; + } + + case WM_QUERYENDSESSION: + { + DWORD dwEndSession = static_cast(lParam); + BOOL fCritical = ENDSESSION_CRITICAL & dwEndSession; + BOOL fCancel = TRUE; + BOOL fRet = FALSE; + + // Always block shutdown in the elevated process, but ask the BA in the non-elevated. + UITHREAD_INFO* pInfo = reinterpret_cast(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); + if (!pInfo->fElevated) + { + // TODO: instead of recommending canceling all non-critical shutdowns, maybe we should only recommend cancel + // when the engine is doing work? + fCancel = !fCritical; + // TODO: There's a race condition here where the BA may not have been loaded, or already was unloaded. + UserExperienceOnSystemShutdown(pInfo->pUserExperience, dwEndSession, &fCancel); + } + + fRet = !fCancel; + LogId(REPORT_STANDARD, MSG_SYSTEM_SHUTDOWN, LoggingBoolToString(fCritical), LoggingBoolToString(pInfo->fElevated), LoggingBoolToString(fRet)); + return fRet; + } + + case WM_DESTROY: + ::PostQuitMessage(0); + return 0; + } + + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); +} diff --git a/src/burn/engine/uithread.h b/src/burn/engine/uithread.h new file mode 100644 index 00000000..41168d52 --- /dev/null +++ b/src/burn/engine/uithread.h @@ -0,0 +1,23 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// functions + +HRESULT UiCreateMessageWindow( + __in HINSTANCE hInstance, + __in BURN_ENGINE_STATE* pEngineState + ); + +void UiCloseMessageWindow( + __in BURN_ENGINE_STATE* pEngineState + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/update.cpp b/src/burn/engine/update.cpp new file mode 100644 index 00000000..b04fa9a4 --- /dev/null +++ b/src/burn/engine/update.cpp @@ -0,0 +1,44 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// internal function declarations + + +// function definitions + +extern "C" HRESULT UpdateParseFromXml( + __in BURN_UPDATE* pUpdate, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNode* pixnUpdateNode = NULL; + + hr = XmlSelectSingleNode(pixnBundle, L"Update", &pixnUpdateNode); + if (S_FALSE == hr) + { + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to select Bundle/Update node."); + + // @Location + hr = XmlGetAttributeEx(pixnUpdateNode, L"Location", &pUpdate->sczUpdateSource); + ExitOnFailure(hr, "Failed to get Update@Location."); + +LExit: + ReleaseObject(pixnUpdateNode); + + return hr; +} + +extern "C" void UpdateUninitialize( + __in BURN_UPDATE* pUpdate + ) +{ + PackageUninitialize(&pUpdate->package); + + ReleaseStr(pUpdate->sczUpdateSource); + memset(pUpdate, 0, sizeof(BURN_UPDATE)); +} diff --git a/src/burn/engine/update.h b/src/burn/engine/update.h new file mode 100644 index 00000000..67d40481 --- /dev/null +++ b/src/burn/engine/update.h @@ -0,0 +1,33 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// structs + +typedef struct _BURN_UPDATE +{ + BOOL fUpdateAvailable; + LPWSTR sczUpdateSource; + + BURN_PACKAGE package; +} BURN_UPDATE; + + +// function declarations + +HRESULT UpdateParseFromXml( + __in BURN_UPDATE* pUpdate, + __in IXMLDOMNode* pixnBundle + ); +void UpdateUninitialize( + __in BURN_UPDATE* pUpdate + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/userexperience.cpp b/src/burn/engine/userexperience.cpp new file mode 100644 index 00000000..2215a070 --- /dev/null +++ b/src/burn/engine/userexperience.cpp @@ -0,0 +1,2653 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// internal function declarations + +static int FilterResult( + __in DWORD dwAllowedResults, + __in int nResult + ); + +static HRESULT FilterExecuteResult( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus, + __in BOOL fRollback, + __in BOOL fCancel, + __in LPCWSTR sczEventName + ); + +static HRESULT SendBAMessage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOTSTRAPPER_APPLICATION_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ); + +static HRESULT SendBAMessageFromInactiveEngine( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOTSTRAPPER_APPLICATION_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ); + + +// function definitions + +/******************************************************************* + UserExperienceParseFromXml - + +*******************************************************************/ +extern "C" HRESULT UserExperienceParseFromXml( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNode* pixnUserExperienceNode = NULL; + + // select UX node + hr = XmlSelectSingleNode(pixnBundle, L"UX", &pixnUserExperienceNode); + if (S_FALSE == hr) + { + hr = E_NOTFOUND; + } + ExitOnFailure(hr, "Failed to select user experience node."); + + // parse splash screen + hr = XmlGetYesNoAttribute(pixnUserExperienceNode, L"SplashScreen", &pUserExperience->fSplashScreen); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to to get UX/@SplashScreen"); + } + + // parse payloads + hr = PayloadsParseFromXml(&pUserExperience->payloads, NULL, NULL, pixnUserExperienceNode); + ExitOnFailure(hr, "Failed to parse user experience payloads."); + + // make sure we have at least one payload + if (0 == pUserExperience->payloads.cPayloads) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Too few UX payloads."); + } + +LExit: + ReleaseObject(pixnUserExperienceNode); + + return hr; +} + +/******************************************************************* + UserExperienceUninitialize - + +*******************************************************************/ +extern "C" void UserExperienceUninitialize( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + ReleaseStr(pUserExperience->sczTempDirectory); + PayloadsUninitialize(&pUserExperience->payloads); + + // clear struct + memset(pUserExperience, 0, sizeof(BURN_USER_EXPERIENCE)); +} + +/******************************************************************* + UserExperienceLoad - + +*******************************************************************/ +extern "C" HRESULT UserExperienceLoad( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOTSTRAPPER_ENGINE_CONTEXT* pEngineContext, + __in BOOTSTRAPPER_COMMAND* pCommand + ) +{ + HRESULT hr = S_OK; + BOOTSTRAPPER_CREATE_ARGS args = { }; + BOOTSTRAPPER_CREATE_RESULTS results = { }; + + args.cbSize = sizeof(BOOTSTRAPPER_CREATE_ARGS); + args.pCommand = pCommand; + args.pfnBootstrapperEngineProc = EngineForApplicationProc; + args.pvBootstrapperEngineProcContext = pEngineContext; + args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 4, 27, 0); + + results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS); + + // Load BA DLL. + pUserExperience->hUXModule = ::LoadLibraryExW(pUserExperience->payloads.rgPayloads[0].sczLocalFilePath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + ExitOnNullWithLastError(pUserExperience->hUXModule, hr, "Failed to load BA DLL."); + + // Get BootstrapperApplicationCreate entry-point. + PFN_BOOTSTRAPPER_APPLICATION_CREATE pfnCreate = (PFN_BOOTSTRAPPER_APPLICATION_CREATE)::GetProcAddress(pUserExperience->hUXModule, "BootstrapperApplicationCreate"); + ExitOnNullWithLastError(pfnCreate, hr, "Failed to get BootstrapperApplicationCreate entry-point"); + + // Create BA. + hr = pfnCreate(&args, &results); + ExitOnFailure(hr, "Failed to create BA."); + + pUserExperience->pfnBAProc = results.pfnBootstrapperApplicationProc; + pUserExperience->pvBAProcContext = results.pvBootstrapperApplicationProcContext; + pUserExperience->fDisableUnloading = results.fDisableUnloading; + +LExit: + return hr; +} + +/******************************************************************* + UserExperienceUnload - + +*******************************************************************/ +extern "C" HRESULT UserExperienceUnload( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + + if (pUserExperience->hUXModule) + { + // Get BootstrapperApplicationDestroy entry-point and call it if it exists. + PFN_BOOTSTRAPPER_APPLICATION_DESTROY pfnDestroy = (PFN_BOOTSTRAPPER_APPLICATION_DESTROY)::GetProcAddress(pUserExperience->hUXModule, "BootstrapperApplicationDestroy"); + if (pfnDestroy) + { + pfnDestroy(); + } + + // Free BA DLL if it supports it. + if (!pUserExperience->fDisableUnloading && !::FreeLibrary(pUserExperience->hUXModule)) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + TraceError(hr, "Failed to unload BA DLL."); + } + pUserExperience->hUXModule = NULL; + } + +//LExit: + return hr; +} + +extern "C" HRESULT UserExperienceEnsureWorkingFolder( + __in LPCWSTR wzBundleId, + __deref_out_z LPWSTR* psczUserExperienceWorkingFolder + ) +{ + HRESULT hr = S_OK; + LPWSTR sczWorkingFolder = NULL; + + hr = CacheEnsureWorkingFolder(wzBundleId, &sczWorkingFolder); + ExitOnFailure(hr, "Failed to create working folder."); + + hr = StrAllocFormatted(psczUserExperienceWorkingFolder, L"%ls%ls\\", sczWorkingFolder, L".ba"); + ExitOnFailure(hr, "Failed to calculate the bootstrapper application working path."); + + hr = DirEnsureExists(*psczUserExperienceWorkingFolder, NULL); + ExitOnFailure(hr, "Failed create bootstrapper application working folder."); + +LExit: + ReleaseStr(sczWorkingFolder); + + return hr; +} + + +extern "C" HRESULT UserExperienceRemove( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + + // Remove temporary UX directory + if (pUserExperience->sczTempDirectory) + { + hr = DirEnsureDeleteEx(pUserExperience->sczTempDirectory, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE); + TraceError(hr, "Could not delete bootstrapper application folder. Some files will be left in the temp folder."); + } + +//LExit: + return hr; +} + +extern "C" int UserExperienceSendError( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOTSTRAPPER_ERROR_TYPE errorType, + __in_z_opt LPCWSTR wzPackageId, + __in HRESULT hrCode, + __in_z_opt LPCWSTR wzError, + __in DWORD uiFlags, + __in int nRecommendation + ) +{ + int nResult = nRecommendation; + DWORD dwCode = HRESULT_CODE(hrCode); + LPWSTR sczError = NULL; + + // If no error string was provided, try to get the error string from the HRESULT. + if (!wzError) + { + if (SUCCEEDED(StrAllocFromError(&sczError, hrCode, NULL))) + { + wzError = sczError; + } + } + + UserExperienceOnError(pUserExperience, errorType, wzPackageId, dwCode, wzError, uiFlags, 0, NULL, &nResult); // ignore return value. + + ReleaseStr(sczError); + return nResult; +} + +extern "C" void UserExperienceActivateEngine( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + ::EnterCriticalSection(&pUserExperience->csEngineActive); + AssertSz(!pUserExperience->fEngineActive, "Engine should have been deactivated before activating it."); + pUserExperience->fEngineActive = TRUE; + ::LeaveCriticalSection(&pUserExperience->csEngineActive); +} + +extern "C" void UserExperienceDeactivateEngine( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + ::EnterCriticalSection(&pUserExperience->csEngineActive); + AssertSz(pUserExperience->fEngineActive, "Engine should have been active before deactivating it."); + pUserExperience->fEngineActive = FALSE; + ::LeaveCriticalSection(&pUserExperience->csEngineActive); +} + +extern "C" HRESULT UserExperienceEnsureEngineInactive( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + // Make a slight optimization here by ignoring the critical section, because all callers should have needed to enter it for their operation anyway. + HRESULT hr = pUserExperience->fEngineActive ? HRESULT_FROM_WIN32(ERROR_BUSY) : S_OK; + ExitOnRootFailure(hr, "Engine is active, cannot proceed."); + +LExit: + return hr; +} + +extern "C" void UserExperienceExecuteReset( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + pUserExperience->hrApplyError = S_OK; + pUserExperience->hwndApply = NULL; +} + +extern "C" void UserExperienceExecutePhaseComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrResult + ) +{ + if (FAILED(hrResult)) + { + pUserExperience->hrApplyError = hrResult; + } +} + +EXTERN_C BAAPI UserExperienceOnApplyBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in DWORD dwPhaseCount + ) +{ + HRESULT hr = S_OK; + BA_ONAPPLYBEGIN_ARGS args = { }; + BA_ONAPPLYBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.dwPhaseCount = dwPhaseCount; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnApplyBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnApplyComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus, + __in BOOTSTRAPPER_APPLY_RESTART restart, + __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction + ) +{ + HRESULT hr = S_OK; + BA_ONAPPLYCOMPLETE_ARGS args = { }; + BA_ONAPPLYCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + args.restart = restart; + args.recommendation = *pAction; + + results.cbSize = sizeof(results); + results.action = *pAction; + + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYCOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnApplyComplete failed."); + + *pAction = results.action; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnBeginMsiTransactionBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId + ) +{ + HRESULT hr = S_OK; + BA_ONBEGINMSITRANSACTIONBEGIN_ARGS args = { }; + BA_ONBEGINMSITRANSACTIONBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzTransactionId = wzTransactionId; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONBEGINMSITRANSACTIONBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnBeginMsiTransactionBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnBeginMsiTransactionComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONBEGINMSITRANSACTIONCOMPLETE_ARGS args = { }; + BA_ONBEGINMSITRANSACTIONCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzTransactionId = wzTransactionId; + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONBEGINMSITRANSACTIONCOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnBeginMsiTransactionComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCacheAcquireBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in_z LPWSTR* pwzSource, + __in_z LPWSTR* pwzDownloadUrl, + __in_z_opt LPCWSTR wzPayloadContainerId, + __out BOOTSTRAPPER_CACHE_OPERATION* pCacheOperation + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEACQUIREBEGIN_ARGS args = { }; + BA_ONCACHEACQUIREBEGIN_RESULTS results = { }; + *pCacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE; + + args.cbSize = sizeof(args); + args.wzPackageOrContainerId = wzPackageOrContainerId; + args.wzPayloadId = wzPayloadId; + args.wzSource = *pwzSource; + args.wzDownloadUrl = *pwzDownloadUrl; + args.wzPayloadContainerId = wzPayloadContainerId; + args.recommendation = *pCacheOperation; + + results.cbSize = sizeof(results); + results.action = *pCacheOperation; + + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnCacheAcquireBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + else + { + // Verify the BA requested an action that is possible. + if (BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD == results.action && *pwzDownloadUrl && **pwzDownloadUrl || + BOOTSTRAPPER_CACHE_OPERATION_EXTRACT == results.action && wzPayloadContainerId || + BOOTSTRAPPER_CACHE_OPERATION_COPY == results.action || + BOOTSTRAPPER_CACHE_OPERATION_NONE == results.action) + { + *pCacheOperation = results.action; + } + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCacheAcquireComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in HRESULT hrStatus, + __inout BOOL* pfRetry + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEACQUIRECOMPLETE_ARGS args = { }; + BA_ONCACHEACQUIRECOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageOrContainerId = wzPackageOrContainerId; + args.wzPayloadId = wzPayloadId; + args.hrStatus = hrStatus; + args.recommendation = *pfRetry ? BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_RETRY : BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_NONE; + + results.cbSize = sizeof(results); + results.action = args.recommendation; + + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRECOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnCacheAcquireComplete failed."); + + if (FAILED(hrStatus)) + { + *pfRetry = BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_RETRY == results.action; + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCacheAcquireProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in DWORD dwOverallPercentage + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEACQUIREPROGRESS_ARGS args = { }; + BA_ONCACHEACQUIREPROGRESS_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageOrContainerId = wzPackageOrContainerId; + args.wzPayloadId = wzPayloadId; + args.dw64Progress = dw64Progress; + args.dw64Total = dw64Total; + args.dwOverallPercentage = dwOverallPercentage; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREPROGRESS, &args, &results); + ExitOnFailure(hr, "BA OnCacheAcquireProgress failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCacheAcquireResolving( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in_z LPWSTR* rgSearchPaths, + __in DWORD cSearchPaths, + __in BOOL fFoundLocal, + __in DWORD* pdwChosenSearchPath, + __in_z_opt LPWSTR* pwzDownloadUrl, + __in_z_opt LPCWSTR wzPayloadContainerId, + __inout BOOTSTRAPPER_CACHE_RESOLVE_OPERATION* pCacheOperation + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEACQUIRERESOLVING_ARGS args = { }; + BA_ONCACHEACQUIRERESOLVING_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageOrContainerId = wzPackageOrContainerId; + args.wzPayloadId = wzPayloadId; + args.rgSearchPaths = const_cast(rgSearchPaths); + args.cSearchPaths = cSearchPaths; + args.fFoundLocal = fFoundLocal; + args.dwRecommendedSearchPath = *pdwChosenSearchPath; + args.wzDownloadUrl = *pwzDownloadUrl; + args.recommendation = *pCacheOperation; + + results.cbSize = sizeof(results); + results.dwChosenSearchPath = *pdwChosenSearchPath; + results.action = *pCacheOperation; + + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRERESOLVING, &args, &results); + ExitOnFailure(hr, "BA OnCacheAcquireResolving failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + else + { + // Verify the BA requested an action that is possible. + if (BOOTSTRAPPER_CACHE_RESOLVE_DOWNLOAD == results.action && *pwzDownloadUrl && **pwzDownloadUrl || + BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER == results.action && wzPayloadContainerId || + BOOTSTRAPPER_CACHE_RESOLVE_RETRY == results.action || + BOOTSTRAPPER_CACHE_RESOLVE_NONE == results.action) + { + *pCacheOperation = results.action; + } + else if (BOOTSTRAPPER_CACHE_RESOLVE_LOCAL == results.action && results.dwChosenSearchPath < cSearchPaths) + { + *pdwChosenSearchPath = results.dwChosenSearchPath; + *pCacheOperation = results.action; + } + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCacheBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEBEGIN_ARGS args = { }; + BA_ONCACHEBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnCacheBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCacheComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONCACHECOMPLETE_ARGS args = { }; + BA_ONCACHECOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnCacheComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCacheContainerOrPayloadVerifyBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId + ) +{ + HRESULT hr = S_OK; + BA_ONCACHECONTAINERORPAYLOADVERIFYBEGIN_ARGS args = { }; + BA_ONCACHECONTAINERORPAYLOADVERIFYBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageOrContainerId = wzPackageOrContainerId; + args.wzPayloadId = wzPayloadId; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECONTAINERORPAYLOADVERIFYBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnCacheContainerOrPayloadVerifyBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCacheContainerOrPayloadVerifyComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONCACHECONTAINERORPAYLOADVERIFYCOMPLETE_ARGS args = { }; + BA_ONCACHECONTAINERORPAYLOADVERIFYCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageOrContainerId = wzPackageOrContainerId; + args.wzPayloadId = wzPayloadId; + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECONTAINERORPAYLOADVERIFYCOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnCacheContainerOrPayloadVerifyComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCacheContainerOrPayloadVerifyProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in DWORD dwOverallPercentage + ) +{ + HRESULT hr = S_OK; + BA_ONCACHECONTAINERORPAYLOADVERIFYPROGRESS_ARGS args = { }; + BA_ONCACHECONTAINERORPAYLOADVERIFYPROGRESS_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageOrContainerId = wzPackageOrContainerId; + args.wzPayloadId = wzPayloadId; + args.dw64Progress = dw64Progress; + args.dw64Total = dw64Total; + args.dwOverallPercentage = dwOverallPercentage; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECONTAINERORPAYLOADVERIFYPROGRESS, &args, &results); + ExitOnFailure(hr, "BA OnCacheContainerOrPayloadVerifyProgress failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCachePackageBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in DWORD cCachePayloads, + __in DWORD64 dw64PackageCacheSize + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEPACKAGEBEGIN_ARGS args = { }; + BA_ONCACHEPACKAGEBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.cCachePayloads = cCachePayloads; + args.dw64PackageCacheSize = dw64PackageCacheSize; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGEBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnCachePackageBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCachePackageComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in HRESULT hrStatus, + __inout BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION* pAction + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEPACKAGECOMPLETE_ARGS args = { }; + BA_ONCACHEPACKAGECOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.hrStatus = hrStatus; + args.recommendation = *pAction; + + results.cbSize = sizeof(results); + results.action = *pAction; + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGECOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnCachePackageComplete failed."); + + if (FAILED(hrStatus)) + { + *pAction = results.action; + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCachePayloadExtractBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzContainerId, + __in_z_opt LPCWSTR wzPayloadId + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEPAYLOADEXTRACTBEGIN_ARGS args = { }; + BA_ONCACHEPAYLOADEXTRACTBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzContainerId = wzContainerId; + args.wzPayloadId = wzPayloadId; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPAYLOADEXTRACTBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnCachePayloadExtractBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCachePayloadExtractComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEPAYLOADEXTRACTCOMPLETE_ARGS args = { }; + BA_ONCACHEPAYLOADEXTRACTCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzContainerId = wzContainerId; + args.wzPayloadId = wzPayloadId; + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPAYLOADEXTRACTCOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnCachePayloadExtractComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCachePayloadExtractProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in DWORD dwOverallPercentage + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEPAYLOADEXTRACTPROGRESS_ARGS args = { }; + BA_ONCACHEPAYLOADEXTRACTPROGRESS_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzContainerId = wzContainerId; + args.wzPayloadId = wzPayloadId; + args.dw64Progress = dw64Progress; + args.dw64Total = dw64Total; + args.dwOverallPercentage = dwOverallPercentage; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPAYLOADEXTRACTPROGRESS, &args, &results); + ExitOnFailure(hr, "BA OnCachePayloadExtractProgress failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCacheVerifyBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEVERIFYBEGIN_ARGS args = { }; + BA_ONCACHEVERIFYBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageOrContainerId = wzPackageOrContainerId; + args.wzPayloadId = wzPayloadId; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnCacheVerifyBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCacheVerifyComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in HRESULT hrStatus, + __inout BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION* pAction + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEVERIFYCOMPLETE_ARGS args = { }; + BA_ONCACHEVERIFYCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageOrContainerId = wzPackageOrContainerId; + args.wzPayloadId = wzPayloadId; + args.hrStatus = hrStatus; + args.recommendation = *pAction; + + results.cbSize = sizeof(results); + results.action = *pAction; + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYCOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnCacheVerifyComplete failed."); + + if (FAILED(hrStatus)) + { + *pAction = results.action; + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCacheVerifyProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in DWORD dwOverallPercentage, + __in BOOTSTRAPPER_CACHE_VERIFY_STEP verifyStep + ) +{ + HRESULT hr = S_OK; + BA_ONCACHEVERIFYPROGRESS_ARGS args = { }; + BA_ONCACHEVERIFYPROGRESS_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageOrContainerId = wzPackageOrContainerId; + args.wzPayloadId = wzPayloadId; + args.dw64Progress = dw64Progress; + args.dw64Total = dw64Total; + args.dwOverallPercentage = dwOverallPercentage; + args.verifyStep = verifyStep; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYPROGRESS, &args, &results); + ExitOnFailure(hr, "BA OnCacheVerifyProgress failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCommitMsiTransactionBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId + ) +{ + HRESULT hr = S_OK; + BA_ONCOMMITMSITRANSACTIONBEGIN_ARGS args = { }; + BA_ONCOMMITMSITRANSACTIONBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzTransactionId = wzTransactionId; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCOMMITMSITRANSACTIONBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnCommitMsiTransactionBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnCommitMsiTransactionComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONCOMMITMSITRANSACTIONCOMPLETE_ARGS args = { }; + BA_ONCOMMITMSITRANSACTIONCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzTransactionId = wzTransactionId; + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCOMMITMSITRANSACTIONCOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnCommitMsiTransactionComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fCached, + __in BOOL fInstalled, + __in DWORD cPackages + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTBEGIN_ARGS args = { }; + BA_ONDETECTBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.cPackages = cPackages; + args.fInstalled = fInstalled; + args.fCached = fCached; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnDetectBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus, + __in BOOL fEligibleForCleanup + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTCOMPLETE_ARGS args = { }; + BA_ONDETECTCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + args.fEligibleForCleanup = fEligibleForCleanup; + + results.cbSize = sizeof(results); + + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnDetectComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectForwardCompatibleBundle( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzBundleId, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in_z LPCWSTR wzBundleTag, + __in BOOL fPerMachine, + __in VERUTIL_VERSION* pVersion, + __in BOOL fMissingFromCache + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_ARGS args = { }; + BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzBundleId = wzBundleId; + args.relationType = relationType; + args.wzBundleTag = wzBundleTag; + args.fPerMachine = fPerMachine; + args.wzVersion = pVersion->sczVersion; + args.fMissingFromCache = fMissingFromCache; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTFORWARDCOMPATIBLEBUNDLE, &args, &results); + ExitOnFailure(hr, "BA OnDetectForwardCompatibleBundle failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectMsiFeature( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzFeatureId, + __in BOOTSTRAPPER_FEATURE_STATE state + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTMSIFEATURE_ARGS args = { }; + BA_ONDETECTMSIFEATURE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.wzFeatureId = wzFeatureId; + args.state = state; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTMSIFEATURE, &args, &results); + ExitOnFailure(hr, "BA OnDetectMsiFeature failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectPackageBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTPACKAGEBEGIN_ARGS args = { }; + BA_ONDETECTPACKAGEBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGEBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnDetectPackageBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectPackageComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in HRESULT hrStatus, + __in BOOTSTRAPPER_PACKAGE_STATE state, + __in BOOL fCached + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTPACKAGECOMPLETE_ARGS args = { }; + BA_ONDETECTPACKAGECOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.hrStatus = hrStatus; + args.state = state; + args.fCached = fCached; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGECOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnDetectPackageComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectRelatedBundle( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzBundleId, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in_z LPCWSTR wzBundleTag, + __in BOOL fPerMachine, + __in VERUTIL_VERSION* pVersion, + __in BOOTSTRAPPER_RELATED_OPERATION operation, + __in BOOL fMissingFromCache + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTRELATEDBUNDLE_ARGS args = { }; + BA_ONDETECTRELATEDBUNDLE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzBundleId = wzBundleId; + args.relationType = relationType; + args.wzBundleTag = wzBundleTag; + args.fPerMachine = fPerMachine; + args.wzVersion = pVersion->sczVersion; + args.operation = operation; + args.fMissingFromCache = fMissingFromCache; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLE, &args, &results); + ExitOnFailure(hr, "BA OnDetectRelatedBundle failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectRelatedMsiPackage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzUpgradeCode, + __in_z LPCWSTR wzProductCode, + __in BOOL fPerMachine, + __in VERUTIL_VERSION* pVersion, + __in BOOTSTRAPPER_RELATED_OPERATION operation + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTRELATEDMSIPACKAGE_ARGS args = { }; + BA_ONDETECTRELATEDMSIPACKAGE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.wzUpgradeCode = wzUpgradeCode; + args.wzProductCode = wzProductCode; + args.fPerMachine = fPerMachine; + args.wzVersion = pVersion->sczVersion; + args.operation = operation; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDMSIPACKAGE, &args, &results); + ExitOnFailure(hr, "BA OnDetectRelatedMsiPackage failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectPatchTarget( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzProductCode, + __in BOOTSTRAPPER_PACKAGE_STATE patchState + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTPATCHTARGET_ARGS args = { }; + BA_ONDETECTPATCHTARGET_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.wzProductCode = wzProductCode; + args.patchState = patchState; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPATCHTARGET, &args, &results); + ExitOnFailure(hr, "BA OnDetectPatchTarget failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectUpdate( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzUpdateLocation, + __in DWORD64 dw64Size, + __in VERUTIL_VERSION* pVersion, + __in_z_opt LPCWSTR wzTitle, + __in_z_opt LPCWSTR wzSummary, + __in_z_opt LPCWSTR wzContentType, + __in_z_opt LPCWSTR wzContent, + __inout BOOL* pfStopProcessingUpdates + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTUPDATE_ARGS args = { }; + BA_ONDETECTUPDATE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzUpdateLocation = wzUpdateLocation; + args.dw64Size = dw64Size; + args.wzVersion = pVersion->sczVersion; + args.wzTitle = wzTitle; + args.wzSummary = wzSummary; + args.wzContentType = wzContentType; + args.wzContent = wzContent; + + results.cbSize = sizeof(results); + results.fStopProcessingUpdates = *pfStopProcessingUpdates; + + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATE, &args, &results); + ExitOnFailure(hr, "BA OnDetectUpdate failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + *pfStopProcessingUpdates = results.fStopProcessingUpdates; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectUpdateBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzUpdateLocation, + __inout BOOL* pfSkip + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTUPDATEBEGIN_ARGS args = { }; + BA_ONDETECTUPDATEBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzUpdateLocation = wzUpdateLocation; + + results.cbSize = sizeof(results); + results.fSkip = *pfSkip; + + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATEBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnDetectUpdateBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + *pfSkip = results.fSkip; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnDetectUpdateComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus, + __inout BOOL* pfIgnoreError + ) +{ + HRESULT hr = S_OK; + BA_ONDETECTUPDATECOMPLETE_ARGS args = { }; + BA_ONDETECTUPDATECOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + results.fIgnoreError = *pfIgnoreError; + + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATECOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnDetectUpdateComplete failed."); + + if (FAILED(hrStatus)) + { + *pfIgnoreError = results.fIgnoreError; + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnElevateBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + BA_ONELEVATEBEGIN_ARGS args = { }; + BA_ONELEVATEBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATEBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnElevateBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnElevateComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONELEVATECOMPLETE_ARGS args = { }; + BA_ONELEVATECOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATECOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnElevateComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnError( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOTSTRAPPER_ERROR_TYPE errorType, + __in_z_opt LPCWSTR wzPackageId, + __in DWORD dwCode, + __in_z_opt LPCWSTR wzError, + __in DWORD dwUIHint, + __in DWORD cData, + __in_ecount_z_opt(cData) LPCWSTR* rgwzData, + __inout int* pnResult + ) +{ + HRESULT hr = S_OK; + BA_ONERROR_ARGS args = { }; + BA_ONERROR_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.errorType = errorType; + args.wzPackageId = wzPackageId; + args.dwCode = dwCode; + args.wzError = wzError; + args.dwUIHint = dwUIHint; + args.cData = cData; + args.rgwzData = rgwzData; + args.nRecommendation = *pnResult; + + results.cbSize = sizeof(results); + results.nResult = *pnResult; + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONERROR, &args, &results); + ExitOnFailure(hr, "BA OnError failed."); + + *pnResult = results.nResult; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnExecuteBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in DWORD cExecutingPackages + ) +{ + HRESULT hr = S_OK; + BA_ONEXECUTEBEGIN_ARGS args = { }; + BA_ONEXECUTEBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.cExecutingPackages = cExecutingPackages; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnExecuteBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnExecuteComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONEXECUTECOMPLETE_ARGS args = { }; + BA_ONEXECUTECOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTECOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnExecuteComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnExecuteFilesInUse( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in DWORD cFiles, + __in_ecount_z_opt(cFiles) LPCWSTR* rgwzFiles, + __inout int* pnResult + ) +{ + HRESULT hr = S_OK; + BA_ONEXECUTEFILESINUSE_ARGS args = { }; + BA_ONEXECUTEFILESINUSE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.cFiles = cFiles; + args.rgwzFiles = rgwzFiles; + args.nRecommendation = *pnResult; + + results.cbSize = sizeof(results); + results.nResult = *pnResult; + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEFILESINUSE, &args, &results); + ExitOnFailure(hr, "BA OnExecuteFilesInUse failed."); + + *pnResult = results.nResult; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnExecuteMsiMessage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in INSTALLMESSAGE messageType, + __in DWORD dwUIHint, + __in_z LPCWSTR wzMessage, + __in DWORD cData, + __in_ecount_z_opt(cData) LPCWSTR* rgwzData, + __inout int* pnResult + ) +{ + HRESULT hr = S_OK; + BA_ONEXECUTEMSIMESSAGE_ARGS args = { }; + BA_ONEXECUTEMSIMESSAGE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.messageType = messageType; + args.dwUIHint = dwUIHint; + args.wzMessage = wzMessage; + args.cData = cData; + args.rgwzData = rgwzData; + args.nRecommendation = *pnResult; + + results.cbSize = sizeof(results); + results.nResult = *pnResult; + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEMSIMESSAGE, &args, &results); + ExitOnFailure(hr, "BA OnExecuteMsiMessage failed."); + + *pnResult = results.nResult; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnExecutePackageBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in BOOL fExecute, + __in BOOTSTRAPPER_ACTION_STATE action, + __in INSTALLUILEVEL uiLevel, + __in BOOL fDisableExternalUiHandler + ) +{ + HRESULT hr = S_OK; + BA_ONEXECUTEPACKAGEBEGIN_ARGS args = { }; + BA_ONEXECUTEPACKAGEBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.fExecute = fExecute; + args.action = action; + args.uiLevel = uiLevel; + args.fDisableExternalUiHandler = fDisableExternalUiHandler; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGEBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnExecutePackageBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnExecutePackageComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in HRESULT hrStatus, + __in BOOTSTRAPPER_APPLY_RESTART restart, + __inout BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION* pAction + ) +{ + HRESULT hr = S_OK; + BA_ONEXECUTEPACKAGECOMPLETE_ARGS args = { }; + BA_ONEXECUTEPACKAGECOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.hrStatus = hrStatus; + args.restart = restart; + args.recommendation = *pAction; + + results.cbSize = sizeof(results); + results.action = *pAction; + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGECOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnExecutePackageComplete failed."); + + *pAction = results.action; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnExecutePatchTarget( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzTargetProductCode + ) +{ + HRESULT hr = S_OK; + BA_ONEXECUTEPATCHTARGET_ARGS args = { }; + BA_ONEXECUTEPATCHTARGET_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.wzTargetProductCode = wzTargetProductCode; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPATCHTARGET, &args, &results); + ExitOnFailure(hr, "BA OnExecutePatchTarget failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnExecuteProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in DWORD dwProgressPercentage, + __in DWORD dwOverallPercentage, + __out int* pnResult + ) +{ + HRESULT hr = S_OK; + BA_ONEXECUTEPROGRESS_ARGS args = { }; + BA_ONEXECUTEPROGRESS_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.dwProgressPercentage = dwProgressPercentage; + args.dwOverallPercentage = dwOverallPercentage; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROGRESS, &args, &results); + ExitOnFailure(hr, "BA OnExecuteProgress failed."); + +LExit: + if (FAILED(hr)) + { + *pnResult = IDERROR; + } + else if (results.fCancel) + { + *pnResult = IDCANCEL; + } + else + { + *pnResult = IDNOACTION; + } + return hr; +} + +EXTERN_C BAAPI UserExperienceOnLaunchApprovedExeBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + BA_ONLAUNCHAPPROVEDEXEBEGIN_ARGS args = { }; + BA_ONLAUNCHAPPROVEDEXEBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXEBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnLaunchApprovedExeBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnLaunchApprovedExeComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus, + __in DWORD dwProcessId + ) +{ + HRESULT hr = S_OK; + BA_ONLAUNCHAPPROVEDEXECOMPLETE_ARGS args = { }; + BA_ONLAUNCHAPPROVEDEXECOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + args.dwProcessId = dwProcessId; + + results.cbSize = sizeof(results); + + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXECOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnLaunchApprovedExeComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPauseAUBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + BA_ONPAUSEAUTOMATICUPDATESBEGIN_ARGS args = { }; + BA_ONPAUSEAUTOMATICUPDATESBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPAUSEAUTOMATICUPDATESBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnPauseAUBegin failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPauseAUComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONPAUSEAUTOMATICUPDATESCOMPLETE_ARGS args = { }; + BA_ONPAUSEAUTOMATICUPDATESCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPAUSEAUTOMATICUPDATESCOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnPauseAUComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPlanBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in DWORD cPackages + ) +{ + HRESULT hr = S_OK; + BA_ONPLANBEGIN_ARGS args = { }; + BA_ONPLANBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.cPackages = cPackages; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnPlanBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPlanMsiFeature( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzFeatureId, + __inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState + ) +{ + HRESULT hr = S_OK; + BA_ONPLANMSIFEATURE_ARGS args = { }; + BA_ONPLANMSIFEATURE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.wzFeatureId = wzFeatureId; + args.recommendedState = *pRequestedState; + + results.cbSize = sizeof(results); + results.requestedState = *pRequestedState; + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIFEATURE, &args, &results); + ExitOnFailure(hr, "BA OnPlanMsiFeature failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + *pRequestedState = results.requestedState; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPlanComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONPLANCOMPLETE_ARGS args = { }; + BA_ONPLANCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnPlanComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPlanForwardCompatibleBundle( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzBundleId, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in_z LPCWSTR wzBundleTag, + __in BOOL fPerMachine, + __in VERUTIL_VERSION* pVersion, + __inout BOOL* pfIgnoreBundle + ) +{ + HRESULT hr = S_OK; + BA_ONPLANFORWARDCOMPATIBLEBUNDLE_ARGS args = { }; + BA_ONPLANFORWARDCOMPATIBLEBUNDLE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzBundleId = wzBundleId; + args.relationType = relationType; + args.wzBundleTag = wzBundleTag; + args.fPerMachine = fPerMachine; + args.wzVersion = pVersion->sczVersion; + args.fRecommendedIgnoreBundle = *pfIgnoreBundle; + + results.cbSize = sizeof(results); + results.fIgnoreBundle = *pfIgnoreBundle; + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANFORWARDCOMPATIBLEBUNDLE, &args, &results); + ExitOnFailure(hr, "BA OnPlanForwardCompatibleBundle failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + *pfIgnoreBundle = results.fIgnoreBundle; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPlanMsiPackage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in BOOL fExecute, + __in BOOTSTRAPPER_ACTION_STATE action, + __inout BURN_MSI_PROPERTY* pActionMsiProperty, + __inout INSTALLUILEVEL* pUiLevel, + __inout BOOL* pfDisableExternalUiHandler + ) +{ + HRESULT hr = S_OK; + BA_ONPLANMSIPACKAGE_ARGS args = { }; + BA_ONPLANMSIPACKAGE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.fExecute = fExecute; + args.action = action; + + results.cbSize = sizeof(results); + results.actionMsiProperty = *pActionMsiProperty; + results.uiLevel = *pUiLevel; + results.fDisableExternalUiHandler = *pfDisableExternalUiHandler; + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIPACKAGE, &args, &results); + ExitOnFailure(hr, "BA OnPlanMsiPackage failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + *pActionMsiProperty = results.actionMsiProperty; + *pUiLevel = results.uiLevel; + *pfDisableExternalUiHandler = results.fDisableExternalUiHandler; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPlannedPackage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in BOOTSTRAPPER_ACTION_STATE execute, + __in BOOTSTRAPPER_ACTION_STATE rollback, + __in BOOL fPlannedCache, + __in BOOL fPlannedUncache + ) +{ + HRESULT hr = S_OK; + BA_ONPLANNEDPACKAGE_ARGS args = { }; + BA_ONPLANNEDPACKAGE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.execute = execute; + args.rollback = rollback; + args.fPlannedCache = fPlannedCache; + args.fPlannedUncache = fPlannedUncache; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDPACKAGE, &args, &results); + ExitOnFailure(hr, "BA OnPlannedPackage failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPlanPackageBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in BOOTSTRAPPER_PACKAGE_STATE state, + __in BOOL fCached, + __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState, + __inout BOOTSTRAPPER_CACHE_TYPE* pRequestedCacheType + ) +{ + HRESULT hr = S_OK; + BA_ONPLANPACKAGEBEGIN_ARGS args = { }; + BA_ONPLANPACKAGEBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.state = state; + args.fCached = fCached; + args.installCondition = installCondition; + args.recommendedState = *pRequestedState; + args.recommendedCacheType = *pRequestedCacheType; + + results.cbSize = sizeof(results); + results.requestedState = *pRequestedState; + results.requestedCacheType = *pRequestedCacheType; + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGEBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnPlanPackageBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + *pRequestedState = results.requestedState; + *pRequestedCacheType = results.requestedCacheType; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPlanPackageComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in HRESULT hrStatus, + __in BOOTSTRAPPER_REQUEST_STATE requested + ) +{ + HRESULT hr = S_OK; + BA_ONPLANPACKAGECOMPLETE_ARGS args = { }; + BA_ONPLANPACKAGECOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.hrStatus = hrStatus; + args.requested = requested; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGECOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnPlanPackageComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPlanRelatedBundle( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzBundleId, + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState + ) +{ + HRESULT hr = S_OK; + BA_ONPLANRELATEDBUNDLE_ARGS args = { }; + BA_ONPLANRELATEDBUNDLE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzBundleId = wzBundleId; + args.recommendedState = *pRequestedState; + + results.cbSize = sizeof(results); + results.requestedState = *pRequestedState; + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLE, &args, &results); + ExitOnFailure(hr, "BA OnPlanRelatedBundle failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + *pRequestedState = results.requestedState; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnPlanPatchTarget( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzProductCode, + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState + ) +{ + HRESULT hr = S_OK; + BA_ONPLANPATCHTARGET_ARGS args = { }; + BA_ONPLANPATCHTARGET_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzPackageId = wzPackageId; + args.wzProductCode = wzProductCode; + args.recommendedState = *pRequestedState; + + results.cbSize = sizeof(results); + results.requestedState = *pRequestedState; + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPATCHTARGET, &args, &results); + ExitOnFailure(hr, "BA OnPlanPatchTarget failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + *pRequestedState = results.requestedState; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fRollback, + __in DWORD dwProgressPercentage, + __in DWORD dwOverallPercentage + ) +{ + HRESULT hr = S_OK; + BA_ONPROGRESS_ARGS args = { }; + BA_ONPROGRESS_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.dwProgressPercentage = dwProgressPercentage; + args.dwOverallPercentage = dwOverallPercentage; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPROGRESS, &args, &results); + hr = FilterExecuteResult(pUserExperience, hr, fRollback, results.fCancel, L"OnProgress"); + + return hr; +} + +EXTERN_C BAAPI UserExperienceOnRegisterBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + BA_ONREGISTERBEGIN_ARGS args = { }; + BA_ONREGISTERBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnRegisterBegin failed."); + + if (results.fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnRegisterComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONREGISTERCOMPLETE_ARGS args = { }; + BA_ONREGISTERCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERCOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnRegisterComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnRollbackMsiTransactionBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId + ) +{ + HRESULT hr = S_OK; + BA_ONROLLBACKMSITRANSACTIONBEGIN_ARGS args = { }; + BA_ONROLLBACKMSITRANSACTIONBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzTransactionId = wzTransactionId; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONROLLBACKMSITRANSACTIONBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnRollbackMsiTransactionBegin failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnRollbackMsiTransactionComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONROLLBACKMSITRANSACTIONCOMPLETE_ARGS args = { }; + BA_ONROLLBACKMSITRANSACTIONCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.wzTransactionId = wzTransactionId; + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONROLLBACKMSITRANSACTIONCOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnRollbackMsiTransactionComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnShutdown( + __in BURN_USER_EXPERIENCE* pUserExperience, + __inout BOOTSTRAPPER_SHUTDOWN_ACTION* pAction + ) +{ + HRESULT hr = S_OK; + BA_ONSHUTDOWN_ARGS args = { }; + BA_ONSHUTDOWN_RESULTS results = { }; + + args.cbSize = sizeof(args); + + results.cbSize = sizeof(results); + results.action = *pAction; + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSHUTDOWN, &args, &results); + ExitOnFailure(hr, "BA OnShutdown failed."); + + *pAction = results.action; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnStartup( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + BA_ONSTARTUP_ARGS args = { }; + BA_ONSTARTUP_RESULTS results = { }; + + args.cbSize = sizeof(args); + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSTARTUP, &args, &results); + ExitOnFailure(hr, "BA OnStartup failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnSystemRestorePointBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ) +{ + HRESULT hr = S_OK; + BA_ONSYSTEMRESTOREPOINTBEGIN_ARGS args = { }; + BA_ONSYSTEMRESTOREPOINTBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMRESTOREPOINTBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnSystemRestorePointBegin failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnSystemRestorePointComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONSYSTEMRESTOREPOINTCOMPLETE_ARGS args = { }; + BA_ONSYSTEMRESTOREPOINTCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMRESTOREPOINTCOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnSystemRestorePointComplete failed."); + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnSystemShutdown( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in DWORD dwEndSession, + __inout BOOL* pfCancel + ) +{ + HRESULT hr = S_OK; + BA_ONSYSTEMSHUTDOWN_ARGS args = { }; + BA_ONSYSTEMSHUTDOWN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.dwEndSession = dwEndSession; + + results.cbSize = sizeof(results); + results.fCancel = *pfCancel; + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMSHUTDOWN, &args, &results); + ExitOnFailure(hr, "BA OnSystemShutdown failed."); + + *pfCancel = results.fCancel; + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnUnregisterBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __inout BOOL* pfKeepRegistration + ) +{ + HRESULT hr = S_OK; + BA_ONUNREGISTERBEGIN_ARGS args = { }; + BA_ONUNREGISTERBEGIN_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.fKeepRegistration = *pfKeepRegistration; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERBEGIN, &args, &results); + ExitOnFailure(hr, "BA OnUnregisterBegin failed."); + + if (!args.fKeepRegistration && results.fForceKeepRegistration) + { + *pfKeepRegistration = TRUE; + } + +LExit: + return hr; +} + +EXTERN_C BAAPI UserExperienceOnUnregisterComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONUNREGISTERCOMPLETE_ARGS args = { }; + BA_ONUNREGISTERCOMPLETE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrStatus = hrStatus; + + results.cbSize = sizeof(results); + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERCOMPLETE, &args, &results); + ExitOnFailure(hr, "BA OnUnregisterComplete failed."); + +LExit: + return hr; +} + +extern "C" int UserExperienceCheckExecuteResult( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fRollback, + __in DWORD dwAllowedResults, + __in int nResult + ) +{ + // Do not allow canceling while rolling back. + if (fRollback && (IDCANCEL == nResult || IDABORT == nResult)) + { + nResult = IDNOACTION; + } + else if (FAILED(pUserExperience->hrApplyError) && !fRollback) // if we failed cancel except not during rollback. + { + nResult = IDCANCEL; + } + + nResult = FilterResult(dwAllowedResults, nResult); + return nResult; +} + +extern "C" HRESULT UserExperienceInterpretExecuteResult( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fRollback, + __in DWORD dwAllowedResults, + __in int nResult + ) +{ + HRESULT hr = S_OK; + + // If we failed return that error unless this is rollback which should roll on. + if (FAILED(pUserExperience->hrApplyError) && !fRollback) + { + hr = pUserExperience->hrApplyError; + } + else + { + int nCheckedResult = UserExperienceCheckExecuteResult(pUserExperience, fRollback, dwAllowedResults, nResult); + hr = IDOK == nCheckedResult || IDNOACTION == nCheckedResult ? S_OK : IDCANCEL == nCheckedResult || IDABORT == nCheckedResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); + } + + return hr; +} + + +// internal functions + +static int FilterResult( + __in DWORD dwAllowedResults, + __in int nResult + ) +{ + DWORD dwFilteredAllowedResults = dwAllowedResults & MB_TYPEMASK; + if (IDNOACTION == nResult || IDERROR == nResult) // do nothing and errors pass through. + { + } + else + { + switch (dwFilteredAllowedResults) + { + case MB_OK: + nResult = IDOK; + break; + + case MB_OKCANCEL: + if (IDOK == nResult || IDYES == nResult) + { + nResult = IDOK; + } + else if (IDCANCEL == nResult || IDABORT == nResult || IDNO == nResult) + { + nResult = IDCANCEL; + } + else + { + nResult = IDNOACTION; + } + break; + + case MB_ABORTRETRYIGNORE: + if (IDCANCEL == nResult || IDABORT == nResult) + { + nResult = IDABORT; + } + else if (IDRETRY == nResult || IDTRYAGAIN == nResult) + { + nResult = IDRETRY; + } + else if (IDIGNORE == nResult) + { + nResult = IDIGNORE; + } + else + { + nResult = IDNOACTION; + } + break; + + case MB_YESNO: + if (IDOK == nResult || IDYES == nResult) + { + nResult = IDYES; + } + else if (IDCANCEL == nResult || IDABORT == nResult || IDNO == nResult) + { + nResult = IDNO; + } + else + { + nResult = IDNOACTION; + } + break; + + case MB_YESNOCANCEL: + if (IDOK == nResult || IDYES == nResult) + { + nResult = IDYES; + } + else if (IDNO == nResult) + { + nResult = IDNO; + } + else if (IDCANCEL == nResult || IDABORT == nResult) + { + nResult = IDCANCEL; + } + else + { + nResult = IDNOACTION; + } + break; + + case MB_RETRYCANCEL: + if (IDRETRY == nResult || IDTRYAGAIN == nResult) + { + nResult = IDRETRY; + } + else if (IDCANCEL == nResult || IDABORT == nResult) + { + nResult = IDABORT; + } + else + { + nResult = IDNOACTION; + } + break; + + case MB_CANCELTRYCONTINUE: + if (IDCANCEL == nResult || IDABORT == nResult) + { + nResult = IDABORT; + } + else if (IDRETRY == nResult || IDTRYAGAIN == nResult) + { + nResult = IDRETRY; + } + else if (IDCONTINUE == nResult || IDIGNORE == nResult) + { + nResult = IDCONTINUE; + } + else + { + nResult = IDNOACTION; + } + break; + + case WIU_MB_OKIGNORECANCELRETRY: // custom Windows Installer utility return code. + if (IDOK == nResult || IDYES == nResult) + { + nResult = IDOK; + } + else if (IDCONTINUE == nResult || IDIGNORE == nResult) + { + nResult = IDIGNORE; + } + else if (IDCANCEL == nResult || IDABORT == nResult) + { + nResult = IDCANCEL; + } + else if (IDRETRY == nResult || IDTRYAGAIN == nResult || IDNO == nResult) + { + nResult = IDRETRY; + } + else + { + nResult = IDNOACTION; + } + break; + + case MB_RETRYTRYAGAIN: // custom return code. + if (IDRETRY != nResult && IDTRYAGAIN != nResult) + { + nResult = IDNOACTION; + } + break; + + default: + AssertSz(FALSE, "Unknown allowed results."); + break; + } + } + + return nResult; +} + +// This filters the BA's responses to events during apply. +// If an apply thread failed, then return its error so this thread will bail out. +// During rollback, the BA can't cancel. +static HRESULT FilterExecuteResult( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus, + __in BOOL fRollback, + __in BOOL fCancel, + __in LPCWSTR sczEventName + ) +{ + HRESULT hr = hrStatus; + HRESULT hrApplyError = pUserExperience->hrApplyError; // make sure to use the same value for the whole method, since it can be changed in other threads. + + // If we failed return that error unless this is rollback which should roll on. + if (FAILED(hrApplyError) && !fRollback) + { + hr = hrApplyError; + } + else if (fRollback) + { + if (fCancel) + { + LogId(REPORT_STANDARD, MSG_APPLY_CANCEL_IGNORED_DURING_ROLLBACK, sczEventName); + } + // TODO: since cancel isn't allowed, should the BA's HRESULT be ignored as well? + // In the previous code, they could still alter rollback by returning IDERROR. + } + else + { + ExitOnFailure(hr, "BA %ls failed.", sczEventName); + + if (fCancel) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + } + +LExit: + return hr; +} + +static HRESULT SendBAMessage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOTSTRAPPER_APPLICATION_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + + if (!pUserExperience->hUXModule) + { + ExitFunction(); + } + + hr = pUserExperience->pfnBAProc(message, pvArgs, pvResults, pUserExperience->pvBAProcContext); + if (hr == E_NOTIMPL) + { + hr = S_OK; + } + +LExit: + return hr; +} + +static HRESULT SendBAMessageFromInactiveEngine( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOTSTRAPPER_APPLICATION_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults + ) +{ + HRESULT hr = S_OK; + + if (!pUserExperience->hUXModule) + { + ExitFunction(); + } + + UserExperienceDeactivateEngine(pUserExperience); + + hr = SendBAMessage(pUserExperience, message, pvArgs, pvResults); + + UserExperienceActivateEngine(pUserExperience); + +LExit: + return hr; +} diff --git a/src/burn/engine/userexperience.h b/src/burn/engine/userexperience.h new file mode 100644 index 00000000..f2453dca --- /dev/null +++ b/src/burn/engine/userexperience.h @@ -0,0 +1,545 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#define BAAPI HRESULT __stdcall + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + +const DWORD MB_RETRYTRYAGAIN = 0xF; + + +// structs + +typedef struct _BOOTSTRAPPER_ENGINE_CONTEXT BOOTSTRAPPER_ENGINE_CONTEXT; + +typedef struct _BURN_USER_EXPERIENCE +{ + BOOL fSplashScreen; + BURN_PAYLOADS payloads; + + HMODULE hUXModule; + PFN_BOOTSTRAPPER_APPLICATION_PROC pfnBAProc; + LPVOID pvBAProcContext; + BOOL fDisableUnloading; + LPWSTR sczTempDirectory; + + CRITICAL_SECTION csEngineActive; // Changing the engine active state in the user experience must be + // syncronized through this critical section. + // Note: The engine must never do a UX callback while in this critical section. + + BOOL fEngineActive; // Indicates that the engine is currently active with one of the execution + // steps (detect, plan, apply), and cannot accept requests from the UX. + // This flag should be cleared by the engine prior to UX callbacks that + // allows altering of the engine state. + + HRESULT hrApplyError; // Tracks is an error occurs during apply that requires the cache or + // execute threads to bail. + + HWND hwndApply; // The window handle provided at the beginning of Apply(). Only valid + // during apply. + + HWND hwndDetect; // The window handle provided at the beginning of Detect(). Only valid + // during Detect. + + DWORD dwExitCode; // Exit code returned by the user experience for the engine overall. +} BURN_USER_EXPERIENCE; + +// functions + +HRESULT UserExperienceParseFromXml( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in IXMLDOMNode* pixnBundle + ); +void UserExperienceUninitialize( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +HRESULT UserExperienceLoad( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOTSTRAPPER_ENGINE_CONTEXT* pEngineContext, + __in BOOTSTRAPPER_COMMAND* pCommand + ); +HRESULT UserExperienceUnload( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +HRESULT UserExperienceEnsureWorkingFolder( + __in LPCWSTR wzBundleId, + __deref_out_z LPWSTR* psczUserExperienceWorkingFolder + ); +HRESULT UserExperienceRemove( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +int UserExperienceSendError( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOTSTRAPPER_ERROR_TYPE errorType, + __in_z_opt LPCWSTR wzPackageId, + __in HRESULT hrCode, + __in_z_opt LPCWSTR wzError, + __in DWORD uiFlags, + __in int nRecommendation + ); +void UserExperienceActivateEngine( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +void UserExperienceDeactivateEngine( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +/******************************************************************** + UserExperienceEnsureEngineInactive - Verifies the engine is inactive. + The caller MUST enter the csActive critical section before calling. + +*********************************************************************/ +HRESULT UserExperienceEnsureEngineInactive( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +void UserExperienceExecuteReset( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +void UserExperienceExecutePhaseComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrResult + ); +BAAPI UserExperienceOnApplyBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in DWORD dwPhaseCount + ); +BAAPI UserExperienceOnApplyComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus, + __in BOOTSTRAPPER_APPLY_RESTART restart, + __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction + ); +BAAPI UserExperienceOnBeginMsiTransactionBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId + ); +BAAPI UserExperienceOnBeginMsiTransactionComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId, + __in HRESULT hrStatus + ); +BAAPI UserExperienceOnCacheAcquireBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in_z LPWSTR* pwzSource, + __in_z LPWSTR* pwzDownloadUrl, + __in_z_opt LPCWSTR wzPayloadContainerId, + __out BOOTSTRAPPER_CACHE_OPERATION* pCacheOperation + ); +BAAPI UserExperienceOnCacheAcquireComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in HRESULT hrStatus, + __inout BOOL* pfRetry + ); +BAAPI UserExperienceOnCacheAcquireProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in DWORD dwOverallPercentage + ); +BAAPI UserExperienceOnCacheAcquireResolving( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in_z LPWSTR* rgSearchPaths, + __in DWORD cSearchPaths, + __in BOOL fFoundLocal, + __in DWORD* pdwChosenSearchPath, + __in_z_opt LPWSTR* pwzDownloadUrl, + __in_z_opt LPCWSTR wzPayloadContainerId, + __inout BOOTSTRAPPER_CACHE_RESOLVE_OPERATION* pCacheOperation + ); +BAAPI UserExperienceOnCacheBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +BAAPI UserExperienceOnCacheComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ); +BAAPI UserExperienceOnCacheContainerOrPayloadVerifyBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId + ); +BAAPI UserExperienceOnCacheContainerOrPayloadVerifyComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in HRESULT hrStatus + ); +BAAPI UserExperienceOnCacheContainerOrPayloadVerifyProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in DWORD dwOverallPercentage + ); +BAAPI UserExperienceOnCachePackageBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in DWORD cCachePayloads, + __in DWORD64 dw64PackageCacheSize + ); +BAAPI UserExperienceOnCachePackageComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in HRESULT hrStatus, + __inout BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION* pAction + ); +BAAPI UserExperienceOnCachePayloadExtractBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzContainerId, + __in_z_opt LPCWSTR wzPayloadId + ); +BAAPI UserExperienceOnCachePayloadExtractComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in HRESULT hrStatus + ); +BAAPI UserExperienceOnCachePayloadExtractProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in DWORD dwOverallPercentage + ); +BAAPI UserExperienceOnCacheVerifyBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId + ); +BAAPI UserExperienceOnCacheVerifyComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in HRESULT hrStatus, + __inout BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION* pAction + ); +BAAPI UserExperienceOnCacheVerifyProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in DWORD dwOverallPercentage, + __in BOOTSTRAPPER_CACHE_VERIFY_STEP verifyStep + ); +BAAPI UserExperienceOnCommitMsiTransactionBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId + ); +BAAPI UserExperienceOnCommitMsiTransactionComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId, + __in HRESULT hrStatus + ); +BAAPI UserExperienceOnDetectBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fCached, + __in BOOL fInstalled, + __in DWORD cPackages + ); +BAAPI UserExperienceOnDetectComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus, + __in BOOL fEligibleForCleanup + ); +BAAPI UserExperienceOnDetectForwardCompatibleBundle( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzBundleId, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in_z LPCWSTR wzBundleTag, + __in BOOL fPerMachine, + __in VERUTIL_VERSION* pVersion, + __in BOOL fMissingFromCache + ); +BAAPI UserExperienceOnDetectMsiFeature( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzFeatureId, + __in BOOTSTRAPPER_FEATURE_STATE state + ); +BAAPI UserExperienceOnDetectPackageBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId + ); +BAAPI UserExperienceOnDetectPackageComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in HRESULT hrStatus, + __in BOOTSTRAPPER_PACKAGE_STATE state, + __in BOOL fCached + ); +BAAPI UserExperienceOnDetectRelatedBundle( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzBundleId, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in_z LPCWSTR wzBundleTag, + __in BOOL fPerMachine, + __in VERUTIL_VERSION* pVersion, + __in BOOTSTRAPPER_RELATED_OPERATION operation, + __in BOOL fMissingFromCache + ); +BAAPI UserExperienceOnDetectRelatedMsiPackage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzUpgradeCode, + __in_z LPCWSTR wzProductCode, + __in BOOL fPerMachine, + __in VERUTIL_VERSION* pVersion, + __in BOOTSTRAPPER_RELATED_OPERATION operation + ); +BAAPI UserExperienceOnDetectPatchTarget( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzProductCode, + __in BOOTSTRAPPER_PACKAGE_STATE patchState + ); +BAAPI UserExperienceOnDetectUpdate( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z_opt LPCWSTR wzUpdateLocation, + __in DWORD64 dw64Size, + __in VERUTIL_VERSION* pVersion, + __in_z_opt LPCWSTR wzTitle, + __in_z_opt LPCWSTR wzSummary, + __in_z_opt LPCWSTR wzContentType, + __in_z_opt LPCWSTR wzContent, + __inout BOOL* pfStopProcessingUpdates + ); +BAAPI UserExperienceOnDetectUpdateBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzUpdateLocation, + __inout BOOL* pfSkip + ); +BAAPI UserExperienceOnDetectUpdateComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus, + __inout BOOL* pfIgnoreError + ); +BAAPI UserExperienceOnElevateBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +BAAPI UserExperienceOnElevateComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ); +BAAPI UserExperienceOnError( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOTSTRAPPER_ERROR_TYPE errorType, + __in_z_opt LPCWSTR wzPackageId, + __in DWORD dwCode, + __in_z_opt LPCWSTR wzError, + __in DWORD dwUIHint, + __in DWORD cData, + __in_ecount_z_opt(cData) LPCWSTR* rgwzData, + __inout int* pnResult + ); +BAAPI UserExperienceOnExecuteBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in DWORD cExecutingPackages + ); +BAAPI UserExperienceOnExecuteComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ); +BAAPI UserExperienceOnExecuteFilesInUse( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in DWORD cFiles, + __in_ecount_z_opt(cFiles) LPCWSTR* rgwzFiles, + __inout int* pnResult + ); +BAAPI UserExperienceOnExecuteMsiMessage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in INSTALLMESSAGE messageType, + __in DWORD dwUIHint, + __in_z LPCWSTR wzMessage, + __in DWORD cData, + __in_ecount_z_opt(cData) LPCWSTR* rgwzData, + __inout int* pnResult + ); +BAAPI UserExperienceOnExecutePackageBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in BOOL fExecute, + __in BOOTSTRAPPER_ACTION_STATE action, + __in INSTALLUILEVEL uiLevel, + __in BOOL fDisableExternalUiHandler + ); +BAAPI UserExperienceOnExecutePackageComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in HRESULT hrStatus, + __in BOOTSTRAPPER_APPLY_RESTART restart, + __inout BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION* pAction + ); +BAAPI UserExperienceOnExecutePatchTarget( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzTargetProductCode + ); +BAAPI UserExperienceOnExecuteProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in DWORD dwProgressPercentage, + __in DWORD dwOverallPercentage, + __out int* pnResult + ); +BAAPI UserExperienceOnLaunchApprovedExeBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +BAAPI UserExperienceOnLaunchApprovedExeComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus, + __in DWORD dwProcessId + ); +BAAPI UserExperienceOnPauseAUBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +BAAPI UserExperienceOnPauseAUComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ); +BAAPI UserExperienceOnPlanBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in DWORD cPackages + ); +BAAPI UserExperienceOnPlanComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ); +BAAPI UserExperienceOnPlanForwardCompatibleBundle( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzBundleId, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in_z LPCWSTR wzBundleTag, + __in BOOL fPerMachine, + __in VERUTIL_VERSION* pVersion, + __inout BOOL* pfIgnoreBundle + ); +BAAPI UserExperienceOnPlanMsiFeature( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzFeatureId, + __inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState + ); +BAAPI UserExperienceOnPlanMsiPackage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in BOOL fExecute, + __in BOOTSTRAPPER_ACTION_STATE action, + __inout BURN_MSI_PROPERTY* pActionMsiProperty, + __inout INSTALLUILEVEL* pUiLevel, + __inout BOOL* pfDisableExternalUiHandler + ); +BAAPI UserExperienceOnPlannedPackage( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in BOOTSTRAPPER_ACTION_STATE execute, + __in BOOTSTRAPPER_ACTION_STATE rollback, + __in BOOL fPlannedCache, + __in BOOL fPlannedUncache + ); +BAAPI UserExperienceOnPlanPackageBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in BOOTSTRAPPER_PACKAGE_STATE state, + __in BOOL fCached, + __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState, + __inout BOOTSTRAPPER_CACHE_TYPE* pRequestedCacheType + ); +BAAPI UserExperienceOnPlanPackageComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in HRESULT hrStatus, + __in BOOTSTRAPPER_REQUEST_STATE requested + ); +BAAPI UserExperienceOnPlanRelatedBundle( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzBundleId, + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState + ); +BAAPI UserExperienceOnPlanPatchTarget( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzProductCode, + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState + ); +BAAPI UserExperienceOnProgress( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fRollback, + __in DWORD dwProgressPercentage, + __in DWORD dwOverallPercentage + ); +BAAPI UserExperienceOnRegisterBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +BAAPI UserExperienceOnRegisterComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ); +BAAPI UserExperienceOnRollbackMsiTransactionBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId + ); +BAAPI UserExperienceOnRollbackMsiTransactionComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in LPCWSTR wzTransactionId, + __in HRESULT hrStatus + ); +BAAPI UserExperienceOnShutdown( + __in BURN_USER_EXPERIENCE* pUserExperience, + __inout BOOTSTRAPPER_SHUTDOWN_ACTION* pAction + ); +BAAPI UserExperienceOnStartup( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +BAAPI UserExperienceOnSystemRestorePointBegin( + __in BURN_USER_EXPERIENCE* pUserExperience + ); +BAAPI UserExperienceOnSystemRestorePointComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ); +BAAPI UserExperienceOnSystemShutdown( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in DWORD dwEndSession, + __inout BOOL* pfCancel + ); +BAAPI UserExperienceOnUnregisterBegin( + __in BURN_USER_EXPERIENCE* pUserExperience, + __inout BOOL* pfKeepRegistration + ); +BAAPI UserExperienceOnUnregisterComplete( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in HRESULT hrStatus + ); +int UserExperienceCheckExecuteResult( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fRollback, + __in DWORD dwAllowedResults, + __in int nResult + ); +HRESULT UserExperienceInterpretExecuteResult( + __in BURN_USER_EXPERIENCE* pUserExperience, + __in BOOL fRollback, + __in DWORD dwAllowedResults, + __in int nResult + ); +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/variable.cpp b/src/burn/engine/variable.cpp new file mode 100644 index 00000000..6f818ff3 --- /dev/null +++ b/src/burn/engine/variable.cpp @@ -0,0 +1,2323 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// structs + +typedef const struct _BUILT_IN_VARIABLE_DECLARATION +{ + LPCWSTR wzVariable; + PFN_INITIALIZEVARIABLE pfnInitialize; + DWORD_PTR dwpInitializeData; + BOOL fPersist; + BOOL fOverridable; +} BUILT_IN_VARIABLE_DECLARATION; + + +// constants + +const DWORD GROW_VARIABLE_ARRAY = 3; + +enum OS_INFO_VARIABLE +{ + OS_INFO_VARIABLE_NONE, + OS_INFO_VARIABLE_VersionNT, + OS_INFO_VARIABLE_VersionNT64, + OS_INFO_VARIABLE_ServicePackLevel, + OS_INFO_VARIABLE_NTProductType, + OS_INFO_VARIABLE_NTSuiteBackOffice, + OS_INFO_VARIABLE_NTSuiteDataCenter, + OS_INFO_VARIABLE_NTSuiteEnterprise, + OS_INFO_VARIABLE_NTSuitePersonal, + OS_INFO_VARIABLE_NTSuiteSmallBusiness, + OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted, + OS_INFO_VARIABLE_NTSuiteWebServer, + OS_INFO_VARIABLE_CompatibilityMode, + OS_INFO_VARIABLE_TerminalServer, + OS_INFO_VARIABLE_ProcessorArchitecture, + OS_INFO_VARIABLE_WindowsBuildNumber, +}; + +enum SET_VARIABLE +{ + SET_VARIABLE_NOT_BUILTIN, + SET_VARIABLE_OVERRIDE_BUILTIN, + SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS, + SET_VARIABLE_ANY, +}; + +// internal function declarations + +static HRESULT FormatString( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzIn, + __out_z_opt LPWSTR* psczOut, + __out_opt SIZE_T* pcchOut, + __in BOOL fObfuscateHiddenVariables, + __out BOOL* pfContainsHiddenVariable + ); +static HRESULT GetFormatted( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out_z LPWSTR* psczValue, + __out BOOL* pfContainsHiddenVariable + ); +static HRESULT AddBuiltInVariable( + __in BURN_VARIABLES* pVariables, + __in LPCWSTR wzVariable, + __in PFN_INITIALIZEVARIABLE pfnInitialize, + __in DWORD_PTR dwpInitializeData, + __in BOOL fPersist, + __in BOOL fOverridable + ); +static HRESULT GetVariable( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out BURN_VARIABLE** ppVariable + ); +static HRESULT FindVariableIndexByName( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out DWORD* piVariable + ); +static HRESULT InsertVariable( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in DWORD iPosition + ); +static HRESULT SetVariableValue( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in BURN_VARIANT* pVariant, + __in SET_VARIABLE setBuiltin, + __in BOOL fLog + ); +static HRESULT InitializeVariableVersionNT( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableOsInfo( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableSystemInfo( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableComputerName( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableVersionMsi( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableCsidlFolder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableWindowsVolumeFolder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableTempFolder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableSystemFolder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariablePrivileged( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeSystemLanguageID( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeUserUILanguageID( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeUserLanguageID( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableString( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableNumeric( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariable6432Folder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableDate( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableInstallerName( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableInstallerVersion( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableVersion( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT InitializeVariableLogonUser( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +static HRESULT Get64bitFolderFromRegistry( + __in int nFolder, + __deref_out_z LPWSTR* psczPath + ); + +#if !defined(_WIN64) +static HRESULT InitializeVariableRegistryFolder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); +#endif + + +// function definitions + +extern "C" HRESULT VariableInitialize( + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + + ::InitializeCriticalSection(&pVariables->csAccess); + + const BUILT_IN_VARIABLE_DECLARATION vrgBuiltInVariables[] = { + {L"AdminToolsFolder", InitializeVariableCsidlFolder, CSIDL_ADMINTOOLS}, + {L"AppDataFolder", InitializeVariableCsidlFolder, CSIDL_APPDATA}, + {L"CommonAppDataFolder", InitializeVariableCsidlFolder, CSIDL_COMMON_APPDATA}, +#if defined(_WIN64) + {L"CommonFiles64Folder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMON}, + {L"CommonFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMONX86}, +#else + {L"CommonFiles64Folder", InitializeVariableRegistryFolder, CSIDL_PROGRAM_FILES_COMMON}, + {L"CommonFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMON}, +#endif + {L"CommonFiles6432Folder", InitializeVariable6432Folder, CSIDL_PROGRAM_FILES_COMMON}, + {L"CompatibilityMode", InitializeVariableOsInfo, OS_INFO_VARIABLE_CompatibilityMode}, + {VARIABLE_DATE, InitializeVariableDate, 0}, + {L"ComputerName", InitializeVariableComputerName, 0}, + {L"DesktopFolder", InitializeVariableCsidlFolder, CSIDL_DESKTOP}, + {L"FavoritesFolder", InitializeVariableCsidlFolder, CSIDL_FAVORITES}, + {L"FontsFolder", InitializeVariableCsidlFolder, CSIDL_FONTS}, + {VARIABLE_INSTALLERNAME, InitializeVariableInstallerName, 0}, + {VARIABLE_INSTALLERVERSION, InitializeVariableInstallerVersion, 0}, + {L"LocalAppDataFolder", InitializeVariableCsidlFolder, CSIDL_LOCAL_APPDATA}, + {VARIABLE_LOGONUSER, InitializeVariableLogonUser, 0}, + {L"MyPicturesFolder", InitializeVariableCsidlFolder, CSIDL_MYPICTURES}, + {L"NTProductType", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTProductType}, + {L"NTSuiteBackOffice", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteBackOffice}, + {L"NTSuiteDataCenter", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteDataCenter}, + {L"NTSuiteEnterprise", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteEnterprise}, + {L"NTSuitePersonal", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuitePersonal}, + {L"NTSuiteSmallBusiness", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteSmallBusiness}, + {L"NTSuiteSmallBusinessRestricted", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted}, + {L"NTSuiteWebServer", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteWebServer}, + {L"PersonalFolder", InitializeVariableCsidlFolder, CSIDL_PERSONAL}, + {L"Privileged", InitializeVariablePrivileged, 0}, + {L"ProcessorArchitecture", InitializeVariableSystemInfo, OS_INFO_VARIABLE_ProcessorArchitecture}, +#if defined(_WIN64) + {L"ProgramFiles64Folder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES}, + {L"ProgramFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILESX86}, +#else + {L"ProgramFiles64Folder", InitializeVariableRegistryFolder, CSIDL_PROGRAM_FILES}, + {L"ProgramFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES}, +#endif + {L"ProgramFiles6432Folder", InitializeVariable6432Folder, CSIDL_PROGRAM_FILES}, + {L"ProgramMenuFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAMS}, + {L"SendToFolder", InitializeVariableCsidlFolder, CSIDL_SENDTO}, + {L"ServicePackLevel", InitializeVariableVersionNT, OS_INFO_VARIABLE_ServicePackLevel}, + {L"StartMenuFolder", InitializeVariableCsidlFolder, CSIDL_STARTMENU}, + {L"StartupFolder", InitializeVariableCsidlFolder, CSIDL_STARTUP}, + {L"SystemFolder", InitializeVariableSystemFolder, FALSE}, + {L"System64Folder", InitializeVariableSystemFolder, TRUE}, + {L"SystemLanguageID", InitializeSystemLanguageID, 0}, + {L"TempFolder", InitializeVariableTempFolder, 0}, + {L"TemplateFolder", InitializeVariableCsidlFolder, CSIDL_TEMPLATES}, + {L"TerminalServer", InitializeVariableOsInfo, OS_INFO_VARIABLE_TerminalServer}, + {L"UserUILanguageID", InitializeUserUILanguageID, 0}, + {L"UserLanguageID", InitializeUserLanguageID, 0}, + {L"VersionMsi", InitializeVariableVersionMsi, 0}, + {L"VersionNT", InitializeVariableVersionNT, OS_INFO_VARIABLE_VersionNT}, + {L"VersionNT64", InitializeVariableVersionNT, OS_INFO_VARIABLE_VersionNT64}, + {L"WindowsBuildNumber", InitializeVariableVersionNT, OS_INFO_VARIABLE_WindowsBuildNumber}, + {L"WindowsFolder", InitializeVariableCsidlFolder, CSIDL_WINDOWS}, + {L"WindowsVolume", InitializeVariableWindowsVolumeFolder, 0}, + {BURN_BUNDLE_ACTION, InitializeVariableNumeric, 0, FALSE, TRUE}, + {BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, InitializeVariableString, NULL, FALSE, TRUE}, + {BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, InitializeVariableString, NULL, FALSE, TRUE}, + {BURN_BUNDLE_FORCED_RESTART_PACKAGE, InitializeVariableString, NULL, TRUE, TRUE}, + {BURN_BUNDLE_INSTALLED, InitializeVariableNumeric, 0, FALSE, TRUE}, + {BURN_BUNDLE_ELEVATED, InitializeVariableNumeric, 0, FALSE, TRUE}, + {BURN_BUNDLE_ACTIVE_PARENT, InitializeVariableString, NULL, FALSE, TRUE}, + {BURN_BUNDLE_PROVIDER_KEY, InitializeVariableString, (DWORD_PTR)L"", FALSE, TRUE}, + {BURN_BUNDLE_SOURCE_PROCESS_PATH, InitializeVariableString, NULL, FALSE, TRUE}, + {BURN_BUNDLE_SOURCE_PROCESS_FOLDER, InitializeVariableString, NULL, FALSE, TRUE}, + {BURN_BUNDLE_TAG, InitializeVariableString, (DWORD_PTR)L"", FALSE, TRUE}, + {BURN_BUNDLE_UILEVEL, InitializeVariableNumeric, 0, FALSE, TRUE}, + {BURN_BUNDLE_VERSION, InitializeVariableVersion, (DWORD_PTR)L"0", FALSE, TRUE}, + }; + + for (DWORD i = 0; i < countof(vrgBuiltInVariables); ++i) + { + BUILT_IN_VARIABLE_DECLARATION* pBuiltInVariable = &vrgBuiltInVariables[i]; + + hr = AddBuiltInVariable(pVariables, pBuiltInVariable->wzVariable, pBuiltInVariable->pfnInitialize, pBuiltInVariable->dwpInitializeData, pBuiltInVariable->fPersist, pBuiltInVariable->fOverridable); + ExitOnFailure(hr, "Failed to add built-in variable: %ls.", pBuiltInVariable->wzVariable); + } + +LExit: + return hr; +} + +extern "C" HRESULT VariablesParseFromXml( + __in BURN_VARIABLES* pVariables, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + LPWSTR sczId = NULL; + LPWSTR scz = NULL; + BURN_VARIANT value = { }; + BURN_VARIANT_TYPE valueType = BURN_VARIANT_TYPE_NONE; + BOOL fHidden = FALSE; + BOOL fPersisted = FALSE; + DWORD iVariable = 0; + + ::EnterCriticalSection(&pVariables->csAccess); + + // select variable nodes + hr = XmlSelectNodes(pixnBundle, L"Variable", &pixnNodes); + ExitOnFailure(hr, "Failed to select variable nodes."); + + // get variable node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get variable node count."); + + // parse variable elements + for (DWORD i = 0; i < cNodes; ++i) + { + hr = XmlNextElement(pixnNodes, &pixnNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &sczId); + ExitOnFailure(hr, "Failed to get @Id."); + + // @Hidden + hr = XmlGetYesNoAttribute(pixnNode, L"Hidden", &fHidden); + ExitOnFailure(hr, "Failed to get @Hidden."); + + // @Persisted + hr = XmlGetYesNoAttribute(pixnNode, L"Persisted", &fPersisted); + ExitOnFailure(hr, "Failed to get @Persisted."); + + // @Value + hr = XmlGetAttributeEx(pixnNode, L"Value", &scz); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Value."); + + hr = BVariantSetString(&value, scz, 0, FALSE); + ExitOnFailure(hr, "Failed to set variant value."); + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"formatted", -1)) + { + if (!fHidden) + { + LogStringLine(REPORT_STANDARD, "Initializing formatted variable '%ls' to value '%ls'", sczId, value.sczValue); + } + valueType = BURN_VARIANT_TYPE_FORMATTED; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) + { + if (!fHidden) + { + LogStringLine(REPORT_STANDARD, "Initializing numeric variable '%ls' to value '%ls'", sczId, value.sczValue); + } + valueType = BURN_VARIANT_TYPE_NUMERIC; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1)) + { + if (!fHidden) + { + LogStringLine(REPORT_STANDARD, "Initializing string variable '%ls' to value '%ls'", sczId, value.sczValue); + } + valueType = BURN_VARIANT_TYPE_STRING; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) + { + if (!fHidden) + { + LogStringLine(REPORT_STANDARD, "Initializing version variable '%ls' to value '%ls'", sczId, value.sczValue); + } + valueType = BURN_VARIANT_TYPE_VERSION; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else + { + valueType = BURN_VARIANT_TYPE_NONE; + } + + if (fHidden) + { + LogStringLine(REPORT_STANDARD, "Initializing hidden variable '%ls'", sczId); + } + + // change value variant to correct type + hr = BVariantChangeType(&value, valueType); + ExitOnFailure(hr, "Failed to change variant type."); + + if (BURN_VARIANT_TYPE_VERSION == valueType && value.pValue->fInvalid) + { + LogId(REPORT_WARNING, MSG_VARIABLE_INVALID_VERSION, sczId); + } + + // find existing variable + hr = FindVariableIndexByName(pVariables, sczId, &iVariable); + ExitOnFailure(hr, "Failed to find variable value '%ls'.", sczId); + + // insert element if not found + if (S_FALSE == hr) + { + hr = InsertVariable(pVariables, sczId, iVariable); + ExitOnFailure(hr, "Failed to insert variable '%ls'.", sczId); + } + else if (BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariables->rgVariables[iVariable].internalType) + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Attempt to set built-in variable value: %ls", sczId); + } + pVariables->rgVariables[iVariable].fHidden = fHidden; + pVariables->rgVariables[iVariable].fPersisted = fPersisted; + + // update variable value + hr = BVariantSetValue(&pVariables->rgVariables[iVariable].Value, &value); + ExitOnFailure(hr, "Failed to set value of variable: %ls", sczId); + + // prepare next iteration + ReleaseNullObject(pixnNode); + BVariantUninitialize(&value); + ReleaseNullStrSecure(scz); + } + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseStr(scz); + ReleaseStr(sczId); + BVariantUninitialize(&value); + + return hr; +} + +extern "C" void VariablesUninitialize( + __in BURN_VARIABLES* pVariables + ) +{ + ::DeleteCriticalSection(&pVariables->csAccess); + + if (pVariables->rgVariables) + { + for (DWORD i = 0; i < pVariables->cVariables; ++i) + { + BURN_VARIABLE* pVariable = &pVariables->rgVariables[i]; + if (pVariable) + { + ReleaseStr(pVariable->sczName); + BVariantUninitialize(&pVariable->Value); + } + } + MemFree(pVariables->rgVariables); + } +} + +extern "C" void VariablesDump( + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + + for (DWORD i = 0; i < pVariables->cVariables; ++i) + { + BURN_VARIABLE* pVariable = &pVariables->rgVariables[i]; + if (pVariable && BURN_VARIANT_TYPE_NONE != pVariable->Value.Type) + { + hr = StrAllocFormatted(&sczValue, L"%ls = [%ls]", pVariable->sczName, pVariable->sczName); + if (SUCCEEDED(hr)) + { + if (pVariable->fHidden) + { + hr = VariableFormatStringObfuscated(pVariables, sczValue, &sczValue, NULL); + } + else + { + hr = VariableFormatString(pVariables, sczValue, &sczValue, NULL); + } + } + + if (FAILED(hr)) + { + // already logged; best-effort to dump the rest on our way out the door + continue; + } + + LogId(REPORT_VERBOSE, MSG_VARIABLE_DUMP, sczValue); + + ReleaseNullStrSecure(sczValue); + } + } + + StrSecureZeroFreeString(sczValue); +} + +extern "C" HRESULT VariableGetNumeric( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out LONGLONG* pllValue + ) +{ + HRESULT hr = S_OK; + BURN_VARIABLE* pVariable = NULL; + + ::EnterCriticalSection(&pVariables->csAccess); + + hr = GetVariable(pVariables, wzVariable, &pVariable); + if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) + { + ExitFunction1(hr = E_NOTFOUND); + } + else if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); + + hr = BVariantGetNumeric(&pVariable->Value, pllValue); + ExitOnFailure(hr, "Failed to get value as numeric for variable: %ls", wzVariable); + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + + return hr; +} + +extern "C" HRESULT VariableGetString( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out_z LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + BURN_VARIABLE* pVariable = NULL; + + ::EnterCriticalSection(&pVariables->csAccess); + + hr = GetVariable(pVariables, wzVariable, &pVariable); + if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) + { + ExitFunction1(hr = E_NOTFOUND); + } + else if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); + + hr = BVariantGetString(&pVariable->Value, psczValue); + ExitOnFailure(hr, "Failed to get value as string for variable: %ls", wzVariable); + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + + return hr; +} + +extern "C" HRESULT VariableGetVersion( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in VERUTIL_VERSION** ppValue + ) +{ + HRESULT hr = S_OK; + BURN_VARIABLE* pVariable = NULL; + + ::EnterCriticalSection(&pVariables->csAccess); + + hr = GetVariable(pVariables, wzVariable, &pVariable); + if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) + { + ExitFunction1(hr = E_NOTFOUND); + } + else if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); + + hr = BVariantGetVersionHidden(&pVariable->Value, pVariable->fHidden, ppValue); + ExitOnFailure(hr, "Failed to get value as version for variable: %ls", wzVariable); + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + + return hr; +} + +extern "C" HRESULT VariableGetVariant( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + BURN_VARIABLE* pVariable = NULL; + + ::EnterCriticalSection(&pVariables->csAccess); + + hr = GetVariable(pVariables, wzVariable, &pVariable); + if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); + + hr = BVariantCopy(&pVariable->Value, pValue); + ExitOnFailure(hr, "Failed to copy value of variable: %ls", wzVariable); + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + + return hr; +} + +extern "C" HRESULT VariableGetFormatted( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out_z LPWSTR* psczValue, + __out BOOL* pfContainsHiddenVariable + ) +{ + HRESULT hr = S_OK; + + if (pfContainsHiddenVariable) + { + *pfContainsHiddenVariable = FALSE; + } + + hr = GetFormatted(pVariables, wzVariable, psczValue, pfContainsHiddenVariable); + + return hr; +} + +extern "C" HRESULT VariableSetNumeric( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in LONGLONG llValue, + __in BOOL fOverwriteBuiltIn + ) +{ + BURN_VARIANT variant = { }; + + variant.llValue = llValue; + variant.Type = BURN_VARIANT_TYPE_NUMERIC; + + return SetVariableValue(pVariables, wzVariable, &variant, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); +} + +extern "C" HRESULT VariableSetString( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in_z_opt LPCWSTR wzValue, + __in BOOL fOverwriteBuiltIn, + __in BOOL fFormatted + ) +{ + BURN_VARIANT variant = { }; + + variant.sczValue = (LPWSTR)wzValue; + variant.Type = fFormatted ? BURN_VARIANT_TYPE_FORMATTED : BURN_VARIANT_TYPE_STRING; + + return SetVariableValue(pVariables, wzVariable, &variant, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); +} + +extern "C" HRESULT VariableSetVersion( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in VERUTIL_VERSION* pValue, + __in BOOL fOverwriteBuiltIn + ) +{ + BURN_VARIANT variant = { }; + + variant.pValue = pValue; + variant.Type = BURN_VARIANT_TYPE_VERSION; + + return SetVariableValue(pVariables, wzVariable, &variant, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); +} + +extern "C" HRESULT VariableSetVariant( + __in BURN_VARIABLES * pVariables, + __in_z LPCWSTR wzVariable, + __in BURN_VARIANT * pVariant + ) +{ + return SetVariableValue(pVariables, wzVariable, pVariant, SET_VARIABLE_NOT_BUILTIN, TRUE); +} + +extern "C" HRESULT VariableFormatString( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzIn, + __out_z_opt LPWSTR* psczOut, + __out_opt SIZE_T* pcchOut + ) +{ + return FormatString(pVariables, wzIn, psczOut, pcchOut, FALSE, NULL); +} + +extern "C" HRESULT VariableFormatStringObfuscated( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzIn, + __out_z_opt LPWSTR* psczOut, + __out_opt SIZE_T* pcchOut + ) +{ + return FormatString(pVariables, wzIn, psczOut, pcchOut, TRUE, NULL); +} + +extern "C" HRESULT VariableEscapeString( + __in_z LPCWSTR wzIn, + __out_z LPWSTR* psczOut + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzRead = NULL; + LPWSTR pwzEscaped = NULL; + LPWSTR pwz = NULL; + SIZE_T i = 0; + + // allocate buffer for escaped string + hr = StrAlloc(&pwzEscaped, lstrlenW(wzIn) + 1); + ExitOnFailure(hr, "Failed to allocate buffer for escaped string."); + + // read through string and move characters, inserting escapes as needed + wzRead = wzIn; + for (;;) + { + // find next character needing escaping + i = wcscspn(wzRead, L"[]{}"); + + // copy skipped characters + if (0 < i) + { + hr = StrAllocConcat(&pwzEscaped, wzRead, i); + ExitOnFailure(hr, "Failed to append characters."); + } + + if (L'\0' == wzRead[i]) + { + break; // end reached + } + + // escape character + hr = StrAllocFormatted(&pwz, L"[\\%c]", wzRead[i]); + ExitOnFailure(hr, "Failed to format escape sequence."); + + hr = StrAllocConcat(&pwzEscaped, pwz, 0); + ExitOnFailure(hr, "Failed to append escape sequence."); + + // update read pointer + wzRead += i + 1; + } + + // return value + hr = StrAllocString(psczOut, pwzEscaped, 0); + ExitOnFailure(hr, "Failed to copy string."); + +LExit: + ReleaseStr(pwzEscaped); + ReleaseStr(pwz); + return hr; +} + +extern "C" HRESULT VariableSerialize( + __in BURN_VARIABLES* pVariables, + __in BOOL fPersisting, + __inout BYTE** ppbBuffer, + __inout SIZE_T* piBuffer + ) +{ + HRESULT hr = S_OK; + BOOL fIncluded = FALSE; + LONGLONG ll = 0; + LPWSTR scz = NULL; + + ::EnterCriticalSection(&pVariables->csAccess); + + // Write variable count. + hr = BuffWriteNumber(ppbBuffer, piBuffer, pVariables->cVariables); + ExitOnFailure(hr, "Failed to write variable count."); + + // Write variables. + for (DWORD i = 0; i < pVariables->cVariables; ++i) + { + BURN_VARIABLE* pVariable = &pVariables->rgVariables[i]; + + // If we aren't persisting, include only variables that aren't rejected by the elevated process. + // If we are persisting, include only variables that should be persisted. + fIncluded = (!fPersisting && BURN_VARIABLE_INTERNAL_TYPE_BUILTIN != pVariable->internalType) || + (fPersisting && pVariable->fPersisted); + + // Write included flag. + hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)fIncluded); + ExitOnFailure(hr, "Failed to write included flag."); + + if (!fIncluded) + { + continue; + } + + // Write variable name. + hr = BuffWriteString(ppbBuffer, piBuffer, pVariable->sczName); + ExitOnFailure(hr, "Failed to write variable name."); + + // Write variable value type. + hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)pVariable->Value.Type); + ExitOnFailure(hr, "Failed to write variable value type."); + + // Write variable value. + switch (pVariable->Value.Type) + { + case BURN_VARIANT_TYPE_NONE: + break; + case BURN_VARIANT_TYPE_NUMERIC: + hr = BVariantGetNumeric(&pVariable->Value, &ll); + ExitOnFailure(hr, "Failed to get numeric."); + + hr = BuffWriteNumber64(ppbBuffer, piBuffer, static_cast(ll)); + ExitOnFailure(hr, "Failed to write variable value as number."); + + SecureZeroMemory(&ll, sizeof(ll)); + break; + case BURN_VARIANT_TYPE_VERSION: __fallthrough; + case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; + case BURN_VARIANT_TYPE_STRING: + hr = BVariantGetString(&pVariable->Value, &scz); + ExitOnFailure(hr, "Failed to get string."); + + hr = BuffWriteString(ppbBuffer, piBuffer, scz); + ExitOnFailure(hr, "Failed to write variable value as string."); + + ReleaseNullStrSecure(scz); + break; + default: + hr = E_INVALIDARG; + ExitOnFailure(hr, "Unsupported variable type."); + } + } + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + SecureZeroMemory(&ll, sizeof(ll)); + StrSecureZeroFreeString(scz); + + return hr; +} + +extern "C" HRESULT VariableDeserialize( + __in BURN_VARIABLES* pVariables, + __in BOOL fWasPersisted, + __in_bcount(cbBuffer) BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer + ) +{ + HRESULT hr = S_OK; + DWORD cVariables = 0; + LPWSTR sczName = NULL; + BOOL fIncluded = FALSE; + BURN_VARIANT value = { }; + LPWSTR scz = NULL; + DWORD64 qw = 0; + VERUTIL_VERSION* pVersion = NULL; + + ::EnterCriticalSection(&pVariables->csAccess); + + // Read variable count. + hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, &cVariables); + ExitOnFailure(hr, "Failed to read variable count."); + + // Read variables. + for (DWORD i = 0; i < cVariables; ++i) + { + // Read variable included flag. + hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&fIncluded); + ExitOnFailure(hr, "Failed to read variable included flag."); + + if (!fIncluded) + { + continue; // if variable is not included, skip. + } + + // Read variable name. + hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &sczName); + ExitOnFailure(hr, "Failed to read variable name."); + + // Read variable value type. + hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&value.Type); + ExitOnFailure(hr, "Failed to read variable value type."); + + // Read variable value. + switch (value.Type) + { + case BURN_VARIANT_TYPE_NONE: + break; + case BURN_VARIANT_TYPE_NUMERIC: + hr = BuffReadNumber64(pbBuffer, cbBuffer, piBuffer, &qw); + ExitOnFailure(hr, "Failed to read variable value as number."); + + hr = BVariantSetNumeric(&value, static_cast(qw)); + ExitOnFailure(hr, "Failed to set variable value."); + + SecureZeroMemory(&qw, sizeof(qw)); + break; + case BURN_VARIANT_TYPE_VERSION: + hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &scz); + ExitOnFailure(hr, "Failed to read variable value as string."); + + hr = VerParseVersion(scz, 0, FALSE, &pVersion); + ExitOnFailure(hr, "Failed to parse variable value as version."); + + hr = BVariantSetVersion(&value, pVersion); + ExitOnFailure(hr, "Failed to set variable value."); + + SecureZeroMemory(&qw, sizeof(qw)); + break; + case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; + case BURN_VARIANT_TYPE_STRING: + hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &scz); + ExitOnFailure(hr, "Failed to read variable value as string."); + + hr = BVariantSetString(&value, scz, NULL, BURN_VARIANT_TYPE_FORMATTED == value.Type); + ExitOnFailure(hr, "Failed to set variable value."); + + ReleaseNullStrSecure(scz); + break; + default: + hr = E_INVALIDARG; + ExitOnFailure(hr, "Unsupported variable type."); + } + + // Set variable. + hr = SetVariableValue(pVariables, sczName, &value, fWasPersisted ? SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS : SET_VARIABLE_ANY, FALSE); + ExitOnFailure(hr, "Failed to set variable."); + + // Clean up. + BVariantUninitialize(&value); + } + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + + ReleaseVerutilVersion(pVersion); + ReleaseStr(sczName); + BVariantUninitialize(&value); + SecureZeroMemory(&qw, sizeof(qw)); + StrSecureZeroFreeString(scz); + + return hr; +} + +extern "C" HRESULT VariableStrAlloc( + __in BOOL fZeroOnRealloc, + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in DWORD_PTR cch + ) +{ + HRESULT hr = S_OK; + + if (fZeroOnRealloc) + { + hr = StrAllocSecure(ppwz, cch); + } + else + { + hr = StrAlloc(ppwz, cch); + } + + return hr; +} + +extern "C" HRESULT VariableStrAllocString( + __in BOOL fZeroOnRealloc, + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource + ) +{ + HRESULT hr = S_OK; + + if (fZeroOnRealloc) + { + hr = StrAllocStringSecure(ppwz, wzSource, cchSource); + } + else + { + hr = StrAllocString(ppwz, wzSource, cchSource); + } + + return hr; +} + +extern "C" HRESULT VariableStrAllocConcat( + __in BOOL fZeroOnRealloc, + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource + ) +{ + HRESULT hr = S_OK; + + if (fZeroOnRealloc) + { + hr = StrAllocConcatSecure(ppwz, wzSource, cchSource); + } + else + { + hr = StrAllocConcat(ppwz, wzSource, cchSource); + } + + return hr; +} + +extern "C" HRESULT __cdecl VariableStrAllocFormatted( + __in BOOL fZeroOnRealloc, + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ) +{ + HRESULT hr = S_OK; + va_list args; + + va_start(args, wzFormat); + if (fZeroOnRealloc) + { + hr = StrAllocFormattedArgsSecure(ppwz, wzFormat, args); + } + else + { + hr = StrAllocFormattedArgs(ppwz, wzFormat, args); + } + va_end(args); + + return hr; +} + +extern "C" HRESULT VariableIsHidden( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out BOOL* pfHidden + ) +{ + HRESULT hr = S_OK; + BURN_VARIABLE* pVariable = NULL; + + ::EnterCriticalSection(&pVariables->csAccess); + + hr = GetVariable(pVariables, wzVariable, &pVariable); + if (E_NOTFOUND == hr) + { + // A missing variable does not need its data hidden. + *pfHidden = FALSE; + + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to get visibility of variable: %ls", wzVariable); + + *pfHidden = pVariable->fHidden; + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + + return hr; +} + + +// internal function definitions + +static HRESULT FormatString( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzIn, + __out_z_opt LPWSTR* psczOut, + __out_opt SIZE_T* pcchOut, + __in BOOL fObfuscateHiddenVariables, + __out BOOL* pfContainsHiddenVariable + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + LPWSTR sczUnformatted = NULL; + LPWSTR sczFormat = NULL; + LPCWSTR wzRead = NULL; + LPCWSTR wzOpen = NULL; + LPCWSTR wzClose = NULL; + LPWSTR scz = NULL; + LPWSTR* rgVariables = NULL; + DWORD cVariables = 0; + DWORD cch = 0; + size_t cchIn = 0; + BOOL fHidden = FALSE; + MSIHANDLE hRecord = NULL; + + ::EnterCriticalSection(&pVariables->csAccess); + + // allocate buffer for format string + hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_LENGTH, &cchIn); + ExitOnFailure(hr, "Failed to length of format string."); + + hr = StrAlloc(&sczFormat, cchIn + 1); + ExitOnFailure(hr, "Failed to allocate buffer for format string."); + + // read out variables from the unformatted string and build a format string + wzRead = wzIn; + for (;;) + { + // scan for opening '[' + wzOpen = wcschr(wzRead, L'['); + if (!wzOpen) + { + // end reached, append the remainder of the string and end loop + hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, 0); + ExitOnFailure(hr, "Failed to append string."); + break; + } + + // scan for closing ']' + wzClose = wcschr(wzOpen + 1, L']'); + if (!wzClose) + { + // end reached, treat unterminated expander as literal + hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, 0); + ExitOnFailure(hr, "Failed to append string."); + break; + } + cch = (DWORD)(wzClose - wzOpen - 1); + + if (0 == cch) + { + // blank, copy all text including the terminator + hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, (DWORD_PTR)(wzClose - wzRead) + 1); + ExitOnFailure(hr, "Failed to append string."); + } + else + { + // append text preceding expander + if (wzOpen > wzRead) + { + hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, (DWORD_PTR)(wzOpen - wzRead)); + ExitOnFailure(hr, "Failed to append string."); + } + + // get variable name + hr = VariableStrAllocString(!fObfuscateHiddenVariables, &scz, wzOpen + 1, cch); + ExitOnFailure(hr, "Failed to get variable name."); + + // allocate space in variable array + if (rgVariables) + { + LPVOID pv = MemReAlloc(rgVariables, sizeof(LPWSTR) * (cVariables + 1), TRUE); + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate variable array."); + rgVariables = (LPWSTR*)pv; + } + else + { + rgVariables = (LPWSTR*)MemAlloc(sizeof(LPWSTR) * (cVariables + 1), TRUE); + ExitOnNull(rgVariables, hr, E_OUTOFMEMORY, "Failed to allocate variable array."); + } + + // set variable value + if (2 <= cch && L'\\' == wzOpen[1]) + { + // escape sequence, copy character + hr = VariableStrAllocString(!fObfuscateHiddenVariables, &rgVariables[cVariables], &wzOpen[2], 1); + } + else + { + hr = VariableIsHidden(pVariables, scz, &fHidden); + ExitOnFailure(hr, "Failed to determine variable visibility: '%ls'.", scz); + + if (pfContainsHiddenVariable) + { + *pfContainsHiddenVariable |= fHidden; + } + + if (fObfuscateHiddenVariables && fHidden) + { + hr = StrAllocString(&rgVariables[cVariables], L"*****", 0); + } + else + { + // get formatted variable value + hr = GetFormatted(pVariables, scz, &rgVariables[cVariables], pfContainsHiddenVariable); + if (E_NOTFOUND == hr) // variable not found + { + hr = StrAllocStringSecure(&rgVariables[cVariables], L"", 0); + } + } + } + ExitOnFailure(hr, "Failed to set variable value."); + ++cVariables; + + // append placeholder to format string + hr = VariableStrAllocFormatted(!fObfuscateHiddenVariables, &scz, L"[%d]", cVariables); + ExitOnFailure(hr, "Failed to format placeholder string."); + + hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, scz, 0); + ExitOnFailure(hr, "Failed to append placeholder."); + } + + // update read pointer + wzRead = wzClose + 1; + } + + // create record + hRecord = ::MsiCreateRecord(cVariables); + ExitOnNull(hRecord, hr, E_OUTOFMEMORY, "Failed to allocate record."); + + // set format string + er = ::MsiRecordSetStringW(hRecord, 0, sczFormat); + ExitOnWin32Error(er, hr, "Failed to set record format string."); + + // copy record fields + for (DWORD i = 0; i < cVariables; ++i) + { + if (*rgVariables[i]) // not setting if blank + { + er = ::MsiRecordSetStringW(hRecord, i + 1, rgVariables[i]); + ExitOnWin32Error(er, hr, "Failed to set record string."); + } + } + + // get formatted character count + cch = 0; +#pragma prefast(push) +#pragma prefast(disable:6298) + er = ::MsiFormatRecordW(NULL, hRecord, L"", &cch); +#pragma prefast(pop) + if (ERROR_MORE_DATA != er) + { + ExitOnWin32Error(er, hr, "Failed to get formatted length."); + } + + // return formatted string + if (psczOut) + { + hr = VariableStrAlloc(!fObfuscateHiddenVariables, &scz, ++cch); + ExitOnFailure(hr, "Failed to allocate string."); + + er = ::MsiFormatRecordW(NULL, hRecord, scz, &cch); + ExitOnWin32Error(er, hr, "Failed to format record."); + + hr = VariableStrAllocString(!fObfuscateHiddenVariables, psczOut, scz, 0); + ExitOnFailure(hr, "Failed to copy string."); + } + + // return character count + if (pcchOut) + { + *pcchOut = cch; + } + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + + if (rgVariables) + { + for (DWORD i = 0; i < cVariables; ++i) + { + if (fObfuscateHiddenVariables) + { + ReleaseStr(rgVariables[i]); + } + else + { + StrSecureZeroFreeString(rgVariables[i]); + } + } + MemFree(rgVariables); + } + + if (hRecord) + { + ::MsiCloseHandle(hRecord); + } + + if (fObfuscateHiddenVariables) + { + ReleaseStr(sczUnformatted); + ReleaseStr(sczFormat); + ReleaseStr(scz); + } + else + { + StrSecureZeroFreeString(sczUnformatted); + StrSecureZeroFreeString(sczFormat); + StrSecureZeroFreeString(scz); + } + + return hr; +} + +static HRESULT GetFormatted( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out_z LPWSTR* psczValue, + __out BOOL* pfContainsHiddenVariable + ) +{ + HRESULT hr = S_OK; + BURN_VARIABLE* pVariable = NULL; + LPWSTR scz = NULL; + + ::EnterCriticalSection(&pVariables->csAccess); + + hr = GetVariable(pVariables, wzVariable, &pVariable); + if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) + { + ExitFunction1(hr = E_NOTFOUND); + } + else if (E_NOTFOUND == hr) + { + ExitFunction(); + } + ExitOnFailure(hr, "Failed to get variable: %ls", wzVariable); + + if (pfContainsHiddenVariable) + { + *pfContainsHiddenVariable |= pVariable->fHidden; + } + + if (BURN_VARIANT_TYPE_FORMATTED == pVariable->Value.Type) + { + hr = BVariantGetString(&pVariable->Value, &scz); + ExitOnFailure(hr, "Failed to get unformatted string."); + + hr = FormatString(pVariables, scz, psczValue, NULL, FALSE, pfContainsHiddenVariable); + ExitOnFailure(hr, "Failed to format value '%ls' of variable: %ls", pVariable->fHidden ? L"*****" : pVariable->Value.sczValue, wzVariable); + } + else + { + hr = BVariantGetString(&pVariable->Value, psczValue); + ExitOnFailure(hr, "Failed to get value as string for variable: %ls", wzVariable); + } + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + StrSecureZeroFreeString(scz); + + return hr; +} + +static HRESULT AddBuiltInVariable( + __in BURN_VARIABLES* pVariables, + __in LPCWSTR wzVariable, + __in PFN_INITIALIZEVARIABLE pfnInitialize, + __in DWORD_PTR dwpInitializeData, + __in BOOL fPersist, + __in BOOL fOverridable + ) +{ + HRESULT hr = S_OK; + DWORD iVariable = 0; + BURN_VARIABLE* pVariable = NULL; + + hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable); + ExitOnFailure(hr, "Failed to find variable value."); + + // insert element if not found + if (S_FALSE == hr) + { + hr = InsertVariable(pVariables, wzVariable, iVariable); + ExitOnFailure(hr, "Failed to insert variable."); + } + + // set variable values + pVariable = &pVariables->rgVariables[iVariable]; + pVariable->fPersisted = fPersist; + pVariable->internalType = fOverridable ? BURN_VARIABLE_INTERNAL_TYPE_OVERRIDABLE_BUILTIN : BURN_VARIABLE_INTERNAL_TYPE_BUILTIN; + pVariable->pfnInitialize = pfnInitialize; + pVariable->dwpInitializeData = dwpInitializeData; + +LExit: + return hr; +} + +static HRESULT GetVariable( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out BURN_VARIABLE** ppVariable + ) +{ + HRESULT hr = S_OK; + DWORD iVariable = 0; + BURN_VARIABLE* pVariable = NULL; + + hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable); + ExitOnFailure(hr, "Failed to find variable value '%ls'.", wzVariable); + + if (S_FALSE == hr) + { + ExitFunction1(hr = E_NOTFOUND); + } + + pVariable = &pVariables->rgVariables[iVariable]; + + // initialize built-in variable + if (BURN_VARIANT_TYPE_NONE == pVariable->Value.Type && BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariable->internalType) + { + hr = pVariable->pfnInitialize(pVariable->dwpInitializeData, &pVariable->Value); + ExitOnFailure(hr, "Failed to initialize built-in variable value '%ls'.", wzVariable); + } + + *ppVariable = pVariable; + +LExit: + return hr; +} + +static HRESULT FindVariableIndexByName( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out DWORD* piVariable + ) +{ + HRESULT hr = S_OK; + DWORD iRangeFirst = 0; + DWORD cRangeLength = pVariables->cVariables; + + while (cRangeLength) + { + // get variable in middle of range + DWORD iPosition = cRangeLength / 2; + BURN_VARIABLE* pVariable = &pVariables->rgVariables[iRangeFirst + iPosition]; + + switch (::CompareStringW(LOCALE_INVARIANT, SORT_STRINGSORT, wzVariable, -1, pVariable->sczName, -1)) + { + case CSTR_LESS_THAN: + // restrict range to elements before the current + cRangeLength = iPosition; + break; + case CSTR_EQUAL: + // variable found + *piVariable = iRangeFirst + iPosition; + ExitFunction1(hr = S_OK); + case CSTR_GREATER_THAN: + // restrict range to elements after the current + iRangeFirst += iPosition + 1; + cRangeLength -= iPosition + 1; + break; + default: + ExitWithLastError(hr, "Failed to compare strings."); + } + } + + *piVariable = iRangeFirst; + hr = S_FALSE; // variable not found + +LExit: + return hr; +} + +static HRESULT InsertVariable( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in DWORD iPosition + ) +{ + HRESULT hr = S_OK; + size_t cbAllocSize = 0; + + // ensure there is room in the variable array + if (pVariables->cVariables == pVariables->dwMaxVariables) + { + hr = ::DWordAdd(pVariables->dwMaxVariables, GROW_VARIABLE_ARRAY, &(pVariables->dwMaxVariables)); + ExitOnRootFailure(hr, "Overflow while growing variable array size"); + + if (pVariables->rgVariables) + { + hr = ::SizeTMult(sizeof(BURN_VARIABLE), pVariables->dwMaxVariables, &cbAllocSize); + ExitOnRootFailure(hr, "Overflow while calculating size of variable array buffer"); + + LPVOID pv = MemReAlloc(pVariables->rgVariables, cbAllocSize, FALSE); + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate room for more variables."); + + // Prefast claims it's possible to hit this. Putting the check in just in case. + if (pVariables->dwMaxVariables < pVariables->cVariables) + { + hr = INTSAFE_E_ARITHMETIC_OVERFLOW; + ExitOnRootFailure(hr, "Overflow while dealing with variable array buffer allocation"); + } + + pVariables->rgVariables = (BURN_VARIABLE*)pv; + memset(&pVariables->rgVariables[pVariables->cVariables], 0, sizeof(BURN_VARIABLE) * (pVariables->dwMaxVariables - pVariables->cVariables)); + } + else + { + pVariables->rgVariables = (BURN_VARIABLE*)MemAlloc(sizeof(BURN_VARIABLE) * pVariables->dwMaxVariables, TRUE); + ExitOnNull(pVariables->rgVariables, hr, E_OUTOFMEMORY, "Failed to allocate room for variables."); + } + } + + // move variables + if (0 < pVariables->cVariables - iPosition) + { + memmove(&pVariables->rgVariables[iPosition + 1], &pVariables->rgVariables[iPosition], sizeof(BURN_VARIABLE) * (pVariables->cVariables - iPosition)); + memset(&pVariables->rgVariables[iPosition], 0, sizeof(BURN_VARIABLE)); + } + + ++pVariables->cVariables; + + // allocate name + hr = StrAllocString(&pVariables->rgVariables[iPosition].sczName, wzVariable, 0); + ExitOnFailure(hr, "Failed to copy variable name."); + +LExit: + return hr; +} + +static HRESULT SetVariableValue( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in BURN_VARIANT* pVariant, + __in SET_VARIABLE setBuiltin, + __in BOOL fLog + ) +{ + HRESULT hr = S_OK; + DWORD iVariable = 0; + + ::EnterCriticalSection(&pVariables->csAccess); + + hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable); + ExitOnFailure(hr, "Failed to find variable value '%ls'.", wzVariable); + + // Insert element if not found. + if (S_FALSE == hr) + { + hr = InsertVariable(pVariables, wzVariable, iVariable); + ExitOnFailure(hr, "Failed to insert variable '%ls'.", wzVariable); + } + else if (BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariables->rgVariables[iVariable].internalType) // built-in variables must be overridden. + { + if (SET_VARIABLE_OVERRIDE_BUILTIN == setBuiltin || + (SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS == setBuiltin && pVariables->rgVariables[iVariable].fPersisted) || + SET_VARIABLE_ANY == setBuiltin && BURN_VARIABLE_INTERNAL_TYPE_BUILTIN != pVariables->rgVariables[iVariable].internalType) + { + hr = S_OK; + } + else + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Attempt to set built-in variable value: %ls", wzVariable); + } + } + else // must *not* be a built-in variable so caller should not have tried to override it as a built-in. + { + // Not possible from external callers so just assert. + AssertSz(SET_VARIABLE_OVERRIDE_BUILTIN != setBuiltin, "Intent to overwrite non-built-in variable."); + } + + // Log value when not overwriting a built-in variable. + if (fLog && BURN_VARIABLE_INTERNAL_TYPE_NORMAL == pVariables->rgVariables[iVariable].internalType) + { + if (pVariables->rgVariables[iVariable].fHidden) + { + LogStringLine(REPORT_STANDARD, "Setting hidden variable '%ls'", wzVariable); + } + else + { + switch (pVariant->Type) + { + case BURN_VARIANT_TYPE_NONE: + if (BURN_VARIANT_TYPE_NONE != pVariables->rgVariables[iVariable].Value.Type) + { + LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable); + } + break; + + case BURN_VARIANT_TYPE_NUMERIC: + LogStringLine(REPORT_STANDARD, "Setting numeric variable '%ls' to value %lld", wzVariable, pVariant->llValue); + break; + + case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; + case BURN_VARIANT_TYPE_STRING: + if (!pVariant->sczValue) + { + LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable); + } + else + { + LogStringLine(REPORT_STANDARD, "Setting %ls variable '%ls' to value '%ls'", BURN_VARIANT_TYPE_FORMATTED == pVariant->Type ? L"formatted" : L"string", wzVariable, pVariant->sczValue); + } + break; + + case BURN_VARIANT_TYPE_VERSION: + if (!pVariant->pValue) + { + LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable); + } + else + { + LogStringLine(REPORT_STANDARD, "Setting version variable '%ls' to value '%ls'", wzVariable, pVariant->pValue->sczVersion); + } + break; + + default: + AssertSz(FALSE, "Unknown variant type."); + break; + } + } + + if (BURN_VARIANT_TYPE_VERSION == pVariant->Type && pVariant->pValue && pVariant->pValue->fInvalid) + { + LogId(REPORT_WARNING, MSG_VARIABLE_INVALID_VERSION, wzVariable); + } + } + + // Update variable value. + hr = BVariantSetValue(&pVariables->rgVariables[iVariable].Value, pVariant); + ExitOnFailure(hr, "Failed to set value of variable: %ls", wzVariable); + +LExit: + ::LeaveCriticalSection(&pVariables->csAccess); + + if (FAILED(hr) && fLog) + { + LogStringLine(REPORT_STANDARD, "Setting variable failed: ID '%ls', HRESULT 0x%x", wzVariable, hr); + } + + return hr; +} + +static HRESULT InitializeVariableVersionNT( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + RTL_OSVERSIONINFOEXW ovix = { }; + BURN_VARIANT value = { }; + VERUTIL_VERSION* pVersion = NULL; + + hr = OsRtlGetVersion(&ovix); + ExitOnFailure(hr, "Failed to get OS info."); + + switch ((OS_INFO_VARIABLE)dwpData) + { + case OS_INFO_VARIABLE_ServicePackLevel: + if (0 != ovix.wServicePackMajor) + { + value.llValue = static_cast(ovix.wServicePackMajor); + value.Type = BURN_VARIANT_TYPE_NUMERIC; + } + break; + case OS_INFO_VARIABLE_VersionNT: + hr = VerVersionFromQword(MAKEQWORDVERSION(ovix.dwMajorVersion, ovix.dwMinorVersion, 0, 0), &pVersion); + ExitOnFailure(hr, "Failed to create VersionNT from QWORD."); + + value.pValue = pVersion; + value.Type = BURN_VARIANT_TYPE_VERSION; + break; + case OS_INFO_VARIABLE_VersionNT64: + { +#if !defined(_WIN64) + BOOL fIsWow64 = FALSE; + + ProcWow64(::GetCurrentProcess(), &fIsWow64); + if (fIsWow64) +#endif + { + hr = VerVersionFromQword(MAKEQWORDVERSION(ovix.dwMajorVersion, ovix.dwMinorVersion, 0, 0), &pVersion); + ExitOnFailure(hr, "Failed to create VersionNT64 from QWORD."); + + value.pValue = pVersion; + value.Type = BURN_VARIANT_TYPE_VERSION; + } + } + break; + case OS_INFO_VARIABLE_WindowsBuildNumber: + value.llValue = static_cast(ovix.dwBuildNumber); + value.Type = BURN_VARIANT_TYPE_NUMERIC; + default: + AssertSz(FALSE, "Unknown OS info type."); + break; + } + + hr = BVariantCopy(&value, pValue); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + ReleaseVerutilVersion(pVersion); + + return hr; +} + +static HRESULT InitializeVariableOsInfo( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + RTL_OSVERSIONINFOEXW ovix = { }; + BURN_VARIANT value = { }; + + hr = OsRtlGetVersion(&ovix); + ExitOnFailure(hr, "Failed to get OS info."); + + switch ((OS_INFO_VARIABLE)dwpData) + { + case OS_INFO_VARIABLE_NTProductType: + value.llValue = ovix.wProductType; + value.Type = BURN_VARIANT_TYPE_NUMERIC; + break; + case OS_INFO_VARIABLE_NTSuiteBackOffice: + value.llValue = VER_SUITE_BACKOFFICE & ovix.wSuiteMask ? 1 : 0; + value.Type = BURN_VARIANT_TYPE_NUMERIC; + break; + case OS_INFO_VARIABLE_NTSuiteDataCenter: + value.llValue = VER_SUITE_DATACENTER & ovix.wSuiteMask ? 1 : 0; + value.Type = BURN_VARIANT_TYPE_NUMERIC; + break; + case OS_INFO_VARIABLE_NTSuiteEnterprise: + value.llValue = VER_SUITE_ENTERPRISE & ovix.wSuiteMask ? 1 : 0; + value.Type = BURN_VARIANT_TYPE_NUMERIC; + break; + case OS_INFO_VARIABLE_NTSuitePersonal: + value.llValue = VER_SUITE_PERSONAL & ovix.wSuiteMask ? 1 : 0; + value.Type = BURN_VARIANT_TYPE_NUMERIC; + break; + case OS_INFO_VARIABLE_NTSuiteSmallBusiness: + value.llValue = VER_SUITE_SMALLBUSINESS & ovix.wSuiteMask ? 1 : 0; + value.Type = BURN_VARIANT_TYPE_NUMERIC; + break; + case OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted: + value.llValue = VER_SUITE_SMALLBUSINESS_RESTRICTED & ovix.wSuiteMask ? 1 : 0; + value.Type = BURN_VARIANT_TYPE_NUMERIC; + break; + case OS_INFO_VARIABLE_NTSuiteWebServer: + value.llValue = VER_SUITE_BLADE & ovix.wSuiteMask ? 1 : 0; + value.Type = BURN_VARIANT_TYPE_NUMERIC; + break; + case OS_INFO_VARIABLE_CompatibilityMode: + { + DWORDLONG dwlConditionMask = 0; + VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_EQUAL); + VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_EQUAL); + VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMAJOR, VER_EQUAL); + VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMINOR, VER_EQUAL); + + value.llValue = ::VerifyVersionInfoW(&ovix, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, dwlConditionMask); + value.Type = BURN_VARIANT_TYPE_NUMERIC; + } + break; + case OS_INFO_VARIABLE_TerminalServer: + value.llValue = (VER_SUITE_TERMINAL == (ovix.wSuiteMask & VER_SUITE_TERMINAL)) && (VER_SUITE_SINGLEUSERTS != (ovix.wSuiteMask & VER_SUITE_SINGLEUSERTS)) ? 1 : 0; + value.Type = BURN_VARIANT_TYPE_NUMERIC; + break; + default: + AssertSz(FALSE, "Unknown OS info type."); + break; + } + + hr = BVariantCopy(&value, pValue); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeVariableSystemInfo( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + SYSTEM_INFO si = { }; + BURN_VARIANT value = { }; + + ::GetNativeSystemInfo(&si); + + switch ((OS_INFO_VARIABLE)dwpData) + { + case OS_INFO_VARIABLE_ProcessorArchitecture: + value.llValue = si.wProcessorArchitecture; + value.Type = BURN_VARIANT_TYPE_NUMERIC; + break; + default: + AssertSz(FALSE, "Unknown OS info type."); + break; + } + + hr = BVariantCopy(&value, pValue); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeVariableComputerName( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + UNREFERENCED_PARAMETER(dwpData); + + HRESULT hr = S_OK; + WCHAR wzComputerName[MAX_COMPUTERNAME_LENGTH + 1] = { }; + DWORD cchComputerName = countof(wzComputerName); + + // get computer name + if (!::GetComputerNameW(wzComputerName, &cchComputerName)) + { + ExitWithLastError(hr, "Failed to get computer name."); + } + + // set value + hr = BVariantSetString(pValue, wzComputerName, 0, FALSE); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeVariableVersionMsi( + __in DWORD_PTR /*dwpData*/, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + DLLGETVERSIONPROC pfnMsiDllGetVersion = NULL; + DLLVERSIONINFO msiVersionInfo = { }; + VERUTIL_VERSION* pVersion = NULL; + + // get DllGetVersion proc address + pfnMsiDllGetVersion = (DLLGETVERSIONPROC)::GetProcAddress(::GetModuleHandleW(L"msi"), "DllGetVersion"); + ExitOnNullWithLastError(pfnMsiDllGetVersion, hr, "Failed to find DllGetVersion entry point in msi.dll."); + + // get msi.dll version info + msiVersionInfo.cbSize = sizeof(DLLVERSIONINFO); + hr = pfnMsiDllGetVersion(&msiVersionInfo); + ExitOnFailure(hr, "Failed to get msi.dll version info."); + + hr = VerVersionFromQword(MAKEQWORDVERSION(msiVersionInfo.dwMajorVersion, msiVersionInfo.dwMinorVersion, 0, 0), &pVersion); + ExitOnFailure(hr, "Failed to create msi.dll version from QWORD."); + + hr = BVariantSetVersion(pValue, pVersion); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + ReleaseVerutilVersion(pVersion); + + return hr; +} + +static HRESULT InitializeVariableCsidlFolder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + int nFolder = (int)dwpData; + + // get folder path + hr = ShelGetFolder(&sczPath, nFolder); + ExitOnRootFailure(hr, "Failed to get shell folder."); + + // set value + hr = BVariantSetString(pValue, sczPath, 0, FALSE); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + ReleaseStr(sczPath); + + return hr; +} + +static HRESULT InitializeVariableTempFolder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + UNREFERENCED_PARAMETER(dwpData); + + HRESULT hr = S_OK; + WCHAR wzPath[MAX_PATH] = { }; + + // get volume path name + if (!::GetTempPathW(MAX_PATH, wzPath)) + { + ExitWithLastError(hr, "Failed to get temp path."); + } + + // set value + hr = BVariantSetString(pValue, wzPath, 0, FALSE); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeVariableSystemFolder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + BOOL f64 = (BOOL)dwpData; + WCHAR wzSystemFolder[MAX_PATH] = { }; + +#if !defined(_WIN64) + BOOL fIsWow64 = FALSE; + ProcWow64(::GetCurrentProcess(), &fIsWow64); + + if (fIsWow64) + { + if (f64) + { + if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder))) + { + ExitWithLastError(hr, "Failed to get 64-bit system folder."); + } + } + else + { + if (!::GetSystemWow64DirectoryW(wzSystemFolder, countof(wzSystemFolder))) + { + ExitWithLastError(hr, "Failed to get 32-bit system folder."); + } + } + } + else + { + if (!f64) + { + if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder))) + { + ExitWithLastError(hr, "Failed to get 32-bit system folder."); + } + } + } +#else + if (f64) + { + if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder))) + { + ExitWithLastError(hr, "Failed to get 64-bit system folder."); + } + } + else + { + if (!::GetSystemWow64DirectoryW(wzSystemFolder, countof(wzSystemFolder))) + { + ExitWithLastError(hr, "Failed to get 32-bit system folder."); + } + } +#endif + + if (*wzSystemFolder) + { + hr = PathFixedBackslashTerminate(wzSystemFolder, countof(wzSystemFolder)); + ExitOnFailure(hr, "Failed to backslash terminate system folder."); + } + + // set value + hr = BVariantSetString(pValue, wzSystemFolder, 0, FALSE); + ExitOnFailure(hr, "Failed to set system folder variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeVariableWindowsVolumeFolder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + UNREFERENCED_PARAMETER(dwpData); + + HRESULT hr = S_OK; + WCHAR wzWindowsPath[MAX_PATH] = { }; + WCHAR wzVolumePath[MAX_PATH] = { }; + + // get windows directory + if (!::GetWindowsDirectoryW(wzWindowsPath, countof(wzWindowsPath))) + { + ExitWithLastError(hr, "Failed to get windows directory."); + } + + // get volume path name + if (!::GetVolumePathNameW(wzWindowsPath, wzVolumePath, MAX_PATH)) + { + ExitWithLastError(hr, "Failed to get volume path name."); + } + + // set value + hr = BVariantSetString(pValue, wzVolumePath, 0, FALSE); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeVariablePrivileged( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + UNREFERENCED_PARAMETER(dwpData); + + HRESULT hr = S_OK; + BOOL fPrivileged = FALSE; + + // check if process could run privileged. + hr = OsCouldRunPrivileged(&fPrivileged); + ExitOnFailure(hr, "Failed to check if process could run privileged."); + + // set value + hr = BVariantSetNumeric(pValue, fPrivileged); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeSystemLanguageID( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + UNREFERENCED_PARAMETER(dwpData); + + HRESULT hr = S_OK; + LANGID langid = ::GetSystemDefaultLangID(); + + hr = BVariantSetNumeric(pValue, langid); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeUserUILanguageID( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + UNREFERENCED_PARAMETER(dwpData); + + HRESULT hr = S_OK; + LANGID langid = ::GetUserDefaultUILanguage(); + + hr = BVariantSetNumeric(pValue, langid); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeUserLanguageID( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + UNREFERENCED_PARAMETER(dwpData); + + HRESULT hr = S_OK; + LANGID langid = ::GetUserDefaultLangID(); + + hr = BVariantSetNumeric(pValue, langid); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeVariableString( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzValue = (LPCWSTR)dwpData; + + // set value + hr = BVariantSetString(pValue, wzValue, 0, FALSE); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeVariableNumeric( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + LONGLONG llValue = (LONGLONG)dwpData; + + // set value + hr = BVariantSetNumeric(pValue, llValue); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +#if !defined(_WIN64) +static HRESULT InitializeVariableRegistryFolder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + int nFolder = (int)dwpData; + LPWSTR sczPath = NULL; + + BOOL fIsWow64 = FALSE; + + ProcWow64(::GetCurrentProcess(), &fIsWow64); + if (!fIsWow64) // on 32-bit machines, variables aren't set + { + ExitFunction(); + } + + hr = Get64bitFolderFromRegistry(nFolder, &sczPath); + ExitOnFailure(hr, "Failed to get 64-bit folder."); + + // set value + hr = BVariantSetString(pValue, sczPath, 0, FALSE); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + ReleaseStr(sczPath); + + return hr; +} +#endif + +static HRESULT InitializeVariable6432Folder( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + int nFolder = (int)dwpData; + LPWSTR sczPath = NULL; + +#if !defined(_WIN64) + BOOL fIsWow64 = FALSE; + + // If 32-bit use shell-folder. + ProcWow64(::GetCurrentProcess(), &fIsWow64); + if (!fIsWow64) + { + hr = ShelGetFolder(&sczPath, nFolder); + ExitOnRootFailure(hr, "Failed to get shell folder."); + } + else +#endif + { + hr = Get64bitFolderFromRegistry(nFolder, &sczPath); + ExitOnFailure(hr, "Failed to get 64-bit folder."); + } + + // set value + hr = BVariantSetString(pValue, sczPath, 0, FALSE); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + ReleaseStr(sczPath); + + return hr; +} + +// Get the date in the same format as Windows Installer. +static HRESULT InitializeVariableDate( + __in DWORD_PTR /*dwpData*/, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + SYSTEMTIME systime = { }; + LPWSTR sczDate = NULL; + int cchDate = 0; + + ::GetSystemTime(&systime); + + cchDate = ::GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, NULL, NULL, cchDate); + if (!cchDate) + { + ExitOnLastError(hr, "Failed to get the required buffer length for the Date."); + } + + hr = StrAlloc(&sczDate, cchDate); + ExitOnFailure(hr, "Failed to allocate the buffer for the Date."); + + if (!::GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, NULL, sczDate, cchDate)) + { + ExitOnLastError(hr, "Failed to get the Date."); + } + + // set value + hr = BVariantSetString(pValue, sczDate, cchDate, FALSE); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + ReleaseStr(sczDate); + + return hr; +} + +static HRESULT InitializeVariableInstallerName( + __in DWORD_PTR /*dwpData*/, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + + // set value + hr = BVariantSetString(pValue, L"WiX Burn", 0, FALSE); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT InitializeVariableInstallerVersion( + __in DWORD_PTR /*dwpData*/, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + LPWSTR sczVersion = NULL; + + hr = StrAllocStringAnsi(&sczVersion, szVerMajorMinorBuild, 0, CP_ACP); + ExitOnFailure(hr, "Failed to copy the engine version."); + + // set value + hr = BVariantSetString(pValue, sczVersion, 0, FALSE); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + ReleaseStr(sczVersion); + + return hr; +} + +static HRESULT InitializeVariableVersion( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzValue = (LPCWSTR)dwpData; + VERUTIL_VERSION* pVersion = NULL; + + hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); + ExitOnFailure(hr, "Failed to initialize version."); + + // set value + hr = BVariantSetVersion(pValue, pVersion); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + ReleaseVerutilVersion(pVersion); + + return hr; +} + +// Get the current user the same as Windows Installer. +static HRESULT InitializeVariableLogonUser( + __in DWORD_PTR /*dwpData*/, + __inout BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + WCHAR wzUserName[UNLEN + 1]; + DWORD cchUserName = countof(wzUserName); + + if (!::GetUserNameW(wzUserName, &cchUserName)) + { + ExitOnLastError(hr, "Failed to get the user name."); + } + + // set value + hr = BVariantSetString(pValue, wzUserName, 0, FALSE); + ExitOnFailure(hr, "Failed to set variant value."); + +LExit: + return hr; +} + +static HRESULT Get64bitFolderFromRegistry( + __in int nFolder, + __deref_out_z LPWSTR* psczPath + ) +{ + HRESULT hr = S_OK; + HKEY hkFolders = NULL; + + AssertSz(CSIDL_PROGRAM_FILES == nFolder || CSIDL_PROGRAM_FILES_COMMON == nFolder, "Unknown folder CSIDL."); + LPCWSTR wzFolderValue = CSIDL_PROGRAM_FILES_COMMON == nFolder ? L"CommonFilesDir" : L"ProgramFilesDir"; + + hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion", KEY_READ | KEY_WOW64_64KEY, &hkFolders); + ExitOnFailure(hr, "Failed to open Windows folder key."); + + hr = RegReadString(hkFolders, wzFolderValue, psczPath); + ExitOnFailure(hr, "Failed to read folder path for '%ls'.", wzFolderValue); + + hr = PathBackslashTerminate(psczPath); + ExitOnFailure(hr, "Failed to ensure path was backslash terminated."); + +LExit: + ReleaseRegKey(hkFolders); + + return hr; +} + diff --git a/src/burn/engine/variable.h b/src/burn/engine/variable.h new file mode 100644 index 00000000..a38c9daa --- /dev/null +++ b/src/burn/engine/variable.h @@ -0,0 +1,185 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + +const LPCWSTR VARIABLE_DATE = L"Date"; +const LPCWSTR VARIABLE_LOGONUSER = L"LogonUser"; +const LPCWSTR VARIABLE_INSTALLERNAME = L"InstallerName"; +const LPCWSTR VARIABLE_INSTALLERVERSION = L"InstallerVersion"; + + +// typedefs + +typedef HRESULT (*PFN_INITIALIZEVARIABLE)( + __in DWORD_PTR dwpData, + __inout BURN_VARIANT* pValue + ); + + +// constants + +enum BURN_VARIABLE_INTERNAL_TYPE +{ + BURN_VARIABLE_INTERNAL_TYPE_NORMAL, // the BA can set this variable. + BURN_VARIABLE_INTERNAL_TYPE_OVERRIDABLE_BUILTIN, // the BA can't set this variable, but the unelevated process can serialize it to the elevated process. + BURN_VARIABLE_INTERNAL_TYPE_BUILTIN, // the BA can't set this variable, and the unelevated process can't serialize it to the elevated process. +}; + + +// structs + +typedef struct _BURN_VARIABLE +{ + LPWSTR sczName; + BURN_VARIANT Value; + BOOL fHidden; + BOOL fPersisted; + + // used for late initialization of built-in variables + BURN_VARIABLE_INTERNAL_TYPE internalType; + PFN_INITIALIZEVARIABLE pfnInitialize; + DWORD_PTR dwpInitializeData; +} BURN_VARIABLE; + +typedef struct _BURN_VARIABLES +{ + CRITICAL_SECTION csAccess; + DWORD dwMaxVariables; + DWORD cVariables; + BURN_VARIABLE* rgVariables; +} BURN_VARIABLES; + + +// function declarations + +HRESULT VariableInitialize( + __in BURN_VARIABLES* pVariables + ); +HRESULT VariablesParseFromXml( + __in BURN_VARIABLES* pVariables, + __in IXMLDOMNode* pixnBundle + ); +void VariablesUninitialize( + __in BURN_VARIABLES* pVariables + ); +void VariablesDump( + __in BURN_VARIABLES* pVariables + ); +HRESULT VariableGetNumeric( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out LONGLONG* pllValue + ); +HRESULT VariableGetString( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out_z LPWSTR* psczValue + ); +HRESULT VariableGetVersion( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in VERUTIL_VERSION** ppValue + ); +HRESULT VariableGetVariant( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in BURN_VARIANT* pValue + ); +HRESULT VariableGetFormatted( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out_z LPWSTR* psczValue, + __out BOOL* pfContainsHiddenVariable + ); +HRESULT VariableSetNumeric( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in LONGLONG llValue, + __in BOOL fOverwriteBuiltIn + ); +HRESULT VariableSetString( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in_z_opt LPCWSTR wzValue, + __in BOOL fOverwriteBuiltIn, + __in BOOL fFormatted + ); +HRESULT VariableSetVersion( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in VERUTIL_VERSION* pValue, + __in BOOL fOverwriteBuiltIn + ); +HRESULT VariableSetVariant( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __in BURN_VARIANT* pVariant + ); +HRESULT VariableFormatString( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzIn, + __out_z_opt LPWSTR* psczOut, + __out_opt SIZE_T* pcchOut + ); +HRESULT VariableFormatStringObfuscated( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzIn, + __out_z_opt LPWSTR* psczOut, + __out_opt SIZE_T* pcchOut + ); +HRESULT VariableEscapeString( + __in_z LPCWSTR wzIn, + __out_z LPWSTR* psczOut + ); +HRESULT VariableSerialize( + __in BURN_VARIABLES* pVariables, + __in BOOL fPersisting, + __inout BYTE** ppbBuffer, + __inout SIZE_T* piBuffer + ); +HRESULT VariableDeserialize( + __in BURN_VARIABLES* pVariables, + __in BOOL fWasPersisted, + __in_bcount(cbBuffer) BYTE* pbBuffer, + __in SIZE_T cbBuffer, + __inout SIZE_T* piBuffer + ); +HRESULT VariableStrAlloc( + __in BOOL fZeroOnRealloc, + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, + __in DWORD_PTR cch + ); +HRESULT VariableStrAllocString( + __in BOOL fZeroOnRealloc, + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource + ); +HRESULT VariableStrAllocConcat( + __in BOOL fZeroOnRealloc, + __deref_out_z LPWSTR* ppwz, + __in_z LPCWSTR wzSource, + __in DWORD_PTR cchSource + ); +HRESULT __cdecl VariableStrAllocFormatted( + __in BOOL fZeroOnRealloc, + __deref_out_z LPWSTR* ppwz, + __in __format_string LPCWSTR wzFormat, + ... + ); +HRESULT VariableIsHidden( + __in BURN_VARIABLES* pVariables, + __in_z LPCWSTR wzVariable, + __out BOOL* pfHidden + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/engine/variant.cpp b/src/burn/engine/variant.cpp new file mode 100644 index 00000000..2267ee7b --- /dev/null +++ b/src/burn/engine/variant.cpp @@ -0,0 +1,321 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +// internal function declarations + +static HRESULT GetVersionInternal( + __in BURN_VARIANT* pVariant, + __in BOOL fHidden, + __in BOOL fSilent, + __out VERUTIL_VERSION** ppValue + ); + +// function definitions + +extern "C" void BVariantUninitialize( + __in BURN_VARIANT* pVariant + ) +{ + if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type || + BURN_VARIANT_TYPE_STRING == pVariant->Type) + { + StrSecureZeroFreeString(pVariant->sczValue); + } + SecureZeroMemory(pVariant, sizeof(BURN_VARIANT)); +} + +extern "C" HRESULT BVariantGetNumeric( + __in BURN_VARIANT* pVariant, + __out LONGLONG* pllValue + ) +{ + HRESULT hr = S_OK; + + switch (pVariant->Type) + { + case BURN_VARIANT_TYPE_NUMERIC: + *pllValue = pVariant->llValue; + break; + case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; + case BURN_VARIANT_TYPE_STRING: + hr = StrStringToInt64(pVariant->sczValue, 0, pllValue); + if (FAILED(hr)) + { + hr = DISP_E_TYPEMISMATCH; + } + break; + case BURN_VARIANT_TYPE_VERSION: + hr = StrStringToInt64(pVariant->pValue ? pVariant->pValue->sczVersion : NULL, 0, pllValue); + if (FAILED(hr)) + { + hr = DISP_E_TYPEMISMATCH; + } + break; + default: + hr = E_INVALIDARG; + break; + } + + return hr; +} + +extern "C" HRESULT BVariantGetString( + __in BURN_VARIANT* pVariant, + __out_z LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + + switch (pVariant->Type) + { + case BURN_VARIANT_TYPE_NUMERIC: + hr = StrAllocFormattedSecure(psczValue, L"%I64d", pVariant->llValue); + ExitOnFailure(hr, "Failed to convert int64 to string."); + break; + case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; + case BURN_VARIANT_TYPE_STRING: + hr = StrAllocStringSecure(psczValue, pVariant->sczValue, 0); + ExitOnFailure(hr, "Failed to copy string value."); + break; + case BURN_VARIANT_TYPE_VERSION: + hr = StrAllocStringSecure(psczValue, pVariant->pValue ? pVariant->pValue->sczVersion : NULL, 0); + ExitOnFailure(hr, "Failed to copy version value."); + break; + default: + hr = E_INVALIDARG; + break; + } + +LExit: + return hr; +} + +extern "C" HRESULT BVariantGetVersion( + __in BURN_VARIANT* pVariant, + __out VERUTIL_VERSION** ppValue + ) +{ + return GetVersionInternal(pVariant, FALSE, FALSE, ppValue); +} + +extern "C" HRESULT BVariantGetVersionHidden( + __in BURN_VARIANT* pVariant, + __in BOOL fHidden, + __out VERUTIL_VERSION** ppValue + ) +{ + return GetVersionInternal(pVariant, fHidden, FALSE, ppValue); +} + +extern "C" HRESULT BVariantGetVersionSilent( + __in BURN_VARIANT* pVariant, + __in BOOL fSilent, + __out VERUTIL_VERSION** ppValue + ) +{ + return GetVersionInternal(pVariant, FALSE, fSilent, ppValue); +} + +static HRESULT GetVersionInternal( + __in BURN_VARIANT* pVariant, + __in BOOL fHidden, + __in BOOL fSilent, + __out VERUTIL_VERSION** ppValue + ) +{ + HRESULT hr = S_OK; + + switch (pVariant->Type) + { + case BURN_VARIANT_TYPE_NUMERIC: + hr = VerVersionFromQword(pVariant->llValue, ppValue); + break; + case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; + case BURN_VARIANT_TYPE_STRING: + hr = VerParseVersion(pVariant->sczValue, 0, FALSE, ppValue); + if (SUCCEEDED(hr) && !fSilent && (*ppValue)->fInvalid) + { + LogId(REPORT_WARNING, MSG_INVALID_VERSION_COERSION, fHidden ? L"*****" : pVariant->sczValue); + } + break; + case BURN_VARIANT_TYPE_VERSION: + if (!pVariant->pValue) + { + *ppValue = NULL; + } + else + { + hr = VerCopyVersion(pVariant->pValue, ppValue); + } + break; + default: + hr = E_INVALIDARG; + break; + } + + return hr; +} + +extern "C" HRESULT BVariantSetNumeric( + __in BURN_VARIANT* pVariant, + __in LONGLONG llValue + ) +{ + HRESULT hr = S_OK; + + if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type || + BURN_VARIANT_TYPE_STRING == pVariant->Type) + { + StrSecureZeroFreeString(pVariant->sczValue); + } + memset(pVariant, 0, sizeof(BURN_VARIANT)); + pVariant->llValue = llValue; + pVariant->Type = BURN_VARIANT_TYPE_NUMERIC; + + return hr; +} + +extern "C" HRESULT BVariantSetString( + __in BURN_VARIANT* pVariant, + __in_z_opt LPCWSTR wzValue, + __in DWORD_PTR cchValue, + __in BOOL fFormatted + ) +{ + HRESULT hr = S_OK; + + if (!wzValue) // if we're nulling out the string, make the variable NONE. + { + BVariantUninitialize(pVariant); + } + else // assign the value. + { + if (BURN_VARIANT_TYPE_FORMATTED != pVariant->Type && + BURN_VARIANT_TYPE_STRING != pVariant->Type) + { + memset(pVariant, 0, sizeof(BURN_VARIANT)); + } + + hr = StrAllocStringSecure(&pVariant->sczValue, wzValue, cchValue); + ExitOnFailure(hr, "Failed to copy string."); + + pVariant->Type = fFormatted ? BURN_VARIANT_TYPE_FORMATTED : BURN_VARIANT_TYPE_STRING; + } + +LExit: + return hr; +} + +extern "C" HRESULT BVariantSetVersion( + __in BURN_VARIANT* pVariant, + __in VERUTIL_VERSION* pValue + ) +{ + HRESULT hr = S_OK; + + if (!pValue) // if we're nulling out the version, make the variable NONE. + { + BVariantUninitialize(pVariant); + } + else // assign the value. + { + if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type || + BURN_VARIANT_TYPE_STRING == pVariant->Type) + { + StrSecureZeroFreeString(pVariant->sczValue); + } + memset(pVariant, 0, sizeof(BURN_VARIANT)); + hr = VerCopyVersion(pValue, &pVariant->pValue); + pVariant->Type = BURN_VARIANT_TYPE_VERSION; + } + + return hr; +} + +extern "C" HRESULT BVariantSetValue( + __in BURN_VARIANT* pVariant, + __in BURN_VARIANT* pValue + ) +{ + HRESULT hr = S_OK; + + switch (pValue->Type) + { + case BURN_VARIANT_TYPE_NONE: + BVariantUninitialize(pVariant); + break; + case BURN_VARIANT_TYPE_NUMERIC: + hr = BVariantSetNumeric(pVariant, pValue->llValue); + break; + case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; + case BURN_VARIANT_TYPE_STRING: + hr = BVariantSetString(pVariant, pValue->sczValue, 0, BURN_VARIANT_TYPE_FORMATTED == pValue->Type); + break; + case BURN_VARIANT_TYPE_VERSION: + hr = BVariantSetVersion(pVariant, pValue->pValue); + break; + default: + hr = E_INVALIDARG; + } + ExitOnFailure(hr, "Failed to copy variant value."); + +LExit: + return hr; +} + +extern "C" HRESULT BVariantCopy( + __in BURN_VARIANT* pSource, + __out BURN_VARIANT* pTarget + ) +{ + return BVariantSetValue(pTarget, pSource); +} + +extern "C" HRESULT BVariantChangeType( + __in BURN_VARIANT* pVariant, + __in BURN_VARIANT_TYPE type + ) +{ + HRESULT hr = S_OK; + BURN_VARIANT variant = { }; + + if (pVariant->Type == type) + { + ExitFunction(); // variant already is of the requested type + } + else if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type && BURN_VARIANT_TYPE_STRING == type || + BURN_VARIANT_TYPE_STRING == pVariant->Type && BURN_VARIANT_TYPE_FORMATTED == type) + { + pVariant->Type = type; + ExitFunction(); + } + + switch (type) + { + case BURN_VARIANT_TYPE_NONE: + hr = S_OK; + break; + case BURN_VARIANT_TYPE_NUMERIC: + hr = BVariantGetNumeric(pVariant, &variant.llValue); + break; + case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; + case BURN_VARIANT_TYPE_STRING: + hr = BVariantGetString(pVariant, &variant.sczValue); + break; + case BURN_VARIANT_TYPE_VERSION: + hr = BVariantGetVersionSilent(pVariant, TRUE, &variant.pValue); + break; + default: + ExitFunction1(hr = E_INVALIDARG); + } + variant.Type = type; + ExitOnFailure(hr, "Failed to copy variant value."); + + BVariantUninitialize(pVariant); + memcpy_s(pVariant, sizeof(BURN_VARIANT), &variant, sizeof(BURN_VARIANT)); + SecureZeroMemory(&variant, sizeof(BURN_VARIANT)); + +LExit: + return hr; +} diff --git a/src/burn/engine/variant.h b/src/burn/engine/variant.h new file mode 100644 index 00000000..e460005b --- /dev/null +++ b/src/burn/engine/variant.h @@ -0,0 +1,100 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#if defined(__cplusplus) +extern "C" { +#endif + + +// constants + +enum BURN_VARIANT_TYPE +{ + BURN_VARIANT_TYPE_NONE, + BURN_VARIANT_TYPE_FORMATTED, + BURN_VARIANT_TYPE_NUMERIC, + BURN_VARIANT_TYPE_STRING, // when formatting this value should be used as is (don't continue recursively formatting). + BURN_VARIANT_TYPE_VERSION, +}; + + +// struct + +typedef struct _BURN_VARIANT +{ + union + { + LONGLONG llValue; + VERUTIL_VERSION* pValue; + LPWSTR sczValue; + }; + BURN_VARIANT_TYPE Type; +} BURN_VARIANT; + + +// function declarations + +void BVariantUninitialize( + __in BURN_VARIANT* pVariant + ); +HRESULT BVariantGetNumeric( + __in BURN_VARIANT* pVariant, + __out LONGLONG* pllValue + ); +HRESULT BVariantGetString( + __in BURN_VARIANT* pVariant, + __out_z LPWSTR* psczValue + ); +HRESULT BVariantGetVersion( + __in BURN_VARIANT* pVariant, + __out VERUTIL_VERSION** ppValue + ); +HRESULT BVariantGetVersionHidden( + __in BURN_VARIANT* pVariant, + __in BOOL fHidden, + __out VERUTIL_VERSION** ppValue + ); +HRESULT BVariantGetVersionSilent( + __in BURN_VARIANT* pVariant, + __in BOOL fSilent, + __out VERUTIL_VERSION** ppValue + ); +HRESULT BVariantSetNumeric( + __in BURN_VARIANT* pVariant, + __in LONGLONG llValue + ); +HRESULT BVariantSetString( + __in BURN_VARIANT* pVariant, + __in_z_opt LPCWSTR wzValue, + __in DWORD_PTR cchValue, + __in BOOL fFormatted + ); +HRESULT BVariantSetVersion( + __in BURN_VARIANT* pVariant, + __in VERUTIL_VERSION* pValue + ); +/******************************************************************** +BVariantSetValue - Convenience function that calls BVariantUninitialize, + BVariantSetNumeric, BVariantSetString, or + BVariantSetVersion based on the type of pValue. +********************************************************************/ +HRESULT BVariantSetValue( + __in BURN_VARIANT* pVariant, + __in BURN_VARIANT* pValue + ); +/******************************************************************** +BVariantCopy - creates a copy of pSource. +********************************************************************/ +HRESULT BVariantCopy( + __in BURN_VARIANT* pSource, + __out BURN_VARIANT* pTarget + ); +HRESULT BVariantChangeType( + __in BURN_VARIANT* pVariant, + __in BURN_VARIANT_TYPE type + ); + +#if defined(__cplusplus) +} +#endif diff --git a/src/burn/nuget.config b/src/burn/nuget.config new file mode 100644 index 00000000..237c522e --- /dev/null +++ b/src/burn/nuget.config @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/burn/stub/StubSection.cpp b/src/burn/stub/StubSection.cpp new file mode 100644 index 00000000..962bb3cf --- /dev/null +++ b/src/burn/stub/StubSection.cpp @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +#pragma section(".wixburn",read) + +// If these defaults ever change, be sure to update constants in burn\engine\section.cpp as well. +#pragma data_seg(push, ".wixburn") +static DWORD dwMagic = 0x00f14300; +static DWORD dwVersion = 0x00000002; + +static GUID guidBundleId = { }; + +static DWORD dwStubSize = 0; +static DWORD dwOriginalChecksum = 0; +static DWORD dwOriginalSignatureOffset = 0; +static DWORD dwOriginalSignatureSize = 0; + +static DWORD dwContainerFormat = 1; +static DWORD dwContainerCount = 0; +static DWORD qwBootstrapperApplicationContainerSize = 0; +static DWORD qwAttachedContainerSize = 0; +#pragma data_seg(pop) diff --git a/src/burn/stub/WixToolset.Burn.props b/src/burn/stub/WixToolset.Burn.props new file mode 100644 index 00000000..38cd333e --- /dev/null +++ b/src/burn/stub/WixToolset.Burn.props @@ -0,0 +1,13 @@ + + + + + + + + %(RecursiveDir)%(FileName)%(Extension) + False + PreserveNewest + + + diff --git a/src/burn/stub/packages.config b/src/burn/stub/packages.config new file mode 100644 index 00000000..a98c0c8e --- /dev/null +++ b/src/burn/stub/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/burn/stub/precomp.cpp b/src/burn/stub/precomp.cpp new file mode 100644 index 00000000..37664a1c --- /dev/null +++ b/src/burn/stub/precomp.cpp @@ -0,0 +1,3 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" diff --git a/src/burn/stub/precomp.h b/src/burn/stub/precomp.h new file mode 100644 index 00000000..bb7ded9c --- /dev/null +++ b/src/burn/stub/precomp.h @@ -0,0 +1,17 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "engine.h" diff --git a/src/burn/stub/stub.cpp b/src/burn/stub/stub.cpp new file mode 100644 index 00000000..0cb202e0 --- /dev/null +++ b/src/burn/stub/stub.cpp @@ -0,0 +1,106 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +static void CALLBACK BurnTraceError( + __in_z LPCSTR szFile, + __in int iLine, + __in REPORT_LEVEL rl, + __in UINT source, + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ); + +int WINAPI wWinMain( + __in HINSTANCE hInstance, + __in_opt HINSTANCE /* hPrevInstance */, + __in_z_opt LPWSTR lpCmdLine, + __in int nCmdShow + ) +{ + HRESULT hr = S_OK; + DWORD dwExitCode = 0; + LPWSTR sczPath = NULL; + HANDLE hEngineFile = INVALID_HANDLE_VALUE; + + LPCWSTR rgsczSafelyLoadSystemDlls[] = + { + L"cabinet.dll", // required by Burn. + L"msi.dll", // required by Burn. + L"version.dll", // required by Burn. + L"wininet.dll", // required by Burn. + + L"comres.dll", // required by CLSIDFromProgID() when loading clbcatq.dll. + L"clbcatq.dll", // required by CLSIDFromProgID() when loading msxml?.dll. + + L"msasn1.dll", // required by DecryptFile() when loading crypt32.dll. + L"crypt32.dll", // required by DecryptFile() when loading feclient.dll. + L"feclient.dll", // unsafely loaded by DecryptFile(). + }; + + DutilInitialize(&BurnTraceError); + + // Best effort attempt to get our file handle as soon as possible. + hr = PathForCurrentProcess(&sczPath, NULL); + if (SUCCEEDED(hr)) + { + hEngineFile = ::CreateFileW(sczPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + } + + // If the engine is in the clean room, we'll do the unsafe initialization + // because some systems in Windows (namely GDI+) will fail when run in + // a process that protects against DLL hijacking. Since we know the clean + // room is in a clean folder and not subject to DLL hijacking we won't + // make ourselves perfectly secure so that we can load BAs that still + // depend on those parts of Windows that are insecure to DLL hijacking. + if (EngineInCleanRoom(lpCmdLine)) + { + AppInitializeUnsafe(); + } + else + { + AppInitialize(rgsczSafelyLoadSystemDlls, countof(rgsczSafelyLoadSystemDlls)); + } + + // call run + hr = EngineRun(hInstance, hEngineFile, lpCmdLine, nCmdShow, &dwExitCode); + ExitOnFailure(hr, "Failed to run application."); + +LExit: + ReleaseFileHandle(hEngineFile); + ReleaseStr(sczPath); + + DutilUninitialize(); + + return FAILED(hr) ? (int)hr : (int)dwExitCode; +} + +static void CALLBACK BurnTraceError( + __in_z LPCSTR /*szFile*/, + __in int /*iLine*/, + __in REPORT_LEVEL /*rl*/, + __in UINT source, + __in HRESULT hrError, + __in_z __format_string LPCSTR szFormat, + __in va_list args + ) +{ + BOOL fLog = FALSE; + + switch (source) + { + case DUTIL_SOURCE_DEFAULT: + fLog = TRUE; + break; + default: + fLog = REPORT_VERBOSE < LogGetLevel(); + break; + } + + if (fLog) + { + LogErrorStringArgs(hrError, szFormat, args); + } +} diff --git a/src/burn/stub/stub.ico b/src/burn/stub/stub.ico new file mode 100644 index 00000000..c2e2717c Binary files /dev/null and b/src/burn/stub/stub.ico differ diff --git a/src/burn/stub/stub.nuspec b/src/burn/stub/stub.nuspec new file mode 100644 index 00000000..968feff3 --- /dev/null +++ b/src/burn/stub/stub.nuspec @@ -0,0 +1,25 @@ + + + + $id$ + $version$ + $title$ + $description$ + $authors$ + MS-RL + false + $copyright$ + $projectUrl$ + + + + + + + + + + + + + diff --git a/src/burn/stub/stub.rc b/src/burn/stub/stub.rc new file mode 100644 index 00000000..80e1aac4 --- /dev/null +++ b/src/burn/stub/stub.rc @@ -0,0 +1,3 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +1 ICON "stub.ico" diff --git a/src/burn/stub/stub.vcxproj b/src/burn/stub/stub.vcxproj new file mode 100644 index 00000000..97972848 --- /dev/null +++ b/src/burn/stub/stub.vcxproj @@ -0,0 +1,120 @@ + + + + + + + + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + Debug + ARM64 + + + Release + ARM64 + + + + + {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1} + Application + Windows + burn + v142 + Unicode + false + Native component of WixToolset.Burn + + 1033 + Burn + WixToolset.Burn + false + + + + + + + + + + + + + + + + + $(ProjectDir)..\engine\inc + cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;wuguid.lib;engine.res + + + + + true + true + cabinet.dll;crypt32.dll;msi.dll;shlwapi.dll;version.dll;wininet.dll + + + + + + + + + Create + + + + + false + + + + + + + + + {8119537D-E1D9-6591-D51A-49768A2F9C37} + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}. + + + + + + + + + + + diff --git a/src/burn/test/BurnUnitTest/AssemblyInfo.cpp b/src/burn/test/BurnUnitTest/AssemblyInfo.cpp new file mode 100644 index 00000000..0282b1b7 --- /dev/null +++ b/src/burn/test/BurnUnitTest/AssemblyInfo.cpp @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System::Reflection; +using namespace System::Runtime::CompilerServices; +using namespace System::Runtime::InteropServices; + +[assembly: AssemblyTitleAttribute("Windows Installer XML Burn unit tests")]; +[assembly: AssemblyDescriptionAttribute("Burn unit tests")]; +[assembly: AssemblyCultureAttribute("")]; +[assembly: ComVisible(false)]; diff --git a/src/burn/test/BurnUnitTest/BurnTestException.h b/src/burn/test/BurnUnitTest/BurnTestException.h new file mode 100644 index 00000000..bd94b4fc --- /dev/null +++ b/src/burn/test/BurnUnitTest/BurnTestException.h @@ -0,0 +1,93 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + + public ref struct BurnTestException : public System::Exception + { + public: + BurnTestException(HRESULT error) + { + this->HResult = error; + } + + BurnTestException(HRESULT error, String^ message) + : Exception(message) + { + this->HResult = error; + } + + property Int32 ErrorCode + { + Int32 get() + { + return this->HResult; + } + } + + }; +} +} +} +} +} + +// this class is used by __TestThrowOnFailure_Format() below to deallocate +// the string created after the function call has returned +class __TestThrowOnFailure_StringFree +{ + LPWSTR m_scz; + +public: + __TestThrowOnFailure_StringFree(LPWSTR scz) + { + m_scz = scz; + } + + ~__TestThrowOnFailure_StringFree() + { + ReleaseStr(m_scz); + } + + operator LPCWSTR() + { + return m_scz; + } +}; + +// used by the TestThrowOnFailure macros to format the error string and +// return an LPCWSTR that can be used to initialize a System::String +#pragma warning (push) +#pragma warning (disable : 4793) +inline __TestThrowOnFailure_StringFree __TestThrowOnFailure_Format(LPCWSTR wzFormat, ...) +{ + Assert(wzFormat && *wzFormat); + + HRESULT hr = S_OK; + LPWSTR scz = NULL; + va_list args; + + va_start(args, wzFormat); + hr = StrAllocFormattedArgs(&scz, wzFormat, args); + va_end(args); + ExitOnFailure(hr, "Failed to format message string."); + +LExit: + return scz; +} +#pragma warning (pop) + +#define TestThrowOnFailure(hr, s) if (FAILED(hr)) { throw gcnew Microsoft::Tools::WindowsInstallerXml::Test::Bootstrapper::BurnTestException(hr, gcnew System::String(s)); } +#define TestThrowOnFailure1(hr, s, p) if (FAILED(hr)) { throw gcnew Microsoft::Tools::WindowsInstallerXml::Test::Bootstrapper::BurnTestException(hr, gcnew System::String(__TestThrowOnFailure_Format(s, p))); } +#define TestThrowOnFailure2(hr, s, p1, p2) if (FAILED(hr)) { throw gcnew Microsoft::Tools::WindowsInstallerXml::Test::Bootstrapper::BurnTestException(hr, gcnew System::String(__TestThrowOnFailure_Format(s, p1, p2))); } diff --git a/src/burn/test/BurnUnitTest/BurnTestFixture.h b/src/burn/test/BurnUnitTest/BurnTestFixture.h new file mode 100644 index 00000000..103972ef --- /dev/null +++ b/src/burn/test/BurnUnitTest/BurnTestFixture.h @@ -0,0 +1,75 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace WixBuildTools::TestSupport; + + public ref class BurnTestFixture : IDisposable + { + public: + BurnTestFixture() + { + HRESULT hr = XmlInitialize(); + TestThrowOnFailure(hr, L"Failed to initialize XML support."); + + hr = RegInitialize(); + TestThrowOnFailure(hr, L"Failed to initialize Regutil."); + + hr = CrypInitialize(); + TestThrowOnFailure(hr, L"Failed to initialize Cryputil."); + + PlatformInitialize(); + + this->testDirectory = WixBuildTools::TestSupport::TestData::Get(); + + LogInitialize(::GetModuleHandleW(NULL)); + + LogSetLevel(REPORT_DEBUG, FALSE); + + hr = LogOpen(NULL, L"BurnUnitTest", NULL, L"txt", FALSE, FALSE, NULL); + TestThrowOnFailure(hr, L"Failed to open log."); + } + + ~BurnTestFixture() + { + CrypUninitialize(); + XmlUninitialize(); + RegUninitialize(); + LogUninitialize(FALSE); + } + + property String^ DataDirectory + { + String^ get() + { + return this->testDirectory; + } + } + + property String^ TestDirectory + { + String^ get() + { + return this->testDirectory; + } + } + + private: + String^ testDirectory; + }; +} +} +} +} +} diff --git a/src/burn/test/BurnUnitTest/BurnUnitTest.h b/src/burn/test/BurnUnitTest/BurnUnitTest.h new file mode 100644 index 00000000..ed1d2956 --- /dev/null +++ b/src/burn/test/BurnUnitTest/BurnUnitTest.h @@ -0,0 +1,48 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace Xunit; + + [CollectionDefinition("Burn")] + public ref class BurnCollectionDefinition : ICollectionFixture + { + + }; + + [Collection("Burn")] + public ref class BurnUnitTest + { + public: + BurnUnitTest(BurnTestFixture^ fixture) + { + this->testContext = fixture; + } + + property BurnTestFixture^ TestContext + { + BurnTestFixture^ get() + { + return this->testContext; + } + } + + private: + BurnTestFixture^ testContext; + }; +} +} +} +} +} diff --git a/src/burn/test/BurnUnitTest/BurnUnitTest.rc b/src/burn/test/BurnUnitTest/BurnUnitTest.rc new file mode 100644 index 00000000..3a815db2 --- /dev/null +++ b/src/burn/test/BurnUnitTest/BurnUnitTest.rc @@ -0,0 +1,6 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#define VER_APP +#define VER_ORIGINAL_FILENAME "BurnUnitTest.dll" +#define VER_INTERNAL_NAME "setup" +#define VER_FILE_DESCRIPTION "WiX Toolset Bootstrapper unit tests" diff --git a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj new file mode 100644 index 00000000..33c8ed6c --- /dev/null +++ b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -0,0 +1,109 @@ + + + + + + + + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + + + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942} + {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67} + UnitTest + ManagedCProj + DynamicLibrary + Unicode + true + false + + + + + + + $(ProjectDir)..\..\..\..\balutil\src\WixToolset.BootstrapperCore.Native\inc + $(ProjectAdditionalIncludeDirectories);..\..\engine + cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib + + + + + + + + + + + Create + + 4564;4691 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ..\..\..\packages\WixBuildTools.TestSupport.4.0.50\lib\net472\WixBuildTools.TestSupport.dll + + + ..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.50\lib\net472\WixBuildTools.TestSupport.Native.dll + + + + + {8119537D-E1D9-6591-D51A-49770A2F9C37} + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + diff --git a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters new file mode 100644 index 00000000..f9461f53 --- /dev/null +++ b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters @@ -0,0 +1,80 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/burn/test/BurnUnitTest/CacheTest.cpp b/src/burn/test/BurnUnitTest/CacheTest.cpp new file mode 100644 index 00000000..d0cc237f --- /dev/null +++ b/src/burn/test/BurnUnitTest/CacheTest.cpp @@ -0,0 +1,119 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static HRESULT CALLBACK CacheTestEventRoutine( + __in BURN_CACHE_MESSAGE* pMessage, + __in LPVOID pvContext + ); + +static DWORD CALLBACK CacheTestProgressRoutine( + __in LARGE_INTEGER TotalFileSize, + __in LARGE_INTEGER TotalBytesTransferred, + __in LARGE_INTEGER StreamSize, + __in LARGE_INTEGER StreamBytesTransferred, + __in DWORD dwStreamNumber, + __in DWORD dwCallbackReason, + __in HANDLE hSourceFile, + __in HANDLE hDestinationFile, + __in_opt LPVOID lpData + ); + +typedef struct _CACHE_TEST_CONTEXT +{ +} CACHE_TEST_CONTEXT; + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace System::IO; + using namespace Xunit; + + public ref class CacheTest : BurnUnitTest + { + public: + CacheTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] + void CacheSignatureTest() + { + HRESULT hr = S_OK; + BURN_PACKAGE package = { }; + BURN_PAYLOAD payload = { }; + LPWSTR sczPayloadPath = NULL; + BYTE* pb = NULL; + DWORD cb = NULL; + CACHE_TEST_CONTEXT context = { }; + + try + { + pin_ptr dataDirectory = PtrToStringChars(this->TestContext->TestDirectory); + hr = PathConcat(dataDirectory, L"TestData\\CacheTest\\CacheSignatureTest.File", &sczPayloadPath); + Assert::True(S_OK == hr, "Failed to get path to test file."); + Assert::True(FileExistsEx(sczPayloadPath, NULL), "Test file does not exist."); + + hr = StrAllocHexDecode(L"25e61cd83485062b70713aebddd3fe4992826cb121466fddc8de3eacb1e42f39d4bdd8455d95eec8c9529ced4c0296ab861931fe2c86df2f2b4e8d259a6d9223", &pb, &cb); + Assert::Equal(S_OK, hr); + + package.fPerMachine = FALSE; + package.sczCacheId = L"Bootstrapper.CacheTest.CacheSignatureTest"; + payload.sczKey = L"CacheSignatureTest.PayloadKey"; + payload.sczFilePath = L"CacheSignatureTest.File"; + payload.pbHash = pb; + payload.cbHash = cb; + + hr = CacheCompletePayload(package.fPerMachine, &payload, package.sczCacheId, sczPayloadPath, FALSE, CacheTestEventRoutine, CacheTestProgressRoutine, &context); + Assert::Equal(S_OK, hr); + } + finally + { + ReleaseMem(pb); + ReleaseStr(sczPayloadPath); + + String^ filePath = Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), "Package Cache\\Bootstrapper.CacheTest.CacheSignatureTest\\CacheSignatureTest.File"); + if (File::Exists(filePath)) + { + File::SetAttributes(filePath, FileAttributes::Normal); + File::Delete(filePath); + } + } + } + }; +} +} +} +} +} + +static HRESULT CALLBACK CacheTestEventRoutine( + __in BURN_CACHE_MESSAGE* /*pMessage*/, + __in LPVOID /*pvContext*/ + ) +{ + return S_OK; +} + +static DWORD CALLBACK CacheTestProgressRoutine( + __in LARGE_INTEGER /*TotalFileSize*/, + __in LARGE_INTEGER /*TotalBytesTransferred*/, + __in LARGE_INTEGER /*StreamSize*/, + __in LARGE_INTEGER /*StreamBytesTransferred*/, + __in DWORD /*dwStreamNumber*/, + __in DWORD /*dwCallbackReason*/, + __in HANDLE /*hSourceFile*/, + __in HANDLE /*hDestinationFile*/, + __in_opt LPVOID /*lpData*/ + ) +{ + return PROGRESS_QUIET; +} diff --git a/src/burn/test/BurnUnitTest/ElevationTest.cpp b/src/burn/test/BurnUnitTest/ElevationTest.cpp new file mode 100644 index 00000000..3d144128 --- /dev/null +++ b/src/burn/test/BurnUnitTest/ElevationTest.cpp @@ -0,0 +1,221 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +const DWORD TEST_CHILD_SENT_MESSAGE_ID = 0xFFFE; +const DWORD TEST_PARENT_SENT_MESSAGE_ID = 0xFFFF; +const HRESULT S_TEST_SUCCEEDED = 0x3133; +const char TEST_MESSAGE_DATA[] = "{94949868-7EAE-4ac5-BEAC-AFCA2821DE01}"; + + +static BOOL STDAPICALLTYPE ElevateTest_ShellExecuteExW( + __inout LPSHELLEXECUTEINFOW lpExecInfo + ); +static DWORD CALLBACK ElevateTest_ThreadProc( + __in LPVOID lpThreadParameter + ); +static HRESULT ProcessParentMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); +static HRESULT ProcessChildMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace System::IO; + using namespace System::Threading; + using namespace Xunit; + + public ref class ElevationTest : BurnUnitTest + { + public: + ElevationTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] + void ElevateTest() + { + HRESULT hr = S_OK; + BURN_PIPE_CONNECTION connection = { }; + HANDLE hEvent = NULL; + DWORD dwResult = S_OK; + try + { + ShelFunctionOverride(ElevateTest_ShellExecuteExW); + + PipeConnectionInitialize(&connection); + + // + // per-user side setup + // + hr = PipeCreateNameAndSecret(&connection.sczName, &connection.sczSecret); + TestThrowOnFailure(hr, L"Failed to create connection name and secret."); + + hr = PipeCreatePipes(&connection, TRUE, &hEvent); + TestThrowOnFailure(hr, L"Failed to create pipes."); + + hr = PipeLaunchChildProcess(L"tests\\ignore\\this\\path\\to\\burn.exe", &connection, TRUE, NULL); + TestThrowOnFailure(hr, L"Failed to create elevated process."); + + hr = PipeWaitForChildConnect(&connection); + TestThrowOnFailure(hr, L"Failed to wait for child process to connect."); + + // post execute message + hr = PipeSendMessage(connection.hPipe, TEST_PARENT_SENT_MESSAGE_ID, NULL, 0, ProcessParentMessages, NULL, &dwResult); + TestThrowOnFailure(hr, "Failed to post execute message to per-machine process."); + + // + // initiate termination + // + hr = PipeTerminateChildProcess(&connection, 666, FALSE); + TestThrowOnFailure(hr, L"Failed to terminate elevated process."); + + // check flags + Assert::Equal(S_TEST_SUCCEEDED, (HRESULT)dwResult); + } + finally + { + PipeConnectionUninitialize(&connection); + ReleaseHandle(hEvent); + } + } + }; +} +} +} +} +} + + +static BOOL STDAPICALLTYPE ElevateTest_ShellExecuteExW( + __inout LPSHELLEXECUTEINFOW lpExecInfo + ) +{ + HRESULT hr = S_OK; + LPWSTR scz = NULL; + + hr = StrAllocString(&scz, lpExecInfo->lpParameters, 0); + ExitOnFailure(hr, "Failed to copy arguments."); + + // Pretend this thread is the elevated process. + lpExecInfo->hProcess = ::CreateThread(NULL, 0, ElevateTest_ThreadProc, scz, 0, NULL); + ExitOnNullWithLastError(lpExecInfo->hProcess, hr, "Failed to create thread."); + scz = NULL; + +LExit: + ReleaseStr(scz); + + return SUCCEEDED(hr); +} + +static DWORD CALLBACK ElevateTest_ThreadProc( + __in LPVOID lpThreadParameter + ) +{ + HRESULT hr = S_OK; + LPWSTR sczArguments = (LPWSTR)lpThreadParameter; + BURN_PIPE_CONNECTION connection = { }; + BURN_PIPE_RESULT result = { }; + + PipeConnectionInitialize(&connection); + + StrAlloc(&connection.sczName, MAX_PATH); + StrAlloc(&connection.sczSecret, MAX_PATH); + + // parse command line arguments + if (3 != swscanf_s(sczArguments, L"-q -burn.elevated %s %s %u", connection.sczName, MAX_PATH, connection.sczSecret, MAX_PATH, &connection.dwProcessId)) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Failed to parse argument string."); + } + + // set up connection with per-user process + hr = PipeChildConnect(&connection, TRUE); + ExitOnFailure(hr, "Failed to connect to per-user process."); + + // pump messages + hr = PipePumpMessages(connection.hPipe, ProcessChildMessages, static_cast(connection.hPipe), &result); + ExitOnFailure(hr, "Failed while pumping messages in child 'process'."); + +LExit: + PipeConnectionUninitialize(&connection); + ReleaseStr(sczArguments); + + return FAILED(hr) ? (DWORD)hr : result.dwResult; +} + +static HRESULT ProcessParentMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID /*pvContext*/, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + HRESULT hrResult = E_INVALIDDATA; + + // Process the message. + switch (pMsg->dwMessage) + { + case TEST_CHILD_SENT_MESSAGE_ID: + if (sizeof(TEST_MESSAGE_DATA) == pMsg->cbData && 0 == memcmp(TEST_MESSAGE_DATA, pMsg->pvData, sizeof(TEST_MESSAGE_DATA))) + { + hrResult = S_TEST_SUCCEEDED; + } + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Unexpected elevated message sent to parent process, msg: %u", pMsg->dwMessage); + } + + *pdwResult = static_cast(hrResult); + +LExit: + return hr; +} + +static HRESULT ProcessChildMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + HANDLE hPipe = static_cast(pvContext); + DWORD dwResult = 0; + + // Process the message. + switch (pMsg->dwMessage) + { + case TEST_PARENT_SENT_MESSAGE_ID: + // send test message + hr = PipeSendMessage(hPipe, TEST_CHILD_SENT_MESSAGE_ID, (LPVOID)TEST_MESSAGE_DATA, sizeof(TEST_MESSAGE_DATA), NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send message to per-machine process."); + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Unexpected elevated message sent to child process, msg: %u", pMsg->dwMessage); + } + + *pdwResult = dwResult; + +LExit: + return hr; +} diff --git a/src/burn/test/BurnUnitTest/ManifestHelpers.cpp b/src/burn/test/BurnUnitTest/ManifestHelpers.cpp new file mode 100644 index 00000000..96d5fab4 --- /dev/null +++ b/src/burn/test/BurnUnitTest/ManifestHelpers.cpp @@ -0,0 +1,41 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +using namespace System; +using namespace Xunit; + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + void LoadBundleXmlHelper(LPCWSTR wzDocument, IXMLDOMElement** ppixeBundle) + { + HRESULT hr = S_OK; + IXMLDOMDocument* pixdDocument = NULL; + try + { + hr = XmlLoadDocument(wzDocument, &pixdDocument); + TestThrowOnFailure(hr, L"Failed to load XML document."); + + hr = pixdDocument->get_documentElement(ppixeBundle); + TestThrowOnFailure(hr, L"Failed to get bundle element."); + } + finally + { + ReleaseObject(pixdDocument); + } + } +} +} +} +} +} diff --git a/src/burn/test/BurnUnitTest/ManifestHelpers.h b/src/burn/test/BurnUnitTest/ManifestHelpers.h new file mode 100644 index 00000000..e3e57555 --- /dev/null +++ b/src/burn/test/BurnUnitTest/ManifestHelpers.h @@ -0,0 +1,24 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + + +void LoadBundleXmlHelper(LPCWSTR wzDocument, IXMLDOMElement** ppixeBundle); + + +} +} +} +} +} diff --git a/src/burn/test/BurnUnitTest/ManifestTest.cpp b/src/burn/test/BurnUnitTest/ManifestTest.cpp new file mode 100644 index 00000000..963be156 --- /dev/null +++ b/src/burn/test/BurnUnitTest/ManifestTest.cpp @@ -0,0 +1,62 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace Xunit; + + public ref class ManifestTest : BurnUnitTest + { + public: + ManifestTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] + void ManifestLoadXmlTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + try + { + LPCSTR szDocument = + "" + " " + " " + " " + " " + " "; + + hr = VariableInitialize(&engineState.variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // load manifest from XML + hr = ManifestLoadXmlFromBuffer((BYTE*)szDocument, lstrlenA(szDocument), &engineState); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // check variable values + Assert::True(VariableExistsHelper(&engineState.variables, L"Variable1")); + } + finally + { + //CoreUninitialize(&engineState); + } + } + }; +} +} +} +} +} diff --git a/src/burn/test/BurnUnitTest/PlanTest.cpp b/src/burn/test/BurnUnitTest/PlanTest.cpp new file mode 100644 index 00000000..a7c1d83c --- /dev/null +++ b/src/burn/test/BurnUnitTest/PlanTest.cpp @@ -0,0 +1,1473 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static HRESULT WINAPI PlanTestBAProc( + __in BOOTSTRAPPER_APPLICATION_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults, + __in_opt LPVOID pvContext + ); + +static LPCWSTR wzMsiTransactionManifestFileName = L"MsiTransaction_BundleAv1_manifest.xml"; +static LPCWSTR wzSingleMsiManifestFileName = L"BasicFunctionality_BundleA_manifest.xml"; +static LPCWSTR wzSlipstreamManifestFileName = L"Slipstream_BundleA_manifest.xml"; + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace Xunit; + + public ref class PlanTest : BurnUnitTest + { + public: + PlanTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] + void MsiTransactionInstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzMsiTransactionManifestFileName, pEngineState); + DetectPackagesAsAbsent(pEngineState); + DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"1.0.0.0"); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 9); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageB"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 14); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageC"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(107082ull, pPlan->qwEstimatedSize); + Assert::Equal(506145ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteBeginMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[5].syncpoint.hEvent); + dwExecuteCheckpointId += 1; // cache checkpoints + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[8].syncpoint.hEvent); + dwExecuteCheckpointId += 1; // cache checkpoints + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCommitMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageB"); + dwExecuteCheckpointId += 1; // cache checkpoints + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageC"); + dwExecuteCheckpointId += 1; // cache checkpoints + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(4ul, pPlan->cExecutePackagesTotal); + Assert::Equal(7ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(3ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + } + + [Fact] + void MsiTransactionUninstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzMsiTransactionManifestFileName, pEngineState); + DetectPackagesAsPresentAndCached(pEngineState); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(0ull, pPlan->qwEstimatedSize); + Assert::Equal(0ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteBeginMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCommitMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(3ul, pPlan->cExecutePackagesTotal); + Assert::Equal(3ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + ValidateCleanAction(pPlan, dwIndex++, L"PackageC"); + ValidateCleanAction(pPlan, dwIndex++, L"PackageB"); + ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{A497C5E5-C78B-4F0B-BF72-B33E1DB1C4B8}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{D1D01094-23CE-4AF0-84B6-4A1A133F21D3}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{01E6B748-7B95-4BA9-976D-B6F35076CEF4}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(3ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + } + + [Fact] + void RelatedBundleMissingFromCacheTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); + DetectAttachedContainerAsAttached(pEngineState); + DetectPackagesAsAbsent(pEngineState); + BURN_RELATED_BUNDLE* pRelatedBundle = DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"0.9.0.0"); + pRelatedBundle->fPlannable = FALSE; + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(35694ull, pPlan->qwEstimatedSize); + Assert::Equal(168715ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(1ul, pPlan->cExecutePackagesTotal); + Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(1ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + } + + [Fact] + void SingleMsiCacheTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); + DetectAttachedContainerAsAttached(pEngineState); + DetectPackagesAsAbsent(pEngineState); + DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"0.9.0.0"); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_CACHE); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_CACHE, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(33743ull, pPlan->qwEstimatedSize); + Assert::Equal(168715ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(0ul, pPlan->cExecutePackagesTotal); + Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(1ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + } + + [Fact] + void SingleMsiInstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); + DetectAttachedContainerAsAttached(pEngineState); + DetectPackagesAsAbsent(pEngineState); + DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"0.9.0.0"); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(35694ull, pPlan->qwEstimatedSize); + Assert::Equal(168715ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(2ul, pPlan->cExecutePackagesTotal); + Assert::Equal(3ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(1ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + } + + [Fact] + void SingleMsiInstalledWithNoInstalledPackagesModifyTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); + DetectPackagesAsAbsent(pEngineState); + + pEngineState->registration.fInstalled = TRUE; + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_MODIFY); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_MODIFY, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(0ull, pPlan->qwEstimatedSize); + Assert::Equal(0ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(0ul, pPlan->cExecutePackagesTotal); + Assert::Equal(0ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(1ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + } + + [Fact] + void SingleMsiUninstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); + DetectPackagesAsPresentAndCached(pEngineState); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(0ull, pPlan->qwEstimatedSize); + Assert::Equal(0ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(1ul, pPlan->cExecutePackagesTotal); + Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{64633047-D172-4BBB-B202-64337D15C952}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(1ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + } + + [Fact] + void SingleMsiUninstallTestFromUpgradeBundleWithSameExactPackage() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); + DetectAsRelatedUpgradeBundle(&engineState, L"{02940F3E-C83E-452D-BFCF-C943777ACEAE}", L"2.0.0.0"); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(0ull, pPlan->qwEstimatedSize); + Assert::Equal(0ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(0ul, pPlan->cExecutePackagesTotal); + Assert::Equal(0ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(1ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_IGNORED, BURN_PACKAGE_REGISTRATION_STATE_IGNORED); + } + + [Fact] + void SlipstreamInstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSlipstreamManifestFileName, pEngineState); + DetectPermanentPackagesAsPresentAndCached(pEngineState); + PlanTestDetectPatchInitialize(pEngineState); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PatchA"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 2); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 2); + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(3055111ull, pPlan->qwEstimatedSize); + Assert::Equal(212992ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 3; + BURN_EXECUTE_ACTION* pExecuteAction = NULL; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[5].syncpoint.hEvent); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_REGISTER); + pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_INSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE); + ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 3; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PatchA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE); + ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA"); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(2ul, pPlan->cExecutePackagesTotal); + Assert::Equal(4ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(3ul, pEngineState->packages.cPackages); + ValidatePermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"NetFx48Web"); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PatchA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + } + + [Fact] + void SlipstreamUninstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSlipstreamManifestFileName, pEngineState); + DetectPackagesAsPresentAndCached(pEngineState); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(0ull, pPlan->qwEstimatedSize); + Assert::Equal(0ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 1; + BURN_EXECUTE_ACTION* pExecuteAction = NULL; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_UNREGISTER); + pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE); + ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_INSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE); + ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(2ul, pPlan->cExecutePackagesTotal); + Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + ValidateCleanAction(pPlan, dwIndex++, L"PatchA"); + ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{0A5113E3-06A5-4CE0-8E83-9EB42F6764A6}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(3ul, pEngineState->packages.cPackages); + ValidatePermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"NetFx48Web"); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PatchA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + } + + private: + // This doesn't initialize everything, just enough for CorePlan to work. + void InitializeEngineStateForCorePlan(LPCWSTR wzManifestFileName, BURN_ENGINE_STATE* pEngineState) + { + HRESULT hr = S_OK; + LPWSTR sczFilePath = NULL; + + ::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive); + + hr = VariableInitialize(&pEngineState->variables); + NativeAssert::Succeeded(hr, "Failed to initialize variables."); + + try + { + pin_ptr dataDirectory = PtrToStringChars(this->TestContext->TestDirectory); + hr = PathConcat(dataDirectory, L"TestData\\PlanTest", &sczFilePath); + NativeAssert::Succeeded(hr, "Failed to get path to test file directory."); + hr = PathConcat(sczFilePath, wzManifestFileName, &sczFilePath); + NativeAssert::Succeeded(hr, "Failed to get path to test file."); + Assert::True(FileExistsEx(sczFilePath, NULL), "Test file does not exist."); + + hr = ManifestLoadXmlFromFile(sczFilePath, pEngineState); + NativeAssert::Succeeded(hr, "Failed to load manifest."); + } + finally + { + ReleaseStr(sczFilePath); + } + + hr = CoreInitializeConstants(pEngineState); + NativeAssert::Succeeded(hr, "Failed to initialize core constants"); + + pEngineState->userExperience.pfnBAProc = PlanTestBAProc; + } + + void PlanTestDetect(BURN_ENGINE_STATE* pEngineState) + { + HRESULT hr = S_OK; + BURN_REGISTRATION* pRegistration = &pEngineState->registration; + + DetectReset(pRegistration, &pEngineState->packages); + PlanReset(&pEngineState->plan, &pEngineState->containers, &pEngineState->packages, &pEngineState->layoutPayloads); + + hr = DepDependencyArrayAlloc(&pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies, pRegistration->sczProviderKey, NULL); + NativeAssert::Succeeded(hr, "Failed to add the bundle provider key to the list of dependencies to ignore."); + + pEngineState->userExperience.fEngineActive = TRUE; + pEngineState->fDetected = TRUE; + } + + void PlanTestDetectPatchInitialize(BURN_ENGINE_STATE* pEngineState) + { + HRESULT hr = MsiEngineDetectInitialize(&pEngineState->packages); + NativeAssert::Succeeded(hr, "MsiEngineDetectInitialize failed"); + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + + if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + j; + + if (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN == pTargetProduct->patchPackageState) + { + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + } + } + } + } + } + + void DetectAttachedContainerAsAttached(BURN_ENGINE_STATE* pEngineState) + { + for (DWORD i = 0; i < pEngineState->containers.cContainers; ++i) + { + BURN_CONTAINER* pContainer = pEngineState->containers.rgContainers + i; + if (pContainer->fAttached) + { + pContainer->fActuallyAttached = TRUE; + } + } + } + + void DetectPackageAsAbsent(BURN_PACKAGE* pPackage) + { + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + if (pPackage->fCanAffectRegistration) + { + pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + } + + void DetectPackageAsPresentAndCached(BURN_PACKAGE* pPackage) + { + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; + pPackage->fCached = TRUE; + if (pPackage->fCanAffectRegistration) + { + pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + } + + void DetectPackageDependent(BURN_PACKAGE* pPackage, LPCWSTR wzId) + { + HRESULT hr = S_OK; + + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + i; + + hr = DepDependencyArrayAlloc(&pProvider->rgDependents, &pProvider->cDependents, wzId, NULL); + NativeAssert::Succeeded(hr, "Failed to add package dependent"); + } + } + + void DetectPackagesAsAbsent(BURN_ENGINE_STATE* pEngineState) + { + PlanTestDetect(pEngineState); + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + DetectPackageAsAbsent(pPackage); + } + } + + void DetectPackagesAsPresentAndCached(BURN_ENGINE_STATE* pEngineState) + { + PlanTestDetect(pEngineState); + + pEngineState->registration.fInstalled = TRUE; + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + DetectPackageAsPresentAndCached(pPackage); + DetectPackageDependent(pPackage, pEngineState->registration.sczId); + + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + { + for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) + { + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; + + BURN_PACKAGE* pMspPackage = pPackage->Msi.rgSlipstreamMsps[j].pMspPackage; + MspEngineAddDetectedTargetProduct(&pEngineState->packages, pMspPackage, j, pPackage->Msi.sczProductCode, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED); + + BURN_MSPTARGETPRODUCT* pTargetProduct = pMspPackage->Msp.rgTargetProducts + (pMspPackage->Msp.cTargetProductCodes - 1); + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; + pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + } + } + } + + void DetectPermanentPackagesAsPresentAndCached(BURN_ENGINE_STATE* pEngineState) + { + PlanTestDetect(pEngineState); + + pEngineState->registration.fInstalled = TRUE; + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + if (pPackage->fUninstallable) + { + DetectPackageAsAbsent(pPackage); + } + else + { + DetectPackageAsPresentAndCached(pPackage); + DetectPackageDependent(pPackage, pEngineState->registration.sczId); + } + } + } + + BURN_RELATED_BUNDLE* DetectUpgradeBundle( + __in BURN_ENGINE_STATE* pEngineState, + __in LPCWSTR wzId, + __in LPCWSTR wzVersion + ) + { + HRESULT hr = S_OK; + BURN_RELATED_BUNDLES* pRelatedBundles = &pEngineState->registration.relatedBundles; + BURN_DEPENDENCY_PROVIDER dependencyProvider = { }; + + hr = StrAllocString(&dependencyProvider.sczKey, wzId, 0); + NativeAssert::Succeeded(hr, "Failed to copy provider key"); + + dependencyProvider.fImported = TRUE; + + hr = StrAllocString(&dependencyProvider.sczVersion, wzVersion, 0); + NativeAssert::Succeeded(hr, "Failed to copy version"); + + hr = MemEnsureArraySize(reinterpret_cast(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5); + NativeAssert::Succeeded(hr, "Failed to ensure there is space for related bundles."); + + BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles; + + hr = VerParseVersion(wzVersion, 0, FALSE, &pRelatedBundle->pVersion); + NativeAssert::Succeeded(hr, "Failed to parse pseudo bundle version: %ls", wzVersion); + + pRelatedBundle->fPlannable = TRUE; + pRelatedBundle->relationType = BOOTSTRAPPER_RELATION_UPGRADE; + + hr = PseudoBundleInitialize(0, &pRelatedBundle->package, TRUE, wzId, pRelatedBundle->relationType, BOOTSTRAPPER_PACKAGE_STATE_PRESENT, TRUE, NULL, NULL, NULL, 0, FALSE, L"-quiet", L"-repair -quiet", L"-uninstall -quiet", &dependencyProvider, NULL, 0); + NativeAssert::Succeeded(hr, "Failed to initialize related bundle to represent bundle: %ls", wzId); + + ++pRelatedBundles->cRelatedBundles; + + return pRelatedBundle; + } + + void DetectAsRelatedUpgradeBundle( + __in BURN_ENGINE_STATE* pEngineState, + __in LPCWSTR wzId, + __in LPCWSTR wzVersion + ) + { + HRESULT hr = StrAllocString(&pEngineState->registration.sczAncestors, wzId, 0); + NativeAssert::Succeeded(hr, "Failed to set registration's ancestors"); + + pEngineState->command.relationType = BOOTSTRAPPER_RELATION_UPGRADE; + + DetectPackagesAsPresentAndCached(pEngineState); + DetectUpgradeBundle(pEngineState, wzId, wzVersion); + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + DetectPackageDependent(pPackage, wzId); + } + } + + void ValidateCacheContainer( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzContainerId + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_CONTAINER, pAction->type); + NativeAssert::StringEqual(wzContainerId, pAction->container.pContainer->sczId); + } + + BURN_CACHE_ACTION* ValidateCacheActionExists(BURN_PLAN* pPlan, BOOL fRollback, DWORD dwIndex) + { + Assert::InRange(dwIndex + 1ul, 1ul, (fRollback ? pPlan->cRollbackCacheActions : pPlan->cCacheActions)); + return (fRollback ? pPlan->rgRollbackCacheActions : pPlan->rgCacheActions) + dwIndex; + } + + void ValidateCacheCheckpoint( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in DWORD dwId + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_CHECKPOINT, pAction->type); + Assert::Equal(dwId, pAction->checkpoint.dwId); + } + + DWORD ValidateCachePackage( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_PACKAGE, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->package.pPackage->sczId); + return dwIndex + 1; + } + + void ValidateCacheRollbackPackage( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->rollbackPackage.pPackage->sczId); + } + + void ValidateCacheSignalSyncpoint( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT, pAction->type); + Assert::NotEqual((DWORD_PTR)NULL, (DWORD_PTR)pAction->syncpoint.hEvent); + } + + void ValidateCleanAction( + __in BURN_PLAN* pPlan, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId + ) + { + Assert::InRange(dwIndex + 1ul, 1ul, pPlan->cCleanActions); + + BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + dwIndex; + Assert::NotEqual((DWORD_PTR)0, (DWORD_PTR)pCleanAction->pPackage); + NativeAssert::StringEqual(wzPackageId, pCleanAction->pPackage->sczId); + } + + BURN_EXECUTE_ACTION* ValidateExecuteActionExists(BURN_PLAN* pPlan, BOOL fRollback, DWORD dwIndex) + { + Assert::InRange(dwIndex + 1ul, 1ul, (fRollback ? pPlan->cRollbackActions : pPlan->cExecuteActions)); + return (fRollback ? pPlan->rgRollbackActions : pPlan->rgExecuteActions) + dwIndex; + } + + void ValidateExecuteBeginMsiTransaction( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzRollbackBoundaryId + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION, pAction->type); + NativeAssert::StringEqual(wzRollbackBoundaryId, pAction->msiTransaction.pRollbackBoundary->sczId); + Assert::Equal(FALSE, pAction->fDeleted); + } + + void ValidateExecuteCheckpoint( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in DWORD dwId + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_CHECKPOINT, pAction->type); + Assert::Equal(dwId, pAction->checkpoint.dwId); + Assert::Equal(FALSE, pAction->fDeleted); + } + + void ValidateExecuteCommitMsiTransaction( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzRollbackBoundaryId + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION, pAction->type); + NativeAssert::StringEqual(wzRollbackBoundaryId, pAction->msiTransaction.pRollbackBoundary->sczId); + Assert::Equal(FALSE, pAction->fDeleted); + } + + void ValidateExecuteExePackage( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId, + __in BOOTSTRAPPER_ACTION_STATE action, + __in LPCWSTR wzIgnoreDependencies + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->exePackage.pPackage->sczId); + Assert::Equal(action, pAction->exePackage.action); + NativeAssert::StringEqual(wzIgnoreDependencies, pAction->exePackage.sczIgnoreDependencies); + Assert::Equal(FALSE, pAction->fDeleted); + } + + void ValidateExecuteMsiPackage( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in_z LPCWSTR wzPackageId, + __in BOOTSTRAPPER_ACTION_STATE action, + __in BURN_MSI_PROPERTY actionMsiProperty, + __in DWORD uiLevel, + __in BOOL fDisableExternalUiHandler, + __in DWORD dwLoggingAttributes + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->msiPackage.pPackage->sczId); + Assert::Equal(action, pAction->msiPackage.action); + Assert::Equal(actionMsiProperty, pAction->msiPackage.actionMsiProperty); + Assert::Equal(uiLevel, pAction->msiPackage.uiLevel); + Assert::Equal(fDisableExternalUiHandler, pAction->msiPackage.fDisableExternalUiHandler); + NativeAssert::NotNull(pAction->msiPackage.sczLogPath); + Assert::Equal(dwLoggingAttributes, pAction->msiPackage.dwLoggingAttributes); + Assert::Equal(FALSE, pAction->fDeleted); + } + + BURN_EXECUTE_ACTION* ValidateDeletedExecuteMspTarget( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in_z LPCWSTR wzPackageId, + __in BOOTSTRAPPER_ACTION_STATE action, + __in_z LPCWSTR wzTargetProductCode, + __in BOOL fPerMachineTarget, + __in BURN_MSI_PROPERTY actionMsiProperty, + __in DWORD uiLevel, + __in BOOL fDisableExternalUiHandler, + __in BOOL fDeleted + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_MSP_TARGET, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->mspTarget.pPackage->sczId); + Assert::Equal(action, pAction->mspTarget.action); + NativeAssert::StringEqual(wzTargetProductCode, pAction->mspTarget.sczTargetProductCode); + Assert::Equal(fPerMachineTarget, pAction->mspTarget.fPerMachineTarget); + Assert::Equal(actionMsiProperty, pAction->mspTarget.actionMsiProperty); + Assert::Equal(uiLevel, pAction->mspTarget.uiLevel); + Assert::Equal(fDisableExternalUiHandler, pAction->mspTarget.fDisableExternalUiHandler); + NativeAssert::NotNull(pAction->mspTarget.sczLogPath); + Assert::Equal(fDeleted, pAction->fDeleted); + return pAction; + } + + void ValidateExecuteMspTargetPatch( + __in BURN_EXECUTE_ACTION* pAction, + __in DWORD dwIndex, + __in_z LPCWSTR wzPackageId + ) + { + Assert::InRange(dwIndex + 1ul, 1ul, pAction->mspTarget.cOrderedPatches); + BURN_ORDERED_PATCHES* pOrderedPatch = pAction->mspTarget.rgOrderedPatches + dwIndex; + NativeAssert::StringEqual(wzPackageId, pOrderedPatch->pPackage->sczId); + } + + void ValidateExecutePackageDependency( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId, + __in LPCWSTR wzBundleProviderKey, + __in BURN_DEPENDENCY_ACTION action + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->packageDependency.pPackage->sczId); + NativeAssert::StringEqual(wzBundleProviderKey, pAction->packageDependency.sczBundleProviderKey); + Assert::Equal(action, pAction->packageDependency.action); + Assert::Equal(FALSE, pAction->fDeleted); + } + + void ValidateExecutePackageProvider( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId, + __in BURN_DEPENDENCY_ACTION action + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->packageProvider.pPackage->sczId); + Assert::Equal(action, pAction->packageProvider.action); + Assert::Equal(FALSE, pAction->fDeleted); + } + + void ValidateExecuteRollbackBoundary( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzId, + __in BOOL fVital, + __in BOOL fTransaction + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY, pAction->type); + NativeAssert::StringEqual(wzId, pAction->rollbackBoundary.pRollbackBoundary->sczId); + Assert::Equal(fVital, pAction->rollbackBoundary.pRollbackBoundary->fVital); + Assert::Equal(fTransaction, pAction->rollbackBoundary.pRollbackBoundary->fTransaction); + Assert::Equal(FALSE, pAction->fDeleted); + } + + void ValidateExecuteUncachePackage( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->uncachePackage.pPackage->sczId); + Assert::Equal(FALSE, pAction->fDeleted); + } + + void ValidateExecuteWaitSyncpoint( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in HANDLE hEvent + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT, pAction->type); + Assert::Equal((DWORD_PTR)hEvent, (DWORD_PTR)pAction->syncpoint.hEvent); + Assert::Equal(FALSE, pAction->fDeleted); + } + + void ValidateNonPermanentPackageExpectedStates( + __in BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzPackageId, + __in BURN_PACKAGE_REGISTRATION_STATE expectedCacheState, + __in BURN_PACKAGE_REGISTRATION_STATE expectedInstallState + ) + { + NativeAssert::StringEqual(wzPackageId, pPackage->sczId); + Assert::Equal(TRUE, pPackage->fCanAffectRegistration); + Assert::Equal(expectedCacheState, pPackage->expectedCacheRegistrationState); + Assert::Equal(expectedInstallState, pPackage->expectedInstallRegistrationState); + } + + void ValidatePermanentPackageExpectedStates( + __in BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzPackageId + ) + { + NativeAssert::StringEqual(wzPackageId, pPackage->sczId); + Assert::Equal(FALSE, pPackage->fCanAffectRegistration); + Assert::Equal(BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, pPackage->expectedCacheRegistrationState); + Assert::Equal(BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, pPackage->expectedInstallRegistrationState); + } + + void ValidatePlannedProvider( + __in BURN_PLAN* pPlan, + __in UINT uIndex, + __in LPCWSTR wzKey, + __in LPCWSTR wzName + ) + { + Assert::InRange(uIndex + 1u, 1u, pPlan->cPlannedProviders); + + DEPENDENCY* pProvider = pPlan->rgPlannedProviders + uIndex; + NativeAssert::StringEqual(wzKey, pProvider->sczKey); + NativeAssert::StringEqual(wzName, pProvider->sczName); + } + }; +} +} +} +} +} + +static HRESULT WINAPI PlanTestBAProc( + __in BOOTSTRAPPER_APPLICATION_MESSAGE /*message*/, + __in const LPVOID /*pvArgs*/, + __inout LPVOID /*pvResults*/, + __in_opt LPVOID /*pvContext*/ + ) +{ + return S_OK; +} diff --git a/src/burn/test/BurnUnitTest/RegistrationTest.cpp b/src/burn/test/BurnUnitTest/RegistrationTest.cpp new file mode 100644 index 00000000..7b126f61 --- /dev/null +++ b/src/burn/test/BurnUnitTest/RegistrationTest.cpp @@ -0,0 +1,772 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +#define ROOT_PATH L"SOFTWARE\\WiX_Burn_UnitTest" +#define HKLM_PATH L"SOFTWARE\\WiX_Burn_UnitTest\\HKLM" +#define HKCU_PATH L"SOFTWARE\\WiX_Burn_UnitTest\\HKCU" +#define REGISTRY_UNINSTALL_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall" +#define REGISTRY_RUN_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce" + +#define TEST_UNINSTALL_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_UNINSTALL_KEY L"\\{D54F896D-1952-43e6-9C67-B5652240618C}" +#define TEST_RUN_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_RUN_KEY + + +static LSTATUS APIENTRY RegistrationTest_RegCreateKeyExW( + __in HKEY hKey, + __in LPCWSTR lpSubKey, + __reserved DWORD Reserved, + __in_opt LPWSTR lpClass, + __in DWORD dwOptions, + __in REGSAM samDesired, + __in_opt CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes, + __out PHKEY phkResult, + __out_opt LPDWORD lpdwDisposition + ); +static LSTATUS APIENTRY RegistrationTest_RegOpenKeyExW( + __in HKEY hKey, + __in_opt LPCWSTR lpSubKey, + __reserved DWORD ulOptions, + __in REGSAM samDesired, + __out PHKEY phkResult + ); +static LSTATUS APIENTRY RegistrationTest_RegDeleteKeyExW( + __in HKEY hKey, + __in LPCWSTR lpSubKey, + __in REGSAM samDesired, + __reserved DWORD Reserved + ); + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace Microsoft::Win32; + using namespace System; + using namespace System::IO; + using namespace Xunit; + + public ref class RegistrationTest : BurnUnitTest + { + public: + RegistrationTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] + void RegisterBasicTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + LPWSTR sczCurrentProcess = NULL; + BURN_VARIABLES variables = { }; + BURN_USER_EXPERIENCE userExperience = { }; + BOOTSTRAPPER_COMMAND command = { }; + BURN_REGISTRATION registration = { }; + BURN_LOGGING logging = { }; + BURN_PACKAGES packages = { }; + String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); + + try + { + // set mock API's + RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); + + Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH)); + + logging.sczPath = L"BurnUnitTest.txt"; + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + hr = UserExperienceParseFromXml(&userExperience, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse UX from XML."); + + hr = RegistrationParseFromXml(®istration, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse registration from XML."); + + hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging); + TestThrowOnFailure(hr, L"Failed to set registration resume command."); + + hr = PathForCurrentProcess(&sczCurrentProcess, NULL); + TestThrowOnFailure(hr, L"Failed to get current process path."); + + // write registration + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE | BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + // verify that registration was created + Assert::True(Directory::Exists(cacheDirectory)); + Assert::True(File::Exists(Path::Combine(cacheDirectory, gcnew String(L"setup.exe")))); + + Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)(Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr))); + + // end session + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + TestThrowOnFailure(hr, L"Failed to unregister bundle."); + + // verify that registration was removed + Assert::False(Directory::Exists(cacheDirectory)); + + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + } + finally + { + ReleaseStr(sczCurrentProcess); + ReleaseObject(pixeBundle); + UserExperienceUninitialize(&userExperience); + RegistrationUninitialize(®istration); + VariablesUninitialize(&variables); + + Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH)); + if (Directory::Exists(cacheDirectory)) + { + Directory::Delete(cacheDirectory, true); + } + + RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + } + } + + [Fact] + void RegisterArpMinimumTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + LPWSTR sczCurrentProcess = NULL; + BURN_VARIABLES variables = { }; + BURN_USER_EXPERIENCE userExperience = { }; + BOOTSTRAPPER_COMMAND command = { }; + BURN_REGISTRATION registration = { }; + BURN_LOGGING logging = { }; + BURN_PACKAGES packages = { }; + String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); + try + { + // set mock API's + RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); + + Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH)); + + logging.sczPath = L"BurnUnitTest.txt"; + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + hr = UserExperienceParseFromXml(&userExperience, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse UX from XML."); + + hr = RegistrationParseFromXml(®istration, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse registration from XML."); + + hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging); + TestThrowOnFailure(hr, L"Failed to set registration resume command."); + + hr = PathForCurrentProcess(&sczCurrentProcess, NULL); + TestThrowOnFailure(hr, L"Failed to get current process path."); + + // + // install + // + + // write registration + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + // verify that registration was created + Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // complete registration + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); + TestThrowOnFailure(hr, L"Failed to unregister bundle."); + + // verify that registration was updated + Assert::Equal(Int32(BURN_RESUME_MODE_ARP), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // + // uninstall + // + + // write registration + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, 0); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + // verify that registration was updated + Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // delete registration + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + TestThrowOnFailure(hr, L"Failed to unregister bundle."); + + // verify that registration was removed + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + } + finally + { + ReleaseStr(sczCurrentProcess); + ReleaseObject(pixeBundle); + UserExperienceUninitialize(&userExperience); + RegistrationUninitialize(®istration); + VariablesUninitialize(&variables); + + Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH)); + if (Directory::Exists(cacheDirectory)) + { + Directory::Delete(cacheDirectory, true); + } + + RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + } + } + + [Fact] + void RegisterVariablesTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + LPWSTR sczCurrentProcess = NULL; + BURN_VARIABLES variables = { }; + BURN_USER_EXPERIENCE userExperience = { }; + BOOTSTRAPPER_COMMAND command = { }; + BURN_REGISTRATION registration = { }; + BURN_LOGGING logging = { }; + BURN_PACKAGES packages = { }; + String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); + try + { + // set mock API's + RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); + + Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH)); + + logging.sczPath = L"BurnUnitTest.txt"; + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + hr = UserExperienceParseFromXml(&userExperience, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse UX from XML."); + + hr = RegistrationParseFromXml(®istration, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse registration from XML."); + + hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging); + TestThrowOnFailure(hr, L"Failed to set registration resume command."); + + hr = PathForCurrentProcess(&sczCurrentProcess, NULL); + TestThrowOnFailure(hr, L"Failed to get current process path."); + + // + // install + // + + // write registration + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + // verify that registration was created + Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // complete registration + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_REQUIRED, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); + TestThrowOnFailure(hr, L"Failed to unregister bundle."); + + // verify that registration variables were updated + registration.fInstalled = TRUE; + + hr = RegistrationSetVariables(®istration, &variables); + TestThrowOnFailure(hr, L"Failed to set registration variables."); + + Assert::Equal(1ll, VariableGetNumericHelper(&variables, BURN_BUNDLE_INSTALLED)); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, BURN_REBOOT_PENDING)); + Assert::Equal(gcnew String(L"foo"), VariableGetStringHelper(&variables, BURN_BUNDLE_TAG)); + Assert::Equal(gcnew String(L"bar"), VariableGetStringHelper(&variables, BURN_BUNDLE_PROVIDER_KEY)); + Assert::Equal(gcnew String(L"1.0.0.0"), VariableGetVersionHelper(&variables, BURN_BUNDLE_VERSION)); + + // + // uninstall + // + + // delete registration + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + TestThrowOnFailure(hr, L"Failed to unregister bundle."); + + // verify that registration was removed + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + } + finally + { + ReleaseStr(sczCurrentProcess); + ReleaseObject(pixeBundle); + UserExperienceUninitialize(&userExperience); + RegistrationUninitialize(®istration); + VariablesUninitialize(&variables); + + Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH)); + if (Directory::Exists(cacheDirectory)) + { + Directory::Delete(cacheDirectory, true); + } + + RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + } + } + + [Fact] + void RegisterArpFullTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + LPWSTR sczCurrentProcess = NULL; + BURN_VARIABLES variables = { }; + BURN_USER_EXPERIENCE userExperience = { }; + BOOTSTRAPPER_COMMAND command = { }; + BURN_REGISTRATION registration = { }; + BURN_LOGGING logging = { }; + BURN_PACKAGES packages = { }; + String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); + try + { + // set mock API's + RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); + + Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH)); + + logging.sczPath = L"BurnUnitTest.txt"; + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + hr = UserExperienceParseFromXml(&userExperience, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse UX from XML."); + + hr = RegistrationParseFromXml(®istration, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse registration from XML."); + + hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging); + TestThrowOnFailure(hr, L"Failed to set registration resume command."); + + hr = PathForCurrentProcess(&sczCurrentProcess, NULL); + TestThrowOnFailure(hr, L"Failed to get current process path."); + + // + // install + // + + // write registration + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + // verify that registration was created + Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // finish registration + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + // verify that registration was updated + Assert::Equal(Int32(BURN_RESUME_MODE_ARP), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + Assert::Equal(gcnew String(L"DisplayName1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"DisplayName"), nullptr)); + Assert::Equal(gcnew String(L"1.2.3.4"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"DisplayVersion"), nullptr)); + Assert::Equal(gcnew String(L"Publisher1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Publisher"), nullptr)); + Assert::Equal(gcnew String(L"http://www.microsoft.com/help"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"HelpLink"), nullptr)); + Assert::Equal(gcnew String(L"555-555-5555"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"HelpTelephone"), nullptr)); + Assert::Equal(gcnew String(L"http://www.microsoft.com/about"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"URLInfoAbout"), nullptr)); + Assert::Equal(gcnew String(L"http://www.microsoft.com/update"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"URLUpdateInfo"), nullptr)); + Assert::Equal(gcnew String(L"Comments1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Comments"), nullptr)); + Assert::Equal(gcnew String(L"Contact1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Contact"), nullptr)); + Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"NoModify"), nullptr)); + Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"NoRemove"), nullptr)); + + // + // uninstall + // + + // write registration + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, 0); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + // verify that registration was updated + Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // delete registration + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + TestThrowOnFailure(hr, L"Failed to unregister bundle."); + + // verify that registration was removed + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + } + finally + { + ReleaseStr(sczCurrentProcess); + ReleaseObject(pixeBundle); + UserExperienceUninitialize(&userExperience); + RegistrationUninitialize(®istration); + VariablesUninitialize(&variables); + + Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH)); + if (Directory::Exists(cacheDirectory)) + { + Directory::Delete(cacheDirectory, true); + } + + RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + } + } + + [Fact(Skip = "Currently fails")] + void ResumeTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + LPWSTR sczCurrentProcess = NULL; + BURN_VARIABLES variables = { }; + BURN_USER_EXPERIENCE userExperience = { }; + BOOTSTRAPPER_COMMAND command = { }; + BURN_REGISTRATION registration = { }; + BURN_LOGGING logging = { }; + BURN_PACKAGES packages = { }; + BYTE rgbData[256] = { }; + BOOTSTRAPPER_RESUME_TYPE resumeType = BOOTSTRAPPER_RESUME_TYPE_NONE; + BYTE* pbBuffer = NULL; + SIZE_T cbBuffer = 0; + String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); + try + { + for (DWORD i = 0; i < 256; ++i) + { + rgbData[i] = (BYTE)i; + } + + // set mock API's + RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); + + Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH)); + + logging.sczPath = L"BurnUnitTest.txt"; + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + hr = UserExperienceParseFromXml(&userExperience, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse UX from XML."); + + hr = RegistrationParseFromXml(®istration, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse registration from XML."); + + hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging); + TestThrowOnFailure(hr, L"Failed to set registration resume command."); + + hr = PathForCurrentProcess(&sczCurrentProcess, NULL); + TestThrowOnFailure(hr, L"Failed to get current process path."); + + // read resume type before session + hr = RegistrationDetectResumeType(®istration, &resumeType); + TestThrowOnFailure(hr, L"Failed to read resume type."); + + Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_NONE, (int)resumeType); + + // begin session + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + hr = RegistrationSaveState(®istration, rgbData, sizeof(rgbData)); + TestThrowOnFailure(hr, L"Failed to save state."); + + // read interrupted resume type + hr = RegistrationDetectResumeType(®istration, &resumeType); + TestThrowOnFailure(hr, L"Failed to read interrupted resume type."); + + Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_INTERRUPTED, (int)resumeType); + + // suspend session + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_SUSPEND, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); + TestThrowOnFailure(hr, L"Failed to suspend session."); + + // verify that run key was removed + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // read suspend resume type + hr = RegistrationDetectResumeType(®istration, &resumeType); + TestThrowOnFailure(hr, L"Failed to read suspend resume type."); + + Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_SUSPEND, (int)resumeType); + + // read state back + hr = RegistrationLoadState(®istration, &pbBuffer, &cbBuffer); + TestThrowOnFailure(hr, L"Failed to load state."); + + Assert::Equal((SIZE_T)sizeof(rgbData), cbBuffer); + Assert::True(0 == memcmp(pbBuffer, rgbData, sizeof(rgbData))); + + // write active resume mode + hr = RegistrationSessionResume(®istration, &variables); + TestThrowOnFailure(hr, L"Failed to write active resume mode."); + + // verify that run key was put back + Assert::NotEqual((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // end session + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + TestThrowOnFailure(hr, L"Failed to unregister bundle."); + + // read resume type after session + hr = RegistrationDetectResumeType(®istration, &resumeType); + TestThrowOnFailure(hr, L"Failed to read resume type."); + + Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_NONE, (int)resumeType); + } + finally + { + ReleaseStr(sczCurrentProcess); + ReleaseObject(pixeBundle); + UserExperienceUninitialize(&userExperience); + RegistrationUninitialize(®istration); + VariablesUninitialize(&variables); + + Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH)); + if (Directory::Exists(cacheDirectory)) + { + Directory::Delete(cacheDirectory, true); + } + + RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + } + } + + //BOOTSTRAPPER_RESUME_TYPE_NONE, + //BOOTSTRAPPER_RESUME_TYPE_INVALID, // resume information is present but invalid + //BOOTSTRAPPER_RESUME_TYPE_UNEXPECTED, // relaunched after an unexpected interruption + //BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING, // reboot has not taken place yet + //BOOTSTRAPPER_RESUME_TYPE_REBOOT, // relaunched after reboot + //BOOTSTRAPPER_RESUME_TYPE_SUSPEND, // relaunched after suspend + //BOOTSTRAPPER_RESUME_TYPE_ARP, // launched from ARP + }; +} +} +} +} +} + + +static LSTATUS APIENTRY RegistrationTest_RegCreateKeyExW( + __in HKEY hKey, + __in LPCWSTR lpSubKey, + __reserved DWORD Reserved, + __in_opt LPWSTR lpClass, + __in DWORD dwOptions, + __in REGSAM samDesired, + __in_opt CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes, + __out PHKEY phkResult, + __out_opt LPDWORD lpdwDisposition + ) +{ + LSTATUS ls = ERROR_SUCCESS; + LPCWSTR wzRoot = NULL; + HKEY hkRoot = NULL; + + if (HKEY_LOCAL_MACHINE == hKey) + { + wzRoot = HKLM_PATH; + } + else if (HKEY_CURRENT_USER == hKey) + { + wzRoot = HKCU_PATH; + } + else + { + hkRoot = hKey; + } + + if (wzRoot) + { + ls = ::RegOpenKeyExW(HKEY_CURRENT_USER, wzRoot, 0, KEY_WRITE, &hkRoot); + if (ERROR_SUCCESS != ls) + { + ExitFunction(); + } + } + + ls = ::RegCreateKeyExW(hkRoot, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition); + +LExit: + ReleaseRegKey(hkRoot); + + return ls; +} + +static LSTATUS APIENTRY RegistrationTest_RegOpenKeyExW( + __in HKEY hKey, + __in_opt LPCWSTR lpSubKey, + __reserved DWORD ulOptions, + __in REGSAM samDesired, + __out PHKEY phkResult + ) +{ + LSTATUS ls = ERROR_SUCCESS; + LPCWSTR wzRoot = NULL; + HKEY hkRoot = NULL; + + if (HKEY_LOCAL_MACHINE == hKey) + { + wzRoot = HKLM_PATH; + } + else if (HKEY_CURRENT_USER == hKey) + { + wzRoot = HKCU_PATH; + } + else + { + hkRoot = hKey; + } + + if (wzRoot) + { + ls = ::RegOpenKeyExW(HKEY_CURRENT_USER, wzRoot, 0, KEY_WRITE, &hkRoot); + if (ERROR_SUCCESS != ls) + { + ExitFunction(); + } + } + + ls = ::RegOpenKeyExW(hkRoot, lpSubKey, ulOptions, samDesired, phkResult); + +LExit: + ReleaseRegKey(hkRoot); + + return ls; +} + +static LSTATUS APIENTRY RegistrationTest_RegDeleteKeyExW( + __in HKEY hKey, + __in LPCWSTR lpSubKey, + __in REGSAM samDesired, + __reserved DWORD Reserved + ) +{ + LSTATUS ls = ERROR_SUCCESS; + LPCWSTR wzRoot = NULL; + HKEY hkRoot = NULL; + + if (HKEY_LOCAL_MACHINE == hKey) + { + wzRoot = HKLM_PATH; + } + else if (HKEY_CURRENT_USER == hKey) + { + wzRoot = HKCU_PATH; + } + else + { + hkRoot = hKey; + } + + if (wzRoot) + { + ls = ::RegOpenKeyExW(HKEY_CURRENT_USER, wzRoot, 0, KEY_WRITE | samDesired, &hkRoot); + if (ERROR_SUCCESS != ls) + { + ExitFunction(); + } + } + + ls = ::RegDeleteKeyExW(hkRoot, lpSubKey, samDesired, Reserved); + +LExit: + ReleaseRegKey(hkRoot); + + return ls; +} diff --git a/src/burn/test/BurnUnitTest/SearchTest.cpp b/src/burn/test/BurnUnitTest/SearchTest.cpp new file mode 100644 index 00000000..eca01f5f --- /dev/null +++ b/src/burn/test/BurnUnitTest/SearchTest.cpp @@ -0,0 +1,815 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiGetComponentPathW( + __in LPCWSTR szProduct, + __in LPCWSTR szComponent, + __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, + __inout_opt LPDWORD pcchBuf + ); +static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiLocateComponentW( + __in LPCWSTR szComponent, + __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, + __inout_opt LPDWORD pcchBuf + ); +static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoW( + __in LPCWSTR szProductCode, + __in LPCWSTR szProperty, + __out_ecount_opt(*pcchValue) LPWSTR szValue, + __inout_opt LPDWORD pcchValue + ); +static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoExW( + __in LPCWSTR szProductCode, + __in_opt LPCWSTR szUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in LPCWSTR szProperty, + __out_ecount_opt(*pcchValue) LPWSTR szValue, + __inout_opt LPDWORD pcchValue + ); + +using namespace System; +using namespace Xunit; +using namespace Microsoft::Win32; + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + public ref class SearchTest : BurnUnitTest + { + public: + SearchTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] + void DirectorySearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + pin_ptr wzDirectory1 = PtrToStringChars(this->TestContext->TestDirectory); + pin_ptr wzDirectory2 = PtrToStringChars(System::IO::Path::Combine(this->TestContext->TestDirectory, gcnew String(L"none"))); + + VariableSetStringHelper(&variables, L"Directory1", wzDirectory1, FALSE); + VariableSetStringHelper(&variables, L"Directory2", wzDirectory2, FALSE); + + LPCWSTR wzDocument = + L"" + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + + // check variable values + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable1")); + Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable2")); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + + [Fact(Skip = "Currently fails")] + void FileSearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; + ULARGE_INTEGER uliVersion = { }; + VERUTIL_VERSION* pVersion = NULL; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + pin_ptr wzFile1 = PtrToStringChars(System::IO::Path::Combine(this->TestContext->TestDirectory, gcnew String(L"none.txt"))); + pin_ptr wzFile2 = PtrToStringChars(System::Reflection::Assembly::GetExecutingAssembly()->Location); + + hr = FileVersion(wzFile2, &uliVersion.HighPart, &uliVersion.LowPart); + TestThrowOnFailure(hr, L"Failed to get DLL version."); + + hr = VerVersionFromQword(uliVersion.QuadPart, &pVersion); + NativeAssert::Succeeded(hr, "Failed to create version."); + + VariableSetStringHelper(&variables, L"File1", wzFile1, FALSE); + VariableSetStringHelper(&variables, L"File2", wzFile2, FALSE); + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + + // check variable values + Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable1")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable2")); + Assert::Equal(gcnew String(pVersion->sczVersion), VariableGetVersionHelper(&variables, L"Variable3")); + } + finally + { + ReleaseVerutilVersion(pVersion); + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + + [Fact] + void RegistrySearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; + HKEY hkey32 = NULL; + HKEY hkey64 = NULL; + BOOL f64bitMachine = (nullptr != Environment::GetEnvironmentVariable("ProgramFiles(x86)")); + + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"String"), gcnew String(L"String1 %TEMP%"), RegistryValueKind::String); + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"StringExpand"), gcnew String(L"String1 %TEMP%"), RegistryValueKind::ExpandString); + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"DWord"), 1, RegistryValueKind::DWord); + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"QWord"), 1ll, RegistryValueKind::QWord); + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"VersionString"), gcnew String(L"1.1.1.1"), RegistryValueKind::String); + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"VersionQWord"), MAKEQWORDVERSION(1,1,1,1), RegistryValueKind::QWord); + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\String"), nullptr, gcnew String(L"String1"), RegistryValueKind::String); + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Numeric"), nullptr, 1ll, RegistryValueKind::DWord); + + if (f64bitMachine) + { + hr = RegCreate(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness\\", KEY_WRITE | KEY_WOW64_32KEY, &hkey32); + Assert::True(SUCCEEDED(hr)); + + hr = RegCreate(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness\\", KEY_WRITE | KEY_WOW64_64KEY, &hkey64); + Assert::True(SUCCEEDED(hr)); + + hr = RegWriteString(hkey64, L"TestStringSpecificToBitness", L"64-bit"); + Assert::True(SUCCEEDED(hr)); + + hr = RegWriteString(hkey32, L"TestStringSpecificToBitness", L"32-bit"); + Assert::True(SUCCEEDED(hr)); + } + + VariableSetStringHelper(&variables, L"MyKey", L"SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value", FALSE); + VariableSetStringHelper(&variables, L"MyValue", L"String", FALSE); + VariableSetStringHelper(&variables, L"Variable27", L"Default27", FALSE); + VariableSetStringHelper(&variables, L"Variable28", L"Default28", FALSE); + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + + // check variable values + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable1")); + Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable2")); + Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable3")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable4")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable5")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable6")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable7")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable8")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable9")); + Assert::NotEqual(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable10")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable11")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable12")); + Assert::Equal(gcnew String(L"1.1.1.1"), VariableGetVersionHelper(&variables, L"Variable13")); + Assert::Equal(gcnew String(L"1.1.1.1"), VariableGetVersionHelper(&variables, L"Variable14")); + Assert::Equal(gcnew String(L"String1"), VariableGetStringHelper(&variables, L"Variable15")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable16")); + Assert::False(VariableExistsHelper(&variables, L"Variable17")); + Assert::False(VariableExistsHelper(&variables, L"Variable18")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable19")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable20")); + if (f64bitMachine) + { + Assert::Equal(gcnew String(L"32-bit"), VariableGetStringHelper(&variables, L"Variable21")); + Assert::Equal(gcnew String(L"64-bit"), VariableGetStringHelper(&variables, L"Variable22")); + } + + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable23")); + Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable24")); + Assert::Equal(gcnew String(L"Msi.Package"), VariableGetStringHelper(&variables, L"Variable25")); + Assert::Equal(gcnew String(L"Msi.Package"), VariableGetStringHelper(&variables, L"Variable26")); + Assert::Equal(gcnew String(L"Default27"), VariableGetStringHelper(&variables, L"Variable27")); + Assert::Equal(gcnew String(L"Default28"), VariableGetStringHelper(&variables, L"Variable28")); + } + finally + { + ReleaseRegKey(hkey32); + ReleaseRegKey(hkey64); + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + + Registry::CurrentUser->DeleteSubKeyTree(gcnew String(L"SOFTWARE\\Microsoft\\WiX_Burn_UnitTest")); + if (f64bitMachine) + { + RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness", REG_KEY_32BIT, FALSE); + RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest", REG_KEY_32BIT, FALSE); + RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness", REG_KEY_64BIT, FALSE); + RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest", REG_KEY_64BIT, FALSE); + } + } + } + + [Fact] + void MsiComponentSearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // set mock API's + WiuFunctionOverride(NULL, MsiComponentSearchTest_MsiGetComponentPathW, MsiComponentSearchTest_MsiLocateComponentW, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " // todo: value key path + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + + // check variable values + Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable1")); + Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable2")); + Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable3")); + Assert::Equal(gcnew String(L"C:\\directory\\file1.txt"), VariableGetStringHelper(&variables, L"Variable4")); + Assert::Equal(gcnew String(L"C:\\directory\\file2.txt"), VariableGetStringHelper(&variables, L"Variable5")); + Assert::Equal(gcnew String(L"C:\\directory\\file3.txt"), VariableGetStringHelper(&variables, L"Variable6")); + Assert::Equal(gcnew String(L"C:\\directory\\file4.txt"), VariableGetStringHelper(&variables, L"Variable7")); + Assert::Equal(gcnew String(L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\"), VariableGetStringHelper(&variables, L"Variable8")); + Assert::Equal(gcnew String(L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), VariableGetStringHelper(&variables, L"Variable9")); + Assert::Equal(3ll, VariableGetNumericHelper(&variables, L"Variable10")); + Assert::Equal(3ll, VariableGetNumericHelper(&variables, L"Variable11")); + Assert::Equal(4ll, VariableGetNumericHelper(&variables, L"Variable12")); + Assert::Equal(4ll, VariableGetNumericHelper(&variables, L"Variable13")); + Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable14")); + Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable15")); + Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable16")); + Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable17")); + Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable18")); + Assert::Equal(gcnew String(L"C:\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\file5.txt"), VariableGetStringHelper(&variables, L"Variable19")); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + + [Fact] + void MsiProductSearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // set mock API's + WiuFunctionOverride(NULL, NULL, NULL, NULL, MsiProductSearchTest_MsiGetProductInfoW, MsiProductSearchTest_MsiGetProductInfoExW, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + + // check variable values + Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable1")); + Assert::Equal(gcnew String(L"1.0.0.0"), VariableGetVersionHelper(&variables, L"Variable2")); + Assert::Equal(1033ll, VariableGetNumericHelper(&variables, L"Variable3")); + Assert::Equal(5ll, VariableGetNumericHelper(&variables, L"Variable4")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable5")); + Assert::Equal(gcnew String(L"1.0.0.0"), VariableGetVersionHelper(&variables, L"Variable6")); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + + [Fact] + void MsiFeatureSearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; + try + { + LPCWSTR wzDocument = + L"" + L" " + L""; + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + + [Fact] + void ConditionalSearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; + try + { + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L""; + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + + // check variable values + Assert::False(VariableExistsHelper(&variables, L"Variable1")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable2")); + Assert::False(VariableExistsHelper(&variables, L"Variable3")); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + [Fact] + void NoSearchesTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; + try + { + LPCWSTR wzDocument = + L"" + L""; + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + + [Fact] + void SetVariableSearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; + try + { + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // set variables + VariableSetStringHelper(&variables, L"OVERWRITTEN_STRING", L"ORIGINAL", FALSE); + VariableSetNumericHelper(&variables, L"OVERWRITTEN_NUMBER", 5); + VariableSetNumericHelper(&variables, L"REMOVED_NUMBER", 22); + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + + // check variable values + Assert::Equal(gcnew String(L"VAL1"), VariableGetStringHelper(&variables, L"PROP1")); + Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"PROP2")); + Assert::Equal(gcnew String(L"2"), VariableGetStringHelper(&variables, L"PROP2")); + Assert::Equal(gcnew String(L"VAL3"), VariableGetStringHelper(&variables, L"PROP3")); + Assert::Equal(gcnew String(L"VAL4"), VariableGetStringHelper(&variables, L"PROP4")); + Assert::Equal(gcnew String(L"VAL5"), VariableGetStringHelper(&variables, L"PROP5")); + Assert::Equal(gcnew String(L"VAL6"), VariableGetStringHelper(&variables, L"PROP6")); + Assert::Equal(7ll, VariableGetNumericHelper(&variables, L"PROP7")); + Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetVersionHelper(&variables, L"PROP8")); + Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetStringHelper(&variables, L"PROP8")); + Assert::Equal(gcnew String(L"[VAL9]"), VariableGetStringHelper(&variables, L"PROP9")); + + Assert::Equal(42ll, VariableGetNumericHelper(&variables, L"OVERWRITTEN_STRING")); + Assert::Equal(gcnew String(L"NEW"), VariableGetStringHelper(&variables, L"OVERWRITTEN_NUMBER")); + Assert::Equal((int)BURN_VARIANT_TYPE_NONE, VariableGetTypeHelper(&variables, L"REMOVED_NUMBER")); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + }; +} +} +} +} +} + + +static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiGetComponentPathW( + __in LPCWSTR szProduct, + __in LPCWSTR szComponent, + __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, + __inout_opt LPDWORD pcchBuf + ) +{ + INSTALLSTATE is = INSTALLSTATE_INVALIDARG; + String^ product = gcnew String(szProduct); + + if (String::Equals(product, gcnew String(L"{BAD00000-0000-0000-0000-000000000000}"))) + { + is = INSTALLSTATE_UNKNOWN; + } + else if (String::Equals(product, gcnew String(L"{600D0000-0000-0000-0000-000000000000}"))) + { + is = MsiComponentSearchTest_MsiLocateComponentW(szComponent, lpPathBuf, pcchBuf); + } + + return is; +} + +static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiLocateComponentW( + __in LPCWSTR szComponent, + __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, + __inout_opt LPDWORD pcchBuf + ) +{ + HRESULT hr = S_OK; + INSTALLSTATE is = INSTALLSTATE_INVALIDARG; + String^ component = gcnew String(szComponent); + LPCWSTR wzValue = NULL; + + if (String::Equals(component, gcnew String(L"{BAD00000-1000-0000-0000-000000000000}"))) + { + is = INSTALLSTATE_UNKNOWN; + } + else if (String::Equals(component, gcnew String(L"{600D0000-1000-1000-0000-000000000000}"))) + { + wzValue = L"C:\\directory\\file1.txt"; + is = INSTALLSTATE_LOCAL; + } + else if (String::Equals(component, gcnew String(L"{600D0000-1000-2000-0000-000000000000}"))) + { + wzValue = L"C:\\directory\\file2.txt"; + is = INSTALLSTATE_SOURCE; + } + else if (String::Equals(component, gcnew String(L"{600D0000-1000-3000-0000-000000000000}"))) + { + wzValue = L"C:\\directory\\file3.txt"; + is = INSTALLSTATE_SOURCEABSENT; + } + else if (String::Equals(component, gcnew String(L"{600D0000-1000-4000-0000-000000000000}"))) + { + wzValue = L"C:\\directory\\file4.txt"; + is = INSTALLSTATE_ABSENT; + } + else if (String::Equals(component, gcnew String(L"{600D0000-1000-5000-0000-000000000000}"))) + { + wzValue = L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\"; + is = INSTALLSTATE_LOCAL; + } + else if (String::Equals(component, gcnew String(L"{600D0000-1000-6000-0000-000000000000}"))) + { + wzValue = L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"; + is = INSTALLSTATE_LOCAL; + } + else if (String::Equals(component, gcnew String(L"{600D0000-1000-7000-0000-000000000000}"))) + { + wzValue = L"C:\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\file5.txt"; + is = INSTALLSTATE_ABSENT; + } + + if (wzValue && lpPathBuf) + { + hr = ::StringCchCopyW(lpPathBuf, *pcchBuf, wzValue); + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + *pcchBuf = lstrlenW(wzValue); + is = INSTALLSTATE_MOREDATA; + } + else if (FAILED(hr)) + { + is = INSTALLSTATE_INVALIDARG; + } + } + + return is; +} + +static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoW( + __in LPCWSTR szProductCode, + __in LPCWSTR szProperty, + __out_ecount_opt(*pcchValue) LPWSTR szValue, + __inout_opt LPDWORD pcchValue + ) +{ + if (String::Equals(gcnew String(szProductCode), gcnew String(L"{600D0000-0000-0000-0000-000000000000}")) && + String::Equals(gcnew String(szProperty), gcnew String(INSTALLPROPERTY_PRODUCTSTATE))) + { + // force call to WiuGetProductInfoEx + return ERROR_UNKNOWN_PROPERTY; + } + + UINT er = MsiProductSearchTest_MsiGetProductInfoExW(szProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, szProperty, szValue, pcchValue); + return er; +} + +static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoExW( + __in LPCWSTR szProductCode, + __in_opt LPCWSTR /*szUserSid*/, + __in MSIINSTALLCONTEXT dwContext, + __in LPCWSTR szProperty, + __out_ecount_opt(*pcchValue) LPWSTR szValue, + __inout_opt LPDWORD pcchValue + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_FUNCTION_FAILED; + LPCWSTR wzValue = NULL; + + String^ productCode = gcnew String(szProductCode); + String^ _property = gcnew String(szProperty); + switch (dwContext) + { + case MSIINSTALLCONTEXT_USERMANAGED: + er = ERROR_UNKNOWN_PRODUCT; + break; + case MSIINSTALLCONTEXT_USERUNMANAGED: + if (String::Equals(productCode, gcnew String(L"{600D0000-0000-0000-0000-000000000000}"))) + { + if (String::Equals(_property, gcnew String(INSTALLPROPERTY_PRODUCTSTATE))) + { + wzValue = L"5"; + } + } + break; + case MSIINSTALLCONTEXT_MACHINE: + if (String::Equals(productCode, gcnew String(L"{BAD00000-0000-0000-0000-000000000000}"))) + { + er = ERROR_UNKNOWN_PRODUCT; + } + else if (String::Equals(productCode, gcnew String(L"{600D0000-0000-0000-0000-000000000000}"))) + { + if (String::Equals(_property, gcnew String(INSTALLPROPERTY_VERSIONSTRING))) + { + wzValue = L"1.0.0.0"; + } + else if (String::Equals(_property, gcnew String(INSTALLPROPERTY_LANGUAGE))) + { + wzValue = L"1033"; + } + else if (String::Equals(_property, gcnew String(INSTALLPROPERTY_ASSIGNMENTTYPE))) + { + wzValue = L"1"; + } + else if (String::Equals(_property, gcnew String(INSTALLPROPERTY_PRODUCTSTATE))) + { + // try again in per-user context + er = ERROR_UNKNOWN_PRODUCT; + } + } + else if (String::Equals(productCode, gcnew String(L"{600D0000-1000-0000-0000-000000000000}"))) + { + static BOOL fFlipp = FALSE; + if (fFlipp) + { + if (String::Equals(_property, gcnew String(INSTALLPROPERTY_VERSIONSTRING))) + { + wzValue = L"1.0.0.0"; + } + } + else + { + *pcchValue = MAX_PATH * 2; + er = ERROR_MORE_DATA; + } + fFlipp = !fFlipp; + } + break; + } + + if (wzValue) + { + hr = ::StringCchCopyW(szValue, *pcchValue, wzValue); + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + *pcchValue = lstrlenW(wzValue); + er = ERROR_MORE_DATA; + } + else if (SUCCEEDED(hr)) + { + er = ERROR_SUCCESS; + } + } + + return er; +} diff --git a/src/burn/test/BurnUnitTest/TestData/CacheTest/CacheSignatureTest.File b/src/burn/test/BurnUnitTest/TestData/CacheTest/CacheSignatureTest.File new file mode 100644 index 00000000..896ac017 --- /dev/null +++ b/src/burn/test/BurnUnitTest/TestData/CacheTest/CacheSignatureTest.File @@ -0,0 +1 @@ +This file has a known hash. \ No newline at end of file diff --git a/src/burn/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml b/src/burn/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml new file mode 100644 index 00000000..65e3c63d --- /dev/null +++ b/src/burn/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/burn/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml b/src/burn/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml new file mode 100644 index 00000000..cca9a982 --- /dev/null +++ b/src/burn/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/burn/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml b/src/burn/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml new file mode 100644 index 00000000..996976b2 --- /dev/null +++ b/src/burn/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/burn/test/BurnUnitTest/VariableHelpers.cpp b/src/burn/test/BurnUnitTest/VariableHelpers.cpp new file mode 100644 index 00000000..40f958f8 --- /dev/null +++ b/src/burn/test/BurnUnitTest/VariableHelpers.cpp @@ -0,0 +1,217 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +using namespace System; +using namespace Xunit; + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue, BOOL fFormatted) + { + HRESULT hr = S_OK; + + hr = VariableSetString(pVariables, wzVariable, wzValue, FALSE, fFormatted); + TestThrowOnFailure2(hr, L"Failed to set %s to: %s", wzVariable, wzValue); + } + + void VariableSetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LONGLONG llValue) + { + HRESULT hr = S_OK; + + hr = VariableSetNumeric(pVariables, wzVariable, llValue, FALSE); + TestThrowOnFailure2(hr, L"Failed to set %s to: %I64d", wzVariable, llValue); + } + + void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue) + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion = NULL; + + try + { + hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); + TestThrowOnFailure1(hr, L"Failed to parse version '%ls'", wzValue); + + hr = VariableSetVersion(pVariables, wzVariable, pVersion, FALSE); + TestThrowOnFailure2(hr, L"Failed to set %s to: '%ls'", wzVariable, wzValue); + } + finally + { + ReleaseVerutilVersion(pVersion); + } + } + + String^ VariableGetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) + { + HRESULT hr = S_OK; + LPWSTR scz = NULL; + try + { + hr = VariableGetString(pVariables, wzVariable, &scz); + TestThrowOnFailure1(hr, L"Failed to get: %s", wzVariable); + + return gcnew String(scz); + } + finally + { + ReleaseStr(scz); + } + } + + __int64 VariableGetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) + { + HRESULT hr = S_OK; + LONGLONG llValue = 0; + + hr = VariableGetNumeric(pVariables, wzVariable, &llValue); + TestThrowOnFailure1(hr, L"Failed to get: %s", wzVariable); + + return llValue; + } + + String^ VariableGetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pValue = NULL; + + try + { + hr = VariableGetVersion(pVariables, wzVariable, &pValue); + TestThrowOnFailure1(hr, L"Failed to get: %s", wzVariable); + + return gcnew String(pValue->sczVersion); + } + finally + { + ReleaseVerutilVersion(pValue); + } + } + + String^ VariableGetFormattedHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, BOOL* pfContainsHiddenVariable) + { + HRESULT hr = S_OK; + LPWSTR scz = NULL; + try + { + hr = VariableGetFormatted(pVariables, wzVariable, &scz, pfContainsHiddenVariable); + TestThrowOnFailure1(hr, L"Failed to get formatted: %s", wzVariable); + + return gcnew String(scz); + } + finally + { + ReleaseStr(scz); + } + } + + String^ VariableFormatStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzIn) + { + HRESULT hr = S_OK; + LPWSTR scz = NULL; + try + { + hr = VariableFormatString(pVariables, wzIn, &scz, NULL); + TestThrowOnFailure1(hr, L"Failed to format string: '%s'", wzIn); + + return gcnew String(scz); + } + finally + { + ReleaseStr(scz); + } + } + + String^ VariableEscapeStringHelper(LPCWSTR wzIn) + { + HRESULT hr = S_OK; + LPWSTR scz = NULL; + try + { + hr = VariableEscapeString(wzIn, &scz); + TestThrowOnFailure1(hr, L"Failed to escape string: '%s'", wzIn); + + return gcnew String(scz); + } + finally + { + ReleaseStr(scz); + } + } + + bool EvaluateConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition) + { + HRESULT hr = S_OK; + BOOL f = FALSE; + + hr = ConditionEvaluate(pVariables, wzCondition, &f); + TestThrowOnFailure1(hr, L"Failed to evaluate condition: '%s'", wzCondition); + + return f ? true : false; + } + + bool EvaluateFailureConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition) + { + HRESULT hr = S_OK; + BOOL f = FALSE; + + hr = ConditionEvaluate(pVariables, wzCondition, &f); + return E_INVALIDDATA == hr ? true : false; + } + + bool VariableExistsHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) + { + HRESULT hr = S_OK; + BURN_VARIANT value = { }; + + try + { + hr = VariableGetVariant(pVariables, wzVariable, &value); + if (E_NOTFOUND == hr || value.Type == BURN_VARIANT_TYPE_NONE) + { + return false; + } + else + { + TestThrowOnFailure1(hr, L"Failed to find variable: '%s'", wzVariable); + return true; + } + } + finally + { + BVariantUninitialize(&value); + } + } + + int VariableGetTypeHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) + { + HRESULT hr = S_OK; + BURN_VARIANT value = { }; + + try + { + hr = VariableGetVariant(pVariables, wzVariable, &value); + TestThrowOnFailure1(hr, L"Failed to find variable: '%s'", wzVariable); + + return (int)value.Type; + } + finally + { + BVariantUninitialize(&value); + } + } +} +} +} +} +} diff --git a/src/burn/test/BurnUnitTest/VariableHelpers.h b/src/burn/test/BurnUnitTest/VariableHelpers.h new file mode 100644 index 00000000..d460c60f --- /dev/null +++ b/src/burn/test/BurnUnitTest/VariableHelpers.h @@ -0,0 +1,36 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + + +void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue, BOOL fFormatted); +void VariableSetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LONGLONG llValue); +void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue); +System::String^ VariableGetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); +__int64 VariableGetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); +System::String^ VariableGetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); +System::String^ VariableGetFormattedHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, BOOL* pfContainsHiddenVariable); +System::String^ VariableFormatStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzIn); +System::String^ VariableEscapeStringHelper(LPCWSTR wzIn); +bool EvaluateConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition); +bool EvaluateFailureConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition); +bool VariableExistsHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); +int VariableGetTypeHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); + + +} +} +} +} +} diff --git a/src/burn/test/BurnUnitTest/VariableTest.cpp b/src/burn/test/BurnUnitTest/VariableTest.cpp new file mode 100644 index 00000000..5c9dce03 --- /dev/null +++ b/src/burn/test/BurnUnitTest/VariableTest.cpp @@ -0,0 +1,532 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" +#undef GetTempPath +#undef GetEnvironmentVariable + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace Xunit; + + public ref class VariableTest : BurnUnitTest + { + public: + VariableTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] + void VariablesBasicTest() + { + HRESULT hr = S_OK; + BURN_VARIABLES variables = { }; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // set variables + VariableSetStringHelper(&variables, L"PROP1", L"VAL1", FALSE); + VariableSetNumericHelper(&variables, L"PROP2", 2); + VariableSetStringHelper(&variables, L"PROP5", L"VAL5", FALSE); + VariableSetStringHelper(&variables, L"PROP3", L"VAL3", FALSE); + VariableSetStringHelper(&variables, L"PROP4", L"VAL4", FALSE); + VariableSetStringHelper(&variables, L"PROP6", L"VAL6", FALSE); + VariableSetStringHelper(&variables, L"PROP7", L"7", FALSE); + VariableSetVersionHelper(&variables, L"PROP8", L"1.1.0.0"); + VariableSetStringHelper(&variables, L"PROP9", L"[VAL9]", TRUE); + + // set overwritten variables + VariableSetStringHelper(&variables, L"OVERWRITTEN_STRING", L"ORIGINAL", FALSE); + VariableSetNumericHelper(&variables, L"OVERWRITTEN_STRING", 42); + + VariableSetNumericHelper(&variables, L"OVERWRITTEN_NUMBER", 5); + VariableSetStringHelper(&variables, L"OVERWRITTEN_NUMBER", L"NEW", FALSE); + + // get and verify variable values + Assert::Equal(gcnew String(L"VAL1"), VariableGetStringHelper(&variables, L"PROP1")); + Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"PROP2")); + Assert::Equal(gcnew String(L"2"), VariableGetStringHelper(&variables, L"PROP2")); + Assert::Equal(gcnew String(L"VAL3"), VariableGetStringHelper(&variables, L"PROP3")); + Assert::Equal(gcnew String(L"VAL4"), VariableGetStringHelper(&variables, L"PROP4")); + Assert::Equal(gcnew String(L"VAL5"), VariableGetStringHelper(&variables, L"PROP5")); + Assert::Equal(gcnew String(L"VAL6"), VariableGetStringHelper(&variables, L"PROP6")); + Assert::Equal(7ll, VariableGetNumericHelper(&variables, L"PROP7")); + Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetVersionHelper(&variables, L"PROP8")); + Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetStringHelper(&variables, L"PROP8")); + Assert::Equal(gcnew String(L"[VAL9]"), VariableGetStringHelper(&variables, L"PROP9")); + + Assert::Equal(42ll, VariableGetNumericHelper(&variables, L"OVERWRITTEN_STRING")); + Assert::Equal(gcnew String(L"NEW"), VariableGetStringHelper(&variables, L"OVERWRITTEN_NUMBER")); + } + finally + { + VariablesUninitialize(&variables); + } + } + + [Fact] + void VariablesParseXmlTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BOOL fContainsHiddenData = FALSE; + try + { + LPCWSTR wzDocument = + L"" + L" "; + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = VariablesParseFromXml(&variables, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse variables from XML."); + + // get and verify variable values + Assert::Equal((int)BURN_VARIANT_TYPE_NUMERIC, VariableGetTypeHelper(&variables, L"Var1")); + Assert::Equal((int)BURN_VARIANT_TYPE_STRING, VariableGetTypeHelper(&variables, L"Var2")); + Assert::Equal((int)BURN_VARIANT_TYPE_VERSION, VariableGetTypeHelper(&variables, L"Var3")); + Assert::Equal((int)BURN_VARIANT_TYPE_NONE, VariableGetTypeHelper(&variables, L"Var4")); + Assert::Equal((int)BURN_VARIANT_TYPE_FORMATTED, VariableGetTypeHelper(&variables, L"Var6")); + + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Var1")); + Assert::Equal(gcnew String(L"String value."), VariableGetStringHelper(&variables, L"Var2")); + Assert::Equal(gcnew String(L"1.2.3.4"), VariableGetVersionHelper(&variables, L"Var3")); + Assert::Equal(gcnew String(L"[Formatted]"), VariableGetStringHelper(&variables, L"Var6")); + Assert::Equal(gcnew String(L"supersecret"), VariableGetFormattedHelper(&variables, L"Formatted", &fContainsHiddenData)); + Assert::Equal(TRUE, fContainsHiddenData); + Assert::Equal(gcnew String(L"supersecret"), VariableGetFormattedHelper(&variables, L"Var6", &fContainsHiddenData)); + Assert::Equal(TRUE, fContainsHiddenData); + Assert::Equal(gcnew String(L"String value."), VariableGetFormattedHelper(&variables, L"Var2", &fContainsHiddenData)); + Assert::Equal(FALSE, fContainsHiddenData); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + } + } + + [Fact] + void VariablesFormatTest() + { + HRESULT hr = S_OK; + BURN_VARIABLES variables = { }; + LPWSTR scz = NULL; + SIZE_T cch = 0; + BOOL fContainsHiddenData = FALSE; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // set variables + VariableSetStringHelper(&variables, L"PROP1", L"VAL1", FALSE); + VariableSetStringHelper(&variables, L"PROP2", L"VAL2", FALSE); + VariableSetNumericHelper(&variables, L"PROP3", 3); + VariableSetStringHelper(&variables, L"PROP4", L"[PROP1]", FALSE); + VariableSetStringHelper(&variables, L"PROP5", L"[PROP2]", FALSE); + VariableSetStringHelper(&variables, L"PROP6", L"[PROP4]", TRUE); + VariableSetStringHelper(&variables, L"PROP7", L"[PROP5]", TRUE); + + // test string formatting + Assert::Equal(gcnew String(L"NOPROP"), VariableFormatStringHelper(&variables, L"NOPROP")); + Assert::Equal(gcnew String(L"VAL1"), VariableFormatStringHelper(&variables, L"[PROP1]")); + Assert::Equal(gcnew String(L" VAL1 "), VariableFormatStringHelper(&variables, L" [PROP1] ")); + Assert::Equal(gcnew String(L"PRE VAL1"), VariableFormatStringHelper(&variables, L"PRE [PROP1]")); + Assert::Equal(gcnew String(L"VAL1 POST"), VariableFormatStringHelper(&variables, L"[PROP1] POST")); + Assert::Equal(gcnew String(L"PRE VAL1 POST"), VariableFormatStringHelper(&variables, L"PRE [PROP1] POST")); + Assert::Equal(gcnew String(L"VAL1 MID VAL2"), VariableFormatStringHelper(&variables, L"[PROP1] MID [PROP2]")); + Assert::Equal(gcnew String(L""), VariableFormatStringHelper(&variables, L"[NONE]")); + Assert::Equal(gcnew String(L""), VariableFormatStringHelper(&variables, L"[prop1]")); + Assert::Equal(gcnew String(L"["), VariableFormatStringHelper(&variables, L"[\\[]")); + Assert::Equal(gcnew String(L"]"), VariableFormatStringHelper(&variables, L"[\\]]")); + Assert::Equal(gcnew String(L"[]"), VariableFormatStringHelper(&variables, L"[]")); + Assert::Equal(gcnew String(L"[NONE"), VariableFormatStringHelper(&variables, L"[NONE")); + Assert::Equal(gcnew String(L"VAL2"), VariableGetFormattedHelper(&variables, L"PROP2", &fContainsHiddenData)); + Assert::Equal(FALSE, fContainsHiddenData); + Assert::Equal(gcnew String(L"3"), VariableGetFormattedHelper(&variables, L"PROP3", &fContainsHiddenData)); + Assert::Equal(FALSE, fContainsHiddenData); + Assert::Equal(gcnew String(L"[PROP1]"), VariableGetFormattedHelper(&variables, L"PROP4", &fContainsHiddenData)); + Assert::Equal(FALSE, fContainsHiddenData); + Assert::Equal(gcnew String(L"[PROP2]"), VariableGetFormattedHelper(&variables, L"PROP5", &fContainsHiddenData)); + Assert::Equal(FALSE, fContainsHiddenData); + Assert::Equal(gcnew String(L"[PROP1]"), VariableGetFormattedHelper(&variables, L"PROP6", &fContainsHiddenData)); + Assert::Equal(FALSE, fContainsHiddenData); + Assert::Equal(gcnew String(L"[PROP2]"), VariableGetFormattedHelper(&variables, L"PROP7", &fContainsHiddenData)); + Assert::Equal(FALSE, fContainsHiddenData); + + hr = VariableFormatString(&variables, L"PRE [PROP1] POST", &scz, &cch); + TestThrowOnFailure(hr, L"Failed to format string"); + + Assert::Equal((SIZE_T)lstrlenW(scz), cch); + + hr = VariableFormatString(&variables, L"PRE [PROP1] POST", NULL, &cch); + TestThrowOnFailure(hr, L"Failed to format string"); + + Assert::Equal((SIZE_T)lstrlenW(scz), cch); + } + finally + { + VariablesUninitialize(&variables); + ReleaseStr(scz); + } + } + + [Fact] + void VariablesEscapeTest() + { + // test string escaping + Assert::Equal(gcnew String(L"[\\[]"), VariableEscapeStringHelper(L"[")); + Assert::Equal(gcnew String(L"[\\]]"), VariableEscapeStringHelper(L"]")); + Assert::Equal(gcnew String(L" [\\[]TEXT[\\]] "), VariableEscapeStringHelper(L" [TEXT] ")); + } + + [Fact] + void VariablesConditionTest() + { + HRESULT hr = S_OK; + BURN_VARIABLES variables = { }; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // set variables + VariableSetStringHelper(&variables, L"PROP1", L"VAL1", FALSE); + VariableSetStringHelper(&variables, L"PROP2", L"VAL2", FALSE); + VariableSetStringHelper(&variables, L"PROP3", L"VAL3", FALSE); + VariableSetStringHelper(&variables, L"PROP4", L"BEGIN MID END", FALSE); + VariableSetNumericHelper(&variables, L"PROP5", 5); + VariableSetNumericHelper(&variables, L"PROP6", 6); + VariableSetStringHelper(&variables, L"PROP7", L"", FALSE); + VariableSetNumericHelper(&variables, L"PROP8", 0); + VariableSetStringHelper(&variables, L"_PROP9", L"VAL9", FALSE); + VariableSetNumericHelper(&variables, L"PROP10", -10); + VariableSetNumericHelper(&variables, L"PROP11", 9223372036854775807ll); + VariableSetNumericHelper(&variables, L"PROP12", -9223372036854775808ll); + VariableSetNumericHelper(&variables, L"PROP13", 0x00010000); + VariableSetNumericHelper(&variables, L"PROP14", 0x00000001); + VariableSetNumericHelper(&variables, L"PROP15", 0x00010001); + VariableSetVersionHelper(&variables, L"PROP16", L"0.0.0.0"); + VariableSetVersionHelper(&variables, L"PROP17", L"1.0.0.0"); + VariableSetVersionHelper(&variables, L"PROP18", L"1.1.0.0"); + VariableSetVersionHelper(&variables, L"PROP19", L"1.1.1.0"); + VariableSetVersionHelper(&variables, L"PROP20", L"1.1.1.1"); + VariableSetNumericHelper(&variables, L"vPROP21", 1); + VariableSetVersionHelper(&variables, L"PROP22", L"65535.65535.65535.65535"); + VariableSetStringHelper(&variables, L"PROP23", L"1.1.1", FALSE); + VariableSetStringHelper(&variables, L"PROP24", L"[PROP1]", TRUE); + VariableSetStringHelper(&variables, L"PROP25", L"[PROP7]", TRUE); + VariableSetStringHelper(&variables, L"PROP26", L"[PROP8]", TRUE); + VariableSetStringHelper(&variables, L"PROP27", L"[PROP16]", TRUE); + + // test conditions + Assert::True(EvaluateConditionHelper(&variables, L"PROP1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP5")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP7")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP8")); + Assert::True(EvaluateConditionHelper(&variables, L"_PROP9")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP16")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP17")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP24=\"VAL1\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP25")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP26")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP27")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\"")); + Assert::False(EvaluateConditionHelper(&variables, L"NONE = \"NOT\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 <> \"VAL1\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 ~<> \"VAL1\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 ~<> \"Val1\"")); + Assert::True(EvaluateConditionHelper(&variables, L"NONE <> \"NOT\"")); + Assert::True(EvaluateConditionHelper(&variables, L"NONE ~<> \"NOT\"")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 ~= \"val1\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"val1\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 ~<> \"val1\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 <> \"val1\"")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 = 5")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP5 = 0")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP5 <> 5")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 <> 0")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP10 = -10")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP10 <> -10")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP17 = v1")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP17 = v0")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP17 <> v1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP17 <> v0")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP16 = v0")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP17 = v1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP18 = v1.1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP19 = v1.1.1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP20 = v1.1.1.1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP20 > v1.1.1.1.0")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP20 > v1.1.1.1.1")); + Assert::True(EvaluateConditionHelper(&variables, L"vPROP21 = 1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP23 = v1.1.1")); + Assert::True(EvaluateConditionHelper(&variables, L"v1.1.1 = PROP23")); + Assert::False(EvaluateConditionHelper(&variables, L"v1.1.1<>PROP23")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 <> v1.1.1")); + Assert::True(EvaluateConditionHelper(&variables, L"v1.1.1 <> PROP1")); + + Assert::False(EvaluateConditionHelper(&variables, L"PROP11 = 9223372036854775806")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP11 = 9223372036854775807")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP11 = 9223372036854775808")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP11 = 92233720368547758070000")); + + Assert::False(EvaluateConditionHelper(&variables, L"PROP12 = -9223372036854775807")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP12 = -9223372036854775808")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP12 = -9223372036854775809")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP12 = -92233720368547758080000")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP22 = v65535.65535.65535.65535")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP22 < v65536.65535.65535.65535")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP22 < v65535.655350000.65535.65535")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 < 6")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP5 < 5")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 > 4")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP5 > 5")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 <= 6")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 <= 5")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP5 <= 4")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 >= 4")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 >= 5")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP5 >= 6")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP4 << \"BEGIN\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP4 << \"END\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP4 >> \"END\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP4 >> \"BEGIN\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP4 >< \"MID\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP4 >< \"NONE\"")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP16 < v1.1")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP16 < v0")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP17 > v0.12")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP17 > v1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP18 >= v1.0")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP18 >= v1.1")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP18 >= v2.1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP19 <= v1.1234.1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP19 <= v1.1.1")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP19 <= v1.0.123")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP6 = \"6\"")); + Assert::True(EvaluateConditionHelper(&variables, L"\"6\" = PROP6")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP6 = \"ABC\"")); + Assert::False(EvaluateConditionHelper(&variables, L"\"ABC\" = PROP6")); + Assert::False(EvaluateConditionHelper(&variables, L"\"ABC\" = PROP6")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP13 << 1")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP13 << 0")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP14 >> 1")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP14 >> 0")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP15 >< 65537")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP15 >< 0")); + + Assert::False(EvaluateConditionHelper(&variables, L"NOT PROP1")); + Assert::True(EvaluateConditionHelper(&variables, L"NOT (PROP1 <> \"VAL1\")")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" AND PROP2 = \"VAL2\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" AND PROP2 = \"NOT\"")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" OR PROP2 = \"VAL2\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" OR PROP2 = \"NOT\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" OR PROP2 = \"VAL2\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" OR PROP2 = \"NOT\"")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\" OR PROP3 = \"NOT\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"VAL3\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"NOT\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND (PROP2 = \"NOT\" OR PROP3 = \"VAL3\")")); + Assert::True(EvaluateConditionHelper(&variables, L"(PROP1 = \"VAL1\" AND PROP2 = \"VAL2\") OR PROP3 = \"NOT\"")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"VAL2\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP3 = \"VAL3\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); + Assert::True(EvaluateConditionHelper(&variables, L"(PROP3 = \"NOT\" OR PROP1 = \"VAL1\") AND PROP2 = \"VAL2\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP3 = \"NOT\" OR (PROP1 = \"VAL1\" AND PROP2 = \"VAL2\")")); + + Assert::True(EvaluateFailureConditionHelper(&variables, L"=")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"(PROP1")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"(PROP1 = \"")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"1A")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"*")); + + Assert::True(EvaluateFailureConditionHelper(&variables, L"1 == 1")); + } + finally + { + VariablesUninitialize(&variables); + } + } + + [Fact] + void VariablesSerializationTest() + { + HRESULT hr = S_OK; + BYTE* pbBuffer = NULL; + SIZE_T cbBuffer = 0; + SIZE_T iBuffer = 0; + BURN_VARIABLES variables1 = { }; + BURN_VARIABLES variables2 = { }; + try + { + // serialize + hr = VariableInitialize(&variables1); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + VariableSetStringHelper(&variables1, L"PROP1", L"VAL1", FALSE); + VariableSetNumericHelper(&variables1, L"PROP2", 2); + VariableSetVersionHelper(&variables1, L"PROP3", L"1.1.1.1"); + VariableSetStringHelper(&variables1, L"PROP4", L"VAL4", FALSE); + VariableSetStringHelper(&variables1, L"PROP5", L"[PROP1]", TRUE); + + hr = VariableSerialize(&variables1, FALSE, &pbBuffer, &cbBuffer); + TestThrowOnFailure(hr, L"Failed to serialize variables."); + + // deserialize + hr = VariableInitialize(&variables2); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + hr = VariableDeserialize(&variables2, FALSE, pbBuffer, cbBuffer, &iBuffer); + TestThrowOnFailure(hr, L"Failed to deserialize variables."); + + Assert::Equal(gcnew String(L"VAL1"), VariableGetStringHelper(&variables2, L"PROP1")); + Assert::Equal(2ll, VariableGetNumericHelper(&variables2, L"PROP2")); + Assert::Equal(gcnew String(L"1.1.1.1"), VariableGetVersionHelper(&variables2, L"PROP3")); + Assert::Equal(gcnew String(L"VAL4"), VariableGetStringHelper(&variables2, L"PROP4")); + Assert::Equal(gcnew String(L"[PROP1]"), VariableGetStringHelper(&variables2, L"PROP5")); + + Assert::Equal((int)BURN_VARIANT_TYPE_STRING, VariableGetTypeHelper(&variables2, L"PROP1")); + Assert::Equal((int)BURN_VARIANT_TYPE_NUMERIC, VariableGetTypeHelper(&variables2, L"PROP2")); + Assert::Equal((int)BURN_VARIANT_TYPE_VERSION, VariableGetTypeHelper(&variables2, L"PROP3")); + Assert::Equal((int)BURN_VARIANT_TYPE_STRING, VariableGetTypeHelper(&variables2, L"PROP4")); + Assert::Equal((int)BURN_VARIANT_TYPE_FORMATTED, VariableGetTypeHelper(&variables2, L"PROP5")); + } + finally + { + ReleaseBuffer(pbBuffer); + VariablesUninitialize(&variables1); + VariablesUninitialize(&variables2); + } + } + + [Fact] + void VariablesBuiltInTest() + { + HRESULT hr = S_OK; + BURN_VARIABLES variables = { }; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // VersionMsi + Assert::True(EvaluateConditionHelper(&variables, L"VersionMsi >= v1.1")); + + // VersionNT + Assert::True(EvaluateConditionHelper(&variables, L"VersionNT <> v0.0.0.0")); + + // VersionNT64 + if (nullptr == Environment::GetEnvironmentVariable("ProgramFiles(x86)")) + { + Assert::False(EvaluateConditionHelper(&variables, L"VersionNT64")); + } + else + { + Assert::True(EvaluateConditionHelper(&variables, L"VersionNT64")); + } + + // attempt to set a built-in property + hr = VariableSetString(&variables, L"VersionNT", L"VAL", FALSE, FALSE); + Assert::Equal(E_INVALIDARG, hr); + Assert::False(EvaluateConditionHelper(&variables, L"VersionNT = \"VAL\"")); + + VariableGetNumericHelper(&variables, L"NTProductType"); + VariableGetNumericHelper(&variables, L"NTSuiteBackOffice"); + VariableGetNumericHelper(&variables, L"NTSuiteDataCenter"); + VariableGetNumericHelper(&variables, L"NTSuiteEnterprise"); + VariableGetNumericHelper(&variables, L"NTSuitePersonal"); + VariableGetNumericHelper(&variables, L"NTSuiteSmallBusiness"); + VariableGetNumericHelper(&variables, L"NTSuiteSmallBusinessRestricted"); + VariableGetNumericHelper(&variables, L"NTSuiteWebServer"); + VariableGetNumericHelper(&variables, L"CompatibilityMode"); + VariableGetNumericHelper(&variables, L"Privileged"); + VariableGetNumericHelper(&variables, L"SystemLanguageID"); + VariableGetNumericHelper(&variables, L"TerminalServer"); + VariableGetNumericHelper(&variables, L"UserUILanguageID"); + VariableGetNumericHelper(&variables, L"UserLanguageID"); + + // known folders + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::ApplicationData) + "\\", VariableGetStringHelper(&variables, L"AppDataFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::CommonApplicationData) + "\\", VariableGetStringHelper(&variables, L"CommonAppDataFolder")); + + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::ProgramFiles) + "\\", VariableGetStringHelper(&variables, L"ProgramFilesFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::DesktopDirectory) + "\\", VariableGetStringHelper(&variables, L"DesktopFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Favorites) + "\\", VariableGetStringHelper(&variables, L"FavoritesFolder")); + VariableGetStringHelper(&variables, L"FontsFolder"); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData) + "\\", VariableGetStringHelper(&variables, L"LocalAppDataFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Personal) + "\\", VariableGetStringHelper(&variables, L"PersonalFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Programs) + "\\", VariableGetStringHelper(&variables, L"ProgramMenuFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::SendTo) + "\\", VariableGetStringHelper(&variables, L"SendToFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::StartMenu) + "\\", VariableGetStringHelper(&variables, L"StartMenuFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Startup) + "\\", VariableGetStringHelper(&variables, L"StartupFolder")); + VariableGetStringHelper(&variables, L"SystemFolder"); + VariableGetStringHelper(&variables, L"WindowsFolder"); + VariableGetStringHelper(&variables, L"WindowsVolume"); + + Assert::Equal(System::IO::Path::GetTempPath(), System::IO::Path::GetFullPath(VariableGetStringHelper(&variables, L"TempFolder"))); + + VariableGetStringHelper(&variables, L"AdminToolsFolder"); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::CommonProgramFiles) + "\\", VariableGetStringHelper(&variables, L"CommonFilesFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::MyPictures) + "\\", VariableGetStringHelper(&variables, L"MyPicturesFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Templates) + "\\", VariableGetStringHelper(&variables, L"TemplateFolder")); + + if (Environment::Is64BitOperatingSystem) + { + VariableGetStringHelper(&variables, L"ProgramFiles64Folder"); + VariableGetStringHelper(&variables, L"CommonFiles64Folder"); + VariableGetStringHelper(&variables, L"System64Folder"); + } + } + finally + { + VariablesUninitialize(&variables); + } + } + }; +} +} +} +} +} diff --git a/src/burn/test/BurnUnitTest/VariantTest.cpp b/src/burn/test/BurnUnitTest/VariantTest.cpp new file mode 100644 index 00000000..43899a2b --- /dev/null +++ b/src/burn/test/BurnUnitTest/VariantTest.cpp @@ -0,0 +1,221 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace Xunit; + + public ref class VariantTest : BurnUnitTest + { + public: + VariantTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] + void VariantBasicTest() + { + BURN_VARIANT expectedVariants[10]; + BURN_VARIANT actualVariants[10]; + for (DWORD i = 0; i < 10; i++) + { + BVariantUninitialize(expectedVariants + i); + BVariantUninitialize(actualVariants + i); + } + + try + { + InitNumericValue(expectedVariants + 0, 2, FALSE, L"PROP1", actualVariants + 0); + InitStringValue(expectedVariants + 1, L"VAL2", FALSE, L"PROP2", actualVariants + 1); + InitVersionValue(expectedVariants + 2, L"1.1.0.0", FALSE, L"PROP3", actualVariants + 2); + InitNoneValue(expectedVariants + 3, FALSE, L"PROP4", actualVariants + 3); + InitNoneValue(expectedVariants + 4, TRUE, L"PROP5", actualVariants + 4); + InitVersionValue(expectedVariants + 5, L"1.1.1.0", TRUE, L"PROP6", actualVariants + 5); + InitStringValue(expectedVariants + 6, L"7", TRUE, L"PROP7", actualVariants + 6); + InitNumericValue(expectedVariants + 7, 11, TRUE, L"PROP8", actualVariants + 7); + InitFormattedValue(expectedVariants + 8, L"VAL9", FALSE, L"PROP9", actualVariants + 8); + InitFormattedValue(expectedVariants + 9, L"VAL10", TRUE, L"PROP10", actualVariants + 9); + + VerifyNumericValue(expectedVariants + 0, actualVariants + 0); + VerifyStringValue(expectedVariants + 1, actualVariants + 1); + VerifyVersionValue(expectedVariants + 2, actualVariants + 2); + VerifyNoneValue(expectedVariants + 3, actualVariants + 3); + VerifyNoneValue(expectedVariants + 4, actualVariants + 4); + VerifyVersionValue(expectedVariants + 5, actualVariants + 5); + VerifyStringValue(expectedVariants + 6, actualVariants + 6); + VerifyNumericValue(expectedVariants + 7, actualVariants + 7); + VerifyFormattedValue(expectedVariants + 8, actualVariants + 8); + VerifyFormattedValue(expectedVariants + 9, actualVariants + 9); + } + finally + { + for (DWORD i = 0; i < 10; i++) + { + BVariantUninitialize(expectedVariants + i); + BVariantUninitialize(actualVariants + i); + } + } + } + + private: + void InitFormattedValue(BURN_VARIANT* pValue, LPWSTR wzValue, BOOL /*fHidden*/, LPCWSTR wz, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + pValue->Type = BURN_VARIANT_TYPE_FORMATTED; + + hr = StrAllocString(&pValue->sczValue, wzValue, 0); + NativeAssert::Succeeded(hr, "Failed to alloc string: {0}", wzValue); + + hr = BVariantCopy(pValue, pActualValue); + NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); + } + + void InitNoneValue(BURN_VARIANT* pValue, BOOL /*fHidden*/, LPCWSTR wz, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + pValue->Type = BURN_VARIANT_TYPE_NONE; + + hr = BVariantCopy(pValue, pActualValue); + NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); + } + + void InitNumericValue(BURN_VARIANT* pValue, LONGLONG llValue, BOOL /*fHidden*/, LPCWSTR wz, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + pValue->Type = BURN_VARIANT_TYPE_NUMERIC; + pValue->llValue = llValue; + + hr = BVariantCopy(pValue, pActualValue); + NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); + } + + void InitStringValue(BURN_VARIANT* pValue, LPWSTR wzValue, BOOL /*fHidden*/, LPCWSTR wz, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + pValue->Type = BURN_VARIANT_TYPE_STRING; + + hr = StrAllocString(&pValue->sczValue, wzValue, 0); + NativeAssert::Succeeded(hr, "Failed to alloc string: {0}", wzValue); + + hr = BVariantCopy(pValue, pActualValue); + NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); + } + + void InitVersionValue(BURN_VARIANT* pValue, LPCWSTR wzValue, BOOL /*fHidden*/, LPCWSTR wz, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion = NULL; + + try + { + hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); + NativeAssert::Succeeded(hr, "Failed to parse version {0}", wzValue); + + pValue->Type = BURN_VARIANT_TYPE_VERSION; + pValue->pValue = pVersion; + pVersion = NULL; + + hr = BVariantCopy(pValue, pActualValue); + NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); + } + finally + { + ReleaseVerutilVersion(pVersion); + } + } + + void VerifyFormattedValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + NativeAssert::Equal(BURN_VARIANT_TYPE_FORMATTED, pExpectedValue->Type); + NativeAssert::Equal(BURN_VARIANT_TYPE_FORMATTED, pActualValue->Type); + + try + { + hr = BVariantGetString(pActualValue, &sczValue); + NativeAssert::Succeeded(hr, "Failed to get string value"); + + NativeAssert::StringEqual(pExpectedValue->sczValue, sczValue); + } + finally + { + ReleaseStr(sczValue); + } + } + + void VerifyNumericValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + LONGLONG llValue = 0; + NativeAssert::Equal(BURN_VARIANT_TYPE_NUMERIC, pExpectedValue->Type); + NativeAssert::Equal(BURN_VARIANT_TYPE_NUMERIC, pActualValue->Type); + + hr = BVariantGetNumeric(pActualValue, &llValue); + NativeAssert::Succeeded(hr, "Failed to get numeric value"); + + NativeAssert::Equal(pExpectedValue->llValue, llValue); + } + + void VerifyNoneValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) + { + NativeAssert::Equal(BURN_VARIANT_TYPE_NONE, pExpectedValue->Type); + NativeAssert::Equal(BURN_VARIANT_TYPE_NONE, pActualValue->Type); + NativeAssert::Equal(pExpectedValue->llValue, pActualValue->llValue); + } + + void VerifyStringValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + NativeAssert::Equal(BURN_VARIANT_TYPE_STRING, pExpectedValue->Type); + NativeAssert::Equal(BURN_VARIANT_TYPE_STRING, pActualValue->Type); + + try + { + hr = BVariantGetString(pActualValue, &sczValue); + NativeAssert::Succeeded(hr, "Failed to get string value"); + + NativeAssert::StringEqual(pExpectedValue->sczValue, sczValue); + } + finally + { + ReleaseStr(sczValue); + } + } + + void VerifyVersionValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pValue = NULL; + NativeAssert::Equal(BURN_VARIANT_TYPE_VERSION, pExpectedValue->Type); + NativeAssert::Equal(BURN_VARIANT_TYPE_VERSION, pActualValue->Type); + + try + { + hr = BVariantGetVersion(pActualValue, &pValue); + NativeAssert::Succeeded(hr, "Failed to get version value"); + + NativeAssert::StringEqual(pExpectedValue->pValue->sczVersion, pActualValue->pValue->sczVersion); + } + finally + { + ReleaseVerutilVersion(pValue); + } + } + }; +} +} +} +} +} diff --git a/src/burn/test/BurnUnitTest/packages.config b/src/burn/test/BurnUnitTest/packages.config new file mode 100644 index 00000000..1d36c387 --- /dev/null +++ b/src/burn/test/BurnUnitTest/packages.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/burn/test/BurnUnitTest/precomp.cpp b/src/burn/test/BurnUnitTest/precomp.cpp new file mode 100644 index 00000000..37664a1c --- /dev/null +++ b/src/burn/test/BurnUnitTest/precomp.cpp @@ -0,0 +1,3 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" diff --git a/src/burn/test/BurnUnitTest/precomp.h b/src/burn/test/BurnUnitTest/precomp.h new file mode 100644 index 00000000..d2b57d61 --- /dev/null +++ b/src/burn/test/BurnUnitTest/precomp.h @@ -0,0 +1,79 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "wininet.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "BootstrapperEngine.h" +#include "BootstrapperApplication.h" +#include "BundleExtensionEngine.h" +#include "BundleExtension.h" + +#include "platform.h" +#include "variant.h" +#include "variable.h" +#include "condition.h" +#include "section.h" +#include "approvedexe.h" +#include "container.h" +#include "payload.h" +#include "cabextract.h" +#include "burnextension.h" +#include "search.h" +#include "userexperience.h" +#include "package.h" +#include "update.h" +#include "pseudobundle.h" +#include "registration.h" +#include "plan.h" +#include "pipe.h" +#include "logging.h" +#include "core.h" +#include "cache.h" +#include "apply.h" +#include "exeengine.h" +#include "msiengine.h" +#include "mspengine.h" +#include "msuengine.h" +#include "dependency.h" +#include "elevation.h" +#include "embedded.h" +#include "manifest.h" +#include "splashscreen.h" +#include "detect.h" + +#pragma managed +#include + +#include "BurnTestException.h" +#include "BurnTestFixture.h" +#include "BurnUnitTest.h" +#include "VariableHelpers.h" +#include "ManifestHelpers.h" diff --git a/src/engine/EngineForApplication.cpp b/src/engine/EngineForApplication.cpp deleted file mode 100644 index 83d88ba1..00000000 --- a/src/engine/EngineForApplication.cpp +++ /dev/null @@ -1,529 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -static HRESULT BAEngineGetPackageCount( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_GETPACKAGECOUNT_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_GETPACKAGECOUNT_RESULTS, pResults); - - ExternalEngineGetPackageCount(pContext->pEngineState, &pResults->cPackages); - -LExit: - return hr; -} - -static HRESULT BAEngineGetVariableNumeric( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_GETVARIABLENUMERIC_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_GETVARIABLENUMERIC_RESULTS, pResults); - - hr = ExternalEngineGetVariableNumeric(pContext->pEngineState, pArgs->wzVariable, &pResults->llValue); - -LExit: - return hr; -} - -static HRESULT BAEngineGetVariableString( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_GETVARIABLESTRING_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_GETVARIABLESTRING_RESULTS, pResults); - - hr = ExternalEngineGetVariableString(pContext->pEngineState, pArgs->wzVariable, pResults->wzValue, &pResults->cchValue); - -LExit: - return hr; -} - -static HRESULT BAEngineGetVariableVersion( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_GETVARIABLEVERSION_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_GETVARIABLEVERSION_RESULTS, pResults); - - hr = ExternalEngineGetVariableVersion(pContext->pEngineState, pArgs->wzVariable, pResults->wzValue, &pResults->cchValue); - -LExit: - return hr; -} - -static HRESULT BAEngineFormatString( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_FORMATSTRING_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_FORMATSTRING_RESULTS, pResults); - - hr = ExternalEngineFormatString(pContext->pEngineState, pArgs->wzIn, pResults->wzOut, &pResults->cchOut); - -LExit: - return hr; -} - -static HRESULT BAEngineEscapeString( - __in BOOTSTRAPPER_ENGINE_CONTEXT* /*pContext*/, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_ESCAPESTRING_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_ESCAPESTRING_RESULTS, pResults); - - hr = ExternalEngineEscapeString(pArgs->wzIn, pResults->wzOut, &pResults->cchOut); - -LExit: - return hr; -} - -static HRESULT BAEngineEvaluateCondition( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_EVALUATECONDITION_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_EVALUATECONDITION_RESULTS, pResults); - - hr = ExternalEngineEvaluateCondition(pContext->pEngineState, pArgs->wzCondition, &pResults->f); - -LExit: - return hr; -} - -static HRESULT BAEngineLog( - __in BOOTSTRAPPER_ENGINE_CONTEXT* /*pContext*/, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - REPORT_LEVEL rl = REPORT_NONE; - ValidateMessageArgs(hr, pvArgs, BAENGINE_LOG_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_LOG_RESULTS, pResults); - - switch (pArgs->level) - { - case BOOTSTRAPPER_LOG_LEVEL_STANDARD: - rl = REPORT_STANDARD; - break; - - case BOOTSTRAPPER_LOG_LEVEL_VERBOSE: - rl = REPORT_VERBOSE; - break; - - case BOOTSTRAPPER_LOG_LEVEL_DEBUG: - rl = REPORT_DEBUG; - break; - - case BOOTSTRAPPER_LOG_LEVEL_ERROR: - rl = REPORT_ERROR; - break; - - default: - ExitFunction1(hr = E_INVALIDARG); - } - - hr = ExternalEngineLog(rl, pArgs->wzMessage); - ExitOnFailure(hr, "Failed to log BA message."); - -LExit: - return hr; -} - -static HRESULT BAEngineSendEmbeddedError( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_SENDEMBEDDEDERROR_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_SENDEMBEDDEDERROR_RESULTS, pResults); - - hr = ExternalEngineSendEmbeddedError(pContext->pEngineState, pArgs->dwErrorCode, pArgs->wzMessage, pArgs->dwUIHint, &pResults->nResult); - -LExit: - return hr; -} - -static HRESULT BAEngineSendEmbeddedProgress( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_SENDEMBEDDEDPROGRESS_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_SENDEMBEDDEDPROGRESS_RESULTS, pResults); - - hr = ExternalEngineSendEmbeddedProgress(pContext->pEngineState, pArgs->dwProgressPercentage, pArgs->dwOverallProgressPercentage, &pResults->nResult); - -LExit: - return hr; -} - -static HRESULT BAEngineSetUpdate( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_SETUPDATE_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_SETUPDATE_RESULTS, pResults); - - hr = ExternalEngineSetUpdate(pContext->pEngineState, pArgs->wzLocalSource, pArgs->wzDownloadSource, pArgs->qwSize, pArgs->hashType, pArgs->rgbHash, pArgs->cbHash); - -LExit: - return hr; -} - -static HRESULT BAEngineSetLocalSource( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_SETLOCALSOURCE_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_SETLOCALSOURCE_RESULTS, pResults); - - hr = ExternalEngineSetLocalSource(pContext->pEngineState, pArgs->wzPackageOrContainerId, pArgs->wzPayloadId, pArgs->wzPath); - -LExit: - return hr; -} - -static HRESULT BAEngineSetDownloadSource( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_SETDOWNLOADSOURCE_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_SETDOWNLOADSOURCE_RESULTS, pResults); - - hr = ExternalEngineSetDownloadSource(pContext->pEngineState, pArgs->wzPackageOrContainerId, pArgs->wzPayloadId, pArgs->wzUrl, pArgs->wzUser, pArgs->wzPassword); - -LExit: - return hr; -} - -static HRESULT BAEngineSetVariableNumeric( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_SETVARIABLENUMERIC_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_SETVARIABLENUMERIC_RESULTS, pResults); - - hr = ExternalEngineSetVariableNumeric(pContext->pEngineState, pArgs->wzVariable, pArgs->llValue); - -LExit: - return hr; -} - -static HRESULT BAEngineSetVariableString( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_SETVARIABLESTRING_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_SETVARIABLESTRING_RESULTS, pResults); - - hr = ExternalEngineSetVariableString(pContext->pEngineState, pArgs->wzVariable, pArgs->wzValue, pArgs->fFormatted); - -LExit: - return hr; -} - -static HRESULT BAEngineSetVariableVersion( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_SETVARIABLEVERSION_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_SETVARIABLEVERSION_RESULTS, pResults); - - hr = ExternalEngineSetVariableVersion(pContext->pEngineState, pArgs->wzVariable, pArgs->wzValue); - -LExit: - return hr; -} - -static HRESULT BAEngineCloseSplashScreen( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_CLOSESPLASHSCREEN_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_CLOSESPLASHSCREEN_RESULTS, pResults); - - ExternalEngineCloseSplashScreen(pContext->pEngineState); - -LExit: - return hr; -} - -static HRESULT BAEngineCompareVersions( - __in BOOTSTRAPPER_ENGINE_CONTEXT* /*pContext*/, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_COMPAREVERSIONS_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_COMPAREVERSIONS_RESULTS, pResults); - - hr = ExternalEngineCompareVersions(pArgs->wzVersion1, pArgs->wzVersion2, &pResults->nResult); - -LExit: - return hr; -} - -static HRESULT BAEngineDetect( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_DETECT_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_DETECT_RESULTS, pResults); - - hr = ExternalEngineDetect(pContext->dwThreadId, pArgs->hwndParent); - -LExit: - return hr; -} - -static HRESULT BAEnginePlan( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_PLAN_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_PLAN_RESULTS, pResults); - - hr = ExternalEnginePlan(pContext->dwThreadId, pArgs->action); - -LExit: - return hr; -} - -static HRESULT BAEngineElevate( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_ELEVATE_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_ELEVATE_RESULTS, pResults); - - hr = ExternalEngineElevate(pContext->pEngineState, pContext->dwThreadId, pArgs->hwndParent); - -LExit: - return hr; -} - -static HRESULT BAEngineApply( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_APPLY_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_APPLY_RESULTS, pResults); - - hr = ExternalEngineApply(pContext->dwThreadId, pArgs->hwndParent); - -LExit: - return hr; -} - -static HRESULT BAEngineQuit( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_QUIT_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_QUIT_RESULTS, pResults); - - hr = ExternalEngineQuit(pContext->dwThreadId, pArgs->dwExitCode); - -LExit: - return hr; -} - -static HRESULT BAEngineLaunchApprovedExe( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_LAUNCHAPPROVEDEXE_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_LAUNCHAPPROVEDEXE_RESULTS, pResults); - - hr = ExternalEngineLaunchApprovedExe(pContext->pEngineState, pContext->dwThreadId, pArgs->hwndParent, pArgs->wzApprovedExeForElevationId, pArgs->wzArguments, pArgs->dwWaitForInputIdleTimeout); - -LExit: - return hr; -} - -static HRESULT BAEngineSetUpdateSource( - __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BAENGINE_SETUPDATESOURCE_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BAENGINE_SETUPDATESOURCE_RESULTS, pResults); - - hr = ExternalEngineSetUpdateSource(pContext->pEngineState, pArgs->wzUrl); - -LExit: - return hr; -} - -HRESULT WINAPI EngineForApplicationProc( - __in BOOTSTRAPPER_ENGINE_MESSAGE message, - __in const LPVOID pvArgs, - __inout LPVOID pvResults, - __in_opt LPVOID pvContext - ) -{ - HRESULT hr = S_OK; - BOOTSTRAPPER_ENGINE_CONTEXT* pContext = reinterpret_cast(pvContext); - - if (!pContext || !pvArgs || !pvResults) - { - ExitFunction1(hr = E_INVALIDARG); - } - - switch (message) - { - case BOOTSTRAPPER_ENGINE_MESSAGE_GETPACKAGECOUNT: - hr = BAEngineGetPackageCount(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLENUMERIC: - hr = BAEngineGetVariableNumeric(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLESTRING: - hr = BAEngineGetVariableString(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLEVERSION: - hr = BAEngineGetVariableVersion(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_FORMATSTRING: - hr = BAEngineFormatString(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_ESCAPESTRING: - hr = BAEngineEscapeString(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_EVALUATECONDITION: - hr = BAEngineEvaluateCondition(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_LOG: - hr = BAEngineLog(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_SENDEMBEDDEDERROR: - hr = BAEngineSendEmbeddedError(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_SENDEMBEDDEDPROGRESS: - hr = BAEngineSendEmbeddedProgress(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_SETUPDATE: - hr = BAEngineSetUpdate(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_SETLOCALSOURCE: - hr = BAEngineSetLocalSource(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_SETDOWNLOADSOURCE: - hr = BAEngineSetDownloadSource(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLENUMERIC: - hr = BAEngineSetVariableNumeric(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLESTRING: - hr = BAEngineSetVariableString(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLEVERSION: - hr = BAEngineSetVariableVersion(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_CLOSESPLASHSCREEN: - hr = BAEngineCloseSplashScreen(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_DETECT: - hr = BAEngineDetect(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_PLAN: - hr = BAEnginePlan(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_ELEVATE: - hr = BAEngineElevate(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_APPLY: - hr = BAEngineApply(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_QUIT: - hr = BAEngineQuit(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_LAUNCHAPPROVEDEXE: - hr = BAEngineLaunchApprovedExe(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_SETUPDATESOURCE: - hr = BAEngineSetUpdateSource(pContext, pvArgs, pvResults); - break; - case BOOTSTRAPPER_ENGINE_MESSAGE_COMPAREVERSIONS: - hr = BAEngineCompareVersions(pContext, pvArgs, pvResults); - break; - default: - hr = E_NOTIMPL; - break; - } - -LExit: - return hr; -} diff --git a/src/engine/EngineForApplication.h b/src/engine/EngineForApplication.h deleted file mode 100644 index e5e8f6d7..00000000 --- a/src/engine/EngineForApplication.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - -// constants - -enum WM_BURN -{ - WM_BURN_FIRST = WM_APP + 0xFFF, // this enum value must always be first. - - WM_BURN_DETECT, - WM_BURN_PLAN, - WM_BURN_ELEVATE, - WM_BURN_APPLY, - WM_BURN_LAUNCH_APPROVED_EXE, - WM_BURN_QUIT, - - WM_BURN_LAST, // this enum value must always be last. -}; - -// structs - -typedef struct _BOOTSTRAPPER_ENGINE_CONTEXT -{ - BURN_ENGINE_STATE* pEngineState; - DWORD dwThreadId; -} BOOTSTRAPPER_ENGINE_CONTEXT; - -// function declarations - -HRESULT WINAPI EngineForApplicationProc( - __in BOOTSTRAPPER_ENGINE_MESSAGE message, - __in const LPVOID pvArgs, - __inout LPVOID pvResults, - __in_opt LPVOID pvContext - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/EngineForExtension.cpp b/src/engine/EngineForExtension.cpp deleted file mode 100644 index 2e1c98fd..00000000 --- a/src/engine/EngineForExtension.cpp +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -static HRESULT BEEngineEscapeString( - __in BURN_EXTENSION_ENGINE_CONTEXT* /*pContext*/, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_RESULTS, pResults); - - hr = ExternalEngineEscapeString(pArgs->wzIn, pResults->wzOut, &pResults->cchOut); - -LExit: - return hr; -} - -static HRESULT BEEngineEvaluateCondition( - __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_RESULTS, pResults); - - hr = ExternalEngineEvaluateCondition(pContext->pEngineState, pArgs->wzCondition, &pResults->f); - -LExit: - return hr; -} - -static HRESULT BEEngineFormatString( - __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_FORMATSTRING_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_FORMATSTRING_RESULTS, pResults); - - hr = ExternalEngineFormatString(pContext->pEngineState, pArgs->wzIn, pResults->wzOut, &pResults->cchOut); - -LExit: - return hr; -} - -static HRESULT BEEngineGetVariableNumeric( - __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_RESULTS, pResults); - - hr = ExternalEngineGetVariableNumeric(pContext->pEngineState, pArgs->wzVariable, &pResults->llValue); - -LExit: - return hr; -} - -static HRESULT BEEngineGetVariableString( - __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_RESULTS, pResults); - - hr = ExternalEngineGetVariableString(pContext->pEngineState, pArgs->wzVariable, pResults->wzValue, &pResults->cchValue); - -LExit: - return hr; -} - -static HRESULT BEEngineGetVariableVersion( - __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_RESULTS, pResults); - - hr = ExternalEngineGetVariableVersion(pContext->pEngineState, pArgs->wzVariable, pResults->wzValue, &pResults->cchValue); - -LExit: - return hr; -} - -static HRESULT BEEngineLog( - __in BURN_EXTENSION_ENGINE_CONTEXT* /*pContext*/, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - REPORT_LEVEL rl = REPORT_NONE; - ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_LOG_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_LOG_RESULTS, pResults); - - switch (pArgs->level) - { - case BUNDLE_EXTENSION_LOG_LEVEL_STANDARD: - rl = REPORT_STANDARD; - break; - - case BUNDLE_EXTENSION_LOG_LEVEL_VERBOSE: - rl = REPORT_VERBOSE; - break; - - case BUNDLE_EXTENSION_LOG_LEVEL_DEBUG: - rl = REPORT_DEBUG; - break; - - case BUNDLE_EXTENSION_LOG_LEVEL_ERROR: - rl = REPORT_ERROR; - break; - - default: - ExitFunction1(hr = E_INVALIDARG); - } - - hr = ExternalEngineLog(rl, pArgs->wzMessage); - ExitOnFailure(hr, "Failed to log Bundle Extension message."); - -LExit: - return hr; -} - -static HRESULT BEEngineSetVariableNumeric( - __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_RESULTS, pResults); - - hr = ExternalEngineSetVariableNumeric(pContext->pEngineState, pArgs->wzVariable, pArgs->llValue); - -LExit: - return hr; -} - -static HRESULT BEEngineSetVariableString( - __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_RESULTS, pResults); - - hr = ExternalEngineSetVariableString(pContext->pEngineState, pArgs->wzVariable, pArgs->wzValue, pArgs->fFormatted); - -LExit: - return hr; -} - -static HRESULT BEEngineSetVariableVersion( - __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_RESULTS, pResults); - - hr = ExternalEngineSetVariableVersion(pContext->pEngineState, pArgs->wzVariable, pArgs->wzValue); - -LExit: - return hr; -} - -static HRESULT BEEngineCompareVersions( - __in BURN_EXTENSION_ENGINE_CONTEXT* /*pContext*/, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_ARGS, pArgs); - ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_RESULTS, pResults); - - hr = ExternalEngineCompareVersions(pArgs->wzVersion1, pArgs->wzVersion2, &pResults->nResult); - -LExit: - return hr; -} - -HRESULT WINAPI EngineForExtensionProc( - __in BUNDLE_EXTENSION_ENGINE_MESSAGE message, - __in const LPVOID pvArgs, - __inout LPVOID pvResults, - __in_opt LPVOID pvContext - ) -{ - HRESULT hr = S_OK; - BURN_EXTENSION_ENGINE_CONTEXT* pContext = reinterpret_cast(pvContext); - - if (!pContext || !pvArgs || !pvResults) - { - ExitFunction1(hr = E_INVALIDARG); - } - - switch (message) - { - case BUNDLE_EXTENSION_ENGINE_MESSAGE_ESCAPESTRING: - hr = BEEngineEscapeString(pContext, pvArgs, pvResults); - break; - case BUNDLE_EXTENSION_ENGINE_MESSAGE_EVALUATECONDITION: - hr = BEEngineEvaluateCondition(pContext, pvArgs, pvResults); - break; - case BUNDLE_EXTENSION_ENGINE_MESSAGE_FORMATSTRING: - hr = BEEngineFormatString(pContext, pvArgs, pvResults); - break; - case BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLENUMERIC: - hr = BEEngineGetVariableNumeric(pContext, pvArgs, pvResults); - break; - case BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLESTRING: - hr = BEEngineGetVariableString(pContext, pvArgs, pvResults); - break; - case BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLEVERSION: - hr = BEEngineGetVariableVersion(pContext, pvArgs, pvResults); - break; - case BUNDLE_EXTENSION_ENGINE_MESSAGE_LOG: - hr = BEEngineLog(pContext, pvArgs, pvResults); - break; - case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLENUMERIC: - hr = BEEngineSetVariableNumeric(pContext, pvArgs, pvResults); - break; - case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLESTRING: - hr = BEEngineSetVariableString(pContext, pvArgs, pvResults); - break; - case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLEVERSION: - hr = BEEngineSetVariableVersion(pContext, pvArgs, pvResults); - break; - case BUNDLE_EXTENSION_ENGINE_MESSAGE_COMPAREVERSIONS: - hr = BEEngineCompareVersions(pContext, pvArgs, pvResults); - break; - default: - hr = E_NOTIMPL; - break; - } - -LExit: - return hr; -} diff --git a/src/engine/EngineForExtension.h b/src/engine/EngineForExtension.h deleted file mode 100644 index bad5f08a..00000000 --- a/src/engine/EngineForExtension.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - -// structs - -typedef struct _BURN_EXTENSION_ENGINE_CONTEXT -{ - BURN_ENGINE_STATE* pEngineState; -} BURN_EXTENSION_ENGINE_CONTEXT; - -// function declarations - -HRESULT WINAPI EngineForExtensionProc( - __in BUNDLE_EXTENSION_ENGINE_MESSAGE message, - __in const LPVOID pvArgs, - __inout LPVOID pvResults, - __in_opt LPVOID pvContext - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp deleted file mode 100644 index 58d41b28..00000000 --- a/src/engine/apply.cpp +++ /dev/null @@ -1,3096 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -#ifdef DEBUG - #define IgnoreRollbackError(x, f, ...) if (FAILED(x)) { TraceError(x, f, __VA_ARGS__); } -#else - #define IgnoreRollbackError(x, f, ...) -#endif - -const DWORD BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS = 2; - -enum BURN_CACHE_PROGRESS_TYPE -{ - BURN_CACHE_PROGRESS_TYPE_ACQUIRE, - BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY, - BURN_CACHE_PROGRESS_TYPE_EXTRACT, - BURN_CACHE_PROGRESS_TYPE_FINALIZE, - BURN_CACHE_PROGRESS_TYPE_HASH, - BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY, - BURN_CACHE_PROGRESS_TYPE_STAGE, -}; - -// structs - -typedef struct _BURN_CACHE_CONTEXT -{ - BURN_USER_EXPERIENCE* pUX; - BURN_VARIABLES* pVariables; - BURN_PAYLOADS* pPayloads; - HANDLE hPipe; - HANDLE hSourceEngineFile; - DWORD64 qwTotalCacheSize; - DWORD64 qwSuccessfulCacheProgress; - LPCWSTR wzLayoutDirectory; - LPWSTR* rgSearchPaths; - DWORD cSearchPaths; - DWORD cSearchPathsMax; - LPWSTR sczLastUsedFolderCandidate; -} BURN_CACHE_CONTEXT; - -typedef struct _BURN_CACHE_PROGRESS_CONTEXT -{ - BURN_CACHE_CONTEXT* pCacheContext; - BURN_CACHE_PROGRESS_TYPE type; - BURN_CONTAINER* pContainer; - BURN_PACKAGE* pPackage; - BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem; - BURN_PAYLOAD* pPayload; - - BOOL fCancel; - HRESULT hrError; -} BURN_CACHE_PROGRESS_CONTEXT; - -typedef struct _BURN_EXECUTE_CONTEXT -{ - BURN_USER_EXPERIENCE* pUX; - BOOL fRollback; - BURN_PACKAGE* pExecutingPackage; - DWORD cExecutedPackages; - DWORD cExecutePackagesTotal; - DWORD* pcOverallProgressTicks; -} BURN_EXECUTE_CONTEXT; - - -// internal function declarations -static HRESULT WINAPI AuthenticationRequired( - __in LPVOID pData, - __in HINTERNET hUrl, - __in long lHttpCode, - __out BOOL* pfRetrySend, - __out BOOL* pfRetry - ); - -static void CalculateKeepRegistration( - __in BURN_ENGINE_STATE* pEngineState, - __inout BOOL* pfKeepRegistration - ); -static HRESULT ExecuteDependentRegistrationActions( - __in HANDLE hPipe, - __in const BURN_REGISTRATION* pRegistration, - __in_ecount(cActions) const BURN_DEPENDENT_REGISTRATION_ACTION* rgActions, - __in DWORD cActions - ); -static HRESULT ApplyCachePackage( - __in BURN_CACHE_CONTEXT* pContext, - __in BURN_PACKAGE* pPackage - ); -static HRESULT ApplyExtractContainer( - __in BURN_CACHE_CONTEXT* pContext, - __in BURN_CONTAINER* pContainer - ); -static HRESULT ApplyLayoutBundle( - __in BURN_CACHE_CONTEXT* pContext, - __in BURN_PAYLOAD_GROUP* pPayloads, - __in_z LPCWSTR wzExecutableName, - __in_z LPCWSTR wzUnverifiedPath, - __in DWORD64 qwBundleSize - ); -static HRESULT ApplyLayoutContainer( - __in BURN_CACHE_CONTEXT* pContext, - __in BURN_CONTAINER* pContainer - ); -static HRESULT ApplyProcessPayload( - __in BURN_CACHE_CONTEXT* pContext, - __in_opt BURN_PACKAGE* pPackage, - __in BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem - ); -static HRESULT ApplyCacheVerifyContainerOrPayload( - __in BURN_CACHE_CONTEXT* pContext, - __in_opt BURN_CONTAINER* pContainer, - __in_opt BURN_PACKAGE* pPackage, - __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem - ); -static HRESULT ExtractContainer( - __in BURN_CACHE_CONTEXT* pContext, - __in BURN_CONTAINER* pContainer - ); -static HRESULT LayoutBundle( - __in BURN_CACHE_CONTEXT* pContext, - __in_z LPCWSTR wzExecutableName, - __in_z LPCWSTR wzUnverifiedPath, - __in DWORD64 qwBundleSize - ); -static HRESULT ApplyAcquireContainerOrPayload( - __in BURN_CACHE_CONTEXT* pContext, - __in_opt BURN_CONTAINER* pContainer, - __in_opt BURN_PACKAGE* pPackage, - __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem - ); -static HRESULT AcquireContainerOrPayload( - __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, - __out BOOL* pfRetry - ); -static BOOL IsValidLocalFile( - __in_z LPCWSTR wzFilePath, - __in DWORD64 qwFileSize, - __in BOOL fMinimumFileSize - ); -static HRESULT LayoutOrCacheContainerOrPayload( - __in BURN_CACHE_CONTEXT* pContext, - __in_opt BURN_CONTAINER* pContainer, - __in_opt BURN_PACKAGE* pPackage, - __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem, - __in DWORD cTryAgainAttempts, - __out BOOL* pfRetry - ); -static HRESULT PreparePayloadDestinationPath( - __in_z LPCWSTR wzDestinationPath - ); -static HRESULT CopyPayload( - __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, - __in HANDLE hSourceFile, - __in_z LPCWSTR wzSourcePath, - __in_z LPCWSTR wzDestinationPath - ); -static HRESULT DownloadPayload( - __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, - __in_z LPCWSTR wzDestinationPath - ); -static HRESULT CALLBACK CacheMessageHandler( - __in BURN_CACHE_MESSAGE* pMessage, - __in LPVOID pvContext - ); -static HRESULT CompleteCacheProgress( - __in BURN_CACHE_PROGRESS_CONTEXT* pContext, - __in DWORD64 qwFileSize - ); -static DWORD CALLBACK CacheProgressRoutine( - __in LARGE_INTEGER TotalFileSize, - __in LARGE_INTEGER TotalBytesTransferred, - __in LARGE_INTEGER StreamSize, - __in LARGE_INTEGER StreamBytesTransferred, - __in DWORD dwStreamNumber, - __in DWORD dwCallbackReason, - __in HANDLE hSourceFile, - __in HANDLE hDestinationFile, - __in_opt LPVOID lpData - ); -static void DoRollbackCache( - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_PLAN* pPlan, - __in HANDLE hPipe, - __in DWORD dwCheckpoint - ); -static HRESULT DoExecuteAction( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in_opt HANDLE hCacheThread, - __in BURN_EXECUTE_CONTEXT* pContext, - __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, - __inout BURN_EXECUTE_ACTION_CHECKPOINT** ppCheckpoint, - __out BOOL* pfSuspend, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ); -static HRESULT DoRollbackActions( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_EXECUTE_CONTEXT* pContext, - __in DWORD dwCheckpoint, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ); -static HRESULT ExecuteExePackage( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_EXECUTE_CONTEXT* pContext, - __in BOOL fRollback, - __out BOOL* pfRetry, - __out BOOL* pfSuspend, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ); -static HRESULT ExecuteMsiPackage( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_EXECUTE_CONTEXT* pContext, - __in BOOL fInsideMsiTransaction, - __in BOOL fRollback, - __out BOOL* pfRetry, - __out BOOL* pfSuspend, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ); -static HRESULT ExecuteMspPackage( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_EXECUTE_CONTEXT* pContext, - __in BOOL fInsideMsiTransaction, - __in BOOL fRollback, - __out BOOL* pfRetry, - __out BOOL* pfSuspend, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ); -static HRESULT ExecuteMsuPackage( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_EXECUTE_CONTEXT* pContext, - __in BOOL fRollback, - __in BOOL fStopWusaService, - __out BOOL* pfRetry, - __out BOOL* pfSuspend, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ); -static HRESULT ExecutePackageProviderAction( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_EXECUTE_ACTION* pAction, - __in BURN_EXECUTE_CONTEXT* pContext - ); -static HRESULT ExecuteDependencyAction( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_EXECUTE_ACTION* pAction, - __in BURN_EXECUTE_CONTEXT* pContext - ); -static HRESULT ExecuteMsiBeginTransaction( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, - __in BURN_EXECUTE_CONTEXT* pContext - ); -static HRESULT ExecuteMsiCommitTransaction( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, - __in BURN_EXECUTE_CONTEXT* pContext - ); -static HRESULT ExecuteMsiRollbackTransaction( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, - __in BURN_EXECUTE_CONTEXT* pContext - ); -static void ResetTransactionRegistrationState( - __in BURN_ENGINE_STATE* pEngineState, - __in BOOL fCommit - ); -static HRESULT CleanPackage( - __in HANDLE hElevatedPipe, - __in BURN_PACKAGE* pPackage - ); -static int GenericExecuteMessageHandler( - __in GENERIC_EXECUTE_MESSAGE* pMessage, - __in LPVOID pvContext - ); -static int MsiExecuteMessageHandler( - __in WIU_MSI_EXECUTE_MESSAGE* pMessage, - __in_opt LPVOID pvContext - ); -static HRESULT ReportOverallProgressTicks( - __in BURN_USER_EXPERIENCE* pUX, - __in BOOL fRollback, - __in DWORD cOverallProgressTicksTotal, - __in DWORD cOverallProgressTicks - ); -static HRESULT ExecutePackageComplete( - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_VARIABLES* pVariables, - __in BURN_PACKAGE* pPackage, - __in HRESULT hrOverall, - __in HRESULT hrExecute, - __in BOOL fRollback, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart, - __out BOOL* pfRetry, - __out BOOL* pfSuspend - ); - - -// function definitions - -extern "C" void ApplyInitialize() -{ - // Prevent the system from sleeping. - ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED); -} - -extern "C" void ApplyUninitialize() -{ - ::SetThreadExecutionState(ES_CONTINUOUS); -} - -extern "C" HRESULT ApplySetVariables( - __in BURN_VARIABLES* pVariables - ) -{ - HRESULT hr = S_OK; - - hr = VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, NULL, TRUE, FALSE); - ExitOnFailure(hr, "Failed to set the bundle forced restart package built-in variable."); - -LExit: - return hr; -} - -extern "C" void ApplyReset( - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_PACKAGES* pPackages - ) -{ - UserExperienceExecuteReset(pUX); - - for (DWORD i = 0; i < pPackages->cPackages; ++i) - { - BURN_PACKAGE* pPackage = pPackages->rgPackages + i; - pPackage->hrCacheResult = S_OK; - pPackage->transactionRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; - } -} - -extern "C" HRESULT ApplyLock( - __in BOOL /*fPerMachine*/, - __out HANDLE* phLock - ) -{ - HRESULT hr = S_OK; - *phLock = NULL; - -#if 0 // eventually figure out the correct way to support this. In its current form, embedded bundles (including related bundles) are hosed. - DWORD er = ERROR_SUCCESS; - HANDLE hLock = NULL; - - hLock = ::CreateMutexW(NULL, TRUE, fPerMachine ? L"Global\\WixBurnExecutionLock" : L"Local\\WixBurnExecutionLock"); - ExitOnNullWithLastError(hLock, hr, "Failed to create lock."); - - er = ::GetLastError(); - if (ERROR_ALREADY_EXISTS == er) - { - ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSTALL_ALREADY_RUNNING)); - } - - *phLock = hLock; - hLock = NULL; - -LExit: - ReleaseHandle(hLock); -#endif - return hr; -} - -extern "C" HRESULT ApplyRegister( - __in BURN_ENGINE_STATE* pEngineState - ) -{ - HRESULT hr = S_OK; - LPWSTR sczEngineWorkingPath = NULL; - - hr = UserExperienceOnRegisterBegin(&pEngineState->userExperience); - ExitOnRootFailure(hr, "BA aborted register begin."); - - // If we have a resume mode that suggests the bundle is on the machine. - if (BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING < pEngineState->command.resumeType) - { - // resume previous session - if (pEngineState->registration.fPerMachine) - { - hr = ElevationSessionResume(pEngineState->companionConnection.hPipe, pEngineState->registration.sczResumeCommandLine, pEngineState->registration.fDisableResume, &pEngineState->variables); - ExitOnFailure(hr, "Failed to resume registration session in per-machine process."); - } - else - { - hr = RegistrationSessionResume(&pEngineState->registration, &pEngineState->variables); - ExitOnFailure(hr, "Failed to resume registration session."); - } - } - else // need to complete registration on the machine. - { - hr = CacheCalculateBundleWorkingPath(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &sczEngineWorkingPath); - ExitOnFailure(hr, "Failed to calculate working path for engine."); - - // begin new session - if (pEngineState->registration.fPerMachine) - { - hr = ElevationSessionBegin(pEngineState->companionConnection.hPipe, sczEngineWorkingPath, pEngineState->registration.sczResumeCommandLine, pEngineState->registration.fDisableResume, &pEngineState->variables, pEngineState->plan.dwRegistrationOperations, pEngineState->plan.dependencyRegistrationAction, pEngineState->plan.qwEstimatedSize); - ExitOnFailure(hr, "Failed to begin registration session in per-machine process."); - } - else - { - hr = RegistrationSessionBegin(sczEngineWorkingPath, &pEngineState->registration, &pEngineState->variables, pEngineState->plan.dwRegistrationOperations, pEngineState->plan.dependencyRegistrationAction, pEngineState->plan.qwEstimatedSize); - ExitOnFailure(hr, "Failed to begin registration session."); - } - } - - // Apply any registration actions. - HRESULT hrExecuteRegistration = ExecuteDependentRegistrationActions(pEngineState->companionConnection.hPipe, &pEngineState->registration, pEngineState->plan.rgRegistrationActions, pEngineState->plan.cRegistrationActions); - UNREFERENCED_PARAMETER(hrExecuteRegistration); - - // Try to save engine state. - hr = CoreSaveEngineState(pEngineState); - if (FAILED(hr)) - { - LogErrorId(hr, MSG_STATE_NOT_SAVED); - hr = S_OK; - } - -LExit: - UserExperienceOnRegisterComplete(&pEngineState->userExperience, hr); - ReleaseStr(sczEngineWorkingPath); - - return hr; -} - -extern "C" HRESULT ApplyUnregister( - __in BURN_ENGINE_STATE* pEngineState, - __in BOOL fFailedOrRollback, - __in BOOL fSuspend, - __in BOOTSTRAPPER_APPLY_RESTART restart - ) -{ - HRESULT hr = S_OK; - BURN_RESUME_MODE resumeMode = BURN_RESUME_MODE_NONE; - BOOL fKeepRegistration = pEngineState->plan.fDisallowRemoval; - - CalculateKeepRegistration(pEngineState, &fKeepRegistration); - - hr = UserExperienceOnUnregisterBegin(&pEngineState->userExperience, &fKeepRegistration); - ExitOnRootFailure(hr, "BA aborted unregister begin."); - - // Calculate the correct resume mode. If a restart has been initiated, that trumps all other - // modes. If the user chose to suspend the install then we'll use that as the resume mode. - // Barring those special cases, if it was determined that we should keep the registration then - // do that, otherwise the resume mode was initialized to none and registration will be removed. - if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart) - { - resumeMode = BURN_RESUME_MODE_REBOOT_PENDING; - } - else if (fSuspend) - { - resumeMode = BURN_RESUME_MODE_SUSPEND; - } - else if (fKeepRegistration) - { - resumeMode = BURN_RESUME_MODE_ARP; - } - - // If apply failed in any way and we're going to be keeping the bundle registered then - // execute any rollback dependency registration actions. - if (fFailedOrRollback && fKeepRegistration) - { - // Execute any rollback registration actions. - HRESULT hrRegistrationRollback = ExecuteDependentRegistrationActions(pEngineState->companionConnection.hPipe, &pEngineState->registration, pEngineState->plan.rgRollbackRegistrationActions, pEngineState->plan.cRollbackRegistrationActions); - UNREFERENCED_PARAMETER(hrRegistrationRollback); - } - - if (pEngineState->registration.fPerMachine) - { - hr = ElevationSessionEnd(pEngineState->companionConnection.hPipe, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction); - ExitOnFailure(hr, "Failed to end session in per-machine process."); - } - else - { - hr = RegistrationSessionEnd(&pEngineState->registration, &pEngineState->variables, &pEngineState->packages, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction); - ExitOnFailure(hr, "Failed to end session in per-user process."); - } - - pEngineState->resumeMode = resumeMode; - -LExit: - UserExperienceOnUnregisterComplete(&pEngineState->userExperience, hr); - - return hr; -} - -extern "C" HRESULT ApplyCache( - __in HANDLE hSourceEngineFile, - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_VARIABLES* pVariables, - __in BURN_PLAN* pPlan, - __in HANDLE hPipe, - __inout DWORD* pcOverallProgressTicks, - __inout BOOL* pfRollback - ) -{ - HRESULT hr = S_OK; - DWORD dwCheckpoint = 0; - BURN_CACHE_CONTEXT cacheContext = { }; - BURN_PACKAGE* pPackage = NULL; - - *pfRollback = FALSE; - - hr = UserExperienceOnCacheBegin(pUX); - ExitOnRootFailure(hr, "BA aborted cache."); - - cacheContext.hSourceEngineFile = hSourceEngineFile; - cacheContext.pPayloads = pPlan->pPayloads; - cacheContext.pUX = pUX; - cacheContext.pVariables = pVariables; - cacheContext.qwTotalCacheSize = pPlan->qwCacheSizeTotal; - cacheContext.wzLayoutDirectory = pPlan->sczLayoutDirectory; - - hr = MemAllocArray(reinterpret_cast(&cacheContext.rgSearchPaths), sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); - ExitOnNull(cacheContext.rgSearchPaths, hr, E_OUTOFMEMORY, "Failed to allocate cache search paths array."); - - for (DWORD i = 0; i < pPlan->cCacheActions; ++i) - { - BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + i; - cacheContext.hPipe = hPipe; - pPackage = NULL; - - switch (pCacheAction->type) - { - case BURN_CACHE_ACTION_TYPE_CHECKPOINT: - dwCheckpoint = pCacheAction->checkpoint.dwId; - break; - - case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: - hr = ApplyLayoutBundle(&cacheContext, pCacheAction->bundleLayout.pPayloadGroup, pCacheAction->bundleLayout.sczExecutableName, pCacheAction->bundleLayout.sczUnverifiedPath, pCacheAction->bundleLayout.qwBundleSize); - ExitOnFailure(hr, "Failed cache action: %ls", L"layout bundle"); - - ++(*pcOverallProgressTicks); - - hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks); - LogExitOnFailure(hr, MSG_USER_CANCELED, "Cancel during cache: %ls", L"layout bundle"); - - break; - - case BURN_CACHE_ACTION_TYPE_PACKAGE: - pPackage = pCacheAction->package.pPackage; - - if (!pPackage->fPerMachine && !cacheContext.wzLayoutDirectory) - { - hr = CacheGetCompletedPath(FALSE, pPackage->sczCacheId, &pPackage->sczCacheFolder); - ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", pPackage->sczCacheId); - - cacheContext.hPipe = INVALID_HANDLE_VALUE; - } - - hr = ApplyCachePackage(&cacheContext, pPackage); - ExitOnFailure(hr, "Failed cache action: %ls", L"cache package"); - - ++(*pcOverallProgressTicks); - - hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks); - LogExitOnFailure(hr, MSG_USER_CANCELED, "Cancel during cache: %ls", L"cache package"); - - break; - - case BURN_CACHE_ACTION_TYPE_CONTAINER: - Assert(pPlan->sczLayoutDirectory); - hr = ApplyLayoutContainer(&cacheContext, pCacheAction->container.pContainer); - ExitOnFailure(hr, "Failed cache action: %ls", L"layout container"); - - break; - - case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT: - if (!::SetEvent(pCacheAction->syncpoint.hEvent)) - { - ExitWithLastError(hr, "Failed to set syncpoint event."); - } - break; - - default: - AssertSz(FALSE, "Unknown cache action."); - break; - } - } - -LExit: - if (FAILED(hr)) - { - DoRollbackCache(pUX, pPlan, hPipe, dwCheckpoint); - *pfRollback = TRUE; - } - - // Clean up any remanents in the cache. - if (INVALID_HANDLE_VALUE != hPipe) - { - ElevationCacheCleanup(hPipe); - } - - CacheCleanup(FALSE, pPlan->wzBundleId); - - for (DWORD i = 0; i < cacheContext.cSearchPathsMax; ++i) - { - ReleaseNullStr(cacheContext.rgSearchPaths[i]); - } - ReleaseMem(cacheContext.rgSearchPaths); - ReleaseStr(cacheContext.sczLastUsedFolderCandidate); - - UserExperienceOnCacheComplete(pUX, hr); - return hr; -} - -extern "C" HRESULT ApplyExecute( - __in BURN_ENGINE_STATE* pEngineState, - __in_opt HANDLE hCacheThread, - __inout DWORD* pcOverallProgressTicks, - __out BOOL* pfRollback, - __out BOOL* pfSuspend, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ) -{ - HRESULT hr = S_OK; - HRESULT hrRollback = S_OK; - BURN_EXECUTE_ACTION_CHECKPOINT* pCheckpoint = NULL; - BURN_EXECUTE_CONTEXT context = { }; - BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; - BOOL fSeekNextRollbackBoundary = FALSE; - - context.pUX = &pEngineState->userExperience; - context.cExecutePackagesTotal = pEngineState->plan.cExecutePackagesTotal; - context.pcOverallProgressTicks = pcOverallProgressTicks; - - *pfRollback = FALSE; - *pfSuspend = FALSE; - - // Send execute begin to BA. - hr = UserExperienceOnExecuteBegin(&pEngineState->userExperience, pEngineState->plan.cExecutePackagesTotal); - ExitOnRootFailure(hr, "BA aborted execute begin."); - - // Do execute actions. - for (DWORD i = 0; i < pEngineState->plan.cExecuteActions; ++i) - { - BURN_EXECUTE_ACTION* pExecuteAction = &pEngineState->plan.rgExecuteActions[i]; - if (pExecuteAction->fDeleted) - { - continue; - } - - // If we are seeking the next rollback boundary, skip if this action wasn't it. - if (fSeekNextRollbackBoundary) - { - if (BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY == pExecuteAction->type) - { - continue; - } - else - { - fSeekNextRollbackBoundary = FALSE; - } - } - - // Execute the action. - hr = DoExecuteAction(pEngineState, pExecuteAction, hCacheThread, &context, &pRollbackBoundary, &pCheckpoint, pfSuspend, pRestart); - - if (*pfSuspend || BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart) - { - if (pCheckpoint && pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction) - { - hr = E_INVALIDSTATE; - LogId(REPORT_ERROR, MSG_RESTART_REQUEST_DURING_MSI_TRANSACTION, pCheckpoint->pActiveRollbackBoundary->sczId); - } - else - { - ExitFunction(); - } - } - - if (FAILED(hr)) - { - // If rollback is disabled, keep what we have and always end execution here. - if (pEngineState->plan.fDisableRollback) - { - LogId(REPORT_WARNING, MSG_PLAN_ROLLBACK_DISABLED); - - if (pCheckpoint && pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction) - { - hrRollback = ExecuteMsiCommitTransaction(pEngineState, pCheckpoint->pActiveRollbackBoundary, &context); - IgnoreRollbackError(hrRollback, "Failed commit transaction from disable rollback"); - } - - *pfRollback = TRUE; - break; - } - - if (pCheckpoint) - { - // If inside a MSI transaction, roll it back. - if (pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction) - { - hrRollback = ExecuteMsiRollbackTransaction(pEngineState, pCheckpoint->pActiveRollbackBoundary, &context); - IgnoreRollbackError(hrRollback, "Failed rolling back transaction"); - } - - // The action failed, roll back to previous rollback boundary. - hrRollback = DoRollbackActions(pEngineState, &context, pCheckpoint->dwId, pRestart); - IgnoreRollbackError(hrRollback, "Failed rollback actions"); - } - - // If the rollback boundary is vital, end execution here. - if (pRollbackBoundary && pRollbackBoundary->fVital) - { - *pfRollback = TRUE; - break; - } - - // Move forward to next rollback boundary. - fSeekNextRollbackBoundary = TRUE; - } - } - -LExit: - // Send execute complete to BA. - UserExperienceOnExecuteComplete(&pEngineState->userExperience, hr); - - return hr; -} - -extern "C" void ApplyClean( - __in BURN_USER_EXPERIENCE* /*pUX*/, - __in BURN_PLAN* pPlan, - __in HANDLE hPipe - ) -{ - HRESULT hr = S_OK; - - for (DWORD i = 0; i < pPlan->cCleanActions; ++i) - { - BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + i; - BURN_PACKAGE* pPackage = pCleanAction->pPackage; - - hr = CleanPackage(hPipe, pPackage); - } -} - - -// internal helper functions - -static void CalculateKeepRegistration( - __in BURN_ENGINE_STATE* pEngineState, - __inout BOOL* pfKeepRegistration - ) -{ - LogId(REPORT_STANDARD, MSG_POST_APPLY_CALCULATE_REGISTRATION); - - for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) - { - BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; - - if (BURN_PACKAGE_TYPE_MSP == pPackage->type) - { - MspEngineFinalizeInstallRegistrationState(pPackage); - } - - LogId(REPORT_STANDARD, MSG_POST_APPLY_PACKAGE, pPackage->sczId, LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->installRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->cacheRegistrationState)); - - if (!pPackage->fCanAffectRegistration) - { - continue; - } - - if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState || - BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState) - { - *pfKeepRegistration = TRUE; - } - } -} - -static HRESULT ExecuteDependentRegistrationActions( - __in HANDLE hPipe, - __in const BURN_REGISTRATION* pRegistration, - __in_ecount(cActions) const BURN_DEPENDENT_REGISTRATION_ACTION* rgActions, - __in DWORD cActions - ) -{ - HRESULT hr = S_OK; - - for (DWORD iAction = 0; iAction < cActions; ++iAction) - { - const BURN_DEPENDENT_REGISTRATION_ACTION* pAction = rgActions + iAction; - - if (pRegistration->fPerMachine) - { - hr = ElevationProcessDependentRegistration(hPipe, pAction); - ExitOnFailure(hr, "Failed to execute dependent registration action."); - } - else - { - hr = DependencyProcessDependentRegistration(pRegistration, pAction); - ExitOnFailure(hr, "Failed to process dependency registration action."); - } - } - -LExit: - return hr; -} - -static HRESULT ApplyCachePackage( - __in BURN_CACHE_CONTEXT* pContext, - __in BURN_PACKAGE* pPackage - ) -{ - HRESULT hr = S_OK; - BOOL fCanceledBegin = FALSE; - BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION cachePackageCompleteAction = BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE; - - for (;;) - { - fCanceledBegin = FALSE; - - hr = UserExperienceOnCachePackageBegin(pContext->pUX, pPackage->sczId, pPackage->payloads.cItems, pPackage->payloads.qwTotalSize); - if (FAILED(hr)) - { - fCanceledBegin = TRUE; - } - else - { - for (DWORD i = 0; i < pPackage->payloads.cItems; ++i) - { - BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem = pPackage->payloads.rgItems + i; - - hr = ApplyProcessPayload(pContext, pPackage, pPayloadGroupItem); - if (FAILED(hr)) - { - break; - } - } - } - - pPackage->hrCacheResult = hr; - cachePackageCompleteAction = SUCCEEDED(hr) || pPackage->fVital || fCanceledBegin ? BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE; - UserExperienceOnCachePackageComplete(pContext->pUX, pPackage->sczId, hr, &cachePackageCompleteAction); - - if (SUCCEEDED(hr)) - { - break; - } - - if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_RETRY == cachePackageCompleteAction) - { - for (DWORD i = 0; i < pPackage->payloads.cItems; ++i) - { - BURN_PAYLOAD_GROUP_ITEM* pItem = pPackage->payloads.rgItems + i; - if (pItem->fCached) - { - pItem->pPayload->cRemainingInstances += 1; - pItem->fCached = FALSE; - } - - if (pItem->qwCommittedCacheProgress) - { - pContext->qwSuccessfulCacheProgress -= pItem->qwCommittedCacheProgress; - pItem->qwCommittedCacheProgress = 0; - } - } - - LogErrorId(hr, MSG_CACHE_RETRYING_PACKAGE, pPackage->sczId, NULL, NULL); - - continue; - } - else if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE == cachePackageCompleteAction && !pPackage->fVital) // ignore non-vital download failures. - { - LogId(REPORT_STANDARD, MSG_CACHE_CONTINUING_NONVITAL_PACKAGE, pPackage->sczId, hr); - hr = S_OK; - } - else if (fCanceledBegin) - { - LogExitOnFailure(hr, MSG_USER_CANCELED, "Cancel during cache: %ls: %ls", L"begin cache package", pPackage->sczId); - } - - break; - } - -LExit: - return hr; -} - -static HRESULT ApplyExtractContainer( - __in BURN_CACHE_CONTEXT* pContext, - __in BURN_CONTAINER* pContainer - ) -{ - HRESULT hr = S_OK; - - if (pContainer->qwCommittedCacheProgress) - { - pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedCacheProgress; - pContainer->qwCommittedCacheProgress = 0; - } - - if (pContainer->qwCommittedExtractProgress) - { - pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedExtractProgress; - pContainer->qwCommittedExtractProgress = 0; - } - - if (!pContainer->fActuallyAttached) - { - hr = ApplyAcquireContainerOrPayload(pContext, pContainer, NULL, NULL); - LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_CONTAINER, "Failed to acquire container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath); - } - - hr = ExtractContainer(pContext, pContainer); - LogExitOnFailure(hr, MSG_FAILED_EXTRACT_CONTAINER, "Failed to extract payloads from container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath); - - if (pContext->sczLastUsedFolderCandidate) - { - // We successfully copied from a source location, set that as the last used source. - CacheSetLastUsedSource(pContext->pVariables, pContext->sczLastUsedFolderCandidate, pContainer->sczFilePath); - } - - if (pContainer->qwExtractSizeTotal < pContainer->qwCommittedExtractProgress) - { - AssertSz(FALSE, "Container extracted more than planned."); - pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedExtractProgress; - pContext->qwSuccessfulCacheProgress += pContainer->qwExtractSizeTotal; - } - else - { - pContext->qwSuccessfulCacheProgress += pContainer->qwExtractSizeTotal - pContainer->qwCommittedExtractProgress; - } - - pContainer->qwCommittedExtractProgress = pContainer->qwExtractSizeTotal; - -LExit: - ReleaseNullStr(pContext->sczLastUsedFolderCandidate); - - return hr; -} - -static HRESULT ApplyLayoutBundle( - __in BURN_CACHE_CONTEXT* pContext, - __in BURN_PAYLOAD_GROUP* pPayloads, - __in_z LPCWSTR wzExecutableName, - __in_z LPCWSTR wzUnverifiedPath, - __in DWORD64 qwBundleSize - ) -{ - HRESULT hr = S_OK; - - hr = LayoutBundle(pContext, wzExecutableName, wzUnverifiedPath, qwBundleSize); - ExitOnFailure(hr, "Failed to layout bundle."); - - for (DWORD i = 0; i < pPayloads->cItems; ++i) - { - BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem = pPayloads->rgItems + i; - - hr = ApplyProcessPayload(pContext, NULL, pPayloadGroupItem); - ExitOnFailure(hr, "Failed to layout bundle payload: %ls", pPayloadGroupItem->pPayload->sczKey); - } - -LExit: - return hr; -} - -static HRESULT ApplyLayoutContainer( - __in BURN_CACHE_CONTEXT* pContext, - __in BURN_CONTAINER* pContainer - ) -{ - HRESULT hr = S_OK; - DWORD cTryAgainAttempts = 0; - BOOL fRetry = FALSE; - - Assert(!pContainer->fAttached); - - hr = ApplyCacheVerifyContainerOrPayload(pContext, pContainer, NULL, NULL); - if (SUCCEEDED(hr)) - { - ExitFunction(); - } - - for (;;) - { - fRetry = FALSE; - - hr = ApplyAcquireContainerOrPayload(pContext, pContainer, NULL, NULL); - LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_CONTAINER, "Failed to acquire container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath); - - hr = LayoutOrCacheContainerOrPayload(pContext, pContainer, NULL, NULL, cTryAgainAttempts, &fRetry); - if (SUCCEEDED(hr)) - { - break; - } - else - { - LogErrorId(hr, MSG_FAILED_LAYOUT_CONTAINER, pContainer->sczId, pContext->wzLayoutDirectory, pContainer->sczUnverifiedPath); - - if (!fRetry) - { - ExitFunction(); - } - - ++cTryAgainAttempts; - pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedCacheProgress; - pContainer->qwCommittedCacheProgress = 0; - ReleaseNullStr(pContext->sczLastUsedFolderCandidate); - LogErrorId(hr, MSG_CACHE_RETRYING_CONTAINER, pContainer->sczId, NULL, NULL); - } - } - -LExit: - ReleaseNullStr(pContext->sczLastUsedFolderCandidate); - - return hr; -} - -static HRESULT ApplyProcessPayload( - __in BURN_CACHE_CONTEXT* pContext, - __in_opt BURN_PACKAGE* pPackage, - __in BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem - ) -{ - HRESULT hr = S_OK; - DWORD cTryAgainAttempts = 0; - BOOL fRetry = FALSE; - BURN_PAYLOAD* pPayload = pPayloadGroupItem->pPayload; - - Assert(pContext->pPayloads && pPackage || pContext->wzLayoutDirectory); - - if (pPayload->pContainer && pContext->wzLayoutDirectory) - { - ExitFunction(); - } - - hr = ApplyCacheVerifyContainerOrPayload(pContext, NULL, pPackage, pPayloadGroupItem); - if (SUCCEEDED(hr)) - { - ExitFunction(); - } - - for (;;) - { - fRetry = FALSE; - - hr = ApplyAcquireContainerOrPayload(pContext, NULL, pPackage, pPayloadGroupItem); - LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_PAYLOAD, "Failed to acquire payload: %ls to working path: %ls", pPayload->sczKey, pPayload->sczUnverifiedPath); - - hr = LayoutOrCacheContainerOrPayload(pContext, NULL, pPackage, pPayloadGroupItem, cTryAgainAttempts, &fRetry); - if (SUCCEEDED(hr)) - { - break; - } - else - { - LogErrorId(hr, pContext->wzLayoutDirectory ? MSG_FAILED_LAYOUT_PAYLOAD : MSG_FAILED_CACHE_PAYLOAD, pPayload->sczKey, pContext->wzLayoutDirectory, pPayload->sczUnverifiedPath); - - if (!fRetry) - { - ExitFunction(); - } - - ++cTryAgainAttempts; - pContext->qwSuccessfulCacheProgress -= pPayloadGroupItem->qwCommittedCacheProgress; - pPayloadGroupItem->qwCommittedCacheProgress = 0; - ReleaseNullStr(pContext->sczLastUsedFolderCandidate); - LogErrorId(hr, MSG_CACHE_RETRYING_PAYLOAD, pPayload->sczKey, NULL, NULL); - } - } - -LExit: - ReleaseNullStr(pContext->sczLastUsedFolderCandidate); - - return hr; -} - -static HRESULT ApplyCacheVerifyContainerOrPayload( - __in BURN_CACHE_CONTEXT* pContext, - __in_opt BURN_CONTAINER* pContainer, - __in_opt BURN_PACKAGE* pPackage, - __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem - ) -{ - AssertSz(pContainer || pPayloadGroupItem, "Must provide a container or a payload."); - - HRESULT hr = S_OK; - BURN_CACHE_PROGRESS_CONTEXT progress = { }; - - progress.pCacheContext = pContext; - progress.pContainer = pContainer; - progress.pPackage = pPackage; - progress.pPayloadGroupItem = pPayloadGroupItem; - - if (pContainer) - { - hr = CacheVerifyContainer(pContainer, pContext->wzLayoutDirectory, CacheMessageHandler, CacheProgressRoutine, &progress); - } - else if (!pContext->wzLayoutDirectory && INVALID_HANDLE_VALUE != pContext->hPipe) - { - hr = ElevationCacheVerifyPayload(pContext->hPipe, pPackage, pPayloadGroupItem->pPayload, CacheMessageHandler, CacheProgressRoutine, &progress); - } - else - { - hr = CacheVerifyPayload(pPayloadGroupItem->pPayload, pContext->wzLayoutDirectory ? pContext->wzLayoutDirectory : pPackage->sczCacheFolder, CacheMessageHandler, CacheProgressRoutine, &progress); - } - - return hr; -} - -static HRESULT ExtractContainer( - __in BURN_CACHE_CONTEXT* pContext, - __in BURN_CONTAINER* pContainer - ) -{ - HRESULT hr = S_OK; - BURN_CONTAINER_CONTEXT context = { }; - HANDLE hContainerHandle = INVALID_HANDLE_VALUE; - LPWSTR sczStreamName = NULL; - BURN_PAYLOAD* pExtract = NULL; - BURN_CACHE_PROGRESS_CONTEXT progress = { }; - - progress.pCacheContext = pContext; - progress.pContainer = pContainer; - progress.type = BURN_CACHE_PROGRESS_TYPE_EXTRACT; - - // If the container is actually attached, then it was planned to be acquired through hSourceEngineFile. - if (pContainer->fActuallyAttached) - { - hContainerHandle = pContext->hSourceEngineFile; - } - - hr = ContainerOpen(&context, pContainer, hContainerHandle, pContainer->sczUnverifiedPath); - ExitOnFailure(hr, "Failed to open container: %ls.", pContainer->sczId); - - while (S_OK == (hr = ContainerNextStream(&context, &sczStreamName))) - { - BOOL fExtracted = FALSE; - - hr = PayloadFindEmbeddedBySourcePath(pContext->pPayloads, sczStreamName, &pExtract); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to find embedded payload by source path: %ls container: %ls", sczStreamName, pContainer->sczId); - - // Skip payloads that weren't planned or have already been cached. - if (pExtract->sczUnverifiedPath && pExtract->cRemainingInstances) - { - progress.pPayload = pExtract; - - hr = PreparePayloadDestinationPath(pExtract->sczUnverifiedPath); - ExitOnFailure(hr, "Failed to prepare payload destination path: %ls", pExtract->sczUnverifiedPath); - - hr = UserExperienceOnCachePayloadExtractBegin(pContext->pUX, pContainer->sczId, pExtract->sczKey); - if (FAILED(hr)) - { - UserExperienceOnCachePayloadExtractComplete(pContext->pUX, pContainer->sczId, pExtract->sczKey, hr); - ExitOnRootFailure(hr, "BA aborted cache payload extract begin."); - } - - // TODO: Send progress when extracting stream to file. - hr = ContainerStreamToFile(&context, pExtract->sczUnverifiedPath); - // Error handling happens after sending complete message to BA. - - // If succeeded, send 100% complete here to make sure progress was sent to the BA. - if (SUCCEEDED(hr)) - { - hr = CompleteCacheProgress(&progress, pExtract->qwFileSize); - } - - UserExperienceOnCachePayloadExtractComplete(pContext->pUX, pContainer->sczId, pExtract->sczKey, hr); - ExitOnFailure(hr, "Failed to extract payload: %ls from container: %ls", sczStreamName, pContainer->sczId); - - fExtracted = TRUE; - } - } - - if (!fExtracted) - { - hr = ContainerSkipStream(&context); - ExitOnFailure(hr, "Failed to skip the extraction of payload: %ls from container: %ls", sczStreamName, pContainer->sczId); - } - } - - if (E_NOMOREITEMS == hr) - { - hr = S_OK; - } - ExitOnFailure(hr, "Failed to extract all payloads from container: %ls", pContainer->sczId); - -LExit: - ReleaseStr(sczStreamName); - ContainerClose(&context); - - return hr; -} - -static HRESULT LayoutBundle( - __in BURN_CACHE_CONTEXT* pContext, - __in_z LPCWSTR wzExecutableName, - __in_z LPCWSTR wzUnverifiedPath, - __in DWORD64 qwBundleSize - ) -{ - HRESULT hr = S_OK; - LPWSTR sczBundlePath = NULL; - LPWSTR sczBundleDownloadUrl = NULL; - LPWSTR sczDestinationPath = NULL; - int nEquivalentPaths = 0; - BOOTSTRAPPER_CACHE_OPERATION cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE; - BURN_CACHE_PROGRESS_CONTEXT progress = { }; - BOOL fRetry = FALSE; - BOOL fRetryAcquire = FALSE; - BOOL fCanceledBegin = FALSE; - - progress.pCacheContext = pContext; - - hr = VariableGetString(pContext->pVariables, BURN_BUNDLE_SOURCE_PROCESS_PATH, &sczBundlePath); - if (FAILED(hr)) - { - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get path to bundle source process path to layout."); - } - - hr = PathForCurrentProcess(&sczBundlePath, NULL); - ExitOnFailure(hr, "Failed to get path to bundle to layout."); - } - - hr = PathConcat(pContext->wzLayoutDirectory, wzExecutableName, &sczDestinationPath); - ExitOnFailure(hr, "Failed to concat layout path for bundle."); - - // If the destination path is the currently running bundle, bail. - hr = PathCompare(sczBundlePath, sczDestinationPath, &nEquivalentPaths); - ExitOnFailure(hr, "Failed to determine if layout bundle path was equivalent with current process path."); - - if (CSTR_EQUAL == nEquivalentPaths && FileExistsEx(sczDestinationPath, NULL)) - { - hr = UserExperienceOnCacheContainerOrPayloadVerifyBegin(pContext->pUX, NULL, NULL); - if (FAILED(hr)) - { - UserExperienceOnCacheContainerOrPayloadVerifyComplete(pContext->pUX, NULL, NULL, hr); - ExitOnRootFailure(hr, "BA aborted cache payload verify begin."); - } - - progress.type = BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY; - hr = CompleteCacheProgress(&progress, qwBundleSize); - - UserExperienceOnCacheContainerOrPayloadVerifyComplete(pContext->pUX, NULL, NULL, hr); - - ExitFunction(); - } - - do - { - hr = S_OK; - fRetry = FALSE; - progress.type = BURN_CACHE_PROGRESS_TYPE_ACQUIRE; - - for (;;) - { - fRetryAcquire = FALSE; - progress.fCancel = FALSE; - fCanceledBegin = FALSE; - - hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, NULL, NULL, &sczBundlePath, &sczBundleDownloadUrl, NULL, &cacheOperation); - - if (FAILED(hr)) - { - fCanceledBegin = TRUE; - } - else - { - hr = CopyPayload(&progress, pContext->hSourceEngineFile, sczBundlePath, wzUnverifiedPath); - // Error handling happens after sending complete message to BA. - - // If succeeded, send 100% complete here to make sure progress was sent to the BA. - if (SUCCEEDED(hr)) - { - hr = CompleteCacheProgress(&progress, qwBundleSize); - } - } - - UserExperienceOnCacheAcquireComplete(pContext->pUX, NULL, NULL, hr, &fRetryAcquire); - if (fRetryAcquire) - { - continue; - } - else if (fCanceledBegin) - { - ExitOnRootFailure(hr, "BA aborted cache acquire begin."); - } - - ExitOnFailure(hr, "Failed to copy bundle from: '%ls' to: '%ls'", sczBundlePath, wzUnverifiedPath); - break; - } - - do - { - fCanceledBegin = FALSE; - - hr = UserExperienceOnCacheVerifyBegin(pContext->pUX, NULL, NULL); - - if (FAILED(hr)) - { - fCanceledBegin = TRUE; - } - else - { - hr = CacheLayoutBundle(wzExecutableName, pContext->wzLayoutDirectory, wzUnverifiedPath, qwBundleSize, CacheMessageHandler, CacheProgressRoutine, &progress); - } - - BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE; - UserExperienceOnCacheVerifyComplete(pContext->pUX, NULL, NULL, hr, &action); - if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action) - { - hr = S_FALSE; // retry verify. - } - else if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION == action) - { - fRetry = TRUE; // go back and retry acquire. - } - else if (fCanceledBegin) - { - ExitOnRootFailure(hr, "BA aborted cache verify begin."); - } - } while (S_FALSE == hr); - - if (fRetry) - { - pContext->qwSuccessfulCacheProgress -= qwBundleSize; // Acquire - } - } while (fRetry); - LogExitOnFailure(hr, MSG_FAILED_LAYOUT_BUNDLE, "Failed to layout bundle: %ls to layout directory: %ls", sczBundlePath, pContext->wzLayoutDirectory); - -LExit: - ReleaseStr(sczDestinationPath); - ReleaseStr(sczBundleDownloadUrl); - ReleaseStr(sczBundlePath); - - return hr; -} - -static HRESULT ApplyAcquireContainerOrPayload( - __in BURN_CACHE_CONTEXT* pContext, - __in_opt BURN_CONTAINER* pContainer, - __in_opt BURN_PACKAGE* pPackage, - __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem - ) -{ - AssertSz(pContainer || pPayloadGroupItem, "Must provide a container or a payload."); - - HRESULT hr = S_OK; - BURN_CACHE_PROGRESS_CONTEXT progress = { }; - BOOL fRetry = FALSE; - - progress.pCacheContext = pContext; - progress.type = BURN_CACHE_PROGRESS_TYPE_ACQUIRE; - progress.pContainer = pContainer; - progress.pPackage = pPackage; - progress.pPayloadGroupItem = pPayloadGroupItem; - - do - { - hr = AcquireContainerOrPayload(&progress, &fRetry); - - if (fRetry) - { - LogErrorId(hr, pContainer ? MSG_APPLY_RETRYING_ACQUIRE_CONTAINER : MSG_APPLY_RETRYING_ACQUIRE_PAYLOAD, pContainer ? pContainer->sczId : pPayloadGroupItem->pPayload->sczKey, NULL, NULL); - hr = S_OK; - } - - ExitOnFailure(hr, "Failed to acquire %hs: %ls", pContainer ? "container" : "payload", pContainer ? pContainer->sczId : pPayloadGroupItem->pPayload->sczKey); - } while (fRetry); - -LExit: - return hr; -} - -static HRESULT AcquireContainerOrPayload( - __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, - __out BOOL* pfRetry - ) -{ - BURN_CACHE_CONTEXT* pContext = pProgress->pCacheContext; - BURN_CONTAINER* pContainer = pProgress->pContainer; - BURN_PACKAGE* pPackage = pProgress->pPackage; - BURN_PAYLOAD* pPayload = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload : NULL; - AssertSz(pContainer || pPayload, "Must provide a container or a payload."); - - HRESULT hr = S_OK; - int nEquivalentPaths = 0; - LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : NULL; - LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : NULL; - LPCWSTR wzPayloadContainerId = pPayload && pPayload->pContainer ? pPayload->pContainer->sczId : NULL; - LPCWSTR wzDestinationPath = pContainer ? pContainer->sczUnverifiedPath: pPayload->sczUnverifiedPath; - LPCWSTR wzRelativePath = pContainer ? pContainer->sczFilePath : pPayload->sczFilePath; - DWORD dwChosenSearchPath = 0; - DWORD dwDestinationSearchPath = 0; - BOOTSTRAPPER_CACHE_OPERATION cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE; - BOOTSTRAPPER_CACHE_RESOLVE_OPERATION resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_NONE; - LPWSTR* pwzDownloadUrl = pContainer ? &pContainer->downloadSource.sczUrl : &pPayload->downloadSource.sczUrl; - LPWSTR* pwzSourcePath = pContainer ? &pContainer->sczSourcePath : &pPayload->sczSourcePath; - BOOL fFoundLocal = FALSE; - BOOL fPreferExtract = FALSE; - DWORD64 qwFileSize = 0; - BOOL fMinimumFileSize = FALSE; - - if (pContainer) - { - if (pContainer->fAttached) - { - fMinimumFileSize = TRUE; - qwFileSize = pContainer->qwAttachedOffset + pContainer->qwFileSize; - } - else if (pContainer->pbHash && pContext->wzLayoutDirectory) - { - qwFileSize = pContainer->qwFileSize; - } - } - else if (pPayload->pbHash) - { - qwFileSize = pPayload->qwFileSize; - } - - pContext->cSearchPaths = 0; - *pfRetry = FALSE; - pProgress->fCancel = FALSE; - - hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId, pwzSourcePath, pwzDownloadUrl, wzPayloadContainerId, &cacheOperation); - ExitOnRootFailure(hr, "BA aborted cache acquire begin."); - - // Skip the Resolving event and probing local paths if the BA already knew it wanted to download or extract. - if (BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD != cacheOperation && - BOOTSTRAPPER_CACHE_OPERATION_EXTRACT != cacheOperation) - { - do - { - fFoundLocal = FALSE; - fPreferExtract = FALSE; - resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_NONE; - dwChosenSearchPath = 0; - dwDestinationSearchPath = 0; - - hr = CacheGetLocalSourcePaths(wzRelativePath, *pwzSourcePath, wzDestinationPath, pContext->wzLayoutDirectory, pContext->pVariables, &pContext->rgSearchPaths, &pContext->cSearchPaths, &dwChosenSearchPath, &dwDestinationSearchPath); - ExitOnFailure(hr, "Failed to search local source."); - - if (wzPayloadContainerId) - { - // When a payload comes from a container, the container has the highest chance of being correct. - // But we want to avoid extracting the container multiple times. - // So only consider the destination path, which means the container was already extracted. - if (IsValidLocalFile(pContext->rgSearchPaths[dwDestinationSearchPath], qwFileSize, fMinimumFileSize)) - { - fFoundLocal = TRUE; - dwChosenSearchPath = dwDestinationSearchPath; - } - else // don't prefer the container if extracting it already failed. - { - fPreferExtract = SUCCEEDED(pPayload->pContainer->hrExtract); - } - } - - if (!fFoundLocal) - { - for (DWORD i = 0; i < pContext->cSearchPaths; ++i) - { - // If the file exists locally with the correct size, choose it. - if (IsValidLocalFile(pContext->rgSearchPaths[i], qwFileSize, fMinimumFileSize)) - { - dwChosenSearchPath = i; - - fFoundLocal = TRUE; - break; - } - } - } - - if (BOOTSTRAPPER_CACHE_OPERATION_COPY == cacheOperation) - { - if (fFoundLocal) - { - resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_LOCAL; - } - } - else - { - if (fPreferExtract) // the file comes from a container which hasn't been extracted yet, so extract it. - { - resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER; - } - else if (fFoundLocal) // the file exists locally, so copy it. - { - resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_LOCAL; - } - else if (*pwzDownloadUrl && **pwzDownloadUrl) - { - resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_DOWNLOAD; - } - else if (wzPayloadContainerId) - { - resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER; - } - } - - // Let the BA have a chance to override the source. - hr = UserExperienceOnCacheAcquireResolving(pContext->pUX, wzPackageOrContainerId, wzPayloadId, pContext->rgSearchPaths, pContext->cSearchPaths, fFoundLocal, &dwChosenSearchPath, pwzDownloadUrl, wzPayloadContainerId, &resolveOperation); - ExitOnRootFailure(hr, "BA aborted cache acquire resolving."); - - switch (resolveOperation) - { - case BOOTSTRAPPER_CACHE_RESOLVE_LOCAL: - cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_COPY; - break; - case BOOTSTRAPPER_CACHE_RESOLVE_DOWNLOAD: - cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD; - break; - case BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER: - cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_EXTRACT; - break; - case BOOTSTRAPPER_CACHE_RESOLVE_RETRY: - pContext->cSearchPathsMax = max(pContext->cSearchPaths, pContext->cSearchPathsMax); - break; - } - } while (BOOTSTRAPPER_CACHE_RESOLVE_RETRY == resolveOperation); - } - - switch (cacheOperation) - { - case BOOTSTRAPPER_CACHE_OPERATION_COPY: - // If the source path and destination path are different, do the copy (otherwise there's no point). - hr = PathCompare(pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath, &nEquivalentPaths); - ExitOnFailure(hr, "Failed to determine if payload paths were equivalent, source: %ls, destination: %ls.", pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath); - - if (CSTR_EQUAL != nEquivalentPaths) - { - hr = CopyPayload(pProgress, INVALID_HANDLE_VALUE, pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath); - ExitOnFailure(hr, "Failed to copy payload: %ls", wzPayloadId); - - // Store the source path so it can be used as the LastUsedFolder if it passes verification. - pContext->sczLastUsedFolderCandidate = pContext->rgSearchPaths[dwChosenSearchPath]; - pContext->rgSearchPaths[dwChosenSearchPath] = NULL; - } - - break; - case BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD: - hr = DownloadPayload(pProgress, wzDestinationPath); - ExitOnFailure(hr, "Failed to download payload: %ls", wzPayloadId); - - break; - case BOOTSTRAPPER_CACHE_OPERATION_EXTRACT: - Assert(pPayload && pPayload->pContainer); - - hr = ApplyExtractContainer(pContext, pPayload->pContainer); - ExitOnFailure(hr, "Failed to extract container for payload: %ls", wzPayloadId); - - break; - default: - hr = E_FILENOTFOUND; - LogExitOnFailure(hr, MSG_RESOLVE_SOURCE_FAILED, "Failed to resolve source, payload: %ls, package: %ls, container: %ls", wzPayloadId, pPackage ? pPackage->sczId : NULL, pContainer ? pContainer->sczId : NULL); - } - - // Send 100% complete here. This is sometimes the only progress sent to the BA. - hr = CompleteCacheProgress(pProgress, pContainer ? pContainer->qwFileSize : pPayload->qwFileSize); - -LExit: - if (BOOTSTRAPPER_CACHE_OPERATION_EXTRACT == cacheOperation) - { - if (FAILED(hr) && SUCCEEDED(pPayload->pContainer->hrExtract) && - (fFoundLocal || pPayload->downloadSource.sczUrl && *pPayload->downloadSource.sczUrl)) - { - *pfRetry = TRUE; - } - pPayload->pContainer->hrExtract = hr; - } - UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, pfRetry); - - pContext->cSearchPathsMax = max(pContext->cSearchPaths, pContext->cSearchPathsMax); - - return hr; -} - -static BOOL IsValidLocalFile( - __in_z LPCWSTR wzFilePath, - __in DWORD64 qwFileSize, - __in BOOL fMinimumFileSize - ) -{ - LONGLONG llFileSize = 0; - - if (!qwFileSize) - { - return FileExistsEx(wzFilePath, NULL); - } - else - { - return SUCCEEDED(FileSize(wzFilePath, &llFileSize)) && - (static_cast(llFileSize) == qwFileSize || - fMinimumFileSize && static_cast(llFileSize) > qwFileSize); - } -} - -static HRESULT LayoutOrCacheContainerOrPayload( - __in BURN_CACHE_CONTEXT* pContext, - __in_opt BURN_CONTAINER* pContainer, - __in_opt BURN_PACKAGE* pPackage, - __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem, - __in DWORD cTryAgainAttempts, - __out BOOL* pfRetry - ) -{ - HRESULT hr = S_OK; - BURN_PAYLOAD* pPayload = pPayloadGroupItem ? pPayloadGroupItem->pPayload : NULL; - LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : L""; - LPCWSTR wzUnverifiedPath = pContainer ? pContainer->sczUnverifiedPath : pPayload->sczUnverifiedPath; - LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : L""; - BOOL fCanAffectRegistration = FALSE; - BURN_CACHE_PROGRESS_CONTEXT progress = { }; - BOOL fMove = !pPayload || 1 == pPayload->cRemainingInstances; - BOOL fCanceledBegin = FALSE; - - if (pContainer) - { - Assert(!pPayloadGroupItem); - } - else - { - Assert(pPayload); - AssertSz(0 < pPayload->cRemainingInstances, "Laying out payload more times than planned."); - AssertSz(!pPayloadGroupItem->fCached, "Laying out payload group item that was already cached."); - } - - if (!pContext->wzLayoutDirectory) - { - Assert(!pContainer); - Assert(pPackage); - - fCanAffectRegistration = pPackage->fCanAffectRegistration; - } - - *pfRetry = FALSE; - progress.pCacheContext = pContext; - progress.pContainer = pContainer; - progress.pPackage = pPackage; - progress.pPayloadGroupItem = pPayloadGroupItem; - - do - { - fCanceledBegin = FALSE; - - hr = UserExperienceOnCacheVerifyBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId); - - if (FAILED(hr)) - { - fCanceledBegin = TRUE; - } - else - { - if (pContext->wzLayoutDirectory) // layout the container or payload. - { - if (pContainer) - { - hr = CacheLayoutContainer(pContainer, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress); - } - else - { - hr = CacheLayoutPayload(pPayload, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress); - } - } - else if (INVALID_HANDLE_VALUE != pContext->hPipe) // pass the decision off to the elevated process. - { - hr = ElevationCacheCompletePayload(pContext->hPipe, pPackage, pPayload, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress); - } - else // complete the payload. - { - hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress); - } - } - - if (SUCCEEDED(hr) && fCanAffectRegistration) - { - pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } - - BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = FAILED(hr) && !fCanceledBegin && cTryAgainAttempts < BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS ? BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION : BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE; - UserExperienceOnCacheVerifyComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &action); - if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action) - { - hr = S_FALSE; // retry verify. - } - else if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION == action) - { - *pfRetry = TRUE; // go back and retry acquire. - } - else if (fCanceledBegin) - { - ExitOnRootFailure(hr, "BA aborted cache verify begin."); - } - } while (S_FALSE == hr); - - if (SUCCEEDED(hr) && pPayloadGroupItem) - { - pPayload->cRemainingInstances -= 1; - pPayloadGroupItem->fCached = TRUE; - } - -LExit: - return hr; -} - -static HRESULT PreparePayloadDestinationPath( - __in_z LPCWSTR wzDestinationPath - ) -{ - HRESULT hr = S_OK; - DWORD dwFileAttributes = 0; - - // If the destination file already exists, clear the readonly bit to avoid E_ACCESSDENIED. - if (FileExistsEx(wzDestinationPath, &dwFileAttributes)) - { - if (FILE_ATTRIBUTE_READONLY & dwFileAttributes) - { - dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY; - if (!::SetFileAttributes(wzDestinationPath, dwFileAttributes)) - { - ExitWithLastError(hr, "Failed to clear readonly bit on payload destination path: %ls", wzDestinationPath); - } - } - } - -LExit: - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) - { - hr = S_OK; - } - - return hr; -} - -static HRESULT CopyPayload( - __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, - __in HANDLE hSourceFile, - __in_z LPCWSTR wzSourcePath, - __in_z LPCWSTR wzDestinationPath - ) -{ - HRESULT hr = S_OK; - LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L""; - LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : L""; - HANDLE hDestinationFile = INVALID_HANDLE_VALUE; - HANDLE hSourceOpenedFile = INVALID_HANDLE_VALUE; - - DWORD dwLogId = pProgress->pContainer ? MSG_ACQUIRE_CONTAINER : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; - LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "copy", wzSourcePath); - - hr = PreparePayloadDestinationPath(wzDestinationPath); - ExitOnFailure(hr, "Failed to prepare payload destination path: %ls", wzDestinationPath); - - if (INVALID_HANDLE_VALUE == hSourceFile) - { - hSourceOpenedFile = ::CreateFileW(wzSourcePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); - if (INVALID_HANDLE_VALUE == hSourceOpenedFile) - { - ExitWithLastError(hr, "Failed to open source file to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); - } - - hSourceFile = hSourceOpenedFile; - } - else - { - hr = FileSetPointer(hSourceFile, 0, NULL, FILE_BEGIN); - ExitOnRootFailure(hr, "Failed to read from start of source file to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); - } - - hDestinationFile = ::CreateFileW(wzDestinationPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); - if (INVALID_HANDLE_VALUE == hDestinationFile) - { - ExitWithLastError(hr, "Failed to open destination file to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); - } - - hr = FileCopyUsingHandlesWithProgress(hSourceFile, hDestinationFile, 0, CacheProgressRoutine, pProgress); - if (FAILED(hr)) - { - if (pProgress->fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - ExitOnRootFailure(hr, "BA aborted copy of payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); - } - else - { - ExitOnRootFailure(hr, "Failed attempt to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); - } - } - -LExit: - ReleaseFileHandle(hDestinationFile); - ReleaseFileHandle(hSourceOpenedFile); - - return hr; -} - -static HRESULT DownloadPayload( - __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, - __in_z LPCWSTR wzDestinationPath - ) -{ - HRESULT hr = S_OK; - LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L""; - LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : L""; - DOWNLOAD_SOURCE* pDownloadSource = pProgress->pContainer ? &pProgress->pContainer->downloadSource : &pProgress->pPayloadGroupItem->pPayload->downloadSource; - DWORD64 qwDownloadSize = pProgress->pContainer ? pProgress->pContainer->qwFileSize : pProgress->pPayloadGroupItem->pPayload->qwFileSize; - DOWNLOAD_CACHE_CALLBACK cacheCallback = { }; - DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { }; - APPLY_AUTHENTICATION_REQUIRED_DATA authenticationData = { }; - - DWORD dwLogId = pProgress->pContainer ? MSG_ACQUIRE_CONTAINER : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; - LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "download", pDownloadSource->sczUrl); - - hr = PreparePayloadDestinationPath(wzDestinationPath); - ExitOnFailure(hr, "Failed to prepare payload destination path: %ls", wzDestinationPath); - - cacheCallback.pfnProgress = CacheProgressRoutine; - cacheCallback.pfnCancel = NULL; // TODO: set this - cacheCallback.pv = pProgress; - - authenticationData.pUX = pProgress->pCacheContext->pUX; - authenticationData.wzPackageOrContainerId = wzPackageOrContainerId; - authenticationData.wzPayloadId = wzPayloadId; - authenticationCallback.pv = static_cast(&authenticationData); - authenticationCallback.pfnAuthenticate = &AuthenticationRequired; - - hr = DownloadUrl(pDownloadSource, qwDownloadSize, wzDestinationPath, &cacheCallback, &authenticationCallback); - ExitOnFailure(hr, "Failed attempt to download URL: '%ls' to: '%ls'", pDownloadSource->sczUrl, wzDestinationPath); - -LExit: - return hr; -} - -static HRESULT WINAPI AuthenticationRequired( - __in LPVOID pData, - __in HINTERNET hUrl, - __in long lHttpCode, - __out BOOL* pfRetrySend, - __out BOOL* pfRetry - ) -{ - Assert(401 == lHttpCode || 407 == lHttpCode); - - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - BOOTSTRAPPER_ERROR_TYPE errorType = (401 == lHttpCode) ? BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER : BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY; - LPWSTR sczError = NULL; - int nResult = IDNOACTION; - - *pfRetrySend = FALSE; - *pfRetry = FALSE; - - hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL); - ExitOnFailure(hr, "Failed to allocation error string."); - - APPLY_AUTHENTICATION_REQUIRED_DATA* authenticationData = reinterpret_cast(pData); - - UserExperienceOnError(authenticationData->pUX, errorType, authenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value; - nResult = UserExperienceCheckExecuteResult(authenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult); - if (IDTRYAGAIN == nResult && authenticationData->pUX->hwndApply) - { - er = ::InternetErrorDlg(authenticationData->pUX->hwndApply, hUrl, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL); - if (ERROR_SUCCESS == er || ERROR_CANCELLED == er) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - else if (ERROR_INTERNET_FORCE_RETRY == er) - { - *pfRetrySend = TRUE; - hr = S_OK; - } - else - { - hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); - } - } - else if (IDRETRY == nResult) - { - *pfRetry = TRUE; - hr = S_OK; - } - else - { - hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); - } - -LExit: - ReleaseStr(sczError); - - return hr; -} - -static HRESULT CALLBACK CacheMessageHandler( - __in BURN_CACHE_MESSAGE* pMessage, - __in LPVOID pvContext - ) -{ - HRESULT hr = S_OK; - BURN_CACHE_PROGRESS_CONTEXT* pProgress = static_cast(pvContext); - LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : NULL; - LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : pProgress->pPayload ? pProgress->pPayload->sczKey : NULL; - - switch (pMessage->type) - { - case BURN_CACHE_MESSAGE_BEGIN: - switch (pMessage->begin.cacheStep) - { - case BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE: - pProgress->type = BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY; - hr = UserExperienceOnCacheContainerOrPayloadVerifyBegin(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId); - break; - case BURN_CACHE_STEP_HASH_TO_SKIP_VERIFY: - pProgress->type = BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY; - break; - case BURN_CACHE_STEP_STAGE: - pProgress->type = BURN_CACHE_PROGRESS_TYPE_STAGE; - break; - case BURN_CACHE_STEP_HASH: - pProgress->type = BURN_CACHE_PROGRESS_TYPE_HASH; - break; - case BURN_CACHE_STEP_FINALIZE: - pProgress->type = BURN_CACHE_PROGRESS_TYPE_FINALIZE; - break; - } - break; - case BURN_CACHE_MESSAGE_SUCCESS: - hr = CompleteCacheProgress(pProgress, pMessage->success.qwFileSize); - break; - case BURN_CACHE_MESSAGE_COMPLETE: - switch (pProgress->type) - { - case BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY: - hr = UserExperienceOnCacheContainerOrPayloadVerifyComplete(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, hr); - break; - } - } - - return hr; -} - -static HRESULT CompleteCacheProgress( - __in BURN_CACHE_PROGRESS_CONTEXT* pContext, - __in DWORD64 qwFileSize - ) -{ - HRESULT hr = S_OK; - LARGE_INTEGER liContainerOrPayloadSize = { }; - LARGE_INTEGER liZero = { }; - DWORD dwResult = 0; - DWORD64 qwCommitSize = 0; - - liContainerOrPayloadSize.QuadPart = qwFileSize; - - // Need to commit the steps that were skipped. - if (BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY == pContext->type || BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY == pContext->type) - { - Assert(!pContext->pPayload); - - qwCommitSize = qwFileSize * (pContext->pCacheContext->wzLayoutDirectory ? 2 : 3); // Acquire (+ Stage) + Hash + Finalize - 1 (that's added later) - - pContext->pCacheContext->qwSuccessfulCacheProgress += qwCommitSize; - - if (pContext->pContainer) - { - pContext->pContainer->qwCommittedCacheProgress += qwCommitSize; - } - else if (pContext->pPayloadGroupItem) - { - pContext->pPayloadGroupItem->qwCommittedCacheProgress += qwCommitSize; - } - } - - dwResult = CacheProgressRoutine(liContainerOrPayloadSize, liContainerOrPayloadSize, liZero, liZero, 0, 0, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, pContext); - - if (PROGRESS_CONTINUE == dwResult) - { - pContext->pCacheContext->qwSuccessfulCacheProgress += qwFileSize; - - if (pContext->pPayload) - { - pContext->pContainer->qwCommittedExtractProgress += qwFileSize; - } - else if (pContext->pContainer) - { - pContext->pContainer->qwCommittedCacheProgress += qwFileSize; - } - else if (pContext->pPayloadGroupItem) - { - pContext->pPayloadGroupItem->qwCommittedCacheProgress += qwFileSize; - } - - if (BURN_CACHE_PROGRESS_TYPE_FINALIZE == pContext->type && pContext->pCacheContext->sczLastUsedFolderCandidate) - { - // We successfully copied from a source location, set that as the last used source. - CacheSetLastUsedSource(pContext->pCacheContext->pVariables, pContext->pCacheContext->sczLastUsedFolderCandidate, pContext->pContainer ? pContext->pContainer->sczFilePath : pContext->pPayloadGroupItem->pPayload->sczFilePath); - } - } - else if (PROGRESS_CANCEL == dwResult) - { - if (pContext->fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - else - { - hr = pContext->hrError; - } - - if (qwCommitSize) - { - pContext->pCacheContext->qwSuccessfulCacheProgress -= qwCommitSize; - - if (pContext->pContainer) - { - pContext->pContainer->qwCommittedCacheProgress -= qwCommitSize; - } - else if (pContext->pPayloadGroupItem) - { - pContext->pPayloadGroupItem->qwCommittedCacheProgress -= qwCommitSize; - } - } - } - - return hr; -} - -static DWORD CALLBACK CacheProgressRoutine( - __in LARGE_INTEGER TotalFileSize, - __in LARGE_INTEGER TotalBytesTransferred, - __in LARGE_INTEGER /*StreamSize*/, - __in LARGE_INTEGER /*StreamBytesTransferred*/, - __in DWORD /*dwStreamNumber*/, - __in DWORD /*dwCallbackReason*/, - __in HANDLE /*hSourceFile*/, - __in HANDLE /*hDestinationFile*/, - __in_opt LPVOID lpData - ) -{ - HRESULT hr = S_OK; - DWORD dwResult = PROGRESS_CONTINUE; - BURN_CACHE_PROGRESS_CONTEXT* pProgress = static_cast(lpData); - LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : NULL; - LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : pProgress->pPayload ? pProgress->pPayload->sczKey : NULL; - DWORD64 qwCacheProgress = pProgress->pCacheContext->qwSuccessfulCacheProgress + TotalBytesTransferred.QuadPart; - if (qwCacheProgress > pProgress->pCacheContext->qwTotalCacheSize) - { - //AssertSz(FALSE, "Apply has cached more than Plan envisioned."); - qwCacheProgress = pProgress->pCacheContext->qwTotalCacheSize; - } - DWORD dwOverallPercentage = pProgress->pCacheContext->qwTotalCacheSize ? static_cast(qwCacheProgress * 100 / pProgress->pCacheContext->qwTotalCacheSize) : 0; - - switch (pProgress->type) - { - case BURN_CACHE_PROGRESS_TYPE_ACQUIRE: - hr = UserExperienceOnCacheAcquireProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); - ExitOnRootFailure(hr, "BA aborted acquire of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); - break; - case BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY: - hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_HASH); - ExitOnRootFailure(hr, "BA aborted payload verify step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); - break; - case BURN_CACHE_PROGRESS_TYPE_STAGE: - hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_STAGE); - ExitOnRootFailure(hr, "BA aborted stage step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); - break; - case BURN_CACHE_PROGRESS_TYPE_HASH: - hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_HASH); - ExitOnRootFailure(hr, "BA aborted hash step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); - break; - case BURN_CACHE_PROGRESS_TYPE_FINALIZE: - hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_FINALIZE); - ExitOnRootFailure(hr, "BA aborted finalize step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId); - break; - case BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY: - hr = UserExperienceOnCacheContainerOrPayloadVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); - ExitOnRootFailure(hr, "BA aborted container or payload verify: %ls", wzPayloadId); - break; - case BURN_CACHE_PROGRESS_TYPE_EXTRACT: - hr = UserExperienceOnCachePayloadExtractProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); - ExitOnRootFailure(hr, "BA aborted extract container: %ls, payload: %ls", wzPackageOrContainerId, wzPayloadId); - break; - } - -LExit: - if (HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr) - { - dwResult = PROGRESS_CANCEL; - pProgress->fCancel = TRUE; - } - else if (FAILED(hr)) - { - dwResult = PROGRESS_CANCEL; - pProgress->hrError = hr; - } - else - { - dwResult = PROGRESS_CONTINUE; - } - - return dwResult; -} - -static void DoRollbackCache( - __in BURN_USER_EXPERIENCE* /*pUX*/, - __in BURN_PLAN* pPlan, - __in HANDLE hPipe, - __in DWORD dwCheckpoint - ) -{ - HRESULT hr = S_OK; - DWORD iCheckpoint = 0; - - // Scan to last checkpoint. - for (DWORD i = 0; i < pPlan->cRollbackCacheActions; ++i) - { - BURN_CACHE_ACTION* pRollbackCacheAction = &pPlan->rgRollbackCacheActions[i]; - - if (BURN_CACHE_ACTION_TYPE_CHECKPOINT == pRollbackCacheAction->type && pRollbackCacheAction->checkpoint.dwId == dwCheckpoint) - { - iCheckpoint = i; - break; - } - } - - // Rollback cache actions. - if (iCheckpoint) - { - // i has to be a signed integer so it doesn't get decremented to 0xFFFFFFFF. - for (int i = iCheckpoint - 1; i >= 0; --i) - { - BURN_CACHE_ACTION* pRollbackCacheAction = &pPlan->rgRollbackCacheActions[i]; - - switch (pRollbackCacheAction->type) - { - case BURN_CACHE_ACTION_TYPE_CHECKPOINT: - break; - - case BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE: - hr = CleanPackage(hPipe, pRollbackCacheAction->rollbackPackage.pPackage); - break; - - default: - AssertSz(FALSE, "Invalid rollback cache action."); - break; - } - } - } -} - -static HRESULT DoExecuteAction( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in_opt HANDLE hCacheThread, - __in BURN_EXECUTE_CONTEXT* pContext, - __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, - __inout BURN_EXECUTE_ACTION_CHECKPOINT** ppCheckpoint, - __out BOOL* pfSuspend, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ) -{ - Assert(!pExecuteAction->fDeleted); - - HRESULT hr = S_OK; - HANDLE rghWait[2] = { }; - BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; - BOOL fRetry = FALSE; - BOOL fStopWusaService = FALSE; - BOOL fInsideMsiTransaction = FALSE; - - pContext->fRollback = FALSE; - - do - { - fInsideMsiTransaction = *ppRollbackBoundary && (*ppRollbackBoundary)->fActiveTransaction; - - switch (pExecuteAction->type) - { - case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT: - *ppCheckpoint = &pExecuteAction->checkpoint; - break; - - case BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT: - // wait for cache sync-point - rghWait[0] = pExecuteAction->syncpoint.hEvent; - rghWait[1] = hCacheThread; - switch (::WaitForMultipleObjects(rghWait[1] ? 2 : 1, rghWait, FALSE, INFINITE)) - { - case WAIT_OBJECT_0: - break; - - case WAIT_OBJECT_0 + 1: - if (!::GetExitCodeThread(hCacheThread, (DWORD*)&hr)) - { - ExitWithLastError(hr, "Failed to get cache thread exit code."); - } - - if (SUCCEEDED(hr)) - { - hr = E_UNEXPECTED; - } - ExitOnFailure(hr, "Cache thread exited unexpectedly."); - - case WAIT_FAILED: __fallthrough; - default: - ExitWithLastError(hr, "Failed to wait for cache check-point."); - } - break; - - case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: - hr = ExecuteExePackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart); - ExitOnFailure(hr, "Failed to execute EXE package."); - break; - - case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: - hr = ExecuteMsiPackage(pEngineState, pExecuteAction, pContext, fInsideMsiTransaction, FALSE, &fRetry, pfSuspend, &restart); - ExitOnFailure(hr, "Failed to execute MSI package."); - break; - - case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: - hr = ExecuteMspPackage(pEngineState, pExecuteAction, pContext, fInsideMsiTransaction, FALSE, &fRetry, pfSuspend, &restart); - ExitOnFailure(hr, "Failed to execute MSP package."); - break; - - case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: - hr = ExecuteMsuPackage(pEngineState, pExecuteAction, pContext, FALSE, fStopWusaService, &fRetry, pfSuspend, &restart); - fStopWusaService = fRetry; - ExitOnFailure(hr, "Failed to execute MSU package."); - break; - - case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER: - hr = ExecutePackageProviderAction(pEngineState, pExecuteAction, pContext); - ExitOnFailure(hr, "Failed to execute package provider registration action."); - break; - - case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: - hr = ExecuteDependencyAction(pEngineState, pExecuteAction, pContext); - ExitOnFailure(hr, "Failed to execute dependency action."); - break; - - break; - - case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY: - *ppRollbackBoundary = pExecuteAction->rollbackBoundary.pRollbackBoundary; - break; - - case BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION: - hr = ExecuteMsiBeginTransaction(pEngineState, pExecuteAction->msiTransaction.pRollbackBoundary, pContext); - ExitOnFailure(hr, "Failed to execute begin MSI transaction action."); - break; - - case BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION: - hr = ExecuteMsiCommitTransaction(pEngineState, pExecuteAction->msiTransaction.pRollbackBoundary, pContext); - ExitOnFailure(hr, "Failed to execute commit MSI transaction action."); - break; - - default: - hr = E_UNEXPECTED; - ExitOnFailure(hr, "Invalid execute action."); - } - - if (*pRestart < restart) - { - *pRestart = restart; - } - } while (fRetry && *pRestart < BOOTSTRAPPER_APPLY_RESTART_INITIATED); - -LExit: - return hr; -} - -static HRESULT DoRollbackActions( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_EXECUTE_CONTEXT* pContext, - __in DWORD dwCheckpoint, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ) -{ - HRESULT hr = S_OK; - DWORD iCheckpoint = 0; - BOOL fRetryIgnored = FALSE; - BOOL fSuspendIgnored = FALSE; - - pContext->fRollback = TRUE; - - // scan to last checkpoint - for (DWORD i = 0; i < pEngineState->plan.cRollbackActions; ++i) - { - BURN_EXECUTE_ACTION* pRollbackAction = &pEngineState->plan.rgRollbackActions[i]; - if (pRollbackAction->fDeleted) - { - continue; - } - - if (BURN_EXECUTE_ACTION_TYPE_CHECKPOINT == pRollbackAction->type) - { - if (pRollbackAction->checkpoint.dwId == dwCheckpoint) - { - iCheckpoint = i; - break; - } - } - } - - // execute rollback actions - if (iCheckpoint) - { - // i has to be a signed integer so it doesn't get decremented to 0xFFFFFFFF. - for (int i = iCheckpoint - 1; i >= 0; --i) - { - BURN_EXECUTE_ACTION* pRollbackAction = &pEngineState->plan.rgRollbackActions[i]; - if (pRollbackAction->fDeleted) - { - continue; - } - - BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; - switch (pRollbackAction->type) - { - case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT: - break; - - case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: - hr = ExecuteExePackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); - IgnoreRollbackError(hr, "Failed to rollback EXE package."); - break; - - case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: - hr = ExecuteMsiPackage(pEngineState, pRollbackAction, pContext, FALSE, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); - IgnoreRollbackError(hr, "Failed to rollback MSI package."); - break; - - case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: - hr = ExecuteMspPackage(pEngineState, pRollbackAction, pContext, FALSE, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); - IgnoreRollbackError(hr, "Failed to rollback MSP package."); - break; - - case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: - hr = ExecuteMsuPackage(pEngineState, pRollbackAction, pContext, TRUE, FALSE, &fRetryIgnored, &fSuspendIgnored, &restart); - IgnoreRollbackError(hr, "Failed to rollback MSU package."); - break; - - case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER: - hr = ExecutePackageProviderAction(pEngineState, pRollbackAction, pContext); - IgnoreRollbackError(hr, "Failed to rollback package provider action."); - break; - - case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: - hr = ExecuteDependencyAction(pEngineState, pRollbackAction, pContext); - IgnoreRollbackError(hr, "Failed to rollback dependency action."); - break; - - case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY: - ExitFunction1(hr = S_OK); - - case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE: - // TODO: This used to be skipped if the package was already cached. - // Need to figure out new logic for when (if?) to skip it. - hr = CleanPackage(pEngineState->companionConnection.hPipe, pRollbackAction->uncachePackage.pPackage); - IgnoreRollbackError(hr, "Failed to uncache package for rollback."); - break; - - default: - hr = E_UNEXPECTED; - ExitOnFailure(hr, "Invalid rollback action: %d.", pRollbackAction->type); - } - - if (*pRestart < restart) - { - *pRestart = restart; - } - } - } - -LExit: - return hr; -} - -static HRESULT ExecuteExePackage( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_EXECUTE_CONTEXT* pContext, - __in BOOL fRollback, - __out BOOL* pfRetry, - __out BOOL* pfSuspend, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ) -{ - HRESULT hr = S_OK; - HRESULT hrExecute = S_OK; - GENERIC_EXECUTE_MESSAGE message = { }; - int nResult = 0; - BOOL fBeginCalled = FALSE; - BOOL fExecuted = FALSE; - BURN_PACKAGE* pPackage = pExecuteAction->exePackage.pPackage; - - if (FAILED(pPackage->hrCacheResult)) - { - LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult); - ExitFunction1(hr = S_OK); - } - - Assert(pContext->fRollback == fRollback); - pContext->pExecutingPackage = pPackage; - fBeginCalled = TRUE; - - // Send package execute begin to BA. - hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->exePackage.action, INSTALLUILEVEL_NOCHANGE, FALSE); - ExitOnRootFailure(hr, "BA aborted execute EXE package begin."); - - message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; - message.dwAllowedResults = MB_OKCANCEL; - message.progress.dwPercentage = fRollback ? 100 : 0; - nResult = GenericExecuteMessageHandler(&message, pContext); - hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); - ExitOnRootFailure(hr, "BA aborted EXE progress."); - - fExecuted = TRUE; - - // Execute package. - if (pPackage->fPerMachine) - { - hrExecute = ElevationExecuteExePackage(pEngineState->companionConnection.hPipe, pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart); - ExitOnFailure(hrExecute, "Failed to configure per-machine EXE package."); - } - else - { - hrExecute = ExeEngineExecutePackage(pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart); - ExitOnFailure(hrExecute, "Failed to configure per-user EXE package."); - } - - message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; - message.dwAllowedResults = MB_OKCANCEL; - message.progress.dwPercentage = fRollback ? 0 : 100; - nResult = GenericExecuteMessageHandler(&message, pContext); - hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); - ExitOnRootFailure(hr, "BA aborted EXE progress."); - - pContext->cExecutedPackages += fRollback ? -1 : 1; - (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; - - hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); - ExitOnRootFailure(hr, "BA aborted EXE package execute progress."); - -LExit: - if (fExecuted) - { - ExeEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute); - } - - if (fBeginCalled) - { - hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); - } - - return hr; -} - -static HRESULT ExecuteMsiPackage( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_EXECUTE_CONTEXT* pContext, - __in BOOL fInsideMsiTransaction, - __in BOOL fRollback, - __out BOOL* pfRetry, - __out BOOL* pfSuspend, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ) -{ - HRESULT hr = S_OK; - HRESULT hrExecute = S_OK; - BOOL fBeginCalled = FALSE; - BOOL fExecuted = FALSE; - BURN_PACKAGE* pPackage = pExecuteAction->msiPackage.pPackage; - - if (FAILED(pPackage->hrCacheResult)) - { - LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult); - ExitFunction1(hr = S_OK); - } - - Assert(pContext->fRollback == fRollback); - pContext->pExecutingPackage = pPackage; - fBeginCalled = TRUE; - - // Send package execute begin to BA. - hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->msiPackage.action, pExecuteAction->msiPackage.uiLevel, pExecuteAction->msiPackage.fDisableExternalUiHandler); - ExitOnRootFailure(hr, "BA aborted execute MSI package begin."); - - fExecuted = TRUE; - - // execute package - if (pPackage->fPerMachine) - { - hrExecute = ElevationExecuteMsiPackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); - ExitOnFailure(hrExecute, "Failed to configure per-machine MSI package."); - } - else - { - hrExecute = MsiEngineExecutePackage(pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); - ExitOnFailure(hrExecute, "Failed to configure per-user MSI package."); - } - - pContext->cExecutedPackages += fRollback ? -1 : 1; - (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; - - hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); - ExitOnRootFailure(hr, "BA aborted MSI package execute progress."); - -LExit: - if (fExecuted) - { - MsiEngineUpdateInstallRegistrationState(pExecuteAction, fRollback, hrExecute, fInsideMsiTransaction); - } - - if (fBeginCalled) - { - hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); - } - - return hr; -} - -static HRESULT ExecuteMspPackage( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_EXECUTE_CONTEXT* pContext, - __in BOOL fInsideMsiTransaction, - __in BOOL fRollback, - __out BOOL* pfRetry, - __out BOOL* pfSuspend, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ) -{ - HRESULT hr = S_OK; - HRESULT hrExecute = S_OK; - BOOL fBeginCalled = FALSE; - BOOL fExecuted = FALSE; - BURN_PACKAGE* pPackage = pExecuteAction->mspTarget.pPackage; - - if (FAILED(pPackage->hrCacheResult)) - { - LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult); - ExitFunction1(hr = S_OK); - } - - Assert(pContext->fRollback == fRollback); - pContext->pExecutingPackage = pPackage; - fBeginCalled = TRUE; - - // Send package execute begin to BA. - hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->mspTarget.action, pExecuteAction->mspTarget.uiLevel, pExecuteAction->mspTarget.fDisableExternalUiHandler); - ExitOnRootFailure(hr, "BA aborted execute MSP package begin."); - - // Now send all the patches that target this product code. - for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i) - { - BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage; - - hr = UserExperienceOnExecutePatchTarget(&pEngineState->userExperience, pMspPackage->sczId, pExecuteAction->mspTarget.sczTargetProductCode); - ExitOnRootFailure(hr, "BA aborted execute MSP target."); - } - - fExecuted = TRUE; - - // execute package - if (pExecuteAction->mspTarget.fPerMachineTarget) - { - hrExecute = ElevationExecuteMspPackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); - ExitOnFailure(hrExecute, "Failed to configure per-machine MSP package."); - } - else - { - hrExecute = MspEngineExecutePackage(pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); - ExitOnFailure(hrExecute, "Failed to configure per-user MSP package."); - } - - pContext->cExecutedPackages += fRollback ? -1 : 1; - (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; - - hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); - ExitOnRootFailure(hr, "BA aborted MSP package execute progress."); - -LExit: - if (fExecuted) - { - MspEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute, fInsideMsiTransaction); - } - - if (fBeginCalled) - { - hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); - } - - return hr; -} - -static HRESULT ExecuteMsuPackage( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_EXECUTE_CONTEXT* pContext, - __in BOOL fRollback, - __in BOOL fStopWusaService, - __out BOOL* pfRetry, - __out BOOL* pfSuspend, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ) -{ - HRESULT hr = S_OK; - HRESULT hrExecute = S_OK; - GENERIC_EXECUTE_MESSAGE message = { }; - int nResult = 0; - BOOL fBeginCalled = FALSE; - BOOL fExecuted = FALSE; - BURN_PACKAGE* pPackage = pExecuteAction->msuPackage.pPackage; - - if (FAILED(pPackage->hrCacheResult)) - { - LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult); - ExitFunction1(hr = S_OK); - } - - Assert(pContext->fRollback == fRollback); - pContext->pExecutingPackage = pPackage; - fBeginCalled = TRUE; - - // Send package execute begin to BA. - hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->msuPackage.action, INSTALLUILEVEL_NOCHANGE, FALSE); - ExitOnRootFailure(hr, "BA aborted execute MSU package begin."); - - message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; - message.dwAllowedResults = MB_OKCANCEL; - message.progress.dwPercentage = fRollback ? 100 : 0; - nResult = GenericExecuteMessageHandler(&message, pContext); - hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); - ExitOnRootFailure(hr, "BA aborted MSU progress."); - - fExecuted = TRUE; - - // execute package - if (pPackage->fPerMachine) - { - hrExecute = ElevationExecuteMsuPackage(pEngineState->companionConnection.hPipe, pExecuteAction, fRollback, fStopWusaService, GenericExecuteMessageHandler, pContext, pRestart); - ExitOnFailure(hrExecute, "Failed to configure per-machine MSU package."); - } - else - { - hrExecute = E_UNEXPECTED; - ExitOnFailure(hr, "MSU packages cannot be per-user."); - } - - message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; - message.dwAllowedResults = MB_OKCANCEL; - message.progress.dwPercentage = fRollback ? 0 : 100; - nResult = GenericExecuteMessageHandler(&message, pContext); - hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); - ExitOnRootFailure(hr, "BA aborted MSU progress."); - - pContext->cExecutedPackages += fRollback ? -1 : 1; - (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; - - hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); - ExitOnRootFailure(hr, "BA aborted MSU package execute progress."); - -LExit: - if (fExecuted) - { - MsuEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute); - } - - if (fBeginCalled) - { - hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); - } - - return hr; -} - -static HRESULT ExecutePackageProviderAction( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_EXECUTE_ACTION* pAction, - __in BURN_EXECUTE_CONTEXT* /*pContext*/ - ) -{ - HRESULT hr = S_OK; - - if (pAction->packageProvider.pPackage->fPerMachine) - { - hr = ElevationExecutePackageProviderAction(pEngineState->companionConnection.hPipe, pAction); - ExitOnFailure(hr, "Failed to register the package provider on per-machine package."); - } - else - { - hr = DependencyExecutePackageProviderAction(pAction); - ExitOnFailure(hr, "Failed to register the package provider on per-user package."); - } - -LExit: - return hr; -} - -static HRESULT ExecuteDependencyAction( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_EXECUTE_ACTION* pAction, - __in BURN_EXECUTE_CONTEXT* /*pContext*/ - ) -{ - HRESULT hr = S_OK; - - if (pAction->packageDependency.pPackage->fPerMachine) - { - hr = ElevationExecutePackageDependencyAction(pEngineState->companionConnection.hPipe, pAction); - ExitOnFailure(hr, "Failed to register the dependency on per-machine package."); - } - else - { - hr = DependencyExecutePackageDependencyAction(FALSE, pAction); - ExitOnFailure(hr, "Failed to register the dependency on per-user package."); - } - - if (pAction->packageDependency.pPackage->fCanAffectRegistration) - { - if (BURN_DEPENDENCY_ACTION_REGISTER == pAction->packageDependency.action) - { - if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pAction->packageDependency.pPackage->cacheRegistrationState) - { - pAction->packageDependency.pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } - - if (BURN_PACKAGE_TYPE_MSP == pAction->packageDependency.pPackage->type) - { - for (DWORD i = 0; i < pAction->packageDependency.pPackage->Msp.cTargetProductCodes; ++i) - { - BURN_MSPTARGETPRODUCT* pTargetProduct = pAction->packageDependency.pPackage->Msp.rgTargetProducts + i; - - if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pTargetProduct->registrationState) - { - pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } - } - } - else if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pAction->packageDependency.pPackage->installRegistrationState) - { - pAction->packageDependency.pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } - } - else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pAction->packageDependency.action) - { - if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pAction->packageDependency.pPackage->cacheRegistrationState) - { - pAction->packageDependency.pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; - } - - if (BURN_PACKAGE_TYPE_MSP == pAction->packageDependency.pPackage->type) - { - for (DWORD i = 0; i < pAction->packageDependency.pPackage->Msp.cTargetProductCodes; ++i) - { - BURN_MSPTARGETPRODUCT* pTargetProduct = pAction->packageDependency.pPackage->Msp.rgTargetProducts + i; - - if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pTargetProduct->registrationState) - { - pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; - } - } - } - else if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pAction->packageDependency.pPackage->installRegistrationState) - { - pAction->packageDependency.pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; - } - } - } - -LExit: - return hr; -} - -static HRESULT ExecuteMsiBeginTransaction( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, - __in BURN_EXECUTE_CONTEXT* /*pContext*/ - ) -{ - HRESULT hr = S_OK; - BOOL fBeginCalled = FALSE; - - if (pRollbackBoundary->fActiveTransaction) - { - ExitFunction1(hr = E_INVALIDSTATE); - } - - fBeginCalled = TRUE; - hr = UserExperienceOnBeginMsiTransactionBegin(&pEngineState->userExperience, pRollbackBoundary->sczId); - ExitOnRootFailure(hr, "BA aborted execute begin MSI transaction."); - - if (pEngineState->plan.fPerMachine) - { - hr = ElevationMsiBeginTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary); - ExitOnFailure(hr, "Failed to begin an elevated MSI transaction."); - } - else - { - hr = MsiEngineBeginTransaction(pRollbackBoundary); - } - - if (SUCCEEDED(hr)) - { - pRollbackBoundary->fActiveTransaction = TRUE; - - ResetTransactionRegistrationState(pEngineState, FALSE); - } - -LExit: - if (fBeginCalled) - { - UserExperienceOnBeginMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr); - } - - return hr; -} - -static HRESULT ExecuteMsiCommitTransaction( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, - __in BURN_EXECUTE_CONTEXT* /*pContext*/ - ) -{ - HRESULT hr = S_OK; - BOOL fCommitBeginCalled = FALSE; - - if (!pRollbackBoundary->fActiveTransaction) - { - ExitFunction1(hr = E_INVALIDSTATE); - } - - fCommitBeginCalled = TRUE; - hr = UserExperienceOnCommitMsiTransactionBegin(&pEngineState->userExperience, pRollbackBoundary->sczId); - ExitOnRootFailure(hr, "BA aborted execute commit MSI transaction."); - - if (pEngineState->plan.fPerMachine) - { - hr = ElevationMsiCommitTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary); - ExitOnFailure(hr, "Failed to commit an elevated MSI transaction."); - } - else - { - hr = MsiEngineCommitTransaction(pRollbackBoundary); - } - - if (SUCCEEDED(hr)) - { - pRollbackBoundary->fActiveTransaction = FALSE; - - ResetTransactionRegistrationState(pEngineState, TRUE); - } - -LExit: - if (fCommitBeginCalled) - { - UserExperienceOnCommitMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr); - } - - return hr; -} - -static HRESULT ExecuteMsiRollbackTransaction( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, - __in BURN_EXECUTE_CONTEXT* /*pContext*/ - ) -{ - HRESULT hr = S_OK; - BOOL fRollbackBeginCalled = FALSE; - - if (!pRollbackBoundary->fActiveTransaction) - { - ExitFunction(); - } - - fRollbackBeginCalled = TRUE; - UserExperienceOnRollbackMsiTransactionBegin(&pEngineState->userExperience, pRollbackBoundary->sczId); - - if (pEngineState->plan.fPerMachine) - { - hr = ElevationMsiRollbackTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary); - ExitOnFailure(hr, "Failed to rollback an elevated MSI transaction."); - } - else - { - hr = MsiEngineRollbackTransaction(pRollbackBoundary); - } - -LExit: - pRollbackBoundary->fActiveTransaction = FALSE; - - ResetTransactionRegistrationState(pEngineState, FALSE); - - if (fRollbackBeginCalled) - { - UserExperienceOnRollbackMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr); - } - - return hr; -} - -static void ResetTransactionRegistrationState( - __in BURN_ENGINE_STATE* pEngineState, - __in BOOL fCommit - ) -{ - for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) - { - BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; - - if (BURN_PACKAGE_TYPE_MSP == pPackage->type) - { - for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j) - { - BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + j; - - if (fCommit && BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN != pTargetProduct->transactionRegistrationState) - { - pTargetProduct->registrationState = pTargetProduct->transactionRegistrationState; - } - - pTargetProduct->transactionRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; - } - } - else if (fCommit && BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN != pPackage->transactionRegistrationState) - { - pPackage->installRegistrationState = pPackage->transactionRegistrationState; - } - - pPackage->transactionRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; - } -} - -static HRESULT CleanPackage( - __in HANDLE hElevatedPipe, - __in BURN_PACKAGE* pPackage - ) -{ - HRESULT hr = S_OK; - - if (pPackage->fPerMachine) - { - hr = ElevationCleanPackage(hElevatedPipe, pPackage); - } - else - { - hr = CacheRemovePackage(FALSE, pPackage->sczId, pPackage->sczCacheId); - } - - if (pPackage->fCanAffectRegistration) - { - pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - } - - return hr; -} - -static int GenericExecuteMessageHandler( - __in GENERIC_EXECUTE_MESSAGE* pMessage, - __in LPVOID pvContext - ) -{ - BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext; - int nResult = IDNOACTION; - - switch (pMessage->type) - { - case GENERIC_EXECUTE_MESSAGE_PROGRESS: - { - DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0; - UserExperienceOnExecuteProgress(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value. - } - break; - - case GENERIC_EXECUTE_MESSAGE_ERROR: - UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_EXE_PACKAGE, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwAllowedResults, 0, NULL, &nResult); // ignore return value. - break; - - case GENERIC_EXECUTE_MESSAGE_FILES_IN_USE: - UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->filesInUse.cFiles, pMessage->filesInUse.rgwzFiles, &nResult); // ignore return value. - break; - } - - nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult); - return nResult; -} - -static int MsiExecuteMessageHandler( - __in WIU_MSI_EXECUTE_MESSAGE* pMessage, - __in_opt LPVOID pvContext - ) -{ - BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext; - int nResult = IDNOACTION; - - switch (pMessage->type) - { - case WIU_MSI_EXECUTE_MESSAGE_PROGRESS: - { - DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0; - UserExperienceOnExecuteProgress(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value. - } - break; - - case WIU_MSI_EXECUTE_MESSAGE_ERROR: - nResult = pMessage->nResultRecommendation; - UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwAllowedResults, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value. - break; - - case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE: - nResult = pMessage->nResultRecommendation; - UserExperienceOnExecuteMsiMessage(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiMessage.mt, pMessage->dwAllowedResults, pMessage->msiMessage.wzMessage, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value. - break; - - case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE: - UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiFilesInUse.cFiles, pMessage->msiFilesInUse.rgwzFiles, &nResult); // ignore return value. - break; - } - - nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult); - return nResult; -} - -static HRESULT ReportOverallProgressTicks( - __in BURN_USER_EXPERIENCE* pUX, - __in BOOL fRollback, - __in DWORD cOverallProgressTicksTotal, - __in DWORD cOverallProgressTicks - ) -{ - HRESULT hr = S_OK; - DWORD dwProgress = cOverallProgressTicksTotal ? (cOverallProgressTicks * 100 / cOverallProgressTicksTotal) : 0; - - // TODO: consider sending different progress numbers in the future. - hr = UserExperienceOnProgress(pUX, fRollback, dwProgress, dwProgress); - - return hr; -} - -static HRESULT ExecutePackageComplete( - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_VARIABLES* pVariables, - __in BURN_PACKAGE* pPackage, - __in HRESULT hrOverall, - __in HRESULT hrExecute, - __in BOOL fRollback, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart, - __out BOOL* pfRetry, - __out BOOL* pfSuspend - ) -{ - HRESULT hr = FAILED(hrOverall) ? hrOverall : hrExecute; // if the overall function failed use that otherwise use the execution result. - BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION executePackageCompleteAction = FAILED(hrOverall) || SUCCEEDED(hrExecute) || pPackage->fVital ? BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE; - - // Send package execute complete to BA. - UserExperienceOnExecutePackageComplete(pUX, pPackage->sczId, hr, *pRestart, &executePackageCompleteAction); - if (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RESTART == executePackageCompleteAction) - { - *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; - } - *pfRetry = (FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RETRY == executePackageCompleteAction); // allow retry only on failures. - *pfSuspend = (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_SUSPEND == executePackageCompleteAction); - - // Remember this package as the package that initiated the forced restart. - if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart) - { - // Best effort to set the forced restart package variable. - VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, pPackage->sczId, TRUE, FALSE); - } - - // If we're retrying, leave a message in the log file and say everything is okay. - if (*pfRetry) - { - LogId(REPORT_STANDARD, MSG_APPLY_RETRYING_PACKAGE, pPackage->sczId, hrExecute); - hr = S_OK; - } - else if (SUCCEEDED(hrOverall) && FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE == executePackageCompleteAction && !pPackage->fVital) // If we *only* failed to execute and the BA ignored this *not-vital* package, say everything is okay. - { - LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, pPackage->sczId, hrExecute); - hr = S_OK; - } - else - { - LogId(REPORT_STANDARD, MSG_APPLY_COMPLETED_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, hr, LoggingRestartToString(*pRestart)); - } - - return hr; -} diff --git a/src/engine/apply.h b/src/engine/apply.h deleted file mode 100644 index 548e147d..00000000 --- a/src/engine/apply.h +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - - -enum GENERIC_EXECUTE_MESSAGE_TYPE -{ - GENERIC_EXECUTE_MESSAGE_NONE, - GENERIC_EXECUTE_MESSAGE_ERROR, - GENERIC_EXECUTE_MESSAGE_PROGRESS, - GENERIC_EXECUTE_MESSAGE_FILES_IN_USE, -}; - -typedef struct _APPLY_AUTHENTICATION_REQUIRED_DATA -{ - BURN_USER_EXPERIENCE* pUX; - LPCWSTR wzPackageOrContainerId; - LPCWSTR wzPayloadId; -} APPLY_AUTHENTICATION_REQUIRED_DATA; - -typedef struct _GENERIC_EXECUTE_MESSAGE -{ - GENERIC_EXECUTE_MESSAGE_TYPE type; - DWORD dwAllowedResults; - - union - { - struct - { - DWORD dwErrorCode; - LPCWSTR wzMessage; - } error; - struct - { - DWORD dwPercentage; - } progress; - struct - { - DWORD cFiles; - LPCWSTR* rgwzFiles; - } filesInUse; - }; -} GENERIC_EXECUTE_MESSAGE; - - -typedef int (*PFN_GENERICMESSAGEHANDLER)( - __in GENERIC_EXECUTE_MESSAGE* pMessage, - __in LPVOID pvContext - ); - - -void ApplyInitialize(); -void ApplyUninitialize(); -HRESULT ApplySetVariables( - __in BURN_VARIABLES* pVariables - ); -void ApplyReset( - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_PACKAGES* pPackages - ); -HRESULT ApplyLock( - __in BOOL fPerMachine, - __out HANDLE* phLock - ); -HRESULT ApplyRegister( - __in BURN_ENGINE_STATE* pEngineState - ); -HRESULT ApplyUnregister( - __in BURN_ENGINE_STATE* pEngineState, - __in BOOL fFailedOrRollback, - __in BOOL fSuspend, - __in BOOTSTRAPPER_APPLY_RESTART restart - ); -HRESULT ApplyCache( - __in HANDLE hSourceEngineFile, - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_VARIABLES* pVariables, - __in BURN_PLAN* pPlan, - __in HANDLE hPipe, - __inout DWORD* pcOverallProgressTicks, - __inout BOOL* pfRollback - ); -HRESULT ApplyExecute( - __in BURN_ENGINE_STATE* pEngineState, - __in_opt HANDLE hCacheThread, - __inout DWORD* pcOverallProgressTicks, - __out BOOL* pfRollback, - __out BOOL* pfSuspend, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ); -void ApplyClean( - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_PLAN* pPlan, - __in HANDLE hPipe - ); - - -#ifdef __cplusplus -} -#endif diff --git a/src/engine/approvedexe.cpp b/src/engine/approvedexe.cpp deleted file mode 100644 index 55518519..00000000 --- a/src/engine/approvedexe.cpp +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// function definitions - -extern "C" HRESULT ApprovedExesParseFromXml( - __in BURN_APPROVED_EXES* pApprovedExes, - __in IXMLDOMNode* pixnBundle - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnNodes = NULL; - IXMLDOMNode* pixnNode = NULL; - DWORD cNodes = 0; - LPWSTR scz = NULL; - - // select approved exe nodes - hr = XmlSelectNodes(pixnBundle, L"ApprovedExeForElevation", &pixnNodes); - ExitOnFailure(hr, "Failed to select approved exe nodes."); - - // get approved exe node count - hr = pixnNodes->get_length((long*)&cNodes); - ExitOnFailure(hr, "Failed to get approved exe node count."); - - if (!cNodes) - { - ExitFunction(); - } - - // allocate memory for approved exes - pApprovedExes->rgApprovedExes = (BURN_APPROVED_EXE*)MemAlloc(sizeof(BURN_APPROVED_EXE) * cNodes, TRUE); - ExitOnNull(pApprovedExes->rgApprovedExes, hr, E_OUTOFMEMORY, "Failed to allocate memory for approved exe structs."); - - pApprovedExes->cApprovedExes = cNodes; - - // parse approved exe elements - for (DWORD i = 0; i < cNodes; ++i) - { - BURN_APPROVED_EXE* pApprovedExe = &pApprovedExes->rgApprovedExes[i]; - - hr = XmlNextElement(pixnNodes, &pixnNode, NULL); - ExitOnFailure(hr, "Failed to get next node."); - - // @Id - hr = XmlGetAttributeEx(pixnNode, L"Id", &pApprovedExe->sczId); - ExitOnFailure(hr, "Failed to get @Id."); - - // @Key - hr = XmlGetAttributeEx(pixnNode, L"Key", &pApprovedExe->sczKey); - ExitOnFailure(hr, "Failed to get @Key."); - - // @ValueName - hr = XmlGetAttributeEx(pixnNode, L"ValueName", &pApprovedExe->sczValueName); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @ValueName."); - } - - // @Win64 - hr = XmlGetYesNoAttribute(pixnNode, L"Win64", &pApprovedExe->fWin64); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @Win64."); - } - - // prepare next iteration - ReleaseNullObject(pixnNode); - ReleaseNullStr(scz); - } - - hr = S_OK; - -LExit: - ReleaseObject(pixnNodes); - ReleaseObject(pixnNode); - ReleaseStr(scz); - return hr; -} - -extern "C" void ApprovedExesUninitialize( - __in BURN_APPROVED_EXES* pApprovedExes - ) -{ - if (pApprovedExes->rgApprovedExes) - { - for (DWORD i = 0; i < pApprovedExes->cApprovedExes; ++i) - { - BURN_APPROVED_EXE* pApprovedExe = &pApprovedExes->rgApprovedExes[i]; - - ReleaseStr(pApprovedExe->sczId); - ReleaseStr(pApprovedExe->sczKey); - ReleaseStr(pApprovedExe->sczValueName); - } - MemFree(pApprovedExes->rgApprovedExes); - } -} - -extern "C" void ApprovedExesUninitializeLaunch( - __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe - ) -{ - if (pLaunchApprovedExe) - { - ReleaseStr(pLaunchApprovedExe->sczArguments); - ReleaseStr(pLaunchApprovedExe->sczExecutablePath); - ReleaseStr(pLaunchApprovedExe->sczId); - MemFree(pLaunchApprovedExe); - } -} - -extern "C" HRESULT ApprovedExesFindById( - __in BURN_APPROVED_EXES* pApprovedExes, - __in_z LPCWSTR wzId, - __out BURN_APPROVED_EXE** ppApprovedExe - ) -{ - HRESULT hr = S_OK; - BURN_APPROVED_EXE* pApprovedExe = NULL; - - for (DWORD i = 0; i < pApprovedExes->cApprovedExes; ++i) - { - pApprovedExe = &pApprovedExes->rgApprovedExes[i]; - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pApprovedExe->sczId, -1, wzId, -1)) - { - *ppApprovedExe = pApprovedExe; - ExitFunction1(hr = S_OK); - } - } - - hr = E_NOTFOUND; - -LExit: - return hr; -} - -extern "C" HRESULT ApprovedExesLaunch( - __in BURN_VARIABLES* pVariables, - __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe, - __out DWORD* pdwProcessId - ) -{ - HRESULT hr = S_OK; - LPWSTR sczArgumentsFormatted = NULL; - LPWSTR sczArgumentsObfuscated = NULL; - LPWSTR sczCommand = NULL; - LPWSTR sczCommandObfuscated = NULL; - LPWSTR sczExecutableDirectory = NULL; - STARTUPINFOW si = { }; - PROCESS_INFORMATION pi = { }; - - // build command - if (pLaunchApprovedExe->sczArguments && *pLaunchApprovedExe->sczArguments) - { - hr = VariableFormatString(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsFormatted, NULL); - ExitOnFailure(hr, "Failed to format argument string."); - - hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsFormatted); - ExitOnFailure(hr, "Failed to create executable command."); - - hr = VariableFormatStringObfuscated(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsObfuscated, NULL); - ExitOnFailure(hr, "Failed to format obfuscated argument string."); - - hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsObfuscated); - } - else - { - hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath); - ExitOnFailure(hr, "Failed to create executable command."); - - hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath); - } - ExitOnFailure(hr, "Failed to create obfuscated executable command."); - - // Try to get the directory of the executable so we can set the current directory of the process to help those executables - // that expect stuff to be relative to them. Best effort only. - hr = PathGetDirectory(pLaunchApprovedExe->sczExecutablePath, &sczExecutableDirectory); - if (FAILED(hr)) - { - ReleaseNullStr(sczExecutableDirectory); - } - - LogId(REPORT_STANDARD, MSG_LAUNCHING_APPROVED_EXE, pLaunchApprovedExe->sczExecutablePath, sczCommandObfuscated); - - si.cb = sizeof(si); - if (!::CreateProcessW(pLaunchApprovedExe->sczExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NEW_PROCESS_GROUP, NULL, sczExecutableDirectory, &si, &pi)) - { - ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", pLaunchApprovedExe->sczExecutablePath); - } - - *pdwProcessId = pi.dwProcessId; - - if (pLaunchApprovedExe->dwWaitForInputIdleTimeout) - { - ::WaitForInputIdle(pi.hProcess, pLaunchApprovedExe->dwWaitForInputIdleTimeout); - } - -LExit: - StrSecureZeroFreeString(sczArgumentsFormatted); - ReleaseStr(sczArgumentsObfuscated); - StrSecureZeroFreeString(sczCommand); - ReleaseStr(sczCommandObfuscated); - ReleaseStr(sczExecutableDirectory); - - ReleaseHandle(pi.hThread); - ReleaseHandle(pi.hProcess); - - return hr; -} - -extern "C" HRESULT ApprovedExesVerifySecureLocation( - __in BURN_VARIABLES* pVariables, - __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe - ) -{ - HRESULT hr = S_OK; - LPWSTR scz = NULL; - - const LPCWSTR vrgSecureFolderVariables[] = { - L"ProgramFiles64Folder", - L"ProgramFilesFolder", - }; - - for (DWORD i = 0; i < countof(vrgSecureFolderVariables); ++i) - { - LPCWSTR wzSecureFolderVariable = vrgSecureFolderVariables[i]; - - hr = VariableGetString(pVariables, wzSecureFolderVariable, &scz); - if (SUCCEEDED(hr)) - { - hr = PathDirectoryContainsPath(scz, pLaunchApprovedExe->sczExecutablePath); - if (S_OK == hr) - { - ExitFunction(); - } - } - else if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get the variable: %ls", wzSecureFolderVariable); - } - } - - // The problem with using a Variable for the root package cache folder is that it might not have been secured yet. - // Getting it through CacheGetRootCompletedPath makes sure it has been secured. - hr = CacheGetRootCompletedPath(TRUE, TRUE, &scz); - ExitOnFailure(hr, "Failed to get the root package cache folder."); - - hr = PathDirectoryContainsPath(scz, pLaunchApprovedExe->sczExecutablePath); - if (S_OK == hr) - { - ExitFunction(); - } - - hr = S_FALSE; - -LExit: - ReleaseStr(scz); - - return hr; -} diff --git a/src/engine/approvedexe.h b/src/engine/approvedexe.h deleted file mode 100644 index 23f3b1bb..00000000 --- a/src/engine/approvedexe.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -// structs - -typedef struct _BURN_APPROVED_EXE -{ - LPWSTR sczId; - LPWSTR sczKey; - LPWSTR sczValueName; - BOOL fWin64; -} BURN_APPROVED_EXE; - -typedef struct _BURN_APPROVED_EXES -{ - BURN_APPROVED_EXE* rgApprovedExes; - DWORD cApprovedExes; -} BURN_APPROVED_EXES; - -typedef struct _BURN_LAUNCH_APPROVED_EXE -{ - HWND hwndParent; - LPWSTR sczId; - LPWSTR sczExecutablePath; - LPWSTR sczArguments; - DWORD dwWaitForInputIdleTimeout; -} BURN_LAUNCH_APPROVED_EXE; - - -// function declarations - -HRESULT ApprovedExesParseFromXml( - __in BURN_APPROVED_EXES* pApprovedExes, - __in IXMLDOMNode* pixnBundle - ); - -void ApprovedExesUninitialize( - __in BURN_APPROVED_EXES* pApprovedExes - ); -void ApprovedExesUninitializeLaunch( - __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe - ); -HRESULT ApprovedExesFindById( - __in BURN_APPROVED_EXES* pApprovedExes, - __in_z LPCWSTR wzId, - __out BURN_APPROVED_EXE** ppApprovedExe - ); -HRESULT ApprovedExesLaunch( - __in BURN_VARIABLES* pVariables, - __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe, - __out DWORD* pdwProcessId - ); -HRESULT ApprovedExesVerifySecureLocation( - __in BURN_VARIABLES* pVariables, - __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe - ); - - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/burnextension.cpp b/src/engine/burnextension.cpp deleted file mode 100644 index 475df1c5..00000000 --- a/src/engine/burnextension.cpp +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -static HRESULT SendRequiredBextMessage( - __in BURN_EXTENSION* pExtension, - __in BUNDLE_EXTENSION_MESSAGE message, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ); - -// function definitions - -/******************************************************************* - BurnExtensionParseFromXml - - -*******************************************************************/ -EXTERN_C HRESULT BurnExtensionParseFromXml( - __in BURN_EXTENSIONS* pBurnExtensions, - __in BURN_PAYLOADS* pBaPayloads, - __in IXMLDOMNode* pixnBundle - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnNodes = NULL; - IXMLDOMNode* pixnNode = NULL; - DWORD cNodes = 0; - - // Select BundleExtension nodes. - hr = XmlSelectNodes(pixnBundle, L"BundleExtension", &pixnNodes); - ExitOnFailure(hr, "Failed to select BundleExtension nodes."); - - // Get BundleExtension node count. - hr = pixnNodes->get_length((long*)&cNodes); - ExitOnFailure(hr, "Failed to get BundleExtension node count."); - - if (!cNodes) - { - ExitFunction(); - } - - // Allocate memory for BundleExtensions. - pBurnExtensions->rgExtensions = (BURN_EXTENSION*)MemAlloc(sizeof(BURN_EXTENSION) * cNodes, TRUE); - ExitOnNull(pBurnExtensions->rgExtensions, hr, E_OUTOFMEMORY, "Failed to allocate memory for BundleExtension structs."); - - pBurnExtensions->cExtensions = cNodes; - - // parse search elements - for (DWORD i = 0; i < cNodes; ++i) - { - BURN_EXTENSION* pExtension = &pBurnExtensions->rgExtensions[i]; - - hr = XmlNextElement(pixnNodes, &pixnNode, NULL); - ExitOnFailure(hr, "Failed to get next node."); - - // @Id - hr = XmlGetAttributeEx(pixnNode, L"Id", &pExtension->sczId); - ExitOnFailure(hr, "Failed to get @Id."); - - // @EntryPayloadId - hr = XmlGetAttributeEx(pixnNode, L"EntryPayloadId", &pExtension->sczEntryPayloadId); - ExitOnFailure(hr, "Failed to get @EntryPayloadId."); - - hr = PayloadFindById(pBaPayloads, pExtension->sczEntryPayloadId, &pExtension->pEntryPayload); - ExitOnFailure(hr, "Failed to find BundleExtension EntryPayload '%ls'.", pExtension->sczEntryPayloadId); - - // prepare next iteration - ReleaseNullObject(pixnNode); - } - - hr = S_OK; - -LExit: - ReleaseObject(pixnNode); - ReleaseObject(pixnNodes); - - return hr; -} - -/******************************************************************* - BurnExtensionUninitialize - - -*******************************************************************/ -EXTERN_C void BurnExtensionUninitialize( - __in BURN_EXTENSIONS* pBurnExtensions - ) -{ - if (pBurnExtensions->rgExtensions) - { - for (DWORD i = 0; i < pBurnExtensions->cExtensions; ++i) - { - BURN_EXTENSION* pExtension = &pBurnExtensions->rgExtensions[i]; - - ReleaseStr(pExtension->sczEntryPayloadId); - ReleaseStr(pExtension->sczId); - } - MemFree(pBurnExtensions->rgExtensions); - } - - // clear struct - memset(pBurnExtensions, 0, sizeof(BURN_EXTENSIONS)); -} - -/******************************************************************* - BurnExtensionLoad - - -*******************************************************************/ -EXTERN_C HRESULT BurnExtensionLoad( - __in BURN_EXTENSIONS * pBurnExtensions, - __in BURN_EXTENSION_ENGINE_CONTEXT* pEngineContext - ) -{ - HRESULT hr = S_OK; - LPWSTR sczBundleExtensionDataPath = NULL; - BUNDLE_EXTENSION_CREATE_ARGS args = { }; - BUNDLE_EXTENSION_CREATE_RESULTS results = { }; - - if (!pBurnExtensions->rgExtensions || !pBurnExtensions->cExtensions) - { - ExitFunction(); - } - - hr = PathConcat(pEngineContext->pEngineState->userExperience.sczTempDirectory, L"BundleExtensionData.xml", &sczBundleExtensionDataPath); - ExitOnFailure(hr, "Failed to get BundleExtensionDataPath."); - - for (DWORD i = 0; i < pBurnExtensions->cExtensions; ++i) - { - BURN_EXTENSION* pExtension = &pBurnExtensions->rgExtensions[i]; - - memset(&args, 0, sizeof(BUNDLE_EXTENSION_CREATE_ARGS)); - memset(&results, 0, sizeof(BUNDLE_EXTENSION_CREATE_RESULTS)); - - args.cbSize = sizeof(BUNDLE_EXTENSION_CREATE_ARGS); - args.pfnBundleExtensionEngineProc = EngineForExtensionProc; - args.pvBundleExtensionEngineProcContext = pEngineContext; - args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 4, 27, 0); - args.wzBootstrapperWorkingFolder = pEngineContext->pEngineState->userExperience.sczTempDirectory; - args.wzBundleExtensionDataPath = sczBundleExtensionDataPath; - args.wzExtensionId = pExtension->sczId; - - results.cbSize = sizeof(BUNDLE_EXTENSION_CREATE_RESULTS); - - // Load BundleExtension DLL. - pExtension->hBextModule = ::LoadLibraryExW(pExtension->pEntryPayload->sczLocalFilePath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); - ExitOnNullWithLastError(pExtension->hBextModule, hr, "Failed to load BundleExtension DLL '%ls': '%ls'.", pExtension->sczId, pExtension->pEntryPayload->sczLocalFilePath); - - // Get BundleExtensionCreate entry-point. - PFN_BUNDLE_EXTENSION_CREATE pfnCreate = (PFN_BUNDLE_EXTENSION_CREATE)::GetProcAddress(pExtension->hBextModule, "BundleExtensionCreate"); - ExitOnNullWithLastError(pfnCreate, hr, "Failed to get BundleExtensionCreate entry-point '%ls'.", pExtension->sczId); - - // Create BundleExtension. - hr = pfnCreate(&args, &results); - ExitOnFailure(hr, "Failed to create BundleExtension '%ls'.", pExtension->sczId); - - pExtension->pfnBurnExtensionProc = results.pfnBundleExtensionProc; - pExtension->pvBurnExtensionProcContext = results.pvBundleExtensionProcContext; - } - -LExit: - ReleaseStr(sczBundleExtensionDataPath); - - return hr; -} - -/******************************************************************* - BurnExtensionUnload - - -*******************************************************************/ -EXTERN_C void BurnExtensionUnload( - __in BURN_EXTENSIONS * pBurnExtensions - ) -{ - HRESULT hr = S_OK; - - if (pBurnExtensions->rgExtensions) - { - for (DWORD i = 0; i < pBurnExtensions->cExtensions; ++i) - { - BURN_EXTENSION* pExtension = &pBurnExtensions->rgExtensions[i]; - - if (pExtension->hBextModule) - { - // Get BundleExtensionDestroy entry-point and call it if it exists. - PFN_BUNDLE_EXTENSION_DESTROY pfnDestroy = (PFN_BUNDLE_EXTENSION_DESTROY)::GetProcAddress(pExtension->hBextModule, "BundleExtensionDestroy"); - if (pfnDestroy) - { - pfnDestroy(); - } - - // Free BundleExtension DLL. - if (!::FreeLibrary(pExtension->hBextModule)) - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - TraceError(hr, "Failed to unload BundleExtension DLL."); - } - pExtension->hBextModule = NULL; - } - } - } -} - -EXTERN_C HRESULT BurnExtensionFindById( - __in BURN_EXTENSIONS* pBurnExtensions, - __in_z LPCWSTR wzId, - __out BURN_EXTENSION** ppExtension - ) -{ - HRESULT hr = S_OK; - BURN_EXTENSION* pExtension = NULL; - - for (DWORD i = 0; i < pBurnExtensions->cExtensions; ++i) - { - pExtension = &pBurnExtensions->rgExtensions[i]; - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pExtension->sczId, -1, wzId, -1)) - { - *ppExtension = pExtension; - ExitFunction1(hr = S_OK); - } - } - - hr = E_NOTFOUND; - -LExit: - return hr; -} - -EXTERN_C BEEAPI BurnExtensionPerformSearch( - __in BURN_EXTENSION* pExtension, - __in LPWSTR wzSearchId, - __in LPWSTR wzVariable - ) -{ - HRESULT hr = S_OK; - BUNDLE_EXTENSION_SEARCH_ARGS args = { }; - BUNDLE_EXTENSION_SEARCH_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzId = wzSearchId; - args.wzVariable = wzVariable; - - results.cbSize = sizeof(results); - - hr = SendRequiredBextMessage(pExtension, BUNDLE_EXTENSION_MESSAGE_SEARCH, &args, &results); - ExitOnFailure(hr, "BundleExtension '%ls' Search '%ls' failed.", pExtension->sczId, wzSearchId); - -LExit: - return hr; -} - -static HRESULT SendRequiredBextMessage( - __in BURN_EXTENSION* pExtension, - __in BUNDLE_EXTENSION_MESSAGE message, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - - hr = pExtension->pfnBurnExtensionProc(message, pvArgs, pvResults, pExtension->pvBurnExtensionProcContext); - - return hr; -} diff --git a/src/engine/burnextension.h b/src/engine/burnextension.h deleted file mode 100644 index 370ddd2d..00000000 --- a/src/engine/burnextension.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#define BEEAPI HRESULT __stdcall - -#if defined(__cplusplus) -extern "C" { -#endif - -// structs - -typedef struct _BURN_EXTENSION_ENGINE_CONTEXT BURN_EXTENSION_ENGINE_CONTEXT; - -typedef struct _BURN_EXTENSION -{ - LPWSTR sczEntryPayloadId; - LPWSTR sczId; - - BURN_PAYLOAD* pEntryPayload; - - HMODULE hBextModule; - PFN_BUNDLE_EXTENSION_PROC pfnBurnExtensionProc; - LPVOID pvBurnExtensionProcContext; -} BURN_EXTENSION; - -typedef struct _BURN_EXTENSIONS -{ - BURN_EXTENSION* rgExtensions; - DWORD cExtensions; -} BURN_EXTENSIONS; - -// functions - -HRESULT BurnExtensionParseFromXml( - __in BURN_EXTENSIONS* pBurnExtensions, - __in BURN_PAYLOADS* pBaPayloads, - __in IXMLDOMNode* pixnBundle - ); -void BurnExtensionUninitialize( - __in BURN_EXTENSIONS* pBurnExtensions - ); -HRESULT BurnExtensionLoad( - __in BURN_EXTENSIONS* pBurnExtensions, - __in BURN_EXTENSION_ENGINE_CONTEXT* pEngineContext - ); -void BurnExtensionUnload( - __in BURN_EXTENSIONS* pBurnExtensions - ); -HRESULT BurnExtensionFindById( - __in BURN_EXTENSIONS* pBurnExtensions, - __in_z LPCWSTR wzId, - __out BURN_EXTENSION** ppExtension - ); -BEEAPI BurnExtensionPerformSearch( - __in BURN_EXTENSION* pExtension, - __in LPWSTR wzSearchId, - __in LPWSTR wzVariable - ); -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/cabextract.cpp b/src/engine/cabextract.cpp deleted file mode 100644 index 5a02ff8a..00000000 --- a/src/engine/cabextract.cpp +++ /dev/null @@ -1,974 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -#include - -#define ARRAY_GROWTH_SIZE 2 - -const LPSTR INVALID_CAB_NAME = ".cab"; - -// structs - -typedef struct _BURN_CAB_CONTEXT -{ - HANDLE hFile; - DWORD64 qwOffset; - DWORD64 qwSize; - - HANDLE hThread; - HANDLE hBeginOperationEvent; - HANDLE hOperationCompleteEvent; - - BURN_CAB_OPERATION operation; - HRESULT hrError; - - LPWSTR* psczStreamName; - LPCWSTR wzTargetFile; - HANDLE hTargetFile; - BYTE* pbTargetBuffer; - DWORD cbTargetBuffer; - DWORD iTargetBuffer; -} BURN_CAB_CONTEXT; - - -// internal function declarations - -static HRESULT BeginAndWaitForOperation( - __in BURN_CONTAINER_CONTEXT* pContext - ); -static HRESULT WaitForOperation( - __in BURN_CONTAINER_CONTEXT* pContext - ); -static DWORD WINAPI ExtractThreadProc( - __in LPVOID lpThreadParameter - ); -static INT_PTR DIAMONDAPI CabNotifyCallback( - __in FDINOTIFICATIONTYPE iNotification, - __inout FDINOTIFICATION *pFDINotify - ); -static INT_PTR CopyFileCallback( - __in BURN_CONTAINER_CONTEXT* pContext, - __inout FDINOTIFICATION *pFDINotify - ); -static INT_PTR CloseFileInfoCallback( - __in BURN_CONTAINER_CONTEXT* pContext, - __inout FDINOTIFICATION *pFDINotify - ); -static LPVOID DIAMONDAPI CabAlloc( - __in DWORD dwSize - ); -static void DIAMONDAPI CabFree( - __in LPVOID pvData - ); -static INT_PTR FAR DIAMONDAPI CabOpen( - __in char FAR *pszFile, - __in int /* oflag */, - __in int /* pmode */ - ); -static UINT FAR DIAMONDAPI CabRead( - __in INT_PTR hf, - __out void FAR *pv, - __in UINT cb - ); -static UINT FAR DIAMONDAPI CabWrite( - __in INT_PTR hf, - __in void FAR *pv, - __in UINT cb - ); -static long FAR DIAMONDAPI CabSeek( - __in INT_PTR hf, - __in long dist, - __in int seektype - ); -static int FAR DIAMONDAPI CabClose( - __in INT_PTR hf - ); -static HRESULT AddVirtualFilePointer( - __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, - __in HANDLE hFile, - __in LONGLONG llInitialFilePointer - ); -static HRESULT ReadIfVirtualFilePointer( - __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, - __in HANDLE hFile, - __in DWORD cbRead - ); -static BOOL SetIfVirtualFilePointer( - __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, - __in HANDLE hFile, - __in LONGLONG llDistance, - __out LONGLONG* pllNewPostion, - __in DWORD dwSeekType - ); -static HRESULT CloseIfVirturalFilePointer( - __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, - __in HANDLE hFile - ); -static BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* GetVirtualFilePointer( - __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, - __in HANDLE hFile - ); - - -// internal variables - -__declspec(thread) static BURN_CONTAINER_CONTEXT* vpContext; - - -// function definitions - -extern "C" void CabExtractInitialize() -{ -} - -extern "C" HRESULT CabExtractOpen( - __in BURN_CONTAINER_CONTEXT* pContext, - __in LPCWSTR wzFilePath - ) -{ - HRESULT hr = S_OK; - - // initialize context - pContext->Cabinet.hTargetFile = INVALID_HANDLE_VALUE; - - hr = StrAllocString(&pContext->Cabinet.sczFile, wzFilePath, 0); - ExitOnFailure(hr, "Failed to copy file name."); - - // create events - pContext->Cabinet.hBeginOperationEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL); - ExitOnNullWithLastError(pContext->Cabinet.hBeginOperationEvent, hr, "Failed to create begin operation event."); - - pContext->Cabinet.hOperationCompleteEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL); - ExitOnNullWithLastError(pContext->Cabinet.hOperationCompleteEvent, hr, "Failed to create operation complete event."); - - // create extraction thread - pContext->Cabinet.hThread = ::CreateThread(NULL, 0, ExtractThreadProc, pContext, 0, NULL); - ExitOnNullWithLastError(pContext->Cabinet.hThread, hr, "Failed to create extraction thread."); - - // wait for operation to complete - hr = WaitForOperation(pContext); - ExitOnFailure(hr, "Failed to wait for operation complete."); - -LExit: - return hr; -} - -extern "C" HRESULT CabExtractNextStream( - __in BURN_CONTAINER_CONTEXT* pContext, - __inout_z LPWSTR* psczStreamName - ) -{ - HRESULT hr = S_OK; - - // set operation to move to next stream - pContext->Cabinet.operation = BURN_CAB_OPERATION_NEXT_STREAM; - pContext->Cabinet.psczStreamName = psczStreamName; - - // begin operation and wait - hr = BeginAndWaitForOperation(pContext); - if (E_ABORT != hr && E_NOMOREITEMS != hr) - { - ExitOnFailure(hr, "Failed to begin and wait for operation."); - } - -LExit: - return hr; -} - -extern "C" HRESULT CabExtractStreamToFile( - __in BURN_CONTAINER_CONTEXT* pContext, - __in_z LPCWSTR wzFileName - ) -{ - HRESULT hr = S_OK; - - // set operation to move to next stream - pContext->Cabinet.operation = BURN_CAB_OPERATION_STREAM_TO_FILE; - pContext->Cabinet.wzTargetFile = wzFileName; - - // begin operation and wait - hr = BeginAndWaitForOperation(pContext); - ExitOnFailure(hr, "Failed to begin and wait for operation."); - - // clear file name - pContext->Cabinet.wzTargetFile = NULL; - -LExit: - return hr; -} - -extern "C" HRESULT CabExtractStreamToBuffer( - __in BURN_CONTAINER_CONTEXT* pContext, - __out BYTE** ppbBuffer, - __out SIZE_T* pcbBuffer - ) -{ - HRESULT hr = S_OK; - - // set operation to move to next stream - pContext->Cabinet.operation = BURN_CAB_OPERATION_STREAM_TO_BUFFER; - - // begin operation and wait - hr = BeginAndWaitForOperation(pContext); - ExitOnFailure(hr, "Failed to begin and wait for operation."); - - // return values - *ppbBuffer = pContext->Cabinet.pbTargetBuffer; - *pcbBuffer = pContext->Cabinet.cbTargetBuffer; - - // clear buffer variables - pContext->Cabinet.pbTargetBuffer = NULL; - pContext->Cabinet.cbTargetBuffer = 0; - pContext->Cabinet.iTargetBuffer = 0; - -LExit: - return hr; -} - -extern "C" HRESULT CabExtractSkipStream( - __in BURN_CONTAINER_CONTEXT* pContext - ) -{ - HRESULT hr = S_OK; - - // set operation to move to next stream - pContext->Cabinet.operation = BURN_CAB_OPERATION_SKIP_STREAM; - - // begin operation and wait - hr = BeginAndWaitForOperation(pContext); - ExitOnFailure(hr, "Failed to begin and wait for operation."); - -LExit: - return hr; -} - -extern "C" HRESULT CabExtractClose( - __in BURN_CONTAINER_CONTEXT* pContext - ) -{ - HRESULT hr = S_OK; - - // terminate worker thread - if (pContext->Cabinet.hThread) - { - // set operation to move to close - pContext->Cabinet.operation = BURN_CAB_OPERATION_CLOSE; - - // set begin operation event - if (!::SetEvent(pContext->Cabinet.hBeginOperationEvent)) - { - ExitWithLastError(hr, "Failed to set begin operation event."); - } - - // wait for thread to terminate - if (WAIT_OBJECT_0 != ::WaitForSingleObject(pContext->Cabinet.hThread, INFINITE)) - { - ExitWithLastError(hr, "Failed to wait for thread to terminate."); - } - } - -LExit: - ReleaseHandle(pContext->Cabinet.hThread); - ReleaseHandle(pContext->Cabinet.hBeginOperationEvent); - ReleaseHandle(pContext->Cabinet.hOperationCompleteEvent); - ReleaseMem(pContext->Cabinet.rgVirtualFilePointers); - ReleaseStr(pContext->Cabinet.sczFile); - - return hr; -} - - -// internal helper functions - -static HRESULT BeginAndWaitForOperation( - __in BURN_CONTAINER_CONTEXT* pContext - ) -{ - HRESULT hr = S_OK; - - // set begin operation event - if (!::SetEvent(pContext->Cabinet.hBeginOperationEvent)) - { - ExitWithLastError(hr, "Failed to set begin operation event."); - } - - // wait for operation to complete - hr = WaitForOperation(pContext); - -LExit: - return hr; -} - -static HRESULT WaitForOperation( - __in BURN_CONTAINER_CONTEXT* pContext - ) -{ - HRESULT hr = S_OK; - HANDLE rghWait[2] = { }; - - // wait for operation complete event - rghWait[0] = pContext->Cabinet.hOperationCompleteEvent; - rghWait[1] = pContext->Cabinet.hThread; - switch (::WaitForMultipleObjects(countof(rghWait), rghWait, FALSE, INFINITE)) - { - case WAIT_OBJECT_0: - if (!::ResetEvent(pContext->Cabinet.hOperationCompleteEvent)) - { - ExitWithLastError(hr, "Failed to reset operation complete event."); - } - break; - - case WAIT_OBJECT_0 + 1: - if (!::GetExitCodeThread(pContext->Cabinet.hThread, (DWORD*)&hr)) - { - ExitWithLastError(hr, "Failed to get extraction thread exit code."); - } - ExitFunction(); - - case WAIT_FAILED: __fallthrough; - default: - ExitWithLastError(hr, "Failed to wait for operation complete event."); - } - - // clear operation - pContext->Cabinet.operation = BURN_CAB_OPERATION_NONE; - -LExit: - return hr; -} - -static DWORD WINAPI ExtractThreadProc( - __in LPVOID lpThreadParameter - ) -{ - HRESULT hr = S_OK; - BURN_CONTAINER_CONTEXT* pContext = (BURN_CONTAINER_CONTEXT*)lpThreadParameter; - BOOL fComInitialized = FALSE; - HFDI hfdi = NULL; - ERF erf = { }; - - // initialize COM - hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); - ExitOnFailure(hr, "Failed to initialize COM."); - fComInitialized = TRUE; - - // save context in TLS storage - vpContext = pContext; - - // create FDI context - hfdi = ::FDICreate(CabAlloc, CabFree, CabOpen, CabRead, CabWrite, CabClose, CabSeek, cpuUNKNOWN, &erf); - ExitOnNull(hfdi, hr, E_FAIL, "Failed to initialize cabinet.dll."); - - // begin CAB extraction - if (!::FDICopy(hfdi, INVALID_CAB_NAME, "", 0, CabNotifyCallback, NULL, NULL)) - { - hr = pContext->Cabinet.hrError; - if (E_ABORT == hr || E_NOMOREITEMS == hr) - { - ExitFunction(); - } - else if (SUCCEEDED(hr)) - { - if (ERROR_SUCCESS != erf.erfType) - { - hr = HRESULT_FROM_WIN32(erf.erfType); - } - else - { - switch (erf.erfOper) - { - case FDIERROR_NONE: - hr = E_UNEXPECTED; - break; - case FDIERROR_CABINET_NOT_FOUND: - hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); - break; - case FDIERROR_NOT_A_CABINET: - hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); - break; - case FDIERROR_UNKNOWN_CABINET_VERSION: - hr = HRESULT_FROM_WIN32(ERROR_VERSION_PARSE_ERROR); - break; - case FDIERROR_CORRUPT_CABINET: - hr = HRESULT_FROM_WIN32(ERROR_FILE_CORRUPT); - break; - case FDIERROR_ALLOC_FAIL: - hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); - break; - case FDIERROR_BAD_COMPR_TYPE: - hr = HRESULT_FROM_WIN32(ERROR_UNSUPPORTED_COMPRESSION); - break; - case FDIERROR_MDI_FAIL: - hr = HRESULT_FROM_WIN32(ERROR_BAD_COMPRESSION_BUFFER); - break; - case FDIERROR_TARGET_FILE: - hr = HRESULT_FROM_WIN32(ERROR_WRITE_FAULT); - break; - case FDIERROR_RESERVE_MISMATCH: - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - break; - case FDIERROR_WRONG_CABINET: - hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH); - break; - case FDIERROR_USER_ABORT: - hr = E_ABORT; - break; - default: - hr = E_FAIL; - break; - } - } - } - ExitOnFailure(hr, "Failed to extract all files from container, erf: %d:%X:%d", erf.fError, erf.erfOper, erf.erfType); - } - - // set operation complete event - if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent)) - { - ExitWithLastError(hr, "Failed to set operation complete event."); - } - - // wait for begin operation event - if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE)) - { - ExitWithLastError(hr, "Failed to wait for begin operation event."); - } - - if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent)) - { - ExitWithLastError(hr, "Failed to reset begin operation event."); - } - - // read operation - switch (pContext->Cabinet.operation) - { - case BURN_CAB_OPERATION_NEXT_STREAM: - ExitFunction1(hr = E_NOMOREITEMS); - break; - - case BURN_CAB_OPERATION_CLOSE: - ExitFunction1(hr = S_OK); - - default: - hr = E_INVALIDSTATE; - ExitOnRootFailure(hr, "Invalid operation for this state."); - } - -LExit: - if (hfdi) - { - ::FDIDestroy(hfdi); - } - if (fComInitialized) - { - ::CoUninitialize(); - } - - return (DWORD)hr; -} - -static INT_PTR DIAMONDAPI CabNotifyCallback( - __in FDINOTIFICATIONTYPE iNotification, - __inout FDINOTIFICATION *pFDINotify - ) -{ - BURN_CONTAINER_CONTEXT* pContext = vpContext; - INT_PTR ipResult = 0; // result to return on success - - switch (iNotification) - { - case fdintCOPY_FILE: - ipResult = CopyFileCallback(pContext, pFDINotify); - break; - - case fdintCLOSE_FILE_INFO: // resource extraction complete - ipResult = CloseFileInfoCallback(pContext, pFDINotify); - break; - - case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages - case fdintNEXT_CABINET: __fallthrough; - case fdintENUMERATE: __fallthrough; - case fdintCABINET_INFO: - break; - - default: - AssertSz(FALSE, "CabExtractCallback() - unknown FDI notification command"); - }; - -//LExit: - return ipResult; -} - -static INT_PTR CopyFileCallback( - __in BURN_CONTAINER_CONTEXT* pContext, - __inout FDINOTIFICATION* pFDINotify - ) -{ - HRESULT hr = S_OK; - INT_PTR ipResult = 1; // result to return on success - LPWSTR pwzPath = NULL; - LARGE_INTEGER li = { }; - - // set operation complete event - if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent)) - { - ExitWithLastError(hr, "Failed to set operation complete event."); - } - - // wait for begin operation event - if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE)) - { - ExitWithLastError(hr, "Failed to wait for begin operation event."); - } - - if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent)) - { - ExitWithLastError(hr, "Failed to reset begin operation event."); - } - - // read operation - switch (pContext->Cabinet.operation) - { - case BURN_CAB_OPERATION_NEXT_STREAM: - break; - - case BURN_CAB_OPERATION_CLOSE: - ExitFunction1(hr = E_ABORT); - - default: - hr = E_INVALIDSTATE; - ExitOnRootFailure(hr, "Invalid operation for this state."); - } - - // copy stream name - hr = StrAllocStringAnsi(pContext->Cabinet.psczStreamName, pFDINotify->psz1, 0, CP_UTF8); - ExitOnFailure(hr, "Failed to copy stream name: %hs", pFDINotify->psz1); - - // set operation complete event - if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent)) - { - ExitWithLastError(hr, "Failed to set operation complete event."); - } - - // wait for begin operation event - if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE)) - { - ExitWithLastError(hr, "Failed to wait for begin operation event."); - } - - if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent)) - { - ExitWithLastError(hr, "Failed to reset begin operation event."); - } - - // read operation - switch (pContext->Cabinet.operation) - { - case BURN_CAB_OPERATION_STREAM_TO_FILE: - // create file - pContext->Cabinet.hTargetFile = ::CreateFileW(pContext->Cabinet.wzTargetFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == pContext->Cabinet.hTargetFile) - { - ExitWithLastError(hr, "Failed to create file: %ls", pContext->Cabinet.wzTargetFile); - } - - // set file size - li.QuadPart = pFDINotify->cb; - if (!::SetFilePointerEx(pContext->Cabinet.hTargetFile, li, NULL, FILE_BEGIN)) - { - ExitWithLastError(hr, "Failed to set file pointer to end of file."); - } - - if (!::SetEndOfFile(pContext->Cabinet.hTargetFile)) - { - ExitWithLastError(hr, "Failed to set end of file."); - } - - li.QuadPart = 0; - if (!::SetFilePointerEx(pContext->Cabinet.hTargetFile, li, NULL, FILE_BEGIN)) - { - ExitWithLastError(hr, "Failed to set file pointer to beginning of file."); - } - - break; - - case BURN_CAB_OPERATION_STREAM_TO_BUFFER: - // allocate buffer for stream - pContext->Cabinet.pbTargetBuffer = (BYTE*)MemAlloc(pFDINotify->cb, TRUE); - ExitOnNull(pContext->Cabinet.pbTargetBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for stream."); - - // set buffer size and write position - pContext->Cabinet.cbTargetBuffer = pFDINotify->cb; - pContext->Cabinet.iTargetBuffer = 0; - - break; - - case BURN_CAB_OPERATION_SKIP_STREAM: - ipResult = 0; - break; - - case BURN_CAB_OPERATION_CLOSE: - ExitFunction1(hr = E_ABORT); - - default: - hr = E_INVALIDSTATE; - ExitOnRootFailure(hr, "Invalid operation for this state."); - } - -LExit: - ReleaseStr(pwzPath); - - pContext->Cabinet.hrError = hr; - return SUCCEEDED(hr) ? ipResult : -1; -} - -static INT_PTR CloseFileInfoCallback( - __in BURN_CONTAINER_CONTEXT* pContext, - __inout FDINOTIFICATION *pFDINotify - ) -{ - HRESULT hr = S_OK; - INT_PTR ipResult = 1; // result to return on success - FILETIME ftLocal = { }; - FILETIME ft = { }; - - // read operation - switch (pContext->Cabinet.operation) - { - case BURN_CAB_OPERATION_STREAM_TO_FILE: - // Make a best effort to set the time on the new file before - // we close it. - if (::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal)) - { - if (::LocalFileTimeToFileTime(&ftLocal, &ft)) - { - ::SetFileTime(pContext->Cabinet.hTargetFile, &ft, &ft, &ft); - } - } - - // close file - ReleaseFile(pContext->Cabinet.hTargetFile); - break; - - case BURN_CAB_OPERATION_STREAM_TO_BUFFER: - break; - - case BURN_CAB_OPERATION_CLOSE: - ExitFunction1(hr = E_ABORT); - - default: - hr = E_INVALIDSTATE; - ExitOnRootFailure(hr, "Invalid operation for this state."); - } - - //if (pContext->pfnProgress) - //{ - // hr = StrAllocFormatted(&pwzPath, L"%s%ls", pContext->wzRootPath, pFDINotify->psz1); - // ExitOnFailure(hr, "Failed to calculate file path from: %ls and %s", pContext->wzRootPath, pFDINotify->psz1); - // if (SUCCEEDED(hr)) - // { - // hr = pContext->pfnProgress(BOX_PROGRESS_DECOMPRESSION_END, pwzPath, 0, pContext->pvContext); - // if (S_OK != hr) - // { - // pContext->hrError = hr; - // ExitFunction(); - // } - // } - //} - -LExit: - pContext->Cabinet.hrError = hr; - return SUCCEEDED(hr) ? ipResult : -1; -} - -static LPVOID DIAMONDAPI CabAlloc( - __in DWORD dwSize - ) -{ - return MemAlloc(dwSize, FALSE); -} - -static void DIAMONDAPI CabFree( - __in LPVOID pvData - ) -{ - MemFree(pvData); -} - -static INT_PTR FAR DIAMONDAPI CabOpen( - __in char FAR * pszFile, - __in int /* oflag */, - __in int /* pmode */ - ) -{ - HRESULT hr = S_OK; - BURN_CONTAINER_CONTEXT* pContext = vpContext; - HANDLE hFile = INVALID_HANDLE_VALUE; - - // If this is the invalid cab name, use our file handle. - if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, INVALID_CAB_NAME, -1, pszFile, -1)) - { - if (!::DuplicateHandle(::GetCurrentProcess(), pContext->hFile, ::GetCurrentProcess(), &hFile, 0, FALSE, DUPLICATE_SAME_ACCESS)) - { - ExitWithLastError(hr, "Failed to duplicate handle to cab container."); - } - - // Use a virtual file pointer since duplicated file handles share their file pointer. Seek to container offset - // to start. - hr = AddVirtualFilePointer(&pContext->Cabinet, hFile, pContext->qwOffset); - ExitOnFailure(hr, "Failed to add virtual file pointer for cab container."); - } - else // open file requested. This is used in the rare cases where the CAB API wants to create a temp file. - { - hFile = ::CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); - ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open cabinet file: %hs", pszFile); - } - -LExit: - pContext->Cabinet.hrError = hr; - return FAILED(hr) ? -1 : (INT_PTR)hFile; -} - -static UINT FAR DIAMONDAPI CabRead( - __in INT_PTR hf, - __out void FAR *pv, - __in UINT cb - ) -{ - HRESULT hr = S_OK; - BURN_CONTAINER_CONTEXT* pContext = vpContext; - HANDLE hFile = (HANDLE)hf; - DWORD cbRead = 0; - - ReadIfVirtualFilePointer(&pContext->Cabinet, hFile, cb); - - if (!::ReadFile(hFile, pv, cb, &cbRead, NULL)) - { - ExitWithLastError(hr, "Failed to read during cabinet extraction."); - } - -LExit: - pContext->Cabinet.hrError = hr; - return FAILED(hr) ? -1 : cbRead; -} - -static UINT FAR DIAMONDAPI CabWrite( - __in INT_PTR /* hf */, - __in void FAR *pv, - __in UINT cb - ) -{ - HRESULT hr = S_OK; - BURN_CONTAINER_CONTEXT* pContext = vpContext; - DWORD cbWrite = 0; - - switch (pContext->Cabinet.operation) - { - case BURN_CAB_OPERATION_STREAM_TO_FILE: - // write file - if (!::WriteFile(pContext->Cabinet.hTargetFile, pv, cb, &cbWrite, NULL)) - { - ExitWithLastError(hr, "Failed to write during cabinet extraction."); - } - break; - - case BURN_CAB_OPERATION_STREAM_TO_BUFFER: - // copy to target buffer - memcpy_s(pContext->Cabinet.pbTargetBuffer + pContext->Cabinet.iTargetBuffer, pContext->Cabinet.cbTargetBuffer - pContext->Cabinet.iTargetBuffer, pv, cb); - pContext->Cabinet.iTargetBuffer += cb; - - cbWrite = cb; - break; - - default: - hr = E_INVALIDSTATE; - ExitOnFailure(hr, "Unexpected call to CabWrite()."); - } - -LExit: - pContext->Cabinet.hrError = hr; - return FAILED(hr) ? -1 : cbWrite; -} - -static long FAR DIAMONDAPI CabSeek( - __in INT_PTR hf, - __in long dist, - __in int seektype - ) -{ - HRESULT hr = S_OK; - BURN_CONTAINER_CONTEXT* pContext = vpContext; - HANDLE hFile = (HANDLE)hf; - LARGE_INTEGER liDistance = { }; - LARGE_INTEGER liNewPointer = { }; - DWORD dwSeekType = 0; - - // We assume that CabSeek() will only be called to seek the - // cabinet itself so we have to offset the seek operations to - // where the internal cabinet starts. - switch (seektype) - { - case FILE_BEGIN: - liDistance.QuadPart = pContext->qwOffset + dist; - dwSeekType = FILE_BEGIN; - break; - - case FILE_CURRENT: - liDistance.QuadPart = dist; - dwSeekType = FILE_CURRENT; - break; - - case FILE_END: - liDistance.QuadPart = pContext->qwOffset + pContext->qwSize + dist; - dwSeekType = FILE_BEGIN; - break; - - default: - hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid seek type.");; - } - - if (SetIfVirtualFilePointer(&pContext->Cabinet, hFile, liDistance.QuadPart, &liNewPointer.QuadPart, seektype)) - { - // set file pointer - if (!::SetFilePointerEx(hFile, liDistance, &liNewPointer, seektype)) - { - ExitWithLastError(hr, "Failed to move file pointer 0x%x bytes.", dist); - } - } - - liNewPointer.QuadPart -= pContext->qwOffset; - -LExit: - pContext->Cabinet.hrError = hr; - return FAILED(hr) ? -1 : liNewPointer.LowPart; -} - -static int FAR DIAMONDAPI CabClose( - __in INT_PTR hf - ) -{ - BURN_CONTAINER_CONTEXT* pContext = vpContext; - HANDLE hFile = (HANDLE)hf; - - CloseIfVirturalFilePointer(&pContext->Cabinet, hFile); - ReleaseFileHandle(hFile); - - return 0; -} - -static HRESULT AddVirtualFilePointer( - __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, - __in HANDLE hFile, - __in LONGLONG llInitialFilePointer - ) -{ - HRESULT hr = S_OK; - - hr = MemEnsureArraySize(reinterpret_cast(&pCabinetContext->rgVirtualFilePointers), pCabinetContext->cVirtualFilePointers, sizeof(BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER), ARRAY_GROWTH_SIZE); - ExitOnFailure(hr, "Failed to allocate memory for the virtual file pointer array."); - - pCabinetContext->rgVirtualFilePointers[pCabinetContext->cVirtualFilePointers].hFile = hFile; - pCabinetContext->rgVirtualFilePointers[pCabinetContext->cVirtualFilePointers].liPosition.QuadPart = llInitialFilePointer; - ++pCabinetContext->cVirtualFilePointers; - -LExit: - return hr; -} - -static HRESULT ReadIfVirtualFilePointer( - __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, - __in HANDLE hFile, - __in DWORD cbRead - ) -{ - HRESULT hr = E_NOTFOUND; - - BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile); - if (pVfp) - { - // Set the file handle to the virtual file pointer. - if (!::SetFilePointerEx(hFile, pVfp->liPosition, NULL, FILE_BEGIN)) - { - ExitWithLastError(hr, "Failed to move to virtual file pointer."); - } - - pVfp->liPosition.QuadPart += cbRead; // add the amount that will be read to advance the pointer. - hr = S_OK; - } - -LExit: - return hr; -} - -static BOOL SetIfVirtualFilePointer( - __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, - __in HANDLE hFile, - __in LONGLONG llDistance, - __out LONGLONG* pllNewPostion, - __in DWORD dwSeekType - ) -{ - BOOL fFound = FALSE; - - BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile); - if (pVfp) - { - switch (dwSeekType) - { - case FILE_BEGIN: - pVfp->liPosition.QuadPart = llDistance; - break; - - case FILE_CURRENT: - pVfp->liPosition.QuadPart += llDistance; - break; - - case FILE_END: __fallthrough; - default: - AssertSz(FALSE, "Unsupported seek type."); - break; - } - - *pllNewPostion = pVfp->liPosition.QuadPart; - fFound = TRUE; - } - - return fFound; -} - -static HRESULT CloseIfVirturalFilePointer( - __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, - __in HANDLE hFile - ) -{ - HRESULT hr = E_NOTFOUND; - - BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile); - if (pVfp) - { - pVfp->hFile = INVALID_HANDLE_VALUE; - pVfp->liPosition.QuadPart = 0; - hr = S_OK; - } - - return hr; -} - -static BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* GetVirtualFilePointer( - __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, - __in HANDLE hFile - ) -{ - for (DWORD i = 0; i < pCabinetContext->cVirtualFilePointers; ++i) - { - BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = pCabinetContext->rgVirtualFilePointers + i; - if (pVfp->hFile == hFile) - { - return pVfp; - } - } - - return NULL; -} diff --git a/src/engine/cabextract.h b/src/engine/cabextract.h deleted file mode 100644 index 31667f2b..00000000 --- a/src/engine/cabextract.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -// function declarations - -void CabExtractInitialize(); -HRESULT CabExtractOpen( - __in BURN_CONTAINER_CONTEXT* pContext, - __in LPCWSTR wzFilePath - ); -HRESULT CabExtractNextStream( - __in BURN_CONTAINER_CONTEXT* pContext, - __inout_z LPWSTR* psczStreamName - ); -HRESULT CabExtractStreamToFile( - __in BURN_CONTAINER_CONTEXT* pContext, - __in_z LPCWSTR wzFileName - ); -HRESULT CabExtractStreamToBuffer( - __in BURN_CONTAINER_CONTEXT* pContext, - __out BYTE** ppbBuffer, - __out SIZE_T* pcbBuffer - ); -HRESULT CabExtractSkipStream( - __in BURN_CONTAINER_CONTEXT* pContext - ); -HRESULT CabExtractClose( - __in BURN_CONTAINER_CONTEXT* pContext - ); - - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/cache.cpp b/src/engine/cache.cpp deleted file mode 100644 index 59daf139..00000000 --- a/src/engine/cache.cpp +++ /dev/null @@ -1,2052 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -static const LPCWSTR BUNDLE_CLEAN_ROOM_WORKING_FOLDER_NAME = L".cr"; -static const LPCWSTR BUNDLE_WORKING_FOLDER_NAME = L".be"; -static const LPCWSTR UNVERIFIED_CACHE_FOLDER_NAME = L".unverified"; -static const LPCWSTR PACKAGE_CACHE_FOLDER_NAME = L"Package Cache"; -static const DWORD FILE_OPERATION_RETRY_COUNT = 3; -static const DWORD FILE_OPERATION_RETRY_WAIT = 2000; - -static BOOL vfInitializedCache = FALSE; -static BOOL vfRunningFromCache = FALSE; -static LPWSTR vsczSourceProcessFolder = NULL; -static LPWSTR vsczWorkingFolder = NULL; -static LPWSTR vsczDefaultUserPackageCache = NULL; -static LPWSTR vsczDefaultMachinePackageCache = NULL; -static LPWSTR vsczCurrentMachinePackageCache = NULL; - -static HRESULT CalculateWorkingFolder( - __in_z LPCWSTR wzBundleId, - __deref_out_z LPWSTR* psczWorkingFolder - ); -static HRESULT GetLastUsedSourceFolder( - __in BURN_VARIABLES* pVariables, - __out_z LPWSTR* psczLastSource - ); -static HRESULT CreateCompletedPath( - __in BOOL fPerMachine, - __in LPCWSTR wzCacheId, - __out LPWSTR* psczCacheDirectory - ); -static HRESULT CreateUnverifiedPath( - __in BOOL fPerMachine, - __in_z LPCWSTR wzPayloadId, - __out_z LPWSTR* psczUnverifiedPayloadPath - ); -static HRESULT GetRootPath( - __in BOOL fPerMachine, - __in BOOL fAllowRedirect, - __deref_out_z LPWSTR* psczRootPath - ); -static HRESULT VerifyThenTransferContainer( - __in BURN_CONTAINER* pContainer, - __in_z LPCWSTR wzCachedPath, - __in_z LPCWSTR wzUnverifiedContainerPath, - __in BOOL fMove, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ); -static HRESULT VerifyThenTransferPayload( - __in BURN_PAYLOAD* pPayload, - __in_z LPCWSTR wzCachedPath, - __in_z LPCWSTR wzUnverifiedPayloadPath, - __in BOOL fMove, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ); -static HRESULT CacheTransferFileWithRetry( - __in_z LPCWSTR wzSourcePath, - __in_z LPCWSTR wzDestinationPath, - __in BOOL fMove, - __in BURN_CACHE_STEP cacheStep, - __in DWORD64 qwFileSize, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ); -static HRESULT VerifyFileAgainstContainer( - __in BURN_CONTAINER* pContainer, - __in_z LPCWSTR wzVerifyPath, - __in BOOL fAlreadyCached, - __in BURN_CACHE_STEP cacheStep, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ); -static HRESULT VerifyFileAgainstPayload( - __in BURN_PAYLOAD* pPayload, - __in_z LPCWSTR wzVerifyPath, - __in BOOL fAlreadyCached, - __in BURN_CACHE_STEP cacheStep, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ); -static HRESULT ResetPathPermissions( - __in BOOL fPerMachine, - __in_z LPCWSTR wzPath - ); -static HRESULT SecurePath( - __in LPCWSTR wzPath - ); -static HRESULT CopyEngineToWorkingFolder( - __in_z LPCWSTR wzSourcePath, - __in_z LPCWSTR wzWorkingFolderName, - __in_z LPCWSTR wzExecutableName, - __in BURN_SECTION* pSection, - __deref_out_z_opt LPWSTR* psczEngineWorkingPath - ); -static HRESULT CopyEngineWithSignatureFixup( - __in HANDLE hEngineFile, - __in_z LPCWSTR wzEnginePath, - __in_z LPCWSTR wzTargetPath, - __in BURN_SECTION* pSection - ); -static HRESULT RemoveBundleOrPackage( - __in BOOL fBundle, - __in BOOL fPerMachine, - __in_z LPCWSTR wzBundleOrPackageId, - __in_z LPCWSTR wzCacheId - ); -static HRESULT VerifyHash( - __in BYTE* pbHash, - __in DWORD cbHash, - __in DWORD64 qwFileSize, - __in_z LPCWSTR wzUnverifiedPayloadPath, - __in HANDLE hFile, - __in BURN_CACHE_STEP cacheStep, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ); -static HRESULT SendCacheBeginMessage( - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPVOID pContext, - __in BURN_CACHE_STEP cacheStep - ); -static HRESULT SendCacheSuccessMessage( - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPVOID pContext, - __in DWORD64 qwFileSize - ); -static HRESULT SendCacheCompleteMessage( - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPVOID pContext, - __in HRESULT hrStatus - ); - - -extern "C" HRESULT CacheInitialize( - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables, - __in_z_opt LPCWSTR wzSourceProcessPath - ) -{ - HRESULT hr = S_OK; - LPWSTR sczCurrentPath = NULL; - LPWSTR sczCompletedFolder = NULL; - LPWSTR sczCompletedPath = NULL; - LPWSTR sczOriginalSource = NULL; - LPWSTR sczOriginalSourceFolder = NULL; - int nCompare = 0; - - if (!vfInitializedCache) - { - hr = PathForCurrentProcess(&sczCurrentPath, NULL); - ExitOnFailure(hr, "Failed to get current process path."); - - // Determine if we are running from the package cache or not. - hr = CacheGetCompletedPath(pRegistration->fPerMachine, pRegistration->sczId, &sczCompletedFolder); - ExitOnFailure(hr, "Failed to get completed path for bundle."); - - hr = PathConcat(sczCompletedFolder, pRegistration->sczExecutableName, &sczCompletedPath); - ExitOnFailure(hr, "Failed to combine working path with engine file name."); - - hr = PathCompare(sczCurrentPath, sczCompletedPath, &nCompare); - ExitOnFailure(hr, "Failed to compare current path for bundle: %ls", sczCurrentPath); - - vfRunningFromCache = (CSTR_EQUAL == nCompare); - - // If a source process path was not provided (e.g. we are not being - // run in a clean room) then use the current process path as the - // source process path. - if (!wzSourceProcessPath) - { - wzSourceProcessPath = sczCurrentPath; - } - - hr = PathGetDirectory(wzSourceProcessPath, &vsczSourceProcessFolder); - ExitOnFailure(hr, "Failed to initialize cache source folder."); - - // If we're not running from the cache, ensure the original source is set. - if (!vfRunningFromCache) - { - // If the original source has not been set already then set it where the bundle is - // running from right now. This value will be persisted and we'll use it when launched - // from the clean room or package cache since none of our packages will be relative to - // those locations. - hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource); - if (E_NOTFOUND == hr) - { - hr = VariableSetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, wzSourceProcessPath, FALSE, FALSE); - ExitOnFailure(hr, "Failed to set original source variable."); - - hr = StrAllocString(&sczOriginalSource, wzSourceProcessPath, 0); - ExitOnFailure(hr, "Failed to copy current path to original source."); - } - - hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, &sczOriginalSourceFolder); - if (E_NOTFOUND == hr) - { - hr = PathGetDirectory(sczOriginalSource, &sczOriginalSourceFolder); - ExitOnFailure(hr, "Failed to get directory from original source path."); - - hr = VariableSetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, sczOriginalSourceFolder, FALSE, FALSE); - ExitOnFailure(hr, "Failed to set original source directory variable."); - } - } - - vfInitializedCache = TRUE; - } - -LExit: - ReleaseStr(sczCurrentPath); - ReleaseStr(sczCompletedFolder); - ReleaseStr(sczCompletedPath); - ReleaseStr(sczOriginalSource); - ReleaseStr(sczOriginalSourceFolder); - - return hr; -} - -extern "C" HRESULT CacheEnsureWorkingFolder( - __in_z_opt LPCWSTR wzBundleId, - __deref_out_z_opt LPWSTR* psczWorkingFolder - ) -{ - HRESULT hr = S_OK; - LPWSTR sczWorkingFolder = NULL; - - hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder); - ExitOnFailure(hr, "Failed to calculate working folder to ensure it exists."); - - hr = DirEnsureExists(sczWorkingFolder, NULL); - ExitOnFailure(hr, "Failed create working folder."); - - // Best effort to ensure our working folder is not encrypted. - ::DecryptFileW(sczWorkingFolder, 0); - - if (psczWorkingFolder) - { - hr = StrAllocString(psczWorkingFolder, sczWorkingFolder, 0); - ExitOnFailure(hr, "Failed to copy working folder."); - } - -LExit: - ReleaseStr(sczWorkingFolder); - - return hr; -} - -extern "C" HRESULT CacheCalculateBundleWorkingPath( - __in_z LPCWSTR wzBundleId, - __in LPCWSTR wzExecutableName, - __deref_out_z LPWSTR* psczWorkingPath - ) -{ - Assert(vfInitializedCache); - - HRESULT hr = S_OK; - LPWSTR sczWorkingFolder = NULL; - - // If the bundle is running out of the package cache then we use that as the - // working folder since we feel safe in the package cache. - if (vfRunningFromCache) - { - hr = PathForCurrentProcess(psczWorkingPath, NULL); - ExitOnFailure(hr, "Failed to get current process path."); - } - else // Otherwise, use the real working folder. - { - hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder); - ExitOnFailure(hr, "Failed to get working folder for bundle."); - - hr = StrAllocFormatted(psczWorkingPath, L"%ls%ls\\%ls", sczWorkingFolder, BUNDLE_WORKING_FOLDER_NAME, wzExecutableName); - ExitOnFailure(hr, "Failed to calculate the bundle working path."); - } - -LExit: - ReleaseStr(sczWorkingFolder); - - return hr; -} - -extern "C" HRESULT CacheCalculateBundleLayoutWorkingPath( - __in_z LPCWSTR wzBundleId, - __deref_out_z LPWSTR* psczWorkingPath - ) -{ - HRESULT hr = S_OK; - LPWSTR sczWorkingFolder = NULL; - - hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath); - ExitOnFailure(hr, "Failed to get working folder for bundle layout."); - - hr = StrAllocConcat(psczWorkingPath, wzBundleId, 0); - ExitOnFailure(hr, "Failed to append bundle id for bundle layout working path."); - -LExit: - ReleaseStr(sczWorkingFolder); - - return hr; -} - -extern "C" HRESULT CacheCalculatePayloadWorkingPath( - __in_z LPCWSTR wzBundleId, - __in BURN_PAYLOAD* pPayload, - __deref_out_z LPWSTR* psczWorkingPath - ) -{ - HRESULT hr = S_OK; - - hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath); - ExitOnFailure(hr, "Failed to get working folder for payload."); - - hr = StrAllocConcat(psczWorkingPath, pPayload->sczKey, 0); - ExitOnFailure(hr, "Failed to append Id as payload unverified path."); - -LExit: - return hr; -} - -extern "C" HRESULT CacheCalculateContainerWorkingPath( - __in_z LPCWSTR wzBundleId, - __in BURN_CONTAINER* pContainer, - __deref_out_z LPWSTR* psczWorkingPath - ) -{ - HRESULT hr = S_OK; - - hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath); - ExitOnFailure(hr, "Failed to get working folder for container."); - - hr = StrAllocConcat(psczWorkingPath, pContainer->sczHash, 0); - ExitOnFailure(hr, "Failed to append hash as container unverified path."); - -LExit: - return hr; -} - -extern "C" HRESULT CacheGetRootCompletedPath( - __in BOOL fPerMachine, - __in BOOL fForceInitialize, - __deref_out_z LPWSTR* psczRootCompletedPath - ) -{ - HRESULT hr = S_OK; - - if (fForceInitialize) - { - hr = CreateCompletedPath(fPerMachine, L"", psczRootCompletedPath); - } - else - { - hr = GetRootPath(fPerMachine, TRUE, psczRootCompletedPath); - } - - return hr; -} - -extern "C" HRESULT CacheGetCompletedPath( - __in BOOL fPerMachine, - __in_z LPCWSTR wzCacheId, - __deref_out_z LPWSTR* psczCompletedPath - ) -{ - HRESULT hr = S_OK; - BOOL fRedirected = FALSE; - LPWSTR sczRootPath = NULL; - LPWSTR sczCurrentCompletedPath = NULL; - LPWSTR sczDefaultCompletedPath = NULL; - - hr = GetRootPath(fPerMachine, TRUE, &sczRootPath); - ExitOnFailure(hr, "Failed to get %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user"); - - // GetRootPath returns S_FALSE if the package cache is redirected elsewhere. - fRedirected = S_FALSE == hr; - - hr = PathConcat(sczRootPath, wzCacheId, &sczCurrentCompletedPath); - ExitOnFailure(hr, "Failed to construct cache path."); - - hr = PathBackslashTerminate(&sczCurrentCompletedPath); - ExitOnFailure(hr, "Failed to ensure cache path was backslash terminated."); - - // Return the old package cache directory if the new directory does not exist but the old directory does. - // If neither package cache directory exists return the (possibly) redirected package cache directory. - if (fRedirected && !DirExists(sczCurrentCompletedPath, NULL)) - { - hr = GetRootPath(fPerMachine, FALSE, &sczRootPath); - ExitOnFailure(hr, "Failed to get old %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user"); - - hr = PathConcat(sczRootPath, wzCacheId, &sczDefaultCompletedPath); - ExitOnFailure(hr, "Failed to construct cache path."); - - hr = PathBackslashTerminate(&sczDefaultCompletedPath); - ExitOnFailure(hr, "Failed to ensure cache path was backslash terminated."); - - if (DirExists(sczDefaultCompletedPath, NULL)) - { - *psczCompletedPath = sczDefaultCompletedPath; - sczDefaultCompletedPath = NULL; - - ExitFunction(); - } - } - - *psczCompletedPath = sczCurrentCompletedPath; - sczCurrentCompletedPath = NULL; - -LExit: - ReleaseNullStr(sczDefaultCompletedPath); - ReleaseNullStr(sczCurrentCompletedPath); - ReleaseNullStr(sczRootPath); - - return hr; -} - -extern "C" HRESULT CacheGetResumePath( - __in_z LPCWSTR wzPayloadWorkingPath, - __deref_out_z LPWSTR* psczResumePath - ) -{ - HRESULT hr = S_OK; - - hr = StrAllocFormatted(psczResumePath, L"%ls.R", wzPayloadWorkingPath); - ExitOnFailure(hr, "Failed to create resume path."); - -LExit: - return hr; -} - -extern "C" HRESULT CacheGetLocalSourcePaths( - __in_z LPCWSTR wzRelativePath, - __in_z LPCWSTR wzSourcePath, - __in_z LPCWSTR wzDestinationPath, - __in_z_opt LPCWSTR wzLayoutDirectory, - __in BURN_VARIABLES* pVariables, - __inout LPWSTR** prgSearchPaths, - __out DWORD* pcSearchPaths, - __out DWORD* pdwLikelySearchPath, - __out DWORD* pdwDestinationSearchPath - ) -{ - HRESULT hr = S_OK; - LPWSTR sczCurrentPath = NULL; - LPWSTR sczLastSourceFolder = NULL; - LPWSTR* psczPath = NULL; - BOOL fPreferSourcePathLocation = FALSE; - BOOL fTryLastFolder = FALSE; - BOOL fTryRelativePath = FALSE; - BOOL fSourceIsAbsolute = FALSE; - DWORD cSearchPaths = 0; - DWORD dwLikelySearchPath = 0; - DWORD dwDestinationSearchPath = 0; - - AssertSz(vfInitializedCache, "Cache wasn't initialized"); - - hr = GetLastUsedSourceFolder(pVariables, &sczLastSourceFolder); - fPreferSourcePathLocation = !vfRunningFromCache || FAILED(hr); - fTryLastFolder = SUCCEEDED(hr) && sczLastSourceFolder && *sczLastSourceFolder && CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, vsczSourceProcessFolder, -1, sczLastSourceFolder, -1); - fTryRelativePath = CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzSourcePath, -1, wzRelativePath, -1); - fSourceIsAbsolute = PathIsAbsolute(wzSourcePath); - - // If the source path provided is a full path, try that first. - if (fSourceIsAbsolute) - { - hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); - ExitOnFailure(hr, "Failed to ensure size for search paths array."); - - psczPath = *prgSearchPaths + cSearchPaths; - ++cSearchPaths; - - hr = StrAllocString(psczPath, wzSourcePath, 0); - ExitOnFailure(hr, "Failed to copy absolute source path."); - } - else - { - // If none of the paths exist, then most BAs will want to prompt the user with a possible path. - // The destination path is a temporary location and so not really a possible path. - dwLikelySearchPath = 1; - } - - // Try the destination path next. - hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); - ExitOnFailure(hr, "Failed to ensure size for search paths array."); - - dwDestinationSearchPath = cSearchPaths; - psczPath = *prgSearchPaths + cSearchPaths; - ++cSearchPaths; - - hr = StrAllocString(psczPath, wzDestinationPath, 0); - ExitOnFailure(hr, "Failed to copy absolute source path."); - - if (!fSourceIsAbsolute) - { - // Calculate the source path location. - // In the case where we are in the bundle's package cache and - // couldn't find a last used source that will be the package cache path - // which isn't likely to have what we are looking for. - hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); - ExitOnFailure(hr, "Failed to ensure size for search paths array."); - - hr = PathConcat(vsczSourceProcessFolder, wzSourcePath, &sczCurrentPath); - ExitOnFailure(hr, "Failed to combine source process folder with source."); - - // If we're not running from cache or we couldn't get the last source, - // try the source path location next. - if (fPreferSourcePathLocation) - { - (*prgSearchPaths)[cSearchPaths] = sczCurrentPath; - ++cSearchPaths; - sczCurrentPath = NULL; - } - - // If we have a last used source and it is not the source path location, - // add the last used source to the search path next. - if (fTryLastFolder) - { - hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); - ExitOnFailure(hr, "Failed to ensure size for search paths array."); - - psczPath = *prgSearchPaths + cSearchPaths; - ++cSearchPaths; - - hr = PathConcat(sczLastSourceFolder, wzSourcePath, psczPath); - ExitOnFailure(hr, "Failed to combine last source with source."); - } - - if (!fPreferSourcePathLocation) - { - (*prgSearchPaths)[cSearchPaths] = sczCurrentPath; - ++cSearchPaths; - sczCurrentPath = NULL; - } - - // Also consider the layout directory if doing Layout. - if (wzLayoutDirectory) - { - hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); - ExitOnFailure(hr, "Failed to ensure size for search paths array."); - - psczPath = *prgSearchPaths + cSearchPaths; - ++cSearchPaths; - - hr = PathConcat(wzLayoutDirectory, wzSourcePath, psczPath); - ExitOnFailure(hr, "Failed to combine layout source with source."); - } - } - - if (fTryRelativePath) - { - hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); - ExitOnFailure(hr, "Failed to ensure size for search paths array."); - - hr = PathConcat(vsczSourceProcessFolder, wzRelativePath, &sczCurrentPath); - ExitOnFailure(hr, "Failed to combine source process folder with relative."); - - if (fPreferSourcePathLocation) - { - (*prgSearchPaths)[cSearchPaths] = sczCurrentPath; - ++cSearchPaths; - sczCurrentPath = NULL; - } - - if (fTryLastFolder) - { - hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); - ExitOnFailure(hr, "Failed to ensure size for search paths array."); - - psczPath = *prgSearchPaths + cSearchPaths; - ++cSearchPaths; - - hr = PathConcat(sczLastSourceFolder, wzRelativePath, psczPath); - ExitOnFailure(hr, "Failed to combine last source with relative."); - } - - if (!fPreferSourcePathLocation) - { - (*prgSearchPaths)[cSearchPaths] = sczCurrentPath; - ++cSearchPaths; - sczCurrentPath = NULL; - } - - if (wzLayoutDirectory) - { - hr = MemEnsureArraySize(reinterpret_cast(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS); - ExitOnFailure(hr, "Failed to ensure size for search paths array."); - - psczPath = *prgSearchPaths + cSearchPaths; - ++cSearchPaths; - - hr = PathConcat(wzLayoutDirectory, wzSourcePath, psczPath); - ExitOnFailure(hr, "Failed to combine layout source with relative."); - } - } - -LExit: - ReleaseStr(sczCurrentPath); - ReleaseStr(sczLastSourceFolder); - - AssertSz(cSearchPaths <= BURN_CACHE_MAX_SEARCH_PATHS, "Got more than BURN_CACHE_MAX_SEARCH_PATHS search paths"); - *pcSearchPaths = cSearchPaths; - *pdwLikelySearchPath = dwLikelySearchPath; - *pdwDestinationSearchPath = dwDestinationSearchPath; - - return hr; -} - -extern "C" HRESULT CacheSetLastUsedSource( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzSourcePath, - __in_z LPCWSTR wzRelativePath - ) -{ - HRESULT hr = S_OK; - size_t cchSourcePath = 0; - size_t cchRelativePath = 0; - size_t iSourceRelativePath = 0; - LPWSTR sczSourceFolder = NULL; - LPWSTR sczLastSourceFolder = NULL; - int nCompare = 0; - - hr = ::StringCchLengthW(wzSourcePath, STRSAFE_MAX_CCH, &cchSourcePath); - ExitOnFailure(hr, "Failed to determine length of source path."); - - hr = ::StringCchLengthW(wzRelativePath, STRSAFE_MAX_CCH, &cchRelativePath); - ExitOnFailure(hr, "Failed to determine length of relative path."); - - // If the source path is smaller than the relative path (plus space for "X:\") then we know they - // are not relative to each other. - if (cchSourcePath < cchRelativePath + 3) - { - ExitFunction(); - } - - // If the source path ends with the relative path then this source could be a new path. - iSourceRelativePath = cchSourcePath - cchRelativePath; - if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzSourcePath + iSourceRelativePath, -1, wzRelativePath, -1)) - { - hr = StrAllocString(&sczSourceFolder, wzSourcePath, iSourceRelativePath); - ExitOnFailure(hr, "Failed to trim source folder."); - - hr = VariableGetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, &sczLastSourceFolder); - if (SUCCEEDED(hr)) - { - nCompare = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczSourceFolder, -1, sczLastSourceFolder, -1); - } - else if (E_NOTFOUND == hr) - { - nCompare = CSTR_GREATER_THAN; - hr = S_OK; - } - - if (CSTR_EQUAL != nCompare) - { - hr = VariableSetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, sczSourceFolder, FALSE, FALSE); - ExitOnFailure(hr, "Failed to set last source."); - } - } - -LExit: - ReleaseStr(sczLastSourceFolder); - ReleaseStr(sczSourceFolder); - - return hr; -} - -extern "C" HRESULT CacheSendProgressCallback( - __in DOWNLOAD_CACHE_CALLBACK* pCallback, - __in DWORD64 dw64Progress, - __in DWORD64 dw64Total, - __in HANDLE hDestinationFile - ) -{ - static LARGE_INTEGER LARGE_INTEGER_ZERO = { }; - - HRESULT hr = S_OK; - DWORD dwResult = PROGRESS_CONTINUE; - LARGE_INTEGER liTotalSize = { }; - LARGE_INTEGER liTotalTransferred = { }; - - if (pCallback->pfnProgress) - { - liTotalSize.QuadPart = dw64Total; - liTotalTransferred.QuadPart = dw64Progress; - - dwResult = (*pCallback->pfnProgress)(liTotalSize, liTotalTransferred, LARGE_INTEGER_ZERO, LARGE_INTEGER_ZERO, 1, CALLBACK_CHUNK_FINISHED, INVALID_HANDLE_VALUE, hDestinationFile, pCallback->pv); - switch (dwResult) - { - case PROGRESS_CONTINUE: - hr = S_OK; - break; - - case PROGRESS_CANCEL: __fallthrough; // TODO: should cancel and stop be treated differently? - case PROGRESS_STOP: - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - ExitOnRootFailure(hr, "UX aborted on download progress."); - - case PROGRESS_QUIET: // Not actually an error, just an indication to the caller to stop requesting progress. - pCallback->pfnProgress = NULL; - hr = S_OK; - break; - - default: - hr = E_UNEXPECTED; - ExitOnRootFailure(hr, "Invalid return code from progress routine."); - } - } - -LExit: - return hr; -} - -extern "C" void CacheSendErrorCallback( - __in DOWNLOAD_CACHE_CALLBACK* pCallback, - __in HRESULT hrError, - __in_z_opt LPCWSTR wzError, - __out_opt BOOL* pfRetry - ) -{ - if (pfRetry) - { - *pfRetry = FALSE; - } - - if (pCallback->pfnCancel) - { - int nResult = (*pCallback->pfnCancel)(hrError, wzError, pfRetry != NULL, pCallback->pv); - if (pfRetry && IDRETRY == nResult) - { - *pfRetry = TRUE; - } - } -} - -extern "C" BOOL CacheBundleRunningFromCache() -{ - return vfRunningFromCache; -} - -extern "C" HRESULT CacheBundleToCleanRoom( - __in BURN_SECTION* pSection, - __deref_out_z_opt LPWSTR* psczCleanRoomBundlePath - ) -{ - HRESULT hr = S_OK; - LPWSTR sczSourcePath = NULL; - LPWSTR wzExecutableName = NULL; - - hr = PathForCurrentProcess(&sczSourcePath, NULL); - ExitOnFailure(hr, "Failed to get current path for process to cache to clean room."); - - wzExecutableName = PathFile(sczSourcePath); - - hr = CopyEngineToWorkingFolder(sczSourcePath, BUNDLE_CLEAN_ROOM_WORKING_FOLDER_NAME, wzExecutableName, pSection, psczCleanRoomBundlePath); - ExitOnFailure(hr, "Failed to cache bundle to clean room."); - -LExit: - ReleaseStr(sczSourcePath); - - return hr; -} - -extern "C" HRESULT CacheBundleToWorkingDirectory( - __in_z LPCWSTR /*wzBundleId*/, - __in_z LPCWSTR wzExecutableName, - __in BURN_SECTION* pSection, - __deref_out_z_opt LPWSTR* psczEngineWorkingPath - ) -{ - Assert(vfInitializedCache); - - HRESULT hr = S_OK; - LPWSTR sczSourcePath = NULL; - - // Initialize the source. - hr = PathForCurrentProcess(&sczSourcePath, NULL); - ExitOnFailure(hr, "Failed to get current process path."); - - // If the bundle is running out of the package cache then we don't need to copy it to - // the working folder since we feel safe in the package cache and will run from there. - if (vfRunningFromCache) - { - hr = StrAllocString(psczEngineWorkingPath, sczSourcePath, 0); - ExitOnFailure(hr, "Failed to use current process path as target path."); - } - else // otherwise, carry on putting the bundle in the working folder. - { - hr = CopyEngineToWorkingFolder(sczSourcePath, BUNDLE_WORKING_FOLDER_NAME, wzExecutableName, pSection, psczEngineWorkingPath); - ExitOnFailure(hr, "Failed to copy engine to working folder."); - } - -LExit: - ReleaseStr(sczSourcePath); - - return hr; -} - -extern "C" HRESULT CacheLayoutBundle( - __in_z LPCWSTR wzExecutableName, - __in_z LPCWSTR wzLayoutDirectory, - __in_z LPCWSTR wzSourceBundlePath, - __in DWORD64 qwBundleSize, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ) -{ - HRESULT hr = S_OK; - LPWSTR sczTargetPath = NULL; - - hr = PathConcat(wzLayoutDirectory, wzExecutableName, &sczTargetPath); - ExitOnFailure(hr, "Failed to combine completed path with engine file name for layout."); - - LogStringLine(REPORT_STANDARD, "Layout bundle from: '%ls' to: '%ls'", wzSourceBundlePath, sczTargetPath); - - hr = CacheTransferFileWithRetry(wzSourceBundlePath, sczTargetPath, TRUE, BURN_CACHE_STEP_FINALIZE, qwBundleSize, pfnCacheMessageHandler, pfnProgress, pContext); - ExitOnFailure(hr, "Failed to layout bundle from: '%ls' to '%ls'", wzSourceBundlePath, sczTargetPath); - -LExit: - ReleaseStr(sczTargetPath); - - return hr; -} - -extern "C" HRESULT CacheCompleteBundle( - __in BOOL fPerMachine, - __in_z LPCWSTR wzExecutableName, - __in_z LPCWSTR wzBundleId, - __in_z LPCWSTR wzSourceBundlePath -#ifdef DEBUG - , __in_z LPCWSTR wzExecutablePath -#endif - ) -{ - HRESULT hr = S_OK; - int nCompare = 0; - LPWSTR sczTargetDirectory = NULL; - LPWSTR sczTargetPath = NULL; - LPWSTR sczSourceDirectory = NULL; - LPWSTR sczPayloadSourcePath = NULL; - - hr = CreateCompletedPath(fPerMachine, wzBundleId, &sczTargetDirectory); - ExitOnFailure(hr, "Failed to create completed cache path for bundle."); - - hr = PathConcat(sczTargetDirectory, wzExecutableName, &sczTargetPath); - ExitOnFailure(hr, "Failed to combine completed path with engine file name."); - - // We can't just use wzExecutablePath because we needed to call CreateCompletedPath to ensure that the destination was secured. - Assert(CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzExecutablePath, -1, sczTargetPath, -1)); - - // If the bundle is running out of the package cache then we don't need to copy it there - // (and don't want to since it'll be in use) so bail. - hr = PathCompare(wzSourceBundlePath, sczTargetPath, &nCompare); - ExitOnFailure(hr, "Failed to compare completed cache path for bundle: %ls", wzSourceBundlePath); - - if (CSTR_EQUAL == nCompare) - { - ExitFunction(); - } - - // Otherwise, carry on putting the bundle in the cache. - LogStringLine(REPORT_STANDARD, "Caching bundle from: '%ls' to: '%ls'", wzSourceBundlePath, sczTargetPath); - - FileRemoveFromPendingRename(sczTargetPath); // best effort to ensure bundle is not deleted from cache post restart. - - hr = FileEnsureCopyWithRetry(wzSourceBundlePath, sczTargetPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); - ExitOnFailure(hr, "Failed to cache bundle from: '%ls' to '%ls'", wzSourceBundlePath, sczTargetPath); - - // Reset the path permissions in the cache. - hr = ResetPathPermissions(fPerMachine, sczTargetPath); - ExitOnFailure(hr, "Failed to reset permissions on cached bundle: '%ls'", sczTargetPath); - - hr = PathGetDirectory(wzSourceBundlePath, &sczSourceDirectory); - ExitOnFailure(hr, "Failed to get directory from engine working path: %ls", wzSourceBundlePath); - -LExit: - ReleaseStr(sczPayloadSourcePath); - ReleaseStr(sczSourceDirectory); - ReleaseStr(sczTargetPath); - ReleaseStr(sczTargetDirectory); - - return hr; -} - -extern "C" HRESULT CacheLayoutContainer( - __in BURN_CONTAINER* pContainer, - __in_z_opt LPCWSTR wzLayoutDirectory, - __in_z LPCWSTR wzUnverifiedContainerPath, - __in BOOL fMove, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ) -{ - HRESULT hr = S_OK; - LPWSTR sczCachedPath = NULL; - - hr = PathConcat(wzLayoutDirectory, pContainer->sczFilePath, &sczCachedPath); - ExitOnFailure(hr, "Failed to concat complete cached path."); - - hr = VerifyThenTransferContainer(pContainer, sczCachedPath, wzUnverifiedContainerPath, fMove, pfnCacheMessageHandler, pfnProgress, pContext); - ExitOnFailure(hr, "Failed to layout container from cached path: %ls", sczCachedPath); - -LExit: - ReleaseStr(sczCachedPath); - - return hr; -} - -extern "C" HRESULT CacheLayoutPayload( - __in BURN_PAYLOAD* pPayload, - __in_z_opt LPCWSTR wzLayoutDirectory, - __in_z LPCWSTR wzUnverifiedPayloadPath, - __in BOOL fMove, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ) -{ - HRESULT hr = S_OK; - LPWSTR sczCachedPath = NULL; - - hr = PathConcat(wzLayoutDirectory, pPayload->sczFilePath, &sczCachedPath); - ExitOnFailure(hr, "Failed to concat complete cached path."); - - hr = VerifyThenTransferPayload(pPayload, sczCachedPath, wzUnverifiedPayloadPath, fMove, pfnCacheMessageHandler, pfnProgress, pContext); - ExitOnFailure(hr, "Failed to layout payload from cached payload: %ls", sczCachedPath); - -LExit: - ReleaseStr(sczCachedPath); - - return hr; -} - -extern "C" HRESULT CacheCompletePayload( - __in BOOL fPerMachine, - __in BURN_PAYLOAD* pPayload, - __in_z LPCWSTR wzCacheId, - __in_z LPCWSTR wzWorkingPayloadPath, - __in BOOL fMove, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ) -{ - HRESULT hr = S_OK; - LPWSTR sczCachedDirectory = NULL; - LPWSTR sczCachedPath = NULL; - LPWSTR sczUnverifiedPayloadPath = NULL; - - hr = CreateCompletedPath(fPerMachine, wzCacheId, &sczCachedDirectory); - ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", wzCacheId); - - hr = PathConcat(sczCachedDirectory, pPayload->sczFilePath, &sczCachedPath); - ExitOnFailure(hr, "Failed to concat complete cached path."); - - // If the cached file matches what we expected, we're good. - hr = VerifyFileAgainstPayload(pPayload, sczCachedPath, TRUE, BURN_CACHE_STEP_HASH_TO_SKIP_VERIFY, pfnCacheMessageHandler, pfnProgress, pContext); - if (SUCCEEDED(hr)) - { - ExitFunction(); - } - - hr = CreateUnverifiedPath(fPerMachine, pPayload->sczKey, &sczUnverifiedPayloadPath); - ExitOnFailure(hr, "Failed to create unverified path."); - - // If the working path exists, let's get it into the unverified path so we can reset the ACLs and verify the file. - if (FileExistsEx(wzWorkingPayloadPath, NULL)) - { - hr = CacheTransferFileWithRetry(wzWorkingPayloadPath, sczUnverifiedPayloadPath, fMove, BURN_CACHE_STEP_STAGE, pPayload->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext); - ExitOnFailure(hr, "Failed to transfer working path to unverified path for payload: %ls.", pPayload->sczKey); - } - else if (FileExistsEx(sczUnverifiedPayloadPath, NULL)) - { - // Make sure the staging progress is sent even though there was nothing to do. - hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, BURN_CACHE_STEP_STAGE); - if (SUCCEEDED(hr)) - { - hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, pPayload->qwFileSize); - } - SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr); - ExitOnFailure(hr, "Aborted transferring working path to unverified path for payload: %ls.", pPayload->sczKey); - } - else // if the working path and unverified path do not exist, nothing we can do. - { - hr = E_FILENOTFOUND; - ExitOnFailure(hr, "Failed to find payload: %ls in working path: %ls and unverified path: %ls", pPayload->sczKey, wzWorkingPayloadPath, sczUnverifiedPayloadPath); - } - - hr = ResetPathPermissions(fPerMachine, sczUnverifiedPayloadPath); - ExitOnFailure(hr, "Failed to reset permissions on unverified cached payload: %ls", pPayload->sczKey); - - hr = VerifyFileAgainstPayload(pPayload, sczUnverifiedPayloadPath, FALSE, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext); - LogExitOnFailure(hr, MSG_FAILED_VERIFY_PAYLOAD, "Failed to verify payload: %ls at path: %ls", pPayload->sczKey, sczUnverifiedPayloadPath, NULL); - - LogId(REPORT_STANDARD, MSG_VERIFIED_ACQUIRED_PAYLOAD, pPayload->sczKey, sczUnverifiedPayloadPath, fMove ? "moving" : "copying", sczCachedPath); - - hr = CacheTransferFileWithRetry(sczUnverifiedPayloadPath, sczCachedPath, TRUE, BURN_CACHE_STEP_FINALIZE, pPayload->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext); - ExitOnFailure(hr, "Failed to move verified file to complete payload path: %ls", sczCachedPath); - - ::DecryptFileW(sczCachedPath, 0); // Let's try to make sure it's not encrypted. - -LExit: - ReleaseStr(sczUnverifiedPayloadPath); - ReleaseStr(sczCachedPath); - ReleaseStr(sczCachedDirectory); - - return hr; -} - -extern "C" HRESULT CacheVerifyContainer( - __in BURN_CONTAINER* pContainer, - __in_z LPCWSTR wzCachedDirectory, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ) -{ - HRESULT hr = S_OK; - LPWSTR sczCachedPath = NULL; - - hr = PathConcat(wzCachedDirectory, pContainer->sczFilePath, &sczCachedPath); - ExitOnFailure(hr, "Failed to concat complete cached path."); - - hr = VerifyFileAgainstContainer(pContainer, sczCachedPath, TRUE, BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE, pfnCacheMessageHandler, pfnProgress, pContext); - -LExit: - ReleaseStr(sczCachedPath); - - return hr; -} - -extern "C" HRESULT CacheVerifyPayload( - __in BURN_PAYLOAD* pPayload, - __in_z LPCWSTR wzCachedDirectory, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ) -{ - HRESULT hr = S_OK; - LPWSTR sczCachedPath = NULL; - - hr = PathConcat(wzCachedDirectory, pPayload->sczFilePath, &sczCachedPath); - ExitOnFailure(hr, "Failed to concat complete cached path."); - - hr = VerifyFileAgainstPayload(pPayload, sczCachedPath, TRUE, BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE, pfnCacheMessageHandler, pfnProgress, pContext); - -LExit: - ReleaseStr(sczCachedPath); - - return hr; -} - -extern "C" HRESULT CacheRemoveWorkingFolder( - __in_z_opt LPCWSTR wzBundleId - ) -{ - HRESULT hr = S_OK; - LPWSTR sczWorkingFolder = NULL; - - if (vfInitializedCache) - { - hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder); - ExitOnFailure(hr, "Failed to calculate the working folder to remove it."); - - // Try to clean out everything in the working folder. - hr = DirEnsureDeleteEx(sczWorkingFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE); - TraceError(hr, "Could not delete bundle engine working folder."); - } - -LExit: - ReleaseStr(sczWorkingFolder); - - return hr; -} - -extern "C" HRESULT CacheRemoveBundle( - __in BOOL fPerMachine, - __in_z LPCWSTR wzBundleId - ) -{ - HRESULT hr = S_OK; - - hr = RemoveBundleOrPackage(TRUE, fPerMachine, wzBundleId, wzBundleId); - ExitOnFailure(hr, "Failed to remove bundle id: %ls.", wzBundleId); - -LExit: - return hr; -} - -extern "C" HRESULT CacheRemovePackage( - __in BOOL fPerMachine, - __in_z LPCWSTR wzPackageId, - __in_z LPCWSTR wzCacheId - ) -{ - HRESULT hr = S_OK; - - hr = RemoveBundleOrPackage(FALSE, fPerMachine, wzPackageId, wzCacheId); - ExitOnFailure(hr, "Failed to remove package id: %ls.", wzPackageId); - -LExit: - return hr; -} - -extern "C" void CacheCleanup( - __in BOOL fPerMachine, - __in_z LPCWSTR wzBundleId - ) -{ - HRESULT hr = S_OK; - LPWSTR sczFolder = NULL; - LPWSTR sczFiles = NULL; - LPWSTR sczDelete = NULL; - HANDLE hFind = INVALID_HANDLE_VALUE; - WIN32_FIND_DATAW wfd = { }; - size_t cchFileName = 0; - - hr = CacheGetCompletedPath(fPerMachine, UNVERIFIED_CACHE_FOLDER_NAME, &sczFolder); - if (SUCCEEDED(hr)) - { - hr = DirEnsureDeleteEx(sczFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE); - } - - if (!fPerMachine) - { - hr = CalculateWorkingFolder(wzBundleId, &sczFolder); - if (SUCCEEDED(hr)) - { - hr = PathConcat(sczFolder, L"*.*", &sczFiles); - if (SUCCEEDED(hr)) - { - hFind = ::FindFirstFileW(sczFiles, &wfd); - if (INVALID_HANDLE_VALUE != hFind) - { - do - { - // Skip directories. - if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - continue; - } - - // Skip resume files (they end with ".R"). - hr = ::StringCchLengthW(wfd.cFileName, MAX_PATH, &cchFileName); - if (FAILED(hr) || - 2 < cchFileName && L'.' == wfd.cFileName[cchFileName - 2] && (L'R' == wfd.cFileName[cchFileName - 1] || L'r' == wfd.cFileName[cchFileName - 1])) - { - continue; - } - - hr = PathConcatCch(sczFolder, 0, wfd.cFileName, cchFileName, &sczDelete); - if (SUCCEEDED(hr)) - { - hr = FileEnsureDelete(sczDelete); - } - } while (::FindNextFileW(hFind, &wfd)); - } - } - } - } - - if (INVALID_HANDLE_VALUE != hFind) - { - ::FindClose(hFind); - } - - ReleaseStr(sczDelete); - ReleaseStr(sczFiles); - ReleaseStr(sczFolder); -} - -extern "C" void CacheUninitialize() -{ - ReleaseNullStr(vsczCurrentMachinePackageCache); - ReleaseNullStr(vsczDefaultMachinePackageCache); - ReleaseNullStr(vsczDefaultUserPackageCache); - ReleaseNullStr(vsczWorkingFolder); - ReleaseNullStr(vsczSourceProcessFolder); - - vfRunningFromCache = FALSE; - vfInitializedCache = FALSE; -} - -// Internal functions. - -static HRESULT CalculateWorkingFolder( - __in_z_opt LPCWSTR /*wzBundleId*/, - __deref_out_z LPWSTR* psczWorkingFolder - ) -{ - HRESULT hr = S_OK; - RPC_STATUS rs = RPC_S_OK; - BOOL fElevated = FALSE; - WCHAR wzTempPath[MAX_PATH] = { }; - UUID guid = {}; - WCHAR wzGuid[39]; - - if (!vsczWorkingFolder) - { - ProcElevated(::GetCurrentProcess(), &fElevated); - - if (fElevated) - { - if (!::GetWindowsDirectoryW(wzTempPath, countof(wzTempPath))) - { - ExitWithLastError(hr, "Failed to get windows path for working folder."); - } - - hr = PathFixedBackslashTerminate(wzTempPath, countof(wzTempPath)); - ExitOnFailure(hr, "Failed to ensure windows path for working folder ended in backslash."); - - hr = ::StringCchCatW(wzTempPath, countof(wzTempPath), L"Temp\\"); - ExitOnFailure(hr, "Failed to concat Temp directory on windows path for working folder."); - } - else if (0 == ::GetTempPathW(countof(wzTempPath), wzTempPath)) - { - ExitWithLastError(hr, "Failed to get temp path for working folder."); - } - - rs = ::UuidCreate(&guid); - hr = HRESULT_FROM_RPC(rs); - ExitOnFailure(hr, "Failed to create working folder guid."); - - if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid))) - { - hr = E_OUTOFMEMORY; - ExitOnRootFailure(hr, "Failed to convert working folder guid into string."); - } - - hr = StrAllocFormatted(&vsczWorkingFolder, L"%ls%ls\\", wzTempPath, wzGuid); - ExitOnFailure(hr, "Failed to append bundle id on to temp path for working folder."); - } - - hr = StrAllocString(psczWorkingFolder, vsczWorkingFolder, 0); - ExitOnFailure(hr, "Failed to copy working folder path."); - -LExit: - return hr; -} - -static HRESULT GetRootPath( - __in BOOL fPerMachine, - __in BOOL fAllowRedirect, - __deref_out_z LPWSTR* psczRootPath - ) -{ - HRESULT hr = S_OK; - LPWSTR sczAppData = NULL; - int nCompare = 0; - - // Cache paths are initialized once so they cannot be changed while the engine is caching payloads. - if (fPerMachine) - { - // Always construct the default machine package cache path so we can determine if we're redirected. - if (!vsczDefaultMachinePackageCache) - { - hr = PathGetKnownFolder(CSIDL_COMMON_APPDATA, &sczAppData); - ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-machine"); - - hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &vsczDefaultMachinePackageCache); - ExitOnFailure(hr, "Failed to construct %hs package cache directory name.", "per-machine"); - - hr = PathBackslashTerminate(&vsczDefaultMachinePackageCache); - ExitOnFailure(hr, "Failed to backslash terminate default %hs package cache directory name.", "per-machine"); - } - - if (!vsczCurrentMachinePackageCache) - { - hr = PolcReadString(POLICY_BURN_REGISTRY_PATH, L"PackageCache", NULL, &vsczCurrentMachinePackageCache); - ExitOnFailure(hr, "Failed to read PackageCache policy directory."); - - if (vsczCurrentMachinePackageCache) - { - hr = PathBackslashTerminate(&vsczCurrentMachinePackageCache); - ExitOnFailure(hr, "Failed to backslash terminate redirected per-machine package cache directory name."); - } - else - { - hr = StrAllocString(&vsczCurrentMachinePackageCache, vsczDefaultMachinePackageCache, 0); - ExitOnFailure(hr, "Failed to copy default package cache directory to current package cache directory."); - } - } - - hr = StrAllocString(psczRootPath, fAllowRedirect ? vsczCurrentMachinePackageCache : vsczDefaultMachinePackageCache, 0); - ExitOnFailure(hr, "Failed to copy %hs package cache root directory.", "per-machine"); - - hr = PathCompare(vsczDefaultMachinePackageCache, *psczRootPath, &nCompare); - ExitOnFailure(hr, "Failed to compare default and current package cache directories."); - - // Return S_FALSE if the current location is not the default location (redirected). - hr = CSTR_EQUAL == nCompare ? S_OK : S_FALSE; - } - else - { - if (!vsczDefaultUserPackageCache) - { - hr = PathGetKnownFolder(CSIDL_LOCAL_APPDATA, &sczAppData); - ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-user"); - - hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &vsczDefaultUserPackageCache); - ExitOnFailure(hr, "Failed to construct %hs package cache directory name.", "per-user"); - - hr = PathBackslashTerminate(&vsczDefaultUserPackageCache); - ExitOnFailure(hr, "Failed to backslash terminate default %hs package cache directory name.", "per-user"); - } - - hr = StrAllocString(psczRootPath, vsczDefaultUserPackageCache, 0); - ExitOnFailure(hr, "Failed to copy %hs package cache root directory.", "per-user"); - } - -LExit: - ReleaseStr(sczAppData); - - return hr; -} - -static HRESULT GetLastUsedSourceFolder( - __in BURN_VARIABLES* pVariables, - __out_z LPWSTR* psczLastSource - ) -{ - HRESULT hr = S_OK; - - hr = VariableGetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, psczLastSource); - if (E_NOTFOUND == hr) - { - // Try the original source folder. - hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, psczLastSource); - } - - return hr; -} - -static HRESULT CreateCompletedPath( - __in BOOL fPerMachine, - __in LPCWSTR wzId, - __out LPWSTR* psczCacheDirectory - ) -{ - static BOOL fPerMachineCacheRootVerified = FALSE; - - HRESULT hr = S_OK; - LPWSTR sczCacheDirectory = NULL; - - // If we are doing a permachine install but have not yet verified that the root cache folder - // was created with the correct ACLs yet, do that now. - if (fPerMachine && !fPerMachineCacheRootVerified) - { - hr = GetRootPath(fPerMachine, TRUE, &sczCacheDirectory); - ExitOnFailure(hr, "Failed to get cache directory."); - - hr = DirEnsureExists(sczCacheDirectory, NULL); - ExitOnFailure(hr, "Failed to create cache directory: %ls", sczCacheDirectory); - - hr = SecurePath(sczCacheDirectory); - ExitOnFailure(hr, "Failed to secure cache directory: %ls", sczCacheDirectory); - - fPerMachineCacheRootVerified = TRUE; - } - - // Get the cache completed path, ensure it exists, and reset any permissions people - // might have tried to set on the directory so we inherit the (correct!) security - // permissions from the parent directory. - hr = CacheGetCompletedPath(fPerMachine, wzId, &sczCacheDirectory); - ExitOnFailure(hr, "Failed to get cache directory."); - - hr = DirEnsureExists(sczCacheDirectory, NULL); - ExitOnFailure(hr, "Failed to create cache directory: %ls", sczCacheDirectory); - - ResetPathPermissions(fPerMachine, sczCacheDirectory); - - *psczCacheDirectory = sczCacheDirectory; - sczCacheDirectory = NULL; - -LExit: - ReleaseStr(sczCacheDirectory); - return hr; -} - -static HRESULT CreateUnverifiedPath( - __in BOOL fPerMachine, - __in_z LPCWSTR wzPayloadId, - __out_z LPWSTR* psczUnverifiedPayloadPath - ) -{ - static BOOL fUnverifiedCacheFolderCreated = FALSE; - - HRESULT hr = S_OK; - LPWSTR sczUnverifiedCacheFolder = NULL; - - hr = CacheGetCompletedPath(fPerMachine, UNVERIFIED_CACHE_FOLDER_NAME, &sczUnverifiedCacheFolder); - ExitOnFailure(hr, "Failed to get cache directory."); - - if (!fUnverifiedCacheFolderCreated) - { - hr = DirEnsureExists(sczUnverifiedCacheFolder, NULL); - ExitOnFailure(hr, "Failed to create unverified cache directory: %ls", sczUnverifiedCacheFolder); - - ResetPathPermissions(fPerMachine, sczUnverifiedCacheFolder); - } - - hr = PathConcat(sczUnverifiedCacheFolder, wzPayloadId, psczUnverifiedPayloadPath); - ExitOnFailure(hr, "Failed to concat payload id to unverified folder path."); - -LExit: - ReleaseStr(sczUnverifiedCacheFolder); - - return hr; -} - -static HRESULT VerifyThenTransferContainer( - __in BURN_CONTAINER* pContainer, - __in_z LPCWSTR wzCachedPath, - __in_z LPCWSTR wzUnverifiedContainerPath, - __in BOOL fMove, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ) -{ - HRESULT hr = S_OK; - HANDLE hFile = INVALID_HANDLE_VALUE; - - // Get the container on disk actual hash. - hFile = ::CreateFileW(wzUnverifiedContainerPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); - if (INVALID_HANDLE_VALUE == hFile) - { - ExitWithLastError(hr, "Failed to open container in working path: %ls", wzUnverifiedContainerPath); - } - - // Container should have a hash we can use to verify with. - if (pContainer->pbHash) - { - hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, pContainer->qwFileSize, wzUnverifiedContainerPath, hFile, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext); - ExitOnFailure(hr, "Failed to verify container hash: %ls", wzCachedPath); - } - - LogStringLine(REPORT_STANDARD, "%ls container from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedContainerPath, wzCachedPath); - - hr = CacheTransferFileWithRetry(wzUnverifiedContainerPath, wzCachedPath, fMove, BURN_CACHE_STEP_FINALIZE, pContainer->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext); - -LExit: - ReleaseFileHandle(hFile); - - return hr; -} - -static HRESULT VerifyThenTransferPayload( - __in BURN_PAYLOAD* pPayload, - __in_z LPCWSTR wzCachedPath, - __in_z LPCWSTR wzUnverifiedPayloadPath, - __in BOOL fMove, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ) -{ - HRESULT hr = S_OK; - HANDLE hFile = INVALID_HANDLE_VALUE; - - // Get the payload on disk actual hash. - hFile = ::CreateFileW(wzUnverifiedPayloadPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); - if (INVALID_HANDLE_VALUE == hFile) - { - ExitWithLastError(hr, "Failed to open payload in working path: %ls", wzUnverifiedPayloadPath); - } - - if (pPayload->pbHash) // the payload should have a hash we can use to verify it. - { - hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, wzUnverifiedPayloadPath, hFile, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext); - ExitOnFailure(hr, "Failed to verify payload hash: %ls", wzCachedPath); - } - - LogStringLine(REPORT_STANDARD, "%ls payload from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedPayloadPath, wzCachedPath); - - hr = CacheTransferFileWithRetry(wzUnverifiedPayloadPath, wzCachedPath, fMove, BURN_CACHE_STEP_FINALIZE, pPayload->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext); - -LExit: - ReleaseFileHandle(hFile); - - return hr; -} - -static HRESULT CacheTransferFileWithRetry( - __in_z LPCWSTR wzSourcePath, - __in_z LPCWSTR wzDestinationPath, - __in BOOL fMove, - __in BURN_CACHE_STEP cacheStep, - __in DWORD64 qwFileSize, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE /*pfnProgress*/, - __in LPVOID pContext - ) -{ - HRESULT hr = S_OK; - - hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, cacheStep); - ExitOnFailure(hr, "Aborted cache file transfer begin."); - - // TODO: send progress during the file transfer. - if (fMove) - { - hr = FileEnsureMoveWithRetry(wzSourcePath, wzDestinationPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); - ExitOnFailure(hr, "Failed to move %ls to %ls", wzSourcePath, wzDestinationPath); - } - else - { - hr = FileEnsureCopyWithRetry(wzSourcePath, wzDestinationPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); - ExitOnFailure(hr, "Failed to copy %ls to %ls", wzSourcePath, wzDestinationPath); - } - - hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, qwFileSize); - -LExit: - SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr); - - return hr; -} - -static HRESULT VerifyFileAgainstContainer( - __in BURN_CONTAINER* pContainer, - __in_z LPCWSTR wzVerifyPath, - __in BOOL fAlreadyCached, - __in BURN_CACHE_STEP cacheStep, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ) -{ - HRESULT hr = S_OK; - HANDLE hFile = INVALID_HANDLE_VALUE; - - // Get the container on disk actual hash. - hFile = ::CreateFileW(wzVerifyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); - if (INVALID_HANDLE_VALUE == hFile) - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - if (E_PATHNOTFOUND == hr || E_FILENOTFOUND == hr) - { - ExitFunction(); // do not log error when the file was not found. - } - ExitOnRootFailure(hr, "Failed to open container at path: %ls", wzVerifyPath); - } - - if (pContainer->pbHash) // the container should have a hash we can use to verify it. - { - hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, pContainer->qwFileSize, wzVerifyPath, hFile, cacheStep, pfnCacheMessageHandler, pfnProgress, pContext); - ExitOnFailure(hr, "Failed to verify hash of container: %ls", pContainer->sczId); - } - - if (fAlreadyCached) - { - LogId(REPORT_STANDARD, MSG_VERIFIED_EXISTING_CONTAINER, pContainer->sczId, wzVerifyPath); - ::DecryptFileW(wzVerifyPath, 0); // Let's try to make sure it's not encrypted. - } - -LExit: - ReleaseFileHandle(hFile); - - if (FAILED(hr) && E_PATHNOTFOUND != hr && E_FILENOTFOUND != hr) - { - if (fAlreadyCached) - { - LogErrorId(hr, MSG_FAILED_VERIFY_CONTAINER, pContainer->sczId, wzVerifyPath, NULL); - } - - FileEnsureDelete(wzVerifyPath); // if the file existed but did not verify correctly, make it go away. - } - - return hr; -} - -static HRESULT VerifyFileAgainstPayload( - __in BURN_PAYLOAD* pPayload, - __in_z LPCWSTR wzVerifyPath, - __in BOOL fAlreadyCached, - __in BURN_CACHE_STEP cacheStep, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ) -{ - HRESULT hr = S_OK; - HANDLE hFile = INVALID_HANDLE_VALUE; - - // Get the payload on disk actual hash. - hFile = ::CreateFileW(wzVerifyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); - if (INVALID_HANDLE_VALUE == hFile) - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - if (E_PATHNOTFOUND == hr || E_FILENOTFOUND == hr) - { - ExitFunction(); // do not log error when the file was not found. - } - ExitOnRootFailure(hr, "Failed to open payload at path: %ls", wzVerifyPath); - } - - if (pPayload->pbHash) // the payload should have a hash we can use to verify it. - { - hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, wzVerifyPath, hFile, cacheStep, pfnCacheMessageHandler, pfnProgress, pContext); - ExitOnFailure(hr, "Failed to verify hash of payload: %ls", pPayload->sczKey); - } - - if (fAlreadyCached) - { - LogId(REPORT_STANDARD, MSG_VERIFIED_EXISTING_PAYLOAD, pPayload->sczKey, wzVerifyPath); - ::DecryptFileW(wzVerifyPath, 0); // Let's try to make sure it's not encrypted. - } - -LExit: - ReleaseFileHandle(hFile); - - if (FAILED(hr) && E_PATHNOTFOUND != hr && E_FILENOTFOUND != hr) - { - if (fAlreadyCached) - { - LogErrorId(hr, MSG_FAILED_VERIFY_PAYLOAD, pPayload->sczKey, wzVerifyPath, NULL); - } - - FileEnsureDelete(wzVerifyPath); // if the file existed but did not verify correctly, make it go away. - } - - return hr; -} - -static HRESULT AllocateSid( - __in WELL_KNOWN_SID_TYPE type, - __out PSID* ppSid - ) -{ - HRESULT hr = S_OK; - PSID pAllocSid = NULL; - DWORD cbSid = SECURITY_MAX_SID_SIZE; - - pAllocSid = static_cast(MemAlloc(cbSid, TRUE)); - ExitOnNull(pAllocSid, hr, E_OUTOFMEMORY, "Failed to allocate memory for well known SID."); - - if (!::CreateWellKnownSid(type, NULL, pAllocSid, &cbSid)) - { - ExitWithLastError(hr, "Failed to create well known SID."); - } - - *ppSid = pAllocSid; - pAllocSid = NULL; - -LExit: - ReleaseMem(pAllocSid); - return hr; -} - - -static HRESULT ResetPathPermissions( - __in BOOL fPerMachine, - __in_z LPCWSTR wzPath - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - DWORD dwSetSecurity = DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION; - ACL acl = { }; - PSID pSid = NULL; - - if (fPerMachine) - { - hr = AllocateSid(WinBuiltinAdministratorsSid, &pSid); - ExitOnFailure(hr, "Failed to allocate administrator SID."); - - // Create an empty (not NULL!) ACL to reset the permissions on the file to purely inherit from parent. - if (!::InitializeAcl(&acl, sizeof(acl), ACL_REVISION)) - { - ExitWithLastError(hr, "Failed to initialize ACL."); - } - - dwSetSecurity |= OWNER_SECURITY_INFORMATION; - } - - hr = AclSetSecurityWithRetry(wzPath, SE_FILE_OBJECT, dwSetSecurity, pSid, NULL, &acl, NULL, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); - ExitOnWin32Error(er, hr, "Failed to reset the ACL on cached file: %ls", wzPath); - - ::SetFileAttributesW(wzPath, FILE_ATTRIBUTE_NORMAL); // Let's try to reset any possible read-only/system bits. - -LExit: - ReleaseMem(pSid); - return hr; -} - - -static HRESULT GrantAccessAndAllocateSid( - __in WELL_KNOWN_SID_TYPE type, - __in DWORD dwGrantAccess, - __in EXPLICIT_ACCESS* pAccess - ) -{ - HRESULT hr = S_OK; - - hr = AllocateSid(type, reinterpret_cast(&pAccess->Trustee.ptstrName)); - ExitOnFailure(hr, "Failed to allocate SID to grate access."); - - pAccess->grfAccessMode = GRANT_ACCESS; - pAccess->grfAccessPermissions = dwGrantAccess; - pAccess->grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; - pAccess->Trustee.TrusteeForm = TRUSTEE_IS_SID; - pAccess->Trustee.TrusteeType = TRUSTEE_IS_GROUP; - -LExit: - return hr; -} - - -static HRESULT SecurePath( - __in LPCWSTR wzPath - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - EXPLICIT_ACCESSW access[4] = { }; - PACL pAcl = NULL; - - // Administrators must be the first one in the array so we can reuse the allocated SID below. - hr = GrantAccessAndAllocateSid(WinBuiltinAdministratorsSid, FILE_ALL_ACCESS, &access[0]); - ExitOnFailure(hr, "Failed to allocate access for Administrators group to path: %ls", wzPath); - - hr = GrantAccessAndAllocateSid(WinLocalSystemSid, FILE_ALL_ACCESS, &access[1]); - ExitOnFailure(hr, "Failed to allocate access for SYSTEM group to path: %ls", wzPath); - - hr = GrantAccessAndAllocateSid(WinWorldSid, GENERIC_READ | GENERIC_EXECUTE, &access[2]); - ExitOnFailure(hr, "Failed to allocate access for Everyone group to path: %ls", wzPath); - - hr = GrantAccessAndAllocateSid(WinBuiltinUsersSid, GENERIC_READ | GENERIC_EXECUTE, &access[3]); - ExitOnFailure(hr, "Failed to allocate access for Users group to path: %ls", wzPath); - - er = ::SetEntriesInAclW(countof(access), access, NULL, &pAcl); - ExitOnWin32Error(er, hr, "Failed to create ACL to secure cache path: %ls", wzPath); - - // Set the ACL and ensure the Administrators group ends up the owner - hr = AclSetSecurityWithRetry(wzPath, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION, - reinterpret_cast(access[0].Trustee.ptstrName), NULL, pAcl, NULL, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); - ExitOnFailure(hr, "Failed to secure cache path: %ls", wzPath); - -LExit: - if (pAcl) - { - ::LocalFree(pAcl); - } - - for (DWORD i = 0; i < countof(access); ++i) - { - ReleaseMem(access[i].Trustee.ptstrName); - } - - return hr; -} - - -static HRESULT CopyEngineToWorkingFolder( - __in_z LPCWSTR wzSourcePath, - __in_z LPCWSTR wzWorkingFolderName, - __in_z LPCWSTR wzExecutableName, - __in BURN_SECTION* pSection, - __deref_out_z_opt LPWSTR* psczEngineWorkingPath - ) -{ - HRESULT hr = S_OK; - LPWSTR sczWorkingFolder = NULL; - LPWSTR sczTargetDirectory = NULL; - LPWSTR sczTargetPath = NULL; - LPWSTR sczSourceDirectory = NULL; - LPWSTR sczPayloadSourcePath = NULL; - LPWSTR sczPayloadTargetPath = NULL; - - hr = CacheEnsureWorkingFolder(NULL, &sczWorkingFolder); - ExitOnFailure(hr, "Failed to create working path to copy engine."); - - hr = PathConcat(sczWorkingFolder, wzWorkingFolderName, &sczTargetDirectory); - ExitOnFailure(hr, "Failed to calculate the bundle working folder target name."); - - hr = DirEnsureExists(sczTargetDirectory, NULL); - ExitOnFailure(hr, "Failed create bundle working folder."); - - hr = PathConcat(sczTargetDirectory, wzExecutableName, &sczTargetPath); - ExitOnFailure(hr, "Failed to combine working path with engine file name."); - - // Copy the engine without any attached containers to the working path. - hr = CopyEngineWithSignatureFixup(pSection->hEngineFile, wzSourcePath, sczTargetPath, pSection); - ExitOnFailure(hr, "Failed to copy engine: '%ls' to working path: %ls", wzSourcePath, sczTargetPath); - - if (psczEngineWorkingPath) - { - hr = StrAllocString(psczEngineWorkingPath, sczTargetPath, 0); - ExitOnFailure(hr, "Failed to copy target path for engine working path."); - } - -LExit: - ReleaseStr(sczPayloadTargetPath); - ReleaseStr(sczPayloadSourcePath); - ReleaseStr(sczSourceDirectory); - ReleaseStr(sczTargetPath); - ReleaseStr(sczTargetDirectory); - ReleaseStr(sczWorkingFolder); - - return hr; -} - - -static HRESULT CopyEngineWithSignatureFixup( - __in HANDLE hEngineFile, - __in_z LPCWSTR wzEnginePath, - __in_z LPCWSTR wzTargetPath, - __in BURN_SECTION* pSection - ) -{ - HRESULT hr = S_OK; - HANDLE hTarget = INVALID_HANDLE_VALUE; - LARGE_INTEGER li = { }; - DWORD dwZeroOriginals[3] = { }; - - hTarget = ::CreateFileW(wzTargetPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); - if (INVALID_HANDLE_VALUE == hTarget) - { - ExitWithLastError(hr, "Failed to create engine file at path: %ls", wzTargetPath); - } - - hr = FileSetPointer(hEngineFile, 0, NULL, FILE_BEGIN); - ExitOnFailure(hr, "Failed to seek to beginning of engine file: %ls", wzEnginePath); - - hr = FileCopyUsingHandles(hEngineFile, hTarget, pSection->cbEngineSize, NULL); - ExitOnFailure(hr, "Failed to copy engine from: %ls to: %ls", wzEnginePath, wzTargetPath); - - // If the original executable was signed, let's put back the checksum and signature. - if (pSection->dwOriginalSignatureOffset) - { - // Fix up the checksum. - li.QuadPart = pSection->dwChecksumOffset; - if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN)) - { - ExitWithLastError(hr, "Failed to seek to checksum in exe header."); - } - - hr = FileWriteHandle(hTarget, reinterpret_cast(&pSection->dwOriginalChecksum), sizeof(pSection->dwOriginalChecksum)); - ExitOnFailure(hr, "Failed to update signature offset."); - - // Fix up the signature information. - li.QuadPart = pSection->dwCertificateTableOffset; - if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN)) - { - ExitWithLastError(hr, "Failed to seek to signature table in exe header."); - } - - hr = FileWriteHandle(hTarget, reinterpret_cast(&pSection->dwOriginalSignatureOffset), sizeof(pSection->dwOriginalSignatureOffset)); - ExitOnFailure(hr, "Failed to update signature offset."); - - hr = FileWriteHandle(hTarget, reinterpret_cast(&pSection->dwOriginalSignatureSize), sizeof(pSection->dwOriginalSignatureSize)); - ExitOnFailure(hr, "Failed to update signature offset."); - - // Zero out the original information since that is how it was when the file was originally signed. - li.QuadPart = pSection->dwOriginalChecksumAndSignatureOffset; - if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN)) - { - ExitWithLastError(hr, "Failed to seek to original data in exe burn section header."); - } - - hr = FileWriteHandle(hTarget, reinterpret_cast(&dwZeroOriginals), sizeof(dwZeroOriginals)); - ExitOnFailure(hr, "Failed to zero out original data offset."); - } - -LExit: - ReleaseFileHandle(hTarget); - - return hr; -} - - -static HRESULT RemoveBundleOrPackage( - __in BOOL fBundle, - __in BOOL fPerMachine, - __in_z LPCWSTR wzBundleOrPackageId, - __in_z LPCWSTR wzCacheId - ) -{ - HRESULT hr = S_OK; - LPWSTR sczRootCacheDirectory = NULL; - LPWSTR sczDirectory = NULL; - - hr = CacheGetCompletedPath(fPerMachine, wzCacheId, &sczDirectory); - ExitOnFailure(hr, "Failed to calculate cache path."); - - LogId(REPORT_STANDARD, fBundle ? MSG_UNCACHE_BUNDLE : MSG_UNCACHE_PACKAGE, wzBundleOrPackageId, sczDirectory); - - // Try really hard to remove the cache directory. - hr = E_FAIL; - for (DWORD iRetry = 0; FAILED(hr) && iRetry < FILE_OPERATION_RETRY_COUNT; ++iRetry) - { - if (0 < iRetry) - { - ::Sleep(FILE_OPERATION_RETRY_WAIT); - } - - hr = DirEnsureDeleteEx(sczDirectory, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE); - if (E_PATHNOTFOUND == hr) - { - break; - } - } - - if (E_PATHNOTFOUND != hr && FAILED(hr)) - { - LogId(REPORT_STANDARD, fBundle ? MSG_UNABLE_UNCACHE_BUNDLE : MSG_UNABLE_UNCACHE_PACKAGE, wzBundleOrPackageId, sczDirectory, hr); - hr = S_OK; - } - else - { - // Try to remove root package cache in the off chance it is now empty. - hr = GetRootPath(fPerMachine, TRUE, &sczRootCacheDirectory); - ExitOnFailure(hr, "Failed to get %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user"); - DirEnsureDeleteEx(sczRootCacheDirectory, DIR_DELETE_SCHEDULE); - - // GetRootPath returns S_FALSE if the package cache is redirected elsewhere. - if (S_FALSE == hr) - { - hr = GetRootPath(fPerMachine, FALSE, &sczRootCacheDirectory); - ExitOnFailure(hr, "Failed to get old %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user"); - DirEnsureDeleteEx(sczRootCacheDirectory, DIR_DELETE_SCHEDULE); - } - } - -LExit: - ReleaseStr(sczDirectory); - ReleaseStr(sczRootCacheDirectory); - - return hr; -} - -static HRESULT VerifyHash( - __in BYTE* pbHash, - __in DWORD cbHash, - __in DWORD64 qwFileSize, - __in_z LPCWSTR wzUnverifiedPayloadPath, - __in HANDLE hFile, - __in BURN_CACHE_STEP cacheStep, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE /*pfnProgress*/, - __in LPVOID pContext - ) -{ - UNREFERENCED_PARAMETER(wzUnverifiedPayloadPath); - - HRESULT hr = S_OK; - BYTE rgbActualHash[SHA512_HASH_LEN] = { }; - DWORD64 qwHashedBytes = 0; - LONGLONG llSize = 0; - LPWSTR pszExpected = NULL; - LPWSTR pszActual = NULL; - - hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, cacheStep); - ExitOnFailure(hr, "Aborted cache verify hash begin."); - - hr = FileSizeByHandle(hFile, &llSize); - ExitOnFailure(hr, "Failed to get file size for path: %ls", wzUnverifiedPayloadPath); - - if (static_cast(llSize) != qwFileSize) - { - ExitOnFailure(hr = ERROR_FILE_CORRUPT, "File size mismatch for path: %ls, expected: %llu, actual: %lld", wzUnverifiedPayloadPath, qwFileSize, llSize); - } - - // TODO: create a cryp hash file that sends progress. - hr = CrypHashFileHandle(hFile, PROV_RSA_AES, CALG_SHA_512, rgbActualHash, sizeof(rgbActualHash), &qwHashedBytes); - ExitOnFailure(hr, "Failed to calculate hash for path: %ls", wzUnverifiedPayloadPath); - - // Compare hashes. - if (cbHash != sizeof(rgbActualHash) || 0 != memcmp(pbHash, rgbActualHash, sizeof(rgbActualHash))) - { - hr = CRYPT_E_HASH_VALUE; - - // Best effort to log the expected and actual hash value strings. - if (SUCCEEDED(StrAllocHexEncode(pbHash, cbHash, &pszExpected)) && - SUCCEEDED(StrAllocHexEncode(rgbActualHash, sizeof(rgbActualHash), &pszActual))) - { - ExitOnFailure(hr, "Hash mismatch for path: %ls, expected: %ls, actual: %ls", wzUnverifiedPayloadPath, pszExpected, pszActual); - } - else - { - ExitOnFailure(hr, "Hash mismatch for path: %ls", wzUnverifiedPayloadPath); - } - } - - hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, qwFileSize); - -LExit: - SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr); - - ReleaseStr(pszActual); - ReleaseStr(pszExpected); - - return hr; -} - -static HRESULT SendCacheBeginMessage( - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPVOID pContext, - __in BURN_CACHE_STEP cacheStep - ) -{ - HRESULT hr = S_OK; - BURN_CACHE_MESSAGE message = { }; - - message.type = BURN_CACHE_MESSAGE_BEGIN; - message.begin.cacheStep = cacheStep; - - hr = pfnCacheMessageHandler(&message, pContext); - - return hr; -} - -static HRESULT SendCacheSuccessMessage( - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPVOID pContext, - __in DWORD64 qwFileSize - ) -{ - HRESULT hr = S_OK; - BURN_CACHE_MESSAGE message = { }; - - message.type = BURN_CACHE_MESSAGE_SUCCESS; - message.success.qwFileSize = qwFileSize; - - hr = pfnCacheMessageHandler(&message, pContext); - - return hr; -} - -static HRESULT SendCacheCompleteMessage( - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPVOID pContext, - __in HRESULT hrStatus - ) -{ - HRESULT hr = S_OK; - BURN_CACHE_MESSAGE message = { }; - - message.type = BURN_CACHE_MESSAGE_COMPLETE; - message.complete.hrStatus = hrStatus; - - hr = pfnCacheMessageHandler(&message, pContext); - - return hr; -} diff --git a/src/engine/cache.h b/src/engine/cache.h deleted file mode 100644 index 0152d33b..00000000 --- a/src/engine/cache.h +++ /dev/null @@ -1,216 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#define BURN_CACHE_MAX_SEARCH_PATHS 7 - -#ifdef __cplusplus -extern "C" { -#endif - - -enum BURN_CACHE_MESSAGE_TYPE -{ - BURN_CACHE_MESSAGE_BEGIN, - BURN_CACHE_MESSAGE_SUCCESS, - BURN_CACHE_MESSAGE_COMPLETE, -}; - -enum BURN_CACHE_STEP -{ - BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE, - BURN_CACHE_STEP_HASH_TO_SKIP_VERIFY, - BURN_CACHE_STEP_STAGE, - BURN_CACHE_STEP_HASH, - BURN_CACHE_STEP_FINALIZE, -}; - -typedef struct _BURN_CACHE_MESSAGE -{ - BURN_CACHE_MESSAGE_TYPE type; - - union - { - struct - { - BURN_CACHE_STEP cacheStep; - } begin; - struct - { - DWORD64 qwFileSize; - } success; - struct - { - HRESULT hrStatus; - } complete; - }; -} BURN_CACHE_MESSAGE; - -typedef HRESULT(CALLBACK* PFN_BURNCACHEMESSAGEHANDLER)( - __in BURN_CACHE_MESSAGE* pMessage, - __in LPVOID pvContext - ); - -// functions - -HRESULT CacheInitialize( - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables, - __in_z_opt LPCWSTR wzSourceProcessPath - ); -HRESULT CacheEnsureWorkingFolder( - __in_z_opt LPCWSTR wzBundleId, - __deref_out_z_opt LPWSTR* psczWorkingFolder - ); -HRESULT CacheCalculateBundleWorkingPath( - __in_z LPCWSTR wzBundleId, - __in LPCWSTR wzExecutableName, - __deref_out_z LPWSTR* psczWorkingPath - ); -HRESULT CacheCalculateBundleLayoutWorkingPath( - __in_z LPCWSTR wzBundleId, - __deref_out_z LPWSTR* psczWorkingPath - ); -HRESULT CacheCalculatePayloadWorkingPath( - __in_z LPCWSTR wzBundleId, - __in BURN_PAYLOAD* pPayload, - __deref_out_z LPWSTR* psczWorkingPath - ); -HRESULT CacheCalculateContainerWorkingPath( - __in_z LPCWSTR wzBundleId, - __in BURN_CONTAINER* pContainer, - __deref_out_z LPWSTR* psczWorkingPath - ); -HRESULT CacheGetRootCompletedPath( - __in BOOL fPerMachine, - __in BOOL fForceInitialize, - __deref_out_z LPWSTR* psczRootCompletedPath - ); -HRESULT CacheGetCompletedPath( - __in BOOL fPerMachine, - __in_z LPCWSTR wzCacheId, - __deref_out_z LPWSTR* psczCompletedPath - ); -HRESULT CacheGetResumePath( - __in_z LPCWSTR wzPayloadWorkingPath, - __deref_out_z LPWSTR* psczResumePath - ); -HRESULT CacheGetLocalSourcePaths( - __in_z LPCWSTR wzRelativePath, - __in_z LPCWSTR wzSourcePath, - __in_z LPCWSTR wzDestinationPath, - __in_z_opt LPCWSTR wzLayoutDirectory, - __in BURN_VARIABLES* pVariables, - __inout LPWSTR** prgSearchPaths, - __out DWORD* pcSearchPaths, - __out DWORD* pdwLikelySearchPath, - __out DWORD* pdwDestinationSearchPath - ); -HRESULT CacheSetLastUsedSource( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzSourcePath, - __in_z LPCWSTR wzRelativePath - ); -HRESULT CacheSendProgressCallback( - __in DOWNLOAD_CACHE_CALLBACK* pCallback, - __in DWORD64 dw64Progress, - __in DWORD64 dw64Total, - __in HANDLE hDestinationFile - ); -void CacheSendErrorCallback( - __in DOWNLOAD_CACHE_CALLBACK* pCallback, - __in HRESULT hrError, - __in_z_opt LPCWSTR wzError, - __out_opt BOOL* pfRetry - ); -BOOL CacheBundleRunningFromCache(); -HRESULT CacheBundleToCleanRoom( - __in BURN_SECTION* pSection, - __deref_out_z_opt LPWSTR* psczCleanRoomBundlePath - ); -HRESULT CacheBundleToWorkingDirectory( - __in_z LPCWSTR wzBundleId, - __in_z LPCWSTR wzExecutableName, - __in BURN_SECTION* pSection, - __deref_out_z_opt LPWSTR* psczEngineWorkingPath - ); -HRESULT CacheLayoutBundle( - __in_z LPCWSTR wzExecutableName, - __in_z LPCWSTR wzLayoutDirectory, - __in_z LPCWSTR wzSourceBundlePath, - __in DWORD64 qwBundleSize, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ); -HRESULT CacheCompleteBundle( - __in BOOL fPerMachine, - __in_z LPCWSTR wzExecutableName, - __in_z LPCWSTR wzBundleId, - __in_z LPCWSTR wzSourceBundlePath -#ifdef DEBUG - , __in_z LPCWSTR wzExecutablePath -#endif - ); -HRESULT CacheLayoutContainer( - __in BURN_CONTAINER* pContainer, - __in_z_opt LPCWSTR wzLayoutDirectory, - __in_z LPCWSTR wzUnverifiedContainerPath, - __in BOOL fMove, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ); -HRESULT CacheLayoutPayload( - __in BURN_PAYLOAD* pPayload, - __in_z_opt LPCWSTR wzLayoutDirectory, - __in_z LPCWSTR wzUnverifiedPayloadPath, - __in BOOL fMove, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ); -HRESULT CacheCompletePayload( - __in BOOL fPerMachine, - __in BURN_PAYLOAD* pPayload, - __in_z LPCWSTR wzCacheId, - __in_z LPCWSTR wzUnverifiedPayloadPath, - __in BOOL fMove, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ); -HRESULT CacheVerifyContainer( - __in BURN_CONTAINER* pContainer, - __in_z LPCWSTR wzCachedDirectory, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ); -HRESULT CacheVerifyPayload( - __in BURN_PAYLOAD* pPayload, - __in_z LPCWSTR wzCachedDirectory, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ); -HRESULT CacheRemoveWorkingFolder( - __in_z_opt LPCWSTR wzBundleId - ); -HRESULT CacheRemoveBundle( - __in BOOL fPerMachine, - __in_z LPCWSTR wzPackageId - ); -HRESULT CacheRemovePackage( - __in BOOL fPerMachine, - __in_z LPCWSTR wzPackageId, - __in_z LPCWSTR wzCacheId - ); -void CacheCleanup( - __in BOOL fPerMachine, - __in_z LPCWSTR wzBundleId - ); -void CacheUninitialize(); - -#ifdef __cplusplus -} -#endif diff --git a/src/engine/condition.cpp b/src/engine/condition.cpp deleted file mode 100644 index b7cd7413..00000000 --- a/src/engine/condition.cpp +++ /dev/null @@ -1,1057 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// -// parse rules -// -// value variable | literal | integer | version -// comparison-operator < | > | <= | >= | = | <> | >< | << | >> -// term value | value comparison-operator value | ( expression ) -// boolean-factor term | NOT term -// boolean-term boolean-factor | boolean-factor AND boolean-term -// expression boolean-term | boolean-term OR expression -// - - -// constants - -#define COMPARISON 0x00010000 -#define INSENSITIVE 0x00020000 - -enum BURN_SYMBOL_TYPE -{ - // terminals - BURN_SYMBOL_TYPE_NONE = 0, - BURN_SYMBOL_TYPE_END = 1, - BURN_SYMBOL_TYPE_OR = 2, // OR - BURN_SYMBOL_TYPE_AND = 3, // AND - BURN_SYMBOL_TYPE_NOT = 4, // NOT - BURN_SYMBOL_TYPE_LT = 5 | COMPARISON, // < - BURN_SYMBOL_TYPE_GT = 6 | COMPARISON, // > - BURN_SYMBOL_TYPE_LE = 7 | COMPARISON, // <= - BURN_SYMBOL_TYPE_GE = 8 | COMPARISON, // >= - BURN_SYMBOL_TYPE_EQ = 9 | COMPARISON, // = - BURN_SYMBOL_TYPE_NE = 10 | COMPARISON, // <> - BURN_SYMBOL_TYPE_BAND = 11 | COMPARISON, // >< - BURN_SYMBOL_TYPE_HIEQ = 12 | COMPARISON, // << - BURN_SYMBOL_TYPE_LOEQ = 13 | COMPARISON, // >> - BURN_SYMBOL_TYPE_LT_I = 5 | COMPARISON | INSENSITIVE, // ~< - BURN_SYMBOL_TYPE_GT_I = 6 | COMPARISON | INSENSITIVE, // ~> - BURN_SYMBOL_TYPE_LE_I = 7 | COMPARISON | INSENSITIVE, // ~<= - BURN_SYMBOL_TYPE_GE_I = 8 | COMPARISON | INSENSITIVE, // ~>= - BURN_SYMBOL_TYPE_EQ_I = 9 | COMPARISON | INSENSITIVE, // ~= - BURN_SYMBOL_TYPE_NE_I = 10 | COMPARISON | INSENSITIVE, // ~<> - BURN_SYMBOL_TYPE_BAND_I = 11 | COMPARISON | INSENSITIVE, // ~>< - BURN_SYMBOL_TYPE_HIEQ_I = 12 | COMPARISON | INSENSITIVE, // ~<< - BURN_SYMBOL_TYPE_LOEQ_I = 13 | COMPARISON | INSENSITIVE, // ~>> - BURN_SYMBOL_TYPE_LPAREN = 14, // ( - BURN_SYMBOL_TYPE_RPAREN = 15, // ) - BURN_SYMBOL_TYPE_NUMBER = 16, - BURN_SYMBOL_TYPE_IDENTIFIER = 17, - BURN_SYMBOL_TYPE_LITERAL = 18, - BURN_SYMBOL_TYPE_VERSION = 19, -}; - - -// structs - -struct BURN_SYMBOL -{ - BURN_SYMBOL_TYPE Type; - DWORD iPosition; - BURN_VARIANT Value; -}; - -struct BURN_CONDITION_PARSE_CONTEXT -{ - BURN_VARIABLES* pVariables; - LPCWSTR wzCondition; - LPCWSTR wzRead; - BURN_SYMBOL NextSymbol; - BOOL fError; -}; - -struct BURN_CONDITION_OPERAND -{ - BOOL fHidden; - BURN_VARIANT Value; -}; - - -// internal function declarations - -static HRESULT ParseExpression( - __in BURN_CONDITION_PARSE_CONTEXT* pContext, - __out BOOL* pf - ); -static HRESULT ParseBooleanTerm( - __in BURN_CONDITION_PARSE_CONTEXT* pContext, - __out BOOL* pf - ); -static HRESULT ParseBooleanFactor( - __in BURN_CONDITION_PARSE_CONTEXT* pContext, - __out BOOL* pf - ); -static HRESULT ParseTerm( - __in BURN_CONDITION_PARSE_CONTEXT* pContext, - __out BOOL* pf - ); -static HRESULT ParseOperand( - __in BURN_CONDITION_PARSE_CONTEXT* pContext, - __out BURN_CONDITION_OPERAND* pOperand - ); -static HRESULT Expect( - __in BURN_CONDITION_PARSE_CONTEXT* pContext, - __in BURN_SYMBOL_TYPE symbolType - ); -static HRESULT NextSymbol( - __in BURN_CONDITION_PARSE_CONTEXT* pContext - ); -static HRESULT CompareOperands( - __in BURN_SYMBOL_TYPE comparison, - __in BURN_CONDITION_OPERAND* pLeftOperand, - __in BURN_CONDITION_OPERAND* pRightOperand, - __out BOOL* pfResult - ); -static HRESULT CompareStringValues( - __in BURN_SYMBOL_TYPE comparison, - __in_z LPCWSTR wzLeftOperand, - __in_z LPCWSTR wzRightOperand, - __out BOOL* pfResult - ); -static HRESULT CompareIntegerValues( - __in BURN_SYMBOL_TYPE comparison, - __in LONGLONG llLeftOperand, - __in LONGLONG llRightOperand, - __out BOOL* pfResult - ); -static HRESULT CompareVersionValues( - __in BURN_SYMBOL_TYPE comparison, - __in VERUTIL_VERSION* pLeftOperand, - __in VERUTIL_VERSION* pRightOperand, - __out BOOL* pfResult - ); - - -// function definitions - -extern "C" HRESULT ConditionEvaluate( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzCondition, - __out BOOL* pf - ) -{ - HRESULT hr = S_OK; - BURN_CONDITION_PARSE_CONTEXT context = { }; - BOOL f = FALSE; - - context.pVariables = pVariables; - context.wzCondition = wzCondition; - context.wzRead = wzCondition; - - hr = NextSymbol(&context); - ExitOnFailure(hr, "Failed to read next symbol."); - - hr = ParseExpression(&context, &f); - ExitOnFailure(hr, "Failed to parse expression."); - - hr = Expect(&context, BURN_SYMBOL_TYPE_END); - ExitOnFailure(hr, "Failed to expect end symbol."); - - LogId(REPORT_VERBOSE, MSG_CONDITION_RESULT, wzCondition, LoggingTrueFalseToString(f)); - - *pf = f; - hr = S_OK; - -LExit: - if (context.fError) - { - Assert(FAILED(hr)); - LogErrorId(hr, MSG_FAILED_PARSE_CONDITION, wzCondition, NULL, NULL); - } - - return hr; -} - -extern "C" HRESULT ConditionGlobalCheck( - __in BURN_VARIABLES* pVariables, - __in BURN_CONDITION* pCondition, - __in BOOTSTRAPPER_DISPLAY display, - __in_z LPCWSTR wzBundleName, - __out DWORD *pdwExitCode, - __out BOOL *pfContinueExecution - ) -{ - HRESULT hr = S_OK; - BOOL fSuccess = TRUE; - HRESULT hrError = HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION); - - // Only run on Windows Vista SP2 or newer, or Windows Server 2008 SP2 or newer. - if (!::IsWindowsVistaSP2OrGreater()) - { - fSuccess = FALSE; - } - else - { - if (NULL != pCondition->sczConditionString) - { - hr = ConditionEvaluate(pVariables, pCondition->sczConditionString, &fSuccess); - ExitOnFailure(hr, "Failed to evaluate condition: %ls", pCondition->sczConditionString); - } - } - - if (!fSuccess) - { - // Display the error messagebox, as long as we're in an appropriate display mode - hr = SplashScreenDisplayError(display, wzBundleName, hrError); - ExitOnFailure(hr, "Failed to display error dialog"); - - *pdwExitCode = static_cast(hrError); - *pfContinueExecution = FALSE; - } - -LExit: - return hr; -} - -HRESULT ConditionGlobalParseFromXml( - __in BURN_CONDITION* pCondition, - __in IXMLDOMNode* pixnBundle - ) -{ - HRESULT hr = S_OK; - IXMLDOMNode* pixnNode = NULL; - BSTR bstrExpression = NULL; - - // select variable nodes - hr = XmlSelectSingleNode(pixnBundle, L"Condition", &pixnNode); - if (S_FALSE == hr) - { - ExitFunction1(hr = S_OK); - } - ExitOnFailure(hr, "Failed to select condition node."); - - // @Condition - hr = XmlGetText(pixnNode, &bstrExpression); - ExitOnFailure(hr, "Failed to get Condition inner text."); - - hr = StrAllocString(&pCondition->sczConditionString, bstrExpression, 0); - ExitOnFailure(hr, "Failed to copy condition string from BSTR"); - -LExit: - ReleaseBSTR(bstrExpression); - ReleaseObject(pixnNode); - - return hr; -} - - -// internal function definitions - -static HRESULT ParseExpression( - __in BURN_CONDITION_PARSE_CONTEXT* pContext, - __out BOOL* pf - ) -{ - HRESULT hr = S_OK; - BOOL fFirst = FALSE; - BOOL fSecond = FALSE; - - hr = ParseBooleanTerm(pContext, &fFirst); - ExitOnFailure(hr, "Failed to parse boolean-term."); - - if (BURN_SYMBOL_TYPE_OR == pContext->NextSymbol.Type) - { - hr = NextSymbol(pContext); - ExitOnFailure(hr, "Failed to read next symbol."); - - hr = ParseExpression(pContext, &fSecond); - ExitOnFailure(hr, "Failed to parse expression."); - - *pf = fFirst || fSecond; - } - else - { - *pf = fFirst; - } - -LExit: - return hr; -} - -static HRESULT ParseBooleanTerm( - __in BURN_CONDITION_PARSE_CONTEXT* pContext, - __out BOOL* pf - ) -{ - HRESULT hr = S_OK; - BOOL fFirst = FALSE; - BOOL fSecond = FALSE; - - hr = ParseBooleanFactor(pContext, &fFirst); - ExitOnFailure(hr, "Failed to parse boolean-factor."); - - if (BURN_SYMBOL_TYPE_AND == pContext->NextSymbol.Type) - { - hr = NextSymbol(pContext); - ExitOnFailure(hr, "Failed to read next symbol."); - - hr = ParseBooleanTerm(pContext, &fSecond); - ExitOnFailure(hr, "Failed to parse boolean-term."); - - *pf = fFirst && fSecond; - } - else - { - *pf = fFirst; - } - -LExit: - return hr; -} - -static HRESULT ParseBooleanFactor( - __in BURN_CONDITION_PARSE_CONTEXT* pContext, - __out BOOL* pf - ) -{ - HRESULT hr = S_OK; - BOOL fNot = FALSE; - BOOL f = FALSE; - - if (BURN_SYMBOL_TYPE_NOT == pContext->NextSymbol.Type) - { - hr = NextSymbol(pContext); - ExitOnFailure(hr, "Failed to read next symbol."); - - fNot = TRUE; - } - - hr = ParseTerm(pContext, &f); - ExitOnFailure(hr, "Failed to parse term."); - - *pf = fNot ? !f : f; - -LExit: - return hr; -} - -static HRESULT ParseTerm( - __in BURN_CONDITION_PARSE_CONTEXT* pContext, - __out BOOL* pf - ) -{ - HRESULT hr = S_OK; - BURN_CONDITION_OPERAND firstOperand = { }; - BURN_CONDITION_OPERAND secondOperand = { }; - - if (BURN_SYMBOL_TYPE_LPAREN == pContext->NextSymbol.Type) - { - hr = NextSymbol(pContext); - ExitOnFailure(hr, "Failed to read next symbol."); - - hr = ParseExpression(pContext, pf); - ExitOnFailure(hr, "Failed to parse expression."); - - hr = Expect(pContext, BURN_SYMBOL_TYPE_RPAREN); - ExitOnFailure(hr, "Failed to expect right parenthesis."); - - ExitFunction1(hr = S_OK); - } - - hr = ParseOperand(pContext, &firstOperand); - ExitOnFailure(hr, "Failed to parse operand."); - - if (COMPARISON & pContext->NextSymbol.Type) - { - BURN_SYMBOL_TYPE comparison = pContext->NextSymbol.Type; - - hr = NextSymbol(pContext); - ExitOnFailure(hr, "Failed to read next symbol."); - - hr = ParseOperand(pContext, &secondOperand); - ExitOnFailure(hr, "Failed to parse operand."); - - hr = CompareOperands(comparison, &firstOperand, &secondOperand, pf); - ExitOnFailure(hr, "Failed to compare operands."); - } - else - { - LONGLONG llValue = 0; - LPWSTR sczValue = NULL; - VERUTIL_VERSION* pVersion = NULL; - switch (firstOperand.Value.Type) - { - case BURN_VARIANT_TYPE_NONE: - *pf = FALSE; - break; - case BURN_VARIANT_TYPE_STRING: - hr = BVariantGetString(&firstOperand.Value, &sczValue); - if (SUCCEEDED(hr)) - { - *pf = sczValue && *sczValue; - } - StrSecureZeroFreeString(sczValue); - break; - case BURN_VARIANT_TYPE_NUMERIC: - hr = BVariantGetNumeric(&firstOperand.Value, &llValue); - if (SUCCEEDED(hr)) - { - *pf = 0 != llValue; - } - SecureZeroMemory(&llValue, sizeof(llValue)); - break; - case BURN_VARIANT_TYPE_VERSION: - hr = BVariantGetVersionHidden(&firstOperand.Value, firstOperand.fHidden, &pVersion); - if (SUCCEEDED(hr)) - { - *pf = 0 != *pVersion->sczVersion; - } - ReleaseVerutilVersion(pVersion); - break; - default: - ExitFunction1(hr = E_UNEXPECTED); - } - } - -LExit: - BVariantUninitialize(&firstOperand.Value); - BVariantUninitialize(&secondOperand.Value); - return hr; -} - -static HRESULT ParseOperand( - __in BURN_CONDITION_PARSE_CONTEXT* pContext, - __out BURN_CONDITION_OPERAND* pOperand - ) -{ - HRESULT hr = S_OK; - LPWSTR sczFormatted = NULL; - - switch (pContext->NextSymbol.Type) - { - case BURN_SYMBOL_TYPE_IDENTIFIER: - Assert(BURN_VARIANT_TYPE_STRING == pContext->NextSymbol.Value.Type); - - // find variable - hr = VariableGetVariant(pContext->pVariables, pContext->NextSymbol.Value.sczValue, &pOperand->Value); - if (E_NOTFOUND != hr) - { - ExitOnRootFailure(hr, "Failed to find variable."); - - hr = VariableIsHidden(pContext->pVariables, pContext->NextSymbol.Value.sczValue, &pOperand->fHidden); - ExitOnRootFailure(hr, "Failed to get if variable is hidden."); - } - - if (BURN_VARIANT_TYPE_FORMATTED == pOperand->Value.Type) - { - hr = VariableGetFormatted(pContext->pVariables, pContext->NextSymbol.Value.sczValue, &sczFormatted, &pOperand->fHidden); - ExitOnRootFailure(hr, "Failed to format variable '%ls' for condition '%ls'", pContext->NextSymbol.Value.sczValue, pContext->wzCondition); - - hr = BVariantSetString(&pOperand->Value, sczFormatted, 0, FALSE); - ExitOnRootFailure(hr, "Failed to store formatted value for variable '%ls' for condition '%ls'", pContext->NextSymbol.Value.sczValue, pContext->wzCondition); - } - break; - - case BURN_SYMBOL_TYPE_NUMBER: __fallthrough; - case BURN_SYMBOL_TYPE_LITERAL: __fallthrough; - case BURN_SYMBOL_TYPE_VERSION: - pOperand->fHidden = FALSE; - // steal value of symbol - memcpy_s(&pOperand->Value, sizeof(BURN_VARIANT), &pContext->NextSymbol.Value, sizeof(BURN_VARIANT)); - memset(&pContext->NextSymbol.Value, 0, sizeof(BURN_VARIANT)); - break; - - default: - pContext->fError = TRUE; - hr = E_INVALIDDATA; - ExitOnRootFailure(hr, "Failed to parse condition '%ls' at position: %u", pContext->wzCondition, pContext->NextSymbol.iPosition); - } - - // get next symbol - hr = NextSymbol(pContext); - ExitOnFailure(hr, "Failed to read next symbol."); - -LExit: - StrSecureZeroFreeString(sczFormatted); - - return hr; -} - -// -// Expect - expects a symbol. -// -static HRESULT Expect( - __in BURN_CONDITION_PARSE_CONTEXT* pContext, - __in BURN_SYMBOL_TYPE symbolType - ) -{ - HRESULT hr = S_OK; - - if (pContext->NextSymbol.Type != symbolType) - { - pContext->fError = TRUE; - hr = E_INVALIDDATA; - ExitOnRootFailure(hr, "Failed to parse condition '%ls' at position: %u", pContext->wzCondition, pContext->NextSymbol.iPosition); - } - - hr = NextSymbol(pContext); - ExitOnFailure(hr, "Failed to read next symbol."); - -LExit: - return hr; -} - -// -// NextSymbol - finds the next symbol in an expression string. -// -static HRESULT NextSymbol( - __in BURN_CONDITION_PARSE_CONTEXT* pContext - ) -{ - HRESULT hr = S_OK; - WORD charType = 0; - ptrdiff_t cchPosition = 0; - DWORD iPosition = 0; - DWORD n = 0; - - // free existing symbol - BVariantUninitialize(&pContext->NextSymbol.Value); - memset(&pContext->NextSymbol, 0, sizeof(BURN_SYMBOL)); - - // skip past blanks - while (L'\0' != pContext->wzRead[0]) - { - ::GetStringTypeW(CT_CTYPE1, pContext->wzRead, 1, &charType); - if (0 == (C1_BLANK & charType)) - { - break; // no blank, done - } - ++pContext->wzRead; - } - - cchPosition = pContext->wzRead - pContext->wzCondition; - if (DWORD_MAX < cchPosition || 0 > cchPosition) - { - ExitOnFailure(hr = E_INVALIDARG, "Symbol was too long: %ls", pContext->wzCondition); - } - iPosition = (DWORD)cchPosition; - - // read depending on first character type - switch (pContext->wzRead[0]) - { - case L'\0': - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_END; - break; - case L'~': - switch (pContext->wzRead[1]) - { - case L'=': - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_EQ_I; - n = 2; - break; - case L'>': - switch (pContext->wzRead[2]) - { - case '=': - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GE_I; - n = 3; - break; - case L'>': - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LOEQ_I; - n = 3; - break; - case L'<': - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_BAND_I; - n = 3; - break; - default: - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GT_I; - n = 2; - } - break; - case L'<': - switch (pContext->wzRead[2]) - { - case '=': - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LE_I; - n = 3; - break; - case L'<': - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_HIEQ_I; - n = 3; - break; - case '>': - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NE_I; - n = 3; - break; - default: - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LT_I; - n = 2; - } - break; - default: - // error - pContext->fError = TRUE; - hr = E_INVALIDDATA; - ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unexpected '~' operator at position %d.", pContext->wzCondition, iPosition); - } - break; - case L'>': - switch (pContext->wzRead[1]) - { - case L'=': - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GE; - n = 2; - break; - case L'>': - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LOEQ; - n = 2; - break; - case L'<': - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_BAND; - n = 2; - break; - default: - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GT; - n = 1; - } - break; - case L'<': - switch (pContext->wzRead[1]) - { - case L'=': - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LE; - n = 2; - break; - case L'<': - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_HIEQ; - n = 2; - break; - case L'>': - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NE; - n = 2; - break; - default: - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LT; - n = 1; - } - break; - case L'=': - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_EQ; - n = 1; - break; - case L'(': - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LPAREN; - n = 1; - break; - case L')': - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_RPAREN; - n = 1; - break; - case L'"': // literal - do - { - ++n; - if (L'\0' == pContext->wzRead[n]) - { - // error - pContext->fError = TRUE; - hr = E_INVALIDDATA; - ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unterminated literal at position %d.", pContext->wzCondition, iPosition); - } - } while (L'"' != pContext->wzRead[n]); - ++n; // terminating '"' - - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LITERAL; - hr = BVariantSetString(&pContext->NextSymbol.Value, &pContext->wzRead[1], n - 2, FALSE); - ExitOnFailure(hr, "Failed to set symbol value."); - break; - default: - if (C1_DIGIT & charType || L'-' == pContext->wzRead[0]) - { - do - { - ++n; - ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType); - if (C1_ALPHA & charType || L'_' == pContext->wzRead[n]) - { - // error, identifier cannot start with a digit - pContext->fError = TRUE; - hr = E_INVALIDDATA; - ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Identifier cannot start at a digit, at position %d.", pContext->wzCondition, iPosition); - } - } while (C1_DIGIT & charType); - - // number - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NUMBER; - - LONGLONG ll = 0; - hr = StrStringToInt64(pContext->wzRead, n, &ll); - if (FAILED(hr)) - { - pContext->fError = TRUE; - hr = E_INVALIDDATA; - ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Constant too big, at position %d.", pContext->wzCondition, iPosition); - } - - hr = BVariantSetNumeric(&pContext->NextSymbol.Value, ll); - ExitOnFailure(hr, "Failed to set symbol value."); - } - else if (C1_ALPHA & charType || L'_' == pContext->wzRead[0]) - { - ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[1], 1, &charType); - if (L'v' == pContext->wzRead[0] && C1_DIGIT & charType) - { - // version - do - { - ++n; - } while (pContext->wzRead[n] >= L'0' && pContext->wzRead[n] <= L'9' || - pContext->wzRead[n] >= L'A' && pContext->wzRead[n] <= L'Z' || - pContext->wzRead[n] >= L'a' && pContext->wzRead[n] <= L'z' || - pContext->wzRead[n] == L'_' || - pContext->wzRead[n] == L'+' || - pContext->wzRead[n] == L'-' || - pContext->wzRead[n] == L'.'); - - hr = VerParseVersion(&pContext->wzRead[1], n - 1, FALSE, &pContext->NextSymbol.Value.pValue); - if (FAILED(hr)) - { - pContext->fError = TRUE; - hr = E_INVALIDDATA; - ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Invalid version format, at position %d.", pContext->wzCondition, iPosition); - } - else if (pContext->NextSymbol.Value.pValue->fInvalid) - { - LogId(REPORT_WARNING, MSG_CONDITION_INVALID_VERSION, pContext->wzCondition, pContext->NextSymbol.Value.pValue->sczVersion); - } - - pContext->NextSymbol.Value.Type = BURN_VARIANT_TYPE_VERSION; - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_VERSION; - } - else - { - do - { - ++n; - ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType); - } while (C1_ALPHA & charType || C1_DIGIT & charType || L'_' == pContext->wzRead[n]); - - if (2 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 2, L"OR", 2)) - { - // OR - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_OR; - } - else if (3 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 3, L"AND", 3)) - { - // AND - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_AND; - } - else if (3 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 3, L"NOT", 3)) - { - // NOT - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NOT; - } - else - { - // identifier - pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_IDENTIFIER; - hr = BVariantSetString(&pContext->NextSymbol.Value, pContext->wzRead, n, FALSE); - ExitOnFailure(hr, "Failed to set symbol value."); - } - } - } - else - { - // error, unexpected character - pContext->fError = TRUE; - hr = E_INVALIDDATA; - ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unexpected character at position %d.", pContext->wzCondition, iPosition); - } - } - pContext->NextSymbol.iPosition = iPosition; - pContext->wzRead += n; - -LExit: - return hr; -} - -// -// CompareOperands - compares two variant values using a given comparison. -// -static HRESULT CompareOperands( - __in BURN_SYMBOL_TYPE comparison, - __in BURN_CONDITION_OPERAND* pLeftOperand, - __in BURN_CONDITION_OPERAND* pRightOperand, - __out BOOL* pfResult - ) -{ - HRESULT hr = S_OK; - LONGLONG llLeft = 0; - VERUTIL_VERSION* pVersionLeft = 0; - LPWSTR sczLeft = NULL; - LONGLONG llRight = 0; - VERUTIL_VERSION* pVersionRight = 0; - LPWSTR sczRight = NULL; - BURN_VARIANT* pLeftValue = &pLeftOperand->Value; - BURN_VARIANT* pRightValue = &pRightOperand->Value; - - // get values to compare based on type - if (BURN_VARIANT_TYPE_STRING == pLeftValue->Type && BURN_VARIANT_TYPE_STRING == pRightValue->Type) - { - hr = BVariantGetString(pLeftValue, &sczLeft); - ExitOnFailure(hr, "Failed to get the left string"); - hr = BVariantGetString(pRightValue, &sczRight); - ExitOnFailure(hr, "Failed to get the right string"); - hr = CompareStringValues(comparison, sczLeft, sczRight, pfResult); - } - else if (BURN_VARIANT_TYPE_NUMERIC == pLeftValue->Type && BURN_VARIANT_TYPE_NUMERIC == pRightValue->Type) - { - hr = BVariantGetNumeric(pLeftValue, &llLeft); - ExitOnFailure(hr, "Failed to get the left numeric"); - hr = BVariantGetNumeric(pRightValue, &llRight); - ExitOnFailure(hr, "Failed to get the right numeric"); - hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult); - } - else if (BURN_VARIANT_TYPE_VERSION == pLeftValue->Type && BURN_VARIANT_TYPE_VERSION == pRightValue->Type) - { - hr = BVariantGetVersionHidden(pLeftValue, pLeftOperand->fHidden, &pVersionLeft); - ExitOnFailure(hr, "Failed to get the left version"); - hr = BVariantGetVersionHidden(pRightValue, pRightOperand->fHidden, &pVersionRight); - ExitOnFailure(hr, "Failed to get the right version"); - hr = CompareVersionValues(comparison, pVersionLeft, pVersionRight, pfResult); - } - else if (BURN_VARIANT_TYPE_VERSION == pLeftValue->Type && BURN_VARIANT_TYPE_STRING == pRightValue->Type) - { - hr = BVariantGetVersionHidden(pLeftValue, pLeftOperand->fHidden, &pVersionLeft); - ExitOnFailure(hr, "Failed to get the left version"); - hr = BVariantGetVersionHidden(pRightValue, pRightOperand->fHidden, &pVersionRight); - if (FAILED(hr)) - { - if (DISP_E_TYPEMISMATCH != hr) - { - ExitOnFailure(hr, "Failed to get the right version"); - } - *pfResult = (BURN_SYMBOL_TYPE_NE == comparison); - hr = S_OK; - } - else - { - hr = CompareVersionValues(comparison, pVersionLeft, pVersionRight, pfResult); - } - } - else if (BURN_VARIANT_TYPE_STRING == pLeftValue->Type && BURN_VARIANT_TYPE_VERSION == pRightValue->Type) - { - hr = BVariantGetVersionHidden(pRightValue, pRightOperand->fHidden, &pVersionRight); - ExitOnFailure(hr, "Failed to get the right version"); - hr = BVariantGetVersionHidden(pLeftValue, pLeftOperand->fHidden, &pVersionLeft); - if (FAILED(hr)) - { - if (DISP_E_TYPEMISMATCH != hr) - { - ExitOnFailure(hr, "Failed to get the left version"); - } - *pfResult = (BURN_SYMBOL_TYPE_NE == comparison); - hr = S_OK; - } - else - { - hr = CompareVersionValues(comparison, pVersionLeft, pVersionRight, pfResult); - } - } - else if (BURN_VARIANT_TYPE_NUMERIC == pLeftValue->Type && BURN_VARIANT_TYPE_STRING == pRightValue->Type) - { - hr = BVariantGetNumeric(pLeftValue, &llLeft); - ExitOnFailure(hr, "Failed to get the left numeric"); - hr = BVariantGetNumeric(pRightValue, &llRight); - if (FAILED(hr)) - { - if (DISP_E_TYPEMISMATCH != hr) - { - ExitOnFailure(hr, "Failed to get the right numeric"); - } - *pfResult = (BURN_SYMBOL_TYPE_NE == comparison); - hr = S_OK; - } - else - { - hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult); - } - } - else if (BURN_VARIANT_TYPE_STRING == pLeftValue->Type && BURN_VARIANT_TYPE_NUMERIC == pRightValue->Type) - { - hr = BVariantGetNumeric(pRightValue, &llRight); - ExitOnFailure(hr, "Failed to get the right numeric"); - hr = BVariantGetNumeric(pLeftValue, &llLeft); - if (FAILED(hr)) - { - if (DISP_E_TYPEMISMATCH != hr) - { - ExitOnFailure(hr, "Failed to get the left numeric"); - } - *pfResult = (BURN_SYMBOL_TYPE_NE == comparison); - hr = S_OK; - } - else - { - hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult); - } - } - else - { - // not a combination that can be compared - *pfResult = (BURN_SYMBOL_TYPE_NE == comparison || BURN_SYMBOL_TYPE_NE_I == comparison); - } - -LExit: - ReleaseVerutilVersion(pVersionLeft); - SecureZeroMemory(&llLeft, sizeof(LONGLONG)); - StrSecureZeroFreeString(sczLeft); - ReleaseVerutilVersion(pVersionRight); - SecureZeroMemory(&llRight, sizeof(LONGLONG)); - StrSecureZeroFreeString(sczRight); - - return hr; -} - -// -// CompareStringValues - compares two string values using a given comparison. -// -static HRESULT CompareStringValues( - __in BURN_SYMBOL_TYPE comparison, - __in_z LPCWSTR wzLeftOperand, - __in_z LPCWSTR wzRightOperand, - __out BOOL* pfResult - ) -{ - HRESULT hr = S_OK; - DWORD dwCompareString = (comparison & INSENSITIVE) ? NORM_IGNORECASE : 0; - size_t cchLeftSize = 0; - size_t cchRightSize = 0; - int cchLeft = 0; - int cchRight = 0; - - hr = ::StringCchLengthW(wzLeftOperand, STRSAFE_MAX_CCH, &cchLeftSize); - ExitOnRootFailure(hr, "Failed to get length of left string: %ls", wzLeftOperand); - - hr = ::StringCchLengthW(wzRightOperand, STRSAFE_MAX_CCH, &cchRightSize); - ExitOnRootFailure(hr, "Failed to get length of right string: %ls", wzRightOperand); - - cchLeft = static_cast(cchLeftSize); - cchRight = static_cast(cchRightSize); - - switch (comparison) - { - case BURN_SYMBOL_TYPE_LT: - case BURN_SYMBOL_TYPE_GT: - case BURN_SYMBOL_TYPE_LE: - case BURN_SYMBOL_TYPE_GE: - case BURN_SYMBOL_TYPE_EQ: - case BURN_SYMBOL_TYPE_NE: - case BURN_SYMBOL_TYPE_LT_I: - case BURN_SYMBOL_TYPE_GT_I: - case BURN_SYMBOL_TYPE_LE_I: - case BURN_SYMBOL_TYPE_GE_I: - case BURN_SYMBOL_TYPE_EQ_I: - case BURN_SYMBOL_TYPE_NE_I: - { - int i = ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand, cchLeft, wzRightOperand, cchRight); - hr = CompareIntegerValues(comparison, i, CSTR_EQUAL, pfResult); - } - break; - case BURN_SYMBOL_TYPE_BAND: - case BURN_SYMBOL_TYPE_BAND_I: - // test if left string contains right string - for (int i = 0; (i + cchRight) <= cchLeft; ++i) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand + i, cchRight, wzRightOperand, cchRight)) - { - *pfResult = TRUE; - ExitFunction(); - } - } - *pfResult = FALSE; - break; - case BURN_SYMBOL_TYPE_HIEQ: - case BURN_SYMBOL_TYPE_HIEQ_I: - // test if left string starts with right string - *pfResult = cchLeft >= cchRight && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand, cchRight, wzRightOperand, cchRight); - break; - case BURN_SYMBOL_TYPE_LOEQ: - case BURN_SYMBOL_TYPE_LOEQ_I: - // test if left string ends with right string - *pfResult = cchLeft >= cchRight && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand + (cchLeft - cchRight), cchRight, wzRightOperand, cchRight); - break; - default: - ExitFunction1(hr = E_INVALIDARG); - } - -LExit: - return hr; -} - -// -// CompareIntegerValues - compares two integer values using a given comparison. -// -static HRESULT CompareIntegerValues( - __in BURN_SYMBOL_TYPE comparison, - __in LONGLONG llLeftOperand, - __in LONGLONG llRightOperand, - __out BOOL* pfResult - ) -{ - HRESULT hr = S_OK; - - switch (comparison) - { - case BURN_SYMBOL_TYPE_LT: case BURN_SYMBOL_TYPE_LT_I: *pfResult = llLeftOperand < llRightOperand; break; - case BURN_SYMBOL_TYPE_GT: case BURN_SYMBOL_TYPE_GT_I: *pfResult = llLeftOperand > llRightOperand; break; - case BURN_SYMBOL_TYPE_LE: case BURN_SYMBOL_TYPE_LE_I: *pfResult = llLeftOperand <= llRightOperand; break; - case BURN_SYMBOL_TYPE_GE: case BURN_SYMBOL_TYPE_GE_I: *pfResult = llLeftOperand >= llRightOperand; break; - case BURN_SYMBOL_TYPE_EQ: case BURN_SYMBOL_TYPE_EQ_I: *pfResult = llLeftOperand == llRightOperand; break; - case BURN_SYMBOL_TYPE_NE: case BURN_SYMBOL_TYPE_NE_I: *pfResult = llLeftOperand != llRightOperand; break; - case BURN_SYMBOL_TYPE_BAND: case BURN_SYMBOL_TYPE_BAND_I: *pfResult = (llLeftOperand & llRightOperand) ? TRUE : FALSE; break; - case BURN_SYMBOL_TYPE_HIEQ: case BURN_SYMBOL_TYPE_HIEQ_I: *pfResult = ((llLeftOperand >> 16) & 0xFFFF) == llRightOperand; break; - case BURN_SYMBOL_TYPE_LOEQ: case BURN_SYMBOL_TYPE_LOEQ_I: *pfResult = (llLeftOperand & 0xFFFF) == llRightOperand; break; - default: - ExitFunction1(hr = E_INVALIDARG); - } - -LExit: - return hr; -} - -// -// CompareVersionValues - compares two quad-word version values using a given comparison. -// -static HRESULT CompareVersionValues( - __in BURN_SYMBOL_TYPE comparison, - __in VERUTIL_VERSION* pLeftOperand, - __in VERUTIL_VERSION* pRightOperand, - __out BOOL* pfResult - ) -{ - HRESULT hr = S_OK; - int nResult = 0; - - hr = VerCompareParsedVersions(pLeftOperand, pRightOperand, &nResult); - ExitOnFailure(hr, "Failed to compare condition versions: '%ls', '%ls'", pLeftOperand->sczVersion, pRightOperand->sczVersion); - - switch (comparison) - { - case BURN_SYMBOL_TYPE_LT: *pfResult = nResult < 0; break; - case BURN_SYMBOL_TYPE_GT: *pfResult = nResult > 0; break; - case BURN_SYMBOL_TYPE_LE: *pfResult = nResult <= 0; break; - case BURN_SYMBOL_TYPE_GE: *pfResult = nResult >= 0; break; - case BURN_SYMBOL_TYPE_EQ: *pfResult = nResult == 0; break; - case BURN_SYMBOL_TYPE_NE: *pfResult = nResult != 0; break; - default: - ExitFunction1(hr = E_INVALIDARG); - } - -LExit: - return hr; -} diff --git a/src/engine/condition.h b/src/engine/condition.h deleted file mode 100644 index 91627f3c..00000000 --- a/src/engine/condition.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -typedef struct _BURN_CONDITION -{ - // The is an expression a condition string to fire the built-in "need newer OS" message - LPWSTR sczConditionString; -} BURN_CONDITION; - - -// function declarations - -HRESULT ConditionEvaluate( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzCondition, - __out BOOL* pf - ); -HRESULT ConditionGlobalCheck( - __in BURN_VARIABLES* pVariables, - __in BURN_CONDITION* pBlock, - __in BOOTSTRAPPER_DISPLAY display, - __in_z LPCWSTR wzBundleName, - __out DWORD *pdwExitCode, - __out BOOL *pfContinueExecution - ); -HRESULT ConditionGlobalParseFromXml( - __in BURN_CONDITION* pBlock, - __in IXMLDOMNode* pixnBundle - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/container.cpp b/src/engine/container.cpp deleted file mode 100644 index 0cce3131..00000000 --- a/src/engine/container.cpp +++ /dev/null @@ -1,398 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// function definitions - -extern "C" HRESULT ContainersParseFromXml( - __in BURN_CONTAINERS* pContainers, - __in IXMLDOMNode* pixnBundle - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnNodes = NULL; - IXMLDOMNode* pixnNode = NULL; - DWORD cNodes = 0; - LPWSTR scz = NULL; - - // select container nodes - hr = XmlSelectNodes(pixnBundle, L"Container", &pixnNodes); - ExitOnFailure(hr, "Failed to select container nodes."); - - // get container node count - hr = pixnNodes->get_length((long*)&cNodes); - ExitOnFailure(hr, "Failed to get container node count."); - - if (!cNodes) - { - ExitFunction(); - } - - // allocate memory for searches - pContainers->rgContainers = (BURN_CONTAINER*)MemAlloc(sizeof(BURN_CONTAINER) * cNodes, TRUE); - ExitOnNull(pContainers->rgContainers, hr, E_OUTOFMEMORY, "Failed to allocate memory for container structs."); - - pContainers->cContainers = cNodes; - - // parse search elements - for (DWORD i = 0; i < cNodes; ++i) - { - BURN_CONTAINER* pContainer = &pContainers->rgContainers[i]; - - hr = XmlNextElement(pixnNodes, &pixnNode, NULL); - ExitOnFailure(hr, "Failed to get next node."); - - // TODO: Read type from manifest. Today only CABINET is supported. - pContainer->type = BURN_CONTAINER_TYPE_CABINET; - - // @Id - hr = XmlGetAttributeEx(pixnNode, L"Id", &pContainer->sczId); - ExitOnFailure(hr, "Failed to get @Id."); - - // @Primary - hr = XmlGetYesNoAttribute(pixnNode, L"Primary", &pContainer->fPrimary); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @Primary."); - } - - // @Attached - hr = XmlGetYesNoAttribute(pixnNode, L"Attached", &pContainer->fAttached); - if (E_NOTFOUND != hr || pContainer->fPrimary) // if it is a primary container, it has to be attached - { - ExitOnFailure(hr, "Failed to get @Attached."); - } - - // @AttachedIndex - hr = XmlGetAttributeNumber(pixnNode, L"AttachedIndex", &pContainer->dwAttachedIndex); - if (E_NOTFOUND != hr || pContainer->fAttached) // if it is an attached container it must have an index - { - ExitOnFailure(hr, "Failed to get @AttachedIndex."); - } - - // Attached containers are always found attached to the current process, so use the current proccess's - // name instead of what may be in the manifest. - if (pContainer->fAttached) - { - hr = PathForCurrentProcess(&scz, NULL); - ExitOnFailure(hr, "Failed to get path to current process for attached container."); - - LPCWSTR wzFileName = PathFile(scz); - - hr = StrAllocString(&pContainer->sczFilePath, wzFileName, 0); - ExitOnFailure(hr, "Failed to set attached container file path."); - } - else - { - // @FilePath - hr = XmlGetAttributeEx(pixnNode, L"FilePath", &pContainer->sczFilePath); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @FilePath."); - } - } - - // The source path starts as the file path. - hr = StrAllocString(&pContainer->sczSourcePath, pContainer->sczFilePath, 0); - ExitOnFailure(hr, "Failed to copy @FilePath"); - - // @DownloadUrl - hr = XmlGetAttributeEx(pixnNode, L"DownloadUrl", &pContainer->downloadSource.sczUrl); - if (E_NOTFOUND != hr || (!pContainer->fPrimary && !pContainer->sczSourcePath)) // if the package is not a primary package, it must have a source path or a download url - { - ExitOnFailure(hr, "Failed to get @DownloadUrl. Either @SourcePath or @DownloadUrl needs to be provided."); - } - - // @Hash - hr = XmlGetAttributeEx(pixnNode, L"Hash", &pContainer->sczHash); - if (SUCCEEDED(hr)) - { - hr = StrAllocHexDecode(pContainer->sczHash, &pContainer->pbHash, &pContainer->cbHash); - ExitOnFailure(hr, "Failed to hex decode the Container/@Hash."); - } - else if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @Hash."); - } - - // prepare next iteration - ReleaseNullObject(pixnNode); - } - - hr = S_OK; - -LExit: - ReleaseObject(pixnNodes); - ReleaseObject(pixnNode); - ReleaseStr(scz); - - return hr; -} - -extern "C" HRESULT ContainersInitialize( - __in BURN_CONTAINERS* pContainers, - __in BURN_SECTION* pSection - ) -{ - HRESULT hr = S_OK; - - if (pContainers->rgContainers) - { - for (DWORD i = 0; i < pContainers->cContainers; ++i) - { - BURN_CONTAINER* pContainer = &pContainers->rgContainers[i]; - - // If the container is attached, make sure the information in the section matches what the - // manifest contained and get the offset to the container. - if (pContainer->fAttached) - { - hr = SectionGetAttachedContainerInfo(pSection, pContainer->dwAttachedIndex, pContainer->type, &pContainer->qwAttachedOffset, &pContainer->qwFileSize, &pContainer->fActuallyAttached); - ExitOnFailure(hr, "Failed to get attached container information."); - } - } - } - -LExit: - return hr; -} - -extern "C" void ContainersUninitialize( - __in BURN_CONTAINERS* pContainers - ) -{ - if (pContainers->rgContainers) - { - for (DWORD i = 0; i < pContainers->cContainers; ++i) - { - BURN_CONTAINER* pContainer = &pContainers->rgContainers[i]; - - ReleaseStr(pContainer->sczId); - ReleaseStr(pContainer->sczHash); - ReleaseStr(pContainer->sczSourcePath); - ReleaseStr(pContainer->sczFilePath); - ReleaseMem(pContainer->pbHash); - ReleaseStr(pContainer->downloadSource.sczUrl); - ReleaseStr(pContainer->downloadSource.sczUser); - ReleaseStr(pContainer->downloadSource.sczPassword); - ReleaseStr(pContainer->sczUnverifiedPath); - } - MemFree(pContainers->rgContainers); - } - - // clear struct - memset(pContainers, 0, sizeof(BURN_CONTAINERS)); -} - -extern "C" HRESULT ContainerOpenUX( - __in BURN_SECTION* pSection, - __in BURN_CONTAINER_CONTEXT* pContext - ) -{ - HRESULT hr = S_OK; - BURN_CONTAINER container = { }; - LPWSTR sczExecutablePath = NULL; - - // open attached container - container.type = BURN_CONTAINER_TYPE_CABINET; - container.fPrimary = TRUE; - container.fAttached = TRUE; - container.dwAttachedIndex = 0; - - hr = SectionGetAttachedContainerInfo(pSection, container.dwAttachedIndex, container.type, &container.qwAttachedOffset, &container.qwFileSize, &container.fActuallyAttached); - ExitOnFailure(hr, "Failed to get container information for UX container."); - - AssertSz(container.fActuallyAttached, "The BA container must always be found attached."); - - hr = PathForCurrentProcess(&sczExecutablePath, NULL); - ExitOnFailure(hr, "Failed to get path for executing module."); - - hr = ContainerOpen(pContext, &container, pSection->hEngineFile, sczExecutablePath); - ExitOnFailure(hr, "Failed to open attached container."); - -LExit: - ReleaseStr(sczExecutablePath); - - return hr; -} - -extern "C" HRESULT ContainerOpen( - __in BURN_CONTAINER_CONTEXT* pContext, - __in BURN_CONTAINER* pContainer, - __in HANDLE hContainerFile, - __in_z LPCWSTR wzFilePath - ) -{ - HRESULT hr = S_OK; - LARGE_INTEGER li = { }; - - // initialize context - pContext->type = pContainer->type; - pContext->qwSize = pContainer->qwFileSize; - pContext->qwOffset = pContainer->qwAttachedOffset; - - // If the handle to the container is not open already, open container file - if (INVALID_HANDLE_VALUE == hContainerFile) - { - pContext->hFile = ::CreateFileW(wzFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); - ExitOnInvalidHandleWithLastError(pContext->hFile, hr, "Failed to open file: %ls", wzFilePath); - } - else // use the container file handle. - { - if (!::DuplicateHandle(::GetCurrentProcess(), hContainerFile, ::GetCurrentProcess(), &pContext->hFile, 0, FALSE, DUPLICATE_SAME_ACCESS)) - { - ExitWithLastError(hr, "Failed to duplicate handle to container: %ls", wzFilePath); - } - } - - // If it is a container attached to an executable, seek to the container offset. - if (pContainer->fAttached) - { - li.QuadPart = (LONGLONG)pContext->qwOffset; - } - - if (!::SetFilePointerEx(pContext->hFile, li, NULL, FILE_BEGIN)) - { - ExitWithLastError(hr, "Failed to move file pointer to container offset."); - } - - // open the archive - switch (pContext->type) - { - case BURN_CONTAINER_TYPE_CABINET: - hr = CabExtractOpen(pContext, wzFilePath); - break; - } - ExitOnFailure(hr, "Failed to open container."); - -LExit: - return hr; -} - -extern "C" HRESULT ContainerNextStream( - __in BURN_CONTAINER_CONTEXT* pContext, - __inout_z LPWSTR* psczStreamName - ) -{ - HRESULT hr = S_OK; - - switch (pContext->type) - { - case BURN_CONTAINER_TYPE_CABINET: - hr = CabExtractNextStream(pContext, psczStreamName); - break; - } - -//LExit: - return hr; -} - -extern "C" HRESULT ContainerStreamToFile( - __in BURN_CONTAINER_CONTEXT* pContext, - __in_z LPCWSTR wzFileName - ) -{ - HRESULT hr = S_OK; - - switch (pContext->type) - { - case BURN_CONTAINER_TYPE_CABINET: - hr = CabExtractStreamToFile(pContext, wzFileName); - break; - } - -//LExit: - return hr; -} - -extern "C" HRESULT ContainerStreamToBuffer( - __in BURN_CONTAINER_CONTEXT* pContext, - __out BYTE** ppbBuffer, - __out SIZE_T* pcbBuffer - ) -{ - HRESULT hr = S_OK; - - switch (pContext->type) - { - case BURN_CONTAINER_TYPE_CABINET: - hr = CabExtractStreamToBuffer(pContext, ppbBuffer, pcbBuffer); - break; - - default: - *ppbBuffer = NULL; - *pcbBuffer = 0; - } - -//LExit: - return hr; -} - -extern "C" HRESULT ContainerSkipStream( - __in BURN_CONTAINER_CONTEXT* pContext - ) -{ - HRESULT hr = S_OK; - - switch (pContext->type) - { - case BURN_CONTAINER_TYPE_CABINET: - hr = CabExtractSkipStream(pContext); - break; - } - -//LExit: - return hr; -} - -extern "C" HRESULT ContainerClose( - __in BURN_CONTAINER_CONTEXT* pContext - ) -{ - HRESULT hr = S_OK; - - // close container - switch (pContext->type) - { - case BURN_CONTAINER_TYPE_CABINET: - hr = CabExtractClose(pContext); - ExitOnFailure(hr, "Failed to close cabinet."); - break; - } - -LExit: - ReleaseFile(pContext->hFile); - - if (SUCCEEDED(hr)) - { - memset(pContext, 0, sizeof(BURN_CONTAINER_CONTEXT)); - } - - return hr; -} - -extern "C" HRESULT ContainerFindById( - __in BURN_CONTAINERS* pContainers, - __in_z LPCWSTR wzId, - __out BURN_CONTAINER** ppContainer - ) -{ - HRESULT hr = S_OK; - BURN_CONTAINER* pContainer = NULL; - - for (DWORD i = 0; i < pContainers->cContainers; ++i) - { - pContainer = &pContainers->rgContainers[i]; - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pContainer->sczId, -1, wzId, -1)) - { - *ppContainer = pContainer; - ExitFunction1(hr = S_OK); - } - } - - hr = E_NOTFOUND; - -LExit: - return hr; -} diff --git a/src/engine/container.h b/src/engine/container.h deleted file mode 100644 index c2c1c9a8..00000000 --- a/src/engine/container.h +++ /dev/null @@ -1,191 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -// typedefs - -//typedef HRESULT (*PFN_EXTRACTOPEN)( -// __in HANDLE hFile, -// __in DWORD64 qwOffset, -// __in DWORD64 qwSize, -// __out void** ppCookie -// ); -//typedef HRESULT (*PFN_EXTRACTNEXTSTREAM)( -// __in void* pCookie, -// __inout_z LPWSTR* psczStreamName -// ); -//typedef HRESULT (*PFN_EXTRACTSTREAMTOFILE)( -// __in void* pCookie, -// __in_z LPCWSTR wzFileName -// ); -//typedef HRESULT (*PFN_EXTRACTSTREAMTOBUFFER)( -// __in void* pCookie, -// __out BYTE** ppbBuffer, -// __out SIZE_T* pcbBuffer -// ); -//typedef HRESULT (*PFN_EXTRACTCLOSE)( -// __in void* pCookie -// ); - - -// constants - -enum BURN_CONTAINER_TYPE -{ - BURN_CONTAINER_TYPE_NONE, - BURN_CONTAINER_TYPE_CABINET, - BURN_CONTAINER_TYPE_SEVENZIP, -}; - -enum BURN_CAB_OPERATION -{ - BURN_CAB_OPERATION_NONE, - BURN_CAB_OPERATION_NEXT_STREAM, - BURN_CAB_OPERATION_STREAM_TO_FILE, - BURN_CAB_OPERATION_STREAM_TO_BUFFER, - BURN_CAB_OPERATION_SKIP_STREAM, - BURN_CAB_OPERATION_CLOSE, -}; - - -// structs - -typedef struct _BURN_CONTAINER -{ - LPWSTR sczId; - BURN_CONTAINER_TYPE type; - BOOL fPrimary; - BOOL fAttached; - DWORD dwAttachedIndex; - DWORD64 qwFileSize; - LPWSTR sczHash; - LPWSTR sczFilePath; // relative path to container. - DOWNLOAD_SOURCE downloadSource; - - BYTE* pbHash; - DWORD cbHash; - DWORD64 qwAttachedOffset; - BOOL fActuallyAttached; // indicates whether an attached container is attached or missing. - - // mutable members - BOOL fPlanned; - LPWSTR sczSourcePath; - LPWSTR sczUnverifiedPath; - DWORD64 qwExtractSizeTotal; - DWORD64 qwCommittedCacheProgress; - DWORD64 qwCommittedExtractProgress; - HRESULT hrExtract; -} BURN_CONTAINER; - -typedef struct _BURN_CONTAINERS -{ - BURN_CONTAINER* rgContainers; - DWORD cContainers; -} BURN_CONTAINERS; - -typedef struct _BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER -{ - HANDLE hFile; - LARGE_INTEGER liPosition; -} BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER; - -typedef struct _BURN_CONTAINER_CONTEXT_CABINET -{ - LPWSTR sczFile; - - HANDLE hThread; - HANDLE hBeginOperationEvent; - HANDLE hOperationCompleteEvent; - - BURN_CAB_OPERATION operation; - HRESULT hrError; - - LPWSTR* psczStreamName; - LPCWSTR wzTargetFile; - HANDLE hTargetFile; - BYTE* pbTargetBuffer; - DWORD cbTargetBuffer; - DWORD iTargetBuffer; - - BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* rgVirtualFilePointers; - DWORD cVirtualFilePointers; -} BURN_CONTAINER_CONTEXT_CABINET; - -typedef struct _BURN_CONTAINER_CONTEXT -{ - HANDLE hFile; - DWORD64 qwOffset; - DWORD64 qwSize; - - //PFN_EXTRACTOPEN pfnExtractOpen; - //PFN_EXTRACTNEXTSTREAM pfnExtractNextStream; - //PFN_EXTRACTSTREAMTOFILE pfnExtractStreamToFile; - //PFN_EXTRACTSTREAMTOBUFFER pfnExtractStreamToBuffer; - //PFN_EXTRACTCLOSE pfnExtractClose; - //void* pCookie; - BURN_CONTAINER_TYPE type; - union - { - BURN_CONTAINER_CONTEXT_CABINET Cabinet; - }; - -} BURN_CONTAINER_CONTEXT; - - -// functions - -HRESULT ContainersParseFromXml( - __in BURN_CONTAINERS* pContainers, - __in IXMLDOMNode* pixnBundle - ); -HRESULT ContainersInitialize( - __in BURN_CONTAINERS* pContainers, - __in BURN_SECTION* pSection - ); -void ContainersUninitialize( - __in BURN_CONTAINERS* pContainers - ); -HRESULT ContainerOpenUX( - __in BURN_SECTION* pSection, - __in BURN_CONTAINER_CONTEXT* pContext - ); -HRESULT ContainerOpen( - __in BURN_CONTAINER_CONTEXT* pContext, - __in BURN_CONTAINER* pContainer, - __in HANDLE hContainerFile, - __in_z LPCWSTR wzFilePath - ); -HRESULT ContainerNextStream( - __in BURN_CONTAINER_CONTEXT* pContext, - __inout_z LPWSTR* psczStreamName - ); -HRESULT ContainerStreamToFile( - __in BURN_CONTAINER_CONTEXT* pContext, - __in_z LPCWSTR wzFileName - ); -HRESULT ContainerStreamToBuffer( - __in BURN_CONTAINER_CONTEXT* pContext, - __out BYTE** ppbBuffer, - __out SIZE_T* pcbBuffer - ); -HRESULT ContainerSkipStream( - __in BURN_CONTAINER_CONTEXT* pContext - ); -HRESULT ContainerClose( - __in BURN_CONTAINER_CONTEXT* pContext - ); -HRESULT ContainerFindById( - __in BURN_CONTAINERS* pContainers, - __in_z LPCWSTR wzId, - __out BURN_CONTAINER** ppContainer - ); - - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/core.cpp b/src/engine/core.cpp deleted file mode 100644 index 535043af..00000000 --- a/src/engine/core.cpp +++ /dev/null @@ -1,1856 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// structs - -struct BURN_CACHE_THREAD_CONTEXT -{ - BURN_ENGINE_STATE* pEngineState; - DWORD* pcOverallProgressTicks; - BOOL* pfRollback; -}; - - -// internal function declarations - -static HRESULT ParseCommandLine( - __in int argc, - __in LPWSTR* argv, - __in BOOTSTRAPPER_COMMAND* pCommand, - __in BURN_PIPE_CONNECTION* pCompanionConnection, - __in BURN_PIPE_CONNECTION* pEmbeddedConnection, - __in BURN_VARIABLES* pVariables, - __out BURN_MODE* pMode, - __out BURN_AU_PAUSE_ACTION* pAutomaticUpdates, - __out BOOL* pfDisableSystemRestore, - __out_z LPWSTR* psczSourceProcessPath, - __out_z LPWSTR* psczOriginalSource, - __out BOOL* pfDisableUnelevate, - __out DWORD *pdwLoggingAttributes, - __out_z LPWSTR* psczLogFile, - __out_z LPWSTR* psczActiveParent, - __out_z LPWSTR* psczIgnoreDependencies, - __out_z LPWSTR* psczAncestors, - __out_z LPWSTR* psczSanitizedCommandLine - ); -static HRESULT ParsePipeConnection( - __in_ecount(3) LPWSTR* rgArgs, - __in BURN_PIPE_CONNECTION* pConnection - ); -static HRESULT DetectPackage( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_PACKAGE* pPackage - ); -static HRESULT DetectPackagePayloadsCached( - __in BURN_PACKAGE* pPackage - ); -static DWORD WINAPI CacheThreadProc( - __in LPVOID lpThreadParameter - ); -static HRESULT WaitForCacheThread( - __in HANDLE hCacheThread - ); -static void LogPackages( - __in_opt const BURN_PACKAGE* pUpgradeBundlePackage, - __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage, - __in const BURN_PACKAGES* pPackages, - __in const BURN_RELATED_BUNDLES* pRelatedBundles, - __in const BOOTSTRAPPER_ACTION action - ); -static void LogRelatedBundles( - __in const BURN_RELATED_BUNDLES* pRelatedBundles, - __in BOOL fReverse - ); - - -// function definitions - -extern "C" HRESULT CoreInitialize( - __in BURN_ENGINE_STATE* pEngineState - ) -{ - HRESULT hr = S_OK; - LPWSTR sczSanitizedCommandLine = NULL; - LPWSTR sczStreamName = NULL; - BYTE* pbBuffer = NULL; - SIZE_T cbBuffer = 0; - BURN_CONTAINER_CONTEXT containerContext = { }; - BOOL fElevated = FALSE; - LPWSTR sczSourceProcessPath = NULL; - LPWSTR sczSourceProcessFolder = NULL; - LPWSTR sczOriginalSource = NULL; - - // Initialize variables. - hr = VariableInitialize(&pEngineState->variables); - ExitOnFailure(hr, "Failed to initialize variables."); - - // Open attached UX container. - hr = ContainerOpenUX(&pEngineState->section, &containerContext); - ExitOnFailure(hr, "Failed to open attached UX container."); - - // Load manifest. - hr = ContainerNextStream(&containerContext, &sczStreamName); - ExitOnFailure(hr, "Failed to open manifest stream."); - - hr = ContainerStreamToBuffer(&containerContext, &pbBuffer, &cbBuffer); - ExitOnFailure(hr, "Failed to get manifest stream from container."); - - hr = ManifestLoadXmlFromBuffer(pbBuffer, cbBuffer, pEngineState); - ExitOnFailure(hr, "Failed to load manifest."); - - hr = ContainersInitialize(&pEngineState->containers, &pEngineState->section); - ExitOnFailure(hr, "Failed to initialize containers."); - - // Parse command line. - hr = ParseCommandLine(pEngineState->argc, pEngineState->argv, &pEngineState->command, &pEngineState->companionConnection, &pEngineState->embeddedConnection, &pEngineState->variables, &pEngineState->mode, &pEngineState->automaticUpdates, &pEngineState->fDisableSystemRestore, &sczSourceProcessPath, &sczOriginalSource, &pEngineState->fDisableUnelevate, &pEngineState->log.dwAttributes, &pEngineState->log.sczPath, &pEngineState->registration.sczActiveParent, &pEngineState->sczIgnoreDependencies, &pEngineState->registration.sczAncestors, &sczSanitizedCommandLine); - ExitOnFailure(hr, "Failed to parse command line."); - - LogId(REPORT_STANDARD, MSG_BURN_COMMAND_LINE, sczSanitizedCommandLine ? sczSanitizedCommandLine : L""); - - hr = CoreInitializeConstants(pEngineState); - ExitOnFailure(hr, "Failed to initialize contants."); - - // Retain whether bundle was initially run elevated. - ProcElevated(::GetCurrentProcess(), &fElevated); - - hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, fElevated, TRUE); - ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_ELEVATED); - - hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_UILEVEL, pEngineState->command.display, TRUE); - ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_UILEVEL); - - if (sczSourceProcessPath) - { - hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_PATH, sczSourceProcessPath, TRUE, FALSE); - ExitOnFailure(hr, "Failed to set source process path variable."); - - hr = PathGetDirectory(sczSourceProcessPath, &sczSourceProcessFolder); - ExitOnFailure(hr, "Failed to get source process folder from path."); - - hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, sczSourceProcessFolder, TRUE, FALSE); - ExitOnFailure(hr, "Failed to set source process folder variable."); - } - - // Set BURN_BUNDLE_ORIGINAL_SOURCE, if it was passed in on the command line. - // Needs to be done after ManifestLoadXmlFromBuffer. - if (sczOriginalSource) - { - hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_ORIGINAL_SOURCE, sczOriginalSource, FALSE, FALSE); - ExitOnFailure(hr, "Failed to set original source variable."); - } - - if (BURN_MODE_UNTRUSTED == pEngineState->mode || BURN_MODE_NORMAL == pEngineState->mode || BURN_MODE_EMBEDDED == pEngineState->mode) - { - hr = CacheInitialize(&pEngineState->registration, &pEngineState->variables, sczSourceProcessPath); - ExitOnFailure(hr, "Failed to initialize internal cache functionality."); - } - - // If we're not elevated then we'll be loading the bootstrapper application, so extract - // the payloads from the BA container. - if (BURN_MODE_NORMAL == pEngineState->mode || BURN_MODE_EMBEDDED == pEngineState->mode) - { - // Extract all UX payloads to working folder. - hr = UserExperienceEnsureWorkingFolder(pEngineState->registration.sczId, &pEngineState->userExperience.sczTempDirectory); - ExitOnFailure(hr, "Failed to get unique temporary folder for bootstrapper application."); - - hr = PayloadExtractUXContainer(&pEngineState->userExperience.payloads, &containerContext, pEngineState->userExperience.sczTempDirectory); - ExitOnFailure(hr, "Failed to extract bootstrapper application payloads."); - - hr = PathConcat(pEngineState->userExperience.sczTempDirectory, L"BootstrapperApplicationData.xml", &pEngineState->command.wzBootstrapperApplicationDataPath); - ExitOnFailure(hr, "Failed to get BootstrapperApplicationDataPath."); - - hr = StrAllocString(&pEngineState->command.wzBootstrapperWorkingFolder, pEngineState->userExperience.sczTempDirectory, 0); - ExitOnFailure(hr, "Failed to copy sczBootstrapperWorkingFolder."); - } - -LExit: - ReleaseStr(sczOriginalSource); - ReleaseStr(sczSourceProcessFolder); - ReleaseStr(sczSourceProcessPath); - ContainerClose(&containerContext); - ReleaseStr(sczStreamName); - ReleaseStr(sczSanitizedCommandLine); - ReleaseMem(pbBuffer); - - return hr; -} - -extern "C" HRESULT CoreInitializeConstants( - __in BURN_ENGINE_STATE* pEngineState - ) -{ - HRESULT hr = S_OK; - BURN_REGISTRATION* pRegistration = &pEngineState->registration; - - hr = DependencyInitialize(pRegistration, pEngineState->sczIgnoreDependencies); - ExitOnFailure(hr, "Failed to initialize dependency data."); - - // Support passing Ancestors to embedded burn bundles. - if (pRegistration->sczAncestors && *pRegistration->sczAncestors) - { - hr = StrAllocFormatted(&pRegistration->sczBundlePackageAncestors, L"%ls;%ls", pRegistration->sczAncestors, pRegistration->sczId); - ExitOnFailure(hr, "Failed to copy ancestors and self to bundle package ancestors."); - } - else - { - hr = StrAllocString(&pRegistration->sczBundlePackageAncestors, pRegistration->sczId, 0); - ExitOnFailure(hr, "Failed to copy self to bundle package ancestors."); - } - - for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) - { - BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; - - if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) // TODO: Don't assume exePackages with burn protocol are bundles. - { - // Pass along any ancestors and ourself to prevent infinite loops. - pPackage->Exe.wzAncestors = pRegistration->sczBundlePackageAncestors; - } - } - -LExit: - return hr; -} - -extern "C" HRESULT CoreSerializeEngineState( - __in BURN_ENGINE_STATE* pEngineState, - __inout BYTE** ppbBuffer, - __inout SIZE_T* piBuffer - ) -{ - HRESULT hr = S_OK; - - hr = VariableSerialize(&pEngineState->variables, TRUE, ppbBuffer, piBuffer); - ExitOnFailure(hr, "Failed to serialize variables."); - -LExit: - return hr; -} - -extern "C" HRESULT CoreQueryRegistration( - __in BURN_ENGINE_STATE* pEngineState - ) -{ - HRESULT hr = S_OK; - BYTE* pbBuffer = NULL; - SIZE_T cbBuffer = 0; - SIZE_T iBuffer = 0; - - // Detect if bundle is already installed. - hr = RegistrationDetectInstalled(&pEngineState->registration); - ExitOnFailure(hr, "Failed to detect bundle install state."); - - // detect resume type - hr = RegistrationDetectResumeType(&pEngineState->registration, &pEngineState->command.resumeType); - ExitOnFailure(hr, "Failed to detect resume type."); - - // If we have a resume mode that suggests the bundle might already be present, try to load any - // previously stored state. - if (BOOTSTRAPPER_RESUME_TYPE_INVALID < pEngineState->command.resumeType) - { - // load resume state - hr = RegistrationLoadState(&pEngineState->registration, &pbBuffer, &cbBuffer); - if (SUCCEEDED(hr)) - { - hr = VariableDeserialize(&pEngineState->variables, TRUE, pbBuffer, cbBuffer, &iBuffer); - } - - // Log any failures and continue. - if (FAILED(hr)) - { - LogId(REPORT_STANDARD, MSG_CANNOT_LOAD_STATE_FILE, hr, pEngineState->registration.sczStateFile); - hr = S_OK; - } - } - -LExit: - ReleaseBuffer(pbBuffer); - - return hr; -} - -extern "C" HRESULT CoreDetect( - __in BURN_ENGINE_STATE* pEngineState, - __in_opt HWND hwndParent - ) -{ - HRESULT hr = S_OK; - BOOL fDetectBegan = FALSE; - BURN_PACKAGE* pPackage = NULL; - HRESULT hrFirstPackageFailure = S_OK; - - LogId(REPORT_STANDARD, MSG_DETECT_BEGIN, pEngineState->packages.cPackages); - - // Always reset the detect state which means the plan should be reset too. - pEngineState->fDetected = FALSE; - pEngineState->fPlanned = FALSE; - DetectReset(&pEngineState->registration, &pEngineState->packages); - PlanReset(&pEngineState->plan, &pEngineState->containers, &pEngineState->packages, &pEngineState->layoutPayloads); - - // Detect if bundle installed state has changed since start up. This - // only happens if Apply() changed the state of bundle (installed or - // uninstalled). In that case, Detect() can be used here to reset - // the installed state. - hr = RegistrationDetectInstalled(&pEngineState->registration); - ExitOnFailure(hr, "Failed to detect bundle install state."); - - if (pEngineState->registration.fInstalled) - { - hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_INSTALLED, 1, TRUE); - ExitOnFailure(hr, "Failed to set the bundle installed built-in variable."); - } - else - { - hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_INSTALLED, NULL, TRUE, FALSE); - ExitOnFailure(hr, "Failed to unset the bundle installed built-in variable."); - } - - fDetectBegan = TRUE; - hr = UserExperienceOnDetectBegin(&pEngineState->userExperience, pEngineState->registration.fCached, pEngineState->registration.fInstalled, pEngineState->packages.cPackages); - ExitOnRootFailure(hr, "UX aborted detect begin."); - - pEngineState->userExperience.hwndDetect = hwndParent; - - hr = SearchesExecute(&pEngineState->searches, &pEngineState->variables); - ExitOnFailure(hr, "Failed to execute searches."); - - // Load all of the related bundles. - hr = RegistrationDetectRelatedBundles(&pEngineState->registration); - ExitOnFailure(hr, "Failed to detect related bundles."); - - hr = DependencyDetectProviderKeyBundleId(&pEngineState->registration); - if (SUCCEEDED(hr)) - { - hr = DetectForwardCompatibleBundles(&pEngineState->userExperience, &pEngineState->registration); - ExitOnFailure(hr, "Failed to detect forward compatible bundle."); - } - else if (E_NOTFOUND == hr) - { - hr = S_OK; - } - ExitOnFailure(hr, "Failed to detect provider key bundle id."); - - // Report the related bundles. - hr = DetectReportRelatedBundles(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, pEngineState->command.action, &pEngineState->registration.fEligibleForCleanup); - ExitOnFailure(hr, "Failed to report detected related bundles."); - - // Do update detection. - hr = DetectUpdate(pEngineState->registration.sczId, &pEngineState->userExperience, &pEngineState->update); - ExitOnFailure(hr, "Failed to detect update."); - - // Detecting MSPs requires special initialization before processing each package but - // only do the detection if there are actually patch packages to detect because it - // can be expensive. - if (pEngineState->packages.cPatchInfo) - { - hr = MspEngineDetectInitialize(&pEngineState->packages); - ExitOnFailure(hr, "Failed to initialize MSP engine detection."); - - hr = MsiEngineDetectInitialize(&pEngineState->packages); - ExitOnFailure(hr, "Failed to initialize MSI engine detection."); - } - - for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) - { - pPackage = pEngineState->packages.rgPackages + i; - - hr = DetectPackage(pEngineState, pPackage); - - // If the package detection failed, ensure the package state is set to unknown. - if (FAILED(hr)) - { - if (SUCCEEDED(hrFirstPackageFailure)) - { - hrFirstPackageFailure = hr; - } - - pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; - pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; - pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; - } - } - - hr = DependencyDetect(pEngineState); - ExitOnFailure(hr, "Failed to detect the dependencies."); - - // Log the detected states. - for (DWORD iPackage = 0; iPackage < pEngineState->packages.cPackages; ++iPackage) - { - pPackage = pEngineState->packages.rgPackages + iPackage; - - // If any packages that can affect registration are present, then the bundle should not automatically be uninstalled. - if (pEngineState->registration.fEligibleForCleanup && pPackage->fCanAffectRegistration && - (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState || - BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState)) - { - pEngineState->registration.fEligibleForCleanup = FALSE; - } - - LogId(REPORT_STANDARD, MSG_DETECTED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingBoolToString(pPackage->fCached), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->installRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->cacheRegistrationState)); - - if (BURN_PACKAGE_TYPE_MSI == pPackage->type) - { - for (DWORD iFeature = 0; iFeature < pPackage->Msi.cFeatures; ++iFeature) - { - const BURN_MSIFEATURE* pFeature = pPackage->Msi.rgFeatures + iFeature; - LogId(REPORT_STANDARD, MSG_DETECTED_MSI_FEATURE, pPackage->sczId, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState)); - } - } - else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) - { - for (DWORD iTargetProduct = 0; iTargetProduct < pPackage->Msp.cTargetProductCodes; ++iTargetProduct) - { - const BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + iTargetProduct; - LogId(REPORT_STANDARD, MSG_DETECTED_MSP_TARGET, pPackage->sczId, pTargetProduct->wzTargetProductCode, LoggingPackageStateToString(pTargetProduct->patchPackageState)); - } - } - } - -LExit: - if (SUCCEEDED(hr)) - { - hr = hrFirstPackageFailure; - } - - if (SUCCEEDED(hr)) - { - pEngineState->fDetected = TRUE; - } - - if (fDetectBegan) - { - UserExperienceOnDetectComplete(&pEngineState->userExperience, hr, pEngineState->registration.fEligibleForCleanup); - } - - pEngineState->userExperience.hwndDetect = NULL; - - LogId(REPORT_STANDARD, MSG_DETECT_COMPLETE, hr, !fDetectBegan ? "(failed)" : LoggingBoolToString(pEngineState->registration.fInstalled), !fDetectBegan ? "(failed)" : LoggingBoolToString(pEngineState->registration.fCached), FAILED(hr) ? "(failed)" : LoggingBoolToString(pEngineState->registration.fEligibleForCleanup)); - - return hr; -} - -extern "C" HRESULT CorePlan( - __in BURN_ENGINE_STATE* pEngineState, - __in BOOTSTRAPPER_ACTION action - ) -{ - HRESULT hr = S_OK; - BOOL fPlanBegan = FALSE; - BURN_PACKAGE* pUpgradeBundlePackage = NULL; - BURN_PACKAGE* pForwardCompatibleBundlePackage = NULL; - BOOL fContinuePlanning = TRUE; // assume we won't skip planning due to dependencies. - - LogId(REPORT_STANDARD, MSG_PLAN_BEGIN, pEngineState->packages.cPackages, LoggingBurnActionToString(action)); - - fPlanBegan = TRUE; - hr = UserExperienceOnPlanBegin(&pEngineState->userExperience, pEngineState->packages.cPackages); - ExitOnRootFailure(hr, "BA aborted plan begin."); - - if (!pEngineState->fDetected) - { - ExitOnFailure(hr = E_INVALIDSTATE, "Plan cannot be done without a successful Detect."); - } - else if (pEngineState->plan.fAffectedMachineState) - { - ExitOnFailure(hr = E_INVALIDSTATE, "Plan requires a new successful Detect after calling Apply."); - } - - // Always reset the plan. - pEngineState->fPlanned = FALSE; - PlanReset(&pEngineState->plan, &pEngineState->containers, &pEngineState->packages, &pEngineState->layoutPayloads); - - // Remember the overall action state in the plan since it shapes the changes - // we make everywhere. - pEngineState->plan.action = action; - pEngineState->plan.pPayloads = &pEngineState->payloads; - pEngineState->plan.wzBundleId = pEngineState->registration.sczId; - pEngineState->plan.wzBundleProviderKey = pEngineState->registration.sczId; - pEngineState->plan.fDisableRollback = pEngineState->fDisableRollback; - - hr = PlanSetVariables(action, &pEngineState->variables); - ExitOnFailure(hr, "Failed to update action."); - - // Set resume commandline - hr = PlanSetResumeCommand(&pEngineState->registration, action, &pEngineState->command, &pEngineState->log); - ExitOnFailure(hr, "Failed to set resume command"); - - hr = DependencyPlanInitialize(&pEngineState->registration, &pEngineState->plan); - ExitOnFailure(hr, "Failed to initialize the dependencies for the plan."); - - if (BOOTSTRAPPER_ACTION_LAYOUT == action) - { - Assert(!pEngineState->plan.fPerMachine); - - // Plan the bundle's layout. - hr = PlanLayoutBundle(&pEngineState->plan, pEngineState->registration.sczExecutableName, pEngineState->section.qwBundleSize, &pEngineState->variables, &pEngineState->layoutPayloads); - ExitOnFailure(hr, "Failed to plan the layout of the bundle."); - - // Plan the packages' layout. - hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType); - ExitOnFailure(hr, "Failed to plan packages."); - } - else if (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == action || BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED == action) - { - Assert(!pEngineState->plan.fPerMachine); - - pUpgradeBundlePackage = &pEngineState->update.package; - - hr = PlanUpdateBundle(&pEngineState->userExperience, pUpgradeBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType); - ExitOnFailure(hr, "Failed to plan update."); - } - else - { - hr = PlanForwardCompatibleBundles(&pEngineState->userExperience, &pEngineState->command, &pEngineState->plan, &pEngineState->registration, action); - ExitOnFailure(hr, "Failed to plan forward compatible bundles."); - - if (pEngineState->plan.fEnabledForwardCompatibleBundle) - { - Assert(!pEngineState->plan.fPerMachine); - - pForwardCompatibleBundlePackage = &pEngineState->plan.forwardCompatibleBundle; - - hr = PlanPassThroughBundle(&pEngineState->userExperience, pForwardCompatibleBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType); - ExitOnFailure(hr, "Failed to plan passthrough."); - } - else // doing an action that modifies the machine state. - { - pEngineState->plan.fPerMachine = pEngineState->registration.fPerMachine; // default the scope of the plan to the per-machine state of the bundle. - - hr = PlanRegistration(&pEngineState->plan, &pEngineState->registration, pEngineState->command.resumeType, pEngineState->command.relationType, &fContinuePlanning); - ExitOnFailure(hr, "Failed to plan registration."); - - if (fContinuePlanning) - { - // Remember the early index, because we want to be able to insert some related bundles - // into the plan before other executed packages. This particularly occurs for uninstallation - // of addons and patches, which should be uninstalled before the main product. - DWORD dwExecuteActionEarlyIndex = pEngineState->plan.cExecuteActions; - - // Plan the related bundles first to support downgrades with ref-counting. - hr = PlanRelatedBundlesBegin(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->plan); - ExitOnFailure(hr, "Failed to plan related bundles."); - - hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType); - ExitOnFailure(hr, "Failed to plan packages."); - - // Schedule the update of related bundles last. - hr = PlanRelatedBundlesComplete(&pEngineState->registration, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, dwExecuteActionEarlyIndex); - ExitOnFailure(hr, "Failed to schedule related bundles."); - } - } - } - - if (fContinuePlanning) - { - // Finally, display all packages and related bundles in the log. - LogPackages(pUpgradeBundlePackage, pForwardCompatibleBundlePackage, &pEngineState->packages, &pEngineState->registration.relatedBundles, action); - } - - PlanDump(&pEngineState->plan); - -LExit: - if (SUCCEEDED(hr)) - { - pEngineState->fPlanned = TRUE; - } - - if (fPlanBegan) - { - UserExperienceOnPlanComplete(&pEngineState->userExperience, hr); - } - - LogId(REPORT_STANDARD, MSG_PLAN_COMPLETE, hr); - - return hr; -} - -extern "C" HRESULT CoreElevate( - __in BURN_ENGINE_STATE* pEngineState, - __in_opt HWND hwndParent - ) -{ - HRESULT hr = S_OK; - DWORD cAVRetryAttempts = 0; - - while (INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe) - { - // If the elevated companion pipe isn't created yet, let's make that happen. - if (!pEngineState->sczBundleEngineWorkingPath) - { - hr = CacheBundleToWorkingDirectory(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath); - ExitOnFailure(hr, "Failed to cache engine to working directory."); - } - - hr = ElevationElevate(pEngineState, hwndParent); - if (E_SUSPECTED_AV_INTERFERENCE == hr && 1 > cAVRetryAttempts) - { - ++cAVRetryAttempts; - continue; - } - ExitOnFailure(hr, "Failed to actually elevate."); - - hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, TRUE, TRUE); - ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_ELEVATED); - } - -LExit: - return hr; -} - -extern "C" HRESULT CoreApply( - __in BURN_ENGINE_STATE* pEngineState, - __in_opt HWND hwndParent - ) -{ - HRESULT hr = S_OK; - HANDLE hLock = NULL; - DWORD cOverallProgressTicks = 0; - HANDLE hCacheThread = NULL; - BOOL fApplyInitialize = FALSE; - BOOL fElevated = FALSE; - BOOL fRegistered = FALSE; - BOOL fRollback = FALSE; - BOOL fSuspend = FALSE; - BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; - BURN_CACHE_THREAD_CONTEXT cacheThreadContext = { }; - DWORD dwPhaseCount = 0; - BOOTSTRAPPER_APPLYCOMPLETE_ACTION applyCompleteAction = BOOTSTRAPPER_APPLYCOMPLETE_ACTION_NONE; - - LogId(REPORT_STANDARD, MSG_APPLY_BEGIN); - - if (!pEngineState->fPlanned) - { - ExitOnFailure(hr = E_INVALIDSTATE, "Apply cannot be done without a successful Plan."); - } - else if (pEngineState->plan.fAffectedMachineState) - { - ExitOnFailure(hr = E_INVALIDSTATE, "Plans cannot be applied multiple times."); - } - - // Ensure any previous attempts to execute are reset. - ApplyReset(&pEngineState->userExperience, &pEngineState->packages); - - if (pEngineState->plan.cCacheActions) - { - ++dwPhaseCount; - } - if (pEngineState->plan.cExecuteActions) - { - ++dwPhaseCount; - } - - hr = UserExperienceOnApplyBegin(&pEngineState->userExperience, dwPhaseCount); - ExitOnRootFailure(hr, "BA aborted apply begin."); - - pEngineState->plan.fAffectedMachineState = pEngineState->plan.fCanAffectMachineState; - - // Abort if this bundle already requires a restart. - if (BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING == pEngineState->command.resumeType) - { - restart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; - - hr = HRESULT_FROM_WIN32(ERROR_FAIL_NOACTION_REBOOT); - UserExperienceSendError(&pEngineState->userExperience, BOOTSTRAPPER_ERROR_TYPE_APPLY, NULL, hr, NULL, MB_ICONERROR | MB_OK, IDNOACTION); // ignore return value. - ExitFunction(); - } - - hr = ApplyLock(FALSE, &hLock); - ExitOnFailure(hr, "Another per-user setup is already executing."); - - // Initialize only after getting a lock. - fApplyInitialize = TRUE; - ApplyInitialize(); - - pEngineState->userExperience.hwndApply = hwndParent; - - hr = ApplySetVariables(&pEngineState->variables); - ExitOnFailure(hr, "Failed to set initial apply variables."); - - // If the plan is empty of work to do, skip everything. - if (!(pEngineState->plan.cRegistrationActions || pEngineState->plan.cCacheActions || pEngineState->plan.cExecuteActions || pEngineState->plan.cCleanActions)) - { - LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED); - ExitFunction(); - } - - // Ensure the engine is cached to the working path. - if (!pEngineState->sczBundleEngineWorkingPath) - { - hr = CacheBundleToWorkingDirectory(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath); - ExitOnFailure(hr, "Failed to cache engine to working directory."); - } - - // Elevate. - if (pEngineState->plan.fPerMachine) - { - hr = CoreElevate(pEngineState, pEngineState->userExperience.hwndApply); - ExitOnFailure(hr, "Failed to elevate."); - - hr = ElevationApplyInitialize(pEngineState->companionConnection.hPipe, &pEngineState->userExperience, &pEngineState->variables, pEngineState->plan.action, pEngineState->automaticUpdates, !pEngineState->fDisableSystemRestore); - ExitOnFailure(hr, "Failed to initialize apply in elevated process."); - - fElevated = TRUE; - } - - // Register. - if (pEngineState->plan.fCanAffectMachineState) - { - fRegistered = TRUE; - hr = ApplyRegister(pEngineState); - ExitOnFailure(hr, "Failed to register bundle."); - } - - // Cache. - if (pEngineState->plan.cCacheActions) - { - // Launch the cache thread. - cacheThreadContext.pEngineState = pEngineState; - cacheThreadContext.pcOverallProgressTicks = &cOverallProgressTicks; - cacheThreadContext.pfRollback = &fRollback; - - hCacheThread = ::CreateThread(NULL, 0, CacheThreadProc, &cacheThreadContext, 0, NULL); - ExitOnNullWithLastError(hCacheThread, hr, "Failed to create cache thread."); - - // If we're not caching in parallel, wait for the cache thread to terminate. - if (!pEngineState->fParallelCacheAndExecute) - { - hr = WaitForCacheThread(hCacheThread); - ExitOnFailure(hr, "Failed while caching, aborting execution."); - - ReleaseHandle(hCacheThread); - } - } - - // Execute. - if (pEngineState->plan.cExecuteActions) - { - hr = ApplyExecute(pEngineState, hCacheThread, &cOverallProgressTicks, &fRollback, &fSuspend, &restart); - UserExperienceExecutePhaseComplete(&pEngineState->userExperience, hr); // signal that execute completed. - } - - // Wait for cache thread to terminate, this should return immediately unless we're waiting for layout to complete. - if (hCacheThread) - { - HRESULT hrCached = WaitForCacheThread(hCacheThread); - if (SUCCEEDED(hr)) - { - hr = hrCached; - } - } - - // If something went wrong or force restarted, skip cleaning. - if (FAILED(hr) || fRollback || fSuspend || BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart) - { - ExitFunction(); - } - - // Clean. - if (pEngineState->plan.cCleanActions) - { - ApplyClean(&pEngineState->userExperience, &pEngineState->plan, pEngineState->companionConnection.hPipe); - } - -LExit: - // Unregister. - if (fRegistered) - { - ApplyUnregister(pEngineState, FAILED(hr) || fRollback, fSuspend, restart); - } - - if (fElevated) - { - ElevationApplyUninitialize(pEngineState->companionConnection.hPipe); - } - - pEngineState->userExperience.hwndApply = NULL; - - if (fApplyInitialize) - { - ApplyUninitialize(); - } - - if (hLock) - { - ::ReleaseMutex(hLock); - ::CloseHandle(hLock); - } - - ReleaseHandle(hCacheThread); - - UserExperienceOnApplyComplete(&pEngineState->userExperience, hr, restart, &applyCompleteAction); - if (BOOTSTRAPPER_APPLYCOMPLETE_ACTION_RESTART == applyCompleteAction) - { - pEngineState->fRestart = TRUE; - } - - LogId(REPORT_STANDARD, MSG_APPLY_COMPLETE, hr, LoggingRestartToString(restart), LoggingBoolToString(pEngineState->fRestart)); - - return hr; -} - -extern "C" HRESULT CoreLaunchApprovedExe( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe - ) -{ - HRESULT hr = S_OK; - DWORD dwProcessId = 0; - - LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_BEGIN, pLaunchApprovedExe->sczId); - - hr = UserExperienceOnLaunchApprovedExeBegin(&pEngineState->userExperience); - ExitOnRootFailure(hr, "BA aborted LaunchApprovedExe begin."); - - // Elevate. - hr = CoreElevate(pEngineState, pLaunchApprovedExe->hwndParent); - ExitOnFailure(hr, "Failed to elevate."); - - // Launch. - hr = ElevationLaunchApprovedExe(pEngineState->companionConnection.hPipe, pLaunchApprovedExe, &dwProcessId); - -LExit: - UserExperienceOnLaunchApprovedExeComplete(&pEngineState->userExperience, hr, dwProcessId); - - LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_COMPLETE, hr, dwProcessId); - - ApprovedExesUninitializeLaunch(pLaunchApprovedExe); - - return hr; -} - -extern "C" HRESULT CoreQuit( - __in BURN_ENGINE_STATE* pEngineState, - __in int nExitCode - ) -{ - HRESULT hr = S_OK; - - // Save engine state if resume mode is unequal to "none". - if (BURN_RESUME_MODE_NONE != pEngineState->resumeMode) - { - hr = CoreSaveEngineState(pEngineState); - if (FAILED(hr)) - { - LogErrorId(hr, MSG_STATE_NOT_SAVED); - hr = S_OK; - } - } - - LogId(REPORT_STANDARD, MSG_QUIT, nExitCode); - - pEngineState->fQuit = TRUE; - - ::PostQuitMessage(nExitCode); // go bye-bye. - - return hr; -} - -extern "C" HRESULT CoreSaveEngineState( - __in BURN_ENGINE_STATE* pEngineState - ) -{ - HRESULT hr = S_OK; - BYTE* pbBuffer = NULL; - SIZE_T cbBuffer = 0; - - // serialize engine state - hr = CoreSerializeEngineState(pEngineState, &pbBuffer, &cbBuffer); - ExitOnFailure(hr, "Failed to serialize engine state."); - - // write to registration store - if (pEngineState->registration.fPerMachine) - { - hr = ElevationSaveState(pEngineState->companionConnection.hPipe, pbBuffer, cbBuffer); - ExitOnFailure(hr, "Failed to save engine state in per-machine process."); - } - else - { - hr = RegistrationSaveState(&pEngineState->registration, pbBuffer, cbBuffer); - ExitOnFailure(hr, "Failed to save engine state."); - } - -LExit: - ReleaseBuffer(pbBuffer); - - return hr; -} - -extern "C" LPCWSTR CoreRelationTypeToCommandLineString( - __in BOOTSTRAPPER_RELATION_TYPE relationType - ) -{ - LPCWSTR wzRelationTypeCommandLine = NULL; - switch (relationType) - { - case BOOTSTRAPPER_RELATION_DETECT: - wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_DETECT; - break; - case BOOTSTRAPPER_RELATION_UPGRADE: - wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE; - break; - case BOOTSTRAPPER_RELATION_ADDON: - wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_ADDON; - break; - case BOOTSTRAPPER_RELATION_PATCH: - wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_PATCH; - break; - case BOOTSTRAPPER_RELATION_UPDATE: - wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_UPDATE; - break; - case BOOTSTRAPPER_RELATION_DEPENDENT: - break; - case BOOTSTRAPPER_RELATION_NONE: __fallthrough; - default: - wzRelationTypeCommandLine = NULL; - break; - } - - return wzRelationTypeCommandLine; -} - -extern "C" HRESULT CoreRecreateCommandLine( - __deref_inout_z LPWSTR* psczCommandLine, - __in BOOTSTRAPPER_ACTION action, - __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_RESTART restart, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in BOOL fPassthrough, - __in_z_opt LPCWSTR wzActiveParent, - __in_z_opt LPCWSTR wzAncestors, - __in_z_opt LPCWSTR wzAppendLogPath, - __in_z_opt LPCWSTR wzAdditionalCommandLineArguments - ) -{ - HRESULT hr = S_OK; - LPWSTR scz = NULL; - LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType); - - hr = StrAllocString(psczCommandLine, L"", 0); - ExitOnFailure(hr, "Failed to empty command line."); - - switch (display) - { - case BOOTSTRAPPER_DISPLAY_NONE: - hr = StrAllocConcat(psczCommandLine, L" /quiet", 0); - break; - case BOOTSTRAPPER_DISPLAY_PASSIVE: - hr = StrAllocConcat(psczCommandLine, L" /passive", 0); - break; - } - ExitOnFailure(hr, "Failed to append display state to command-line"); - - switch (action) - { - case BOOTSTRAPPER_ACTION_MODIFY: - hr = StrAllocConcat(psczCommandLine, L" /modify", 0); - break; - case BOOTSTRAPPER_ACTION_REPAIR: - hr = StrAllocConcat(psczCommandLine, L" /repair", 0); - break; - case BOOTSTRAPPER_ACTION_UNINSTALL: - hr = StrAllocConcat(psczCommandLine, L" /uninstall", 0); - break; - } - ExitOnFailure(hr, "Failed to append action state to command-line"); - - switch (restart) - { - case BOOTSTRAPPER_RESTART_ALWAYS: - hr = StrAllocConcat(psczCommandLine, L" /forcerestart", 0); - break; - case BOOTSTRAPPER_RESTART_NEVER: - hr = StrAllocConcat(psczCommandLine, L" /norestart", 0); - break; - } - ExitOnFailure(hr, "Failed to append restart state to command-line"); - - if (wzActiveParent) - { - if (*wzActiveParent) - { - hr = StrAllocFormatted(&scz, L" /%ls \"%ls\"", BURN_COMMANDLINE_SWITCH_PARENT, wzActiveParent); - ExitOnFailure(hr, "Failed to format active parent command-line for command-line."); - } - else - { - hr = StrAllocFormatted(&scz, L" /%ls", BURN_COMMANDLINE_SWITCH_PARENT_NONE); - ExitOnFailure(hr, "Failed to format parent:none command-line for command-line."); - } - - hr = StrAllocConcat(psczCommandLine, scz, 0); - ExitOnFailure(hr, "Failed to append active parent command-line to command-line."); - } - - if (wzAncestors) - { - hr = StrAllocFormatted(&scz, L" /%ls=%ls", BURN_COMMANDLINE_SWITCH_ANCESTORS, wzAncestors); - ExitOnFailure(hr, "Failed to format ancestors for command-line."); - - hr = StrAllocConcat(psczCommandLine, scz, 0); - ExitOnFailure(hr, "Failed to append ancestors to command-line."); - } - - if (wzRelationTypeCommandLine) - { - hr = StrAllocFormatted(&scz, L" /%ls", wzRelationTypeCommandLine); - ExitOnFailure(hr, "Failed to format relation type for command-line."); - - hr = StrAllocConcat(psczCommandLine, scz, 0); - ExitOnFailure(hr, "Failed to append relation type to command-line."); - } - - if (fPassthrough) - { - hr = StrAllocFormatted(&scz, L" /%ls", BURN_COMMANDLINE_SWITCH_PASSTHROUGH); - ExitOnFailure(hr, "Failed to format passthrough for command-line."); - - hr = StrAllocConcat(psczCommandLine, scz, 0); - ExitOnFailure(hr, "Failed to append passthrough to command-line."); - } - - if (wzAppendLogPath && *wzAppendLogPath) - { - hr = StrAllocFormatted(&scz, L" /%ls \"%ls\"", BURN_COMMANDLINE_SWITCH_LOG_APPEND, wzAppendLogPath); - ExitOnFailure(hr, "Failed to format append log command-line for command-line."); - - hr = StrAllocConcat(psczCommandLine, scz, 0); - ExitOnFailure(hr, "Failed to append log command-line to command-line"); - } - - if (wzAdditionalCommandLineArguments && *wzAdditionalCommandLineArguments) - { - hr = StrAllocConcat(psczCommandLine, L" ", 0); - ExitOnFailure(hr, "Failed to append space to command-line."); - - hr = StrAllocConcat(psczCommandLine, wzAdditionalCommandLineArguments, 0); - ExitOnFailure(hr, "Failed to append command-line to command-line."); - } - -LExit: - ReleaseStr(scz); - - return hr; -} - -extern "C" HRESULT CoreAppendFileHandleAttachedToCommandLine( - __in HANDLE hFileWithAttachedContainer, - __out HANDLE* phExecutableFile, - __deref_inout_z LPWSTR* psczCommandLine - ) -{ - HRESULT hr = S_OK; - HANDLE hExecutableFile = INVALID_HANDLE_VALUE; - - *phExecutableFile = INVALID_HANDLE_VALUE; - - if (!::DuplicateHandle(::GetCurrentProcess(), hFileWithAttachedContainer, ::GetCurrentProcess(), &hExecutableFile, 0, TRUE, DUPLICATE_SAME_ACCESS)) - { - ExitWithLastError(hr, "Failed to duplicate file handle for attached container."); - } - - hr = StrAllocFormattedSecure(psczCommandLine, L"%ls -%ls=%Iu", *psczCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, reinterpret_cast(hExecutableFile)); - ExitOnFailure(hr, "Failed to append the file handle to the command line."); - - *phExecutableFile = hExecutableFile; - hExecutableFile = INVALID_HANDLE_VALUE; - -LExit: - ReleaseFileHandle(hExecutableFile); - - return hr; -} - -extern "C" HRESULT CoreAppendFileHandleSelfToCommandLine( - __in LPCWSTR wzExecutablePath, - __out HANDLE* phExecutableFile, - __deref_inout_z LPWSTR* psczCommandLine, - __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine - ) -{ - HRESULT hr = S_OK; - HANDLE hExecutableFile = INVALID_HANDLE_VALUE; - SECURITY_ATTRIBUTES securityAttributes = { }; - securityAttributes.bInheritHandle = TRUE; - *phExecutableFile = INVALID_HANDLE_VALUE; - - hExecutableFile = ::CreateFileW(wzExecutablePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, &securityAttributes, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE != hExecutableFile) - { - hr = StrAllocFormattedSecure(psczCommandLine, L"%ls -%ls=%Iu", *psczCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, reinterpret_cast(hExecutableFile)); - ExitOnFailure(hr, "Failed to append the file handle to the command line."); - - if (psczObfuscatedCommandLine) - { - hr = StrAllocFormatted(psczObfuscatedCommandLine, L"%ls -%ls=%Iu", *psczObfuscatedCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, reinterpret_cast(hExecutableFile)); - ExitOnFailure(hr, "Failed to append the file handle to the obfuscated command line."); - } - - *phExecutableFile = hExecutableFile; - hExecutableFile = INVALID_HANDLE_VALUE; - } - -LExit: - ReleaseFileHandle(hExecutableFile); - - return hr; -} - -extern "C" void CoreCleanup( - __in BURN_ENGINE_STATE* pEngineState - ) -{ - HRESULT hr = S_OK; - LONGLONG llValue = 0; - BOOL fNeedsElevation = pEngineState->registration.fPerMachine && INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe; - - LogId(REPORT_STANDARD, MSG_CLEANUP_BEGIN); - - if (pEngineState->plan.fAffectedMachineState) - { - LogId(REPORT_STANDARD, MSG_CLEANUP_SKIPPED_APPLY); - ExitFunction(); - } - - if (fNeedsElevation) - { - hr = VariableGetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, &llValue); - ExitOnFailure(hr, "Failed to get value of WixBundleElevated variable during cleanup"); - - if (llValue) - { - fNeedsElevation = FALSE; - } - else - { - LogId(REPORT_STANDARD, MSG_CLEANUP_SKIPPED_ELEVATION_REQUIRED); - ExitFunction(); - } - } - - if (!pEngineState->fDetected) - { - hr = CoreDetect(pEngineState, pEngineState->hMessageWindow); - ExitOnFailure(hr, "Detect during cleanup failed"); - } - - if (!pEngineState->registration.fEligibleForCleanup) - { - ExitFunction(); - } - - hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); - ExitOnFailure(hr, "Plan during cleanup failed"); - - hr = CoreApply(pEngineState, pEngineState->hMessageWindow); - ExitOnFailure(hr, "Apply during cleanup failed"); - -LExit: - LogId(REPORT_STANDARD, MSG_CLEANUP_COMPLETE, hr); -} - -// internal helper functions - -static HRESULT ParseCommandLine( - __in int argc, - __in LPWSTR* argv, - __in BOOTSTRAPPER_COMMAND* pCommand, - __in BURN_PIPE_CONNECTION* pCompanionConnection, - __in BURN_PIPE_CONNECTION* pEmbeddedConnection, - __in BURN_VARIABLES* pVariables, - __out BURN_MODE* pMode, - __out BURN_AU_PAUSE_ACTION* pAutomaticUpdates, - __out BOOL* pfDisableSystemRestore, - __out_z LPWSTR* psczSourceProcessPath, - __out_z LPWSTR* psczOriginalSource, - __out BOOL* pfDisableUnelevate, - __out DWORD *pdwLoggingAttributes, - __out_z LPWSTR* psczLogFile, - __out_z LPWSTR* psczActiveParent, - __out_z LPWSTR* psczIgnoreDependencies, - __out_z LPWSTR* psczAncestors, - __out_z LPWSTR* psczSanitizedCommandLine - ) -{ - HRESULT hr = S_OK; - BOOL fUnknownArg = FALSE; - BOOL fHidden = FALSE; - LPWSTR sczCommandLine = NULL; - LPWSTR sczSanitizedArgument = NULL; - LPWSTR sczVariableName = NULL; - - for (int i = 0; i < argc; ++i) - { - fUnknownArg = FALSE; - int originalIndex = i; - ReleaseNullStr(sczSanitizedArgument); - - if (argv[i][0] == L'-' || argv[i][0] == L'/') - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"l", -1) || - CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"log", -1) || - CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"xlog", -1)) - { - *pdwLoggingAttributes &= ~BURN_LOGGING_ATTRIBUTE_APPEND; - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], 1, L"x", 1)) - { - *pdwLoggingAttributes |= BURN_LOGGING_ATTRIBUTE_VERBOSE | BURN_LOGGING_ATTRIBUTE_EXTRADEBUG; - } - - if (i + 1 >= argc) - { - ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for log."); - } - - ++i; - - hr = StrAllocString(psczLogFile, argv[i], 0); - ExitOnFailure(hr, "Failed to copy log file path."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"?", -1) || - CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"h", -1) || - CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"help", -1)) - { - pCommand->action = BOOTSTRAPPER_ACTION_HELP; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"q", -1) || - CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"quiet", -1) || - CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"s", -1) || - CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"silent", -1)) - { - pCommand->display = BOOTSTRAPPER_DISPLAY_NONE; - - if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart) - { - pCommand->restart = BOOTSTRAPPER_RESTART_AUTOMATIC; - } - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"passive", -1)) - { - pCommand->display = BOOTSTRAPPER_DISPLAY_PASSIVE; - - if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart) - { - pCommand->restart = BOOTSTRAPPER_RESTART_AUTOMATIC; - } - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"norestart", -1)) - { - pCommand->restart = BOOTSTRAPPER_RESTART_NEVER; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"forcerestart", -1)) - { - pCommand->restart = BOOTSTRAPPER_RESTART_ALWAYS; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"promptrestart", -1)) - { - pCommand->restart = BOOTSTRAPPER_RESTART_PROMPT; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"layout", -1)) - { - if (BOOTSTRAPPER_ACTION_HELP != pCommand->action) - { - pCommand->action = BOOTSTRAPPER_ACTION_LAYOUT; - } - - // If there is another command line argument and it is not a switch, use that as the layout directory. - if (i + 1 < argc && argv[i + 1][0] != L'-' && argv[i + 1][0] != L'/') - { - ++i; - - hr = PathExpand(&pCommand->wzLayoutDirectory, argv[i], PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); - ExitOnFailure(hr, "Failed to copy path for layout directory."); - } - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"uninstall", -1)) - { - if (BOOTSTRAPPER_ACTION_HELP != pCommand->action) - { - pCommand->action = BOOTSTRAPPER_ACTION_UNINSTALL; - } - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"repair", -1)) - { - if (BOOTSTRAPPER_ACTION_HELP != pCommand->action) - { - pCommand->action = BOOTSTRAPPER_ACTION_REPAIR; - } - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"modify", -1)) - { - if (BOOTSTRAPPER_ACTION_HELP != pCommand->action) - { - pCommand->action = BOOTSTRAPPER_ACTION_MODIFY; - } - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"package", -1) || - CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"update", -1)) - { - if (BOOTSTRAPPER_ACTION_UNKNOWN == pCommand->action) - { - pCommand->action = BOOTSTRAPPER_ACTION_INSTALL; - } - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"noaupause", -1)) - { - *pAutomaticUpdates = BURN_AU_PAUSE_ACTION_NONE; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"keepaupaused", -1)) - { - // Switch /noaupause takes precedence. - if (BURN_AU_PAUSE_ACTION_NONE != *pAutomaticUpdates) - { - *pAutomaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME; - } - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"disablesystemrestore", -1)) - { - *pfDisableSystemRestore = TRUE; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"originalsource", -1)) - { - if (i + 1 >= argc) - { - ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for original source."); - } - - ++i; - hr = StrAllocString(psczOriginalSource, argv[i], 0); - ExitOnFailure(hr, "Failed to copy last used source."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PARENT, -1)) - { - if (i + 1 >= argc) - { - ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a value for parent."); - } - - ++i; - - hr = StrAllocString(psczActiveParent, argv[i], 0); - ExitOnFailure(hr, "Failed to copy parent."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PARENT_NONE, -1)) - { - hr = StrAllocString(psczActiveParent, L"", 0); - ExitOnFailure(hr, "Failed to initialize parent to none."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_LOG_APPEND, -1)) - { - if (i + 1 >= argc) - { - ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for append log."); - } - - ++i; - - hr = StrAllocString(psczLogFile, argv[i], 0); - ExitOnFailure(hr, "Failed to copy append log file path."); - - *pdwLoggingAttributes |= BURN_LOGGING_ATTRIBUTE_APPEND; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_ELEVATED, -1)) - { - if (i + 3 >= argc) - { - ExitOnRootFailure(hr = E_INVALIDARG, "Must specify the elevated name, token and parent process id."); - } - - if (BURN_MODE_UNTRUSTED != *pMode) - { - ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided."); - } - - *pMode = BURN_MODE_ELEVATED; - - ++i; - - hr = ParsePipeConnection(argv + i, pCompanionConnection); - ExitOnFailure(hr, "Failed to parse elevated connection."); - - i += 2; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM), BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM))) - { - // Get a pointer to the next character after the switch. - LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM)]; - if (L'=' != wzParam[0] || L'\0' == wzParam[1]) - { - ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_CLEAN_ROOM); - } - - if (BURN_MODE_UNTRUSTED != *pMode) - { - ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided."); - } - - *pMode = BURN_MODE_NORMAL; - - hr = StrAllocString(psczSourceProcessPath, wzParam + 1, 0); - ExitOnFailure(hr, "Failed to copy source process path."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_EMBEDDED, -1)) - { - if (i + 3 >= argc) - { - ExitOnRootFailure(hr = E_INVALIDARG, "Must specify the embedded name, token and parent process id."); - } - - switch (*pMode) - { - case BURN_MODE_UNTRUSTED: - // Leave mode as UNTRUSTED to launch the clean room process. - break; - case BURN_MODE_NORMAL: - // The initialization code already assumes that the - // clean room switch is at the beginning of the command line, - // so it's safe to assume that the mode is NORMAL in the clean room. - *pMode = BURN_MODE_EMBEDDED; - break; - default: - ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided."); - } - - ++i; - - hr = ParsePipeConnection(argv + i, pEmbeddedConnection); - ExitOnFailure(hr, "Failed to parse embedded connection."); - - i += 2; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_DETECT, -1)) - { - pCommand->relationType = BOOTSTRAPPER_RELATION_DETECT; - - LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType)); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE, -1)) - { - pCommand->relationType = BOOTSTRAPPER_RELATION_UPGRADE; - - LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType)); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_ADDON, -1)) - { - pCommand->relationType = BOOTSTRAPPER_RELATION_ADDON; - - LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType)); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_PATCH, -1)) - { - pCommand->relationType = BOOTSTRAPPER_RELATION_PATCH; - - LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType)); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_UPDATE, -1)) - { - pCommand->relationType = BOOTSTRAPPER_RELATION_UPDATE; - - LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType)); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PASSTHROUGH, -1)) - { - pCommand->fPassthrough = TRUE; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_DISABLE_UNELEVATE, -1)) - { - *pfDisableUnelevate = TRUE; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RUNONCE, -1)) - { - if (BURN_MODE_UNTRUSTED != *pMode) - { - ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided."); - } - - *pMode = BURN_MODE_RUNONCE; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES), BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES))) - { - // Get a pointer to the next character after the switch. - LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES)]; - if (L'=' != wzParam[0] || L'\0' == wzParam[1]) - { - ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES); - } - - hr = StrAllocString(psczIgnoreDependencies, &wzParam[1], 0); - ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS), BURN_COMMANDLINE_SWITCH_ANCESTORS, lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS))) - { - // Get a pointer to the next character after the switch. - LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS)]; - if (L'=' != wzParam[0] || L'\0' == wzParam[1]) - { - ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_ANCESTORS); - } - - hr = StrAllocString(psczAncestors, &wzParam[1], 0); - ExitOnFailure(hr, "Failed to allocate the list of ancestors."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED), BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED))) - { - // Already processed in InitializeEngineState. - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF), BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF))) - { - // Already processed in InitializeEngineState. - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX), BURN_COMMANDLINE_SWITCH_PREFIX, lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX))) - { - // Skip (but log) any other private burn switches we don't recognize, so that - // adding future private variables doesn't break old bundles - LogId(REPORT_STANDARD, MSG_BURN_UNKNOWN_PRIVATE_SWITCH, &argv[i][1]); - } - else - { - fUnknownArg = TRUE; - } - } - else - { - fUnknownArg = TRUE; - - const wchar_t* pwc = wcschr(argv[i], L'='); - if (pwc) - { - hr = StrAllocString(&sczVariableName, argv[i], pwc - argv[i]); - ExitOnFailure(hr, "Failed to copy variable name."); - - hr = VariableIsHidden(pVariables, sczVariableName, &fHidden); - ExitOnFailure(hr, "Failed to determine whether variable is hidden."); - - if (fHidden) - { - hr = StrAllocFormatted(&sczSanitizedArgument, L"%ls=*****", sczVariableName); - ExitOnFailure(hr, "Failed to copy sanitized argument."); - } - } - } - - // Remember command-line switch to pass off to UX. - if (fUnknownArg) - { - PathCommandLineAppend(&pCommand->wzCommandLine, argv[i]); - } - - if (sczSanitizedArgument) - { - PathCommandLineAppend(psczSanitizedCommandLine, sczSanitizedArgument); - } - else - { - for (; originalIndex <= i; ++originalIndex) - { - PathCommandLineAppend(psczSanitizedCommandLine, argv[originalIndex]); - } - } - } - - // If embedded, ensure the display goes embedded as well. - if (BURN_MODE_EMBEDDED == *pMode) - { - pCommand->display = BOOTSTRAPPER_DISPLAY_EMBEDDED; - } - - // Set the defaults if nothing was set above. - if (BOOTSTRAPPER_ACTION_UNKNOWN == pCommand->action) - { - pCommand->action = BOOTSTRAPPER_ACTION_INSTALL; - } - - if (BOOTSTRAPPER_DISPLAY_UNKNOWN == pCommand->display) - { - pCommand->display = BOOTSTRAPPER_DISPLAY_FULL; - } - - if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart) - { - pCommand->restart = BOOTSTRAPPER_RESTART_PROMPT; - } - -LExit: - ReleaseStr(sczVariableName); - ReleaseStr(sczSanitizedArgument); - ReleaseStr(sczCommandLine); - - return hr; -} - -static HRESULT ParsePipeConnection( - __in_ecount(3) LPWSTR* rgArgs, - __in BURN_PIPE_CONNECTION* pConnection - ) -{ - HRESULT hr = S_OK; - - hr = StrAllocString(&pConnection->sczName, rgArgs[0], 0); - ExitOnFailure(hr, "Failed to copy connection name from command line."); - - hr = StrAllocString(&pConnection->sczSecret, rgArgs[1], 0); - ExitOnFailure(hr, "Failed to copy connection secret from command line."); - - hr = StrStringToUInt32(rgArgs[2], 0, reinterpret_cast(&pConnection->dwProcessId)); - ExitOnFailure(hr, "Failed to copy parent process id from command line."); - -LExit: - return hr; -} - -static HRESULT DetectPackage( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_PACKAGE* pPackage - ) -{ - HRESULT hr = S_OK; - BOOL fBegan = FALSE; - - fBegan = TRUE; - hr = UserExperienceOnDetectPackageBegin(&pEngineState->userExperience, pPackage->sczId); - ExitOnRootFailure(hr, "BA aborted detect package begin."); - - // Detect the cache state of the package. - hr = DetectPackagePayloadsCached(pPackage); - ExitOnFailure(hr, "Failed to detect if payloads are all cached for package: %ls", pPackage->sczId); - - // Use the correct engine to detect the package. - switch (pPackage->type) - { - case BURN_PACKAGE_TYPE_EXE: - hr = ExeEngineDetectPackage(pPackage, &pEngineState->variables); - break; - - case BURN_PACKAGE_TYPE_MSI: - hr = MsiEngineDetectPackage(pPackage, &pEngineState->userExperience); - break; - - case BURN_PACKAGE_TYPE_MSP: - hr = MspEngineDetectPackage(pPackage, &pEngineState->userExperience); - break; - - case BURN_PACKAGE_TYPE_MSU: - hr = MsuEngineDetectPackage(pPackage, &pEngineState->variables); - break; - - default: - hr = E_NOTIMPL; - ExitOnRootFailure(hr, "Package type not supported by detect yet."); - } - -LExit: - if (FAILED(hr)) - { - LogErrorId(hr, MSG_FAILED_DETECT_PACKAGE, pPackage->sczId, NULL, NULL); - } - - if (fBegan) - { - UserExperienceOnDetectPackageComplete(&pEngineState->userExperience, pPackage->sczId, hr, pPackage->currentState, pPackage->fCached); - } - - return hr; -} - -static HRESULT DetectPackagePayloadsCached( - __in BURN_PACKAGE* pPackage - ) -{ - HRESULT hr = S_OK; - LPWSTR sczCachePath = NULL; - BOOL fCached = FALSE; // assume the package is not cached. - LPWSTR sczPayloadCachePath = NULL; - - if (pPackage->sczCacheId && *pPackage->sczCacheId) - { - hr = CacheGetCompletedPath(pPackage->fPerMachine, pPackage->sczCacheId, &sczCachePath); - ExitOnFailure(hr, "Failed to get completed cache path."); - - // If the cached directory exists, we have something. - if (DirExists(sczCachePath, NULL)) - { - // Check all payloads to see if any exist. - for (DWORD i = 0; i < pPackage->payloads.cItems; ++i) - { - BURN_PAYLOAD* pPayload = pPackage->payloads.rgItems[i].pPayload; - - hr = PathConcat(sczCachePath, pPayload->sczFilePath, &sczPayloadCachePath); - ExitOnFailure(hr, "Failed to concat payload cache path."); - - if (FileExistsEx(sczPayloadCachePath, NULL)) - { - fCached = TRUE; - break; - } - else - { - LogId(REPORT_STANDARD, MSG_DETECT_PACKAGE_NOT_FULLY_CACHED, pPackage->sczId, pPayload->sczKey); - } - } - } - } - - pPackage->fCached = fCached; - - if (pPackage->fCanAffectRegistration) - { - pPackage->cacheRegistrationState = pPackage->fCached ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - } - -LExit: - ReleaseStr(sczPayloadCachePath); - ReleaseStr(sczCachePath); - return hr; -} - -static DWORD WINAPI CacheThreadProc( - __in LPVOID lpThreadParameter - ) -{ - HRESULT hr = S_OK; - BURN_CACHE_THREAD_CONTEXT* pContext = reinterpret_cast(lpThreadParameter); - BURN_ENGINE_STATE* pEngineState = pContext->pEngineState; - DWORD* pcOverallProgressTicks = pContext->pcOverallProgressTicks; - BOOL* pfRollback = pContext->pfRollback; - BOOL fComInitialized = FALSE; - - // initialize COM - hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); - ExitOnFailure(hr, "Failed to initialize COM on cache thread."); - fComInitialized = TRUE; - - // cache packages - hr = ApplyCache(pEngineState->section.hSourceEngineFile, &pEngineState->userExperience, &pEngineState->variables, &pEngineState->plan, pEngineState->companionConnection.hCachePipe, pcOverallProgressTicks, pfRollback); - -LExit: - UserExperienceExecutePhaseComplete(&pEngineState->userExperience, hr); // signal that cache completed. - - if (fComInitialized) - { - ::CoUninitialize(); - } - - return (DWORD)hr; -} - -static HRESULT WaitForCacheThread( - __in HANDLE hCacheThread - ) -{ - HRESULT hr = S_OK; - - if (WAIT_OBJECT_0 != ::WaitForSingleObject(hCacheThread, INFINITE)) - { - ExitWithLastError(hr, "Failed to wait for cache thread to terminate."); - } - - if (!::GetExitCodeThread(hCacheThread, (DWORD*)&hr)) - { - ExitWithLastError(hr, "Failed to get cache thread exit code."); - } - -LExit: - return hr; -} - -static void LogPackages( - __in_opt const BURN_PACKAGE* pUpgradeBundlePackage, - __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage, - __in const BURN_PACKAGES* pPackages, - __in const BURN_RELATED_BUNDLES* pRelatedBundles, - __in const BOOTSTRAPPER_ACTION action - ) -{ - if (pUpgradeBundlePackage) - { - LogId(REPORT_STANDARD, MSG_PLANNED_UPGRADE_BUNDLE, pUpgradeBundlePackage->sczId, LoggingRequestStateToString(pUpgradeBundlePackage->defaultRequested), LoggingRequestStateToString(pUpgradeBundlePackage->requested), LoggingActionStateToString(pUpgradeBundlePackage->execute), LoggingActionStateToString(pUpgradeBundlePackage->rollback), LoggingDependencyActionToString(pUpgradeBundlePackage->dependencyExecute)); - } - else if (pForwardCompatibleBundlePackage) - { - LogId(REPORT_STANDARD, MSG_PLANNED_FORWARD_COMPATIBLE_BUNDLE, pForwardCompatibleBundlePackage->sczId, LoggingRequestStateToString(pForwardCompatibleBundlePackage->defaultRequested), LoggingRequestStateToString(pForwardCompatibleBundlePackage->requested), LoggingActionStateToString(pForwardCompatibleBundlePackage->execute), LoggingActionStateToString(pForwardCompatibleBundlePackage->rollback), LoggingDependencyActionToString(pForwardCompatibleBundlePackage->dependencyExecute)); - } - else - { - // Display related bundles first if uninstalling. - if (BOOTSTRAPPER_ACTION_UNINSTALL == action) - { - LogRelatedBundles(pRelatedBundles, TRUE); - } - - // Display all the packages in the log. - for (DWORD i = 0; i < pPackages->cPackages; ++i) - { - const DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? pPackages->cPackages - 1 - i : i; - const BURN_PACKAGE* pPackage = &pPackages->rgPackages[iPackage]; - - LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingBoolToString(pPackage->fPlannedCache), LoggingBoolToString(pPackage->fPlannedUncache), LoggingDependencyActionToString(pPackage->dependencyExecute), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedInstallRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedCacheRegistrationState)); - - if (BURN_PACKAGE_TYPE_MSI == pPackage->type) - { - if (pPackage->Msi.cFeatures) - { - LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURES, pPackage->Msi.cFeatures, pPackage->sczId); - - for (DWORD j = 0; j < pPackage->Msi.cFeatures; ++j) - { - const BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[j]; - - LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURE, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState), LoggingMsiFeatureStateToString(pFeature->defaultRequested), LoggingMsiFeatureStateToString(pFeature->requested), LoggingMsiFeatureActionToString(pFeature->execute), LoggingMsiFeatureActionToString(pFeature->rollback)); - } - } - - if (pPackage->Msi.cSlipstreamMspPackages) - { - LogId(REPORT_STANDARD, MSG_PLANNED_SLIPSTREAMED_MSP_TARGETS, pPackage->Msi.cSlipstreamMspPackages, pPackage->sczId); - - for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) - { - const BURN_SLIPSTREAM_MSP* pSlipstreamMsp = &pPackage->Msi.rgSlipstreamMsps[j]; - - LogId(REPORT_STANDARD, MSG_PLANNED_SLIPSTREAMED_MSP_TARGET, pSlipstreamMsp->pMspPackage->sczId, LoggingActionStateToString(pSlipstreamMsp->execute), LoggingActionStateToString(pSlipstreamMsp->rollback)); - } - } - } - else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.cTargetProductCodes) - { - LogId(REPORT_STANDARD, MSG_PLANNED_MSP_TARGETS, pPackage->Msp.cTargetProductCodes, pPackage->sczId); - - for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j) - { - const BURN_MSPTARGETPRODUCT* pTargetProduct = &pPackage->Msp.rgTargetProducts[j]; - - LogId(REPORT_STANDARD, MSG_PLANNED_MSP_TARGET, pTargetProduct->wzTargetProductCode, LoggingPackageStateToString(pTargetProduct->patchPackageState), LoggingRequestStateToString(pTargetProduct->defaultRequested), LoggingRequestStateToString(pTargetProduct->requested), LoggingMspTargetActionToString(pTargetProduct->execute, pTargetProduct->executeSkip), LoggingMspTargetActionToString(pTargetProduct->rollback, pTargetProduct->rollbackSkip)); - } - } - } - - // Display related bundles last if caching, installing, modifying, or repairing. - if (BOOTSTRAPPER_ACTION_UNINSTALL < action) - { - LogRelatedBundles(pRelatedBundles, FALSE); - } - } -} - -static void LogRelatedBundles( - __in const BURN_RELATED_BUNDLES* pRelatedBundles, - __in BOOL fReverse - ) -{ - if (0 < pRelatedBundles->cRelatedBundles) - { - for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i) - { - const DWORD iRelatedBundle = fReverse ? pRelatedBundles->cRelatedBundles - 1 - i : i; - const BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + iRelatedBundle; - const BURN_PACKAGE* pPackage = &pRelatedBundle->package; - - if (pRelatedBundle->fPlannable) - { - LogId(REPORT_STANDARD, MSG_PLANNED_RELATED_BUNDLE, pPackage->sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingDependencyActionToString(pPackage->dependencyExecute)); - } - } - } -} diff --git a/src/engine/core.h b/src/engine/core.h deleted file mode 100644 index e96440bb..00000000 --- a/src/engine/core.h +++ /dev/null @@ -1,218 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -// constants - -const LPCWSTR BURN_POLICY_REGISTRY_PATH = L"WiX\\Burn"; - -const LPCWSTR BURN_COMMANDLINE_SWITCH_PARENT = L"parent"; -const LPCWSTR BURN_COMMANDLINE_SWITCH_PARENT_NONE = L"parent:none"; -const LPCWSTR BURN_COMMANDLINE_SWITCH_CLEAN_ROOM = L"burn.clean.room"; -const LPCWSTR BURN_COMMANDLINE_SWITCH_ELEVATED = L"burn.elevated"; -const LPCWSTR BURN_COMMANDLINE_SWITCH_EMBEDDED = L"burn.embedded"; -const LPCWSTR BURN_COMMANDLINE_SWITCH_RUNONCE = L"burn.runonce"; -const LPCWSTR BURN_COMMANDLINE_SWITCH_LOG_APPEND = L"burn.log.append"; -const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_DETECT = L"burn.related.detect"; -const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE = L"burn.related.upgrade"; -const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_ADDON = L"burn.related.addon"; -const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_PATCH = L"burn.related.patch"; -const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPDATE = L"burn.related.update"; -const LPCWSTR BURN_COMMANDLINE_SWITCH_PASSTHROUGH = L"burn.passthrough"; -const LPCWSTR BURN_COMMANDLINE_SWITCH_DISABLE_UNELEVATE = L"burn.disable.unelevate"; -const LPCWSTR BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES = L"burn.ignoredependencies"; -const LPCWSTR BURN_COMMANDLINE_SWITCH_ANCESTORS = L"burn.ancestors"; -const LPCWSTR BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED = L"burn.filehandle.attached"; -const LPCWSTR BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF = L"burn.filehandle.self"; -const LPCWSTR BURN_COMMANDLINE_SWITCH_PREFIX = L"burn."; - -const LPCWSTR BURN_BUNDLE_LAYOUT_DIRECTORY = L"WixBundleLayoutDirectory"; -const LPCWSTR BURN_BUNDLE_ACTION = L"WixBundleAction"; -const LPCWSTR BURN_BUNDLE_ACTIVE_PARENT = L"WixBundleActiveParent"; -const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER = L"WixBundleExecutePackageCacheFolder"; -const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_ACTION = L"WixBundleExecutePackageAction"; -const LPCWSTR BURN_BUNDLE_FORCED_RESTART_PACKAGE = L"WixBundleForcedRestartPackage"; -const LPCWSTR BURN_BUNDLE_INSTALLED = L"WixBundleInstalled"; -const LPCWSTR BURN_BUNDLE_ELEVATED = L"WixBundleElevated"; -const LPCWSTR BURN_BUNDLE_PROVIDER_KEY = L"WixBundleProviderKey"; -const LPCWSTR BURN_BUNDLE_MANUFACTURER = L"WixBundleManufacturer"; -const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_PATH = L"WixBundleSourceProcessPath"; -const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_FOLDER = L"WixBundleSourceProcessFolder"; -const LPCWSTR BURN_BUNDLE_TAG = L"WixBundleTag"; -const LPCWSTR BURN_BUNDLE_UILEVEL = L"WixBundleUILevel"; -const LPCWSTR BURN_BUNDLE_VERSION = L"WixBundleVersion"; -const LPCWSTR BURN_REBOOT_PENDING = L"RebootPending"; - -// The following constants must stay in sync with src\wix\Binder.cs -const LPCWSTR BURN_BUNDLE_NAME = L"WixBundleName"; -const LPCWSTR BURN_BUNDLE_ORIGINAL_SOURCE = L"WixBundleOriginalSource"; -const LPCWSTR BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER = L"WixBundleOriginalSourceFolder"; -const LPCWSTR BURN_BUNDLE_LAST_USED_SOURCE = L"WixBundleLastUsedSource"; - - -// enums - -enum BURN_MODE -{ - BURN_MODE_UNTRUSTED, - BURN_MODE_NORMAL, - BURN_MODE_ELEVATED, - BURN_MODE_EMBEDDED, - BURN_MODE_RUNONCE, -}; - -enum BURN_AU_PAUSE_ACTION -{ - BURN_AU_PAUSE_ACTION_NONE, - BURN_AU_PAUSE_ACTION_IFELEVATED, - BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME, -}; - - -// structs - -typedef struct _BURN_ENGINE_STATE -{ - // UX flow control - BOOL fDetected; - BOOL fPlanned; - BOOL fQuit; - //BOOL fSuspend; // Is TRUE when UX made Suspend() call on core. - //BOOL fForcedReboot; // Is TRUE when UX made Reboot() call on core. - //BOOL fCancelled; // Is TRUE when UX return cancel on UX OnXXX() methods. - //BOOL fReboot; // Is TRUE when UX confirms OnRestartRequried(). - BOOL fRestart; // Set TRUE when UX returns IDRESTART during Apply(). - - // engine data - BOOTSTRAPPER_COMMAND command; - BURN_SECTION section; - BURN_VARIABLES variables; - BURN_CONDITION condition; - BURN_SEARCHES searches; - BURN_USER_EXPERIENCE userExperience; - BURN_REGISTRATION registration; - BURN_CONTAINERS containers; - BURN_PAYLOADS payloads; - BURN_PACKAGES packages; - BURN_UPDATE update; - BURN_APPROVED_EXES approvedExes; - BURN_EXTENSIONS extensions; - - HWND hMessageWindow; - HANDLE hMessageWindowThread; - - BOOL fDisableRollback; - BOOL fDisableSystemRestore; - BOOL fParallelCacheAndExecute; - - BURN_LOGGING log; - - BURN_PAYLOAD_GROUP layoutPayloads; - - BURN_PLAN plan; - - BURN_MODE mode; - BURN_AU_PAUSE_ACTION automaticUpdates; - - DWORD dwElevatedLoggingTlsId; - - LPWSTR sczBundleEngineWorkingPath; - BURN_PIPE_CONNECTION companionConnection; - BURN_PIPE_CONNECTION embeddedConnection; - - BURN_RESUME_MODE resumeMode; - BOOL fDisableUnelevate; - - LPWSTR sczIgnoreDependencies; - - int argc; - LPWSTR* argv; -} BURN_ENGINE_STATE; - - -// function declarations - -HRESULT CoreInitialize( - __in BURN_ENGINE_STATE* pEngineState - ); -HRESULT CoreInitializeConstants( - __in BURN_ENGINE_STATE* pEngineState - ); -HRESULT CoreSerializeEngineState( - __in BURN_ENGINE_STATE* pEngineState, - __inout BYTE** ppbBuffer, - __inout SIZE_T* piBuffer - ); -HRESULT CoreQueryRegistration( - __in BURN_ENGINE_STATE* pEngineState - ); -//HRESULT CoreDeserializeEngineState( -// __in BURN_ENGINE_STATE* pEngineState, -// __in_bcount(cbBuffer) BYTE* pbBuffer, -// __in SIZE_T cbBuffer -// ); -HRESULT CoreDetect( - __in BURN_ENGINE_STATE* pEngineState, - __in_opt HWND hwndParent - ); -HRESULT CorePlan( - __in BURN_ENGINE_STATE* pEngineState, - __in BOOTSTRAPPER_ACTION action - ); -HRESULT CoreElevate( - __in BURN_ENGINE_STATE* pEngineState, - __in_opt HWND hwndParent - ); -HRESULT CoreApply( - __in BURN_ENGINE_STATE* pEngineState, - __in_opt HWND hwndParent - ); -HRESULT CoreLaunchApprovedExe( - __in BURN_ENGINE_STATE* pEngineState, - __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe - ); -HRESULT CoreQuit( - __in BURN_ENGINE_STATE* pEngineState, - __in int nExitCode - ); -HRESULT CoreSaveEngineState( - __in BURN_ENGINE_STATE* pEngineState - ); -LPCWSTR CoreRelationTypeToCommandLineString( - __in BOOTSTRAPPER_RELATION_TYPE relationType - ); -HRESULT CoreRecreateCommandLine( - __deref_inout_z LPWSTR* psczCommandLine, - __in BOOTSTRAPPER_ACTION action, - __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_RESTART restart, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in BOOL fPassthrough, - __in_z_opt LPCWSTR wzActiveParent, - __in_z_opt LPCWSTR wzAncestors, - __in_z_opt LPCWSTR wzAppendLogPath, - __in_z_opt LPCWSTR wzAdditionalCommandLineArguments - ); -HRESULT CoreAppendFileHandleAttachedToCommandLine( - __in HANDLE hFileWithAttachedContainer, - __out HANDLE* phExecutableFile, - __deref_inout_z LPWSTR* psczCommandLine - ); -HRESULT CoreAppendFileHandleSelfToCommandLine( - __in LPCWSTR wzExecutablePath, - __out HANDLE* phExecutableFile, - __deref_inout_z LPWSTR* psczCommandLine, - __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine - ); -void CoreCleanup( - __in BURN_ENGINE_STATE* pEngineState - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/dependency.cpp b/src/engine/dependency.cpp deleted file mode 100644 index 876cd8b3..00000000 --- a/src/engine/dependency.cpp +++ /dev/null @@ -1,1312 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// constants - -#define INITIAL_STRINGDICT_SIZE 48 -const LPCWSTR vcszIgnoreDependenciesDelim = L";"; - - -// internal function declarations - -static HRESULT DetectPackageDependents( - __in BURN_PACKAGE* pPackage, - __in STRINGDICT_HANDLE sdIgnoredDependents, - __in const BURN_REGISTRATION* pRegistration - ); - -static HRESULT SplitIgnoreDependencies( - __in_z LPCWSTR wzIgnoreDependencies, - __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, - __inout LPUINT pcDependencies, - __out BOOL* pfIgnoreAll - ); - -static HRESULT JoinIgnoreDependencies( - __out_z LPWSTR* psczIgnoreDependencies, - __in_ecount(cDependencies) const DEPENDENCY* rgDependencies, - __in UINT cDependencies - ); - -static HRESULT GetIgnoredDependents( - __in const BURN_PACKAGE* pPackage, - __in const BURN_PLAN* pPlan, - __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents - ); - -static BOOL GetProviderExists( - __in HKEY hkRoot, - __in_z LPCWSTR wzProviderKey - ); - -static void CalculateDependencyActionStates( - __in const BURN_PACKAGE* pPackage, - __in const BOOTSTRAPPER_ACTION action, - __out BURN_DEPENDENCY_ACTION* pDependencyExecuteAction, - __out BURN_DEPENDENCY_ACTION* pDependencyRollbackAction - ); - -static HRESULT AddPackageDependencyActions( - __in_opt DWORD *pdwInsertSequence, - __in const BURN_PACKAGE* pPackage, - __in BURN_PLAN* pPlan, - __in const BURN_DEPENDENCY_ACTION dependencyExecuteAction, - __in const BURN_DEPENDENCY_ACTION dependencyRollbackAction - ); - -static HRESULT RegisterPackageProvider( - __in const BURN_PACKAGE* pPackage - ); - -static void UnregisterPackageProvider( - __in const BURN_PACKAGE* pPackage - ); - -static HRESULT RegisterPackageDependency( - __in BOOL fPerMachine, - __in const BURN_PACKAGE* pPackage, - __in_z LPCWSTR wzDependentProviderKey - ); - -static void UnregisterPackageDependency( - __in BOOL fPerMachine, - __in const BURN_PACKAGE* pPackage, - __in_z LPCWSTR wzDependentProviderKey - ); - - -// functions - -extern "C" void DependencyUninitializeProvider( - __in BURN_DEPENDENCY_PROVIDER* pProvider - ) -{ - ReleaseStr(pProvider->sczKey); - ReleaseStr(pProvider->sczVersion); - ReleaseStr(pProvider->sczDisplayName); - ReleaseDependencyArray(pProvider->rgDependents, pProvider->cDependents); - - memset(pProvider, 0, sizeof(BURN_DEPENDENCY_PROVIDER)); -} - -extern "C" HRESULT DependencyParseProvidersFromXml( - __in BURN_PACKAGE* pPackage, - __in IXMLDOMNode* pixnPackage - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnNodes = NULL; - DWORD cNodes = 0; - IXMLDOMNode* pixnNode = NULL; - - // Select dependency provider nodes. - hr = XmlSelectNodes(pixnPackage, L"Provides", &pixnNodes); - ExitOnFailure(hr, "Failed to select dependency provider nodes."); - - // Get dependency provider node count. - hr = pixnNodes->get_length((long*)&cNodes); - ExitOnFailure(hr, "Failed to get the dependency provider node count."); - - if (!cNodes) - { - ExitFunction1(hr = S_OK); - } - - // Allocate memory for dependency provider pointers. - pPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER) * cNodes, TRUE); - ExitOnNull(pPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers."); - - pPackage->cDependencyProviders = cNodes; - - // Parse dependency provider elements. - for (DWORD i = 0; i < cNodes; i++) - { - BURN_DEPENDENCY_PROVIDER* pDependencyProvider = &pPackage->rgDependencyProviders[i]; - - hr = XmlNextElement(pixnNodes, &pixnNode, NULL); - ExitOnFailure(hr, "Failed to get the next dependency provider node."); - - // @Key - hr = XmlGetAttributeEx(pixnNode, L"Key", &pDependencyProvider->sczKey); - ExitOnFailure(hr, "Failed to get the Key attribute."); - - // @Version - hr = XmlGetAttributeEx(pixnNode, L"Version", &pDependencyProvider->sczVersion); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get the Version attribute."); - } - - // @DisplayName - hr = XmlGetAttributeEx(pixnNode, L"DisplayName", &pDependencyProvider->sczDisplayName); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get the DisplayName attribute."); - } - - // @Imported - hr = XmlGetYesNoAttribute(pixnNode, L"Imported", &pDependencyProvider->fImported); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get the Imported attribute."); - } - else - { - pDependencyProvider->fImported = FALSE; - hr = S_OK; - } - - // Prepare next iteration. - ReleaseNullObject(pixnNode); - } - - hr = S_OK; - -LExit: - ReleaseObject(pixnNode); - ReleaseObject(pixnNodes); - - return hr; -} - -extern "C" HRESULT DependencyInitialize( - __in BURN_REGISTRATION* pRegistration, - __in_z_opt LPCWSTR wzIgnoreDependencies - ) -{ - HRESULT hr = S_OK; - - // If no parent was specified at all, use the bundle id as the self dependent. - if (!pRegistration->sczActiveParent) - { - pRegistration->wzSelfDependent = pRegistration->sczId; - } - else if (*pRegistration->sczActiveParent) // if parent was specified use that as the self dependent. - { - pRegistration->wzSelfDependent = pRegistration->sczActiveParent; - } - // else parent:none was used which means we should not register a dependency on ourself. - - // The current bundle provider key should always be ignored for dependency checks. - hr = DepDependencyArrayAlloc(&pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies, pRegistration->sczProviderKey, NULL); - ExitOnFailure(hr, "Failed to add the bundle provider key to the list of dependencies to ignore."); - - // Add the list of dependencies to ignore. - if (wzIgnoreDependencies) - { - hr = SplitIgnoreDependencies(wzIgnoreDependencies, &pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies, &pRegistration->fIgnoreAllDependents); - ExitOnFailure(hr, "Failed to split the list of dependencies to ignore."); - } - -LExit: - return hr; -} - -extern "C" HRESULT DependencyDetectProviderKeyBundleId( - __in BURN_REGISTRATION* pRegistration - ) -{ - HRESULT hr = S_OK; - - hr = DepGetProviderInformation(pRegistration->hkRoot, pRegistration->sczProviderKey, &pRegistration->sczDetectedProviderKeyBundleId, NULL, NULL); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get provider key bundle id."); - - // If a bundle id was not explicitly set, default the provider key bundle id to this bundle's provider key. - if (!pRegistration->sczDetectedProviderKeyBundleId || !*pRegistration->sczDetectedProviderKeyBundleId) - { - hr = StrAllocString(&pRegistration->sczDetectedProviderKeyBundleId, pRegistration->sczProviderKey, 0); - ExitOnFailure(hr, "Failed to initialize provider key bundle id."); - } - -LExit: - return hr; -} - -extern "C" HRESULT DependencyDetect( - __in BURN_ENGINE_STATE* pEngineState - ) -{ - HRESULT hr = S_OK; - BURN_REGISTRATION* pRegistration = &pEngineState->registration; - STRINGDICT_HANDLE sdIgnoredDependents = NULL; - BURN_PACKAGE* pPackage = NULL; - BOOL fSelfDependent = NULL != pRegistration->wzSelfDependent; - BOOL fActiveParent = NULL != pRegistration->sczActiveParent && NULL != *pRegistration->sczActiveParent; - - // Always leave this empty so that all dependents get detected. Plan will ignore dependents based on its own logic. - hr = DictCreateStringList(&sdIgnoredDependents, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); - ExitOnFailure(hr, "Failed to create the string dictionary."); - - hr = DepCheckDependents(pRegistration->hkRoot, pRegistration->sczProviderKey, 0, sdIgnoredDependents, &pRegistration->rgDependents, &pRegistration->cDependents); - if (E_FILENOTFOUND != hr) - { - ExitOnFailure(hr, "Failed dependents check on bundle"); - } - else - { - hr = S_OK; - } - - for (DWORD iPackage = 0; iPackage < pEngineState->packages.cPackages; ++iPackage) - { - pPackage = pEngineState->packages.rgPackages + iPackage; - hr = DetectPackageDependents(pPackage, sdIgnoredDependents, pRegistration); - ExitOnFailure(hr, "Failed to detect dependents for package '%ls'", pPackage->sczId); - } - - for (DWORD iRelatedBundle = 0; iRelatedBundle < pEngineState->registration.relatedBundles.cRelatedBundles; ++iRelatedBundle) - { - BURN_RELATED_BUNDLE* pRelatedBundle = pEngineState->registration.relatedBundles.rgRelatedBundles + iRelatedBundle; - if (!pRelatedBundle->fPlannable) - { - continue; - } - - pPackage = &pRelatedBundle->package; - hr = DetectPackageDependents(pPackage, sdIgnoredDependents, pRegistration); - ExitOnFailure(hr, "Failed to detect dependents for related bundle '%ls'", pPackage->sczId); - } - - if (fSelfDependent || fActiveParent) - { - for (DWORD i = 0; i < pRegistration->cDependents; ++i) - { - DEPENDENCY* pDependent = pRegistration->rgDependents + i; - - if (fActiveParent && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczActiveParent, -1, pDependent->sczKey, -1)) - { - pRegistration->fParentRegisteredAsDependent = TRUE; - } - - if (fSelfDependent && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->wzSelfDependent, -1, pDependent->sczKey, -1)) - { - pRegistration->fSelfRegisteredAsDependent = TRUE; - } - } - } - -LExit: - ReleaseDict(sdIgnoredDependents); - - return hr; -} - -extern "C" HRESULT DependencyPlanInitialize( - __in const BURN_REGISTRATION* pRegistration, - __in BURN_PLAN* pPlan - ) -{ - HRESULT hr = S_OK; - - // TODO: After adding enumeration to STRINGDICT, a single STRINGDICT_HANDLE can be used everywhere. - for (DWORD i = 0; i < pRegistration->cIgnoredDependencies; ++i) - { - DEPENDENCY* pDependency = pRegistration->rgIgnoredDependencies + i; - - hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pDependency->sczKey, pDependency->sczName); - ExitOnFailure(hr, "Failed to add the detected provider to the list of dependencies to ignore."); - } - -LExit: - return hr; -} - -extern "C" HRESULT DependencyAllocIgnoreDependencies( - __in const BURN_PLAN *pPlan, - __out_z LPWSTR* psczIgnoreDependencies - ) -{ - HRESULT hr = S_OK; - - // Join the list of dependencies to ignore for each related bundle. - if (0 < pPlan->cPlannedProviders) - { - hr = JoinIgnoreDependencies(psczIgnoreDependencies, pPlan->rgPlannedProviders, pPlan->cPlannedProviders); - ExitOnFailure(hr, "Failed to join the list of dependencies to ignore."); - } - -LExit: - return hr; -} - -extern "C" HRESULT DependencyAddIgnoreDependencies( - __in STRINGDICT_HANDLE sdIgnoreDependencies, - __in_z LPCWSTR wzAddIgnoreDependencies - ) -{ - HRESULT hr = S_OK; - LPWSTR wzContext = NULL; - - // Parse through the semicolon-delimited tokens and add to the array. - for (LPCWSTR wzToken = ::wcstok_s(const_cast(wzAddIgnoreDependencies), vcszIgnoreDependenciesDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, vcszIgnoreDependenciesDelim, &wzContext)) - { - hr = DictKeyExists(sdIgnoreDependencies, wzToken); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies."); - } - else - { - hr = DictAddKey(sdIgnoreDependencies, wzToken); - ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzToken); - } - } - -LExit: - return hr; -} - -extern "C" HRESULT DependencyPlanPackageBegin( - __in BOOL fPerMachine, - __in BURN_PACKAGE* pPackage, - __in BURN_PLAN* pPlan - ) -{ - HRESULT hr = S_OK; - STRINGDICT_HANDLE sdIgnoredDependents = NULL; - BURN_DEPENDENCY_ACTION dependencyExecuteAction = BURN_DEPENDENCY_ACTION_NONE; - BURN_DEPENDENCY_ACTION dependencyRollbackAction = BURN_DEPENDENCY_ACTION_NONE; - - pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE; - pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE; - - // Make sure the package defines at least one provider. - if (0 == pPackage->cDependencyProviders) - { - LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_SKIP_NOPROVIDERS, pPackage->sczId); - ExitFunction1(hr = S_OK); - } - - // Make sure the package is in the same scope as the bundle. - if (fPerMachine != pPackage->fPerMachine) - { - LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine)); - ExitFunction1(hr = S_OK); - } - - // If we're uninstalling the package, check if any dependents are registered. - if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) - { - // Build up a list of dependents to ignore, including the current bundle. - hr = GetIgnoredDependents(pPackage, pPlan, &sdIgnoredDependents); - ExitOnFailure(hr, "Failed to build the list of ignored dependents."); - - // Skip the dependency check if "ALL" was authored for IGNOREDEPENDENCIES. - hr = DictKeyExists(sdIgnoredDependents, L"ALL"); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to check if \"ALL\" was set in IGNOREDEPENDENCIES."); - } - else - { - hr = S_OK; - - for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) - { - const BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + i; - - for (DWORD j = 0; j < pProvider->cDependents; ++j) - { - const DEPENDENCY* pDependency = pProvider->rgDependents + j; - - hr = DictKeyExists(sdIgnoredDependents, pDependency->sczKey); - if (E_NOTFOUND == hr) - { - hr = S_OK; - - if (!pPackage->fDependencyManagerWasHere) - { - pPackage->fDependencyManagerWasHere = TRUE; - - LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS, pPackage->sczId); - } - - LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_DEPENDENT, pDependency->sczKey, LoggingStringOrUnknownIfNull(pDependency->sczName)); - } - ExitOnFailure(hr, "Failed to check the dictionary of ignored dependents."); - } - } - } - } - - // Calculate the dependency actions before the package itself is planned. - CalculateDependencyActionStates(pPackage, pPlan->action, &dependencyExecuteAction, &dependencyRollbackAction); - - // If dependents were found, change the action to not uninstall the package. - if (pPackage->fDependencyManagerWasHere) - { - pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; - pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - } - else - { - // Use the calculated dependency actions as the provider actions if there - // are any non-imported providers that need to be registered and the package - // is current (not obsolete). - if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE != pPackage->currentState) - { - BOOL fAllImportedProviders = TRUE; // assume all providers were imported. - for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) - { - const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; - if (!pProvider->fImported) - { - fAllImportedProviders = FALSE; - break; - } - } - - if (!fAllImportedProviders) - { - pPackage->providerExecute = dependencyExecuteAction; - pPackage->providerRollback = dependencyRollbackAction; - } - } - - // If the package will be removed, add its providers to the growing list in the plan. - if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) - { - for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) - { - const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; - - hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, NULL); - ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey); - } - } - } - - pPackage->dependencyExecute = dependencyExecuteAction; - pPackage->dependencyRollback = dependencyRollbackAction; - -LExit: - ReleaseDict(sdIgnoredDependents); - - return hr; -} - -extern "C" HRESULT DependencyPlanPackage( - __in_opt DWORD *pdwInsertSequence, - __in const BURN_PACKAGE* pPackage, - __in BURN_PLAN* pPlan - ) -{ - HRESULT hr = S_OK; - BURN_EXECUTE_ACTION* pAction = NULL; - - // If the dependency execution action is to unregister, add the dependency actions to the plan - // *before* the provider key is potentially removed. - if (BURN_DEPENDENCY_ACTION_UNREGISTER == pPackage->dependencyExecute) - { - hr = AddPackageDependencyActions(pdwInsertSequence, pPackage, pPlan, pPackage->dependencyExecute, pPackage->dependencyRollback); - ExitOnFailure(hr, "Failed to plan the dependency actions for package: %ls", pPackage->sczId); - } - - // Add the provider rollback plan. - if (BURN_DEPENDENCY_ACTION_NONE != pPackage->providerRollback) - { - hr = PlanAppendRollbackAction(pPlan, &pAction); - ExitOnFailure(hr, "Failed to append provider rollback action."); - - pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER; - pAction->packageProvider.pPackage = const_cast(pPackage); - pAction->packageProvider.action = pPackage->providerRollback; - - // Put a checkpoint before the execute action so that rollback happens - // if execute fails. - hr = PlanExecuteCheckpoint(pPlan); - ExitOnFailure(hr, "Failed to plan provider checkpoint action."); - } - - // Add the provider execute plan. This comes after rollback so if something goes wrong - // rollback will try to clean up after us. - if (BURN_DEPENDENCY_ACTION_NONE != pPackage->providerExecute) - { - if (NULL != pdwInsertSequence) - { - hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction); - ExitOnFailure(hr, "Failed to insert provider execute action."); - - // Always move the sequence after this dependency action so the provider registration - // stays in front of the inserted actions. - ++(*pdwInsertSequence); - } - else - { - hr = PlanAppendExecuteAction(pPlan, &pAction); - ExitOnFailure(hr, "Failed to append provider execute action."); - } - - pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER; - pAction->packageProvider.pPackage = const_cast(pPackage); - pAction->packageProvider.action = pPackage->providerExecute; - } - -LExit: - return hr; -} - -extern "C" HRESULT DependencyPlanPackageComplete( - __in BURN_PACKAGE* pPackage, - __in BURN_PLAN* pPlan - ) -{ - HRESULT hr = S_OK; - - // Registration of dependencies happens here, after the package is planned to be - // installed and all that good stuff. - if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute) - { - // Recalculate the dependency actions in case other operations may have changed - // the package execution state. - CalculateDependencyActionStates(pPackage, pPlan->action, &pPackage->dependencyExecute, &pPackage->dependencyRollback); - - // If the dependency execution action is *still* to register, add the dependency actions to the plan. - if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute) - { - hr = AddPackageDependencyActions(NULL, pPackage, pPlan, pPackage->dependencyExecute, pPackage->dependencyRollback); - ExitOnFailure(hr, "Failed to plan the dependency actions for package: %ls", pPackage->sczId); - } - } - -LExit: - return hr; -} - -extern "C" HRESULT DependencyExecutePackageProviderAction( - __in const BURN_EXECUTE_ACTION* pAction - ) -{ - AssertSz(BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER == pAction->type, "Execute action type not supported by this function."); - - HRESULT hr = S_OK; - const BURN_PACKAGE* pPackage = pAction->packageProvider.pPackage; - - // Register or unregister the package provider(s). - if (BURN_DEPENDENCY_ACTION_REGISTER == pAction->packageProvider.action) - { - hr = RegisterPackageProvider(pPackage); - ExitOnFailure(hr, "Failed to register the package providers."); - } - else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pAction->packageProvider.action) - { - UnregisterPackageProvider(pPackage); - } - -LExit: - if (!pPackage->fVital) - { - hr = S_OK; - } - - return hr; -} - -extern "C" HRESULT DependencyExecutePackageDependencyAction( - __in BOOL fPerMachine, - __in const BURN_EXECUTE_ACTION* pAction - ) -{ - AssertSz(BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY == pAction->type, "Execute action type not supported by this function."); - - HRESULT hr = S_OK; - const BURN_PACKAGE* pPackage = pAction->packageDependency.pPackage; - - // Register or unregister the bundle as a dependent of each package dependency provider. - if (BURN_DEPENDENCY_ACTION_REGISTER == pAction->packageDependency.action) - { - hr = RegisterPackageDependency(fPerMachine, pPackage, pAction->packageDependency.sczBundleProviderKey); - ExitOnFailure(hr, "Failed to register the dependency on the package provider."); - } - else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pAction->packageDependency.action) - { - UnregisterPackageDependency(fPerMachine, pPackage, pAction->packageDependency.sczBundleProviderKey); - } - -LExit: - if (!pPackage->fVital) - { - hr = S_OK; - } - - return hr; -} - -extern "C" HRESULT DependencyRegisterBundle( - __in const BURN_REGISTRATION* pRegistration - ) -{ - HRESULT hr = S_OK; - - LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_REGISTER, pRegistration->sczProviderKey, pRegistration->pVersion->sczVersion); - - // Register the bundle provider key. - hr = DepRegisterDependency(pRegistration->hkRoot, pRegistration->sczProviderKey, pRegistration->pVersion->sczVersion, pRegistration->sczDisplayName, pRegistration->sczId, 0); - ExitOnFailure(hr, "Failed to register the bundle dependency provider."); - -LExit: - return hr; -} - -extern "C" HRESULT DependencyProcessDependentRegistration( - __in const BURN_REGISTRATION* pRegistration, - __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction - ) -{ - HRESULT hr = S_OK; - - switch (pAction->type) - { - case BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER: - hr = DepRegisterDependent(pRegistration->hkRoot, pRegistration->sczProviderKey, pAction->sczDependentProviderKey, NULL, NULL, 0); - ExitOnFailure(hr, "Failed to register dependent: %ls", pAction->sczDependentProviderKey); - break; - - case BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER: - hr = DepUnregisterDependent(pRegistration->hkRoot, pRegistration->sczProviderKey, pAction->sczDependentProviderKey); - ExitOnFailure(hr, "Failed to unregister dependent: %ls", pAction->sczDependentProviderKey); - break; - - default: - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Unrecognized registration action type: %d", pAction->type); - } - -LExit: - return hr; -} - -extern "C" void DependencyUnregisterBundle( - __in const BURN_REGISTRATION* pRegistration, - __in const BURN_PACKAGES* pPackages - ) -{ - HRESULT hr = S_OK; - LPCWSTR wzDependentProviderKey = pRegistration->sczId; - - // Remove the bundle provider key. - hr = DepUnregisterDependency(pRegistration->hkRoot, pRegistration->sczProviderKey); - if (SUCCEEDED(hr)) - { - LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_UNREGISTERED, pRegistration->sczProviderKey); - } - else if (FAILED(hr) && E_FILENOTFOUND != hr) - { - LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_UNREGISTERED_FAILED, pRegistration->sczProviderKey, hr); - } - - // Best effort to make sure this bundle is not registered as a dependent for anything. - for (DWORD i = 0; i < pPackages->cPackages; ++i) - { - const BURN_PACKAGE* pPackage = pPackages->rgPackages + i; - UnregisterPackageDependency(pPackage->fPerMachine, pPackage, wzDependentProviderKey); - } - - for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) - { - const BURN_PACKAGE* pPackage = &pRegistration->relatedBundles.rgRelatedBundles[i].package; - UnregisterPackageDependency(pPackage->fPerMachine, pPackage, wzDependentProviderKey); - } -} - -// internal functions - - -static HRESULT DetectPackageDependents( - __in BURN_PACKAGE* pPackage, - __in STRINGDICT_HANDLE sdIgnoredDependents, - __in const BURN_REGISTRATION* pRegistration - ) -{ - HRESULT hr = S_OK; - HKEY hkHive = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - BOOL fCanIgnorePresence = pPackage->fCanAffectRegistration && 0 < pPackage->cDependencyProviders && - (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState || BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState); - BOOL fBundleRegisteredAsDependent = FALSE; - - // There's currently no point in getting the dependents if the scope doesn't match, - // because they will just get ignored. - if (pRegistration->fPerMachine != pPackage->fPerMachine) - { - ExitFunction(); - } - - for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) - { - BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; - - hr = DepCheckDependents(hkHive, pProvider->sczKey, 0, sdIgnoredDependents, &pProvider->rgDependents, &pProvider->cDependents); - if (E_FILENOTFOUND != hr) - { - ExitOnFailure(hr, "Failed dependents check on package provider: %ls", pProvider->sczKey); - - if (!pPackage->fPackageProviderExists && (0 < pProvider->cDependents || GetProviderExists(hkHive, pProvider->sczKey))) - { - pPackage->fPackageProviderExists = TRUE; - } - - if (fCanIgnorePresence && !fBundleRegisteredAsDependent) - { - for (DWORD iDependent = 0; iDependent < pProvider->cDependents; ++iDependent) - { - DEPENDENCY* pDependent = pProvider->rgDependents + iDependent; - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczId, -1, pDependent->sczKey, -1)) - { - fBundleRegisteredAsDependent = TRUE; - break; - } - } - } - } - else - { - hr = S_OK; - - if (!pPackage->fPackageProviderExists && GetProviderExists(hkHive, pProvider->sczKey)) - { - pPackage->fPackageProviderExists = TRUE; - } - } - } - - // Older bundles may not have written the id so try the default. - if (!pPackage->fPackageProviderExists && BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.sczProductCode && GetProviderExists(hkHive, pPackage->Msi.sczProductCode)) - { - pPackage->fPackageProviderExists = TRUE; - } - - if (fCanIgnorePresence && !fBundleRegisteredAsDependent) - { - if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState) - { - pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; - } - if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState) - { - pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; - } - if (BURN_PACKAGE_TYPE_MSP == pPackage->type) - { - for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) - { - BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; - - if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pTargetProduct->registrationState) - { - pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; - } - } - } - } - -LExit: - return hr; -} - -/******************************************************************** - SplitIgnoreDependencies - Splits a semicolon-delimited - string into a list of unique dependencies to ignore. - -*********************************************************************/ -static HRESULT SplitIgnoreDependencies( - __in_z LPCWSTR wzIgnoreDependencies, - __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, - __inout LPUINT pcDependencies, - __out BOOL* pfIgnoreAll - ) -{ - HRESULT hr = S_OK; - LPWSTR wzContext = NULL; - STRINGDICT_HANDLE sdIgnoreDependencies = NULL; - *pfIgnoreAll = FALSE; - - // Create a dictionary to hold unique dependencies. - hr = DictCreateStringList(&sdIgnoreDependencies, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); - ExitOnFailure(hr, "Failed to create the string dictionary."); - - // Parse through the semicolon-delimited tokens and add to the array. - for (LPCWSTR wzToken = ::wcstok_s(const_cast(wzIgnoreDependencies), vcszIgnoreDependenciesDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, vcszIgnoreDependenciesDelim, &wzContext)) - { - hr = DictKeyExists(sdIgnoreDependencies, wzToken); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies."); - } - else - { - hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzToken, NULL); - ExitOnFailure(hr, "Failed to add \"%ls\" to the list of dependencies to ignore.", wzToken); - - hr = DictAddKey(sdIgnoreDependencies, wzToken); - ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzToken); - - if (!*pfIgnoreAll && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, L"ALL", -1, wzToken, -1)) - { - *pfIgnoreAll = TRUE; - } - } - } - -LExit: - ReleaseDict(sdIgnoreDependencies); - - return hr; -} - -/******************************************************************** - JoinIgnoreDependencies - Joins a list of dependencies - to ignore into a semicolon-delimited string of unique values. - -*********************************************************************/ -static HRESULT JoinIgnoreDependencies( - __out_z LPWSTR* psczIgnoreDependencies, - __in_ecount(cDependencies) const DEPENDENCY* rgDependencies, - __in UINT cDependencies - ) -{ - HRESULT hr = S_OK; - STRINGDICT_HANDLE sdIgnoreDependencies = NULL; - - // Make sure we pass back an empty string if there are no dependencies. - if (0 == cDependencies) - { - ExitFunction1(hr = S_OK); - } - - // Create a dictionary to hold unique dependencies. - hr = DictCreateStringList(&sdIgnoreDependencies, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); - ExitOnFailure(hr, "Failed to create the string dictionary."); - - for (UINT i = 0; i < cDependencies; ++i) - { - const DEPENDENCY* pDependency = &rgDependencies[i]; - - hr = DictKeyExists(sdIgnoreDependencies, pDependency->sczKey); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies."); - } - else - { - if (0 < i) - { - hr = StrAllocConcat(psczIgnoreDependencies, vcszIgnoreDependenciesDelim, 1); - ExitOnFailure(hr, "Failed to append the string delimiter."); - } - - hr = StrAllocConcat(psczIgnoreDependencies, pDependency->sczKey, 0); - ExitOnFailure(hr, "Failed to append the key \"%ls\".", pDependency->sczKey); - - hr = DictAddKey(sdIgnoreDependencies, pDependency->sczKey); - ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", pDependency->sczKey); - } - } - -LExit: - ReleaseDict(sdIgnoreDependencies); - - return hr; -} - -/******************************************************************** - GetIgnoredDependents - Combines the current bundle's - provider key, packages' provider keys that are being uninstalled, - and any ignored dependencies authored for packages into a string - list to pass to deputil. - -*********************************************************************/ -static HRESULT GetIgnoredDependents( - __in const BURN_PACKAGE* pPackage, - __in const BURN_PLAN* pPlan, - __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents - ) -{ - HRESULT hr = S_OK; - LPWSTR sczIgnoreDependencies = NULL; - - // Create the dictionary and add the bundle provider key initially. - hr = DictCreateStringList(psdIgnoredDependents, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); - ExitOnFailure(hr, "Failed to create the string dictionary."); - - hr = DictAddKey(*psdIgnoredDependents, pPlan->wzBundleProviderKey); - ExitOnFailure(hr, "Failed to add the bundle provider key \"%ls\" to the list of ignored dependencies.", pPlan->wzBundleProviderKey); - - // Add previously planned package providers to the dictionary. - for (DWORD i = 0; i < pPlan->cPlannedProviders; ++i) - { - const DEPENDENCY* pDependency = &pPlan->rgPlannedProviders[i]; - - hr = DictAddKey(*psdIgnoredDependents, pDependency->sczKey); - ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the list of ignored dependencies.", pDependency->sczKey); - } - - // Get the IGNOREDEPENDENCIES property if defined. - hr = PackageGetProperty(pPackage, DEPENDENCY_IGNOREDEPENDENCIES, &sczIgnoreDependencies); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get the package property: %ls", DEPENDENCY_IGNOREDEPENDENCIES); - - hr = DependencyAddIgnoreDependencies(*psdIgnoredDependents, sczIgnoreDependencies); - ExitOnFailure(hr, "Failed to add the authored ignored dependencies to the cumulative list of ignored dependencies."); - } - else - { - hr = S_OK; - } - -LExit: - ReleaseStr(sczIgnoreDependencies); - - return hr; -} - -/******************************************************************** - GetProviderExists - Gets whether the provider key is registered. - -*********************************************************************/ -static BOOL GetProviderExists( - __in HKEY hkRoot, - __in_z LPCWSTR wzProviderKey - ) -{ - HRESULT hr = DepGetProviderInformation(hkRoot, wzProviderKey, NULL, NULL, NULL); - return SUCCEEDED(hr); -} - -/******************************************************************** - CalculateDependencyActionStates - Calculates the dependency execute and - rollback actions for a package. - -*********************************************************************/ -static void CalculateDependencyActionStates( - __in const BURN_PACKAGE* pPackage, - __in const BOOTSTRAPPER_ACTION action, - __out BURN_DEPENDENCY_ACTION* pDependencyExecuteAction, - __out BURN_DEPENDENCY_ACTION* pDependencyRollbackAction - ) -{ - switch (action) - { - case BOOTSTRAPPER_ACTION_UNINSTALL: - // Always remove the dependency when uninstalling a bundle even if the package is absent. - *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_UNREGISTER; - break; - case BOOTSTRAPPER_ACTION_INSTALL: __fallthrough; - case BOOTSTRAPPER_ACTION_CACHE: - // Always remove the dependency during rollback when installing a bundle. - *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_UNREGISTER; - __fallthrough; - case BOOTSTRAPPER_ACTION_MODIFY: __fallthrough; - case BOOTSTRAPPER_ACTION_REPAIR: - switch (pPackage->execute) - { - case BOOTSTRAPPER_ACTION_STATE_NONE: - switch (pPackage->requested) - { - case BOOTSTRAPPER_REQUEST_STATE_NONE: - // Register if a newer, compatible package is already installed. - switch (pPackage->currentState) - { - case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: - if (!pPackage->fPackageProviderExists) - { - break; - } - __fallthrough; - case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: - *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER; - break; - } - break; - case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_MEND: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_REPAIR: - // Register if the package is requested but already installed. - switch (pPackage->currentState) - { - case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: - if (!pPackage->fPackageProviderExists) - { - break; - } - __fallthrough; - case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough; - case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: - *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER; - break; - } - break; - } - break; - case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: - *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_UNREGISTER; - break; - case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough; - case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough; - case BOOTSTRAPPER_ACTION_STATE_MEND: __fallthrough; - case BOOTSTRAPPER_ACTION_STATE_REPAIR: __fallthrough; - case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: __fallthrough; - *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER; - break; - } - break; - } - - switch (*pDependencyExecuteAction) - { - case BURN_DEPENDENCY_ACTION_REGISTER: - switch (pPackage->currentState) - { - case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough; - case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough; - *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_UNREGISTER; - break; - } - break; - case BURN_DEPENDENCY_ACTION_UNREGISTER: - switch (pPackage->currentState) - { - case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough; - case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: - *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_REGISTER; - break; - } - break; - } -} - -/******************************************************************** - AddPackageDependencyActions - Adds the dependency execute and rollback - actions to the plan. - -*********************************************************************/ -static HRESULT AddPackageDependencyActions( - __in_opt DWORD *pdwInsertSequence, - __in const BURN_PACKAGE* pPackage, - __in BURN_PLAN* pPlan, - __in const BURN_DEPENDENCY_ACTION dependencyExecuteAction, - __in const BURN_DEPENDENCY_ACTION dependencyRollbackAction - ) -{ - HRESULT hr = S_OK; - BURN_EXECUTE_ACTION* pAction = NULL; - - // Add the rollback plan. - if (BURN_DEPENDENCY_ACTION_NONE != dependencyRollbackAction) - { - hr = PlanAppendRollbackAction(pPlan, &pAction); - ExitOnFailure(hr, "Failed to append rollback action."); - - pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY; - pAction->packageDependency.pPackage = const_cast(pPackage); - pAction->packageDependency.action = dependencyRollbackAction; - - hr = StrAllocString(&pAction->packageDependency.sczBundleProviderKey, pPlan->wzBundleProviderKey, 0); - ExitOnFailure(hr, "Failed to copy the bundle dependency provider."); - - // Put a checkpoint before the execute action so that rollback happens - // if execute fails. - hr = PlanExecuteCheckpoint(pPlan); - ExitOnFailure(hr, "Failed to plan dependency checkpoint action."); - } - - // Add the execute plan. This comes after rollback so if something goes wrong - // rollback will try to clean up after us correctly. - if (BURN_DEPENDENCY_ACTION_NONE != dependencyExecuteAction) - { - if (NULL != pdwInsertSequence) - { - hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction); - ExitOnFailure(hr, "Failed to insert execute action."); - - // Always move the sequence after this dependency action so the dependency registration - // stays in front of the inserted actions. - ++(*pdwInsertSequence); - } - else - { - hr = PlanAppendExecuteAction(pPlan, &pAction); - ExitOnFailure(hr, "Failed to append execute action."); - } - - pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY; - pAction->packageDependency.pPackage = const_cast(pPackage); - pAction->packageDependency.action = dependencyExecuteAction; - - hr = StrAllocString(&pAction->packageDependency.sczBundleProviderKey, pPlan->wzBundleProviderKey, 0); - ExitOnFailure(hr, "Failed to copy the bundle dependency provider."); - } - -LExit: - return hr; -} - -static HRESULT RegisterPackageProvider( - __in const BURN_PACKAGE* pPackage - ) -{ - HRESULT hr = S_OK; - LPWSTR wzId = NULL; - HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - - if (pPackage->rgDependencyProviders) - { - if (BURN_PACKAGE_TYPE_MSI == pPackage->type) - { - wzId = pPackage->Msi.sczProductCode; - } - else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) - { - wzId = pPackage->Msp.sczPatchCode; - } - - for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) - { - const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; - - if (!pProvider->fImported) - { - LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_REGISTER, pProvider->sczKey, pProvider->sczVersion, pPackage->sczId); - - hr = DepRegisterDependency(hkRoot, pProvider->sczKey, pProvider->sczVersion, pProvider->sczDisplayName, wzId, 0); - ExitOnFailure(hr, "Failed to register the package dependency provider: %ls", pProvider->sczKey); - } - } - } - -LExit: - if (!pPackage->fVital) - { - hr = S_OK; - } - - return hr; -} - -/******************************************************************** - UnregisterPackageProvider - Removes each dependency provider - for the package (if not imported from the package itself). - - Note: Does not check for existing dependents before removing the key. -*********************************************************************/ -static void UnregisterPackageProvider( - __in const BURN_PACKAGE* pPackage - ) -{ - HRESULT hr = S_OK; - HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - - if (pPackage->rgDependencyProviders) - { - for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) - { - const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; - - if (!pProvider->fImported) - { - hr = DepUnregisterDependency(hkRoot, pProvider->sczKey); - if (SUCCEEDED(hr)) - { - LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED, pProvider->sczKey, pPackage->sczId); - } - else if (FAILED(hr) && E_FILENOTFOUND != hr) - { - LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_FAILED, pProvider->sczKey, pPackage->sczId, hr); - } - } - } - } -} - -/******************************************************************** - RegisterPackageDependency - Registers the provider key - as a dependent of a package. - -*********************************************************************/ -static HRESULT RegisterPackageDependency( - __in BOOL fPerMachine, - __in const BURN_PACKAGE* pPackage, - __in_z LPCWSTR wzDependentProviderKey - ) -{ - HRESULT hr = S_OK; - HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - - // Do not register a dependency on a package in a different install context. - if (fPerMachine != pPackage->fPerMachine) - { - LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine)); - ExitFunction1(hr = S_OK); - } - - if (pPackage->rgDependencyProviders) - { - for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) - { - const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; - - LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_REGISTER_DEPENDENCY, wzDependentProviderKey, pProvider->sczKey, pPackage->sczId); - - hr = DepRegisterDependent(hkRoot, pProvider->sczKey, wzDependentProviderKey, NULL, NULL, 0); - if (E_FILENOTFOUND != hr || pPackage->fVital) - { - ExitOnFailure(hr, "Failed to register the dependency on package dependency provider: %ls", pProvider->sczKey); - } - else - { - LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_SKIP_MISSING, pProvider->sczKey, pPackage->sczId); - hr = S_OK; - } - } - } - -LExit: - return hr; -} - -/******************************************************************** - UnregisterPackageDependency - Unregisters the provider key - as a dependent of a package. - -*********************************************************************/ -static void UnregisterPackageDependency( - __in BOOL fPerMachine, - __in const BURN_PACKAGE* pPackage, - __in_z LPCWSTR wzDependentProviderKey - ) -{ - HRESULT hr = S_OK; - HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - - // Should be no registration to remove since we don't write keys across contexts. - if (fPerMachine != pPackage->fPerMachine) - { - LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine)); - return; - } - - // Loop through each package provider and remove the bundle dependency key. - if (pPackage->rgDependencyProviders) - { - for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) - { - const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; - - hr = DepUnregisterDependent(hkRoot, pProvider->sczKey, wzDependentProviderKey); - if (SUCCEEDED(hr)) - { - LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY, wzDependentProviderKey, pProvider->sczKey, pPackage->sczId); - } - else if (FAILED(hr) && E_FILENOTFOUND != hr) - { - LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY_FAILED, wzDependentProviderKey, pProvider->sczKey, pPackage->sczId, hr); - } - } - } -} diff --git a/src/engine/dependency.h b/src/engine/dependency.h deleted file mode 100644 index 06a01a20..00000000 --- a/src/engine/dependency.h +++ /dev/null @@ -1,168 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - -// constants - -const LPCWSTR DEPENDENCY_IGNOREDEPENDENCIES = L"IGNOREDEPENDENCIES"; - - -// function declarations - -/******************************************************************** - DependencyUninitializeProvider - Frees and zeros memory allocated in - the dependency provider. - -*********************************************************************/ -void DependencyUninitializeProvider( - __in BURN_DEPENDENCY_PROVIDER* pProvider - ); - -/******************************************************************** - DependencyParseProvidersFromXml - Parses dependency information - from the manifest for the specified package. - -*********************************************************************/ -HRESULT DependencyParseProvidersFromXml( - __in BURN_PACKAGE* pPackage, - __in IXMLDOMNode* pixnPackage - ); - -HRESULT DependencyInitialize( - __in BURN_REGISTRATION* pRegistration, - __in_z_opt LPCWSTR wzIgnoreDependencies - ); - -/******************************************************************** - DependencyDetectProviderKeyBundleId - Detect if the provider key is - registered and if so what bundle is registered. - - Note: Returns E_NOTFOUND if the provider key is not registered. -*********************************************************************/ -HRESULT DependencyDetectProviderKeyBundleId( - __in BURN_REGISTRATION* pRegistration - ); - -/******************************************************************** - DependencyDetect - Detects dependency information. - -*********************************************************************/ -HRESULT DependencyDetect( - __in BURN_ENGINE_STATE* pEngineState - ); - -/******************************************************************** - DependencyPlanInitialize - Initializes the plan. - -*********************************************************************/ -HRESULT DependencyPlanInitialize( - __in const BURN_REGISTRATION* pRegistration, - __in BURN_PLAN* pPlan - ); - -/******************************************************************** - DependencyAllocIgnoreDependencies - Allocates the dependencies to - ignore as a semicolon-delimited string. - -*********************************************************************/ -HRESULT DependencyAllocIgnoreDependencies( - __in const BURN_PLAN *pPlan, - __out_z LPWSTR* psczIgnoreDependencies - ); - -/******************************************************************** - DependencyAddIgnoreDependencies - Populates the ignore dependency - names. - -*********************************************************************/ -HRESULT DependencyAddIgnoreDependencies( - __in STRINGDICT_HANDLE sdIgnoreDependencies, - __in_z LPCWSTR wzAddIgnoreDependencies - ); - -/******************************************************************** - DependencyPlanPackageBegin - Updates the dependency registration - action depending on the calculated state for the package. - -*********************************************************************/ -HRESULT DependencyPlanPackageBegin( - __in BOOL fPerMachine, - __in BURN_PACKAGE* pPackage, - __in BURN_PLAN* pPlan - ); - -/******************************************************************** - DependencyPlanPackage - adds dependency related actions to the plan - for this package. - -*********************************************************************/ -HRESULT DependencyPlanPackage( - __in_opt DWORD *pdwInsertSequence, - __in const BURN_PACKAGE* pPackage, - __in BURN_PLAN* pPlan - ); - -/******************************************************************** - DependencyPlanPackageComplete - Updates the dependency registration - action depending on the planned action for the package. - -*********************************************************************/ -HRESULT DependencyPlanPackageComplete( - __in BURN_PACKAGE* pPackage, - __in BURN_PLAN* pPlan - ); - -/******************************************************************** - DependencyExecutePackageProviderAction - Registers or unregisters - provider information for the package contained within the action. - -*********************************************************************/ -HRESULT DependencyExecutePackageProviderAction( - __in const BURN_EXECUTE_ACTION* pAction - ); - -/******************************************************************** - DependencyExecutePackageDependencyAction - Registers or unregisters - dependency information for the package contained within the action. - -*********************************************************************/ -HRESULT DependencyExecutePackageDependencyAction( - __in BOOL fPerMachine, - __in const BURN_EXECUTE_ACTION* pAction - ); - -/******************************************************************** - DependencyRegisterBundle - Registers the bundle dependency provider. - -*********************************************************************/ -HRESULT DependencyRegisterBundle( - __in const BURN_REGISTRATION* pRegistration - ); - -/******************************************************************** - DependencyProcessDependentRegistration - Registers or unregisters dependents - on the bundle based on the action. - -*********************************************************************/ -HRESULT DependencyProcessDependentRegistration( - __in const BURN_REGISTRATION* pRegistration, - __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction - ); - -/******************************************************************** - DependencyUnregisterBundle - Removes the bundle dependency provider. - - Note: Does not check for existing dependents before removing the key. -*********************************************************************/ -void DependencyUnregisterBundle( - __in const BURN_REGISTRATION* pRegistration, - __in const BURN_PACKAGES* pPackages - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp deleted file mode 100644 index dc35e747..00000000 --- a/src/engine/detect.cpp +++ /dev/null @@ -1,469 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -typedef struct _DETECT_AUTHENTICATION_REQUIRED_DATA -{ - BURN_USER_EXPERIENCE* pUX; - LPCWSTR wzPackageOrContainerId; -} DETECT_AUTHENTICATION_REQUIRED_DATA; - -// internal function definitions -static HRESULT WINAPI AuthenticationRequired( - __in LPVOID pData, - __in HINTERNET hUrl, - __in long lHttpCode, - __out BOOL* pfRetrySend, - __out BOOL* pfRetry - ); - -static HRESULT DetectAtomFeedUpdate( - __in_z LPCWSTR wzBundleId, - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_UPDATE* pUpdate - ); - -static HRESULT DownloadUpdateFeed( - __in_z LPCWSTR wzBundleId, - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_UPDATE* pUpdate, - __deref_inout_z LPWSTR* psczTempFile - ); - -// function definitions - -extern "C" void DetectReset( - __in BURN_REGISTRATION* pRegistration, - __in BURN_PACKAGES* pPackages - ) -{ - RelatedBundlesUninitialize(&pRegistration->relatedBundles); - ReleaseNullStr(pRegistration->sczDetectedProviderKeyBundleId); - pRegistration->fSelfRegisteredAsDependent = FALSE; - pRegistration->fParentRegisteredAsDependent = FALSE; - pRegistration->fForwardCompatibleBundleExists = FALSE; - pRegistration->fEligibleForCleanup = FALSE; - - if (pRegistration->rgIgnoredDependencies) - { - ReleaseDependencyArray(pRegistration->rgIgnoredDependencies, pRegistration->cIgnoredDependencies); - } - pRegistration->rgIgnoredDependencies = NULL; - pRegistration->cIgnoredDependencies = 0; - - if (pRegistration->rgDependents) - { - ReleaseDependencyArray(pRegistration->rgDependents, pRegistration->cDependents); - } - pRegistration->rgDependents = NULL; - pRegistration->cDependents = 0; - - for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage) - { - BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage; - - pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; - pPackage->fPackageProviderExists = FALSE; - pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; - pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; - - pPackage->fCached = FALSE; - - if (BURN_PACKAGE_TYPE_MSI == pPackage->type) - { - for (DWORD iFeature = 0; iFeature < pPackage->Msi.cFeatures; ++iFeature) - { - BURN_MSIFEATURE* pFeature = pPackage->Msi.rgFeatures + iFeature; - - pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; - } - - for (DWORD iSlipstreamMsp = 0; iSlipstreamMsp < pPackage->Msi.cSlipstreamMspPackages; ++iSlipstreamMsp) - { - BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + iSlipstreamMsp; - - pSlipstreamMsp->dwMsiChainedPatchIndex = BURN_PACKAGE_INVALID_PATCH_INDEX; - } - - ReleaseNullMem(pPackage->Msi.rgChainedPatches); - pPackage->Msi.cChainedPatches = 0; - } - else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) - { - ReleaseNullMem(pPackage->Msp.rgTargetProducts); - pPackage->Msp.cTargetProductCodes = 0; - } - - for (DWORD iProvider = 0; iProvider < pPackage->cDependencyProviders; ++iProvider) - { - BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + iProvider; - - if (pProvider->rgDependents) - { - ReleaseDependencyArray(pProvider->rgDependents, pProvider->cDependents); - } - pProvider->rgDependents = NULL; - pProvider->cDependents = 0; - } - } - - for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo) - { - MSIPATCHSEQUENCEINFOW* pPatchInfo = pPackages->rgPatchInfo + iPatchInfo; - pPatchInfo->dwOrder = 0; - pPatchInfo->uStatus = 0; - } -} - -extern "C" HRESULT DetectForwardCompatibleBundles( - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_REGISTRATION* pRegistration - ) -{ - HRESULT hr = S_OK; - int nCompareResult = 0; - - if (pRegistration->sczDetectedProviderKeyBundleId && - CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRegistration->sczId, -1)) - { - for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) - { - BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle; - - if (BOOTSTRAPPER_RELATION_UPGRADE == pRelatedBundle->relationType && - CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRelatedBundle->package.sczId, -1)) - { - hr = VerCompareParsedVersions(pRegistration->pVersion, pRelatedBundle->pVersion, &nCompareResult); - ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistration->pVersion->sczVersion, pRelatedBundle->pVersion->sczVersion); - - if (nCompareResult <= 0) - { - if (pRelatedBundle->fPlannable) - { - pRelatedBundle->fForwardCompatible = TRUE; - pRegistration->fForwardCompatibleBundleExists = TRUE; - } - - hr = UserExperienceOnDetectForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, !pRelatedBundle->package.fCached); - ExitOnRootFailure(hr, "BA aborted detect forward compatible bundle."); - - LogId(REPORT_STANDARD, MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingBoolToString(pRelatedBundle->package.fCached)); - } - } - } - } - -LExit: - return hr; -} - -extern "C" HRESULT DetectReportRelatedBundles( - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_REGISTRATION* pRegistration, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in BOOTSTRAPPER_ACTION action, - __out BOOL* pfEligibleForCleanup - ) -{ - HRESULT hr = S_OK; - int nCompareResult = 0; - BOOTSTRAPPER_REQUEST_STATE uninstallRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; - *pfEligibleForCleanup = pRegistration->fInstalled || pRegistration->fCached; - - for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) - { - const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle; - BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE; - - switch (pRelatedBundle->relationType) - { - case BOOTSTRAPPER_RELATION_UPGRADE: - if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < action) - { - hr = VerCompareParsedVersions(pRegistration->pVersion, pRelatedBundle->pVersion, &nCompareResult); - ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistration->pVersion->sczVersion, pRelatedBundle->pVersion->sczVersion); - - if (nCompareResult < 0) - { - operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; - } - else - { - operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE; - } - } - break; - - case BOOTSTRAPPER_RELATION_PATCH: __fallthrough; - case BOOTSTRAPPER_RELATION_ADDON: - if (BOOTSTRAPPER_ACTION_UNINSTALL == action) - { - operation = BOOTSTRAPPER_RELATED_OPERATION_REMOVE; - } - else if (BOOTSTRAPPER_ACTION_INSTALL == action || BOOTSTRAPPER_ACTION_MODIFY == action) - { - operation = BOOTSTRAPPER_RELATED_OPERATION_INSTALL; - } - else if (BOOTSTRAPPER_ACTION_REPAIR == action) - { - operation = BOOTSTRAPPER_RELATED_OPERATION_REPAIR; - } - break; - - case BOOTSTRAPPER_RELATION_DETECT: __fallthrough; - case BOOTSTRAPPER_RELATION_DEPENDENT: - break; - - default: - hr = E_FAIL; - ExitOnRootFailure(hr, "Unexpected relation type encountered: %d", pRelatedBundle->relationType); - break; - } - - LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingRelatedOperationToString(operation), LoggingBoolToString(pRelatedBundle->package.fCached)); - - hr = UserExperienceOnDetectRelatedBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, operation, !pRelatedBundle->package.fCached); - ExitOnRootFailure(hr, "BA aborted detect related bundle."); - - // For now, if any related bundles will be executed during uninstall by default then never automatically clean up the bundle. - if (*pfEligibleForCleanup && pRelatedBundle->fPlannable) - { - uninstallRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; - hr = PlanDefaultRelatedBundleRequestState(relationType, pRelatedBundle->relationType, BOOTSTRAPPER_ACTION_UNINSTALL, pRegistration->pVersion, pRelatedBundle->pVersion, &uninstallRequestState); - ExitOnFailure(hr, "Failed to get the default request state for related bundle for calculating fEligibleForCleanup"); - - if (BOOTSTRAPPER_REQUEST_STATE_NONE != uninstallRequestState) - { - *pfEligibleForCleanup = FALSE; - } - } - } - -LExit: - return hr; -} - -extern "C" HRESULT DetectUpdate( - __in_z LPCWSTR wzBundleId, - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_UPDATE* pUpdate - ) -{ - HRESULT hr = S_OK; - BOOL fBeginCalled = FALSE; - BOOL fSkip = TRUE; - BOOL fIgnoreError = FALSE; - LPWSTR sczOriginalSource = NULL; - - // If no update source was specified, skip update detection. - if (!pUpdate->sczUpdateSource || !*pUpdate->sczUpdateSource) - { - ExitFunction(); - } - - fBeginCalled = TRUE; - - hr = StrAllocString(&sczOriginalSource, pUpdate->sczUpdateSource, 0); - ExitOnFailure(hr, "Failed to duplicate update feed source."); - - hr = UserExperienceOnDetectUpdateBegin(pUX, sczOriginalSource, &fSkip); - ExitOnRootFailure(hr, "BA aborted detect update begin."); - - if (!fSkip) - { - hr = DetectAtomFeedUpdate(wzBundleId, pUX, pUpdate); - ExitOnFailure(hr, "Failed to detect atom feed update."); - } - -LExit: - ReleaseStr(sczOriginalSource); - - if (fBeginCalled) - { - UserExperienceOnDetectUpdateComplete(pUX, hr, &fIgnoreError); - if (fIgnoreError) - { - hr = S_OK; - } - } - - return hr; -} - -static HRESULT WINAPI AuthenticationRequired( - __in LPVOID pData, - __in HINTERNET hUrl, - __in long lHttpCode, - __out BOOL* pfRetrySend, - __out BOOL* pfRetry - ) -{ - Assert(401 == lHttpCode || 407 == lHttpCode); - - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - BOOTSTRAPPER_ERROR_TYPE errorType = (401 == lHttpCode) ? BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER : BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY; - LPWSTR sczError = NULL; - DETECT_AUTHENTICATION_REQUIRED_DATA* pAuthenticationData = reinterpret_cast(pData); - int nResult = IDNOACTION; - - *pfRetrySend = FALSE; - *pfRetry = FALSE; - - hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL); - ExitOnFailure(hr, "Failed to allocation error string."); - - UserExperienceOnError(pAuthenticationData->pUX, errorType, pAuthenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value. - nResult = UserExperienceCheckExecuteResult(pAuthenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult); - if (IDTRYAGAIN == nResult && pAuthenticationData->pUX->hwndDetect) - { - er = ::InternetErrorDlg(pAuthenticationData->pUX->hwndDetect, hUrl, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL); - if (ERROR_SUCCESS == er || ERROR_CANCELLED == er) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - else if (ERROR_INTERNET_FORCE_RETRY == er) - { - *pfRetrySend = TRUE; - hr = S_OK; - } - else - { - hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); - } - } - else if (IDRETRY == nResult) - { - *pfRetry = TRUE; - hr = S_OK; - } - else - { - hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); - } - -LExit: - ReleaseStr(sczError); - - return hr; -} - -static HRESULT DownloadUpdateFeed( - __in_z LPCWSTR wzBundleId, - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_UPDATE* pUpdate, - __deref_inout_z LPWSTR* psczTempFile - ) -{ - HRESULT hr = S_OK; - DOWNLOAD_SOURCE downloadSource = { }; - DOWNLOAD_CACHE_CALLBACK cacheCallback = { }; - DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { }; - DETECT_AUTHENTICATION_REQUIRED_DATA authenticationData = { }; - LPWSTR sczUpdateId = NULL; - LPWSTR sczError = NULL; - DWORD64 qwDownloadSize = 0; - - // Always do our work in the working folder, even if cached. - hr = PathCreateTimeBasedTempFile(NULL, L"UpdateFeed", NULL, L"xml", psczTempFile, NULL); - ExitOnFailure(hr, "Failed to create UpdateFeed based on current system time."); - - // Do we need a means of the BA to pass in a user name and password? If so, we should copy it to downloadSource here - hr = StrAllocString(&downloadSource.sczUrl, pUpdate->sczUpdateSource, 0); - ExitOnFailure(hr, "Failed to copy update url."); - - cacheCallback.pfnProgress = NULL; //UpdateProgressRoutine; - cacheCallback.pfnCancel = NULL; // TODO: set this - cacheCallback.pv = NULL; //pProgress; - - authenticationData.pUX = pUX; - authenticationData.wzPackageOrContainerId = wzBundleId; - - authenticationCallback.pv = static_cast(&authenticationData); - authenticationCallback.pfnAuthenticate = &AuthenticationRequired; - - hr = DownloadUrl(&downloadSource, qwDownloadSize, *psczTempFile, &cacheCallback, &authenticationCallback); - ExitOnFailure(hr, "Failed attempt to download update feed from URL: '%ls' to: '%ls'", downloadSource.sczUrl, *psczTempFile); - -LExit: - if (FAILED(hr)) - { - if (*psczTempFile) - { - FileEnsureDelete(*psczTempFile); - } - - ReleaseNullStr(*psczTempFile); - } - - ReleaseStr(downloadSource.sczUrl); - ReleaseStr(downloadSource.sczUser); - ReleaseStr(downloadSource.sczPassword); - ReleaseStr(sczUpdateId); - ReleaseStr(sczError); - return hr; -} - - -static HRESULT DetectAtomFeedUpdate( - __in_z LPCWSTR wzBundleId, - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_UPDATE* pUpdate - ) -{ - Assert(pUpdate && pUpdate->sczUpdateSource && *pUpdate->sczUpdateSource); -#ifdef DEBUG - LogStringLine(REPORT_STANDARD, "DetectAtomFeedUpdate() - update location: %ls", pUpdate->sczUpdateSource); -#endif - - - HRESULT hr = S_OK; - LPWSTR sczUpdateFeedTempFile = NULL; - ATOM_FEED* pAtomFeed = NULL; - APPLICATION_UPDATE_CHAIN* pApupChain = NULL; - BOOL fStopProcessingUpdates = FALSE; - - hr = AtomInitialize(); - ExitOnFailure(hr, "Failed to initialize Atom."); - - hr = DownloadUpdateFeed(wzBundleId, pUX, pUpdate, &sczUpdateFeedTempFile); - ExitOnFailure(hr, "Failed to download update feed."); - - hr = AtomParseFromFile(sczUpdateFeedTempFile, &pAtomFeed); - ExitOnFailure(hr, "Failed to parse update atom feed: %ls.", sczUpdateFeedTempFile); - - hr = ApupAllocChainFromAtom(pAtomFeed, &pApupChain); - ExitOnFailure(hr, "Failed to allocate update chain from atom feed."); - - if (0 < pApupChain->cEntries) - { - for (DWORD i = 0; i < pApupChain->cEntries; ++i) - { - APPLICATION_UPDATE_ENTRY* pAppUpdateEntry = &pApupChain->rgEntries[i]; - - hr = UserExperienceOnDetectUpdate(pUX, pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->wzUrl : NULL, - pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->dw64Size : 0, - pAppUpdateEntry->pVersion, pAppUpdateEntry->wzTitle, - pAppUpdateEntry->wzSummary, pAppUpdateEntry->wzContentType, pAppUpdateEntry->wzContent, &fStopProcessingUpdates); - ExitOnRootFailure(hr, "BA aborted detect update."); - - if (fStopProcessingUpdates) - { - break; - } - } - } - -LExit: - if (sczUpdateFeedTempFile && *sczUpdateFeedTempFile) - { - FileEnsureDelete(sczUpdateFeedTempFile); - } - - ApupFreeChain(pApupChain); - AtomFreeFeed(pAtomFeed); - ReleaseStr(sczUpdateFeedTempFile); - AtomUninitialize(); - - return hr; -} diff --git a/src/engine/detect.h b/src/engine/detect.h deleted file mode 100644 index 9bc34882..00000000 --- a/src/engine/detect.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -// constants - - -// structs - - -// functions - -void DetectReset( - __in BURN_REGISTRATION* pRegistration, - __in BURN_PACKAGES* pPackages - ); - -HRESULT DetectForwardCompatibleBundles( - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_REGISTRATION* pRegistration - ); - -HRESULT DetectReportRelatedBundles( - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_REGISTRATION* pRegistration, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in BOOTSTRAPPER_ACTION action, - __out BOOL* pfEligibleForCleanup - ); - -HRESULT DetectUpdate( - __in_z LPCWSTR wzBundleId, - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_UPDATE* pUpdate - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp deleted file mode 100644 index 9d1b8fc7..00000000 --- a/src/engine/elevation.cpp +++ /dev/null @@ -1,3239 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -const DWORD BURN_TIMEOUT = 5 * 60 * 1000; // TODO: is 5 minutes good? - -typedef enum _BURN_ELEVATION_MESSAGE_TYPE -{ - BURN_ELEVATION_MESSAGE_TYPE_UNKNOWN, - BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE, - BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE, - BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN, - BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME, - BURN_ELEVATION_MESSAGE_TYPE_SESSION_END, - BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE, - BURN_ELEVATION_MESSAGE_TYPE_CACHE_COMPLETE_PAYLOAD, - BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_PAYLOAD, - BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP, - BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION, - BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE, - BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE, - BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE, - BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE, - BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER, - BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY, - BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_EMBEDDED_CHILD, - BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE, - BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE, - BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION, - BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION, - BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION, - - BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_BEGIN, - BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_COMPLETE, - BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_BEGIN, - BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_COMPLETE, - BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_BEGIN, - BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_COMPLETE, - BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_SUCCESS, - BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS, - BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR, - BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE, - BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE, - BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID, - BURN_ELEVATION_MESSAGE_TYPE_PROGRESS_ROUTINE, -} BURN_ELEVATION_MESSAGE_TYPE; - - -// struct - -typedef struct _BURN_ELEVATION_APPLY_INITIALIZE_MESSAGE_CONTEXT -{ - BURN_USER_EXPERIENCE* pBA; - BOOL fPauseCompleteNeeded; - BOOL fSrpCompleteNeeded; -} BURN_ELEVATION_APPLY_INITIALIZE_MESSAGE_CONTEXT; - -typedef struct _BURN_ELEVATION_CACHE_MESSAGE_CONTEXT -{ - PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler; - LPPROGRESS_ROUTINE pfnProgress; - LPVOID pvContext; -} BURN_ELEVATION_CACHE_MESSAGE_CONTEXT; - -typedef struct _BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT -{ - PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler; - LPVOID pvContext; -} BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT; - -typedef struct _BURN_ELEVATION_MSI_MESSAGE_CONTEXT -{ - PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler; - LPVOID pvContext; -} BURN_ELEVATION_MSI_MESSAGE_CONTEXT; - -typedef struct _BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT -{ - DWORD dwProcessId; -} BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT; - -typedef struct _BURN_ELEVATION_CHILD_MESSAGE_CONTEXT -{ - DWORD dwLoggingTlsId; - HANDLE hPipe; - HANDLE* phLock; - BOOL* pfDisabledAutomaticUpdates; - BURN_APPROVED_EXES* pApprovedExes; - BURN_CONTAINERS* pContainers; - BURN_PACKAGES* pPackages; - BURN_PAYLOADS* pPayloads; - BURN_VARIABLES* pVariables; - BURN_REGISTRATION* pRegistration; - BURN_USER_EXPERIENCE* pUserExperience; -} BURN_ELEVATION_CHILD_MESSAGE_CONTEXT; - - -// internal function declarations - -static DWORD WINAPI ElevatedChildCacheThreadProc( - __in LPVOID lpThreadParameter - ); -static HRESULT WaitForElevatedChildCacheThread( - __in HANDLE hCacheThread, - __in DWORD dwExpectedExitCode - ); -static HRESULT ProcessApplyInitializeMessages( - __in BURN_PIPE_MESSAGE* pMsg, - __in_opt LPVOID pvContext, - __out DWORD* pdwResult - ); -static HRESULT ProcessBurnCacheMessages( - __in BURN_PIPE_MESSAGE* pMsg, - __in LPVOID pvContext, - __out DWORD* pdwResult - ); -static HRESULT ProcessGenericExecuteMessages( - __in BURN_PIPE_MESSAGE* pMsg, - __in LPVOID pvContext, - __out DWORD* pdwResult - ); -static HRESULT ProcessMsiPackageMessages( - __in BURN_PIPE_MESSAGE* pMsg, - __in_opt LPVOID pvContext, - __out DWORD* pdwResult - ); -static HRESULT ProcessLaunchApprovedExeMessages( - __in BURN_PIPE_MESSAGE* pMsg, - __in_opt LPVOID pvContext, - __out DWORD* pdwResult - ); -static HRESULT ProcessProgressRoutineMessage( - __in BURN_PIPE_MESSAGE* pMsg, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pvContext, - __out DWORD* pdwResult - ); -static HRESULT ProcessElevatedChildMessage( - __in BURN_PIPE_MESSAGE* pMsg, - __in_opt LPVOID pvContext, - __out DWORD* pdwResult - ); -static HRESULT ProcessElevatedChildCacheMessage( - __in BURN_PIPE_MESSAGE* pMsg, - __in_opt LPVOID pvContext, - __out DWORD* pdwResult - ); -static HRESULT ProcessResult( - __in DWORD dwResult, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ); -static HRESULT OnApplyInitialize( - __in HANDLE hPipe, - __in BURN_VARIABLES* pVariables, - __in BURN_REGISTRATION* pRegistration, - __in HANDLE* phLock, - __in BOOL* pfDisabledWindowsUpdate, - __in BYTE* pbData, - __in SIZE_T cbData - ); -static HRESULT OnApplyUninitialize( - __in HANDLE* phLock - ); -static HRESULT OnSessionBegin( - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables, - __in BYTE* pbData, - __in SIZE_T cbData - ); -static HRESULT OnSessionResume( - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables, - __in BYTE* pbData, - __in SIZE_T cbData - ); -static HRESULT OnSessionEnd( - __in BURN_PACKAGES* pPackages, - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables, - __in BYTE* pbData, - __in SIZE_T cbData - ); -static HRESULT OnSaveState( - __in BURN_REGISTRATION* pRegistration, - __in BYTE* pbData, - __in SIZE_T cbData - ); -static HRESULT OnCacheCompletePayload( - __in HANDLE hPipe, - __in BURN_PACKAGES* pPackages, - __in BURN_PAYLOADS* pPayloads, - __in BYTE* pbData, - __in SIZE_T cbData - ); -static HRESULT OnCacheVerifyPayload( - __in HANDLE hPipe, - __in BURN_PACKAGES* pPackages, - __in BURN_PAYLOADS* pPayloads, - __in BYTE* pbData, - __in SIZE_T cbData - ); -static void OnCacheCleanup( - __in_z LPCWSTR wzBundleId - ); -static HRESULT OnProcessDependentRegistration( - __in const BURN_REGISTRATION* pRegistration, - __in BYTE* pbData, - __in SIZE_T cbData - ); -static HRESULT OnExecuteExePackage( - __in HANDLE hPipe, - __in BURN_PACKAGES* pPackages, - __in BURN_RELATED_BUNDLES* pRelatedBundles, - __in BURN_VARIABLES* pVariables, - __in BYTE* pbData, - __in SIZE_T cbData - ); -static HRESULT OnExecuteMsiPackage( - __in HANDLE hPipe, - __in BURN_PACKAGES* pPackages, - __in BURN_VARIABLES* pVariables, - __in BYTE* pbData, - __in SIZE_T cbData - ); -static HRESULT OnExecuteMspPackage( - __in HANDLE hPipe, - __in BURN_PACKAGES* pPackages, - __in BURN_VARIABLES* pVariables, - __in BYTE* pbData, - __in SIZE_T cbData - ); -static HRESULT OnExecuteMsuPackage( - __in HANDLE hPipe, - __in BURN_PACKAGES* pPackages, - __in BURN_VARIABLES* pVariables, - __in BYTE* pbData, - __in SIZE_T cbData - ); -static HRESULT OnExecutePackageProviderAction( - __in BURN_PACKAGES* pPackages, - __in BURN_RELATED_BUNDLES* pRelatedBundles, - __in BYTE* pbData, - __in SIZE_T cbData - ); -static HRESULT OnExecutePackageDependencyAction( - __in BURN_PACKAGES* pPackages, - __in BURN_RELATED_BUNDLES* pRelatedBundles, - __in BYTE* pbData, - __in SIZE_T cbData - ); -static HRESULT CALLBACK BurnCacheMessageHandler( - __in BURN_CACHE_MESSAGE* pMessage, - __in LPVOID pvContext - ); -static DWORD CALLBACK ElevatedProgressRoutine( - __in LARGE_INTEGER TotalFileSize, - __in LARGE_INTEGER TotalBytesTransferred, - __in LARGE_INTEGER StreamSize, - __in LARGE_INTEGER StreamBytesTransferred, - __in DWORD dwStreamNumber, - __in DWORD dwCallbackReason, - __in HANDLE hSourceFile, - __in HANDLE hDestinationFile, - __in_opt LPVOID lpData - ); -static int GenericExecuteMessageHandler( - __in GENERIC_EXECUTE_MESSAGE* pMessage, - __in LPVOID pvContext - ); -static int MsiExecuteMessageHandler( - __in WIU_MSI_EXECUTE_MESSAGE* pMessage, - __in_opt LPVOID pvContext - ); -static HRESULT OnCleanPackage( - __in BURN_PACKAGES* pPackages, - __in BYTE* pbData, - __in SIZE_T cbData - ); -static HRESULT OnLaunchApprovedExe( - __in HANDLE hPipe, - __in BURN_APPROVED_EXES* pApprovedExes, - __in BURN_VARIABLES* pVariables, - __in BYTE* pbData, - __in SIZE_T cbData - ); -static HRESULT OnMsiBeginTransaction( - __in BURN_PACKAGES* pPackages, - __in BYTE* pbData, - __in SIZE_T cbData - ); -static HRESULT OnMsiCommitTransaction( - __in BURN_PACKAGES* pPackages, - __in BYTE* pbData, - __in SIZE_T cbData - ); -static HRESULT OnMsiRollbackTransaction( - __in BURN_PACKAGES* pPackages, - __in BYTE* pbData, - __in SIZE_T cbData - ); -static HRESULT ElevatedOnPauseAUBegin( - __in HANDLE hPipe - ); -static HRESULT ElevatedOnPauseAUComplete( - __in HANDLE hPipe, - __in HRESULT hrStatus - ); -static HRESULT ElevatedOnSystemRestorePointBegin( - __in HANDLE hPipe - ); -static HRESULT ElevatedOnSystemRestorePointComplete( - __in HANDLE hPipe, - __in HRESULT hrStatus - ); - - -// function definitions - -extern "C" HRESULT ElevationElevate( - __in BURN_ENGINE_STATE* pEngineState, - __in_opt HWND hwndParent - ) -{ - Assert(BURN_MODE_ELEVATED != pEngineState->mode); - Assert(!pEngineState->companionConnection.sczName); - Assert(!pEngineState->companionConnection.sczSecret); - Assert(!pEngineState->companionConnection.hProcess); - Assert(!pEngineState->companionConnection.dwProcessId); - Assert(INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe); - Assert(INVALID_HANDLE_VALUE == pEngineState->companionConnection.hCachePipe); - - HRESULT hr = S_OK; - int nResult = IDOK; - HANDLE hPipesCreatedEvent = INVALID_HANDLE_VALUE; - - hr = UserExperienceOnElevateBegin(&pEngineState->userExperience); - ExitOnRootFailure(hr, "BA aborted elevation requirement."); - - hr = PipeCreateNameAndSecret(&pEngineState->companionConnection.sczName, &pEngineState->companionConnection.sczSecret); - ExitOnFailure(hr, "Failed to create pipe name and client token."); - - hr = PipeCreatePipes(&pEngineState->companionConnection, TRUE, &hPipesCreatedEvent); - ExitOnFailure(hr, "Failed to create pipe and cache pipe."); - - LogId(REPORT_STANDARD, MSG_LAUNCH_ELEVATED_ENGINE_STARTING); - - do - { - nResult = IDOK; - - // Create the elevated process and if successful, wait for it to connect. - hr = PipeLaunchChildProcess(pEngineState->sczBundleEngineWorkingPath, &pEngineState->companionConnection, TRUE, hwndParent); - if (SUCCEEDED(hr)) - { - LogId(REPORT_STANDARD, MSG_LAUNCH_ELEVATED_ENGINE_SUCCESS); - - hr = PipeWaitForChildConnect(&pEngineState->companionConnection); - if (HRESULT_FROM_WIN32(ERROR_NO_DATA) == hr) - { - hr = E_SUSPECTED_AV_INTERFERENCE; - } - ExitOnFailure(hr, "Failed to connect to elevated child process."); - - LogId(REPORT_STANDARD, MSG_CONNECT_TO_ELEVATED_ENGINE_SUCCESS); - } - else if (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr) - { - // The user clicked "Cancel" on the elevation prompt or the elevation prompt timed out, provide the notification with the option to retry. - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - nResult = UserExperienceSendError(&pEngineState->userExperience, BOOTSTRAPPER_ERROR_TYPE_ELEVATE, NULL, hr, NULL, MB_ICONERROR | MB_RETRYCANCEL, IDNOACTION); - } - } while (IDRETRY == nResult); - ExitOnFailure(hr, "Failed to elevate."); - -LExit: - ReleaseHandle(hPipesCreatedEvent); - - if (FAILED(hr)) - { - PipeConnectionUninitialize(&pEngineState->companionConnection); - } - - UserExperienceOnElevateComplete(&pEngineState->userExperience, hr); - - return hr; -} - -extern "C" HRESULT ElevationApplyInitialize( - __in HANDLE hPipe, - __in BURN_USER_EXPERIENCE* pBA, - __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_ACTION action, - __in BURN_AU_PAUSE_ACTION auAction, - __in BOOL fTakeSystemRestorePoint - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwResult = 0; - BURN_ELEVATION_APPLY_INITIALIZE_MESSAGE_CONTEXT context = { }; - - context.pBA = pBA; - - // serialize message data - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)action); - ExitOnFailure(hr, "Failed to write action to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)auAction); - ExitOnFailure(hr, "Failed to write update action to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fTakeSystemRestorePoint); - ExitOnFailure(hr, "Failed to write system restore point action to message buffer."); - - hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); - ExitOnFailure(hr, "Failed to write variables."); - - // send message - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE, pbData, cbData, ProcessApplyInitializeMessages, &context, &dwResult); - ExitOnFailure(hr, "Failed to send message to per-machine process."); - - hr = (HRESULT)dwResult; - - // Best effort to keep the sequence of BA events sane. - if (context.fPauseCompleteNeeded) - { - UserExperienceOnPauseAUComplete(pBA, hr); - } - if (context.fSrpCompleteNeeded) - { - UserExperienceOnSystemRestorePointComplete(pBA, hr); - } - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -extern "C" HRESULT ElevationApplyUninitialize( - __in HANDLE hPipe - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwResult = 0; - - // send message - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE, pbData, cbData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send message to per-machine process."); - - hr = (HRESULT)dwResult; - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -/******************************************************************* - ElevationSessionBegin - - -*******************************************************************/ -extern "C" HRESULT ElevationSessionBegin( - __in HANDLE hPipe, - __in_z LPCWSTR wzEngineWorkingPath, - __in_z LPCWSTR wzResumeCommandLine, - __in BOOL fDisableResume, - __in BURN_VARIABLES* pVariables, - __in DWORD dwRegistrationOperations, - __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction, - __in DWORD64 qwEstimatedSize - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwResult = 0; - - // serialize message data - hr = BuffWriteString(&pbData, &cbData, wzEngineWorkingPath); - ExitOnFailure(hr, "Failed to write engine working path to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, wzResumeCommandLine); - ExitOnFailure(hr, "Failed to write resume command line to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, fDisableResume); - ExitOnFailure(hr, "Failed to write resume flag."); - - hr = BuffWriteNumber(&pbData, &cbData, dwRegistrationOperations); - ExitOnFailure(hr, "Failed to write registration operations to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)dependencyRegistrationAction); - ExitOnFailure(hr, "Failed to write dependency registration action to message buffer."); - - hr = BuffWriteNumber64(&pbData, &cbData, qwEstimatedSize); - ExitOnFailure(hr, "Failed to write estimated size to message buffer."); - - hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); - ExitOnFailure(hr, "Failed to write variables."); - - // send message - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN, pbData, cbData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send message to per-machine process."); - - hr = (HRESULT)dwResult; - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -/******************************************************************* - ElevationSessionResume - - -*******************************************************************/ -extern "C" HRESULT ElevationSessionResume( - __in HANDLE hPipe, - __in_z LPCWSTR wzResumeCommandLine, - __in BOOL fDisableResume, - __in BURN_VARIABLES* pVariables - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwResult = 0; - - // serialize message data - hr = BuffWriteString(&pbData, &cbData, wzResumeCommandLine); - ExitOnFailure(hr, "Failed to write resume command line to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, fDisableResume); - ExitOnFailure(hr, "Failed to write resume flag."); - - hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); - ExitOnFailure(hr, "Failed to write variables."); - - // send message - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME, pbData, cbData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send message to per-machine process."); - - hr = (HRESULT)dwResult; - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -/******************************************************************* - ElevationSessionEnd - - -*******************************************************************/ -extern "C" HRESULT ElevationSessionEnd( - __in HANDLE hPipe, - __in BURN_RESUME_MODE resumeMode, - __in BOOTSTRAPPER_APPLY_RESTART restart, - __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwResult = 0; - - // serialize message data - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)resumeMode); - ExitOnFailure(hr, "Failed to write resume mode to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)restart); - ExitOnFailure(hr, "Failed to write restart enum to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)dependencyRegistrationAction); - ExitOnFailure(hr, "Failed to write dependency registration action to message buffer."); - - // send message - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SESSION_END, pbData, cbData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send message to per-machine process."); - - hr = (HRESULT)dwResult; - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -/******************************************************************* - ElevationSaveState - - -*******************************************************************/ -HRESULT ElevationSaveState( - __in HANDLE hPipe, - __in_bcount(cbBuffer) BYTE* pbBuffer, - __in SIZE_T cbBuffer - ) -{ - HRESULT hr = S_OK; - DWORD dwResult = 0; - - // send message - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE, pbBuffer, cbBuffer, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send message to per-machine process."); - - hr = (HRESULT)dwResult; - -LExit: - return hr; -} - -/******************************************************************* - ElevationCacheCompletePayload - - -*******************************************************************/ -extern "C" HRESULT ElevationCacheCompletePayload( - __in HANDLE hPipe, - __in BURN_PACKAGE* pPackage, - __in BURN_PAYLOAD* pPayload, - __in_z LPCWSTR wzUnverifiedPath, - __in BOOL fMove, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwResult = 0; - BURN_ELEVATION_CACHE_MESSAGE_CONTEXT context = { }; - - context.pfnCacheMessageHandler = pfnCacheMessageHandler; - context.pfnProgress = pfnProgress; - context.pvContext = pContext; - - // serialize message data - hr = BuffWriteString(&pbData, &cbData, pPackage->sczId); - ExitOnFailure(hr, "Failed to write package id to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pPayload->sczKey); - ExitOnFailure(hr, "Failed to write payload id to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, wzUnverifiedPath); - ExitOnFailure(hr, "Failed to write unverified path to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fMove); - ExitOnFailure(hr, "Failed to write move flag to message buffer."); - - // send message - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_COMPLETE_PAYLOAD, pbData, cbData, ProcessBurnCacheMessages, &context, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_COMPLETE_PAYLOAD message to per-machine process."); - - hr = (HRESULT)dwResult; - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -extern "C" HRESULT ElevationCacheVerifyPayload( - __in HANDLE hPipe, - __in BURN_PACKAGE* pPackage, - __in BURN_PAYLOAD* pPayload, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwResult = 0; - BURN_ELEVATION_CACHE_MESSAGE_CONTEXT context = { }; - - context.pfnCacheMessageHandler = pfnCacheMessageHandler; - context.pfnProgress = pfnProgress; - context.pvContext = pContext; - - // serialize message data - hr = BuffWriteString(&pbData, &cbData, pPackage->sczId); - ExitOnFailure(hr, "Failed to write package id to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pPayload->sczKey); - ExitOnFailure(hr, "Failed to write payload id to message buffer."); - - // send message - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_PAYLOAD, pbData, cbData, ProcessBurnCacheMessages, &context, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_PAYLOAD message to per-machine process."); - - hr = (HRESULT)dwResult; - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -/******************************************************************* - ElevationCacheCleanup - - -*******************************************************************/ -extern "C" HRESULT ElevationCacheCleanup( - __in HANDLE hPipe - ) -{ - HRESULT hr = S_OK; - DWORD dwResult = 0; - - // send message - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP, NULL, 0, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP message to per-machine process."); - - hr = (HRESULT)dwResult; - -LExit: - return hr; -} - -extern "C" HRESULT ElevationProcessDependentRegistration( - __in HANDLE hPipe, - __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwResult = 0; - - // serialize message data - hr = BuffWriteNumber(&pbData, &cbData, pAction->type); - ExitOnFailure(hr, "Failed to write action type to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pAction->sczBundleId); - ExitOnFailure(hr, "Failed to write bundle id to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pAction->sczDependentProviderKey); - ExitOnFailure(hr, "Failed to write dependent provider key to message buffer."); - - // send message - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION, pbData, cbData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION message to per-machine process."); - - hr = (HRESULT)dwResult; - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -/******************************************************************* - ElevationExecuteExePackage - - -*******************************************************************/ -extern "C" HRESULT ElevationExecuteExePackage( - __in HANDLE hPipe, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_VARIABLES* pVariables, - __in BOOL fRollback, - __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, - __in LPVOID pvContext, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT context = { }; - DWORD dwResult = 0; - - // serialize message data - hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.pPackage->sczId); - ExitOnFailure(hr, "Failed to write package id to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->exePackage.action); - ExitOnFailure(hr, "Failed to write action to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, fRollback); - ExitOnFailure(hr, "Failed to write rollback."); - - hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.sczIgnoreDependencies); - ExitOnFailure(hr, "Failed to write the list of dependencies to ignore to the message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.sczAncestors); - ExitOnFailure(hr, "Failed to write the list of ancestors to the message buffer."); - - hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); - ExitOnFailure(hr, "Failed to write variables."); - - // send message - context.pfnGenericMessageHandler = pfnGenericMessageHandler; - context.pvContext = pvContext; - - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE, pbData, cbData, ProcessGenericExecuteMessages, &context, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE message to per-machine process."); - - hr = ProcessResult(dwResult, pRestart); - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -extern "C" HRESULT ElevationMsiBeginTransaction( - __in HANDLE hPipe, - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwResult = ERROR_SUCCESS; - - // serialize message data - hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczId); - ExitOnFailure(hr, "Failed to write transaction name to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczLogPath); - ExitOnFailure(hr, "Failed to write transaction log path to message buffer."); - - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION, pbData, cbData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION message to per-machine process."); - - hr = static_cast(dwResult); - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -extern "C" HRESULT ElevationMsiCommitTransaction( - __in HANDLE hPipe, - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwResult = ERROR_SUCCESS; - - // serialize message data - hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczId); - ExitOnFailure(hr, "Failed to write transaction name to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczLogPath); - ExitOnFailure(hr, "Failed to write transaction log path to message buffer."); - - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION, pbData, cbData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION message to per-machine process."); - - hr = static_cast(dwResult); - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -extern "C" HRESULT ElevationMsiRollbackTransaction( - __in HANDLE hPipe, - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwResult = ERROR_SUCCESS; - - // serialize message data - hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczId); - ExitOnFailure(hr, "Failed to write transaction name to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczLogPath); - ExitOnFailure(hr, "Failed to write transaction log path to message buffer."); - - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION, pbData, cbData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION message to per-machine process."); - - hr = static_cast(dwResult); - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - - - -/******************************************************************* - ElevationExecuteMsiPackage - - -*******************************************************************/ -extern "C" HRESULT ElevationExecuteMsiPackage( - __in HANDLE hPipe, - __in_opt HWND hwndParent, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_VARIABLES* pVariables, - __in BOOL fRollback, - __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, - __in LPVOID pvContext, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = { }; - DWORD dwResult = 0; - - // serialize message data - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fRollback); - ExitOnFailure(hr, "Failed to write rollback flag to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msiPackage.pPackage->sczId); - ExitOnFailure(hr, "Failed to write package id to message buffer."); - - hr = BuffWritePointer(&pbData, &cbData, (DWORD_PTR)hwndParent); - ExitOnFailure(hr, "Failed to write parent hwnd to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msiPackage.sczLogPath); - ExitOnFailure(hr, "Failed to write package log to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.actionMsiProperty); - ExitOnFailure(hr, "Failed to write actionMsiProperty to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.uiLevel); - ExitOnFailure(hr, "Failed to write UI level to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.fDisableExternalUiHandler); - ExitOnFailure(hr, "Failed to write fDisableExternalUiHandler to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.action); - ExitOnFailure(hr, "Failed to write action to message buffer."); - - // Feature actions. - for (DWORD i = 0; i < pExecuteAction->msiPackage.pPackage->Msi.cFeatures; ++i) - { - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.rgFeatures[i]); - ExitOnFailure(hr, "Failed to write feature action to message buffer."); - } - - // Slipstream patches actions. - for (DWORD i = 0; i < pExecuteAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++i) - { - BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pExecuteAction->msiPackage.pPackage->Msi.rgSlipstreamMsps + i; - BOOTSTRAPPER_ACTION_STATE* pAction = fRollback ? &pSlipstreamMsp->rollback : &pSlipstreamMsp->execute; - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)*pAction); - ExitOnFailure(hr, "Failed to write slipstream patch action to message buffer."); - } - - hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); - ExitOnFailure(hr, "Failed to write variables."); - - - // send message - context.pfnMessageHandler = pfnMessageHandler; - context.pvContext = pvContext; - - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE, pbData, cbData, ProcessMsiPackageMessages, &context, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process."); - - hr = ProcessResult(dwResult, pRestart); - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -/******************************************************************* - ElevationExecuteMspPackage - - -*******************************************************************/ -extern "C" HRESULT ElevationExecuteMspPackage( - __in HANDLE hPipe, - __in_opt HWND hwndParent, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_VARIABLES* pVariables, - __in BOOL fRollback, - __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, - __in LPVOID pvContext, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = { }; - DWORD dwResult = 0; - - // serialize message data - hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.pPackage->sczId); - ExitOnFailure(hr, "Failed to write package id to message buffer."); - - hr = BuffWritePointer(&pbData, &cbData, (DWORD_PTR)hwndParent); - ExitOnFailure(hr, "Failed to write parent hwnd to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.sczTargetProductCode); - ExitOnFailure(hr, "Failed to write target product code to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.sczLogPath); - ExitOnFailure(hr, "Failed to write package log to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.actionMsiProperty); - ExitOnFailure(hr, "Failed to write actionMsiProperty to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.uiLevel); - ExitOnFailure(hr, "Failed to write UI level to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.fDisableExternalUiHandler); - ExitOnFailure(hr, "Failed to write fDisableExternalUiHandler to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.action); - ExitOnFailure(hr, "Failed to write action to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->mspTarget.cOrderedPatches); - ExitOnFailure(hr, "Failed to write count of ordered patches to message buffer."); - - for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i) - { - hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage->sczId); - ExitOnFailure(hr, "Failed to write ordered patch id to message buffer."); - } - - hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); - ExitOnFailure(hr, "Failed to write variables."); - - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fRollback); - ExitOnFailure(hr, "Failed to write rollback flag to message buffer."); - - // send message - context.pfnMessageHandler = pfnMessageHandler; - context.pvContext = pvContext; - - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE, pbData, cbData, ProcessMsiPackageMessages, &context, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE message to per-machine process."); - - hr = ProcessResult(dwResult, pRestart); - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -/******************************************************************* - ElevationExecuteMsuPackage - - -*******************************************************************/ -extern "C" HRESULT ElevationExecuteMsuPackage( - __in HANDLE hPipe, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BOOL fRollback, - __in BOOL fStopWusaService, - __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, - __in LPVOID pvContext, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT context = { }; - DWORD dwResult = 0; - - // serialize message data - hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msuPackage.pPackage->sczId); - ExitOnFailure(hr, "Failed to write package id to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msuPackage.sczLogPath); - ExitOnFailure(hr, "Failed to write package log to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msuPackage.action); - ExitOnFailure(hr, "Failed to write action to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, fRollback); - ExitOnFailure(hr, "Failed to write rollback."); - - hr = BuffWriteNumber(&pbData, &cbData, fStopWusaService); - ExitOnFailure(hr, "Failed to write StopWusaService."); - - // send message - context.pfnGenericMessageHandler = pfnGenericMessageHandler; - context.pvContext = pvContext; - - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE, pbData, cbData, ProcessGenericExecuteMessages, &context, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE message to per-machine process."); - - hr = ProcessResult(dwResult, pRestart); - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -extern "C" HRESULT ElevationExecutePackageProviderAction( - __in HANDLE hPipe, - __in BURN_EXECUTE_ACTION* pExecuteAction - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwResult = 0; - BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; - - // Serialize the message data. - hr = BuffWriteString(&pbData, &cbData, pExecuteAction->packageProvider.pPackage->sczId); - ExitOnFailure(hr, "Failed to write package id to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->packageProvider.action); - ExitOnFailure(hr, "Failed to write action to message buffer."); - - // Send the message. - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER, pbData, cbData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER message to per-machine process."); - - // Ignore the restart since this action only results in registry writes. - hr = ProcessResult(dwResult, &restart); - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -extern "C" HRESULT ElevationExecutePackageDependencyAction( - __in HANDLE hPipe, - __in BURN_EXECUTE_ACTION* pExecuteAction - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwResult = 0; - BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; - - // Serialize the message data. - hr = BuffWriteString(&pbData, &cbData, pExecuteAction->packageDependency.pPackage->sczId); - ExitOnFailure(hr, "Failed to write package id to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pExecuteAction->packageDependency.sczBundleProviderKey); - ExitOnFailure(hr, "Failed to write bundle dependency key to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->packageDependency.action); - ExitOnFailure(hr, "Failed to write action to message buffer."); - - // Send the message. - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY, pbData, cbData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY message to per-machine process."); - - // Ignore the restart since this action only results in registry writes. - hr = ProcessResult(dwResult, &restart); - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -/******************************************************************* - ElevationCleanPackage - - -*******************************************************************/ -extern "C" HRESULT ElevationCleanPackage( - __in HANDLE hPipe, - __in BURN_PACKAGE* pPackage - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwResult = 0; - - // serialize message data - hr = BuffWriteString(&pbData, &cbData, pPackage->sczId); - ExitOnFailure(hr, "Failed to write clean package id to message buffer."); - - // send message - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE, pbData, cbData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE message to per-machine process."); - - hr = (HRESULT)dwResult; - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -extern "C" HRESULT ElevationLaunchApprovedExe( - __in HANDLE hPipe, - __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe, - __out DWORD* pdwProcessId - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwResult = 0; - BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT context = { }; - - // Serialize message data. - hr = BuffWriteString(&pbData, &cbData, pLaunchApprovedExe->sczId); - ExitOnFailure(hr, "Failed to write approved exe id to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pLaunchApprovedExe->sczArguments); - ExitOnFailure(hr, "Failed to write approved exe arguments to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, pLaunchApprovedExe->dwWaitForInputIdleTimeout); - ExitOnFailure(hr, "Failed to write approved exe WaitForInputIdle timeout to message buffer."); - - // Send the message. - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE, pbData, cbData, ProcessLaunchApprovedExeMessages, &context, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE message to per-machine process."); - - hr = (HRESULT)dwResult; - *pdwProcessId = context.dwProcessId; - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -/******************************************************************* - ElevationChildPumpMessages - - -*******************************************************************/ -extern "C" HRESULT ElevationChildPumpMessages( - __in DWORD dwLoggingTlsId, - __in HANDLE hPipe, - __in HANDLE hCachePipe, - __in BURN_APPROVED_EXES* pApprovedExes, - __in BURN_CONTAINERS* pContainers, - __in BURN_PACKAGES* pPackages, - __in BURN_PAYLOADS* pPayloads, - __in BURN_VARIABLES* pVariables, - __in BURN_REGISTRATION* pRegistration, - __in BURN_USER_EXPERIENCE* pUserExperience, - __out HANDLE* phLock, - __out BOOL* pfDisabledAutomaticUpdates, - __out DWORD* pdwChildExitCode, - __out BOOL* pfRestart - ) -{ - HRESULT hr = S_OK; - BURN_ELEVATION_CHILD_MESSAGE_CONTEXT cacheContext = { }; - BURN_ELEVATION_CHILD_MESSAGE_CONTEXT context = { }; - HANDLE hCacheThread = NULL; - BURN_PIPE_RESULT result = { }; - - cacheContext.dwLoggingTlsId = dwLoggingTlsId; - cacheContext.hPipe = hCachePipe; - cacheContext.pContainers = pContainers; - cacheContext.pPackages = pPackages; - cacheContext.pPayloads = pPayloads; - cacheContext.pVariables = pVariables; - cacheContext.pRegistration = pRegistration; - cacheContext.pUserExperience = pUserExperience; - - context.dwLoggingTlsId = dwLoggingTlsId; - context.hPipe = hPipe; - context.phLock = phLock; - context.pfDisabledAutomaticUpdates = pfDisabledAutomaticUpdates; - context.pApprovedExes = pApprovedExes; - context.pContainers = pContainers; - context.pPackages = pPackages; - context.pPayloads = pPayloads; - context.pVariables = pVariables; - context.pRegistration = pRegistration; - context.pUserExperience = pUserExperience; - - hCacheThread = ::CreateThread(NULL, 0, ElevatedChildCacheThreadProc, &cacheContext, 0, NULL); - ExitOnNullWithLastError(hCacheThread, hr, "Failed to create elevated cache thread."); - - hr = PipePumpMessages(hPipe, ProcessElevatedChildMessage, &context, &result); - ExitOnFailure(hr, "Failed to pump messages in child process."); - - // Wait for the cache thread and verify it gets the right result but don't fail if things - // don't work out. - WaitForElevatedChildCacheThread(hCacheThread, result.dwResult); - - *pdwChildExitCode = result.dwResult; - *pfRestart = result.fRestart; - -LExit: - ReleaseHandle(hCacheThread); - - return hr; -} - -extern "C" HRESULT ElevationChildResumeAutomaticUpdates() -{ - HRESULT hr = S_OK; - - LogId(REPORT_STANDARD, MSG_RESUME_AU_STARTING); - - hr = WuaResumeAutomaticUpdates(); - ExitOnFailure(hr, "Failed to resume automatic updates after pausing them, continuing..."); - - LogId(REPORT_STANDARD, MSG_RESUME_AU_SUCCEEDED); - -LExit: - return hr; -} - -// internal function definitions - -static DWORD WINAPI ElevatedChildCacheThreadProc( - __in LPVOID lpThreadParameter - ) -{ - HRESULT hr = S_OK; - BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = reinterpret_cast(lpThreadParameter); - BOOL fComInitialized = FALSE; - BURN_PIPE_RESULT result = { }; - - if (!::TlsSetValue(pContext->dwLoggingTlsId, pContext->hPipe)) - { - ExitWithLastError(hr, "Failed to set elevated cache pipe into thread local storage for logging."); - } - - // initialize COM - hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); - ExitOnFailure(hr, "Failed to initialize COM."); - fComInitialized = TRUE; - - hr = PipePumpMessages(pContext->hPipe, ProcessElevatedChildCacheMessage, pContext, &result); - ExitOnFailure(hr, "Failed to pump messages in child process."); - - hr = (HRESULT)result.dwResult; - -LExit: - if (fComInitialized) - { - ::CoUninitialize(); - } - - return (DWORD)hr; -} - -static HRESULT WaitForElevatedChildCacheThread( - __in HANDLE hCacheThread, - __in DWORD dwExpectedExitCode - ) -{ - UNREFERENCED_PARAMETER(dwExpectedExitCode); - - HRESULT hr = S_OK; - DWORD dwExitCode = ERROR_SUCCESS; - - if (WAIT_OBJECT_0 != ::WaitForSingleObject(hCacheThread, BURN_TIMEOUT)) - { - ExitWithLastError(hr, "Failed to wait for cache thread to terminate."); - } - - if (!::GetExitCodeThread(hCacheThread, &dwExitCode)) - { - ExitWithLastError(hr, "Failed to get cache thread exit code."); - } - - AssertSz(dwExitCode == dwExpectedExitCode, "Cache thread should have exited with the expected exit code."); - -LExit: - return hr; -} - -static HRESULT ProcessApplyInitializeMessages( - __in BURN_PIPE_MESSAGE* pMsg, - __in_opt LPVOID pvContext, - __out DWORD* pdwResult - ) -{ - HRESULT hr = S_OK; - BURN_ELEVATION_APPLY_INITIALIZE_MESSAGE_CONTEXT* pContext = static_cast(pvContext); - BYTE* pbData = (BYTE*)pMsg->pvData; - SIZE_T iData = 0; - HRESULT hrStatus = S_OK; - HRESULT hrBA = S_OK; - - // Process the message. - switch (pMsg->dwMessage) - { - case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_BEGIN: - pContext->fPauseCompleteNeeded = TRUE; - hrBA = UserExperienceOnPauseAUBegin(pContext->pBA); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_COMPLETE: - // read hrStatus - hr = BuffReadNumber(pbData, pMsg->cbData, &iData, reinterpret_cast(&hrStatus)); - ExitOnFailure(hr, "Failed to read pause AU hrStatus."); - - pContext->fPauseCompleteNeeded = FALSE; - hrBA = UserExperienceOnPauseAUComplete(pContext->pBA, hrStatus); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_BEGIN: - if (pContext->fPauseCompleteNeeded) - { - pContext->fPauseCompleteNeeded = FALSE; - hrBA = UserExperienceOnPauseAUComplete(pContext->pBA, E_INVALIDSTATE); - } - - pContext->fSrpCompleteNeeded = TRUE; - hrBA = UserExperienceOnSystemRestorePointBegin(pContext->pBA); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_COMPLETE: - // read hrStatus - hr = BuffReadNumber(pbData, pMsg->cbData, &iData, reinterpret_cast(&hrStatus)); - ExitOnFailure(hr, "Failed to read system restore point hrStatus."); - - pContext->fSrpCompleteNeeded = FALSE; - hrBA = UserExperienceOnSystemRestorePointComplete(pContext->pBA, hrStatus); - break; - - default: - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Invalid apply initialize message."); - break; - } - - *pdwResult = static_cast(hrBA); - -LExit: - return hr; -} - -static HRESULT ProcessBurnCacheMessages( - __in BURN_PIPE_MESSAGE* pMsg, - __in LPVOID pvContext, - __out DWORD* pdwResult - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - BURN_ELEVATION_CACHE_MESSAGE_CONTEXT* pContext = static_cast(pvContext); - BURN_CACHE_MESSAGE message = { }; - BOOL fProgressRoutine = FALSE; - - // Process the message. - switch (pMsg->dwMessage) - { - case BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_BEGIN: - // read message parameters - hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, reinterpret_cast(&message.begin.cacheStep)); - ExitOnFailure(hr, "Failed to read begin cache step."); - - message.type = BURN_CACHE_MESSAGE_BEGIN; - break; - - case BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_COMPLETE: - // read message parameters - hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, reinterpret_cast(&message.complete.hrStatus)); - ExitOnFailure(hr, "Failed to read complete hresult."); - - message.type = BURN_CACHE_MESSAGE_COMPLETE; - break; - - case BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_SUCCESS: - // read message parameters - hr = BuffReadNumber64((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.success.qwFileSize); - ExitOnFailure(hr, "Failed to read begin cache step."); - - message.type = BURN_CACHE_MESSAGE_SUCCESS; - break; - - case BURN_ELEVATION_MESSAGE_TYPE_PROGRESS_ROUTINE: - fProgressRoutine = TRUE; - break; - - default: - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Invalid burn cache message."); - break; - } - - if (fProgressRoutine) - { - hr = ProcessProgressRoutineMessage(pMsg, pContext->pfnProgress, pContext->pvContext, pdwResult); - } - else - { - hr = pContext->pfnCacheMessageHandler(&message, pContext->pvContext); - *pdwResult = static_cast(hr); - } - -LExit: - return hr; -} - -static HRESULT ProcessGenericExecuteMessages( - __in BURN_PIPE_MESSAGE* pMsg, - __in LPVOID pvContext, - __out DWORD* pdwResult - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT* pContext = static_cast(pvContext); - LPWSTR sczMessage = NULL; - DWORD cFiles = 0; - LPWSTR* rgwzFiles = NULL; - GENERIC_EXECUTE_MESSAGE message = { }; - - hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.dwAllowedResults); - ExitOnFailure(hr, "Failed to allowed results."); - - // Process the message. - switch (pMsg->dwMessage) - { - case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS: - message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; - - // read message parameters - hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.progress.dwPercentage); - ExitOnFailure(hr, "Failed to progress."); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR: - message.type = GENERIC_EXECUTE_MESSAGE_ERROR; - - // read message parameters - hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.error.dwErrorCode); - ExitOnFailure(hr, "Failed to read error code."); - - hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &sczMessage); - ExitOnFailure(hr, "Failed to read message."); - - message.error.wzMessage = sczMessage; - break; - - case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE: - message.type = GENERIC_EXECUTE_MESSAGE_FILES_IN_USE; - - // read message parameters - hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &cFiles); - ExitOnFailure(hr, "Failed to read file count."); - - rgwzFiles = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cFiles, TRUE); - ExitOnNull(rgwzFiles, hr, E_OUTOFMEMORY, "Failed to allocate buffer for files in use."); - - for (DWORD i = 0; i < cFiles; ++i) - { - hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &rgwzFiles[i]); - ExitOnFailure(hr, "Failed to read file name: %u", i); - } - - message.filesInUse.cFiles = cFiles; - message.filesInUse.rgwzFiles = (LPCWSTR*)rgwzFiles; - break; - - default: - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Invalid package message."); - break; - } - - // send message - *pdwResult = (DWORD)pContext->pfnGenericMessageHandler(&message, pContext->pvContext); - -LExit: - ReleaseStr(sczMessage); - - if (rgwzFiles) - { - for (DWORD i = 0; i < cFiles; ++i) - { - ReleaseStr(rgwzFiles[i]); - } - MemFree(rgwzFiles); - } - return hr; -} - -static HRESULT ProcessMsiPackageMessages( - __in BURN_PIPE_MESSAGE* pMsg, - __in_opt LPVOID pvContext, - __out DWORD* pdwResult - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - WIU_MSI_EXECUTE_MESSAGE message = { }; - DWORD cMsiData = 0; - LPWSTR* rgwzMsiData = NULL; - BURN_ELEVATION_MSI_MESSAGE_CONTEXT* pContext = static_cast(pvContext); - LPWSTR sczMessage = NULL; - - // Read MSI extended message data. - hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &cMsiData); - ExitOnFailure(hr, "Failed to read MSI data count."); - - if (cMsiData) - { - rgwzMsiData = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cMsiData, TRUE); - ExitOnNull(rgwzMsiData, hr, E_OUTOFMEMORY, "Failed to allocate buffer to read MSI data."); - - for (DWORD i = 0; i < cMsiData; ++i) - { - hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &rgwzMsiData[i]); - ExitOnFailure(hr, "Failed to read MSI data: %u", i); - } - - message.cData = cMsiData; - message.rgwzData = (LPCWSTR*)rgwzMsiData; - } - - hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.dwAllowedResults); - ExitOnFailure(hr, "Failed to read UI flags."); - - // Process the rest of the message. - switch (pMsg->dwMessage) - { - case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS: - // read message parameters - message.type = WIU_MSI_EXECUTE_MESSAGE_PROGRESS; - - hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.progress.dwPercentage); - ExitOnFailure(hr, "Failed to read progress."); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR: - // read message parameters - message.type = WIU_MSI_EXECUTE_MESSAGE_ERROR; - - hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.error.dwErrorCode); - ExitOnFailure(hr, "Failed to read error code."); - - hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &sczMessage); - ExitOnFailure(hr, "Failed to read message."); - message.error.wzMessage = sczMessage; - break; - - case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE: - // read message parameters - message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE; - - hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, (DWORD*)&message.msiMessage.mt); - ExitOnFailure(hr, "Failed to read message type."); - - hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &sczMessage); - ExitOnFailure(hr, "Failed to read message."); - message.msiMessage.wzMessage = sczMessage; - break; - - case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE: - message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE; - message.msiFilesInUse.cFiles = cMsiData; - message.msiFilesInUse.rgwzFiles = (LPCWSTR*)rgwzMsiData; - break; - - default: - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Invalid package message."); - break; - } - - // send message - *pdwResult = (DWORD)pContext->pfnMessageHandler(&message, pContext->pvContext); - -LExit: - ReleaseStr(sczMessage); - - if (rgwzMsiData) - { - for (DWORD i = 0; i < cMsiData; ++i) - { - ReleaseStr(rgwzMsiData[i]); - } - - MemFree(rgwzMsiData); - } - - return hr; -} - -static HRESULT ProcessLaunchApprovedExeMessages( - __in BURN_PIPE_MESSAGE* pMsg, - __in_opt LPVOID pvContext, - __out DWORD* pdwResult - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT* pContext = static_cast(pvContext); - DWORD dwProcessId = 0; - - // Process the message. - switch (pMsg->dwMessage) - { - case BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID: - // read message parameters - hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &dwProcessId); - ExitOnFailure(hr, "Failed to read approved exe process id."); - pContext->dwProcessId = dwProcessId; - break; - - default: - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Invalid launch approved exe message."); - break; - } - - *pdwResult = static_cast(hr); - -LExit: - return hr; -} - -static HRESULT ProcessProgressRoutineMessage( - __in BURN_PIPE_MESSAGE* pMsg, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pvContext, - __out DWORD* pdwResult - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - LARGE_INTEGER liTotalFileSize = { }; - LARGE_INTEGER liTotalBytesTransferred = { }; - LARGE_INTEGER liStreamSize = { }; - LARGE_INTEGER liStreamBytesTransferred = { }; - DWORD dwStreamNumber = 0; - DWORD dwCallbackReason = CALLBACK_CHUNK_FINISHED; - HANDLE hSourceFile = INVALID_HANDLE_VALUE; - HANDLE hDestinationFile = INVALID_HANDLE_VALUE; - - hr = BuffReadNumber64((BYTE*)pMsg->pvData, pMsg->cbData, &iData, reinterpret_cast(&liTotalFileSize.QuadPart)); - ExitOnFailure(hr, "Failed to read total file size for progress."); - - hr = BuffReadNumber64((BYTE*)pMsg->pvData, pMsg->cbData, &iData, reinterpret_cast(&liTotalBytesTransferred.QuadPart)); - ExitOnFailure(hr, "Failed to read total bytes transferred for progress."); - - *pdwResult = pfnProgress(liTotalFileSize, liTotalBytesTransferred, liStreamSize, liStreamBytesTransferred, dwStreamNumber, dwCallbackReason, hSourceFile, hDestinationFile, pvContext); - -LExit: - return hr; -} - -static HRESULT ProcessElevatedChildMessage( - __in BURN_PIPE_MESSAGE* pMsg, - __in_opt LPVOID pvContext, - __out DWORD* pdwResult - ) -{ - HRESULT hr = S_OK; - BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = static_cast(pvContext); - HRESULT hrResult = S_OK; - DWORD dwPid = 0; - - switch (pMsg->dwMessage) - { - case BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION: - hrResult = OnMsiBeginTransaction(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION: - hrResult = OnMsiCommitTransaction(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION: - hrResult = OnMsiRollbackTransaction(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE: - hrResult = OnApplyInitialize(pContext->hPipe, pContext->pVariables, pContext->pRegistration, pContext->phLock, pContext->pfDisabledAutomaticUpdates, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE: - hrResult = OnApplyUninitialize(pContext->phLock); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN: - hrResult = OnSessionBegin(pContext->pRegistration, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME: - hrResult = OnSessionResume(pContext->pRegistration, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_SESSION_END: - hrResult = OnSessionEnd(pContext->pPackages, pContext->pRegistration, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE: - hrResult = OnSaveState(pContext->pRegistration, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION: - hrResult = OnProcessDependentRegistration(pContext->pRegistration, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE: - hrResult = OnExecuteExePackage(pContext->hPipe, pContext->pPackages, &pContext->pRegistration->relatedBundles, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE: - hrResult = OnExecuteMsiPackage(pContext->hPipe, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE: - hrResult = OnExecuteMspPackage(pContext->hPipe, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE: - hrResult = OnExecuteMsuPackage(pContext->hPipe, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER: - hrResult = OnExecutePackageProviderAction(pContext->pPackages, &pContext->pRegistration->relatedBundles, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY: - hrResult = OnExecutePackageDependencyAction(pContext->pPackages, &pContext->pRegistration->relatedBundles, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE: - hrResult = OnCleanPackage(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE: - hrResult = OnLaunchApprovedExe(pContext->hPipe, pContext->pApprovedExes, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - - default: - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Unexpected elevated message sent to child process, msg: %u", pMsg->dwMessage); - } - - *pdwResult = dwPid ? dwPid : (DWORD)hrResult; - -LExit: - return hr; -} - -static HRESULT ProcessElevatedChildCacheMessage( - __in BURN_PIPE_MESSAGE* pMsg, - __in_opt LPVOID pvContext, - __out DWORD* pdwResult - ) -{ - HRESULT hr = S_OK; - BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = static_cast(pvContext); - HRESULT hrResult = S_OK; - - switch (pMsg->dwMessage) - { - case BURN_ELEVATION_MESSAGE_TYPE_CACHE_COMPLETE_PAYLOAD: - hrResult = OnCacheCompletePayload(pContext->hPipe, pContext->pPackages, pContext->pPayloads, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_PAYLOAD: - hrResult = OnCacheVerifyPayload(pContext->hPipe, pContext->pPackages, pContext->pPayloads, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - - case BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP: - OnCacheCleanup(pContext->pRegistration->sczId); - hrResult = S_OK; - break; - - case BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE: - hrResult = OnCleanPackage(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData); - break; - - default: - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Unexpected elevated cache message sent to child process, msg: %u", pMsg->dwMessage); - } - - *pdwResult = (DWORD)hrResult; - -LExit: - return hr; -} - -static HRESULT ProcessResult( - __in DWORD dwResult, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ) -{ - HRESULT hr = static_cast(dwResult); - if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == hr) - { - *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; - hr = S_OK; - } - else if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED) == hr) - { - *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; - hr = S_OK; - } - - return hr; -} - -static HRESULT OnApplyInitialize( - __in HANDLE hPipe, - __in BURN_VARIABLES* pVariables, - __in BURN_REGISTRATION* pRegistration, - __in HANDLE* phLock, - __in BOOL* pfDisabledWindowsUpdate, - __in BYTE* pbData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - DWORD dwAction = 0; - DWORD dwAUAction = 0; - DWORD dwTakeSystemRestorePoint = 0; - LPWSTR sczBundleName = NULL; - HRESULT hrStatus = S_OK; - - // Deserialize message data. - hr = BuffReadNumber(pbData, cbData, &iData, &dwAction); - ExitOnFailure(hr, "Failed to read action."); - - hr = BuffReadNumber(pbData, cbData, &iData, &dwAUAction); - ExitOnFailure(hr, "Failed to read update action."); - - hr = BuffReadNumber(pbData, cbData, &iData, &dwTakeSystemRestorePoint); - ExitOnFailure(hr, "Failed to read system restore point action."); - - hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); - ExitOnFailure(hr, "Failed to read variables."); - - // Initialize. - hr = ApplyLock(TRUE, phLock); - ExitOnFailure(hr, "Failed to acquire lock due to setup in other session."); - - // Reset and reload the related bundles. - RelatedBundlesUninitialize(&pRegistration->relatedBundles); - - hr = RelatedBundlesInitializeForScope(TRUE, pRegistration, &pRegistration->relatedBundles); - ExitOnFailure(hr, "Failed to initialize per-machine related bundles."); - - // Attempt to pause AU with best effort. - if (BURN_AU_PAUSE_ACTION_IFELEVATED == dwAUAction || BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME == dwAUAction) - { - hr = ElevatedOnPauseAUBegin(hPipe); - ExitOnFailure(hr, "ElevatedOnPauseAUBegin failed."); - - LogId(REPORT_STANDARD, MSG_PAUSE_AU_STARTING); - - hrStatus = hr = WuaPauseAutomaticUpdates(); - if (FAILED(hr)) - { - LogId(REPORT_STANDARD, MSG_FAILED_PAUSE_AU, hr); - hr = S_OK; - } - else - { - LogId(REPORT_STANDARD, MSG_PAUSE_AU_SUCCEEDED); - if (BURN_AU_PAUSE_ACTION_IFELEVATED == dwAUAction) - { - *pfDisabledWindowsUpdate = TRUE; - } - } - - hr = ElevatedOnPauseAUComplete(hPipe, hrStatus); - ExitOnFailure(hr, "ElevatedOnPauseAUComplete failed."); - } - - if (dwTakeSystemRestorePoint) - { - hr = VariableGetString(pVariables, BURN_BUNDLE_NAME, &sczBundleName); - if (FAILED(hr)) - { - hr = S_OK; - ExitFunction(); - } - - hr = ElevatedOnSystemRestorePointBegin(hPipe); - ExitOnFailure(hr, "ElevatedOnSystemRestorePointBegin failed."); - - LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_STARTING); - - BOOTSTRAPPER_ACTION action = static_cast(dwAction); - SRP_ACTION restoreAction = (BOOTSTRAPPER_ACTION_INSTALL == action) ? SRP_ACTION_INSTALL : (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? SRP_ACTION_UNINSTALL : SRP_ACTION_MODIFY; - hrStatus = hr = SrpCreateRestorePoint(sczBundleName, restoreAction); - if (SUCCEEDED(hr)) - { - LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_SUCCEEDED); - } - else if (E_NOTIMPL == hr) - { - LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_DISABLED); - hr = S_OK; - } - else - { - LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_FAILED, hr); - hr = S_OK; - } - - hr = ElevatedOnSystemRestorePointComplete(hPipe, hrStatus); - ExitOnFailure(hr, "ElevatedOnSystemRestorePointComplete failed."); - } - -LExit: - ReleaseStr(sczBundleName); - return hr; -} - -static HRESULT OnApplyUninitialize( - __in HANDLE* phLock - ) -{ - Assert(phLock); - - // TODO: end system restore point. - - if (*phLock) - { - ::ReleaseMutex(*phLock); - ::CloseHandle(*phLock); - *phLock = NULL; - } - - return S_OK; -} - -static HRESULT OnSessionBegin( - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables, - __in BYTE* pbData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - LPWSTR sczEngineWorkingPath = NULL; - DWORD dwRegistrationOperations = 0; - DWORD dwDependencyRegistrationAction = 0; - DWORD64 qwEstimatedSize = 0; - - // Deserialize message data. - hr = BuffReadString(pbData, cbData, &iData, &sczEngineWorkingPath); - ExitOnFailure(hr, "Failed to read engine working path."); - - hr = BuffReadString(pbData, cbData, &iData, &pRegistration->sczResumeCommandLine); - ExitOnFailure(hr, "Failed to read resume command line."); - - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&pRegistration->fDisableResume); - ExitOnFailure(hr, "Failed to read resume flag."); - - hr = BuffReadNumber(pbData, cbData, &iData, &dwRegistrationOperations); - ExitOnFailure(hr, "Failed to read registration operations."); - - hr = BuffReadNumber(pbData, cbData, &iData, &dwDependencyRegistrationAction); - ExitOnFailure(hr, "Failed to read dependency registration action."); - - hr = BuffReadNumber64(pbData, cbData, &iData, &qwEstimatedSize); - ExitOnFailure(hr, "Failed to read estimated size."); - - hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); - ExitOnFailure(hr, "Failed to read variables."); - - // Begin session in per-machine process. - hr = RegistrationSessionBegin(sczEngineWorkingPath, pRegistration, pVariables, dwRegistrationOperations, (BURN_DEPENDENCY_REGISTRATION_ACTION)dwDependencyRegistrationAction, qwEstimatedSize); - ExitOnFailure(hr, "Failed to begin registration session."); - -LExit: - ReleaseStr(sczEngineWorkingPath); - - return hr; -} - -static HRESULT OnSessionResume( - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables, - __in BYTE* pbData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - - // Deserialize message data. - hr = BuffReadString(pbData, cbData, &iData, &pRegistration->sczResumeCommandLine); - ExitOnFailure(hr, "Failed to read resume command line."); - - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&pRegistration->fDisableResume); - ExitOnFailure(hr, "Failed to read resume flag."); - - hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); - ExitOnFailure(hr, "Failed to read variables."); - - // resume session in per-machine process - hr = RegistrationSessionResume(pRegistration, pVariables); - ExitOnFailure(hr, "Failed to resume registration session."); - -LExit: - return hr; -} - -static HRESULT OnSessionEnd( - __in BURN_PACKAGES* pPackages, - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables, - __in BYTE* pbData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - DWORD dwResumeMode = 0; - DWORD dwRestart = 0; - DWORD dwDependencyRegistrationAction = 0; - - // Deserialize message data. - hr = BuffReadNumber(pbData, cbData, &iData, &dwResumeMode); - ExitOnFailure(hr, "Failed to read resume mode enum."); - - hr = BuffReadNumber(pbData, cbData, &iData, &dwRestart); - ExitOnFailure(hr, "Failed to read restart enum."); - - hr = BuffReadNumber(pbData, cbData, &iData, &dwDependencyRegistrationAction); - ExitOnFailure(hr, "Failed to read dependency registration action."); - - // suspend session in per-machine process - hr = RegistrationSessionEnd(pRegistration, pVariables, pPackages, (BURN_RESUME_MODE)dwResumeMode, (BOOTSTRAPPER_APPLY_RESTART)dwRestart, (BURN_DEPENDENCY_REGISTRATION_ACTION)dwDependencyRegistrationAction); - ExitOnFailure(hr, "Failed to suspend registration session."); - -LExit: - return hr; -} - -static HRESULT OnSaveState( - __in BURN_REGISTRATION* pRegistration, - __in BYTE* pbData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - - // save state in per-machine process - hr = RegistrationSaveState(pRegistration, pbData, cbData); - ExitOnFailure(hr, "Failed to save state."); - -LExit: - return hr; -} - -static HRESULT OnCacheCompletePayload( - __in HANDLE hPipe, - __in BURN_PACKAGES* pPackages, - __in BURN_PAYLOADS* pPayloads, - __in BYTE* pbData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - LPWSTR scz = NULL; - BURN_PACKAGE* pPackage = NULL; - BURN_PAYLOAD* pPayload = NULL; - LPWSTR sczUnverifiedPath = NULL; - BOOL fMove = FALSE; - - // Deserialize message data. - hr = BuffReadString(pbData, cbData, &iData, &scz); - ExitOnFailure(hr, "Failed to read package id."); - - if (scz && *scz) - { - hr = PackageFindById(pPackages, scz, &pPackage); - ExitOnFailure(hr, "Failed to find package: %ls", scz); - } - - hr = BuffReadString(pbData, cbData, &iData, &scz); - ExitOnFailure(hr, "Failed to read payload id."); - - if (scz && *scz) - { - hr = PayloadFindById(pPayloads, scz, &pPayload); - ExitOnFailure(hr, "Failed to find payload: %ls", scz); - } - - hr = BuffReadString(pbData, cbData, &iData, &sczUnverifiedPath); - ExitOnFailure(hr, "Failed to read unverified path."); - - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fMove); - ExitOnFailure(hr, "Failed to read move flag."); - - if (pPackage && pPayload) // complete payload. - { - hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, sczUnverifiedPath, fMove, BurnCacheMessageHandler, ElevatedProgressRoutine, hPipe); - ExitOnFailure(hr, "Failed to cache payload: %ls", pPayload->sczKey); - } - else - { - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Invalid data passed to cache complete payload."); - } - -LExit: - ReleaseStr(sczUnverifiedPath); - ReleaseStr(scz); - - return hr; -} - -static HRESULT OnCacheVerifyPayload( - __in HANDLE hPipe, - __in BURN_PACKAGES* pPackages, - __in BURN_PAYLOADS* pPayloads, - __in BYTE* pbData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - LPWSTR scz = NULL; - BURN_PACKAGE* pPackage = NULL; - BURN_PAYLOAD* pPayload = NULL; - LPWSTR sczCacheDirectory = NULL; - - // Deserialize message data. - hr = BuffReadString(pbData, cbData, &iData, &scz); - ExitOnFailure(hr, "Failed to read package id."); - - if (scz && *scz) - { - hr = PackageFindById(pPackages, scz, &pPackage); - ExitOnFailure(hr, "Failed to find package: %ls", scz); - } - - hr = BuffReadString(pbData, cbData, &iData, &scz); - ExitOnFailure(hr, "Failed to read payload id."); - - if (scz && *scz) - { - hr = PayloadFindById(pPayloads, scz, &pPayload); - ExitOnFailure(hr, "Failed to find payload: %ls", scz); - } - - if (pPackage && pPayload) - { - hr = CacheGetCompletedPath(TRUE, pPackage->sczCacheId, &sczCacheDirectory); - ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", pPackage->sczCacheId); - - hr = CacheVerifyPayload(pPayload, sczCacheDirectory, BurnCacheMessageHandler, ElevatedProgressRoutine, hPipe); - } - else - { - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Invalid data passed to cache verify payload."); - } - // Nothing should be logged on failure. - -LExit: - ReleaseStr(sczCacheDirectory); - ReleaseStr(scz); - - return hr; -} - -static void OnCacheCleanup( - __in_z LPCWSTR wzBundleId - ) -{ - CacheCleanup(TRUE, wzBundleId); -} - -static HRESULT OnProcessDependentRegistration( - __in const BURN_REGISTRATION* pRegistration, - __in BYTE* pbData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - BURN_DEPENDENT_REGISTRATION_ACTION action = { }; - - // Deserialize message data. - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&action.type); - ExitOnFailure(hr, "Failed to read action type."); - - hr = BuffReadString(pbData, cbData, &iData, &action.sczBundleId); - ExitOnFailure(hr, "Failed to read bundle id."); - - hr = BuffReadString(pbData, cbData, &iData, &action.sczDependentProviderKey); - ExitOnFailure(hr, "Failed to read dependent provider key."); - - // Execute the registration action. - hr = DependencyProcessDependentRegistration(pRegistration, &action); - ExitOnFailure(hr, "Failed to execute dependent registration action for provider key: %ls", action.sczDependentProviderKey); - -LExit: - // TODO: do the right thing here. - //DependencyUninitializeRegistrationAction(&action); - ReleaseStr(action.sczDependentProviderKey); - ReleaseStr(action.sczBundleId) - - return hr; -} - -static HRESULT OnExecuteExePackage( - __in HANDLE hPipe, - __in BURN_PACKAGES* pPackages, - __in BURN_RELATED_BUNDLES* pRelatedBundles, - __in BURN_VARIABLES* pVariables, - __in BYTE* pbData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - LPWSTR sczPackage = NULL; - DWORD dwRollback = 0; - BURN_EXECUTE_ACTION executeAction = { }; - LPWSTR sczIgnoreDependencies = NULL; - LPWSTR sczAncestors = NULL; - BOOTSTRAPPER_APPLY_RESTART exeRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; - - executeAction.type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE; - - // Deserialize message data. - hr = BuffReadString(pbData, cbData, &iData, &sczPackage); - ExitOnFailure(hr, "Failed to read EXE package id."); - - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.exePackage.action); - ExitOnFailure(hr, "Failed to read action."); - - hr = BuffReadNumber(pbData, cbData, &iData, &dwRollback); - ExitOnFailure(hr, "Failed to read rollback."); - - hr = BuffReadString(pbData, cbData, &iData, &sczIgnoreDependencies); - ExitOnFailure(hr, "Failed to read the list of dependencies to ignore."); - - hr = BuffReadString(pbData, cbData, &iData, &sczAncestors); - ExitOnFailure(hr, "Failed to read the list of ancestors."); - - hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); - ExitOnFailure(hr, "Failed to read variables."); - - hr = PackageFindById(pPackages, sczPackage, &executeAction.exePackage.pPackage); - if (E_NOTFOUND == hr) - { - hr = PackageFindRelatedById(pRelatedBundles, sczPackage, &executeAction.exePackage.pPackage); - } - ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); - - // Pass the list of dependencies to ignore, if any, to the related bundle. - if (sczIgnoreDependencies && *sczIgnoreDependencies) - { - hr = StrAllocString(&executeAction.exePackage.sczIgnoreDependencies, sczIgnoreDependencies, 0); - ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore."); - } - - // Pass the list of ancestors, if any, to the related bundle. - if (sczAncestors && *sczAncestors) - { - hr = StrAllocString(&executeAction.exePackage.sczAncestors, sczAncestors, 0); - ExitOnFailure(hr, "Failed to allocate the list of ancestors."); - } - - // Execute EXE package. - hr = ExeEngineExecutePackage(&executeAction, pVariables, static_cast(dwRollback), GenericExecuteMessageHandler, hPipe, &exeRestart); - ExitOnFailure(hr, "Failed to execute EXE package."); - -LExit: - ReleaseStr(sczAncestors); - ReleaseStr(sczIgnoreDependencies); - ReleaseStr(sczPackage); - PlanUninitializeExecuteAction(&executeAction); - - if (SUCCEEDED(hr)) - { - if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == exeRestart) - { - hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); - } - else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == exeRestart) - { - hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED); - } - } - - return hr; -} - -static HRESULT OnExecuteMsiPackage( - __in HANDLE hPipe, - __in BURN_PACKAGES* pPackages, - __in BURN_VARIABLES* pVariables, - __in BYTE* pbData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - LPWSTR sczPackage = NULL; - HWND hwndParent = NULL; - BOOL fRollback = 0; - BURN_EXECUTE_ACTION executeAction = { }; - BOOTSTRAPPER_APPLY_RESTART msiRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; - - executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE; - - // Deserialize message data. - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback); - ExitOnFailure(hr, "Failed to read rollback flag."); - - hr = BuffReadString(pbData, cbData, &iData, &sczPackage); - ExitOnFailure(hr, "Failed to read MSI package id."); - - hr = PackageFindById(pPackages, sczPackage, &executeAction.msiPackage.pPackage); - ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); - - hr = BuffReadPointer(pbData, cbData, &iData, (DWORD_PTR*)&hwndParent); - ExitOnFailure(hr, "Failed to read parent hwnd."); - - hr = BuffReadString(pbData, cbData, &iData, &executeAction.msiPackage.sczLogPath); - ExitOnFailure(hr, "Failed to read package log."); - - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.actionMsiProperty); - ExitOnFailure(hr, "Failed to read actionMsiProperty."); - - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.uiLevel); - ExitOnFailure(hr, "Failed to read UI level."); - - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.fDisableExternalUiHandler); - ExitOnFailure(hr, "Failed to read fDisableExternalUiHandler."); - - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.action); - ExitOnFailure(hr, "Failed to read action."); - - // Read feature actions. - if (executeAction.msiPackage.pPackage->Msi.cFeatures) - { - executeAction.msiPackage.rgFeatures = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(executeAction.msiPackage.pPackage->Msi.cFeatures * sizeof(BOOTSTRAPPER_FEATURE_ACTION), TRUE); - ExitOnNull(executeAction.msiPackage.rgFeatures, hr, E_OUTOFMEMORY, "Failed to allocate memory for feature actions."); - - for (DWORD i = 0; i < executeAction.msiPackage.pPackage->Msi.cFeatures; ++i) - { - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.rgFeatures[i]); - ExitOnFailure(hr, "Failed to read feature action."); - } - } - - // Read slipstream patches actions. - if (executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages) - { - for (DWORD i = 0; i < executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++i) - { - BURN_SLIPSTREAM_MSP* pSlipstreamMsp = executeAction.msiPackage.pPackage->Msi.rgSlipstreamMsps + i; - BOOTSTRAPPER_ACTION_STATE* pAction = fRollback ? &pSlipstreamMsp->rollback : &pSlipstreamMsp->execute; - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)pAction); - ExitOnFailure(hr, "Failed to read slipstream action."); - } - } - - hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); - ExitOnFailure(hr, "Failed to read variables."); - - // Execute MSI package. - hr = MsiEngineExecutePackage(hwndParent, &executeAction, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &msiRestart); - ExitOnFailure(hr, "Failed to execute MSI package."); - -LExit: - ReleaseStr(sczPackage); - PlanUninitializeExecuteAction(&executeAction); - - if (SUCCEEDED(hr)) - { - if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == msiRestart) - { - hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); - } - else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == msiRestart) - { - hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED); - } - } - - return hr; -} - -static HRESULT OnExecuteMspPackage( - __in HANDLE hPipe, - __in BURN_PACKAGES* pPackages, - __in BURN_VARIABLES* pVariables, - __in BYTE* pbData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - LPWSTR sczPackage = NULL; - HWND hwndParent = NULL; - BOOL fRollback = 0; - BURN_EXECUTE_ACTION executeAction = { }; - BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; - - executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSP_TARGET; - - // Deserialize message data. - hr = BuffReadString(pbData, cbData, &iData, &sczPackage); - ExitOnFailure(hr, "Failed to read MSP package id."); - - hr = PackageFindById(pPackages, sczPackage, &executeAction.mspTarget.pPackage); - ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); - - hr = BuffReadPointer(pbData, cbData, &iData, (DWORD_PTR*)&hwndParent); - ExitOnFailure(hr, "Failed to read parent hwnd."); - - executeAction.mspTarget.fPerMachineTarget = TRUE; // we're in the elevated process, clearly we're targeting a per-machine product. - - hr = BuffReadString(pbData, cbData, &iData, &executeAction.mspTarget.sczTargetProductCode); - ExitOnFailure(hr, "Failed to read target product code."); - - hr = BuffReadString(pbData, cbData, &iData, &executeAction.mspTarget.sczLogPath); - ExitOnFailure(hr, "Failed to read package log."); - - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.actionMsiProperty); - ExitOnFailure(hr, "Failed to read actionMsiProperty."); - - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.uiLevel); - ExitOnFailure(hr, "Failed to read UI level."); - - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.fDisableExternalUiHandler); - ExitOnFailure(hr, "Failed to read fDisableExternalUiHandler."); - - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.action); - ExitOnFailure(hr, "Failed to read action."); - - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.cOrderedPatches); - ExitOnFailure(hr, "Failed to read count of ordered patches."); - - if (executeAction.mspTarget.cOrderedPatches) - { - executeAction.mspTarget.rgOrderedPatches = (BURN_ORDERED_PATCHES*)MemAlloc(executeAction.mspTarget.cOrderedPatches * sizeof(BURN_ORDERED_PATCHES), TRUE); - ExitOnNull(executeAction.mspTarget.rgOrderedPatches, hr, E_OUTOFMEMORY, "Failed to allocate memory for ordered patches."); - - for (DWORD i = 0; i < executeAction.mspTarget.cOrderedPatches; ++i) - { - hr = BuffReadString(pbData, cbData, &iData, &sczPackage); - ExitOnFailure(hr, "Failed to read ordered patch package id."); - - hr = PackageFindById(pPackages, sczPackage, &executeAction.mspTarget.rgOrderedPatches[i].pPackage); - ExitOnFailure(hr, "Failed to find ordered patch package: %ls", sczPackage); - } - } - - hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); - ExitOnFailure(hr, "Failed to read variables."); - - hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback); - ExitOnFailure(hr, "Failed to read rollback flag."); - - // Execute MSP package. - hr = MspEngineExecutePackage(hwndParent, &executeAction, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &restart); - ExitOnFailure(hr, "Failed to execute MSP package."); - -LExit: - ReleaseStr(sczPackage); - PlanUninitializeExecuteAction(&executeAction); - - if (SUCCEEDED(hr)) - { - if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == restart) - { - hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); - } - else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart) - { - hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED); - } - } - - return hr; -} - -static HRESULT OnExecuteMsuPackage( - __in HANDLE hPipe, - __in BURN_PACKAGES* pPackages, - __in BURN_VARIABLES* pVariables, - __in BYTE* pbData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - LPWSTR sczPackage = NULL; - DWORD dwRollback = 0; - DWORD dwStopWusaService = 0; - BURN_EXECUTE_ACTION executeAction = { }; - BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; - - executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE; - - // Deserialize message data. - hr = BuffReadString(pbData, cbData, &iData, &sczPackage); - ExitOnFailure(hr, "Failed to read MSU package id."); - - hr = BuffReadString(pbData, cbData, &iData, &executeAction.msuPackage.sczLogPath); - ExitOnFailure(hr, "Failed to read package log."); - - hr = BuffReadNumber(pbData, cbData, &iData, reinterpret_cast(&executeAction.msuPackage.action)); - ExitOnFailure(hr, "Failed to read action."); - - hr = BuffReadNumber(pbData, cbData, &iData, &dwRollback); - ExitOnFailure(hr, "Failed to read rollback."); - - hr = BuffReadNumber(pbData, cbData, &iData, &dwStopWusaService); - ExitOnFailure(hr, "Failed to read StopWusaService."); - - hr = PackageFindById(pPackages, sczPackage, &executeAction.msuPackage.pPackage); - ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); - - // execute MSU package - hr = MsuEngineExecutePackage(&executeAction, pVariables, static_cast(dwRollback), static_cast(dwStopWusaService), GenericExecuteMessageHandler, hPipe, &restart); - ExitOnFailure(hr, "Failed to execute MSU package."); - -LExit: - ReleaseStr(sczPackage); - PlanUninitializeExecuteAction(&executeAction); - - if (SUCCEEDED(hr)) - { - if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == restart) - { - hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); - } - else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart) - { - hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED); - } - } - - return hr; -} - -static HRESULT OnExecutePackageProviderAction( - __in BURN_PACKAGES* pPackages, - __in BURN_RELATED_BUNDLES* pRelatedBundles, - __in BYTE* pbData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - LPWSTR sczPackage = NULL; - BURN_EXECUTE_ACTION executeAction = { }; - - executeAction.type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER; - - // Deserialize the message data. - hr = BuffReadString(pbData, cbData, &iData, &sczPackage); - ExitOnFailure(hr, "Failed to read package id from message buffer."); - - hr = BuffReadNumber(pbData, cbData, &iData, reinterpret_cast(&executeAction.packageProvider.action)); - ExitOnFailure(hr, "Failed to read action."); - - // Find the package again. - hr = PackageFindById(pPackages, sczPackage, &executeAction.packageProvider.pPackage); - if (E_NOTFOUND == hr) - { - hr = PackageFindRelatedById(pRelatedBundles, sczPackage, &executeAction.packageProvider.pPackage); - } - ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); - - // Execute the package provider action. - hr = DependencyExecutePackageProviderAction(&executeAction); - ExitOnFailure(hr, "Failed to execute package provider action."); - -LExit: - ReleaseStr(sczPackage); - PlanUninitializeExecuteAction(&executeAction); - - return hr; -} - -static HRESULT OnExecutePackageDependencyAction( - __in BURN_PACKAGES* pPackages, - __in BURN_RELATED_BUNDLES* pRelatedBundles, - __in BYTE* pbData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - LPWSTR sczPackage = NULL; - BURN_EXECUTE_ACTION executeAction = { }; - - executeAction.type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY; - - // Deserialize the message data. - hr = BuffReadString(pbData, cbData, &iData, &sczPackage); - ExitOnFailure(hr, "Failed to read package id from message buffer."); - - hr = BuffReadString(pbData, cbData, &iData, &executeAction.packageDependency.sczBundleProviderKey); - ExitOnFailure(hr, "Failed to read bundle dependency key from message buffer."); - - hr = BuffReadNumber(pbData, cbData, &iData, reinterpret_cast(&executeAction.packageDependency.action)); - ExitOnFailure(hr, "Failed to read action."); - - // Find the package again. - hr = PackageFindById(pPackages, sczPackage, &executeAction.packageDependency.pPackage); - if (E_NOTFOUND == hr) - { - hr = PackageFindRelatedById(pRelatedBundles, sczPackage, &executeAction.packageDependency.pPackage); - } - ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); - - // Execute the package dependency action. - hr = DependencyExecutePackageDependencyAction(TRUE, &executeAction); - ExitOnFailure(hr, "Failed to execute package dependency action."); - -LExit: - ReleaseStr(sczPackage); - PlanUninitializeExecuteAction(&executeAction); - - return hr; -} - -static HRESULT CALLBACK BurnCacheMessageHandler( - __in BURN_CACHE_MESSAGE* pMessage, - __in LPVOID pvContext - ) -{ - HRESULT hr = S_OK; - DWORD dwResult = 0; - HANDLE hPipe = (HANDLE)pvContext; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwMessage = 0; - - switch (pMessage->type) - { - case BURN_CACHE_MESSAGE_BEGIN: - // serialize message data - hr = BuffWriteNumber(&pbData, &cbData, pMessage->begin.cacheStep); - ExitOnFailure(hr, "Failed to write progress percentage to message buffer."); - - dwMessage = BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_BEGIN; - break; - - case BURN_CACHE_MESSAGE_COMPLETE: - // serialize message data - hr = BuffWriteNumber(&pbData, &cbData, pMessage->complete.hrStatus); - ExitOnFailure(hr, "Failed to write error code to message buffer."); - - dwMessage = BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_COMPLETE; - break; - - case BURN_CACHE_MESSAGE_SUCCESS: - hr = BuffWriteNumber64(&pbData, &cbData, pMessage->success.qwFileSize); - ExitOnFailure(hr, "Failed to count of files in use to message buffer."); - - dwMessage = BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_SUCCESS; - break; - } - - // send message - hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send burn cache message to per-user process."); - - hr = dwResult; - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -static DWORD CALLBACK ElevatedProgressRoutine( - __in LARGE_INTEGER TotalFileSize, - __in LARGE_INTEGER TotalBytesTransferred, - __in LARGE_INTEGER /*StreamSize*/, - __in LARGE_INTEGER /*StreamBytesTransferred*/, - __in DWORD /*dwStreamNumber*/, - __in DWORD /*dwCallbackReason*/, - __in HANDLE /*hSourceFile*/, - __in HANDLE /*hDestinationFile*/, - __in_opt LPVOID lpData - ) -{ - HRESULT hr = S_OK; - DWORD dwResult = 0; - HANDLE hPipe = (HANDLE)lpData; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwMessage = BURN_ELEVATION_MESSAGE_TYPE_PROGRESS_ROUTINE; - - hr = BuffWriteNumber64(&pbData, &cbData, TotalFileSize.QuadPart); - ExitOnFailure(hr, "Failed to write total file size progress to message buffer."); - - hr = BuffWriteNumber64(&pbData, &cbData, TotalBytesTransferred.QuadPart); - ExitOnFailure(hr, "Failed to write total bytes transferred progress to message buffer."); - - // send message - hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send progress routine message to per-user process."); - -LExit: - ReleaseBuffer(pbData); - - return dwResult; -} - -static int GenericExecuteMessageHandler( - __in GENERIC_EXECUTE_MESSAGE* pMessage, - __in LPVOID pvContext - ) -{ - HRESULT hr = S_OK; - int nResult = IDOK; - HANDLE hPipe = (HANDLE)pvContext; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwMessage = 0; - - hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwAllowedResults); - ExitOnFailure(hr, "Failed to write UI flags."); - - switch(pMessage->type) - { - case GENERIC_EXECUTE_MESSAGE_PROGRESS: - // serialize message data - hr = BuffWriteNumber(&pbData, &cbData, pMessage->progress.dwPercentage); - ExitOnFailure(hr, "Failed to write progress percentage to message buffer."); - - dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS; - break; - - case GENERIC_EXECUTE_MESSAGE_ERROR: - // serialize message data - hr = BuffWriteNumber(&pbData, &cbData, pMessage->error.dwErrorCode); - ExitOnFailure(hr, "Failed to write error code to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pMessage->error.wzMessage); - ExitOnFailure(hr, "Failed to write message to message buffer."); - - dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR; - break; - - case GENERIC_EXECUTE_MESSAGE_FILES_IN_USE: - hr = BuffWriteNumber(&pbData, &cbData, pMessage->filesInUse.cFiles); - ExitOnFailure(hr, "Failed to count of files in use to message buffer."); - - for (DWORD i = 0; i < pMessage->filesInUse.cFiles; ++i) - { - hr = BuffWriteString(&pbData, &cbData, pMessage->filesInUse.rgwzFiles[i]); - ExitOnFailure(hr, "Failed to write file in use to message buffer."); - } - - dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE; - break; - } - - // send message - hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, reinterpret_cast(&nResult)); - ExitOnFailure(hr, "Failed to send message to per-user process."); - -LExit: - ReleaseBuffer(pbData); - - return nResult; -} - -static int MsiExecuteMessageHandler( - __in WIU_MSI_EXECUTE_MESSAGE* pMessage, - __in_opt LPVOID pvContext - ) -{ - HRESULT hr = S_OK; - int nResult = IDOK; - HANDLE hPipe = (HANDLE)pvContext; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwMessage = 0; - - // Always send any extra data via the struct first. - hr = BuffWriteNumber(&pbData, &cbData, pMessage->cData); - ExitOnFailure(hr, "Failed to write MSI data count to message buffer."); - - for (DWORD i = 0; i < pMessage->cData; ++i) - { - hr = BuffWriteString(&pbData, &cbData, pMessage->rgwzData[i]); - ExitOnFailure(hr, "Failed to write MSI data to message buffer."); - } - - hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwAllowedResults); - ExitOnFailure(hr, "Failed to write UI flags."); - - switch (pMessage->type) - { - case WIU_MSI_EXECUTE_MESSAGE_PROGRESS: - // serialize message data - hr = BuffWriteNumber(&pbData, &cbData, pMessage->progress.dwPercentage); - ExitOnFailure(hr, "Failed to write progress percentage to message buffer."); - - // set message id - dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS; - break; - - case WIU_MSI_EXECUTE_MESSAGE_ERROR: - // serialize message data - hr = BuffWriteNumber(&pbData, &cbData, pMessage->error.dwErrorCode); - ExitOnFailure(hr, "Failed to write error code to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pMessage->error.wzMessage); - ExitOnFailure(hr, "Failed to write message to message buffer."); - - // set message id - dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR; - break; - - case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE: - // serialize message data - hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pMessage->msiMessage.mt); - ExitOnFailure(hr, "Failed to write MSI message type to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, pMessage->msiMessage.wzMessage); - ExitOnFailure(hr, "Failed to write message to message buffer."); - - // set message id - dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE; - break; - - case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE: - // NOTE: we do not serialize other message data here because all the "files in use" are in the data above. - - // set message id - dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE; - break; - - default: - hr = E_UNEXPECTED; - ExitOnFailure(hr, "Invalid message type: %d", pMessage->type); - } - - // send message - hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, (DWORD*)&nResult); - ExitOnFailure(hr, "Failed to send msi message to per-user process."); - -LExit: - ReleaseBuffer(pbData); - - return nResult; -} - -static HRESULT OnCleanPackage( - __in BURN_PACKAGES* pPackages, - __in BYTE* pbData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - LPWSTR sczPackage = NULL; - BURN_PACKAGE* pPackage = NULL; - - // Deserialize message data. - hr = BuffReadString(pbData, cbData, &iData, &sczPackage); - ExitOnFailure(hr, "Failed to read package id."); - - hr = PackageFindById(pPackages, sczPackage, &pPackage); - ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); - - // Remove the package from the cache. - hr = CacheRemovePackage(TRUE, pPackage->sczId, pPackage->sczCacheId); - ExitOnFailure(hr, "Failed to remove from cache package: %ls", pPackage->sczId); - -LExit: - ReleaseStr(sczPackage); - return hr; -} - -static HRESULT OnLaunchApprovedExe( - __in HANDLE hPipe, - __in BURN_APPROVED_EXES* pApprovedExes, - __in BURN_VARIABLES* pVariables, - __in BYTE* pbData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe = NULL; - BURN_APPROVED_EXE* pApprovedExe = NULL; - REGSAM samDesired = KEY_QUERY_VALUE; - HKEY hKey = NULL; - DWORD dwProcessId = 0; - BYTE* pbSendData = NULL; - SIZE_T cbSendData = 0; - DWORD dwResult = 0; - - pLaunchApprovedExe = (BURN_LAUNCH_APPROVED_EXE*)MemAlloc(sizeof(BURN_LAUNCH_APPROVED_EXE), TRUE); - - // Deserialize message data. - hr = BuffReadString(pbData, cbData, &iData, &pLaunchApprovedExe->sczId); - ExitOnFailure(hr, "Failed to read approved exe id."); - - hr = BuffReadString(pbData, cbData, &iData, &pLaunchApprovedExe->sczArguments); - ExitOnFailure(hr, "Failed to read approved exe arguments."); - - hr = BuffReadNumber(pbData, cbData, &iData, &pLaunchApprovedExe->dwWaitForInputIdleTimeout); - ExitOnFailure(hr, "Failed to read approved exe WaitForInputIdle timeout."); - - hr = ApprovedExesFindById(pApprovedExes, pLaunchApprovedExe->sczId, &pApprovedExe); - ExitOnFailure(hr, "The per-user process requested unknown approved exe with id: %ls", pLaunchApprovedExe->sczId); - - LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_SEARCH, pApprovedExe->sczKey, pApprovedExe->sczValueName ? pApprovedExe->sczValueName : L"", pApprovedExe->fWin64 ? L"yes" : L"no"); - - if (pApprovedExe->fWin64) - { - samDesired |= KEY_WOW64_64KEY; - } - - hr = RegOpen(HKEY_LOCAL_MACHINE, pApprovedExe->sczKey, samDesired, &hKey); - ExitOnFailure(hr, "Failed to open the registry key for the approved exe path."); - - hr = RegReadString(hKey, pApprovedExe->sczValueName, &pLaunchApprovedExe->sczExecutablePath); - ExitOnFailure(hr, "Failed to read the value for the approved exe path."); - - hr = ApprovedExesVerifySecureLocation(pVariables, pLaunchApprovedExe); - ExitOnFailure(hr, "Failed to verify the executable path is in a secure location: %ls", pLaunchApprovedExe->sczExecutablePath); - if (S_FALSE == hr) - { - LogStringLine(REPORT_STANDARD, "The executable path is not in a secure location: %ls", pLaunchApprovedExe->sczExecutablePath); - ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)); - } - - hr = ApprovedExesLaunch(pVariables, pLaunchApprovedExe, &dwProcessId); - ExitOnFailure(hr, "Failed to launch approved exe: %ls", pLaunchApprovedExe->sczExecutablePath); - - //send process id over pipe - hr = BuffWriteNumber(&pbSendData, &cbSendData, dwProcessId); - ExitOnFailure(hr, "Failed to write the approved exe process id to message buffer."); - - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID, pbSendData, cbSendData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID message to per-user process."); - -LExit: - ReleaseBuffer(pbSendData); - ApprovedExesUninitializeLaunch(pLaunchApprovedExe); - return hr; -} - -static HRESULT OnMsiBeginTransaction( - __in BURN_PACKAGES* pPackages, - __in BYTE* pbData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - LPWSTR sczId = NULL; - LPWSTR sczLogPath = NULL; - BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; - - // Deserialize message data. - hr = BuffReadString(pbData, cbData, &iData, &sczId); - ExitOnFailure(hr, "Failed to read rollback boundary id."); - - hr = BuffReadString(pbData, cbData, &iData, &sczLogPath); - ExitOnFailure(hr, "Failed to read transaction log path."); - - hr = PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary); - ExitOnFailure(hr, "Failed to find rollback boundary: %ls", sczId); - - pRollbackBoundary->sczLogPath = sczLogPath; - - hr = MsiEngineBeginTransaction(pRollbackBoundary); - -LExit: - ReleaseStr(sczId); - ReleaseStr(sczLogPath); - - if (pRollbackBoundary) - { - pRollbackBoundary->sczLogPath = NULL; - } - - return hr; -} - -static HRESULT OnMsiCommitTransaction( - __in BURN_PACKAGES* pPackages, - __in BYTE* pbData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - LPWSTR sczId = NULL; - LPWSTR sczLogPath = NULL; - BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; - - // Deserialize message data. - hr = BuffReadString(pbData, cbData, &iData, &sczId); - ExitOnFailure(hr, "Failed to read rollback boundary id."); - - hr = BuffReadString(pbData, cbData, &iData, &sczLogPath); - ExitOnFailure(hr, "Failed to read transaction log path."); - - hr = PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary); - ExitOnFailure(hr, "Failed to find rollback boundary: %ls", sczId); - - pRollbackBoundary->sczLogPath = sczLogPath; - - hr = MsiEngineCommitTransaction(pRollbackBoundary); - -LExit: - ReleaseStr(sczId); - ReleaseStr(sczLogPath); - - if (pRollbackBoundary) - { - pRollbackBoundary->sczLogPath = NULL; - } - - return hr; -} - -static HRESULT OnMsiRollbackTransaction( - __in BURN_PACKAGES* pPackages, - __in BYTE* pbData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - LPWSTR sczId = NULL; - LPWSTR sczLogPath = NULL; - BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; - - // Deserialize message data. - hr = BuffReadString(pbData, cbData, &iData, &sczId); - ExitOnFailure(hr, "Failed to read rollback boundary id."); - - hr = BuffReadString(pbData, cbData, &iData, &sczLogPath); - ExitOnFailure(hr, "Failed to read transaction log path."); - - hr = PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary); - ExitOnFailure(hr, "Failed to find rollback boundary: %ls", sczId); - - pRollbackBoundary->sczLogPath = sczLogPath; - - hr = MsiEngineRollbackTransaction(pRollbackBoundary); - -LExit: - ReleaseStr(sczId); - ReleaseStr(sczLogPath); - - if (pRollbackBoundary) - { - pRollbackBoundary->sczLogPath = NULL; - } - - return hr; -} - -static HRESULT ElevatedOnPauseAUBegin( - __in HANDLE hPipe - ) -{ - HRESULT hr = S_OK; - DWORD dwResult = 0; - - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_BEGIN, NULL, 0, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_BEGIN message to per-user process."); - -LExit: - return hr; -} - -static HRESULT ElevatedOnPauseAUComplete( - __in HANDLE hPipe, - __in HRESULT hrStatus - ) -{ - HRESULT hr = S_OK; - BYTE* pbSendData = NULL; - SIZE_T cbSendData = 0; - DWORD dwResult = 0; - - hr = BuffWriteNumber(&pbSendData, &cbSendData, hrStatus); - ExitOnFailure(hr, "Failed to write the pause au status to message buffer."); - - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_COMPLETE, pbSendData, cbSendData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_COMPLETE message to per-user process."); - -LExit: - ReleaseBuffer(pbSendData); - - return hr; -} - -static HRESULT ElevatedOnSystemRestorePointBegin( - __in HANDLE hPipe - ) -{ - HRESULT hr = S_OK; - DWORD dwResult = 0; - - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_BEGIN, NULL, 0, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_BEGIN message to per-user process."); - -LExit: - return hr; -} - -static HRESULT ElevatedOnSystemRestorePointComplete( - __in HANDLE hPipe, - __in HRESULT hrStatus - ) -{ - HRESULT hr = S_OK; - BYTE* pbSendData = NULL; - SIZE_T cbSendData = 0; - DWORD dwResult = 0; - - hr = BuffWriteNumber(&pbSendData, &cbSendData, hrStatus); - ExitOnFailure(hr, "Failed to write the system restore point status to message buffer."); - - hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_COMPLETE, pbSendData, cbSendData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_COMPLETE message to per-user process."); - -LExit: - ReleaseBuffer(pbSendData); - - return hr; -} diff --git a/src/engine/elevation.h b/src/engine/elevation.h deleted file mode 100644 index 9244f36c..00000000 --- a/src/engine/elevation.h +++ /dev/null @@ -1,176 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - - -// Parent (per-user process) side functions. -HRESULT ElevationElevate( - __in BURN_ENGINE_STATE* pEngineState, - __in_opt HWND hwndParent - ); -HRESULT ElevationApplyInitialize( - __in HANDLE hPipe, - __in BURN_USER_EXPERIENCE* pBA, - __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_ACTION action, - __in BURN_AU_PAUSE_ACTION auAction, - __in BOOL fTakeSystemRestorePoint - ); -HRESULT ElevationApplyUninitialize( - __in HANDLE hPipe - ); -HRESULT ElevationSessionBegin( - __in HANDLE hPipe, - __in_z LPCWSTR wzEngineWorkingPath, - __in_z LPCWSTR wzResumeCommandLine, - __in BOOL fDisableResume, - __in BURN_VARIABLES* pVariables, - __in DWORD dwRegistrationOperations, - __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction, - __in DWORD64 qwEstimatedSize - ); -HRESULT ElevationSessionResume( - __in HANDLE hPipe, - __in_z LPCWSTR wzResumeCommandLine, - __in BOOL fDisableResume, - __in BURN_VARIABLES* pVariables - ); -HRESULT ElevationSessionEnd( - __in HANDLE hPipe, - __in BURN_RESUME_MODE resumeMode, - __in BOOTSTRAPPER_APPLY_RESTART restart, - __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction - ); -HRESULT ElevationSaveState( - __in HANDLE hPipe, - __in_bcount(cbBuffer) BYTE* pbBuffer, - __in SIZE_T cbBuffer - ); -HRESULT ElevationCacheCompletePayload( - __in HANDLE hPipe, - __in BURN_PACKAGE* pPackage, - __in BURN_PAYLOAD* pPayload, - __in_z LPCWSTR wzUnverifiedPath, - __in BOOL fMove, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ); -HRESULT ElevationCacheVerifyPayload( - __in HANDLE hPipe, - __in BURN_PACKAGE* pPackage, - __in BURN_PAYLOAD* pPayload, - __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, - __in LPPROGRESS_ROUTINE pfnProgress, - __in LPVOID pContext - ); -HRESULT ElevationCacheCleanup( - __in HANDLE hPipe - ); -HRESULT ElevationProcessDependentRegistration( - __in HANDLE hPipe, - __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction - ); -HRESULT ElevationExecuteExePackage( - __in HANDLE hPipe, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_VARIABLES* pVariables, - __in BOOL fRollback, - __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress, - __in LPVOID pvContext, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ); -HRESULT ElevationExecuteMsiPackage( - __in HANDLE hPipe, - __in_opt HWND hwndParent, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_VARIABLES* pVariables, - __in BOOL fRollback, - __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, - __in LPVOID pvContext, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ); -HRESULT ElevationExecuteMspPackage( - __in HANDLE hPipe, - __in_opt HWND hwndParent, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_VARIABLES* pVariables, - __in BOOL fRollback, - __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, - __in LPVOID pvContext, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ); -HRESULT ElevationExecuteMsuPackage( - __in HANDLE hPipe, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BOOL fRollback, - __in BOOL fStopWusaService, - __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress, - __in LPVOID pvContext, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ); -HRESULT ElevationExecutePackageProviderAction( - __in HANDLE hPipe, - __in BURN_EXECUTE_ACTION* pExecuteAction - ); -HRESULT ElevationExecutePackageDependencyAction( - __in HANDLE hPipe, - __in BURN_EXECUTE_ACTION* pExecuteAction - ); -HRESULT ElevationLaunchElevatedChild( - __in HANDLE hPipe, - __in BURN_PACKAGE* pPackage, - __in LPCWSTR wzPipeName, - __in LPCWSTR wzPipeToken, - __out DWORD* pdwChildPid - ); -HRESULT ElevationCleanPackage( - __in HANDLE hPipe, - __in BURN_PACKAGE* pPackage - ); -HRESULT ElevationLaunchApprovedExe( - __in HANDLE hPipe, - __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe, - __out DWORD* pdwProcessId - ); - -// Child (per-machine process) side functions. -HRESULT ElevationChildPumpMessages( - __in DWORD dwLoggingTlsId, - __in HANDLE hPipe, - __in HANDLE hCachePipe, - __in BURN_APPROVED_EXES* pApprovedExes, - __in BURN_CONTAINERS* pContainers, - __in BURN_PACKAGES* pPackages, - __in BURN_PAYLOADS* pPayloads, - __in BURN_VARIABLES* pVariables, - __in BURN_REGISTRATION* pRegistration, - __in BURN_USER_EXPERIENCE* pUserExperience, - __out HANDLE* phLock, - __out BOOL* pfDisabledAutomaticUpdates, - __out DWORD* pdwChildExitCode, - __out BOOL* pfRestart - ); -HRESULT ElevationChildResumeAutomaticUpdates(); - - -HRESULT ElevationMsiBeginTransaction( - __in HANDLE hPipe, - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary - ); -HRESULT ElevationMsiCommitTransaction( - __in HANDLE hPipe, - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary - ); -HRESULT ElevationMsiRollbackTransaction( - __in HANDLE hPipe, - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/engine/embedded.cpp b/src/engine/embedded.cpp deleted file mode 100644 index 03898ebd..00000000 --- a/src/engine/embedded.cpp +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// struct - -struct BURN_EMBEDDED_CALLBACK_CONTEXT -{ - PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler; - LPVOID pvContext; -}; - -// internal function declarations - -static HRESULT ProcessEmbeddedMessages( - __in BURN_PIPE_MESSAGE* pMsg, - __in_opt LPVOID pvContext, - __out DWORD* pdwResult - ); -static HRESULT OnEmbeddedErrorMessage( - __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, - __in LPVOID pvContext, - __in_bcount(cbData) BYTE* pbData, - __in SIZE_T cbData, - __out DWORD* pdwResult - ); -static HRESULT OnEmbeddedProgress( - __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, - __in LPVOID pvContext, - __in_bcount(cbData) BYTE* pbData, - __in SIZE_T cbData, - __out DWORD* pdwResult - ); - -// function definitions - -/******************************************************************* - EmbeddedLaunchChildProcess - - -*******************************************************************/ -extern "C" HRESULT EmbeddedRunBundle( - __in LPCWSTR wzExecutablePath, - __in LPCWSTR wzArguments, - __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, - __in LPVOID pvContext, - __out DWORD* pdwExitCode - ) -{ - HRESULT hr = S_OK; - DWORD dwCurrentProcessId = ::GetCurrentProcessId(); - HANDLE hCreatedPipesEvent = NULL; - LPWSTR sczCommand = NULL; - STARTUPINFOW si = { }; - PROCESS_INFORMATION pi = { }; - BURN_PIPE_RESULT result = { }; - - BURN_PIPE_CONNECTION connection = { }; - PipeConnectionInitialize(&connection); - - BURN_EMBEDDED_CALLBACK_CONTEXT context = { }; - context.pfnGenericMessageHandler = pfnGenericMessageHandler; - context.pvContext = pvContext; - - hr = PipeCreateNameAndSecret(&connection.sczName, &connection.sczSecret); - ExitOnFailure(hr, "Failed to create embedded pipe name and client token."); - - hr = PipeCreatePipes(&connection, FALSE, &hCreatedPipesEvent); - ExitOnFailure(hr, "Failed to create embedded pipe."); - - hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls %ls %ls %u", wzArguments, BURN_COMMANDLINE_SWITCH_EMBEDDED, connection.sczName, connection.sczSecret, dwCurrentProcessId); - ExitOnFailure(hr, "Failed to allocate embedded command."); - - if (!::CreateProcessW(wzExecutablePath, sczCommand, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) - { - ExitWithLastError(hr, "Failed to create embedded process at path: %ls", wzExecutablePath); - } - - connection.dwProcessId = ::GetProcessId(pi.hProcess); - connection.hProcess = pi.hProcess; - pi.hProcess = NULL; - - hr = PipeWaitForChildConnect(&connection); - ExitOnFailure(hr, "Failed to wait for embedded process to connect to pipe."); - - hr = PipePumpMessages(connection.hPipe, ProcessEmbeddedMessages, &context, &result); - ExitOnFailure(hr, "Failed to process messages from embedded message."); - - // Get the return code from the embedded process. - hr = ProcWaitForCompletion(connection.hProcess, INFINITE, pdwExitCode); - ExitOnFailure(hr, "Failed to wait for embedded executable: %ls", wzExecutablePath); - -LExit: - ReleaseHandle(pi.hThread); - ReleaseHandle(pi.hProcess); - - StrSecureZeroFreeString(sczCommand); - ReleaseHandle(hCreatedPipesEvent); - PipeConnectionUninitialize(&connection); - - return hr; -} - - -// internal function definitions - -static HRESULT ProcessEmbeddedMessages( - __in BURN_PIPE_MESSAGE* pMsg, - __in_opt LPVOID pvContext, - __out DWORD* pdwResult - ) -{ - HRESULT hr = S_OK; - BURN_EMBEDDED_CALLBACK_CONTEXT* pContext = static_cast(pvContext); - DWORD dwResult = 0; - - // Process the message. - switch (pMsg->dwMessage) - { - case BURN_EMBEDDED_MESSAGE_TYPE_ERROR: - hr = OnEmbeddedErrorMessage(pContext->pfnGenericMessageHandler, pContext->pvContext, static_cast(pMsg->pvData), pMsg->cbData, &dwResult); - ExitOnFailure(hr, "Failed to process embedded error message."); - break; - - case BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS: - hr = OnEmbeddedProgress(pContext->pfnGenericMessageHandler, pContext->pvContext, static_cast(pMsg->pvData), pMsg->cbData, &dwResult); - ExitOnFailure(hr, "Failed to process embedded progress message."); - break; - - default: - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Unexpected embedded message sent to child process, msg: %u", pMsg->dwMessage); - } - - *pdwResult = dwResult; - -LExit: - return hr; -} - -static HRESULT OnEmbeddedErrorMessage( - __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, - __in LPVOID pvContext, - __in_bcount(cbData) BYTE* pbData, - __in SIZE_T cbData, - __out DWORD* pdwResult - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - GENERIC_EXECUTE_MESSAGE message = { }; - LPWSTR sczMessage = NULL; - - message.type = GENERIC_EXECUTE_MESSAGE_ERROR; - - hr = BuffReadNumber(pbData, cbData, &iData, &message.error.dwErrorCode); - ExitOnFailure(hr, "Failed to read error code from buffer."); - - hr = BuffReadString(pbData, cbData, &iData, &sczMessage); - ExitOnFailure(hr, "Failed to read error message from buffer."); - - message.error.wzMessage = sczMessage; - - hr = BuffReadNumber(pbData, cbData, &iData, &message.dwAllowedResults); - ExitOnFailure(hr, "Failed to read UI hint from buffer."); - - *pdwResult = (DWORD)pfnMessageHandler(&message, pvContext); - -LExit: - ReleaseStr(sczMessage); - - return hr; -} - -static HRESULT OnEmbeddedProgress( - __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, - __in LPVOID pvContext, - __in_bcount(cbData) BYTE* pbData, - __in SIZE_T cbData, - __out DWORD* pdwResult - ) -{ - HRESULT hr = S_OK; - SIZE_T iData = 0; - GENERIC_EXECUTE_MESSAGE message = { }; - - message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; - message.dwAllowedResults = MB_OKCANCEL; - - hr = BuffReadNumber(pbData, cbData, &iData, &message.progress.dwPercentage); - ExitOnFailure(hr, "Failed to read progress from buffer."); - - *pdwResult = (DWORD)pfnMessageHandler(&message, pvContext); - -LExit: - return hr; -} diff --git a/src/engine/embedded.h b/src/engine/embedded.h deleted file mode 100644 index 08adeae0..00000000 --- a/src/engine/embedded.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum _BURN_EMBEDDED_MESSAGE_TYPE -{ - BURN_EMBEDDED_MESSAGE_TYPE_UNKNOWN, - BURN_EMBEDDED_MESSAGE_TYPE_ERROR, - BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS, -} BURN_EMBEDDED_MESSAGE_TYPE; - - -HRESULT EmbeddedRunBundle( - __in LPCWSTR wzExecutablePath, - __in LPCWSTR wzArguments, - __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, - __in LPVOID pvContext, - __out DWORD* pdwExitCode - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp deleted file mode 100644 index 8f024e98..00000000 --- a/src/engine/engine.cpp +++ /dev/null @@ -1,992 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// constants - -const DWORD RESTART_RETRIES = 10; - -// internal function declarations - -static HRESULT InitializeEngineState( - __in BURN_ENGINE_STATE* pEngineState, - __in HANDLE hEngineFile - ); -static void UninitializeEngineState( - __in BURN_ENGINE_STATE* pEngineState - ); -static HRESULT RunUntrusted( - __in LPCWSTR wzCommandLine, - __in BURN_ENGINE_STATE* pEngineState - ); -static HRESULT RunNormal( - __in HINSTANCE hInstance, - __in BURN_ENGINE_STATE* pEngineState - ); -static HRESULT RunElevated( - __in HINSTANCE hInstance, - __in LPCWSTR wzCommandLine, - __in BURN_ENGINE_STATE* pEngineState - ); -static HRESULT RunEmbedded( - __in HINSTANCE hInstance, - __in BURN_ENGINE_STATE* pEngineState - ); -static HRESULT RunRunOnce( - __in const BURN_REGISTRATION* pRegistration, - __in int nCmdShow - ); -static HRESULT RunApplication( - __in BURN_ENGINE_STATE* pEngineState, - __out BOOL* pfReloadApp, - __out BOOL* pfSkipCleanup - ); -static HRESULT ProcessMessage( - __in BURN_ENGINE_STATE* pEngineState, - __in const MSG* pmsg - ); -static HRESULT DAPI RedirectLoggingOverPipe( - __in_z LPCSTR szString, - __in_opt LPVOID pvContext - ); -static HRESULT Restart(); - - -// function definitions - -extern "C" BOOL EngineInCleanRoom( - __in_z_opt LPCWSTR wzCommandLine - ) -{ - // Be very careful with the functions you call from here. - // This function will be called before ::SetDefaultDllDirectories() - // has been called so dependencies outside of kernel32.dll are - // very likely to introduce DLL hijacking opportunities. - - static DWORD cchCleanRoomSwitch = lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM); - - // This check is wholly dependent on the clean room command line switch being - // present at the beginning of the command line. Since Burn is the only thing - // that should be setting this command line option, that is in our control. - BOOL fInCleanRoom = (wzCommandLine && - (wzCommandLine[0] == L'-' || wzCommandLine[0] == L'/') && - CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzCommandLine + 1, cchCleanRoomSwitch, BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, cchCleanRoomSwitch) && - wzCommandLine[1 + cchCleanRoomSwitch] == L'=' - ); - - return fInCleanRoom; -} - -extern "C" HRESULT EngineRun( - __in HINSTANCE hInstance, - __in HANDLE hEngineFile, - __in_z_opt LPCWSTR wzCommandLine, - __in int nCmdShow, - __out DWORD* pdwExitCode - ) -{ - HRESULT hr = S_OK; - BOOL fComInitialized = FALSE; - BOOL fLogInitialized = FALSE; - BOOL fCrypInitialized = FALSE; - BOOL fDpiuInitialized = FALSE; - BOOL fRegInitialized = FALSE; - BOOL fWiuInitialized = FALSE; - BOOL fXmlInitialized = FALSE; - SYSTEM_INFO si = { }; - RTL_OSVERSIONINFOEXW ovix = { }; - LPWSTR sczExePath = NULL; - BOOL fRunNormal = FALSE; - BOOL fRestart = FALSE; - - BURN_ENGINE_STATE engineState = { }; - engineState.command.cbSize = sizeof(BOOTSTRAPPER_COMMAND); - - // Always initialize logging first - LogInitialize(::GetModuleHandleW(NULL)); - fLogInitialized = TRUE; - - // Ensure that log contains approriate level of information -#ifdef _DEBUG - LogSetLevel(REPORT_DEBUG, FALSE); -#else - LogSetLevel(REPORT_VERBOSE, FALSE); // FALSE means don't write an additional text line to the log saying the level changed -#endif - - hr = AppParseCommandLine(wzCommandLine, &engineState.argc, &engineState.argv); - ExitOnFailure(hr, "Failed to parse command line."); - - hr = InitializeEngineState(&engineState, hEngineFile); - ExitOnFailure(hr, "Failed to initialize engine state."); - - engineState.command.nCmdShow = nCmdShow; - - // initialize platform layer - PlatformInitialize(); - - // initialize COM - hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); - ExitOnFailure(hr, "Failed to initialize COM."); - fComInitialized = TRUE; - - // Initialize dutil. - hr = CrypInitialize(); - ExitOnFailure(hr, "Failed to initialize Cryputil."); - fCrypInitialized = TRUE; - - DpiuInitialize(); - fDpiuInitialized = TRUE; - - hr = RegInitialize(); - ExitOnFailure(hr, "Failed to initialize Regutil."); - fRegInitialized = TRUE; - - hr = WiuInitialize(); - ExitOnFailure(hr, "Failed to initialize Wiutil."); - fWiuInitialized = TRUE; - - hr = XmlInitialize(); - ExitOnFailure(hr, "Failed to initialize XML util."); - fXmlInitialized = TRUE; - - hr = OsRtlGetVersion(&ovix); - ExitOnFailure(hr, "Failed to get OS info."); - -#if defined(_M_ARM64) - LPCSTR szBurnPlatform = "ARM64"; -#elif defined(_M_AMD64) - LPCSTR szBurnPlatform = "x64"; -#else - LPCSTR szBurnPlatform = "x86"; -#endif - - LPCSTR szMachinePlatform = "unknown architecture"; - ::GetNativeSystemInfo(&si); - switch (si.wProcessorArchitecture) - { - case PROCESSOR_ARCHITECTURE_AMD64: - szMachinePlatform = "x64"; - break; - case PROCESSOR_ARCHITECTURE_ARM: - szMachinePlatform = "ARM"; - break; - case PROCESSOR_ARCHITECTURE_ARM64: - szMachinePlatform = "ARM64"; - break; - case PROCESSOR_ARCHITECTURE_INTEL: - szMachinePlatform = "x86"; - break; - } - - PathForCurrentProcess(&sczExePath, NULL); // Ignore failure. - LogId(REPORT_STANDARD, MSG_BURN_INFO, szVerMajorMinorBuild, ovix.dwMajorVersion, ovix.dwMinorVersion, ovix.dwBuildNumber, ovix.wServicePackMajor, sczExePath, szBurnPlatform, szMachinePlatform); - ReleaseNullStr(sczExePath); - - // initialize core - hr = CoreInitialize(&engineState); - ExitOnFailure(hr, "Failed to initialize core."); - - // Select run mode. - switch (engineState.mode) - { - case BURN_MODE_UNTRUSTED: - hr = RunUntrusted(wzCommandLine, &engineState); - ExitOnFailure(hr, "Failed to run untrusted mode."); - break; - - case BURN_MODE_NORMAL: - fRunNormal = TRUE; - - hr = RunNormal(hInstance, &engineState); - ExitOnFailure(hr, "Failed to run per-user mode."); - break; - - case BURN_MODE_ELEVATED: - hr = RunElevated(hInstance, wzCommandLine, &engineState); - ExitOnFailure(hr, "Failed to run per-machine mode."); - break; - - case BURN_MODE_EMBEDDED: - fRunNormal = TRUE; - - hr = RunEmbedded(hInstance, &engineState); - ExitOnFailure(hr, "Failed to run embedded mode."); - break; - - case BURN_MODE_RUNONCE: - hr = RunRunOnce(&engineState.registration, nCmdShow); - ExitOnFailure(hr, "Failed to run RunOnce mode."); - break; - - default: - hr = E_UNEXPECTED; - ExitOnFailure(hr, "Invalid run mode."); - } - - // set exit code and remember if we are supposed to restart. - *pdwExitCode = engineState.userExperience.dwExitCode; - fRestart = engineState.fRestart; - -LExit: - ReleaseStr(sczExePath); - - // If anything went wrong but the log was never open, try to open a "failure" log - // and that will dump anything captured in the log memory buffer to the log. - if (FAILED(hr) && BURN_LOGGING_STATE_CLOSED == engineState.log.state) - { - LoggingOpenFailed(); - } - - UserExperienceRemove(&engineState.userExperience); - - CacheRemoveWorkingFolder(engineState.registration.sczId); - CacheUninitialize(); - - // If this is a related bundle (but not an update) suppress restart and return the standard restart error code. - if (fRestart && BOOTSTRAPPER_RELATION_NONE != engineState.command.relationType && BOOTSTRAPPER_RELATION_UPDATE != engineState.command.relationType) - { - LogId(REPORT_STANDARD, MSG_RESTART_ABORTED, LoggingRelationTypeToString(engineState.command.relationType)); - - fRestart = FALSE; - hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); - } - - UninitializeEngineState(&engineState); - - if (fXmlInitialized) - { - XmlUninitialize(); - } - - if (fWiuInitialized) - { - WiuUninitialize(); - } - - if (fRegInitialized) - { - RegUninitialize(); - } - - if (fDpiuInitialized) - { - DpiuUninitialize(); - } - - if (fCrypInitialized) - { - CrypUninitialize(); - } - - if (fComInitialized) - { - ::CoUninitialize(); - } - - if (fRunNormal) - { - LogId(REPORT_STANDARD, MSG_EXITING, FAILED(hr) ? (int)hr : *pdwExitCode, LoggingBoolToString(fRestart)); - - if (fRestart) - { - LogId(REPORT_STANDARD, MSG_RESTARTING); - } - } - - if (fLogInitialized) - { - LogClose(FALSE); - } - - if (fRestart) - { - Restart(); - } - - if (fLogInitialized) - { - LogUninitialize(FALSE); - } - - return hr; -} - - -// internal function definitions - -static HRESULT InitializeEngineState( - __in BURN_ENGINE_STATE* pEngineState, - __in HANDLE hEngineFile - ) -{ - HRESULT hr = S_OK; - LPCWSTR wzParam = NULL; - HANDLE hSectionFile = hEngineFile; - HANDLE hSourceEngineFile = INVALID_HANDLE_VALUE; - DWORD64 qw = 0; - - pEngineState->automaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED; - pEngineState->dwElevatedLoggingTlsId = TLS_OUT_OF_INDEXES; - ::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive); - PipeConnectionInitialize(&pEngineState->companionConnection); - PipeConnectionInitialize(&pEngineState->embeddedConnection); - - for (int i = 0; i < pEngineState->argc; ++i) - { - if (pEngineState->argv[i][0] == L'-') - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &pEngineState->argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED), BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED))) - { - wzParam = &pEngineState->argv[i][2 + lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED)]; - if (L'=' != wzParam[-1] || L'\0' == wzParam[0]) - { - ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED); - } - - hr = StrStringToUInt64(wzParam, 0, &qw); - ExitOnFailure(hr, "Failed to parse file handle: '%ls'", (wzParam)); - - hSourceEngineFile = (HANDLE)qw; - } - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &pEngineState->argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF), BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF))) - { - wzParam = &pEngineState->argv[i][2 + lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF)]; - if (L'=' != wzParam[-1] || L'\0' == wzParam[0]) - { - ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF); - } - - hr = StrStringToUInt64(wzParam, 0, &qw); - ExitOnFailure(hr, "Failed to parse file handle: '%ls'", (wzParam)); - - hSectionFile = (HANDLE)qw; - } - } - } - - hr = SectionInitialize(&pEngineState->section, hSectionFile, hSourceEngineFile); - ExitOnFailure(hr, "Failed to initialize engine section."); - -LExit: - return hr; -} - -static void UninitializeEngineState( - __in BURN_ENGINE_STATE* pEngineState - ) -{ - if (pEngineState->argv) - { - AppFreeCommandLineArgs(pEngineState->argv); - } - - ReleaseStr(pEngineState->sczIgnoreDependencies); - - PipeConnectionUninitialize(&pEngineState->embeddedConnection); - PipeConnectionUninitialize(&pEngineState->companionConnection); - ReleaseStr(pEngineState->sczBundleEngineWorkingPath) - - ReleaseHandle(pEngineState->hMessageWindowThread); - - BurnExtensionUninitialize(&pEngineState->extensions); - - ::DeleteCriticalSection(&pEngineState->userExperience.csEngineActive); - UserExperienceUninitialize(&pEngineState->userExperience); - - ApprovedExesUninitialize(&pEngineState->approvedExes); - UpdateUninitialize(&pEngineState->update); - VariablesUninitialize(&pEngineState->variables); - SearchesUninitialize(&pEngineState->searches); - RegistrationUninitialize(&pEngineState->registration); - PayloadsUninitialize(&pEngineState->payloads); - PackagesUninitialize(&pEngineState->packages); - SectionUninitialize(&pEngineState->section); - ContainersUninitialize(&pEngineState->containers); - - ReleaseStr(pEngineState->command.wzBootstrapperApplicationDataPath); - ReleaseStr(pEngineState->command.wzBootstrapperWorkingFolder); - ReleaseStr(pEngineState->command.wzLayoutDirectory); - ReleaseStr(pEngineState->command.wzCommandLine); - - ReleaseStr(pEngineState->log.sczExtension); - ReleaseStr(pEngineState->log.sczPrefix); - ReleaseStr(pEngineState->log.sczPath); - ReleaseStr(pEngineState->log.sczPathVariable); - - if (TLS_OUT_OF_INDEXES != pEngineState->dwElevatedLoggingTlsId) - { - ::TlsFree(pEngineState->dwElevatedLoggingTlsId); - } - - // clear struct - memset(pEngineState, 0, sizeof(BURN_ENGINE_STATE)); -} - -static HRESULT RunUntrusted( - __in LPCWSTR wzCommandLine, - __in BURN_ENGINE_STATE* pEngineState - ) -{ - HRESULT hr = S_OK; - LPWSTR sczCurrentProcessPath = NULL; - LPWSTR wzCleanRoomBundlePath = NULL; - LPWSTR sczCachedCleanRoomBundlePath = NULL; - LPWSTR sczParameters = NULL; - LPWSTR sczFullCommandLine = NULL; - STARTUPINFOW si = { }; - PROCESS_INFORMATION pi = { }; - HANDLE hFileAttached = NULL; - HANDLE hFileSelf = NULL; - HANDLE hProcess = NULL; - - hr = PathForCurrentProcess(&sczCurrentProcessPath, NULL); - ExitOnFailure(hr, "Failed to get path for current process."); - - BOOL fRunningFromCache = CacheBundleRunningFromCache(); - - // If we're running from the package cache, we're in a secure - // folder (DLLs cannot be inserted here for hijacking purposes) - // so just launch the current process's path as the clean room - // process. Technically speaking, we'd be able to skip creating - // a clean room process at all (since we're already running from - // a secure folder) but it makes the code that only wants to run - // in clean room more complicated if we don't launch an explicit - // clean room process. - if (fRunningFromCache) - { - wzCleanRoomBundlePath = sczCurrentProcessPath; - } - else - { - hr = CacheBundleToCleanRoom(&pEngineState->section, &sczCachedCleanRoomBundlePath); - ExitOnFailure(hr, "Failed to cache to clean room."); - - wzCleanRoomBundlePath = sczCachedCleanRoomBundlePath; - } - - // The clean room switch must always be at the front of the command line so - // the EngineInCleanRoom function will operate correctly. - hr = StrAllocFormatted(&sczParameters, L"-%ls=\"%ls\"", BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, sczCurrentProcessPath); - ExitOnFailure(hr, "Failed to allocate parameters for unelevated process."); - - // Send a file handle for the child Burn process to access the attached container. - hr = CoreAppendFileHandleAttachedToCommandLine(pEngineState->section.hEngineFile, &hFileAttached, &sczParameters); - ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED); - - // Grab a file handle for the child Burn process. - hr = CoreAppendFileHandleSelfToCommandLine(wzCleanRoomBundlePath, &hFileSelf, &sczParameters, NULL); - ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF); - - hr = StrAllocFormattedSecure(&sczParameters, L"%ls %ls", sczParameters, wzCommandLine); - ExitOnFailure(hr, "Failed to append original command line."); - -#ifdef ENABLE_UNELEVATE - // TODO: Pass file handle to unelevated process if this ever gets reenabled. - if (!pEngineState->fDisableUnelevate) - { - // Try to launch unelevated and if that fails for any reason, we'll launch our process normally (even though that may make it elevated). - hr = ProcExecuteAsInteractiveUser(wzCleanRoomBundlePath, sczParameters, &hProcess); - } -#endif - - if (!hProcess) - { - hr = StrAllocFormattedSecure(&sczFullCommandLine, L"\"%ls\" %ls", wzCleanRoomBundlePath, sczParameters); - ExitOnFailure(hr, "Failed to allocate full command-line."); - - si.cb = sizeof(si); - si.wShowWindow = static_cast(pEngineState->command.nCmdShow); - if (!::CreateProcessW(wzCleanRoomBundlePath, sczFullCommandLine, NULL, NULL, TRUE, 0, 0, NULL, &si, &pi)) - { - ExitWithLastError(hr, "Failed to launch clean room process: %ls", sczFullCommandLine); - } - - hProcess = pi.hProcess; - pi.hProcess = NULL; - } - - hr = ProcWaitForCompletion(hProcess, INFINITE, &pEngineState->userExperience.dwExitCode); - ExitOnFailure(hr, "Failed to wait for clean room process: %ls", wzCleanRoomBundlePath); - -LExit: - ReleaseHandle(pi.hThread); - ReleaseFileHandle(hFileSelf); - ReleaseFileHandle(hFileAttached); - ReleaseHandle(hProcess); - StrSecureZeroFreeString(sczFullCommandLine); - StrSecureZeroFreeString(sczParameters); - ReleaseStr(sczCachedCleanRoomBundlePath); - ReleaseStr(sczCurrentProcessPath); - - return hr; -} - -static HRESULT RunNormal( - __in HINSTANCE hInstance, - __in BURN_ENGINE_STATE* pEngineState - ) -{ - HRESULT hr = S_OK; - LPWSTR sczOriginalSource = NULL; - LPWSTR sczCopiedOriginalSource = NULL; - BOOL fContinueExecution = TRUE; - BOOL fReloadApp = FALSE; - BOOL fSkipCleanup = FALSE; - BURN_EXTENSION_ENGINE_CONTEXT extensionEngineContext = { }; - - // Initialize logging. - hr = LoggingOpen(&pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->registration.sczDisplayName); - ExitOnFailure(hr, "Failed to open log."); - - // Ensure we're on a supported operating system. - hr = ConditionGlobalCheck(&pEngineState->variables, &pEngineState->condition, pEngineState->command.display, pEngineState->registration.sczDisplayName, &pEngineState->userExperience.dwExitCode, &fContinueExecution); - ExitOnFailure(hr, "Failed to check global conditions"); - - if (!fContinueExecution) - { - LogId(REPORT_STANDARD, MSG_FAILED_CONDITION_CHECK); - - // If the block told us to abort, abort! - ExitFunction1(hr = S_OK); - } - - if (pEngineState->userExperience.fSplashScreen && BOOTSTRAPPER_DISPLAY_NONE < pEngineState->command.display) - { - SplashScreenCreate(hInstance, NULL, &pEngineState->command.hwndSplashScreen); - } - - // Create a top-level window to handle system messages. - hr = UiCreateMessageWindow(hInstance, pEngineState); - ExitOnFailure(hr, "Failed to create the message window."); - - // Query registration state. - hr = CoreQueryRegistration(pEngineState); - ExitOnFailure(hr, "Failed to query registration."); - - // Best effort to set the source of attached containers to BURN_BUNDLE_ORIGINAL_SOURCE. - hr = VariableGetString(&pEngineState->variables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource); - if (SUCCEEDED(hr)) - { - for (DWORD i = 0; i < pEngineState->containers.cContainers; ++i) - { - BURN_CONTAINER* pContainer = pEngineState->containers.rgContainers + i; - if (pContainer->fAttached) - { - hr = StrAllocString(&sczCopiedOriginalSource, sczOriginalSource, 0); - if (SUCCEEDED(hr)) - { - ReleaseNullStr(pContainer->sczSourcePath); - pContainer->sczSourcePath = sczCopiedOriginalSource; - sczCopiedOriginalSource = NULL; - } - } - } - } - hr = S_OK; - - // Set some built-in variables before loading the BA. - hr = PlanSetVariables(pEngineState->command.action, &pEngineState->variables); - ExitOnFailure(hr, "Failed to set action variables."); - - hr = RegistrationSetVariables(&pEngineState->registration, &pEngineState->variables); - ExitOnFailure(hr, "Failed to set registration variables."); - - // If a layout directory was specified on the command-line, set it as a well-known variable. - if (pEngineState->command.wzLayoutDirectory && *pEngineState->command.wzLayoutDirectory) - { - hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_LAYOUT_DIRECTORY, pEngineState->command.wzLayoutDirectory, FALSE, FALSE); - ExitOnFailure(hr, "Failed to set layout directory variable to value provided from command-line."); - } - - // Setup the extension engine. - extensionEngineContext.pEngineState = pEngineState; - - // Load the extensions. - hr = BurnExtensionLoad(&pEngineState->extensions, &extensionEngineContext); - ExitOnFailure(hr, "Failed to load BundleExtensions."); - - do - { - fReloadApp = FALSE; - pEngineState->fQuit = FALSE; - - hr = RunApplication(pEngineState, &fReloadApp, &fSkipCleanup); - ExitOnFailure(hr, "Failed while running "); - } while (fReloadApp); - -LExit: - if (!fSkipCleanup) - { - CoreCleanup(pEngineState); - } - - BurnExtensionUnload(&pEngineState->extensions); - - // If the message window is still around, close it. - UiCloseMessageWindow(pEngineState); - - VariablesDump(&pEngineState->variables); - - // end per-machine process if running - if (INVALID_HANDLE_VALUE != pEngineState->companionConnection.hPipe) - { - PipeTerminateChildProcess(&pEngineState->companionConnection, pEngineState->userExperience.dwExitCode, FALSE); - } - - // If the splash screen is still around, close it. - if (::IsWindow(pEngineState->command.hwndSplashScreen)) - { - ::PostMessageW(pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0); - } - - ReleaseStr(sczOriginalSource); - ReleaseStr(sczCopiedOriginalSource); - - return hr; -} - -static HRESULT RunElevated( - __in HINSTANCE hInstance, - __in LPCWSTR /*wzCommandLine*/, - __in BURN_ENGINE_STATE* pEngineState - ) -{ - HRESULT hr = S_OK; - HANDLE hLock = NULL; - BOOL fDisabledAutomaticUpdates = FALSE; - - // connect to per-user process - hr = PipeChildConnect(&pEngineState->companionConnection, TRUE); - ExitOnFailure(hr, "Failed to connect to unelevated process."); - - // Set up the thread local storage to store the correct pipe to communicate logging then - // override logging to write over the pipe. - pEngineState->dwElevatedLoggingTlsId = ::TlsAlloc(); - if (TLS_OUT_OF_INDEXES == pEngineState->dwElevatedLoggingTlsId) - { - ExitWithLastError(hr, "Failed to allocate thread local storage for logging."); - } - - if (!::TlsSetValue(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe)) - { - ExitWithLastError(hr, "Failed to set elevated pipe into thread local storage for logging."); - } - - LogRedirect(RedirectLoggingOverPipe, pEngineState); - - // Create a top-level window to prevent shutting down the elevated process. - hr = UiCreateMessageWindow(hInstance, pEngineState); - ExitOnFailure(hr, "Failed to create the message window."); - - SrpInitialize(TRUE); - - // Pump messages from parent process. - hr = ElevationChildPumpMessages(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe, pEngineState->companionConnection.hCachePipe, &pEngineState->approvedExes, &pEngineState->containers, &pEngineState->packages, &pEngineState->payloads, &pEngineState->variables, &pEngineState->registration, &pEngineState->userExperience, &hLock, &fDisabledAutomaticUpdates, &pEngineState->userExperience.dwExitCode, &pEngineState->fRestart); - LogRedirect(NULL, NULL); // reset logging so the next failure gets written to "log buffer" for the failure log. - ExitOnFailure(hr, "Failed to pump messages from parent process."); - -LExit: - LogRedirect(NULL, NULL); // we're done talking to the child so always reset logging now. - - // If the message window is still around, close it. - UiCloseMessageWindow(pEngineState); - - if (fDisabledAutomaticUpdates) - { - ElevationChildResumeAutomaticUpdates(); - } - - if (hLock) - { - ::ReleaseMutex(hLock); - ::CloseHandle(hLock); - } - - return hr; -} - -static HRESULT RunEmbedded( - __in HINSTANCE hInstance, - __in BURN_ENGINE_STATE* pEngineState - ) -{ - HRESULT hr = S_OK; - - // Disable system restore since the parent bundle may have done it. - pEngineState->fDisableSystemRestore = TRUE; - - // Connect to parent process. - hr = PipeChildConnect(&pEngineState->embeddedConnection, FALSE); - ExitOnFailure(hr, "Failed to connect to parent of embedded process."); - - // Do not register the bundle to automatically restart if embedded. - if (BOOTSTRAPPER_DISPLAY_EMBEDDED == pEngineState->command.display) - { - pEngineState->registration.fDisableResume = TRUE; - } - - // Now run the application like normal. - hr = RunNormal(hInstance, pEngineState); - ExitOnFailure(hr, "Failed to run bootstrapper application embedded."); - -LExit: - return hr; -} - -static HRESULT RunRunOnce( - __in const BURN_REGISTRATION* pRegistration, - __in int nCmdShow - ) -{ - HRESULT hr = S_OK; - LPWSTR sczNewCommandLine = NULL; - LPWSTR sczBurnPath = NULL; - HANDLE hProcess = NULL; - - hr = RegistrationGetResumeCommandLine(pRegistration, &sczNewCommandLine); - ExitOnFailure(hr, "Unable to get resume command line from the registry"); - - // and re-launch - hr = PathForCurrentProcess(&sczBurnPath, NULL); - ExitOnFailure(hr, "Failed to get current process path."); - - hr = ProcExec(sczBurnPath, 0 < sczNewCommandLine ? sczNewCommandLine : L"", nCmdShow, &hProcess); - ExitOnFailure(hr, "Failed to re-launch bundle process after RunOnce: %ls", sczBurnPath); - -LExit: - ReleaseHandle(hProcess); - ReleaseStr(sczNewCommandLine); - ReleaseStr(sczBurnPath); - - return hr; -} - -static HRESULT RunApplication( - __in BURN_ENGINE_STATE* pEngineState, - __out BOOL* pfReloadApp, - __out BOOL* pfSkipCleanup - ) -{ - HRESULT hr = S_OK; - BOOTSTRAPPER_ENGINE_CONTEXT engineContext = { }; - BOOL fStartupCalled = FALSE; - BOOL fRet = FALSE; - MSG msg = { }; - BOOTSTRAPPER_SHUTDOWN_ACTION shutdownAction = BOOTSTRAPPER_SHUTDOWN_ACTION_NONE; - - ::PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); - - // Setup the bootstrapper engine. - engineContext.dwThreadId = ::GetCurrentThreadId(); - engineContext.pEngineState = pEngineState; - - // Load the bootstrapper application. - hr = UserExperienceLoad(&pEngineState->userExperience, &engineContext, &pEngineState->command); - ExitOnFailure(hr, "Failed to load BA."); - - fStartupCalled = TRUE; - hr = UserExperienceOnStartup(&pEngineState->userExperience); - ExitOnFailure(hr, "Failed to start bootstrapper application."); - - // Enter the message pump. - while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) - { - if (-1 == fRet) - { - hr = E_UNEXPECTED; - ExitOnRootFailure(hr, "Unexpected return value from message pump."); - } - else - { - // When the BA makes a request from its own thread, it's common for the PostThreadMessage in externalengine.cpp - // to block until this thread waits on something. It's also common for Detect and Plan to never wait on something. - // In the extreme case, the engine could be elevating in Apply before the Detect call returned to the BA. - // This helps to avoid that situation, which could be blocking a UI thread. - ::Sleep(0); - - ProcessMessage(pEngineState, &msg); - } - } - - // Get exit code. - pEngineState->userExperience.dwExitCode = (DWORD)msg.wParam; - -LExit: - if (fStartupCalled) - { - UserExperienceOnShutdown(&pEngineState->userExperience, &shutdownAction); - if (BOOTSTRAPPER_SHUTDOWN_ACTION_RESTART == shutdownAction) - { - LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RESTART, LoggingBoolToString(pEngineState->fRestart)); - pEngineState->fRestart = TRUE; - } - else if (BOOTSTRAPPER_SHUTDOWN_ACTION_RELOAD_BOOTSTRAPPER == shutdownAction) - { - LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RELOAD); - *pfReloadApp = TRUE; - } - else if (BOOTSTRAPPER_SHUTDOWN_ACTION_SKIP_CLEANUP == shutdownAction) - { - LogId(REPORT_STANDARD, MSG_BA_REQUESTED_SKIP_CLEANUP); - *pfSkipCleanup = TRUE; - } - } - - // Unload BA. - UserExperienceUnload(&pEngineState->userExperience); - - return hr; -} - -static HRESULT ProcessMessage( - __in BURN_ENGINE_STATE* pEngineState, - __in const MSG* pmsg - ) -{ - HRESULT hr = S_OK; - - UserExperienceActivateEngine(&pEngineState->userExperience); - - if (pEngineState->fQuit) - { - LogId(REPORT_WARNING, MSG_IGNORE_OPERATION_AFTER_QUIT, LoggingBurnMessageToString(pmsg->message)); - ExitFunction1(hr = E_INVALIDSTATE); - } - - switch (pmsg->message) - { - case WM_BURN_DETECT: - hr = CoreDetect(pEngineState, reinterpret_cast(pmsg->lParam)); - break; - - case WM_BURN_PLAN: - hr = CorePlan(pEngineState, static_cast(pmsg->lParam)); - break; - - case WM_BURN_ELEVATE: - hr = CoreElevate(pEngineState, reinterpret_cast(pmsg->lParam)); - break; - - case WM_BURN_APPLY: - hr = CoreApply(pEngineState, reinterpret_cast(pmsg->lParam)); - break; - - case WM_BURN_LAUNCH_APPROVED_EXE: - hr = CoreLaunchApprovedExe(pEngineState, reinterpret_cast(pmsg->lParam)); - break; - - case WM_BURN_QUIT: - hr = CoreQuit(pEngineState, static_cast(pmsg->wParam)); - break; - } - -LExit: - UserExperienceDeactivateEngine(&pEngineState->userExperience); - - return hr; -} - -static HRESULT DAPI RedirectLoggingOverPipe( - __in_z LPCSTR szString, - __in_opt LPVOID pvContext - ) -{ - static BOOL s_fCurrentlyLoggingToPipe = FALSE; - - HRESULT hr = S_OK; - BURN_ENGINE_STATE* pEngineState = static_cast(pvContext); - BOOL fStartedLogging = FALSE; - HANDLE hPipe = INVALID_HANDLE_VALUE; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwResult = 0; - - // Prevent this function from being called recursively. - if (s_fCurrentlyLoggingToPipe) - { - ExitFunction(); - } - - s_fCurrentlyLoggingToPipe = TRUE; - fStartedLogging = TRUE; - - // Make sure the current thread set the pipe in TLS. - hPipe = ::TlsGetValue(pEngineState->dwElevatedLoggingTlsId); - if (!hPipe || INVALID_HANDLE_VALUE == hPipe) - { - hr = HRESULT_FROM_WIN32(ERROR_PIPE_NOT_CONNECTED); - ExitFunction(); - } - - // Do not log or use ExitOnFailure() macro here because they will be discarded - // by the recursive block at the top of this function. - hr = BuffWriteStringAnsi(&pbData, &cbData, szString); - if (SUCCEEDED(hr)) - { - hr = PipeSendMessage(hPipe, static_cast(BURN_PIPE_MESSAGE_TYPE_LOG), pbData, cbData, NULL, NULL, &dwResult); - if (SUCCEEDED(hr)) - { - hr = (HRESULT)dwResult; - } - } - -LExit: - ReleaseBuffer(pbData); - - // We started logging so remember to say we are no longer logging. - if (fStartedLogging) - { - s_fCurrentlyLoggingToPipe = FALSE; - } - - return hr; -} - -static HRESULT Restart() -{ - HRESULT hr = S_OK; - HANDLE hProcessToken = NULL; - TOKEN_PRIVILEGES priv = { }; - DWORD dwRetries = 0; - - if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken)) - { - ExitWithLastError(hr, "Failed to get process token."); - } - - priv.PrivilegeCount = 1; - priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - if (!::LookupPrivilegeValueW(NULL, L"SeShutdownPrivilege", &priv.Privileges[0].Luid)) - { - ExitWithLastError(hr, "Failed to get shutdown privilege LUID."); - } - - if (!::AdjustTokenPrivileges(hProcessToken, FALSE, &priv, sizeof(TOKEN_PRIVILEGES), NULL, 0)) - { - ExitWithLastError(hr, "Failed to adjust token to add shutdown privileges."); - } - - do - { - hr = S_OK; - - // Wait a second to let the companion process (assuming we did an elevated install) to get to the - // point where it too is thinking about restarting the computer. Only one will schedule the restart - // but both will have their log files closed and otherwise be ready to exit. - // - // On retry, we'll also wait a second to let the OS try to get to a place where the restart can - // be initiated. - ::Sleep(1000); - - if (!vpfnInitiateSystemShutdownExW(NULL, NULL, 0, FALSE, TRUE, SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_INSTALLATION | SHTDN_REASON_FLAG_PLANNED)) - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - } - } while (dwRetries++ < RESTART_RETRIES && (HRESULT_FROM_WIN32(ERROR_MACHINE_LOCKED) == hr || HRESULT_FROM_WIN32(ERROR_NOT_READY) == hr)); - ExitOnRootFailure(hr, "Failed to schedule restart."); - -LExit: - ReleaseHandle(hProcessToken); - return hr; -} diff --git a/src/engine/engine.mc b/src/engine/engine.mc deleted file mode 100644 index 25d5b4e4..00000000 --- a/src/engine/engine.mc +++ /dev/null @@ -1,1090 +0,0 @@ -; // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -MessageIdTypedef=DWORD - -LanguageNames=(English=0x409:MSG00409) - - -; // message definitions - -; // MessageId=# -; // Severity=Success -; // SymbolicName=MSG_SUCCESS -; // Language=English -; // Success %1. -; // . -; -; // MessageId=# -; // Severity=Warning -; // SymbolicName=MSG_WARNING -; // Language=English -; // Warning %1. -; // . -; -; // MessageId=# -; // Severity=Error -; // SymbolicName=MSG_ERROR -; // Language=English -; // Error %1. -; // . - -MessageId=1 -Severity=Success -SymbolicName=MSG_BURN_INFO -Language=English -Burn %7!hs! v%1!hs!, Windows v%2!d!.%3!d! %8!hs! (Build %4!d!: Service Pack %5!d!), path: %6!ls! -. - -MessageId=2 -Severity=Warning -SymbolicName=MSG_BURN_UNKNOWN_PRIVATE_SWITCH -Language=English -Unknown burn internal command-line switch encountered: '%1!ls!'. -. - -MessageId=3 -Severity=Success -SymbolicName=MSG_BURN_RUN_BY_RELATED_BUNDLE -Language=English -This bundle is being run by a related bundle as type '%1!hs!'. -. - -MessageId=4 -Severity=Success -SymbolicName=MSG_BA_REQUESTED_RESTART -Language=English -Bootstrapper application requested restart at shutdown. Planned to restart already: %1!hs!. -. - -MessageId=5 -Severity=Warning -SymbolicName=MSG_RESTARTING -Language=English -Restarting computer... -======================================= -. - -MessageId=6 -Severity=Success -SymbolicName=MSG_BA_REQUESTED_RELOAD -Language=English -Bootstrapper application requested to be reloaded. -. - -MessageId=7 -Severity=Success -SymbolicName=MSG_EXITING -Language=English -Exit code: 0x%1!x!, restarting: %2!hs! -. - -MessageId=8 -Severity=Warning -SymbolicName=MSG_RESTART_ABORTED -Language=English -Preventing requested restart because bundle is related: '%1!hs!'. Returning restart requested to parent bundle. -. - -MessageId=9 -Severity=Success -SymbolicName=MSG_BURN_COMMAND_LINE -Language=English -Command Line: '%1!ls!' -. - -MessageId=10 -Severity=Success -SymbolicName=MSG_LAUNCH_ELEVATED_ENGINE_STARTING -Language=English -Launching elevated engine process. -. - -MessageId=11 -Severity=Success -SymbolicName=MSG_LAUNCH_ELEVATED_ENGINE_SUCCESS -Language=English -Launched elevated engine process. -. - -MessageId=12 -Severity=Success -SymbolicName=MSG_CONNECT_TO_ELEVATED_ENGINE_SUCCESS -Language=English -Connected to elevated engine. -. - -MessageId=13 -Severity=Warning -SymbolicName=MSG_MANIFEST_INVALID_VERSION -Language=English -The manifest contains an invalid version string: '%1!ls!' -. - -MessageId=14 -Severity=Success -SymbolicName=MSG_BA_REQUESTED_SKIP_CLEANUP -Language=English -Bootstrapper application opted out of any engine behavior to automatically uninstall the bundle during shutdown. -. - -MessageId=51 -Severity=Error -SymbolicName=MSG_FAILED_PARSE_CONDITION -Language=English -Error %1!hs!. Failed to parse condition %2!ls!. Unexpected symbol at position %3!hs! -. - -MessageId=52 -Severity=Success -SymbolicName=MSG_CONDITION_RESULT -Language=English -Condition '%1!ls!' evaluates to %2!hs!. -. - -MessageId=53 -Severity=Error -SymbolicName=MSG_FAILED_CONDITION_CHECK -Language=English -Bundle global condition check didn't succeed - aborting without loading application. -. - -MessageId=54 -Severity=Error -SymbolicName=MSG_RESOLVE_SOURCE_FAILED -Language=English -Failed to resolve source for payload: %2!ls!, package: %3!ls!, container: %4!ls!, error: %1!ls!. -. - -MessageId=55 -Severity=Warning -SymbolicName=MSG_CANNOT_LOAD_STATE_FILE -Language=English -Could not load or read state file: %2!ls!, error: 0x%1!x!. -. - -MessageId=56 -Severity=Error -SymbolicName=MSG_USER_CANCELED -Language=English -Application canceled operation: %2!ls!, error: %1!ls! -. - -MessageId=57 -Severity=Warning -SymbolicName=MSG_CONDITION_INVALID_VERSION -Language=English -Condition '%1!ls!' contains invalid version string '%2!ls!'. -. - -MessageId=58 -Severity=Warning -SymbolicName=MSG_IGNORE_OPERATION_AFTER_QUIT -Language=English -Bootstrapper application already requested to quit, ignoring request: '%1!hs!'. -. - -MessageId=100 -Severity=Success -SymbolicName=MSG_DETECT_BEGIN -Language=English -Detect begin, %1!u! packages -. - -MessageId=101 -Severity=Success -SymbolicName=MSG_DETECTED_PACKAGE -Language=English -Detected package: %1!ls!, state: %2!hs!, cached: %3!hs!, install registration state: %4!hs!, cache registration state: %5!hs! -. - -MessageId=102 -Severity=Success -SymbolicName=MSG_DETECTED_RELATED_BUNDLE -Language=English -Detected related bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!ls!, operation: %5!hs!, cached: %6!hs! -. - -MessageId=103 -Severity=Success -SymbolicName=MSG_DETECTED_RELATED_PACKAGE -Language=English -Detected related package: %1!ls!, scope: %2!hs!, version: %3!ls!, language: %4!u! operation: %5!hs! -. - -MessageId=104 -Severity=Success -SymbolicName=MSG_DETECTED_MSI_FEATURE -Language=English -Detected package: %1!ls!, feature: %2!ls!, state: %3!hs! -. - -MessageId=105 -Severity=Success -SymbolicName=MSG_DETECTED_MSP_TARGET -Language=English -Detected package: %1!ls! target: %2!ls!, state: %3!hs! -. - -MessageId=106 -Severity=Success -SymbolicName=MSG_DETECT_CALCULATE_PATCH_APPLICABILITY -Language=English -Calculating patch applicability for target product code: %1!ls!, context: %2!hs! -. - -MessageId=107 -Severity=Success -SymbolicName=MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE -Language=English -Detected forward compatible bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!ls!, cached: %5!hs! -. - -MessageId=108 -Severity=Warning -SymbolicName=MSG_DETECT_RELATED_BUNDLE_NOT_CACHED -Language=English -Detected related bundle missing from cache: %1!ls!, cache path: %2!ls! -. - -MessageId=120 -Severity=Warning -SymbolicName=MSG_DETECT_PACKAGE_NOT_FULLY_CACHED -Language=English -Detected partially cached package: %1!ls!, missing payload: %2!ls! -. - -MessageId=121 -Severity=Warning -SymbolicName=MSG_DETECT_FAILED_CALCULATE_PATCH_APPLICABILITY -Language=English -Could not calculate patch applicability for target product code: %1!ls!, context: %2!hs!, reason: 0x%3!x! -. - -MessageId=122 -Severity=Warning -SymbolicName=MSG_RELATED_PACKAGE_INVALID_VERSION -Language=English -Related package: '%1!ls!' has invalid version: %2!ls! -. - -MessageId=123 -Severity=Warning -SymbolicName=MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION -Language=English -Detected msi package with invalid version, product code: '%1!ls!', version: '%2!ls!' -. - -MessageId=151 -Severity=Error -SymbolicName=MSG_FAILED_DETECT_PACKAGE -Language=English -Detect failed for package: %2!ls!, error: %1!ls! -. - -MessageId=152 -Severity=Error -SymbolicName=MSG_FAILED_READ_RELATED_PACKAGE_LANGUAGE -Language=English -Detected related package: %2!ls!, but failed to read language: %3!hs!, error: 0x%1!x! -. - -MessageId=170 -Severity=Warning -SymbolicName=MSG_DETECT_BAD_PRODUCT_CONFIGURATION -Language=English -Detected bad configuration for product: %1!ls! -. - -MessageId=199 -Severity=Success -SymbolicName=MSG_DETECT_COMPLETE -Language=English -Detect complete, result: 0x%1!x!, installed: %2!hs!, cached: %3!hs!, eligible for cleanup: %4!hs! -. - -MessageId=200 -Severity=Success -SymbolicName=MSG_PLAN_BEGIN -Language=English -Plan begin, %1!u! packages, action: %2!hs! -. - -MessageId=201 -Severity=Success -SymbolicName=MSG_PLANNED_PACKAGE -Language=English -Planned package: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, cache: %7!hs!, uncache: %8!hs!, dependency: %9!hs!, expected install registration state: %10!hs!, expected cache registration state: %11!hs! -. - -MessageId=202 -Severity=Success -SymbolicName=MSG_PLANNED_BUNDLE_UX_CHANGED_REQUEST -Language=English -Planned bundle: %1!ls!, ba requested state: %2!hs! over default: %3!hs! -. - -MessageId=203 -Severity=Success -SymbolicName=MSG_PLANNED_MSI_FEATURE -Language=English - Planned feature: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute action: %5!hs!, rollback action: %6!hs! -. - -MessageId=204 -Severity=Success -SymbolicName=MSG_PLANNED_MSI_FEATURES -Language=English - Plan %1!u! msi features for package: %2!ls! -. - -MessageId=205 -Severity=Warning -SymbolicName=MSG_PLAN_SKIP_PATCH_ACTION -Language=English -Plan %5!hs! skipped patch: %1!ls!, action: %2!hs! because chained target package: %3!ls! being uninstalled -. - -MessageId=206 -Severity=Warning -SymbolicName=MSG_PLAN_SKIP_SLIPSTREAM_ACTION -Language=English -Plan %5!hs! skipped patch: %1!ls!, action: %2!hs! because slipstreamed into chained target package: %3!ls!, action: %4!hs! -. - -MessageId=207 -Severity=Success -SymbolicName=MSG_PLANNED_RELATED_BUNDLE -Language=English -Planned related bundle: %1!ls!, type: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, dependency: %7!hs! -. - -MessageId=208 -Severity=Warning -SymbolicName=MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE -Language=English -Plan disabled rollback due to incomplete cache for package: %1!ls!, original rollback action: %2!hs! -. - -MessageId=209 -Severity=Warning -SymbolicName=MSG_PLAN_SKIPPED_PROVIDER_KEY_REMOVAL -Language=English -Plan skipped removal of provider key: %1!ls! because it is registered to a different bundle: %2!ls! -. - -MessageId=210 -Severity=Warning -SymbolicName=MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS -Language=English -Plan skipped due to remaining dependents: -. - -MessageId=211 -Severity=Success -SymbolicName=MSG_PLANNED_UPGRADE_BUNDLE -Language=English -Planned upgrade bundle: %1!ls!, default requested: %2!hs!, ba requested: %3!hs!, execute: %4!hs!, rollback: %5!hs!, dependency: %6!hs! -. - -MessageId=212 -Severity=Success -SymbolicName=MSG_PLANNED_FORWARD_COMPATIBLE_BUNDLE -Language=English -Planned forward compatible bundle: %1!ls!, default requested: %2!hs!, ba requested: %3!hs!, execute: %4!hs!, rollback: %5!hs!, dependency: %6!hs! -. - -MessageId=213 -Severity=Success -SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_DEPENDENT -Language=English -Plan skipped related bundle: %1!ls!, type: %2!hs!, because it was dependent and the current bundle is being executed as type: %3!hs!. -. - -MessageId=214 -Severity=Success -SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_SCHEDULED -Language=English -Plan skipped related bundle: %1!ls!, type: %2!hs!, because it was previously scheduled. -. - -MessageId=216 -Severity=Success -SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER -Language=English -Plan skipped related bundle: %1!ls!, type: %2!hs!, provider key: %3!ls!, because an embedded bundle with the same provider key is being installed. -. - -MessageId=217 -Severity=Success -SymbolicName=MSG_PLAN_SKIPPED_DEPENDENT_BUNDLE_REPAIR -Language=English -Plan skipped dependent bundle repair: %1!ls!, type: %2!hs!, because no packages are being executed during this uninstall operation. -. - -MessageId=218 -Severity=Success -SymbolicName=MSG_PLANNED_MSP_TARGETS -Language=English - Plan %1!u! patch targets for package: %2!ls! -. - -MessageId=219 -Severity=Success -SymbolicName=MSG_PLANNED_MSP_TARGET -Language=English - Planned patch target: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs! -. - -MessageId=220 -Severity=Success -SymbolicName=MSG_PLANNED_SLIPSTREAMED_MSP_TARGETS -Language=English - Plan %1!u! slipstream patches for package: %2!ls! -. - -MessageId=221 -Severity=Success -SymbolicName=MSG_PLANNED_SLIPSTREAMED_MSP_TARGET -Language=English - Planned slipstreamed patch: %1!ls!, execute: %2!hs!, rollback: %3!hs! -. - -MessageId=299 -Severity=Success -SymbolicName=MSG_PLAN_COMPLETE -Language=English -Plan complete, result: 0x%1!x! -. - -MessageId=300 -Severity=Success -SymbolicName=MSG_APPLY_BEGIN -Language=English -Apply begin -. - -MessageId=301 -Severity=Success -SymbolicName=MSG_APPLYING_PACKAGE -Language=English -Applying %1!hs! package: %2!ls!, action: %3!hs!, path: %4!ls!, arguments: '%5!ls!' -. - -MessageId=302 -Severity=Success -SymbolicName=MSG_ACQUIRED_PAYLOAD -Language=English -Acquired payload: %1!ls! to working path: %2!ls! from: %4!ls!. -. - -MessageId=303 -Severity=Success -SymbolicName=MSG_VERIFIED_EXISTING_CONTAINER -Language=English -Verified existing container: %1!ls! at path: %2!ls!. -. - -MessageId=304 -Severity=Success -SymbolicName=MSG_VERIFIED_EXISTING_PAYLOAD -Language=English -Verified existing payload: %1!ls! at path: %2!ls!. -. - -MessageId=305 -Severity=Success -SymbolicName=MSG_VERIFIED_ACQUIRED_PAYLOAD -Language=English -Verified acquired payload: %1!ls! at path: %2!ls!, %3!hs! to: %4!ls!. -. - -MessageId=306 -Severity=Success -SymbolicName=MSG_APPLYING_PATCH_PACKAGE -Language=English -Applying package: %1!ls!, target: %5!ls!, action: %2!hs!, path: %3!ls!, arguments: '%4!ls!' -. - -MessageId=307 -Severity=Warning -SymbolicName=MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE -Language=English -Attempted to uninstall absent package: %1!ls!. Continuing... -. - -MessageId=308 -Severity=Warning -SymbolicName=MSG_FAILED_PAUSE_AU -Language=English -Automatic updates could not be paused due to error: 0x%1!x!. Continuing... -. - -MessageId=309 -Severity=Warning -SymbolicName=MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE -Language=English -Skipping apply of package: %1!ls! due to cache error: 0x%2!x!. Continuing... -. - -MessageId=310 -Severity=Error -SymbolicName=MSG_FAILED_VERIFY_PAYLOAD -Language=English -Failed to verify payload: %2!ls! at path: %3!ls!, error: %1!ls!. Deleting file. -. - -MessageId=311 -Severity=Error -SymbolicName=MSG_FAILED_ACQUIRE_CONTAINER -Language=English -Failed to acquire container: %2!ls! to working path: %3!ls!, error: %1!ls!. -. - -MessageId=312 -Severity=Error -SymbolicName=MSG_FAILED_EXTRACT_CONTAINER -Language=English -Failed to extract payloads from container: %2!ls! to working path: %3!ls!, error: %1!ls!. -. - -MessageId=313 -Severity=Error -SymbolicName=MSG_FAILED_ACQUIRE_PAYLOAD -Language=English -Failed to acquire payload: %2!ls! to working path: %3!ls!, error: %1!ls!. -. - -MessageId=314 -Severity=Error -SymbolicName=MSG_FAILED_CACHE_PAYLOAD -Language=English -Failed to cache payload: %2!ls! from working path: %4!ls!, error: %1!ls!. -. - -MessageId=315 -Severity=Error -SymbolicName=MSG_FAILED_LAYOUT_BUNDLE -Language=English -Failed to layout bundle: %2!ls! to layout directory: %3!ls!, error: %1!ls!. -. - -MessageId=316 -Severity=Error -SymbolicName=MSG_FAILED_LAYOUT_CONTAINER -Language=English -Failed to layout container: %2!ls! to layout directory: %3!ls!, error: %1!ls!. -. - - -MessageId=317 -Severity=Error -SymbolicName=MSG_FAILED_LAYOUT_PAYLOAD -Language=English -Failed to layout payload: %2!ls! from working path: %4!ls! to layout directory: %3!ls!, error: %1!ls!. -. - -MessageId=318 -Severity=Success -SymbolicName=MSG_ROLLBACK_PACKAGE_SKIPPED -Language=English -Skipped rollback of package: %1!ls!, action: %2!hs!, already: %3!hs! -. - -MessageId=319 -Severity=Success -SymbolicName=MSG_APPLY_COMPLETED_PACKAGE -Language=English -Applied %1!hs! package: %2!ls!, result: 0x%3!x!, restart: %4!hs! -. - -MessageId=320 -Severity=Success -SymbolicName=MSG_DEPENDENCY_BUNDLE_REGISTER -Language=English -Registering bundle dependency provider: %1!ls!, version: %2!ls! -. - -MessageId=321 -Severity=Warning -SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_NOPROVIDERS -Language=English -Skipping dependency registration on package with no dependency providers: %1!ls! -. - -MessageId=322 -Severity=Warning -SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE -Language=English -Skipping cross-scope dependency registration on package: %1!ls!, bundle scope: %2!hs!, package scope: %3!hs! -. - -MessageId=323 -Severity=Success -SymbolicName=MSG_DEPENDENCY_PACKAGE_REGISTER -Language=English -Registering package dependency provider: %1!ls!, version: %2!ls!, package: %3!ls! -. - -MessageId=324 -Severity=Warning -SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_MISSING -Language=English -Skipping dependency registration on missing package provider: %1!ls!, package: %2!ls! -. - -MessageId=325 -Severity=Success -SymbolicName=MSG_DEPENDENCY_PACKAGE_REGISTER_DEPENDENCY -Language=English -Registering dependency: %1!ls! on package provider: %2!ls!, package: %3!ls! -. - -MessageId=326 -Severity=Success -SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY -Language=English -Removed dependency: %1!ls! on package provider: %2!ls!, package %3!ls! -. - -MessageId=327 -Severity=Warning -SymbolicName=MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS -Language=English -Will not uninstall package: %1!ls!, found dependents: -. - -MessageId=328 -Severity=Warning -SymbolicName=MSG_DEPENDENCY_PACKAGE_DEPENDENT -Language=English -Found dependent: %1!ls!, name: %2!ls! -. - -MessageId=329 -Severity=Success -SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED -Language=English -Removed package dependency provider: %1!ls!, package: %2!ls! -. - -MessageId=330 -Severity=Success -SymbolicName=MSG_DEPENDENCY_BUNDLE_UNREGISTERED -Language=English -Removed bundle dependency provider: %1!ls! -. - -MessageId=331 -Severity=Warning -SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY_FAILED -Language=English -Could not remove dependency: %1!ls! on package provider: %2!ls!, package %3!ls!, error: 0x%4!x! -. - -MessageId=332 -Severity=Warning -SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_FAILED -Language=English -Could not remove package dependency provider: %1!ls!, package: %2!ls!, error: 0x%3!x! -. - -MessageId=333 -Severity=Warning -SymbolicName=MSG_DEPENDENCY_BUNDLE_UNREGISTERED_FAILED -Language=English -Could not remove bundle dependency provider: %1!ls!, error: 0x%2!x! -. - -MessageId=334 -Severity=Warning -SymbolicName=MSG_DEPENDENCY_BUNDLE_DEPENDENT -Language=English -Found dependent: %1!ls!, name: %2!ls! -. - -MessageId=335 -Severity=Success -SymbolicName=MSG_ACQUIRE_BUNDLE_PAYLOAD -Language=English -Acquiring bundle payload: %2!ls!, %3!hs! from: %4!ls! -. - -MessageId=336 -Severity=Success -SymbolicName=MSG_ACQUIRE_CONTAINER -Language=English -Acquiring container: %1!ls!, %3!hs! from: %4!ls! -. - -MessageId=338 -Severity=Success -SymbolicName=MSG_ACQUIRE_PACKAGE_PAYLOAD -Language=English -Acquiring package: %1!ls!, payload: %2!ls!, %3!hs! from: %4!ls! -. - -MessageId=339 -Severity=Error -SymbolicName=MSG_FAILED_VERIFY_CONTAINER -Language=English -Failed to verify container: %2!ls! at path: %3!ls!, error: %1!ls!. Deleting file. -. - -MessageId=340 -Severity=Warning -SymbolicName=MSG_CACHE_CONTINUING_NONVITAL_PACKAGE -Language=English -Cached non-vital package: %1!ls!, encountered error: 0x%2!x!. Continuing... -. - -MessageId=346 -Severity=Warning -SymbolicName=MSG_CACHE_RETRYING_PACKAGE -Language=English -Application requested retry of caching package: %1!ls!, encountered error: 0x%2!x!. Retrying... -. - -MessageId=347 -Severity=Warning -SymbolicName=MSG_CACHE_RETRYING_CONTAINER -Language=English -Application requested retry of caching container: %2!ls!, encountered error: %1!ls!. Retrying... -. - -MessageId=348 -Severity=Warning -SymbolicName=MSG_APPLY_RETRYING_PACKAGE -Language=English -Application requested retry of executing package: %1!ls!, encountered error: 0x%2!x!. Retrying... -. - -MessageId=349 -Severity=Warning -SymbolicName=MSG_CACHE_RETRYING_PAYLOAD -Language=English -Application requested retry of caching payload: %2!ls!, encountered error: %1!ls!. Retrying... -. - -MessageId=350 -Severity=Warning -SymbolicName=MSG_APPLY_CONTINUING_NONVITAL_PACKAGE -Language=English -Applied non-vital package: %1!ls!, encountered error: 0x%2!x!. Continuing... -. - -MessageId=351 -Severity=Success -SymbolicName=MSG_UNCACHE_PACKAGE -Language=English -Removing cached package: %1!ls!, from path: %2!ls! -. - -MessageId=352 -Severity=Success -SymbolicName=MSG_UNCACHE_BUNDLE -Language=English -Removing cached bundle: %1!ls!, from path: %2!ls! -. - -MessageId=353 -Severity=Warning -SymbolicName=MSG_UNABLE_UNCACHE_PACKAGE -Language=English -Unable to remove cached package: %1!ls!, from path: %2!ls!, reason: 0x%3!x!. Continuing... -. - -MessageId=354 -Severity=Warning -SymbolicName=MSG_UNABLE_UNCACHE_BUNDLE -Language=English -Unable to remove cached bundle: %1!ls!, from path: %2!ls!, reason: 0x%3!x!. Continuing... -. - -MessageId=355 -Severity=Warning -SymbolicName=MSG_SOURCELIST_REGISTER -Language=English -Unable to register source directory: %1!ls!, product: %2!ls!, reason: 0x%3!x!. Continuing... -. - -MessageId=356 -Severity=Warning -SymbolicName=MSG_APPLY_RETRYING_ACQUIRE_CONTAINER -Language=English -Application requested retry acquire of container: %2!ls!, encountered error: %1!ls!. Retrying... -. - -MessageId=357 -Severity=Warning -SymbolicName=MSG_APPLY_RETRYING_ACQUIRE_PAYLOAD -Language=English -Application requested retry acquire of payload: %2!ls!, encountered error: %1!ls!. Retrying... -. - -MessageId=358 -Severity=Success -SymbolicName=MSG_PAUSE_AU_STARTING -Language=English -Pausing automatic updates. -. - -MessageId=359 -Severity=Success -SymbolicName=MSG_PAUSE_AU_SUCCEEDED -Language=English -Paused automatic updates. -. - -MessageId=360 -Severity=Success -SymbolicName=MSG_SYSTEM_RESTORE_POINT_STARTING -Language=English -Creating a system restore point. -. - -MessageId=361 -Severity=Success -SymbolicName=MSG_SYSTEM_RESTORE_POINT_SUCCEEDED -Language=English -Created a system restore point. -. - -MessageId=362 -Severity=Success -SymbolicName=MSG_SYSTEM_RESTORE_POINT_DISABLED -Language=English -System restore disabled, system restore point not created. -. - -MessageId=363 -Severity=Warning -SymbolicName=MSG_SYSTEM_RESTORE_POINT_FAILED -Language=English -Could not create system restore point, error: 0x%1!x!. Continuing... -. - -MessageId=370 -Severity=Success -SymbolicName=MSG_SESSION_BEGIN -Language=English -Session begin, registration key: %1!ls!, options: 0x%2!x!, disable resume: %3!hs! -. - -MessageId=371 -Severity=Success -SymbolicName=MSG_SESSION_UPDATE -Language=English -Updating session, registration key: %1!ls!, resume: %2!hs!, restart initiated: %3!hs!, disable resume: %4!hs! -. - -MessageId=372 -Severity=Success -SymbolicName=MSG_SESSION_END -Language=English -Session end, registration key: %1!ls!, resume: %2!hs!, restart: %3!hs!, disable resume: %4!hs! -. - -MessageId=373 -Severity=Success -SymbolicName=MSG_POST_APPLY_CALCULATE_REGISTRATION -Language=English -Calculating whether to keep registration -. - - -MessageId=374 -Severity=Success -SymbolicName=MSG_POST_APPLY_PACKAGE -Language=English - package: %1!ls!, install registration state: %2!hs!, cache registration state: %3!hs! -. - -MessageId=380 -Severity=Warning -SymbolicName=MSG_APPLY_SKIPPED -Language=English -Apply skipped, no planned actions -. - -MessageId=381 -Severity=Warning -SymbolicName=MSG_APPLY_CANCEL_IGNORED_DURING_ROLLBACK -Language=English -Ignoring application request to cancel from %1!ls! during rollback. -. - -MessageId=382 -Severity=Warning -SymbolicName=MSG_PLAN_ROLLBACK_DISABLED -Language=English -Rollback is disabled for this bundle. -. - -MessageId=383 -Severity=Error -SymbolicName=MSG_MSI_TRANSACTIONS_DISABLED -Language=English -Windows Installer rollback is disabled on this computer. It must be enabled for this bundle to proceed. -. - -MessageId=384 -Severity=Success -SymbolicName=MSG_MSI_TRANSACTION_BEGIN -Language=English -Starting a new MSI transaction, id: %1!ls! -. - -MessageId=385 -Severity=Success -SymbolicName=MSG_MSI_TRANSACTION_COMMIT -Language=English -Committing MSI transaction, id: %1!ls! -. - -MessageId=386 -Severity=Warning -SymbolicName=MSG_MSI_TRANSACTION_ROLLBACK -Language=English -Rolling back MSI transaction, id: %1!ls! -. - -MessageId=387 -Severity=Error -SymbolicName=MSG_RESTART_REQUEST_DURING_MSI_TRANSACTION -Language=English -Illegal state: Reboot requested within an MSI transaction, id: %1!ls! -. - -MessageId=399 -Severity=Success -SymbolicName=MSG_APPLY_COMPLETE -Language=English -Apply complete, result: 0x%1!x!, restart: %2!hs!, ba requested restart: %3!hs! -. - -MessageId=400 -Severity=Success -SymbolicName=MSG_SYSTEM_SHUTDOWN -Language=English -Received system request to shut down the process: critical: %1!hs!, elevated: %2!hs!, allowed: %3!hs! -. - -MessageId=410 -Severity=Success -SymbolicName=MSG_VARIABLE_DUMP -Language=English -Variable: %1!ls! -. - -MessageId=411 -Severity=Warning -SymbolicName=MSG_VARIABLE_INVALID_VERSION -Language=English -The variable '%1!ls!' is being set with an invalid version string. -. - -MessageId=412 -Severity=Warning -SymbolicName=MSG_INVALID_VERSION_COERSION -Language=English -The string '%1!ls!' could not be coerced to a valid version. -. - -MessageId=420 -Severity=Success -SymbolicName=MSG_RESUME_AU_STARTING -Language=English -Resuming automatic updates. -. - -MessageId=421 -Severity=Success -SymbolicName=MSG_RESUME_AU_SUCCEEDED -Language=English -Resumed automatic updates. -. - -MessageId=500 -Severity=Success -SymbolicName=MSG_QUIT -Language=English -Shutting down, exit code: 0x%1!x! -. - -MessageId=501 -Severity=Warning -SymbolicName=MSG_STATE_NOT_SAVED -Language=English -The state file could not be saved, error: %1!ls!. Continuing... -. - -MessageId=502 -Severity=Success -SymbolicName=MSG_CLEANUP_BEGIN -Language=English -Cleanup begin. -. - -MessageId=503 -Severity=Success -SymbolicName=MSG_CLEANUP_SKIPPED_APPLY -Language=English -Cleanup not required due to running Apply. -. - -MessageId=504 -Severity=Success -SymbolicName=MSG_CLEANUP_SKIPPED_ELEVATION_REQUIRED -Language=English -Cleanup check skipped since this per-machine bundle would require elevation. -. - -MessageId=599 -Severity=Success -SymbolicName=MSG_CLEANUP_COMPLETE -Language=English -Cleanup complete, result: 0x%1!x! -. - -MessageId=600 -Severity=Success -SymbolicName=MSG_LAUNCH_APPROVED_EXE_BEGIN -Language=English -LaunchApprovedExe begin, id: %1!ls! -. - -MessageId=601 -Severity=Success -SymbolicName=MSG_LAUNCH_APPROVED_EXE_SEARCH -Language=English -Searching registry for approved exe path, key: %1!ls!, value: '%2!ls!', win64: %3!ls! -. - -MessageId=602 -Severity=Success -SymbolicName=MSG_LAUNCHING_APPROVED_EXE -Language=English -Launching approved exe, path: '%1!ls!', 'command: %2!ls!' -. - -MessageId=699 -Severity=Success -SymbolicName=MSG_LAUNCH_APPROVED_EXE_COMPLETE -Language=English -LaunchApprovedExe complete, result: 0x%1!x!, processId: %2!lu! -. - -MessageId=700 -Severity=Success -SymbolicName=MSG_MSI_PROPERTY_CONDITION_FAILED -Language=English -Skipping MSI property '%1!ls!' because condition '%2!ls!' evaluates to %3!hs!. -. - -MessageId=701 -Severity=Warning -SymbolicName=MSG_PENDING_REBOOT_DETECTED -Language=English -A reboot is pending from a prior execution of this bundle: %1!ls!. Apply will be blocked. Continuing... -. diff --git a/src/engine/engine.vcxproj b/src/engine/engine.vcxproj deleted file mode 100644 index b3a0f81b..00000000 --- a/src/engine/engine.vcxproj +++ /dev/null @@ -1,186 +0,0 @@ - - - - - - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - Debug - ARM64 - - - Release - ARM64 - - - - - {8119537D-E1D9-6591-D51A-49768A2F9C37} - StaticLibrary - engine - v142 - Unicode - Native component of WixToolset.Burn - - - - - - - $(ProjectDir)..\..\..\balutil\src\WixToolset.BootstrapperCore.Native\inc;$(ProjectAdditionalIncludeDirectories) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Create - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Compiling message file... - mc.exe -h "$(IntDir)." -r "$(IntDir)." -A -c -z engine.messages "$(InputDir)engine.mc" -rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc" - $(IntDir)engine.messages.h;$(IntDir)engine.messages.rc;$(OutDir)engine.res - - - - - - $(MajorMinorVersion.Split(`.`)[0]) - $(MajorMinorVersion.Split(`.`)[1]) - 0 - $(BuildNumber) - $(rmj).$(rmm).$(rup).$(rpr) - rmj=$(rmj);rmm=$(rmm);rup=$(rup);rpr=$(rpr);szVerMajorMinorBuild="$(szVerMajorMinorBuild)" - - - - - $(wixver);%(PreprocessorDefinitions) - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - diff --git a/src/engine/exeengine.cpp b/src/engine/exeengine.cpp deleted file mode 100644 index c0ba93e0..00000000 --- a/src/engine/exeengine.cpp +++ /dev/null @@ -1,816 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// internal function declarations - -static HRESULT HandleExitCode( - __in BURN_PACKAGE* pPackage, - __in DWORD dwExitCode, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ); -static HRESULT ParseCommandLineArgumentsFromXml( - __in IXMLDOMNode* pixnExePackage, - __in BURN_PACKAGE* pPackage - ); -static HRESULT ParseExitCodesFromXml( - __in IXMLDOMNode* pixnExePackage, - __in BURN_PACKAGE* pPackage - ); - - -// function definitions - -extern "C" HRESULT ExeEngineParsePackageFromXml( - __in IXMLDOMNode* pixnExePackage, - __in BURN_PACKAGE* pPackage - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnNodes = NULL; - IXMLDOMNode* pixnNode = NULL; - LPWSTR scz = NULL; - - // @DetectCondition - hr = XmlGetAttributeEx(pixnExePackage, L"DetectCondition", &pPackage->Exe.sczDetectCondition); - ExitOnFailure(hr, "Failed to get @DetectCondition."); - - // @InstallArguments - hr = XmlGetAttributeEx(pixnExePackage, L"InstallArguments", &pPackage->Exe.sczInstallArguments); - ExitOnFailure(hr, "Failed to get @InstallArguments."); - - // @UninstallArguments - hr = XmlGetAttributeEx(pixnExePackage, L"UninstallArguments", &pPackage->Exe.sczUninstallArguments); - ExitOnFailure(hr, "Failed to get @UninstallArguments."); - - // @RepairArguments - hr = XmlGetAttributeEx(pixnExePackage, L"RepairArguments", &pPackage->Exe.sczRepairArguments); - ExitOnFailure(hr, "Failed to get @RepairArguments."); - - // @Repairable - hr = XmlGetYesNoAttribute(pixnExePackage, L"Repairable", &pPackage->Exe.fRepairable); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @Repairable."); - } - - // @Protocol - hr = XmlGetAttributeEx(pixnExePackage, L"Protocol", &scz); - if (SUCCEEDED(hr)) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"burn", -1)) - { - pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_BURN; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"netfx4", -1)) - { - pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_NETFX4; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"none", -1)) - { - pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_NONE; - } - else - { - hr = E_UNEXPECTED; - ExitOnFailure(hr, "Invalid protocol type: %ls", scz); - } - } - else if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @Protocol."); - } - - hr = ParseExitCodesFromXml(pixnExePackage, pPackage); - ExitOnFailure(hr, "Failed to parse exit codes."); - - hr = ParseCommandLineArgumentsFromXml(pixnExePackage, pPackage); - ExitOnFailure(hr, "Failed to parse command lines."); - -LExit: - ReleaseObject(pixnNodes); - ReleaseObject(pixnNode); - ReleaseStr(scz); - - return hr; -} - -extern "C" void ExeEnginePackageUninitialize( - __in BURN_PACKAGE* pPackage - ) -{ - ReleaseStr(pPackage->Exe.sczDetectCondition); - ReleaseStr(pPackage->Exe.sczInstallArguments); - ReleaseStr(pPackage->Exe.sczRepairArguments); - ReleaseStr(pPackage->Exe.sczUninstallArguments); - ReleaseStr(pPackage->Exe.sczIgnoreDependencies); - //ReleaseStr(pPackage->Exe.sczProgressSwitch); - ReleaseMem(pPackage->Exe.rgExitCodes); - - // free command-line arguments - if (pPackage->Exe.rgCommandLineArguments) - { - for (DWORD i = 0; i < pPackage->Exe.cCommandLineArguments; ++i) - { - BURN_EXE_COMMAND_LINE_ARGUMENT* pCommandLineArgument = &pPackage->Exe.rgCommandLineArguments[i]; - ReleaseStr(pCommandLineArgument->sczInstallArgument); - ReleaseStr(pCommandLineArgument->sczUninstallArgument); - ReleaseStr(pCommandLineArgument->sczRepairArgument); - ReleaseStr(pCommandLineArgument->sczCondition); - } - MemFree(pPackage->Exe.rgCommandLineArguments); - } - - // clear struct - memset(&pPackage->Exe, 0, sizeof(pPackage->Exe)); -} - -extern "C" HRESULT ExeEngineDetectPackage( - __in BURN_PACKAGE* pPackage, - __in BURN_VARIABLES* pVariables - ) -{ - HRESULT hr = S_OK; - BOOL fDetected = FALSE; - - // evaluate detect condition - if (pPackage->Exe.sczDetectCondition && *pPackage->Exe.sczDetectCondition) - { - hr = ConditionEvaluate(pVariables, pPackage->Exe.sczDetectCondition, &fDetected); - ExitOnFailure(hr, "Failed to evaluate executable package detect condition."); - } - - // update detect state - pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT; - - if (pPackage->fCanAffectRegistration) - { - pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - } - -LExit: - return hr; -} - -// -// PlanCalculate - calculates the execute and rollback state for the requested package state. -// -extern "C" HRESULT ExeEnginePlanCalculatePackage( - __in BURN_PACKAGE* pPackage - ) -{ - HRESULT hr = S_OK; - BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; - BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - - // execute action - switch (pPackage->currentState) - { - case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: - switch (pPackage->requested) - { - case BOOTSTRAPPER_REQUEST_STATE_PRESENT: - execute = pPackage->Exe.fPseudoBundle ? BOOTSTRAPPER_ACTION_STATE_INSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; - break; - case BOOTSTRAPPER_REQUEST_STATE_REPAIR: - execute = pPackage->Exe.fRepairable ? BOOTSTRAPPER_ACTION_STATE_REPAIR : BOOTSTRAPPER_ACTION_STATE_NONE; - break; - case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_CACHE: - execute = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; - break; - case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: - execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; - break; - default: - execute = BOOTSTRAPPER_ACTION_STATE_NONE; - break; - } - break; - - case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: - switch (pPackage->requested) - { - case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_REPAIR: - execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; - break; - default: - execute = BOOTSTRAPPER_ACTION_STATE_NONE; - break; - } - break; - - default: - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Invalid package current state: %d.", pPackage->currentState); - } - - // Calculate the rollback action if there is an execute action. - if (BOOTSTRAPPER_ACTION_STATE_NONE != execute) - { - switch (pPackage->currentState) - { - case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: - switch (pPackage->requested) - { - case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_REPAIR: - rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - break; - case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_ABSENT: - rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL; - break; - default: - rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - break; - } - break; - - case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: - switch (pPackage->requested) - { - case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_REPAIR: - rollback = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; - break; - case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_ABSENT: - rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - break; - default: - rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - break; - } - break; - - default: - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Invalid package expected state."); - } - } - - // return values - pPackage->execute = execute; - pPackage->rollback = rollback; - -LExit: - return hr; -} - -// -// PlanAdd - adds the calculated execute and rollback actions for the package. -// -extern "C" HRESULT ExeEnginePlanAddPackage( - __in_opt DWORD *pdwInsertSequence, - __in BURN_PACKAGE* pPackage, - __in BURN_PLAN* pPlan, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in_opt HANDLE hCacheEvent - ) -{ - HRESULT hr = S_OK; - BURN_EXECUTE_ACTION* pAction = NULL; - - // add wait for cache - if (hCacheEvent) - { - hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent); - ExitOnFailure(hr, "Failed to plan package cache syncpoint"); - } - - hr = DependencyPlanPackage(pdwInsertSequence, pPackage, pPlan); - ExitOnFailure(hr, "Failed to plan package dependency actions."); - - // add execute action - if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute) - { - if (NULL != pdwInsertSequence) - { - hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction); - ExitOnFailure(hr, "Failed to insert execute action."); - } - else - { - hr = PlanAppendExecuteAction(pPlan, &pAction); - ExitOnFailure(hr, "Failed to append execute action."); - } - - pAction->type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE; - pAction->exePackage.pPackage = pPackage; - pAction->exePackage.fFireAndForget = (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == pPlan->action); - pAction->exePackage.action = pPackage->execute; - - if (pPackage->Exe.sczIgnoreDependencies) - { - hr = StrAllocString(&pAction->exePackage.sczIgnoreDependencies, pPackage->Exe.sczIgnoreDependencies, 0); - ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore."); - } - - if (pPackage->Exe.wzAncestors) - { - hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.wzAncestors, 0); - ExitOnFailure(hr, "Failed to allocate the list of ancestors."); - } - - LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, NULL); // ignore errors. - } - - // add rollback action - if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback) - { - hr = PlanAppendRollbackAction(pPlan, &pAction); - ExitOnFailure(hr, "Failed to append rollback action."); - - pAction->type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE; - pAction->exePackage.pPackage = pPackage; - pAction->exePackage.action = pPackage->rollback; - - if (pPackage->Exe.sczIgnoreDependencies) - { - hr = StrAllocString(&pAction->exePackage.sczIgnoreDependencies, pPackage->Exe.sczIgnoreDependencies, 0); - ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore."); - } - - if (pPackage->Exe.wzAncestors) - { - hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.wzAncestors, 0); - ExitOnFailure(hr, "Failed to allocate the list of ancestors."); - } - - LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, NULL); // ignore errors. - } - -LExit: - return hr; -} - -extern "C" HRESULT ExeEngineExecutePackage( - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_VARIABLES* pVariables, - __in BOOL fRollback, - __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, - __in LPVOID pvContext, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ) -{ - HRESULT hr = S_OK; - int nResult = IDNOACTION; - LPCWSTR wzArguments = NULL; - LPWSTR sczArguments = NULL; - LPWSTR sczArgumentsFormatted = NULL; - LPWSTR sczArgumentsObfuscated = NULL; - LPWSTR sczCachedDirectory = NULL; - LPWSTR sczExecutablePath = NULL; - LPWSTR sczCommand = NULL; - LPWSTR sczCommandObfuscated = NULL; - HANDLE hExecutableFile = INVALID_HANDLE_VALUE; - STARTUPINFOW si = { }; - PROCESS_INFORMATION pi = { }; - DWORD dwExitCode = 0; - GENERIC_EXECUTE_MESSAGE message = { }; - BURN_PACKAGE* pPackage = pExecuteAction->exePackage.pPackage; - BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload; - - // get cached executable path - hr = CacheGetCompletedPath(pPackage->fPerMachine, pPackage->sczCacheId, &sczCachedDirectory); - ExitOnFailure(hr, "Failed to get cached path for package: %ls", pPackage->sczId); - - // Best effort to set the execute package cache folder and action variables. - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE); - VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->exePackage.action, TRUE); - - hr = PathConcat(sczCachedDirectory, pPackagePayload->sczFilePath, &sczExecutablePath); - ExitOnFailure(hr, "Failed to build executable path."); - - // pick arguments - switch (pExecuteAction->exePackage.action) - { - case BOOTSTRAPPER_ACTION_STATE_INSTALL: - wzArguments = pPackage->Exe.sczInstallArguments; - break; - - case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: - wzArguments = pPackage->Exe.sczUninstallArguments; - break; - - case BOOTSTRAPPER_ACTION_STATE_REPAIR: - wzArguments = pPackage->Exe.sczRepairArguments; - break; - - default: - hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action); - } - - // now add optional arguments - hr = StrAllocString(&sczArguments, wzArguments && *wzArguments ? wzArguments : L"", 0); - ExitOnFailure(hr, "Failed to copy package arguments."); - - for (DWORD i = 0; i < pPackage->Exe.cCommandLineArguments; ++i) - { - BURN_EXE_COMMAND_LINE_ARGUMENT* commandLineArgument = &pPackage->Exe.rgCommandLineArguments[i]; - BOOL fCondition = FALSE; - - hr = ConditionEvaluate(pVariables, commandLineArgument->sczCondition, &fCondition); - ExitOnFailure(hr, "Failed to evaluate executable package command-line condition."); - - if (fCondition) - { - hr = StrAllocConcat(&sczArguments, L" ", 0); - ExitOnFailure(hr, "Failed to separate command-line arguments."); - - switch (pExecuteAction->exePackage.action) - { - case BOOTSTRAPPER_ACTION_STATE_INSTALL: - hr = StrAllocConcat(&sczArguments, commandLineArgument->sczInstallArgument, 0); - ExitOnFailure(hr, "Failed to get command-line argument for install."); - break; - - case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: - hr = StrAllocConcat(&sczArguments, commandLineArgument->sczUninstallArgument, 0); - ExitOnFailure(hr, "Failed to get command-line argument for uninstall."); - break; - - case BOOTSTRAPPER_ACTION_STATE_REPAIR: - hr = StrAllocConcat(&sczArguments, commandLineArgument->sczRepairArgument, 0); - ExitOnFailure(hr, "Failed to get command-line argument for repair."); - break; - - default: - hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action); - } - } - } - - // build command - if (*sczArguments) - { - hr = VariableFormatString(pVariables, sczArguments, &sczArgumentsFormatted, NULL); - ExitOnFailure(hr, "Failed to format argument string."); - - hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", sczExecutablePath, sczArgumentsFormatted); - ExitOnFailure(hr, "Failed to create executable command."); - - hr = VariableFormatStringObfuscated(pVariables, sczArguments, &sczArgumentsObfuscated, NULL); - ExitOnFailure(hr, "Failed to format obfuscated argument string."); - - hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", sczExecutablePath, sczArgumentsObfuscated); - } - else - { - hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", sczExecutablePath); - ExitOnFailure(hr, "Failed to create executable command."); - - hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", sczExecutablePath); - } - ExitOnFailure(hr, "Failed to create obfuscated executable command."); - - if (pPackage->Exe.fSupportsAncestors) - { - // Add the list of dependencies to ignore, if any, to the burn command line. - if (pExecuteAction->exePackage.sczIgnoreDependencies && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) - { - hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies); - ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line."); - - hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies); - ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the obfuscated command line."); - } - - // Add the list of ancestors, if any, to the burn command line. - if (pExecuteAction->exePackage.sczAncestors) - { - hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors); - ExitOnFailure(hr, "Failed to append the list of ancestors to the command line."); - - hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors); - ExitOnFailure(hr, "Failed to append the list of ancestors to the obfuscated command line."); - } - } - - if (BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) - { - hr = CoreAppendFileHandleSelfToCommandLine(sczExecutablePath, &hExecutableFile, &sczCommand, &sczCommandObfuscated); - ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF); - } - - // Log before we add the secret pipe name and client token for embedded processes. - LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, LoggingActionStateToString(pExecuteAction->exePackage.action), sczExecutablePath, sczCommandObfuscated); - - if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) - { - hr = EmbeddedRunBundle(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode); - ExitOnFailure(hr, "Failed to run bundle as embedded from path: %ls", sczExecutablePath); - } - else if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_NETFX4 == pPackage->Exe.protocol) - { - hr = NetFxRunChainer(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode); - ExitOnFailure(hr, "Failed to run netfx chainer: %ls", sczExecutablePath); - } - else // create and wait for the executable process while sending fake progress to allow cancel. - { - // Make the cache location of the executable the current directory to help those executables - // that expect stuff to be relative to them. - si.cb = sizeof(si); - if (!::CreateProcessW(sczExecutablePath, sczCommand, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, sczCachedDirectory, &si, &pi)) - { - ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", sczExecutablePath); - } - - if (pExecuteAction->exePackage.fFireAndForget) - { - ::WaitForInputIdle(pi.hProcess, 5000); - ExitFunction(); - } - - do - { - message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; - message.dwAllowedResults = MB_OKCANCEL; - message.progress.dwPercentage = 50; - nResult = pfnGenericMessageHandler(&message, pvContext); - hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); - ExitOnRootFailure(hr, "Bootstrapper application aborted during EXE progress."); - - hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode); - if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr) - { - ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", sczExecutablePath); - } - } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr); - } - - hr = HandleExitCode(pPackage, dwExitCode, pRestart); - ExitOnRootFailure(hr, "Process returned error: 0x%x", dwExitCode); - -LExit: - StrSecureZeroFreeString(sczArguments); - StrSecureZeroFreeString(sczArgumentsFormatted); - ReleaseStr(sczArgumentsObfuscated); - ReleaseStr(sczCachedDirectory); - ReleaseStr(sczExecutablePath); - StrSecureZeroFreeString(sczCommand); - ReleaseStr(sczCommandObfuscated); - - ReleaseHandle(pi.hThread); - ReleaseHandle(pi.hProcess); - ReleaseFileHandle(hExecutableFile); - - // Best effort to clear the execute package cache folder and action variables. - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE); - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE, FALSE); - - return hr; -} - -extern "C" void ExeEngineUpdateInstallRegistrationState( - __in BURN_EXECUTE_ACTION* pAction, - __in HRESULT hrExecute - ) -{ - BURN_PACKAGE* pPackage = pAction->exePackage.pPackage; - - if (FAILED(hrExecute) || !pPackage->fCanAffectRegistration) - { - ExitFunction(); - } - - if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->exePackage.action) - { - pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - } - else - { - pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } - -LExit: - return; -} - - -// internal helper functions - -static HRESULT ParseExitCodesFromXml( - __in IXMLDOMNode* pixnExePackage, - __in BURN_PACKAGE* pPackage - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnNodes = NULL; - IXMLDOMNode* pixnNode = NULL; - DWORD cNodes = 0; - LPWSTR scz = NULL; - - // select exit code nodes - hr = XmlSelectNodes(pixnExePackage, L"ExitCode", &pixnNodes); - ExitOnFailure(hr, "Failed to select exit code nodes."); - - // get exit code node count - hr = pixnNodes->get_length((long*) &cNodes); - ExitOnFailure(hr, "Failed to get exit code node count."); - - if (cNodes) - { - // allocate memory for exit codes - pPackage->Exe.rgExitCodes = (BURN_EXE_EXIT_CODE*) MemAlloc(sizeof(BURN_EXE_EXIT_CODE) * cNodes, TRUE); - ExitOnNull(pPackage->Exe.rgExitCodes, hr, E_OUTOFMEMORY, "Failed to allocate memory for exit code structs."); - - pPackage->Exe.cExitCodes = cNodes; - - // parse package elements - for (DWORD i = 0; i < cNodes; ++i) - { - BURN_EXE_EXIT_CODE* pExitCode = &pPackage->Exe.rgExitCodes[i]; - - hr = XmlNextElement(pixnNodes, &pixnNode, NULL); - ExitOnFailure(hr, "Failed to get next node."); - - // @Type - hr = XmlGetAttributeNumber(pixnNode, L"Type", (DWORD*)&pExitCode->type); - ExitOnFailure(hr, "Failed to get @Type."); - - // @Code - hr = XmlGetAttributeEx(pixnNode, L"Code", &scz); - ExitOnFailure(hr, "Failed to get @Code."); - - if (L'*' == scz[0]) - { - pExitCode->fWildcard = TRUE; - } - else - { - hr = StrStringToUInt32(scz, 0, (UINT*) &pExitCode->dwCode); - ExitOnFailure(hr, "Failed to parse @Code value: %ls", scz); - } - - // prepare next iteration - ReleaseNullObject(pixnNode); - } - } - - hr = S_OK; - -LExit: - ReleaseObject(pixnNodes); - ReleaseObject(pixnNode); - ReleaseStr(scz); - - return hr; -} - -static HRESULT ParseCommandLineArgumentsFromXml( - __in IXMLDOMNode* pixnExePackage, - __in BURN_PACKAGE* pPackage - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnNodes = NULL; - IXMLDOMNode* pixnNode = NULL; - DWORD cNodes = 0; - LPWSTR scz = NULL; - - // Select command-line argument nodes. - hr = XmlSelectNodes(pixnExePackage, L"CommandLine", &pixnNodes); - ExitOnFailure(hr, "Failed to select command-line argument nodes."); - - // Get command-line argument node count. - hr = pixnNodes->get_length((long*) &cNodes); - ExitOnFailure(hr, "Failed to get command-line argument count."); - - if (cNodes) - { - pPackage->Exe.rgCommandLineArguments = (BURN_EXE_COMMAND_LINE_ARGUMENT*) MemAlloc(sizeof(BURN_EXE_COMMAND_LINE_ARGUMENT) * cNodes, TRUE); - ExitOnNull(pPackage->Exe.rgCommandLineArguments, hr, E_OUTOFMEMORY, "Failed to allocate memory for command-line argument structs."); - - pPackage->Exe.cCommandLineArguments = cNodes; - - // Parse command-line argument elements. - for (DWORD i = 0; i < cNodes; ++i) - { - BURN_EXE_COMMAND_LINE_ARGUMENT* pCommandLineArgument = &pPackage->Exe.rgCommandLineArguments[i]; - - hr = XmlNextElement(pixnNodes, &pixnNode, NULL); - ExitOnFailure(hr, "Failed to get next command-line argument node."); - - // @InstallArgument - hr = XmlGetAttributeEx(pixnNode, L"InstallArgument", &pCommandLineArgument->sczInstallArgument); - ExitOnFailure(hr, "Failed to get @InstallArgument."); - - // @UninstallArgument - hr = XmlGetAttributeEx(pixnNode, L"UninstallArgument", &pCommandLineArgument->sczUninstallArgument); - ExitOnFailure(hr, "Failed to get @UninstallArgument."); - - // @RepairArgument - hr = XmlGetAttributeEx(pixnNode, L"RepairArgument", &pCommandLineArgument->sczRepairArgument); - ExitOnFailure(hr, "Failed to get @RepairArgument."); - - // @Condition - hr = XmlGetAttributeEx(pixnNode, L"Condition", &pCommandLineArgument->sczCondition); - ExitOnFailure(hr, "Failed to get @Condition."); - - // Prepare next iteration. - ReleaseNullObject(pixnNode); - } - } - - hr = S_OK; - -LExit: - ReleaseObject(pixnNodes); - ReleaseObject(pixnNode); - ReleaseStr(scz); - - return hr; -} - -static HRESULT HandleExitCode( - __in BURN_PACKAGE* pPackage, - __in DWORD dwExitCode, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ) -{ - HRESULT hr = S_OK; - BURN_EXE_EXIT_CODE_TYPE typeCode = BURN_EXE_EXIT_CODE_TYPE_NONE; - - for (DWORD i = 0; i < pPackage->Exe.cExitCodes; ++i) - { - BURN_EXE_EXIT_CODE* pExitCode = &pPackage->Exe.rgExitCodes[i]; - - // If this is a wildcard, use the last one we come across. - if (pExitCode->fWildcard) - { - typeCode = pExitCode->type; - } - else if (dwExitCode == pExitCode->dwCode) // If we have an exact match on the error code use that and stop looking. - { - typeCode = pExitCode->type; - break; - } - } - - // If we didn't find a matching code then treat 0 as success, the standard restarts codes as restarts - // and everything else as an error. - if (BURN_EXE_EXIT_CODE_TYPE_NONE == typeCode) - { - if (0 == dwExitCode) - { - typeCode = BURN_EXE_EXIT_CODE_TYPE_SUCCESS; - } - else if (ERROR_SUCCESS_REBOOT_REQUIRED == dwExitCode || - HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast(dwExitCode) || - ERROR_SUCCESS_RESTART_REQUIRED == dwExitCode || - HRESULT_FROM_WIN32(ERROR_SUCCESS_RESTART_REQUIRED) == static_cast(dwExitCode)) - { - typeCode = BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT; - } - else if (ERROR_SUCCESS_REBOOT_INITIATED == dwExitCode || - HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED) == static_cast(dwExitCode)) - { - typeCode = BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT; - } - else - { - typeCode = BURN_EXE_EXIT_CODE_TYPE_ERROR; - } - } - - switch (typeCode) - { - case BURN_EXE_EXIT_CODE_TYPE_SUCCESS: - *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; - hr = S_OK; - break; - - case BURN_EXE_EXIT_CODE_TYPE_ERROR: - *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; - hr = HRESULT_FROM_WIN32(dwExitCode); - if (SUCCEEDED(hr)) - { - hr = E_FAIL; - } - break; - - case BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT: - *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; - hr = S_OK; - break; - - case BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT: - *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; - hr = S_OK; - break; - - default: - hr = E_UNEXPECTED; - break; - } - -//LExit: - return hr; -} diff --git a/src/engine/exeengine.h b/src/engine/exeengine.h deleted file mode 100644 index e032ea01..00000000 --- a/src/engine/exeengine.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -// function declarations - -HRESULT ExeEngineParsePackageFromXml( - __in IXMLDOMNode* pixnExePackage, - __in BURN_PACKAGE* pPackage - ); -void ExeEnginePackageUninitialize( - __in BURN_PACKAGE* pPackage - ); -HRESULT ExeEngineDetectPackage( - __in BURN_PACKAGE* pPackage, - __in BURN_VARIABLES* pVariables - ); -HRESULT ExeEnginePlanCalculatePackage( - __in BURN_PACKAGE* pPackage - ); -HRESULT ExeEnginePlanAddPackage( - __in_opt DWORD *pdwInsertSequence, - __in BURN_PACKAGE* pPackage, - __in BURN_PLAN* pPlan, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in_opt HANDLE hCacheEvent - ); -HRESULT ExeEngineExecutePackage( - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_VARIABLES* pVariables, - __in BOOL fRollback, - __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress, - __in LPVOID pvContext, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ); -void ExeEngineUpdateInstallRegistrationState( - __in BURN_EXECUTE_ACTION* pAction, - __in HRESULT hrExecute - ); - - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/externalengine.cpp b/src/engine/externalengine.cpp deleted file mode 100644 index 409353e4..00000000 --- a/src/engine/externalengine.cpp +++ /dev/null @@ -1,805 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -static HRESULT CopyStringToExternal( - __in_z LPWSTR wzValue, - __in_z_opt LPWSTR wzBuffer, - __inout SIZE_T* pcchBuffer - ); - -// function definitions - -void ExternalEngineGetPackageCount( - __in BURN_ENGINE_STATE* pEngineState, - __out DWORD* pcPackages - ) -{ - *pcPackages = pEngineState->packages.cPackages; -} - -HRESULT ExternalEngineGetVariableNumeric( - __in BURN_ENGINE_STATE* pEngineState, - __in_z LPCWSTR wzVariable, - __out LONGLONG* pllValue - ) -{ - HRESULT hr = S_OK; - - if (wzVariable && *wzVariable) - { - hr = VariableGetNumeric(&pEngineState->variables, wzVariable, pllValue); - } - else - { - *pllValue = 0; - hr = E_INVALIDARG; - } - - return hr; -} - -HRESULT ExternalEngineGetVariableString( - __in BURN_ENGINE_STATE* pEngineState, - __in_z LPCWSTR wzVariable, - __out_ecount_opt(*pcchValue) LPWSTR wzValue, - __inout SIZE_T* pcchValue - ) -{ - HRESULT hr = S_OK; - LPWSTR sczValue = NULL; - - if (wzVariable && *wzVariable) - { - hr = VariableGetString(&pEngineState->variables, wzVariable, &sczValue); - if (SUCCEEDED(hr)) - { - hr = CopyStringToExternal(sczValue, wzValue, pcchValue); - } - } - else - { - hr = E_INVALIDARG; - } - - StrSecureZeroFreeString(sczValue); - - return hr; -} - -HRESULT ExternalEngineGetVariableVersion( - __in BURN_ENGINE_STATE* pEngineState, - __in_z LPCWSTR wzVariable, - __out_ecount_opt(*pcchValue) LPWSTR wzValue, - __inout SIZE_T* pcchValue - ) -{ - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion = NULL; - - if (wzVariable && *wzVariable) - { - hr = VariableGetVersion(&pEngineState->variables, wzVariable, &pVersion); - if (SUCCEEDED(hr)) - { - hr = CopyStringToExternal(pVersion->sczVersion, wzValue, pcchValue); - } - } - else - { - hr = E_INVALIDARG; - } - - ReleaseVerutilVersion(pVersion); - - return hr; -} - -HRESULT ExternalEngineFormatString( - __in BURN_ENGINE_STATE* pEngineState, - __in_z LPCWSTR wzIn, - __out_ecount_opt(*pcchOut) LPWSTR wzOut, - __inout SIZE_T* pcchOut - ) -{ - HRESULT hr = S_OK; - LPWSTR sczValue = NULL; - - if (wzIn && *wzIn) - { - hr = VariableFormatString(&pEngineState->variables, wzIn, &sczValue, NULL); - if (SUCCEEDED(hr)) - { - hr = CopyStringToExternal(sczValue, wzOut, pcchOut); - } - } - else - { - hr = E_INVALIDARG; - } - - StrSecureZeroFreeString(sczValue); - - return hr; -} - -HRESULT ExternalEngineEscapeString( - __in_z LPCWSTR wzIn, - __out_ecount_opt(*pcchOut) LPWSTR wzOut, - __inout SIZE_T* pcchOut - ) -{ - HRESULT hr = S_OK; - LPWSTR sczValue = NULL; - - if (wzIn && *wzIn) - { - hr = VariableEscapeString(wzIn, &sczValue); - if (SUCCEEDED(hr)) - { - hr = CopyStringToExternal(sczValue, wzOut, pcchOut); - } - } - else - { - hr = E_INVALIDARG; - } - - StrSecureZeroFreeString(sczValue); - - return hr; -} - -HRESULT ExternalEngineEvaluateCondition( - __in BURN_ENGINE_STATE* pEngineState, - __in_z LPCWSTR wzCondition, - __out BOOL* pf - ) -{ - HRESULT hr = S_OK; - - if (wzCondition && *wzCondition) - { - hr = ConditionEvaluate(&pEngineState->variables, wzCondition, pf); - } - else - { - *pf = FALSE; - hr = E_INVALIDARG; - } - - return hr; -} - -HRESULT ExternalEngineLog( - __in REPORT_LEVEL rl, - __in_z LPCWSTR wzMessage - ) -{ - HRESULT hr = S_OK; - - hr = LogStringLine(rl, "%ls", wzMessage); - - return hr; -} - -HRESULT ExternalEngineSendEmbeddedError( - __in BURN_ENGINE_STATE* pEngineState, - __in const DWORD dwErrorCode, - __in_z LPCWSTR wzMessage, - __in const DWORD dwUIHint, - __out int* pnResult - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwResult = *pnResult = 0; - - if (BURN_MODE_EMBEDDED != pEngineState->mode) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE); - ExitOnRootFailure(hr, "BA requested to send embedded message when not in embedded mode."); - } - - hr = BuffWriteNumber(&pbData, &cbData, dwErrorCode); - ExitOnFailure(hr, "Failed to write error code to message buffer."); - - hr = BuffWriteString(&pbData, &cbData, wzMessage ? wzMessage : L""); - ExitOnFailure(hr, "Failed to write message string to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, dwUIHint); - ExitOnFailure(hr, "Failed to write UI hint to message buffer."); - - hr = PipeSendMessage(pEngineState->embeddedConnection.hPipe, BURN_EMBEDDED_MESSAGE_TYPE_ERROR, pbData, cbData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send embedded message over pipe."); - - *pnResult = static_cast(dwResult); - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -HRESULT ExternalEngineSendEmbeddedProgress( - __in BURN_ENGINE_STATE* pEngineState, - __in const DWORD dwProgressPercentage, - __in const DWORD dwOverallProgressPercentage, - __out int* pnResult - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - DWORD dwResult = *pnResult = 0; - - if (BURN_MODE_EMBEDDED != pEngineState->mode) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE); - ExitOnRootFailure(hr, "BA requested to send embedded progress message when not in embedded mode."); - } - - hr = BuffWriteNumber(&pbData, &cbData, dwProgressPercentage); - ExitOnFailure(hr, "Failed to write progress percentage to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, dwOverallProgressPercentage); - ExitOnFailure(hr, "Failed to write overall progress percentage to message buffer."); - - hr = PipeSendMessage(pEngineState->embeddedConnection.hPipe, BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS, pbData, cbData, NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send embedded progress message over pipe."); - - *pnResult = static_cast(dwResult); - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -HRESULT ExternalEngineSetUpdate( - __in BURN_ENGINE_STATE* pEngineState, - __in_z_opt LPCWSTR wzLocalSource, - __in_z_opt LPCWSTR wzDownloadSource, - __in const DWORD64 qwSize, - __in const BOOTSTRAPPER_UPDATE_HASH_TYPE hashType, - __in_opt const BYTE* rgbHash, - __in const DWORD cbHash - ) -{ - HRESULT hr = S_OK; - LPWSTR sczFilePath = NULL; - LPWSTR sczCommandline = NULL; - UUID guid = { }; - WCHAR wzGuid[39]; - RPC_STATUS rs = RPC_S_OK; - - ::EnterCriticalSection(&pEngineState->userExperience.csEngineActive); - hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience); - ExitOnFailure(hr, "Engine is active, cannot change engine state."); - - if ((!wzLocalSource || !*wzLocalSource) && (!wzDownloadSource || !*wzDownloadSource)) - { - UpdateUninitialize(&pEngineState->update); - } - else if (BOOTSTRAPPER_UPDATE_HASH_TYPE_NONE == hashType && (0 != cbHash || rgbHash)) - { - hr = E_INVALIDARG; - } - else if (BOOTSTRAPPER_UPDATE_HASH_TYPE_SHA512 == hashType && (SHA512_HASH_LEN != cbHash || !rgbHash)) - { - hr = E_INVALIDARG; - } - else - { - UpdateUninitialize(&pEngineState->update); - - hr = CoreRecreateCommandLine(&sczCommandline, BOOTSTRAPPER_ACTION_INSTALL, pEngineState->command.display, pEngineState->command.restart, BOOTSTRAPPER_RELATION_NONE, FALSE, pEngineState->registration.sczActiveParent, pEngineState->registration.sczAncestors, NULL, pEngineState->command.wzCommandLine); - ExitOnFailure(hr, "Failed to recreate command-line for update bundle."); - - // Bundles would fail to use the downloaded update bundle, as the running bundle would be one of the search paths. - // Here I am generating a random guid, but in the future it would be nice if the feed would provide the ID of the update. - rs = ::UuidCreate(&guid); - hr = HRESULT_FROM_RPC(rs); - ExitOnFailure(hr, "Failed to create bundle update guid."); - - if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid))) - { - hr = E_OUTOFMEMORY; - ExitOnRootFailure(hr, "Failed to convert bundle update guid into string."); - } - - hr = StrAllocFormatted(&sczFilePath, L"%ls\\%ls", wzGuid, pEngineState->registration.sczExecutableName); - ExitOnFailure(hr, "Failed to build bundle update file path."); - - if (!wzLocalSource || !*wzLocalSource) - { - wzLocalSource = sczFilePath; - } - - hr = PseudoBundleInitialize(FILEMAKEVERSION(rmj, rmm, rup, rpr), &pEngineState->update.package, FALSE, pEngineState->registration.sczId, BOOTSTRAPPER_RELATION_UPDATE, BOOTSTRAPPER_PACKAGE_STATE_ABSENT, FALSE, sczFilePath, wzLocalSource, wzDownloadSource, qwSize, TRUE, sczCommandline, NULL, NULL, NULL, rgbHash, cbHash); - ExitOnFailure(hr, "Failed to set update bundle."); - - pEngineState->update.fUpdateAvailable = TRUE; - } - -LExit: - ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive); - - ReleaseStr(sczCommandline); - ReleaseStr(sczFilePath); - - return hr; -} - -HRESULT ExternalEngineSetLocalSource( - __in BURN_ENGINE_STATE* pEngineState, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in_z LPCWSTR wzPath - ) -{ - HRESULT hr = S_OK; - BURN_CONTAINER* pContainer = NULL; - BURN_PAYLOAD* pPayload = NULL; - - ::EnterCriticalSection(&pEngineState->userExperience.csEngineActive); - hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience); - ExitOnFailure(hr, "Engine is active, cannot change engine state."); - - if (!wzPath || !*wzPath) - { - hr = E_INVALIDARG; - } - else if (wzPayloadId && *wzPayloadId) - { - hr = PayloadFindById(&pEngineState->payloads, wzPayloadId, &pPayload); - ExitOnFailure(hr, "BA requested unknown payload with id: %ls", wzPayloadId); - - hr = StrAllocString(&pPayload->sczSourcePath, wzPath, 0); - ExitOnFailure(hr, "Failed to set source path for payload."); - } - else if (wzPackageOrContainerId && *wzPackageOrContainerId) - { - hr = ContainerFindById(&pEngineState->containers, wzPackageOrContainerId, &pContainer); - ExitOnFailure(hr, "BA requested unknown container with id: %ls", wzPackageOrContainerId); - - hr = StrAllocString(&pContainer->sczSourcePath, wzPath, 0); - ExitOnFailure(hr, "Failed to set source path for container."); - } - else - { - hr = E_INVALIDARG; - } - -LExit: - ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive); - - return hr; -} - -HRESULT ExternalEngineSetDownloadSource( - __in BURN_ENGINE_STATE* pEngineState, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in_z_opt LPCWSTR wzUrl, - __in_z_opt LPCWSTR wzUser, - __in_z_opt LPCWSTR wzPassword - ) -{ - HRESULT hr = S_OK; - BURN_CONTAINER* pContainer = NULL; - BURN_PAYLOAD* pPayload = NULL; - DOWNLOAD_SOURCE* pDownloadSource = NULL; - - ::EnterCriticalSection(&pEngineState->userExperience.csEngineActive); - hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience); - ExitOnFailure(hr, "Engine is active, cannot change engine state."); - - if (wzPayloadId && *wzPayloadId) - { - hr = PayloadFindById(&pEngineState->payloads, wzPayloadId, &pPayload); - ExitOnFailure(hr, "BA requested unknown payload with id: %ls", wzPayloadId); - - pDownloadSource = &pPayload->downloadSource; - } - else if (wzPackageOrContainerId && *wzPackageOrContainerId) - { - hr = ContainerFindById(&pEngineState->containers, wzPackageOrContainerId, &pContainer); - ExitOnFailure(hr, "BA requested unknown container with id: %ls", wzPackageOrContainerId); - - pDownloadSource = &pContainer->downloadSource; - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "BA did not provide container or payload id."); - } - - if (wzUrl && *wzUrl) - { - hr = StrAllocString(&pDownloadSource->sczUrl, wzUrl, 0); - ExitOnFailure(hr, "Failed to set download URL."); - - if (wzUser && *wzUser) - { - hr = StrAllocString(&pDownloadSource->sczUser, wzUser, 0); - ExitOnFailure(hr, "Failed to set download user."); - - if (wzPassword && *wzPassword) - { - hr = StrAllocString(&pDownloadSource->sczPassword, wzPassword, 0); - ExitOnFailure(hr, "Failed to set download password."); - } - else // no password. - { - ReleaseNullStr(pDownloadSource->sczPassword); - } - } - else // no user means no password either. - { - ReleaseNullStr(pDownloadSource->sczUser); - ReleaseNullStr(pDownloadSource->sczPassword); - } - } - else // no URL provided means clear out the whole download source. - { - ReleaseNullStr(pDownloadSource->sczUrl); - ReleaseNullStr(pDownloadSource->sczUser); - ReleaseNullStr(pDownloadSource->sczPassword); - } - -LExit: - ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive); - - return hr; -} - -HRESULT ExternalEngineSetVariableNumeric( - __in BURN_ENGINE_STATE* pEngineState, - __in_z LPCWSTR wzVariable, - __in const LONGLONG llValue - ) -{ - HRESULT hr = S_OK; - - if (wzVariable && *wzVariable) - { - hr = VariableSetNumeric(&pEngineState->variables, wzVariable, llValue, FALSE); - ExitOnFailure(hr, "Failed to set numeric variable."); - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "SetVariableNumeric did not provide variable name."); - } - -LExit: - return hr; -} - -HRESULT ExternalEngineSetVariableString( - __in BURN_ENGINE_STATE* pEngineState, - __in_z LPCWSTR wzVariable, - __in_z_opt LPCWSTR wzValue, - __in const BOOL fFormatted - ) -{ - HRESULT hr = S_OK; - - if (wzVariable && *wzVariable) - { - hr = VariableSetString(&pEngineState->variables, wzVariable, wzValue, FALSE, fFormatted); - ExitOnFailure(hr, "Failed to set string variable."); - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "SetVariableString did not provide variable name."); - } - -LExit: - return hr; -} - -HRESULT ExternalEngineSetVariableVersion( - __in BURN_ENGINE_STATE* pEngineState, - __in_z LPCWSTR wzVariable, - __in_z_opt LPCWSTR wzValue - ) -{ - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion = NULL; - - if (wzVariable && *wzVariable) - { - if (wzValue) - { - hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); - ExitOnFailure(hr, "Failed to parse new version value."); - } - - hr = VariableSetVersion(&pEngineState->variables, wzVariable, pVersion, FALSE); - ExitOnFailure(hr, "Failed to set version variable."); - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "SetVariableVersion did not provide variable name."); - } - -LExit: - ReleaseVerutilVersion(pVersion); - - return hr; -} - -void ExternalEngineCloseSplashScreen( - __in BURN_ENGINE_STATE* pEngineState - ) -{ - // If the splash screen is still around, close it. - if (::IsWindow(pEngineState->command.hwndSplashScreen)) - { - ::PostMessageW(pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0); - } -} - -HRESULT ExternalEngineCompareVersions( - __in_z LPCWSTR wzVersion1, - __in_z LPCWSTR wzVersion2, - __out int* pnResult - ) -{ - HRESULT hr = S_OK; - - hr = VerCompareStringVersions(wzVersion1, wzVersion2, FALSE, pnResult); - - return hr; -} - -HRESULT ExternalEngineDetect( - __in const DWORD dwThreadId, - __in_opt const HWND hwndParent - ) -{ - HRESULT hr = S_OK; - - if (!::PostThreadMessageW(dwThreadId, WM_BURN_DETECT, 0, reinterpret_cast(hwndParent))) - { - ExitWithLastError(hr, "Failed to post detect message."); - } - -LExit: - return hr; -} - -HRESULT ExternalEnginePlan( - __in const DWORD dwThreadId, - __in const BOOTSTRAPPER_ACTION action - ) -{ - HRESULT hr = S_OK; - - if (BOOTSTRAPPER_ACTION_LAYOUT > action || BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED < action) - { - ExitOnRootFailure(hr = E_INVALIDARG, "BA passed invalid action to Plan: %u.", action); - } - - if (!::PostThreadMessageW(dwThreadId, WM_BURN_PLAN, 0, action)) - { - ExitWithLastError(hr, "Failed to post plan message."); - } - -LExit: - return hr; -} - -HRESULT ExternalEngineElevate( - __in BURN_ENGINE_STATE* pEngineState, - __in const DWORD dwThreadId, - __in_opt const HWND hwndParent - ) -{ - HRESULT hr = S_OK; - - if (INVALID_HANDLE_VALUE != pEngineState->companionConnection.hPipe) - { - hr = HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED); - } - else if (!::PostThreadMessageW(dwThreadId, WM_BURN_ELEVATE, 0, reinterpret_cast(hwndParent))) - { - ExitWithLastError(hr, "Failed to post elevate message."); - } - -LExit: - return hr; -} - -HRESULT ExternalEngineApply( - __in const DWORD dwThreadId, - __in_opt const HWND hwndParent - ) -{ - HRESULT hr = S_OK; - - ExitOnNull(hwndParent, hr, E_INVALIDARG, "BA passed NULL hwndParent to Apply."); - if (!::IsWindow(hwndParent)) - { - ExitOnRootFailure(hr = E_INVALIDARG, "BA passed invalid hwndParent to Apply."); - } - - if (!::PostThreadMessageW(dwThreadId, WM_BURN_APPLY, 0, reinterpret_cast(hwndParent))) - { - ExitWithLastError(hr, "Failed to post apply message."); - } - -LExit: - return hr; -} - -HRESULT ExternalEngineQuit( - __in const DWORD dwThreadId, - __in const DWORD dwExitCode - ) -{ - HRESULT hr = S_OK; - - if (!::PostThreadMessageW(dwThreadId, WM_BURN_QUIT, static_cast(dwExitCode), 0)) - { - ExitWithLastError(hr, "Failed to post shutdown message."); - } - -LExit: - return hr; -} - -HRESULT ExternalEngineLaunchApprovedExe( - __in BURN_ENGINE_STATE* pEngineState, - __in const DWORD dwThreadId, - __in_opt const HWND hwndParent, - __in_z LPCWSTR wzApprovedExeForElevationId, - __in_z_opt LPCWSTR wzArguments, - __in const DWORD dwWaitForInputIdleTimeout - ) -{ - HRESULT hr = S_OK; - BURN_APPROVED_EXE* pApprovedExe = NULL; - BOOL fLeaveCriticalSection = FALSE; - BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe = NULL; - - pLaunchApprovedExe = (BURN_LAUNCH_APPROVED_EXE*)MemAlloc(sizeof(BURN_LAUNCH_APPROVED_EXE), TRUE); - ExitOnNull(pLaunchApprovedExe, hr, E_OUTOFMEMORY, "Failed to alloc BURN_LAUNCH_APPROVED_EXE"); - - ::EnterCriticalSection(&pEngineState->userExperience.csEngineActive); - fLeaveCriticalSection = TRUE; - hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience); - ExitOnFailure(hr, "Engine is active, cannot change engine state."); - - if (!wzApprovedExeForElevationId || !*wzApprovedExeForElevationId) - { - ExitFunction1(hr = E_INVALIDARG); - } - - hr = ApprovedExesFindById(&pEngineState->approvedExes, wzApprovedExeForElevationId, &pApprovedExe); - ExitOnFailure(hr, "BA requested unknown approved exe with id: %ls", wzApprovedExeForElevationId); - - hr = StrAllocString(&pLaunchApprovedExe->sczId, wzApprovedExeForElevationId, NULL); - ExitOnFailure(hr, "Failed to copy the id."); - - if (wzArguments) - { - hr = StrAllocString(&pLaunchApprovedExe->sczArguments, wzArguments, NULL); - ExitOnFailure(hr, "Failed to copy the arguments."); - } - - pLaunchApprovedExe->dwWaitForInputIdleTimeout = dwWaitForInputIdleTimeout; - - pLaunchApprovedExe->hwndParent = hwndParent; - - if (!::PostThreadMessageW(dwThreadId, WM_BURN_LAUNCH_APPROVED_EXE, 0, reinterpret_cast(pLaunchApprovedExe))) - { - ExitWithLastError(hr, "Failed to post launch approved exe message."); - } - -LExit: - if (fLeaveCriticalSection) - { - ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive); - } - - if (FAILED(hr)) - { - ApprovedExesUninitializeLaunch(pLaunchApprovedExe); - } - - return hr; -} - -HRESULT ExternalEngineSetUpdateSource( - __in BURN_ENGINE_STATE* pEngineState, - __in_z LPCWSTR wzUrl - ) -{ - HRESULT hr = S_OK; - BOOL fLeaveCriticalSection = FALSE; - - ::EnterCriticalSection(&pEngineState->userExperience.csEngineActive); - fLeaveCriticalSection = TRUE; - hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience); - ExitOnFailure(hr, "Engine is active, cannot change engine state."); - - if (wzUrl && *wzUrl) - { - hr = StrAllocString(&pEngineState->update.sczUpdateSource, wzUrl, 0); - ExitOnFailure(hr, "Failed to set feed download URL."); - } - else // no URL provided means clear out the whole download source. - { - ReleaseNullStr(pEngineState->update.sczUpdateSource); - } - -LExit: - if (fLeaveCriticalSection) - { - ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive); - } - - return hr; -} - -// TODO: callers need to provide the original size (at the time of first public release) of the struct instead of the current size. -HRESULT WINAPI ExternalEngineValidateMessageParameter( - __in_opt const LPVOID pv, - __in SIZE_T cbSizeOffset, - __in DWORD dwMinimumSize - ) -{ - HRESULT hr = S_OK; - - if (!pv) - { - ExitFunction1(hr = E_INVALIDARG); - } - - DWORD cbSize = *(DWORD*)((BYTE*)pv + cbSizeOffset); - if (dwMinimumSize < cbSize) - { - ExitFunction1(hr = E_INVALIDARG); - } - -LExit: - return hr; -} - -static HRESULT CopyStringToExternal( - __in_z LPWSTR wzValue, - __in_z_opt LPWSTR wzBuffer, - __inout SIZE_T* pcchBuffer - ) -{ - HRESULT hr = S_OK; - BOOL fTooSmall = !wzBuffer; - - if (!fTooSmall) - { - hr = ::StringCchCopyExW(wzBuffer, *pcchBuffer, wzValue, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); - if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) - { - fTooSmall = TRUE; - } - } - - if (fTooSmall) - { - hr = ::StringCchLengthW(wzValue, STRSAFE_MAX_LENGTH, reinterpret_cast(pcchBuffer)); - if (SUCCEEDED(hr)) - { - hr = E_MOREDATA; - *pcchBuffer += 1; // null terminator. - } - } - - return hr; -} diff --git a/src/engine/externalengine.h b/src/engine/externalengine.h deleted file mode 100644 index 2903615d..00000000 --- a/src/engine/externalengine.h +++ /dev/null @@ -1,181 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#define ValidateMessageParameter(x, pv, type) { x = ExternalEngineValidateMessageParameter(pv, offsetof(type, cbSize), sizeof(type)); if (FAILED(x)) { goto LExit; }} -#define ValidateMessageArgs(x, pv, type, identifier) ValidateMessageParameter(x, pv, type); const type* identifier = reinterpret_cast(pv); UNREFERENCED_PARAMETER(identifier) -#define ValidateMessageResults(x, pv, type, identifier) ValidateMessageParameter(x, pv, type); type* identifier = reinterpret_cast(pv); UNREFERENCED_PARAMETER(identifier) - - -#if defined(__cplusplus) -extern "C" { -#endif - -void ExternalEngineGetPackageCount( - __in BURN_ENGINE_STATE* pEngineState, - __out DWORD* pcPackages - ); - -HRESULT ExternalEngineGetVariableNumeric( - __in BURN_ENGINE_STATE* pEngineState, - __in_z LPCWSTR wzVariable, - __out LONGLONG* pllValue - ); - -HRESULT ExternalEngineGetVariableString( - __in BURN_ENGINE_STATE* pEngineState, - __in_z LPCWSTR wzVariable, - __out_ecount_opt(*pcchValue) LPWSTR wzValue, - __inout SIZE_T* pcchValue - ); - -HRESULT ExternalEngineGetVariableVersion( - __in BURN_ENGINE_STATE* pEngineState, - __in_z LPCWSTR wzVariable, - __out_ecount_opt(*pcchValue) LPWSTR wzValue, - __inout SIZE_T* pcchValue - ); - -HRESULT ExternalEngineFormatString( - __in BURN_ENGINE_STATE* pEngineState, - __in_z LPCWSTR wzIn, - __out_ecount_opt(*pcchOut) LPWSTR wzOut, - __inout SIZE_T* pcchOut - ); - -HRESULT ExternalEngineEscapeString( - __in_z LPCWSTR wzIn, - __out_ecount_opt(*pcchOut) LPWSTR wzOut, - __inout SIZE_T* pcchOut - ); - -HRESULT ExternalEngineEvaluateCondition( - __in BURN_ENGINE_STATE* pEngineState, - __in_z LPCWSTR wzCondition, - __out BOOL* pf - ); - -HRESULT ExternalEngineLog( - __in REPORT_LEVEL rl, - __in_z LPCWSTR wzMessage - ); - -HRESULT ExternalEngineSendEmbeddedError( - __in BURN_ENGINE_STATE* pEngineState, - __in const DWORD dwErrorCode, - __in_z LPCWSTR wzMessage, - __in const DWORD dwUIHint, - __out int* pnResult - ); - -HRESULT ExternalEngineSendEmbeddedProgress( - __in BURN_ENGINE_STATE* pEngineState, - __in const DWORD dwProgressPercentage, - __in const DWORD dwOverallProgressPercentage, - __out int* pnResult - ); - -HRESULT ExternalEngineSetUpdate( - __in BURN_ENGINE_STATE* pEngineState, - __in_z_opt LPCWSTR wzLocalSource, - __in_z_opt LPCWSTR wzDownloadSource, - __in const DWORD64 qwSize, - __in const BOOTSTRAPPER_UPDATE_HASH_TYPE hashType, - __in_opt const BYTE* rgbHash, - __in const DWORD cbHash - ); - -HRESULT ExternalEngineSetLocalSource( - __in BURN_ENGINE_STATE* pEngineState, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in_z LPCWSTR wzPath - ); - -HRESULT ExternalEngineSetDownloadSource( - __in BURN_ENGINE_STATE* pEngineState, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in_z_opt LPCWSTR wzUrl, - __in_z_opt LPCWSTR wzUser, - __in_z_opt LPCWSTR wzPassword - ); - -HRESULT ExternalEngineSetVariableNumeric( - __in BURN_ENGINE_STATE* pEngineState, - __in_z LPCWSTR wzVariable, - __in const LONGLONG llValue - ); - -HRESULT ExternalEngineSetVariableString( - __in BURN_ENGINE_STATE* pEngineState, - __in_z LPCWSTR wzVariable, - __in_z_opt LPCWSTR wzValue, - __in const BOOL fFormatted - ); - -HRESULT ExternalEngineSetVariableVersion( - __in BURN_ENGINE_STATE* pEngineState, - __in_z LPCWSTR wzVariable, - __in_z_opt LPCWSTR wzValue - ); - -void ExternalEngineCloseSplashScreen( - __in BURN_ENGINE_STATE* pEngineState - ); - -HRESULT ExternalEngineCompareVersions( - __in_z LPCWSTR wzVersion1, - __in_z LPCWSTR wzVersion2, - __out int* pnResult - ); - -HRESULT ExternalEngineDetect( - __in const DWORD dwThreadId, - __in_opt const HWND hwndParent - ); - -HRESULT ExternalEnginePlan( - __in const DWORD dwThreadId, - __in const BOOTSTRAPPER_ACTION action - ); - -HRESULT ExternalEngineElevate( - __in BURN_ENGINE_STATE* pEngineState, - __in const DWORD dwThreadId, - __in_opt const HWND hwndParent - ); - -HRESULT ExternalEngineApply( - __in const DWORD dwThreadId, - __in_opt const HWND hwndParent - ); - -HRESULT ExternalEngineQuit( - __in const DWORD dwThreadId, - __in const DWORD dwExitCode - ); - -HRESULT ExternalEngineLaunchApprovedExe( - __in BURN_ENGINE_STATE* pEngineState, - __in const DWORD dwThreadId, - __in_opt const HWND hwndParent, - __in_z LPCWSTR wzApprovedExeForElevationId, - __in_z_opt LPCWSTR wzArguments, - __in const DWORD dwWaitForInputIdleTimeout - ); - -HRESULT ExternalEngineSetUpdateSource( - __in BURN_ENGINE_STATE* pEngineState, - __in_z LPCWSTR wzUrl - ); - -HRESULT WINAPI ExternalEngineValidateMessageParameter( - __in_opt const LPVOID pv, - __in SIZE_T cbSizeOffset, - __in DWORD dwMinimumSize - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/inc/burnsources.h b/src/engine/inc/burnsources.h deleted file mode 100644 index bff79ed5..00000000 --- a/src/engine/inc/burnsources.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_EXTERNAL diff --git a/src/engine/inc/engine.h b/src/engine/inc/engine.h deleted file mode 100644 index 808bb91a..00000000 --- a/src/engine/inc/engine.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -// function declarations - -BOOL EngineInCleanRoom( - __in_z_opt LPCWSTR wzCommandLine - ); - -HRESULT EngineRun( - __in HINSTANCE hInstance, - __in HANDLE hEngineFile, - __in_z_opt LPCWSTR wzCommandLine, - __in int nCmdShow, - __out DWORD* pdwExitCode - ); - - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/logging.cpp b/src/engine/logging.cpp deleted file mode 100644 index 065ef907..00000000 --- a/src/engine/logging.cpp +++ /dev/null @@ -1,754 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -static DWORD vdwPackageSequence = 0; -static const DWORD LOG_OPEN_RETRY_COUNT = 3; -static const DWORD LOG_OPEN_RETRY_WAIT = 2000; -static CONST LPWSTR LOG_FAILED_EVENT_LOG_MESSAGE = L"Burn Engine Fatal Error: failed to open log file."; - -// structs - - - -// internal function declarations - -static void CheckLoggingPolicy( - __out DWORD *pdwAttributes - ); -static HRESULT GetNonSessionSpecificTempFolder( - __deref_out_z LPWSTR* psczNonSessionTempFolder - ); - - -// function definitions - -extern "C" HRESULT LoggingOpen( - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_DISPLAY display, - __in_z LPCWSTR wzBundleName - ) -{ - HRESULT hr = S_OK; - LPWSTR sczLoggingBaseFolder = NULL; - LPWSTR sczPrefixFormatted = NULL; - - // Check if the logging policy is set and configure the logging appropriately. - CheckLoggingPolicy(&pLog->dwAttributes); - - if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_VERBOSE || pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG) - { - if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG) - { - LogSetLevel(REPORT_DEBUG, FALSE); - } - else if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_VERBOSE) - { - LogSetLevel(REPORT_VERBOSE, FALSE); - } - - if ((!pLog->sczPath || !*pLog->sczPath) && (!pLog->sczPrefix || !*pLog->sczPrefix)) - { - PathCreateTimeBasedTempFile(NULL, L"Setup", NULL, L"log", &pLog->sczPath, NULL); - } - } - - // Open the log approriately. - if (pLog->sczPath && *pLog->sczPath) - { - DWORD cRetry = 0; - - hr = DirGetCurrent(&sczLoggingBaseFolder); - ExitOnFailure(hr, "Failed to get current directory."); - - // Try pretty hard to open the log file when appending. - do - { - if (0 < cRetry) - { - ::Sleep(LOG_OPEN_RETRY_WAIT); - } - - hr = LogOpen(sczLoggingBaseFolder, pLog->sczPath, NULL, NULL, pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND, FALSE, &pLog->sczPath); - if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND && HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) == hr) - { - ++cRetry; - } - } while (cRetry > 0 && cRetry <= LOG_OPEN_RETRY_COUNT); - - if (FAILED(hr)) - { - // Log is not open, so note that. - LogDisable(); - pLog->state = BURN_LOGGING_STATE_DISABLED; - - if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND) - { - // If appending, ignore the failure and continue. - hr = S_OK; - } - else // specifically tried to create a log file so show an error if appropriate and bail. - { - HRESULT hrOriginal = hr; - - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_LOG_FAILURE); - SplashScreenDisplayError(display, wzBundleName, hr); - - ExitOnFailure(hrOriginal, "Failed to open log: %ls", pLog->sczPath); - } - } - else - { - pLog->state = BURN_LOGGING_STATE_OPEN; - } - } - else - { - if (pLog->sczPrefix && *pLog->sczPrefix) - { - hr = VariableFormatString(pVariables, pLog->sczPrefix, &sczPrefixFormatted, NULL); - } - - if (sczPrefixFormatted && *sczPrefixFormatted) - { - LPCWSTR wzPrefix = sczPrefixFormatted; - - // Best effort to open default logging. - if (PathIsAbsolute(sczPrefixFormatted)) - { - hr = PathGetDirectory(sczPrefixFormatted, &sczLoggingBaseFolder); - ExitOnFailure(hr, "Failed to get parent directory from '%ls'.", sczPrefixFormatted); - - wzPrefix = PathFile(sczPrefixFormatted); - } - else - { - hr = GetNonSessionSpecificTempFolder(&sczLoggingBaseFolder); - ExitOnFailure(hr, "Failed to get non-session specific TEMP folder."); - } - - hr = LogOpen(sczLoggingBaseFolder, wzPrefix, NULL, pLog->sczExtension, FALSE, FALSE, &pLog->sczPath); - if (FAILED(hr)) - { - LogDisable(); - pLog->state = BURN_LOGGING_STATE_DISABLED; - - hr = S_OK; - } - else - { - pLog->state = BURN_LOGGING_STATE_OPEN; - } - } - else // no logging enabled. - { - LogDisable(); - pLog->state = BURN_LOGGING_STATE_DISABLED; - } - } - - // If the log was opened, write the header info and update the prefix and extension to match - // the log name so future logs are opened with the same pattern. - if (BURN_LOGGING_STATE_OPEN == pLog->state) - { - LPCWSTR wzExtension = PathExtension(pLog->sczPath); - if (wzExtension && *wzExtension) - { - hr = StrAllocString(&pLog->sczPrefix, pLog->sczPath, wzExtension - pLog->sczPath); - ExitOnFailure(hr, "Failed to copy log path to prefix."); - - hr = StrAllocString(&pLog->sczExtension, wzExtension + 1, 0); - ExitOnFailure(hr, "Failed to copy log extension to extension."); - } - else - { - hr = StrAllocString(&pLog->sczPrefix, pLog->sczPath, 0); - ExitOnFailure(hr, "Failed to copy full log path to prefix."); - } - - if (pLog->sczPathVariable && *pLog->sczPathVariable) - { - VariableSetString(pVariables, pLog->sczPathVariable, pLog->sczPath, FALSE, FALSE); // Ignore failure. - } - } - -LExit: - ReleaseStr(sczLoggingBaseFolder); - StrSecureZeroFreeString(sczPrefixFormatted); - - return hr; -} - -extern "C" void LoggingOpenFailed() -{ - HRESULT hr = S_OK; - HANDLE hEventLog = NULL; - LPCWSTR* lpStrings = const_cast(&LOG_FAILED_EVENT_LOG_MESSAGE); - WORD wNumStrings = 1; - - hr = LogOpen(NULL, L"Setup", L"_Failed", L"txt", FALSE, FALSE, NULL); - if (SUCCEEDED(hr)) - { - ExitFunction(); - } - - // If opening the "failure" log failed, then attempt to record that in the Application event log. - hEventLog = ::OpenEventLogW(NULL, L"Application"); - ExitOnNullWithLastError(hEventLog, hr, "Failed to open Application event log"); - - hr = ::ReportEventW(hEventLog, EVENTLOG_ERROR_TYPE, 1, 1, NULL, wNumStrings, 0, lpStrings, NULL); - ExitOnNullWithLastError(hEventLog, hr, "Failed to write event log entry"); - -LExit: - if (hEventLog) - { - ::CloseEventLog(hEventLog); - } -} - -extern "C" void LoggingIncrementPackageSequence() -{ - ++vdwPackageSequence; -} - -extern "C" HRESULT LoggingSetPackageVariable( - __in BURN_PACKAGE* pPackage, - __in_z_opt LPCWSTR wzSuffix, - __in BOOL fRollback, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __out_opt LPWSTR* psczLogPath - ) -{ - HRESULT hr = S_OK; - LPWSTR sczLogPath = NULL; - - // Make sure that no package log files are created when logging has been disabled via Log element. - if (BURN_LOGGING_STATE_DISABLED == pLog->state) - { - if (psczLogPath) - { - *psczLogPath = NULL; - } - - ExitFunction(); - } - - if ((!fRollback && pPackage->sczLogPathVariable && *pPackage->sczLogPathVariable) || - (fRollback && pPackage->sczRollbackLogPathVariable && *pPackage->sczRollbackLogPathVariable)) - { - hr = StrAllocFormatted(&sczLogPath, L"%ls%hs%ls_%03u_%ls%ls.%ls", pLog->sczPrefix, wzSuffix && *wzSuffix ? "_" : "", wzSuffix && *wzSuffix ? wzSuffix : L"", vdwPackageSequence, pPackage->sczId, fRollback ? L"_rollback" : L"", pLog->sczExtension); - ExitOnFailure(hr, "Failed to allocate path for package log."); - - hr = VariableSetString(pVariables, fRollback ? pPackage->sczRollbackLogPathVariable : pPackage->sczLogPathVariable, sczLogPath, FALSE, FALSE); - ExitOnFailure(hr, "Failed to set log path into variable."); - - if (psczLogPath) - { - hr = StrAllocString(psczLogPath, sczLogPath, 0); - ExitOnFailure(hr, "Failed to copy package log path."); - } - } - -LExit: - ReleaseStr(sczLogPath); - - return hr; -} - -extern "C" LPCSTR LoggingBurnActionToString( - __in BOOTSTRAPPER_ACTION action - ) -{ - switch (action) - { - case BOOTSTRAPPER_ACTION_UNKNOWN: - return "Unknown"; - case BOOTSTRAPPER_ACTION_HELP: - return "Help"; - case BOOTSTRAPPER_ACTION_LAYOUT: - return "Layout"; - case BOOTSTRAPPER_ACTION_CACHE: - return "Cache"; - case BOOTSTRAPPER_ACTION_UNINSTALL: - return "Uninstall"; - case BOOTSTRAPPER_ACTION_INSTALL: - return "Install"; - case BOOTSTRAPPER_ACTION_MODIFY: - return "Modify"; - case BOOTSTRAPPER_ACTION_REPAIR: - return "Repair"; - case BOOTSTRAPPER_ACTION_UPDATE_REPLACE: - return "UpdateReplace"; - case BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED: - return "UpdateReplaceEmbedded"; - default: - return "Invalid"; - } -} - -LPCSTR LoggingBurnMessageToString( - __in UINT message - ) -{ - switch (message) - { - case WM_BURN_APPLY: - return "Apply"; - case WM_BURN_DETECT: - return "Detect"; - case WM_BURN_ELEVATE: - return "Elevate"; - case WM_BURN_LAUNCH_APPROVED_EXE: - return "LaunchApprovedExe"; - case WM_BURN_PLAN: - return "Plan"; - case WM_BURN_QUIT: - return "Quit"; - default: - return "Invalid"; - } -} - -extern "C" LPCSTR LoggingActionStateToString( - __in BOOTSTRAPPER_ACTION_STATE actionState - ) -{ - switch (actionState) - { - case BOOTSTRAPPER_ACTION_STATE_NONE: - return "None"; - case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: - return "Uninstall"; - case BOOTSTRAPPER_ACTION_STATE_INSTALL: - return "Install"; - case BOOTSTRAPPER_ACTION_STATE_MODIFY: - return "Modify"; - case BOOTSTRAPPER_ACTION_STATE_MEND: - return "Mend"; - case BOOTSTRAPPER_ACTION_STATE_REPAIR: - return "Repair"; - case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: - return "MinorUpgrade"; - default: - return "Invalid"; - } -} - -extern "C" LPCSTR LoggingDependencyActionToString( - BURN_DEPENDENCY_ACTION action - ) -{ - switch (action) - { - case BURN_DEPENDENCY_ACTION_NONE: - return "None"; - case BURN_DEPENDENCY_ACTION_REGISTER: - return "Register"; - case BURN_DEPENDENCY_ACTION_UNREGISTER: - return "Unregister"; - default: - return "Invalid"; - } -} - -extern "C" LPCSTR LoggingBoolToString( - __in BOOL f - ) -{ - if (f) - { - return "Yes"; - } - - return "No"; -} - -extern "C" LPCSTR LoggingTrueFalseToString( - __in BOOL f - ) -{ - if (f) - { - return "true"; - } - - return "false"; -} - -extern "C" LPCSTR LoggingPackageStateToString( - __in BOOTSTRAPPER_PACKAGE_STATE packageState - ) -{ - switch (packageState) - { - case BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN: - return "Unknown"; - case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: - return "Obsolete"; - case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: - return "Absent"; - case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: - return "Present"; - case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: - return "Superseded"; - default: - return "Invalid"; - } -} - -extern "C" LPCSTR LoggingPackageRegistrationStateToString( - __in BOOL fCanAffectRegistration, - __in BURN_PACKAGE_REGISTRATION_STATE registrationState - ) -{ - if (!fCanAffectRegistration) - { - return "(permanent)"; - } - - switch (registrationState) - { - case BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN: - return "Unknown"; - case BURN_PACKAGE_REGISTRATION_STATE_IGNORED: - return "Ignored"; - case BURN_PACKAGE_REGISTRATION_STATE_ABSENT: - return "Absent"; - case BURN_PACKAGE_REGISTRATION_STATE_PRESENT: - return "Present"; - default: - return "Invalid"; - } -} - -extern "C" LPCSTR LoggingMsiFeatureStateToString( - __in BOOTSTRAPPER_FEATURE_STATE featureState - ) -{ - switch (featureState) - { - case BOOTSTRAPPER_FEATURE_STATE_UNKNOWN: - return "Unknown"; - case BOOTSTRAPPER_FEATURE_STATE_ABSENT: - return "Absent"; - case BOOTSTRAPPER_FEATURE_STATE_ADVERTISED: - return "Advertised"; - case BOOTSTRAPPER_FEATURE_STATE_LOCAL: - return "Local"; - case BOOTSTRAPPER_FEATURE_STATE_SOURCE: - return "Source"; - default: - return "Invalid"; - } -} - -extern "C" LPCSTR LoggingMsiFeatureActionToString( - __in BOOTSTRAPPER_FEATURE_ACTION featureAction - ) -{ - switch (featureAction) - { - case BOOTSTRAPPER_FEATURE_ACTION_NONE: - return "None"; - case BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL: - return "AddLocal"; - case BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE: - return "AddSource"; - case BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT: - return "AddDefault"; - case BOOTSTRAPPER_FEATURE_ACTION_REINSTALL: - return "Reinstall"; - case BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE: - return "Advertise"; - case BOOTSTRAPPER_FEATURE_ACTION_REMOVE: - return "Remove"; - default: - return "Invalid"; - } -} - -extern "C" LPCSTR LoggingMsiInstallContext( - __in MSIINSTALLCONTEXT context - ) -{ - switch (context) - { - case MSIINSTALLCONTEXT_ALL: - return "All"; - case MSIINSTALLCONTEXT_ALLUSERMANAGED: - return "AllUserManaged"; - case MSIINSTALLCONTEXT_MACHINE: - return "Machine"; - case MSIINSTALLCONTEXT_NONE: - return "None"; - case MSIINSTALLCONTEXT_USERMANAGED: - return "UserManaged"; - case MSIINSTALLCONTEXT_USERUNMANAGED: - return "UserUnmanaged"; - default: - return "Invalid"; - } -} - -extern "C" LPCWSTR LoggingBurnMsiPropertyToString( - __in BURN_MSI_PROPERTY burnMsiProperty - ) -{ - switch (burnMsiProperty) - { - case BURN_MSI_PROPERTY_INSTALL: - return BURNMSIINSTALL_PROPERTY_NAME; - case BURN_MSI_PROPERTY_MODIFY: - return BURNMSIMODIFY_PROPERTY_NAME; - case BURN_MSI_PROPERTY_NONE: - return L"(none)"; - case BURN_MSI_PROPERTY_REPAIR: - return BURNMSIREPAIR_PROPERTY_NAME; - case BURN_MSI_PROPERTY_UNINSTALL: - return BURNMSIUNINSTALL_PROPERTY_NAME; - default: - return L"Invalid"; - } -} - -extern "C" LPCSTR LoggingMspTargetActionToString( - __in BOOTSTRAPPER_ACTION_STATE action, - __in BURN_PATCH_SKIP_STATE skipState - ) -{ - switch (skipState) - { - case BURN_PATCH_SKIP_STATE_NONE: - return LoggingActionStateToString(action); - case BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL: - return "Skipped (target uninstall)"; - case BURN_PATCH_SKIP_STATE_SLIPSTREAM: - return "Skipped (slipstream)"; - default: - return "Invalid"; - } -} - -extern "C" LPCSTR LoggingPerMachineToString( - __in BOOL fPerMachine - ) -{ - if (fPerMachine) - { - return "PerMachine"; - } - - return "PerUser"; -} - -extern "C" LPCSTR LoggingRestartToString( - __in BOOTSTRAPPER_APPLY_RESTART restart - ) -{ - switch (restart) - { - case BOOTSTRAPPER_APPLY_RESTART_NONE: - return "None"; - case BOOTSTRAPPER_APPLY_RESTART_REQUIRED: - return "Required"; - case BOOTSTRAPPER_APPLY_RESTART_INITIATED: - return "Initiated"; - default: - return "Invalid"; - } -} - -extern "C" LPCSTR LoggingResumeModeToString( - __in BURN_RESUME_MODE resumeMode - ) -{ - switch (resumeMode) - { - case BURN_RESUME_MODE_NONE: - return "None"; - case BURN_RESUME_MODE_ACTIVE: - return "Active"; - case BURN_RESUME_MODE_SUSPEND: - return "Suspend"; - case BURN_RESUME_MODE_ARP: - return "ARP"; - case BURN_RESUME_MODE_REBOOT_PENDING: - return "Reboot Pending"; - default: - return "Invalid"; - } -} - -extern "C" LPCSTR LoggingRelationTypeToString( - __in BOOTSTRAPPER_RELATION_TYPE type - ) -{ - switch (type) - { - case BOOTSTRAPPER_RELATION_NONE: - return "None"; - case BOOTSTRAPPER_RELATION_DETECT: - return "Detect"; - case BOOTSTRAPPER_RELATION_UPGRADE: - return "Upgrade"; - case BOOTSTRAPPER_RELATION_ADDON: - return "Addon"; - case BOOTSTRAPPER_RELATION_PATCH: - return "Patch"; - case BOOTSTRAPPER_RELATION_DEPENDENT: - return "Dependent"; - case BOOTSTRAPPER_RELATION_UPDATE: - return "Update"; - default: - return "Invalid"; - } -} - -extern "C" LPCSTR LoggingRelatedOperationToString( - __in BOOTSTRAPPER_RELATED_OPERATION operation - ) -{ - switch (operation) - { - case BOOTSTRAPPER_RELATED_OPERATION_NONE: - return "None"; - case BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE: - return "Downgrade"; - case BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE: - return "MinorUpdate"; - case BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE: - return "MajorUpgrade"; - case BOOTSTRAPPER_RELATED_OPERATION_REMOVE: - return "Remove"; - case BOOTSTRAPPER_RELATED_OPERATION_INSTALL: - return "Install"; - case BOOTSTRAPPER_RELATED_OPERATION_REPAIR: - return "Repair"; - default: - return "Invalid"; - } -} - -extern "C" LPCSTR LoggingRequestStateToString( - __in BOOTSTRAPPER_REQUEST_STATE requestState - ) -{ - switch (requestState) - { - case BOOTSTRAPPER_REQUEST_STATE_NONE: - return "None"; - case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: - return "ForceAbsent"; - case BOOTSTRAPPER_REQUEST_STATE_ABSENT: - return "Absent"; - case BOOTSTRAPPER_REQUEST_STATE_CACHE: - return "Cache"; - case BOOTSTRAPPER_REQUEST_STATE_PRESENT: - return "Present"; - case BOOTSTRAPPER_REQUEST_STATE_MEND: - return "Mend"; - case BOOTSTRAPPER_REQUEST_STATE_REPAIR: - return "Repair"; - default: - return "Invalid"; - } -} - -extern "C" LPCSTR LoggingRollbackOrExecute( - __in BOOL fRollback - ) -{ - return fRollback ? "rollback" : "execute"; -} - -extern "C" LPWSTR LoggingStringOrUnknownIfNull( - __in LPCWSTR wz - ) -{ - return wz ? wz : L"Unknown"; -} - - -// internal function declarations - -static void CheckLoggingPolicy( - __out DWORD *pdwAttributes - ) -{ - HRESULT hr = S_OK; - HKEY hk = NULL; - LPWSTR sczLoggingPolicy = NULL; - - hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Policies\\Microsoft\\Windows\\Installer", KEY_READ, &hk); - if (SUCCEEDED(hr)) - { - hr = RegReadString(hk, L"Logging", &sczLoggingPolicy); - if (SUCCEEDED(hr)) - { - LPCWSTR wz = sczLoggingPolicy; - while (*wz) - { - if (L'v' == *wz || L'V' == *wz) - { - *pdwAttributes |= BURN_LOGGING_ATTRIBUTE_VERBOSE; - } - else if (L'x' == *wz || L'X' == *wz) - { - *pdwAttributes |= BURN_LOGGING_ATTRIBUTE_EXTRADEBUG; - } - - ++wz; - } - } - } - - ReleaseStr(sczLoggingPolicy); - ReleaseRegKey(hk); -} - -static HRESULT GetNonSessionSpecificTempFolder( - __deref_out_z LPWSTR* psczNonSessionTempFolder - ) -{ - HRESULT hr = S_OK; - WCHAR wzTempFolder[MAX_PATH] = { }; - SIZE_T cchTempFolder = 0; - DWORD dwSessionId = 0; - LPWSTR sczSessionId = 0; - SIZE_T cchSessionId = 0; - - if (!::GetTempPathW(countof(wzTempFolder), wzTempFolder)) - { - ExitWithLastError(hr, "Failed to get temp folder."); - } - - hr = ::StringCchLengthW(wzTempFolder, countof(wzTempFolder), reinterpret_cast(&cchTempFolder)); - ExitOnFailure(hr, "Failed to get length of temp folder."); - - // If our session id is in the TEMP path then remove that part so we get the non-session - // specific temporary folder. - if (::ProcessIdToSessionId(::GetCurrentProcessId(), &dwSessionId)) - { - hr = StrAllocFormatted(&sczSessionId, L"%u\\", dwSessionId); - ExitOnFailure(hr, "Failed to format session id as a string."); - - hr = ::StringCchLengthW(sczSessionId, STRSAFE_MAX_CCH, reinterpret_cast(&cchSessionId)); - ExitOnFailure(hr, "Failed to get length of session id string."); - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzTempFolder + cchTempFolder - cchSessionId, static_cast(cchSessionId), sczSessionId, static_cast(cchSessionId))) - { - cchTempFolder -= cchSessionId; - } - } - - hr = StrAllocString(psczNonSessionTempFolder, wzTempFolder, cchTempFolder); - ExitOnFailure(hr, "Failed to copy temp folder."); - -LExit: - ReleaseStr(sczSessionId); - - return hr; -} diff --git a/src/engine/logging.h b/src/engine/logging.h deleted file mode 100644 index 601039f9..00000000 --- a/src/engine/logging.h +++ /dev/null @@ -1,153 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -// constants - -enum BURN_LOGGING_STATE -{ - BURN_LOGGING_STATE_CLOSED, - BURN_LOGGING_STATE_OPEN, - BURN_LOGGING_STATE_DISABLED, -}; - -enum BURN_LOGGING_ATTRIBUTE -{ - BURN_LOGGING_ATTRIBUTE_APPEND = 0x1, - BURN_LOGGING_ATTRIBUTE_VERBOSE = 0x2, - BURN_LOGGING_ATTRIBUTE_EXTRADEBUG = 0x4, -}; - - -// structs - -typedef struct _BURN_LOGGING -{ - BURN_LOGGING_STATE state; - LPWSTR sczPathVariable; - - DWORD dwAttributes; - LPWSTR sczPath; - LPWSTR sczPrefix; - LPWSTR sczExtension; -} BURN_LOGGING; - - - -// function declarations - -HRESULT LoggingOpen( - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_DISPLAY display, - __in_z LPCWSTR wzBundleName - ); - -void LoggingOpenFailed(); - -void LoggingIncrementPackageSequence(); - -HRESULT LoggingSetPackageVariable( - __in BURN_PACKAGE* pPackage, - __in_z_opt LPCWSTR wzSuffix, - __in BOOL fRollback, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __out_opt LPWSTR* psczLogPath - ); - -LPCSTR LoggingBurnActionToString( - __in BOOTSTRAPPER_ACTION action - ); - -LPCSTR LoggingBurnMessageToString( - __in UINT message - ); - -LPCSTR LoggingActionStateToString( - __in BOOTSTRAPPER_ACTION_STATE actionState - ); - -LPCSTR LoggingDependencyActionToString( - BURN_DEPENDENCY_ACTION action - ); - -LPCSTR LoggingBoolToString( - __in BOOL f - ); - -LPCSTR LoggingTrueFalseToString( - __in BOOL f - ); - -LPCSTR LoggingPackageStateToString( - __in BOOTSTRAPPER_PACKAGE_STATE packageState - ); - -LPCSTR LoggingPackageRegistrationStateToString( - __in BOOL fCanAffectRegistration, - __in BURN_PACKAGE_REGISTRATION_STATE registrationState - ); - -LPCSTR LoggingMsiFeatureStateToString( - __in BOOTSTRAPPER_FEATURE_STATE featureState - ); - -LPCSTR LoggingMsiFeatureActionToString( - __in BOOTSTRAPPER_FEATURE_ACTION featureAction - ); - -LPCSTR LoggingMsiInstallContext( - __in MSIINSTALLCONTEXT context - ); - -LPCWSTR LoggingBurnMsiPropertyToString( - __in BURN_MSI_PROPERTY burnMsiProperty - ); - -LPCSTR LoggingMspTargetActionToString( - __in BOOTSTRAPPER_ACTION_STATE action, - __in BURN_PATCH_SKIP_STATE skipState - ); - -LPCSTR LoggingPerMachineToString( - __in BOOL fPerMachine - ); - -LPCSTR LoggingRestartToString( - __in BOOTSTRAPPER_APPLY_RESTART restart - ); - -LPCSTR LoggingResumeModeToString( - __in BURN_RESUME_MODE resumeMode - ); - -LPCSTR LoggingRelationTypeToString( - __in BOOTSTRAPPER_RELATION_TYPE type - ); - -LPCSTR LoggingRelatedOperationToString( - __in BOOTSTRAPPER_RELATED_OPERATION operation - ); - -LPCSTR LoggingRequestStateToString( - __in BOOTSTRAPPER_REQUEST_STATE requestState - ); - -LPCSTR LoggingRollbackOrExecute( - __in BOOL fRollback - ); - -LPWSTR LoggingStringOrUnknownIfNull( - __in LPCWSTR wz - ); - - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/manifest.cpp b/src/engine/manifest.cpp deleted file mode 100644 index b1740083..00000000 --- a/src/engine/manifest.cpp +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -static HRESULT ParseFromXml( - __in IXMLDOMDocument* pixdDocument, - __in BURN_ENGINE_STATE* pEngineState - ); - -// function definitions - -extern "C" HRESULT ManifestLoadXmlFromFile( - __in LPCWSTR wzPath, - __in BURN_ENGINE_STATE* pEngineState - ) -{ - HRESULT hr = S_OK; - IXMLDOMDocument* pixdDocument = NULL; - - // load xml document - hr = XmlLoadDocumentFromFile(wzPath, &pixdDocument); - ExitOnFailure(hr, "Failed to load manifest as XML document."); - - hr = ParseFromXml(pixdDocument, pEngineState); - -LExit: - ReleaseObject(pixdDocument); - - return hr; -} - -extern "C" HRESULT ManifestLoadXmlFromBuffer( - __in_bcount(cbBuffer) BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __in BURN_ENGINE_STATE* pEngineState - ) -{ - HRESULT hr = S_OK; - IXMLDOMDocument* pixdDocument = NULL; - - // load xml document - hr = XmlLoadDocumentFromBuffer(pbBuffer, cbBuffer, &pixdDocument); - ExitOnFailure(hr, "Failed to load manifest as XML document."); - - hr = ParseFromXml(pixdDocument, pEngineState); - -LExit: - ReleaseObject(pixdDocument); - - return hr; -} - -static HRESULT ParseFromXml( - __in IXMLDOMDocument* pixdDocument, - __in BURN_ENGINE_STATE* pEngineState - ) -{ - HRESULT hr = S_OK; - IXMLDOMElement* pixeBundle = NULL; - IXMLDOMNode* pixnLog = NULL; - IXMLDOMNode* pixnChain = NULL; - - // get bundle element - hr = pixdDocument->get_documentElement(&pixeBundle); - ExitOnFailure(hr, "Failed to get bundle element."); - - // parse the log element, if present. - hr = XmlSelectSingleNode(pixeBundle, L"Log", &pixnLog); - ExitOnFailure(hr, "Failed to get Log element."); - - if (S_OK == hr) - { - hr = XmlGetAttributeEx(pixnLog, L"PathVariable", &pEngineState->log.sczPathVariable); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get Log/@PathVariable."); - } - - hr = XmlGetAttributeEx(pixnLog, L"Prefix", &pEngineState->log.sczPrefix); - ExitOnFailure(hr, "Failed to get Log/@Prefix attribute."); - - hr = XmlGetAttributeEx(pixnLog, L"Extension", &pEngineState->log.sczExtension); - ExitOnFailure(hr, "Failed to get Log/@Extension attribute."); - } - - // get the chain element - hr = XmlSelectSingleNode(pixeBundle, L"Chain", &pixnChain); - ExitOnFailure(hr, "Failed to get chain element."); - - if (S_OK == hr) - { - // parse disable rollback - hr = XmlGetYesNoAttribute(pixnChain, L"DisableRollback", &pEngineState->fDisableRollback); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get Chain/@DisableRollback"); - } - - // parse disable system restore - hr = XmlGetYesNoAttribute(pixnChain, L"DisableSystemRestore", &pEngineState->fDisableSystemRestore); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get Chain/@DisableSystemRestore"); - } - - // parse parallel cache - hr = XmlGetYesNoAttribute(pixnChain, L"ParallelCache", &pEngineState->fParallelCacheAndExecute); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get Chain/@ParallelCache"); - } - } - - // parse built-in condition - hr = ConditionGlobalParseFromXml(&pEngineState->condition, pixeBundle); - ExitOnFailure(hr, "Failed to parse global condition."); - - // parse variables - hr = VariablesParseFromXml(&pEngineState->variables, pixeBundle); - ExitOnFailure(hr, "Failed to parse variables."); - - // parse user experience - hr = UserExperienceParseFromXml(&pEngineState->userExperience, pixeBundle); - ExitOnFailure(hr, "Failed to parse user experience."); - - // parse extensions - hr = BurnExtensionParseFromXml(&pEngineState->extensions, &pEngineState->userExperience.payloads, pixeBundle); - ExitOnFailure(hr, "Failed to parse extensions."); - - // parse searches - hr = SearchesParseFromXml(&pEngineState->searches, &pEngineState->extensions, pixeBundle); - ExitOnFailure(hr, "Failed to parse searches."); - - // parse registration - hr = RegistrationParseFromXml(&pEngineState->registration, pixeBundle); - ExitOnFailure(hr, "Failed to parse registration."); - - // parse update - hr = UpdateParseFromXml(&pEngineState->update, pixeBundle); - ExitOnFailure(hr, "Failed to parse update."); - - // parse containers - hr = ContainersParseFromXml(&pEngineState->containers, pixeBundle); - ExitOnFailure(hr, "Failed to parse containers."); - - // parse payloads - hr = PayloadsParseFromXml(&pEngineState->payloads, &pEngineState->containers, &pEngineState->layoutPayloads, pixeBundle); - ExitOnFailure(hr, "Failed to parse payloads."); - - // parse packages - hr = PackagesParseFromXml(&pEngineState->packages, &pEngineState->payloads, pixeBundle); - ExitOnFailure(hr, "Failed to parse packages."); - - // parse approved exes for elevation - hr = ApprovedExesParseFromXml(&pEngineState->approvedExes, pixeBundle); - ExitOnFailure(hr, "Failed to parse approved exes."); - -LExit: - ReleaseObject(pixnChain); - ReleaseObject(pixnLog); - ReleaseObject(pixeBundle); - return hr; -} diff --git a/src/engine/manifest.h b/src/engine/manifest.h deleted file mode 100644 index 8c527279..00000000 --- a/src/engine/manifest.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -interface IBurnPayload; // forward declare. - -#if defined(__cplusplus) -extern "C" { -#endif - - -// function declarations - -HRESULT ManifestLoadXmlFromFile( - __in LPCWSTR wzPath, - __in BURN_ENGINE_STATE* pEngineState - ); - -HRESULT ManifestLoadXmlFromBuffer( - __in_bcount(cbBuffer) BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __in BURN_ENGINE_STATE* pEngineState - ); - - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp deleted file mode 100644 index 3e96e5f9..00000000 --- a/src/engine/msiengine.cpp +++ /dev/null @@ -1,2035 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// constants - - -// structs - - - -// internal function declarations - -static HRESULT ParseRelatedMsiFromXml( - __in IXMLDOMNode* pixnRelatedMsi, - __in BURN_RELATED_MSI* pRelatedMsi - ); -static HRESULT EvaluateActionStateConditions( - __in BURN_VARIABLES* pVariables, - __in_z_opt LPCWSTR sczAddLocalCondition, - __in_z_opt LPCWSTR sczAddSourceCondition, - __in_z_opt LPCWSTR sczAdvertiseCondition, - __out BOOTSTRAPPER_FEATURE_STATE* pState - ); -static HRESULT CalculateFeatureAction( - __in BOOTSTRAPPER_FEATURE_STATE currentState, - __in BOOTSTRAPPER_FEATURE_STATE requestedState, - __in BOOL fRepair, - __out BOOTSTRAPPER_FEATURE_ACTION* pFeatureAction, - __inout BOOL* pfDelta - ); -static HRESULT EscapePropertyArgumentString( - __in LPCWSTR wzProperty, - __inout_z LPWSTR* psczEscapedValue, - __in BOOL fZeroOnRealloc - ); -static HRESULT ConcatFeatureActionProperties( - __in BURN_PACKAGE* pPackage, - __in BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions, - __inout_z LPWSTR* psczArguments - ); -static HRESULT ConcatPatchProperty( - __in BURN_PACKAGE* pPackage, - __in BOOL fRollback, - __inout_z LPWSTR* psczArguments - ); -static void RegisterSourceDirectory( - __in BURN_PACKAGE* pPackage, - __in_z LPCWSTR wzCacheDirectory - ); - - -// function definitions - -extern "C" HRESULT MsiEngineParsePackageFromXml( - __in IXMLDOMNode* pixnMsiPackage, - __in BURN_PACKAGE* pPackage - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnNodes = NULL; - IXMLDOMNode* pixnNode = NULL; - DWORD cNodes = 0; - LPWSTR scz = NULL; - - // @ProductCode - hr = XmlGetAttributeEx(pixnMsiPackage, L"ProductCode", &pPackage->Msi.sczProductCode); - ExitOnFailure(hr, "Failed to get @ProductCode."); - - // @Language - hr = XmlGetAttributeNumber(pixnMsiPackage, L"Language", &pPackage->Msi.dwLanguage); - ExitOnFailure(hr, "Failed to get @Language."); - - // @Version - hr = XmlGetAttributeEx(pixnMsiPackage, L"Version", &scz); - ExitOnFailure(hr, "Failed to get @Version."); - - hr = VerParseVersion(scz, 0, FALSE, &pPackage->Msi.pVersion); - ExitOnFailure(hr, "Failed to parse @Version: %ls", scz); - - if (pPackage->Msi.pVersion->fInvalid) - { - LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz); - } - - // @UpgradeCode - hr = XmlGetAttributeEx(pixnMsiPackage, L"UpgradeCode", &pPackage->Msi.sczUpgradeCode); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @UpgradeCode."); - } - - // select feature nodes - hr = XmlSelectNodes(pixnMsiPackage, L"MsiFeature", &pixnNodes); - ExitOnFailure(hr, "Failed to select feature nodes."); - - // get feature node count - hr = pixnNodes->get_length((long*)&cNodes); - ExitOnFailure(hr, "Failed to get feature node count."); - - if (cNodes) - { - // allocate memory for features - pPackage->Msi.rgFeatures = (BURN_MSIFEATURE*)MemAlloc(sizeof(BURN_MSIFEATURE) * cNodes, TRUE); - ExitOnNull(pPackage->Msi.rgFeatures, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSI feature structs."); - - pPackage->Msi.cFeatures = cNodes; - - // parse feature elements - for (DWORD i = 0; i < cNodes; ++i) - { - BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; - - hr = XmlNextElement(pixnNodes, &pixnNode, NULL); - ExitOnFailure(hr, "Failed to get next node."); - - // @Id - hr = XmlGetAttributeEx(pixnNode, L"Id", &pFeature->sczId); - ExitOnFailure(hr, "Failed to get @Id."); - - // @AddLocalCondition - hr = XmlGetAttributeEx(pixnNode, L"AddLocalCondition", &pFeature->sczAddLocalCondition); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @AddLocalCondition."); - } - - // @AddSourceCondition - hr = XmlGetAttributeEx(pixnNode, L"AddSourceCondition", &pFeature->sczAddSourceCondition); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @AddSourceCondition."); - } - - // @AdvertiseCondition - hr = XmlGetAttributeEx(pixnNode, L"AdvertiseCondition", &pFeature->sczAdvertiseCondition); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @AdvertiseCondition."); - } - - // @RollbackAddLocalCondition - hr = XmlGetAttributeEx(pixnNode, L"RollbackAddLocalCondition", &pFeature->sczRollbackAddLocalCondition); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @RollbackAddLocalCondition."); - } - - // @RollbackAddSourceCondition - hr = XmlGetAttributeEx(pixnNode, L"RollbackAddSourceCondition", &pFeature->sczRollbackAddSourceCondition); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @RollbackAddSourceCondition."); - } - - // @RollbackAdvertiseCondition - hr = XmlGetAttributeEx(pixnNode, L"RollbackAdvertiseCondition", &pFeature->sczRollbackAdvertiseCondition); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @RollbackAdvertiseCondition."); - } - - // prepare next iteration - ReleaseNullObject(pixnNode); - } - } - - ReleaseNullObject(pixnNodes); // done with the MsiFeature elements. - - hr = MsiEngineParsePropertiesFromXml(pixnMsiPackage, &pPackage->Msi.rgProperties, &pPackage->Msi.cProperties); - ExitOnFailure(hr, "Failed to parse properties from XML."); - - // select related MSI nodes - hr = XmlSelectNodes(pixnMsiPackage, L"RelatedPackage", &pixnNodes); - ExitOnFailure(hr, "Failed to select related MSI nodes."); - - // get related MSI node count - hr = pixnNodes->get_length((long*)&cNodes); - ExitOnFailure(hr, "Failed to get related MSI node count."); - - if (cNodes) - { - // allocate memory for related MSIs - pPackage->Msi.rgRelatedMsis = (BURN_RELATED_MSI*)MemAlloc(sizeof(BURN_RELATED_MSI) * cNodes, TRUE); - ExitOnNull(pPackage->Msi.rgRelatedMsis, hr, E_OUTOFMEMORY, "Failed to allocate memory for related MSI structs."); - - pPackage->Msi.cRelatedMsis = cNodes; - - // parse related MSI elements - for (DWORD i = 0; i < cNodes; ++i) - { - hr = XmlNextElement(pixnNodes, &pixnNode, NULL); - ExitOnFailure(hr, "Failed to get next node."); - - // parse related MSI element - hr = ParseRelatedMsiFromXml(pixnNode, &pPackage->Msi.rgRelatedMsis[i]); - ExitOnFailure(hr, "Failed to parse related MSI element."); - - // prepare next iteration - ReleaseNullObject(pixnNode); - } - } - - ReleaseNullObject(pixnNodes); // done with the RelatedPackage elements. - - // Select slipstream MSP nodes. - hr = XmlSelectNodes(pixnMsiPackage, L"SlipstreamMsp", &pixnNodes); - ExitOnFailure(hr, "Failed to select related MSI nodes."); - - hr = pixnNodes->get_length((long*)&cNodes); - ExitOnFailure(hr, "Failed to get related MSI node count."); - - if (cNodes) - { - pPackage->Msi.rgSlipstreamMsps = reinterpret_cast(MemAlloc(sizeof(BURN_SLIPSTREAM_MSP) * cNodes, TRUE)); - ExitOnNull(pPackage->Msi.rgSlipstreamMsps, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP packages."); - - pPackage->Msi.rgsczSlipstreamMspPackageIds = reinterpret_cast(MemAlloc(sizeof(LPWSTR*) * cNodes, TRUE)); - ExitOnNull(pPackage->Msi.rgsczSlipstreamMspPackageIds, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP ids."); - - pPackage->Msi.cSlipstreamMspPackages = cNodes; - - // Parse slipstream MSP Ids. - for (DWORD i = 0; i < cNodes; ++i) - { - hr = XmlNextElement(pixnNodes, &pixnNode, NULL); - ExitOnFailure(hr, "Failed to get next slipstream MSP node."); - - hr = XmlGetAttributeEx(pixnNode, L"Id", pPackage->Msi.rgsczSlipstreamMspPackageIds + i); - ExitOnFailure(hr, "Failed to parse slipstream MSP ids."); - - ReleaseNullObject(pixnNode); - } - } - - hr = S_OK; - -LExit: - ReleaseObject(pixnNodes); - ReleaseObject(pixnNode); - ReleaseStr(scz); - - return hr; -} - -extern "C" HRESULT MsiEngineParsePropertiesFromXml( - __in IXMLDOMNode* pixnPackage, - __out BURN_MSIPROPERTY** prgProperties, - __out DWORD* pcProperties - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnNodes = NULL; - IXMLDOMNode* pixnNode = NULL; - DWORD cNodes = 0; - - BURN_MSIPROPERTY* pProperties = NULL; - - // select property nodes - hr = XmlSelectNodes(pixnPackage, L"MsiProperty", &pixnNodes); - ExitOnFailure(hr, "Failed to select property nodes."); - - // get property node count - hr = pixnNodes->get_length((long*)&cNodes); - ExitOnFailure(hr, "Failed to get property node count."); - - if (cNodes) - { - // allocate memory for properties - pProperties = (BURN_MSIPROPERTY*)MemAlloc(sizeof(BURN_MSIPROPERTY) * cNodes, TRUE); - ExitOnNull(pProperties, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSI property structs."); - - // parse property elements - for (DWORD i = 0; i < cNodes; ++i) - { - BURN_MSIPROPERTY* pProperty = &pProperties[i]; - - hr = XmlNextElement(pixnNodes, &pixnNode, NULL); - ExitOnFailure(hr, "Failed to get next node."); - - // @Id - hr = XmlGetAttributeEx(pixnNode, L"Id", &pProperty->sczId); - ExitOnFailure(hr, "Failed to get @Id."); - - // @Value - hr = XmlGetAttributeEx(pixnNode, L"Value", &pProperty->sczValue); - ExitOnFailure(hr, "Failed to get @Value."); - - // @RollbackValue - hr = XmlGetAttributeEx(pixnNode, L"RollbackValue", &pProperty->sczRollbackValue); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @RollbackValue."); - } - - // @Condition - hr = XmlGetAttributeEx(pixnNode, L"Condition", &pProperty->sczCondition); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @Condition."); - } - - // prepare next iteration - ReleaseNullObject(pixnNode); - } - } - - *pcProperties = cNodes; - *prgProperties = pProperties; - pProperties = NULL; - - hr = S_OK; - -LExit: - ReleaseNullObject(pixnNodes); - ReleaseMem(pProperties); - - return hr; -} - -extern "C" void MsiEnginePackageUninitialize( - __in BURN_PACKAGE* pPackage - ) -{ - ReleaseStr(pPackage->Msi.sczProductCode); - ReleaseStr(pPackage->Msi.sczUpgradeCode); - - // free features - if (pPackage->Msi.rgFeatures) - { - for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) - { - BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; - - ReleaseStr(pFeature->sczId); - ReleaseStr(pFeature->sczAddLocalCondition); - ReleaseStr(pFeature->sczAddSourceCondition); - ReleaseStr(pFeature->sczAdvertiseCondition); - ReleaseStr(pFeature->sczRollbackAddLocalCondition); - ReleaseStr(pFeature->sczRollbackAddSourceCondition); - ReleaseStr(pFeature->sczRollbackAdvertiseCondition); - } - MemFree(pPackage->Msi.rgFeatures); - } - - // free properties - if (pPackage->Msi.rgProperties) - { - for (DWORD i = 0; i < pPackage->Msi.cProperties; ++i) - { - BURN_MSIPROPERTY* pProperty = &pPackage->Msi.rgProperties[i]; - - ReleaseStr(pProperty->sczId); - ReleaseStr(pProperty->sczValue); - ReleaseStr(pProperty->sczRollbackValue); - ReleaseStr(pProperty->sczCondition); - } - MemFree(pPackage->Msi.rgProperties); - } - - // free related MSIs - if (pPackage->Msi.rgRelatedMsis) - { - for (DWORD i = 0; i < pPackage->Msi.cRelatedMsis; ++i) - { - BURN_RELATED_MSI* pRelatedMsi = &pPackage->Msi.rgRelatedMsis[i]; - - ReleaseStr(pRelatedMsi->sczUpgradeCode); - ReleaseMem(pRelatedMsi->rgdwLanguages); - } - MemFree(pPackage->Msi.rgRelatedMsis); - } - - // free slipstream MSPs - if (pPackage->Msi.rgsczSlipstreamMspPackageIds) - { - for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) - { - ReleaseStr(pPackage->Msi.rgsczSlipstreamMspPackageIds[i]); - } - - MemFree(pPackage->Msi.rgsczSlipstreamMspPackageIds); - } - - if (pPackage->Msi.rgSlipstreamMsps) - { - MemFree(pPackage->Msi.rgSlipstreamMsps); - } - - if (pPackage->Msi.rgChainedPatches) - { - MemFree(pPackage->Msi.rgChainedPatches); - } - - // clear struct - memset(&pPackage->Msi, 0, sizeof(pPackage->Msi)); -} - -extern "C" HRESULT MsiEngineDetectInitialize( - __in BURN_PACKAGES* pPackages - ) -{ - AssertSz(pPackages->cPatchInfo, "MsiEngineDetectInitialize() should only be called if there are MSP packages."); - - HRESULT hr = S_OK; - - // Add target products for slipstream MSIs that weren't detected. - for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage) - { - BURN_PACKAGE* pMsiPackage = pPackages->rgPackages + iPackage; - if (BURN_PACKAGE_TYPE_MSI == pMsiPackage->type) - { - for (DWORD j = 0; j < pMsiPackage->Msi.cSlipstreamMspPackages; ++j) - { - BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pMsiPackage->Msi.rgSlipstreamMsps + j; - Assert(pSlipstreamMsp->pMspPackage && BURN_PACKAGE_TYPE_MSP == pSlipstreamMsp->pMspPackage->type); - - if (pSlipstreamMsp->pMspPackage && BURN_PACKAGE_INVALID_PATCH_INDEX == pSlipstreamMsp->dwMsiChainedPatchIndex) - { - hr = MspEngineAddMissingSlipstreamTarget(pMsiPackage, pSlipstreamMsp); - ExitOnFailure(hr, "Failed to add slipstreamed target product code to package: %ls", pSlipstreamMsp->pMspPackage->sczId); - } - } - } - } - -LExit: - return hr; -} - -extern "C" HRESULT MsiEngineDetectPackage( - __in BURN_PACKAGE* pPackage, - __in BURN_USER_EXPERIENCE* pUserExperience - ) -{ - Trace(REPORT_STANDARD, "Detecting MSI package 0x%p", pPackage); - - HRESULT hr = S_OK; - int nCompareResult = 0; - LPWSTR sczInstalledVersion = NULL; - LPWSTR sczInstalledLanguage = NULL; - INSTALLSTATE installState = INSTALLSTATE_UNKNOWN; - BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE; - BOOTSTRAPPER_RELATED_OPERATION relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE; - WCHAR wzProductCode[MAX_GUID_CHARS + 1] = { }; - VERUTIL_VERSION* pVersion = NULL; - UINT uLcid = 0; - BOOL fPerMachine = FALSE; - - // detect self by product code - // TODO: what to do about MSIINSTALLCONTEXT_USERMANAGED? - hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); - if (SUCCEEDED(hr)) - { - hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pPackage->Msi.pInstalledVersion); - ExitOnFailure(hr, "Failed to parse installed version: '%ls' for ProductCode: %ls", sczInstalledVersion, pPackage->Msi.sczProductCode); - - if (pPackage->Msi.pInstalledVersion->fInvalid) - { - LogId(REPORT_WARNING, MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION, pPackage->Msi.sczProductCode, sczInstalledVersion); - } - - // compare versions - hr = VerCompareParsedVersions(pPackage->Msi.pVersion, pPackage->Msi.pInstalledVersion, &nCompareResult); - ExitOnFailure(hr, "Failed to compare version '%ls' to installed version: '%ls'", pPackage->Msi.pVersion->sczVersion, pPackage->Msi.pInstalledVersion->sczVersion); - - if (nCompareResult < 0) - { - operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; - pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; - } - else - { - if (nCompareResult > 0) - { - operation = BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE; - } - - pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; - } - - // Report related MSI package to BA. - if (BOOTSTRAPPER_RELATED_OPERATION_NONE != operation) - { - LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, pPackage->Msi.sczProductCode, LoggingPerMachineToString(pPackage->fPerMachine), pPackage->Msi.pInstalledVersion->sczVersion, pPackage->Msi.dwLanguage, LoggingRelatedOperationToString(operation)); - - hr = UserExperienceOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pPackage->Msi.sczUpgradeCode, pPackage->Msi.sczProductCode, pPackage->fPerMachine, pPackage->Msi.pInstalledVersion, operation); - ExitOnRootFailure(hr, "BA aborted detect related MSI package."); - } - } - else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr || HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr) // package not present. - { - pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to get product information for ProductCode: %ls", pPackage->Msi.sczProductCode); - } - - // detect related packages by upgrade code - for (DWORD i = 0; i < pPackage->Msi.cRelatedMsis; ++i) - { - BURN_RELATED_MSI* pRelatedMsi = &pPackage->Msi.rgRelatedMsis[i]; - - for (DWORD iProduct = 0; ; ++iProduct) - { - // get product - hr = WiuEnumRelatedProducts(pRelatedMsi->sczUpgradeCode, iProduct, wzProductCode); - if (E_NOMOREITEMS == hr) - { - hr = S_OK; - break; - } - ExitOnFailure(hr, "Failed to enum related products."); - - // If we found ourselves, skip because saying that a package is related to itself is nonsensical. - if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pPackage->Msi.sczProductCode, -1, wzProductCode, -1)) - { - continue; - } - - // get product version - hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); - if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr) - { - ExitOnFailure(hr, "Failed to get version for product in machine context: %ls", wzProductCode); - fPerMachine = TRUE; - } - else - { - hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); - if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr) - { - ExitOnFailure(hr, "Failed to get version for product in user unmanaged context: %ls", wzProductCode); - fPerMachine = FALSE; - } - else - { - hr = S_OK; - continue; - } - } - - hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pVersion); - ExitOnFailure(hr, "Failed to parse related installed version: '%ls' for ProductCode: %ls", sczInstalledVersion, wzProductCode); - - if (pVersion->fInvalid) - { - LogId(REPORT_WARNING, MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION, wzProductCode, sczInstalledVersion); - } - - // compare versions - if (pRelatedMsi->fMinProvided) - { - hr = VerCompareParsedVersions(pVersion, pRelatedMsi->pMinVersion, &nCompareResult); - ExitOnFailure(hr, "Failed to compare related installed version '%ls' to related min version: '%ls'", pVersion->sczVersion, pRelatedMsi->pMinVersion->sczVersion); - - if (pRelatedMsi->fMinInclusive ? (nCompareResult < 0) : (nCompareResult <= 0)) - { - continue; - } - } - - if (pRelatedMsi->fMaxProvided) - { - hr = VerCompareParsedVersions(pVersion, pRelatedMsi->pMaxVersion, &nCompareResult); - ExitOnFailure(hr, "Failed to compare related installed version '%ls' to related max version: '%ls'", pVersion->sczVersion, pRelatedMsi->pMaxVersion->sczVersion); - - if (pRelatedMsi->fMaxInclusive ? (nCompareResult > 0) : (nCompareResult >= 0)) - { - continue; - } - } - - // Filter by language if necessary. - uLcid = 0; // always reset the found language. - if (pRelatedMsi->cLanguages) - { - // If there is a language to get, convert it into an LCID. - hr = WiuGetProductInfoEx(wzProductCode, NULL, fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_LANGUAGE, &sczInstalledLanguage); - if (SUCCEEDED(hr)) - { - hr = StrStringToUInt32(sczInstalledLanguage, 0, &uLcid); - } - - // Ignore related product where we can't read the language. - if (FAILED(hr)) - { - LogErrorId(hr, MSG_FAILED_READ_RELATED_PACKAGE_LANGUAGE, wzProductCode, sczInstalledLanguage, NULL); - - hr = S_OK; - continue; - } - - BOOL fMatchedLcid = FALSE; - for (DWORD iLanguage = 0; iLanguage < pRelatedMsi->cLanguages; ++iLanguage) - { - if (uLcid == pRelatedMsi->rgdwLanguages[iLanguage]) - { - fMatchedLcid = TRUE; - break; - } - } - - // Skip the product if the language did not meet the inclusive/exclusive criteria. - if ((pRelatedMsi->fLangInclusive && !fMatchedLcid) || (!pRelatedMsi->fLangInclusive && fMatchedLcid)) - { - continue; - } - } - - // If this is a detect-only related package and we're not installed yet, then we'll assume a downgrade - // would take place since that is the overwhelmingly common use of detect-only related packages. If - // not detect-only then it's easy; we're clearly doing a major upgrade. - if (pRelatedMsi->fOnlyDetect) - { - // If we've already detected a major upgrade that trumps any guesses that the detect is a downgrade - // or even something else. - if (BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE == operation) - { - relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE; - } - // It can't be a downgrade if the upgrade codes aren't the same. - else if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == pPackage->currentState && - pPackage->Msi.sczUpgradeCode && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pPackage->Msi.sczUpgradeCode, -1, pRelatedMsi->sczUpgradeCode, -1)) - { - relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; - operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; - pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE; - } - else // we're already on the machine so the detect-only *must* be for detection purposes only. - { - relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE; - } - } - else - { - relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE; - operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE; - } - - LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, wzProductCode, LoggingPerMachineToString(fPerMachine), pVersion->sczVersion, uLcid, LoggingRelatedOperationToString(relatedMsiOperation)); - - // Pass to BA. - hr = UserExperienceOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pRelatedMsi->sczUpgradeCode, wzProductCode, fPerMachine, pVersion, relatedMsiOperation); - ExitOnRootFailure(hr, "BA aborted detect related MSI package."); - } - } - - // detect features - if (pPackage->Msi.cFeatures) - { - for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) - { - BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; - - // Try to detect features state if the product is present on the machine. - if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT <= pPackage->currentState) - { - hr = WiuQueryFeatureState(pPackage->Msi.sczProductCode, pFeature->sczId, &installState); - ExitOnFailure(hr, "Failed to query feature state."); - - if (INSTALLSTATE_UNKNOWN == installState) // in case of an upgrade a feature could be removed. - { - installState = INSTALLSTATE_ABSENT; - } - } - else // MSI not installed then the features can't be either. - { - installState = INSTALLSTATE_ABSENT; - } - - // set current state - switch (installState) - { - case INSTALLSTATE_ABSENT: - pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_ABSENT; - break; - case INSTALLSTATE_ADVERTISED: - pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_ADVERTISED; - break; - case INSTALLSTATE_LOCAL: - pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_LOCAL; - break; - case INSTALLSTATE_SOURCE: - pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_SOURCE; - break; - default: - hr = E_UNEXPECTED; - ExitOnRootFailure(hr, "Invalid state value."); - } - - // Pass to BA. - hr = UserExperienceOnDetectMsiFeature(pUserExperience, pPackage->sczId, pFeature->sczId, pFeature->currentState); - ExitOnRootFailure(hr, "BA aborted detect MSI feature."); - } - } - - if (pPackage->fCanAffectRegistration) - { - pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - } - -LExit: - ReleaseStr(sczInstalledLanguage); - ReleaseStr(sczInstalledVersion); - ReleaseVerutilVersion(pVersion); - - return hr; -} - -extern "C" HRESULT MsiEnginePlanInitializePackage( - __in BURN_PACKAGE* pPackage, - __in BURN_VARIABLES* pVariables, - __in BURN_USER_EXPERIENCE* pUserExperience - ) -{ - HRESULT hr = S_OK; - - if (pPackage->Msi.cFeatures) - { - // get feature request states - for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) - { - BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; - - // Evaluate feature conditions. - hr = EvaluateActionStateConditions(pVariables, pFeature->sczAddLocalCondition, pFeature->sczAddSourceCondition, pFeature->sczAdvertiseCondition, &pFeature->defaultRequested); - ExitOnFailure(hr, "Failed to evaluate requested state conditions."); - - hr = EvaluateActionStateConditions(pVariables, pFeature->sczRollbackAddLocalCondition, pFeature->sczRollbackAddSourceCondition, pFeature->sczRollbackAdvertiseCondition, &pFeature->expectedState); - ExitOnFailure(hr, "Failed to evaluate expected state conditions."); - - // Remember the default feature requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it. - pFeature->requested = pFeature->defaultRequested; - - // Send plan MSI feature message to BA. - hr = UserExperienceOnPlanMsiFeature(pUserExperience, pPackage->sczId, pFeature->sczId, &pFeature->requested); - ExitOnRootFailure(hr, "BA aborted plan MSI feature."); - } - } - -LExit: - return hr; -} - -// -// PlanCalculate - calculates the execute and rollback state for the requested package state. -// -extern "C" HRESULT MsiEnginePlanCalculatePackage( - __in BURN_PACKAGE* pPackage, - __in BOOL fInsideMsiTransaction - ) -{ - Trace(REPORT_STANDARD, "Planning MSI package 0x%p", pPackage); - - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion = pPackage->Msi.pVersion; - VERUTIL_VERSION* pInstalledVersion = pPackage->Msi.pInstalledVersion; - int nCompareResult = 0; - BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; - BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - BOOL fFeatureActionDelta = FALSE; - BOOL fRollbackFeatureActionDelta = FALSE; - - if (pPackage->Msi.cFeatures) - { - // If the package is present and we're repairing it. - BOOL fRepairingPackage = (BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested); - - // plan features - for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) - { - BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; - - // Calculate feature actions. - hr = CalculateFeatureAction(pFeature->currentState, pFeature->requested, fRepairingPackage, &pFeature->execute, &fFeatureActionDelta); - ExitOnFailure(hr, "Failed to calculate execute feature state."); - - hr = CalculateFeatureAction(pFeature->requested, BOOTSTRAPPER_FEATURE_ACTION_NONE == pFeature->execute ? pFeature->expectedState : pFeature->currentState, FALSE, &pFeature->rollback, &fRollbackFeatureActionDelta); - ExitOnFailure(hr, "Failed to calculate rollback feature state."); - } - } - - // execute action - switch (pPackage->currentState) - { - case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough; - case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: - if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_MEND == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) - { - hr = VerCompareParsedVersions(pVersion, pInstalledVersion, &nCompareResult); - ExitOnFailure(hr, "Failed to compare '%ls' to '%ls' for planning.", pVersion->sczVersion, pInstalledVersion->sczVersion); - - // Take a look at the version and determine if this is a potential - // minor upgrade (same ProductCode newer ProductVersion), otherwise, - // there is a newer version so no work necessary. - if (nCompareResult > 0) - { - execute = BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE; - } - else if (BOOTSTRAPPER_REQUEST_STATE_MEND == pPackage->requested) - { - execute = BOOTSTRAPPER_ACTION_STATE_MEND; - } - else if (BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) - { - execute = BOOTSTRAPPER_ACTION_STATE_REPAIR; - } - else - { - execute = fFeatureActionDelta ? BOOTSTRAPPER_ACTION_STATE_MODIFY : BOOTSTRAPPER_ACTION_STATE_NONE; - } - } - else if ((BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_CACHE == pPackage->requested) && - pPackage->fUninstallable) // removing a package that can be removed. - { - execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; - } - else if (BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested) - { - execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; - } - else - { - execute = BOOTSTRAPPER_ACTION_STATE_NONE; - } - break; - - case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough; - case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: - switch (pPackage->requested) - { - case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_MEND: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_REPAIR: - execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; - break; - - default: - execute = BOOTSTRAPPER_ACTION_STATE_NONE; - break; - } - break; - - default: - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Invalid package current state result encountered during plan: %d", pPackage->currentState); - } - - // Calculate the rollback action if there is an execute action. - if (BOOTSTRAPPER_ACTION_STATE_NONE != execute && !fInsideMsiTransaction) - { - switch (pPackage->currentState) - { - case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough; - case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: - switch (pPackage->requested) - { - case BOOTSTRAPPER_REQUEST_STATE_PRESENT: - rollback = fRollbackFeatureActionDelta ? BOOTSTRAPPER_ACTION_STATE_MODIFY : BOOTSTRAPPER_ACTION_STATE_NONE; - break; - case BOOTSTRAPPER_REQUEST_STATE_REPAIR: - rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - break; - case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_ABSENT: - rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL; - break; - default: - rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - break; - } - break; - - case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough; - case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough; - // If the package is uninstallable and we requested to put the package on the machine then - // remove the package during rollback. - if (pPackage->fUninstallable && - (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || - BOOTSTRAPPER_REQUEST_STATE_MEND == pPackage->requested || - BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested)) - { - rollback = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; - } - else - { - rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - } - break; - - default: - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Invalid package detection result encountered."); - } - } - - // return values - pPackage->execute = execute; - pPackage->rollback = rollback; - -LExit: - return hr; -} - -// -// PlanAdd - adds the calculated execute and rollback actions for the package. -// -extern "C" HRESULT MsiEnginePlanAddPackage( - __in BOOTSTRAPPER_DISPLAY display, - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BURN_PACKAGE* pPackage, - __in BURN_PLAN* pPlan, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in_opt HANDLE hCacheEvent - ) -{ - HRESULT hr = S_OK; - BURN_EXECUTE_ACTION* pAction = NULL; - BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions = NULL; - BOOTSTRAPPER_FEATURE_ACTION* rgRollbackFeatureActions = NULL; - - if (pPackage->Msi.cFeatures) - { - // Allocate and populate array for feature actions. - rgFeatureActions = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(sizeof(BOOTSTRAPPER_FEATURE_ACTION) * pPackage->Msi.cFeatures, TRUE); - ExitOnNull(rgFeatureActions, hr, E_OUTOFMEMORY, "Failed to allocate memory for feature actions."); - - rgRollbackFeatureActions = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(sizeof(BOOTSTRAPPER_FEATURE_ACTION) * pPackage->Msi.cFeatures, TRUE); - ExitOnNull(rgRollbackFeatureActions, hr, E_OUTOFMEMORY, "Failed to allocate memory for rollback feature actions."); - - for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) - { - BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; - - // calculate feature actions - rgFeatureActions[i] = pFeature->execute; - rgRollbackFeatureActions[i] = pFeature->rollback; - } - } - - // add wait for cache - if (hCacheEvent) - { - hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent); - ExitOnFailure(hr, "Failed to plan package cache syncpoint"); - } - - hr = DependencyPlanPackage(NULL, pPackage, pPlan); - ExitOnFailure(hr, "Failed to plan package dependency actions."); - - // add rollback action - if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback) - { - hr = PlanAppendRollbackAction(pPlan, &pAction); - ExitOnFailure(hr, "Failed to append rollback action."); - - pAction->type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE; - pAction->msiPackage.pPackage = pPackage; - pAction->msiPackage.action = pPackage->rollback; - pAction->msiPackage.rgFeatures = rgRollbackFeatureActions; - rgRollbackFeatureActions = NULL; - - hr = MsiEngineCalculateInstallUiLevel(display, pUserExperience, pPackage->sczId, FALSE, pAction->msiPackage.action, - &pAction->msiPackage.actionMsiProperty, &pAction->msiPackage.uiLevel, &pAction->msiPackage.fDisableExternalUiHandler); - ExitOnFailure(hr, "Failed to get msi ui options."); - - LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors. - pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes; - - // Plan a checkpoint between rollback and execute so that we always attempt - // rollback in the case that the MSI was not able to rollback itself (e.g. - // user pushes cancel after InstallFinalize). - hr = PlanExecuteCheckpoint(pPlan); - ExitOnFailure(hr, "Failed to append execute checkpoint."); - } - - // add execute action - if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute) - { - hr = PlanAppendExecuteAction(pPlan, &pAction); - ExitOnFailure(hr, "Failed to append execute action."); - - pAction->type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE; - pAction->msiPackage.pPackage = pPackage; - pAction->msiPackage.action = pPackage->execute; - pAction->msiPackage.rgFeatures = rgFeatureActions; - rgFeatureActions = NULL; - - hr = MsiEngineCalculateInstallUiLevel(display, pUserExperience, pPackage->sczId, TRUE, pAction->msiPackage.action, - &pAction->msiPackage.actionMsiProperty, &pAction->msiPackage.uiLevel, &pAction->msiPackage.fDisableExternalUiHandler); - ExitOnFailure(hr, "Failed to get msi ui options."); - - LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors. - pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes; - } - -LExit: - ReleaseMem(rgFeatureActions); - ReleaseMem(rgRollbackFeatureActions); - - return hr; -} - -extern "C" HRESULT MsiEngineBeginTransaction( - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary - ) -{ - HRESULT hr = S_OK; - MSIHANDLE hTransactionHandle = NULL; - HANDLE hChangeOfOwnerEvent = NULL; - - LogId(REPORT_STANDARD, MSG_MSI_TRANSACTION_BEGIN, pRollbackBoundary->sczId); - - hr = WiuBeginTransaction(pRollbackBoundary->sczId, 0, &hTransactionHandle, &hChangeOfOwnerEvent, WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE, pRollbackBoundary->sczLogPath); - - if (HRESULT_FROM_WIN32(ERROR_ROLLBACK_DISABLED) == hr) - { - LogId(REPORT_ERROR, MSG_MSI_TRANSACTIONS_DISABLED); - } - - ExitOnFailure(hr, "Failed to begin an MSI transaction"); - -LExit: - ReleaseMsi(hTransactionHandle); - ReleaseHandle(hChangeOfOwnerEvent); - - return hr; -} - -extern "C" HRESULT MsiEngineCommitTransaction( - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary - ) -{ - HRESULT hr = S_OK; - - LogId(REPORT_STANDARD, MSG_MSI_TRANSACTION_COMMIT, pRollbackBoundary->sczId); - - hr = WiuEndTransaction(MSITRANSACTIONSTATE_COMMIT, WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE, pRollbackBoundary->sczLogPath); - ExitOnFailure(hr, "Failed to commit the MSI transaction"); - -LExit: - - return hr; -} - -extern "C" HRESULT MsiEngineRollbackTransaction( - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary - ) -{ - HRESULT hr = S_OK; - - LogId(REPORT_WARNING, MSG_MSI_TRANSACTION_ROLLBACK, pRollbackBoundary->sczId); - - hr = WiuEndTransaction(MSITRANSACTIONSTATE_ROLLBACK, WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE, pRollbackBoundary->sczLogPath); - ExitOnFailure(hr, "Failed to rollback the MSI transaction"); - -LExit: - - return hr; -} - -extern "C" HRESULT MsiEngineExecutePackage( - __in_opt HWND hwndParent, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_VARIABLES* pVariables, - __in BOOL fRollback, - __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, - __in LPVOID pvContext, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ) -{ - HRESULT hr = S_OK; - WIU_MSI_EXECUTE_CONTEXT context = { }; - WIU_RESTART restart = WIU_RESTART_NONE; - - LPWSTR sczInstalledVersion = NULL; - LPWSTR sczCachedDirectory = NULL; - LPWSTR sczMsiPath = NULL; - LPWSTR sczProperties = NULL; - LPWSTR sczObfuscatedProperties = NULL; - BURN_PACKAGE* pPackage = pExecuteAction->msiPackage.pPackage; - BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload; - - // During rollback, if the package is already in the rollback state we expect don't - // touch it again. - if (fRollback) - { - if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->msiPackage.action) - { - hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); - if (FAILED(hr)) // package not present. - { - LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_ABSENT)); - - hr = S_OK; - ExitFunction(); - } - } - else if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->msiPackage.action) - { - hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); - if (SUCCEEDED(hr)) // package present. - { - LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_PRESENT)); - - hr = S_OK; - ExitFunction(); - } - - hr = S_OK; - } - } - - // Default to "verbose" logging and set extra debug mode only if explicitly required. - DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE; - - if (pExecuteAction->msiPackage.dwLoggingAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG) - { - dwLogMode |= INSTALLLOGMODE_EXTRADEBUG; - } - - if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL != pExecuteAction->msiPackage.action) - { - // get cached MSI path - hr = CacheGetCompletedPath(pPackage->fPerMachine, pPackage->sczCacheId, &sczCachedDirectory); - ExitOnFailure(hr, "Failed to get cached path for package: %ls", pPackage->sczId); - - // Best effort to set the execute package cache folder variable. - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE); - - hr = PathConcat(sczCachedDirectory, pPackagePayload->sczFilePath, &sczMsiPath); - ExitOnFailure(hr, "Failed to build MSI path."); - } - - // Best effort to set the execute package action variable. - VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->msiPackage.action, TRUE); - - // Wire up the external UI handler and logging. - if (pExecuteAction->msiPackage.fDisableExternalUiHandler) - { - hr = WiuInitializeInternalUI(pExecuteAction->msiPackage.uiLevel, hwndParent, &context); - ExitOnFailure(hr, "Failed to initialize internal UI for MSI package."); - } - else - { - hr = WiuInitializeExternalUI(pfnMessageHandler, pExecuteAction->msiPackage.uiLevel, hwndParent, pvContext, fRollback, &context); - ExitOnFailure(hr, "Failed to initialize external UI handler."); - } - - if (pExecuteAction->msiPackage.sczLogPath && *pExecuteAction->msiPackage.sczLogPath) - { - hr = WiuEnableLog(dwLogMode, pExecuteAction->msiPackage.sczLogPath, 0); - ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pPackage->sczId, pExecuteAction->msiPackage.sczLogPath); - } - - // set up properties - hr = MsiEngineConcatProperties(pPackage->Msi.rgProperties, pPackage->Msi.cProperties, pVariables, fRollback, &sczProperties, FALSE); - ExitOnFailure(hr, "Failed to add properties to argument string."); - - hr = MsiEngineConcatProperties(pPackage->Msi.rgProperties, pPackage->Msi.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE); - ExitOnFailure(hr, "Failed to add obfuscated properties to argument string."); - - // add feature action properties - hr = ConcatFeatureActionProperties(pPackage, pExecuteAction->msiPackage.rgFeatures, &sczProperties); - ExitOnFailure(hr, "Failed to add feature action properties to argument string."); - - hr = ConcatFeatureActionProperties(pPackage, pExecuteAction->msiPackage.rgFeatures, &sczObfuscatedProperties); - ExitOnFailure(hr, "Failed to add feature action properties to obfuscated argument string."); - - // add slipstream patch properties - hr = ConcatPatchProperty(pPackage, fRollback, &sczProperties); - ExitOnFailure(hr, "Failed to add patch properties to argument string."); - - hr = ConcatPatchProperty(pPackage, fRollback, &sczObfuscatedProperties); - ExitOnFailure(hr, "Failed to add patch properties to obfuscated argument string."); - - hr = MsiEngineConcatActionProperty(pExecuteAction->msiPackage.actionMsiProperty, &sczProperties); - ExitOnFailure(hr, "Failed to add action property to argument string."); - - hr = MsiEngineConcatActionProperty(pExecuteAction->msiPackage.actionMsiProperty, &sczObfuscatedProperties); - ExitOnFailure(hr, "Failed to add action property to obfuscated argument string."); - - LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), sczMsiPath, sczObfuscatedProperties ? sczObfuscatedProperties : L""); - - // - // Do the actual action. - // - switch (pExecuteAction->msiPackage.action) - { - case BOOTSTRAPPER_ACTION_STATE_INSTALL: - hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0); - ExitOnFailure(hr, "Failed to add reboot suppression property on install."); - - hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart); - ExitOnFailure(hr, "Failed to install MSI package."); - - RegisterSourceDirectory(pPackage, sczMsiPath); - break; - - case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: - // If feature selection is not enabled, then reinstall the existing features to ensure they get - // updated. - if (0 == pPackage->Msi.cFeatures) - { - hr = StrAllocConcatSecure(&sczProperties, L" REINSTALL=ALL", 0); - ExitOnFailure(hr, "Failed to add reinstall all property on minor upgrade."); - } - - hr = StrAllocConcatSecure(&sczProperties, L" REINSTALLMODE=\"vomus\" REBOOT=ReallySuppress", 0); - ExitOnFailure(hr, "Failed to add reinstall mode and reboot suppression properties on minor upgrade."); - - hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart); - ExitOnFailure(hr, "Failed to perform minor upgrade of MSI package."); - - RegisterSourceDirectory(pPackage, sczMsiPath); - break; - - case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough; - case BOOTSTRAPPER_ACTION_STATE_MEND: __fallthrough; - case BOOTSTRAPPER_ACTION_STATE_REPAIR: - { - LPCWSTR wzReinstallAll = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action || - pPackage->Msi.cFeatures) ? L"" : L" REINSTALL=ALL"; - LPCWSTR wzReinstallMode = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action || BOOTSTRAPPER_ACTION_STATE_MEND == pExecuteAction->msiPackage.action) ? L"o" : L"e"; - - hr = StrAllocFormattedSecure(&sczProperties, L"%ls%ls REINSTALLMODE=\"cmus%ls\" REBOOT=ReallySuppress", sczProperties ? sczProperties : L"", wzReinstallAll, wzReinstallMode); - ExitOnFailure(hr, "Failed to add reinstall mode and reboot suppression properties on repair."); - } - - // Ignore all dependencies, since the Burn engine already performed the check. - hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES); - ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties."); - - hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart); - ExitOnFailure(hr, "Failed to run maintenance mode for MSI package."); - break; - - case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: - hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0); - ExitOnFailure(hr, "Failed to add reboot suppression property on uninstall."); - - // Ignore all dependencies, since the Burn engine already performed the check. - hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES); - ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties."); - - hr = WiuConfigureProductEx(pPackage->Msi.sczProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, sczProperties, &restart); - if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr) - { - LogId(REPORT_STANDARD, MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE, pPackage->sczId); - hr = S_OK; - } - ExitOnFailure(hr, "Failed to uninstall MSI package."); - break; - } - -LExit: - WiuUninitializeExternalUI(&context); - - StrSecureZeroFreeString(sczProperties); - ReleaseStr(sczObfuscatedProperties); - ReleaseStr(sczMsiPath); - ReleaseStr(sczCachedDirectory); - ReleaseStr(sczInstalledVersion); - - switch (restart) - { - case WIU_RESTART_NONE: - *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; - break; - - case WIU_RESTART_REQUIRED: - *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; - break; - - case WIU_RESTART_INITIATED: - *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; - break; - } - - // Best effort to clear the execute package cache folder and action variables. - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE); - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE, FALSE); - - return hr; -} - -extern "C" HRESULT MsiEngineConcatActionProperty( - __in BURN_MSI_PROPERTY actionMsiProperty, - __deref_out_z LPWSTR* psczProperties - ) -{ - HRESULT hr = S_OK; - LPCWSTR wzPropertyName = NULL; - - switch (actionMsiProperty) - { - case BURN_MSI_PROPERTY_INSTALL: - wzPropertyName = BURNMSIINSTALL_PROPERTY_NAME; - break; - case BURN_MSI_PROPERTY_MODIFY: - wzPropertyName = BURNMSIMODIFY_PROPERTY_NAME; - break; - case BURN_MSI_PROPERTY_REPAIR: - wzPropertyName = BURNMSIREPAIR_PROPERTY_NAME; - break; - case BURN_MSI_PROPERTY_UNINSTALL: - wzPropertyName = BURNMSIUNINSTALL_PROPERTY_NAME; - break; - } - - if (wzPropertyName) - { - hr = StrAllocConcatFormattedSecure(psczProperties, L" %ls=1", wzPropertyName); - ExitOnFailure(hr, "Failed to add burn action property."); - } - -LExit: - return hr; -} - -extern "C" HRESULT MsiEngineConcatProperties( - __in_ecount(cProperties) BURN_MSIPROPERTY* rgProperties, - __in DWORD cProperties, - __in BURN_VARIABLES* pVariables, - __in BOOL fRollback, - __deref_out_z LPWSTR* psczProperties, - __in BOOL fObfuscateHiddenVariables - ) -{ - HRESULT hr = S_OK; - LPWSTR sczValue = NULL; - LPWSTR sczEscapedValue = NULL; - LPWSTR sczProperty = NULL; - - for (DWORD i = 0; i < cProperties; ++i) - { - BURN_MSIPROPERTY* pProperty = &rgProperties[i]; - - if (pProperty->sczCondition && *pProperty->sczCondition) - { - BOOL fCondition = FALSE; - - hr = ConditionEvaluate(pVariables, pProperty->sczCondition, &fCondition); - if (FAILED(hr) || !fCondition) - { - LogId(REPORT_VERBOSE, MSG_MSI_PROPERTY_CONDITION_FAILED, pProperty->sczId, pProperty->sczCondition, LoggingTrueFalseToString(fCondition)); - continue; - } - } - - // format property value - if (fObfuscateHiddenVariables) - { - hr = VariableFormatStringObfuscated(pVariables, (fRollback && pProperty->sczRollbackValue) ? pProperty->sczRollbackValue : pProperty->sczValue, &sczValue, NULL); - } - else - { - hr = VariableFormatString(pVariables, (fRollback && pProperty->sczRollbackValue) ? pProperty->sczRollbackValue : pProperty->sczValue, &sczValue, NULL); - ExitOnFailure(hr, "Failed to format property value."); - } - ExitOnFailure(hr, "Failed to format property value."); - - // escape property value - hr = EscapePropertyArgumentString(sczValue, &sczEscapedValue, !fObfuscateHiddenVariables); - ExitOnFailure(hr, "Failed to escape string."); - - // build part - hr = VariableStrAllocFormatted(!fObfuscateHiddenVariables, &sczProperty, L" %s%=\"%s\"", pProperty->sczId, sczEscapedValue); - ExitOnFailure(hr, "Failed to format property string part."); - - // append to property string - hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, psczProperties, sczProperty, 0); - ExitOnFailure(hr, "Failed to append property string part."); - } - -LExit: - StrSecureZeroFreeString(sczValue); - StrSecureZeroFreeString(sczEscapedValue); - StrSecureZeroFreeString(sczProperty); - return hr; -} - -extern "C" HRESULT MsiEngineCalculateInstallUiLevel( - __in BOOTSTRAPPER_DISPLAY display, - __in BURN_USER_EXPERIENCE* pUserExperience, - __in LPCWSTR wzPackageId, - __in BOOL fExecute, - __in BOOTSTRAPPER_ACTION_STATE actionState, - __out BURN_MSI_PROPERTY* pActionMsiProperty, - __out INSTALLUILEVEL* pUiLevel, - __out BOOL* pfDisableExternalUiHandler - ) -{ - *pUiLevel = INSTALLUILEVEL_NONE; - *pfDisableExternalUiHandler = FALSE; - - if (BOOTSTRAPPER_DISPLAY_FULL == display || - BOOTSTRAPPER_DISPLAY_PASSIVE == display) - { - *pUiLevel = static_cast(*pUiLevel | INSTALLUILEVEL_SOURCERESONLY); - } - - switch (actionState) - { - case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: - *pActionMsiProperty = BURN_MSI_PROPERTY_UNINSTALL; - break; - case BOOTSTRAPPER_ACTION_STATE_REPAIR: - *pActionMsiProperty = BURN_MSI_PROPERTY_REPAIR; - break; - case BOOTSTRAPPER_ACTION_STATE_MODIFY: - *pActionMsiProperty = BURN_MSI_PROPERTY_MODIFY; - break; - default: - *pActionMsiProperty = BURN_MSI_PROPERTY_INSTALL; - break; - } - - return UserExperienceOnPlanMsiPackage(pUserExperience, wzPackageId, fExecute, actionState, pActionMsiProperty, pUiLevel, pfDisableExternalUiHandler); -} - -extern "C" void MsiEngineUpdateInstallRegistrationState( - __in BURN_EXECUTE_ACTION* pAction, - __in BOOL fRollback, - __in HRESULT hrExecute, - __in BOOL fInsideMsiTransaction - ) -{ - BURN_PACKAGE_REGISTRATION_STATE newState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; - BURN_PACKAGE* pPackage = pAction->msiPackage.pPackage; - - if (FAILED(hrExecute) || !pPackage->fCanAffectRegistration) - { - ExitFunction(); - } - - if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->msiPackage.action) - { - newState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - } - else - { - newState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } - - if (fInsideMsiTransaction) - { - pPackage->transactionRegistrationState = newState; - } - else - { - pPackage->installRegistrationState = newState; - } - - if (BURN_PACKAGE_REGISTRATION_STATE_ABSENT == newState) - { - for (DWORD i = 0; i < pPackage->Msi.cChainedPatches; ++i) - { - BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + i; - BURN_MSPTARGETPRODUCT* pTargetProduct = pChainedPatch->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex; - - if (fInsideMsiTransaction) - { - pTargetProduct->transactionRegistrationState = newState; - } - else - { - pTargetProduct->registrationState = newState; - } - } - } - else - { - for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) - { - BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + i; - BOOTSTRAPPER_ACTION_STATE patchExecuteAction = fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute; - - if (BOOTSTRAPPER_ACTION_STATE_INSTALL > patchExecuteAction) - { - continue; - } - - BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + pSlipstreamMsp->dwMsiChainedPatchIndex; - BURN_MSPTARGETPRODUCT* pTargetProduct = pChainedPatch->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex; - - if (fInsideMsiTransaction) - { - pTargetProduct->transactionRegistrationState = newState; - } - else - { - pTargetProduct->registrationState = newState; - } - } - } - -LExit: - return; -} - - -// internal helper functions - -static HRESULT ParseRelatedMsiFromXml( - __in IXMLDOMNode* pixnRelatedMsi, - __in BURN_RELATED_MSI* pRelatedMsi - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnNodes = NULL; - IXMLDOMNode* pixnNode = NULL; - DWORD cNodes = 0; - LPWSTR scz = NULL; - - // @Id - hr = XmlGetAttributeEx(pixnRelatedMsi, L"Id", &pRelatedMsi->sczUpgradeCode); - ExitOnFailure(hr, "Failed to get @Id."); - - // @MinVersion - hr = XmlGetAttributeEx(pixnRelatedMsi, L"MinVersion", &scz); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @MinVersion."); - - hr = VerParseVersion(scz, 0, FALSE, &pRelatedMsi->pMinVersion); - ExitOnFailure(hr, "Failed to parse @MinVersion: %ls", scz); - - if (pRelatedMsi->pMinVersion->fInvalid) - { - LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz); - } - - // flag that we have a min version - pRelatedMsi->fMinProvided = TRUE; - - // @MinInclusive - hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"MinInclusive", &pRelatedMsi->fMinInclusive); - ExitOnFailure(hr, "Failed to get @MinInclusive."); - } - - // @MaxVersion - hr = XmlGetAttributeEx(pixnRelatedMsi, L"MaxVersion", &scz); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @MaxVersion."); - - hr = VerParseVersion(scz, 0, FALSE, &pRelatedMsi->pMaxVersion); - ExitOnFailure(hr, "Failed to parse @MaxVersion: %ls", scz); - - if (pRelatedMsi->pMaxVersion->fInvalid) - { - LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz); - } - - // flag that we have a max version - pRelatedMsi->fMaxProvided = TRUE; - - // @MaxInclusive - hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"MaxInclusive", &pRelatedMsi->fMaxInclusive); - ExitOnFailure(hr, "Failed to get @MaxInclusive."); - } - - // @OnlyDetect - hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"OnlyDetect", &pRelatedMsi->fOnlyDetect); - ExitOnFailure(hr, "Failed to get @OnlyDetect."); - - // select language nodes - hr = XmlSelectNodes(pixnRelatedMsi, L"Language", &pixnNodes); - ExitOnFailure(hr, "Failed to select language nodes."); - - // get language node count - hr = pixnNodes->get_length((long*)&cNodes); - ExitOnFailure(hr, "Failed to get language node count."); - - if (cNodes) - { - // @LangInclusive - hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"LangInclusive", &pRelatedMsi->fLangInclusive); - ExitOnFailure(hr, "Failed to get @LangInclusive."); - - // allocate memory for language IDs - pRelatedMsi->rgdwLanguages = (DWORD*)MemAlloc(sizeof(DWORD) * cNodes, TRUE); - ExitOnNull(pRelatedMsi->rgdwLanguages, hr, E_OUTOFMEMORY, "Failed to allocate memory for language IDs."); - - pRelatedMsi->cLanguages = cNodes; - - // parse language elements - for (DWORD i = 0; i < cNodes; ++i) - { - hr = XmlNextElement(pixnNodes, &pixnNode, NULL); - ExitOnFailure(hr, "Failed to get next node."); - - // @Id - hr = XmlGetAttributeNumber(pixnNode, L"Id", &pRelatedMsi->rgdwLanguages[i]); - ExitOnFailure(hr, "Failed to get Language/@Id."); - - // prepare next iteration - ReleaseNullObject(pixnNode); - } - } - - hr = S_OK; - -LExit: - ReleaseObject(pixnNodes); - ReleaseObject(pixnNode); - ReleaseStr(scz); - - return hr; -} - -static HRESULT EvaluateActionStateConditions( - __in BURN_VARIABLES* pVariables, - __in_z_opt LPCWSTR sczAddLocalCondition, - __in_z_opt LPCWSTR sczAddSourceCondition, - __in_z_opt LPCWSTR sczAdvertiseCondition, - __out BOOTSTRAPPER_FEATURE_STATE* pState - ) -{ - HRESULT hr = S_OK; - BOOL fCondition = FALSE; - - // if no condition was set, return no feature state - if (!sczAddLocalCondition && !sczAddSourceCondition && !sczAdvertiseCondition) - { - *pState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; - ExitFunction(); - } - - if (sczAddLocalCondition) - { - hr = ConditionEvaluate(pVariables, sczAddLocalCondition, &fCondition); - ExitOnFailure(hr, "Failed to evaluate add local condition."); - - if (fCondition) - { - *pState = BOOTSTRAPPER_FEATURE_STATE_LOCAL; - ExitFunction(); - } - } - - if (sczAddSourceCondition) - { - hr = ConditionEvaluate(pVariables, sczAddSourceCondition, &fCondition); - ExitOnFailure(hr, "Failed to evaluate add source condition."); - - if (fCondition) - { - *pState = BOOTSTRAPPER_FEATURE_STATE_SOURCE; - ExitFunction(); - } - } - - if (sczAdvertiseCondition) - { - hr = ConditionEvaluate(pVariables, sczAdvertiseCondition, &fCondition); - ExitOnFailure(hr, "Failed to evaluate advertise condition."); - - if (fCondition) - { - *pState = BOOTSTRAPPER_FEATURE_STATE_ADVERTISED; - ExitFunction(); - } - } - - // if no condition was true, set to absent - *pState = BOOTSTRAPPER_FEATURE_STATE_ABSENT; - -LExit: - return hr; -} - -static HRESULT CalculateFeatureAction( - __in BOOTSTRAPPER_FEATURE_STATE currentState, - __in BOOTSTRAPPER_FEATURE_STATE requestedState, - __in BOOL fRepair, - __out BOOTSTRAPPER_FEATURE_ACTION* pFeatureAction, - __inout BOOL* pfDelta - ) -{ - HRESULT hr = S_OK; - - *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_NONE; - switch (requestedState) - { - case BOOTSTRAPPER_FEATURE_STATE_UNKNOWN: - *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_NONE; - break; - - case BOOTSTRAPPER_FEATURE_STATE_ABSENT: - if (BOOTSTRAPPER_FEATURE_STATE_ABSENT != currentState) - { - *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REMOVE; - } - break; - - case BOOTSTRAPPER_FEATURE_STATE_ADVERTISED: - if (BOOTSTRAPPER_FEATURE_STATE_ADVERTISED != currentState) - { - *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE; - } - else if (fRepair) - { - *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL; - } - break; - - case BOOTSTRAPPER_FEATURE_STATE_LOCAL: - if (BOOTSTRAPPER_FEATURE_STATE_LOCAL != currentState) - { - *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL; - } - else if (fRepair) - { - *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL; - } - break; - - case BOOTSTRAPPER_FEATURE_STATE_SOURCE: - if (BOOTSTRAPPER_FEATURE_STATE_SOURCE != currentState) - { - *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE; - } - else if (fRepair) - { - *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL; - } - break; - - default: - hr = E_UNEXPECTED; - ExitOnRootFailure(hr, "Invalid state value."); - } - - if (BOOTSTRAPPER_FEATURE_ACTION_NONE != *pFeatureAction) - { - *pfDelta = TRUE; - } - -LExit: - return hr; -} - -static HRESULT EscapePropertyArgumentString( - __in LPCWSTR wzProperty, - __inout_z LPWSTR* psczEscapedValue, - __in BOOL fZeroOnRealloc - ) -{ - HRESULT hr = S_OK; - DWORD cch = 0; - DWORD cchEscape = 0; - LPCWSTR wzSource = NULL; - LPWSTR wzTarget = NULL; - - // count characters to escape - wzSource = wzProperty; - while (*wzSource) - { - ++cch; - if (L'\"' == *wzSource) - { - ++cchEscape; - } - ++wzSource; - } - - // allocate target buffer - hr = VariableStrAlloc(fZeroOnRealloc, psczEscapedValue, cch + cchEscape + 1); // character count, plus escape character count, plus null terminator - ExitOnFailure(hr, "Failed to allocate string buffer."); - - // write to target buffer - wzSource = wzProperty; - wzTarget = *psczEscapedValue; - while (*wzSource) - { - *wzTarget = *wzSource; - if (L'\"' == *wzTarget) - { - ++wzTarget; - *wzTarget = L'\"'; - } - - ++wzSource; - ++wzTarget; - } - - *wzTarget = L'\0'; // add null terminator - -LExit: - return hr; -} - -static HRESULT ConcatFeatureActionProperties( - __in BURN_PACKAGE* pPackage, - __in BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions, - __inout_z LPWSTR* psczArguments - ) -{ - HRESULT hr = S_OK; - LPWSTR scz = NULL; - LPWSTR sczAddLocal = NULL; - LPWSTR sczAddSource = NULL; - LPWSTR sczAddDefault = NULL; - LPWSTR sczReinstall = NULL; - LPWSTR sczAdvertise = NULL; - LPWSTR sczRemove = NULL; - - // features - for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) - { - BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; - - switch (rgFeatureActions[i]) - { - case BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL: - if (sczAddLocal) - { - hr = StrAllocConcat(&sczAddLocal, L",", 0); - ExitOnFailure(hr, "Failed to concat separator."); - } - hr = StrAllocConcat(&sczAddLocal, pFeature->sczId, 0); - ExitOnFailure(hr, "Failed to concat feature."); - break; - - case BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE: - if (sczAddSource) - { - hr = StrAllocConcat(&sczAddSource, L",", 0); - ExitOnFailure(hr, "Failed to concat separator."); - } - hr = StrAllocConcat(&sczAddSource, pFeature->sczId, 0); - ExitOnFailure(hr, "Failed to concat feature."); - break; - - case BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT: - if (sczAddDefault) - { - hr = StrAllocConcat(&sczAddDefault, L",", 0); - ExitOnFailure(hr, "Failed to concat separator."); - } - hr = StrAllocConcat(&sczAddDefault, pFeature->sczId, 0); - ExitOnFailure(hr, "Failed to concat feature."); - break; - - case BOOTSTRAPPER_FEATURE_ACTION_REINSTALL: - if (sczReinstall) - { - hr = StrAllocConcat(&sczReinstall, L",", 0); - ExitOnFailure(hr, "Failed to concat separator."); - } - hr = StrAllocConcat(&sczReinstall, pFeature->sczId, 0); - ExitOnFailure(hr, "Failed to concat feature."); - break; - - case BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE: - if (sczAdvertise) - { - hr = StrAllocConcat(&sczAdvertise, L",", 0); - ExitOnFailure(hr, "Failed to concat separator."); - } - hr = StrAllocConcat(&sczAdvertise, pFeature->sczId, 0); - ExitOnFailure(hr, "Failed to concat feature."); - break; - - case BOOTSTRAPPER_FEATURE_ACTION_REMOVE: - if (sczRemove) - { - hr = StrAllocConcat(&sczRemove, L",", 0); - ExitOnFailure(hr, "Failed to concat separator."); - } - hr = StrAllocConcat(&sczRemove, pFeature->sczId, 0); - ExitOnFailure(hr, "Failed to concat feature."); - break; - } - } - - if (sczAddLocal) - { - hr = StrAllocFormatted(&scz, L" ADDLOCAL=\"%s\"", sczAddLocal, 0); - ExitOnFailure(hr, "Failed to format ADDLOCAL string."); - - hr = StrAllocConcatSecure(psczArguments, scz, 0); - ExitOnFailure(hr, "Failed to concat argument string."); - } - - if (sczAddSource) - { - hr = StrAllocFormatted(&scz, L" ADDSOURCE=\"%s\"", sczAddSource, 0); - ExitOnFailure(hr, "Failed to format ADDSOURCE string."); - - hr = StrAllocConcatSecure(psczArguments, scz, 0); - ExitOnFailure(hr, "Failed to concat argument string."); - } - - if (sczAddDefault) - { - hr = StrAllocFormatted(&scz, L" ADDDEFAULT=\"%s\"", sczAddDefault, 0); - ExitOnFailure(hr, "Failed to format ADDDEFAULT string."); - - hr = StrAllocConcatSecure(psczArguments, scz, 0); - ExitOnFailure(hr, "Failed to concat argument string."); - } - - if (sczReinstall) - { - hr = StrAllocFormatted(&scz, L" REINSTALL=\"%s\"", sczReinstall, 0); - ExitOnFailure(hr, "Failed to format REINSTALL string."); - - hr = StrAllocConcatSecure(psczArguments, scz, 0); - ExitOnFailure(hr, "Failed to concat argument string."); - } - - if (sczAdvertise) - { - hr = StrAllocFormatted(&scz, L" ADVERTISE=\"%s\"", sczAdvertise, 0); - ExitOnFailure(hr, "Failed to format ADVERTISE string."); - - hr = StrAllocConcatSecure(psczArguments, scz, 0); - ExitOnFailure(hr, "Failed to concat argument string."); - } - - if (sczRemove) - { - hr = StrAllocFormatted(&scz, L" REMOVE=\"%s\"", sczRemove, 0); - ExitOnFailure(hr, "Failed to format REMOVE string."); - - hr = StrAllocConcatSecure(psczArguments, scz, 0); - ExitOnFailure(hr, "Failed to concat argument string."); - } - -LExit: - ReleaseStr(scz); - ReleaseStr(sczAddLocal); - ReleaseStr(sczAddSource); - ReleaseStr(sczAddDefault); - ReleaseStr(sczReinstall); - ReleaseStr(sczAdvertise); - ReleaseStr(sczRemove); - - return hr; -} - -static HRESULT ConcatPatchProperty( - __in BURN_PACKAGE* pPackage, - __in BOOL fRollback, - __inout_z LPWSTR* psczArguments - ) -{ - HRESULT hr = S_OK; - LPWSTR sczCachedDirectory = NULL; - LPWSTR sczMspPath = NULL; - LPWSTR sczPatches = NULL; - - // If there are slipstream patch actions, build up their patch action. - if (pPackage->Msi.cSlipstreamMspPackages) - { - for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) - { - BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + i; - BURN_PACKAGE* pMspPackage = pSlipstreamMsp->pMspPackage; - BURN_PAYLOAD* pMspPackagePayload = pMspPackage->payloads.rgItems[0].pPayload; - BOOTSTRAPPER_ACTION_STATE patchExecuteAction = fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute; - - if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < patchExecuteAction) - { - hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory); - ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId); - - hr = PathConcat(sczCachedDirectory, pMspPackagePayload->sczFilePath, &sczMspPath); - ExitOnFailure(hr, "Failed to build MSP path."); - - if (!sczPatches) - { - hr = StrAllocConcat(&sczPatches, L" PATCH=\"", 0); - ExitOnFailure(hr, "Failed to prefix with PATCH property."); - } - else - { - hr = StrAllocConcat(&sczPatches, L";", 0); - ExitOnFailure(hr, "Failed to semi-colon delimit patches."); - } - - hr = StrAllocConcat(&sczPatches, sczMspPath, 0); - ExitOnFailure(hr, "Failed to append patch path."); - } - } - - if (sczPatches) - { - hr = StrAllocConcat(&sczPatches, L"\"", 0); - ExitOnFailure(hr, "Failed to close the quoted PATCH property."); - - hr = StrAllocConcatSecure(psczArguments, sczPatches, 0); - ExitOnFailure(hr, "Failed to append PATCH property."); - } - } - -LExit: - ReleaseStr(sczMspPath); - ReleaseStr(sczCachedDirectory); - ReleaseStr(sczPatches); - return hr; -} - -static void RegisterSourceDirectory( - __in BURN_PACKAGE* pPackage, - __in_z LPCWSTR wzMsiPath - ) -{ - HRESULT hr = S_OK; - LPWSTR sczMsiDirectory = NULL; - MSIINSTALLCONTEXT dwContext = pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED; - - hr = PathGetDirectory(wzMsiPath, &sczMsiDirectory); - ExitOnFailure(hr, "Failed to get directory for path: %ls", wzMsiPath); - - hr = WiuSourceListAddSourceEx(pPackage->Msi.sczProductCode, NULL, dwContext, MSICODE_PRODUCT, sczMsiDirectory, 1); - if (FAILED(hr)) - { - LogId(REPORT_VERBOSE, MSG_SOURCELIST_REGISTER, sczMsiDirectory, pPackage->Msi.sczProductCode, hr); - ExitFunction(); - } - -LExit: - ReleaseStr(sczMsiDirectory); - - return; -} diff --git a/src/engine/msiengine.h b/src/engine/msiengine.h deleted file mode 100644 index 8b5bcdd0..00000000 --- a/src/engine/msiengine.h +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -// constants -#define BURNMSIINSTALL_PROPERTY_NAME L"BURNMSIINSTALL" -#define BURNMSIMODIFY_PROPERTY_NAME L"BURNMSIMODIFY" -#define BURNMSIREPAIR_PROPERTY_NAME L"BURNMSIREPAIR" -#define BURNMSIUNINSTALL_PROPERTY_NAME L"BURNMSIUNINSTALL" - - -#if defined(__cplusplus) -extern "C" { -#endif - - -// function declarations - -HRESULT MsiEngineParsePackageFromXml( - __in IXMLDOMNode* pixnBundle, - __in BURN_PACKAGE* pPackage - ); -HRESULT MsiEngineParsePropertiesFromXml( - __in IXMLDOMNode* pixnPackage, - __out BURN_MSIPROPERTY** prgProperties, - __out DWORD* pcProperties - ); -void MsiEnginePackageUninitialize( - __in BURN_PACKAGE* pPackage - ); -HRESULT MsiEngineDetectInitialize( - __in BURN_PACKAGES* pPackages - ); -HRESULT MsiEngineDetectPackage( - __in BURN_PACKAGE* pPackage, - __in BURN_USER_EXPERIENCE* pUserExperience - ); -HRESULT MsiEnginePlanInitializePackage( - __in BURN_PACKAGE* pPackage, - __in BURN_VARIABLES* pVariables, - __in BURN_USER_EXPERIENCE* pUserExperience - ); -HRESULT MsiEnginePlanCalculatePackage( - __in BURN_PACKAGE* pPackage, - __in BOOL fInsideMsiTransaction - ); -HRESULT MsiEnginePlanAddPackage( - __in BOOTSTRAPPER_DISPLAY display, - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BURN_PACKAGE* pPackage, - __in BURN_PLAN* pPlan, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in_opt HANDLE hCacheEvent - ); -HRESULT MsiEngineBeginTransaction( - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary - ); -HRESULT MsiEngineCommitTransaction( - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary - ); -HRESULT MsiEngineRollbackTransaction( - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary - ); -HRESULT MsiEngineExecutePackage( - __in_opt HWND hwndParent, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_VARIABLES* pVariables, - __in BOOL fRollback, - __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, - __in LPVOID pvContext, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ); -HRESULT MsiEngineConcatActionProperty( - __in BURN_MSI_PROPERTY actionMsiProperty, - __deref_out_z LPWSTR* psczProperties - ); -HRESULT MsiEngineConcatProperties( - __in_ecount(cProperties) BURN_MSIPROPERTY* rgProperties, - __in DWORD cProperties, - __in BURN_VARIABLES* pVariables, - __in BOOL fRollback, - __deref_out_z LPWSTR* psczProperties, - __in BOOL fObfuscateHiddenVariables - ); -HRESULT MsiEngineCalculateInstallUiLevel( - __in BOOTSTRAPPER_DISPLAY display, - __in BURN_USER_EXPERIENCE* pUserExperience, - __in LPCWSTR wzPackageId, - __in BOOL fExecute, - __in BOOTSTRAPPER_ACTION_STATE actionState, - __out BURN_MSI_PROPERTY* pActionMsiProperty, - __out INSTALLUILEVEL* pUiLevel, - __out BOOL* pfDisableExternalUiHandler - ); -void MsiEngineUpdateInstallRegistrationState( - __in BURN_EXECUTE_ACTION* pAction, - __in BOOL fRollback, - __in HRESULT hrExecute, - __in BOOL fInsideMsiTransaction - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp deleted file mode 100644 index 6d58d324..00000000 --- a/src/engine/mspengine.cpp +++ /dev/null @@ -1,1197 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// constants - - -// structs - -struct POSSIBLE_TARGETPRODUCT -{ - WCHAR wzProductCode[39]; - LPWSTR pszLocalPackage; - MSIINSTALLCONTEXT context; -}; - -// internal function declarations - -static HRESULT GetPossibleTargetProductCodes( - __in BURN_PACKAGES* pPackages, - __deref_inout_ecount_opt(*pcPossibleTargetProductCodes) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProductCodes, - __inout DWORD* pcPossibleTargetProductCodes - ); -static HRESULT AddPossibleTargetProduct( - __in STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes, - __in_z LPCWSTR wzPossibleTargetProductCode, - __in MSIINSTALLCONTEXT context, - __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts, - __inout DWORD* pcPossibleTargetProducts - ); -static HRESULT AddDetectedTargetProduct( - __in BURN_PACKAGE* pPackage, - __in DWORD dwOrder, - __in_z LPCWSTR wzProductCode, - __in MSIINSTALLCONTEXT context, - __out DWORD* pdwTargetProductIndex - ); -static HRESULT AddMsiChainedPatch( - __in BURN_PACKAGE* pPackage, - __in BURN_PACKAGE* pMspPackage, - __in DWORD dwMspTargetProductIndex, - __out DWORD* pdwChainedPatchIndex - ); -static HRESULT DeterminePatchChainedTarget( - __in BURN_PACKAGES* pPackages, - __in BURN_PACKAGE* pMspPackage, - __in LPCWSTR wzTargetProductCode, - __in DWORD dwMspTargetProductIndex - ); -static HRESULT PlanTargetProduct( - __in BOOTSTRAPPER_DISPLAY display, - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BOOL fRollback, - __in BURN_PLAN* pPlan, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_ACTION_STATE actionState, - __in BURN_PACKAGE* pPackage, - __in BURN_MSPTARGETPRODUCT* pTargetProduct, - __in_opt HANDLE hCacheEvent - ); - - -// function definitions - -extern "C" HRESULT MspEngineParsePackageFromXml( - __in IXMLDOMNode* pixnMspPackage, - __in BURN_PACKAGE* pPackage - ) -{ - HRESULT hr = S_OK; - - // @PatchCode - hr = XmlGetAttributeEx(pixnMspPackage, L"PatchCode", &pPackage->Msp.sczPatchCode); - ExitOnFailure(hr, "Failed to get @PatchCode."); - - // @PatchXml - hr = XmlGetAttributeEx(pixnMspPackage, L"PatchXml", &pPackage->Msp.sczApplicabilityXml); - ExitOnFailure(hr, "Failed to get @PatchXml."); - - // Read properties. - hr = MsiEngineParsePropertiesFromXml(pixnMspPackage, &pPackage->Msp.rgProperties, &pPackage->Msp.cProperties); - ExitOnFailure(hr, "Failed to parse properties from XML."); - -LExit: - - return hr; -} - -extern "C" void MspEnginePackageUninitialize( - __in BURN_PACKAGE* pPackage - ) -{ - ReleaseStr(pPackage->Msp.sczPatchCode); - ReleaseStr(pPackage->Msp.sczApplicabilityXml); - - // free properties - if (pPackage->Msp.rgProperties) - { - for (DWORD i = 0; i < pPackage->Msp.cProperties; ++i) - { - BURN_MSIPROPERTY* pProperty = &pPackage->Msp.rgProperties[i]; - - ReleaseStr(pProperty->sczId); - ReleaseStr(pProperty->sczValue); - ReleaseStr(pProperty->sczRollbackValue); - } - MemFree(pPackage->Msp.rgProperties); - } - - // free target products - ReleaseMem(pPackage->Msp.rgTargetProducts); - - // clear struct - memset(&pPackage->Msp, 0, sizeof(pPackage->Msp)); -} - -extern "C" HRESULT MspEngineDetectInitialize( - __in BURN_PACKAGES* pPackages - ) -{ - AssertSz(pPackages->cPatchInfo, "MspEngineDetectInitialize() should only be called if there are MSP packages."); - - HRESULT hr = S_OK; - POSSIBLE_TARGETPRODUCT* rgPossibleTargetProducts = NULL; - DWORD cPossibleTargetProducts = 0; - -#ifdef DEBUG - // All patch info should be initialized to zero. - for (DWORD i = 0; i < pPackages->cPatchInfo; ++i) - { - BURN_PACKAGE* pPackage = pPackages->rgPatchInfoToPackage[i]; - Assert(!pPackage->Msp.cTargetProductCodes); - Assert(!pPackage->Msp.rgTargetProducts); - } -#endif - - // Figure out which product codes to target on the machine. In the worst case all products on the machine - // will be returned. - hr = GetPossibleTargetProductCodes(pPackages, &rgPossibleTargetProducts, &cPossibleTargetProducts); - ExitOnFailure(hr, "Failed to get possible target product codes."); - - // Loop through possible target products, testing the collective patch applicability against each product in - // the appropriate context. Store the result with the appropriate patch package. - for (DWORD iSearch = 0; iSearch < cPossibleTargetProducts; ++iSearch) - { - const POSSIBLE_TARGETPRODUCT* pPossibleTargetProduct = rgPossibleTargetProducts + iSearch; - - LogId(REPORT_STANDARD, MSG_DETECT_CALCULATE_PATCH_APPLICABILITY, pPossibleTargetProduct->wzProductCode, LoggingMsiInstallContext(pPossibleTargetProduct->context)); - - if (pPossibleTargetProduct->pszLocalPackage) - { - // Ignores current machine state to determine just patch applicability. - // Superseded and obsolesced patches will be planned separately. - hr = WiuDetermineApplicablePatches(pPossibleTargetProduct->pszLocalPackage, pPackages->rgPatchInfo, pPackages->cPatchInfo); - } - else - { - hr = WiuDeterminePatchSequence(pPossibleTargetProduct->wzProductCode, NULL, pPossibleTargetProduct->context, pPackages->rgPatchInfo, pPackages->cPatchInfo); - } - - if (SUCCEEDED(hr)) - { - for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo) - { - hr = HRESULT_FROM_WIN32(pPackages->rgPatchInfo[iPatchInfo].uStatus); - BURN_PACKAGE* pMspPackage = pPackages->rgPatchInfoToPackage[iPatchInfo]; - Assert(BURN_PACKAGE_TYPE_MSP == pMspPackage->type); - - if (S_OK == hr) - { - // Note that we do add superseded and obsolete MSP packages. Package Detect and Plan will sort them out later. - hr = MspEngineAddDetectedTargetProduct(pPackages, pMspPackage, pPackages->rgPatchInfo[iPatchInfo].dwOrder, pPossibleTargetProduct->wzProductCode, pPossibleTargetProduct->context); - ExitOnFailure(hr, "Failed to add target product code to package: %ls", pMspPackage->sczId); - } - else - { - LogStringLine(REPORT_DEBUG, " 0x%x: Patch applicability failed for package: %ls", hr, pMspPackage->sczId); - } - } - } - else - { - LogId(REPORT_STANDARD, MSG_DETECT_FAILED_CALCULATE_PATCH_APPLICABILITY, pPossibleTargetProduct->wzProductCode, LoggingMsiInstallContext(pPossibleTargetProduct->context), hr); - } - - hr = S_OK; // always reset so we test all possible target products. - } - -LExit: - if (rgPossibleTargetProducts) - { - for (DWORD i = 0; i < cPossibleTargetProducts; ++i) - { - ReleaseStr(rgPossibleTargetProducts[i].pszLocalPackage); - } - MemFree(rgPossibleTargetProducts); - } - - return hr; -} - -extern "C" HRESULT MspEngineAddDetectedTargetProduct( - __in BURN_PACKAGES* pPackages, - __in BURN_PACKAGE* pPackage, - __in DWORD dwOrder, - __in_z LPCWSTR wzProductCode, - __in MSIINSTALLCONTEXT context - ) -{ - HRESULT hr = S_OK; - DWORD dwTargetProductIndex = 0; - - hr = AddDetectedTargetProduct(pPackage, dwOrder, wzProductCode, context, &dwTargetProductIndex); - ExitOnFailure(hr, "Failed to add detected target product."); - - hr = DeterminePatchChainedTarget(pPackages, pPackage, wzProductCode, dwTargetProductIndex); - ExitOnFailure(hr, "Failed to determine patch chained target."); - -LExit: - return hr; -} - -extern "C" HRESULT MspEngineAddMissingSlipstreamTarget( - __in BURN_PACKAGE* pMsiPackage, - __in BURN_SLIPSTREAM_MSP* pSlipstreamMsp - ) -{ - HRESULT hr = S_OK; - DWORD dwTargetProductIndex = 0; - BURN_MSPTARGETPRODUCT* pTargetProduct = NULL; - DWORD dwChainedPatchIndex = 0; - - hr = AddDetectedTargetProduct(pSlipstreamMsp->pMspPackage, 0, pMsiPackage->Msi.sczProductCode, pMsiPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, &dwTargetProductIndex); - ExitOnFailure(hr, "Failed to add missing slipstream target."); - - pTargetProduct = pSlipstreamMsp->pMspPackage->Msp.rgTargetProducts + dwTargetProductIndex; - pTargetProduct->fSlipstream = TRUE; - pTargetProduct->fSlipstreamRequired = TRUE; - pTargetProduct->pChainedTargetPackage = pMsiPackage; - - hr = AddMsiChainedPatch(pMsiPackage, pSlipstreamMsp->pMspPackage, dwTargetProductIndex, &dwChainedPatchIndex); - ExitOnFailure(hr, "Failed to add chained patch."); - - pSlipstreamMsp->dwMsiChainedPatchIndex = dwChainedPatchIndex; - -LExit: - return hr; -} - -extern "C" HRESULT MspEngineDetectPackage( - __in BURN_PACKAGE* pPackage, - __in BURN_USER_EXPERIENCE* pUserExperience - ) -{ - HRESULT hr = S_OK; - LPWSTR sczState = NULL; - - if (pPackage->fCanAffectRegistration) - { - pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - } - - if (0 == pPackage->Msp.cTargetProductCodes) - { - pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; - } - else - { - // Start the package state at the highest state then loop through all the - // target product codes and end up setting the current state to the lowest - // package state applied to the target product codes. - pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; - - for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) - { - BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; - - hr = WiuGetPatchInfoEx(pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode, NULL, pTargetProduct->context, INSTALLPROPERTY_PATCHSTATE, &sczState); - if (SUCCEEDED(hr)) - { - switch (*sczState) - { - case '1': - pTargetProduct->fInstalled = TRUE; - pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; - break; - - case '2': - pTargetProduct->fInstalled = TRUE; - pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; - break; - - case '4': - pTargetProduct->fInstalled = TRUE; - pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE; - break; - - default: - pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; - break; - } - } - else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PATCH) == hr || HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr) - { - pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; - hr = S_OK; - } - ExitOnFailure(hr, "Failed to get patch information for patch code: %ls, target product code: %ls", pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode); - - if (pPackage->currentState > pTargetProduct->patchPackageState) - { - pPackage->currentState = pTargetProduct->patchPackageState; - } - - if (pPackage->fCanAffectRegistration) - { - pTargetProduct->registrationState = pTargetProduct->fInstalled ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - - if (pTargetProduct->fInstalled) - { - pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } - } - - hr = UserExperienceOnDetectPatchTarget(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, pTargetProduct->patchPackageState); - ExitOnRootFailure(hr, "BA aborted detect patch target."); - } - } - -LExit: - ReleaseStr(sczState); - - return hr; -} - -extern "C" HRESULT MspEnginePlanInitializePackage( - __in BURN_PACKAGE* pPackage, - __in BURN_USER_EXPERIENCE* pUserExperience - ) -{ - HRESULT hr = S_OK; - - for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) - { - BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; - - if (!pTargetProduct->fInstalled && pTargetProduct->fSlipstreamRequired && BOOTSTRAPPER_REQUEST_STATE_PRESENT > pTargetProduct->pChainedTargetPackage->requested) - { - // There's no way to apply the patch if the target isn't installed. - pTargetProduct->defaultRequested = pTargetProduct->requested = BOOTSTRAPPER_REQUEST_STATE_NONE; - continue; - } - - pTargetProduct->defaultRequested = pTargetProduct->requested = pPackage->requested; - - hr = UserExperienceOnPlanPatchTarget(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, &pTargetProduct->requested); - ExitOnRootFailure(hr, "BA aborted plan patch target."); - } - -LExit: - return hr; -} - -// -// PlanCalculate - calculates the execute and rollback state for the requested package state. -// -extern "C" HRESULT MspEnginePlanCalculatePackage( - __in BURN_PACKAGE* pPackage, - __in BOOL fInsideMsiTransaction - ) -{ - HRESULT hr = S_OK; - BOOL fWillUninstallAll = TRUE; - - for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) - { - BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; - - BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; - BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - - // Calculate the execute action. - switch (pTargetProduct->patchPackageState) - { - case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: - switch (pTargetProduct->requested) - { - case BOOTSTRAPPER_REQUEST_STATE_REPAIR: - execute = BOOTSTRAPPER_ACTION_STATE_REPAIR; - fWillUninstallAll = FALSE; - break; - - case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_CACHE: - execute = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; - break; - - case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: - execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; - break; - - default: - execute = BOOTSTRAPPER_ACTION_STATE_NONE; - fWillUninstallAll = FALSE; - break; - } - break; - - case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: - switch (pTargetProduct->requested) - { - case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_REPAIR: - execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; - fWillUninstallAll = FALSE; - break; - - default: - execute = BOOTSTRAPPER_ACTION_STATE_NONE; - break; - } - break; - - default: - if (pTargetProduct->fInstalled) - { - fWillUninstallAll = FALSE; - } - break; - } - - // Calculate the rollback action if there is an execute action. - if (BOOTSTRAPPER_ACTION_STATE_NONE != execute && !fInsideMsiTransaction) - { - switch (pPackage->currentState) - { - case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: - switch (pTargetProduct->requested) - { - case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_ABSENT: - rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL; - break; - - default: - rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - break; - } - break; - - case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough; - switch (pTargetProduct->requested) - { - case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_REPAIR: - rollback = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; - break; - - default: - rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - break; - } - break; - - default: - rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - break; - } - } - - pTargetProduct->execute = execute; - pTargetProduct->rollback = rollback; - - // The highest aggregate action state found will be returned. - if (pPackage->execute < execute) - { - pPackage->execute = execute; - } - - if (pPackage->rollback < rollback) - { - pPackage->rollback = rollback; - } - } - - // The dependency manager will do the wrong thing if the package level action is UNINSTALL - // when the patch will still be applied to at least one product. - if (!fWillUninstallAll && BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) - { - pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; - } - - return hr; -} - -// -// PlanAdd - adds the calculated execute and rollback actions for the package. -// -extern "C" HRESULT MspEnginePlanAddPackage( - __in BOOTSTRAPPER_DISPLAY display, - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BURN_PACKAGE* pPackage, - __in BURN_PLAN* pPlan, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in_opt HANDLE hCacheEvent - ) -{ - HRESULT hr = S_OK; - - // TODO: need to handle the case where this patch adds itself to an earlier patch's list of target products. That would - // essentially bump this patch earlier in the plan and we need to make sure this patch is downloaded. - // add wait for cache - if (hCacheEvent) - { - hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent); - ExitOnFailure(hr, "Failed to plan package cache syncpoint"); - } - - hr = DependencyPlanPackage(NULL, pPackage, pPlan); - ExitOnFailure(hr, "Failed to plan package dependency actions."); - - // Plan the actions for each target product code. - for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) - { - BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; - - // If the dependency manager changed the action state for the patch, change the target product actions. - if (pPackage->fDependencyManagerWasHere) - { - pTargetProduct->execute = pPackage->execute; - pTargetProduct->rollback = pPackage->rollback; - } - - if (BOOTSTRAPPER_ACTION_STATE_NONE != pTargetProduct->execute) - { - hr = PlanTargetProduct(display, pUserExperience, FALSE, pPlan, pLog, pVariables, pTargetProduct->execute, pPackage, pTargetProduct, hCacheEvent); - ExitOnFailure(hr, "Failed to plan target product."); - } - - if (BOOTSTRAPPER_ACTION_STATE_NONE != pTargetProduct->rollback) - { - hr = PlanTargetProduct(display, pUserExperience, TRUE, pPlan, pLog, pVariables, pTargetProduct->rollback, pPackage, pTargetProduct, hCacheEvent); - ExitOnFailure(hr, "Failed to plan rollback target product."); - } - } - -LExit: - - return hr; -} - -extern "C" HRESULT MspEngineExecutePackage( - __in_opt HWND hwndParent, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_VARIABLES* pVariables, - __in BOOL fRollback, - __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, - __in LPVOID pvContext, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ) -{ - HRESULT hr = S_OK; - WIU_MSI_EXECUTE_CONTEXT context = { }; - WIU_RESTART restart = WIU_RESTART_NONE; - - LPWSTR sczCachedDirectory = NULL; - LPWSTR sczMspPath = NULL; - LPWSTR sczPatches = NULL; - LPWSTR sczProperties = NULL; - LPWSTR sczObfuscatedProperties = NULL; - - // default to "verbose" logging - DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE; - - // get cached MSP paths - for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i) - { - LPCWSTR wzAppend = NULL; - BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage; - BURN_PAYLOAD* pMspPackagePayload = pMspPackage->payloads.rgItems[0].pPayload; - AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Invalid package type added to ordered patches."); - - if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->mspTarget.action) - { - hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory); - ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId); - - // TODO: Figure out if this makes sense -- the variable is set to the last patch's path only - // Best effort to set the execute package cache folder variable. - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE); - - hr = PathConcat(sczCachedDirectory, pMspPackagePayload->sczFilePath, &sczMspPath); - ExitOnFailure(hr, "Failed to build MSP path."); - - wzAppend = sczMspPath; - } - else // uninstall - { - wzAppend = pMspPackage->Msp.sczPatchCode; - } - - if (NULL != sczPatches) - { - hr = StrAllocConcat(&sczPatches, L";", 0); - ExitOnFailure(hr, "Failed to semi-colon delimit patches."); - } - - hr = StrAllocConcat(&sczPatches, wzAppend, 0); - ExitOnFailure(hr, "Failed to append patch."); - } - - // Best effort to set the execute package action variable. - VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->mspTarget.action, TRUE); - - // Wire up the external UI handler and logging. - if (pExecuteAction->mspTarget.fDisableExternalUiHandler) - { - hr = WiuInitializeInternalUI(pExecuteAction->mspTarget.uiLevel, hwndParent, &context); - ExitOnFailure(hr, "Failed to initialize internal UI for MSP package."); - } - else - { - hr = WiuInitializeExternalUI(pfnMessageHandler, pExecuteAction->mspTarget.uiLevel, hwndParent, pvContext, fRollback, &context); - ExitOnFailure(hr, "Failed to initialize external UI handler."); - } - - //if (BURN_LOGGING_LEVEL_DEBUG == logLevel) - //{ - // dwLogMode | INSTALLLOGMODE_EXTRADEBUG; - //} - - if (pExecuteAction->mspTarget.sczLogPath && *pExecuteAction->mspTarget.sczLogPath) - { - hr = WiuEnableLog(dwLogMode, pExecuteAction->mspTarget.sczLogPath, 0); - ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pExecuteAction->mspTarget.pPackage->sczId, pExecuteAction->mspTarget.sczLogPath); - } - - // set up properties - hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczProperties, FALSE); - ExitOnFailure(hr, "Failed to add properties to argument string."); - - hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE); - ExitOnFailure(hr, "Failed to add properties to obfuscated argument string."); - - hr = MsiEngineConcatActionProperty(pExecuteAction->mspTarget.actionMsiProperty, &sczProperties); - ExitOnFailure(hr, "Failed to add action property to argument string."); - - hr = MsiEngineConcatActionProperty(pExecuteAction->mspTarget.actionMsiProperty, &sczObfuscatedProperties); - ExitOnFailure(hr, "Failed to add action property to obfuscated argument string."); - - LogId(REPORT_STANDARD, MSG_APPLYING_PATCH_PACKAGE, pExecuteAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pExecuteAction->mspTarget.action), sczPatches, sczObfuscatedProperties, pExecuteAction->mspTarget.sczTargetProductCode); - - // - // Do the actual action. - // - switch (pExecuteAction->mspTarget.action) - { - case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough; - case BOOTSTRAPPER_ACTION_STATE_REPAIR: - hr = StrAllocConcatSecure(&sczProperties, L" PATCH=\"", 0); - ExitOnFailure(hr, "Failed to add PATCH property on install."); - - hr = StrAllocConcatSecure(&sczProperties, sczPatches, 0); - ExitOnFailure(hr, "Failed to add patches to PATCH property on install."); - - hr = StrAllocConcatSecure(&sczProperties, L"\" REBOOT=ReallySuppress", 0); - ExitOnFailure(hr, "Failed to add reboot suppression property on install."); - - hr = WiuConfigureProductEx(pExecuteAction->mspTarget.sczTargetProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, sczProperties, &restart); - ExitOnFailure(hr, "Failed to install MSP package."); - break; - - case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: - hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0); - ExitOnFailure(hr, "Failed to add reboot suppression property on uninstall."); - - // Ignore all dependencies, since the Burn engine already performed the check. - hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES); - ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties."); - - hr = WiuRemovePatches(sczPatches, pExecuteAction->mspTarget.sczTargetProductCode, sczProperties, &restart); - ExitOnFailure(hr, "Failed to uninstall MSP package."); - break; - } - -LExit: - WiuUninitializeExternalUI(&context); - - ReleaseStr(sczCachedDirectory); - ReleaseStr(sczMspPath); - StrSecureZeroFreeString(sczProperties); - ReleaseStr(sczObfuscatedProperties); - ReleaseStr(sczPatches); - - switch (restart) - { - case WIU_RESTART_NONE: - *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; - break; - - case WIU_RESTART_REQUIRED: - *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; - break; - - case WIU_RESTART_INITIATED: - *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; - break; - } - - // Best effort to clear the execute package cache folder and action variables. - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE); - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE, FALSE); - - return hr; -} - -extern "C" void MspEngineUpdateInstallRegistrationState( - __in BURN_EXECUTE_ACTION* pAction, - __in HRESULT hrExecute, - __in BOOL fInsideMsiTransaction - ) -{ - BURN_PACKAGE_REGISTRATION_STATE newState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; - - if (FAILED(hrExecute)) - { - ExitFunction(); - } - - if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->mspTarget.action) - { - newState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - } - else - { - newState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } - - for (DWORD i = 0; i < pAction->mspTarget.cOrderedPatches; ++i) - { - BURN_ORDERED_PATCHES* pOrderedPatches = pAction->mspTarget.rgOrderedPatches + i; - BURN_PACKAGE* pPackage = pOrderedPatches->pPackage; - BURN_MSPTARGETPRODUCT* pTargetProduct = NULL; - - Assert(BURN_PACKAGE_TYPE_MSP == pPackage->type); - - if (!pPackage->fCanAffectRegistration) - { - continue; - } - - for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j) - { - pTargetProduct = pPackage->Msp.rgTargetProducts + j; - if (pAction->mspTarget.fPerMachineTarget == (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context) && - CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pAction->mspTarget.sczTargetProductCode, -1, pTargetProduct->wzTargetProductCode, -1)) - { - break; - } - - pTargetProduct = NULL; - } - - if (!pTargetProduct) - { - AssertSz(pTargetProduct, "Ordered patch didn't have corresponding target product"); - continue; - } - - if (fInsideMsiTransaction) - { - pTargetProduct->transactionRegistrationState = newState; - } - else - { - pTargetProduct->registrationState = newState; - } - } - -LExit: - return; -} - -extern "C" void MspEngineFinalizeInstallRegistrationState( - __in BURN_PACKAGE* pPackage - ) -{ - if (!pPackage->fCanAffectRegistration) - { - ExitFunction(); - } - - if (!pPackage->Msp.cTargetProductCodes) - { - pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - } - else - { - pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; - - for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) - { - BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; - - if (pPackage->installRegistrationState < pTargetProduct->registrationState) - { - pPackage->installRegistrationState = pTargetProduct->registrationState; - } - } - } - -LExit: - return; -} - - -// internal helper functions - -static HRESULT GetPossibleTargetProductCodes( - __in BURN_PACKAGES* pPackages, - __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts, - __inout DWORD* pcPossibleTargetProducts - ) -{ - HRESULT hr = S_OK; - STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes = NULL; - BOOL fCheckAll = FALSE; - WCHAR wzPossibleTargetProductCode[MAX_GUID_CHARS + 1]; - - // Use a dictionary to ensure we capture unique product codes. Otherwise, we could end up - // doing patch applicability for the same product code multiple times and that would confuse - // everything down stream. - hr = DictCreateStringList(&sdUniquePossibleTargetProductCodes, 5, DICT_FLAG_NONE); - ExitOnFailure(hr, "Failed to create unique target product codes."); - - // If the patches target a specific set of product/upgrade codes, search only those. This - // should be much faster than searching all packages on the machine. - if (pPackages->rgPatchTargetCodes) - { - for (DWORD i = 0; i < pPackages->cPatchTargetCodes; ++i) - { - BURN_PATCH_TARGETCODE* pTargetCode = pPackages->rgPatchTargetCodes + i; - - // If targeting a product, add the unique product code to the list. - if (BURN_PATCH_TARGETCODE_TYPE_PRODUCT == pTargetCode->type) - { - hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, pTargetCode->sczTargetCode, MSIINSTALLCONTEXT_NONE, prgPossibleTargetProducts, pcPossibleTargetProducts); - ExitOnFailure(hr, "Failed to add product code to possible target product codes."); - } - else if (BURN_PATCH_TARGETCODE_TYPE_UPGRADE == pTargetCode->type) - { - // Enumerate all unique related products to the target upgrade code. - for (DWORD iProduct = 0; SUCCEEDED(hr); ++iProduct) - { - hr = WiuEnumRelatedProducts(pTargetCode->sczTargetCode, iProduct, wzPossibleTargetProductCode); - if (SUCCEEDED(hr)) - { - hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode, MSIINSTALLCONTEXT_NONE, prgPossibleTargetProducts, pcPossibleTargetProducts); - ExitOnFailure(hr, "Failed to add upgrade product code to possible target product codes."); - } - else if (E_BADCONFIGURATION == hr) - { - // Skip product's with bad configuration and continue. - LogId(REPORT_STANDARD, MSG_DETECT_BAD_PRODUCT_CONFIGURATION, wzPossibleTargetProductCode); - - hr = S_OK; - } - } - - if (E_NOMOREITEMS == hr) - { - hr = S_OK; - } - ExitOnFailure(hr, "Failed to enumerate all products to patch related to upgrade code: %ls", pTargetCode->sczTargetCode); - } - else - { - // The element does not target a specific product. - fCheckAll = TRUE; - - break; - } - } - } - else - { - fCheckAll = TRUE; - } - - // One or more of the patches do not target a specific product so search everything on the machine. - if (fCheckAll) - { - for (DWORD iProduct = 0; SUCCEEDED(hr); ++iProduct) - { - MSIINSTALLCONTEXT context = MSIINSTALLCONTEXT_NONE; - - hr = WiuEnumProductsEx(NULL, NULL, MSIINSTALLCONTEXT_ALL, iProduct, wzPossibleTargetProductCode, &context, NULL, NULL); - if (SUCCEEDED(hr)) - { - hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode, context, prgPossibleTargetProducts, pcPossibleTargetProducts); - ExitOnFailure(hr, "Failed to add product code to search product codes."); - } - else if (E_BADCONFIGURATION == hr) - { - // Skip products with bad configuration and continue. - LogId(REPORT_STANDARD, MSG_DETECT_BAD_PRODUCT_CONFIGURATION, wzPossibleTargetProductCode); - - hr = S_OK; - } - } - - if (E_NOMOREITEMS == hr) - { - hr = S_OK; - } - ExitOnFailure(hr, "Failed to enumerate all products on the machine for patches applicability."); - } - -LExit: - ReleaseDict(sdUniquePossibleTargetProductCodes); - - return hr; -} - -static HRESULT AddPossibleTargetProduct( - __in STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes, - __in_z LPCWSTR wzPossibleTargetProductCode, - __in MSIINSTALLCONTEXT context, - __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts, - __inout DWORD* pcPossibleTargetProducts - ) -{ - HRESULT hr = S_OK; - LPWSTR pszLocalPackage = NULL; - - // Only add this possible target code if we haven't queried for it already. - if (E_NOTFOUND == DictKeyExists(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode)) - { - // If the install context is not known, ask the Windows Installer for it. If we can't get the context - // then bail. - if (MSIINSTALLCONTEXT_NONE == context) - { - hr = WiuEnumProductsEx(wzPossibleTargetProductCode, NULL, MSIINSTALLCONTEXT_ALL, 0, NULL, &context, NULL, NULL); - if (FAILED(hr)) - { - ExitFunction1(hr = S_OK); - } - } - - hr = DictAddKey(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode); - ExitOnFailure(hr, "Failed to add possible target code to unique product codes."); - - hr = MemEnsureArraySize(reinterpret_cast(prgPossibleTargetProducts), *pcPossibleTargetProducts + 1, sizeof(POSSIBLE_TARGETPRODUCT), 3); - ExitOnFailure(hr, "Failed to grow array of possible target products."); - - POSSIBLE_TARGETPRODUCT *const pPossibleTargetProduct = *prgPossibleTargetProducts + *pcPossibleTargetProducts; - - hr = ::StringCchCopyW(pPossibleTargetProduct->wzProductCode, countof(pPossibleTargetProduct->wzProductCode), wzPossibleTargetProductCode); - ExitOnFailure(hr, "Failed to copy possible target product code."); - - // Attempt to get the local package path so we can more quickly determine patch applicability later. - hr = WiuGetProductInfoEx(wzPossibleTargetProductCode, NULL, context, INSTALLPROPERTY_LOCALPACKAGE, &pszLocalPackage); - if (SUCCEEDED(hr)) - { - pPossibleTargetProduct->pszLocalPackage = pszLocalPackage; - pszLocalPackage = NULL; - } - else - { - // Will instead call MsiDeterminePatchSequence later. - hr = S_OK; - } - - pPossibleTargetProduct->context = context; - - ++(*pcPossibleTargetProducts); - } - -LExit: - ReleaseStr(pszLocalPackage); - - return hr; -} - -static HRESULT AddDetectedTargetProduct( - __in BURN_PACKAGE* pPackage, - __in DWORD dwOrder, - __in_z LPCWSTR wzProductCode, - __in MSIINSTALLCONTEXT context, - __out DWORD* pdwTargetProductIndex - ) -{ - HRESULT hr = S_OK; - BURN_MSPTARGETPRODUCT* pTargetProduct = NULL; - - *pdwTargetProductIndex = BURN_PACKAGE_INVALID_PATCH_INDEX; - - hr = MemEnsureArraySize(reinterpret_cast(&pPackage->Msp.rgTargetProducts), pPackage->Msp.cTargetProductCodes + 1, sizeof(BURN_MSPTARGETPRODUCT), 5); - ExitOnFailure(hr, "Failed to ensure enough target product codes were allocated."); - - pTargetProduct = pPackage->Msp.rgTargetProducts + pPackage->Msp.cTargetProductCodes; - - hr = ::StringCchCopyW(pTargetProduct->wzTargetProductCode, countof(pTargetProduct->wzTargetProductCode), wzProductCode); - ExitOnFailure(hr, "Failed to copy target product code."); - - pTargetProduct->context = context; - pTargetProduct->dwOrder = dwOrder; - - *pdwTargetProductIndex = pPackage->Msp.cTargetProductCodes; - ++pPackage->Msp.cTargetProductCodes; - -LExit: - return hr; -} - -static HRESULT AddMsiChainedPatch( - __in BURN_PACKAGE* pPackage, - __in BURN_PACKAGE* pMspPackage, - __in DWORD dwMspTargetProductIndex, - __out DWORD* pdwChainedPatchIndex - ) -{ - HRESULT hr = S_OK; - - hr = MemEnsureArraySize(reinterpret_cast(&pPackage->Msi.rgChainedPatches), pPackage->Msi.cChainedPatches + 1, sizeof(BURN_CHAINED_PATCH), 5); - ExitOnFailure(hr, "Failed to ensure enough chained patches were allocated."); - - BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + pPackage->Msi.cChainedPatches; - pChainedPatch->pMspPackage = pMspPackage; - pChainedPatch->dwMspTargetProductIndex = dwMspTargetProductIndex; - - *pdwChainedPatchIndex = pPackage->Msi.cChainedPatches; - ++pPackage->Msi.cChainedPatches; -LExit: - return hr; -} - -static HRESULT DeterminePatchChainedTarget( - __in BURN_PACKAGES* pPackages, - __in BURN_PACKAGE* pMspPackage, - __in LPCWSTR wzTargetProductCode, - __in DWORD dwMspTargetProductIndex - ) -{ - HRESULT hr = S_OK; - DWORD dwChainedPatchIndex = 0; - BURN_MSPTARGETPRODUCT* pTargetProduct = pMspPackage->Msp.rgTargetProducts + dwMspTargetProductIndex; - - for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage) - { - BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage; - - if (BURN_PACKAGE_TYPE_MSI == pPackage->type && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzTargetProductCode, -1, pPackage->Msi.sczProductCode, -1)) - { - pTargetProduct->pChainedTargetPackage = pPackage; - - hr = AddMsiChainedPatch(pPackage, pMspPackage, dwMspTargetProductIndex, &dwChainedPatchIndex); - ExitOnFailure(hr, "Failed to add chained patch."); - - for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) - { - BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + j; - if (pSlipstreamMsp->pMspPackage == pMspPackage) - { - AssertSz(BURN_PACKAGE_INVALID_PATCH_INDEX == pSlipstreamMsp->dwMsiChainedPatchIndex, "An MSP should only show up as a slipstreamed patch in an MSI once."); - pTargetProduct->fSlipstream = TRUE; - pSlipstreamMsp->dwMsiChainedPatchIndex = dwChainedPatchIndex; - break; - } - } - - break; - } - } - -LExit: - return hr; -} - -static HRESULT PlanTargetProduct( - __in BOOTSTRAPPER_DISPLAY display, - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BOOL fRollback, - __in BURN_PLAN* pPlan, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_ACTION_STATE actionState, - __in BURN_PACKAGE* pPackage, - __in BURN_MSPTARGETPRODUCT* pTargetProduct, - __in_opt HANDLE hCacheEvent - ) -{ - HRESULT hr = S_OK; - BURN_EXECUTE_ACTION* rgActions = fRollback ? pPlan->rgRollbackActions : pPlan->rgExecuteActions; - DWORD cActions = fRollback ? pPlan->cRollbackActions : pPlan->cExecuteActions; - BURN_EXECUTE_ACTION* pAction = NULL; - DWORD dwInsertSequence = 0; - - // Try to find another MSP action with the exact same action (install or uninstall) targeting - // the same product in the same machine context (per-user or per-machine). - for (DWORD i = 0; i < cActions; ++i) - { - pAction = rgActions + i; - - if (BURN_EXECUTE_ACTION_TYPE_MSP_TARGET == pAction->type && - pAction->mspTarget.action == actionState && - pAction->mspTarget.fPerMachineTarget == (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context) && - CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pAction->mspTarget.sczTargetProductCode, -1, pTargetProduct->wzTargetProductCode, -1)) - { - dwInsertSequence = i; - break; - } - - pAction = NULL; - } - - // If we didn't find an MSP target action already updating the product, create a new action. - if (!pAction) - { - if (fRollback) - { - hr = PlanAppendRollbackAction(pPlan, &pAction); - } - else - { - hr = PlanAppendExecuteAction(pPlan, &pAction); - } - ExitOnFailure(hr, "Failed to plan action for target product."); - - pAction->type = BURN_EXECUTE_ACTION_TYPE_MSP_TARGET; - pAction->mspTarget.action = actionState; - pAction->mspTarget.pPackage = pPackage; - pAction->mspTarget.fPerMachineTarget = (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context); - pAction->mspTarget.pChainedTargetPackage = pTargetProduct->pChainedTargetPackage; - pAction->mspTarget.fSlipstream = pTargetProduct->fSlipstream; - hr = StrAllocString(&pAction->mspTarget.sczTargetProductCode, pTargetProduct->wzTargetProductCode, 0); - ExitOnFailure(hr, "Failed to copy target product code."); - - hr = MsiEngineCalculateInstallUiLevel(display, pUserExperience, pPackage->sczId, !fRollback, pAction->mspTarget.action, - &pAction->mspTarget.actionMsiProperty, &pAction->mspTarget.uiLevel, &pAction->mspTarget.fDisableExternalUiHandler); - ExitOnFailure(hr, "Failed to get msp ui options."); - - // If this is a per-machine target product, then the plan needs to be per-machine as well. - if (pAction->mspTarget.fPerMachineTarget) - { - pPlan->fPerMachine = TRUE; - } - - LoggingSetPackageVariable(pPackage, pAction->mspTarget.sczTargetProductCode, fRollback, pLog, pVariables, &pAction->mspTarget.sczLogPath); // ignore errors. - } - else - { - if (!fRollback && hCacheEvent) - { - // Since a previouse MSP target action is being updated with the new MSP, - // insert a wait syncpoint to before this action since we need to cache the current MSI before using it. - BURN_EXECUTE_ACTION* pWaitSyncPointAction = NULL; - hr = PlanInsertExecuteAction(dwInsertSequence, pPlan, &pWaitSyncPointAction); - ExitOnFailure(hr, "Failed to insert execute action."); - - pWaitSyncPointAction->type = BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT; - pWaitSyncPointAction->syncpoint.hEvent = hCacheEvent; - - // Since we inserted an action before the MSP target action that we will be updating, need to update the pointer. - pAction = pPlan->rgExecuteActions + (dwInsertSequence + 1); - } - } - - // Add our target product to the array and sort based on their order determined during detection. - hr = MemEnsureArraySize(reinterpret_cast(&pAction->mspTarget.rgOrderedPatches), pAction->mspTarget.cOrderedPatches + 1, sizeof(BURN_ORDERED_PATCHES), 2); - ExitOnFailure(hr, "Failed grow array of ordered patches."); - - pAction->mspTarget.rgOrderedPatches[pAction->mspTarget.cOrderedPatches].pTargetProduct = pTargetProduct; - pAction->mspTarget.rgOrderedPatches[pAction->mspTarget.cOrderedPatches].pPackage = pPackage; - ++pAction->mspTarget.cOrderedPatches; - - // Insertion sort to keep the patches ordered. - for (DWORD i = pAction->mspTarget.cOrderedPatches - 1; i > 0; --i) - { - if (pAction->mspTarget.rgOrderedPatches[i].pTargetProduct->dwOrder < pAction->mspTarget.rgOrderedPatches[i - 1].pTargetProduct->dwOrder) - { - BURN_ORDERED_PATCHES temp = pAction->mspTarget.rgOrderedPatches[i - 1]; - pAction->mspTarget.rgOrderedPatches[i - 1] = pAction->mspTarget.rgOrderedPatches[i]; - pAction->mspTarget.rgOrderedPatches[i] = temp; - } - else // no swap necessary, we're done. - { - break; - } - } - -LExit: - return hr; -} diff --git a/src/engine/mspengine.h b/src/engine/mspengine.h deleted file mode 100644 index 79998030..00000000 --- a/src/engine/mspengine.h +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -// constants - - -// structures - - -// typedefs - - -// function declarations - -HRESULT MspEngineParsePackageFromXml( - __in IXMLDOMNode* pixnBundle, - __in BURN_PACKAGE* pPackage - ); -void MspEnginePackageUninitialize( - __in BURN_PACKAGE* pPackage - ); -HRESULT MspEngineDetectInitialize( - __in BURN_PACKAGES* pPackages - ); -HRESULT MspEngineAddDetectedTargetProduct( - __in BURN_PACKAGES* pPackages, - __in BURN_PACKAGE* pPackage, - __in DWORD dwOrder, - __in_z LPCWSTR wzProductCode, - __in MSIINSTALLCONTEXT context - ); -HRESULT MspEngineAddMissingSlipstreamTarget( - __in BURN_PACKAGE* pMsiPackage, - __in BURN_SLIPSTREAM_MSP* pSlipstreamMsp - ); -HRESULT MspEngineDetectPackage( - __in BURN_PACKAGE* pPackage, - __in BURN_USER_EXPERIENCE* pUserExperience - ); -HRESULT MspEnginePlanInitializePackage( - __in BURN_PACKAGE* pPackage, - __in BURN_USER_EXPERIENCE* pUserExperience - ); -HRESULT MspEnginePlanCalculatePackage( - __in BURN_PACKAGE* pPackage, - __in BOOL fInsideMsiTransaction - ); -HRESULT MspEnginePlanAddPackage( - __in BOOTSTRAPPER_DISPLAY display, - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BURN_PACKAGE* pPackage, - __in BURN_PLAN* pPlan, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in_opt HANDLE hCacheEvent - ); -HRESULT MspEngineExecutePackage( - __in_opt HWND hwndParent, - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_VARIABLES* pVariables, - __in BOOL fRollback, - __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, - __in LPVOID pvContext, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ); -void MspEngineUpdateInstallRegistrationState( - __in BURN_EXECUTE_ACTION* pAction, - __in HRESULT hrExecute, - __in BOOL fInsideMsiTransaction - ); -void MspEngineFinalizeInstallRegistrationState( - __in BURN_PACKAGE* pPackage - ); - - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/msuengine.cpp b/src/engine/msuengine.cpp deleted file mode 100644 index 6003123b..00000000 --- a/src/engine/msuengine.cpp +++ /dev/null @@ -1,529 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// constants - -#define WU_S_REBOOT_REQUIRED 0x00240005L -#define WU_S_ALREADY_INSTALLED 0x00240006L - - -// function definitions -static HRESULT EnsureWUServiceEnabled( - __in BOOL fStopWusaService, - __out SC_HANDLE* pschWu, - __out BOOL* pfPreviouslyDisabled - ); -static HRESULT SetServiceStartType( - __in SC_HANDLE sch, - __in DWORD stratType - ); -static HRESULT StopWUService( - __in SC_HANDLE schWu - ); - - -extern "C" HRESULT MsuEngineParsePackageFromXml( - __in IXMLDOMNode* pixnMsuPackage, - __in BURN_PACKAGE* pPackage - ) -{ - HRESULT hr = S_OK; - - // @KB - hr = XmlGetAttributeEx(pixnMsuPackage, L"KB", &pPackage->Msu.sczKB); - ExitOnFailure(hr, "Failed to get @KB."); - - // @DetectCondition - hr = XmlGetAttributeEx(pixnMsuPackage, L"DetectCondition", &pPackage->Msu.sczDetectCondition); - ExitOnFailure(hr, "Failed to get @DetectCondition."); - -LExit: - return hr; -} - -extern "C" void MsuEnginePackageUninitialize( - __in BURN_PACKAGE* pPackage - ) -{ - ReleaseNullStr(pPackage->Msu.sczKB); - ReleaseNullStr(pPackage->Msu.sczDetectCondition); -} - -extern "C" HRESULT MsuEngineDetectPackage( - __in BURN_PACKAGE* pPackage, - __in BURN_VARIABLES* pVariables - ) -{ - HRESULT hr = S_OK; - BOOL fDetected = FALSE; - - // evaluate detect condition - if (pPackage->Msu.sczDetectCondition && *pPackage->Msu.sczDetectCondition) - { - hr = ConditionEvaluate(pVariables, pPackage->Msu.sczDetectCondition, &fDetected); - ExitOnFailure(hr, "Failed to evaluate MSU package detect condition."); - } - - // update detect state - pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT; - - if (pPackage->fCanAffectRegistration) - { - pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - } - -LExit: - return hr; -} - -// -// PlanCalculate - calculates the execute and rollback state for the requested package state. -// -extern "C" HRESULT MsuEnginePlanCalculatePackage( - __in BURN_PACKAGE* pPackage - ) -{ - HRESULT hr = S_OK; - BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; - BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - BOOL fAllowUninstall = FALSE; - - // We can only uninstall MSU packages if they have a KB and we are on Win7 or newer. - fAllowUninstall = pPackage->Msu.sczKB && *pPackage->Msu.sczKB && ::IsWindows7OrGreater(); - - // execute action - switch (pPackage->currentState) - { - case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: - switch (pPackage->requested) - { - case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_REPAIR: - execute = BOOTSTRAPPER_ACTION_STATE_NONE; - break; - - case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_CACHE: - execute = fAllowUninstall && pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; - break; - - case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: - execute = fAllowUninstall ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; - break; - - default: - execute = BOOTSTRAPPER_ACTION_STATE_NONE; - break; - } - break; - - case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: - switch (pPackage->requested) - { - case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_REPAIR: - execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; - break; - - default: - execute = BOOTSTRAPPER_ACTION_STATE_NONE; - break; - } - break; - - default: - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Invalid package state."); - } - - // Calculate the rollback action if there is an execute action. - if (BOOTSTRAPPER_ACTION_STATE_NONE != execute) - { - switch (pPackage->currentState) - { - case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: - switch (pPackage->requested) - { - case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_ABSENT: - rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL; - break; - - default: - rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - break; - } - break; - - case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: - switch (pPackage->requested) - { - case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; - case BOOTSTRAPPER_REQUEST_STATE_REPAIR: - rollback = fAllowUninstall ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; - break; - - default: - rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - break; - } - break; - - default: - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Invalid package expected state."); - } - } - - // return values - pPackage->execute = execute; - pPackage->rollback = rollback; - -LExit: - return hr; -} - -// -// PlanAdd - adds the calculated execute and rollback actions for the package. -// -extern "C" HRESULT MsuEnginePlanAddPackage( - __in BURN_PACKAGE* pPackage, - __in BURN_PLAN* pPlan, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in HANDLE hCacheEvent - ) -{ - HRESULT hr = S_OK; - BURN_EXECUTE_ACTION* pAction = NULL; - - // add wait for cache - if (hCacheEvent) - { - hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent); - ExitOnFailure(hr, "Failed to plan package cache syncpoint"); - } - - hr = DependencyPlanPackage(NULL, pPackage, pPlan); - ExitOnFailure(hr, "Failed to plan package dependency actions."); - - // add execute action - if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute) - { - hr = PlanAppendExecuteAction(pPlan, &pAction); - ExitOnFailure(hr, "Failed to append execute action."); - - pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE; - pAction->msuPackage.pPackage = pPackage; - pAction->msuPackage.action = pPackage->execute; - - LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors. - } - - // add rollback action - if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback) - { - hr = PlanAppendRollbackAction(pPlan, &pAction); - ExitOnFailure(hr, "Failed to append rollback action."); - - pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE; - pAction->msuPackage.pPackage = pPackage; - pAction->msuPackage.action = pPackage->rollback; - - LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors. - } - -LExit: - return hr; -} - -extern "C" HRESULT MsuEngineExecutePackage( - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_VARIABLES* pVariables, - __in BOOL fRollback, - __in BOOL fStopWusaService, - __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, - __in LPVOID pvContext, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ) -{ - HRESULT hr = S_OK; - int nResult = IDNOACTION; - LPWSTR sczCachedDirectory = NULL; - LPWSTR sczMsuPath = NULL; - LPWSTR sczWindowsPath = NULL; - LPWSTR sczSystemPath = NULL; - LPWSTR sczWusaPath = NULL; - LPWSTR sczCommand = NULL; - SC_HANDLE schWu = NULL; - BOOL fWuWasDisabled = FALSE; - STARTUPINFOW si = { }; - PROCESS_INFORMATION pi = { }; - GENERIC_EXECUTE_MESSAGE message = { }; - DWORD dwExitCode = 0; - BOOL fUseSysNativePath = FALSE; - BURN_PACKAGE* pPackage = pExecuteAction->msuPackage.pPackage; - BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload; - -#if !defined(_WIN64) - hr = ProcWow64(::GetCurrentProcess(), &fUseSysNativePath); - ExitOnFailure(hr, "Failed to determine WOW64 status."); -#endif - - *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; - - // get wusa.exe path - if (fUseSysNativePath) - { - hr = PathGetKnownFolder(CSIDL_WINDOWS, &sczWindowsPath); - ExitOnFailure(hr, "Failed to find Windows directory."); - - hr = PathConcat(sczWindowsPath, L"SysNative\\", &sczSystemPath); - ExitOnFailure(hr, "Failed to append SysNative directory."); - } - else - { - hr = PathGetKnownFolder(CSIDL_SYSTEM, &sczSystemPath); - ExitOnFailure(hr, "Failed to find System32 directory."); - } - - hr = PathConcat(sczSystemPath, L"wusa.exe", &sczWusaPath); - ExitOnFailure(hr, "Failed to allocate WUSA.exe path."); - - // build command - switch (pExecuteAction->msuPackage.action) - { - case BOOTSTRAPPER_ACTION_STATE_INSTALL: - // get cached MSU path - hr = CacheGetCompletedPath(TRUE, pPackage->sczCacheId, &sczCachedDirectory); - ExitOnFailure(hr, "Failed to get cached path for package: %ls", pPackage->sczId); - - // Best effort to set the execute package cache folder variable. - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE); - - hr = PathConcat(sczCachedDirectory, pPackagePayload->sczFilePath, &sczMsuPath); - ExitOnFailure(hr, "Failed to build MSU path."); - - // format command - hr = StrAllocFormatted(&sczCommand, L"\"%ls\" \"%ls\" /quiet /norestart", sczWusaPath, sczMsuPath); - ExitOnFailure(hr, "Failed to format MSU install command."); - break; - - case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: - // format command - hr = StrAllocFormatted(&sczCommand, L"\"%ls\" /uninstall /kb:%ls /quiet /norestart", sczWusaPath, pPackage->Msu.sczKB); - ExitOnFailure(hr, "Failed to format MSU uninstall command."); - break; - - default: - hr = E_UNEXPECTED; - ExitOnFailure(hr, "Failed to get action arguments for MSU package."); - } - - if (pExecuteAction->msuPackage.sczLogPath && *pExecuteAction->msuPackage.sczLogPath) - { - hr = StrAllocConcat(&sczCommand, L" /log:", 0); - ExitOnFailure(hr, "Failed to append log switch to MSU command-line."); - - hr = StrAllocConcat(&sczCommand, pExecuteAction->msuPackage.sczLogPath, 0); - ExitOnFailure(hr, "Failed to append log path to MSU command-line."); - } - - LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, LoggingActionStateToString(pExecuteAction->msuPackage.action), sczMsuPath ? sczMsuPath : pPackage->Msu.sczKB, sczCommand); - - hr = EnsureWUServiceEnabled(fStopWusaService, &schWu, &fWuWasDisabled); - ExitOnFailure(hr, "Failed to ensure WU service was enabled to install MSU package."); - - // create process - si.cb = sizeof(si); - if (!::CreateProcessW(sczWusaPath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) - { - ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", sczWusaPath); - } - - do - { - message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; - message.dwAllowedResults = MB_OKCANCEL; - message.progress.dwPercentage = 50; - nResult = pfnGenericMessageHandler(&message, pvContext); - hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); - ExitOnRootFailure(hr, "Bootstrapper application aborted during MSU progress."); - - // wait for process to terminate - hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode); - if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr) - { - ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", sczWusaPath); - } - } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr); - - // get process exit code - if (!::GetExitCodeProcess(pi.hProcess, &dwExitCode)) - { - ExitWithLastError(hr, "Failed to get process exit code."); - } - - // We'll normalize the restart required error code from wusa.exe just in case. Most likely - // that on reboot we'll actually get WU_S_REBOOT_REQUIRED. - if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast(dwExitCode)) - { - dwExitCode = ERROR_SUCCESS_REBOOT_REQUIRED; - } - - // handle exit code - switch (dwExitCode) - { - case S_OK: __fallthrough; - case S_FALSE: __fallthrough; - case WU_S_ALREADY_INSTALLED: - hr = S_OK; - break; - - case ERROR_SUCCESS_REBOOT_REQUIRED: __fallthrough; - case WU_S_REBOOT_REQUIRED: - *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; - hr = S_OK; - break; - - default: - hr = static_cast(dwExitCode); - break; - } - -LExit: - ReleaseStr(sczCachedDirectory); - ReleaseStr(sczMsuPath); - ReleaseStr(sczSystemPath); - ReleaseStr(sczWindowsPath); - ReleaseStr(sczWusaPath); - ReleaseStr(sczCommand); - - ReleaseHandle(pi.hProcess); - ReleaseHandle(pi.hThread); - - if (fWuWasDisabled) - { - SetServiceStartType(schWu, SERVICE_DISABLED); - } - - // Best effort to clear the execute package cache folder variable. - VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE); - - return hr; -} - -extern "C" void MsuEngineUpdateInstallRegistrationState( - __in BURN_EXECUTE_ACTION* pAction, - __in HRESULT hrExecute - ) -{ - BURN_PACKAGE* pPackage = pAction->msuPackage.pPackage; - - if (FAILED(hrExecute) || !pPackage->fCanAffectRegistration) - { - ExitFunction(); - } - - if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->msuPackage.action) - { - pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - } - else - { - pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } - -LExit: - return; -} - -static HRESULT EnsureWUServiceEnabled( - __in BOOL fStopWusaService, - __out SC_HANDLE* pschWu, - __out BOOL* pfPreviouslyDisabled - ) -{ - HRESULT hr = S_OK; - SC_HANDLE schSCM = NULL; - SC_HANDLE schWu = NULL; - SERVICE_STATUS serviceStatus = { }; - QUERY_SERVICE_CONFIGW* pConfig = NULL; - - schSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); - ExitOnNullWithLastError(schSCM, hr, "Failed to open service control manager."); - - schWu = ::OpenServiceW(schSCM, L"wuauserv", SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_STOP ); - ExitOnNullWithLastError(schWu, hr, "Failed to open WU service."); - - if (!::QueryServiceStatus(schWu, &serviceStatus) ) - { - ExitWithLastError(hr, "Failed to query status of WU service."); - } - - // Stop service if requested to. - if (SERVICE_STOPPED != serviceStatus.dwCurrentState && fStopWusaService) - { - hr = StopWUService(schWu); - } - - // If the service is not running then it might be disabled so let's check. - if (SERVICE_RUNNING != serviceStatus.dwCurrentState) - { - hr = SvcQueryConfig(schWu, &pConfig); - ExitOnFailure(hr, "Failed to read configuration for WU service."); - - // If WU is disabled, change it to a demand start service (but touch nothing else). - if (SERVICE_DISABLED == pConfig->dwStartType) - { - hr = SetServiceStartType(schWu, SERVICE_DEMAND_START); - ExitOnFailure(hr, "Failed to mark WU service to start on demand."); - - *pfPreviouslyDisabled = TRUE; - } - } - - *pschWu = schWu; - schWu = NULL; - -LExit: - ReleaseMem(pConfig); - ReleaseServiceHandle(schWu); - ReleaseServiceHandle(schSCM); - - return hr; -} - -static HRESULT SetServiceStartType( - __in SC_HANDLE sch, - __in DWORD startType - ) -{ - HRESULT hr = S_OK; - - if (!::ChangeServiceConfigW(sch, SERVICE_NO_CHANGE, startType, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) - { - ExitWithLastError(hr, "Failed to set service start type."); - } - -LExit: - return hr; -} - -static HRESULT StopWUService( - __in SC_HANDLE schWu - ) -{ - HRESULT hr = S_OK; - SERVICE_STATUS serviceStatus = { }; - - if(!::ControlService(schWu, SERVICE_CONTROL_STOP, &serviceStatus)) - { - ExitWithLastError(hr, "Failed to stop wusa service."); - } - -LExit: - return hr; -} diff --git a/src/engine/msuengine.h b/src/engine/msuengine.h deleted file mode 100644 index fda7a5ab..00000000 --- a/src/engine/msuengine.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -// function declarations - -HRESULT MsuEngineParsePackageFromXml( - __in IXMLDOMNode* pixnMsiPackage, - __in BURN_PACKAGE* pPackage - ); -void MsuEnginePackageUninitialize( - __in BURN_PACKAGE* pPackage - ); -HRESULT MsuEngineDetectPackage( - __in BURN_PACKAGE* pPackage, - __in BURN_VARIABLES* pVariables - ); -HRESULT MsuEnginePlanCalculatePackage( - __in BURN_PACKAGE* pPackage - ); -HRESULT MsuEnginePlanAddPackage( - __in BURN_PACKAGE* pPackage, - __in BURN_PLAN* pPlan, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in HANDLE hCacheEvent - ); -HRESULT MsuEngineExecutePackage( - __in BURN_EXECUTE_ACTION* pExecuteAction, - __in BURN_VARIABLES* pVariables, - __in BOOL fRollback, - __in BOOL fStopWusaService, - __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, - __in LPVOID pvContext, - __out BOOTSTRAPPER_APPLY_RESTART* pRestart - ); -void MsuEngineUpdateInstallRegistrationState( - __in BURN_EXECUTE_ACTION* pAction, - __in HRESULT hrExecute - ); - - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/netfxchainer.cpp b/src/engine/netfxchainer.cpp deleted file mode 100644 index 4e7a7720..00000000 --- a/src/engine/netfxchainer.cpp +++ /dev/null @@ -1,418 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -static VOID DestroyNetFxChainer( - __in NetFxChainer* pChainer - ) -{ - if (pChainer) - { - ReleaseHandle(pChainer->hSection); - ReleaseHandle(pChainer->hEventChaineeSend); - ReleaseHandle(pChainer->hEventChainerSend); - ReleaseHandle(pChainer->hMutex); - - if (pChainer->pData) - { - ::UnmapViewOfFile(pChainer->pData); - } - - MemFree(pChainer); - } -} - -static HRESULT CreateNetFxChainer( - __in LPCWSTR wzSectionName, - __in LPCWSTR wzEventName, - __out NetFxChainer** ppChainer - ) -{ - HRESULT hr = S_OK; - LPWSTR sczName = NULL; - NetFxChainer* pChainer = NULL; - - pChainer = (NetFxChainer*)MemAlloc(sizeof(NetFxChainer), TRUE); - ExitOnNull(pChainer, hr, E_OUTOFMEMORY, "Failed to allocate memory for NetFxChainer struct."); - - pChainer->hEventChaineeSend = ::CreateEvent(NULL, FALSE, FALSE, wzEventName); - ExitOnNullWithLastError(pChainer->hEventChaineeSend, hr, "Failed to create event: %ls", wzEventName); - - hr = StrAllocFormatted(&sczName, L"%ls_send", wzEventName); - ExitOnFailure(hr, "failed to allocate memory for event name"); - - pChainer->hEventChainerSend = ::CreateEvent(NULL, FALSE, FALSE, sczName); - ExitOnNullWithLastError(pChainer->hEventChainerSend, hr, "Failed to create event: %ls", sczName); - - hr = StrAllocFormatted(&sczName, L"%ls_mutex", wzEventName); - ExitOnFailure(hr, "failed to allocate memory for mutex name"); - - // Create the mutex, we initially own - pChainer->hMutex = ::CreateMutex(NULL, TRUE, sczName); - ExitOnNullWithLastError(pChainer->hMutex, hr, "Failed to create mutex: %ls", sczName); - - pChainer->hSection = ::CreateFileMapping(INVALID_HANDLE_VALUE, - NULL, // security attributes - PAGE_READWRITE, - 0, // high-order DWORD of maximum size - NETFXDATA_SIZE, // low-order DWORD of maximum size - wzSectionName); - ExitOnNullWithLastError(pChainer->hSection, hr, "Failed to memory map cabinet file: %ls", wzSectionName); - - pChainer->pData = reinterpret_cast(::MapViewOfFile(pChainer->hSection, - FILE_MAP_WRITE, - 0, 0, // offsets - 0 // map entire file - )); - ExitOnNullWithLastError(pChainer->pData, hr, "Failed to MapViewOfFile for %ls.", wzSectionName); - - // Initialize the shared memory - hr = ::StringCchCopyW(pChainer->pData->szEventName, countof(pChainer->pData->szEventName), wzEventName); - ExitOnFailure(hr, "failed to copy event name to shared memory structure."); - pChainer->pData->downloadFinished = false; - pChainer->pData->downloadSoFar = 0; - pChainer->pData->hrDownloadFinished = E_PENDING; - pChainer->pData->downloadAbort = false; - pChainer->pData->installFinished = false; - pChainer->pData->installSoFar = 0; - pChainer->pData->hrInstallFinished = E_PENDING; - pChainer->pData->installAbort = false; - pChainer->pData->hrInternalError = S_OK; - pChainer->pData->version = NETFXDATA_VERSION; - pChainer->pData->messageCode = 0; - pChainer->pData->messageResponse = 0; - pChainer->pData->messageDataLength = 0; - - // Done with initialization, allow others to access. - ::ReleaseMutex(pChainer->hMutex); - - *ppChainer = pChainer; - pChainer = NULL; - -LExit: - ReleaseStr(sczName); - - if (pChainer) - { - // Something failed, release the mutex and destroy the object - if (pChainer->hMutex) - { - ::ReleaseMutex(pChainer->hMutex); - } - - DestroyNetFxChainer(pChainer); - } - - return hr; -} - - -static VOID NetFxAbort( - __in NetFxChainer* pChainer - ) -{ - ::WaitForSingleObject(pChainer->hMutex, INFINITE); - - pChainer->pData->downloadAbort = true; - pChainer->pData->installAbort = true; - - ::ReleaseMutex(pChainer->hMutex); - - ::SetEvent(pChainer->hEventChainerSend); -} - -static BYTE NetFxGetProgress( - __in NetFxChainer* pChainer - ) -{ - BYTE bProgress = 0; - ::WaitForSingleObject(pChainer->hMutex, INFINITE); - - bProgress = (pChainer->pData->installSoFar + pChainer->pData->downloadSoFar) / 2; - - ::ReleaseMutex(pChainer->hMutex); - - return bProgress; -} - -static HRESULT NetFxGetMessage( - __in NetFxChainer* pChainer, - __out DWORD* pdwMessage, - __out LPVOID* ppBuffer, - __out DWORD* pdwBufferSize - ) -{ - HRESULT hr = S_OK; - ::WaitForSingleObject(pChainer->hMutex, INFINITE); - - *pdwMessage = pChainer->pData->messageCode; - *ppBuffer = NULL; - *pdwBufferSize = 0; - - if (NETFX_NO_MESSAGE != *pdwMessage) - { - *ppBuffer = MemAlloc(pChainer->pData->messageDataLength, TRUE); - ExitOnNull(*ppBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for message data"); - - memcpy(*ppBuffer, pChainer->pData->messageData, pChainer->pData->messageDataLength); - *pdwBufferSize = pChainer->pData->messageDataLength; - } - -LExit: - ::ReleaseMutex(pChainer->hMutex); - - return hr; -} - -static void NetFxRespond( - __in NetFxChainer* pChainer, - __in DWORD dwResponse - ) -{ - ::WaitForSingleObject(pChainer->hMutex, INFINITE); - - pChainer->pData->messageCode = NETFX_NO_MESSAGE; - pChainer->pData->messageResponse = dwResponse; - if (IDCANCEL == dwResponse) - { - pChainer->pData->downloadAbort = true; - pChainer->pData->installAbort = true; - } - - ::ReleaseMutex(pChainer->hMutex); - - ::SetEvent(pChainer->hEventChainerSend); -} - -static HRESULT NetFxGetResult( - __in NetFxChainer* pChainer, - __out HRESULT* phrInternalError - ) -{ - HRESULT hr = S_OK; - ::WaitForSingleObject(pChainer->hMutex, INFINITE); - - hr = pChainer->pData->hrInstallFinished; - - if (FAILED(pChainer->pData->hrDownloadFinished) && // Download failed - (S_OK == hr || E_ABORT == hr)) // Install succeeded or was aborted - { - hr = pChainer->pData->hrDownloadFinished; - } - - if (phrInternalError) - { - *phrInternalError = pChainer->pData->hrInternalError; - } - - ::ReleaseMutex(pChainer->hMutex); - - return hr; -} - -static HRESULT OnNetFxFilesInUse( - __in NetFxChainer* pNetfxChainer, - __in NetFxCloseApplications* pCloseApps, - __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, - __in LPVOID pvContext - ) -{ - HRESULT hr = S_OK; - DWORD cFiles = 0; - LPWSTR* rgwzFiles = NULL; - GENERIC_EXECUTE_MESSAGE message = { }; - DWORD dwResponse = 0; - - cFiles = pCloseApps->dwApplicationsSize; - rgwzFiles = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cFiles, TRUE); - ExitOnNull(rgwzFiles, hr, E_OUTOFMEMORY, "Failed to allocate buffer."); - - for (DWORD i = 0; i < pCloseApps->dwApplicationsSize; ++i) - { - rgwzFiles[i] = pCloseApps->applications[i].szName; - } - - // send message - message.type = GENERIC_EXECUTE_MESSAGE_FILES_IN_USE; - message.dwAllowedResults = MB_ABORTRETRYIGNORE; - message.filesInUse.cFiles = cFiles; - message.filesInUse.rgwzFiles = (LPCWSTR*)rgwzFiles; - dwResponse = (DWORD)pfnMessageHandler(&message, pvContext); - - NetFxRespond(pNetfxChainer, dwResponse); - -LExit: - ReleaseMem(rgwzFiles); - - return hr; -} - -static HRESULT OnNetFxProgress( - __in NetFxChainer* pNetfxChainer, - __in BYTE bProgress, - __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, - __in LPVOID pvContext - ) -{ - GENERIC_EXECUTE_MESSAGE message = { }; - DWORD dwResponse = 0; - - // send message - message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; - message.dwAllowedResults = MB_OKCANCEL; - message.progress.dwPercentage = 100 * (DWORD)bProgress / BYTE_MAX; - dwResponse = (DWORD)pfnMessageHandler(&message, pvContext); - - if (IDCANCEL == dwResponse) - { - NetFxAbort(pNetfxChainer); - } - - return S_OK; -} - -static HRESULT OnNetFxError( - __in NetFxChainer* /*pNetfxChainer*/, - __in HRESULT hrError, - __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, - __in LPVOID pvContext - ) -{ - GENERIC_EXECUTE_MESSAGE message = { }; - DWORD dwResponse = 0; - - // send message - message.type = GENERIC_EXECUTE_MESSAGE_ERROR; - message.dwAllowedResults = MB_OK; - message.error.dwErrorCode = hrError; - message.error.wzMessage = NULL; - dwResponse = (DWORD)pfnMessageHandler(&message, pvContext); - - return S_OK; -} - -static HRESULT ProcessNetFxMessage( - __in NetFxChainer* pNetfxChainer, - __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, - __in LPVOID pvContext - ) -{ - HRESULT hr = S_OK; - DWORD dwMessage = NETFX_NO_MESSAGE; - DWORD dwBufferSize = 0; - LPVOID pBuffer = NULL; - - // send progress - hr = OnNetFxProgress(pNetfxChainer, NetFxGetProgress(pNetfxChainer), pfnGenericMessageHandler, pvContext); - ExitOnFailure(hr, "Failed to send progress from netfx chainer."); - - // Check for message - hr = NetFxGetMessage(pNetfxChainer, &dwMessage, &pBuffer, &dwBufferSize); - ExitOnFailure(hr, "Failed to get message from netfx chainer."); - - switch(dwMessage) - { - case NETFX_CLOSE_APPS: - hr = OnNetFxFilesInUse(pNetfxChainer, (NetFxCloseApplications*)pBuffer, pfnGenericMessageHandler, pvContext); - ExitOnFailure(hr, "Failed to send files in use message from netfx chainer."); - break; - - default: - // No message we understand. - break; - } - -LExit: - ReleaseMem(pBuffer); - - return hr; -} - -extern "C" HRESULT NetFxRunChainer( - __in LPCWSTR wzExecutablePath, - __in LPCWSTR wzArguments, - __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, - __in LPVOID pvContext, - __out DWORD* pdwExitCode - ) -{ - HRESULT hr = S_OK; - DWORD er = 0; - WCHAR wzGuid[GUID_STRING_LENGTH]; - LPWSTR sczEventName = NULL; - LPWSTR sczSectionName = NULL; - LPWSTR sczCommand = NULL; - NetFxChainer* pNetfxChainer = NULL; - STARTUPINFOW si = { }; - PROCESS_INFORMATION pi = { }; - HRESULT hrInternalError = 0; - - // Create the unique name suffix. - hr = GuidFixedCreate(wzGuid); - ExitOnRootFailure(hr, "Failed to create netfx chainer guid."); - - hr = StrAllocFormatted(&sczSectionName, L"NetFxSection.%ls", wzGuid); - ExitOnFailure(hr, "Failed to allocate section name."); - - hr = StrAllocFormatted(&sczEventName, L"NetFxEvent.%ls", wzGuid); - ExitOnFailure(hr, "Failed to allocate event name."); - - hr = CreateNetFxChainer(sczSectionName, sczEventName, &pNetfxChainer); - ExitOnFailure(hr, "Failed to create netfx chainer."); - - hr = StrAllocFormattedSecure(&sczCommand, L"%ls /pipe %ls", wzArguments, sczSectionName); - ExitOnFailure(hr, "Failed to allocate netfx chainer arguments."); - - si.cb = sizeof(si); - if (!::CreateProcessW(wzExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) - { - ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", wzExecutablePath); - } - - HANDLE handles[2] = { pi.hProcess, pNetfxChainer->hEventChaineeSend }; - - for (;;) - { - er = ::WaitForMultipleObjects(2, handles, FALSE, 100); - if (WAIT_OBJECT_0 == er) - { - // Process has exited - *pdwExitCode = NetFxGetResult(pNetfxChainer, &hrInternalError); - if (E_PENDING == *pdwExitCode) - { - if (!::GetExitCodeProcess(pi.hProcess, pdwExitCode)) - { - ExitWithLastError(hr, "Failed to get netfx return code."); - } - } - else if (FAILED(hrInternalError)) - { - // push internal error message - OnNetFxError(pNetfxChainer, hrInternalError, pfnGenericMessageHandler, pvContext); - ExitOnFailure(hr, "Failed to send internal error message from netfx chainer."); - } - - break; - } - else if (WAIT_OBJECT_0 + 1 == er) - { - // Chainee has notified us of a change. - hr = ProcessNetFxMessage(pNetfxChainer, pfnGenericMessageHandler, pvContext); - ExitOnFailure(hr, "Failed to process netfx chainer message."); - } - else if (WAIT_FAILED == er) - { - ExitWithLastError(hr, "Failed to wait for netfx chainer process to complete"); - } - } - -LExit: - ReleaseStr(sczSectionName); - ReleaseStr(sczEventName); - StrSecureZeroFreeString(sczCommand); - DestroyNetFxChainer(pNetfxChainer); - ReleaseHandle(pi.hThread); - ReleaseHandle(pi.hProcess); - - return hr; -} diff --git a/src/engine/netfxchainer.h b/src/engine/netfxchainer.h deleted file mode 100644 index 7d3aff1c..00000000 --- a/src/engine/netfxchainer.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - -struct NetFxDataStructure -{ - bool downloadFinished; // download done yet? - bool installFinished; // install done yet? - bool downloadAbort; // set downloader to abort - bool installAbort; // set installer to abort - HRESULT hrDownloadFinished; // resultant HRESULT for download - HRESULT hrInstallFinished; // resultant HRESULT for install - HRESULT hrInternalError; - WCHAR szCurrentItemStep[MAX_PATH]; - BYTE downloadSoFar; // download progress 0 - 255 (0 to 100% done) - BYTE installSoFar; // install progress 0 - 255 (0 to 100% done) - WCHAR szEventName[MAX_PATH]; // event that chainer 'creates' and chainee 'opens'to sync communications - - BYTE version; // version of the data structure, set by chainer. - - DWORD messageCode; // current message being sent by the chainee, 0 if no message is active - DWORD messageResponse; // chainer's response to current message, 0 if not yet handled - DWORD messageDataLength; // length of the m_messageData field in bytes - BYTE messageData[1]; // variable length buffer, content depends on m_messageCode -}; - -struct NetFxChainer -{ - HANDLE hSection; - - HANDLE hEventChaineeSend; - HANDLE hEventChainerSend; - HANDLE hMutex; - - NetFxDataStructure* pData; - DWORD dwDataSize; -}; - -#define NETFXDATA_SIZE 65536 - -#define NETFXDATA_VERSION 1 - -#define NETFX_MESSAGE(version, defaultResponse, messageCode) \ - ((((DWORD)version & 0xFF) << 24) | (((DWORD)defaultResponse & 0xFF) << 16) | ((DWORD)messageCode & 0xFFFF)) -#define NETFX_MESSAGE_CODE(messageId) \ - (messageId & 0xFFFF) -#define NETFX_MESSAGE_DEFAULT_RESPONSE(messageId) \ - ((messageId >> 16) & 0xFF) -#define NETFX_MESSAGE_VERSION(messageId) \ - ((messageId >>24) & 0xFF) - -#define NETFX_NO_MESSAGE 0 - - -//------------------------------------------------------------------------------ -// NETFX_CLOSE_APPS -// -// Sent by the chainee when it detects that applications are holding files in -// use. Respond to this message in order to tell the chainee to close the -// applications to prevent a reboot. -// -// pData : NetFxCloseApplications : The list of applications -// Acceptable responses: -// IDYES : Indicates that the chainee should attempt to shutdown the apps. -// If all apps do not successfully close the message may be sent again. -// IDNO : Indicates that the chainee should not attempt to close apps. -// IDRETRY : Indicates that the chainee should refresh the list of apps. -// Another NETFX_CLOSE_APPS message will be sent asynchronously with -// the new list of apps. -//------------------------------------------------------------------------------ -#define NETFX_CLOSE_APPS NETFX_MESSAGE(NETFXDATA_VERSION, IDNO, 1) - -struct NetFxApplication -{ - WCHAR szName[MAX_PATH]; - DWORD dwPid; -}; - -struct NetFxCloseApplications -{ - DWORD dwApplicationsSize; - NetFxApplication applications[1]; -}; - -HRESULT NetFxRunChainer( - __in LPCWSTR wzExecutablePath, - __in LPCWSTR wzArguments, - __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, - __in LPVOID pvContext, - __out DWORD* pdwExitCode - ); -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/package.cpp b/src/engine/package.cpp deleted file mode 100644 index 3f8c8b0f..00000000 --- a/src/engine/package.cpp +++ /dev/null @@ -1,692 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// internal function declarations - -static HRESULT ParsePayloadRefsFromXml( - __in BURN_PACKAGE* pPackage, - __in BURN_PAYLOADS* pPayloads, - __in IXMLDOMNode* pixnPackage - ); -static HRESULT ParsePatchTargetCode( - __in BURN_PACKAGES* pPackages, - __in IXMLDOMNode* pixnBundle - ); -static HRESULT FindRollbackBoundaryById( - __in BURN_PACKAGES* pPackages, - __in_z LPCWSTR wzId, - __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary - ); - - -// function definitions - -extern "C" HRESULT PackagesParseFromXml( - __in BURN_PACKAGES* pPackages, - __in BURN_PAYLOADS* pPayloads, - __in IXMLDOMNode* pixnBundle - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnNodes = NULL; - IXMLDOMNode* pixnNode = NULL; - DWORD cNodes = 0; - BSTR bstrNodeName = NULL; - DWORD cMspPackages = 0; - LPWSTR scz = NULL; - - // select rollback boundary nodes - hr = XmlSelectNodes(pixnBundle, L"RollbackBoundary", &pixnNodes); - ExitOnFailure(hr, "Failed to select rollback boundary nodes."); - - // get rollback boundary node count - hr = pixnNodes->get_length((long*)&cNodes); - ExitOnFailure(hr, "Failed to get rollback bundary node count."); - - if (cNodes) - { - // allocate memory for rollback boundaries - pPackages->rgRollbackBoundaries = (BURN_ROLLBACK_BOUNDARY*)MemAlloc(sizeof(BURN_ROLLBACK_BOUNDARY) * cNodes, TRUE); - ExitOnNull(pPackages->rgRollbackBoundaries, hr, E_OUTOFMEMORY, "Failed to allocate memory for rollback boundary structs."); - - pPackages->cRollbackBoundaries = cNodes; - - // parse rollback boundary elements - for (DWORD i = 0; i < cNodes; ++i) - { - BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = &pPackages->rgRollbackBoundaries[i]; - - hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName); - ExitOnFailure(hr, "Failed to get next node."); - - // @Id - hr = XmlGetAttributeEx(pixnNode, L"Id", &pRollbackBoundary->sczId); - ExitOnFailure(hr, "Failed to get @Id."); - - // @Vital - hr = XmlGetYesNoAttribute(pixnNode, L"Vital", &pRollbackBoundary->fVital); - ExitOnFailure(hr, "Failed to get @Vital."); - - // @Transaction - hr = XmlGetYesNoAttribute(pixnNode, L"Transaction", &pRollbackBoundary->fTransaction); - ExitOnFailure(hr, "Failed to get @Transaction."); - - // prepare next iteration - ReleaseNullObject(pixnNode); - ReleaseNullBSTR(bstrNodeName); - } - } - - ReleaseNullObject(pixnNodes); // done with the RollbackBoundary elements. - - // select package nodes - hr = XmlSelectNodes(pixnBundle, L"Chain/ExePackage|Chain/MsiPackage|Chain/MspPackage|Chain/MsuPackage", &pixnNodes); - ExitOnFailure(hr, "Failed to select package nodes."); - - // get package node count - hr = pixnNodes->get_length((long*)&cNodes); - ExitOnFailure(hr, "Failed to get package node count."); - - if (!cNodes) - { - ExitFunction1(hr = S_OK); - } - - // allocate memory for packages - pPackages->rgPackages = (BURN_PACKAGE*)MemAlloc(sizeof(BURN_PACKAGE) * cNodes, TRUE); - ExitOnNull(pPackages->rgPackages, hr, E_OUTOFMEMORY, "Failed to allocate memory for package structs."); - - pPackages->cPackages = cNodes; - - // parse package elements - for (DWORD i = 0; i < cNodes; ++i) - { - BURN_PACKAGE* pPackage = &pPackages->rgPackages[i]; - - hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName); - ExitOnFailure(hr, "Failed to get next node."); - - // @Id - hr = XmlGetAttributeEx(pixnNode, L"Id", &pPackage->sczId); - ExitOnFailure(hr, "Failed to get @Id."); - - // @Cache - hr = XmlGetAttributeEx(pixnNode, L"Cache", &scz); - if (SUCCEEDED(hr)) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"remove", -1)) - { - pPackage->authoredCacheType = BOOTSTRAPPER_CACHE_TYPE_REMOVE; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"keep", -1)) - { - pPackage->authoredCacheType = BOOTSTRAPPER_CACHE_TYPE_KEEP; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"force", -1)) - { - pPackage->authoredCacheType = BOOTSTRAPPER_CACHE_TYPE_FORCE; - } - else - { - hr = E_UNEXPECTED; - ExitOnRootFailure(hr, "Invalid cache type: %ls", scz); - } - } - ExitOnFailure(hr, "Failed to get @Cache."); - - // @CacheId - hr = XmlGetAttributeEx(pixnNode, L"CacheId", &pPackage->sczCacheId); - ExitOnFailure(hr, "Failed to get @CacheId."); - - // @Size - hr = XmlGetAttributeLargeNumber(pixnNode, L"Size", &pPackage->qwSize); - ExitOnFailure(hr, "Failed to get @Size."); - - // @InstallSize - hr = XmlGetAttributeLargeNumber(pixnNode, L"InstallSize", &pPackage->qwInstallSize); - ExitOnFailure(hr, "Failed to get @InstallSize."); - - // @PerMachine - hr = XmlGetYesNoAttribute(pixnNode, L"PerMachine", &pPackage->fPerMachine); - ExitOnFailure(hr, "Failed to get @PerMachine."); - - // @Permanent - hr = XmlGetYesNoAttribute(pixnNode, L"Permanent", &pPackage->fUninstallable); - ExitOnFailure(hr, "Failed to get @Permanent."); - pPackage->fUninstallable = !pPackage->fUninstallable; // TODO: change "Uninstallable" variable name to permanent, until then Uninstallable is the opposite of Permanent so fix the variable. - pPackage->fCanAffectRegistration = pPackage->fUninstallable; - - // @Vital - hr = XmlGetYesNoAttribute(pixnNode, L"Vital", &pPackage->fVital); - ExitOnFailure(hr, "Failed to get @Vital."); - - // @LogPathVariable - hr = XmlGetAttributeEx(pixnNode, L"LogPathVariable", &pPackage->sczLogPathVariable); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @LogPathVariable."); - } - - // @RollbackLogPathVariable - hr = XmlGetAttributeEx(pixnNode, L"RollbackLogPathVariable", &pPackage->sczRollbackLogPathVariable); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @RollbackLogPathVariable."); - } - - // @InstallCondition - hr = XmlGetAttributeEx(pixnNode, L"InstallCondition", &pPackage->sczInstallCondition); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @InstallCondition."); - } - - // @RollbackBoundaryForward - hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryForward", &scz); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @RollbackBoundaryForward."); - - hr = FindRollbackBoundaryById(pPackages, scz, &pPackage->pRollbackBoundaryForward); - ExitOnFailure(hr, "Failed to find forward transaction boundary: %ls", scz); - } - - // @RollbackBoundaryBackward - hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryBackward", &scz); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @RollbackBoundaryBackward."); - - hr = FindRollbackBoundaryById(pPackages, scz, &pPackage->pRollbackBoundaryBackward); - ExitOnFailure(hr, "Failed to find backward transaction boundary: %ls", scz); - } - - // read type specific attributes - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"ExePackage", -1)) - { - pPackage->type = BURN_PACKAGE_TYPE_EXE; - - hr = ExeEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization - ExitOnFailure(hr, "Failed to parse EXE package."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiPackage", -1)) - { - pPackage->type = BURN_PACKAGE_TYPE_MSI; - - hr = MsiEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization - ExitOnFailure(hr, "Failed to parse MSI package."); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MspPackage", -1)) - { - pPackage->type = BURN_PACKAGE_TYPE_MSP; - - hr = MspEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization - ExitOnFailure(hr, "Failed to parse MSP package."); - - ++cMspPackages; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsuPackage", -1)) - { - pPackage->type = BURN_PACKAGE_TYPE_MSU; - - hr = MsuEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization - ExitOnFailure(hr, "Failed to parse MSU package."); - } - else - { - // ignore other package types for now - } - - // parse payload references - hr = ParsePayloadRefsFromXml(pPackage, pPayloads, pixnNode); - ExitOnFailure(hr, "Failed to parse payload references."); - - // parse dependency providers - hr = DependencyParseProvidersFromXml(pPackage, pixnNode); - ExitOnFailure(hr, "Failed to parse dependency providers."); - - // prepare next iteration - ReleaseNullObject(pixnNode); - ReleaseNullBSTR(bstrNodeName); - } - - if (cMspPackages) - { - pPackages->rgPatchInfo = static_cast(MemAlloc(sizeof(MSIPATCHSEQUENCEINFOW) * cMspPackages, TRUE)); - ExitOnNull(pPackages->rgPatchInfo, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSP patch sequence information."); - - pPackages->rgPatchInfoToPackage = static_cast(MemAlloc(sizeof(BURN_PACKAGE*) * cMspPackages, TRUE)); - ExitOnNull(pPackages->rgPatchInfoToPackage, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch sequence information to package lookup."); - - for (DWORD i = 0; i < pPackages->cPackages; ++i) - { - BURN_PACKAGE* pPackage = &pPackages->rgPackages[i]; - - if (BURN_PACKAGE_TYPE_MSP == pPackage->type) - { - pPackages->rgPatchInfo[pPackages->cPatchInfo].szPatchData = pPackage->Msp.sczApplicabilityXml; - pPackages->rgPatchInfo[pPackages->cPatchInfo].ePatchDataType = MSIPATCH_DATATYPE_XMLBLOB; - pPackages->rgPatchInfoToPackage[pPackages->cPatchInfo] = pPackage; - ++pPackages->cPatchInfo; - - // Loop through all MSI packages seeing if any of them slipstream this MSP. - for (DWORD j = 0; j < pPackages->cPackages; ++j) - { - BURN_PACKAGE* pMsiPackage = &pPackages->rgPackages[j]; - - if (BURN_PACKAGE_TYPE_MSI == pMsiPackage->type) - { - for (DWORD k = 0; k < pMsiPackage->Msi.cSlipstreamMspPackages; ++k) - { - if (pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k] && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k], -1)) - { - BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pMsiPackage->Msi.rgSlipstreamMsps + k; - pSlipstreamMsp->pMspPackage = pPackage; - pSlipstreamMsp->dwMsiChainedPatchIndex = BURN_PACKAGE_INVALID_PATCH_INDEX; - - ReleaseNullStr(pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k]); // we don't need the slipstream package id any longer so free it. - } - } - } - } - } - } - } - - AssertSz(pPackages->cPatchInfo == cMspPackages, "Count of packages patch info should be equal to the number of MSP packages."); - -#if DEBUG - // Loop through all MSI packages seeing if any of them are missing their slipstream MSP. - for (DWORD i = 0; i < pPackages->cPackages; ++i) - { - BURN_PACKAGE* pPackage = &pPackages->rgPackages[i]; - - if (BURN_PACKAGE_TYPE_MSI == pPackage->type) - { - for (DWORD k = 0; k < pPackage->Msi.cSlipstreamMspPackages; ++k) - { - if (pPackage->Msi.rgsczSlipstreamMspPackageIds[k]) - { - AssertSz(FALSE, "MSI slipstream MSP package doesn't exist."); - } - } - } - } -#endif - - hr = ParsePatchTargetCode(pPackages, pixnBundle); - ExitOnFailure(hr, "Failed to parse target product codes."); - - hr = S_OK; - -LExit: - ReleaseObject(pixnNodes); - ReleaseObject(pixnNode); - ReleaseBSTR(bstrNodeName); - ReleaseStr(scz); - - return hr; -} - -extern "C" void PackageUninitialize( - __in BURN_PACKAGE* pPackage - ) -{ - ReleaseStr(pPackage->sczId); - ReleaseStr(pPackage->sczLogPathVariable); - ReleaseStr(pPackage->sczRollbackLogPathVariable); - ReleaseStr(pPackage->sczInstallCondition); - ReleaseStr(pPackage->sczCacheId); - - if (pPackage->rgDependencyProviders) - { - for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) - { - DependencyUninitializeProvider(pPackage->rgDependencyProviders + i); - } - MemFree(pPackage->rgDependencyProviders); - } - - ReleaseMem(pPackage->payloads.rgItems); - - switch (pPackage->type) - { - case BURN_PACKAGE_TYPE_EXE: - ExeEnginePackageUninitialize(pPackage); // TODO: Modularization - break; - case BURN_PACKAGE_TYPE_MSI: - MsiEnginePackageUninitialize(pPackage); // TODO: Modularization - break; - case BURN_PACKAGE_TYPE_MSP: - MspEnginePackageUninitialize(pPackage); // TODO: Modularization - break; - case BURN_PACKAGE_TYPE_MSU: - MsuEnginePackageUninitialize(pPackage); // TODO: Modularization - break; - } -} - -extern "C" void PackagesUninitialize( - __in BURN_PACKAGES* pPackages - ) -{ - if (pPackages->rgRollbackBoundaries) - { - for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i) - { - ReleaseStr(pPackages->rgRollbackBoundaries[i].sczId); - ReleaseStr(pPackages->rgRollbackBoundaries[i].sczLogPath); - } - MemFree(pPackages->rgRollbackBoundaries); - } - - if (pPackages->rgPackages) - { - for (DWORD i = 0; i < pPackages->cPackages; ++i) - { - PackageUninitialize(pPackages->rgPackages + i); - } - MemFree(pPackages->rgPackages); - } - - if (pPackages->rgPatchTargetCodes) - { - for (DWORD i = 0; i < pPackages->cPatchTargetCodes; ++i) - { - ReleaseStr(pPackages->rgPatchTargetCodes[i].sczTargetCode); - } - MemFree(pPackages->rgPatchTargetCodes); - } - - ReleaseMem(pPackages->rgPatchInfo); - ReleaseMem(pPackages->rgPatchInfoToPackage); - - // clear struct - memset(pPackages, 0, sizeof(BURN_PACKAGES)); -} - -extern "C" HRESULT PackageFindById( - __in BURN_PACKAGES* pPackages, - __in_z LPCWSTR wzId, - __out BURN_PACKAGE** ppPackage - ) -{ - HRESULT hr = S_OK; - BURN_PACKAGE* pPackage = NULL; - - for (DWORD i = 0; i < pPackages->cPackages; ++i) - { - pPackage = &pPackages->rgPackages[i]; - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, wzId, -1)) - { - *ppPackage = pPackage; - ExitFunction1(hr = S_OK); - } - } - - hr = E_NOTFOUND; - -LExit: - return hr; -} - - -extern "C" HRESULT PackageFindRelatedById( - __in BURN_RELATED_BUNDLES* pRelatedBundles, - __in_z LPCWSTR wzId, - __out BURN_PACKAGE** ppPackage - ) -{ - HRESULT hr = S_OK; - BURN_PACKAGE* pPackage = NULL; - - for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i) - { - pPackage = &pRelatedBundles->rgRelatedBundles[i].package; - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, wzId, -1)) - { - *ppPackage = pPackage; - ExitFunction1(hr = S_OK); - } - } - - hr = E_NOTFOUND; - -LExit: - return hr; -} - -/******************************************************************** - PackageGetProperty - Determines if the property is defined - and optionally copies the property value. - - Note: The caller must free psczValue if requested. - - Note: Returns E_NOTFOUND if the property was not defined or if the - package does not support properties. - -*********************************************************************/ -extern "C" HRESULT PackageGetProperty( - __in const BURN_PACKAGE* pPackage, - __in_z LPCWSTR wzProperty, - __out_z_opt LPWSTR* psczValue - ) -{ - HRESULT hr = E_NOTFOUND; - BURN_MSIPROPERTY* rgProperties = NULL; - DWORD cProperties = 0; - - // For MSIs and MSPs, enumerate the properties looking for wzProperty. - if (BURN_PACKAGE_TYPE_MSI == pPackage->type) - { - rgProperties = pPackage->Msi.rgProperties; - cProperties = pPackage->Msi.cProperties; - } - else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) - { - rgProperties = pPackage->Msp.rgProperties; - cProperties = pPackage->Msp.cProperties; - } - - for (DWORD i = 0; i < cProperties; ++i) - { - const BURN_MSIPROPERTY* pProperty = &rgProperties[i]; - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pProperty->sczId, -1, wzProperty, -1)) - { - if (psczValue) - { - hr = StrAllocString(psczValue, pProperty->sczValue, 0); - ExitOnFailure(hr, "Failed to copy the property value."); - } - - ExitFunction1(hr = S_OK); - } - } - -LExit: - return hr; -} - -extern "C" HRESULT PackageFindRollbackBoundaryById( - __in BURN_PACKAGES* pPackages, - __in_z LPCWSTR wzId, - __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary - ) -{ - HRESULT hr = S_OK; - BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; - - for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i) - { - pRollbackBoundary = &pPackages->rgRollbackBoundaries[i]; - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pRollbackBoundary->sczId, -1, wzId, -1)) - { - *ppRollbackBoundary = pRollbackBoundary; - ExitFunction1(hr = S_OK); - } - } - - hr = E_NOTFOUND; - -LExit: - return hr; -} - - -// internal function declarations - -static HRESULT ParsePayloadRefsFromXml( - __in BURN_PACKAGE* pPackage, - __in BURN_PAYLOADS* pPayloads, - __in IXMLDOMNode* pixnPackage - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnNodes = NULL; - IXMLDOMNode* pixnNode = NULL; - DWORD cNodes = 0; - LPWSTR sczId = NULL; - - // select package nodes - hr = XmlSelectNodes(pixnPackage, L"PayloadRef", &pixnNodes); - ExitOnFailure(hr, "Failed to select package nodes."); - - // get package node count - hr = pixnNodes->get_length((long*)&cNodes); - ExitOnFailure(hr, "Failed to get package node count."); - - if (!cNodes) - { - ExitFunction1(hr = S_OK); - } - - // allocate memory for payload pointers - pPackage->payloads.rgItems = (BURN_PAYLOAD_GROUP_ITEM*)MemAlloc(sizeof(BURN_PAYLOAD_GROUP_ITEM) * cNodes, TRUE); - ExitOnNull(pPackage->payloads.rgItems, hr, E_OUTOFMEMORY, "Failed to allocate memory for package payloads."); - - pPackage->payloads.cItems = cNodes; - - // parse package elements - for (DWORD i = 0; i < cNodes; ++i) - { - BURN_PAYLOAD_GROUP_ITEM* pPackagePayload = pPackage->payloads.rgItems + i; - - hr = XmlNextElement(pixnNodes, &pixnNode, NULL); - ExitOnFailure(hr, "Failed to get next node."); - - // @Id - hr = XmlGetAttributeEx(pixnNode, L"Id", &sczId); - ExitOnFailure(hr, "Failed to get Id attribute."); - - // find payload - hr = PayloadFindById(pPayloads, sczId, &pPackagePayload->pPayload); - ExitOnFailure(hr, "Failed to find payload."); - - pPackage->payloads.qwTotalSize += pPackagePayload->pPayload->qwFileSize; - - // prepare next iteration - ReleaseNullObject(pixnNode); - } - - hr = S_OK; - -LExit: - ReleaseObject(pixnNodes); - ReleaseObject(pixnNode); - ReleaseStr(sczId); - - return hr; -} - -static HRESULT ParsePatchTargetCode( - __in BURN_PACKAGES* pPackages, - __in IXMLDOMNode* pixnBundle - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnNodes = NULL; - IXMLDOMNode* pixnNode = NULL; - DWORD cNodes = 0; - BSTR bstrNodeText = NULL; - BOOL fProduct; - - hr = XmlSelectNodes(pixnBundle, L"PatchTargetCode", &pixnNodes); - ExitOnFailure(hr, "Failed to select PatchTargetCode nodes."); - - hr = pixnNodes->get_length((long*)&cNodes); - ExitOnFailure(hr, "Failed to get PatchTargetCode node count."); - - if (!cNodes) - { - ExitFunction1(hr = S_OK); - } - - pPackages->rgPatchTargetCodes = (BURN_PATCH_TARGETCODE*)MemAlloc(sizeof(BURN_PATCH_TARGETCODE) * cNodes, TRUE); - ExitOnNull(pPackages->rgPatchTargetCodes, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch targetcodes."); - - pPackages->cPatchTargetCodes = cNodes; - - for (DWORD i = 0; i < cNodes; ++i) - { - BURN_PATCH_TARGETCODE* pTargetCode = pPackages->rgPatchTargetCodes + i; - - hr = XmlNextElement(pixnNodes, &pixnNode, NULL); - ExitOnFailure(hr, "Failed to get next node."); - - hr = XmlGetAttributeEx(pixnNode, L"TargetCode", &pTargetCode->sczTargetCode); - ExitOnFailure(hr, "Failed to get @TargetCode attribute."); - - hr = XmlGetYesNoAttribute(pixnNode, L"Product", &fProduct); - if (E_NOTFOUND == hr) - { - fProduct = FALSE; - hr = S_OK; - } - ExitOnFailure(hr, "Failed to get @Product."); - - pTargetCode->type = fProduct ? BURN_PATCH_TARGETCODE_TYPE_PRODUCT : BURN_PATCH_TARGETCODE_TYPE_UPGRADE; - - // prepare next iteration - ReleaseNullBSTR(bstrNodeText); - ReleaseNullObject(pixnNode); - } - -LExit: - ReleaseBSTR(bstrNodeText); - ReleaseObject(pixnNode); - ReleaseObject(pixnNodes); - - return hr; -} - -static HRESULT FindRollbackBoundaryById( - __in BURN_PACKAGES* pPackages, - __in_z LPCWSTR wzId, - __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary - ) -{ - HRESULT hr = S_OK; - BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; - - for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i) - { - pRollbackBoundary = &pPackages->rgRollbackBoundaries[i]; - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pRollbackBoundary->sczId, -1, wzId, -1)) - { - *ppRollbackBoundary = pRollbackBoundary; - ExitFunction1(hr = S_OK); - } - } - - hr = E_NOTFOUND; - -LExit: - return hr; -} diff --git a/src/engine/package.h b/src/engine/package.h deleted file mode 100644 index 89a3d6e9..00000000 --- a/src/engine/package.h +++ /dev/null @@ -1,380 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - -struct _BURN_RELATED_BUNDLES; -typedef _BURN_RELATED_BUNDLES BURN_RELATED_BUNDLES; - -struct _BURN_PACKAGE; -typedef _BURN_PACKAGE BURN_PACKAGE; - -// constants - -const DWORD BURN_PACKAGE_INVALID_PATCH_INDEX = 0x80000000; - -enum BURN_EXE_EXIT_CODE_TYPE -{ - BURN_EXE_EXIT_CODE_TYPE_NONE, - BURN_EXE_EXIT_CODE_TYPE_SUCCESS, - BURN_EXE_EXIT_CODE_TYPE_ERROR, - BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT, - BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT, -}; - -enum BURN_EXE_PROTOCOL_TYPE -{ - BURN_EXE_PROTOCOL_TYPE_NONE, - BURN_EXE_PROTOCOL_TYPE_BURN, - BURN_EXE_PROTOCOL_TYPE_NETFX4, -}; - -enum BURN_PACKAGE_TYPE -{ - BURN_PACKAGE_TYPE_NONE, - BURN_PACKAGE_TYPE_EXE, - BURN_PACKAGE_TYPE_MSI, - BURN_PACKAGE_TYPE_MSP, - BURN_PACKAGE_TYPE_MSU, -}; - -enum BURN_DEPENDENCY_ACTION -{ - BURN_DEPENDENCY_ACTION_NONE, - BURN_DEPENDENCY_ACTION_REGISTER, - BURN_DEPENDENCY_ACTION_UNREGISTER, -}; - -enum BURN_PATCH_TARGETCODE_TYPE -{ - BURN_PATCH_TARGETCODE_TYPE_UNKNOWN, - BURN_PATCH_TARGETCODE_TYPE_PRODUCT, - BURN_PATCH_TARGETCODE_TYPE_UPGRADE, -}; - -enum BOOTSTRAPPER_FEATURE_ACTION -{ - BOOTSTRAPPER_FEATURE_ACTION_NONE, - BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL, - BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE, - BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT, - BOOTSTRAPPER_FEATURE_ACTION_REINSTALL, - BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE, - BOOTSTRAPPER_FEATURE_ACTION_REMOVE, -}; - -enum BURN_PACKAGE_REGISTRATION_STATE -{ - BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, - BURN_PACKAGE_REGISTRATION_STATE_ABSENT, - BURN_PACKAGE_REGISTRATION_STATE_IGNORED, - BURN_PACKAGE_REGISTRATION_STATE_PRESENT, -}; - -enum BURN_PATCH_SKIP_STATE -{ - BURN_PATCH_SKIP_STATE_NONE, - BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL, - BURN_PATCH_SKIP_STATE_SLIPSTREAM, -}; - -// structs - -typedef struct _BURN_EXE_EXIT_CODE -{ - BURN_EXE_EXIT_CODE_TYPE type; - DWORD dwCode; - BOOL fWildcard; -} BURN_EXE_EXIT_CODE; - -typedef struct _BURN_EXE_COMMAND_LINE_ARGUMENT -{ - LPWSTR sczInstallArgument; - LPWSTR sczUninstallArgument; - LPWSTR sczRepairArgument; - LPWSTR sczCondition; -} BURN_EXE_COMMAND_LINE_ARGUMENT; - -typedef struct _BURN_MSPTARGETPRODUCT -{ - MSIINSTALLCONTEXT context; - DWORD dwOrder; - WCHAR wzTargetProductCode[39]; - BURN_PACKAGE* pChainedTargetPackage; - BOOL fInstalled; - BOOL fSlipstream; - BOOL fSlipstreamRequired; // this means the target product is not present on the machine, but is available in the chain as a slipstream target. - - BOOTSTRAPPER_PACKAGE_STATE patchPackageState; // only valid after Detect. - BOOTSTRAPPER_REQUEST_STATE defaultRequested; // only valid during Plan. - BOOTSTRAPPER_REQUEST_STATE requested; // only valid during Plan. - BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan. - BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan. - BURN_PATCH_SKIP_STATE executeSkip; // only valid during Plan. - BURN_PATCH_SKIP_STATE rollbackSkip; // only valid during Plan. - - BURN_PACKAGE_REGISTRATION_STATE registrationState; // initialized during Detect, updated during Apply. - BURN_PACKAGE_REGISTRATION_STATE transactionRegistrationState;// only valid during Apply inside an MSI transaction. -} BURN_MSPTARGETPRODUCT; - -typedef struct _BURN_MSIPROPERTY -{ - LPWSTR sczId; - LPWSTR sczValue; // used during forward execution - LPWSTR sczRollbackValue; // used during rollback - LPWSTR sczCondition; -} BURN_MSIPROPERTY; - -typedef struct _BURN_MSIFEATURE -{ - LPWSTR sczId; - LPWSTR sczAddLocalCondition; - LPWSTR sczAddSourceCondition; - LPWSTR sczAdvertiseCondition; - LPWSTR sczRollbackAddLocalCondition; - LPWSTR sczRollbackAddSourceCondition; - LPWSTR sczRollbackAdvertiseCondition; - - BOOTSTRAPPER_FEATURE_STATE currentState; // only valid after Detect. - BOOTSTRAPPER_FEATURE_STATE expectedState; // only valid during Plan. - BOOTSTRAPPER_FEATURE_STATE defaultRequested; // only valid during Plan. - BOOTSTRAPPER_FEATURE_STATE requested; // only valid during Plan. - BOOTSTRAPPER_FEATURE_ACTION execute; // only valid during Plan. - BOOTSTRAPPER_FEATURE_ACTION rollback; // only valid during Plan. -} BURN_MSIFEATURE; - -typedef struct _BURN_RELATED_MSI -{ - LPWSTR sczUpgradeCode; - VERUTIL_VERSION* pMinVersion; - VERUTIL_VERSION* pMaxVersion; - BOOL fMinProvided; - BOOL fMaxProvided; - BOOL fMinInclusive; - BOOL fMaxInclusive; - BOOL fOnlyDetect; - BOOL fLangInclusive; - - DWORD* rgdwLanguages; - DWORD cLanguages; -} BURN_RELATED_MSI; - -typedef struct _BURN_CHAINED_PATCH -{ - BURN_PACKAGE* pMspPackage; - DWORD dwMspTargetProductIndex; // index into the Msp.rgTargetProducts -} BURN_CHAINED_PATCH; - -typedef struct _BURN_SLIPSTREAM_MSP -{ - BURN_PACKAGE* pMspPackage; - DWORD dwMsiChainedPatchIndex; // index into the Msi.rgChainedPatches - - BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan. - BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan. -} BURN_SLIPSTREAM_MSP; - -typedef struct _BURN_DEPENDENCY_PROVIDER -{ - LPWSTR sczKey; - LPWSTR sczVersion; - LPWSTR sczDisplayName; - BOOL fImported; - - DEPENDENCY* rgDependents; // only valid after Detect. - UINT cDependents; // only valid after Detect. -} BURN_DEPENDENCY_PROVIDER; - -typedef struct _BURN_ROLLBACK_BOUNDARY -{ - LPWSTR sczId; - BOOL fVital; - BOOL fTransaction; - BOOL fActiveTransaction; // only valid during Apply. - LPWSTR sczLogPath; -} BURN_ROLLBACK_BOUNDARY; - -typedef struct _BURN_PATCH_TARGETCODE -{ - LPWSTR sczTargetCode; - BURN_PATCH_TARGETCODE_TYPE type; -} BURN_PATCH_TARGETCODE; - -typedef struct _BURN_PACKAGE -{ - LPWSTR sczId; - - LPWSTR sczLogPathVariable; // name of the variable that will be set to the log path. - LPWSTR sczRollbackLogPathVariable; // name of the variable that will be set to the rollback path. - - LPWSTR sczInstallCondition; - BOOL fPerMachine; - BOOL fUninstallable; - BOOL fVital; - BOOL fCanAffectRegistration; - - BOOTSTRAPPER_CACHE_TYPE authoredCacheType; - LPWSTR sczCacheId; - - DWORD64 qwInstallSize; - DWORD64 qwSize; - - BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryForward; // used during install and repair. - BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryBackward; // used during uninstall. - - BOOTSTRAPPER_PACKAGE_STATE currentState; // only valid after Detect. - BOOL fCached; // only valid after Detect. - BOOL fPackageProviderExists; // only valid after Detect. - BOOTSTRAPPER_CACHE_TYPE cacheType; // only valid during Plan. - BOOTSTRAPPER_REQUEST_STATE defaultRequested;// only valid during Plan. - BOOTSTRAPPER_REQUEST_STATE requested; // only valid during Plan. - BOOL fPlannedCache; // only valid during Plan. - BOOL fPlannedUncache; // only valid during Plan. - BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan. - BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan. - BURN_DEPENDENCY_ACTION providerExecute; // only valid during Plan. - BURN_DEPENDENCY_ACTION providerRollback; // only valid during Plan. - BURN_DEPENDENCY_ACTION dependencyExecute; // only valid during Plan. - BURN_DEPENDENCY_ACTION dependencyRollback; // only valid during Plan. - BOOL fDependencyManagerWasHere; // only valid during Plan. - LPWSTR sczCacheFolder; // only valid during Apply. - HRESULT hrCacheResult; // only valid during Apply. - - BURN_PACKAGE_REGISTRATION_STATE cacheRegistrationState; // initialized during Detect, updated during Apply. - BURN_PACKAGE_REGISTRATION_STATE installRegistrationState; // initialized during Detect, updated during Apply. - BURN_PACKAGE_REGISTRATION_STATE expectedCacheRegistrationState; // only valid after Plan. - BURN_PACKAGE_REGISTRATION_STATE expectedInstallRegistrationState;// only valid after Plan. - BURN_PACKAGE_REGISTRATION_STATE transactionRegistrationState; // only valid during Apply inside an MSI transaction. - - BURN_PAYLOAD_GROUP payloads; - - BURN_DEPENDENCY_PROVIDER* rgDependencyProviders; - DWORD cDependencyProviders; - - BURN_PACKAGE_TYPE type; - union - { - struct - { - LPWSTR sczDetectCondition; - LPWSTR sczInstallArguments; - LPWSTR sczRepairArguments; - LPWSTR sczUninstallArguments; - LPWSTR sczIgnoreDependencies; - LPCWSTR wzAncestors; // points directly into engine state. - - BOOL fPseudoBundle; - - BOOL fRepairable; - BURN_EXE_PROTOCOL_TYPE protocol; - - BOOL fSupportsAncestors; - - BURN_EXE_EXIT_CODE* rgExitCodes; - DWORD cExitCodes; - - BURN_EXE_COMMAND_LINE_ARGUMENT* rgCommandLineArguments; - DWORD cCommandLineArguments; - } Exe; - struct - { - LPWSTR sczProductCode; - DWORD dwLanguage; - VERUTIL_VERSION* pVersion; - VERUTIL_VERSION* pInstalledVersion; - LPWSTR sczUpgradeCode; - - BURN_MSIPROPERTY* rgProperties; - DWORD cProperties; - - BURN_MSIFEATURE* rgFeatures; - DWORD cFeatures; - - BURN_RELATED_MSI* rgRelatedMsis; - DWORD cRelatedMsis; - - BURN_SLIPSTREAM_MSP* rgSlipstreamMsps; - LPWSTR* rgsczSlipstreamMspPackageIds; - DWORD cSlipstreamMspPackages; - - BURN_CHAINED_PATCH* rgChainedPatches; - DWORD cChainedPatches; - } Msi; - struct - { - LPWSTR sczPatchCode; - LPWSTR sczApplicabilityXml; - - BURN_MSIPROPERTY* rgProperties; - DWORD cProperties; - - BURN_MSPTARGETPRODUCT* rgTargetProducts; - DWORD cTargetProductCodes; - } Msp; - struct - { - LPWSTR sczDetectCondition; - LPWSTR sczKB; - } Msu; - }; -} BURN_PACKAGE; - -typedef struct _BURN_PACKAGES -{ - BURN_ROLLBACK_BOUNDARY* rgRollbackBoundaries; - DWORD cRollbackBoundaries; - - BURN_PACKAGE* rgPackages; - DWORD cPackages; - - BURN_PATCH_TARGETCODE* rgPatchTargetCodes; - DWORD cPatchTargetCodes; - - MSIPATCHSEQUENCEINFOW* rgPatchInfo; - BURN_PACKAGE** rgPatchInfoToPackage; // direct lookup from patch information to the (MSP) package it describes. - // Thus this array is the exact same size as rgPatchInfo. - DWORD cPatchInfo; -} BURN_PACKAGES; - - -// function declarations - -HRESULT PackagesParseFromXml( - __in BURN_PACKAGES* pPackages, - __in BURN_PAYLOADS* pPayloads, - __in IXMLDOMNode* pixnBundle - ); -void PackageUninitialize( - __in BURN_PACKAGE* pPackage - ); -void PackagesUninitialize( - __in BURN_PACKAGES* pPackages - ); -HRESULT PackageFindById( - __in BURN_PACKAGES* pPackages, - __in_z LPCWSTR wzId, - __out BURN_PACKAGE** ppPackage - ); -HRESULT PackageFindRelatedById( - __in BURN_RELATED_BUNDLES* pRelatedBundles, - __in_z LPCWSTR wzId, - __out BURN_PACKAGE** ppPackage - ); -HRESULT PackageGetProperty( - __in const BURN_PACKAGE* pPackage, - __in_z LPCWSTR wzProperty, - __out_z_opt LPWSTR* psczValue - ); -HRESULT PackageFindRollbackBoundaryById( - __in BURN_PACKAGES* pPackages, - __in_z LPCWSTR wzId, - __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary - ); - - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/packages.config b/src/engine/packages.config deleted file mode 100644 index 7219a3da..00000000 --- a/src/engine/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/engine/payload.cpp b/src/engine/payload.cpp deleted file mode 100644 index 72eb3476..00000000 --- a/src/engine/payload.cpp +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// internal function declarations - -static HRESULT FindEmbeddedBySourcePath( - __in BURN_PAYLOADS* pPayloads, - __in_opt BURN_CONTAINER* pContainer, - __in_z LPCWSTR wzStreamName, - __out BURN_PAYLOAD** ppPayload - ); - - -// function definitions - -extern "C" HRESULT PayloadsParseFromXml( - __in BURN_PAYLOADS* pPayloads, - __in_opt BURN_CONTAINERS* pContainers, - __in_opt BURN_PAYLOAD_GROUP* pLayoutPayloads, - __in IXMLDOMNode* pixnBundle - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnNodes = NULL; - IXMLDOMNode* pixnNode = NULL; - DWORD cNodes = 0; - LPWSTR scz = NULL; - - // select payload nodes - hr = XmlSelectNodes(pixnBundle, L"Payload", &pixnNodes); - ExitOnFailure(hr, "Failed to select payload nodes."); - - // get payload node count - hr = pixnNodes->get_length((long*)&cNodes); - ExitOnFailure(hr, "Failed to get payload node count."); - - if (!cNodes) - { - ExitFunction(); - } - - // allocate memory for payloads - pPayloads->rgPayloads = (BURN_PAYLOAD*)MemAlloc(sizeof(BURN_PAYLOAD) * cNodes, TRUE); - ExitOnNull(pPayloads->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate memory for payload structs."); - - pPayloads->cPayloads = cNodes; - - // parse search elements - for (DWORD i = 0; i < cNodes; ++i) - { - BURN_PAYLOAD* pPayload = &pPayloads->rgPayloads[i]; - - hr = XmlNextElement(pixnNodes, &pixnNode, NULL); - ExitOnFailure(hr, "Failed to get next node."); - - // @Id - hr = XmlGetAttributeEx(pixnNode, L"Id", &pPayload->sczKey); - ExitOnFailure(hr, "Failed to get @Id."); - - // @FilePath - hr = XmlGetAttributeEx(pixnNode, L"FilePath", &pPayload->sczFilePath); - ExitOnFailure(hr, "Failed to get @FilePath."); - - // @Packaging - hr = XmlGetAttributeEx(pixnNode, L"Packaging", &scz); - ExitOnFailure(hr, "Failed to get @Packaging."); - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"embedded", -1)) - { - pPayload->packaging = BURN_PAYLOAD_PACKAGING_EMBEDDED; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"external", -1)) - { - pPayload->packaging = BURN_PAYLOAD_PACKAGING_EXTERNAL; - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid value for @Packaging: %ls", scz); - } - - // @Container - if (pContainers) - { - hr = XmlGetAttributeEx(pixnNode, L"Container", &scz); - if (E_NOTFOUND != hr || BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging) - { - ExitOnFailure(hr, "Failed to get @Container."); - - // find container - hr = ContainerFindById(pContainers, scz, &pPayload->pContainer); - ExitOnFailure(hr, "Failed to to find container: %ls", scz); - } - } - - // @LayoutOnly - hr = XmlGetYesNoAttribute(pixnNode, L"LayoutOnly", &pPayload->fLayoutOnly); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @LayoutOnly."); - } - - // @SourcePath - hr = XmlGetAttributeEx(pixnNode, L"SourcePath", &pPayload->sczSourcePath); - ExitOnFailure(hr, "Failed to get @SourcePath."); - - // @DownloadUrl - hr = XmlGetAttributeEx(pixnNode, L"DownloadUrl", &pPayload->downloadSource.sczUrl); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @DownloadUrl."); - } - - // @FileSize - hr = XmlGetAttributeEx(pixnNode, L"FileSize", &scz); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @FileSize."); - - hr = StrStringToUInt64(scz, 0, &pPayload->qwFileSize); - ExitOnFailure(hr, "Failed to parse @FileSize."); - } - - // @Hash - hr = XmlGetAttributeEx(pixnNode, L"Hash", &scz); - ExitOnFailure(hr, "Failed to get @Hash."); - - hr = StrAllocHexDecode(scz, &pPayload->pbHash, &pPayload->cbHash); - ExitOnFailure(hr, "Failed to hex decode the Payload/@Hash."); - - if (pPayload->fLayoutOnly && pLayoutPayloads) - { - hr = MemEnsureArraySize(reinterpret_cast(&pLayoutPayloads->rgItems), pLayoutPayloads->cItems + 1, sizeof(BURN_PAYLOAD_GROUP_ITEM), 5); - ExitOnFailure(hr, "Failed to allocate memory for layout payloads."); - - pLayoutPayloads->rgItems[pLayoutPayloads->cItems].pPayload = pPayload; - ++pLayoutPayloads->cItems; - - pLayoutPayloads->qwTotalSize += pPayload->qwFileSize; - } - - // prepare next iteration - ReleaseNullObject(pixnNode); - } - - hr = S_OK; - -LExit: - ReleaseObject(pixnNodes); - ReleaseObject(pixnNode); - ReleaseStr(scz); - - return hr; -} - -extern "C" void PayloadUninitialize( - __in BURN_PAYLOAD* pPayload - ) -{ - if (pPayload) - { - ReleaseStr(pPayload->sczKey); - ReleaseStr(pPayload->sczFilePath); - ReleaseMem(pPayload->pbHash); - ReleaseStr(pPayload->sczSourcePath); - ReleaseStr(pPayload->sczLocalFilePath); - ReleaseStr(pPayload->downloadSource.sczUrl); - ReleaseStr(pPayload->downloadSource.sczUser); - ReleaseStr(pPayload->downloadSource.sczPassword); - ReleaseStr(pPayload->sczUnverifiedPath); - } -} - -extern "C" void PayloadsUninitialize( - __in BURN_PAYLOADS* pPayloads - ) -{ - if (pPayloads->rgPayloads) - { - for (DWORD i = 0; i < pPayloads->cPayloads; ++i) - { - PayloadUninitialize(pPayloads->rgPayloads + i); - } - MemFree(pPayloads->rgPayloads); - } - - // clear struct - memset(pPayloads, 0, sizeof(BURN_PAYLOADS)); -} - -extern "C" HRESULT PayloadExtractUXContainer( - __in BURN_PAYLOADS* pPayloads, - __in BURN_CONTAINER_CONTEXT* pContainerContext, - __in_z LPCWSTR wzTargetDir - ) -{ - HRESULT hr = S_OK; - LPWSTR sczStreamName = NULL; - LPWSTR sczDirectory = NULL; - BURN_PAYLOAD* pPayload = NULL; - - // extract all payloads - for (;;) - { - // get next stream - hr = ContainerNextStream(pContainerContext, &sczStreamName); - if (E_NOMOREITEMS == hr) - { - hr = S_OK; - break; - } - ExitOnFailure(hr, "Failed to get next stream."); - - // find payload by stream name - hr = PayloadFindEmbeddedBySourcePath(pPayloads, sczStreamName, &pPayload); - ExitOnFailure(hr, "Failed to find embedded payload: %ls", sczStreamName); - - // make file path - hr = PathConcat(wzTargetDir, pPayload->sczFilePath, &pPayload->sczLocalFilePath); - ExitOnFailure(hr, "Failed to concat file paths."); - - // extract file - hr = PathGetDirectory(pPayload->sczLocalFilePath, &sczDirectory); - ExitOnFailure(hr, "Failed to get directory portion of local file path"); - - hr = DirEnsureExists(sczDirectory, NULL); - ExitOnFailure(hr, "Failed to ensure directory exists"); - - hr = ContainerStreamToFile(pContainerContext, pPayload->sczLocalFilePath); - ExitOnFailure(hr, "Failed to extract file."); - - // flag that the payload has been acquired - pPayload->state = BURN_PAYLOAD_STATE_ACQUIRED; - } - - // locate any payloads that were not extracted - for (DWORD i = 0; i < pPayloads->cPayloads; ++i) - { - pPayload = &pPayloads->rgPayloads[i]; - - // if the payload has not been acquired - if (BURN_PAYLOAD_STATE_ACQUIRED > pPayload->state) - { - hr = E_INVALIDDATA; - ExitOnRootFailure(hr, "Payload was not found in container: %ls", pPayload->sczKey); - } - } - -LExit: - ReleaseStr(sczStreamName); - ReleaseStr(sczDirectory); - - return hr; -} - -extern "C" HRESULT PayloadFindById( - __in BURN_PAYLOADS* pPayloads, - __in_z LPCWSTR wzId, - __out BURN_PAYLOAD** ppPayload - ) -{ - HRESULT hr = S_OK; - BURN_PAYLOAD* pPayload = NULL; - - for (DWORD i = 0; i < pPayloads->cPayloads; ++i) - { - pPayload = &pPayloads->rgPayloads[i]; - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPayload->sczKey, -1, wzId, -1)) - { - *ppPayload = pPayload; - ExitFunction1(hr = S_OK); - } - } - - hr = E_NOTFOUND; - -LExit: - return hr; -} - -extern "C" HRESULT PayloadFindEmbeddedBySourcePath( - __in BURN_PAYLOADS* pPayloads, - __in_z LPCWSTR wzStreamName, - __out BURN_PAYLOAD** ppPayload - ) -{ - HRESULT hr = S_OK; - BURN_PAYLOAD* pPayload = NULL; - - for (DWORD i = 0; i < pPayloads->cPayloads; ++i) - { - pPayload = &pPayloads->rgPayloads[i]; - - if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPayload->sczSourcePath, -1, wzStreamName, -1)) - { - *ppPayload = pPayload; - ExitFunction1(hr = S_OK); - } - } - } - - hr = E_NOTFOUND; - -LExit: - return hr; -} - - -// internal function definitions diff --git a/src/engine/payload.h b/src/engine/payload.h deleted file mode 100644 index f28b437f..00000000 --- a/src/engine/payload.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -// constants - -enum BURN_PAYLOAD_PACKAGING -{ - BURN_PAYLOAD_PACKAGING_NONE, - BURN_PAYLOAD_PACKAGING_EMBEDDED, - BURN_PAYLOAD_PACKAGING_EXTERNAL, -}; - -enum BURN_PAYLOAD_STATE -{ - BURN_PAYLOAD_STATE_NONE, - BURN_PAYLOAD_STATE_ACQUIRED, - BURN_PAYLOAD_STATE_CACHED, -}; - - -// structs - -typedef struct _BURN_PAYLOAD -{ - LPWSTR sczKey; - BURN_PAYLOAD_PACKAGING packaging; - BOOL fLayoutOnly; - DWORD64 qwFileSize; - LPWSTR sczFilePath; // file path relative to the execute location - - BYTE* pbHash; - DWORD cbHash; - - LPWSTR sczSourcePath; - BURN_CONTAINER* pContainer; - DOWNLOAD_SOURCE downloadSource; - - // mutable members - BURN_PAYLOAD_STATE state; - LPWSTR sczLocalFilePath; // location of extracted or downloaded copy - - LPWSTR sczUnverifiedPath; - DWORD cRemainingInstances; -} BURN_PAYLOAD; - -typedef struct _BURN_PAYLOADS -{ - BURN_PAYLOAD* rgPayloads; - DWORD cPayloads; -} BURN_PAYLOADS; - -typedef struct _BURN_PAYLOAD_GROUP_ITEM -{ - BURN_PAYLOAD* pPayload; - - // mutable members - BOOL fCached; - DWORD64 qwCommittedCacheProgress; -} BURN_PAYLOAD_GROUP_ITEM; - -typedef struct _BURN_PAYLOAD_GROUP -{ - BURN_PAYLOAD_GROUP_ITEM* rgItems; - DWORD cItems; - DWORD64 qwTotalSize; -} BURN_PAYLOAD_GROUP; - -// functions - -HRESULT PayloadsParseFromXml( - __in BURN_PAYLOADS* pPayloads, - __in_opt BURN_CONTAINERS* pContainers, - __in_opt BURN_PAYLOAD_GROUP* pLayoutPayloads, - __in IXMLDOMNode* pixnBundle - ); -void PayloadUninitialize( - __in BURN_PAYLOAD* pPayload - ); -void PayloadsUninitialize( - __in BURN_PAYLOADS* pPayloads - ); -HRESULT PayloadExtractUXContainer( - __in BURN_PAYLOADS* pPayloads, - __in BURN_CONTAINER_CONTEXT* pContainerContext, - __in_z LPCWSTR wzTargetDir - ); -HRESULT PayloadFindById( - __in BURN_PAYLOADS* pPayloads, - __in_z LPCWSTR wzId, - __out BURN_PAYLOAD** ppPayload - ); -HRESULT PayloadFindEmbeddedBySourcePath( - __in BURN_PAYLOADS* pPayloads, - __in_z LPCWSTR wzStreamName, - __out BURN_PAYLOAD** ppPayload - ); - - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/pipe.cpp b/src/engine/pipe.cpp deleted file mode 100644 index a9fd24e8..00000000 --- a/src/engine/pipe.cpp +++ /dev/null @@ -1,821 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -static const DWORD PIPE_64KB = 64 * 1024; -static const DWORD PIPE_WAIT_FOR_CONNECTION = 100; // wait a 10th of a second, -static const DWORD PIPE_RETRY_FOR_CONNECTION = 1800; // for up to 3 minutes. - -static const LPCWSTR PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls"; -static const LPCWSTR CACHE_PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls.Cache"; - -static HRESULT AllocatePipeMessage( - __in DWORD dwMessage, - __in_bcount_opt(cbData) LPVOID pvData, - __in SIZE_T cbData, - __out_bcount(cb) LPVOID* ppvMessage, - __out SIZE_T* cbMessage - ); -static void FreePipeMessage( - __in BURN_PIPE_MESSAGE *pMsg - ); -static HRESULT WritePipeMessage( - __in HANDLE hPipe, - __in DWORD dwMessage, - __in_bcount_opt(cbData) LPVOID pvData, - __in SIZE_T cbData - ); -static HRESULT GetPipeMessage( - __in HANDLE hPipe, - __in BURN_PIPE_MESSAGE* pMsg - ); -static HRESULT ChildPipeConnected( - __in HANDLE hPipe, - __in_z LPCWSTR wzSecret, - __inout DWORD* pdwProcessId - ); - - - -/******************************************************************* - PipeConnectionInitialize - initialize pipe connection data. - -*******************************************************************/ -void PipeConnectionInitialize( - __in BURN_PIPE_CONNECTION* pConnection - ) -{ - memset(pConnection, 0, sizeof(BURN_PIPE_CONNECTION)); - pConnection->hPipe = INVALID_HANDLE_VALUE; - pConnection->hCachePipe = INVALID_HANDLE_VALUE; -} - -/******************************************************************* - PipeConnectionUninitialize - free data in a pipe connection. - -*******************************************************************/ -void PipeConnectionUninitialize( - __in BURN_PIPE_CONNECTION* pConnection - ) -{ - ReleaseFileHandle(pConnection->hCachePipe); - ReleaseFileHandle(pConnection->hPipe); - ReleaseHandle(pConnection->hProcess); - ReleaseStr(pConnection->sczSecret); - ReleaseStr(pConnection->sczName); - - memset(pConnection, 0, sizeof(BURN_PIPE_CONNECTION)); - pConnection->hPipe = INVALID_HANDLE_VALUE; - pConnection->hCachePipe = INVALID_HANDLE_VALUE; -} - -/******************************************************************* - PipeSendMessage - - -*******************************************************************/ -extern "C" HRESULT PipeSendMessage( - __in HANDLE hPipe, - __in DWORD dwMessage, - __in_bcount_opt(cbData) LPVOID pvData, - __in SIZE_T cbData, - __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, - __in_opt LPVOID pvContext, - __out DWORD* pdwResult - ) -{ - HRESULT hr = S_OK; - BURN_PIPE_RESULT result = { }; - - hr = WritePipeMessage(hPipe, dwMessage, pvData, cbData); - ExitOnFailure(hr, "Failed to write send message to pipe."); - - hr = PipePumpMessages(hPipe, pfnCallback, pvContext, &result); - ExitOnFailure(hr, "Failed to pump messages during send message to pipe."); - - *pdwResult = result.dwResult; - -LExit: - return hr; -} - -/******************************************************************* - PipePumpMessages - - -*******************************************************************/ -extern "C" HRESULT PipePumpMessages( - __in HANDLE hPipe, - __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, - __in_opt LPVOID pvContext, - __in BURN_PIPE_RESULT* pResult - ) -{ - HRESULT hr = S_OK; - BURN_PIPE_MESSAGE msg = { }; - SIZE_T iData = 0; - LPSTR sczMessage = NULL; - DWORD dwResult = 0; - - // Pump messages from child process. - while (S_OK == (hr = GetPipeMessage(hPipe, &msg))) - { - switch (msg.dwMessage) - { - case BURN_PIPE_MESSAGE_TYPE_LOG: - iData = 0; - - hr = BuffReadStringAnsi((BYTE*)msg.pvData, msg.cbData, &iData, &sczMessage); - ExitOnFailure(hr, "Failed to read log message."); - - hr = LogStringWorkRaw(sczMessage); - ExitOnFailure(hr, "Failed to write log message:'%hs'.", sczMessage); - - dwResult = static_cast(hr); - break; - - case BURN_PIPE_MESSAGE_TYPE_COMPLETE: - if (!msg.pvData || sizeof(DWORD) != msg.cbData) - { - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "No status returned to PipePumpMessages()"); - } - - pResult->dwResult = *static_cast(msg.pvData); - ExitFunction1(hr = S_OK); // exit loop. - - case BURN_PIPE_MESSAGE_TYPE_TERMINATE: - iData = 0; - - hr = BuffReadNumber(static_cast(msg.pvData), msg.cbData, &iData, &pResult->dwResult); - ExitOnFailure(hr, "Failed to read returned result to PipePumpMessages()"); - - if (sizeof(DWORD) * 2 == msg.cbData) - { - hr = BuffReadNumber(static_cast(msg.pvData), msg.cbData, &iData, (DWORD*)&pResult->fRestart); - ExitOnFailure(hr, "Failed to read returned restart to PipePumpMessages()"); - } - - ExitFunction1(hr = S_OK); // exit loop. - - default: - if (pfnCallback) - { - hr = pfnCallback(&msg, pvContext, &dwResult); - } - else - { - hr = E_INVALIDARG; - } - ExitOnFailure(hr, "Failed to process message: %u", msg.dwMessage); - break; - } - - // post result - hr = WritePipeMessage(hPipe, static_cast(BURN_PIPE_MESSAGE_TYPE_COMPLETE), &dwResult, sizeof(dwResult)); - ExitOnFailure(hr, "Failed to post result to child process."); - - FreePipeMessage(&msg); - } - ExitOnFailure(hr, "Failed to get message over pipe"); - - if (S_FALSE == hr) - { - hr = S_OK; - } - -LExit: - ReleaseStr(sczMessage); - FreePipeMessage(&msg); - - return hr; -} - -/******************************************************************* - PipeCreateNameAndSecret - - -*******************************************************************/ -extern "C" HRESULT PipeCreateNameAndSecret( - __out_z LPWSTR *psczConnectionName, - __out_z LPWSTR *psczSecret - ) -{ - HRESULT hr = S_OK; - WCHAR wzGuid[GUID_STRING_LENGTH]; - LPWSTR sczConnectionName = NULL; - LPWSTR sczSecret = NULL; - - // Create the unique pipe name. - hr = GuidFixedCreate(wzGuid); - ExitOnRootFailure(hr, "Failed to create pipe guid."); - - hr = StrAllocFormatted(&sczConnectionName, L"BurnPipe.%s", wzGuid); - ExitOnFailure(hr, "Failed to allocate pipe name."); - - // Create the unique client secret. - hr = GuidFixedCreate(wzGuid); - ExitOnRootFailure(hr, "Failed to create pipe secret."); - - hr = StrAllocString(&sczSecret, wzGuid, 0); - ExitOnFailure(hr, "Failed to allocate pipe secret."); - - *psczConnectionName = sczConnectionName; - sczConnectionName = NULL; - *psczSecret = sczSecret; - sczSecret = NULL; - -LExit: - ReleaseStr(sczSecret); - ReleaseStr(sczConnectionName); - - return hr; -} - -/******************************************************************* - PipeCreatePipes - create the pipes and event to signal child process. - -*******************************************************************/ -extern "C" HRESULT PipeCreatePipes( - __in BURN_PIPE_CONNECTION* pConnection, - __in BOOL fCreateCachePipe, - __out HANDLE* phEvent - ) -{ - Assert(pConnection->sczName); - Assert(INVALID_HANDLE_VALUE == pConnection->hPipe); - Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe); - - HRESULT hr = S_OK; - PSECURITY_DESCRIPTOR psd = NULL; - SECURITY_ATTRIBUTES sa = { }; - LPWSTR sczFullPipeName = NULL; - HANDLE hPipe = INVALID_HANDLE_VALUE; - HANDLE hCachePipe = INVALID_HANDLE_VALUE; - - // Only the grant special rights when the pipe is being used for "embedded" - // scenarios (aka: there is no cache pipe). - if (!fCreateCachePipe) - { - // Create the security descriptor that grants read/write/sync access to Everyone. - // TODO: consider locking down "WD" to LogonIds (logon session) - LPCWSTR wzSddl = L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW0x00100000;;;WD)"; - if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(wzSddl, SDDL_REVISION_1, &psd, NULL)) - { - ExitWithLastError(hr, "Failed to create the security descriptor for the connection event and pipe."); - } - - sa.nLength = sizeof(sa); - sa.lpSecurityDescriptor = psd; - sa.bInheritHandle = FALSE; - } - - // Create the pipe. - hr = StrAllocFormatted(&sczFullPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName); - ExitOnFailure(hr, "Failed to allocate full name of pipe: %ls", pConnection->sczName); - - // TODO: consider using overlapped IO to do waits on the pipe and still be able to cancel and such. - hPipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, psd ? &sa : NULL); - if (INVALID_HANDLE_VALUE == hPipe) - { - ExitWithLastError(hr, "Failed to create pipe: %ls", sczFullPipeName); - } - - if (fCreateCachePipe) - { - // Create the cache pipe. - hr = StrAllocFormatted(&sczFullPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName); - ExitOnFailure(hr, "Failed to allocate full name of cache pipe: %ls", pConnection->sczName); - - hCachePipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, NULL); - if (INVALID_HANDLE_VALUE == hCachePipe) - { - ExitWithLastError(hr, "Failed to create pipe: %ls", sczFullPipeName); - } - } - - pConnection->hCachePipe = hCachePipe; - hCachePipe = INVALID_HANDLE_VALUE; - - pConnection->hPipe = hPipe; - hPipe = INVALID_HANDLE_VALUE; - - // TODO: remove the following - *phEvent = NULL; - -LExit: - ReleaseFileHandle(hCachePipe); - ReleaseFileHandle(hPipe); - ReleaseStr(sczFullPipeName); - - if (psd) - { - ::LocalFree(psd); - } - - return hr; -} - -/******************************************************************* - PipeLaunchParentProcess - Called from the per-machine process to create - a per-user process and set up the - communication pipe. - -*******************************************************************/ -const LPCWSTR BURN_COMMANDLINE_SWITCH_UNELEVATED = L"burn.unelevated"; -HRESULT PipeLaunchParentProcess( - __in_z LPCWSTR wzCommandLine, - __in int nCmdShow, - __in_z LPWSTR sczConnectionName, - __in_z LPWSTR sczSecret, - __in BOOL /*fDisableUnelevate*/ - ) -{ - HRESULT hr = S_OK; - DWORD dwProcessId = 0; - LPWSTR sczBurnPath = NULL; - LPWSTR sczParameters = NULL; - HANDLE hProcess = NULL; - - dwProcessId = ::GetCurrentProcessId(); - - hr = PathForCurrentProcess(&sczBurnPath, NULL); - ExitOnFailure(hr, "Failed to get current process path."); - - hr = StrAllocFormatted(&sczParameters, L"-%ls %ls %ls %u %ls", BURN_COMMANDLINE_SWITCH_UNELEVATED, sczConnectionName, sczSecret, dwProcessId, wzCommandLine); - ExitOnFailure(hr, "Failed to allocate parameters for unelevated process."); - -#ifdef ENABLE_UNELEVATE - if (fDisableUnelevate) - { - hr = ProcExec(sczBurnPath, sczParameters, nCmdShow, &hProcess); - ExitOnFailure(hr, "Failed to launch parent process with unelevate disabled: %ls", sczBurnPath); - } - else - { - // Try to launch unelevated and if that fails for any reason, try launch our process normally (even though that may make it elevated). - hr = ProcExecuteAsInteractiveUser(sczBurnPath, sczParameters, &hProcess); - if (FAILED(hr)) - { - hr = ShelExecUnelevated(sczBurnPath, sczParameters, L"open", NULL, nCmdShow); - if (FAILED(hr)) - { - hr = ShelExec(sczBurnPath, sczParameters, L"open", NULL, nCmdShow, NULL, NULL); - ExitOnFailure(hr, "Failed to launch parent process: %ls", sczBurnPath); - } - } - } -#else - hr = ProcExec(sczBurnPath, sczParameters, nCmdShow, &hProcess); - ExitOnFailure(hr, "Failed to launch parent process with unelevate disabled: %ls", sczBurnPath); -#endif - -LExit: - ReleaseHandle(hProcess); - ReleaseStr(sczParameters); - ReleaseStr(sczBurnPath); - - return hr; -} - -/******************************************************************* - PipeLaunchChildProcess - Called from the per-user process to create - the per-machine process and set up the - communication pipe. - -*******************************************************************/ -extern "C" HRESULT PipeLaunchChildProcess( - __in_z LPCWSTR wzExecutablePath, - __in BURN_PIPE_CONNECTION* pConnection, - __in BOOL fElevate, - __in_opt HWND hwndParent - ) -{ - HRESULT hr = S_OK; - DWORD dwCurrentProcessId = ::GetCurrentProcessId(); - LPWSTR sczParameters = NULL; - LPCWSTR wzVerb = NULL; - HANDLE hProcess = NULL; - - hr = StrAllocFormatted(&sczParameters, L"-q -%ls %ls %ls %u", BURN_COMMANDLINE_SWITCH_ELEVATED, pConnection->sczName, pConnection->sczSecret, dwCurrentProcessId); - ExitOnFailure(hr, "Failed to allocate parameters for elevated process."); - - wzVerb = !fElevate ? L"open" : L"runas"; - - // Since ShellExecuteEx doesn't support passing inherited handles, don't bother with CoreAppendFileHandleSelfToCommandLine. - // We could fallback to using ::DuplicateHandle to inject the file handle later if necessary. - hr = ShelExec(wzExecutablePath, sczParameters, wzVerb, NULL, SW_SHOWNA, hwndParent, &hProcess); - ExitOnFailure(hr, "Failed to launch elevated child process: %ls", wzExecutablePath); - - pConnection->dwProcessId = ::GetProcessId(hProcess); - pConnection->hProcess = hProcess; - hProcess = NULL; - -LExit: - ReleaseHandle(hProcess); - ReleaseStr(sczParameters); - - return hr; -} - -/******************************************************************* - PipeWaitForChildConnect - - -*******************************************************************/ -extern "C" HRESULT PipeWaitForChildConnect( - __in BURN_PIPE_CONNECTION* pConnection - ) -{ - HRESULT hr = S_OK; - HANDLE hPipes[2] = { pConnection->hPipe, pConnection->hCachePipe}; - LPCWSTR wzSecret = pConnection->sczSecret; - DWORD cbSecret = lstrlenW(wzSecret) * sizeof(WCHAR); - DWORD dwCurrentProcessId = ::GetCurrentProcessId(); - DWORD dwAck = 0; - - for (DWORD i = 0; i < countof(hPipes) && INVALID_HANDLE_VALUE != hPipes[i]; ++i) - { - HANDLE hPipe = hPipes[i]; - DWORD dwPipeState = PIPE_READMODE_BYTE | PIPE_NOWAIT; - - // Temporarily make the pipe non-blocking so we will not get stuck in ::ConnectNamedPipe() forever - // if the child decides not to show up. - if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL)) - { - ExitWithLastError(hr, "Failed to set pipe to non-blocking."); - } - - // Loop for a while waiting for a connection from child process. - DWORD cRetry = 0; - do - { - if (!::ConnectNamedPipe(hPipe, NULL)) - { - DWORD er = ::GetLastError(); - if (ERROR_PIPE_CONNECTED == er) - { - hr = S_OK; - break; - } - else if (ERROR_PIPE_LISTENING == er) - { - if (cRetry < PIPE_RETRY_FOR_CONNECTION) - { - hr = HRESULT_FROM_WIN32(er); - } - else - { - hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); - break; - } - - ++cRetry; - ::Sleep(PIPE_WAIT_FOR_CONNECTION); - } - else - { - hr = HRESULT_FROM_WIN32(er); - break; - } - } - } while (HRESULT_FROM_WIN32(ERROR_PIPE_LISTENING) == hr); - ExitOnRootFailure(hr, "Failed to wait for child to connect to pipe."); - - // Put the pipe back in blocking mode. - dwPipeState = PIPE_READMODE_BYTE | PIPE_WAIT; - if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL)) - { - ExitWithLastError(hr, "Failed to reset pipe to blocking."); - } - - // Prove we are the one that created the elevated process by passing the secret. - hr = FileWriteHandle(hPipe, reinterpret_cast(&cbSecret), sizeof(cbSecret)); - ExitOnFailure(hr, "Failed to write secret length to pipe."); - - hr = FileWriteHandle(hPipe, reinterpret_cast(wzSecret), cbSecret); - ExitOnFailure(hr, "Failed to write secret to pipe."); - - hr = FileWriteHandle(hPipe, reinterpret_cast(&dwCurrentProcessId), sizeof(dwCurrentProcessId)); - ExitOnFailure(hr, "Failed to write our process id to pipe."); - - // Wait until the elevated process responds that it is ready to go. - hr = FileReadHandle(hPipe, reinterpret_cast(&dwAck), sizeof(dwAck)); - ExitOnFailure(hr, "Failed to read ACK from pipe."); - - // The ACK should match out expected child process id. - //if (pConnection->dwProcessId != dwAck) - //{ - // hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - // ExitOnRootFailure(hr, "Incorrect ACK from elevated pipe: %u", dwAck); - //} - } - -LExit: - return hr; -} - -/******************************************************************* - PipeTerminateChildProcess - - -*******************************************************************/ -extern "C" HRESULT PipeTerminateChildProcess( - __in BURN_PIPE_CONNECTION* pConnection, - __in DWORD dwParentExitCode, - __in BOOL fRestart - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - - // Prepare the exit message. - hr = BuffWriteNumber(&pbData, &cbData, dwParentExitCode); - ExitOnFailure(hr, "Failed to write exit code to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, fRestart); - ExitOnFailure(hr, "Failed to write restart to message buffer."); - - // Send the messages. - if (INVALID_HANDLE_VALUE != pConnection->hCachePipe) - { - hr = WritePipeMessage(pConnection->hCachePipe, static_cast(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData); - ExitOnFailure(hr, "Failed to post terminate message to child process cache thread."); - } - - hr = WritePipeMessage(pConnection->hPipe, static_cast(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData); - ExitOnFailure(hr, "Failed to post terminate message to child process."); - - // If we were able to get a handle to the other process, wait for it to exit. - if (pConnection->hProcess) - { - if (WAIT_FAILED == ::WaitForSingleObject(pConnection->hProcess, PIPE_WAIT_FOR_CONNECTION * PIPE_RETRY_FOR_CONNECTION)) - { - ExitWithLastError(hr, "Failed to wait for child process exit."); - } - -#ifdef DEBUG - DWORD dwChildExitCode = 0; - DWORD dwErrorCode = ERROR_SUCCESS; - BOOL fReturnedExitCode = ::GetExitCodeProcess(pConnection->hProcess, &dwChildExitCode); - if (!fReturnedExitCode) - { - dwErrorCode = ::GetLastError(); // if the other process is elevated and we are not, then we'll get ERROR_ACCESS_DENIED. - - // The unit test use a thread instead of a process so try to get the exit code from - // the thread because we failed to get it from the process. - if (ERROR_INVALID_HANDLE == dwErrorCode) - { - fReturnedExitCode = ::GetExitCodeThread(pConnection->hProcess, &dwChildExitCode); - } - } - AssertSz((fReturnedExitCode && dwChildExitCode == dwParentExitCode) || - (!fReturnedExitCode && ERROR_ACCESS_DENIED == dwErrorCode), - "Child elevated process did not return matching exit code to parent process."); -#endif - } - -LExit: - return hr; -} - -/******************************************************************* - PipeChildConnect - Called from the child process to connect back - to the pipe provided by the parent process. - -*******************************************************************/ -extern "C" HRESULT PipeChildConnect( - __in BURN_PIPE_CONNECTION* pConnection, - __in BOOL fConnectCachePipe - ) -{ - Assert(pConnection->sczName); - Assert(pConnection->sczSecret); - Assert(!pConnection->hProcess); - Assert(INVALID_HANDLE_VALUE == pConnection->hPipe); - Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe); - - HRESULT hr = S_OK; - LPWSTR sczPipeName = NULL; - - // Try to connect to the parent. - hr = StrAllocFormatted(&sczPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName); - ExitOnFailure(hr, "Failed to allocate name of parent pipe."); - - hr = E_UNEXPECTED; - for (DWORD cRetry = 0; FAILED(hr) && cRetry < PIPE_RETRY_FOR_CONNECTION; ++cRetry) - { - pConnection->hPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); - if (INVALID_HANDLE_VALUE == pConnection->hPipe) - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - if (E_FILENOTFOUND == hr) // if the pipe isn't created, call it a timeout waiting on the parent. - { - hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); - } - - ::Sleep(PIPE_WAIT_FOR_CONNECTION); - } - else // we have a connection, go with it. - { - hr = S_OK; - } - } - ExitOnRootFailure(hr, "Failed to open parent pipe: %ls", sczPipeName) - - // Verify the parent and notify it that the child connected. - hr = ChildPipeConnected(pConnection->hPipe, pConnection->sczSecret, &pConnection->dwProcessId); - ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName); - - if (fConnectCachePipe) - { - // Connect to the parent for the cache pipe. - hr = StrAllocFormatted(&sczPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName); - ExitOnFailure(hr, "Failed to allocate name of parent cache pipe."); - - pConnection->hCachePipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); - if (INVALID_HANDLE_VALUE == pConnection->hCachePipe) - { - ExitWithLastError(hr, "Failed to open parent pipe: %ls", sczPipeName) - } - - // Verify the parent and notify it that the child connected. - hr = ChildPipeConnected(pConnection->hCachePipe, pConnection->sczSecret, &pConnection->dwProcessId); - ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName); - } - - pConnection->hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, pConnection->dwProcessId); - ExitOnNullWithLastError(pConnection->hProcess, hr, "Failed to open companion process with PID: %u", pConnection->dwProcessId); - -LExit: - ReleaseStr(sczPipeName); - - return hr; -} - - -static HRESULT AllocatePipeMessage( - __in DWORD dwMessage, - __in_bcount_opt(cbData) LPVOID pvData, - __in SIZE_T cbData, - __out_bcount(cb) LPVOID* ppvMessage, - __out SIZE_T* cbMessage - ) -{ - HRESULT hr = S_OK; - LPVOID pv = NULL; - SIZE_T cb = 0; - - // If no data was provided, ensure the count of bytes is zero. - if (!pvData) - { - cbData = 0; - } - - // Allocate the message. - cb = sizeof(dwMessage) + sizeof(cbData) + cbData; - pv = MemAlloc(cb, FALSE); - ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for message."); - - memcpy_s(pv, cb, &dwMessage, sizeof(dwMessage)); - memcpy_s(static_cast(pv) + sizeof(dwMessage), cb - sizeof(dwMessage), &cbData, sizeof(cbData)); - if (cbData) - { - memcpy_s(static_cast(pv) + sizeof(dwMessage) + sizeof(cbData), cb - sizeof(dwMessage) - sizeof(cbData), pvData, cbData); - } - - *cbMessage = cb; - *ppvMessage = pv; - pv = NULL; - -LExit: - ReleaseMem(pv); - return hr; -} - -static void FreePipeMessage( - __in BURN_PIPE_MESSAGE *pMsg - ) -{ - if (pMsg->fAllocatedData) - { - ReleaseNullMem(pMsg->pvData); - pMsg->fAllocatedData = FALSE; - } -} - -static HRESULT WritePipeMessage( - __in HANDLE hPipe, - __in DWORD dwMessage, - __in_bcount_opt(cbData) LPVOID pvData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - LPVOID pv = NULL; - SIZE_T cb = 0; - - hr = AllocatePipeMessage(dwMessage, pvData, cbData, &pv, &cb); - ExitOnFailure(hr, "Failed to allocate message to write."); - - // Write the message. - hr = FileWriteHandle(hPipe, reinterpret_cast(pv), cb); - ExitOnFailure(hr, "Failed to write message type to pipe."); - -LExit: - ReleaseMem(pv); - return hr; -} - -static HRESULT GetPipeMessage( - __in HANDLE hPipe, - __in BURN_PIPE_MESSAGE* pMsg - ) -{ - HRESULT hr = S_OK; - BYTE pbMessageAndByteCount[sizeof(DWORD) + sizeof(SIZE_T)] = { }; - - hr = FileReadHandle(hPipe, pbMessageAndByteCount, sizeof(pbMessageAndByteCount)); - if (HRESULT_FROM_WIN32(ERROR_BROKEN_PIPE) == hr) - { - memset(pbMessageAndByteCount, 0, sizeof(pbMessageAndByteCount)); - hr = S_FALSE; - } - ExitOnFailure(hr, "Failed to read message from pipe."); - - pMsg->dwMessage = *(DWORD*)(pbMessageAndByteCount); - pMsg->cbData = *(SIZE_T*)(pbMessageAndByteCount + sizeof(DWORD)); - if (pMsg->cbData) - { - pMsg->pvData = MemAlloc(pMsg->cbData, FALSE); - ExitOnNull(pMsg->pvData, hr, E_OUTOFMEMORY, "Failed to allocate data for message."); - - hr = FileReadHandle(hPipe, reinterpret_cast(pMsg->pvData), pMsg->cbData); - ExitOnFailure(hr, "Failed to read data for message."); - - pMsg->fAllocatedData = TRUE; - } - -LExit: - if (!pMsg->fAllocatedData && pMsg->pvData) - { - MemFree(pMsg->pvData); - } - - return hr; -} - -static HRESULT ChildPipeConnected( - __in HANDLE hPipe, - __in_z LPCWSTR wzSecret, - __inout DWORD* pdwProcessId - ) -{ - HRESULT hr = S_OK; - LPWSTR sczVerificationSecret = NULL; - DWORD cbVerificationSecret = 0; - DWORD dwVerificationProcessId = 0; - DWORD dwAck = ::GetCurrentProcessId(); // send our process id as the ACK. - - // Read the verification secret. - hr = FileReadHandle(hPipe, reinterpret_cast(&cbVerificationSecret), sizeof(cbVerificationSecret)); - ExitOnFailure(hr, "Failed to read size of verification secret from parent pipe."); - - if (255 < cbVerificationSecret / sizeof(WCHAR)) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Verification secret from parent is too big."); - } - - hr = StrAlloc(&sczVerificationSecret, cbVerificationSecret / sizeof(WCHAR) + 1); - ExitOnFailure(hr, "Failed to allocate buffer for verification secret."); - - FileReadHandle(hPipe, reinterpret_cast(sczVerificationSecret), cbVerificationSecret); - ExitOnFailure(hr, "Failed to read verification secret from parent pipe."); - - // Verify the secrets match. - if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, 0, sczVerificationSecret, -1, wzSecret, -1)) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Verification secret from parent does not match."); - } - - // Read the verification process id. - hr = FileReadHandle(hPipe, reinterpret_cast(&dwVerificationProcessId), sizeof(dwVerificationProcessId)); - ExitOnFailure(hr, "Failed to read verification process id from parent pipe."); - - // If a process id was not provided, we'll trust the process id from the parent. - if (*pdwProcessId == 0) - { - *pdwProcessId = dwVerificationProcessId; - } - else if (*pdwProcessId != dwVerificationProcessId) // verify the ids match. - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Verification process id from parent does not match."); - } - - // All is well, tell the parent process. - hr = FileWriteHandle(hPipe, reinterpret_cast(&dwAck), sizeof(dwAck)); - ExitOnFailure(hr, "Failed to inform parent process that child is running."); - -LExit: - ReleaseStr(sczVerificationSecret); - return hr; -} diff --git a/src/engine/pipe.h b/src/engine/pipe.h deleted file mode 100644 index 429cd824..00000000 --- a/src/engine/pipe.h +++ /dev/null @@ -1,113 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct _BURN_PIPE_CONNECTION -{ - LPWSTR sczName; - LPWSTR sczSecret; - DWORD dwProcessId; - - HANDLE hProcess; - HANDLE hPipe; - HANDLE hCachePipe; -} BURN_PIPE_CONNECTION; - -typedef enum _BURN_PIPE_MESSAGE_TYPE : DWORD -{ - BURN_PIPE_MESSAGE_TYPE_LOG = 0xF0000001, - BURN_PIPE_MESSAGE_TYPE_COMPLETE = 0xF0000002, - BURN_PIPE_MESSAGE_TYPE_TERMINATE = 0xF0000003, -} BURN_PIPE_MESSAGE_TYPE; - -typedef struct _BURN_PIPE_MESSAGE -{ - DWORD dwMessage; - SIZE_T cbData; - - BOOL fAllocatedData; - LPVOID pvData; -} BURN_PIPE_MESSAGE; - -typedef struct _BURN_PIPE_RESULT -{ - DWORD dwResult; - BOOL fRestart; -} BURN_PIPE_RESULT; - - -typedef HRESULT (*PFN_PIPE_MESSAGE_CALLBACK)( - __in BURN_PIPE_MESSAGE* pMsg, - __in_opt LPVOID pvContext, - __out DWORD* pdwResult - ); - - -// Common functions. -void PipeConnectionInitialize( - __in BURN_PIPE_CONNECTION* pConnection - ); -void PipeConnectionUninitialize( - __in BURN_PIPE_CONNECTION* pConnection - ); -HRESULT PipeSendMessage( - __in HANDLE hPipe, - __in DWORD dwMessage, - __in_bcount_opt(cbData) LPVOID pvData, - __in SIZE_T cbData, - __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, - __in_opt LPVOID pvContext, - __out DWORD* pdwResult - ); -HRESULT PipePumpMessages( - __in HANDLE hPipe, - __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, - __in_opt LPVOID pvContext, - __in BURN_PIPE_RESULT* pResult - ); - -// Parent functions. -HRESULT PipeCreateNameAndSecret( - __out_z LPWSTR *psczConnectionName, - __out_z LPWSTR *psczSecret - ); -HRESULT PipeCreatePipes( - __in BURN_PIPE_CONNECTION* pConnection, - __in BOOL fCreateCachePipe, - __out HANDLE* phEvent - ); -HRESULT PipeLaunchParentProcess( - __in LPCWSTR wzCommandLine, - __in int nCmdShow, - __in_z LPWSTR sczConnectionName, - __in_z LPWSTR sczSecret, - __in BOOL fDisableUnelevate - ); -HRESULT PipeLaunchChildProcess( - __in_z LPCWSTR wzExecutablePath, - __in BURN_PIPE_CONNECTION* pConnection, - __in BOOL fElevate, - __in_opt HWND hwndParent - ); -HRESULT PipeWaitForChildConnect( - __in BURN_PIPE_CONNECTION* pConnection - ); -HRESULT PipeTerminateChildProcess( - __in BURN_PIPE_CONNECTION* pConnection, - __in DWORD dwParentExitCode, - __in BOOL fRestart - ); - -// Child functions. -HRESULT PipeChildConnect( - __in BURN_PIPE_CONNECTION* pConnection, - __in BOOL fConnectCachePipe - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp deleted file mode 100644 index 9a4aa5f1..00000000 --- a/src/engine/plan.cpp +++ /dev/null @@ -1,2699 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -#define PlanDumpLevel REPORT_DEBUG - -// internal struct definitions - - -// internal function definitions - -static void UninitializeRegistrationAction( - __in BURN_DEPENDENT_REGISTRATION_ACTION* pAction - ); -static void UninitializeCacheAction( - __in BURN_CACHE_ACTION* pCacheAction - ); -static void ResetPlannedContainerState( - __in BURN_CONTAINER* pContainer - ); -static void ResetPlannedPayloadsState( - __in BURN_PAYLOADS* pPayloads - ); -static void ResetPlannedPayloadGroupState( - __in BURN_PAYLOAD_GROUP* pPayloadGroup - ); -static void ResetPlannedPackageState( - __in BURN_PACKAGE* pPackage - ); -static void ResetPlannedRollbackBoundaryState( - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary - ); -static HRESULT PlanPackagesHelper( - __in BURN_PACKAGE* rgPackages, - __in DWORD cPackages, - __in BOOL fPlanCleanPackages, - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_PLAN* pPlan, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_RELATION_TYPE relationType - ); -static HRESULT InitializePackage( - __in BURN_PLAN* pPlan, - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_VARIABLES* pVariables, - __in BURN_PACKAGE* pPackage, - __in BOOTSTRAPPER_RELATION_TYPE relationType - ); -static HRESULT ProcessPackage( - __in BOOL fBundlePerMachine, - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_DISPLAY display, - __inout HANDLE* phSyncpointEvent, - __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary - ); -static HRESULT ProcessPackageRollbackBoundary( - __in BURN_PLAN* pPlan, - __in_opt BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary, - __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary - ); -static HRESULT GetActionDefaultRequestState( - __in BOOTSTRAPPER_ACTION action, - __in BOOL fPermanent, - __in BOOTSTRAPPER_PACKAGE_STATE currentState, - __out BOOTSTRAPPER_REQUEST_STATE* pRequestState - ); -static HRESULT AddRegistrationAction( - __in BURN_PLAN* pPlan, - __in BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type, - __in_z LPCWSTR wzDependentProviderKey, - __in_z LPCWSTR wzOwnerBundleId - ); -static HRESULT AddCachePackage( - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage, - __out HANDLE* phSyncpointEvent - ); -static HRESULT AddCachePackageHelper( - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage, - __out HANDLE* phSyncpointEvent - ); -static HRESULT AddCacheSlipstreamMsps( - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage - ); -static BOOL AlreadyPlannedCachePackage( - __in BURN_PLAN* pPlan, - __in_z LPCWSTR wzPackageId, - __out HANDLE* phSyncpointEvent - ); -static DWORD GetNextCheckpointId( - __in BURN_PLAN* pPlan - ); -static HRESULT AppendCacheAction( - __in BURN_PLAN* pPlan, - __out BURN_CACHE_ACTION** ppCacheAction - ); -static HRESULT AppendRollbackCacheAction( - __in BURN_PLAN* pPlan, - __out BURN_CACHE_ACTION** ppCacheAction - ); -static HRESULT ProcessPayloadGroup( - __in BURN_PLAN* pPlan, - __in BURN_PAYLOAD_GROUP* pPayloadGroup - ); -static void RemoveUnnecessaryActions( - __in BOOL fExecute, - __in BURN_EXECUTE_ACTION* rgActions, - __in DWORD cActions - ); -static void FinalizePatchActions( - __in BOOL fExecute, - __in BURN_EXECUTE_ACTION* rgActions, - __in DWORD cActions - ); -static void CalculateExpectedRegistrationStates( - __in BURN_PACKAGE* rgPackages, - __in DWORD cPackages - ); -static HRESULT PlanDependencyActions( - __in BOOL fBundlePerMachine, - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage - ); -static HRESULT CalculateExecuteActions( - __in BURN_PACKAGE* pPackage, - __in_opt BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary - ); -static BOOL NeedsCache( - __in BURN_PACKAGE* pPackage, - __in BOOL fExecute - ); -static BOOL ForceCache( - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage - ); - -// function definitions - -extern "C" void PlanReset( - __in BURN_PLAN* pPlan, - __in BURN_CONTAINERS* pContainers, - __in BURN_PACKAGES* pPackages, - __in BURN_PAYLOAD_GROUP* pLayoutPayloads - ) -{ - ReleaseNullStr(pPlan->sczLayoutDirectory); - PackageUninitialize(&pPlan->forwardCompatibleBundle); - - if (pPlan->rgRegistrationActions) - { - for (DWORD i = 0; i < pPlan->cRegistrationActions; ++i) - { - UninitializeRegistrationAction(&pPlan->rgRegistrationActions[i]); - } - MemFree(pPlan->rgRegistrationActions); - } - - if (pPlan->rgRollbackRegistrationActions) - { - for (DWORD i = 0; i < pPlan->cRollbackRegistrationActions; ++i) - { - UninitializeRegistrationAction(&pPlan->rgRollbackRegistrationActions[i]); - } - MemFree(pPlan->rgRollbackRegistrationActions); - } - - if (pPlan->rgCacheActions) - { - for (DWORD i = 0; i < pPlan->cCacheActions; ++i) - { - UninitializeCacheAction(&pPlan->rgCacheActions[i]); - } - MemFree(pPlan->rgCacheActions); - } - - if (pPlan->rgExecuteActions) - { - for (DWORD i = 0; i < pPlan->cExecuteActions; ++i) - { - PlanUninitializeExecuteAction(&pPlan->rgExecuteActions[i]); - } - MemFree(pPlan->rgExecuteActions); - } - - if (pPlan->rgRollbackActions) - { - for (DWORD i = 0; i < pPlan->cRollbackActions; ++i) - { - PlanUninitializeExecuteAction(&pPlan->rgRollbackActions[i]); - } - MemFree(pPlan->rgRollbackActions); - } - - if (pPlan->rgCleanActions) - { - // Nothing needs to be freed inside clean actions today. - MemFree(pPlan->rgCleanActions); - } - - if (pPlan->rgPlannedProviders) - { - ReleaseDependencyArray(pPlan->rgPlannedProviders, pPlan->cPlannedProviders); - } - - if (pPlan->rgContainerProgress) - { - MemFree(pPlan->rgContainerProgress); - } - - if (pPlan->shContainerProgress) - { - ReleaseDict(pPlan->shContainerProgress); - } - - if (pPlan->rgPayloadProgress) - { - MemFree(pPlan->rgPayloadProgress); - } - - if (pPlan->shPayloadProgress) - { - ReleaseDict(pPlan->shPayloadProgress); - } - - if (pPlan->pPayloads) - { - ResetPlannedPayloadsState(pPlan->pPayloads); - } - - memset(pPlan, 0, sizeof(BURN_PLAN)); - - if (pContainers->rgContainers) - { - for (DWORD i = 0; i < pContainers->cContainers; ++i) - { - ResetPlannedContainerState(&pContainers->rgContainers[i]); - } - } - - // Reset the planned actions for each package. - if (pPackages->rgPackages) - { - for (DWORD i = 0; i < pPackages->cPackages; ++i) - { - ResetPlannedPackageState(&pPackages->rgPackages[i]); - } - } - - ResetPlannedPayloadGroupState(pLayoutPayloads); - - // Reset the planned state for each rollback boundary. - if (pPackages->rgRollbackBoundaries) - { - for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i) - { - ResetPlannedRollbackBoundaryState(&pPackages->rgRollbackBoundaries[i]); - } - } -} - -extern "C" void PlanUninitializeExecuteAction( - __in BURN_EXECUTE_ACTION* pExecuteAction - ) -{ - switch (pExecuteAction->type) - { - case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: - ReleaseStr(pExecuteAction->exePackage.sczIgnoreDependencies); - ReleaseStr(pExecuteAction->exePackage.sczAncestors); - break; - - case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: - ReleaseStr(pExecuteAction->msiPackage.sczLogPath); - ReleaseMem(pExecuteAction->msiPackage.rgFeatures); - break; - - case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: - ReleaseStr(pExecuteAction->mspTarget.sczTargetProductCode); - ReleaseStr(pExecuteAction->mspTarget.sczLogPath); - ReleaseMem(pExecuteAction->mspTarget.rgOrderedPatches); - break; - - case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: - ReleaseStr(pExecuteAction->msuPackage.sczLogPath); - break; - - case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: - ReleaseStr(pExecuteAction->packageDependency.sczBundleProviderKey); - break; - } -} - -extern "C" HRESULT PlanSetVariables( - __in BOOTSTRAPPER_ACTION action, - __in BURN_VARIABLES* pVariables - ) -{ - HRESULT hr = S_OK; - - hr = VariableSetNumeric(pVariables, BURN_BUNDLE_ACTION, action, TRUE); - ExitOnFailure(hr, "Failed to set the bundle action built-in variable."); - -LExit: - return hr; -} - -extern "C" HRESULT PlanDefaultPackageRequestState( - __in BURN_PACKAGE_TYPE packageType, - __in BOOTSTRAPPER_PACKAGE_STATE currentState, - __in BOOL fPermanent, - __in BOOTSTRAPPER_ACTION action, - __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __out BOOTSTRAPPER_REQUEST_STATE* pRequestState - ) -{ - HRESULT hr = S_OK; - BOOTSTRAPPER_REQUEST_STATE defaultRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; - - // If doing layout, then always default to requesting the package be cached. - if (BOOTSTRAPPER_ACTION_LAYOUT == action) - { - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE; - } - else if (BOOTSTRAPPER_RELATION_PATCH == relationType && BURN_PACKAGE_TYPE_MSP == packageType) - { - // For patch related bundles, only install a patch if currently absent during install, modify, or repair. - if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == currentState && BOOTSTRAPPER_ACTION_INSTALL <= action) - { - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; - } - else - { - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; - } - } - else if (BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED == currentState && BOOTSTRAPPER_ACTION_UNINSTALL != action) - { - // Superseded means the package is on the machine but not active, so only uninstall operations are allowed. - // All other operations do nothing. - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; - } - else if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == currentState && !(BOOTSTRAPPER_ACTION_UNINSTALL == action && BURN_PACKAGE_TYPE_MSP == packageType)) - { - // Obsolete means the package is not on the machine and should not be installed, *except* patches can be obsolete - // and present so allow them to be removed during uninstall. Everyone else, gets nothing. - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; - } - else // pick the best option for the action state and install condition. - { - hr = GetActionDefaultRequestState(action, fPermanent, currentState, &defaultRequestState); - ExitOnFailure(hr, "Failed to get default request state for action."); - - // If we're doing an install, use the install condition - // to determine whether to use the default request state or make the package absent. - if (BOOTSTRAPPER_ACTION_UNINSTALL != action && BOOTSTRAPPER_PACKAGE_CONDITION_FALSE == installCondition) - { - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT; - } - else // just set the package to the default request state. - { - *pRequestState = defaultRequestState; - } - } - -LExit: - return hr; -} - -extern "C" HRESULT PlanLayoutBundle( - __in BURN_PLAN* pPlan, - __in_z LPCWSTR wzExecutableName, - __in DWORD64 qwBundleSize, - __in BURN_VARIABLES* pVariables, - __in BURN_PAYLOAD_GROUP* pLayoutPayloads - ) -{ - HRESULT hr = S_OK; - BURN_CACHE_ACTION* pCacheAction = NULL; - LPWSTR sczExecutablePath = NULL; - - // Get the layout directory. - hr = VariableGetString(pVariables, BURN_BUNDLE_LAYOUT_DIRECTORY, &pPlan->sczLayoutDirectory); - if (E_NOTFOUND == hr) // if not set, use the current directory as the layout directory. - { - hr = VariableGetString(pVariables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, &pPlan->sczLayoutDirectory); - if (E_NOTFOUND == hr) // if not set, use the current directory as the layout directory. - { - hr = PathForCurrentProcess(&sczExecutablePath, NULL); - ExitOnFailure(hr, "Failed to get path for current executing process as layout directory."); - - hr = PathGetDirectory(sczExecutablePath, &pPlan->sczLayoutDirectory); - ExitOnFailure(hr, "Failed to get executing process as layout directory."); - } - } - ExitOnFailure(hr, "Failed to get bundle layout directory property."); - - hr = PathBackslashTerminate(&pPlan->sczLayoutDirectory); - ExitOnFailure(hr, "Failed to ensure layout directory is backslash terminated."); - - hr = ProcessPayloadGroup(pPlan, pLayoutPayloads); - ExitOnFailure(hr, "Failed to process payload group for bundle."); - - // Plan the layout of the bundle engine itself. - hr = AppendCacheAction(pPlan, &pCacheAction); - ExitOnFailure(hr, "Failed to append bundle start action."); - - pCacheAction->type = BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE; - - hr = StrAllocString(&pCacheAction->bundleLayout.sczExecutableName, wzExecutableName, 0); - ExitOnFailure(hr, "Failed to to copy executable name for bundle."); - - hr = CacheCalculateBundleLayoutWorkingPath(pPlan->wzBundleId, &pCacheAction->bundleLayout.sczUnverifiedPath); - ExitOnFailure(hr, "Failed to calculate bundle layout working path."); - - pCacheAction->bundleLayout.qwBundleSize = qwBundleSize; - pCacheAction->bundleLayout.pPayloadGroup = pLayoutPayloads; - - // Acquire + Verify + Finalize - pPlan->qwCacheSizeTotal += 3 * qwBundleSize; - - ++pPlan->cOverallProgressTicksTotal; - -LExit: - ReleaseStr(sczExecutablePath); - - return hr; -} - -extern "C" HRESULT PlanForwardCompatibleBundles( - __in BURN_USER_EXPERIENCE* pUX, - __in BOOTSTRAPPER_COMMAND* pCommand, - __in BURN_PLAN* pPlan, - __in BURN_REGISTRATION* pRegistration, - __in BOOTSTRAPPER_ACTION action - ) -{ - HRESULT hr = S_OK; - BOOL fRecommendIgnore = TRUE; - BOOL fIgnoreBundle = FALSE; - - if (!pRegistration->fForwardCompatibleBundleExists) - { - ExitFunction(); - } - - // Only change the recommendation if an active parent was provided. - if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent) - { - // On install, recommend running the forward compatible bundle because there is an active parent. This - // will essentially register the parent with the forward compatible bundle. - if (BOOTSTRAPPER_ACTION_INSTALL == action) - { - fRecommendIgnore = FALSE; - } - else if (BOOTSTRAPPER_ACTION_UNINSTALL == action || - BOOTSTRAPPER_ACTION_MODIFY == action || - BOOTSTRAPPER_ACTION_REPAIR == action) - { - // When modifying the bundle, only recommend running the forward compatible bundle if the parent - // is already registered as a dependent of the provider key. - if (pRegistration->fParentRegisteredAsDependent) - { - fRecommendIgnore = FALSE; - } - } - } - - for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) - { - BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle; - if (!pRelatedBundle->fForwardCompatible) - { - continue; - } - - fIgnoreBundle = fRecommendIgnore; - - hr = UserExperienceOnPlanForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, &fIgnoreBundle); - ExitOnRootFailure(hr, "BA aborted plan forward compatible bundle."); - - if (!fIgnoreBundle) - { - hr = PseudoBundleInitializePassthrough(&pPlan->forwardCompatibleBundle, pCommand, NULL, pRegistration->sczActiveParent, pRegistration->sczAncestors, &pRelatedBundle->package); - ExitOnFailure(hr, "Failed to initialize pass through bundle."); - - pPlan->fEnabledForwardCompatibleBundle = TRUE; - break; - } - } - -LExit: - return hr; -} - -extern "C" HRESULT PlanPackages( - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_PACKAGES* pPackages, - __in BURN_PLAN* pPlan, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_RELATION_TYPE relationType - ) -{ - HRESULT hr = S_OK; - - hr = PlanPackagesHelper(pPackages->rgPackages, pPackages->cPackages, TRUE, pUX, pPlan, pLog, pVariables, display, relationType); - - return hr; -} - -extern "C" HRESULT PlanRegistration( - __in BURN_PLAN* pPlan, - __in BURN_REGISTRATION* pRegistration, - __in BOOTSTRAPPER_RESUME_TYPE /*resumeType*/, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __inout BOOL* pfContinuePlanning - ) -{ - HRESULT hr = S_OK; - STRINGDICT_HANDLE sdBundleDependents = NULL; - STRINGDICT_HANDLE sdIgnoreDependents = NULL; - - pPlan->fCanAffectMachineState = TRUE; // register the bundle since we're modifying machine state. - pPlan->fDisallowRemoval = FALSE; // by default the bundle can be planned to be removed - pPlan->fIgnoreAllDependents = pRegistration->fIgnoreAllDependents; - - // Ensure the bundle is cached if not running from the cache. - if (!CacheBundleRunningFromCache()) - { - pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE; - } - - // Always write registration since things may have changed or it just needs to be "fixed up". - pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION; - - // Always update our estimated size registration when installing/modify/repair since things - // may have been added or removed or it just needs to be "fixed up". - pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE; - - if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) - { - // If our provider key was detected and it points to our current bundle then we can - // unregister the bundle dependency. - if (pRegistration->sczDetectedProviderKeyBundleId && - CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczId, -1, pRegistration->sczDetectedProviderKeyBundleId, -1)) - { - pPlan->dependencyRegistrationAction = BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER; - } - else // log that another bundle already owned our registration, hopefully this only happens when a newer version - { // of a bundle installed and is in the process of upgrading us. - LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_PROVIDER_KEY_REMOVAL, pRegistration->sczProviderKey, pRegistration->sczDetectedProviderKeyBundleId); - } - - // Create the dictionary of dependents that should be ignored. - hr = DictCreateStringList(&sdIgnoreDependents, 5, DICT_FLAG_CASEINSENSITIVE); - ExitOnFailure(hr, "Failed to create the string dictionary."); - - // If the self-dependent dependent exists, plan its removal. If we did not do this, we - // would prevent self-removal. - if (pRegistration->fSelfRegisteredAsDependent) - { - hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER, pRegistration->wzSelfDependent, pRegistration->sczId); - ExitOnFailure(hr, "Failed to allocate registration action."); - - hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, pRegistration->wzSelfDependent); - ExitOnFailure(hr, "Failed to add self-dependent to ignore dependents."); - } - - if (!pPlan->fIgnoreAllDependents) - { - // If we are not doing an upgrade, we check to see if there are still dependents on us and if so we skip planning. - // However, when being upgraded, we always execute our uninstall because a newer version of us is probably - // already on the machine and we need to clean up the stuff specific to this bundle. - if (BOOTSTRAPPER_RELATION_UPGRADE != relationType) - { - // If there were other dependencies to ignore, add them. - for (DWORD iDependency = 0; iDependency < pRegistration->cIgnoredDependencies; ++iDependency) - { - DEPENDENCY* pDependency = pRegistration->rgIgnoredDependencies + iDependency; - - hr = DictKeyExists(sdIgnoreDependents, pDependency->sczKey); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to check the dictionary of ignored dependents."); - } - else - { - hr = DictAddKey(sdIgnoreDependents, pDependency->sczKey); - ExitOnFailure(hr, "Failed to add dependent key to ignored dependents."); - } - } - - // For addon or patch bundles, dependent related bundles should be ignored. This allows - // that addon or patch to be removed even though bundles it targets still are registered. - for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) - { - const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i; - - if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType) - { - for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j) - { - const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j; - - hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, pProvider->sczKey); - ExitOnFailure(hr, "Failed to add dependent bundle provider key to ignore dependents."); - } - } - } - - // If there are any (non-ignored and not-planned-to-be-removed) dependents left, skip planning. - for (DWORD iDependent = 0; iDependent < pRegistration->cDependents; ++iDependent) - { - DEPENDENCY* pDependent = pRegistration->rgDependents + iDependent; - - hr = DictKeyExists(sdIgnoreDependents, pDependent->sczKey); - if (E_NOTFOUND == hr) - { - hr = S_OK; - - // TODO: callback to the BA and let it have the option to ignore this dependent? - if (!pPlan->fDisallowRemoval) - { - pPlan->fDisallowRemoval = TRUE; // ensure the registration stays - *pfContinuePlanning = FALSE; // skip the rest of planning. - - LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS); - } - - LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_DEPENDENT, pDependent->sczKey, LoggingStringOrUnknownIfNull(pDependent->sczName)); - } - ExitOnFailure(hr, "Failed to check for remaining dependents during planning."); - } - } - } - } - else - { - BOOL fAddonOrPatchBundle = (pRegistration->cAddonCodes || pRegistration->cPatchCodes); - - // Always plan to write our provider key registration when installing/modify/repair to "fix it" - // if broken. - pPlan->dependencyRegistrationAction = BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER; - - // Create the dictionary of bundle dependents. - hr = DictCreateStringList(&sdBundleDependents, 5, DICT_FLAG_CASEINSENSITIVE); - ExitOnFailure(hr, "Failed to create the string dictionary."); - - for (DWORD iDependent = 0; iDependent < pRegistration->cDependents; ++iDependent) - { - DEPENDENCY* pDependent = pRegistration->rgDependents + iDependent; - - hr = DictKeyExists(sdBundleDependents, pDependent->sczKey); - if (E_NOTFOUND == hr) - { - hr = DictAddKey(sdBundleDependents, pDependent->sczKey); - ExitOnFailure(hr, "Failed to add dependent key to bundle dependents."); - } - ExitOnFailure(hr, "Failed to check the dictionary of bundle dependents."); - } - - // Register each dependent related bundle. The ensures that addons and patches are reference - // counted and stick around until the last targeted bundle is removed. - for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) - { - const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i; - - if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType) - { - for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j) - { - const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j; - - hr = DictKeyExists(sdBundleDependents, pProvider->sczKey); - if (E_NOTFOUND == hr) - { - hr = DictAddKey(sdBundleDependents, pProvider->sczKey); - ExitOnFailure(hr, "Failed to add new dependent key to bundle dependents."); - - hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, pProvider->sczKey, pRelatedBundle->package.sczId); - ExitOnFailure(hr, "Failed to add registration action for dependent related bundle."); - } - ExitOnFailure(hr, "Failed to check the dictionary of bundle dependents."); - } - } - } - - // Only do the following if we decided there was a dependent self to register. If so and and an explicit parent was - // provided, register dependent self. Otherwise, if this bundle is not an addon or patch bundle then self-regisiter - // as our own dependent. - if (pRegistration->wzSelfDependent && !pRegistration->fSelfRegisteredAsDependent && (pRegistration->sczActiveParent || !fAddonOrPatchBundle)) - { - hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, pRegistration->wzSelfDependent, pRegistration->sczId); - ExitOnFailure(hr, "Failed to add registration action for self dependent."); - } - } - -LExit: - ReleaseDict(sdBundleDependents); - ReleaseDict(sdIgnoreDependents); - - return hr; -} - -extern "C" HRESULT PlanPassThroughBundle( - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_PACKAGE* pPackage, - __in BURN_PLAN* pPlan, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_RELATION_TYPE relationType - ) -{ - HRESULT hr = S_OK; - - // Plan passthrough package. - // Passthrough packages are never cleaned up by the calling bundle (they delete themselves when appropriate) - // so we don't need to plan clean up. - hr = PlanPackagesHelper(pPackage, 1, FALSE, pUX, pPlan, pLog, pVariables, display, relationType); - ExitOnFailure(hr, "Failed to process passthrough package."); - -LExit: - return hr; -} - -extern "C" HRESULT PlanUpdateBundle( - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_PACKAGE* pPackage, - __in BURN_PLAN* pPlan, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_RELATION_TYPE relationType - ) -{ - HRESULT hr = S_OK; - - // Plan update package. - hr = PlanPackagesHelper(pPackage, 1, TRUE, pUX, pPlan, pLog, pVariables, display, relationType); - ExitOnFailure(hr, "Failed to process update package."); - -LExit: - return hr; -} - -static HRESULT PlanPackagesHelper( - __in BURN_PACKAGE* rgPackages, - __in DWORD cPackages, - __in BOOL fPlanCleanPackages, - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_PLAN* pPlan, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_RELATION_TYPE relationType - ) -{ - HRESULT hr = S_OK; - BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine. - BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; - HANDLE hSyncpointEvent = NULL; - - // Initialize the packages. - for (DWORD i = 0; i < cPackages; ++i) - { - DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i; - BURN_PACKAGE* pPackage = rgPackages + iPackage; - - hr = InitializePackage(pPlan, pUX, pVariables, pPackage, relationType); - ExitOnFailure(hr, "Failed to initialize package."); - } - - // Initialize the patch targets after all packages, since they could rely on the requested state of packages that are after the patch's package in the chain. - for (DWORD i = 0; i < cPackages; ++i) - { - DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i; - BURN_PACKAGE* pPackage = rgPackages + iPackage; - - if (BURN_PACKAGE_TYPE_MSP == pPackage->type) - { - hr = MspEnginePlanInitializePackage(pPackage, pUX); - ExitOnFailure(hr, "Failed to initialize plan package: %ls", pPackage->sczId); - } - } - - // Plan the packages. - for (DWORD i = 0; i < cPackages; ++i) - { - DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i; - BURN_PACKAGE* pPackage = rgPackages + iPackage; - - hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, &hSyncpointEvent, &pRollbackBoundary); - ExitOnFailure(hr, "Failed to process package."); - } - - // If we still have an open rollback boundary, complete it. - if (pRollbackBoundary) - { - hr = PlanRollbackBoundaryComplete(pPlan); - ExitOnFailure(hr, "Failed to plan final rollback boundary complete."); - - pRollbackBoundary = NULL; - } - - if (fPlanCleanPackages) - { - // Plan clean up of packages. - for (DWORD i = 0; i < cPackages; ++i) - { - DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i; - BURN_PACKAGE* pPackage = rgPackages + iPackage; - - hr = PlanCleanPackage(pPlan, pPackage); - ExitOnFailure(hr, "Failed to plan clean package."); - } - } - - // Remove unnecessary actions. - hr = PlanFinalizeActions(pPlan); - ExitOnFailure(hr, "Failed to remove unnecessary actions from plan."); - - CalculateExpectedRegistrationStates(rgPackages, cPackages); - - // Let the BA know the actions that were planned. - for (DWORD i = 0; i < cPackages; ++i) - { - DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i; - BURN_PACKAGE* pPackage = rgPackages + iPackage; - - UserExperienceOnPlannedPackage(pUX, pPackage->sczId, pPackage->execute, pPackage->rollback, pPackage->fPlannedCache, pPackage->fPlannedUncache); - } - -LExit: - return hr; -} - -static HRESULT InitializePackage( - __in BURN_PLAN* pPlan, - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_VARIABLES* pVariables, - __in BURN_PACKAGE* pPackage, - __in BOOTSTRAPPER_RELATION_TYPE relationType - ) -{ - HRESULT hr = S_OK; - BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition = BOOTSTRAPPER_PACKAGE_CONDITION_DEFAULT; - BOOL fInstallCondition = FALSE; - BOOL fBeginCalled = FALSE; - - if (pPackage->fCanAffectRegistration) - { - pPackage->expectedCacheRegistrationState = pPackage->cacheRegistrationState; - pPackage->expectedInstallRegistrationState = pPackage->installRegistrationState; - } - - if (pPackage->sczInstallCondition && *pPackage->sczInstallCondition) - { - hr = ConditionEvaluate(pVariables, pPackage->sczInstallCondition, &fInstallCondition); - ExitOnFailure(hr, "Failed to evaluate install condition."); - - installCondition = fInstallCondition ? BOOTSTRAPPER_PACKAGE_CONDITION_TRUE : BOOTSTRAPPER_PACKAGE_CONDITION_FALSE; - } - - // Remember the default requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it. - hr = PlanDefaultPackageRequestState(pPackage->type, pPackage->currentState, !pPackage->fUninstallable, pPlan->action, installCondition, relationType, &pPackage->defaultRequested); - ExitOnFailure(hr, "Failed to set default package state."); - - pPackage->requested = pPackage->defaultRequested; - fBeginCalled = TRUE; - - hr = UserExperienceOnPlanPackageBegin(pUX, pPackage->sczId, pPackage->currentState, pPackage->fCached, installCondition, &pPackage->requested, &pPackage->cacheType); - ExitOnRootFailure(hr, "BA aborted plan package begin."); - - if (BURN_PACKAGE_TYPE_MSI == pPackage->type) - { - hr = MsiEnginePlanInitializePackage(pPackage, pVariables, pUX); - ExitOnFailure(hr, "Failed to initialize plan package: %ls", pPackage->sczId); - } - -LExit: - if (fBeginCalled) - { - UserExperienceOnPlanPackageComplete(pUX, pPackage->sczId, hr, pPackage->requested); - } - - return hr; -} - -static HRESULT ProcessPackage( - __in BOOL fBundlePerMachine, - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_DISPLAY display, - __inout HANDLE* phSyncpointEvent, - __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary - ) -{ - HRESULT hr = S_OK; - BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary = NULL; - - pEffectiveRollbackBoundary = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackage->pRollbackBoundaryBackward : pPackage->pRollbackBoundaryForward; - hr = ProcessPackageRollbackBoundary(pPlan, pEffectiveRollbackBoundary, ppRollbackBoundary); - ExitOnFailure(hr, "Failed to process package rollback boundary."); - - if (BOOTSTRAPPER_ACTION_LAYOUT == pPlan->action) - { - if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested) - { - hr = PlanLayoutPackage(pPlan, pPackage); - ExitOnFailure(hr, "Failed to plan layout package."); - } - } - else - { - if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested) - { - // If the package is in a requested state, plan it. - hr = PlanExecutePackage(fBundlePerMachine, display, pUX, pPlan, pPackage, pLog, pVariables, phSyncpointEvent); - ExitOnFailure(hr, "Failed to plan execute package."); - } - else - { - if (ForceCache(pPlan, pPackage)) - { - hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent); - ExitOnFailure(hr, "Failed to plan cache package."); - - if (pPackage->fPerMachine) - { - pPlan->fPerMachine = TRUE; - } - } - - // Make sure the package is properly ref-counted even if no plan is requested. - hr = PlanDependencyActions(fBundlePerMachine, pPlan, pPackage); - ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId); - } - } - - // Add the checkpoint after each package and dependency registration action. - if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute || BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback || BURN_DEPENDENCY_ACTION_NONE != pPackage->dependencyExecute) - { - hr = PlanExecuteCheckpoint(pPlan); - ExitOnFailure(hr, "Failed to append execute checkpoint."); - } - -LExit: - return hr; -} - -static HRESULT ProcessPackageRollbackBoundary( - __in BURN_PLAN* pPlan, - __in_opt BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary, - __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary - ) -{ - HRESULT hr = S_OK; - - // If the package marks the start of a rollback boundary, start a new one. - if (pEffectiveRollbackBoundary) - { - // Complete previous rollback boundary. - if (*ppRollbackBoundary) - { - hr = PlanRollbackBoundaryComplete(pPlan); - ExitOnFailure(hr, "Failed to plan rollback boundary complete."); - } - - // Start new rollback boundary. - hr = PlanRollbackBoundaryBegin(pPlan, pEffectiveRollbackBoundary); - ExitOnFailure(hr, "Failed to plan rollback boundary begin."); - - *ppRollbackBoundary = pEffectiveRollbackBoundary; - } - -LExit: - return hr; -} - -extern "C" HRESULT PlanLayoutContainer( - __in BURN_PLAN* pPlan, - __in BURN_CONTAINER* pContainer - ) -{ - HRESULT hr = S_OK; - BURN_CACHE_ACTION* pCacheAction = NULL; - - Assert(!pContainer->fPlanned); - pContainer->fPlanned = TRUE; - - if (pPlan->sczLayoutDirectory) - { - if (!pContainer->fAttached) - { - hr = AppendCacheAction(pPlan, &pCacheAction); - ExitOnFailure(hr, "Failed to append package start action."); - - pCacheAction->type = BURN_CACHE_ACTION_TYPE_CONTAINER; - pCacheAction->container.pContainer = pContainer; - - // Acquire + Verify + Finalize - pPlan->qwCacheSizeTotal += 3 * pContainer->qwFileSize; - } - } - else - { - if (!pContainer->fActuallyAttached) - { - // Acquire - pPlan->qwCacheSizeTotal += pContainer->qwFileSize; - } - } - - if (!pContainer->sczUnverifiedPath) - { - if (pContainer->fActuallyAttached) - { - hr = PathForCurrentProcess(&pContainer->sczUnverifiedPath, NULL); - ExitOnFailure(hr, "Failed to get path for executing module as attached container working path."); - } - else - { - hr = CacheCalculateContainerWorkingPath(pPlan->wzBundleId, pContainer, &pContainer->sczUnverifiedPath); - ExitOnFailure(hr, "Failed to calculate unverified path for container."); - } - } - -LExit: - return hr; -} - -extern "C" HRESULT PlanLayoutPackage( - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage - ) -{ - HRESULT hr = S_OK; - BURN_CACHE_ACTION* pCacheAction = NULL; - - hr = ProcessPayloadGroup(pPlan, &pPackage->payloads); - ExitOnFailure(hr, "Failed to process payload group for package: %ls.", pPackage->sczId); - - hr = AppendCacheAction(pPlan, &pCacheAction); - ExitOnFailure(hr, "Failed to append package start action."); - - pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE; - pCacheAction->package.pPackage = pPackage; - - ++pPlan->cOverallProgressTicksTotal; - -LExit: - return hr; -} - -extern "C" HRESULT PlanExecutePackage( - __in BOOL fPerMachine, - __in BOOTSTRAPPER_DISPLAY display, - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __inout HANDLE* phSyncpointEvent - ) -{ - HRESULT hr = S_OK; - BOOL fRequestedCache = BOOTSTRAPPER_REQUEST_STATE_CACHE == pPackage->requested || ForceCache(pPlan, pPackage); - - hr = CalculateExecuteActions(pPackage, pPlan->pActiveRollbackBoundary); - ExitOnFailure(hr, "Failed to calculate plan actions for package: %ls", pPackage->sczId); - - // Calculate package states based on reference count and plan certain dependency actions prior to planning the package execute action. - hr = DependencyPlanPackageBegin(fPerMachine, pPackage, pPlan); - ExitOnFailure(hr, "Failed to begin plan dependency actions for package: %ls", pPackage->sczId); - - if (fRequestedCache || NeedsCache(pPackage, TRUE)) - { - hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent); - ExitOnFailure(hr, "Failed to plan cache package."); - } - else if (!pPackage->fCached && NeedsCache(pPackage, FALSE)) - { - // TODO: this decision should be made during apply instead of plan based on whether the package is actually cached. - // If the package is not in the cache, disable any rollback that would require the package from the cache. - LogId(REPORT_STANDARD, MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE, pPackage->sczId, LoggingActionStateToString(pPackage->rollback)); - pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - } - - // Add the cache and install size to estimated size if it will be on the machine at the end of the install - if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || - fRequestedCache || - (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_ABSENT < pPackage->requested) - ) - { - // If the package will remain in the cache, add the package size to the estimated size - if (BOOTSTRAPPER_CACHE_TYPE_REMOVE < pPackage->cacheType) - { - pPlan->qwEstimatedSize += pPackage->qwSize; - } - - // If the package will end up installed on the machine, add the install size to the estimated size. - if (BOOTSTRAPPER_REQUEST_STATE_CACHE < pPackage->requested) - { - // MSP packages get cached automatically by windows installer with any embedded cabs, so include that in the size as well - if (BURN_PACKAGE_TYPE_MSP == pPackage->type) - { - pPlan->qwEstimatedSize += pPackage->qwSize; - } - - pPlan->qwEstimatedSize += pPackage->qwInstallSize; - } - } - - // Add execute actions. - switch (pPackage->type) - { - case BURN_PACKAGE_TYPE_EXE: - hr = ExeEnginePlanAddPackage(NULL, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent); - break; - - case BURN_PACKAGE_TYPE_MSI: - hr = MsiEnginePlanAddPackage(display, pUserExperience, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent); - break; - - case BURN_PACKAGE_TYPE_MSP: - hr = MspEnginePlanAddPackage(display, pUserExperience, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent); - break; - - case BURN_PACKAGE_TYPE_MSU: - hr = MsuEnginePlanAddPackage(pPackage, pPlan, pLog, pVariables, *phSyncpointEvent); - break; - - default: - hr = E_UNEXPECTED; - ExitOnFailure(hr, "Invalid package type."); - } - ExitOnFailure(hr, "Failed to add plan actions for package: %ls", pPackage->sczId); - - // Plan certain dependency actions after planning the package execute action. - hr = DependencyPlanPackageComplete(pPackage, pPlan); - ExitOnFailure(hr, "Failed to complete plan dependency actions for package: %ls", pPackage->sczId); - - // If we are going to take any action on this package, add progress for it. - if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute || BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback) - { - LoggingIncrementPackageSequence(); - - ++pPlan->cExecutePackagesTotal; - ++pPlan->cOverallProgressTicksTotal; - - // If package is per-machine and is being executed, flag the plan to be per-machine as well. - if (pPackage->fPerMachine) - { - pPlan->fPerMachine = TRUE; - } - } - -LExit: - return hr; -} - -extern "C" HRESULT PlanDefaultRelatedBundleRequestState( - __in BOOTSTRAPPER_RELATION_TYPE commandRelationType, - __in BOOTSTRAPPER_RELATION_TYPE relatedBundleRelationType, - __in BOOTSTRAPPER_ACTION action, - __in VERUTIL_VERSION* pRegistrationVersion, - __in VERUTIL_VERSION* pRelatedBundleVersion, - __inout BOOTSTRAPPER_REQUEST_STATE* pRequestState - ) -{ - HRESULT hr = S_OK; - int nCompareResult = 0; - - // Never touch related bundles during Cache. - if (BOOTSTRAPPER_ACTION_CACHE == action) - { - ExitFunction1(*pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE); - } - - switch (relatedBundleRelationType) - { - case BOOTSTRAPPER_RELATION_UPGRADE: - if (BOOTSTRAPPER_RELATION_UPGRADE != commandRelationType && BOOTSTRAPPER_ACTION_UNINSTALL < action) - { - hr = VerCompareParsedVersions(pRegistrationVersion, pRelatedBundleVersion, &nCompareResult); - ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistrationVersion ? pRegistrationVersion->sczVersion : NULL, pRelatedBundleVersion ? pRelatedBundleVersion->sczVersion : NULL); - - *pRequestState = (nCompareResult < 0) ? BOOTSTRAPPER_REQUEST_STATE_NONE : BOOTSTRAPPER_REQUEST_STATE_ABSENT; - } - break; - case BOOTSTRAPPER_RELATION_PATCH: __fallthrough; - case BOOTSTRAPPER_RELATION_ADDON: - if (BOOTSTRAPPER_ACTION_UNINSTALL == action) - { - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT; - } - else if (BOOTSTRAPPER_ACTION_INSTALL == action || BOOTSTRAPPER_ACTION_MODIFY == action) - { - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; - } - else if (BOOTSTRAPPER_ACTION_REPAIR == action) - { - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_REPAIR; - } - break; - case BOOTSTRAPPER_RELATION_DEPENDENT: - // Automatically repair dependent bundles to restore missing - // packages after uninstall unless we're being upgraded with the - // assumption that upgrades are cumulative (as intended). - if (BOOTSTRAPPER_RELATION_UPGRADE != commandRelationType && BOOTSTRAPPER_ACTION_UNINSTALL == action) - { - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_REPAIR; - } - break; - case BOOTSTRAPPER_RELATION_DETECT: - break; - default: - hr = E_UNEXPECTED; - ExitOnFailure(hr, "Unexpected relation type encountered during plan: %d", relatedBundleRelationType); - break; - } - -LExit: - return hr; -} - -extern "C" HRESULT PlanRelatedBundlesBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BURN_REGISTRATION* pRegistration, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in BURN_PLAN* pPlan - ) -{ - HRESULT hr = S_OK; - LPWSTR* rgsczAncestors = NULL; - UINT cAncestors = 0; - STRINGDICT_HANDLE sdAncestors = NULL; - - if (pRegistration->sczAncestors) - { - hr = StrSplitAllocArray(&rgsczAncestors, &cAncestors, pRegistration->sczAncestors, L";"); - ExitOnFailure(hr, "Failed to create string array from ancestors."); - - hr = DictCreateStringListFromArray(&sdAncestors, rgsczAncestors, cAncestors, DICT_FLAG_CASEINSENSITIVE); - ExitOnFailure(hr, "Failed to create dictionary from ancestors array."); - } - - for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) - { - BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i; - - if (!pRelatedBundle->fPlannable) - { - continue; - } - - pRelatedBundle->package.defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE; - pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE; - - // Do not execute the same bundle twice. - if (sdAncestors) - { - hr = DictKeyExists(sdAncestors, pRelatedBundle->package.sczId); - if (SUCCEEDED(hr)) - { - LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_SCHEDULED, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType)); - continue; - } - else if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to lookup the bundle ID in the ancestors dictionary."); - } - } - else if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_RELATION_NONE != relationType) - { - // Avoid repair loops for older bundles that do not handle ancestors. - LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_DEPENDENT, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRelationTypeToString(relationType)); - continue; - } - - // Pass along any ancestors and ourself to prevent infinite loops. - pRelatedBundle->package.Exe.wzAncestors = pRegistration->sczBundlePackageAncestors; - - hr = PlanDefaultRelatedBundleRequestState(relationType, pRelatedBundle->relationType, pPlan->action, pRegistration->pVersion, pRelatedBundle->pVersion, &pRelatedBundle->package.requested); - ExitOnFailure(hr, "Failed to get default request state for related bundle."); - - pRelatedBundle->package.defaultRequested = pRelatedBundle->package.requested; - - hr = UserExperienceOnPlanRelatedBundle(pUserExperience, pRelatedBundle->package.sczId, &pRelatedBundle->package.requested); - ExitOnRootFailure(hr, "BA aborted plan related bundle."); - - // Log when the BA changed the bundle state so the engine doesn't get blamed for planning the wrong thing. - if (pRelatedBundle->package.requested != pRelatedBundle->package.defaultRequested) - { - LogId(REPORT_STANDARD, MSG_PLANNED_BUNDLE_UX_CHANGED_REQUEST, pRelatedBundle->package.sczId, LoggingRequestStateToString(pRelatedBundle->package.requested), LoggingRequestStateToString(pRelatedBundle->package.defaultRequested)); - } - - // If uninstalling and the dependent related bundle may be executed, ignore its provider key to allow for downgrades with ref-counting. - if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested) - { - if (0 < pRelatedBundle->package.cDependencyProviders) - { - // Bundles only support a single provider key. - const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders; - - hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, pProvider->sczDisplayName); - ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey); - } - } - } - -LExit: - ReleaseDict(sdAncestors); - ReleaseStrArray(rgsczAncestors, cAncestors); - - return hr; -} - -extern "C" HRESULT PlanRelatedBundlesComplete( - __in BURN_REGISTRATION* pRegistration, - __in BURN_PLAN* pPlan, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in DWORD dwExecuteActionEarlyIndex - ) -{ - HRESULT hr = S_OK; - LPWSTR sczIgnoreDependencies = NULL; - STRINGDICT_HANDLE sdProviderKeys = NULL; - - // Get the list of dependencies to ignore to pass to related bundles. - hr = DependencyAllocIgnoreDependencies(pPlan, &sczIgnoreDependencies); - ExitOnFailure(hr, "Failed to get the list of dependencies to ignore."); - - hr = DictCreateStringList(&sdProviderKeys, pPlan->cExecuteActions, DICT_FLAG_CASEINSENSITIVE); - ExitOnFailure(hr, "Failed to create dictionary for planned packages."); - - BOOL fExecutingAnyPackage = FALSE; - - for (DWORD i = 0; i < pPlan->cExecuteActions; ++i) - { - if (BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE == pPlan->rgExecuteActions[i].type && BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].exePackage.action) - { - fExecutingAnyPackage = TRUE; - - BURN_PACKAGE* pPackage = pPlan->rgExecuteActions[i].packageProvider.pPackage; - if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) - { - if (0 < pPackage->cDependencyProviders) - { - // Bundles only support a single provider key. - const BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders; - DictAddKey(sdProviderKeys, pProvider->sczKey); - } - } - } - else - { - switch (pPlan->rgExecuteActions[i].type) - { - case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: - fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].msiPackage.action); - break; - - case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: - fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].mspTarget.action); - break; - - case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: - fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].msuPackage.action); - break; - } - } - } - - for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) - { - DWORD *pdwInsertIndex = NULL; - BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i; - - if (!pRelatedBundle->fPlannable) - { - continue; - } - - // Do not execute if a major upgrade to the related bundle is an embedded bundle (Provider keys are the same) - if (0 < pRelatedBundle->package.cDependencyProviders) - { - // Bundles only support a single provider key. - const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders; - hr = DictKeyExists(sdProviderKeys, pProvider->sczKey); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to check the dictionary for a related bundle provider key: \"%ls\".", pProvider->sczKey); - // Key found, so there is an embedded bundle with the same provider key that will be executed. So this related bundle should not be added to the plan - LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), pProvider->sczKey); - continue; - } - else - { - hr = S_OK; - } - } - - // For an uninstall, there is no need to repair dependent bundles if no packages are executing. - if (!fExecutingAnyPackage && BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pRelatedBundle->package.requested && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) - { - pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE; - LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DEPENDENT_BUNDLE_REPAIR, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType)); - } - - if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType) - { - // Addon and patch bundles will be passed a list of dependencies to ignore for planning. - hr = StrAllocString(&pRelatedBundle->package.Exe.sczIgnoreDependencies, sczIgnoreDependencies, 0); - ExitOnFailure(hr, "Failed to copy the list of dependencies to ignore."); - - // Uninstall addons and patches early in the chain, before other packages are uninstalled. - if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) - { - pdwInsertIndex = &dwExecuteActionEarlyIndex; - } - } - - if (BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested) - { - hr = ExeEnginePlanCalculatePackage(&pRelatedBundle->package); - ExitOnFailure(hr, "Failed to calcuate plan for related bundle: %ls", pRelatedBundle->package.sczId); - - // Calculate package states based on reference count for addon and patch related bundles. - if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType) - { - hr = DependencyPlanPackageBegin(pRegistration->fPerMachine, &pRelatedBundle->package, pPlan); - ExitOnFailure(hr, "Failed to begin plan dependency actions to package: %ls", pRelatedBundle->package.sczId); - - // If uninstalling a related bundle, make sure the bundle is uninstalled after removing registration. - if (pdwInsertIndex && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) - { - ++(*pdwInsertIndex); - } - } - - hr = ExeEnginePlanAddPackage(pdwInsertIndex, &pRelatedBundle->package, pPlan, pLog, pVariables, NULL); - ExitOnFailure(hr, "Failed to add to plan related bundle: %ls", pRelatedBundle->package.sczId); - - // Calculate package states based on reference count for addon and patch related bundles. - if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType) - { - hr = DependencyPlanPackageComplete(&pRelatedBundle->package, pPlan); - ExitOnFailure(hr, "Failed to complete plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId); - } - - // If we are going to take any action on this package, add progress for it. - if (BOOTSTRAPPER_ACTION_STATE_NONE != pRelatedBundle->package.execute || BOOTSTRAPPER_ACTION_STATE_NONE != pRelatedBundle->package.rollback) - { - LoggingIncrementPackageSequence(); - - ++pPlan->cExecutePackagesTotal; - ++pPlan->cOverallProgressTicksTotal; - } - - // If package is per-machine and is being executed, flag the plan to be per-machine as well. - if (pRelatedBundle->package.fPerMachine) - { - pPlan->fPerMachine = TRUE; - } - } - else if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType) - { - // Make sure the package is properly ref-counted even if no plan is requested. - hr = DependencyPlanPackageBegin(pRegistration->fPerMachine, &pRelatedBundle->package, pPlan); - ExitOnFailure(hr, "Failed to begin plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId); - - hr = DependencyPlanPackage(pdwInsertIndex, &pRelatedBundle->package, pPlan); - ExitOnFailure(hr, "Failed to plan related bundle package provider actions."); - - hr = DependencyPlanPackageComplete(&pRelatedBundle->package, pPlan); - ExitOnFailure(hr, "Failed to complete plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId); - } - } - -LExit: - ReleaseDict(sdProviderKeys); - ReleaseStr(sczIgnoreDependencies); - - return hr; -} - -extern "C" HRESULT PlanFinalizeActions( - __in BURN_PLAN* pPlan - ) -{ - HRESULT hr = S_OK; - - FinalizePatchActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions); - - FinalizePatchActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions); - - RemoveUnnecessaryActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions); - - RemoveUnnecessaryActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions); - - return hr; -} - -extern "C" HRESULT PlanCleanPackage( - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage - ) -{ - HRESULT hr = S_OK; - BOOL fPlanCleanPackage = FALSE; - BURN_CLEAN_ACTION* pCleanAction = NULL; - - // The following is a complex set of logic that determines when a package should be cleaned from the cache. - if (BOOTSTRAPPER_CACHE_TYPE_FORCE > pPackage->cacheType || BOOTSTRAPPER_ACTION_CACHE > pPlan->action) - { - // The following are all different reasons why the package should be cleaned from the cache. - // The else-ifs are used to make the conditions easier to see (rather than have them combined - // in one huge condition). - if (BOOTSTRAPPER_CACHE_TYPE_KEEP > pPackage->cacheType) // easy, package is not supposed to stay cached. - { - fPlanCleanPackage = TRUE; - } - else if ((BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested || - BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested) && // requested to be removed and - BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) // actually being removed. - { - fPlanCleanPackage = TRUE; - } - else if ((BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested || - BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested) && // requested to be removed but - BOOTSTRAPPER_ACTION_STATE_NONE == pPackage->execute && // execute is do nothing and - !pPackage->fDependencyManagerWasHere && // dependency manager didn't change execute and - BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState) // currently not installed. - { - fPlanCleanPackage = TRUE; - } - else if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && // uninstalling and - BOOTSTRAPPER_REQUEST_STATE_NONE == pPackage->requested && // requested do nothing (aka: default) and - BOOTSTRAPPER_ACTION_STATE_NONE == pPackage->execute && // execute is still do nothing and - !pPackage->fDependencyManagerWasHere && // dependency manager didn't change execute and - BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState) // currently not installed. - { - fPlanCleanPackage = TRUE; - } - } - - if (fPlanCleanPackage) - { - hr = MemEnsureArraySize(reinterpret_cast(&pPlan->rgCleanActions), pPlan->cCleanActions + 1, sizeof(BURN_CLEAN_ACTION), 5); - ExitOnFailure(hr, "Failed to grow plan's array of clean actions."); - - pCleanAction = pPlan->rgCleanActions + pPlan->cCleanActions; - ++pPlan->cCleanActions; - - pCleanAction->pPackage = pPackage; - - pPackage->fPlannedUncache = TRUE; - - if (pPackage->fCanAffectRegistration) - { - pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - } - } - -LExit: - return hr; -} - -extern "C" HRESULT PlanExecuteCacheSyncAndRollback( - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage, - __in HANDLE hCacheEvent - ) -{ - HRESULT hr = S_OK; - BURN_EXECUTE_ACTION* pAction = NULL; - - hr = PlanAppendExecuteAction(pPlan, &pAction); - ExitOnFailure(hr, "Failed to append wait action for caching."); - - pAction->type = BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT; - pAction->syncpoint.hEvent = hCacheEvent; - - hr = PlanAppendRollbackAction(pPlan, &pAction); - ExitOnFailure(hr, "Failed to append rollback action."); - - pAction->type = BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE; - pAction->uncachePackage.pPackage = pPackage; - - hr = PlanExecuteCheckpoint(pPlan); - ExitOnFailure(hr, "Failed to append execute checkpoint for cache rollback."); - -LExit: - return hr; -} - -extern "C" HRESULT PlanExecuteCheckpoint( - __in BURN_PLAN* pPlan - ) -{ - HRESULT hr = S_OK; - BURN_EXECUTE_ACTION* pAction = NULL; - DWORD dwCheckpointId = GetNextCheckpointId(pPlan); - - // execute checkpoint - hr = PlanAppendExecuteAction(pPlan, &pAction); - ExitOnFailure(hr, "Failed to append execute action."); - - pAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT; - pAction->checkpoint.dwId = dwCheckpointId; - pAction->checkpoint.pActiveRollbackBoundary = pPlan->pActiveRollbackBoundary; - - // rollback checkpoint - hr = PlanAppendRollbackAction(pPlan, &pAction); - ExitOnFailure(hr, "Failed to append rollback action."); - - pAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT; - pAction->checkpoint.dwId = dwCheckpointId; - pAction->checkpoint.pActiveRollbackBoundary = pPlan->pActiveRollbackBoundary; - -LExit: - return hr; -} - -extern "C" HRESULT PlanInsertExecuteAction( - __in DWORD dwIndex, - __in BURN_PLAN* pPlan, - __out BURN_EXECUTE_ACTION** ppExecuteAction - ) -{ - HRESULT hr = S_OK; - - hr = MemInsertIntoArray((void**)&pPlan->rgExecuteActions, dwIndex, 1, pPlan->cExecuteActions + 1, sizeof(BURN_EXECUTE_ACTION), 5); - ExitOnFailure(hr, "Failed to grow plan's array of execute actions."); - - *ppExecuteAction = pPlan->rgExecuteActions + dwIndex; - ++pPlan->cExecuteActions; - -LExit: - return hr; -} - -extern "C" HRESULT PlanInsertRollbackAction( - __in DWORD dwIndex, - __in BURN_PLAN* pPlan, - __out BURN_EXECUTE_ACTION** ppRollbackAction - ) -{ - HRESULT hr = S_OK; - - hr = MemInsertIntoArray((void**)&pPlan->rgRollbackActions, dwIndex, 1, pPlan->cRollbackActions + 1, sizeof(BURN_EXECUTE_ACTION), 5); - ExitOnFailure(hr, "Failed to grow plan's array of rollback actions."); - - *ppRollbackAction = pPlan->rgRollbackActions + dwIndex; - ++pPlan->cRollbackActions; - -LExit: - return hr; -} - -extern "C" HRESULT PlanAppendExecuteAction( - __in BURN_PLAN* pPlan, - __out BURN_EXECUTE_ACTION** ppExecuteAction - ) -{ - HRESULT hr = S_OK; - - hr = MemEnsureArraySize((void**)&pPlan->rgExecuteActions, pPlan->cExecuteActions + 1, sizeof(BURN_EXECUTE_ACTION), 5); - ExitOnFailure(hr, "Failed to grow plan's array of execute actions."); - - *ppExecuteAction = pPlan->rgExecuteActions + pPlan->cExecuteActions; - ++pPlan->cExecuteActions; - -LExit: - return hr; -} - -extern "C" HRESULT PlanAppendRollbackAction( - __in BURN_PLAN* pPlan, - __out BURN_EXECUTE_ACTION** ppRollbackAction - ) -{ - HRESULT hr = S_OK; - - hr = MemEnsureArraySize((void**)&pPlan->rgRollbackActions, pPlan->cRollbackActions + 1, sizeof(BURN_EXECUTE_ACTION), 5); - ExitOnFailure(hr, "Failed to grow plan's array of rollback actions."); - - *ppRollbackAction = pPlan->rgRollbackActions + pPlan->cRollbackActions; - ++pPlan->cRollbackActions; - -LExit: - return hr; -} - -extern "C" HRESULT PlanRollbackBoundaryBegin( - __in BURN_PLAN* pPlan, - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary - ) -{ - HRESULT hr = S_OK; - BURN_EXECUTE_ACTION* pExecuteAction = NULL; - - AssertSz(!pPlan->pActiveRollbackBoundary, "PlanRollbackBoundaryBegin called without completing previous RollbackBoundary"); - pPlan->pActiveRollbackBoundary = pRollbackBoundary; - - // Add begin rollback boundary to execute plan. - hr = PlanAppendExecuteAction(pPlan, &pExecuteAction); - ExitOnFailure(hr, "Failed to append rollback boundary begin action."); - - pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY; - pExecuteAction->rollbackBoundary.pRollbackBoundary = pRollbackBoundary; - - // Add begin rollback boundary to rollback plan. - hr = PlanAppendRollbackAction(pPlan, &pExecuteAction); - ExitOnFailure(hr, "Failed to append rollback boundary begin action."); - - pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY; - pExecuteAction->rollbackBoundary.pRollbackBoundary = pRollbackBoundary; - - // Add begin MSI transaction to execute plan. - if (pRollbackBoundary->fTransaction) - { - hr = PlanExecuteCheckpoint(pPlan); - ExitOnFailure(hr, "Failed to append checkpoint before MSI transaction begin action."); - - hr = PlanAppendExecuteAction(pPlan, &pExecuteAction); - ExitOnFailure(hr, "Failed to append MSI transaction begin action."); - - pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION; - pExecuteAction->msiTransaction.pRollbackBoundary = pRollbackBoundary; - } - -LExit: - return hr; -} - -extern "C" HRESULT PlanRollbackBoundaryComplete( - __in BURN_PLAN* pPlan - ) -{ - HRESULT hr = S_OK; - BURN_EXECUTE_ACTION* pExecuteAction = NULL; - BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = pPlan->pActiveRollbackBoundary; - - AssertSz(pRollbackBoundary, "PlanRollbackBoundaryComplete called without an active RollbackBoundary"); - - if (pRollbackBoundary && pRollbackBoundary->fTransaction) - { - // Add commit MSI transaction to execute plan. - hr = PlanAppendExecuteAction(pPlan, &pExecuteAction); - ExitOnFailure(hr, "Failed to append MSI transaction commit action."); - - pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION; - pExecuteAction->msiTransaction.pRollbackBoundary = pRollbackBoundary; - } - - pPlan->pActiveRollbackBoundary = NULL; - - // Add checkpoints. - hr = PlanExecuteCheckpoint(pPlan); - -LExit: - return hr; -} - -/******************************************************************* - PlanSetResumeCommand - Initializes resume command string - -*******************************************************************/ -extern "C" HRESULT PlanSetResumeCommand( - __in BURN_REGISTRATION* pRegistration, - __in BOOTSTRAPPER_ACTION action, - __in BOOTSTRAPPER_COMMAND* pCommand, - __in BURN_LOGGING* pLog - ) -{ - HRESULT hr = S_OK; - - // build the resume command-line. - hr = CoreRecreateCommandLine(&pRegistration->sczResumeCommandLine, action, pCommand->display, pCommand->restart, pCommand->relationType, pCommand->fPassthrough, pRegistration->sczActiveParent, pRegistration->sczAncestors, pLog->sczPath, pCommand->wzCommandLine); - ExitOnFailure(hr, "Failed to recreate resume command-line."); - -LExit: - return hr; -} - - -// internal function definitions - -static void UninitializeRegistrationAction( - __in BURN_DEPENDENT_REGISTRATION_ACTION* pAction - ) -{ - ReleaseStr(pAction->sczDependentProviderKey); - ReleaseStr(pAction->sczBundleId); - memset(pAction, 0, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION)); -} - -static void UninitializeCacheAction( - __in BURN_CACHE_ACTION* pCacheAction - ) -{ - switch (pCacheAction->type) - { - case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT: - ReleaseHandle(pCacheAction->syncpoint.hEvent); - break; - - case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: - ReleaseStr(pCacheAction->bundleLayout.sczExecutableName); - ReleaseStr(pCacheAction->bundleLayout.sczUnverifiedPath); - break; - } -} - -static void ResetPlannedContainerState( - __in BURN_CONTAINER* pContainer - ) -{ - pContainer->fPlanned = FALSE; - pContainer->qwExtractSizeTotal = 0; - pContainer->qwCommittedCacheProgress = 0; - pContainer->qwCommittedExtractProgress = 0; - pContainer->hrExtract = S_OK; -} - -static void ResetPlannedPayloadsState( - __in BURN_PAYLOADS* pPayloads - ) -{ - for (DWORD i = 0; i < pPayloads->cPayloads; ++i) - { - BURN_PAYLOAD* pPayload = pPayloads->rgPayloads + i; - - pPayload->cRemainingInstances = 0; - pPayload->state = BURN_PAYLOAD_STATE_NONE; - ReleaseNullStr(pPayload->sczLocalFilePath); - } -} - -static void ResetPlannedPayloadGroupState( - __in BURN_PAYLOAD_GROUP* pPayloadGroup - ) -{ - for (DWORD i = 0; i < pPayloadGroup->cItems; ++i) - { - BURN_PAYLOAD_GROUP_ITEM* pItem = pPayloadGroup->rgItems + i; - - pItem->fCached = FALSE; - pItem->qwCommittedCacheProgress = 0; - } -} - -static void ResetPlannedPackageState( - __in BURN_PACKAGE* pPackage - ) -{ - // Reset package state that is a result of planning. - pPackage->cacheType = pPackage->authoredCacheType; - pPackage->defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE; - pPackage->requested = BOOTSTRAPPER_REQUEST_STATE_NONE; - pPackage->fPlannedCache = FALSE; - pPackage->fPlannedUncache = FALSE; - pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; - pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - pPackage->providerExecute = BURN_DEPENDENCY_ACTION_NONE; - pPackage->providerRollback = BURN_DEPENDENCY_ACTION_NONE; - pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE; - pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE; - pPackage->fDependencyManagerWasHere = FALSE; - pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; - pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; - - ReleaseNullStr(pPackage->sczCacheFolder); - - if (BURN_PACKAGE_TYPE_MSI == pPackage->type) - { - for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) - { - BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; - - pFeature->expectedState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; - pFeature->defaultRequested = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; - pFeature->requested = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; - pFeature->execute = BOOTSTRAPPER_FEATURE_ACTION_NONE; - pFeature->rollback = BOOTSTRAPPER_FEATURE_ACTION_NONE; - } - - for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) - { - BURN_SLIPSTREAM_MSP* pSlipstreamMsp = &pPackage->Msi.rgSlipstreamMsps[i]; - - pSlipstreamMsp->execute = BOOTSTRAPPER_ACTION_STATE_NONE; - pSlipstreamMsp->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - } - } - else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.rgTargetProducts) - { - for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) - { - BURN_MSPTARGETPRODUCT* pTargetProduct = &pPackage->Msp.rgTargetProducts[i]; - - pTargetProduct->defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE; - pTargetProduct->requested = BOOTSTRAPPER_REQUEST_STATE_NONE; - pTargetProduct->execute = BOOTSTRAPPER_ACTION_STATE_NONE; - pTargetProduct->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - pTargetProduct->executeSkip = BURN_PATCH_SKIP_STATE_NONE; - pTargetProduct->rollbackSkip = BURN_PATCH_SKIP_STATE_NONE; - } - } - - ResetPlannedPayloadGroupState(&pPackage->payloads); -} - -static void ResetPlannedRollbackBoundaryState( - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary - ) -{ - pRollbackBoundary->fActiveTransaction = FALSE; - ReleaseNullStr(pRollbackBoundary->sczLogPath); -} - -static HRESULT GetActionDefaultRequestState( - __in BOOTSTRAPPER_ACTION action, - __in BOOL fPermanent, - __in BOOTSTRAPPER_PACKAGE_STATE currentState, - __out BOOTSTRAPPER_REQUEST_STATE* pRequestState - ) -{ - HRESULT hr = S_OK; - - switch (action) - { - case BOOTSTRAPPER_ACTION_CACHE: - switch (currentState) - { - case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; - break; - - default: - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE; - break; - } - break; - - case BOOTSTRAPPER_ACTION_INSTALL: __fallthrough; - case BOOTSTRAPPER_ACTION_UPDATE_REPLACE: __fallthrough; - case BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED: - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; - break; - - case BOOTSTRAPPER_ACTION_REPAIR: - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_REPAIR; - break; - - case BOOTSTRAPPER_ACTION_UNINSTALL: - *pRequestState = fPermanent ? BOOTSTRAPPER_REQUEST_STATE_NONE : BOOTSTRAPPER_REQUEST_STATE_ABSENT; - break; - - case BOOTSTRAPPER_ACTION_MODIFY: - switch (currentState) - { - case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT; - break; - - case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; - break; - - default: - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; - break; - } - break; - - default: - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Invalid action state."); - } - -LExit: - return hr; -} - -static HRESULT AddRegistrationAction( - __in BURN_PLAN* pPlan, - __in BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type, - __in_z LPCWSTR wzDependentProviderKey, - __in_z LPCWSTR wzOwnerBundleId - ) -{ - HRESULT hr = S_OK; - BURN_DEPENDENT_REGISTRATION_ACTION_TYPE rollbackType = (BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER == type) ? BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER : BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER; - BURN_DEPENDENT_REGISTRATION_ACTION* pAction = NULL; - - // Create forward registration action. - hr = MemEnsureArraySize((void**)&pPlan->rgRegistrationActions, pPlan->cRegistrationActions + 1, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION), 5); - ExitOnFailure(hr, "Failed to grow plan's array of registration actions."); - - pAction = pPlan->rgRegistrationActions + pPlan->cRegistrationActions; - ++pPlan->cRegistrationActions; - - pAction->type = type; - - hr = StrAllocString(&pAction->sczBundleId, wzOwnerBundleId, 0); - ExitOnFailure(hr, "Failed to copy owner bundle to registration action."); - - hr = StrAllocString(&pAction->sczDependentProviderKey, wzDependentProviderKey, 0); - ExitOnFailure(hr, "Failed to copy dependent provider key to registration action."); - - // Create rollback registration action. - hr = MemEnsureArraySize((void**)&pPlan->rgRollbackRegistrationActions, pPlan->cRollbackRegistrationActions + 1, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION), 5); - ExitOnFailure(hr, "Failed to grow plan's array of rollback registration actions."); - - pAction = pPlan->rgRollbackRegistrationActions + pPlan->cRollbackRegistrationActions; - ++pPlan->cRollbackRegistrationActions; - - pAction->type = rollbackType; - - hr = StrAllocString(&pAction->sczBundleId, wzOwnerBundleId, 0); - ExitOnFailure(hr, "Failed to copy owner bundle to registration action."); - - hr = StrAllocString(&pAction->sczDependentProviderKey, wzDependentProviderKey, 0); - ExitOnFailure(hr, "Failed to copy dependent provider key to rollback registration action."); - -LExit: - return hr; -} - -static HRESULT AddCachePackage( - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage, - __out HANDLE* phSyncpointEvent - ) -{ - HRESULT hr = S_OK; - - // If this is an MSI package with slipstream MSPs, ensure the MSPs are cached first. - if (BURN_PACKAGE_TYPE_MSI == pPackage->type && 0 < pPackage->Msi.cSlipstreamMspPackages) - { - hr = AddCacheSlipstreamMsps(pPlan, pPackage); - ExitOnFailure(hr, "Failed to plan slipstream patches for package."); - } - - hr = AddCachePackageHelper(pPlan, pPackage, phSyncpointEvent); - ExitOnFailure(hr, "Failed to plan cache package."); - -LExit: - return hr; -} - -static HRESULT AddCachePackageHelper( - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage, - __out HANDLE* phSyncpointEvent - ) -{ - AssertSz(pPackage->sczCacheId && *pPackage->sczCacheId, "AddCachePackageHelper() expects the package to have a cache id."); - - HRESULT hr = S_OK; - BURN_CACHE_ACTION* pCacheAction = NULL; - DWORD dwCheckpoint = 0; - - BOOL fPlanned = AlreadyPlannedCachePackage(pPlan, pPackage->sczId, phSyncpointEvent); - if (fPlanned) - { - ExitFunction(); - } - - // Cache checkpoints happen before the package is cached because downloading packages' - // payloads will not roll themselves back the way installation packages rollback on - // failure automatically. - dwCheckpoint = GetNextCheckpointId(pPlan); - - hr = AppendCacheAction(pPlan, &pCacheAction); - ExitOnFailure(hr, "Failed to append checkpoint before package start action."); - - pCacheAction->type = BURN_CACHE_ACTION_TYPE_CHECKPOINT; - pCacheAction->checkpoint.dwId = dwCheckpoint; - - // Only plan the cache rollback if the package is also going to be uninstalled; - // otherwise, future operations like repair will not be able to locate the cached package. - BOOL fPlanCacheRollback = (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->rollback); - - if (fPlanCacheRollback) - { - hr = AppendRollbackCacheAction(pPlan, &pCacheAction); - ExitOnFailure(hr, "Failed to append rollback cache action."); - - pCacheAction->type = BURN_CACHE_ACTION_TYPE_CHECKPOINT; - pCacheAction->checkpoint.dwId = dwCheckpoint; - } - - hr = PlanLayoutPackage(pPlan, pPackage); - ExitOnFailure(hr, "Failed to plan cache for package."); - - // Create syncpoint action. - hr = AppendCacheAction(pPlan, &pCacheAction); - ExitOnFailure(hr, "Failed to append cache action."); - - pCacheAction->type = BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT; - pCacheAction->syncpoint.hEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL); - ExitOnNullWithLastError(pCacheAction->syncpoint.hEvent, hr, "Failed to create syncpoint event."); - - *phSyncpointEvent = pCacheAction->syncpoint.hEvent; - - pPackage->fPlannedCache = TRUE; - if (pPackage->fCanAffectRegistration) - { - pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } - -LExit: - return hr; -} - -static HRESULT AddCacheSlipstreamMsps( - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage - ) -{ - HRESULT hr = S_OK; - HANDLE hIgnored = NULL; - - AssertSz(BURN_PACKAGE_TYPE_MSI == pPackage->type, "Only MSI packages can have slipstream patches."); - - for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) - { - BURN_PACKAGE* pMspPackage = pPackage->Msi.rgSlipstreamMsps[i].pMspPackage; - AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches."); - - hr = AddCachePackageHelper(pPlan, pMspPackage, &hIgnored); - ExitOnFailure(hr, "Failed to plan slipstream MSP: %ls", pMspPackage->sczId); - } - -LExit: - return hr; -} - -static BOOL AlreadyPlannedCachePackage( - __in BURN_PLAN* pPlan, - __in_z LPCWSTR wzPackageId, - __out HANDLE* phSyncpointEvent - ) -{ - BOOL fPlanned = FALSE; - - for (DWORD iCacheAction = 0; iCacheAction < pPlan->cCacheActions; ++iCacheAction) - { - BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + iCacheAction; - - if (BURN_CACHE_ACTION_TYPE_PACKAGE == pCacheAction->type) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pCacheAction->package.pPackage->sczId, -1, wzPackageId, -1)) - { - if (iCacheAction + 1 < pPlan->cCacheActions && BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT == pPlan->rgCacheActions[iCacheAction + 1].type) - { - *phSyncpointEvent = pPlan->rgCacheActions[iCacheAction + 1].syncpoint.hEvent; - } - - fPlanned = TRUE; - break; - } - } - } - - return fPlanned; -} - -static DWORD GetNextCheckpointId( - __in BURN_PLAN* pPlan - ) -{ - return ++pPlan->dwNextCheckpointId; -} - -static HRESULT AppendCacheAction( - __in BURN_PLAN* pPlan, - __out BURN_CACHE_ACTION** ppCacheAction - ) -{ - HRESULT hr = S_OK; - - hr = MemEnsureArraySize(reinterpret_cast(&pPlan->rgCacheActions), pPlan->cCacheActions + 1, sizeof(BURN_CACHE_ACTION), 5); - ExitOnFailure(hr, "Failed to grow plan's array of cache actions."); - - *ppCacheAction = pPlan->rgCacheActions + pPlan->cCacheActions; - ++pPlan->cCacheActions; - -LExit: - return hr; -} - -static HRESULT AppendRollbackCacheAction( - __in BURN_PLAN* pPlan, - __out BURN_CACHE_ACTION** ppCacheAction - ) -{ - HRESULT hr = S_OK; - - hr = MemEnsureArraySize(reinterpret_cast(&pPlan->rgRollbackCacheActions), pPlan->cRollbackCacheActions + 1, sizeof(BURN_CACHE_ACTION), 5); - ExitOnFailure(hr, "Failed to grow plan's array of rollback cache actions."); - - *ppCacheAction = pPlan->rgRollbackCacheActions + pPlan->cRollbackCacheActions; - ++pPlan->cRollbackCacheActions; - -LExit: - return hr; -} - -static HRESULT ProcessPayloadGroup( - __in BURN_PLAN* pPlan, - __in BURN_PAYLOAD_GROUP* pPayloadGroup - ) -{ - HRESULT hr = S_OK; - - for (DWORD i = 0; i < pPayloadGroup->cItems; ++i) - { - BURN_PAYLOAD_GROUP_ITEM* pItem = pPayloadGroup->rgItems + i; - BURN_PAYLOAD* pPayload = pItem->pPayload; - - pPayload->cRemainingInstances += 1; - - if (pPayload->pContainer && !pPayload->pContainer->fPlanned) - { - hr = PlanLayoutContainer(pPlan, pPayload->pContainer); - ExitOnFailure(hr, "Failed to plan container: %ls", pPayload->pContainer->sczId); - } - - if (!pPlan->sczLayoutDirectory || !pPayload->pContainer) - { - // Acquire + Verify + Finalize - pPlan->qwCacheSizeTotal += 3 * pPayload->qwFileSize; - - if (!pPlan->sczLayoutDirectory) - { - // Staging - pPlan->qwCacheSizeTotal += pPayload->qwFileSize; - } - } - - if (!pPlan->sczLayoutDirectory && pPayload->pContainer && 1 == pPayload->cRemainingInstances) - { - // Extract - pPlan->qwCacheSizeTotal += pPayload->qwFileSize; - pPayload->pContainer->qwExtractSizeTotal += pPayload->qwFileSize; - } - - if (!pPayload->sczUnverifiedPath) - { - hr = CacheCalculatePayloadWorkingPath(pPlan->wzBundleId, pPayload, &pPayload->sczUnverifiedPath); - ExitOnFailure(hr, "Failed to calculate unverified path for payload."); - } - } - -LExit: - return hr; -} - -static void RemoveUnnecessaryActions( - __in BOOL fExecute, - __in BURN_EXECUTE_ACTION* rgActions, - __in DWORD cActions - ) -{ - LPCSTR szExecuteOrRollback = fExecute ? "execute" : "rollback"; - - for (DWORD i = 0; i < cActions; ++i) - { - BURN_EXECUTE_ACTION* pAction = rgActions + i; - - if (BURN_EXECUTE_ACTION_TYPE_MSP_TARGET == pAction->type && pAction->mspTarget.pChainedTargetPackage) - { - BURN_MSPTARGETPRODUCT* pFirstTargetProduct = pAction->mspTarget.rgOrderedPatches->pTargetProduct; - BURN_PATCH_SKIP_STATE skipState = fExecute ? pFirstTargetProduct->executeSkip : pFirstTargetProduct->rollbackSkip; - BOOTSTRAPPER_ACTION_STATE chainedTargetPackageAction = fExecute ? pAction->mspTarget.pChainedTargetPackage->execute : pAction->mspTarget.pChainedTargetPackage->rollback; - - switch (skipState) - { - case BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL: - pAction->fDeleted = TRUE; - LogId(REPORT_STANDARD, MSG_PLAN_SKIP_PATCH_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback); - break; - case BURN_PATCH_SKIP_STATE_SLIPSTREAM: - pAction->fDeleted = TRUE; - LogId(REPORT_STANDARD, MSG_PLAN_SKIP_SLIPSTREAM_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback); - break; - } - } - } -} - -static void FinalizePatchActions( - __in BOOL fExecute, - __in BURN_EXECUTE_ACTION* rgActions, - __in DWORD cActions - ) -{ - for (DWORD i = 0; i < cActions; ++i) - { - BURN_EXECUTE_ACTION* pAction = rgActions + i; - - if (BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE == pAction->type) - { - BURN_PACKAGE* pPackage = pAction->msiPackage.pPackage; - AssertSz(BOOTSTRAPPER_ACTION_STATE_NONE < pAction->msiPackage.action, "Planned execute MSI action to do nothing"); - - if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->msiPackage.action) - { - // If we are uninstalling the MSI, we must skip all the patches. - for (DWORD j = 0; j < pPackage->Msi.cChainedPatches; ++j) - { - BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + j; - BURN_MSPTARGETPRODUCT* pTargetProduct = pChainedPatch->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex; - - if (fExecute) - { - pTargetProduct->execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; - pTargetProduct->executeSkip = BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL; - } - else - { - pTargetProduct->rollback = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; - pTargetProduct->rollbackSkip = BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL; - } - } - } - else - { - // If the slipstream target is being installed or upgraded (not uninstalled or repaired) then we will slipstream so skip - // the patch's standalone action. Also, if the slipstream target is being repaired and the patch is being - // repaired, skip this operation since it will be redundant. - // - // The primary goal here is to ensure that a slipstream patch that is yet not installed is installed even if the MSI - // is already on the machine. The slipstream must be installed standalone if the MSI is being repaired. - for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) - { - BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + j; - BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + pSlipstreamMsp->dwMsiChainedPatchIndex; - BURN_MSPTARGETPRODUCT* pTargetProduct = pSlipstreamMsp->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex; - BOOTSTRAPPER_ACTION_STATE action = fExecute ? pTargetProduct->execute : pTargetProduct->rollback; - BOOL fSlipstream = BOOTSTRAPPER_ACTION_STATE_UNINSTALL < action && - (BOOTSTRAPPER_ACTION_STATE_REPAIR != pAction->msiPackage.action || BOOTSTRAPPER_ACTION_STATE_REPAIR == action); - - if (fSlipstream) - { - if (fExecute) - { - pSlipstreamMsp->execute = action; - pTargetProduct->executeSkip = BURN_PATCH_SKIP_STATE_SLIPSTREAM; - } - else - { - pSlipstreamMsp->rollback = action; - pTargetProduct->rollbackSkip = BURN_PATCH_SKIP_STATE_SLIPSTREAM; - } - } - } - } - } - } -} - -static void CalculateExpectedRegistrationStates( - __in BURN_PACKAGE* rgPackages, - __in DWORD cPackages - ) -{ - for (DWORD i = 0; i < cPackages; ++i) - { - BURN_PACKAGE* pPackage = rgPackages + i; - - // MspPackages can have actions throughout the plan, so the plan needed to be finalized before anything could be calculated. - if (BURN_PACKAGE_TYPE_MSP == pPackage->type && !pPackage->fDependencyManagerWasHere) - { - pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; - pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; - - for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j) - { - BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + j; - - // The highest aggregate action state found will be used. - if (pPackage->execute < pTargetProduct->execute) - { - pPackage->execute = pTargetProduct->execute; - } - - if (pPackage->rollback < pTargetProduct->rollback) - { - pPackage->rollback = pTargetProduct->rollback; - } - } - } - - if (pPackage->fCanAffectRegistration) - { - if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pPackage->execute) - { - pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } - else if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) - { - pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - } - - if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute) - { - if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pPackage->expectedCacheRegistrationState) - { - pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } - if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pPackage->expectedInstallRegistrationState) - { - pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } - } - else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pPackage->dependencyExecute) - { - if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->expectedCacheRegistrationState) - { - pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; - } - if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->expectedInstallRegistrationState) - { - pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED; - } - } - } - } -} - -static HRESULT PlanDependencyActions( - __in BOOL fBundlePerMachine, - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage - ) -{ - HRESULT hr = S_OK; - - hr = DependencyPlanPackageBegin(fBundlePerMachine, pPackage, pPlan); - ExitOnFailure(hr, "Failed to begin plan dependency actions for package: %ls", pPackage->sczId); - - hr = DependencyPlanPackage(NULL, pPackage, pPlan); - ExitOnFailure(hr, "Failed to plan package dependency actions."); - - hr = DependencyPlanPackageComplete(pPackage, pPlan); - ExitOnFailure(hr, "Failed to complete plan dependency actions for package: %ls", pPackage->sczId); - -LExit: - return hr; -} - -static HRESULT CalculateExecuteActions( - __in BURN_PACKAGE* pPackage, - __in_opt BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary - ) -{ - HRESULT hr = S_OK; - BOOL fInsideMsiTransaction = pActiveRollbackBoundary && pActiveRollbackBoundary->fTransaction; - - // Calculate execute actions. - switch (pPackage->type) - { - case BURN_PACKAGE_TYPE_EXE: - hr = ExeEnginePlanCalculatePackage(pPackage); - break; - - case BURN_PACKAGE_TYPE_MSI: - hr = MsiEnginePlanCalculatePackage(pPackage, fInsideMsiTransaction); - break; - - case BURN_PACKAGE_TYPE_MSP: - hr = MspEnginePlanCalculatePackage(pPackage, fInsideMsiTransaction); - break; - - case BURN_PACKAGE_TYPE_MSU: - hr = MsuEnginePlanCalculatePackage(pPackage); - break; - - default: - hr = E_UNEXPECTED; - ExitOnFailure(hr, "Invalid package type."); - } - -LExit: - return hr; -} - -static BOOL NeedsCache( - __in BURN_PACKAGE* pPackage, - __in BOOL fExecute - ) -{ - BOOTSTRAPPER_ACTION_STATE action = fExecute ? pPackage->execute : pPackage->rollback; - if (BURN_PACKAGE_TYPE_EXE == pPackage->type) // Exe packages require the package for all operations (even uninstall). - { - return BOOTSTRAPPER_ACTION_STATE_NONE != action; - } - else // The other package types can uninstall without the original package. - { - return BOOTSTRAPPER_ACTION_STATE_UNINSTALL < action; - } -} - -static BOOL ForceCache( - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage - ) -{ - // All packages that have cacheType set to force should be cached if the bundle is going to be present. - return BOOTSTRAPPER_CACHE_TYPE_FORCE == pPackage->cacheType && BOOTSTRAPPER_ACTION_UNINSTALL < pPlan->action; -} - -static void CacheActionLog( - __in DWORD iAction, - __in BURN_CACHE_ACTION* pAction, - __in BOOL fRollback - ) -{ - LPCWSTR wzBase = fRollback ? L" Rollback cache" : L" Cache"; - switch (pAction->type) - { - case BURN_CACHE_ACTION_TYPE_CHECKPOINT: - LogStringLine(PlanDumpLevel, "%ls action[%u]: CHECKPOINT id: %u", wzBase, iAction, pAction->checkpoint.dwId); - break; - - case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: - LogStringLine(PlanDumpLevel, "%ls action[%u]: LAYOUT_BUNDLE working path: %ls, exe name: %ls", wzBase, iAction, pAction->bundleLayout.sczUnverifiedPath, pAction->bundleLayout.sczExecutableName); - break; - - case BURN_CACHE_ACTION_TYPE_CONTAINER: - LogStringLine(PlanDumpLevel, "%ls action[%u]: CONTAINER container id: %ls, working path: %ls", wzBase, iAction, pAction->container.pContainer->sczId, pAction->container.pContainer->sczUnverifiedPath); - break; - - case BURN_CACHE_ACTION_TYPE_PACKAGE: - LogStringLine(PlanDumpLevel, "%ls action[%u]: PACKAGE id: %ls", wzBase, iAction, pAction->package.pPackage->sczId); - break; - - case BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE: - LogStringLine(PlanDumpLevel, "%ls action[%u]: ROLLBACK_PACKAGE id: %ls", wzBase, iAction, pAction->rollbackPackage.pPackage->sczId); - break; - - case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT: - LogStringLine(PlanDumpLevel, "%ls action[%u]: SIGNAL_SYNCPOINT event handle: 0x%p", wzBase, iAction, pAction->syncpoint.hEvent); - break; - - default: - AssertSz(FALSE, "Unknown cache action type."); - break; - } -} - -static void ExecuteActionLog( - __in DWORD iAction, - __in BURN_EXECUTE_ACTION* pAction, - __in BOOL fRollback - ) -{ - LPCWSTR wzBase = fRollback ? L" Rollback" : L" Execute"; - switch (pAction->type) - { - case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT: - LogStringLine(PlanDumpLevel, "%ls action[%u]: CHECKPOINT id: %u, msi transaction id: %ls", wzBase, iAction, pAction->checkpoint.dwId, pAction->checkpoint.pActiveRollbackBoundary && pAction->checkpoint.pActiveRollbackBoundary->fTransaction ? pAction->checkpoint.pActiveRollbackBoundary->sczId : L"(none)"); - break; - - case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER: - LogStringLine(PlanDumpLevel, "%ls action[%u]: PACKAGE_PROVIDER package id: %ls, action: %hs", wzBase, iAction, pAction->packageProvider.pPackage->sczId, LoggingDependencyActionToString(pAction->packageProvider.action)); - break; - - case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: - LogStringLine(PlanDumpLevel, "%ls action[%u]: PACKAGE_DEPENDENCY package id: %ls, bundle provider key: %ls, action: %hs", wzBase, iAction, pAction->packageDependency.pPackage->sczId, pAction->packageDependency.sczBundleProviderKey, LoggingDependencyActionToString(pAction->packageDependency.action)); - break; - - case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: - LogStringLine(PlanDumpLevel, "%ls action[%u]: EXE_PACKAGE package id: %ls, action: %hs, ignore dependencies: %ls", wzBase, iAction, pAction->exePackage.pPackage->sczId, LoggingActionStateToString(pAction->exePackage.action), pAction->exePackage.sczIgnoreDependencies); - break; - - case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: - LogStringLine(PlanDumpLevel, "%ls action[%u]: MSI_PACKAGE package id: %ls, action: %hs, action msi property: %ls, ui level: %u, disable externaluihandler: %ls, log path: %ls, logging attrib: %u", wzBase, iAction, pAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pAction->msiPackage.action), LoggingBurnMsiPropertyToString(pAction->msiPackage.actionMsiProperty), pAction->msiPackage.uiLevel, pAction->msiPackage.fDisableExternalUiHandler ? L"yes" : L"no", pAction->msiPackage.sczLogPath, pAction->msiPackage.dwLoggingAttributes); - for (DWORD j = 0; j < pAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++j) - { - const BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pAction->msiPackage.pPackage->Msi.rgSlipstreamMsps + j; - LogStringLine(PlanDumpLevel, " Patch[%u]: msp package id: %ls, action: %hs", j, pSlipstreamMsp->pMspPackage->sczId, LoggingActionStateToString(fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute)); - } - break; - - case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: - LogStringLine(PlanDumpLevel, "%ls action[%u]: MSP_TARGET package id: %ls, action: %hs, target product code: %ls, target per-machine: %ls, action msi property: %ls, ui level: %u, disable externaluihandler: %ls, log path: %ls", wzBase, iAction, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.sczTargetProductCode, pAction->mspTarget.fPerMachineTarget ? L"yes" : L"no", LoggingBurnMsiPropertyToString(pAction->mspTarget.actionMsiProperty), pAction->mspTarget.uiLevel, pAction->mspTarget.fDisableExternalUiHandler ? L"yes" : L"no", pAction->mspTarget.sczLogPath); - for (DWORD j = 0; j < pAction->mspTarget.cOrderedPatches; ++j) - { - LogStringLine(PlanDumpLevel, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->mspTarget.rgOrderedPatches[j].pTargetProduct->dwOrder, pAction->mspTarget.rgOrderedPatches[j].pPackage->sczId); - } - break; - - case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: - LogStringLine(PlanDumpLevel, "%ls action[%u]: MSU_PACKAGE package id: %ls, action: %hs, log path: %ls", wzBase, iAction, pAction->msuPackage.pPackage->sczId, LoggingActionStateToString(pAction->msuPackage.action), pAction->msuPackage.sczLogPath); - break; - - case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY: - LogStringLine(PlanDumpLevel, "%ls action[%u]: ROLLBACK_BOUNDARY id: %ls, vital: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no"); - break; - - case BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT: - LogStringLine(PlanDumpLevel, "%ls action[%u]: WAIT_SYNCPOINT event handle: 0x%p", wzBase, iAction, pAction->syncpoint.hEvent); - break; - - case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE: - LogStringLine(PlanDumpLevel, "%ls action[%u]: UNCACHE_PACKAGE id: %ls", wzBase, iAction, pAction->uncachePackage.pPackage->sczId); - break; - - case BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION: - LogStringLine(PlanDumpLevel, "%ls action[%u]: BEGIN_MSI_TRANSACTION id: %ls", wzBase, iAction, pAction->msiTransaction.pRollbackBoundary->sczId); - break; - - case BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION: - LogStringLine(PlanDumpLevel, "%ls action[%u]: COMMIT_MSI_TRANSACTION id: %ls", wzBase, iAction, pAction->msiTransaction.pRollbackBoundary->sczId); - break; - - default: - AssertSz(FALSE, "Unknown execute action type."); - break; - } - - if (pAction->fDeleted) - { - LogStringLine(PlanDumpLevel, " (deleted action)"); - } -} - -extern "C" void PlanDump( - __in BURN_PLAN* pPlan - ) -{ - LogStringLine(PlanDumpLevel, "--- Begin plan dump ---"); - - LogStringLine(PlanDumpLevel, "Plan action: %hs", LoggingBurnActionToString(pPlan->action)); - LogStringLine(PlanDumpLevel, " per-machine: %hs", LoggingTrueFalseToString(pPlan->fPerMachine)); - LogStringLine(PlanDumpLevel, " disable-rollback: %hs", LoggingTrueFalseToString(pPlan->fDisableRollback)); - LogStringLine(PlanDumpLevel, " estimated size: %llu", pPlan->qwEstimatedSize); - if (pPlan->sczLayoutDirectory) - { - LogStringLine(PlanDumpLevel, " layout directory: %ls", pPlan->sczLayoutDirectory); - } - - LogStringLine(PlanDumpLevel, "Plan cache size: %llu", pPlan->qwCacheSizeTotal); - for (DWORD i = 0; i < pPlan->cCacheActions; ++i) - { - CacheActionLog(i, pPlan->rgCacheActions + i, FALSE); - } - - for (DWORD i = 0; i < pPlan->cRollbackCacheActions; ++i) - { - CacheActionLog(i, pPlan->rgRollbackCacheActions + i, TRUE); - } - - LogStringLine(PlanDumpLevel, "Plan execute package count: %u", pPlan->cExecutePackagesTotal); - LogStringLine(PlanDumpLevel, " overall progress ticks: %u", pPlan->cOverallProgressTicksTotal); - for (DWORD i = 0; i < pPlan->cExecuteActions; ++i) - { - ExecuteActionLog(i, pPlan->rgExecuteActions + i, FALSE); - } - - for (DWORD i = 0; i < pPlan->cRollbackActions; ++i) - { - ExecuteActionLog(i, pPlan->rgRollbackActions + i, TRUE); - } - - for (DWORD i = 0; i < pPlan->cCleanActions; ++i) - { - LogStringLine(PlanDumpLevel, " Clean action[%u]: CLEAN_PACKAGE package id: %ls", i, pPlan->rgCleanActions[i].pPackage->sczId); - } - - for (DWORD i = 0; i < pPlan->cPlannedProviders; ++i) - { - LogStringLine(PlanDumpLevel, " Dependency action[%u]: PLANNED_PROVIDER key: %ls, name: %ls", i, pPlan->rgPlannedProviders[i].sczKey, pPlan->rgPlannedProviders[i].sczName); - } - - LogStringLine(PlanDumpLevel, "--- End plan dump ---"); -} diff --git a/src/engine/plan.h b/src/engine/plan.h deleted file mode 100644 index 00ab5516..00000000 --- a/src/engine/plan.h +++ /dev/null @@ -1,456 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -// constants - -const DWORD BURN_PLAN_INVALID_ACTION_INDEX = 0x80000000; - -enum BURN_REGISTRATION_ACTION_OPERATIONS -{ - BURN_REGISTRATION_ACTION_OPERATIONS_NONE = 0x0, - BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE = 0x1, - BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION = 0x2, - BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE = 0x4, -}; - -enum BURN_DEPENDENCY_REGISTRATION_ACTION -{ - BURN_DEPENDENCY_REGISTRATION_ACTION_NONE, - BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, - BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, -}; - -enum BURN_DEPENDENT_REGISTRATION_ACTION_TYPE -{ - BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_NONE, - BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, - BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER, -}; - -enum BURN_CACHE_ACTION_TYPE -{ - BURN_CACHE_ACTION_TYPE_NONE, - BURN_CACHE_ACTION_TYPE_CHECKPOINT, - BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE, - BURN_CACHE_ACTION_TYPE_PACKAGE, - BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE, - BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT, - BURN_CACHE_ACTION_TYPE_CONTAINER, -}; - -enum BURN_EXECUTE_ACTION_TYPE -{ - BURN_EXECUTE_ACTION_TYPE_NONE, - BURN_EXECUTE_ACTION_TYPE_CHECKPOINT, - BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT, - BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE, - BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE, - BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE, - BURN_EXECUTE_ACTION_TYPE_MSP_TARGET, - BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE, - BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER, - BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY, - BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY, - BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION, - BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION, -}; - -enum BURN_CLEAN_ACTION_TYPE -{ - BURN_CLEAN_ACTION_TYPE_NONE, - BURN_CLEAN_ACTION_TYPE_BUNDLE, - BURN_CLEAN_ACTION_TYPE_PACKAGE, -}; - - -// structs - -typedef struct _BURN_DEPENDENT_REGISTRATION_ACTION -{ - BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type; - LPWSTR sczBundleId; - LPWSTR sczDependentProviderKey; -} BURN_DEPENDENT_REGISTRATION_ACTION; - -typedef struct _BURN_CACHE_CONTAINER_PROGRESS -{ - LPWSTR wzId; - DWORD iIndex; - BOOL fCachedDuringApply; - BURN_CONTAINER* pContainer; -} BURN_CACHE_CONTAINER_PROGRESS; - -typedef struct _BURN_CACHE_PAYLOAD_PROGRESS -{ - LPWSTR wzId; - DWORD iIndex; - BOOL fCachedDuringApply; - BURN_PAYLOAD* pPayload; -} BURN_CACHE_PAYLOAD_PROGRESS; - -typedef struct _BURN_CACHE_ACTION -{ - BURN_CACHE_ACTION_TYPE type; - union - { - struct - { - DWORD dwId; - } checkpoint; - struct - { - LPWSTR sczExecutableName; - LPWSTR sczUnverifiedPath; - DWORD64 qwBundleSize; - BURN_PAYLOAD_GROUP* pPayloadGroup; - } bundleLayout; - struct - { - BURN_PACKAGE* pPackage; - } package; - struct - { - BURN_PACKAGE* pPackage; - } rollbackPackage; - struct - { - HANDLE hEvent; - } syncpoint; - struct - { - BURN_CONTAINER* pContainer; - } container; - }; -} BURN_CACHE_ACTION; - -typedef struct _BURN_ORDERED_PATCHES -{ - BURN_PACKAGE* pPackage; - - BURN_MSPTARGETPRODUCT* pTargetProduct; // only valid in the unelevated engine. -} BURN_ORDERED_PATCHES; - -typedef struct _BURN_EXECUTE_ACTION_CHECKPOINT -{ - DWORD dwId; - BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary; -} BURN_EXECUTE_ACTION_CHECKPOINT; - -typedef struct _BURN_EXECUTE_ACTION -{ - BURN_EXECUTE_ACTION_TYPE type; - BOOL fDeleted; // used to skip an action after it was planned since deleting actions out of the plan is too hard. - union - { - BURN_EXECUTE_ACTION_CHECKPOINT checkpoint; - struct - { - HANDLE hEvent; - } syncpoint; - struct - { - BURN_PACKAGE* pPackage; - } uncachePackage; - struct - { - BURN_PACKAGE* pPackage; - BOOL fFireAndForget; - BOOTSTRAPPER_ACTION_STATE action; - LPWSTR sczIgnoreDependencies; - LPWSTR sczAncestors; - } exePackage; - struct - { - BURN_PACKAGE* pPackage; - LPWSTR sczLogPath; - DWORD dwLoggingAttributes; - BURN_MSI_PROPERTY actionMsiProperty; - INSTALLUILEVEL uiLevel; - BOOL fDisableExternalUiHandler; - BOOTSTRAPPER_ACTION_STATE action; - - BOOTSTRAPPER_FEATURE_ACTION* rgFeatures; - } msiPackage; - struct - { - BURN_PACKAGE* pPackage; - LPWSTR sczTargetProductCode; - BURN_PACKAGE* pChainedTargetPackage; - BOOL fSlipstream; - BOOL fPerMachineTarget; - LPWSTR sczLogPath; - BURN_MSI_PROPERTY actionMsiProperty; - INSTALLUILEVEL uiLevel; - BOOL fDisableExternalUiHandler; - BOOTSTRAPPER_ACTION_STATE action; - - BURN_ORDERED_PATCHES* rgOrderedPatches; - DWORD cOrderedPatches; - } mspTarget; - struct - { - BURN_PACKAGE* pPackage; - LPWSTR sczLogPath; - BOOTSTRAPPER_ACTION_STATE action; - } msuPackage; - struct - { - BURN_ROLLBACK_BOUNDARY* pRollbackBoundary; - } rollbackBoundary; - struct - { - BURN_PACKAGE* pPackage; - BURN_DEPENDENCY_ACTION action; - } packageProvider; - struct - { - BURN_PACKAGE* pPackage; - LPWSTR sczBundleProviderKey; - BURN_DEPENDENCY_ACTION action; - } packageDependency; - struct - { - BURN_ROLLBACK_BOUNDARY* pRollbackBoundary; - } msiTransaction; - }; -} BURN_EXECUTE_ACTION; - -typedef struct _BURN_CLEAN_ACTION -{ - BURN_PACKAGE* pPackage; -} BURN_CLEAN_ACTION; - -typedef struct _BURN_PLAN -{ - BOOTSTRAPPER_ACTION action; - BURN_PAYLOADS* pPayloads; // points directly into parent the ENGINE_STATE. - LPWSTR wzBundleId; // points directly into parent the ENGINE_STATE. - LPWSTR wzBundleProviderKey; // points directly into parent the ENGINE_STATE. - BOOL fPerMachine; - BOOL fCanAffectMachineState; - DWORD dwRegistrationOperations; - BOOL fDisallowRemoval; - BOOL fDisableRollback; - BOOL fAffectedMachineState; - BOOL fIgnoreAllDependents; - LPWSTR sczLayoutDirectory; - - DWORD64 qwCacheSizeTotal; - - DWORD64 qwEstimatedSize; - - DWORD cExecutePackagesTotal; - DWORD cOverallProgressTicksTotal; - - BOOL fEnabledForwardCompatibleBundle; - BURN_PACKAGE forwardCompatibleBundle; - - BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction; - - BURN_DEPENDENT_REGISTRATION_ACTION* rgRegistrationActions; - DWORD cRegistrationActions; - - BURN_DEPENDENT_REGISTRATION_ACTION* rgRollbackRegistrationActions; - DWORD cRollbackRegistrationActions; - - BURN_CACHE_ACTION* rgCacheActions; - DWORD cCacheActions; - - BURN_CACHE_ACTION* rgRollbackCacheActions; - DWORD cRollbackCacheActions; - - BURN_EXECUTE_ACTION* rgExecuteActions; - DWORD cExecuteActions; - - BURN_EXECUTE_ACTION* rgRollbackActions; - DWORD cRollbackActions; - - BURN_CLEAN_ACTION* rgCleanActions; - DWORD cCleanActions; - - DEPENDENCY* rgPlannedProviders; - UINT cPlannedProviders; - - BURN_CACHE_CONTAINER_PROGRESS* rgContainerProgress; - DWORD cContainerProgress; - STRINGDICT_HANDLE shContainerProgress; - - BURN_CACHE_PAYLOAD_PROGRESS* rgPayloadProgress; - DWORD cPayloadProgress; - STRINGDICT_HANDLE shPayloadProgress; - - DWORD dwNextCheckpointId; // for plan internal use - BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary; // for plan internal use -} BURN_PLAN; - - -// functions - -void PlanReset( - __in BURN_PLAN* pPlan, - __in BURN_CONTAINERS* pContainers, - __in BURN_PACKAGES* pPackages, - __in BURN_PAYLOAD_GROUP* pLayoutPayloads - ); -void PlanUninitializeExecuteAction( - __in BURN_EXECUTE_ACTION* pExecuteAction - ); -HRESULT PlanSetVariables( - __in BOOTSTRAPPER_ACTION action, - __in BURN_VARIABLES* pVariables - ); -HRESULT PlanDefaultPackageRequestState( - __in BURN_PACKAGE_TYPE packageType, - __in BOOTSTRAPPER_PACKAGE_STATE currentState, - __in BOOL fPermanent, - __in BOOTSTRAPPER_ACTION action, - __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __out BOOTSTRAPPER_REQUEST_STATE* pRequestState - ); -HRESULT PlanLayoutBundle( - __in BURN_PLAN* pPlan, - __in_z LPCWSTR wzExecutableName, - __in DWORD64 qwBundleSize, - __in BURN_VARIABLES* pVariables, - __in BURN_PAYLOAD_GROUP* pLayoutPayloads - ); -HRESULT PlanForwardCompatibleBundles( - __in BURN_USER_EXPERIENCE* pUX, - __in BOOTSTRAPPER_COMMAND* pCommand, - __in BURN_PLAN* pPlan, - __in BURN_REGISTRATION* pRegistration, - __in BOOTSTRAPPER_ACTION action - ); -HRESULT PlanPackages( - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_PACKAGES* pPackages, - __in BURN_PLAN* pPlan, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_RELATION_TYPE relationType - ); -HRESULT PlanRegistration( - __in BURN_PLAN* pPlan, - __in BURN_REGISTRATION* pRegistration, - __in BOOTSTRAPPER_RESUME_TYPE resumeType, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __inout BOOL* pfContinuePlanning - ); -HRESULT PlanPassThroughBundle( - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_PACKAGE* pPackage, - __in BURN_PLAN* pPlan, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_RELATION_TYPE relationType - ); -HRESULT PlanUpdateBundle( - __in BURN_USER_EXPERIENCE* pUX, - __in BURN_PACKAGE* pPackage, - __in BURN_PLAN* pPlan, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in BOOTSTRAPPER_DISPLAY display, - __in BOOTSTRAPPER_RELATION_TYPE relationType - ); -HRESULT PlanLayoutContainer( - __in BURN_PLAN* pPlan, - __in BURN_CONTAINER* pContainer - ); -HRESULT PlanLayoutPackage( - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage - ); -HRESULT PlanExecutePackage( - __in BOOL fPerMachine, - __in BOOTSTRAPPER_DISPLAY display, - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __inout HANDLE* phSyncpointEvent - ); -HRESULT PlanDefaultRelatedBundleRequestState( - __in BOOTSTRAPPER_RELATION_TYPE commandRelationType, - __in BOOTSTRAPPER_RELATION_TYPE relatedBundleRelationType, - __in BOOTSTRAPPER_ACTION action, - __in VERUTIL_VERSION* pRegistrationVersion, - __in VERUTIL_VERSION* pRelatedBundleVersion, - __inout BOOTSTRAPPER_REQUEST_STATE* pRequestState - ); -HRESULT PlanRelatedBundlesBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BURN_REGISTRATION* pRegistration, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in BURN_PLAN* pPlan - ); -HRESULT PlanRelatedBundlesComplete( - __in BURN_REGISTRATION* pRegistration, - __in BURN_PLAN* pPlan, - __in BURN_LOGGING* pLog, - __in BURN_VARIABLES* pVariables, - __in DWORD dwExecuteActionEarlyIndex - ); -HRESULT PlanFinalizeActions( - __in BURN_PLAN* pPlan - ); -HRESULT PlanCleanPackage( - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage - ); -HRESULT PlanExecuteCacheSyncAndRollback( - __in BURN_PLAN* pPlan, - __in BURN_PACKAGE* pPackage, - __in HANDLE hCacheEvent - ); -HRESULT PlanExecuteCheckpoint( - __in BURN_PLAN* pPlan - ); -HRESULT PlanInsertExecuteAction( - __in DWORD dwIndex, - __in BURN_PLAN* pPlan, - __out BURN_EXECUTE_ACTION** ppExecuteAction - ); -HRESULT PlanInsertRollbackAction( - __in DWORD dwIndex, - __in BURN_PLAN* pPlan, - __out BURN_EXECUTE_ACTION** ppRollbackAction - ); -HRESULT PlanAppendExecuteAction( - __in BURN_PLAN* pPlan, - __out BURN_EXECUTE_ACTION** ppExecuteAction - ); -HRESULT PlanAppendRollbackAction( - __in BURN_PLAN* pPlan, - __out BURN_EXECUTE_ACTION** ppExecuteAction - ); -HRESULT PlanRollbackBoundaryBegin( - __in BURN_PLAN* pPlan, - __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary - ); -HRESULT PlanRollbackBoundaryComplete( - __in BURN_PLAN* pPlan - ); -HRESULT PlanSetResumeCommand( - __in BURN_REGISTRATION* pRegistration, - __in BOOTSTRAPPER_ACTION action, - __in BOOTSTRAPPER_COMMAND* pCommand, - __in BURN_LOGGING* pLog - ); -void PlanDump( - __in BURN_PLAN* pPlan - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/platform.cpp b/src/engine/platform.cpp deleted file mode 100644 index 9469ff49..00000000 --- a/src/engine/platform.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// variables - -PFN_INITIATESYSTEMSHUTDOWNEXW vpfnInitiateSystemShutdownExW; - - -// function definitions - -extern "C" void PlatformInitialize() -{ - vpfnInitiateSystemShutdownExW = ::InitiateSystemShutdownExW; -} diff --git a/src/engine/platform.h b/src/engine/platform.h deleted file mode 100644 index 3681f248..00000000 --- a/src/engine/platform.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -// typedefs - -typedef BOOL (WINAPI *PFN_INITIATESYSTEMSHUTDOWNEXW)( - __in_opt LPWSTR lpMachineName, - __in_opt LPWSTR lpMessage, - __in DWORD dwTimeout, - __in BOOL bForceAppsClosed, - __in BOOL bRebootAfterShutdown, - __in DWORD dwReason - ); - - -// variable declarations - -extern PFN_INITIATESYSTEMSHUTDOWNEXW vpfnInitiateSystemShutdownExW; - - -// function declarations - -void PlatformInitialize(); - - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/precomp.cpp b/src/engine/precomp.cpp deleted file mode 100644 index 37664a1c..00000000 --- a/src/engine/precomp.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" diff --git a/src/engine/precomp.h b/src/engine/precomp.h deleted file mode 100644 index 11b594da..00000000 --- a/src/engine/precomp.h +++ /dev/null @@ -1,102 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "BootstrapperEngine.h" -#include "BootstrapperApplication.h" -#include "BundleExtensionEngine.h" -#include "BundleExtension.h" - -#include "platform.h" -#include "variant.h" -#include "variable.h" -#include "condition.h" -#include "section.h" -#include "approvedexe.h" -#include "container.h" -#include "payload.h" -#include "cabextract.h" -#include "burnextension.h" -#include "search.h" -#include "userexperience.h" -#include "package.h" -#include "update.h" -#include "pseudobundle.h" -#include "registration.h" -#include "relatedbundle.h" -#include "detect.h" -#include "plan.h" -#include "logging.h" -#include "pipe.h" -#include "core.h" -#include "cache.h" -#include "apply.h" -#include "exeengine.h" -#include "msiengine.h" -#include "mspengine.h" -#include "msuengine.h" -#include "dependency.h" -#include "elevation.h" -#include "embedded.h" -#include "manifest.h" -#include "splashscreen.h" -#include "uithread.h" -#include "netfxchainer.h" - -#include "externalengine.h" -#include "EngineForApplication.h" -#include "EngineForExtension.h" -#include "engine.messages.h" diff --git a/src/engine/pseudobundle.cpp b/src/engine/pseudobundle.cpp deleted file mode 100644 index 180cc621..00000000 --- a/src/engine/pseudobundle.cpp +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -extern "C" HRESULT PseudoBundleInitialize( - __in DWORD64 qwEngineVersion, - __in BURN_PACKAGE* pPackage, - __in BOOL fPerMachine, - __in_z LPCWSTR wzId, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in BOOTSTRAPPER_PACKAGE_STATE state, - __in BOOL fCached, - __in_z LPCWSTR wzFilePath, - __in_z LPCWSTR wzLocalSource, - __in_z_opt LPCWSTR wzDownloadSource, - __in DWORD64 qwSize, - __in BOOL fVital, - __in_z_opt LPCWSTR wzInstallArguments, - __in_z_opt LPCWSTR wzRepairArguments, - __in_z_opt LPCWSTR wzUninstallArguments, - __in_opt BURN_DEPENDENCY_PROVIDER* pDependencyProvider, - __in_opt const BYTE* pbHash, - __in const DWORD cbHash - ) -{ - HRESULT hr = S_OK; - LPWSTR sczRelationTypeCommandLineSwitch = NULL; - BURN_PAYLOAD* pPayload = NULL; - - LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType); - if (wzRelationTypeCommandLine) - { - hr = StrAllocFormatted(&sczRelationTypeCommandLineSwitch, L" -%ls", wzRelationTypeCommandLine); - } - - // Initialize the single payload, and fill out all the necessary fields - pPackage->payloads.rgItems = (BURN_PAYLOAD_GROUP_ITEM*)MemAlloc(sizeof(BURN_PAYLOAD_GROUP_ITEM), TRUE); - ExitOnNull(pPackage->payloads.rgItems, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload group inside of related bundle struct"); - pPackage->payloads.cItems = 1; - - pPayload = (BURN_PAYLOAD*)MemAlloc(sizeof(BURN_PAYLOAD), TRUE); - ExitOnNull(pPayload, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload inside of related bundle struct"); - pPackage->payloads.rgItems[0].pPayload = pPayload; - pPayload->packaging = BURN_PAYLOAD_PACKAGING_EXTERNAL; - pPayload->qwFileSize = qwSize; - - hr = StrAllocString(&pPayload->sczKey, wzId, 0); - ExitOnFailure(hr, "Failed to copy key for pseudo bundle payload."); - - hr = StrAllocString(&pPayload->sczFilePath, wzFilePath, 0); - ExitOnFailure(hr, "Failed to copy filename for pseudo bundle."); - - hr = StrAllocString(&pPayload->sczSourcePath, wzLocalSource, 0); - ExitOnFailure(hr, "Failed to copy local source path for pseudo bundle."); - - if (wzDownloadSource && *wzDownloadSource) - { - hr = StrAllocString(&pPayload->downloadSource.sczUrl, wzDownloadSource, 0); - ExitOnFailure(hr, "Failed to copy download source for pseudo bundle."); - } - - if (pbHash) - { - pPayload->pbHash = static_cast(MemAlloc(cbHash, FALSE)); - ExitOnNull(pPayload->pbHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for pseudo bundle payload hash."); - - pPayload->cbHash = cbHash; - memcpy_s(pPayload->pbHash, pPayload->cbHash, pbHash, cbHash); - } - - pPackage->Exe.fPseudoBundle = TRUE; - - pPackage->type = BURN_PACKAGE_TYPE_EXE; - pPackage->fPerMachine = fPerMachine; - pPackage->currentState = state; - pPackage->fCached = fCached; - pPackage->qwInstallSize = qwSize; - pPackage->qwSize = qwSize; - pPackage->fVital = fVital; - - hr = StrAllocString(&pPackage->sczId, wzId, 0); - ExitOnFailure(hr, "Failed to copy key for pseudo bundle."); - - hr = StrAllocString(&pPackage->sczCacheId, wzId, 0); - ExitOnFailure(hr, "Failed to copy cache id for pseudo bundle."); - - // If we are a self updating bundle, we don't have to have Install arguments. - if (wzInstallArguments) - { - hr = StrAllocString(&pPackage->Exe.sczInstallArguments, wzInstallArguments, 0); - ExitOnFailure(hr, "Failed to copy install arguments for related bundle package"); - } - - if (sczRelationTypeCommandLineSwitch) - { - hr = StrAllocConcat(&pPackage->Exe.sczInstallArguments, sczRelationTypeCommandLineSwitch, 0); - ExitOnFailure(hr, "Failed to append relation type to install arguments for related bundle package"); - } - - if (wzRepairArguments) - { - hr = StrAllocString(&pPackage->Exe.sczRepairArguments, wzRepairArguments, 0); - ExitOnFailure(hr, "Failed to copy repair arguments for related bundle package"); - - if (sczRelationTypeCommandLineSwitch) - { - hr = StrAllocConcat(&pPackage->Exe.sczRepairArguments, sczRelationTypeCommandLineSwitch, 0); - ExitOnFailure(hr, "Failed to append relation type to repair arguments for related bundle package"); - } - - pPackage->Exe.fRepairable = TRUE; - } - - if (wzUninstallArguments) - { - hr = StrAllocString(&pPackage->Exe.sczUninstallArguments, wzUninstallArguments, 0); - ExitOnFailure(hr, "Failed to copy uninstall arguments for related bundle package"); - - if (sczRelationTypeCommandLineSwitch) - { - hr = StrAllocConcat(&pPackage->Exe.sczUninstallArguments, sczRelationTypeCommandLineSwitch, 0); - ExitOnFailure(hr, "Failed to append relation type to uninstall arguments for related bundle package"); - } - - pPackage->fUninstallable = TRUE; - } - - // Only support progress from engines that are compatible (aka: version greater than or equal to last protocol breaking change *and* versions that are older or the same as this engine). - pPackage->Exe.protocol = (FILEMAKEVERSION(3, 6, 2221, 0) <= qwEngineVersion && qwEngineVersion <= FILEMAKEVERSION(rmj, rmm, rup, rpr)) ? BURN_EXE_PROTOCOL_TYPE_BURN : BURN_EXE_PROTOCOL_TYPE_NONE; - - // All versions of Burn past v3.9 RTM support suppressing ancestors. - pPackage->Exe.fSupportsAncestors = FILEMAKEVERSION(3, 9, 1006, 0) <= qwEngineVersion; - - if (pDependencyProvider) - { - pPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER), TRUE); - ExitOnNull(pPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers."); - pPackage->cDependencyProviders = 1; - - pPackage->rgDependencyProviders[0].fImported = pDependencyProvider->fImported; - - hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczKey, pDependencyProvider->sczKey, 0); - ExitOnFailure(hr, "Failed to copy key for pseudo bundle."); - - hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczVersion, pDependencyProvider->sczVersion, 0); - ExitOnFailure(hr, "Failed to copy version for pseudo bundle."); - - hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczDisplayName, pDependencyProvider->sczDisplayName, 0); - ExitOnFailure(hr, "Failed to copy display name for pseudo bundle."); - } - -LExit: - ReleaseStr(sczRelationTypeCommandLineSwitch); - - return hr; -} - -extern "C" HRESULT PseudoBundleInitializePassthrough( - __in BURN_PACKAGE* pPassthroughPackage, - __in BOOTSTRAPPER_COMMAND* pCommand, - __in_z_opt LPCWSTR wzAppendLogPath, - __in_z_opt LPCWSTR wzActiveParent, - __in_z_opt LPCWSTR wzAncestors, - __in BURN_PACKAGE* pPackage - ) -{ - Assert(BURN_PACKAGE_TYPE_EXE == pPackage->type); - - HRESULT hr = S_OK; - LPWSTR sczArguments = NULL; - - // Initialize the payloads, and copy the necessary fields. - pPassthroughPackage->payloads.rgItems = (BURN_PAYLOAD_GROUP_ITEM*)MemAlloc(sizeof(BURN_PAYLOAD_GROUP_ITEM) * pPackage->payloads.cItems, TRUE); - ExitOnNull(pPassthroughPackage->payloads.rgItems, hr, E_OUTOFMEMORY, "Failed to allocate space for burn package payload inside of passthrough bundle."); - pPassthroughPackage->payloads.cItems = pPackage->payloads.cItems; - - for (DWORD iPayload = 0; iPayload < pPackage->payloads.cItems; ++iPayload) - { - pPassthroughPackage->payloads.rgItems[iPayload].pPayload = pPackage->payloads.rgItems[iPayload].pPayload; - } - - pPassthroughPackage->Exe.fPseudoBundle = TRUE; - - pPassthroughPackage->fPerMachine = FALSE; // passthrough bundles are always launched per-user. - pPassthroughPackage->type = pPackage->type; - pPassthroughPackage->currentState = pPackage->currentState; - pPassthroughPackage->fCached = pPackage->fCached; - pPassthroughPackage->qwInstallSize = pPackage->qwInstallSize; - pPassthroughPackage->qwSize = pPackage->qwSize; - pPassthroughPackage->fVital = pPackage->fVital; - - hr = StrAllocString(&pPassthroughPackage->sczId, pPackage->sczId, 0); - ExitOnFailure(hr, "Failed to copy key for passthrough pseudo bundle."); - - hr = StrAllocString(&pPassthroughPackage->sczCacheId, pPackage->sczCacheId, 0); - ExitOnFailure(hr, "Failed to copy cache id for passthrough pseudo bundle."); - - pPassthroughPackage->Exe.protocol = pPackage->Exe.protocol; - - // No matter the operation, we're passing the same command-line. That's what makes - // this a passthrough bundle. - hr = CoreRecreateCommandLine(&sczArguments, pCommand->action, pCommand->display, pCommand->restart, pCommand->relationType, TRUE, wzActiveParent, wzAncestors, wzAppendLogPath, pCommand->wzCommandLine); - ExitOnFailure(hr, "Failed to recreate command-line arguments."); - - hr = StrAllocString(&pPassthroughPackage->Exe.sczInstallArguments, sczArguments, 0); - ExitOnFailure(hr, "Failed to copy install arguments for passthrough bundle package"); - - hr = StrAllocString(&pPassthroughPackage->Exe.sczRepairArguments, sczArguments, 0); - ExitOnFailure(hr, "Failed to copy related arguments for passthrough bundle package"); - - pPassthroughPackage->Exe.fRepairable = TRUE; - - hr = StrAllocString(&pPassthroughPackage->Exe.sczUninstallArguments, sczArguments, 0); - ExitOnFailure(hr, "Failed to copy uninstall arguments for passthrough bundle package"); - - pPassthroughPackage->fUninstallable = TRUE; - - // TODO: consider bringing this back in the near future. - //if (pDependencyProvider) - //{ - // pPassthroughPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER), TRUE); - // ExitOnNull(pPassthroughPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers."); - // pPassthroughPackage->cDependencyProviders = 1; - - // pPassthroughPackage->rgDependencyProviders[0].fImported = pDependencyProvider->fImported; - - // hr = StrAllocString(&pPassthroughPackage->rgDependencyProviders[0].sczKey, pDependencyProvider->sczKey, 0); - // ExitOnFailure(hr, "Failed to copy key for pseudo bundle."); - - // hr = StrAllocString(&pPassthroughPackage->rgDependencyProviders[0].sczVersion, pDependencyProvider->sczVersion, 0); - // ExitOnFailure(hr, "Failed to copy version for pseudo bundle."); - - // hr = StrAllocString(&pPassthroughPackage->rgDependencyProviders[0].sczDisplayName, pDependencyProvider->sczDisplayName, 0); - // ExitOnFailure(hr, "Failed to copy display name for pseudo bundle."); - //} - -LExit: - ReleaseStr(sczArguments); - return hr; -} diff --git a/src/engine/pseudobundle.h b/src/engine/pseudobundle.h deleted file mode 100644 index 9fb530aa..00000000 --- a/src/engine/pseudobundle.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - -HRESULT PseudoBundleInitialize( - __in DWORD64 qwEngineVersion, - __in BURN_PACKAGE* pPackage, - __in BOOL fPerMachine, - __in_z LPCWSTR wzId, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in BOOTSTRAPPER_PACKAGE_STATE state, - __in BOOL fCached, - __in_z LPCWSTR wzFilePath, - __in_z LPCWSTR wzLocalSource, - __in_z_opt LPCWSTR wzDownloadSource, - __in DWORD64 qwSize, - __in BOOL fVital, - __in_z_opt LPCWSTR wzInstallArguments, - __in_z_opt LPCWSTR wzRepairArguments, - __in_z_opt LPCWSTR wzUninstallArguments, - __in_opt BURN_DEPENDENCY_PROVIDER* pDependencyProvider, - __in_opt const BYTE* pbHash, - __in const DWORD cbHash - ); -HRESULT PseudoBundleInitializePassthrough( - __in BURN_PACKAGE* pPassthroughPackage, - __in BOOTSTRAPPER_COMMAND* pCommand, - __in_z_opt LPCWSTR wzAppendLogPath, - __in_z_opt LPCWSTR wzActiveParent, - __in_z_opt LPCWSTR wzAncestors, - __in BURN_PACKAGE* pPackage - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/registration.cpp b/src/engine/registration.cpp deleted file mode 100644 index 19da543c..00000000 --- a/src/engine/registration.cpp +++ /dev/null @@ -1,1702 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// constants - -const LPCWSTR REGISTRY_RUN_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"; -const LPCWSTR REGISTRY_RUN_ONCE_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"; -const LPCWSTR REGISTRY_REBOOT_PENDING_FORMAT = L"%ls.RebootRequired"; -const LPCWSTR REGISTRY_BUNDLE_INSTALLED = L"Installed"; -const LPCWSTR REGISTRY_BUNDLE_DISPLAY_ICON = L"DisplayIcon"; -const LPCWSTR REGISTRY_BUNDLE_DISPLAY_VERSION = L"DisplayVersion"; -const LPCWSTR REGISTRY_BUNDLE_ESTIMATED_SIZE = L"EstimatedSize"; -const LPCWSTR REGISTRY_BUNDLE_PUBLISHER = L"Publisher"; -const LPCWSTR REGISTRY_BUNDLE_HELP_LINK = L"HelpLink"; -const LPCWSTR REGISTRY_BUNDLE_HELP_TELEPHONE = L"HelpTelephone"; -const LPCWSTR REGISTRY_BUNDLE_URL_INFO_ABOUT = L"URLInfoAbout"; -const LPCWSTR REGISTRY_BUNDLE_URL_UPDATE_INFO = L"URLUpdateInfo"; -const LPCWSTR REGISTRY_BUNDLE_PARENT_DISPLAY_NAME = L"ParentDisplayName"; -const LPCWSTR REGISTRY_BUNDLE_PARENT_KEY_NAME = L"ParentKeyName"; -const LPCWSTR REGISTRY_BUNDLE_COMMENTS = L"Comments"; -const LPCWSTR REGISTRY_BUNDLE_CONTACT = L"Contact"; -const LPCWSTR REGISTRY_BUNDLE_NO_MODIFY = L"NoModify"; -const LPCWSTR REGISTRY_BUNDLE_MODIFY_PATH = L"ModifyPath"; -const LPCWSTR REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY = L"NoElevateOnModify"; -const LPCWSTR REGISTRY_BUNDLE_NO_REMOVE = L"NoRemove"; -const LPCWSTR REGISTRY_BUNDLE_SYSTEM_COMPONENT = L"SystemComponent"; -const LPCWSTR REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING = L"QuietUninstallString"; -const LPCWSTR REGISTRY_BUNDLE_UNINSTALL_STRING = L"UninstallString"; -const LPCWSTR REGISTRY_BUNDLE_RESUME_COMMAND_LINE = L"BundleResumeCommandLine"; -const LPCWSTR REGISTRY_BUNDLE_VERSION_MAJOR = L"VersionMajor"; -const LPCWSTR REGISTRY_BUNDLE_VERSION_MINOR = L"VersionMinor"; -const LPCWSTR SWIDTAG_FOLDER = L"swidtag"; - -// internal function declarations - -static HRESULT ParseSoftwareTagsFromXml( - __in IXMLDOMNode* pixnRegistrationNode, - __out BURN_SOFTWARE_TAG** prgSoftwareTags, - __out DWORD* pcSoftwareTags - ); -static HRESULT SetPaths( - __in BURN_REGISTRATION* pRegistration - ); -static HRESULT GetBundleManufacturer( - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables, - __out LPWSTR* psczBundleManufacturer - ); -static HRESULT GetBundleName( - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables, - __out LPWSTR* psczBundleName - ); -static HRESULT UpdateResumeMode( - __in BURN_REGISTRATION* pRegistration, - __in HKEY hkRegistration, - __in BURN_RESUME_MODE resumeMode, - __in BOOL fRestartInitiated - ); -static HRESULT ParseRelatedCodes( - __in BURN_REGISTRATION* pRegistration, - __in IXMLDOMNode* pixnBundle - ); -static HRESULT FormatUpdateRegistrationKey( - __in BURN_REGISTRATION* pRegistration, - __out_z LPWSTR* psczKey - ); -static HRESULT WriteSoftwareTags( - __in BURN_VARIABLES* pVariables, - __in BURN_SOFTWARE_TAGS* pSoftwareTags - ); -static HRESULT RemoveSoftwareTags( - __in BURN_VARIABLES* pVariables, - __in BURN_SOFTWARE_TAGS* pSoftwareTags - ); -static HRESULT WriteUpdateRegistration( - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables - ); -static HRESULT RemoveUpdateRegistration( - __in BURN_REGISTRATION* pRegistration - ); -static HRESULT RegWriteStringVariable( - __in HKEY hkKey, - __in BURN_VARIABLES* pVariables, - __in LPCWSTR wzVariable, - __in LPCWSTR wzName - ); -static HRESULT UpdateBundleNameRegistration( - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables, - __in HKEY hkRegistration - ); -static BOOL IsWuRebootPending(); -static BOOL IsBundleRebootPending( - __in BURN_REGISTRATION* pRegistration -); -static BOOL IsRegistryRebootPending(); - -// function definitions - -/******************************************************************* - RegistrationParseFromXml - Parses registration information from manifest. - -*******************************************************************/ -extern "C" HRESULT RegistrationParseFromXml( - __in BURN_REGISTRATION* pRegistration, - __in IXMLDOMNode* pixnBundle - ) -{ - HRESULT hr = S_OK; - IXMLDOMNode* pixnRegistrationNode = NULL; - IXMLDOMNode* pixnArpNode = NULL; - IXMLDOMNode* pixnUpdateNode = NULL; - LPWSTR scz = NULL; - - // select registration node - hr = XmlSelectSingleNode(pixnBundle, L"Registration", &pixnRegistrationNode); - if (S_FALSE == hr) - { - hr = E_NOTFOUND; - } - ExitOnFailure(hr, "Failed to select registration node."); - - // @Id - hr = XmlGetAttributeEx(pixnRegistrationNode, L"Id", &pRegistration->sczId); - ExitOnFailure(hr, "Failed to get @Id."); - - // @Tag - hr = XmlGetAttributeEx(pixnRegistrationNode, L"Tag", &pRegistration->sczTag); - ExitOnFailure(hr, "Failed to get @Tag."); - - hr = ParseRelatedCodes(pRegistration, pixnBundle); - ExitOnFailure(hr, "Failed to parse related bundles"); - - // @Version - hr = XmlGetAttributeEx(pixnRegistrationNode, L"Version", &scz); - ExitOnFailure(hr, "Failed to get @Version."); - - hr = VerParseVersion(scz, 0, FALSE, &pRegistration->pVersion); - ExitOnFailure(hr, "Failed to parse @Version: %ls", scz); - - if (pRegistration->pVersion->fInvalid) - { - LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz); - } - - // @ProviderKey - hr = XmlGetAttributeEx(pixnRegistrationNode, L"ProviderKey", &pRegistration->sczProviderKey); - ExitOnFailure(hr, "Failed to get @ProviderKey."); - - // @ExecutableName - hr = XmlGetAttributeEx(pixnRegistrationNode, L"ExecutableName", &pRegistration->sczExecutableName); - ExitOnFailure(hr, "Failed to get @ExecutableName."); - - // @PerMachine - hr = XmlGetYesNoAttribute(pixnRegistrationNode, L"PerMachine", &pRegistration->fPerMachine); - ExitOnFailure(hr, "Failed to get @PerMachine."); - - // select ARP node - hr = XmlSelectSingleNode(pixnRegistrationNode, L"Arp", &pixnArpNode); - if (S_FALSE != hr) - { - ExitOnFailure(hr, "Failed to select ARP node."); - - // @Register - hr = XmlGetYesNoAttribute(pixnArpNode, L"Register", &pRegistration->fRegisterArp); - ExitOnFailure(hr, "Failed to get @Register."); - - // @DisplayName - hr = XmlGetAttributeEx(pixnArpNode, L"DisplayName", &pRegistration->sczDisplayName); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @DisplayName."); - } - - // @DisplayVersion - hr = XmlGetAttributeEx(pixnArpNode, L"DisplayVersion", &pRegistration->sczDisplayVersion); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @DisplayVersion."); - } - - // @Publisher - hr = XmlGetAttributeEx(pixnArpNode, L"Publisher", &pRegistration->sczPublisher); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @Publisher."); - } - - // @HelpLink - hr = XmlGetAttributeEx(pixnArpNode, L"HelpLink", &pRegistration->sczHelpLink); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @HelpLink."); - } - - // @HelpTelephone - hr = XmlGetAttributeEx(pixnArpNode, L"HelpTelephone", &pRegistration->sczHelpTelephone); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @HelpTelephone."); - } - - // @AboutUrl - hr = XmlGetAttributeEx(pixnArpNode, L"AboutUrl", &pRegistration->sczAboutUrl); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @AboutUrl."); - } - - // @UpdateUrl - hr = XmlGetAttributeEx(pixnArpNode, L"UpdateUrl", &pRegistration->sczUpdateUrl); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @UpdateUrl."); - } - - // @ParentDisplayName - hr = XmlGetAttributeEx(pixnArpNode, L"ParentDisplayName", &pRegistration->sczParentDisplayName); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @ParentDisplayName."); - } - - // @Comments - hr = XmlGetAttributeEx(pixnArpNode, L"Comments", &pRegistration->sczComments); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @Comments."); - } - - // @Contact - hr = XmlGetAttributeEx(pixnArpNode, L"Contact", &pRegistration->sczContact); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @Contact."); - } - - // @DisableModify - hr = XmlGetAttributeEx(pixnArpNode, L"DisableModify", &scz); - if (SUCCEEDED(hr)) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"button", -1)) - { - pRegistration->modify = BURN_REGISTRATION_MODIFY_DISABLE_BUTTON; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"yes", -1)) - { - pRegistration->modify = BURN_REGISTRATION_MODIFY_DISABLE; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"no", -1)) - { - pRegistration->modify = BURN_REGISTRATION_MODIFY_ENABLED; - } - else - { - hr = E_UNEXPECTED; - ExitOnRootFailure(hr, "Invalid modify disabled type: %ls", scz); - } - } - else if (E_NOTFOUND == hr) - { - pRegistration->modify = BURN_REGISTRATION_MODIFY_ENABLED; - hr = S_OK; - } - ExitOnFailure(hr, "Failed to get @DisableModify."); - - // @DisableRemove - hr = XmlGetYesNoAttribute(pixnArpNode, L"DisableRemove", &pRegistration->fNoRemove); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @DisableRemove."); - pRegistration->fNoRemoveDefined = TRUE; - } - } - - hr = ParseSoftwareTagsFromXml(pixnRegistrationNode, &pRegistration->softwareTags.rgSoftwareTags, &pRegistration->softwareTags.cSoftwareTags); - ExitOnFailure(hr, "Failed to parse software tag."); - - // select Update node - hr = XmlSelectSingleNode(pixnRegistrationNode, L"Update", &pixnUpdateNode); - if (S_FALSE != hr) - { - ExitOnFailure(hr, "Failed to select Update node."); - - pRegistration->update.fRegisterUpdate = TRUE; - - // @Manufacturer - hr = XmlGetAttributeEx(pixnUpdateNode, L"Manufacturer", &pRegistration->update.sczManufacturer); - ExitOnFailure(hr, "Failed to get @Manufacturer."); - - // @Department - hr = XmlGetAttributeEx(pixnUpdateNode, L"Department", &pRegistration->update.sczDepartment); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @Department."); - } - - // @ProductFamily - hr = XmlGetAttributeEx(pixnUpdateNode, L"ProductFamily", &pRegistration->update.sczProductFamily); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @ProductFamily."); - } - - // @Name - hr = XmlGetAttributeEx(pixnUpdateNode, L"Name", &pRegistration->update.sczName); - ExitOnFailure(hr, "Failed to get @Name."); - - // @Classification - hr = XmlGetAttributeEx(pixnUpdateNode, L"Classification", &pRegistration->update.sczClassification); - ExitOnFailure(hr, "Failed to get @Classification."); - } - - hr = SetPaths(pRegistration); - ExitOnFailure(hr, "Failed to set registration paths."); - -LExit: - ReleaseObject(pixnRegistrationNode); - ReleaseObject(pixnArpNode); - ReleaseObject(pixnUpdateNode); - ReleaseStr(scz); - - return hr; -} - -/******************************************************************* - RegistrationUninitialize - - -*******************************************************************/ -extern "C" void RegistrationUninitialize( - __in BURN_REGISTRATION* pRegistration - ) -{ - ReleaseStr(pRegistration->sczId); - ReleaseStr(pRegistration->sczTag); - - for (DWORD i = 0; i < pRegistration->cDetectCodes; ++i) - { - ReleaseStr(pRegistration->rgsczDetectCodes[i]); - } - ReleaseMem(pRegistration->rgsczDetectCodes); - - for (DWORD i = 0; i < pRegistration->cUpgradeCodes; ++i) - { - ReleaseStr(pRegistration->rgsczUpgradeCodes[i]); - } - ReleaseMem(pRegistration->rgsczUpgradeCodes); - - for (DWORD i = 0; i < pRegistration->cAddonCodes; ++i) - { - ReleaseStr(pRegistration->rgsczAddonCodes[i]); - } - ReleaseMem(pRegistration->rgsczAddonCodes); - - for (DWORD i = 0; i < pRegistration->cPatchCodes; ++i) - { - ReleaseStr(pRegistration->rgsczPatchCodes[i]); - } - ReleaseMem(pRegistration->rgsczPatchCodes); - - ReleaseStr(pRegistration->sczProviderKey); - ReleaseStr(pRegistration->sczActiveParent); - ReleaseStr(pRegistration->sczExecutableName); - - ReleaseStr(pRegistration->sczRegistrationKey); - ReleaseStr(pRegistration->sczCacheExecutablePath); - ReleaseStr(pRegistration->sczResumeCommandLine); - ReleaseStr(pRegistration->sczStateFile); - - ReleaseStr(pRegistration->sczDisplayName); - ReleaseStr(pRegistration->sczDisplayVersion); - ReleaseStr(pRegistration->sczPublisher); - ReleaseStr(pRegistration->sczHelpLink); - ReleaseStr(pRegistration->sczHelpTelephone); - ReleaseStr(pRegistration->sczAboutUrl); - ReleaseStr(pRegistration->sczUpdateUrl); - ReleaseStr(pRegistration->sczParentDisplayName); - ReleaseStr(pRegistration->sczComments); - ReleaseStr(pRegistration->sczContact); - - ReleaseStr(pRegistration->update.sczManufacturer); - ReleaseStr(pRegistration->update.sczDepartment); - ReleaseStr(pRegistration->update.sczProductFamily); - ReleaseStr(pRegistration->update.sczName); - ReleaseStr(pRegistration->update.sczClassification); - - if (pRegistration->softwareTags.rgSoftwareTags) - { - for (DWORD i = 0; i < pRegistration->softwareTags.cSoftwareTags; ++i) - { - ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczFilename); - ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczRegid); - ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczPath); - ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczTag); - } - - MemFree(pRegistration->softwareTags.rgSoftwareTags); - } - - ReleaseStr(pRegistration->sczDetectedProviderKeyBundleId); - ReleaseStr(pRegistration->sczAncestors); - ReleaseStr(pRegistration->sczBundlePackageAncestors); - RelatedBundlesUninitialize(&pRegistration->relatedBundles); - - // clear struct - memset(pRegistration, 0, sizeof(BURN_REGISTRATION)); -} - -/******************************************************************* - RegistrationSetVariables - Initializes bundle variables that map to - registration entities. - -*******************************************************************/ -extern "C" HRESULT RegistrationSetVariables( - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables - ) -{ - HRESULT hr = S_OK; - LPWSTR sczBundleManufacturer = NULL; - LPWSTR sczBundleName = NULL; - - if (pRegistration->fInstalled) - { - hr = VariableSetNumeric(pVariables, BURN_BUNDLE_INSTALLED, 1, TRUE); - ExitOnFailure(hr, "Failed to set the bundle installed built-in variable."); - } - - // Ensure the registration bundle name is updated. - hr = GetBundleName(pRegistration, pVariables, &sczBundleName); - ExitOnFailure(hr, "Failed to initialize bundle name."); - - hr = GetBundleManufacturer(pRegistration, pVariables, &sczBundleName); - ExitOnFailure(hr, "Failed to initialize bundle manufacturer."); - - if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent) - { - hr = VariableSetString(pVariables, BURN_BUNDLE_ACTIVE_PARENT, pRegistration->sczActiveParent, TRUE, FALSE); - ExitOnFailure(hr, "Failed to overwrite the bundle active parent built-in variable."); - } - - hr = VariableSetString(pVariables, BURN_BUNDLE_PROVIDER_KEY, pRegistration->sczProviderKey, TRUE, FALSE); - ExitOnFailure(hr, "Failed to overwrite the bundle provider key built-in variable."); - - hr = VariableSetString(pVariables, BURN_BUNDLE_TAG, pRegistration->sczTag, TRUE, FALSE); - ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable."); - - hr = VariableSetVersion(pVariables, BURN_BUNDLE_VERSION, pRegistration->pVersion, TRUE); - ExitOnFailure(hr, "Failed to overwrite the bundle version built-in variable."); - - hr = VariableSetNumeric(pVariables, BURN_REBOOT_PENDING, IsBundleRebootPending(pRegistration) || IsWuRebootPending() || IsRegistryRebootPending(), TRUE); - ExitOnFailure(hr, "Failed to overwrite the bundle reboot-pending built-in variable."); - -LExit: - ReleaseStr(sczBundleManufacturer); - ReleaseStr(sczBundleName); - - return hr; -} - -extern "C" HRESULT RegistrationDetectInstalled( - __in BURN_REGISTRATION* pRegistration - ) -{ - HRESULT hr = S_OK; - HKEY hkRegistration = NULL; - DWORD dwInstalled = 0; - - pRegistration->fCached = FileExistsEx(pRegistration->sczCacheExecutablePath, NULL); - - // open registration key - hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration); - if (SUCCEEDED(hr)) - { - hr = RegReadNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, &dwInstalled); - } - - // Not finding the key or value is okay. - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) - { - hr = S_OK; - } - - pRegistration->fInstalled = (1 == dwInstalled); - - ReleaseRegKey(hkRegistration); - return hr; -} - -/******************************************************************* - RegistrationDetectResumeMode - Detects registration information on the system - to determine if a resume is taking place. - -*******************************************************************/ -extern "C" HRESULT RegistrationDetectResumeType( - __in BURN_REGISTRATION* pRegistration, - __out BOOTSTRAPPER_RESUME_TYPE* pResumeType - ) -{ - HRESULT hr = S_OK; - HKEY hkRegistration = NULL; - DWORD dwResume = 0; - - if (IsBundleRebootPending(pRegistration)) - { - LogId(REPORT_STANDARD, MSG_PENDING_REBOOT_DETECTED, pRegistration->sczRegistrationKey); - - *pResumeType = BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING; - ExitFunction1(hr = S_OK); - } - - // open registration key - hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration); - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) - { - *pResumeType = BOOTSTRAPPER_RESUME_TYPE_NONE; - ExitFunction1(hr = S_OK); - } - ExitOnFailure(hr, "Failed to open registration key."); - - // read Resume value - hr = RegReadNumber(hkRegistration, L"Resume", &dwResume); - if (E_FILENOTFOUND == hr) - { - *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INVALID; - ExitFunction1(hr = S_OK); - } - ExitOnFailure(hr, "Failed to read Resume value."); - - switch (dwResume) - { - case BURN_RESUME_MODE_ACTIVE: - // a previous run was interrupted - *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INTERRUPTED; - break; - - case BURN_RESUME_MODE_SUSPEND: - *pResumeType = BOOTSTRAPPER_RESUME_TYPE_SUSPEND; - break; - - case BURN_RESUME_MODE_ARP: - *pResumeType = BOOTSTRAPPER_RESUME_TYPE_ARP; - break; - - case BURN_RESUME_MODE_REBOOT_PENDING: - // The volatile pending registry doesn't exist (checked above) which means - // the system was successfully restarted. - *pResumeType = BOOTSTRAPPER_RESUME_TYPE_REBOOT; - break; - - default: - // the value stored in the registry is not valid - *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INVALID; - break; - } - -LExit: - ReleaseRegKey(hkRegistration); - - return hr; -} - -/******************************************************************* - RegistrationDetectRelatedBundles - finds the bundles with same - upgrade/detect/addon/patch codes. - -*******************************************************************/ -extern "C" HRESULT RegistrationDetectRelatedBundles( - __in BURN_REGISTRATION* pRegistration - ) -{ - HRESULT hr = S_OK; - - hr = RelatedBundlesInitializeForScope(TRUE, pRegistration, &pRegistration->relatedBundles); - ExitOnFailure(hr, "Failed to initialize per-machine related bundles."); - - hr = RelatedBundlesInitializeForScope(FALSE, pRegistration, &pRegistration->relatedBundles); - ExitOnFailure(hr, "Failed to initialize per-user related bundles."); - -LExit: - return hr; -} - -/******************************************************************* - RegistrationSessionBegin - Registers a run session on the system. - -*******************************************************************/ -extern "C" HRESULT RegistrationSessionBegin( - __in_z LPCWSTR wzEngineWorkingPath, - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables, - __in DWORD dwRegistrationOptions, - __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction, - __in DWORD64 qwEstimatedSize - ) -{ - HRESULT hr = S_OK; - DWORD dwSize = 0; - HKEY hkRegistration = NULL; - LPWSTR sczPublisher = NULL; - - LogId(REPORT_VERBOSE, MSG_SESSION_BEGIN, pRegistration->sczRegistrationKey, dwRegistrationOptions, LoggingBoolToString(pRegistration->fDisableResume)); - - // Cache bundle executable. - if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE) - { - hr = CacheCompleteBundle(pRegistration->fPerMachine, pRegistration->sczExecutableName, pRegistration->sczId, wzEngineWorkingPath -#ifdef DEBUG - , pRegistration->sczCacheExecutablePath -#endif - ); - ExitOnFailure(hr, "Failed to cache bundle from path: %ls", wzEngineWorkingPath); - } - - // create registration key - hr = RegCreate(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration); - ExitOnFailure(hr, "Failed to create registration key."); - - // Write any ARP values and software tags. - if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION) - { - // Upgrade information - hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, pRegistration->sczCacheExecutablePath); - ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH); - - hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, pRegistration->rgsczUpgradeCodes, pRegistration->cUpgradeCodes); - ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE); - - hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, pRegistration->rgsczAddonCodes, pRegistration->cAddonCodes); - ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE); - - hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, pRegistration->rgsczDetectCodes, pRegistration->cDetectCodes); - ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE); - - hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, pRegistration->rgsczPatchCodes, pRegistration->cPatchCodes); - ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE); - - hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, pRegistration->pVersion->sczVersion); - ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION); - - hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_VERSION_MAJOR, pRegistration->pVersion->dwMajor); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_VERSION_MAJOR); - - hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_VERSION_MINOR, pRegistration->pVersion->dwMinor); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_VERSION_MINOR); - - if (pRegistration->sczProviderKey) - { - hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY, pRegistration->sczProviderKey); - ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY); - } - - if (pRegistration->sczTag) - { - hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_TAG, pRegistration->sczTag); - ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_TAG); - } - - hr = RegWriteStringFormatted(hkRegistration, BURN_REGISTRATION_REGISTRY_ENGINE_VERSION, L"%hs", szVerMajorMinorBuild); - ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_ENGINE_VERSION); - - // DisplayIcon: [path to exe] and ",0" to refer to the first icon in the executable. - hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_DISPLAY_ICON, L"%s,0", pRegistration->sczCacheExecutablePath); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_DISPLAY_ICON); - - // update display name - hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration); - ExitOnFailure(hr, "Failed to update name and publisher."); - - // DisplayVersion: provided by UI - if (pRegistration->sczDisplayVersion) - { - hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_DISPLAY_VERSION, pRegistration->sczDisplayVersion); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_DISPLAY_VERSION); - } - - // Publisher: provided by UI - hr = GetBundleManufacturer(pRegistration, pVariables, &sczPublisher); - hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PUBLISHER, SUCCEEDED(hr) ? sczPublisher : pRegistration->sczPublisher); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PUBLISHER); - - // HelpLink: provided by UI - if (pRegistration->sczHelpLink) - { - hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_HELP_LINK, pRegistration->sczHelpLink); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_HELP_LINK); - } - - // HelpTelephone: provided by UI - if (pRegistration->sczHelpTelephone) - { - hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_HELP_TELEPHONE, pRegistration->sczHelpTelephone); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_HELP_TELEPHONE); - } - - // URLInfoAbout, provided by UI - if (pRegistration->sczAboutUrl) - { - hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_URL_INFO_ABOUT, pRegistration->sczAboutUrl); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_URL_INFO_ABOUT); - } - - // URLUpdateInfo, provided by UI - if (pRegistration->sczUpdateUrl) - { - hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_URL_UPDATE_INFO, pRegistration->sczUpdateUrl); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_URL_UPDATE_INFO); - } - - // ParentDisplayName - if (pRegistration->sczParentDisplayName) - { - hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PARENT_DISPLAY_NAME, pRegistration->sczParentDisplayName); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PARENT_DISPLAY_NAME); - - // Need to write the ParentKeyName but can be set to anything. - hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PARENT_KEY_NAME, pRegistration->sczParentDisplayName); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PARENT_KEY_NAME); - } - - // Comments, provided by UI - if (pRegistration->sczComments) - { - hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_COMMENTS, pRegistration->sczComments); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_COMMENTS); - } - - // Contact, provided by UI - if (pRegistration->sczContact) - { - hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_CONTACT, pRegistration->sczContact); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_CONTACT); - } - - // InstallLocation: provided by UI - // TODO: need to figure out what "InstallLocation" means in a chainer. - - // NoModify - if (BURN_REGISTRATION_MODIFY_DISABLE == pRegistration->modify) - { - hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_MODIFY, 1); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_MODIFY); - } - else if (BURN_REGISTRATION_MODIFY_DISABLE_BUTTON != pRegistration->modify) // if support modify (aka: did not disable anything) - { - // ModifyPath: [path to exe] /modify - hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_MODIFY_PATH, L"\"%ls\" /modify", pRegistration->sczCacheExecutablePath); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_MODIFY_PATH); - - // NoElevateOnModify: 1 - hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY, 1); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY); - } - - // NoRemove: should this be allowed? - if (pRegistration->fNoRemoveDefined) - { - hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_REMOVE, (DWORD)pRegistration->fNoRemove); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_REMOVE); - } - - // Conditionally hide the ARP entry. - if (!pRegistration->fRegisterArp) - { - hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_SYSTEM_COMPONENT, 1); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_SYSTEM_COMPONENT); - } - - // QuietUninstallString: [path to exe] /uninstall /quiet - hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING, L"\"%ls\" /uninstall /quiet", pRegistration->sczCacheExecutablePath); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING); - - // UninstallString, [path to exe] - // If the modify button is to be disabled, we'll add "/modify" to the uninstall string because the button is "Uninstall/Change". Otherwise, - // it's just the "Uninstall" button so we add "/uninstall" to make the program just go away. - LPCWSTR wzUninstallParameters = (BURN_REGISTRATION_MODIFY_DISABLE_BUTTON == pRegistration->modify) ? L"/modify" : L" /uninstall"; - hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_UNINSTALL_STRING, L"\"%ls\" %ls", pRegistration->sczCacheExecutablePath, wzUninstallParameters); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_UNINSTALL_STRING); - - if (pRegistration->softwareTags.cSoftwareTags) - { - hr = WriteSoftwareTags(pVariables, &pRegistration->softwareTags); - ExitOnFailure(hr, "Failed to write software tags."); - } - - // Update registration. - if (pRegistration->update.fRegisterUpdate) - { - hr = WriteUpdateRegistration(pRegistration, pVariables); - ExitOnFailure(hr, "Failed to write update registration."); - } - } - - // Update estimated size. - if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE) - { - qwEstimatedSize /= 1024; // Convert bytes to KB - if (0 < qwEstimatedSize) - { - if (DWORD_MAX < qwEstimatedSize) - { - // ARP doesn't support QWORDs here - dwSize = DWORD_MAX; - } - else - { - dwSize = static_cast(qwEstimatedSize); - } - - hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_ESTIMATED_SIZE, dwSize); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_ESTIMATED_SIZE); - } - } - - // Register the bundle dependency key. - if (BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER == dependencyRegistrationAction) - { - hr = DependencyRegisterBundle(pRegistration); - ExitOnFailure(hr, "Failed to register the bundle dependency key."); - } - - // update resume mode - hr = UpdateResumeMode(pRegistration, hkRegistration, BURN_RESUME_MODE_ACTIVE, FALSE); - ExitOnFailure(hr, "Failed to update resume mode."); - -LExit: - ReleaseStr(sczPublisher); - ReleaseRegKey(hkRegistration); - - return hr; -} - - -/******************************************************************* - RegistrationSessionResume - Resumes a previous run session. - -*******************************************************************/ -extern "C" HRESULT RegistrationSessionResume( - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables - ) -{ - HRESULT hr = S_OK; - HKEY hkRegistration = NULL; - - // open registration key - hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration); - ExitOnFailure(hr, "Failed to open registration key."); - - // update resume mode - hr = UpdateResumeMode(pRegistration, hkRegistration, BURN_RESUME_MODE_ACTIVE, FALSE); - ExitOnFailure(hr, "Failed to update resume mode."); - - // update display name - hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration); - ExitOnFailure(hr, "Failed to update name and publisher."); - -LExit: - ReleaseRegKey(hkRegistration); - - return hr; -} - - -/******************************************************************* - RegistrationSessionEnd - Unregisters a run session from the system. - - *******************************************************************/ -extern "C" HRESULT RegistrationSessionEnd( - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables, - __in BURN_PACKAGES* pPackages, - __in BURN_RESUME_MODE resumeMode, - __in BOOTSTRAPPER_APPLY_RESTART restart, - __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction - ) -{ - HRESULT hr = S_OK; - LPWSTR sczRebootRequiredKey = NULL; - HKEY hkRebootRequired = NULL; - HKEY hkRegistration = NULL; - - LogId(REPORT_STANDARD, MSG_SESSION_END, pRegistration->sczRegistrationKey, LoggingResumeModeToString(resumeMode), LoggingRestartToString(restart), LoggingBoolToString(pRegistration->fDisableResume)); - - // If a restart is required for any reason, write a volatile registry key to track of - // of that fact until the reboot has taken place. - if (BOOTSTRAPPER_APPLY_RESTART_NONE != restart) - { - // We'll write the volatile registry key right next to the bundle ARP registry key - // because that's easy. This is all best effort since the worst case just means in - // the rare case the user launches the same install again before taking the restart - // the BA won't know a restart was still required. - hr = StrAllocFormatted(&sczRebootRequiredKey, REGISTRY_REBOOT_PENDING_FORMAT, pRegistration->sczRegistrationKey); - if (SUCCEEDED(hr)) - { - hr = RegCreateEx(pRegistration->hkRoot, sczRebootRequiredKey, KEY_WRITE, TRUE, NULL, &hkRebootRequired, NULL); - } - - if (FAILED(hr)) - { - ExitTraceSource(DUTIL_SOURCE_DEFAULT, hr, "Failed to write volatile reboot required registry key."); - hr = S_OK; - } - } - - // If no resume mode, then remove the bundle registration. - if (BURN_RESUME_MODE_NONE == resumeMode) - { - // If we just registered the bundle dependency but something went wrong and caused us to not - // keep the bundle registration (like rollback) or we are supposed to unregister the bundle - // dependency when unregistering the bundle, do so. - if (BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER == dependencyRegistrationAction || - BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER == dependencyRegistrationAction) - { - // Remove the bundle dependency key. - DependencyUnregisterBundle(pRegistration, pPackages); - } - - // Delete update registration key. - if (pRegistration->update.fRegisterUpdate) - { - RemoveUpdateRegistration(pRegistration); - } - - RemoveSoftwareTags(pVariables, &pRegistration->softwareTags); - - // Delete registration key. - hr = RegDelete(pRegistration->hkRoot, pRegistration->sczRegistrationKey, REG_KEY_DEFAULT, FALSE); - if (E_FILENOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to delete registration key: %ls", pRegistration->sczRegistrationKey); - } - - CacheRemoveBundle(pRegistration->fPerMachine, pRegistration->sczId); - } - else // the mode needs to be updated so open the registration key. - { - // Open registration key. - hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration); - ExitOnFailure(hr, "Failed to open registration key."); - } - - // Update resume mode. - hr = UpdateResumeMode(pRegistration, hkRegistration, resumeMode, BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart); - ExitOnFailure(hr, "Failed to update resume mode."); - -LExit: - ReleaseRegKey(hkRegistration); - ReleaseRegKey(hkRebootRequired); - ReleaseStr(sczRebootRequiredKey); - - return hr; -} - -/******************************************************************* - RegistrationSaveState - Saves an engine state BLOB for retreval after a resume. - -*******************************************************************/ -extern "C" HRESULT RegistrationSaveState( - __in BURN_REGISTRATION* pRegistration, - __in_bcount(cbBuffer) BYTE* pbBuffer, - __in SIZE_T cbBuffer - ) -{ - HRESULT hr = S_OK; - - // write data to file - hr = FileWrite(pRegistration->sczStateFile, FILE_ATTRIBUTE_NORMAL, pbBuffer, cbBuffer, NULL); - if (E_PATHNOTFOUND == hr) - { - // TODO: should we log that the bundle's cache folder was not present so the state file wasn't created either? - hr = S_OK; - } - ExitOnFailure(hr, "Failed to write state to file: %ls", pRegistration->sczStateFile); - -LExit: - return hr; -} - -/******************************************************************* - RegistrationLoadState - Loads a previously stored engine state BLOB. - -*******************************************************************/ -extern "C" HRESULT RegistrationLoadState( - __in BURN_REGISTRATION* pRegistration, - __out_bcount(*pcbBuffer) BYTE** ppbBuffer, - __out SIZE_T* pcbBuffer - ) -{ - // read data from file - HRESULT hr = FileRead(ppbBuffer, pcbBuffer, pRegistration->sczStateFile); - return hr; -} - -/******************************************************************* -RegistrationGetResumeCommandLine - Gets the resume command line from the registry - -*******************************************************************/ -extern "C" HRESULT RegistrationGetResumeCommandLine( - __in const BURN_REGISTRATION* pRegistration, - __deref_out_z LPWSTR* psczResumeCommandLine - ) -{ - HRESULT hr = S_OK; - HKEY hkRegistration = NULL; - - // open registration key - hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration); - if (SUCCEEDED(hr)) - { - hr = RegReadString(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE, psczResumeCommandLine); - } - - // Not finding the key or value is okay. - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) - { - hr = S_OK; - } - - ReleaseRegKey(hkRegistration); - - return hr; -} - - -// internal helper functions - -static HRESULT ParseSoftwareTagsFromXml( - __in IXMLDOMNode* pixnRegistrationNode, - __out BURN_SOFTWARE_TAG** prgSoftwareTags, - __out DWORD* pcSoftwareTags - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnNodes = NULL; - IXMLDOMNode* pixnNode = NULL; - DWORD cNodes = 0; - - BURN_SOFTWARE_TAG* pSoftwareTags = NULL; - BSTR bstrTagXml = NULL; - - // select tag nodes - hr = XmlSelectNodes(pixnRegistrationNode, L"SoftwareTag", &pixnNodes); - ExitOnFailure(hr, "Failed to select software tag nodes."); - - // get tag node count - hr = pixnNodes->get_length((long*)&cNodes); - ExitOnFailure(hr, "Failed to get software tag count."); - - if (cNodes) - { - pSoftwareTags = (BURN_SOFTWARE_TAG*)MemAlloc(sizeof(BURN_SOFTWARE_TAG) * cNodes, TRUE); - ExitOnNull(pSoftwareTags, hr, E_OUTOFMEMORY, "Failed to allocate memory for software tag structs."); - - for (DWORD i = 0; i < cNodes; ++i) - { - BURN_SOFTWARE_TAG* pSoftwareTag = &pSoftwareTags[i]; - - hr = XmlNextElement(pixnNodes, &pixnNode, NULL); - ExitOnFailure(hr, "Failed to get next node."); - - hr = XmlGetAttributeEx(pixnNode, L"Filename", &pSoftwareTag->sczFilename); - ExitOnFailure(hr, "Failed to get @Filename."); - - hr = XmlGetAttributeEx(pixnNode, L"Regid", &pSoftwareTag->sczRegid); - ExitOnFailure(hr, "Failed to get @Regid."); - - hr = XmlGetAttributeEx(pixnNode, L"Path", &pSoftwareTag->sczPath); - ExitOnFailure(hr, "Failed to get @Path."); - - hr = XmlGetText(pixnNode, &bstrTagXml); - ExitOnFailure(hr, "Failed to get SoftwareTag text."); - - hr = StrAnsiAllocString(&pSoftwareTag->sczTag, bstrTagXml, 0, CP_UTF8); - ExitOnFailure(hr, "Failed to convert SoftwareTag text to UTF-8"); - - // prepare next iteration - ReleaseNullBSTR(bstrTagXml); - ReleaseNullObject(pixnNode); - } - } - - *pcSoftwareTags = cNodes; - *prgSoftwareTags = pSoftwareTags; - pSoftwareTags = NULL; - - hr = S_OK; - -LExit: - ReleaseBSTR(bstrTagXml); - ReleaseObject(pixnNode); - ReleaseObject(pixnNodes); - ReleaseMem(pSoftwareTags); - - return hr; -} - -static HRESULT SetPaths( - __in BURN_REGISTRATION* pRegistration - ) -{ - HRESULT hr = S_OK; - LPWSTR sczCacheDirectory = NULL; - - // save registration key root - pRegistration->hkRoot = pRegistration->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - - // build uninstall registry key path - hr = StrAllocFormatted(&pRegistration->sczRegistrationKey, L"%s\\%s", BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, pRegistration->sczId); - ExitOnFailure(hr, "Failed to build uninstall registry key path."); - - // build cache directory - hr = CacheGetCompletedPath(pRegistration->fPerMachine, pRegistration->sczId, &sczCacheDirectory); - ExitOnFailure(hr, "Failed to build cache directory."); - - // build cached executable path - hr = PathConcat(sczCacheDirectory, pRegistration->sczExecutableName, &pRegistration->sczCacheExecutablePath); - ExitOnFailure(hr, "Failed to build cached executable path."); - - // build state file path - hr = StrAllocFormatted(&pRegistration->sczStateFile, L"%s\\state.rsm", sczCacheDirectory); - ExitOnFailure(hr, "Failed to build state file path."); - -LExit: - ReleaseStr(sczCacheDirectory); - return hr; -} - -static HRESULT GetBundleManufacturer( - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables, - __out LPWSTR* psczBundleManufacturer - ) -{ - HRESULT hr = S_OK; - - hr = VariableGetString(pVariables, BURN_BUNDLE_MANUFACTURER, psczBundleManufacturer); - if (E_NOTFOUND == hr) - { - hr = VariableSetString(pVariables, BURN_BUNDLE_MANUFACTURER, pRegistration->sczPublisher, FALSE, FALSE); - ExitOnFailure(hr, "Failed to set bundle manufacturer."); - - hr = StrAllocString(psczBundleManufacturer, pRegistration->sczPublisher, 0); - } - ExitOnFailure(hr, "Failed to get bundle manufacturer."); - -LExit: - return hr; -} - -static HRESULT GetBundleName( - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables, - __out LPWSTR* psczBundleName - ) -{ - HRESULT hr = S_OK; - - hr = VariableGetString(pVariables, BURN_BUNDLE_NAME, psczBundleName); - if (E_NOTFOUND == hr) - { - hr = VariableSetString(pVariables, BURN_BUNDLE_NAME, pRegistration->sczDisplayName, FALSE, FALSE); - ExitOnFailure(hr, "Failed to set bundle name."); - - hr = StrAllocString(psczBundleName, pRegistration->sczDisplayName, 0); - } - ExitOnFailure(hr, "Failed to get bundle name."); - -LExit: - return hr; -} - -static HRESULT UpdateResumeMode( - __in BURN_REGISTRATION* pRegistration, - __in HKEY hkRegistration, - __in BURN_RESUME_MODE resumeMode, - __in BOOL fRestartInitiated - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - HKEY hkRebootRequired = NULL; - HKEY hkRun = NULL; - LPWSTR sczResumeCommandLine = NULL; - LPCWSTR sczResumeKey = REGISTRY_RUN_ONCE_KEY; - - LogId(REPORT_STANDARD, MSG_SESSION_UPDATE, pRegistration->sczRegistrationKey, LoggingResumeModeToString(resumeMode), LoggingBoolToString(fRestartInitiated), LoggingBoolToString(pRegistration->fDisableResume)); - - // write resume information - if (hkRegistration) - { - // write Resume value - hr = RegWriteNumber(hkRegistration, L"Resume", (DWORD)resumeMode); - ExitOnFailure(hr, "Failed to write Resume value."); - - // Write the Installed value *only* when the mode is ARP. This will tell us - // that the bundle considers itself "installed" on the machine. Note that we - // never change the value to "0" after that. The bundle will be considered - // "uninstalled" when all of the registration is removed. - if (BURN_RESUME_MODE_ARP == resumeMode) - { - // Write Installed value. - hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, 1); - ExitOnFailure(hr, "Failed to write Installed value."); - } - } - - // If the engine is active write the run key so we resume if there is an unexpected - // power loss. Also, if a restart was initiated in the middle of the chain then - // ensure the run key exists (it should since going active would have written it). - // Do not write the run key when embedded since the containing bundle - // is expected to detect for and restart the embedded bundle. - if ((BURN_RESUME_MODE_ACTIVE == resumeMode || fRestartInitiated) && !pRegistration->fDisableResume) - { - // append RunOnce switch - hr = StrAllocFormatted(&sczResumeCommandLine, L"\"%ls\" /%ls", pRegistration->sczCacheExecutablePath, BURN_COMMANDLINE_SWITCH_RUNONCE); - ExitOnFailure(hr, "Failed to format resume command line for RunOnce."); - - // write run key - hr = RegCreate(pRegistration->hkRoot, sczResumeKey, KEY_WRITE, &hkRun); - ExitOnFailure(hr, "Failed to create run key."); - - hr = RegWriteString(hkRun, pRegistration->sczId, sczResumeCommandLine); - ExitOnFailure(hr, "Failed to write run key value."); - - hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE, pRegistration->sczResumeCommandLine); - ExitOnFailure(hr, "Failed to write resume command line value."); - } - else // delete run key value - { - hr = RegOpen(pRegistration->hkRoot, sczResumeKey, KEY_WRITE, &hkRun); - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) - { - hr = S_OK; - } - else - { - ExitOnWin32Error(er, hr, "Failed to open run key."); - - er = ::RegDeleteValueW(hkRun, pRegistration->sczId); - if (ERROR_FILE_NOT_FOUND == er) - { - er = ERROR_SUCCESS; - } - ExitOnWin32Error(er, hr, "Failed to delete run key value."); - } - - if (hkRegistration) - { - er = ::RegDeleteValueW(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE); - if (ERROR_FILE_NOT_FOUND == er) - { - er = ERROR_SUCCESS; - } - ExitOnWin32Error(er, hr, "Failed to delete resume command line value."); - } - } - -LExit: - ReleaseStr(sczResumeCommandLine); - ReleaseRegKey(hkRebootRequired); - ReleaseRegKey(hkRun); - - return hr; -} - -static HRESULT ParseRelatedCodes( - __in BURN_REGISTRATION* pRegistration, - __in IXMLDOMNode* pixnBundle - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnNodes = NULL; - IXMLDOMNode* pixnElement = NULL; - LPWSTR sczAction = NULL; - LPWSTR sczId = NULL; - DWORD cElements = 0; - - hr = XmlSelectNodes(pixnBundle, L"RelatedBundle", &pixnNodes); - ExitOnFailure(hr, "Failed to get RelatedBundle nodes"); - - hr = pixnNodes->get_length((long*)&cElements); - ExitOnFailure(hr, "Failed to get RelatedBundle element count."); - - for (DWORD i = 0; i < cElements; ++i) - { - hr = XmlNextElement(pixnNodes, &pixnElement, NULL); - ExitOnFailure(hr, "Failed to get next RelatedBundle element."); - - hr = XmlGetAttributeEx(pixnElement, L"Action", &sczAction); - ExitOnFailure(hr, "Failed to get @Action."); - - hr = XmlGetAttributeEx(pixnElement, L"Id", &sczId); - ExitOnFailure(hr, "Failed to get @Id."); - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Detect", -1)) - { - hr = MemEnsureArraySize(reinterpret_cast(&pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes + 1, sizeof(LPWSTR), 5); - ExitOnFailure(hr, "Failed to resize Detect code array in registration"); - - pRegistration->rgsczDetectCodes[pRegistration->cDetectCodes] = sczId; - sczId = NULL; - ++pRegistration->cDetectCodes; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Upgrade", -1)) - { - hr = MemEnsureArraySize(reinterpret_cast(&pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes + 1, sizeof(LPWSTR), 5); - ExitOnFailure(hr, "Failed to resize Upgrade code array in registration"); - - pRegistration->rgsczUpgradeCodes[pRegistration->cUpgradeCodes] = sczId; - sczId = NULL; - ++pRegistration->cUpgradeCodes; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Addon", -1)) - { - hr = MemEnsureArraySize(reinterpret_cast(&pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes + 1, sizeof(LPWSTR), 5); - ExitOnFailure(hr, "Failed to resize Addon code array in registration"); - - pRegistration->rgsczAddonCodes[pRegistration->cAddonCodes] = sczId; - sczId = NULL; - ++pRegistration->cAddonCodes; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Patch", -1)) - { - hr = MemEnsureArraySize(reinterpret_cast(&pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes + 1, sizeof(LPWSTR), 5); - ExitOnFailure(hr, "Failed to resize Patch code array in registration"); - - pRegistration->rgsczPatchCodes[pRegistration->cPatchCodes] = sczId; - sczId = NULL; - ++pRegistration->cPatchCodes; - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid value for @Action: %ls", sczAction); - } - } - -LExit: - ReleaseObject(pixnNodes); - ReleaseObject(pixnElement); - ReleaseStr(sczAction); - ReleaseStr(sczId); - - return hr; -} - -static HRESULT FormatUpdateRegistrationKey( - __in BURN_REGISTRATION* pRegistration, - __out_z LPWSTR* psczKey - ) -{ - HRESULT hr = S_OK; - LPWSTR sczKey = NULL; - - hr = StrAllocFormatted(&sczKey, L"SOFTWARE\\%ls\\Updates\\", pRegistration->update.sczManufacturer); - ExitOnFailure(hr, "Failed to format the key path for update registration."); - - if (pRegistration->update.sczProductFamily) - { - hr = StrAllocFormatted(&sczKey, L"%ls%ls\\", sczKey, pRegistration->update.sczProductFamily); - ExitOnFailure(hr, "Failed to format the key path for update registration."); - } - - hr = StrAllocConcat(&sczKey, pRegistration->update.sczName, 0); - ExitOnFailure(hr, "Failed to format the key path for update registration."); - - *psczKey = sczKey; - sczKey = NULL; - -LExit: - ReleaseStr(sczKey); - - return hr; -} - -static HRESULT WriteSoftwareTags( - __in BURN_VARIABLES* pVariables, - __in BURN_SOFTWARE_TAGS* pSoftwareTags - ) -{ - HRESULT hr = S_OK; - LPWSTR sczRootFolder = NULL; - LPWSTR sczTagFolder = NULL; - LPWSTR sczPath = NULL; - - for (DWORD iTag = 0; iTag < pSoftwareTags->cSoftwareTags; ++iTag) - { - BURN_SOFTWARE_TAG* pSoftwareTag = pSoftwareTags->rgSoftwareTags + iTag; - - hr = VariableFormatString(pVariables, pSoftwareTag->sczPath, &sczRootFolder, NULL); - ExitOnFailure(hr, "Failed to format tag folder path."); - - hr = PathConcat(sczRootFolder, SWIDTAG_FOLDER, &sczTagFolder); - ExitOnFailure(hr, "Failed to allocate regid folder path."); - - hr = PathConcat(sczTagFolder, pSoftwareTag->sczFilename, &sczPath); - ExitOnFailure(hr, "Failed to allocate regid file path."); - - hr = DirEnsureExists(sczTagFolder, NULL); - ExitOnFailure(hr, "Failed to create regid folder: %ls", sczTagFolder); - - hr = FileWrite(sczPath, FILE_ATTRIBUTE_NORMAL, reinterpret_cast(pSoftwareTag->sczTag), lstrlenA(pSoftwareTag->sczTag), NULL); - ExitOnFailure(hr, "Failed to write tag xml to file: %ls", sczPath); - } - -LExit: - ReleaseStr(sczPath); - ReleaseStr(sczTagFolder); - ReleaseStr(sczRootFolder); - - return hr; -} - -static HRESULT RemoveSoftwareTags( - __in BURN_VARIABLES* pVariables, - __in BURN_SOFTWARE_TAGS* pSoftwareTags - ) -{ - HRESULT hr = S_OK; - LPWSTR sczRootFolder = NULL; - LPWSTR sczTagFolder = NULL; - LPWSTR sczPath = NULL; - - for (DWORD iTag = 0; iTag < pSoftwareTags->cSoftwareTags; ++iTag) - { - BURN_SOFTWARE_TAG* pSoftwareTag = pSoftwareTags->rgSoftwareTags + iTag; - - hr = VariableFormatString(pVariables, pSoftwareTag->sczPath, &sczRootFolder, NULL); - ExitOnFailure(hr, "Failed to format tag folder path."); - - hr = PathConcat(sczRootFolder, SWIDTAG_FOLDER, &sczTagFolder); - ExitOnFailure(hr, "Failed to allocate regid folder path."); - - hr = PathConcat(sczTagFolder, pSoftwareTag->sczFilename, &sczPath); - ExitOnFailure(hr, "Failed to allocate regid file path."); - - // Best effort to delete the software tag file and the regid folder. - FileEnsureDelete(sczPath); - - DirDeleteEmptyDirectoriesToRoot(sczTagFolder, 0); - } - -LExit: - ReleaseStr(sczPath); - ReleaseStr(sczTagFolder); - ReleaseStr(sczRootFolder); - - return hr; -} - -static HRESULT WriteUpdateRegistration( - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables - ) -{ - HRESULT hr = S_OK; - LPWSTR sczKey = NULL; - HKEY hkKey = NULL; - - hr = FormatUpdateRegistrationKey(pRegistration, &sczKey); - ExitOnFailure(hr, "Failed to get the formatted key path for update registration."); - - hr = RegCreate(pRegistration->hkRoot, sczKey, KEY_WRITE, &hkKey); - ExitOnFailure(hr, "Failed to create the key for update registration."); - - hr = RegWriteString(hkKey, L"ThisVersionInstalled", L"Y"); - ExitOnFailure(hr, "Failed to write %ls value.", L"ThisVersionInstalled"); - - hr = RegWriteString(hkKey, L"PackageName", pRegistration->sczDisplayName); - ExitOnFailure(hr, "Failed to write %ls value.", L"PackageName"); - - hr = RegWriteString(hkKey, L"PackageVersion", pRegistration->sczDisplayVersion); - ExitOnFailure(hr, "Failed to write %ls value.", L"PackageVersion"); - - hr = RegWriteString(hkKey, L"Publisher", pRegistration->sczPublisher); - ExitOnFailure(hr, "Failed to write %ls value.", L"Publisher"); - - if (pRegistration->update.sczDepartment) - { - hr = RegWriteString(hkKey, L"PublishingGroup", pRegistration->update.sczDepartment); - ExitOnFailure(hr, "Failed to write %ls value.", L"PublishingGroup"); - } - - hr = RegWriteString(hkKey, L"ReleaseType", pRegistration->update.sczClassification); - ExitOnFailure(hr, "Failed to write %ls value.", L"ReleaseType"); - - hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_LOGONUSER, L"InstalledBy"); - ExitOnFailure(hr, "Failed to write %ls value.", L"InstalledBy"); - - hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_DATE, L"InstalledDate"); - ExitOnFailure(hr, "Failed to write %ls value.", L"InstalledDate"); - - hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_INSTALLERNAME, L"InstallerName"); - ExitOnFailure(hr, "Failed to write %ls value.", L"InstallerName"); - - hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_INSTALLERVERSION, L"InstallerVersion"); - ExitOnFailure(hr, "Failed to write %ls value.", L"InstallerVersion"); - -LExit: - ReleaseRegKey(hkKey); - ReleaseStr(sczKey); - - return hr; -} - -static HRESULT RemoveUpdateRegistration( - __in BURN_REGISTRATION* pRegistration - ) -{ - HRESULT hr = S_OK; - LPWSTR sczKey = NULL; - LPWSTR sczPackageVersion = NULL; - HKEY hkKey = NULL; - BOOL fDeleteRegKey = TRUE; - - hr = FormatUpdateRegistrationKey(pRegistration, &sczKey); - ExitOnFailure(hr, "Failed to format key for update registration."); - - // Only delete if the uninstalling bundle's PackageVersion is the same as the - // PackageVersion in the update registration key. - // This is to support build to build upgrades - hr = RegOpen(pRegistration->hkRoot, sczKey, KEY_QUERY_VALUE, &hkKey); - if (SUCCEEDED(hr)) - { - hr = RegReadString(hkKey, L"PackageVersion", &sczPackageVersion); - if (SUCCEEDED(hr)) - { - if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczPackageVersion, -1, pRegistration->sczDisplayVersion, -1)) - { - fDeleteRegKey = FALSE; - } - } - ReleaseRegKey(hkKey); - } - - // Unable to open the key or read the value is okay. - hr = S_OK; - - if (fDeleteRegKey) - { - hr = RegDelete(pRegistration->hkRoot, sczKey, REG_KEY_DEFAULT, FALSE); - if (E_FILENOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to remove update registration key: %ls", sczKey); - } - } - -LExit: - ReleaseStr(sczPackageVersion); - ReleaseStr(sczKey); - - return hr; -} - -static HRESULT RegWriteStringVariable( - __in HKEY hk, - __in BURN_VARIABLES* pVariables, - __in LPCWSTR wzVariable, - __in LPCWSTR wzName - ) -{ - HRESULT hr = S_OK; - LPWSTR sczValue = NULL; - - hr = VariableGetString(pVariables, wzVariable, &sczValue); - ExitOnFailure(hr, "Failed to get the %ls variable.", wzVariable); - - hr = RegWriteString(hk, wzName, sczValue); - ExitOnFailure(hr, "Failed to write %ls value.", wzName); - -LExit: - StrSecureZeroFreeString(sczValue); - - return hr; -} - -static HRESULT UpdateBundleNameRegistration( - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables, - __in HKEY hkRegistration - ) -{ - HRESULT hr = S_OK; - LPWSTR sczDisplayName = NULL; - - // DisplayName: provided by UI - hr = GetBundleName(pRegistration, pVariables, &sczDisplayName); - hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME, SUCCEEDED(hr) ? sczDisplayName : pRegistration->sczDisplayName); - ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME); - -LExit: - ReleaseStr(sczDisplayName); - - return hr; -} - -static BOOL IsWuRebootPending() -{ - HRESULT hr = S_OK; - BOOL fRebootPending = FALSE; - - // Do a best effort to ask WU if a reboot is required. If anything goes - // wrong then let's pretend a reboot is not required. - hr = ::CoInitialize(NULL); - if (SUCCEEDED(hr) || RPC_E_CHANGED_MODE == hr) - { - hr = WuaRestartRequired(&fRebootPending); - if (FAILED(hr)) - { - fRebootPending = FALSE; - } - - ::CoUninitialize(); - } - - return fRebootPending; -} - -static BOOL IsBundleRebootPending(BURN_REGISTRATION* pRegistration) -{ - HRESULT hr = S_OK; - LPWSTR sczRebootRequiredKey = NULL; - HKEY hkRebootRequired = NULL; - BOOL fBundleRebootPending = FALSE; - - // Check to see if a restart is pending for this bundle. - hr = StrAllocFormatted(&sczRebootRequiredKey, REGISTRY_REBOOT_PENDING_FORMAT, pRegistration->sczRegistrationKey); - ExitOnFailure(hr, "Failed to format pending restart registry key to read."); - - hr = RegOpen(pRegistration->hkRoot, sczRebootRequiredKey, KEY_QUERY_VALUE, &hkRebootRequired); - fBundleRebootPending = SUCCEEDED(hr); - -LExit: - ReleaseStr(sczRebootRequiredKey); - ReleaseRegKey(hkRebootRequired); - - return fBundleRebootPending; -} - -static BOOL IsRegistryRebootPending() -{ - HRESULT hr = S_OK; - DWORD dwValue; - HKEY hk = NULL; - BOOL fRebootPending = FALSE; - - hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\ServerManager", L"CurrentRebootAttempts", TRUE, &dwValue); - fRebootPending = SUCCEEDED(hr) && 0 < dwValue; - - if (!fRebootPending) - { - hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Updates", L"UpdateExeVolatile", TRUE, &dwValue); - fRebootPending = SUCCEEDED(hr) && 0 < dwValue; - - if (!fRebootPending) - { - fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootPending", NULL, TRUE); - - if (!fRebootPending) - { - fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootInProgress", NULL, TRUE); - - if (!fRebootPending) - { - hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update", L"AUState", TRUE, &dwValue); - fRebootPending = SUCCEEDED(hr) && 8 == dwValue; - - if (!fRebootPending) - { - fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager", L"PendingFileRenameOperations", TRUE); - - if (!fRebootPending) - { - fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager", L"PendingFileRenameOperations2", TRUE); - - if (!fRebootPending) - { - hr = RegOpen(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\FileRenameOperations", KEY_READ | KEY_WOW64_64KEY, &hk); - if (SUCCEEDED(hr)) - { - DWORD cSubKeys = 0; - DWORD cValues = 0; - hr = RegQueryKey(hk, &cSubKeys, &cValues); - fRebootPending = SUCCEEDED(hr) && (0 < cSubKeys || 0 < cValues); - } - } - } - } - } - } - } - } - - ReleaseRegKey(hk); - - return fRebootPending; -} diff --git a/src/engine/registration.h b/src/engine/registration.h deleted file mode 100644 index 6d8a6d2a..00000000 --- a/src/engine/registration.h +++ /dev/null @@ -1,225 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -enum BURN_MODE; -enum BURN_DEPENDENCY_REGISTRATION_ACTION; -struct _BURN_LOGGING; -typedef _BURN_LOGGING BURN_LOGGING; - -// constants - -const LPCWSTR BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; -const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH = L"BundleCachePath"; -const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE = L"BundleAddonCode"; -const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE = L"BundleDetectCode"; -const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE = L"BundlePatchCode"; -const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode"; -const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME = L"DisplayName"; -const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION = L"BundleVersion"; -const LPCWSTR BURN_REGISTRATION_REGISTRY_ENGINE_VERSION = L"EngineVersion"; -const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey"; -const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_TAG = L"BundleTag"; - -enum BURN_RESUME_MODE -{ - BURN_RESUME_MODE_NONE, - BURN_RESUME_MODE_ACTIVE, - BURN_RESUME_MODE_SUSPEND, - BURN_RESUME_MODE_ARP, - BURN_RESUME_MODE_REBOOT_PENDING, -}; - -enum BURN_REGISTRATION_MODIFY_TYPE -{ - BURN_REGISTRATION_MODIFY_ENABLED, - BURN_REGISTRATION_MODIFY_DISABLE, - BURN_REGISTRATION_MODIFY_DISABLE_BUTTON, -}; - - -// structs - -typedef struct _BURN_UPDATE_REGISTRATION -{ - BOOL fRegisterUpdate; - LPWSTR sczManufacturer; - LPWSTR sczDepartment; - LPWSTR sczProductFamily; - LPWSTR sczName; - LPWSTR sczClassification; -} BURN_UPDATE_REGISTRATION; - -typedef struct _BURN_RELATED_BUNDLE -{ - BOOTSTRAPPER_RELATION_TYPE relationType; - BOOL fForwardCompatible; - - VERUTIL_VERSION* pVersion; - LPWSTR sczTag; - BOOL fPlannable; - - BURN_PACKAGE package; -} BURN_RELATED_BUNDLE; - -typedef struct _BURN_RELATED_BUNDLES -{ - BURN_RELATED_BUNDLE* rgRelatedBundles; - DWORD cRelatedBundles; -} BURN_RELATED_BUNDLES; - -typedef struct _BURN_SOFTWARE_TAG -{ - LPWSTR sczFilename; - LPWSTR sczRegid; - LPWSTR sczPath; - LPSTR sczTag; -} BURN_SOFTWARE_TAG; - -typedef struct _BURN_SOFTWARE_TAGS -{ - BURN_SOFTWARE_TAG* rgSoftwareTags; - DWORD cSoftwareTags; -} BURN_SOFTWARE_TAGS; - -typedef struct _BURN_REGISTRATION -{ - BOOL fPerMachine; - BOOL fRegisterArp; - BOOL fDisableResume; - BOOL fCached; - BOOL fInstalled; - LPWSTR sczId; - LPWSTR sczTag; - - LPWSTR *rgsczDetectCodes; - DWORD cDetectCodes; - - LPWSTR *rgsczUpgradeCodes; - DWORD cUpgradeCodes; - - LPWSTR *rgsczAddonCodes; - DWORD cAddonCodes; - - LPWSTR *rgsczPatchCodes; - DWORD cPatchCodes; - - VERUTIL_VERSION* pVersion; - LPWSTR sczActiveParent; - LPWSTR sczProviderKey; - LPWSTR sczExecutableName; - - // paths - HKEY hkRoot; - LPWSTR sczRegistrationKey; - LPWSTR sczCacheExecutablePath; - LPWSTR sczResumeCommandLine; - LPWSTR sczStateFile; - - // ARP registration - LPWSTR sczDisplayName; - LPWSTR sczDisplayVersion; - LPWSTR sczPublisher; - LPWSTR sczHelpLink; - LPWSTR sczHelpTelephone; - LPWSTR sczAboutUrl; - LPWSTR sczUpdateUrl; - LPWSTR sczParentDisplayName; - LPWSTR sczComments; - //LPWSTR sczReadme; // TODO: this would be a file path - LPWSTR sczContact; - //DWORD64 qwEstimatedSize; // TODO: size should come from disk cost calculation - BURN_REGISTRATION_MODIFY_TYPE modify; - BOOL fNoRemoveDefined; - BOOL fNoRemove; - - BURN_SOFTWARE_TAGS softwareTags; - - // Update registration - BURN_UPDATE_REGISTRATION update; - - BURN_RELATED_BUNDLES relatedBundles; // Only valid after detect. - DEPENDENCY* rgIgnoredDependencies; // Only valid after detect. - UINT cIgnoredDependencies; // Only valid after detect. - DEPENDENCY* rgDependents; // Only valid after detect. - UINT cDependents; // Only valid after detect. - BOOL fIgnoreAllDependents; // Only valid after detect. - LPCWSTR wzSelfDependent; // Only valid after detect. - BOOL fSelfRegisteredAsDependent; // Only valid after detect. - BOOL fParentRegisteredAsDependent; // Only valid after detect. - BOOL fForwardCompatibleBundleExists; // Only valid after detect. - BOOL fEligibleForCleanup; // Only valid after detect. - - LPWSTR sczDetectedProviderKeyBundleId; - LPWSTR sczAncestors; - LPWSTR sczBundlePackageAncestors; -} BURN_REGISTRATION; - - -// functions - -HRESULT RegistrationParseFromXml( - __in BURN_REGISTRATION* pRegistration, - __in IXMLDOMNode* pixnBundle - ); -void RegistrationUninitialize( - __in BURN_REGISTRATION* pRegistration - ); -HRESULT RegistrationSetVariables( - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables - ); -HRESULT RegistrationDetectInstalled( - __in BURN_REGISTRATION* pRegistration - ); -HRESULT RegistrationDetectResumeType( - __in BURN_REGISTRATION* pRegistration, - __out BOOTSTRAPPER_RESUME_TYPE* pResumeType - ); -HRESULT RegistrationDetectRelatedBundles( - __in BURN_REGISTRATION* pRegistration - ); -HRESULT RegistrationSessionBegin( - __in_z LPCWSTR wzEngineWorkingPath, - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables, - __in DWORD dwRegistrationOptions, - __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction, - __in DWORD64 qwEstimatedSize - ); -HRESULT RegistrationSessionResume( - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables - ); -HRESULT RegistrationSessionEnd( - __in BURN_REGISTRATION* pRegistration, - __in BURN_VARIABLES* pVariables, - __in BURN_PACKAGES* pPackages, - __in BURN_RESUME_MODE resumeMode, - __in BOOTSTRAPPER_APPLY_RESTART restart, - __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction - ); -HRESULT RegistrationSaveState( - __in BURN_REGISTRATION* pRegistration, - __in_bcount_opt(cbBuffer) BYTE* pbBuffer, - __in_opt SIZE_T cbBuffer - ); -HRESULT RegistrationLoadState( - __in BURN_REGISTRATION* pRegistration, - __out_bcount(*pcbBuffer) BYTE** ppbBuffer, - __out SIZE_T* pcbBuffer - ); -HRESULT RegistrationGetResumeCommandLine( - __in const BURN_REGISTRATION* pRegistration, - __deref_out_z LPWSTR* psczResumeCommandLine - ); - - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/relatedbundle.cpp b/src/engine/relatedbundle.cpp deleted file mode 100644 index d3c856a6..00000000 --- a/src/engine/relatedbundle.cpp +++ /dev/null @@ -1,483 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// internal function declarations - -static HRESULT LoadIfRelatedBundle( - __in BOOL fPerMachine, - __in HKEY hkUninstallKey, - __in_z LPCWSTR sczRelatedBundleId, - __in BURN_REGISTRATION* pRegistration, - __in BURN_RELATED_BUNDLES* pRelatedBundles - ); -static HRESULT DetermineRelationType( - __in HKEY hkBundleId, - __in BURN_REGISTRATION* pRegistration, - __out BOOTSTRAPPER_RELATION_TYPE* pRelationType - ); -static HRESULT LoadRelatedBundleFromKey( - __in_z LPCWSTR wzRelatedBundleId, - __in HKEY hkBundleId, - __in BOOL fPerMachine, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __inout BURN_RELATED_BUNDLE *pRelatedBundle - ); - - -// function definitions - -extern "C" HRESULT RelatedBundlesInitializeForScope( - __in BOOL fPerMachine, - __in BURN_REGISTRATION* pRegistration, - __in BURN_RELATED_BUNDLES* pRelatedBundles - ) -{ - HRESULT hr = S_OK; - HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - HKEY hkUninstallKey = NULL; - LPWSTR sczRelatedBundleId = NULL; - - hr = RegOpen(hkRoot, BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, &hkUninstallKey); - if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) - { - ExitFunction1(hr = S_OK); - } - ExitOnFailure(hr, "Failed to open uninstall registry key."); - - for (DWORD dwIndex = 0; /* exit via break below */; ++dwIndex) - { - hr = RegKeyEnum(hkUninstallKey, dwIndex, &sczRelatedBundleId); - if (E_NOMOREITEMS == hr) - { - hr = S_OK; - break; - } - ExitOnFailure(hr, "Failed to enumerate uninstall key for related bundles."); - - // If we did not find our bundle id, try to load the subkey as a related bundle. - if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczRelatedBundleId, -1, pRegistration->sczId, -1)) - { - // Ignore failures here since we'll often find products that aren't actually - // related bundles (or even bundles at all). - HRESULT hrRelatedBundle = LoadIfRelatedBundle(fPerMachine, hkUninstallKey, sczRelatedBundleId, pRegistration, pRelatedBundles); - UNREFERENCED_PARAMETER(hrRelatedBundle); - } - } - -LExit: - ReleaseStr(sczRelatedBundleId); - ReleaseRegKey(hkUninstallKey); - - return hr; -} - -extern "C" void RelatedBundlesUninitialize( - __in BURN_RELATED_BUNDLES* pRelatedBundles - ) -{ - if (pRelatedBundles->rgRelatedBundles) - { - for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i) - { - BURN_PACKAGE* pPackage = &pRelatedBundles->rgRelatedBundles[i].package; - - for (DWORD j = 0; j < pPackage->payloads.cItems; ++j) - { - PayloadUninitialize(pPackage->payloads.rgItems[j].pPayload); - } - - PackageUninitialize(pPackage); - ReleaseStr(pRelatedBundles->rgRelatedBundles[i].sczTag); - } - - MemFree(pRelatedBundles->rgRelatedBundles); - } - - memset(pRelatedBundles, 0, sizeof(BURN_RELATED_BUNDLES)); -} - - -// internal helper functions - -static HRESULT LoadIfRelatedBundle( - __in BOOL fPerMachine, - __in HKEY hkUninstallKey, - __in_z LPCWSTR sczRelatedBundleId, - __in BURN_REGISTRATION* pRegistration, - __in BURN_RELATED_BUNDLES* pRelatedBundles - ) -{ - HRESULT hr = S_OK; - HKEY hkBundleId = NULL; - BOOTSTRAPPER_RELATION_TYPE relationType = BOOTSTRAPPER_RELATION_NONE; - - hr = RegOpen(hkUninstallKey, sczRelatedBundleId, KEY_READ, &hkBundleId); - ExitOnFailure(hr, "Failed to open uninstall key for potential related bundle: %ls", sczRelatedBundleId); - - hr = DetermineRelationType(hkBundleId, pRegistration, &relationType); - if (FAILED(hr) || BOOTSTRAPPER_RELATION_NONE == relationType) - { - // Must not be a related bundle. - hr = E_NOTFOUND; - } - else // load the related bundle. - { - hr = MemEnsureArraySize(reinterpret_cast(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5); - ExitOnFailure(hr, "Failed to ensure there is space for related bundles."); - - BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles; - - hr = LoadRelatedBundleFromKey(sczRelatedBundleId, hkBundleId, fPerMachine, relationType, pRelatedBundle); - ExitOnFailure(hr, "Failed to initialize package from related bundle id: %ls", sczRelatedBundleId); - - ++pRelatedBundles->cRelatedBundles; - } - -LExit: - ReleaseRegKey(hkBundleId); - - return hr; -} - -static HRESULT DetermineRelationType( - __in HKEY hkBundleId, - __in BURN_REGISTRATION* pRegistration, - __out BOOTSTRAPPER_RELATION_TYPE* pRelationType - ) -{ - HRESULT hr = S_OK; - LPWSTR* rgsczUpgradeCodes = NULL; - DWORD cUpgradeCodes = 0; - STRINGDICT_HANDLE sdUpgradeCodes = NULL; - LPWSTR* rgsczAddonCodes = NULL; - DWORD cAddonCodes = 0; - STRINGDICT_HANDLE sdAddonCodes = NULL; - LPWSTR* rgsczDetectCodes = NULL; - DWORD cDetectCodes = 0; - STRINGDICT_HANDLE sdDetectCodes = NULL; - LPWSTR* rgsczPatchCodes = NULL; - DWORD cPatchCodes = 0; - STRINGDICT_HANDLE sdPatchCodes = NULL; - - *pRelationType = BOOTSTRAPPER_RELATION_NONE; - - // All remaining operations should treat all related bundles as non-vital. - hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes, &cUpgradeCodes); - if (HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE) == hr) - { - TraceError(hr, "Failed to read upgrade codes as REG_MULTI_SZ. Trying again as REG_SZ in case of older bundles."); - - rgsczUpgradeCodes = reinterpret_cast(MemAlloc(sizeof(LPWSTR), TRUE)); - ExitOnNull(rgsczUpgradeCodes, hr, E_OUTOFMEMORY, "Failed to allocate list for a single upgrade code from older bundle."); - - hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes[0]); - if (SUCCEEDED(hr)) - { - cUpgradeCodes = 1; - } - } - - // Compare upgrade codes. - if (SUCCEEDED(hr)) - { - hr = DictCreateStringListFromArray(&sdUpgradeCodes, rgsczUpgradeCodes, cUpgradeCodes, DICT_FLAG_CASEINSENSITIVE); - ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "upgrade codes"); - - // Upgrade relationship: when their upgrade codes match our upgrade codes. - hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for upgrade code match."); - - *pRelationType = BOOTSTRAPPER_RELATION_UPGRADE; - ExitFunction(); - } - - // Detect relationship: when their upgrade codes match our detect codes. - hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for detect code match."); - - *pRelationType = BOOTSTRAPPER_RELATION_DETECT; - ExitFunction(); - } - - // Dependent relationship: when their upgrade codes match our addon codes. - hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for addon code match."); - - *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT; - ExitFunction(); - } - - // Dependent relationship: when their upgrade codes match our patch codes. - hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for addon code match."); - - *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT; - ExitFunction(); - } - - ReleaseNullDict(sdUpgradeCodes); - ReleaseNullStrArray(rgsczUpgradeCodes, cUpgradeCodes); - } - - // Compare addon codes. - hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, &rgsczAddonCodes, &cAddonCodes); - if (SUCCEEDED(hr)) - { - hr = DictCreateStringListFromArray(&sdAddonCodes, rgsczAddonCodes, cAddonCodes, DICT_FLAG_CASEINSENSITIVE); - ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "addon codes"); - - // Addon relationship: when their addon codes match our detect codes. - hr = DictCompareStringListToArray(sdAddonCodes, const_cast(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for addon code match."); - - *pRelationType = BOOTSTRAPPER_RELATION_ADDON; - ExitFunction(); - } - - // Addon relationship: when their addon codes match our upgrade codes. - hr = DictCompareStringListToArray(sdAddonCodes, const_cast(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for addon code match."); - - *pRelationType = BOOTSTRAPPER_RELATION_ADDON; - ExitFunction(); - } - - ReleaseNullDict(sdAddonCodes); - ReleaseNullStrArray(rgsczAddonCodes, cAddonCodes); - } - - // Compare patch codes. - hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, &rgsczPatchCodes, &cPatchCodes); - if (SUCCEEDED(hr)) - { - hr = DictCreateStringListFromArray(&sdPatchCodes, rgsczPatchCodes, cPatchCodes, DICT_FLAG_CASEINSENSITIVE); - ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "patch codes"); - - // Patch relationship: when their patch codes match our detect codes. - hr = DictCompareStringListToArray(sdPatchCodes, const_cast(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for patch code match."); - - *pRelationType = BOOTSTRAPPER_RELATION_PATCH; - ExitFunction(); - } - - // Patch relationship: when their patch codes match our upgrade codes. - hr = DictCompareStringListToArray(sdPatchCodes, const_cast(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for patch code match."); - - *pRelationType = BOOTSTRAPPER_RELATION_PATCH; - ExitFunction(); - } - - ReleaseNullDict(sdPatchCodes); - ReleaseNullStrArray(rgsczPatchCodes, cPatchCodes); - } - - // Compare detect codes. - hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, &rgsczDetectCodes, &cDetectCodes); - if (SUCCEEDED(hr)) - { - hr = DictCreateStringListFromArray(&sdDetectCodes, rgsczDetectCodes, cDetectCodes, DICT_FLAG_CASEINSENSITIVE); - ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "detect codes"); - - // Detect relationship: when their detect codes match our detect codes. - hr = DictCompareStringListToArray(sdDetectCodes, const_cast(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for detect code match."); - - *pRelationType = BOOTSTRAPPER_RELATION_DETECT; - ExitFunction(); - } - - // Dependent relationship: when their detect codes match our addon codes. - hr = DictCompareStringListToArray(sdDetectCodes, const_cast(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for addon code match."); - - *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT; - ExitFunction(); - } - - // Dependent relationship: when their detect codes match our patch codes. - hr = DictCompareStringListToArray(sdDetectCodes, const_cast(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for addon code match."); - - *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT; - ExitFunction(); - } - - ReleaseNullDict(sdDetectCodes); - ReleaseNullStrArray(rgsczDetectCodes, cDetectCodes); - } - -LExit: - if (SUCCEEDED(hr) && BOOTSTRAPPER_RELATION_NONE == *pRelationType) - { - hr = E_NOTFOUND; - } - - ReleaseDict(sdUpgradeCodes); - ReleaseStrArray(rgsczUpgradeCodes, cUpgradeCodes); - ReleaseDict(sdAddonCodes); - ReleaseStrArray(rgsczAddonCodes, cAddonCodes); - ReleaseDict(sdDetectCodes); - ReleaseStrArray(rgsczDetectCodes, cDetectCodes); - ReleaseDict(sdPatchCodes); - ReleaseStrArray(rgsczPatchCodes, cPatchCodes); - - return hr; -} - -static HRESULT LoadRelatedBundleFromKey( - __in_z LPCWSTR wzRelatedBundleId, - __in HKEY hkBundleId, - __in BOOL fPerMachine, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __inout BURN_RELATED_BUNDLE* pRelatedBundle - ) -{ - HRESULT hr = S_OK; - DWORD64 qwEngineVersion = 0; - LPWSTR sczBundleVersion = NULL; - LPWSTR sczCachePath = NULL; - BOOL fCached = FALSE; - DWORD64 qwFileSize = 0; - BURN_DEPENDENCY_PROVIDER dependencyProvider = { }; - - hr = RegReadVersion(hkBundleId, BURN_REGISTRATION_REGISTRY_ENGINE_VERSION, &qwEngineVersion); - if (FAILED(hr)) - { - qwEngineVersion = 0; - hr = S_OK; - } - - hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, &sczBundleVersion); - ExitOnFailure(hr, "Failed to read version from registry for bundle: %ls", wzRelatedBundleId); - - hr = VerParseVersion(sczBundleVersion, 0, FALSE, &pRelatedBundle->pVersion); - ExitOnFailure(hr, "Failed to parse pseudo bundle version: %ls", sczBundleVersion); - - if (pRelatedBundle->pVersion->fInvalid) - { - LogId(REPORT_WARNING, MSG_RELATED_PACKAGE_INVALID_VERSION, wzRelatedBundleId, sczBundleVersion); - } - - hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, &sczCachePath); - ExitOnFailure(hr, "Failed to read cache path from registry for bundle: %ls", wzRelatedBundleId); - - if (FileExistsEx(sczCachePath, NULL)) - { - fCached = TRUE; - } - else - { - LogId(REPORT_STANDARD, MSG_DETECT_RELATED_BUNDLE_NOT_CACHED, wzRelatedBundleId, sczCachePath); - } - - pRelatedBundle->fPlannable = fCached; - - hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY, &dependencyProvider.sczKey); - if (E_FILENOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to read provider key from registry for bundle: %ls", wzRelatedBundleId); - - dependencyProvider.fImported = TRUE; - - hr = StrAllocString(&dependencyProvider.sczVersion, pRelatedBundle->pVersion->sczVersion, 0); - ExitOnFailure(hr, "Failed to copy version for bundle: %ls", wzRelatedBundleId); - - hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME, &dependencyProvider.sczDisplayName); - if (E_FILENOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to copy display name for bundle: %ls", wzRelatedBundleId); - } - } - - hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_TAG, &pRelatedBundle->sczTag); - if (E_FILENOTFOUND == hr) - { - hr = S_OK; - } - ExitOnFailure(hr, "Failed to read tag from registry for bundle: %ls", wzRelatedBundleId); - - pRelatedBundle->relationType = relationType; - - hr = PseudoBundleInitialize(qwEngineVersion, &pRelatedBundle->package, fPerMachine, wzRelatedBundleId, pRelatedBundle->relationType, - BOOTSTRAPPER_PACKAGE_STATE_PRESENT, fCached, sczCachePath, sczCachePath, NULL, qwFileSize, FALSE, - L"-quiet", L"-repair -quiet", L"-uninstall -quiet", - (dependencyProvider.sczKey && *dependencyProvider.sczKey) ? &dependencyProvider : NULL, - NULL, 0); - ExitOnFailure(hr, "Failed to initialize related bundle to represent bundle: %ls", wzRelatedBundleId); - -LExit: - DependencyUninitializeProvider(&dependencyProvider); - ReleaseStr(sczCachePath); - ReleaseStr(sczBundleVersion); - - return hr; -} diff --git a/src/engine/relatedbundle.h b/src/engine/relatedbundle.h deleted file mode 100644 index 01691c25..00000000 --- a/src/engine/relatedbundle.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - -HRESULT RelatedBundlesInitializeForScope( - __in BOOL fPerMachine, - __in BURN_REGISTRATION* pRegistration, - __in BURN_RELATED_BUNDLES* pRelatedBundles - ); -void RelatedBundlesUninitialize( - __in BURN_RELATED_BUNDLES* pRelatedBundles - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/search.cpp b/src/engine/search.cpp deleted file mode 100644 index 6d5f8d49..00000000 --- a/src/engine/search.cpp +++ /dev/null @@ -1,1303 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// internal function declarations - -static HRESULT DirectorySearchExists( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* pVariables - ); -static HRESULT DirectorySearchPath( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* pVariables - ); -static HRESULT FileSearchExists( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* pVariables - ); -static HRESULT FileSearchVersion( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* pVariables - ); -static HRESULT FileSearchPath( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* pVariables - ); -static HRESULT RegistrySearchExists( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* pVariables - ); -static HRESULT RegistrySearchValue( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* pVariables - ); -static HRESULT MsiComponentSearch( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* pVariables - ); -static HRESULT MsiProductSearch( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* pVariables - ); -static HRESULT MsiFeatureSearch( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* pVariables - ); -static HRESULT PerformExtensionSearch( - __in BURN_SEARCH* pSearch - ); -static HRESULT PerformSetVariable( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* pVariables -); - - -// function definitions - -extern "C" HRESULT SearchesParseFromXml( - __in BURN_SEARCHES* pSearches, - __in BURN_EXTENSIONS* pBurnExtensions, - __in IXMLDOMNode* pixnBundle - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnNodes = NULL; - IXMLDOMNode* pixnNode = NULL; - DWORD cNodes = 0; - BSTR bstrNodeName = NULL; - LPWSTR scz = NULL; - BURN_VARIANT_TYPE valueType = BURN_VARIANT_TYPE_NONE; - - // select search nodes - hr = XmlSelectNodes(pixnBundle, L"DirectorySearch|FileSearch|RegistrySearch|MsiComponentSearch|MsiProductSearch|MsiFeatureSearch|ExtensionSearch|SetVariable", &pixnNodes); - ExitOnFailure(hr, "Failed to select search nodes."); - - // get search node count - hr = pixnNodes->get_length((long*)&cNodes); - ExitOnFailure(hr, "Failed to get search node count."); - - if (!cNodes) - { - ExitFunction(); - } - - // allocate memory for searches - pSearches->rgSearches = (BURN_SEARCH*)MemAlloc(sizeof(BURN_SEARCH) * cNodes, TRUE); - ExitOnNull(pSearches->rgSearches, hr, E_OUTOFMEMORY, "Failed to allocate memory for search structs."); - - pSearches->cSearches = cNodes; - - // parse search elements - for (DWORD i = 0; i < cNodes; ++i) - { - BURN_SEARCH* pSearch = &pSearches->rgSearches[i]; - - hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName); - ExitOnFailure(hr, "Failed to get next node."); - - // @Id - hr = XmlGetAttributeEx(pixnNode, L"Id", &pSearch->sczKey); - ExitOnFailure(hr, "Failed to get @Id."); - - // @Variable - hr = XmlGetAttributeEx(pixnNode, L"Variable", &pSearch->sczVariable); - ExitOnFailure(hr, "Failed to get @Variable."); - - // @Condition - hr = XmlGetAttributeEx(pixnNode, L"Condition", &pSearch->sczCondition); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @Condition."); - } - - // read type specific attributes - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"DirectorySearch", -1)) - { - pSearch->Type = BURN_SEARCH_TYPE_DIRECTORY; - - // @Path - hr = XmlGetAttributeEx(pixnNode, L"Path", &pSearch->DirectorySearch.sczPath); - ExitOnFailure(hr, "Failed to get @Path."); - - // @Type - hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); - ExitOnFailure(hr, "Failed to get @Type."); - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1)) - { - pSearch->DirectorySearch.Type = BURN_DIRECTORY_SEARCH_TYPE_EXISTS; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"path", -1)) - { - pSearch->DirectorySearch.Type = BURN_DIRECTORY_SEARCH_TYPE_PATH; - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); - } - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"FileSearch", -1)) - { - pSearch->Type = BURN_SEARCH_TYPE_FILE; - - // @Path - hr = XmlGetAttributeEx(pixnNode, L"Path", &pSearch->FileSearch.sczPath); - ExitOnFailure(hr, "Failed to get @Path."); - - // @Type - hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); - ExitOnFailure(hr, "Failed to get @Type."); - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1)) - { - pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_EXISTS; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) - { - pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_VERSION; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"path", -1)) - { - pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_PATH; - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); - } - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"RegistrySearch", -1)) - { - pSearch->Type = BURN_SEARCH_TYPE_REGISTRY; - - // @Root - hr = XmlGetAttributeEx(pixnNode, L"Root", &scz); - ExitOnFailure(hr, "Failed to get @Root."); - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKCR", -1)) - { - pSearch->RegistrySearch.hRoot = HKEY_CLASSES_ROOT; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKCU", -1)) - { - pSearch->RegistrySearch.hRoot = HKEY_CURRENT_USER; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKLM", -1)) - { - pSearch->RegistrySearch.hRoot = HKEY_LOCAL_MACHINE; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKU", -1)) - { - pSearch->RegistrySearch.hRoot = HKEY_USERS; - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid value for @Root: %ls", scz); - } - - // @Key - hr = XmlGetAttributeEx(pixnNode, L"Key", &pSearch->RegistrySearch.sczKey); - ExitOnFailure(hr, "Failed to get Key attribute."); - - // @Value - hr = XmlGetAttributeEx(pixnNode, L"Value", &pSearch->RegistrySearch.sczValue); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get Value attribute."); - } - - // @Type - hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); - ExitOnFailure(hr, "Failed to get @Type."); - - hr = XmlGetYesNoAttribute(pixnNode, L"Win64", &pSearch->RegistrySearch.fWin64); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get Win64 attribute."); - } - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1)) - { - pSearch->RegistrySearch.Type = BURN_REGISTRY_SEARCH_TYPE_EXISTS; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"value", -1)) - { - pSearch->RegistrySearch.Type = BURN_REGISTRY_SEARCH_TYPE_VALUE; - - // @ExpandEnvironment - hr = XmlGetYesNoAttribute(pixnNode, L"ExpandEnvironment", &pSearch->RegistrySearch.fExpandEnvironment); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @ExpandEnvironment."); - } - - // @VariableType - hr = XmlGetAttributeEx(pixnNode, L"VariableType", &scz); - ExitOnFailure(hr, "Failed to get @VariableType."); - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"formatted", -1)) - { - pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_FORMATTED; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) - { - pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_NUMERIC; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1)) - { - pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_STRING; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) - { - pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_VERSION; - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid value for @VariableType: %ls", scz); - } - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); - } - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiComponentSearch", -1)) - { - pSearch->Type = BURN_SEARCH_TYPE_MSI_COMPONENT; - - // @ProductCode - hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiComponentSearch.sczProductCode); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @ProductCode."); - } - - // @ComponentId - hr = XmlGetAttributeEx(pixnNode, L"ComponentId", &pSearch->MsiComponentSearch.sczComponentId); - ExitOnFailure(hr, "Failed to get @ComponentId."); - - // @Type - hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); - ExitOnFailure(hr, "Failed to get @Type."); - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"keyPath", -1)) - { - pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1)) - { - pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_STATE; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"directory", -1)) - { - pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY; - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); - } - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiProductSearch", -1)) - { - pSearch->Type = BURN_SEARCH_TYPE_MSI_PRODUCT; - pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE; - - // @ProductCode (if we don't find a product code then look for an upgrade code) - hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiProductSearch.sczGuid); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @ProductCode."); - pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_PRODUCTCODE; - } - else - { - // @UpgradeCode - hr = XmlGetAttributeEx(pixnNode, L"UpgradeCode", &pSearch->MsiProductSearch.sczGuid); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @UpgradeCode."); - pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE; - } - } - - // make sure we found either a product or upgrade code - if (BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE == pSearch->MsiProductSearch.GuidType) - { - hr = E_NOTFOUND; - ExitOnFailure(hr, "Failed to get @ProductCode or @UpgradeCode."); - } - - // @Type - hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); - ExitOnFailure(hr, "Failed to get @Type."); - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) - { - pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"language", -1)) - { - pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1)) - { - pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_STATE; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"assignment", -1)) - { - pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT; - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); - } - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiFeatureSearch", -1)) - { - pSearch->Type = BURN_SEARCH_TYPE_MSI_FEATURE; - - // @ProductCode - hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiFeatureSearch.sczProductCode); - ExitOnFailure(hr, "Failed to get @ProductCode."); - - // @FeatureId - hr = XmlGetAttributeEx(pixnNode, L"FeatureId", &pSearch->MsiFeatureSearch.sczFeatureId); - ExitOnFailure(hr, "Failed to get @FeatureId."); - - // @Type - hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); - ExitOnFailure(hr, "Failed to get @Type."); - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1)) - { - pSearch->MsiFeatureSearch.Type = BURN_MSI_FEATURE_SEARCH_TYPE_STATE; - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); - } - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"ExtensionSearch", -1)) - { - pSearch->Type = BURN_SEARCH_TYPE_EXTENSION; - - // @ExtensionId - hr = XmlGetAttributeEx(pixnNode, L"ExtensionId", &scz); - ExitOnFailure(hr, "Failed to get @ExtensionId."); - - hr = BurnExtensionFindById(pBurnExtensions, scz, &pSearch->ExtensionSearch.pExtension); - ExitOnFailure(hr, "Failed to find extension '%ls' for search '%ls'", scz, pSearch->sczKey); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"SetVariable", -1)) - { - pSearch->Type = BURN_SEARCH_TYPE_SET_VARIABLE; - - // @Value - hr = XmlGetAttributeEx(pixnNode, L"Value", &scz); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @Value."); - - hr = BVariantSetString(&pSearch->SetVariable.value, scz, 0, FALSE); - ExitOnFailure(hr, "Failed to set variant value."); - - // @Type - hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); - ExitOnFailure(hr, "Failed to get @Type."); - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"formatted", -1)) - { - valueType = BURN_VARIANT_TYPE_FORMATTED; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) - { - valueType = BURN_VARIANT_TYPE_NUMERIC; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1)) - { - valueType = BURN_VARIANT_TYPE_STRING; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) - { - valueType = BURN_VARIANT_TYPE_VERSION; - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); - } - } - else - { - valueType = BURN_VARIANT_TYPE_NONE; - } - - // change value variant to correct type - hr = BVariantChangeType(&pSearch->SetVariable.value, valueType); - ExitOnFailure(hr, "Failed to change variant type."); - } - else - { - hr = E_UNEXPECTED; - ExitOnFailure(hr, "Unexpected element name: %ls", bstrNodeName); - } - - // prepare next iteration - ReleaseNullObject(pixnNode); - ReleaseNullBSTR(bstrNodeName); - } - - hr = S_OK; - -LExit: - ReleaseObject(pixnNodes); - ReleaseObject(pixnNode); - ReleaseBSTR(bstrNodeName); - ReleaseStr(scz); - return hr; -} - -extern "C" HRESULT SearchesExecute( - __in BURN_SEARCHES* pSearches, - __in BURN_VARIABLES* pVariables - ) -{ - HRESULT hr = S_OK; - BOOL f = FALSE; - - for (DWORD i = 0; i < pSearches->cSearches; ++i) - { - BURN_SEARCH* pSearch = &pSearches->rgSearches[i]; - - // evaluate condition - if (pSearch->sczCondition && *pSearch->sczCondition) - { - hr = ConditionEvaluate(pVariables, pSearch->sczCondition, &f); - if (E_INVALIDDATA == hr) - { - TraceError(hr, "Failed to parse search condition. Id = '%ls', Condition = '%ls'", pSearch->sczKey, pSearch->sczCondition); - hr = S_OK; - continue; - } - ExitOnFailure(hr, "Failed to evaluate search condition. Id = '%ls', Condition = '%ls'", pSearch->sczKey, pSearch->sczCondition); - - if (!f) - { - continue; // condition evaluated to false, skip - } - } - - switch (pSearch->Type) - { - case BURN_SEARCH_TYPE_DIRECTORY: - switch (pSearch->DirectorySearch.Type) - { - case BURN_DIRECTORY_SEARCH_TYPE_EXISTS: - hr = DirectorySearchExists(pSearch, pVariables); - break; - case BURN_DIRECTORY_SEARCH_TYPE_PATH: - hr = DirectorySearchPath(pSearch, pVariables); - break; - default: - hr = E_UNEXPECTED; - } - break; - case BURN_SEARCH_TYPE_FILE: - switch (pSearch->FileSearch.Type) - { - case BURN_FILE_SEARCH_TYPE_EXISTS: - hr = FileSearchExists(pSearch, pVariables); - break; - case BURN_FILE_SEARCH_TYPE_VERSION: - hr = FileSearchVersion(pSearch, pVariables); - break; - case BURN_FILE_SEARCH_TYPE_PATH: - hr = FileSearchPath(pSearch, pVariables); - break; - default: - hr = E_UNEXPECTED; - } - break; - case BURN_SEARCH_TYPE_REGISTRY: - switch (pSearch->RegistrySearch.Type) - { - case BURN_REGISTRY_SEARCH_TYPE_EXISTS: - hr = RegistrySearchExists(pSearch, pVariables); - break; - case BURN_REGISTRY_SEARCH_TYPE_VALUE: - hr = RegistrySearchValue(pSearch, pVariables); - break; - default: - hr = E_UNEXPECTED; - } - break; - case BURN_SEARCH_TYPE_MSI_COMPONENT: - hr = MsiComponentSearch(pSearch, pVariables); - break; - case BURN_SEARCH_TYPE_MSI_PRODUCT: - hr = MsiProductSearch(pSearch, pVariables); - break; - case BURN_SEARCH_TYPE_MSI_FEATURE: - hr = MsiFeatureSearch(pSearch, pVariables); - break; - case BURN_SEARCH_TYPE_EXTENSION: - hr = PerformExtensionSearch(pSearch); - break; - case BURN_SEARCH_TYPE_SET_VARIABLE: - hr = PerformSetVariable(pSearch, pVariables); - break; - default: - hr = E_UNEXPECTED; - } - - if (FAILED(hr)) - { - TraceError(hr, "Search failed. Id = '%ls'", pSearch->sczKey); - continue; - } - } - - hr = S_OK; - -LExit: - return hr; -} - -extern "C" void SearchesUninitialize( - __in BURN_SEARCHES* pSearches - ) -{ - if (pSearches->rgSearches) - { - for (DWORD i = 0; i < pSearches->cSearches; ++i) - { - BURN_SEARCH* pSearch = &pSearches->rgSearches[i]; - - ReleaseStr(pSearch->sczKey); - ReleaseStr(pSearch->sczVariable); - ReleaseStr(pSearch->sczCondition); - - switch (pSearch->Type) - { - case BURN_SEARCH_TYPE_DIRECTORY: - ReleaseStr(pSearch->DirectorySearch.sczPath); - break; - case BURN_SEARCH_TYPE_FILE: - ReleaseStr(pSearch->FileSearch.sczPath); - break; - case BURN_SEARCH_TYPE_REGISTRY: - ReleaseStr(pSearch->RegistrySearch.sczKey); - ReleaseStr(pSearch->RegistrySearch.sczValue); - break; - case BURN_SEARCH_TYPE_MSI_COMPONENT: - ReleaseStr(pSearch->MsiComponentSearch.sczProductCode); - ReleaseStr(pSearch->MsiComponentSearch.sczComponentId); - break; - case BURN_SEARCH_TYPE_MSI_PRODUCT: - ReleaseStr(pSearch->MsiProductSearch.sczGuid); - break; - case BURN_SEARCH_TYPE_MSI_FEATURE: - ReleaseStr(pSearch->MsiFeatureSearch.sczProductCode); - ReleaseStr(pSearch->MsiFeatureSearch.sczFeatureId); - break; - case BURN_SEARCH_TYPE_SET_VARIABLE: - BVariantUninitialize(&pSearch->SetVariable.value); - break; - } - } - MemFree(pSearches->rgSearches); - } -} - - -// internal function definitions - -static HRESULT DirectorySearchExists( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* pVariables - ) -{ - HRESULT hr = S_OK; - LPWSTR sczPath = NULL; - BOOL fExists = FALSE; - - // format path - hr = VariableFormatString(pVariables, pSearch->DirectorySearch.sczPath, &sczPath, NULL); - ExitOnFailure(hr, "Failed to format variable string."); - - DWORD dwAttributes = ::GetFileAttributesW(sczPath); - if (INVALID_FILE_ATTRIBUTES == dwAttributes) - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) - { - hr = S_OK; // didn't find file, fExists still is false. - } - } - else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - fExists = TRUE; - } - - // else must have found a file. - // What if there is a hidden variable in sczPath? - ExitOnFailure(hr, "Failed while searching directory search: %ls, for path: %ls", pSearch->sczKey, sczPath); - - // set variable - hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE); - ExitOnFailure(hr, "Failed to set variable."); - -LExit: - StrSecureZeroFreeString(sczPath); - - return hr; -} - -static HRESULT DirectorySearchPath( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* pVariables - ) -{ - HRESULT hr = S_OK; - LPWSTR sczPath = NULL; - - // format path - hr = VariableFormatString(pVariables, pSearch->DirectorySearch.sczPath, &sczPath, NULL); - ExitOnFailure(hr, "Failed to format variable string."); - - DWORD dwAttributes = ::GetFileAttributesW(sczPath); - if (INVALID_FILE_ATTRIBUTES == dwAttributes) - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - } - else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE); - ExitOnFailure(hr, "Failed to set directory search path variable."); - } - else // must have found a file. - { - hr = E_PATHNOTFOUND; - } - - // What if there is a hidden variable in sczPath? - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) - { - LogStringLine(REPORT_STANDARD, "Directory search: %ls, did not find path: %ls, reason: 0x%x", pSearch->sczKey, sczPath, hr); - ExitFunction1(hr = S_OK); - } - ExitOnFailure(hr, "Failed while searching directory search: %ls, for path: %ls", pSearch->sczKey, sczPath); - -LExit: - StrSecureZeroFreeString(sczPath); - - return hr; -} - -static HRESULT FileSearchExists( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* pVariables - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - LPWSTR sczPath = NULL; - BOOL fExists = FALSE; - - // format path - hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL); - ExitOnFailure(hr, "Failed to format variable string."); - - // find file - DWORD dwAttributes = ::GetFileAttributesW(sczPath); - if (INVALID_FILE_ATTRIBUTES == dwAttributes) - { - er = ::GetLastError(); - if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er) - { - // What if there is a hidden variable in sczPath? - LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath); - } - else - { - ExitOnWin32Error(er, hr, "Failed get to file attributes. '%ls'", pSearch->DirectorySearch.sczPath); - } - } - else if (FILE_ATTRIBUTE_DIRECTORY != (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) - { - fExists = TRUE; - } - - // set variable - hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE); - ExitOnFailure(hr, "Failed to set variable."); - -LExit: - StrSecureZeroFreeString(sczPath); - return hr; -} - -static HRESULT FileSearchVersion( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* pVariables - ) -{ - HRESULT hr = S_OK; - ULARGE_INTEGER uliVersion = { }; - LPWSTR sczPath = NULL; - VERUTIL_VERSION* pVersion = NULL; - - // format path - hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL); - ExitOnFailure(hr, "Failed to format path string."); - - // get file version - hr = FileVersion(sczPath, &uliVersion.HighPart, &uliVersion.LowPart); - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) - { - // What if there is a hidden variable in sczPath? - LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath); - ExitFunction1(hr = S_OK); - } - ExitOnFailure(hr, "Failed to get file version."); - - hr = VerVersionFromQword(uliVersion.QuadPart, &pVersion); - ExitOnFailure(hr, "Failed to create version from file version."); - - // set variable - hr = VariableSetVersion(pVariables, pSearch->sczVariable, pVersion, FALSE); - ExitOnFailure(hr, "Failed to set variable."); - -LExit: - StrSecureZeroFreeString(sczPath); - ReleaseVerutilVersion(pVersion); - return hr; -} - -static HRESULT FileSearchPath( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* pVariables - ) -{ - HRESULT hr = S_OK; - LPWSTR sczPath = NULL; - - // format path - hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL); - ExitOnFailure(hr, "Failed to format variable string."); - - DWORD dwAttributes = ::GetFileAttributesW(sczPath); - if (INVALID_FILE_ATTRIBUTES == dwAttributes) - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - } - else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) // found a directory. - { - hr = E_FILENOTFOUND; - } - else // found our file. - { - hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE); - ExitOnFailure(hr, "Failed to set variable to file search path."); - } - - // What if there is a hidden variable in sczPath? - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) - { - LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath); - ExitFunction1(hr = S_OK); - } - ExitOnFailure(hr, "Failed while searching file search: %ls, for path: %ls", pSearch->sczKey, sczPath); - -LExit: - StrSecureZeroFreeString(sczPath); - - return hr; -} - -static HRESULT RegistrySearchExists( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* pVariables - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - LPWSTR sczKey = NULL; - LPWSTR sczValue = NULL; - HKEY hKey = NULL; - DWORD dwType = 0; - BOOL fExists = FALSE; - REGSAM samDesired = KEY_QUERY_VALUE; - - if (pSearch->RegistrySearch.fWin64) - { - samDesired = samDesired | KEY_WOW64_64KEY; - } - - // format key string - hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczKey, &sczKey, NULL); - ExitOnFailure(hr, "Failed to format key string."); - - // open key - hr = RegOpen(pSearch->RegistrySearch.hRoot, sczKey, samDesired, &hKey); - if (SUCCEEDED(hr)) - { - fExists = TRUE; - } - else if (E_FILENOTFOUND == hr) - { - // What if there is a hidden variable in sczKey? - LogStringLine(REPORT_STANDARD, "Registry key not found. Key = '%ls'", sczKey); - fExists = FALSE; - hr = S_OK; - } - else - { - // What if there is a hidden variable in sczKey? - ExitOnFailure(hr, "Failed to open registry key. Key = '%ls'", sczKey); - } - - if (fExists && pSearch->RegistrySearch.sczValue) - { - // format value string - hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczValue, &sczValue, NULL); - ExitOnFailure(hr, "Failed to format value string."); - - // query value - er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, NULL, NULL); - switch (er) - { - case ERROR_SUCCESS: - fExists = TRUE; - break; - case ERROR_FILE_NOT_FOUND: - // What if there is a hidden variable in sczKey or sczValue? - LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue); - fExists = FALSE; - break; - default: - ExitOnWin32Error(er, hr, "Failed to query registry key value."); - } - } - - // set variable - hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE); - ExitOnFailure(hr, "Failed to set variable."); - -LExit: - if (FAILED(hr)) - { - // What if there is a hidden variable in sczKey? - LogStringLine(REPORT_STANDARD, "RegistrySearchExists failed: ID '%ls', HRESULT 0x%x", sczKey, hr); - } - - StrSecureZeroFreeString(sczKey); - StrSecureZeroFreeString(sczValue); - ReleaseRegKey(hKey); - - return hr; -} - -static HRESULT RegistrySearchValue( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* pVariables - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - LPWSTR sczKey = NULL; - LPWSTR sczValue = NULL; - HKEY hKey = NULL; - DWORD dwType = 0; - DWORD cbData = 0; - LPBYTE pData = NULL; - DWORD cch = 0; - BURN_VARIANT value = { }; - REGSAM samDesired = KEY_QUERY_VALUE; - - if (pSearch->RegistrySearch.fWin64) - { - samDesired = samDesired | KEY_WOW64_64KEY; - } - - // format key string - hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczKey, &sczKey, NULL); - ExitOnFailure(hr, "Failed to format key string."); - - // format value string - if (pSearch->RegistrySearch.sczValue) - { - hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczValue, &sczValue, NULL); - ExitOnFailure(hr, "Failed to format value string."); - } - - // open key - hr = RegOpen(pSearch->RegistrySearch.hRoot, sczKey, samDesired, &hKey); - if (E_FILENOTFOUND == hr) - { - // What if there is a hidden variable in sczKey? - LogStringLine(REPORT_STANDARD, "Registry key not found. Key = '%ls'", sczKey); - - ExitFunction1(hr = S_OK); - } - ExitOnFailure(hr, "Failed to open registry key."); - - // get value - er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, NULL, &cbData); - if (ERROR_FILE_NOT_FOUND == er) - { - // What if there is a hidden variable in sczKey or sczValue? - LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue); - - ExitFunction1(hr = S_OK); - } - ExitOnWin32Error(er, hr, "Failed to query registry key value size."); - - pData = (LPBYTE)MemAlloc(cbData + sizeof(WCHAR), TRUE); // + sizeof(WCHAR) here to ensure that we always have a null terminator for REG_SZ - ExitOnNull(pData, hr, E_OUTOFMEMORY, "Failed to allocate memory registry value."); - - er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, pData, &cbData); - ExitOnWin32Error(er, hr, "Failed to query registry key value."); - - switch (dwType) - { - case REG_DWORD: - if (sizeof(LONG) != cbData) - { - ExitFunction1(hr = E_UNEXPECTED); - } - hr = BVariantSetNumeric(&value, *((LONG*)pData)); - break; - case REG_QWORD: - if (sizeof(LONGLONG) != cbData) - { - ExitFunction1(hr = E_UNEXPECTED); - } - hr = BVariantSetNumeric(&value, *((LONGLONG*)pData)); - break; - case REG_EXPAND_SZ: - if (pSearch->RegistrySearch.fExpandEnvironment) - { - hr = StrAlloc(&value.sczValue, cbData); - ExitOnFailure(hr, "Failed to allocate string buffer."); - value.Type = BURN_VARIANT_TYPE_STRING; - - cch = ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cbData); - if (cch > cbData) - { - hr = StrAlloc(&value.sczValue, cch); - ExitOnFailure(hr, "Failed to allocate string buffer."); - - if (cch != ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cch)) - { - ExitWithLastError(hr, "Failed to get expand environment string."); - } - } - break; - } - __fallthrough; - case REG_SZ: - hr = BVariantSetString(&value, (LPCWSTR)pData, 0, FALSE); - break; - default: - ExitOnFailure(hr = E_NOTIMPL, "Unsupported registry key value type. Type = '%u'", dwType); - } - ExitOnFailure(hr, "Failed to read registry value."); - - // change value to requested type - hr = BVariantChangeType(&value, pSearch->RegistrySearch.VariableType); - ExitOnFailure(hr, "Failed to change value type."); - - // Set variable. - hr = VariableSetVariant(pVariables, pSearch->sczVariable, &value); - ExitOnFailure(hr, "Failed to set variable."); - -LExit: - if (FAILED(hr)) - { - // What if there is a hidden variable in sczKey? - LogStringLine(REPORT_STANDARD, "RegistrySearchValue failed: ID '%ls', HRESULT 0x%x", sczKey, hr); - } - - StrSecureZeroFreeString(sczKey); - StrSecureZeroFreeString(sczValue); - ReleaseRegKey(hKey); - ReleaseMem(pData); - BVariantUninitialize(&value); - - return hr; -} - -static HRESULT MsiComponentSearch( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* pVariables - ) -{ - HRESULT hr = S_OK; - INSTALLSTATE is = INSTALLSTATE_BROKEN; - LPWSTR sczComponentId = NULL; - LPWSTR sczProductCode = NULL; - LPWSTR sczPath = NULL; - - // format component id string - hr = VariableFormatString(pVariables, pSearch->MsiComponentSearch.sczComponentId, &sczComponentId, NULL); - ExitOnFailure(hr, "Failed to format component id string."); - - if (pSearch->MsiComponentSearch.sczProductCode) - { - // format product code string - hr = VariableFormatString(pVariables, pSearch->MsiComponentSearch.sczProductCode, &sczProductCode, NULL); - ExitOnFailure(hr, "Failed to format product code string."); - } - - if (sczProductCode) - { - hr = WiuGetComponentPath(sczProductCode, sczComponentId, &is, &sczPath); - } - else - { - hr = WiuLocateComponent(sczComponentId, &is, &sczPath); - } - - if (INSTALLSTATE_SOURCEABSENT == is) - { - is = INSTALLSTATE_SOURCE; - } - else if (INSTALLSTATE_UNKNOWN == is || INSTALLSTATE_NOTUSED == is) - { - is = INSTALLSTATE_ABSENT; - } - else if (INSTALLSTATE_ABSENT != is && INSTALLSTATE_LOCAL != is && INSTALLSTATE_SOURCE != is) - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Failed to get component path: %d", is); - } - - // set variable - switch (pSearch->MsiComponentSearch.Type) - { - case BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH: - if (INSTALLSTATE_ABSENT == is || INSTALLSTATE_LOCAL == is || INSTALLSTATE_SOURCE == is) - { - hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE); - } - break; - case BURN_MSI_COMPONENT_SEARCH_TYPE_STATE: - hr = VariableSetNumeric(pVariables, pSearch->sczVariable, is, FALSE); - break; - case BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY: - if (INSTALLSTATE_ABSENT == is || INSTALLSTATE_LOCAL == is || INSTALLSTATE_SOURCE == is) - { - // remove file part from path, if any - LPWSTR wz = wcsrchr(sczPath, L'\\'); - if (wz) - { - wz[1] = L'\0'; - } - - hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE); - } - break; - } - ExitOnFailure(hr, "Failed to set variable."); - -LExit: - if (FAILED(hr)) - { - LogStringLine(REPORT_STANDARD, "MsiComponentSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr); - } - - StrSecureZeroFreeString(sczComponentId); - StrSecureZeroFreeString(sczProductCode); - ReleaseStr(sczPath); - return hr; -} - -static HRESULT MsiProductSearch( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* pVariables - ) -{ - HRESULT hr = S_OK; - LPWSTR sczGuid = NULL; - LPCWSTR wzProperty = NULL; - LPWSTR *rgsczRelatedProductCodes = NULL; - DWORD dwRelatedProducts = 0; - BURN_VARIANT_TYPE type = BURN_VARIANT_TYPE_NONE; - BURN_VARIANT value = { }; - - switch (pSearch->MsiProductSearch.Type) - { - case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION: - wzProperty = INSTALLPROPERTY_VERSIONSTRING; - break; - case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE: - wzProperty = INSTALLPROPERTY_LANGUAGE; - break; - case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE: - wzProperty = INSTALLPROPERTY_PRODUCTSTATE; - break; - case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT: - wzProperty = INSTALLPROPERTY_ASSIGNMENTTYPE; - break; - default: - ExitOnFailure(hr = E_NOTIMPL, "Unsupported product search type: %u", pSearch->MsiProductSearch.Type); - } - - // format guid string - hr = VariableFormatString(pVariables, pSearch->MsiProductSearch.sczGuid, &sczGuid, NULL); - ExitOnFailure(hr, "Failed to format GUID string."); - - // get product info - value.Type = BURN_VARIANT_TYPE_STRING; - - // if this is an upgrade code then get the product code of the highest versioned related product - if (BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE == pSearch->MsiProductSearch.GuidType) - { - // WiuEnumRelatedProductCodes will log sczGuid on errors, what if there's a hidden variable in there? - hr = WiuEnumRelatedProductCodes(sczGuid, &rgsczRelatedProductCodes, &dwRelatedProducts, TRUE); - ExitOnFailure(hr, "Failed to enumerate related products for upgrade code."); - - // if we actually found a related product then use its upgrade code for the rest of the search - if (1 == dwRelatedProducts) - { - hr = StrAllocStringSecure(&sczGuid, rgsczRelatedProductCodes[0], 0); - ExitOnFailure(hr, "Failed to copy upgrade code."); - } - else - { - // set this here so we have a way of knowing that we don't need to bother - // querying for the product information below - hr = HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT); - } - } - - if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr) - { - hr = WiuGetProductInfo(sczGuid, wzProperty, &value.sczValue); - if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr) - { - // product state is available only through MsiGetProductInfoEx - // What if there is a hidden variable in sczGuid? - LogStringLine(REPORT_VERBOSE, "Trying per-machine extended info for property '%ls' for product: %ls", wzProperty, sczGuid); - hr = WiuGetProductInfoEx(sczGuid, NULL, MSIINSTALLCONTEXT_MACHINE, wzProperty, &value.sczValue); - - // if not in per-machine context, try per-user (unmanaged) - if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr) - { - // What if there is a hidden variable in sczGuid? - LogStringLine(REPORT_STANDARD, "Trying per-user extended info for property '%ls' for product: %ls", wzProperty, sczGuid); - hr = WiuGetProductInfoEx(sczGuid, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, wzProperty, &value.sczValue); - } - } - } - - if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr) - { - // What if there is a hidden variable in sczGuid? - LogStringLine(REPORT_STANDARD, "Product or related product not found: %ls", sczGuid); - - // set value to indicate absent - switch (pSearch->MsiProductSearch.Type) - { - case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT: __fallthrough; - case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION: - value.Type = BURN_VARIANT_TYPE_NUMERIC; - value.llValue = 0; - break; - case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE: - // is supposed to remain empty - break; - case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE: - value.Type = BURN_VARIANT_TYPE_NUMERIC; - value.llValue = INSTALLSTATE_ABSENT; - break; - } - - hr = S_OK; - } - ExitOnFailure(hr, "Failed to get product info."); - - // change value type - switch (pSearch->MsiProductSearch.Type) - { - case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION: - type = BURN_VARIANT_TYPE_VERSION; - break; - case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE: - type = BURN_VARIANT_TYPE_STRING; - break; - case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE: __fallthrough; - case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT: - type = BURN_VARIANT_TYPE_NUMERIC; - break; - } - hr = BVariantChangeType(&value, type); - ExitOnFailure(hr, "Failed to change value type."); - - // Set variable. - hr = VariableSetVariant(pVariables, pSearch->sczVariable, &value); - ExitOnFailure(hr, "Failed to set variable."); - -LExit: - if (FAILED(hr)) - { - LogStringLine(REPORT_STANDARD, "MsiProductSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr); - } - - StrSecureZeroFreeString(sczGuid); - ReleaseStrArray(rgsczRelatedProductCodes, dwRelatedProducts); - BVariantUninitialize(&value); - - return hr; -} - -static HRESULT MsiFeatureSearch( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* /*pVariables*/ - ) -{ - HRESULT hr = E_NOTIMPL; - -//LExit: - if (FAILED(hr)) - { - LogStringLine(REPORT_STANDARD, "MsiFeatureSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr); - } - - return hr; -} - -static HRESULT PerformExtensionSearch( - __in BURN_SEARCH* pSearch - ) -{ - HRESULT hr = S_OK; - - hr = BurnExtensionPerformSearch(pSearch->ExtensionSearch.pExtension, pSearch->sczKey, pSearch->sczVariable); - - return hr; -} - -static HRESULT PerformSetVariable( - __in BURN_SEARCH* pSearch, - __in BURN_VARIABLES* pVariables - ) -{ - HRESULT hr = S_OK; - - hr = VariableSetVariant(pVariables, pSearch->sczVariable, &pSearch->SetVariable.value); - ExitOnFailure(hr, "Failed to set variable: %ls", pSearch->sczVariable); - -LExit: - return hr; -} diff --git a/src/engine/search.h b/src/engine/search.h deleted file mode 100644 index c699c97c..00000000 --- a/src/engine/search.h +++ /dev/null @@ -1,163 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -// constants - -enum BURN_SEARCH_TYPE -{ - BURN_SEARCH_TYPE_NONE, - BURN_SEARCH_TYPE_DIRECTORY, - BURN_SEARCH_TYPE_FILE, - BURN_SEARCH_TYPE_REGISTRY, - BURN_SEARCH_TYPE_MSI_COMPONENT, - BURN_SEARCH_TYPE_MSI_PRODUCT, - BURN_SEARCH_TYPE_MSI_FEATURE, - BURN_SEARCH_TYPE_EXTENSION, - BURN_SEARCH_TYPE_SET_VARIABLE, -}; - -enum BURN_DIRECTORY_SEARCH_TYPE -{ - BURN_DIRECTORY_SEARCH_TYPE_NONE, - BURN_DIRECTORY_SEARCH_TYPE_EXISTS, - BURN_DIRECTORY_SEARCH_TYPE_PATH, -}; - -enum BURN_FILE_SEARCH_TYPE -{ - BURN_FILE_SEARCH_TYPE_NONE, - BURN_FILE_SEARCH_TYPE_EXISTS, - BURN_FILE_SEARCH_TYPE_VERSION, - BURN_FILE_SEARCH_TYPE_PATH, -}; - -enum BURN_REGISTRY_SEARCH_TYPE -{ - BURN_REGISTRY_SEARCH_TYPE_NONE, - BURN_REGISTRY_SEARCH_TYPE_EXISTS, - BURN_REGISTRY_SEARCH_TYPE_VALUE, -}; - -enum BURN_MSI_COMPONENT_SEARCH_TYPE -{ - BURN_MSI_COMPONENT_SEARCH_TYPE_NONE, - BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH, - BURN_MSI_COMPONENT_SEARCH_TYPE_STATE, - BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY, -}; - -enum BURN_MSI_PRODUCT_SEARCH_TYPE -{ - BURN_MSI_PRODUCT_SEARCH_TYPE_NONE, - BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION, - BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE, - BURN_MSI_PRODUCT_SEARCH_TYPE_STATE, - BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT, -}; - -enum BURN_MSI_PRODUCT_SEARCH_GUID_TYPE -{ - BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE, - BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_PRODUCTCODE, - BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE -}; - -enum BURN_MSI_FEATURE_SEARCH_TYPE -{ - BURN_MSI_FEATURE_SEARCH_TYPE_NONE, - BURN_MSI_FEATURE_SEARCH_TYPE_STATE, -}; - - -// structs - -typedef struct _BURN_SEARCH -{ - LPWSTR sczKey; - LPWSTR sczVariable; - LPWSTR sczCondition; - - BURN_SEARCH_TYPE Type; - union - { - struct - { - BURN_DIRECTORY_SEARCH_TYPE Type; - LPWSTR sczPath; - } DirectorySearch; - struct - { - BURN_FILE_SEARCH_TYPE Type; - LPWSTR sczPath; - } FileSearch; - struct - { - BURN_REGISTRY_SEARCH_TYPE Type; - BURN_VARIANT_TYPE VariableType; - HKEY hRoot; - LPWSTR sczKey; - LPWSTR sczValue; - BOOL fWin64; - BOOL fExpandEnvironment; - } RegistrySearch; - struct - { - BURN_MSI_COMPONENT_SEARCH_TYPE Type; - LPWSTR sczProductCode; - LPWSTR sczComponentId; - } MsiComponentSearch; - struct - { - BURN_MSI_PRODUCT_SEARCH_TYPE Type; - BURN_MSI_PRODUCT_SEARCH_GUID_TYPE GuidType; - LPWSTR sczGuid; - } MsiProductSearch; - struct - { - BURN_MSI_FEATURE_SEARCH_TYPE Type; - LPWSTR sczProductCode; - LPWSTR sczFeatureId; - } MsiFeatureSearch; - struct - { - BURN_EXTENSION* pExtension; - } ExtensionSearch; - struct - { - BURN_VARIANT value; - } SetVariable; - }; -} BURN_SEARCH; - -typedef struct _BURN_SEARCHES -{ - BURN_SEARCH* rgSearches; - DWORD cSearches; -} BURN_SEARCHES; - - -// function declarations - -HRESULT SearchesParseFromXml( - __in BURN_SEARCHES* pSearches, - __in BURN_EXTENSIONS* pBurnExtensions, - __in IXMLDOMNode* pixnBundle - ); -HRESULT SearchesExecute( - __in BURN_SEARCHES* pSearches, - __in BURN_VARIABLES* pVariables - ); -void SearchesUninitialize( - __in BURN_SEARCHES* pSearches - ); - - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/section.cpp b/src/engine/section.cpp deleted file mode 100644 index 3720155c..00000000 --- a/src/engine/section.cpp +++ /dev/null @@ -1,399 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// constants - -// If these defaults ever change, be sure to update constants in burn\stub\StubSection.cpp as well. -#define BURN_SECTION_NAME ".wixburn" -#define BURN_SECTION_MAGIC 0x00f14300 -#define BURN_SECTION_VERSION 0x00000002 -#define MANIFEST_CABINET_TOKEN L"0" - -// structs -typedef struct _BURN_SECTION_HEADER -{ - DWORD dwMagic; - DWORD dwVersion; - - GUID guidBundleId; - - DWORD dwStubSize; - DWORD dwOriginalChecksum; - DWORD dwOriginalSignatureOffset; - DWORD dwOriginalSignatureSize; - - DWORD dwFormat; - DWORD cContainers; - DWORD rgcbContainers[1]; -} BURN_SECTION_HEADER; - -static HRESULT VerifySectionMatchesMemoryPEHeader( - __in REFGUID pSection - ); - - -extern "C" HRESULT SectionInitialize( - __in BURN_SECTION* pSection, - __in HANDLE hEngineFile, - __in HANDLE hSourceEngineFile - ) -{ - HRESULT hr = S_OK; - DWORD cbRead = 0; - LARGE_INTEGER li = { }; - LONGLONG llSize = 0; - IMAGE_DOS_HEADER dosHeader = { }; - IMAGE_NT_HEADERS ntHeader = { }; - DWORD dwChecksumOffset = 0; - DWORD dwCertificateTableOffset = 0; - DWORD dwSignatureOffset = 0; - DWORD cbSignature = 0; - IMAGE_SECTION_HEADER sectionHeader = { }; - DWORD_PTR dwOriginalChecksumAndSignatureOffset = 0; - BURN_SECTION_HEADER* pBurnSectionHeader = NULL; - - pSection->hEngineFile = hEngineFile; - ExitOnInvalidHandleWithLastError(pSection->hEngineFile, hr, "Failed to open handle to engine process path."); - - pSection->hSourceEngineFile = INVALID_HANDLE_VALUE == hSourceEngineFile ? hEngineFile : hSourceEngineFile; - - // - // First, make sure we have a valid DOS signature. - // - if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN)) - { - ExitWithLastError(hr, "Failed to seek to start of file."); - } - - // read DOS header - if (!::ReadFile(pSection->hEngineFile, &dosHeader, sizeof(IMAGE_DOS_HEADER), &cbRead, NULL)) - { - ExitWithLastError(hr, "Failed to read DOS header."); - } - else if (sizeof(IMAGE_DOS_HEADER) > cbRead || IMAGE_DOS_SIGNATURE != dosHeader.e_magic) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to find valid DOS image header in buffer."); - } - - // - // Now, make sure we have a valid NT signature. - // - - // seek to new header - li.QuadPart = dosHeader.e_lfanew; - if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN)) - { - ExitWithLastError(hr, "Failed to seek to NT header."); - } - - // read NT header - if (!::ReadFile(pSection->hEngineFile, &ntHeader, sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER), &cbRead, NULL)) - { - ExitWithLastError(hr, "Failed to read NT header."); - } - else if ((sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER)) > cbRead || IMAGE_NT_SIGNATURE != ntHeader.Signature) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to find valid NT image header in buffer."); - } - - // Get the table offsets. - dwChecksumOffset = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + (sizeof(DWORD) * 16); - dwCertificateTableOffset = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - (sizeof(IMAGE_DATA_DIRECTORY) * (IMAGE_NUMBEROF_DIRECTORY_ENTRIES - IMAGE_DIRECTORY_ENTRY_SECURITY)); - - // Seek into the certificate table to get the signature size. - li.QuadPart = dwCertificateTableOffset; - if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN)) - { - ExitWithLastError(hr, "Failed to seek to section info."); - } - - if (!::ReadFile(pSection->hEngineFile, &dwSignatureOffset, sizeof(dwSignatureOffset), &cbRead, NULL)) - { - ExitWithLastError(hr, "Failed to read signature offset."); - } - - if (!::ReadFile(pSection->hEngineFile, &cbSignature, sizeof(cbSignature), &cbRead, NULL)) - { - ExitWithLastError(hr, "Failed to read signature size."); - } - - // - // Finally, get into the section table and look for the Burn section info. - // - - // seek past optional headers - li.QuadPart = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + ntHeader.FileHeader.SizeOfOptionalHeader; - if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN)) - { - ExitWithLastError(hr, "Failed to seek past optional headers."); - } - - // read sections one by one until we find our section - for (DWORD i = 0; ; ++i) - { - // read section - if (!::ReadFile(pSection->hEngineFile, §ionHeader, sizeof(IMAGE_SECTION_HEADER), &cbRead, NULL)) - { - ExitWithLastError(hr, "Failed to read image section header, index: %u", i); - } - if (sizeof(IMAGE_SECTION_HEADER) > cbRead) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to read complete image section header, index: %u", i); - } - - // compare header name - C_ASSERT(sizeof(sectionHeader.Name) == sizeof(BURN_SECTION_NAME) - 1); - if (0 == memcmp(sectionHeader.Name, BURN_SECTION_NAME, sizeof(sectionHeader.Name))) - { - break; - } - - // fail if we hit the end - if (i + 1 >= ntHeader.FileHeader.NumberOfSections) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to find Burn section."); - } - } - - // - // We've arrived at the section info. - // - - // check size of section - if (sizeof(BURN_SECTION_HEADER) > sectionHeader.SizeOfRawData) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to read section info, data to short: %u", sectionHeader.SizeOfRawData); - } - - // allocate buffer for section info - pBurnSectionHeader = (BURN_SECTION_HEADER*)MemAlloc(sectionHeader.SizeOfRawData, TRUE); - ExitOnNull(pBurnSectionHeader, hr, E_OUTOFMEMORY, "Failed to allocate buffer for section info."); - - // seek to section info - li.QuadPart = sectionHeader.PointerToRawData; - if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN)) - { - ExitWithLastError(hr, "Failed to seek to section info."); - } - - // Note the location of original checksum and signature information in the burn section header. - dwOriginalChecksumAndSignatureOffset = sectionHeader.PointerToRawData + (reinterpret_cast(&pBurnSectionHeader->dwOriginalChecksum) - reinterpret_cast(pBurnSectionHeader)); - - // read section info - if (!::ReadFile(pSection->hEngineFile, pBurnSectionHeader, sectionHeader.SizeOfRawData, &cbRead, NULL)) - { - ExitWithLastError(hr, "Failed to read section info."); - } - else if (sectionHeader.SizeOfRawData > cbRead) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to read complete section info."); - } - - // validate version of section info - if (BURN_SECTION_VERSION != pBurnSectionHeader->dwVersion) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to read section info, unsupported version: %08x", pBurnSectionHeader->dwVersion); - } - - hr = FileSizeByHandle(pSection->hSourceEngineFile, &llSize); - ExitOnFailure(hr, "Failed to get total size of bundle."); - - pSection->cbStub = pBurnSectionHeader->dwStubSize; - - // If there is an original signature use that to determine the engine size. - if (pBurnSectionHeader->dwOriginalSignatureOffset) - { - pSection->cbEngineSize = pBurnSectionHeader->dwOriginalSignatureOffset + pBurnSectionHeader->dwOriginalSignatureSize; - } - else if (dwSignatureOffset) // if there is a signature, use it. - { - pSection->cbEngineSize = dwSignatureOffset + cbSignature; - } - else // just use the stub and UX container as the size of the engine. - { - pSection->cbEngineSize = pSection->cbStub + pBurnSectionHeader->rgcbContainers[0]; - } - - pSection->qwBundleSize = static_cast(llSize); - - pSection->dwChecksumOffset = dwChecksumOffset; - pSection->dwCertificateTableOffset = dwCertificateTableOffset; - pSection->dwOriginalChecksumAndSignatureOffset = dwOriginalChecksumAndSignatureOffset; - - pSection->dwOriginalChecksum = pBurnSectionHeader->dwOriginalChecksum; - pSection->dwOriginalSignatureOffset = pBurnSectionHeader->dwOriginalSignatureOffset; - pSection->dwOriginalSignatureSize = pBurnSectionHeader->dwOriginalSignatureSize; - - pSection->dwFormat = pBurnSectionHeader->dwFormat; - pSection->cContainers = pBurnSectionHeader->cContainers; - pSection->rgcbContainers = (DWORD*)MemAlloc(sizeof(DWORD) * pSection->cContainers, TRUE); - ExitOnNull(pSection->rgcbContainers, hr, E_OUTOFMEMORY, "Failed to allocate memory for container sizes."); - - memcpy(pSection->rgcbContainers, pBurnSectionHeader->rgcbContainers, sizeof(DWORD) * pSection->cContainers); - - // TODO: verify more than just the GUID. - hr = VerifySectionMatchesMemoryPEHeader(pBurnSectionHeader->guidBundleId); - ExitOnRootFailure(hr, "PE Header from file didn't match PE Header in memory."); - -LExit: - ReleaseMem(pBurnSectionHeader); - - return hr; -} - -extern "C" void SectionUninitialize( - __out BURN_SECTION* pSection - ) -{ - ReleaseMem(pSection->rgcbContainers); - memset(pSection, 0, sizeof(BURN_SECTION)); -} - -extern "C" HRESULT SectionGetAttachedContainerInfo( - __in BURN_SECTION* pSection, - __in DWORD iContainerIndex, - __in DWORD dwExpectedType, - __out DWORD64* pqwOffset, - __out DWORD64* pqwSize, - __out BOOL* pfPresent - ) -{ - HRESULT hr = S_OK; - - // validate container info - if (iContainerIndex >= pSection->cContainers) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to find container info, too few elements: %u", pSection->cContainers); - } - else if (dwExpectedType != pSection->dwFormat) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Unexpected container format."); - } - - // If we are asking for the UX container, find it right after the stub. - if (0 == iContainerIndex) - { - *pqwOffset = pSection->cbStub; - } - else // attached containers start after the whole engine. - { - *pqwOffset = pSection->cbEngineSize; - for (DWORD i = 1; i < iContainerIndex; ++i) - { - *pqwOffset += pSection->rgcbContainers[i]; - } - } - - *pqwSize = pSection->rgcbContainers[iContainerIndex]; - *pfPresent = (*pqwOffset + *pqwSize) <= pSection->qwBundleSize; - - AssertSz(*pfPresent || pSection->qwBundleSize <= *pqwOffset, "An attached container should either be present or completely absent from the bundle. Found a case where the attached container is partially present which is wrong."); - -LExit: - return hr; -} - -HRESULT VerifySectionMatchesMemoryPEHeader( - __in REFGUID pBundleId - ) -{ - HRESULT hr = S_OK; - BYTE* pbPEHeader = NULL; - PIMAGE_DOS_HEADER pDosHeader = NULL; - PIMAGE_NT_HEADERS pNtHeader = NULL; - PIMAGE_SECTION_HEADER pSections = NULL; - PIMAGE_SECTION_HEADER pSectionHeader = NULL; - BURN_SECTION_HEADER* pBurnSectionHeader = NULL; - - pbPEHeader = reinterpret_cast(::GetModuleHandleW(NULL)); - ExitOnNullWithLastError(pbPEHeader, hr, "Failed to get module handle to process."); - - // - // First, make sure we have a valid DOS signature. - // - - pDosHeader = reinterpret_cast(pbPEHeader); - if (IMAGE_DOS_SIGNATURE != pDosHeader->e_magic) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to find valid DOS image header in buffer."); - } - - // - // Now, make sure we have a valid NT signature. - // - - pNtHeader = reinterpret_cast(pbPEHeader + pDosHeader->e_lfanew); - if (IMAGE_NT_SIGNATURE != pNtHeader->Signature) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to find valid NT image header in buffer."); - } - - // - // Finally, get into the section table and look for the Burn section info. - // - - pSections = reinterpret_cast(pbPEHeader + pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + pNtHeader->FileHeader.SizeOfOptionalHeader); - - // Read sections one by one until we find our section. - for (DWORD i = 0; ; ++i) - { - pSectionHeader = pSections + i; - - // Compare header name. - C_ASSERT(sizeof(pSectionHeader->Name) == sizeof(BURN_SECTION_NAME) - 1); - if (0 == memcmp(pSectionHeader->Name, BURN_SECTION_NAME, sizeof(pSectionHeader->Name))) - { - break; - } - - // Fail if we hit the end. - if (i + 1 >= pNtHeader->FileHeader.NumberOfSections) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to find Burn section."); - } - } - - // - // We've arrived at the section info. - // - - // Check size of section. - if (sizeof(BURN_SECTION_HEADER) > pSectionHeader->SizeOfRawData) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to read section info, data to short: %u", pSectionHeader->SizeOfRawData); - } - - // Get Burn section info. - pBurnSectionHeader = reinterpret_cast(pbPEHeader + pSectionHeader->VirtualAddress); - - // Validate version of section info. - if (BURN_SECTION_VERSION != pBurnSectionHeader->dwVersion) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Failed to read section info, unsupported version: %08x", pBurnSectionHeader->dwVersion); - } - - if (!::IsEqualGUID(pBundleId, pBurnSectionHeader->guidBundleId)) - { - hr = E_INVALIDDATA; - ExitOnRootFailure(hr, "Bundle guid didn't match the guid in the PE Header in memory."); - } - -LExit: - return hr; -} diff --git a/src/engine/section.h b/src/engine/section.h deleted file mode 100644 index 6c62ba44..00000000 --- a/src/engine/section.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -// structs - -typedef struct _BURN_SECTION -{ - HANDLE hEngineFile; - HANDLE hSourceEngineFile; - - DWORD cbStub; - DWORD cbEngineSize; // stub + UX container + original certficiate - DWORD64 qwBundleSize; // stub + UX container + original certificate [+ attached containers* + final certificate] - - DWORD dwChecksumOffset; - DWORD dwCertificateTableOffset; - DWORD_PTR dwOriginalChecksumAndSignatureOffset; - - DWORD dwOriginalChecksum; - DWORD dwOriginalSignatureOffset; - DWORD dwOriginalSignatureSize; - - DWORD dwFormat; - DWORD cContainers; - DWORD* rgcbContainers; -} BURN_SECTION; - - -HRESULT SectionInitialize( - __in BURN_SECTION* pSection, - __in HANDLE hEngineFile, - __in HANDLE hSourceEngineFile - ); -void SectionUninitialize( - __in BURN_SECTION* pSection - ); -HRESULT SectionGetAttachedContainerInfo( - __in BURN_SECTION* pSection, - __in DWORD iContainerIndex, - __in DWORD dwExpectedType, - __out DWORD64* pqwOffset, - __out DWORD64* pqwSize, - __out BOOL* pfPresent - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/splashscreen.cpp b/src/engine/splashscreen.cpp deleted file mode 100644 index 90bd5203..00000000 --- a/src/engine/splashscreen.cpp +++ /dev/null @@ -1,355 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -#define BURN_SPLASHSCREEN_CLASS_WINDOW L"WixBurnSplashScreen" -#define IDB_SPLASHSCREEN 1 - -// struct - -struct SPLASHSCREEN_INFO -{ - HBITMAP hBitmap; - SIZE defaultDpiSize; - SIZE size; - UINT nDpi; - HWND hWnd; -}; - -struct SPLASHSCREEN_CONTEXT -{ - HANDLE hInitializedEvent; - HINSTANCE hInstance; - LPCWSTR wzCaption; - - HWND* pHwnd; -}; - -// internal function definitions - -static DWORD WINAPI ThreadProc( - __in LPVOID pvContext - ); -static LRESULT CALLBACK WndProc( - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ); -static HRESULT LoadSplashScreen( - __in SPLASHSCREEN_CONTEXT* pContext, - __in SPLASHSCREEN_INFO* pSplashScreen - ); -static BOOL OnDpiChanged( - __in SPLASHSCREEN_INFO* pSplashScreen, - __in WPARAM wParam, - __in LPARAM lParam - ); -static void OnEraseBkgnd( - __in SPLASHSCREEN_INFO* pSplashScreen, - __in WPARAM wParam - ); -static void OnNcCreate( - __in HWND hWnd, - __in LPARAM lParam - ); -static void ScaleSplashScreen( - __in SPLASHSCREEN_INFO* pSplashScreen, - __in UINT nDpi, - __in int x, - __in int y - ); - - -// function definitions - -extern "C" void SplashScreenCreate( - __in HINSTANCE hInstance, - __in_z_opt LPCWSTR wzCaption, - __out HWND* pHwnd - ) -{ - HRESULT hr = S_OK; - SPLASHSCREEN_CONTEXT context = { }; - HANDLE rgSplashScreenEvents[2] = { }; - DWORD dwSplashScreenThreadId = 0; - - rgSplashScreenEvents[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL); - ExitOnNullWithLastError(rgSplashScreenEvents[0], hr, "Failed to create modal event."); - - // create splash screen thread. - context.hInitializedEvent = rgSplashScreenEvents[0]; - context.hInstance = hInstance; - context.wzCaption = wzCaption; - context.pHwnd = pHwnd; - - rgSplashScreenEvents[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, &dwSplashScreenThreadId); - ExitOnNullWithLastError(rgSplashScreenEvents[1], hr, "Failed to create UI thread."); - - // It doesn't really matter if the thread gets initialized (WAIT_OBJECT_0) or fails and exits - // prematurely (WAIT_OBJECT_0 + 1), we just want to wait long enough for one of those two - // events to happen. - ::WaitForMultipleObjects(countof(rgSplashScreenEvents), rgSplashScreenEvents, FALSE, INFINITE); - -LExit: - ReleaseHandle(rgSplashScreenEvents[1]); - ReleaseHandle(rgSplashScreenEvents[0]); -} - -extern "C" HRESULT SplashScreenDisplayError( - __in BOOTSTRAPPER_DISPLAY display, - __in_z LPCWSTR wzBundleName, - __in HRESULT hrError - ) -{ - HRESULT hr = S_OK; - LPWSTR sczDisplayString = NULL; - - hr = StrAllocFromError(&sczDisplayString, hrError, NULL); - ExitOnFailure(hr, "Failed to allocate string to display error message"); - - Trace(REPORT_STANDARD, "Error message displayed because: %ls", sczDisplayString); - - if (BOOTSTRAPPER_DISPLAY_NONE == display || BOOTSTRAPPER_DISPLAY_PASSIVE == display || BOOTSTRAPPER_DISPLAY_EMBEDDED == display) - { - // Don't display the error dialog in these modes - ExitFunction1(hr = S_OK); - } - - ::MessageBoxW(NULL, sczDisplayString, wzBundleName, MB_OK | MB_ICONERROR | MB_SYSTEMMODAL); - -LExit: - ReleaseStr(sczDisplayString); - - return hr; -} - - -static DWORD WINAPI ThreadProc( - __in LPVOID pvContext - ) -{ - HRESULT hr = S_OK; - SPLASHSCREEN_CONTEXT* pContext = static_cast(pvContext); - - SPLASHSCREEN_INFO splashScreenInfo = { }; - - WNDCLASSW wc = { }; - BOOL fRegistered = TRUE; - - BOOL fRet = FALSE; - MSG msg = { }; - - // Register the window class. - wc.lpfnWndProc = WndProc; - wc.hInstance = pContext->hInstance; - wc.hCursor = ::LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); - wc.lpszClassName = BURN_SPLASHSCREEN_CLASS_WINDOW; - if (!::RegisterClassW(&wc)) - { - ExitWithLastError(hr, "Failed to register window."); - } - - fRegistered = TRUE; - - hr = LoadSplashScreen(pContext, &splashScreenInfo); - ExitOnFailure(hr, "Failed to load splash screen."); - - // Return the splash screen window and free the main thread waiting for us to be initialized. - *pContext->pHwnd = splashScreenInfo.hWnd; - ::SetEvent(pContext->hInitializedEvent); - - // Pump messages until the bootstrapper application destroys the window. - while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) - { - if (-1 == fRet) - { - hr = E_UNEXPECTED; - ExitOnFailure(hr, "Unexpected return value from message pump."); - } - else if (!::IsDialogMessageW(splashScreenInfo.hWnd, &msg)) - { - ::TranslateMessage(&msg); - ::DispatchMessageW(&msg); - } - } - -LExit: - if (fRegistered) - { - ::UnregisterClassW(BURN_SPLASHSCREEN_CLASS_WINDOW, pContext->hInstance); - } - - if (splashScreenInfo.hBitmap) - { - ::DeleteObject(splashScreenInfo.hBitmap); - } - - return hr; -} - -static LRESULT CALLBACK WndProc( - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ) -{ - LRESULT lres = 0; - SPLASHSCREEN_INFO* pSplashScreen = reinterpret_cast(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); - - switch (uMsg) - { - case WM_NCCREATE: - OnNcCreate(hWnd, lParam); - break; - - case WM_NCDESTROY: - lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam); - ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); - ::PostQuitMessage(0); - return lres; - - case WM_NCHITTEST: - return HTCAPTION; // allow window to be moved by grabbing any pixel. - - case WM_DPICHANGED: - if (OnDpiChanged(pSplashScreen, wParam, lParam)) - { - return 0; - } - break; - - case WM_ERASEBKGND: - OnEraseBkgnd(pSplashScreen, wParam); - return 1; - } - - return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); -} - -static HRESULT LoadSplashScreen( - __in SPLASHSCREEN_CONTEXT* pContext, - __in SPLASHSCREEN_INFO* pSplashScreen - ) -{ - HRESULT hr = S_OK; - BITMAP bmp = { }; - POINT pt = { }; - int x = 0; - int y = 0; - DPIU_MONITOR_CONTEXT* pMonitorContext = NULL; - RECT* pMonitorRect = NULL; - - pSplashScreen->nDpi = USER_DEFAULT_SCREEN_DPI; - pSplashScreen->hBitmap = ::LoadBitmapW(pContext->hInstance, MAKEINTRESOURCEW(IDB_SPLASHSCREEN)); - ExitOnNullWithLastError(pSplashScreen->hBitmap, hr, "Failed to load splash screen bitmap."); - - ::GetObject(pSplashScreen->hBitmap, sizeof(bmp), static_cast(&bmp)); - pSplashScreen->defaultDpiSize.cx = pSplashScreen->size.cx = bmp.bmWidth; - pSplashScreen->defaultDpiSize.cy = pSplashScreen->size.cy = bmp.bmHeight; - - // Try to default to the monitor with the mouse, otherwise default to the primary monitor. - if (!::GetCursorPos(&pt)) - { - pt.x = 0; - pt.y = 0; - } - - // Try to center the window on the chosen monitor. - hr = DpiuGetMonitorContextFromPoint(&pt, &pMonitorContext); - if (SUCCEEDED(hr)) - { - pMonitorRect = &pMonitorContext->mi.rcWork; - if (pMonitorContext->nDpi != pSplashScreen->nDpi) - { - ScaleSplashScreen(pSplashScreen, pMonitorContext->nDpi, pMonitorRect->left, pMonitorRect->top); - } - - x = pMonitorRect->left + (pMonitorRect->right - pMonitorRect->left - pSplashScreen->size.cx) / 2; - y = pMonitorRect->top + (pMonitorRect->bottom - pMonitorRect->top - pSplashScreen->size.cy) / 2; - } - else - { - hr = S_OK; - x = CW_USEDEFAULT; - y = CW_USEDEFAULT; - } - - pSplashScreen->hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, BURN_SPLASHSCREEN_CLASS_WINDOW, pContext->wzCaption, WS_POPUP | WS_VISIBLE, x, y, pSplashScreen->size.cx, pSplashScreen->size.cy, HWND_DESKTOP, NULL, pContext->hInstance, pSplashScreen); - ExitOnNullWithLastError(pSplashScreen->hWnd, hr, "Failed to create window."); - -LExit: - MemFree(pMonitorContext); - - return hr; -} - -static BOOL OnDpiChanged( - __in SPLASHSCREEN_INFO* pSplashScreen, - __in WPARAM wParam, - __in LPARAM lParam - ) -{ - UINT nDpi = HIWORD(wParam); - RECT* pRect = reinterpret_cast(lParam); - BOOL fDpiChanged = pSplashScreen->nDpi != nDpi; - - if (fDpiChanged) - { - ScaleSplashScreen(pSplashScreen, nDpi, pRect->left, pRect->top); - } - - return fDpiChanged; -} - -static void OnEraseBkgnd( - __in SPLASHSCREEN_INFO* pSplashScreen, - __in WPARAM wParam - ) -{ - HDC hdc = reinterpret_cast(wParam); - HDC hdcMem = ::CreateCompatibleDC(hdc); - HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pSplashScreen->hBitmap)); - ::StretchBlt(hdc, 0, 0, pSplashScreen->size.cx, pSplashScreen->size.cy, hdcMem, 0, 0, pSplashScreen->defaultDpiSize.cx, pSplashScreen->defaultDpiSize.cy, SRCCOPY); - ::SelectObject(hdcMem, hDefaultBitmap); - ::DeleteDC(hdcMem); -} - -static void OnNcCreate( - __in HWND hWnd, - __in LPARAM lParam - ) -{ - DPIU_WINDOW_CONTEXT windowContext = { }; - CREATESTRUCTW* pCreateStruct = reinterpret_cast(lParam); - SPLASHSCREEN_INFO* pSplashScreen = reinterpret_cast(pCreateStruct->lpCreateParams); - - ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(pSplashScreen)); - pSplashScreen->hWnd = hWnd; - - DpiuGetWindowContext(pSplashScreen->hWnd, &windowContext); - - if (windowContext.nDpi != pSplashScreen->nDpi) - { - ScaleSplashScreen(pSplashScreen, windowContext.nDpi, pCreateStruct->x, pCreateStruct->y); - } -} - -static void ScaleSplashScreen( - __in SPLASHSCREEN_INFO* pSplashScreen, - __in UINT nDpi, - __in int x, - __in int y - ) -{ - pSplashScreen->nDpi = nDpi; - - pSplashScreen->size.cx = DpiuScaleValue(pSplashScreen->defaultDpiSize.cx, pSplashScreen->nDpi); - pSplashScreen->size.cy = DpiuScaleValue(pSplashScreen->defaultDpiSize.cy, pSplashScreen->nDpi); - - if (pSplashScreen->hWnd) - { - ::SetWindowPos(pSplashScreen->hWnd, NULL, x, y, pSplashScreen->size.cx, pSplashScreen->size.cy, SWP_NOACTIVATE | SWP_NOZORDER); - } -} diff --git a/src/engine/splashscreen.h b/src/engine/splashscreen.h deleted file mode 100644 index 8f8817c7..00000000 --- a/src/engine/splashscreen.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -// constants - - -// structs - - -// functions - -void SplashScreenCreate( - __in HINSTANCE hInstance, - __in_z_opt LPCWSTR wzCaption, - __out HWND* pHwnd - ); -HRESULT SplashScreenDisplayError( - __in BOOTSTRAPPER_DISPLAY display, - __in_z LPCWSTR wzBundleName, - __in HRESULT hrError - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/uithread.cpp b/src/engine/uithread.cpp deleted file mode 100644 index 433cb171..00000000 --- a/src/engine/uithread.cpp +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -#define BURN_UITHREAD_CLASS_WINDOW L"WixBurnMessageWindow" - - -// structs - -struct UITHREAD_CONTEXT -{ - HANDLE hInitializedEvent; - HINSTANCE hInstance; - BURN_ENGINE_STATE* pEngineState; -}; - -struct UITHREAD_INFO -{ - BOOL fElevated; - BURN_USER_EXPERIENCE* pUserExperience; -}; - - -// internal function declarations - -static DWORD WINAPI ThreadProc( - __in LPVOID pvContext - ); - -static LRESULT CALLBACK WndProc( - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ); - - -// function definitions - -HRESULT UiCreateMessageWindow( - __in HINSTANCE hInstance, - __in BURN_ENGINE_STATE* pEngineState - ) -{ - HRESULT hr = S_OK; - HANDLE rgWaitHandles[2] = { }; - UITHREAD_CONTEXT context = { }; - - // Create event to signal after the UI thread / window is initialized. - rgWaitHandles[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL); - ExitOnNullWithLastError(rgWaitHandles[0], hr, "Failed to create initialization event."); - - // Pass necessary information to create the window. - context.hInitializedEvent = rgWaitHandles[0]; - context.hInstance = hInstance; - context.pEngineState = pEngineState; - - // Create our separate UI thread. - rgWaitHandles[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, NULL); - ExitOnNullWithLastError(rgWaitHandles[1], hr, "Failed to create the UI thread."); - - // Wait for either the thread to be initialized or the window to exit / fail prematurely. - ::WaitForMultipleObjects(countof(rgWaitHandles), rgWaitHandles, FALSE, INFINITE); - - pEngineState->hMessageWindowThread = rgWaitHandles[1]; - rgWaitHandles[1] = NULL; - -LExit: - ReleaseHandle(rgWaitHandles[1]); - ReleaseHandle(rgWaitHandles[0]); - - return hr; -} - -void UiCloseMessageWindow( - __in BURN_ENGINE_STATE* pEngineState - ) -{ - if (::IsWindow(pEngineState->hMessageWindow)) - { - ::PostMessageW(pEngineState->hMessageWindow, WM_CLOSE, 0, 0); - - // Give the window 15 seconds to close because if it stays open it can prevent - // the engine from starting a reboot (should a reboot actually be necessary). - ::WaitForSingleObject(pEngineState->hMessageWindowThread, 15 * 1000); - } -} - - -// internal function definitions - -static DWORD WINAPI ThreadProc( - __in LPVOID pvContext - ) -{ - HRESULT hr = S_OK; - UITHREAD_CONTEXT* pContext = static_cast(pvContext); - UITHREAD_INFO info = { }; - - WNDCLASSW wc = { }; - BOOL fRegistered = TRUE; - HWND hWnd = NULL; - - BOOL fRet = FALSE; - MSG msg = { }; - - BURN_ENGINE_STATE* pEngineState = pContext->pEngineState; - BOOL fElevated = BURN_MODE_ELEVATED == pContext->pEngineState->mode; - - // If elevated, set up the thread local storage to store the correct pipe to communicate logging. - if (fElevated) - { - Assert(TLS_OUT_OF_INDEXES != pEngineState->dwElevatedLoggingTlsId); - - if (!::TlsSetValue(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe)) - { - // If the function failed we cannot write to the pipe so just terminate. - ExitFunction1(hr = E_INVALIDSTATE); - } - } - - wc.lpfnWndProc = WndProc; - wc.hInstance = pContext->hInstance; - wc.lpszClassName = BURN_UITHREAD_CLASS_WINDOW; - - if (!::RegisterClassW(&wc)) - { - ExitWithLastError(hr, "Failed to register window."); - } - - fRegistered = TRUE; - - info.fElevated = fElevated; - info.pUserExperience = &pEngineState->userExperience; - - // Create the window to handle reboots without activating it. - hWnd = ::CreateWindowExW(WS_EX_NOACTIVATE, wc.lpszClassName, NULL, WS_POPUP, 0, 0, 0, 0, HWND_DESKTOP, NULL, pContext->hInstance, &info); - ExitOnNullWithLastError(hWnd, hr, "Failed to create window."); - - ::ShowWindow(hWnd, SW_SHOWNA); - - // Persist the window handle and let the caller know we've initialized. - pEngineState->hMessageWindow = hWnd; - ::SetEvent(pContext->hInitializedEvent); - - // Pump messages until the window is closed. - while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) - { - if (-1 == fRet) - { - hr = E_UNEXPECTED; - ExitOnFailure(hr, "Unexpected return value from message pump."); - } - else if (!::IsDialogMessageW(msg.hwnd, &msg)) - { - ::TranslateMessage(&msg); - ::DispatchMessageW(&msg); - } - } - -LExit: - if (fRegistered) - { - ::UnregisterClassW(BURN_UITHREAD_CLASS_WINDOW, pContext->hInstance); - } - - return hr; -} - -static LRESULT CALLBACK WndProc( - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ) -{ - switch (uMsg) - { - case WM_NCCREATE: - { - LPCREATESTRUCTW lpcs = reinterpret_cast(lParam); - ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(lpcs->lpCreateParams)); - break; - } - - case WM_NCDESTROY: - { - LRESULT lRes = ::DefWindowProcW(hWnd, uMsg, wParam, lParam); - ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); - return lRes; - } - - case WM_QUERYENDSESSION: - { - DWORD dwEndSession = static_cast(lParam); - BOOL fCritical = ENDSESSION_CRITICAL & dwEndSession; - BOOL fCancel = TRUE; - BOOL fRet = FALSE; - - // Always block shutdown in the elevated process, but ask the BA in the non-elevated. - UITHREAD_INFO* pInfo = reinterpret_cast(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); - if (!pInfo->fElevated) - { - // TODO: instead of recommending canceling all non-critical shutdowns, maybe we should only recommend cancel - // when the engine is doing work? - fCancel = !fCritical; - // TODO: There's a race condition here where the BA may not have been loaded, or already was unloaded. - UserExperienceOnSystemShutdown(pInfo->pUserExperience, dwEndSession, &fCancel); - } - - fRet = !fCancel; - LogId(REPORT_STANDARD, MSG_SYSTEM_SHUTDOWN, LoggingBoolToString(fCritical), LoggingBoolToString(pInfo->fElevated), LoggingBoolToString(fRet)); - return fRet; - } - - case WM_DESTROY: - ::PostQuitMessage(0); - return 0; - } - - return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); -} diff --git a/src/engine/uithread.h b/src/engine/uithread.h deleted file mode 100644 index 41168d52..00000000 --- a/src/engine/uithread.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -// functions - -HRESULT UiCreateMessageWindow( - __in HINSTANCE hInstance, - __in BURN_ENGINE_STATE* pEngineState - ); - -void UiCloseMessageWindow( - __in BURN_ENGINE_STATE* pEngineState - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/update.cpp b/src/engine/update.cpp deleted file mode 100644 index b04fa9a4..00000000 --- a/src/engine/update.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// internal function declarations - - -// function definitions - -extern "C" HRESULT UpdateParseFromXml( - __in BURN_UPDATE* pUpdate, - __in IXMLDOMNode* pixnBundle - ) -{ - HRESULT hr = S_OK; - IXMLDOMNode* pixnUpdateNode = NULL; - - hr = XmlSelectSingleNode(pixnBundle, L"Update", &pixnUpdateNode); - if (S_FALSE == hr) - { - ExitFunction1(hr = S_OK); - } - ExitOnFailure(hr, "Failed to select Bundle/Update node."); - - // @Location - hr = XmlGetAttributeEx(pixnUpdateNode, L"Location", &pUpdate->sczUpdateSource); - ExitOnFailure(hr, "Failed to get Update@Location."); - -LExit: - ReleaseObject(pixnUpdateNode); - - return hr; -} - -extern "C" void UpdateUninitialize( - __in BURN_UPDATE* pUpdate - ) -{ - PackageUninitialize(&pUpdate->package); - - ReleaseStr(pUpdate->sczUpdateSource); - memset(pUpdate, 0, sizeof(BURN_UPDATE)); -} diff --git a/src/engine/update.h b/src/engine/update.h deleted file mode 100644 index 67d40481..00000000 --- a/src/engine/update.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -// structs - -typedef struct _BURN_UPDATE -{ - BOOL fUpdateAvailable; - LPWSTR sczUpdateSource; - - BURN_PACKAGE package; -} BURN_UPDATE; - - -// function declarations - -HRESULT UpdateParseFromXml( - __in BURN_UPDATE* pUpdate, - __in IXMLDOMNode* pixnBundle - ); -void UpdateUninitialize( - __in BURN_UPDATE* pUpdate - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp deleted file mode 100644 index 2215a070..00000000 --- a/src/engine/userexperience.cpp +++ /dev/null @@ -1,2653 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// internal function declarations - -static int FilterResult( - __in DWORD dwAllowedResults, - __in int nResult - ); - -static HRESULT FilterExecuteResult( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus, - __in BOOL fRollback, - __in BOOL fCancel, - __in LPCWSTR sczEventName - ); - -static HRESULT SendBAMessage( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BOOTSTRAPPER_APPLICATION_MESSAGE message, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ); - -static HRESULT SendBAMessageFromInactiveEngine( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BOOTSTRAPPER_APPLICATION_MESSAGE message, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ); - - -// function definitions - -/******************************************************************* - UserExperienceParseFromXml - - -*******************************************************************/ -extern "C" HRESULT UserExperienceParseFromXml( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in IXMLDOMNode* pixnBundle - ) -{ - HRESULT hr = S_OK; - IXMLDOMNode* pixnUserExperienceNode = NULL; - - // select UX node - hr = XmlSelectSingleNode(pixnBundle, L"UX", &pixnUserExperienceNode); - if (S_FALSE == hr) - { - hr = E_NOTFOUND; - } - ExitOnFailure(hr, "Failed to select user experience node."); - - // parse splash screen - hr = XmlGetYesNoAttribute(pixnUserExperienceNode, L"SplashScreen", &pUserExperience->fSplashScreen); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to to get UX/@SplashScreen"); - } - - // parse payloads - hr = PayloadsParseFromXml(&pUserExperience->payloads, NULL, NULL, pixnUserExperienceNode); - ExitOnFailure(hr, "Failed to parse user experience payloads."); - - // make sure we have at least one payload - if (0 == pUserExperience->payloads.cPayloads) - { - hr = E_UNEXPECTED; - ExitOnFailure(hr, "Too few UX payloads."); - } - -LExit: - ReleaseObject(pixnUserExperienceNode); - - return hr; -} - -/******************************************************************* - UserExperienceUninitialize - - -*******************************************************************/ -extern "C" void UserExperienceUninitialize( - __in BURN_USER_EXPERIENCE* pUserExperience - ) -{ - ReleaseStr(pUserExperience->sczTempDirectory); - PayloadsUninitialize(&pUserExperience->payloads); - - // clear struct - memset(pUserExperience, 0, sizeof(BURN_USER_EXPERIENCE)); -} - -/******************************************************************* - UserExperienceLoad - - -*******************************************************************/ -extern "C" HRESULT UserExperienceLoad( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BOOTSTRAPPER_ENGINE_CONTEXT* pEngineContext, - __in BOOTSTRAPPER_COMMAND* pCommand - ) -{ - HRESULT hr = S_OK; - BOOTSTRAPPER_CREATE_ARGS args = { }; - BOOTSTRAPPER_CREATE_RESULTS results = { }; - - args.cbSize = sizeof(BOOTSTRAPPER_CREATE_ARGS); - args.pCommand = pCommand; - args.pfnBootstrapperEngineProc = EngineForApplicationProc; - args.pvBootstrapperEngineProcContext = pEngineContext; - args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 4, 27, 0); - - results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS); - - // Load BA DLL. - pUserExperience->hUXModule = ::LoadLibraryExW(pUserExperience->payloads.rgPayloads[0].sczLocalFilePath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); - ExitOnNullWithLastError(pUserExperience->hUXModule, hr, "Failed to load BA DLL."); - - // Get BootstrapperApplicationCreate entry-point. - PFN_BOOTSTRAPPER_APPLICATION_CREATE pfnCreate = (PFN_BOOTSTRAPPER_APPLICATION_CREATE)::GetProcAddress(pUserExperience->hUXModule, "BootstrapperApplicationCreate"); - ExitOnNullWithLastError(pfnCreate, hr, "Failed to get BootstrapperApplicationCreate entry-point"); - - // Create BA. - hr = pfnCreate(&args, &results); - ExitOnFailure(hr, "Failed to create BA."); - - pUserExperience->pfnBAProc = results.pfnBootstrapperApplicationProc; - pUserExperience->pvBAProcContext = results.pvBootstrapperApplicationProcContext; - pUserExperience->fDisableUnloading = results.fDisableUnloading; - -LExit: - return hr; -} - -/******************************************************************* - UserExperienceUnload - - -*******************************************************************/ -extern "C" HRESULT UserExperienceUnload( - __in BURN_USER_EXPERIENCE* pUserExperience - ) -{ - HRESULT hr = S_OK; - - if (pUserExperience->hUXModule) - { - // Get BootstrapperApplicationDestroy entry-point and call it if it exists. - PFN_BOOTSTRAPPER_APPLICATION_DESTROY pfnDestroy = (PFN_BOOTSTRAPPER_APPLICATION_DESTROY)::GetProcAddress(pUserExperience->hUXModule, "BootstrapperApplicationDestroy"); - if (pfnDestroy) - { - pfnDestroy(); - } - - // Free BA DLL if it supports it. - if (!pUserExperience->fDisableUnloading && !::FreeLibrary(pUserExperience->hUXModule)) - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - TraceError(hr, "Failed to unload BA DLL."); - } - pUserExperience->hUXModule = NULL; - } - -//LExit: - return hr; -} - -extern "C" HRESULT UserExperienceEnsureWorkingFolder( - __in LPCWSTR wzBundleId, - __deref_out_z LPWSTR* psczUserExperienceWorkingFolder - ) -{ - HRESULT hr = S_OK; - LPWSTR sczWorkingFolder = NULL; - - hr = CacheEnsureWorkingFolder(wzBundleId, &sczWorkingFolder); - ExitOnFailure(hr, "Failed to create working folder."); - - hr = StrAllocFormatted(psczUserExperienceWorkingFolder, L"%ls%ls\\", sczWorkingFolder, L".ba"); - ExitOnFailure(hr, "Failed to calculate the bootstrapper application working path."); - - hr = DirEnsureExists(*psczUserExperienceWorkingFolder, NULL); - ExitOnFailure(hr, "Failed create bootstrapper application working folder."); - -LExit: - ReleaseStr(sczWorkingFolder); - - return hr; -} - - -extern "C" HRESULT UserExperienceRemove( - __in BURN_USER_EXPERIENCE* pUserExperience - ) -{ - HRESULT hr = S_OK; - - // Remove temporary UX directory - if (pUserExperience->sczTempDirectory) - { - hr = DirEnsureDeleteEx(pUserExperience->sczTempDirectory, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE); - TraceError(hr, "Could not delete bootstrapper application folder. Some files will be left in the temp folder."); - } - -//LExit: - return hr; -} - -extern "C" int UserExperienceSendError( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BOOTSTRAPPER_ERROR_TYPE errorType, - __in_z_opt LPCWSTR wzPackageId, - __in HRESULT hrCode, - __in_z_opt LPCWSTR wzError, - __in DWORD uiFlags, - __in int nRecommendation - ) -{ - int nResult = nRecommendation; - DWORD dwCode = HRESULT_CODE(hrCode); - LPWSTR sczError = NULL; - - // If no error string was provided, try to get the error string from the HRESULT. - if (!wzError) - { - if (SUCCEEDED(StrAllocFromError(&sczError, hrCode, NULL))) - { - wzError = sczError; - } - } - - UserExperienceOnError(pUserExperience, errorType, wzPackageId, dwCode, wzError, uiFlags, 0, NULL, &nResult); // ignore return value. - - ReleaseStr(sczError); - return nResult; -} - -extern "C" void UserExperienceActivateEngine( - __in BURN_USER_EXPERIENCE* pUserExperience - ) -{ - ::EnterCriticalSection(&pUserExperience->csEngineActive); - AssertSz(!pUserExperience->fEngineActive, "Engine should have been deactivated before activating it."); - pUserExperience->fEngineActive = TRUE; - ::LeaveCriticalSection(&pUserExperience->csEngineActive); -} - -extern "C" void UserExperienceDeactivateEngine( - __in BURN_USER_EXPERIENCE* pUserExperience - ) -{ - ::EnterCriticalSection(&pUserExperience->csEngineActive); - AssertSz(pUserExperience->fEngineActive, "Engine should have been active before deactivating it."); - pUserExperience->fEngineActive = FALSE; - ::LeaveCriticalSection(&pUserExperience->csEngineActive); -} - -extern "C" HRESULT UserExperienceEnsureEngineInactive( - __in BURN_USER_EXPERIENCE* pUserExperience - ) -{ - // Make a slight optimization here by ignoring the critical section, because all callers should have needed to enter it for their operation anyway. - HRESULT hr = pUserExperience->fEngineActive ? HRESULT_FROM_WIN32(ERROR_BUSY) : S_OK; - ExitOnRootFailure(hr, "Engine is active, cannot proceed."); - -LExit: - return hr; -} - -extern "C" void UserExperienceExecuteReset( - __in BURN_USER_EXPERIENCE* pUserExperience - ) -{ - pUserExperience->hrApplyError = S_OK; - pUserExperience->hwndApply = NULL; -} - -extern "C" void UserExperienceExecutePhaseComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrResult - ) -{ - if (FAILED(hrResult)) - { - pUserExperience->hrApplyError = hrResult; - } -} - -EXTERN_C BAAPI UserExperienceOnApplyBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in DWORD dwPhaseCount - ) -{ - HRESULT hr = S_OK; - BA_ONAPPLYBEGIN_ARGS args = { }; - BA_ONAPPLYBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.dwPhaseCount = dwPhaseCount; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnApplyBegin failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnApplyComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus, - __in BOOTSTRAPPER_APPLY_RESTART restart, - __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction - ) -{ - HRESULT hr = S_OK; - BA_ONAPPLYCOMPLETE_ARGS args = { }; - BA_ONAPPLYCOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.hrStatus = hrStatus; - args.restart = restart; - args.recommendation = *pAction; - - results.cbSize = sizeof(results); - results.action = *pAction; - - hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYCOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnApplyComplete failed."); - - *pAction = results.action; - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnBeginMsiTransactionBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in LPCWSTR wzTransactionId - ) -{ - HRESULT hr = S_OK; - BA_ONBEGINMSITRANSACTIONBEGIN_ARGS args = { }; - BA_ONBEGINMSITRANSACTIONBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzTransactionId = wzTransactionId; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONBEGINMSITRANSACTIONBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnBeginMsiTransactionBegin failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnBeginMsiTransactionComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in LPCWSTR wzTransactionId, - __in HRESULT hrStatus - ) -{ - HRESULT hr = S_OK; - BA_ONBEGINMSITRANSACTIONCOMPLETE_ARGS args = { }; - BA_ONBEGINMSITRANSACTIONCOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzTransactionId = wzTransactionId; - args.hrStatus = hrStatus; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONBEGINMSITRANSACTIONCOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnBeginMsiTransactionComplete failed."); - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnCacheAcquireBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in_z LPWSTR* pwzSource, - __in_z LPWSTR* pwzDownloadUrl, - __in_z_opt LPCWSTR wzPayloadContainerId, - __out BOOTSTRAPPER_CACHE_OPERATION* pCacheOperation - ) -{ - HRESULT hr = S_OK; - BA_ONCACHEACQUIREBEGIN_ARGS args = { }; - BA_ONCACHEACQUIREBEGIN_RESULTS results = { }; - *pCacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE; - - args.cbSize = sizeof(args); - args.wzPackageOrContainerId = wzPackageOrContainerId; - args.wzPayloadId = wzPayloadId; - args.wzSource = *pwzSource; - args.wzDownloadUrl = *pwzDownloadUrl; - args.wzPayloadContainerId = wzPayloadContainerId; - args.recommendation = *pCacheOperation; - - results.cbSize = sizeof(results); - results.action = *pCacheOperation; - - hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnCacheAcquireBegin failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - else - { - // Verify the BA requested an action that is possible. - if (BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD == results.action && *pwzDownloadUrl && **pwzDownloadUrl || - BOOTSTRAPPER_CACHE_OPERATION_EXTRACT == results.action && wzPayloadContainerId || - BOOTSTRAPPER_CACHE_OPERATION_COPY == results.action || - BOOTSTRAPPER_CACHE_OPERATION_NONE == results.action) - { - *pCacheOperation = results.action; - } - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnCacheAcquireComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in HRESULT hrStatus, - __inout BOOL* pfRetry - ) -{ - HRESULT hr = S_OK; - BA_ONCACHEACQUIRECOMPLETE_ARGS args = { }; - BA_ONCACHEACQUIRECOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageOrContainerId = wzPackageOrContainerId; - args.wzPayloadId = wzPayloadId; - args.hrStatus = hrStatus; - args.recommendation = *pfRetry ? BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_RETRY : BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_NONE; - - results.cbSize = sizeof(results); - results.action = args.recommendation; - - hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRECOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnCacheAcquireComplete failed."); - - if (FAILED(hrStatus)) - { - *pfRetry = BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_RETRY == results.action; - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnCacheAcquireProgress( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in DWORD64 dw64Progress, - __in DWORD64 dw64Total, - __in DWORD dwOverallPercentage - ) -{ - HRESULT hr = S_OK; - BA_ONCACHEACQUIREPROGRESS_ARGS args = { }; - BA_ONCACHEACQUIREPROGRESS_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageOrContainerId = wzPackageOrContainerId; - args.wzPayloadId = wzPayloadId; - args.dw64Progress = dw64Progress; - args.dw64Total = dw64Total; - args.dwOverallPercentage = dwOverallPercentage; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREPROGRESS, &args, &results); - ExitOnFailure(hr, "BA OnCacheAcquireProgress failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnCacheAcquireResolving( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in_z LPWSTR* rgSearchPaths, - __in DWORD cSearchPaths, - __in BOOL fFoundLocal, - __in DWORD* pdwChosenSearchPath, - __in_z_opt LPWSTR* pwzDownloadUrl, - __in_z_opt LPCWSTR wzPayloadContainerId, - __inout BOOTSTRAPPER_CACHE_RESOLVE_OPERATION* pCacheOperation - ) -{ - HRESULT hr = S_OK; - BA_ONCACHEACQUIRERESOLVING_ARGS args = { }; - BA_ONCACHEACQUIRERESOLVING_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageOrContainerId = wzPackageOrContainerId; - args.wzPayloadId = wzPayloadId; - args.rgSearchPaths = const_cast(rgSearchPaths); - args.cSearchPaths = cSearchPaths; - args.fFoundLocal = fFoundLocal; - args.dwRecommendedSearchPath = *pdwChosenSearchPath; - args.wzDownloadUrl = *pwzDownloadUrl; - args.recommendation = *pCacheOperation; - - results.cbSize = sizeof(results); - results.dwChosenSearchPath = *pdwChosenSearchPath; - results.action = *pCacheOperation; - - hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRERESOLVING, &args, &results); - ExitOnFailure(hr, "BA OnCacheAcquireResolving failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - else - { - // Verify the BA requested an action that is possible. - if (BOOTSTRAPPER_CACHE_RESOLVE_DOWNLOAD == results.action && *pwzDownloadUrl && **pwzDownloadUrl || - BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER == results.action && wzPayloadContainerId || - BOOTSTRAPPER_CACHE_RESOLVE_RETRY == results.action || - BOOTSTRAPPER_CACHE_RESOLVE_NONE == results.action) - { - *pCacheOperation = results.action; - } - else if (BOOTSTRAPPER_CACHE_RESOLVE_LOCAL == results.action && results.dwChosenSearchPath < cSearchPaths) - { - *pdwChosenSearchPath = results.dwChosenSearchPath; - *pCacheOperation = results.action; - } - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnCacheBegin( - __in BURN_USER_EXPERIENCE* pUserExperience - ) -{ - HRESULT hr = S_OK; - BA_ONCACHEBEGIN_ARGS args = { }; - BA_ONCACHEBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnCacheBegin failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnCacheComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus - ) -{ - HRESULT hr = S_OK; - BA_ONCACHECOMPLETE_ARGS args = { }; - BA_ONCACHECOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.hrStatus = hrStatus; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnCacheComplete failed."); - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnCacheContainerOrPayloadVerifyBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId - ) -{ - HRESULT hr = S_OK; - BA_ONCACHECONTAINERORPAYLOADVERIFYBEGIN_ARGS args = { }; - BA_ONCACHECONTAINERORPAYLOADVERIFYBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageOrContainerId = wzPackageOrContainerId; - args.wzPayloadId = wzPayloadId; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECONTAINERORPAYLOADVERIFYBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnCacheContainerOrPayloadVerifyBegin failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnCacheContainerOrPayloadVerifyComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in HRESULT hrStatus - ) -{ - HRESULT hr = S_OK; - BA_ONCACHECONTAINERORPAYLOADVERIFYCOMPLETE_ARGS args = { }; - BA_ONCACHECONTAINERORPAYLOADVERIFYCOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageOrContainerId = wzPackageOrContainerId; - args.wzPayloadId = wzPayloadId; - args.hrStatus = hrStatus; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECONTAINERORPAYLOADVERIFYCOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnCacheContainerOrPayloadVerifyComplete failed."); - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnCacheContainerOrPayloadVerifyProgress( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in DWORD64 dw64Progress, - __in DWORD64 dw64Total, - __in DWORD dwOverallPercentage - ) -{ - HRESULT hr = S_OK; - BA_ONCACHECONTAINERORPAYLOADVERIFYPROGRESS_ARGS args = { }; - BA_ONCACHECONTAINERORPAYLOADVERIFYPROGRESS_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageOrContainerId = wzPackageOrContainerId; - args.wzPayloadId = wzPayloadId; - args.dw64Progress = dw64Progress; - args.dw64Total = dw64Total; - args.dwOverallPercentage = dwOverallPercentage; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECONTAINERORPAYLOADVERIFYPROGRESS, &args, &results); - ExitOnFailure(hr, "BA OnCacheContainerOrPayloadVerifyProgress failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnCachePackageBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in DWORD cCachePayloads, - __in DWORD64 dw64PackageCacheSize - ) -{ - HRESULT hr = S_OK; - BA_ONCACHEPACKAGEBEGIN_ARGS args = { }; - BA_ONCACHEPACKAGEBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - args.cCachePayloads = cCachePayloads; - args.dw64PackageCacheSize = dw64PackageCacheSize; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGEBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnCachePackageBegin failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnCachePackageComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in HRESULT hrStatus, - __inout BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION* pAction - ) -{ - HRESULT hr = S_OK; - BA_ONCACHEPACKAGECOMPLETE_ARGS args = { }; - BA_ONCACHEPACKAGECOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - args.hrStatus = hrStatus; - args.recommendation = *pAction; - - results.cbSize = sizeof(results); - results.action = *pAction; - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGECOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnCachePackageComplete failed."); - - if (FAILED(hrStatus)) - { - *pAction = results.action; - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnCachePayloadExtractBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzContainerId, - __in_z_opt LPCWSTR wzPayloadId - ) -{ - HRESULT hr = S_OK; - BA_ONCACHEPAYLOADEXTRACTBEGIN_ARGS args = { }; - BA_ONCACHEPAYLOADEXTRACTBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzContainerId = wzContainerId; - args.wzPayloadId = wzPayloadId; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPAYLOADEXTRACTBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnCachePayloadExtractBegin failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnCachePayloadExtractComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in HRESULT hrStatus - ) -{ - HRESULT hr = S_OK; - BA_ONCACHEPAYLOADEXTRACTCOMPLETE_ARGS args = { }; - BA_ONCACHEPAYLOADEXTRACTCOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzContainerId = wzContainerId; - args.wzPayloadId = wzPayloadId; - args.hrStatus = hrStatus; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPAYLOADEXTRACTCOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnCachePayloadExtractComplete failed."); - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnCachePayloadExtractProgress( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in DWORD64 dw64Progress, - __in DWORD64 dw64Total, - __in DWORD dwOverallPercentage - ) -{ - HRESULT hr = S_OK; - BA_ONCACHEPAYLOADEXTRACTPROGRESS_ARGS args = { }; - BA_ONCACHEPAYLOADEXTRACTPROGRESS_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzContainerId = wzContainerId; - args.wzPayloadId = wzPayloadId; - args.dw64Progress = dw64Progress; - args.dw64Total = dw64Total; - args.dwOverallPercentage = dwOverallPercentage; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPAYLOADEXTRACTPROGRESS, &args, &results); - ExitOnFailure(hr, "BA OnCachePayloadExtractProgress failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnCacheVerifyBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId - ) -{ - HRESULT hr = S_OK; - BA_ONCACHEVERIFYBEGIN_ARGS args = { }; - BA_ONCACHEVERIFYBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageOrContainerId = wzPackageOrContainerId; - args.wzPayloadId = wzPayloadId; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnCacheVerifyBegin failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnCacheVerifyComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in HRESULT hrStatus, - __inout BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION* pAction - ) -{ - HRESULT hr = S_OK; - BA_ONCACHEVERIFYCOMPLETE_ARGS args = { }; - BA_ONCACHEVERIFYCOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageOrContainerId = wzPackageOrContainerId; - args.wzPayloadId = wzPayloadId; - args.hrStatus = hrStatus; - args.recommendation = *pAction; - - results.cbSize = sizeof(results); - results.action = *pAction; - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYCOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnCacheVerifyComplete failed."); - - if (FAILED(hrStatus)) - { - *pAction = results.action; - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnCacheVerifyProgress( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in DWORD64 dw64Progress, - __in DWORD64 dw64Total, - __in DWORD dwOverallPercentage, - __in BOOTSTRAPPER_CACHE_VERIFY_STEP verifyStep - ) -{ - HRESULT hr = S_OK; - BA_ONCACHEVERIFYPROGRESS_ARGS args = { }; - BA_ONCACHEVERIFYPROGRESS_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageOrContainerId = wzPackageOrContainerId; - args.wzPayloadId = wzPayloadId; - args.dw64Progress = dw64Progress; - args.dw64Total = dw64Total; - args.dwOverallPercentage = dwOverallPercentage; - args.verifyStep = verifyStep; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYPROGRESS, &args, &results); - ExitOnFailure(hr, "BA OnCacheVerifyProgress failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnCommitMsiTransactionBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in LPCWSTR wzTransactionId - ) -{ - HRESULT hr = S_OK; - BA_ONCOMMITMSITRANSACTIONBEGIN_ARGS args = { }; - BA_ONCOMMITMSITRANSACTIONBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzTransactionId = wzTransactionId; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCOMMITMSITRANSACTIONBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnCommitMsiTransactionBegin failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnCommitMsiTransactionComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in LPCWSTR wzTransactionId, - __in HRESULT hrStatus - ) -{ - HRESULT hr = S_OK; - BA_ONCOMMITMSITRANSACTIONCOMPLETE_ARGS args = { }; - BA_ONCOMMITMSITRANSACTIONCOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzTransactionId = wzTransactionId; - args.hrStatus = hrStatus; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCOMMITMSITRANSACTIONCOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnCommitMsiTransactionComplete failed."); - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnDetectBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BOOL fCached, - __in BOOL fInstalled, - __in DWORD cPackages - ) -{ - HRESULT hr = S_OK; - BA_ONDETECTBEGIN_ARGS args = { }; - BA_ONDETECTBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.cPackages = cPackages; - args.fInstalled = fInstalled; - args.fCached = fCached; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnDetectBegin failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnDetectComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus, - __in BOOL fEligibleForCleanup - ) -{ - HRESULT hr = S_OK; - BA_ONDETECTCOMPLETE_ARGS args = { }; - BA_ONDETECTCOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.hrStatus = hrStatus; - args.fEligibleForCleanup = fEligibleForCleanup; - - results.cbSize = sizeof(results); - - hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnDetectComplete failed."); - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnDetectForwardCompatibleBundle( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzBundleId, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in_z LPCWSTR wzBundleTag, - __in BOOL fPerMachine, - __in VERUTIL_VERSION* pVersion, - __in BOOL fMissingFromCache - ) -{ - HRESULT hr = S_OK; - BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_ARGS args = { }; - BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzBundleId = wzBundleId; - args.relationType = relationType; - args.wzBundleTag = wzBundleTag; - args.fPerMachine = fPerMachine; - args.wzVersion = pVersion->sczVersion; - args.fMissingFromCache = fMissingFromCache; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTFORWARDCOMPATIBLEBUNDLE, &args, &results); - ExitOnFailure(hr, "BA OnDetectForwardCompatibleBundle failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnDetectMsiFeature( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in_z LPCWSTR wzFeatureId, - __in BOOTSTRAPPER_FEATURE_STATE state - ) -{ - HRESULT hr = S_OK; - BA_ONDETECTMSIFEATURE_ARGS args = { }; - BA_ONDETECTMSIFEATURE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - args.wzFeatureId = wzFeatureId; - args.state = state; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTMSIFEATURE, &args, &results); - ExitOnFailure(hr, "BA OnDetectMsiFeature failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnDetectPackageBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId - ) -{ - HRESULT hr = S_OK; - BA_ONDETECTPACKAGEBEGIN_ARGS args = { }; - BA_ONDETECTPACKAGEBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGEBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnDetectPackageBegin failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnDetectPackageComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in HRESULT hrStatus, - __in BOOTSTRAPPER_PACKAGE_STATE state, - __in BOOL fCached - ) -{ - HRESULT hr = S_OK; - BA_ONDETECTPACKAGECOMPLETE_ARGS args = { }; - BA_ONDETECTPACKAGECOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - args.hrStatus = hrStatus; - args.state = state; - args.fCached = fCached; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGECOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnDetectPackageComplete failed."); - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnDetectRelatedBundle( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzBundleId, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in_z LPCWSTR wzBundleTag, - __in BOOL fPerMachine, - __in VERUTIL_VERSION* pVersion, - __in BOOTSTRAPPER_RELATED_OPERATION operation, - __in BOOL fMissingFromCache - ) -{ - HRESULT hr = S_OK; - BA_ONDETECTRELATEDBUNDLE_ARGS args = { }; - BA_ONDETECTRELATEDBUNDLE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzBundleId = wzBundleId; - args.relationType = relationType; - args.wzBundleTag = wzBundleTag; - args.fPerMachine = fPerMachine; - args.wzVersion = pVersion->sczVersion; - args.operation = operation; - args.fMissingFromCache = fMissingFromCache; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLE, &args, &results); - ExitOnFailure(hr, "BA OnDetectRelatedBundle failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnDetectRelatedMsiPackage( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in_z LPCWSTR wzUpgradeCode, - __in_z LPCWSTR wzProductCode, - __in BOOL fPerMachine, - __in VERUTIL_VERSION* pVersion, - __in BOOTSTRAPPER_RELATED_OPERATION operation - ) -{ - HRESULT hr = S_OK; - BA_ONDETECTRELATEDMSIPACKAGE_ARGS args = { }; - BA_ONDETECTRELATEDMSIPACKAGE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - args.wzUpgradeCode = wzUpgradeCode; - args.wzProductCode = wzProductCode; - args.fPerMachine = fPerMachine; - args.wzVersion = pVersion->sczVersion; - args.operation = operation; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDMSIPACKAGE, &args, &results); - ExitOnFailure(hr, "BA OnDetectRelatedMsiPackage failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnDetectPatchTarget( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in_z LPCWSTR wzProductCode, - __in BOOTSTRAPPER_PACKAGE_STATE patchState - ) -{ - HRESULT hr = S_OK; - BA_ONDETECTPATCHTARGET_ARGS args = { }; - BA_ONDETECTPATCHTARGET_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - args.wzProductCode = wzProductCode; - args.patchState = patchState; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPATCHTARGET, &args, &results); - ExitOnFailure(hr, "BA OnDetectPatchTarget failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnDetectUpdate( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzUpdateLocation, - __in DWORD64 dw64Size, - __in VERUTIL_VERSION* pVersion, - __in_z_opt LPCWSTR wzTitle, - __in_z_opt LPCWSTR wzSummary, - __in_z_opt LPCWSTR wzContentType, - __in_z_opt LPCWSTR wzContent, - __inout BOOL* pfStopProcessingUpdates - ) -{ - HRESULT hr = S_OK; - BA_ONDETECTUPDATE_ARGS args = { }; - BA_ONDETECTUPDATE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzUpdateLocation = wzUpdateLocation; - args.dw64Size = dw64Size; - args.wzVersion = pVersion->sczVersion; - args.wzTitle = wzTitle; - args.wzSummary = wzSummary; - args.wzContentType = wzContentType; - args.wzContent = wzContent; - - results.cbSize = sizeof(results); - results.fStopProcessingUpdates = *pfStopProcessingUpdates; - - hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATE, &args, &results); - ExitOnFailure(hr, "BA OnDetectUpdate failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - *pfStopProcessingUpdates = results.fStopProcessingUpdates; - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnDetectUpdateBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzUpdateLocation, - __inout BOOL* pfSkip - ) -{ - HRESULT hr = S_OK; - BA_ONDETECTUPDATEBEGIN_ARGS args = { }; - BA_ONDETECTUPDATEBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzUpdateLocation = wzUpdateLocation; - - results.cbSize = sizeof(results); - results.fSkip = *pfSkip; - - hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATEBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnDetectUpdateBegin failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - *pfSkip = results.fSkip; - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnDetectUpdateComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus, - __inout BOOL* pfIgnoreError - ) -{ - HRESULT hr = S_OK; - BA_ONDETECTUPDATECOMPLETE_ARGS args = { }; - BA_ONDETECTUPDATECOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.hrStatus = hrStatus; - - results.cbSize = sizeof(results); - results.fIgnoreError = *pfIgnoreError; - - hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATECOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnDetectUpdateComplete failed."); - - if (FAILED(hrStatus)) - { - *pfIgnoreError = results.fIgnoreError; - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnElevateBegin( - __in BURN_USER_EXPERIENCE* pUserExperience - ) -{ - HRESULT hr = S_OK; - BA_ONELEVATEBEGIN_ARGS args = { }; - BA_ONELEVATEBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATEBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnElevateBegin failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnElevateComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus - ) -{ - HRESULT hr = S_OK; - BA_ONELEVATECOMPLETE_ARGS args = { }; - BA_ONELEVATECOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.hrStatus = hrStatus; - - results.cbSize = sizeof(results); - - hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATECOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnElevateComplete failed."); - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnError( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BOOTSTRAPPER_ERROR_TYPE errorType, - __in_z_opt LPCWSTR wzPackageId, - __in DWORD dwCode, - __in_z_opt LPCWSTR wzError, - __in DWORD dwUIHint, - __in DWORD cData, - __in_ecount_z_opt(cData) LPCWSTR* rgwzData, - __inout int* pnResult - ) -{ - HRESULT hr = S_OK; - BA_ONERROR_ARGS args = { }; - BA_ONERROR_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.errorType = errorType; - args.wzPackageId = wzPackageId; - args.dwCode = dwCode; - args.wzError = wzError; - args.dwUIHint = dwUIHint; - args.cData = cData; - args.rgwzData = rgwzData; - args.nRecommendation = *pnResult; - - results.cbSize = sizeof(results); - results.nResult = *pnResult; - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONERROR, &args, &results); - ExitOnFailure(hr, "BA OnError failed."); - - *pnResult = results.nResult; - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnExecuteBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in DWORD cExecutingPackages - ) -{ - HRESULT hr = S_OK; - BA_ONEXECUTEBEGIN_ARGS args = { }; - BA_ONEXECUTEBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.cExecutingPackages = cExecutingPackages; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnExecuteBegin failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnExecuteComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus - ) -{ - HRESULT hr = S_OK; - BA_ONEXECUTECOMPLETE_ARGS args = { }; - BA_ONEXECUTECOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.hrStatus = hrStatus; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTECOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnExecuteComplete failed."); - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnExecuteFilesInUse( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in DWORD cFiles, - __in_ecount_z_opt(cFiles) LPCWSTR* rgwzFiles, - __inout int* pnResult - ) -{ - HRESULT hr = S_OK; - BA_ONEXECUTEFILESINUSE_ARGS args = { }; - BA_ONEXECUTEFILESINUSE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - args.cFiles = cFiles; - args.rgwzFiles = rgwzFiles; - args.nRecommendation = *pnResult; - - results.cbSize = sizeof(results); - results.nResult = *pnResult; - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEFILESINUSE, &args, &results); - ExitOnFailure(hr, "BA OnExecuteFilesInUse failed."); - - *pnResult = results.nResult; - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnExecuteMsiMessage( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in INSTALLMESSAGE messageType, - __in DWORD dwUIHint, - __in_z LPCWSTR wzMessage, - __in DWORD cData, - __in_ecount_z_opt(cData) LPCWSTR* rgwzData, - __inout int* pnResult - ) -{ - HRESULT hr = S_OK; - BA_ONEXECUTEMSIMESSAGE_ARGS args = { }; - BA_ONEXECUTEMSIMESSAGE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - args.messageType = messageType; - args.dwUIHint = dwUIHint; - args.wzMessage = wzMessage; - args.cData = cData; - args.rgwzData = rgwzData; - args.nRecommendation = *pnResult; - - results.cbSize = sizeof(results); - results.nResult = *pnResult; - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEMSIMESSAGE, &args, &results); - ExitOnFailure(hr, "BA OnExecuteMsiMessage failed."); - - *pnResult = results.nResult; - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnExecutePackageBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in BOOL fExecute, - __in BOOTSTRAPPER_ACTION_STATE action, - __in INSTALLUILEVEL uiLevel, - __in BOOL fDisableExternalUiHandler - ) -{ - HRESULT hr = S_OK; - BA_ONEXECUTEPACKAGEBEGIN_ARGS args = { }; - BA_ONEXECUTEPACKAGEBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - args.fExecute = fExecute; - args.action = action; - args.uiLevel = uiLevel; - args.fDisableExternalUiHandler = fDisableExternalUiHandler; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGEBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnExecutePackageBegin failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnExecutePackageComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in HRESULT hrStatus, - __in BOOTSTRAPPER_APPLY_RESTART restart, - __inout BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION* pAction - ) -{ - HRESULT hr = S_OK; - BA_ONEXECUTEPACKAGECOMPLETE_ARGS args = { }; - BA_ONEXECUTEPACKAGECOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - args.hrStatus = hrStatus; - args.restart = restart; - args.recommendation = *pAction; - - results.cbSize = sizeof(results); - results.action = *pAction; - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGECOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnExecutePackageComplete failed."); - - *pAction = results.action; - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnExecutePatchTarget( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in_z LPCWSTR wzTargetProductCode - ) -{ - HRESULT hr = S_OK; - BA_ONEXECUTEPATCHTARGET_ARGS args = { }; - BA_ONEXECUTEPATCHTARGET_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - args.wzTargetProductCode = wzTargetProductCode; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPATCHTARGET, &args, &results); - ExitOnFailure(hr, "BA OnExecutePatchTarget failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnExecuteProgress( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in DWORD dwProgressPercentage, - __in DWORD dwOverallPercentage, - __out int* pnResult - ) -{ - HRESULT hr = S_OK; - BA_ONEXECUTEPROGRESS_ARGS args = { }; - BA_ONEXECUTEPROGRESS_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - args.dwProgressPercentage = dwProgressPercentage; - args.dwOverallPercentage = dwOverallPercentage; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROGRESS, &args, &results); - ExitOnFailure(hr, "BA OnExecuteProgress failed."); - -LExit: - if (FAILED(hr)) - { - *pnResult = IDERROR; - } - else if (results.fCancel) - { - *pnResult = IDCANCEL; - } - else - { - *pnResult = IDNOACTION; - } - return hr; -} - -EXTERN_C BAAPI UserExperienceOnLaunchApprovedExeBegin( - __in BURN_USER_EXPERIENCE* pUserExperience - ) -{ - HRESULT hr = S_OK; - BA_ONLAUNCHAPPROVEDEXEBEGIN_ARGS args = { }; - BA_ONLAUNCHAPPROVEDEXEBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXEBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnLaunchApprovedExeBegin failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnLaunchApprovedExeComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus, - __in DWORD dwProcessId - ) -{ - HRESULT hr = S_OK; - BA_ONLAUNCHAPPROVEDEXECOMPLETE_ARGS args = { }; - BA_ONLAUNCHAPPROVEDEXECOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.hrStatus = hrStatus; - args.dwProcessId = dwProcessId; - - results.cbSize = sizeof(results); - - hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXECOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnLaunchApprovedExeComplete failed."); - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnPauseAUBegin( - __in BURN_USER_EXPERIENCE* pUserExperience - ) -{ - HRESULT hr = S_OK; - BA_ONPAUSEAUTOMATICUPDATESBEGIN_ARGS args = { }; - BA_ONPAUSEAUTOMATICUPDATESBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPAUSEAUTOMATICUPDATESBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnPauseAUBegin failed."); - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnPauseAUComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus - ) -{ - HRESULT hr = S_OK; - BA_ONPAUSEAUTOMATICUPDATESCOMPLETE_ARGS args = { }; - BA_ONPAUSEAUTOMATICUPDATESCOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.hrStatus = hrStatus; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPAUSEAUTOMATICUPDATESCOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnPauseAUComplete failed."); - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnPlanBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in DWORD cPackages - ) -{ - HRESULT hr = S_OK; - BA_ONPLANBEGIN_ARGS args = { }; - BA_ONPLANBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.cPackages = cPackages; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnPlanBegin failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnPlanMsiFeature( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in_z LPCWSTR wzFeatureId, - __inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState - ) -{ - HRESULT hr = S_OK; - BA_ONPLANMSIFEATURE_ARGS args = { }; - BA_ONPLANMSIFEATURE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - args.wzFeatureId = wzFeatureId; - args.recommendedState = *pRequestedState; - - results.cbSize = sizeof(results); - results.requestedState = *pRequestedState; - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIFEATURE, &args, &results); - ExitOnFailure(hr, "BA OnPlanMsiFeature failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - *pRequestedState = results.requestedState; - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnPlanComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus - ) -{ - HRESULT hr = S_OK; - BA_ONPLANCOMPLETE_ARGS args = { }; - BA_ONPLANCOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.hrStatus = hrStatus; - - results.cbSize = sizeof(results); - - hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnPlanComplete failed."); - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnPlanForwardCompatibleBundle( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzBundleId, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in_z LPCWSTR wzBundleTag, - __in BOOL fPerMachine, - __in VERUTIL_VERSION* pVersion, - __inout BOOL* pfIgnoreBundle - ) -{ - HRESULT hr = S_OK; - BA_ONPLANFORWARDCOMPATIBLEBUNDLE_ARGS args = { }; - BA_ONPLANFORWARDCOMPATIBLEBUNDLE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzBundleId = wzBundleId; - args.relationType = relationType; - args.wzBundleTag = wzBundleTag; - args.fPerMachine = fPerMachine; - args.wzVersion = pVersion->sczVersion; - args.fRecommendedIgnoreBundle = *pfIgnoreBundle; - - results.cbSize = sizeof(results); - results.fIgnoreBundle = *pfIgnoreBundle; - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANFORWARDCOMPATIBLEBUNDLE, &args, &results); - ExitOnFailure(hr, "BA OnPlanForwardCompatibleBundle failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - *pfIgnoreBundle = results.fIgnoreBundle; - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnPlanMsiPackage( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in BOOL fExecute, - __in BOOTSTRAPPER_ACTION_STATE action, - __inout BURN_MSI_PROPERTY* pActionMsiProperty, - __inout INSTALLUILEVEL* pUiLevel, - __inout BOOL* pfDisableExternalUiHandler - ) -{ - HRESULT hr = S_OK; - BA_ONPLANMSIPACKAGE_ARGS args = { }; - BA_ONPLANMSIPACKAGE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - args.fExecute = fExecute; - args.action = action; - - results.cbSize = sizeof(results); - results.actionMsiProperty = *pActionMsiProperty; - results.uiLevel = *pUiLevel; - results.fDisableExternalUiHandler = *pfDisableExternalUiHandler; - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIPACKAGE, &args, &results); - ExitOnFailure(hr, "BA OnPlanMsiPackage failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - *pActionMsiProperty = results.actionMsiProperty; - *pUiLevel = results.uiLevel; - *pfDisableExternalUiHandler = results.fDisableExternalUiHandler; - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnPlannedPackage( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in BOOTSTRAPPER_ACTION_STATE execute, - __in BOOTSTRAPPER_ACTION_STATE rollback, - __in BOOL fPlannedCache, - __in BOOL fPlannedUncache - ) -{ - HRESULT hr = S_OK; - BA_ONPLANNEDPACKAGE_ARGS args = { }; - BA_ONPLANNEDPACKAGE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - args.execute = execute; - args.rollback = rollback; - args.fPlannedCache = fPlannedCache; - args.fPlannedUncache = fPlannedUncache; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDPACKAGE, &args, &results); - ExitOnFailure(hr, "BA OnPlannedPackage failed."); - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnPlanPackageBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in BOOTSTRAPPER_PACKAGE_STATE state, - __in BOOL fCached, - __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, - __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState, - __inout BOOTSTRAPPER_CACHE_TYPE* pRequestedCacheType - ) -{ - HRESULT hr = S_OK; - BA_ONPLANPACKAGEBEGIN_ARGS args = { }; - BA_ONPLANPACKAGEBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - args.state = state; - args.fCached = fCached; - args.installCondition = installCondition; - args.recommendedState = *pRequestedState; - args.recommendedCacheType = *pRequestedCacheType; - - results.cbSize = sizeof(results); - results.requestedState = *pRequestedState; - results.requestedCacheType = *pRequestedCacheType; - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGEBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnPlanPackageBegin failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - *pRequestedState = results.requestedState; - *pRequestedCacheType = results.requestedCacheType; - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnPlanPackageComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in HRESULT hrStatus, - __in BOOTSTRAPPER_REQUEST_STATE requested - ) -{ - HRESULT hr = S_OK; - BA_ONPLANPACKAGECOMPLETE_ARGS args = { }; - BA_ONPLANPACKAGECOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - args.hrStatus = hrStatus; - args.requested = requested; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGECOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnPlanPackageComplete failed."); - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnPlanRelatedBundle( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzBundleId, - __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState - ) -{ - HRESULT hr = S_OK; - BA_ONPLANRELATEDBUNDLE_ARGS args = { }; - BA_ONPLANRELATEDBUNDLE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzBundleId = wzBundleId; - args.recommendedState = *pRequestedState; - - results.cbSize = sizeof(results); - results.requestedState = *pRequestedState; - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLE, &args, &results); - ExitOnFailure(hr, "BA OnPlanRelatedBundle failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - *pRequestedState = results.requestedState; - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnPlanPatchTarget( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in_z LPCWSTR wzProductCode, - __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState - ) -{ - HRESULT hr = S_OK; - BA_ONPLANPATCHTARGET_ARGS args = { }; - BA_ONPLANPATCHTARGET_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzPackageId = wzPackageId; - args.wzProductCode = wzProductCode; - args.recommendedState = *pRequestedState; - - results.cbSize = sizeof(results); - results.requestedState = *pRequestedState; - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPATCHTARGET, &args, &results); - ExitOnFailure(hr, "BA OnPlanPatchTarget failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - *pRequestedState = results.requestedState; - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnProgress( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BOOL fRollback, - __in DWORD dwProgressPercentage, - __in DWORD dwOverallPercentage - ) -{ - HRESULT hr = S_OK; - BA_ONPROGRESS_ARGS args = { }; - BA_ONPROGRESS_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.dwProgressPercentage = dwProgressPercentage; - args.dwOverallPercentage = dwOverallPercentage; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPROGRESS, &args, &results); - hr = FilterExecuteResult(pUserExperience, hr, fRollback, results.fCancel, L"OnProgress"); - - return hr; -} - -EXTERN_C BAAPI UserExperienceOnRegisterBegin( - __in BURN_USER_EXPERIENCE* pUserExperience - ) -{ - HRESULT hr = S_OK; - BA_ONREGISTERBEGIN_ARGS args = { }; - BA_ONREGISTERBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnRegisterBegin failed."); - - if (results.fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnRegisterComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus - ) -{ - HRESULT hr = S_OK; - BA_ONREGISTERCOMPLETE_ARGS args = { }; - BA_ONREGISTERCOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.hrStatus = hrStatus; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERCOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnRegisterComplete failed."); - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnRollbackMsiTransactionBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in LPCWSTR wzTransactionId - ) -{ - HRESULT hr = S_OK; - BA_ONROLLBACKMSITRANSACTIONBEGIN_ARGS args = { }; - BA_ONROLLBACKMSITRANSACTIONBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzTransactionId = wzTransactionId; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONROLLBACKMSITRANSACTIONBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnRollbackMsiTransactionBegin failed."); - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnRollbackMsiTransactionComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in LPCWSTR wzTransactionId, - __in HRESULT hrStatus - ) -{ - HRESULT hr = S_OK; - BA_ONROLLBACKMSITRANSACTIONCOMPLETE_ARGS args = { }; - BA_ONROLLBACKMSITRANSACTIONCOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.wzTransactionId = wzTransactionId; - args.hrStatus = hrStatus; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONROLLBACKMSITRANSACTIONCOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnRollbackMsiTransactionComplete failed."); - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnShutdown( - __in BURN_USER_EXPERIENCE* pUserExperience, - __inout BOOTSTRAPPER_SHUTDOWN_ACTION* pAction - ) -{ - HRESULT hr = S_OK; - BA_ONSHUTDOWN_ARGS args = { }; - BA_ONSHUTDOWN_RESULTS results = { }; - - args.cbSize = sizeof(args); - - results.cbSize = sizeof(results); - results.action = *pAction; - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSHUTDOWN, &args, &results); - ExitOnFailure(hr, "BA OnShutdown failed."); - - *pAction = results.action; - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnStartup( - __in BURN_USER_EXPERIENCE* pUserExperience - ) -{ - HRESULT hr = S_OK; - BA_ONSTARTUP_ARGS args = { }; - BA_ONSTARTUP_RESULTS results = { }; - - args.cbSize = sizeof(args); - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSTARTUP, &args, &results); - ExitOnFailure(hr, "BA OnStartup failed."); - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnSystemRestorePointBegin( - __in BURN_USER_EXPERIENCE* pUserExperience - ) -{ - HRESULT hr = S_OK; - BA_ONSYSTEMRESTOREPOINTBEGIN_ARGS args = { }; - BA_ONSYSTEMRESTOREPOINTBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMRESTOREPOINTBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnSystemRestorePointBegin failed."); - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnSystemRestorePointComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus - ) -{ - HRESULT hr = S_OK; - BA_ONSYSTEMRESTOREPOINTCOMPLETE_ARGS args = { }; - BA_ONSYSTEMRESTOREPOINTCOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.hrStatus = hrStatus; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMRESTOREPOINTCOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnSystemRestorePointComplete failed."); - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnSystemShutdown( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in DWORD dwEndSession, - __inout BOOL* pfCancel - ) -{ - HRESULT hr = S_OK; - BA_ONSYSTEMSHUTDOWN_ARGS args = { }; - BA_ONSYSTEMSHUTDOWN_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.dwEndSession = dwEndSession; - - results.cbSize = sizeof(results); - results.fCancel = *pfCancel; - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMSHUTDOWN, &args, &results); - ExitOnFailure(hr, "BA OnSystemShutdown failed."); - - *pfCancel = results.fCancel; - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnUnregisterBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __inout BOOL* pfKeepRegistration - ) -{ - HRESULT hr = S_OK; - BA_ONUNREGISTERBEGIN_ARGS args = { }; - BA_ONUNREGISTERBEGIN_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.fKeepRegistration = *pfKeepRegistration; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERBEGIN, &args, &results); - ExitOnFailure(hr, "BA OnUnregisterBegin failed."); - - if (!args.fKeepRegistration && results.fForceKeepRegistration) - { - *pfKeepRegistration = TRUE; - } - -LExit: - return hr; -} - -EXTERN_C BAAPI UserExperienceOnUnregisterComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus - ) -{ - HRESULT hr = S_OK; - BA_ONUNREGISTERCOMPLETE_ARGS args = { }; - BA_ONUNREGISTERCOMPLETE_RESULTS results = { }; - - args.cbSize = sizeof(args); - args.hrStatus = hrStatus; - - results.cbSize = sizeof(results); - - hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERCOMPLETE, &args, &results); - ExitOnFailure(hr, "BA OnUnregisterComplete failed."); - -LExit: - return hr; -} - -extern "C" int UserExperienceCheckExecuteResult( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BOOL fRollback, - __in DWORD dwAllowedResults, - __in int nResult - ) -{ - // Do not allow canceling while rolling back. - if (fRollback && (IDCANCEL == nResult || IDABORT == nResult)) - { - nResult = IDNOACTION; - } - else if (FAILED(pUserExperience->hrApplyError) && !fRollback) // if we failed cancel except not during rollback. - { - nResult = IDCANCEL; - } - - nResult = FilterResult(dwAllowedResults, nResult); - return nResult; -} - -extern "C" HRESULT UserExperienceInterpretExecuteResult( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BOOL fRollback, - __in DWORD dwAllowedResults, - __in int nResult - ) -{ - HRESULT hr = S_OK; - - // If we failed return that error unless this is rollback which should roll on. - if (FAILED(pUserExperience->hrApplyError) && !fRollback) - { - hr = pUserExperience->hrApplyError; - } - else - { - int nCheckedResult = UserExperienceCheckExecuteResult(pUserExperience, fRollback, dwAllowedResults, nResult); - hr = IDOK == nCheckedResult || IDNOACTION == nCheckedResult ? S_OK : IDCANCEL == nCheckedResult || IDABORT == nCheckedResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); - } - - return hr; -} - - -// internal functions - -static int FilterResult( - __in DWORD dwAllowedResults, - __in int nResult - ) -{ - DWORD dwFilteredAllowedResults = dwAllowedResults & MB_TYPEMASK; - if (IDNOACTION == nResult || IDERROR == nResult) // do nothing and errors pass through. - { - } - else - { - switch (dwFilteredAllowedResults) - { - case MB_OK: - nResult = IDOK; - break; - - case MB_OKCANCEL: - if (IDOK == nResult || IDYES == nResult) - { - nResult = IDOK; - } - else if (IDCANCEL == nResult || IDABORT == nResult || IDNO == nResult) - { - nResult = IDCANCEL; - } - else - { - nResult = IDNOACTION; - } - break; - - case MB_ABORTRETRYIGNORE: - if (IDCANCEL == nResult || IDABORT == nResult) - { - nResult = IDABORT; - } - else if (IDRETRY == nResult || IDTRYAGAIN == nResult) - { - nResult = IDRETRY; - } - else if (IDIGNORE == nResult) - { - nResult = IDIGNORE; - } - else - { - nResult = IDNOACTION; - } - break; - - case MB_YESNO: - if (IDOK == nResult || IDYES == nResult) - { - nResult = IDYES; - } - else if (IDCANCEL == nResult || IDABORT == nResult || IDNO == nResult) - { - nResult = IDNO; - } - else - { - nResult = IDNOACTION; - } - break; - - case MB_YESNOCANCEL: - if (IDOK == nResult || IDYES == nResult) - { - nResult = IDYES; - } - else if (IDNO == nResult) - { - nResult = IDNO; - } - else if (IDCANCEL == nResult || IDABORT == nResult) - { - nResult = IDCANCEL; - } - else - { - nResult = IDNOACTION; - } - break; - - case MB_RETRYCANCEL: - if (IDRETRY == nResult || IDTRYAGAIN == nResult) - { - nResult = IDRETRY; - } - else if (IDCANCEL == nResult || IDABORT == nResult) - { - nResult = IDABORT; - } - else - { - nResult = IDNOACTION; - } - break; - - case MB_CANCELTRYCONTINUE: - if (IDCANCEL == nResult || IDABORT == nResult) - { - nResult = IDABORT; - } - else if (IDRETRY == nResult || IDTRYAGAIN == nResult) - { - nResult = IDRETRY; - } - else if (IDCONTINUE == nResult || IDIGNORE == nResult) - { - nResult = IDCONTINUE; - } - else - { - nResult = IDNOACTION; - } - break; - - case WIU_MB_OKIGNORECANCELRETRY: // custom Windows Installer utility return code. - if (IDOK == nResult || IDYES == nResult) - { - nResult = IDOK; - } - else if (IDCONTINUE == nResult || IDIGNORE == nResult) - { - nResult = IDIGNORE; - } - else if (IDCANCEL == nResult || IDABORT == nResult) - { - nResult = IDCANCEL; - } - else if (IDRETRY == nResult || IDTRYAGAIN == nResult || IDNO == nResult) - { - nResult = IDRETRY; - } - else - { - nResult = IDNOACTION; - } - break; - - case MB_RETRYTRYAGAIN: // custom return code. - if (IDRETRY != nResult && IDTRYAGAIN != nResult) - { - nResult = IDNOACTION; - } - break; - - default: - AssertSz(FALSE, "Unknown allowed results."); - break; - } - } - - return nResult; -} - -// This filters the BA's responses to events during apply. -// If an apply thread failed, then return its error so this thread will bail out. -// During rollback, the BA can't cancel. -static HRESULT FilterExecuteResult( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus, - __in BOOL fRollback, - __in BOOL fCancel, - __in LPCWSTR sczEventName - ) -{ - HRESULT hr = hrStatus; - HRESULT hrApplyError = pUserExperience->hrApplyError; // make sure to use the same value for the whole method, since it can be changed in other threads. - - // If we failed return that error unless this is rollback which should roll on. - if (FAILED(hrApplyError) && !fRollback) - { - hr = hrApplyError; - } - else if (fRollback) - { - if (fCancel) - { - LogId(REPORT_STANDARD, MSG_APPLY_CANCEL_IGNORED_DURING_ROLLBACK, sczEventName); - } - // TODO: since cancel isn't allowed, should the BA's HRESULT be ignored as well? - // In the previous code, they could still alter rollback by returning IDERROR. - } - else - { - ExitOnFailure(hr, "BA %ls failed.", sczEventName); - - if (fCancel) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - } - -LExit: - return hr; -} - -static HRESULT SendBAMessage( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BOOTSTRAPPER_APPLICATION_MESSAGE message, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - - if (!pUserExperience->hUXModule) - { - ExitFunction(); - } - - hr = pUserExperience->pfnBAProc(message, pvArgs, pvResults, pUserExperience->pvBAProcContext); - if (hr == E_NOTIMPL) - { - hr = S_OK; - } - -LExit: - return hr; -} - -static HRESULT SendBAMessageFromInactiveEngine( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BOOTSTRAPPER_APPLICATION_MESSAGE message, - __in const LPVOID pvArgs, - __inout LPVOID pvResults - ) -{ - HRESULT hr = S_OK; - - if (!pUserExperience->hUXModule) - { - ExitFunction(); - } - - UserExperienceDeactivateEngine(pUserExperience); - - hr = SendBAMessage(pUserExperience, message, pvArgs, pvResults); - - UserExperienceActivateEngine(pUserExperience); - -LExit: - return hr; -} diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h deleted file mode 100644 index f2453dca..00000000 --- a/src/engine/userexperience.h +++ /dev/null @@ -1,545 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#define BAAPI HRESULT __stdcall - -#if defined(__cplusplus) -extern "C" { -#endif - - -// constants - -const DWORD MB_RETRYTRYAGAIN = 0xF; - - -// structs - -typedef struct _BOOTSTRAPPER_ENGINE_CONTEXT BOOTSTRAPPER_ENGINE_CONTEXT; - -typedef struct _BURN_USER_EXPERIENCE -{ - BOOL fSplashScreen; - BURN_PAYLOADS payloads; - - HMODULE hUXModule; - PFN_BOOTSTRAPPER_APPLICATION_PROC pfnBAProc; - LPVOID pvBAProcContext; - BOOL fDisableUnloading; - LPWSTR sczTempDirectory; - - CRITICAL_SECTION csEngineActive; // Changing the engine active state in the user experience must be - // syncronized through this critical section. - // Note: The engine must never do a UX callback while in this critical section. - - BOOL fEngineActive; // Indicates that the engine is currently active with one of the execution - // steps (detect, plan, apply), and cannot accept requests from the UX. - // This flag should be cleared by the engine prior to UX callbacks that - // allows altering of the engine state. - - HRESULT hrApplyError; // Tracks is an error occurs during apply that requires the cache or - // execute threads to bail. - - HWND hwndApply; // The window handle provided at the beginning of Apply(). Only valid - // during apply. - - HWND hwndDetect; // The window handle provided at the beginning of Detect(). Only valid - // during Detect. - - DWORD dwExitCode; // Exit code returned by the user experience for the engine overall. -} BURN_USER_EXPERIENCE; - -// functions - -HRESULT UserExperienceParseFromXml( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in IXMLDOMNode* pixnBundle - ); -void UserExperienceUninitialize( - __in BURN_USER_EXPERIENCE* pUserExperience - ); -HRESULT UserExperienceLoad( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BOOTSTRAPPER_ENGINE_CONTEXT* pEngineContext, - __in BOOTSTRAPPER_COMMAND* pCommand - ); -HRESULT UserExperienceUnload( - __in BURN_USER_EXPERIENCE* pUserExperience - ); -HRESULT UserExperienceEnsureWorkingFolder( - __in LPCWSTR wzBundleId, - __deref_out_z LPWSTR* psczUserExperienceWorkingFolder - ); -HRESULT UserExperienceRemove( - __in BURN_USER_EXPERIENCE* pUserExperience - ); -int UserExperienceSendError( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BOOTSTRAPPER_ERROR_TYPE errorType, - __in_z_opt LPCWSTR wzPackageId, - __in HRESULT hrCode, - __in_z_opt LPCWSTR wzError, - __in DWORD uiFlags, - __in int nRecommendation - ); -void UserExperienceActivateEngine( - __in BURN_USER_EXPERIENCE* pUserExperience - ); -void UserExperienceDeactivateEngine( - __in BURN_USER_EXPERIENCE* pUserExperience - ); -/******************************************************************** - UserExperienceEnsureEngineInactive - Verifies the engine is inactive. - The caller MUST enter the csActive critical section before calling. - -*********************************************************************/ -HRESULT UserExperienceEnsureEngineInactive( - __in BURN_USER_EXPERIENCE* pUserExperience - ); -void UserExperienceExecuteReset( - __in BURN_USER_EXPERIENCE* pUserExperience - ); -void UserExperienceExecutePhaseComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrResult - ); -BAAPI UserExperienceOnApplyBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in DWORD dwPhaseCount - ); -BAAPI UserExperienceOnApplyComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus, - __in BOOTSTRAPPER_APPLY_RESTART restart, - __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction - ); -BAAPI UserExperienceOnBeginMsiTransactionBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in LPCWSTR wzTransactionId - ); -BAAPI UserExperienceOnBeginMsiTransactionComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in LPCWSTR wzTransactionId, - __in HRESULT hrStatus - ); -BAAPI UserExperienceOnCacheAcquireBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in_z LPWSTR* pwzSource, - __in_z LPWSTR* pwzDownloadUrl, - __in_z_opt LPCWSTR wzPayloadContainerId, - __out BOOTSTRAPPER_CACHE_OPERATION* pCacheOperation - ); -BAAPI UserExperienceOnCacheAcquireComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in HRESULT hrStatus, - __inout BOOL* pfRetry - ); -BAAPI UserExperienceOnCacheAcquireProgress( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in DWORD64 dw64Progress, - __in DWORD64 dw64Total, - __in DWORD dwOverallPercentage - ); -BAAPI UserExperienceOnCacheAcquireResolving( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in_z LPWSTR* rgSearchPaths, - __in DWORD cSearchPaths, - __in BOOL fFoundLocal, - __in DWORD* pdwChosenSearchPath, - __in_z_opt LPWSTR* pwzDownloadUrl, - __in_z_opt LPCWSTR wzPayloadContainerId, - __inout BOOTSTRAPPER_CACHE_RESOLVE_OPERATION* pCacheOperation - ); -BAAPI UserExperienceOnCacheBegin( - __in BURN_USER_EXPERIENCE* pUserExperience - ); -BAAPI UserExperienceOnCacheComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus - ); -BAAPI UserExperienceOnCacheContainerOrPayloadVerifyBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId - ); -BAAPI UserExperienceOnCacheContainerOrPayloadVerifyComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in HRESULT hrStatus - ); -BAAPI UserExperienceOnCacheContainerOrPayloadVerifyProgress( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in DWORD64 dw64Progress, - __in DWORD64 dw64Total, - __in DWORD dwOverallPercentage - ); -BAAPI UserExperienceOnCachePackageBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in DWORD cCachePayloads, - __in DWORD64 dw64PackageCacheSize - ); -BAAPI UserExperienceOnCachePackageComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in HRESULT hrStatus, - __inout BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION* pAction - ); -BAAPI UserExperienceOnCachePayloadExtractBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzContainerId, - __in_z_opt LPCWSTR wzPayloadId - ); -BAAPI UserExperienceOnCachePayloadExtractComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in HRESULT hrStatus - ); -BAAPI UserExperienceOnCachePayloadExtractProgress( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in DWORD64 dw64Progress, - __in DWORD64 dw64Total, - __in DWORD dwOverallPercentage - ); -BAAPI UserExperienceOnCacheVerifyBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId - ); -BAAPI UserExperienceOnCacheVerifyComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in HRESULT hrStatus, - __inout BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION* pAction - ); -BAAPI UserExperienceOnCacheVerifyProgress( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in DWORD64 dw64Progress, - __in DWORD64 dw64Total, - __in DWORD dwOverallPercentage, - __in BOOTSTRAPPER_CACHE_VERIFY_STEP verifyStep - ); -BAAPI UserExperienceOnCommitMsiTransactionBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in LPCWSTR wzTransactionId - ); -BAAPI UserExperienceOnCommitMsiTransactionComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in LPCWSTR wzTransactionId, - __in HRESULT hrStatus - ); -BAAPI UserExperienceOnDetectBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BOOL fCached, - __in BOOL fInstalled, - __in DWORD cPackages - ); -BAAPI UserExperienceOnDetectComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus, - __in BOOL fEligibleForCleanup - ); -BAAPI UserExperienceOnDetectForwardCompatibleBundle( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzBundleId, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in_z LPCWSTR wzBundleTag, - __in BOOL fPerMachine, - __in VERUTIL_VERSION* pVersion, - __in BOOL fMissingFromCache - ); -BAAPI UserExperienceOnDetectMsiFeature( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in_z LPCWSTR wzFeatureId, - __in BOOTSTRAPPER_FEATURE_STATE state - ); -BAAPI UserExperienceOnDetectPackageBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId - ); -BAAPI UserExperienceOnDetectPackageComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in HRESULT hrStatus, - __in BOOTSTRAPPER_PACKAGE_STATE state, - __in BOOL fCached - ); -BAAPI UserExperienceOnDetectRelatedBundle( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzBundleId, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in_z LPCWSTR wzBundleTag, - __in BOOL fPerMachine, - __in VERUTIL_VERSION* pVersion, - __in BOOTSTRAPPER_RELATED_OPERATION operation, - __in BOOL fMissingFromCache - ); -BAAPI UserExperienceOnDetectRelatedMsiPackage( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in_z LPCWSTR wzUpgradeCode, - __in_z LPCWSTR wzProductCode, - __in BOOL fPerMachine, - __in VERUTIL_VERSION* pVersion, - __in BOOTSTRAPPER_RELATED_OPERATION operation - ); -BAAPI UserExperienceOnDetectPatchTarget( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in_z LPCWSTR wzProductCode, - __in BOOTSTRAPPER_PACKAGE_STATE patchState - ); -BAAPI UserExperienceOnDetectUpdate( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z_opt LPCWSTR wzUpdateLocation, - __in DWORD64 dw64Size, - __in VERUTIL_VERSION* pVersion, - __in_z_opt LPCWSTR wzTitle, - __in_z_opt LPCWSTR wzSummary, - __in_z_opt LPCWSTR wzContentType, - __in_z_opt LPCWSTR wzContent, - __inout BOOL* pfStopProcessingUpdates - ); -BAAPI UserExperienceOnDetectUpdateBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzUpdateLocation, - __inout BOOL* pfSkip - ); -BAAPI UserExperienceOnDetectUpdateComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus, - __inout BOOL* pfIgnoreError - ); -BAAPI UserExperienceOnElevateBegin( - __in BURN_USER_EXPERIENCE* pUserExperience - ); -BAAPI UserExperienceOnElevateComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus - ); -BAAPI UserExperienceOnError( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BOOTSTRAPPER_ERROR_TYPE errorType, - __in_z_opt LPCWSTR wzPackageId, - __in DWORD dwCode, - __in_z_opt LPCWSTR wzError, - __in DWORD dwUIHint, - __in DWORD cData, - __in_ecount_z_opt(cData) LPCWSTR* rgwzData, - __inout int* pnResult - ); -BAAPI UserExperienceOnExecuteBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in DWORD cExecutingPackages - ); -BAAPI UserExperienceOnExecuteComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus - ); -BAAPI UserExperienceOnExecuteFilesInUse( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in DWORD cFiles, - __in_ecount_z_opt(cFiles) LPCWSTR* rgwzFiles, - __inout int* pnResult - ); -BAAPI UserExperienceOnExecuteMsiMessage( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in INSTALLMESSAGE messageType, - __in DWORD dwUIHint, - __in_z LPCWSTR wzMessage, - __in DWORD cData, - __in_ecount_z_opt(cData) LPCWSTR* rgwzData, - __inout int* pnResult - ); -BAAPI UserExperienceOnExecutePackageBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in BOOL fExecute, - __in BOOTSTRAPPER_ACTION_STATE action, - __in INSTALLUILEVEL uiLevel, - __in BOOL fDisableExternalUiHandler - ); -BAAPI UserExperienceOnExecutePackageComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in HRESULT hrStatus, - __in BOOTSTRAPPER_APPLY_RESTART restart, - __inout BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION* pAction - ); -BAAPI UserExperienceOnExecutePatchTarget( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in_z LPCWSTR wzTargetProductCode - ); -BAAPI UserExperienceOnExecuteProgress( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in DWORD dwProgressPercentage, - __in DWORD dwOverallPercentage, - __out int* pnResult - ); -BAAPI UserExperienceOnLaunchApprovedExeBegin( - __in BURN_USER_EXPERIENCE* pUserExperience - ); -BAAPI UserExperienceOnLaunchApprovedExeComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus, - __in DWORD dwProcessId - ); -BAAPI UserExperienceOnPauseAUBegin( - __in BURN_USER_EXPERIENCE* pUserExperience - ); -BAAPI UserExperienceOnPauseAUComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus - ); -BAAPI UserExperienceOnPlanBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in DWORD cPackages - ); -BAAPI UserExperienceOnPlanComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus - ); -BAAPI UserExperienceOnPlanForwardCompatibleBundle( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzBundleId, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in_z LPCWSTR wzBundleTag, - __in BOOL fPerMachine, - __in VERUTIL_VERSION* pVersion, - __inout BOOL* pfIgnoreBundle - ); -BAAPI UserExperienceOnPlanMsiFeature( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in_z LPCWSTR wzFeatureId, - __inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState - ); -BAAPI UserExperienceOnPlanMsiPackage( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in BOOL fExecute, - __in BOOTSTRAPPER_ACTION_STATE action, - __inout BURN_MSI_PROPERTY* pActionMsiProperty, - __inout INSTALLUILEVEL* pUiLevel, - __inout BOOL* pfDisableExternalUiHandler - ); -BAAPI UserExperienceOnPlannedPackage( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in BOOTSTRAPPER_ACTION_STATE execute, - __in BOOTSTRAPPER_ACTION_STATE rollback, - __in BOOL fPlannedCache, - __in BOOL fPlannedUncache - ); -BAAPI UserExperienceOnPlanPackageBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in BOOTSTRAPPER_PACKAGE_STATE state, - __in BOOL fCached, - __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, - __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState, - __inout BOOTSTRAPPER_CACHE_TYPE* pRequestedCacheType - ); -BAAPI UserExperienceOnPlanPackageComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in HRESULT hrStatus, - __in BOOTSTRAPPER_REQUEST_STATE requested - ); -BAAPI UserExperienceOnPlanRelatedBundle( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzBundleId, - __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState - ); -BAAPI UserExperienceOnPlanPatchTarget( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in_z LPCWSTR wzPackageId, - __in_z LPCWSTR wzProductCode, - __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState - ); -BAAPI UserExperienceOnProgress( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BOOL fRollback, - __in DWORD dwProgressPercentage, - __in DWORD dwOverallPercentage - ); -BAAPI UserExperienceOnRegisterBegin( - __in BURN_USER_EXPERIENCE* pUserExperience - ); -BAAPI UserExperienceOnRegisterComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus - ); -BAAPI UserExperienceOnRollbackMsiTransactionBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in LPCWSTR wzTransactionId - ); -BAAPI UserExperienceOnRollbackMsiTransactionComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in LPCWSTR wzTransactionId, - __in HRESULT hrStatus - ); -BAAPI UserExperienceOnShutdown( - __in BURN_USER_EXPERIENCE* pUserExperience, - __inout BOOTSTRAPPER_SHUTDOWN_ACTION* pAction - ); -BAAPI UserExperienceOnStartup( - __in BURN_USER_EXPERIENCE* pUserExperience - ); -BAAPI UserExperienceOnSystemRestorePointBegin( - __in BURN_USER_EXPERIENCE* pUserExperience - ); -BAAPI UserExperienceOnSystemRestorePointComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus - ); -BAAPI UserExperienceOnSystemShutdown( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in DWORD dwEndSession, - __inout BOOL* pfCancel - ); -BAAPI UserExperienceOnUnregisterBegin( - __in BURN_USER_EXPERIENCE* pUserExperience, - __inout BOOL* pfKeepRegistration - ); -BAAPI UserExperienceOnUnregisterComplete( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in HRESULT hrStatus - ); -int UserExperienceCheckExecuteResult( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BOOL fRollback, - __in DWORD dwAllowedResults, - __in int nResult - ); -HRESULT UserExperienceInterpretExecuteResult( - __in BURN_USER_EXPERIENCE* pUserExperience, - __in BOOL fRollback, - __in DWORD dwAllowedResults, - __in int nResult - ); -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp deleted file mode 100644 index 6f818ff3..00000000 --- a/src/engine/variable.cpp +++ /dev/null @@ -1,2323 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -// structs - -typedef const struct _BUILT_IN_VARIABLE_DECLARATION -{ - LPCWSTR wzVariable; - PFN_INITIALIZEVARIABLE pfnInitialize; - DWORD_PTR dwpInitializeData; - BOOL fPersist; - BOOL fOverridable; -} BUILT_IN_VARIABLE_DECLARATION; - - -// constants - -const DWORD GROW_VARIABLE_ARRAY = 3; - -enum OS_INFO_VARIABLE -{ - OS_INFO_VARIABLE_NONE, - OS_INFO_VARIABLE_VersionNT, - OS_INFO_VARIABLE_VersionNT64, - OS_INFO_VARIABLE_ServicePackLevel, - OS_INFO_VARIABLE_NTProductType, - OS_INFO_VARIABLE_NTSuiteBackOffice, - OS_INFO_VARIABLE_NTSuiteDataCenter, - OS_INFO_VARIABLE_NTSuiteEnterprise, - OS_INFO_VARIABLE_NTSuitePersonal, - OS_INFO_VARIABLE_NTSuiteSmallBusiness, - OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted, - OS_INFO_VARIABLE_NTSuiteWebServer, - OS_INFO_VARIABLE_CompatibilityMode, - OS_INFO_VARIABLE_TerminalServer, - OS_INFO_VARIABLE_ProcessorArchitecture, - OS_INFO_VARIABLE_WindowsBuildNumber, -}; - -enum SET_VARIABLE -{ - SET_VARIABLE_NOT_BUILTIN, - SET_VARIABLE_OVERRIDE_BUILTIN, - SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS, - SET_VARIABLE_ANY, -}; - -// internal function declarations - -static HRESULT FormatString( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzIn, - __out_z_opt LPWSTR* psczOut, - __out_opt SIZE_T* pcchOut, - __in BOOL fObfuscateHiddenVariables, - __out BOOL* pfContainsHiddenVariable - ); -static HRESULT GetFormatted( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __out_z LPWSTR* psczValue, - __out BOOL* pfContainsHiddenVariable - ); -static HRESULT AddBuiltInVariable( - __in BURN_VARIABLES* pVariables, - __in LPCWSTR wzVariable, - __in PFN_INITIALIZEVARIABLE pfnInitialize, - __in DWORD_PTR dwpInitializeData, - __in BOOL fPersist, - __in BOOL fOverridable - ); -static HRESULT GetVariable( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __out BURN_VARIABLE** ppVariable - ); -static HRESULT FindVariableIndexByName( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __out DWORD* piVariable - ); -static HRESULT InsertVariable( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __in DWORD iPosition - ); -static HRESULT SetVariableValue( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __in BURN_VARIANT* pVariant, - __in SET_VARIABLE setBuiltin, - __in BOOL fLog - ); -static HRESULT InitializeVariableVersionNT( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -static HRESULT InitializeVariableOsInfo( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -static HRESULT InitializeVariableSystemInfo( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -static HRESULT InitializeVariableComputerName( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -static HRESULT InitializeVariableVersionMsi( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -static HRESULT InitializeVariableCsidlFolder( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -static HRESULT InitializeVariableWindowsVolumeFolder( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -static HRESULT InitializeVariableTempFolder( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -static HRESULT InitializeVariableSystemFolder( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -static HRESULT InitializeVariablePrivileged( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -static HRESULT InitializeSystemLanguageID( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -static HRESULT InitializeUserUILanguageID( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -static HRESULT InitializeUserLanguageID( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -static HRESULT InitializeVariableString( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -static HRESULT InitializeVariableNumeric( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -static HRESULT InitializeVariable6432Folder( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -static HRESULT InitializeVariableDate( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -static HRESULT InitializeVariableInstallerName( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -static HRESULT InitializeVariableInstallerVersion( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -static HRESULT InitializeVariableVersion( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -static HRESULT InitializeVariableLogonUser( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -static HRESULT Get64bitFolderFromRegistry( - __in int nFolder, - __deref_out_z LPWSTR* psczPath - ); - -#if !defined(_WIN64) -static HRESULT InitializeVariableRegistryFolder( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); -#endif - - -// function definitions - -extern "C" HRESULT VariableInitialize( - __in BURN_VARIABLES* pVariables - ) -{ - HRESULT hr = S_OK; - - ::InitializeCriticalSection(&pVariables->csAccess); - - const BUILT_IN_VARIABLE_DECLARATION vrgBuiltInVariables[] = { - {L"AdminToolsFolder", InitializeVariableCsidlFolder, CSIDL_ADMINTOOLS}, - {L"AppDataFolder", InitializeVariableCsidlFolder, CSIDL_APPDATA}, - {L"CommonAppDataFolder", InitializeVariableCsidlFolder, CSIDL_COMMON_APPDATA}, -#if defined(_WIN64) - {L"CommonFiles64Folder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMON}, - {L"CommonFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMONX86}, -#else - {L"CommonFiles64Folder", InitializeVariableRegistryFolder, CSIDL_PROGRAM_FILES_COMMON}, - {L"CommonFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMON}, -#endif - {L"CommonFiles6432Folder", InitializeVariable6432Folder, CSIDL_PROGRAM_FILES_COMMON}, - {L"CompatibilityMode", InitializeVariableOsInfo, OS_INFO_VARIABLE_CompatibilityMode}, - {VARIABLE_DATE, InitializeVariableDate, 0}, - {L"ComputerName", InitializeVariableComputerName, 0}, - {L"DesktopFolder", InitializeVariableCsidlFolder, CSIDL_DESKTOP}, - {L"FavoritesFolder", InitializeVariableCsidlFolder, CSIDL_FAVORITES}, - {L"FontsFolder", InitializeVariableCsidlFolder, CSIDL_FONTS}, - {VARIABLE_INSTALLERNAME, InitializeVariableInstallerName, 0}, - {VARIABLE_INSTALLERVERSION, InitializeVariableInstallerVersion, 0}, - {L"LocalAppDataFolder", InitializeVariableCsidlFolder, CSIDL_LOCAL_APPDATA}, - {VARIABLE_LOGONUSER, InitializeVariableLogonUser, 0}, - {L"MyPicturesFolder", InitializeVariableCsidlFolder, CSIDL_MYPICTURES}, - {L"NTProductType", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTProductType}, - {L"NTSuiteBackOffice", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteBackOffice}, - {L"NTSuiteDataCenter", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteDataCenter}, - {L"NTSuiteEnterprise", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteEnterprise}, - {L"NTSuitePersonal", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuitePersonal}, - {L"NTSuiteSmallBusiness", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteSmallBusiness}, - {L"NTSuiteSmallBusinessRestricted", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted}, - {L"NTSuiteWebServer", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteWebServer}, - {L"PersonalFolder", InitializeVariableCsidlFolder, CSIDL_PERSONAL}, - {L"Privileged", InitializeVariablePrivileged, 0}, - {L"ProcessorArchitecture", InitializeVariableSystemInfo, OS_INFO_VARIABLE_ProcessorArchitecture}, -#if defined(_WIN64) - {L"ProgramFiles64Folder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES}, - {L"ProgramFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILESX86}, -#else - {L"ProgramFiles64Folder", InitializeVariableRegistryFolder, CSIDL_PROGRAM_FILES}, - {L"ProgramFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES}, -#endif - {L"ProgramFiles6432Folder", InitializeVariable6432Folder, CSIDL_PROGRAM_FILES}, - {L"ProgramMenuFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAMS}, - {L"SendToFolder", InitializeVariableCsidlFolder, CSIDL_SENDTO}, - {L"ServicePackLevel", InitializeVariableVersionNT, OS_INFO_VARIABLE_ServicePackLevel}, - {L"StartMenuFolder", InitializeVariableCsidlFolder, CSIDL_STARTMENU}, - {L"StartupFolder", InitializeVariableCsidlFolder, CSIDL_STARTUP}, - {L"SystemFolder", InitializeVariableSystemFolder, FALSE}, - {L"System64Folder", InitializeVariableSystemFolder, TRUE}, - {L"SystemLanguageID", InitializeSystemLanguageID, 0}, - {L"TempFolder", InitializeVariableTempFolder, 0}, - {L"TemplateFolder", InitializeVariableCsidlFolder, CSIDL_TEMPLATES}, - {L"TerminalServer", InitializeVariableOsInfo, OS_INFO_VARIABLE_TerminalServer}, - {L"UserUILanguageID", InitializeUserUILanguageID, 0}, - {L"UserLanguageID", InitializeUserLanguageID, 0}, - {L"VersionMsi", InitializeVariableVersionMsi, 0}, - {L"VersionNT", InitializeVariableVersionNT, OS_INFO_VARIABLE_VersionNT}, - {L"VersionNT64", InitializeVariableVersionNT, OS_INFO_VARIABLE_VersionNT64}, - {L"WindowsBuildNumber", InitializeVariableVersionNT, OS_INFO_VARIABLE_WindowsBuildNumber}, - {L"WindowsFolder", InitializeVariableCsidlFolder, CSIDL_WINDOWS}, - {L"WindowsVolume", InitializeVariableWindowsVolumeFolder, 0}, - {BURN_BUNDLE_ACTION, InitializeVariableNumeric, 0, FALSE, TRUE}, - {BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, InitializeVariableString, NULL, FALSE, TRUE}, - {BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, InitializeVariableString, NULL, FALSE, TRUE}, - {BURN_BUNDLE_FORCED_RESTART_PACKAGE, InitializeVariableString, NULL, TRUE, TRUE}, - {BURN_BUNDLE_INSTALLED, InitializeVariableNumeric, 0, FALSE, TRUE}, - {BURN_BUNDLE_ELEVATED, InitializeVariableNumeric, 0, FALSE, TRUE}, - {BURN_BUNDLE_ACTIVE_PARENT, InitializeVariableString, NULL, FALSE, TRUE}, - {BURN_BUNDLE_PROVIDER_KEY, InitializeVariableString, (DWORD_PTR)L"", FALSE, TRUE}, - {BURN_BUNDLE_SOURCE_PROCESS_PATH, InitializeVariableString, NULL, FALSE, TRUE}, - {BURN_BUNDLE_SOURCE_PROCESS_FOLDER, InitializeVariableString, NULL, FALSE, TRUE}, - {BURN_BUNDLE_TAG, InitializeVariableString, (DWORD_PTR)L"", FALSE, TRUE}, - {BURN_BUNDLE_UILEVEL, InitializeVariableNumeric, 0, FALSE, TRUE}, - {BURN_BUNDLE_VERSION, InitializeVariableVersion, (DWORD_PTR)L"0", FALSE, TRUE}, - }; - - for (DWORD i = 0; i < countof(vrgBuiltInVariables); ++i) - { - BUILT_IN_VARIABLE_DECLARATION* pBuiltInVariable = &vrgBuiltInVariables[i]; - - hr = AddBuiltInVariable(pVariables, pBuiltInVariable->wzVariable, pBuiltInVariable->pfnInitialize, pBuiltInVariable->dwpInitializeData, pBuiltInVariable->fPersist, pBuiltInVariable->fOverridable); - ExitOnFailure(hr, "Failed to add built-in variable: %ls.", pBuiltInVariable->wzVariable); - } - -LExit: - return hr; -} - -extern "C" HRESULT VariablesParseFromXml( - __in BURN_VARIABLES* pVariables, - __in IXMLDOMNode* pixnBundle - ) -{ - HRESULT hr = S_OK; - IXMLDOMNodeList* pixnNodes = NULL; - IXMLDOMNode* pixnNode = NULL; - DWORD cNodes = 0; - LPWSTR sczId = NULL; - LPWSTR scz = NULL; - BURN_VARIANT value = { }; - BURN_VARIANT_TYPE valueType = BURN_VARIANT_TYPE_NONE; - BOOL fHidden = FALSE; - BOOL fPersisted = FALSE; - DWORD iVariable = 0; - - ::EnterCriticalSection(&pVariables->csAccess); - - // select variable nodes - hr = XmlSelectNodes(pixnBundle, L"Variable", &pixnNodes); - ExitOnFailure(hr, "Failed to select variable nodes."); - - // get variable node count - hr = pixnNodes->get_length((long*)&cNodes); - ExitOnFailure(hr, "Failed to get variable node count."); - - // parse variable elements - for (DWORD i = 0; i < cNodes; ++i) - { - hr = XmlNextElement(pixnNodes, &pixnNode, NULL); - ExitOnFailure(hr, "Failed to get next node."); - - // @Id - hr = XmlGetAttributeEx(pixnNode, L"Id", &sczId); - ExitOnFailure(hr, "Failed to get @Id."); - - // @Hidden - hr = XmlGetYesNoAttribute(pixnNode, L"Hidden", &fHidden); - ExitOnFailure(hr, "Failed to get @Hidden."); - - // @Persisted - hr = XmlGetYesNoAttribute(pixnNode, L"Persisted", &fPersisted); - ExitOnFailure(hr, "Failed to get @Persisted."); - - // @Value - hr = XmlGetAttributeEx(pixnNode, L"Value", &scz); - if (E_NOTFOUND != hr) - { - ExitOnFailure(hr, "Failed to get @Value."); - - hr = BVariantSetString(&value, scz, 0, FALSE); - ExitOnFailure(hr, "Failed to set variant value."); - - // @Type - hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); - ExitOnFailure(hr, "Failed to get @Type."); - - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"formatted", -1)) - { - if (!fHidden) - { - LogStringLine(REPORT_STANDARD, "Initializing formatted variable '%ls' to value '%ls'", sczId, value.sczValue); - } - valueType = BURN_VARIANT_TYPE_FORMATTED; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) - { - if (!fHidden) - { - LogStringLine(REPORT_STANDARD, "Initializing numeric variable '%ls' to value '%ls'", sczId, value.sczValue); - } - valueType = BURN_VARIANT_TYPE_NUMERIC; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1)) - { - if (!fHidden) - { - LogStringLine(REPORT_STANDARD, "Initializing string variable '%ls' to value '%ls'", sczId, value.sczValue); - } - valueType = BURN_VARIANT_TYPE_STRING; - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) - { - if (!fHidden) - { - LogStringLine(REPORT_STANDARD, "Initializing version variable '%ls' to value '%ls'", sczId, value.sczValue); - } - valueType = BURN_VARIANT_TYPE_VERSION; - } - else - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); - } - } - else - { - valueType = BURN_VARIANT_TYPE_NONE; - } - - if (fHidden) - { - LogStringLine(REPORT_STANDARD, "Initializing hidden variable '%ls'", sczId); - } - - // change value variant to correct type - hr = BVariantChangeType(&value, valueType); - ExitOnFailure(hr, "Failed to change variant type."); - - if (BURN_VARIANT_TYPE_VERSION == valueType && value.pValue->fInvalid) - { - LogId(REPORT_WARNING, MSG_VARIABLE_INVALID_VERSION, sczId); - } - - // find existing variable - hr = FindVariableIndexByName(pVariables, sczId, &iVariable); - ExitOnFailure(hr, "Failed to find variable value '%ls'.", sczId); - - // insert element if not found - if (S_FALSE == hr) - { - hr = InsertVariable(pVariables, sczId, iVariable); - ExitOnFailure(hr, "Failed to insert variable '%ls'.", sczId); - } - else if (BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariables->rgVariables[iVariable].internalType) - { - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Attempt to set built-in variable value: %ls", sczId); - } - pVariables->rgVariables[iVariable].fHidden = fHidden; - pVariables->rgVariables[iVariable].fPersisted = fPersisted; - - // update variable value - hr = BVariantSetValue(&pVariables->rgVariables[iVariable].Value, &value); - ExitOnFailure(hr, "Failed to set value of variable: %ls", sczId); - - // prepare next iteration - ReleaseNullObject(pixnNode); - BVariantUninitialize(&value); - ReleaseNullStrSecure(scz); - } - -LExit: - ::LeaveCriticalSection(&pVariables->csAccess); - - ReleaseObject(pixnNodes); - ReleaseObject(pixnNode); - ReleaseStr(scz); - ReleaseStr(sczId); - BVariantUninitialize(&value); - - return hr; -} - -extern "C" void VariablesUninitialize( - __in BURN_VARIABLES* pVariables - ) -{ - ::DeleteCriticalSection(&pVariables->csAccess); - - if (pVariables->rgVariables) - { - for (DWORD i = 0; i < pVariables->cVariables; ++i) - { - BURN_VARIABLE* pVariable = &pVariables->rgVariables[i]; - if (pVariable) - { - ReleaseStr(pVariable->sczName); - BVariantUninitialize(&pVariable->Value); - } - } - MemFree(pVariables->rgVariables); - } -} - -extern "C" void VariablesDump( - __in BURN_VARIABLES* pVariables - ) -{ - HRESULT hr = S_OK; - LPWSTR sczValue = NULL; - - for (DWORD i = 0; i < pVariables->cVariables; ++i) - { - BURN_VARIABLE* pVariable = &pVariables->rgVariables[i]; - if (pVariable && BURN_VARIANT_TYPE_NONE != pVariable->Value.Type) - { - hr = StrAllocFormatted(&sczValue, L"%ls = [%ls]", pVariable->sczName, pVariable->sczName); - if (SUCCEEDED(hr)) - { - if (pVariable->fHidden) - { - hr = VariableFormatStringObfuscated(pVariables, sczValue, &sczValue, NULL); - } - else - { - hr = VariableFormatString(pVariables, sczValue, &sczValue, NULL); - } - } - - if (FAILED(hr)) - { - // already logged; best-effort to dump the rest on our way out the door - continue; - } - - LogId(REPORT_VERBOSE, MSG_VARIABLE_DUMP, sczValue); - - ReleaseNullStrSecure(sczValue); - } - } - - StrSecureZeroFreeString(sczValue); -} - -extern "C" HRESULT VariableGetNumeric( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __out LONGLONG* pllValue - ) -{ - HRESULT hr = S_OK; - BURN_VARIABLE* pVariable = NULL; - - ::EnterCriticalSection(&pVariables->csAccess); - - hr = GetVariable(pVariables, wzVariable, &pVariable); - if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) - { - ExitFunction1(hr = E_NOTFOUND); - } - else if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); - - hr = BVariantGetNumeric(&pVariable->Value, pllValue); - ExitOnFailure(hr, "Failed to get value as numeric for variable: %ls", wzVariable); - -LExit: - ::LeaveCriticalSection(&pVariables->csAccess); - - return hr; -} - -extern "C" HRESULT VariableGetString( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __out_z LPWSTR* psczValue - ) -{ - HRESULT hr = S_OK; - BURN_VARIABLE* pVariable = NULL; - - ::EnterCriticalSection(&pVariables->csAccess); - - hr = GetVariable(pVariables, wzVariable, &pVariable); - if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) - { - ExitFunction1(hr = E_NOTFOUND); - } - else if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); - - hr = BVariantGetString(&pVariable->Value, psczValue); - ExitOnFailure(hr, "Failed to get value as string for variable: %ls", wzVariable); - -LExit: - ::LeaveCriticalSection(&pVariables->csAccess); - - return hr; -} - -extern "C" HRESULT VariableGetVersion( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __in VERUTIL_VERSION** ppValue - ) -{ - HRESULT hr = S_OK; - BURN_VARIABLE* pVariable = NULL; - - ::EnterCriticalSection(&pVariables->csAccess); - - hr = GetVariable(pVariables, wzVariable, &pVariable); - if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) - { - ExitFunction1(hr = E_NOTFOUND); - } - else if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); - - hr = BVariantGetVersionHidden(&pVariable->Value, pVariable->fHidden, ppValue); - ExitOnFailure(hr, "Failed to get value as version for variable: %ls", wzVariable); - -LExit: - ::LeaveCriticalSection(&pVariables->csAccess); - - return hr; -} - -extern "C" HRESULT VariableGetVariant( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __in BURN_VARIANT* pValue - ) -{ - HRESULT hr = S_OK; - BURN_VARIABLE* pVariable = NULL; - - ::EnterCriticalSection(&pVariables->csAccess); - - hr = GetVariable(pVariables, wzVariable, &pVariable); - if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); - - hr = BVariantCopy(&pVariable->Value, pValue); - ExitOnFailure(hr, "Failed to copy value of variable: %ls", wzVariable); - -LExit: - ::LeaveCriticalSection(&pVariables->csAccess); - - return hr; -} - -extern "C" HRESULT VariableGetFormatted( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __out_z LPWSTR* psczValue, - __out BOOL* pfContainsHiddenVariable - ) -{ - HRESULT hr = S_OK; - - if (pfContainsHiddenVariable) - { - *pfContainsHiddenVariable = FALSE; - } - - hr = GetFormatted(pVariables, wzVariable, psczValue, pfContainsHiddenVariable); - - return hr; -} - -extern "C" HRESULT VariableSetNumeric( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __in LONGLONG llValue, - __in BOOL fOverwriteBuiltIn - ) -{ - BURN_VARIANT variant = { }; - - variant.llValue = llValue; - variant.Type = BURN_VARIANT_TYPE_NUMERIC; - - return SetVariableValue(pVariables, wzVariable, &variant, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); -} - -extern "C" HRESULT VariableSetString( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __in_z_opt LPCWSTR wzValue, - __in BOOL fOverwriteBuiltIn, - __in BOOL fFormatted - ) -{ - BURN_VARIANT variant = { }; - - variant.sczValue = (LPWSTR)wzValue; - variant.Type = fFormatted ? BURN_VARIANT_TYPE_FORMATTED : BURN_VARIANT_TYPE_STRING; - - return SetVariableValue(pVariables, wzVariable, &variant, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); -} - -extern "C" HRESULT VariableSetVersion( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __in VERUTIL_VERSION* pValue, - __in BOOL fOverwriteBuiltIn - ) -{ - BURN_VARIANT variant = { }; - - variant.pValue = pValue; - variant.Type = BURN_VARIANT_TYPE_VERSION; - - return SetVariableValue(pVariables, wzVariable, &variant, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); -} - -extern "C" HRESULT VariableSetVariant( - __in BURN_VARIABLES * pVariables, - __in_z LPCWSTR wzVariable, - __in BURN_VARIANT * pVariant - ) -{ - return SetVariableValue(pVariables, wzVariable, pVariant, SET_VARIABLE_NOT_BUILTIN, TRUE); -} - -extern "C" HRESULT VariableFormatString( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzIn, - __out_z_opt LPWSTR* psczOut, - __out_opt SIZE_T* pcchOut - ) -{ - return FormatString(pVariables, wzIn, psczOut, pcchOut, FALSE, NULL); -} - -extern "C" HRESULT VariableFormatStringObfuscated( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzIn, - __out_z_opt LPWSTR* psczOut, - __out_opt SIZE_T* pcchOut - ) -{ - return FormatString(pVariables, wzIn, psczOut, pcchOut, TRUE, NULL); -} - -extern "C" HRESULT VariableEscapeString( - __in_z LPCWSTR wzIn, - __out_z LPWSTR* psczOut - ) -{ - HRESULT hr = S_OK; - LPCWSTR wzRead = NULL; - LPWSTR pwzEscaped = NULL; - LPWSTR pwz = NULL; - SIZE_T i = 0; - - // allocate buffer for escaped string - hr = StrAlloc(&pwzEscaped, lstrlenW(wzIn) + 1); - ExitOnFailure(hr, "Failed to allocate buffer for escaped string."); - - // read through string and move characters, inserting escapes as needed - wzRead = wzIn; - for (;;) - { - // find next character needing escaping - i = wcscspn(wzRead, L"[]{}"); - - // copy skipped characters - if (0 < i) - { - hr = StrAllocConcat(&pwzEscaped, wzRead, i); - ExitOnFailure(hr, "Failed to append characters."); - } - - if (L'\0' == wzRead[i]) - { - break; // end reached - } - - // escape character - hr = StrAllocFormatted(&pwz, L"[\\%c]", wzRead[i]); - ExitOnFailure(hr, "Failed to format escape sequence."); - - hr = StrAllocConcat(&pwzEscaped, pwz, 0); - ExitOnFailure(hr, "Failed to append escape sequence."); - - // update read pointer - wzRead += i + 1; - } - - // return value - hr = StrAllocString(psczOut, pwzEscaped, 0); - ExitOnFailure(hr, "Failed to copy string."); - -LExit: - ReleaseStr(pwzEscaped); - ReleaseStr(pwz); - return hr; -} - -extern "C" HRESULT VariableSerialize( - __in BURN_VARIABLES* pVariables, - __in BOOL fPersisting, - __inout BYTE** ppbBuffer, - __inout SIZE_T* piBuffer - ) -{ - HRESULT hr = S_OK; - BOOL fIncluded = FALSE; - LONGLONG ll = 0; - LPWSTR scz = NULL; - - ::EnterCriticalSection(&pVariables->csAccess); - - // Write variable count. - hr = BuffWriteNumber(ppbBuffer, piBuffer, pVariables->cVariables); - ExitOnFailure(hr, "Failed to write variable count."); - - // Write variables. - for (DWORD i = 0; i < pVariables->cVariables; ++i) - { - BURN_VARIABLE* pVariable = &pVariables->rgVariables[i]; - - // If we aren't persisting, include only variables that aren't rejected by the elevated process. - // If we are persisting, include only variables that should be persisted. - fIncluded = (!fPersisting && BURN_VARIABLE_INTERNAL_TYPE_BUILTIN != pVariable->internalType) || - (fPersisting && pVariable->fPersisted); - - // Write included flag. - hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)fIncluded); - ExitOnFailure(hr, "Failed to write included flag."); - - if (!fIncluded) - { - continue; - } - - // Write variable name. - hr = BuffWriteString(ppbBuffer, piBuffer, pVariable->sczName); - ExitOnFailure(hr, "Failed to write variable name."); - - // Write variable value type. - hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)pVariable->Value.Type); - ExitOnFailure(hr, "Failed to write variable value type."); - - // Write variable value. - switch (pVariable->Value.Type) - { - case BURN_VARIANT_TYPE_NONE: - break; - case BURN_VARIANT_TYPE_NUMERIC: - hr = BVariantGetNumeric(&pVariable->Value, &ll); - ExitOnFailure(hr, "Failed to get numeric."); - - hr = BuffWriteNumber64(ppbBuffer, piBuffer, static_cast(ll)); - ExitOnFailure(hr, "Failed to write variable value as number."); - - SecureZeroMemory(&ll, sizeof(ll)); - break; - case BURN_VARIANT_TYPE_VERSION: __fallthrough; - case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; - case BURN_VARIANT_TYPE_STRING: - hr = BVariantGetString(&pVariable->Value, &scz); - ExitOnFailure(hr, "Failed to get string."); - - hr = BuffWriteString(ppbBuffer, piBuffer, scz); - ExitOnFailure(hr, "Failed to write variable value as string."); - - ReleaseNullStrSecure(scz); - break; - default: - hr = E_INVALIDARG; - ExitOnFailure(hr, "Unsupported variable type."); - } - } - -LExit: - ::LeaveCriticalSection(&pVariables->csAccess); - SecureZeroMemory(&ll, sizeof(ll)); - StrSecureZeroFreeString(scz); - - return hr; -} - -extern "C" HRESULT VariableDeserialize( - __in BURN_VARIABLES* pVariables, - __in BOOL fWasPersisted, - __in_bcount(cbBuffer) BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer - ) -{ - HRESULT hr = S_OK; - DWORD cVariables = 0; - LPWSTR sczName = NULL; - BOOL fIncluded = FALSE; - BURN_VARIANT value = { }; - LPWSTR scz = NULL; - DWORD64 qw = 0; - VERUTIL_VERSION* pVersion = NULL; - - ::EnterCriticalSection(&pVariables->csAccess); - - // Read variable count. - hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, &cVariables); - ExitOnFailure(hr, "Failed to read variable count."); - - // Read variables. - for (DWORD i = 0; i < cVariables; ++i) - { - // Read variable included flag. - hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&fIncluded); - ExitOnFailure(hr, "Failed to read variable included flag."); - - if (!fIncluded) - { - continue; // if variable is not included, skip. - } - - // Read variable name. - hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &sczName); - ExitOnFailure(hr, "Failed to read variable name."); - - // Read variable value type. - hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&value.Type); - ExitOnFailure(hr, "Failed to read variable value type."); - - // Read variable value. - switch (value.Type) - { - case BURN_VARIANT_TYPE_NONE: - break; - case BURN_VARIANT_TYPE_NUMERIC: - hr = BuffReadNumber64(pbBuffer, cbBuffer, piBuffer, &qw); - ExitOnFailure(hr, "Failed to read variable value as number."); - - hr = BVariantSetNumeric(&value, static_cast(qw)); - ExitOnFailure(hr, "Failed to set variable value."); - - SecureZeroMemory(&qw, sizeof(qw)); - break; - case BURN_VARIANT_TYPE_VERSION: - hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &scz); - ExitOnFailure(hr, "Failed to read variable value as string."); - - hr = VerParseVersion(scz, 0, FALSE, &pVersion); - ExitOnFailure(hr, "Failed to parse variable value as version."); - - hr = BVariantSetVersion(&value, pVersion); - ExitOnFailure(hr, "Failed to set variable value."); - - SecureZeroMemory(&qw, sizeof(qw)); - break; - case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; - case BURN_VARIANT_TYPE_STRING: - hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &scz); - ExitOnFailure(hr, "Failed to read variable value as string."); - - hr = BVariantSetString(&value, scz, NULL, BURN_VARIANT_TYPE_FORMATTED == value.Type); - ExitOnFailure(hr, "Failed to set variable value."); - - ReleaseNullStrSecure(scz); - break; - default: - hr = E_INVALIDARG; - ExitOnFailure(hr, "Unsupported variable type."); - } - - // Set variable. - hr = SetVariableValue(pVariables, sczName, &value, fWasPersisted ? SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS : SET_VARIABLE_ANY, FALSE); - ExitOnFailure(hr, "Failed to set variable."); - - // Clean up. - BVariantUninitialize(&value); - } - -LExit: - ::LeaveCriticalSection(&pVariables->csAccess); - - ReleaseVerutilVersion(pVersion); - ReleaseStr(sczName); - BVariantUninitialize(&value); - SecureZeroMemory(&qw, sizeof(qw)); - StrSecureZeroFreeString(scz); - - return hr; -} - -extern "C" HRESULT VariableStrAlloc( - __in BOOL fZeroOnRealloc, - __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, - __in DWORD_PTR cch - ) -{ - HRESULT hr = S_OK; - - if (fZeroOnRealloc) - { - hr = StrAllocSecure(ppwz, cch); - } - else - { - hr = StrAlloc(ppwz, cch); - } - - return hr; -} - -extern "C" HRESULT VariableStrAllocString( - __in BOOL fZeroOnRealloc, - __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in DWORD_PTR cchSource - ) -{ - HRESULT hr = S_OK; - - if (fZeroOnRealloc) - { - hr = StrAllocStringSecure(ppwz, wzSource, cchSource); - } - else - { - hr = StrAllocString(ppwz, wzSource, cchSource); - } - - return hr; -} - -extern "C" HRESULT VariableStrAllocConcat( - __in BOOL fZeroOnRealloc, - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in DWORD_PTR cchSource - ) -{ - HRESULT hr = S_OK; - - if (fZeroOnRealloc) - { - hr = StrAllocConcatSecure(ppwz, wzSource, cchSource); - } - else - { - hr = StrAllocConcat(ppwz, wzSource, cchSource); - } - - return hr; -} - -extern "C" HRESULT __cdecl VariableStrAllocFormatted( - __in BOOL fZeroOnRealloc, - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - ... - ) -{ - HRESULT hr = S_OK; - va_list args; - - va_start(args, wzFormat); - if (fZeroOnRealloc) - { - hr = StrAllocFormattedArgsSecure(ppwz, wzFormat, args); - } - else - { - hr = StrAllocFormattedArgs(ppwz, wzFormat, args); - } - va_end(args); - - return hr; -} - -extern "C" HRESULT VariableIsHidden( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __out BOOL* pfHidden - ) -{ - HRESULT hr = S_OK; - BURN_VARIABLE* pVariable = NULL; - - ::EnterCriticalSection(&pVariables->csAccess); - - hr = GetVariable(pVariables, wzVariable, &pVariable); - if (E_NOTFOUND == hr) - { - // A missing variable does not need its data hidden. - *pfHidden = FALSE; - - ExitFunction1(hr = S_OK); - } - ExitOnFailure(hr, "Failed to get visibility of variable: %ls", wzVariable); - - *pfHidden = pVariable->fHidden; - -LExit: - ::LeaveCriticalSection(&pVariables->csAccess); - - return hr; -} - - -// internal function definitions - -static HRESULT FormatString( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzIn, - __out_z_opt LPWSTR* psczOut, - __out_opt SIZE_T* pcchOut, - __in BOOL fObfuscateHiddenVariables, - __out BOOL* pfContainsHiddenVariable - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - LPWSTR sczUnformatted = NULL; - LPWSTR sczFormat = NULL; - LPCWSTR wzRead = NULL; - LPCWSTR wzOpen = NULL; - LPCWSTR wzClose = NULL; - LPWSTR scz = NULL; - LPWSTR* rgVariables = NULL; - DWORD cVariables = 0; - DWORD cch = 0; - size_t cchIn = 0; - BOOL fHidden = FALSE; - MSIHANDLE hRecord = NULL; - - ::EnterCriticalSection(&pVariables->csAccess); - - // allocate buffer for format string - hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_LENGTH, &cchIn); - ExitOnFailure(hr, "Failed to length of format string."); - - hr = StrAlloc(&sczFormat, cchIn + 1); - ExitOnFailure(hr, "Failed to allocate buffer for format string."); - - // read out variables from the unformatted string and build a format string - wzRead = wzIn; - for (;;) - { - // scan for opening '[' - wzOpen = wcschr(wzRead, L'['); - if (!wzOpen) - { - // end reached, append the remainder of the string and end loop - hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, 0); - ExitOnFailure(hr, "Failed to append string."); - break; - } - - // scan for closing ']' - wzClose = wcschr(wzOpen + 1, L']'); - if (!wzClose) - { - // end reached, treat unterminated expander as literal - hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, 0); - ExitOnFailure(hr, "Failed to append string."); - break; - } - cch = (DWORD)(wzClose - wzOpen - 1); - - if (0 == cch) - { - // blank, copy all text including the terminator - hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, (DWORD_PTR)(wzClose - wzRead) + 1); - ExitOnFailure(hr, "Failed to append string."); - } - else - { - // append text preceding expander - if (wzOpen > wzRead) - { - hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, (DWORD_PTR)(wzOpen - wzRead)); - ExitOnFailure(hr, "Failed to append string."); - } - - // get variable name - hr = VariableStrAllocString(!fObfuscateHiddenVariables, &scz, wzOpen + 1, cch); - ExitOnFailure(hr, "Failed to get variable name."); - - // allocate space in variable array - if (rgVariables) - { - LPVOID pv = MemReAlloc(rgVariables, sizeof(LPWSTR) * (cVariables + 1), TRUE); - ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate variable array."); - rgVariables = (LPWSTR*)pv; - } - else - { - rgVariables = (LPWSTR*)MemAlloc(sizeof(LPWSTR) * (cVariables + 1), TRUE); - ExitOnNull(rgVariables, hr, E_OUTOFMEMORY, "Failed to allocate variable array."); - } - - // set variable value - if (2 <= cch && L'\\' == wzOpen[1]) - { - // escape sequence, copy character - hr = VariableStrAllocString(!fObfuscateHiddenVariables, &rgVariables[cVariables], &wzOpen[2], 1); - } - else - { - hr = VariableIsHidden(pVariables, scz, &fHidden); - ExitOnFailure(hr, "Failed to determine variable visibility: '%ls'.", scz); - - if (pfContainsHiddenVariable) - { - *pfContainsHiddenVariable |= fHidden; - } - - if (fObfuscateHiddenVariables && fHidden) - { - hr = StrAllocString(&rgVariables[cVariables], L"*****", 0); - } - else - { - // get formatted variable value - hr = GetFormatted(pVariables, scz, &rgVariables[cVariables], pfContainsHiddenVariable); - if (E_NOTFOUND == hr) // variable not found - { - hr = StrAllocStringSecure(&rgVariables[cVariables], L"", 0); - } - } - } - ExitOnFailure(hr, "Failed to set variable value."); - ++cVariables; - - // append placeholder to format string - hr = VariableStrAllocFormatted(!fObfuscateHiddenVariables, &scz, L"[%d]", cVariables); - ExitOnFailure(hr, "Failed to format placeholder string."); - - hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, scz, 0); - ExitOnFailure(hr, "Failed to append placeholder."); - } - - // update read pointer - wzRead = wzClose + 1; - } - - // create record - hRecord = ::MsiCreateRecord(cVariables); - ExitOnNull(hRecord, hr, E_OUTOFMEMORY, "Failed to allocate record."); - - // set format string - er = ::MsiRecordSetStringW(hRecord, 0, sczFormat); - ExitOnWin32Error(er, hr, "Failed to set record format string."); - - // copy record fields - for (DWORD i = 0; i < cVariables; ++i) - { - if (*rgVariables[i]) // not setting if blank - { - er = ::MsiRecordSetStringW(hRecord, i + 1, rgVariables[i]); - ExitOnWin32Error(er, hr, "Failed to set record string."); - } - } - - // get formatted character count - cch = 0; -#pragma prefast(push) -#pragma prefast(disable:6298) - er = ::MsiFormatRecordW(NULL, hRecord, L"", &cch); -#pragma prefast(pop) - if (ERROR_MORE_DATA != er) - { - ExitOnWin32Error(er, hr, "Failed to get formatted length."); - } - - // return formatted string - if (psczOut) - { - hr = VariableStrAlloc(!fObfuscateHiddenVariables, &scz, ++cch); - ExitOnFailure(hr, "Failed to allocate string."); - - er = ::MsiFormatRecordW(NULL, hRecord, scz, &cch); - ExitOnWin32Error(er, hr, "Failed to format record."); - - hr = VariableStrAllocString(!fObfuscateHiddenVariables, psczOut, scz, 0); - ExitOnFailure(hr, "Failed to copy string."); - } - - // return character count - if (pcchOut) - { - *pcchOut = cch; - } - -LExit: - ::LeaveCriticalSection(&pVariables->csAccess); - - if (rgVariables) - { - for (DWORD i = 0; i < cVariables; ++i) - { - if (fObfuscateHiddenVariables) - { - ReleaseStr(rgVariables[i]); - } - else - { - StrSecureZeroFreeString(rgVariables[i]); - } - } - MemFree(rgVariables); - } - - if (hRecord) - { - ::MsiCloseHandle(hRecord); - } - - if (fObfuscateHiddenVariables) - { - ReleaseStr(sczUnformatted); - ReleaseStr(sczFormat); - ReleaseStr(scz); - } - else - { - StrSecureZeroFreeString(sczUnformatted); - StrSecureZeroFreeString(sczFormat); - StrSecureZeroFreeString(scz); - } - - return hr; -} - -static HRESULT GetFormatted( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __out_z LPWSTR* psczValue, - __out BOOL* pfContainsHiddenVariable - ) -{ - HRESULT hr = S_OK; - BURN_VARIABLE* pVariable = NULL; - LPWSTR scz = NULL; - - ::EnterCriticalSection(&pVariables->csAccess); - - hr = GetVariable(pVariables, wzVariable, &pVariable); - if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) - { - ExitFunction1(hr = E_NOTFOUND); - } - else if (E_NOTFOUND == hr) - { - ExitFunction(); - } - ExitOnFailure(hr, "Failed to get variable: %ls", wzVariable); - - if (pfContainsHiddenVariable) - { - *pfContainsHiddenVariable |= pVariable->fHidden; - } - - if (BURN_VARIANT_TYPE_FORMATTED == pVariable->Value.Type) - { - hr = BVariantGetString(&pVariable->Value, &scz); - ExitOnFailure(hr, "Failed to get unformatted string."); - - hr = FormatString(pVariables, scz, psczValue, NULL, FALSE, pfContainsHiddenVariable); - ExitOnFailure(hr, "Failed to format value '%ls' of variable: %ls", pVariable->fHidden ? L"*****" : pVariable->Value.sczValue, wzVariable); - } - else - { - hr = BVariantGetString(&pVariable->Value, psczValue); - ExitOnFailure(hr, "Failed to get value as string for variable: %ls", wzVariable); - } - -LExit: - ::LeaveCriticalSection(&pVariables->csAccess); - StrSecureZeroFreeString(scz); - - return hr; -} - -static HRESULT AddBuiltInVariable( - __in BURN_VARIABLES* pVariables, - __in LPCWSTR wzVariable, - __in PFN_INITIALIZEVARIABLE pfnInitialize, - __in DWORD_PTR dwpInitializeData, - __in BOOL fPersist, - __in BOOL fOverridable - ) -{ - HRESULT hr = S_OK; - DWORD iVariable = 0; - BURN_VARIABLE* pVariable = NULL; - - hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable); - ExitOnFailure(hr, "Failed to find variable value."); - - // insert element if not found - if (S_FALSE == hr) - { - hr = InsertVariable(pVariables, wzVariable, iVariable); - ExitOnFailure(hr, "Failed to insert variable."); - } - - // set variable values - pVariable = &pVariables->rgVariables[iVariable]; - pVariable->fPersisted = fPersist; - pVariable->internalType = fOverridable ? BURN_VARIABLE_INTERNAL_TYPE_OVERRIDABLE_BUILTIN : BURN_VARIABLE_INTERNAL_TYPE_BUILTIN; - pVariable->pfnInitialize = pfnInitialize; - pVariable->dwpInitializeData = dwpInitializeData; - -LExit: - return hr; -} - -static HRESULT GetVariable( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __out BURN_VARIABLE** ppVariable - ) -{ - HRESULT hr = S_OK; - DWORD iVariable = 0; - BURN_VARIABLE* pVariable = NULL; - - hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable); - ExitOnFailure(hr, "Failed to find variable value '%ls'.", wzVariable); - - if (S_FALSE == hr) - { - ExitFunction1(hr = E_NOTFOUND); - } - - pVariable = &pVariables->rgVariables[iVariable]; - - // initialize built-in variable - if (BURN_VARIANT_TYPE_NONE == pVariable->Value.Type && BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariable->internalType) - { - hr = pVariable->pfnInitialize(pVariable->dwpInitializeData, &pVariable->Value); - ExitOnFailure(hr, "Failed to initialize built-in variable value '%ls'.", wzVariable); - } - - *ppVariable = pVariable; - -LExit: - return hr; -} - -static HRESULT FindVariableIndexByName( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __out DWORD* piVariable - ) -{ - HRESULT hr = S_OK; - DWORD iRangeFirst = 0; - DWORD cRangeLength = pVariables->cVariables; - - while (cRangeLength) - { - // get variable in middle of range - DWORD iPosition = cRangeLength / 2; - BURN_VARIABLE* pVariable = &pVariables->rgVariables[iRangeFirst + iPosition]; - - switch (::CompareStringW(LOCALE_INVARIANT, SORT_STRINGSORT, wzVariable, -1, pVariable->sczName, -1)) - { - case CSTR_LESS_THAN: - // restrict range to elements before the current - cRangeLength = iPosition; - break; - case CSTR_EQUAL: - // variable found - *piVariable = iRangeFirst + iPosition; - ExitFunction1(hr = S_OK); - case CSTR_GREATER_THAN: - // restrict range to elements after the current - iRangeFirst += iPosition + 1; - cRangeLength -= iPosition + 1; - break; - default: - ExitWithLastError(hr, "Failed to compare strings."); - } - } - - *piVariable = iRangeFirst; - hr = S_FALSE; // variable not found - -LExit: - return hr; -} - -static HRESULT InsertVariable( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __in DWORD iPosition - ) -{ - HRESULT hr = S_OK; - size_t cbAllocSize = 0; - - // ensure there is room in the variable array - if (pVariables->cVariables == pVariables->dwMaxVariables) - { - hr = ::DWordAdd(pVariables->dwMaxVariables, GROW_VARIABLE_ARRAY, &(pVariables->dwMaxVariables)); - ExitOnRootFailure(hr, "Overflow while growing variable array size"); - - if (pVariables->rgVariables) - { - hr = ::SizeTMult(sizeof(BURN_VARIABLE), pVariables->dwMaxVariables, &cbAllocSize); - ExitOnRootFailure(hr, "Overflow while calculating size of variable array buffer"); - - LPVOID pv = MemReAlloc(pVariables->rgVariables, cbAllocSize, FALSE); - ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate room for more variables."); - - // Prefast claims it's possible to hit this. Putting the check in just in case. - if (pVariables->dwMaxVariables < pVariables->cVariables) - { - hr = INTSAFE_E_ARITHMETIC_OVERFLOW; - ExitOnRootFailure(hr, "Overflow while dealing with variable array buffer allocation"); - } - - pVariables->rgVariables = (BURN_VARIABLE*)pv; - memset(&pVariables->rgVariables[pVariables->cVariables], 0, sizeof(BURN_VARIABLE) * (pVariables->dwMaxVariables - pVariables->cVariables)); - } - else - { - pVariables->rgVariables = (BURN_VARIABLE*)MemAlloc(sizeof(BURN_VARIABLE) * pVariables->dwMaxVariables, TRUE); - ExitOnNull(pVariables->rgVariables, hr, E_OUTOFMEMORY, "Failed to allocate room for variables."); - } - } - - // move variables - if (0 < pVariables->cVariables - iPosition) - { - memmove(&pVariables->rgVariables[iPosition + 1], &pVariables->rgVariables[iPosition], sizeof(BURN_VARIABLE) * (pVariables->cVariables - iPosition)); - memset(&pVariables->rgVariables[iPosition], 0, sizeof(BURN_VARIABLE)); - } - - ++pVariables->cVariables; - - // allocate name - hr = StrAllocString(&pVariables->rgVariables[iPosition].sczName, wzVariable, 0); - ExitOnFailure(hr, "Failed to copy variable name."); - -LExit: - return hr; -} - -static HRESULT SetVariableValue( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __in BURN_VARIANT* pVariant, - __in SET_VARIABLE setBuiltin, - __in BOOL fLog - ) -{ - HRESULT hr = S_OK; - DWORD iVariable = 0; - - ::EnterCriticalSection(&pVariables->csAccess); - - hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable); - ExitOnFailure(hr, "Failed to find variable value '%ls'.", wzVariable); - - // Insert element if not found. - if (S_FALSE == hr) - { - hr = InsertVariable(pVariables, wzVariable, iVariable); - ExitOnFailure(hr, "Failed to insert variable '%ls'.", wzVariable); - } - else if (BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariables->rgVariables[iVariable].internalType) // built-in variables must be overridden. - { - if (SET_VARIABLE_OVERRIDE_BUILTIN == setBuiltin || - (SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS == setBuiltin && pVariables->rgVariables[iVariable].fPersisted) || - SET_VARIABLE_ANY == setBuiltin && BURN_VARIABLE_INTERNAL_TYPE_BUILTIN != pVariables->rgVariables[iVariable].internalType) - { - hr = S_OK; - } - else - { - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Attempt to set built-in variable value: %ls", wzVariable); - } - } - else // must *not* be a built-in variable so caller should not have tried to override it as a built-in. - { - // Not possible from external callers so just assert. - AssertSz(SET_VARIABLE_OVERRIDE_BUILTIN != setBuiltin, "Intent to overwrite non-built-in variable."); - } - - // Log value when not overwriting a built-in variable. - if (fLog && BURN_VARIABLE_INTERNAL_TYPE_NORMAL == pVariables->rgVariables[iVariable].internalType) - { - if (pVariables->rgVariables[iVariable].fHidden) - { - LogStringLine(REPORT_STANDARD, "Setting hidden variable '%ls'", wzVariable); - } - else - { - switch (pVariant->Type) - { - case BURN_VARIANT_TYPE_NONE: - if (BURN_VARIANT_TYPE_NONE != pVariables->rgVariables[iVariable].Value.Type) - { - LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable); - } - break; - - case BURN_VARIANT_TYPE_NUMERIC: - LogStringLine(REPORT_STANDARD, "Setting numeric variable '%ls' to value %lld", wzVariable, pVariant->llValue); - break; - - case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; - case BURN_VARIANT_TYPE_STRING: - if (!pVariant->sczValue) - { - LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable); - } - else - { - LogStringLine(REPORT_STANDARD, "Setting %ls variable '%ls' to value '%ls'", BURN_VARIANT_TYPE_FORMATTED == pVariant->Type ? L"formatted" : L"string", wzVariable, pVariant->sczValue); - } - break; - - case BURN_VARIANT_TYPE_VERSION: - if (!pVariant->pValue) - { - LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable); - } - else - { - LogStringLine(REPORT_STANDARD, "Setting version variable '%ls' to value '%ls'", wzVariable, pVariant->pValue->sczVersion); - } - break; - - default: - AssertSz(FALSE, "Unknown variant type."); - break; - } - } - - if (BURN_VARIANT_TYPE_VERSION == pVariant->Type && pVariant->pValue && pVariant->pValue->fInvalid) - { - LogId(REPORT_WARNING, MSG_VARIABLE_INVALID_VERSION, wzVariable); - } - } - - // Update variable value. - hr = BVariantSetValue(&pVariables->rgVariables[iVariable].Value, pVariant); - ExitOnFailure(hr, "Failed to set value of variable: %ls", wzVariable); - -LExit: - ::LeaveCriticalSection(&pVariables->csAccess); - - if (FAILED(hr) && fLog) - { - LogStringLine(REPORT_STANDARD, "Setting variable failed: ID '%ls', HRESULT 0x%x", wzVariable, hr); - } - - return hr; -} - -static HRESULT InitializeVariableVersionNT( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ) -{ - HRESULT hr = S_OK; - RTL_OSVERSIONINFOEXW ovix = { }; - BURN_VARIANT value = { }; - VERUTIL_VERSION* pVersion = NULL; - - hr = OsRtlGetVersion(&ovix); - ExitOnFailure(hr, "Failed to get OS info."); - - switch ((OS_INFO_VARIABLE)dwpData) - { - case OS_INFO_VARIABLE_ServicePackLevel: - if (0 != ovix.wServicePackMajor) - { - value.llValue = static_cast(ovix.wServicePackMajor); - value.Type = BURN_VARIANT_TYPE_NUMERIC; - } - break; - case OS_INFO_VARIABLE_VersionNT: - hr = VerVersionFromQword(MAKEQWORDVERSION(ovix.dwMajorVersion, ovix.dwMinorVersion, 0, 0), &pVersion); - ExitOnFailure(hr, "Failed to create VersionNT from QWORD."); - - value.pValue = pVersion; - value.Type = BURN_VARIANT_TYPE_VERSION; - break; - case OS_INFO_VARIABLE_VersionNT64: - { -#if !defined(_WIN64) - BOOL fIsWow64 = FALSE; - - ProcWow64(::GetCurrentProcess(), &fIsWow64); - if (fIsWow64) -#endif - { - hr = VerVersionFromQword(MAKEQWORDVERSION(ovix.dwMajorVersion, ovix.dwMinorVersion, 0, 0), &pVersion); - ExitOnFailure(hr, "Failed to create VersionNT64 from QWORD."); - - value.pValue = pVersion; - value.Type = BURN_VARIANT_TYPE_VERSION; - } - } - break; - case OS_INFO_VARIABLE_WindowsBuildNumber: - value.llValue = static_cast(ovix.dwBuildNumber); - value.Type = BURN_VARIANT_TYPE_NUMERIC; - default: - AssertSz(FALSE, "Unknown OS info type."); - break; - } - - hr = BVariantCopy(&value, pValue); - ExitOnFailure(hr, "Failed to set variant value."); - -LExit: - ReleaseVerutilVersion(pVersion); - - return hr; -} - -static HRESULT InitializeVariableOsInfo( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ) -{ - HRESULT hr = S_OK; - RTL_OSVERSIONINFOEXW ovix = { }; - BURN_VARIANT value = { }; - - hr = OsRtlGetVersion(&ovix); - ExitOnFailure(hr, "Failed to get OS info."); - - switch ((OS_INFO_VARIABLE)dwpData) - { - case OS_INFO_VARIABLE_NTProductType: - value.llValue = ovix.wProductType; - value.Type = BURN_VARIANT_TYPE_NUMERIC; - break; - case OS_INFO_VARIABLE_NTSuiteBackOffice: - value.llValue = VER_SUITE_BACKOFFICE & ovix.wSuiteMask ? 1 : 0; - value.Type = BURN_VARIANT_TYPE_NUMERIC; - break; - case OS_INFO_VARIABLE_NTSuiteDataCenter: - value.llValue = VER_SUITE_DATACENTER & ovix.wSuiteMask ? 1 : 0; - value.Type = BURN_VARIANT_TYPE_NUMERIC; - break; - case OS_INFO_VARIABLE_NTSuiteEnterprise: - value.llValue = VER_SUITE_ENTERPRISE & ovix.wSuiteMask ? 1 : 0; - value.Type = BURN_VARIANT_TYPE_NUMERIC; - break; - case OS_INFO_VARIABLE_NTSuitePersonal: - value.llValue = VER_SUITE_PERSONAL & ovix.wSuiteMask ? 1 : 0; - value.Type = BURN_VARIANT_TYPE_NUMERIC; - break; - case OS_INFO_VARIABLE_NTSuiteSmallBusiness: - value.llValue = VER_SUITE_SMALLBUSINESS & ovix.wSuiteMask ? 1 : 0; - value.Type = BURN_VARIANT_TYPE_NUMERIC; - break; - case OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted: - value.llValue = VER_SUITE_SMALLBUSINESS_RESTRICTED & ovix.wSuiteMask ? 1 : 0; - value.Type = BURN_VARIANT_TYPE_NUMERIC; - break; - case OS_INFO_VARIABLE_NTSuiteWebServer: - value.llValue = VER_SUITE_BLADE & ovix.wSuiteMask ? 1 : 0; - value.Type = BURN_VARIANT_TYPE_NUMERIC; - break; - case OS_INFO_VARIABLE_CompatibilityMode: - { - DWORDLONG dwlConditionMask = 0; - VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_EQUAL); - VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_EQUAL); - VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMAJOR, VER_EQUAL); - VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMINOR, VER_EQUAL); - - value.llValue = ::VerifyVersionInfoW(&ovix, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, dwlConditionMask); - value.Type = BURN_VARIANT_TYPE_NUMERIC; - } - break; - case OS_INFO_VARIABLE_TerminalServer: - value.llValue = (VER_SUITE_TERMINAL == (ovix.wSuiteMask & VER_SUITE_TERMINAL)) && (VER_SUITE_SINGLEUSERTS != (ovix.wSuiteMask & VER_SUITE_SINGLEUSERTS)) ? 1 : 0; - value.Type = BURN_VARIANT_TYPE_NUMERIC; - break; - default: - AssertSz(FALSE, "Unknown OS info type."); - break; - } - - hr = BVariantCopy(&value, pValue); - ExitOnFailure(hr, "Failed to set variant value."); - -LExit: - return hr; -} - -static HRESULT InitializeVariableSystemInfo( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ) -{ - HRESULT hr = S_OK; - SYSTEM_INFO si = { }; - BURN_VARIANT value = { }; - - ::GetNativeSystemInfo(&si); - - switch ((OS_INFO_VARIABLE)dwpData) - { - case OS_INFO_VARIABLE_ProcessorArchitecture: - value.llValue = si.wProcessorArchitecture; - value.Type = BURN_VARIANT_TYPE_NUMERIC; - break; - default: - AssertSz(FALSE, "Unknown OS info type."); - break; - } - - hr = BVariantCopy(&value, pValue); - ExitOnFailure(hr, "Failed to set variant value."); - -LExit: - return hr; -} - -static HRESULT InitializeVariableComputerName( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ) -{ - UNREFERENCED_PARAMETER(dwpData); - - HRESULT hr = S_OK; - WCHAR wzComputerName[MAX_COMPUTERNAME_LENGTH + 1] = { }; - DWORD cchComputerName = countof(wzComputerName); - - // get computer name - if (!::GetComputerNameW(wzComputerName, &cchComputerName)) - { - ExitWithLastError(hr, "Failed to get computer name."); - } - - // set value - hr = BVariantSetString(pValue, wzComputerName, 0, FALSE); - ExitOnFailure(hr, "Failed to set variant value."); - -LExit: - return hr; -} - -static HRESULT InitializeVariableVersionMsi( - __in DWORD_PTR /*dwpData*/, - __inout BURN_VARIANT* pValue - ) -{ - HRESULT hr = S_OK; - DLLGETVERSIONPROC pfnMsiDllGetVersion = NULL; - DLLVERSIONINFO msiVersionInfo = { }; - VERUTIL_VERSION* pVersion = NULL; - - // get DllGetVersion proc address - pfnMsiDllGetVersion = (DLLGETVERSIONPROC)::GetProcAddress(::GetModuleHandleW(L"msi"), "DllGetVersion"); - ExitOnNullWithLastError(pfnMsiDllGetVersion, hr, "Failed to find DllGetVersion entry point in msi.dll."); - - // get msi.dll version info - msiVersionInfo.cbSize = sizeof(DLLVERSIONINFO); - hr = pfnMsiDllGetVersion(&msiVersionInfo); - ExitOnFailure(hr, "Failed to get msi.dll version info."); - - hr = VerVersionFromQword(MAKEQWORDVERSION(msiVersionInfo.dwMajorVersion, msiVersionInfo.dwMinorVersion, 0, 0), &pVersion); - ExitOnFailure(hr, "Failed to create msi.dll version from QWORD."); - - hr = BVariantSetVersion(pValue, pVersion); - ExitOnFailure(hr, "Failed to set variant value."); - -LExit: - ReleaseVerutilVersion(pVersion); - - return hr; -} - -static HRESULT InitializeVariableCsidlFolder( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ) -{ - HRESULT hr = S_OK; - LPWSTR sczPath = NULL; - int nFolder = (int)dwpData; - - // get folder path - hr = ShelGetFolder(&sczPath, nFolder); - ExitOnRootFailure(hr, "Failed to get shell folder."); - - // set value - hr = BVariantSetString(pValue, sczPath, 0, FALSE); - ExitOnFailure(hr, "Failed to set variant value."); - -LExit: - ReleaseStr(sczPath); - - return hr; -} - -static HRESULT InitializeVariableTempFolder( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ) -{ - UNREFERENCED_PARAMETER(dwpData); - - HRESULT hr = S_OK; - WCHAR wzPath[MAX_PATH] = { }; - - // get volume path name - if (!::GetTempPathW(MAX_PATH, wzPath)) - { - ExitWithLastError(hr, "Failed to get temp path."); - } - - // set value - hr = BVariantSetString(pValue, wzPath, 0, FALSE); - ExitOnFailure(hr, "Failed to set variant value."); - -LExit: - return hr; -} - -static HRESULT InitializeVariableSystemFolder( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ) -{ - HRESULT hr = S_OK; - BOOL f64 = (BOOL)dwpData; - WCHAR wzSystemFolder[MAX_PATH] = { }; - -#if !defined(_WIN64) - BOOL fIsWow64 = FALSE; - ProcWow64(::GetCurrentProcess(), &fIsWow64); - - if (fIsWow64) - { - if (f64) - { - if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder))) - { - ExitWithLastError(hr, "Failed to get 64-bit system folder."); - } - } - else - { - if (!::GetSystemWow64DirectoryW(wzSystemFolder, countof(wzSystemFolder))) - { - ExitWithLastError(hr, "Failed to get 32-bit system folder."); - } - } - } - else - { - if (!f64) - { - if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder))) - { - ExitWithLastError(hr, "Failed to get 32-bit system folder."); - } - } - } -#else - if (f64) - { - if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder))) - { - ExitWithLastError(hr, "Failed to get 64-bit system folder."); - } - } - else - { - if (!::GetSystemWow64DirectoryW(wzSystemFolder, countof(wzSystemFolder))) - { - ExitWithLastError(hr, "Failed to get 32-bit system folder."); - } - } -#endif - - if (*wzSystemFolder) - { - hr = PathFixedBackslashTerminate(wzSystemFolder, countof(wzSystemFolder)); - ExitOnFailure(hr, "Failed to backslash terminate system folder."); - } - - // set value - hr = BVariantSetString(pValue, wzSystemFolder, 0, FALSE); - ExitOnFailure(hr, "Failed to set system folder variant value."); - -LExit: - return hr; -} - -static HRESULT InitializeVariableWindowsVolumeFolder( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ) -{ - UNREFERENCED_PARAMETER(dwpData); - - HRESULT hr = S_OK; - WCHAR wzWindowsPath[MAX_PATH] = { }; - WCHAR wzVolumePath[MAX_PATH] = { }; - - // get windows directory - if (!::GetWindowsDirectoryW(wzWindowsPath, countof(wzWindowsPath))) - { - ExitWithLastError(hr, "Failed to get windows directory."); - } - - // get volume path name - if (!::GetVolumePathNameW(wzWindowsPath, wzVolumePath, MAX_PATH)) - { - ExitWithLastError(hr, "Failed to get volume path name."); - } - - // set value - hr = BVariantSetString(pValue, wzVolumePath, 0, FALSE); - ExitOnFailure(hr, "Failed to set variant value."); - -LExit: - return hr; -} - -static HRESULT InitializeVariablePrivileged( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ) -{ - UNREFERENCED_PARAMETER(dwpData); - - HRESULT hr = S_OK; - BOOL fPrivileged = FALSE; - - // check if process could run privileged. - hr = OsCouldRunPrivileged(&fPrivileged); - ExitOnFailure(hr, "Failed to check if process could run privileged."); - - // set value - hr = BVariantSetNumeric(pValue, fPrivileged); - ExitOnFailure(hr, "Failed to set variant value."); - -LExit: - return hr; -} - -static HRESULT InitializeSystemLanguageID( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ) -{ - UNREFERENCED_PARAMETER(dwpData); - - HRESULT hr = S_OK; - LANGID langid = ::GetSystemDefaultLangID(); - - hr = BVariantSetNumeric(pValue, langid); - ExitOnFailure(hr, "Failed to set variant value."); - -LExit: - return hr; -} - -static HRESULT InitializeUserUILanguageID( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ) -{ - UNREFERENCED_PARAMETER(dwpData); - - HRESULT hr = S_OK; - LANGID langid = ::GetUserDefaultUILanguage(); - - hr = BVariantSetNumeric(pValue, langid); - ExitOnFailure(hr, "Failed to set variant value."); - -LExit: - return hr; -} - -static HRESULT InitializeUserLanguageID( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ) -{ - UNREFERENCED_PARAMETER(dwpData); - - HRESULT hr = S_OK; - LANGID langid = ::GetUserDefaultLangID(); - - hr = BVariantSetNumeric(pValue, langid); - ExitOnFailure(hr, "Failed to set variant value."); - -LExit: - return hr; -} - -static HRESULT InitializeVariableString( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ) -{ - HRESULT hr = S_OK; - LPCWSTR wzValue = (LPCWSTR)dwpData; - - // set value - hr = BVariantSetString(pValue, wzValue, 0, FALSE); - ExitOnFailure(hr, "Failed to set variant value."); - -LExit: - return hr; -} - -static HRESULT InitializeVariableNumeric( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ) -{ - HRESULT hr = S_OK; - LONGLONG llValue = (LONGLONG)dwpData; - - // set value - hr = BVariantSetNumeric(pValue, llValue); - ExitOnFailure(hr, "Failed to set variant value."); - -LExit: - return hr; -} - -#if !defined(_WIN64) -static HRESULT InitializeVariableRegistryFolder( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ) -{ - HRESULT hr = S_OK; - int nFolder = (int)dwpData; - LPWSTR sczPath = NULL; - - BOOL fIsWow64 = FALSE; - - ProcWow64(::GetCurrentProcess(), &fIsWow64); - if (!fIsWow64) // on 32-bit machines, variables aren't set - { - ExitFunction(); - } - - hr = Get64bitFolderFromRegistry(nFolder, &sczPath); - ExitOnFailure(hr, "Failed to get 64-bit folder."); - - // set value - hr = BVariantSetString(pValue, sczPath, 0, FALSE); - ExitOnFailure(hr, "Failed to set variant value."); - -LExit: - ReleaseStr(sczPath); - - return hr; -} -#endif - -static HRESULT InitializeVariable6432Folder( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ) -{ - HRESULT hr = S_OK; - int nFolder = (int)dwpData; - LPWSTR sczPath = NULL; - -#if !defined(_WIN64) - BOOL fIsWow64 = FALSE; - - // If 32-bit use shell-folder. - ProcWow64(::GetCurrentProcess(), &fIsWow64); - if (!fIsWow64) - { - hr = ShelGetFolder(&sczPath, nFolder); - ExitOnRootFailure(hr, "Failed to get shell folder."); - } - else -#endif - { - hr = Get64bitFolderFromRegistry(nFolder, &sczPath); - ExitOnFailure(hr, "Failed to get 64-bit folder."); - } - - // set value - hr = BVariantSetString(pValue, sczPath, 0, FALSE); - ExitOnFailure(hr, "Failed to set variant value."); - -LExit: - ReleaseStr(sczPath); - - return hr; -} - -// Get the date in the same format as Windows Installer. -static HRESULT InitializeVariableDate( - __in DWORD_PTR /*dwpData*/, - __inout BURN_VARIANT* pValue - ) -{ - HRESULT hr = S_OK; - SYSTEMTIME systime = { }; - LPWSTR sczDate = NULL; - int cchDate = 0; - - ::GetSystemTime(&systime); - - cchDate = ::GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, NULL, NULL, cchDate); - if (!cchDate) - { - ExitOnLastError(hr, "Failed to get the required buffer length for the Date."); - } - - hr = StrAlloc(&sczDate, cchDate); - ExitOnFailure(hr, "Failed to allocate the buffer for the Date."); - - if (!::GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, NULL, sczDate, cchDate)) - { - ExitOnLastError(hr, "Failed to get the Date."); - } - - // set value - hr = BVariantSetString(pValue, sczDate, cchDate, FALSE); - ExitOnFailure(hr, "Failed to set variant value."); - -LExit: - ReleaseStr(sczDate); - - return hr; -} - -static HRESULT InitializeVariableInstallerName( - __in DWORD_PTR /*dwpData*/, - __inout BURN_VARIANT* pValue - ) -{ - HRESULT hr = S_OK; - - // set value - hr = BVariantSetString(pValue, L"WiX Burn", 0, FALSE); - ExitOnFailure(hr, "Failed to set variant value."); - -LExit: - return hr; -} - -static HRESULT InitializeVariableInstallerVersion( - __in DWORD_PTR /*dwpData*/, - __inout BURN_VARIANT* pValue - ) -{ - HRESULT hr = S_OK; - LPWSTR sczVersion = NULL; - - hr = StrAllocStringAnsi(&sczVersion, szVerMajorMinorBuild, 0, CP_ACP); - ExitOnFailure(hr, "Failed to copy the engine version."); - - // set value - hr = BVariantSetString(pValue, sczVersion, 0, FALSE); - ExitOnFailure(hr, "Failed to set variant value."); - -LExit: - ReleaseStr(sczVersion); - - return hr; -} - -static HRESULT InitializeVariableVersion( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ) -{ - HRESULT hr = S_OK; - LPCWSTR wzValue = (LPCWSTR)dwpData; - VERUTIL_VERSION* pVersion = NULL; - - hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); - ExitOnFailure(hr, "Failed to initialize version."); - - // set value - hr = BVariantSetVersion(pValue, pVersion); - ExitOnFailure(hr, "Failed to set variant value."); - -LExit: - ReleaseVerutilVersion(pVersion); - - return hr; -} - -// Get the current user the same as Windows Installer. -static HRESULT InitializeVariableLogonUser( - __in DWORD_PTR /*dwpData*/, - __inout BURN_VARIANT* pValue - ) -{ - HRESULT hr = S_OK; - WCHAR wzUserName[UNLEN + 1]; - DWORD cchUserName = countof(wzUserName); - - if (!::GetUserNameW(wzUserName, &cchUserName)) - { - ExitOnLastError(hr, "Failed to get the user name."); - } - - // set value - hr = BVariantSetString(pValue, wzUserName, 0, FALSE); - ExitOnFailure(hr, "Failed to set variant value."); - -LExit: - return hr; -} - -static HRESULT Get64bitFolderFromRegistry( - __in int nFolder, - __deref_out_z LPWSTR* psczPath - ) -{ - HRESULT hr = S_OK; - HKEY hkFolders = NULL; - - AssertSz(CSIDL_PROGRAM_FILES == nFolder || CSIDL_PROGRAM_FILES_COMMON == nFolder, "Unknown folder CSIDL."); - LPCWSTR wzFolderValue = CSIDL_PROGRAM_FILES_COMMON == nFolder ? L"CommonFilesDir" : L"ProgramFilesDir"; - - hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion", KEY_READ | KEY_WOW64_64KEY, &hkFolders); - ExitOnFailure(hr, "Failed to open Windows folder key."); - - hr = RegReadString(hkFolders, wzFolderValue, psczPath); - ExitOnFailure(hr, "Failed to read folder path for '%ls'.", wzFolderValue); - - hr = PathBackslashTerminate(psczPath); - ExitOnFailure(hr, "Failed to ensure path was backslash terminated."); - -LExit: - ReleaseRegKey(hkFolders); - - return hr; -} - diff --git a/src/engine/variable.h b/src/engine/variable.h deleted file mode 100644 index a38c9daa..00000000 --- a/src/engine/variable.h +++ /dev/null @@ -1,185 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -// constants - -const LPCWSTR VARIABLE_DATE = L"Date"; -const LPCWSTR VARIABLE_LOGONUSER = L"LogonUser"; -const LPCWSTR VARIABLE_INSTALLERNAME = L"InstallerName"; -const LPCWSTR VARIABLE_INSTALLERVERSION = L"InstallerVersion"; - - -// typedefs - -typedef HRESULT (*PFN_INITIALIZEVARIABLE)( - __in DWORD_PTR dwpData, - __inout BURN_VARIANT* pValue - ); - - -// constants - -enum BURN_VARIABLE_INTERNAL_TYPE -{ - BURN_VARIABLE_INTERNAL_TYPE_NORMAL, // the BA can set this variable. - BURN_VARIABLE_INTERNAL_TYPE_OVERRIDABLE_BUILTIN, // the BA can't set this variable, but the unelevated process can serialize it to the elevated process. - BURN_VARIABLE_INTERNAL_TYPE_BUILTIN, // the BA can't set this variable, and the unelevated process can't serialize it to the elevated process. -}; - - -// structs - -typedef struct _BURN_VARIABLE -{ - LPWSTR sczName; - BURN_VARIANT Value; - BOOL fHidden; - BOOL fPersisted; - - // used for late initialization of built-in variables - BURN_VARIABLE_INTERNAL_TYPE internalType; - PFN_INITIALIZEVARIABLE pfnInitialize; - DWORD_PTR dwpInitializeData; -} BURN_VARIABLE; - -typedef struct _BURN_VARIABLES -{ - CRITICAL_SECTION csAccess; - DWORD dwMaxVariables; - DWORD cVariables; - BURN_VARIABLE* rgVariables; -} BURN_VARIABLES; - - -// function declarations - -HRESULT VariableInitialize( - __in BURN_VARIABLES* pVariables - ); -HRESULT VariablesParseFromXml( - __in BURN_VARIABLES* pVariables, - __in IXMLDOMNode* pixnBundle - ); -void VariablesUninitialize( - __in BURN_VARIABLES* pVariables - ); -void VariablesDump( - __in BURN_VARIABLES* pVariables - ); -HRESULT VariableGetNumeric( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __out LONGLONG* pllValue - ); -HRESULT VariableGetString( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __out_z LPWSTR* psczValue - ); -HRESULT VariableGetVersion( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __in VERUTIL_VERSION** ppValue - ); -HRESULT VariableGetVariant( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __in BURN_VARIANT* pValue - ); -HRESULT VariableGetFormatted( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __out_z LPWSTR* psczValue, - __out BOOL* pfContainsHiddenVariable - ); -HRESULT VariableSetNumeric( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __in LONGLONG llValue, - __in BOOL fOverwriteBuiltIn - ); -HRESULT VariableSetString( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __in_z_opt LPCWSTR wzValue, - __in BOOL fOverwriteBuiltIn, - __in BOOL fFormatted - ); -HRESULT VariableSetVersion( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __in VERUTIL_VERSION* pValue, - __in BOOL fOverwriteBuiltIn - ); -HRESULT VariableSetVariant( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __in BURN_VARIANT* pVariant - ); -HRESULT VariableFormatString( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzIn, - __out_z_opt LPWSTR* psczOut, - __out_opt SIZE_T* pcchOut - ); -HRESULT VariableFormatStringObfuscated( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzIn, - __out_z_opt LPWSTR* psczOut, - __out_opt SIZE_T* pcchOut - ); -HRESULT VariableEscapeString( - __in_z LPCWSTR wzIn, - __out_z LPWSTR* psczOut - ); -HRESULT VariableSerialize( - __in BURN_VARIABLES* pVariables, - __in BOOL fPersisting, - __inout BYTE** ppbBuffer, - __inout SIZE_T* piBuffer - ); -HRESULT VariableDeserialize( - __in BURN_VARIABLES* pVariables, - __in BOOL fWasPersisted, - __in_bcount(cbBuffer) BYTE* pbBuffer, - __in SIZE_T cbBuffer, - __inout SIZE_T* piBuffer - ); -HRESULT VariableStrAlloc( - __in BOOL fZeroOnRealloc, - __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, - __in DWORD_PTR cch - ); -HRESULT VariableStrAllocString( - __in BOOL fZeroOnRealloc, - __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in DWORD_PTR cchSource - ); -HRESULT VariableStrAllocConcat( - __in BOOL fZeroOnRealloc, - __deref_out_z LPWSTR* ppwz, - __in_z LPCWSTR wzSource, - __in DWORD_PTR cchSource - ); -HRESULT __cdecl VariableStrAllocFormatted( - __in BOOL fZeroOnRealloc, - __deref_out_z LPWSTR* ppwz, - __in __format_string LPCWSTR wzFormat, - ... - ); -HRESULT VariableIsHidden( - __in BURN_VARIABLES* pVariables, - __in_z LPCWSTR wzVariable, - __out BOOL* pfHidden - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/variant.cpp b/src/engine/variant.cpp deleted file mode 100644 index 2267ee7b..00000000 --- a/src/engine/variant.cpp +++ /dev/null @@ -1,321 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -// internal function declarations - -static HRESULT GetVersionInternal( - __in BURN_VARIANT* pVariant, - __in BOOL fHidden, - __in BOOL fSilent, - __out VERUTIL_VERSION** ppValue - ); - -// function definitions - -extern "C" void BVariantUninitialize( - __in BURN_VARIANT* pVariant - ) -{ - if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type || - BURN_VARIANT_TYPE_STRING == pVariant->Type) - { - StrSecureZeroFreeString(pVariant->sczValue); - } - SecureZeroMemory(pVariant, sizeof(BURN_VARIANT)); -} - -extern "C" HRESULT BVariantGetNumeric( - __in BURN_VARIANT* pVariant, - __out LONGLONG* pllValue - ) -{ - HRESULT hr = S_OK; - - switch (pVariant->Type) - { - case BURN_VARIANT_TYPE_NUMERIC: - *pllValue = pVariant->llValue; - break; - case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; - case BURN_VARIANT_TYPE_STRING: - hr = StrStringToInt64(pVariant->sczValue, 0, pllValue); - if (FAILED(hr)) - { - hr = DISP_E_TYPEMISMATCH; - } - break; - case BURN_VARIANT_TYPE_VERSION: - hr = StrStringToInt64(pVariant->pValue ? pVariant->pValue->sczVersion : NULL, 0, pllValue); - if (FAILED(hr)) - { - hr = DISP_E_TYPEMISMATCH; - } - break; - default: - hr = E_INVALIDARG; - break; - } - - return hr; -} - -extern "C" HRESULT BVariantGetString( - __in BURN_VARIANT* pVariant, - __out_z LPWSTR* psczValue - ) -{ - HRESULT hr = S_OK; - - switch (pVariant->Type) - { - case BURN_VARIANT_TYPE_NUMERIC: - hr = StrAllocFormattedSecure(psczValue, L"%I64d", pVariant->llValue); - ExitOnFailure(hr, "Failed to convert int64 to string."); - break; - case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; - case BURN_VARIANT_TYPE_STRING: - hr = StrAllocStringSecure(psczValue, pVariant->sczValue, 0); - ExitOnFailure(hr, "Failed to copy string value."); - break; - case BURN_VARIANT_TYPE_VERSION: - hr = StrAllocStringSecure(psczValue, pVariant->pValue ? pVariant->pValue->sczVersion : NULL, 0); - ExitOnFailure(hr, "Failed to copy version value."); - break; - default: - hr = E_INVALIDARG; - break; - } - -LExit: - return hr; -} - -extern "C" HRESULT BVariantGetVersion( - __in BURN_VARIANT* pVariant, - __out VERUTIL_VERSION** ppValue - ) -{ - return GetVersionInternal(pVariant, FALSE, FALSE, ppValue); -} - -extern "C" HRESULT BVariantGetVersionHidden( - __in BURN_VARIANT* pVariant, - __in BOOL fHidden, - __out VERUTIL_VERSION** ppValue - ) -{ - return GetVersionInternal(pVariant, fHidden, FALSE, ppValue); -} - -extern "C" HRESULT BVariantGetVersionSilent( - __in BURN_VARIANT* pVariant, - __in BOOL fSilent, - __out VERUTIL_VERSION** ppValue - ) -{ - return GetVersionInternal(pVariant, FALSE, fSilent, ppValue); -} - -static HRESULT GetVersionInternal( - __in BURN_VARIANT* pVariant, - __in BOOL fHidden, - __in BOOL fSilent, - __out VERUTIL_VERSION** ppValue - ) -{ - HRESULT hr = S_OK; - - switch (pVariant->Type) - { - case BURN_VARIANT_TYPE_NUMERIC: - hr = VerVersionFromQword(pVariant->llValue, ppValue); - break; - case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; - case BURN_VARIANT_TYPE_STRING: - hr = VerParseVersion(pVariant->sczValue, 0, FALSE, ppValue); - if (SUCCEEDED(hr) && !fSilent && (*ppValue)->fInvalid) - { - LogId(REPORT_WARNING, MSG_INVALID_VERSION_COERSION, fHidden ? L"*****" : pVariant->sczValue); - } - break; - case BURN_VARIANT_TYPE_VERSION: - if (!pVariant->pValue) - { - *ppValue = NULL; - } - else - { - hr = VerCopyVersion(pVariant->pValue, ppValue); - } - break; - default: - hr = E_INVALIDARG; - break; - } - - return hr; -} - -extern "C" HRESULT BVariantSetNumeric( - __in BURN_VARIANT* pVariant, - __in LONGLONG llValue - ) -{ - HRESULT hr = S_OK; - - if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type || - BURN_VARIANT_TYPE_STRING == pVariant->Type) - { - StrSecureZeroFreeString(pVariant->sczValue); - } - memset(pVariant, 0, sizeof(BURN_VARIANT)); - pVariant->llValue = llValue; - pVariant->Type = BURN_VARIANT_TYPE_NUMERIC; - - return hr; -} - -extern "C" HRESULT BVariantSetString( - __in BURN_VARIANT* pVariant, - __in_z_opt LPCWSTR wzValue, - __in DWORD_PTR cchValue, - __in BOOL fFormatted - ) -{ - HRESULT hr = S_OK; - - if (!wzValue) // if we're nulling out the string, make the variable NONE. - { - BVariantUninitialize(pVariant); - } - else // assign the value. - { - if (BURN_VARIANT_TYPE_FORMATTED != pVariant->Type && - BURN_VARIANT_TYPE_STRING != pVariant->Type) - { - memset(pVariant, 0, sizeof(BURN_VARIANT)); - } - - hr = StrAllocStringSecure(&pVariant->sczValue, wzValue, cchValue); - ExitOnFailure(hr, "Failed to copy string."); - - pVariant->Type = fFormatted ? BURN_VARIANT_TYPE_FORMATTED : BURN_VARIANT_TYPE_STRING; - } - -LExit: - return hr; -} - -extern "C" HRESULT BVariantSetVersion( - __in BURN_VARIANT* pVariant, - __in VERUTIL_VERSION* pValue - ) -{ - HRESULT hr = S_OK; - - if (!pValue) // if we're nulling out the version, make the variable NONE. - { - BVariantUninitialize(pVariant); - } - else // assign the value. - { - if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type || - BURN_VARIANT_TYPE_STRING == pVariant->Type) - { - StrSecureZeroFreeString(pVariant->sczValue); - } - memset(pVariant, 0, sizeof(BURN_VARIANT)); - hr = VerCopyVersion(pValue, &pVariant->pValue); - pVariant->Type = BURN_VARIANT_TYPE_VERSION; - } - - return hr; -} - -extern "C" HRESULT BVariantSetValue( - __in BURN_VARIANT* pVariant, - __in BURN_VARIANT* pValue - ) -{ - HRESULT hr = S_OK; - - switch (pValue->Type) - { - case BURN_VARIANT_TYPE_NONE: - BVariantUninitialize(pVariant); - break; - case BURN_VARIANT_TYPE_NUMERIC: - hr = BVariantSetNumeric(pVariant, pValue->llValue); - break; - case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; - case BURN_VARIANT_TYPE_STRING: - hr = BVariantSetString(pVariant, pValue->sczValue, 0, BURN_VARIANT_TYPE_FORMATTED == pValue->Type); - break; - case BURN_VARIANT_TYPE_VERSION: - hr = BVariantSetVersion(pVariant, pValue->pValue); - break; - default: - hr = E_INVALIDARG; - } - ExitOnFailure(hr, "Failed to copy variant value."); - -LExit: - return hr; -} - -extern "C" HRESULT BVariantCopy( - __in BURN_VARIANT* pSource, - __out BURN_VARIANT* pTarget - ) -{ - return BVariantSetValue(pTarget, pSource); -} - -extern "C" HRESULT BVariantChangeType( - __in BURN_VARIANT* pVariant, - __in BURN_VARIANT_TYPE type - ) -{ - HRESULT hr = S_OK; - BURN_VARIANT variant = { }; - - if (pVariant->Type == type) - { - ExitFunction(); // variant already is of the requested type - } - else if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type && BURN_VARIANT_TYPE_STRING == type || - BURN_VARIANT_TYPE_STRING == pVariant->Type && BURN_VARIANT_TYPE_FORMATTED == type) - { - pVariant->Type = type; - ExitFunction(); - } - - switch (type) - { - case BURN_VARIANT_TYPE_NONE: - hr = S_OK; - break; - case BURN_VARIANT_TYPE_NUMERIC: - hr = BVariantGetNumeric(pVariant, &variant.llValue); - break; - case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; - case BURN_VARIANT_TYPE_STRING: - hr = BVariantGetString(pVariant, &variant.sczValue); - break; - case BURN_VARIANT_TYPE_VERSION: - hr = BVariantGetVersionSilent(pVariant, TRUE, &variant.pValue); - break; - default: - ExitFunction1(hr = E_INVALIDARG); - } - variant.Type = type; - ExitOnFailure(hr, "Failed to copy variant value."); - - BVariantUninitialize(pVariant); - memcpy_s(pVariant, sizeof(BURN_VARIANT), &variant, sizeof(BURN_VARIANT)); - SecureZeroMemory(&variant, sizeof(BURN_VARIANT)); - -LExit: - return hr; -} diff --git a/src/engine/variant.h b/src/engine/variant.h deleted file mode 100644 index e460005b..00000000 --- a/src/engine/variant.h +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#if defined(__cplusplus) -extern "C" { -#endif - - -// constants - -enum BURN_VARIANT_TYPE -{ - BURN_VARIANT_TYPE_NONE, - BURN_VARIANT_TYPE_FORMATTED, - BURN_VARIANT_TYPE_NUMERIC, - BURN_VARIANT_TYPE_STRING, // when formatting this value should be used as is (don't continue recursively formatting). - BURN_VARIANT_TYPE_VERSION, -}; - - -// struct - -typedef struct _BURN_VARIANT -{ - union - { - LONGLONG llValue; - VERUTIL_VERSION* pValue; - LPWSTR sczValue; - }; - BURN_VARIANT_TYPE Type; -} BURN_VARIANT; - - -// function declarations - -void BVariantUninitialize( - __in BURN_VARIANT* pVariant - ); -HRESULT BVariantGetNumeric( - __in BURN_VARIANT* pVariant, - __out LONGLONG* pllValue - ); -HRESULT BVariantGetString( - __in BURN_VARIANT* pVariant, - __out_z LPWSTR* psczValue - ); -HRESULT BVariantGetVersion( - __in BURN_VARIANT* pVariant, - __out VERUTIL_VERSION** ppValue - ); -HRESULT BVariantGetVersionHidden( - __in BURN_VARIANT* pVariant, - __in BOOL fHidden, - __out VERUTIL_VERSION** ppValue - ); -HRESULT BVariantGetVersionSilent( - __in BURN_VARIANT* pVariant, - __in BOOL fSilent, - __out VERUTIL_VERSION** ppValue - ); -HRESULT BVariantSetNumeric( - __in BURN_VARIANT* pVariant, - __in LONGLONG llValue - ); -HRESULT BVariantSetString( - __in BURN_VARIANT* pVariant, - __in_z_opt LPCWSTR wzValue, - __in DWORD_PTR cchValue, - __in BOOL fFormatted - ); -HRESULT BVariantSetVersion( - __in BURN_VARIANT* pVariant, - __in VERUTIL_VERSION* pValue - ); -/******************************************************************** -BVariantSetValue - Convenience function that calls BVariantUninitialize, - BVariantSetNumeric, BVariantSetString, or - BVariantSetVersion based on the type of pValue. -********************************************************************/ -HRESULT BVariantSetValue( - __in BURN_VARIANT* pVariant, - __in BURN_VARIANT* pValue - ); -/******************************************************************** -BVariantCopy - creates a copy of pSource. -********************************************************************/ -HRESULT BVariantCopy( - __in BURN_VARIANT* pSource, - __out BURN_VARIANT* pTarget - ); -HRESULT BVariantChangeType( - __in BURN_VARIANT* pVariant, - __in BURN_VARIANT_TYPE type - ); - -#if defined(__cplusplus) -} -#endif diff --git a/src/samples/burn/ManagedBundleRunner/BundleErrorEventArgs.cs b/src/samples/burn/ManagedBundleRunner/BundleErrorEventArgs.cs new file mode 100644 index 00000000..2c377326 --- /dev/null +++ b/src/samples/burn/ManagedBundleRunner/BundleErrorEventArgs.cs @@ -0,0 +1,33 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace Wix.Samples +{ + using System; + + /// + /// Arguments provided when bundle encounters an error. + /// + [Serializable] + public class BundleErrorEventArgs : EventArgs + { + /// + /// Gets the error code. + /// + public int Code { get; set; } + + /// + /// Gets the error message. + /// + public string Message { get; set; } + + /// + /// Gets the recommended display flags for an error dialog. + /// + public int UIHint { get; set; } + + /// + /// Gets or sets the of the operation. This is passed back to the bundle. + /// + public BundleResult Result { get; set; } + } +} diff --git a/src/samples/burn/ManagedBundleRunner/BundleProgressEventArgs.cs b/src/samples/burn/ManagedBundleRunner/BundleProgressEventArgs.cs new file mode 100644 index 00000000..ed42b5b1 --- /dev/null +++ b/src/samples/burn/ManagedBundleRunner/BundleProgressEventArgs.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace Wix.Samples +{ + using System; + + /// + /// Arguments provided when bundle progress is updated. + /// + [Serializable] + public class BundleProgressEventArgs : EventArgs + { + /// + /// Gets the percentage from 0 to 100 completed for a bundle. + /// + public int Progress { get; set; } + + /// + /// Gets or sets the of the operation. This is passed back to the bundle. + /// + public BundleResult Result { get; set; } + } +} diff --git a/src/samples/burn/ManagedBundleRunner/BundleResult.cs b/src/samples/burn/ManagedBundleRunner/BundleResult.cs new file mode 100644 index 00000000..c32644f4 --- /dev/null +++ b/src/samples/burn/ManagedBundleRunner/BundleResult.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace Wix.Samples +{ + /// + /// Result codes. + /// + public enum BundleResult + { + Error = -1, + None, + Ok, + Cancel, + Abort, + Retry, + Ignore, + Yes, + No, + Close, + Help, + TryAgain, + Continue, + } +} diff --git a/src/samples/burn/ManagedBundleRunner/BundleRunner.cs b/src/samples/burn/ManagedBundleRunner/BundleRunner.cs new file mode 100644 index 00000000..e2089787 --- /dev/null +++ b/src/samples/burn/ManagedBundleRunner/BundleRunner.cs @@ -0,0 +1,212 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace Wix.Samples +{ + using System; + using System.Diagnostics; + using System.IO.Pipes; + using System.Text; + using System.Threading; + + /// + /// Runs a bundle with provided command-line. + /// + public class BundleRunner + { + /// + /// Creates a runner for the provided bundle. + /// + /// Path to the bundle to run. + public BundleRunner(string bundle) + { + this.Path = bundle; + } + + /// + /// Fired when the bundle encounters an error. + /// + public event EventHandler Error; + + /// + /// Fired when the bundle progress is udpated. + /// + public event EventHandler Progress; + + /// + /// Gets the path to the bundle to run. + /// + public string Path { get; private set; } + + /// + /// Runs the bundle with the provided command-line. + /// + /// Optional command-line to pass to the bundle. + /// Exit code from the bundle. + public int Run(string commandLine = null) + { + WaitHandle[] waits = new WaitHandle[] { new ManualResetEvent(false), new ManualResetEvent(false) }; + int returnCode = 0; + int pid = Process.GetCurrentProcess().Id; + string pipeName = String.Concat("bpe_", pid); + string pipeSecret = Guid.NewGuid().ToString("N"); + + using (NamedPipeServerStream pipe = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1)) + { + using (Process bundleProcess = new Process()) + { + bundleProcess.StartInfo.FileName = this.Path; + bundleProcess.StartInfo.Arguments = String.Format("{0} -burn.embedded {1} {2} {3}", commandLine ?? String.Empty, pipeName, pipeSecret, pid); + bundleProcess.StartInfo.UseShellExecute = false; + bundleProcess.StartInfo.CreateNoWindow = true; + bundleProcess.Start(); + + Connect(pipe, pipeSecret, pid, bundleProcess.Id); + + PumpMessages(pipe); + + bundleProcess.WaitForExit(); + returnCode = bundleProcess.ExitCode; + } + } + + return returnCode; + } + + /// + /// Called when bundle encounters an error. + /// + /// Additional arguments for this event. + protected virtual void OnError(BundleErrorEventArgs e) + { + EventHandler handler = this.Error; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// Called when bundle progress is updated. + /// + /// Additional arguments for this event. + protected virtual void OnProgress(BundleProgressEventArgs e) + { + EventHandler handler = this.Progress; + if (handler != null) + { + handler(this, e); + } + } + + private void Connect(NamedPipeServerStream pipe, string pipeSecret, int pid, int childPid) + { + pipe.WaitForConnection(); + + WriteSecretToPipe(pipe, pipeSecret); + + WriteNumberToPipe(pipe, (uint)pid); + + uint ack = ReadNumberFromPipe(pipe); + // This is not true when bundle is run under a debugger + //if (ack != childPid) + //{ + // throw new ApplicationException("Incorrect child process."); + //} + } + + private void PumpMessages(NamedPipeServerStream pipe) + { + uint messageId; + while (TryReadNumberFromPipe(pipe, out messageId)) + { + uint messageSize = ReadNumberFromPipe(pipe); + + BundleResult result = BundleResult.None; + switch (messageId) + { + case 1: //error + result = ProcessErrorMessage(pipe); + break; + + case 2: // progress + result = ProcessProgressMessage(pipe); + break; + + default: // unknown message, do nothing. + break; + } + + CompleteMessage(pipe, result); + } + } + + private BundleResult ProcessErrorMessage(NamedPipeServerStream pipe) + { + BundleErrorEventArgs e = new BundleErrorEventArgs(); + e.Code = (int)ReadNumberFromPipe(pipe); + e.Message = ReadStringFromPipe(pipe); + e.UIHint = (int)ReadNumberFromPipe(pipe); + + this.OnError(e); + + return e.Result; + } + + private BundleResult ProcessProgressMessage(NamedPipeServerStream pipe) + { + ReadNumberFromPipe(pipe); // eat the first progress number because it is always zero. + + BundleProgressEventArgs e = new BundleProgressEventArgs(); + e.Progress = (int)ReadNumberFromPipe(pipe); + + this.OnProgress(e); + + return e.Result; + } + + private void CompleteMessage(NamedPipeServerStream pipe, BundleResult result) + { + uint complete = 0xF0000002; + WriteNumberToPipe(pipe, complete); + WriteNumberToPipe(pipe, 4); // size of message data + WriteNumberToPipe(pipe, (uint)result); + } + + private uint ReadNumberFromPipe(NamedPipeServerStream pipe) + { + byte[] buffer = new byte[4]; + pipe.Read(buffer, 0, buffer.Length); + return BitConverter.ToUInt32(buffer, 0); + } + + private string ReadStringFromPipe(NamedPipeServerStream pipe) + { + uint length = ReadNumberFromPipe(pipe); + + byte[] buffer = new byte[length * 2]; + pipe.Read(buffer, 0, buffer.Length); + + return Encoding.Unicode.GetString(buffer); + } + + private bool TryReadNumberFromPipe(NamedPipeServerStream pipe, out uint value) + { + value = ReadNumberFromPipe(pipe); // reading will not block and return zero if pipe is not connected. + return pipe.IsConnected; + } + + private void WriteNumberToPipe(NamedPipeServerStream pipe, uint value) + { + byte[] buffer = BitConverter.GetBytes(value); + pipe.Write(buffer, 0, buffer.Length); + } + + private void WriteSecretToPipe(NamedPipeServerStream pipe, string secret) + { + byte[] buffer = Encoding.Unicode.GetBytes(secret); + + WriteNumberToPipe(pipe, (uint)buffer.Length); + pipe.Write(buffer, 0, buffer.Length); + } + } +} diff --git a/src/samples/burn/runbundle/AssemblyInfo.cs b/src/samples/burn/runbundle/AssemblyInfo.cs new file mode 100644 index 00000000..3a66d5e3 --- /dev/null +++ b/src/samples/burn/runbundle/AssemblyInfo.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("Executable to demonstrate Bundle Runner Sample")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyCulture("")] +[assembly: CLSCompliant(true)] +[assembly: ComVisible(false)] diff --git a/src/samples/burn/runbundle/Program.cs b/src/samples/burn/runbundle/Program.cs new file mode 100644 index 00000000..8edca5dc --- /dev/null +++ b/src/samples/burn/runbundle/Program.cs @@ -0,0 +1,47 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace Wix.Samples +{ + using System; + using System.Linq; + using Wix.Samples; + + /// + /// Example executable that installs then immediately uninstalls a bundle showing progress. + /// + class Program + { + static int Main(string[] args) + { + if (args.Length == 0) + { + Console.WriteLine("Must provide the path to the bundle to install then uninstall."); + return -1; + } + + BundleRunner runner = new BundleRunner(args[0]); + runner.Error += Program.OnError; + runner.Progress += Program.OnProgress; + + Console.WriteLine("Installing: {0}", runner.Path); + int exitCode = runner.Run(String.Join(" ", args.Skip(1).ToArray())); + if (0 == exitCode) + { + Console.WriteLine("\r\nUninstalling: {0}", runner.Path); + exitCode = runner.Run("-uninstall"); + } + + return exitCode; + } + + static void OnError(object sender, BundleErrorEventArgs e) + { + Console.WriteLine("error: {0}, uiHint: {1}, message: {2}", e.Code, e.UIHint, e.Message); + } + + static void OnProgress(object sender, BundleProgressEventArgs e) + { + Console.WriteLine("progresss: {0}%", e.Progress); + } + } +} diff --git a/src/signing.json b/src/signing.json new file mode 100644 index 00000000..fe1c8c9b --- /dev/null +++ b/src/signing.json @@ -0,0 +1,13 @@ +{ + "SignClient": { + "AzureAd": { + "AADInstance": "https://login.microsoftonline.com/", + "ClientId": "c248d68a-ba6f-4aa9-8a68-71fe872063f8", + "TenantId": "16076fdc-fcc1-4a15-b1ca-32c9a255900e" + }, + "Service": { + "Url": "https://codesign.dotnetfoundation.org/", + "ResourceId": "https://SignService/3c30251f-36f3-490b-a955-520addb85001" + } + } +} diff --git a/src/stub/StubSection.cpp b/src/stub/StubSection.cpp deleted file mode 100644 index 962bb3cf..00000000 --- a/src/stub/StubSection.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -#pragma section(".wixburn",read) - -// If these defaults ever change, be sure to update constants in burn\engine\section.cpp as well. -#pragma data_seg(push, ".wixburn") -static DWORD dwMagic = 0x00f14300; -static DWORD dwVersion = 0x00000002; - -static GUID guidBundleId = { }; - -static DWORD dwStubSize = 0; -static DWORD dwOriginalChecksum = 0; -static DWORD dwOriginalSignatureOffset = 0; -static DWORD dwOriginalSignatureSize = 0; - -static DWORD dwContainerFormat = 1; -static DWORD dwContainerCount = 0; -static DWORD qwBootstrapperApplicationContainerSize = 0; -static DWORD qwAttachedContainerSize = 0; -#pragma data_seg(pop) diff --git a/src/stub/WixToolset.Burn.props b/src/stub/WixToolset.Burn.props deleted file mode 100644 index 38cd333e..00000000 --- a/src/stub/WixToolset.Burn.props +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - %(RecursiveDir)%(FileName)%(Extension) - False - PreserveNewest - - - diff --git a/src/stub/packages.config b/src/stub/packages.config deleted file mode 100644 index a98c0c8e..00000000 --- a/src/stub/packages.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/stub/precomp.cpp b/src/stub/precomp.cpp deleted file mode 100644 index 37664a1c..00000000 --- a/src/stub/precomp.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" diff --git a/src/stub/precomp.h b/src/stub/precomp.h deleted file mode 100644 index bb7ded9c..00000000 --- a/src/stub/precomp.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "engine.h" diff --git a/src/stub/stub.cpp b/src/stub/stub.cpp deleted file mode 100644 index 0cb202e0..00000000 --- a/src/stub/stub.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -static void CALLBACK BurnTraceError( - __in_z LPCSTR szFile, - __in int iLine, - __in REPORT_LEVEL rl, - __in UINT source, - __in HRESULT hrError, - __in_z __format_string LPCSTR szFormat, - __in va_list args - ); - -int WINAPI wWinMain( - __in HINSTANCE hInstance, - __in_opt HINSTANCE /* hPrevInstance */, - __in_z_opt LPWSTR lpCmdLine, - __in int nCmdShow - ) -{ - HRESULT hr = S_OK; - DWORD dwExitCode = 0; - LPWSTR sczPath = NULL; - HANDLE hEngineFile = INVALID_HANDLE_VALUE; - - LPCWSTR rgsczSafelyLoadSystemDlls[] = - { - L"cabinet.dll", // required by Burn. - L"msi.dll", // required by Burn. - L"version.dll", // required by Burn. - L"wininet.dll", // required by Burn. - - L"comres.dll", // required by CLSIDFromProgID() when loading clbcatq.dll. - L"clbcatq.dll", // required by CLSIDFromProgID() when loading msxml?.dll. - - L"msasn1.dll", // required by DecryptFile() when loading crypt32.dll. - L"crypt32.dll", // required by DecryptFile() when loading feclient.dll. - L"feclient.dll", // unsafely loaded by DecryptFile(). - }; - - DutilInitialize(&BurnTraceError); - - // Best effort attempt to get our file handle as soon as possible. - hr = PathForCurrentProcess(&sczPath, NULL); - if (SUCCEEDED(hr)) - { - hEngineFile = ::CreateFileW(sczPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - } - - // If the engine is in the clean room, we'll do the unsafe initialization - // because some systems in Windows (namely GDI+) will fail when run in - // a process that protects against DLL hijacking. Since we know the clean - // room is in a clean folder and not subject to DLL hijacking we won't - // make ourselves perfectly secure so that we can load BAs that still - // depend on those parts of Windows that are insecure to DLL hijacking. - if (EngineInCleanRoom(lpCmdLine)) - { - AppInitializeUnsafe(); - } - else - { - AppInitialize(rgsczSafelyLoadSystemDlls, countof(rgsczSafelyLoadSystemDlls)); - } - - // call run - hr = EngineRun(hInstance, hEngineFile, lpCmdLine, nCmdShow, &dwExitCode); - ExitOnFailure(hr, "Failed to run application."); - -LExit: - ReleaseFileHandle(hEngineFile); - ReleaseStr(sczPath); - - DutilUninitialize(); - - return FAILED(hr) ? (int)hr : (int)dwExitCode; -} - -static void CALLBACK BurnTraceError( - __in_z LPCSTR /*szFile*/, - __in int /*iLine*/, - __in REPORT_LEVEL /*rl*/, - __in UINT source, - __in HRESULT hrError, - __in_z __format_string LPCSTR szFormat, - __in va_list args - ) -{ - BOOL fLog = FALSE; - - switch (source) - { - case DUTIL_SOURCE_DEFAULT: - fLog = TRUE; - break; - default: - fLog = REPORT_VERBOSE < LogGetLevel(); - break; - } - - if (fLog) - { - LogErrorStringArgs(hrError, szFormat, args); - } -} diff --git a/src/stub/stub.ico b/src/stub/stub.ico deleted file mode 100644 index c2e2717c..00000000 Binary files a/src/stub/stub.ico and /dev/null differ diff --git a/src/stub/stub.nuspec b/src/stub/stub.nuspec deleted file mode 100644 index 968feff3..00000000 --- a/src/stub/stub.nuspec +++ /dev/null @@ -1,25 +0,0 @@ - - - - $id$ - $version$ - $title$ - $description$ - $authors$ - MS-RL - false - $copyright$ - $projectUrl$ - - - - - - - - - - - - - diff --git a/src/stub/stub.rc b/src/stub/stub.rc deleted file mode 100644 index 80e1aac4..00000000 --- a/src/stub/stub.rc +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -1 ICON "stub.ico" diff --git a/src/stub/stub.vcxproj b/src/stub/stub.vcxproj deleted file mode 100644 index 97972848..00000000 --- a/src/stub/stub.vcxproj +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - Debug - ARM64 - - - Release - ARM64 - - - - - {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1} - Application - Windows - burn - v142 - Unicode - false - Native component of WixToolset.Burn - - 1033 - Burn - WixToolset.Burn - false - - - - - - - - - - - - - - - - - $(ProjectDir)..\engine\inc - cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;wuguid.lib;engine.res - - - - - true - true - cabinet.dll;crypt32.dll;msi.dll;shlwapi.dll;version.dll;wininet.dll - - - - - - - - - Create - - - - - false - - - - - - - - - {8119537D-E1D9-6591-D51A-49768A2F9C37} - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}. - - - - - - - - - - - diff --git a/src/test/BurnUnitTest/AssemblyInfo.cpp b/src/test/BurnUnitTest/AssemblyInfo.cpp deleted file mode 100644 index 0282b1b7..00000000 --- a/src/test/BurnUnitTest/AssemblyInfo.cpp +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -using namespace System::Reflection; -using namespace System::Runtime::CompilerServices; -using namespace System::Runtime::InteropServices; - -[assembly: AssemblyTitleAttribute("Windows Installer XML Burn unit tests")]; -[assembly: AssemblyDescriptionAttribute("Burn unit tests")]; -[assembly: AssemblyCultureAttribute("")]; -[assembly: ComVisible(false)]; diff --git a/src/test/BurnUnitTest/BurnTestException.h b/src/test/BurnUnitTest/BurnTestException.h deleted file mode 100644 index bd94b4fc..00000000 --- a/src/test/BurnUnitTest/BurnTestException.h +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -namespace Microsoft -{ -namespace Tools -{ -namespace WindowsInstallerXml -{ -namespace Test -{ -namespace Bootstrapper -{ - using namespace System; - - public ref struct BurnTestException : public System::Exception - { - public: - BurnTestException(HRESULT error) - { - this->HResult = error; - } - - BurnTestException(HRESULT error, String^ message) - : Exception(message) - { - this->HResult = error; - } - - property Int32 ErrorCode - { - Int32 get() - { - return this->HResult; - } - } - - }; -} -} -} -} -} - -// this class is used by __TestThrowOnFailure_Format() below to deallocate -// the string created after the function call has returned -class __TestThrowOnFailure_StringFree -{ - LPWSTR m_scz; - -public: - __TestThrowOnFailure_StringFree(LPWSTR scz) - { - m_scz = scz; - } - - ~__TestThrowOnFailure_StringFree() - { - ReleaseStr(m_scz); - } - - operator LPCWSTR() - { - return m_scz; - } -}; - -// used by the TestThrowOnFailure macros to format the error string and -// return an LPCWSTR that can be used to initialize a System::String -#pragma warning (push) -#pragma warning (disable : 4793) -inline __TestThrowOnFailure_StringFree __TestThrowOnFailure_Format(LPCWSTR wzFormat, ...) -{ - Assert(wzFormat && *wzFormat); - - HRESULT hr = S_OK; - LPWSTR scz = NULL; - va_list args; - - va_start(args, wzFormat); - hr = StrAllocFormattedArgs(&scz, wzFormat, args); - va_end(args); - ExitOnFailure(hr, "Failed to format message string."); - -LExit: - return scz; -} -#pragma warning (pop) - -#define TestThrowOnFailure(hr, s) if (FAILED(hr)) { throw gcnew Microsoft::Tools::WindowsInstallerXml::Test::Bootstrapper::BurnTestException(hr, gcnew System::String(s)); } -#define TestThrowOnFailure1(hr, s, p) if (FAILED(hr)) { throw gcnew Microsoft::Tools::WindowsInstallerXml::Test::Bootstrapper::BurnTestException(hr, gcnew System::String(__TestThrowOnFailure_Format(s, p))); } -#define TestThrowOnFailure2(hr, s, p1, p2) if (FAILED(hr)) { throw gcnew Microsoft::Tools::WindowsInstallerXml::Test::Bootstrapper::BurnTestException(hr, gcnew System::String(__TestThrowOnFailure_Format(s, p1, p2))); } diff --git a/src/test/BurnUnitTest/BurnTestFixture.h b/src/test/BurnUnitTest/BurnTestFixture.h deleted file mode 100644 index 103972ef..00000000 --- a/src/test/BurnUnitTest/BurnTestFixture.h +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -namespace Microsoft -{ -namespace Tools -{ -namespace WindowsInstallerXml -{ -namespace Test -{ -namespace Bootstrapper -{ - using namespace System; - using namespace WixBuildTools::TestSupport; - - public ref class BurnTestFixture : IDisposable - { - public: - BurnTestFixture() - { - HRESULT hr = XmlInitialize(); - TestThrowOnFailure(hr, L"Failed to initialize XML support."); - - hr = RegInitialize(); - TestThrowOnFailure(hr, L"Failed to initialize Regutil."); - - hr = CrypInitialize(); - TestThrowOnFailure(hr, L"Failed to initialize Cryputil."); - - PlatformInitialize(); - - this->testDirectory = WixBuildTools::TestSupport::TestData::Get(); - - LogInitialize(::GetModuleHandleW(NULL)); - - LogSetLevel(REPORT_DEBUG, FALSE); - - hr = LogOpen(NULL, L"BurnUnitTest", NULL, L"txt", FALSE, FALSE, NULL); - TestThrowOnFailure(hr, L"Failed to open log."); - } - - ~BurnTestFixture() - { - CrypUninitialize(); - XmlUninitialize(); - RegUninitialize(); - LogUninitialize(FALSE); - } - - property String^ DataDirectory - { - String^ get() - { - return this->testDirectory; - } - } - - property String^ TestDirectory - { - String^ get() - { - return this->testDirectory; - } - } - - private: - String^ testDirectory; - }; -} -} -} -} -} diff --git a/src/test/BurnUnitTest/BurnUnitTest.h b/src/test/BurnUnitTest/BurnUnitTest.h deleted file mode 100644 index ed1d2956..00000000 --- a/src/test/BurnUnitTest/BurnUnitTest.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -namespace Microsoft -{ -namespace Tools -{ -namespace WindowsInstallerXml -{ -namespace Test -{ -namespace Bootstrapper -{ - using namespace System; - using namespace Xunit; - - [CollectionDefinition("Burn")] - public ref class BurnCollectionDefinition : ICollectionFixture - { - - }; - - [Collection("Burn")] - public ref class BurnUnitTest - { - public: - BurnUnitTest(BurnTestFixture^ fixture) - { - this->testContext = fixture; - } - - property BurnTestFixture^ TestContext - { - BurnTestFixture^ get() - { - return this->testContext; - } - } - - private: - BurnTestFixture^ testContext; - }; -} -} -} -} -} diff --git a/src/test/BurnUnitTest/BurnUnitTest.rc b/src/test/BurnUnitTest/BurnUnitTest.rc deleted file mode 100644 index 3a815db2..00000000 --- a/src/test/BurnUnitTest/BurnUnitTest.rc +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#define VER_APP -#define VER_ORIGINAL_FILENAME "BurnUnitTest.dll" -#define VER_INTERNAL_NAME "setup" -#define VER_FILE_DESCRIPTION "WiX Toolset Bootstrapper unit tests" diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj deleted file mode 100644 index 33c8ed6c..00000000 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - - - - Debug - ARM64 - - - Debug - Win32 - - - Release - ARM64 - - - Release - Win32 - - - - - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942} - {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67} - UnitTest - ManagedCProj - DynamicLibrary - Unicode - true - false - - - - - - - $(ProjectDir)..\..\..\..\balutil\src\WixToolset.BootstrapperCore.Native\inc - $(ProjectAdditionalIncludeDirectories);..\..\engine - cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib - - - - - - - - - - - Create - - 4564;4691 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ..\..\..\packages\WixBuildTools.TestSupport.4.0.50\lib\net472\WixBuildTools.TestSupport.dll - - - ..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.50\lib\net472\WixBuildTools.TestSupport.Native.dll - - - - - {8119537D-E1D9-6591-D51A-49770A2F9C37} - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - - diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters b/src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters deleted file mode 100644 index f9461f53..00000000 --- a/src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters +++ /dev/null @@ -1,80 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Resource Files - - - \ No newline at end of file diff --git a/src/test/BurnUnitTest/CacheTest.cpp b/src/test/BurnUnitTest/CacheTest.cpp deleted file mode 100644 index d0cc237f..00000000 --- a/src/test/BurnUnitTest/CacheTest.cpp +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -static HRESULT CALLBACK CacheTestEventRoutine( - __in BURN_CACHE_MESSAGE* pMessage, - __in LPVOID pvContext - ); - -static DWORD CALLBACK CacheTestProgressRoutine( - __in LARGE_INTEGER TotalFileSize, - __in LARGE_INTEGER TotalBytesTransferred, - __in LARGE_INTEGER StreamSize, - __in LARGE_INTEGER StreamBytesTransferred, - __in DWORD dwStreamNumber, - __in DWORD dwCallbackReason, - __in HANDLE hSourceFile, - __in HANDLE hDestinationFile, - __in_opt LPVOID lpData - ); - -typedef struct _CACHE_TEST_CONTEXT -{ -} CACHE_TEST_CONTEXT; - -namespace Microsoft -{ -namespace Tools -{ -namespace WindowsInstallerXml -{ -namespace Test -{ -namespace Bootstrapper -{ - using namespace System; - using namespace System::IO; - using namespace Xunit; - - public ref class CacheTest : BurnUnitTest - { - public: - CacheTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) - { - } - - [Fact] - void CacheSignatureTest() - { - HRESULT hr = S_OK; - BURN_PACKAGE package = { }; - BURN_PAYLOAD payload = { }; - LPWSTR sczPayloadPath = NULL; - BYTE* pb = NULL; - DWORD cb = NULL; - CACHE_TEST_CONTEXT context = { }; - - try - { - pin_ptr dataDirectory = PtrToStringChars(this->TestContext->TestDirectory); - hr = PathConcat(dataDirectory, L"TestData\\CacheTest\\CacheSignatureTest.File", &sczPayloadPath); - Assert::True(S_OK == hr, "Failed to get path to test file."); - Assert::True(FileExistsEx(sczPayloadPath, NULL), "Test file does not exist."); - - hr = StrAllocHexDecode(L"25e61cd83485062b70713aebddd3fe4992826cb121466fddc8de3eacb1e42f39d4bdd8455d95eec8c9529ced4c0296ab861931fe2c86df2f2b4e8d259a6d9223", &pb, &cb); - Assert::Equal(S_OK, hr); - - package.fPerMachine = FALSE; - package.sczCacheId = L"Bootstrapper.CacheTest.CacheSignatureTest"; - payload.sczKey = L"CacheSignatureTest.PayloadKey"; - payload.sczFilePath = L"CacheSignatureTest.File"; - payload.pbHash = pb; - payload.cbHash = cb; - - hr = CacheCompletePayload(package.fPerMachine, &payload, package.sczCacheId, sczPayloadPath, FALSE, CacheTestEventRoutine, CacheTestProgressRoutine, &context); - Assert::Equal(S_OK, hr); - } - finally - { - ReleaseMem(pb); - ReleaseStr(sczPayloadPath); - - String^ filePath = Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), "Package Cache\\Bootstrapper.CacheTest.CacheSignatureTest\\CacheSignatureTest.File"); - if (File::Exists(filePath)) - { - File::SetAttributes(filePath, FileAttributes::Normal); - File::Delete(filePath); - } - } - } - }; -} -} -} -} -} - -static HRESULT CALLBACK CacheTestEventRoutine( - __in BURN_CACHE_MESSAGE* /*pMessage*/, - __in LPVOID /*pvContext*/ - ) -{ - return S_OK; -} - -static DWORD CALLBACK CacheTestProgressRoutine( - __in LARGE_INTEGER /*TotalFileSize*/, - __in LARGE_INTEGER /*TotalBytesTransferred*/, - __in LARGE_INTEGER /*StreamSize*/, - __in LARGE_INTEGER /*StreamBytesTransferred*/, - __in DWORD /*dwStreamNumber*/, - __in DWORD /*dwCallbackReason*/, - __in HANDLE /*hSourceFile*/, - __in HANDLE /*hDestinationFile*/, - __in_opt LPVOID /*lpData*/ - ) -{ - return PROGRESS_QUIET; -} diff --git a/src/test/BurnUnitTest/ElevationTest.cpp b/src/test/BurnUnitTest/ElevationTest.cpp deleted file mode 100644 index 3d144128..00000000 --- a/src/test/BurnUnitTest/ElevationTest.cpp +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -const DWORD TEST_CHILD_SENT_MESSAGE_ID = 0xFFFE; -const DWORD TEST_PARENT_SENT_MESSAGE_ID = 0xFFFF; -const HRESULT S_TEST_SUCCEEDED = 0x3133; -const char TEST_MESSAGE_DATA[] = "{94949868-7EAE-4ac5-BEAC-AFCA2821DE01}"; - - -static BOOL STDAPICALLTYPE ElevateTest_ShellExecuteExW( - __inout LPSHELLEXECUTEINFOW lpExecInfo - ); -static DWORD CALLBACK ElevateTest_ThreadProc( - __in LPVOID lpThreadParameter - ); -static HRESULT ProcessParentMessages( - __in BURN_PIPE_MESSAGE* pMsg, - __in_opt LPVOID pvContext, - __out DWORD* pdwResult - ); -static HRESULT ProcessChildMessages( - __in BURN_PIPE_MESSAGE* pMsg, - __in_opt LPVOID pvContext, - __out DWORD* pdwResult - ); - -namespace Microsoft -{ -namespace Tools -{ -namespace WindowsInstallerXml -{ -namespace Test -{ -namespace Bootstrapper -{ - using namespace System; - using namespace System::IO; - using namespace System::Threading; - using namespace Xunit; - - public ref class ElevationTest : BurnUnitTest - { - public: - ElevationTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) - { - } - - [Fact] - void ElevateTest() - { - HRESULT hr = S_OK; - BURN_PIPE_CONNECTION connection = { }; - HANDLE hEvent = NULL; - DWORD dwResult = S_OK; - try - { - ShelFunctionOverride(ElevateTest_ShellExecuteExW); - - PipeConnectionInitialize(&connection); - - // - // per-user side setup - // - hr = PipeCreateNameAndSecret(&connection.sczName, &connection.sczSecret); - TestThrowOnFailure(hr, L"Failed to create connection name and secret."); - - hr = PipeCreatePipes(&connection, TRUE, &hEvent); - TestThrowOnFailure(hr, L"Failed to create pipes."); - - hr = PipeLaunchChildProcess(L"tests\\ignore\\this\\path\\to\\burn.exe", &connection, TRUE, NULL); - TestThrowOnFailure(hr, L"Failed to create elevated process."); - - hr = PipeWaitForChildConnect(&connection); - TestThrowOnFailure(hr, L"Failed to wait for child process to connect."); - - // post execute message - hr = PipeSendMessage(connection.hPipe, TEST_PARENT_SENT_MESSAGE_ID, NULL, 0, ProcessParentMessages, NULL, &dwResult); - TestThrowOnFailure(hr, "Failed to post execute message to per-machine process."); - - // - // initiate termination - // - hr = PipeTerminateChildProcess(&connection, 666, FALSE); - TestThrowOnFailure(hr, L"Failed to terminate elevated process."); - - // check flags - Assert::Equal(S_TEST_SUCCEEDED, (HRESULT)dwResult); - } - finally - { - PipeConnectionUninitialize(&connection); - ReleaseHandle(hEvent); - } - } - }; -} -} -} -} -} - - -static BOOL STDAPICALLTYPE ElevateTest_ShellExecuteExW( - __inout LPSHELLEXECUTEINFOW lpExecInfo - ) -{ - HRESULT hr = S_OK; - LPWSTR scz = NULL; - - hr = StrAllocString(&scz, lpExecInfo->lpParameters, 0); - ExitOnFailure(hr, "Failed to copy arguments."); - - // Pretend this thread is the elevated process. - lpExecInfo->hProcess = ::CreateThread(NULL, 0, ElevateTest_ThreadProc, scz, 0, NULL); - ExitOnNullWithLastError(lpExecInfo->hProcess, hr, "Failed to create thread."); - scz = NULL; - -LExit: - ReleaseStr(scz); - - return SUCCEEDED(hr); -} - -static DWORD CALLBACK ElevateTest_ThreadProc( - __in LPVOID lpThreadParameter - ) -{ - HRESULT hr = S_OK; - LPWSTR sczArguments = (LPWSTR)lpThreadParameter; - BURN_PIPE_CONNECTION connection = { }; - BURN_PIPE_RESULT result = { }; - - PipeConnectionInitialize(&connection); - - StrAlloc(&connection.sczName, MAX_PATH); - StrAlloc(&connection.sczSecret, MAX_PATH); - - // parse command line arguments - if (3 != swscanf_s(sczArguments, L"-q -burn.elevated %s %s %u", connection.sczName, MAX_PATH, connection.sczSecret, MAX_PATH, &connection.dwProcessId)) - { - hr = E_INVALIDARG; - ExitOnFailure(hr, "Failed to parse argument string."); - } - - // set up connection with per-user process - hr = PipeChildConnect(&connection, TRUE); - ExitOnFailure(hr, "Failed to connect to per-user process."); - - // pump messages - hr = PipePumpMessages(connection.hPipe, ProcessChildMessages, static_cast(connection.hPipe), &result); - ExitOnFailure(hr, "Failed while pumping messages in child 'process'."); - -LExit: - PipeConnectionUninitialize(&connection); - ReleaseStr(sczArguments); - - return FAILED(hr) ? (DWORD)hr : result.dwResult; -} - -static HRESULT ProcessParentMessages( - __in BURN_PIPE_MESSAGE* pMsg, - __in_opt LPVOID /*pvContext*/, - __out DWORD* pdwResult - ) -{ - HRESULT hr = S_OK; - HRESULT hrResult = E_INVALIDDATA; - - // Process the message. - switch (pMsg->dwMessage) - { - case TEST_CHILD_SENT_MESSAGE_ID: - if (sizeof(TEST_MESSAGE_DATA) == pMsg->cbData && 0 == memcmp(TEST_MESSAGE_DATA, pMsg->pvData, sizeof(TEST_MESSAGE_DATA))) - { - hrResult = S_TEST_SUCCEEDED; - } - break; - - default: - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Unexpected elevated message sent to parent process, msg: %u", pMsg->dwMessage); - } - - *pdwResult = static_cast(hrResult); - -LExit: - return hr; -} - -static HRESULT ProcessChildMessages( - __in BURN_PIPE_MESSAGE* pMsg, - __in_opt LPVOID pvContext, - __out DWORD* pdwResult - ) -{ - HRESULT hr = S_OK; - HANDLE hPipe = static_cast(pvContext); - DWORD dwResult = 0; - - // Process the message. - switch (pMsg->dwMessage) - { - case TEST_PARENT_SENT_MESSAGE_ID: - // send test message - hr = PipeSendMessage(hPipe, TEST_CHILD_SENT_MESSAGE_ID, (LPVOID)TEST_MESSAGE_DATA, sizeof(TEST_MESSAGE_DATA), NULL, NULL, &dwResult); - ExitOnFailure(hr, "Failed to send message to per-machine process."); - break; - - default: - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "Unexpected elevated message sent to child process, msg: %u", pMsg->dwMessage); - } - - *pdwResult = dwResult; - -LExit: - return hr; -} diff --git a/src/test/BurnUnitTest/ManifestHelpers.cpp b/src/test/BurnUnitTest/ManifestHelpers.cpp deleted file mode 100644 index 96d5fab4..00000000 --- a/src/test/BurnUnitTest/ManifestHelpers.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -using namespace System; -using namespace Xunit; - - -namespace Microsoft -{ -namespace Tools -{ -namespace WindowsInstallerXml -{ -namespace Test -{ -namespace Bootstrapper -{ - void LoadBundleXmlHelper(LPCWSTR wzDocument, IXMLDOMElement** ppixeBundle) - { - HRESULT hr = S_OK; - IXMLDOMDocument* pixdDocument = NULL; - try - { - hr = XmlLoadDocument(wzDocument, &pixdDocument); - TestThrowOnFailure(hr, L"Failed to load XML document."); - - hr = pixdDocument->get_documentElement(ppixeBundle); - TestThrowOnFailure(hr, L"Failed to get bundle element."); - } - finally - { - ReleaseObject(pixdDocument); - } - } -} -} -} -} -} diff --git a/src/test/BurnUnitTest/ManifestHelpers.h b/src/test/BurnUnitTest/ManifestHelpers.h deleted file mode 100644 index e3e57555..00000000 --- a/src/test/BurnUnitTest/ManifestHelpers.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -namespace Microsoft -{ -namespace Tools -{ -namespace WindowsInstallerXml -{ -namespace Test -{ -namespace Bootstrapper -{ - - -void LoadBundleXmlHelper(LPCWSTR wzDocument, IXMLDOMElement** ppixeBundle); - - -} -} -} -} -} diff --git a/src/test/BurnUnitTest/ManifestTest.cpp b/src/test/BurnUnitTest/ManifestTest.cpp deleted file mode 100644 index 963be156..00000000 --- a/src/test/BurnUnitTest/ManifestTest.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -namespace Microsoft -{ -namespace Tools -{ -namespace WindowsInstallerXml -{ -namespace Test -{ -namespace Bootstrapper -{ - using namespace System; - using namespace Xunit; - - public ref class ManifestTest : BurnUnitTest - { - public: - ManifestTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) - { - } - - [Fact] - void ManifestLoadXmlTest() - { - HRESULT hr = S_OK; - BURN_ENGINE_STATE engineState = { }; - try - { - LPCSTR szDocument = - "" - " " - " " - " " - " " - " "; - - hr = VariableInitialize(&engineState.variables); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - // load manifest from XML - hr = ManifestLoadXmlFromBuffer((BYTE*)szDocument, lstrlenA(szDocument), &engineState); - TestThrowOnFailure(hr, L"Failed to parse searches from XML."); - - // check variable values - Assert::True(VariableExistsHelper(&engineState.variables, L"Variable1")); - } - finally - { - //CoreUninitialize(&engineState); - } - } - }; -} -} -} -} -} diff --git a/src/test/BurnUnitTest/PlanTest.cpp b/src/test/BurnUnitTest/PlanTest.cpp deleted file mode 100644 index a7c1d83c..00000000 --- a/src/test/BurnUnitTest/PlanTest.cpp +++ /dev/null @@ -1,1473 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -static HRESULT WINAPI PlanTestBAProc( - __in BOOTSTRAPPER_APPLICATION_MESSAGE message, - __in const LPVOID pvArgs, - __inout LPVOID pvResults, - __in_opt LPVOID pvContext - ); - -static LPCWSTR wzMsiTransactionManifestFileName = L"MsiTransaction_BundleAv1_manifest.xml"; -static LPCWSTR wzSingleMsiManifestFileName = L"BasicFunctionality_BundleA_manifest.xml"; -static LPCWSTR wzSlipstreamManifestFileName = L"Slipstream_BundleA_manifest.xml"; - -namespace Microsoft -{ -namespace Tools -{ -namespace WindowsInstallerXml -{ -namespace Test -{ -namespace Bootstrapper -{ - using namespace System; - using namespace Xunit; - - public ref class PlanTest : BurnUnitTest - { - public: - PlanTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) - { - } - - [Fact] - void MsiTransactionInstallTest() - { - HRESULT hr = S_OK; - BURN_ENGINE_STATE engineState = { }; - BURN_ENGINE_STATE* pEngineState = &engineState; - BURN_PLAN* pPlan = &engineState.plan; - - InitializeEngineStateForCorePlan(wzMsiTransactionManifestFileName, pEngineState); - DetectPackagesAsAbsent(pEngineState); - DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"1.0.0.0"); - - hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL); - NativeAssert::Succeeded(hr, "CorePlan failed"); - - Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); - Assert::Equal(TRUE, pPlan->fPerMachine); - Assert::Equal(FALSE, pPlan->fDisableRollback); - - BOOL fRollback = FALSE; - DWORD dwIndex = 0; - ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); - ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); - ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 9); - ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageB"); - ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); - ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 14); - ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageC"); - ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); - Assert::Equal(dwIndex, pPlan->cCacheActions); - - fRollback = TRUE; - dwIndex = 0; - ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); - - Assert::Equal(107082ull, pPlan->qwEstimatedSize); - Assert::Equal(506145ull, pPlan->qwCacheSizeTotal); - - fRollback = FALSE; - dwIndex = 0; - DWORD dwExecuteCheckpointId = 2; - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteBeginMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); - ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[5].syncpoint.hEvent); - dwExecuteCheckpointId += 1; // cache checkpoints - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[8].syncpoint.hEvent); - dwExecuteCheckpointId += 1; // cache checkpoints - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCommitMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL); - Assert::Equal(dwIndex, pPlan->cExecuteActions); - - fRollback = TRUE; - dwIndex = 0; - dwExecuteCheckpointId = 2; - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageB"); - dwExecuteCheckpointId += 1; // cache checkpoints - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageC"); - dwExecuteCheckpointId += 1; // cache checkpoints - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL); - Assert::Equal(dwIndex, pPlan->cRollbackActions); - - Assert::Equal(4ul, pPlan->cExecutePackagesTotal); - Assert::Equal(7ul, pPlan->cOverallProgressTicksTotal); - - dwIndex = 0; - Assert::Equal(dwIndex, pPlan->cCleanActions); - - UINT uIndex = 0; - ValidatePlannedProvider(pPlan, uIndex++, L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", NULL); - Assert::Equal(uIndex, pPlan->cPlannedProviders); - - Assert::Equal(3ul, pEngineState->packages.cPackages); - ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); - ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); - ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); - } - - [Fact] - void MsiTransactionUninstallTest() - { - HRESULT hr = S_OK; - BURN_ENGINE_STATE engineState = { }; - BURN_ENGINE_STATE* pEngineState = &engineState; - BURN_PLAN* pPlan = &engineState.plan; - - InitializeEngineStateForCorePlan(wzMsiTransactionManifestFileName, pEngineState); - DetectPackagesAsPresentAndCached(pEngineState); - - hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); - NativeAssert::Succeeded(hr, "CorePlan failed"); - - Assert::Equal(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action); - Assert::Equal(TRUE, pPlan->fPerMachine); - Assert::Equal(FALSE, pPlan->fDisableRollback); - - BOOL fRollback = FALSE; - DWORD dwIndex = 0; - Assert::Equal(dwIndex, pPlan->cCacheActions); - - fRollback = TRUE; - dwIndex = 0; - Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); - - Assert::Equal(0ull, pPlan->qwEstimatedSize); - Assert::Equal(0ull, pPlan->qwCacheSizeTotal); - - fRollback = FALSE; - dwIndex = 0; - DWORD dwExecuteCheckpointId = 1; - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteBeginMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCommitMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - Assert::Equal(dwIndex, pPlan->cExecuteActions); - - fRollback = TRUE; - dwIndex = 0; - dwExecuteCheckpointId = 1; - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - Assert::Equal(dwIndex, pPlan->cRollbackActions); - - Assert::Equal(3ul, pPlan->cExecutePackagesTotal); - Assert::Equal(3ul, pPlan->cOverallProgressTicksTotal); - - dwIndex = 0; - ValidateCleanAction(pPlan, dwIndex++, L"PackageC"); - ValidateCleanAction(pPlan, dwIndex++, L"PackageB"); - ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); - Assert::Equal(dwIndex, pPlan->cCleanActions); - - UINT uIndex = 0; - ValidatePlannedProvider(pPlan, uIndex++, L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", NULL); - ValidatePlannedProvider(pPlan, uIndex++, L"{A497C5E5-C78B-4F0B-BF72-B33E1DB1C4B8}", NULL); - ValidatePlannedProvider(pPlan, uIndex++, L"{D1D01094-23CE-4AF0-84B6-4A1A133F21D3}", NULL); - ValidatePlannedProvider(pPlan, uIndex++, L"{01E6B748-7B95-4BA9-976D-B6F35076CEF4}", NULL); - Assert::Equal(uIndex, pPlan->cPlannedProviders); - - Assert::Equal(3ul, pEngineState->packages.cPackages); - ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); - ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); - ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); - } - - [Fact] - void RelatedBundleMissingFromCacheTest() - { - HRESULT hr = S_OK; - BURN_ENGINE_STATE engineState = { }; - BURN_ENGINE_STATE* pEngineState = &engineState; - BURN_PLAN* pPlan = &engineState.plan; - - InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); - DetectAttachedContainerAsAttached(pEngineState); - DetectPackagesAsAbsent(pEngineState); - BURN_RELATED_BUNDLE* pRelatedBundle = DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"0.9.0.0"); - pRelatedBundle->fPlannable = FALSE; - - hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL); - NativeAssert::Succeeded(hr, "CorePlan failed"); - - Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); - Assert::Equal(TRUE, pPlan->fPerMachine); - Assert::Equal(FALSE, pPlan->fDisableRollback); - - BOOL fRollback = FALSE; - DWORD dwIndex = 0; - ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); - ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); - Assert::Equal(dwIndex, pPlan->cCacheActions); - - fRollback = TRUE; - dwIndex = 0; - ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); - - Assert::Equal(35694ull, pPlan->qwEstimatedSize); - Assert::Equal(168715ull, pPlan->qwCacheSizeTotal); - - fRollback = FALSE; - dwIndex = 0; - DWORD dwExecuteCheckpointId = 2; - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - Assert::Equal(dwIndex, pPlan->cExecuteActions); - - fRollback = TRUE; - dwIndex = 0; - dwExecuteCheckpointId = 2; - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - Assert::Equal(dwIndex, pPlan->cRollbackActions); - - Assert::Equal(1ul, pPlan->cExecutePackagesTotal); - Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal); - - dwIndex = 0; - Assert::Equal(dwIndex, pPlan->cCleanActions); - - UINT uIndex = 0; - ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); - Assert::Equal(uIndex, pPlan->cPlannedProviders); - - Assert::Equal(1ul, pEngineState->packages.cPackages); - ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); - } - - [Fact] - void SingleMsiCacheTest() - { - HRESULT hr = S_OK; - BURN_ENGINE_STATE engineState = { }; - BURN_ENGINE_STATE* pEngineState = &engineState; - BURN_PLAN* pPlan = &engineState.plan; - - InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); - DetectAttachedContainerAsAttached(pEngineState); - DetectPackagesAsAbsent(pEngineState); - DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"0.9.0.0"); - - hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_CACHE); - NativeAssert::Succeeded(hr, "CorePlan failed"); - - Assert::Equal(BOOTSTRAPPER_ACTION_CACHE, pPlan->action); - Assert::Equal(TRUE, pPlan->fPerMachine); - Assert::Equal(FALSE, pPlan->fDisableRollback); - - BOOL fRollback = FALSE; - DWORD dwIndex = 0; - ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); - ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); - Assert::Equal(dwIndex, pPlan->cCacheActions); - - fRollback = TRUE; - dwIndex = 0; - Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); - - Assert::Equal(33743ull, pPlan->qwEstimatedSize); - Assert::Equal(168715ull, pPlan->qwCacheSizeTotal); - - fRollback = FALSE; - dwIndex = 0; - DWORD dwExecuteCheckpointId = 2; - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - Assert::Equal(dwIndex, pPlan->cExecuteActions); - - fRollback = TRUE; - dwIndex = 0; - dwExecuteCheckpointId = 2; - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - Assert::Equal(dwIndex, pPlan->cRollbackActions); - - Assert::Equal(0ul, pPlan->cExecutePackagesTotal); - Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal); - - dwIndex = 0; - Assert::Equal(dwIndex, pPlan->cCleanActions); - - UINT uIndex = 0; - ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); - Assert::Equal(uIndex, pPlan->cPlannedProviders); - - Assert::Equal(1ul, pEngineState->packages.cPackages); - ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); - } - - [Fact] - void SingleMsiInstallTest() - { - HRESULT hr = S_OK; - BURN_ENGINE_STATE engineState = { }; - BURN_ENGINE_STATE* pEngineState = &engineState; - BURN_PLAN* pPlan = &engineState.plan; - - InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); - DetectAttachedContainerAsAttached(pEngineState); - DetectPackagesAsAbsent(pEngineState); - DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"0.9.0.0"); - - hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL); - NativeAssert::Succeeded(hr, "CorePlan failed"); - - Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); - Assert::Equal(TRUE, pPlan->fPerMachine); - Assert::Equal(FALSE, pPlan->fDisableRollback); - - BOOL fRollback = FALSE; - DWORD dwIndex = 0; - ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); - ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); - Assert::Equal(dwIndex, pPlan->cCacheActions); - - fRollback = TRUE; - dwIndex = 0; - ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); - - Assert::Equal(35694ull, pPlan->qwEstimatedSize); - Assert::Equal(168715ull, pPlan->qwCacheSizeTotal); - - fRollback = FALSE; - dwIndex = 0; - DWORD dwExecuteCheckpointId = 2; - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL); - Assert::Equal(dwIndex, pPlan->cExecuteActions); - - fRollback = TRUE; - dwIndex = 0; - dwExecuteCheckpointId = 2; - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL); - Assert::Equal(dwIndex, pPlan->cRollbackActions); - - Assert::Equal(2ul, pPlan->cExecutePackagesTotal); - Assert::Equal(3ul, pPlan->cOverallProgressTicksTotal); - - dwIndex = 0; - Assert::Equal(dwIndex, pPlan->cCleanActions); - - UINT uIndex = 0; - ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); - Assert::Equal(uIndex, pPlan->cPlannedProviders); - - Assert::Equal(1ul, pEngineState->packages.cPackages); - ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); - } - - [Fact] - void SingleMsiInstalledWithNoInstalledPackagesModifyTest() - { - HRESULT hr = S_OK; - BURN_ENGINE_STATE engineState = { }; - BURN_ENGINE_STATE* pEngineState = &engineState; - BURN_PLAN* pPlan = &engineState.plan; - - InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); - DetectPackagesAsAbsent(pEngineState); - - pEngineState->registration.fInstalled = TRUE; - - hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_MODIFY); - NativeAssert::Succeeded(hr, "CorePlan failed"); - - Assert::Equal(BOOTSTRAPPER_ACTION_MODIFY, pPlan->action); - Assert::Equal(TRUE, pPlan->fPerMachine); - Assert::Equal(FALSE, pPlan->fDisableRollback); - - BOOL fRollback = FALSE; - DWORD dwIndex = 0; - Assert::Equal(dwIndex, pPlan->cCacheActions); - - fRollback = TRUE; - dwIndex = 0; - Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); - - Assert::Equal(0ull, pPlan->qwEstimatedSize); - Assert::Equal(0ull, pPlan->qwCacheSizeTotal); - - fRollback = FALSE; - dwIndex = 0; - DWORD dwExecuteCheckpointId = 1; - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - Assert::Equal(dwIndex, pPlan->cExecuteActions); - - fRollback = TRUE; - dwIndex = 0; - dwExecuteCheckpointId = 1; - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - Assert::Equal(dwIndex, pPlan->cRollbackActions); - - Assert::Equal(0ul, pPlan->cExecutePackagesTotal); - Assert::Equal(0ul, pPlan->cOverallProgressTicksTotal); - - dwIndex = 0; - ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); - Assert::Equal(dwIndex, pPlan->cCleanActions); - - UINT uIndex = 0; - ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); - Assert::Equal(uIndex, pPlan->cPlannedProviders); - - Assert::Equal(1ul, pEngineState->packages.cPackages); - ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); - } - - [Fact] - void SingleMsiUninstallTest() - { - HRESULT hr = S_OK; - BURN_ENGINE_STATE engineState = { }; - BURN_ENGINE_STATE* pEngineState = &engineState; - BURN_PLAN* pPlan = &engineState.plan; - - InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); - DetectPackagesAsPresentAndCached(pEngineState); - - hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); - NativeAssert::Succeeded(hr, "CorePlan failed"); - - Assert::Equal(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action); - Assert::Equal(TRUE, pPlan->fPerMachine); - Assert::Equal(FALSE, pPlan->fDisableRollback); - - BOOL fRollback = FALSE; - DWORD dwIndex = 0; - Assert::Equal(dwIndex, pPlan->cCacheActions); - - fRollback = TRUE; - dwIndex = 0; - Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); - - Assert::Equal(0ull, pPlan->qwEstimatedSize); - Assert::Equal(0ull, pPlan->qwCacheSizeTotal); - - fRollback = FALSE; - dwIndex = 0; - DWORD dwExecuteCheckpointId = 1; - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - Assert::Equal(dwIndex, pPlan->cExecuteActions); - - fRollback = TRUE; - dwIndex = 0; - dwExecuteCheckpointId = 1; - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - Assert::Equal(dwIndex, pPlan->cRollbackActions); - - Assert::Equal(1ul, pPlan->cExecutePackagesTotal); - Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal); - - dwIndex = 0; - ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); - Assert::Equal(dwIndex, pPlan->cCleanActions); - - UINT uIndex = 0; - ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); - ValidatePlannedProvider(pPlan, uIndex++, L"{64633047-D172-4BBB-B202-64337D15C952}", NULL); - Assert::Equal(uIndex, pPlan->cPlannedProviders); - - Assert::Equal(1ul, pEngineState->packages.cPackages); - ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); - } - - [Fact] - void SingleMsiUninstallTestFromUpgradeBundleWithSameExactPackage() - { - HRESULT hr = S_OK; - BURN_ENGINE_STATE engineState = { }; - BURN_ENGINE_STATE* pEngineState = &engineState; - BURN_PLAN* pPlan = &engineState.plan; - - InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); - DetectAsRelatedUpgradeBundle(&engineState, L"{02940F3E-C83E-452D-BFCF-C943777ACEAE}", L"2.0.0.0"); - - hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); - NativeAssert::Succeeded(hr, "CorePlan failed"); - - Assert::Equal(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action); - Assert::Equal(TRUE, pPlan->fPerMachine); - Assert::Equal(FALSE, pPlan->fDisableRollback); - - BOOL fRollback = FALSE; - DWORD dwIndex = 0; - Assert::Equal(dwIndex, pPlan->cCacheActions); - - fRollback = TRUE; - dwIndex = 0; - Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); - - Assert::Equal(0ull, pPlan->qwEstimatedSize); - Assert::Equal(0ull, pPlan->qwCacheSizeTotal); - - fRollback = FALSE; - dwIndex = 0; - DWORD dwExecuteCheckpointId = 1; - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - Assert::Equal(dwIndex, pPlan->cExecuteActions); - - fRollback = TRUE; - dwIndex = 0; - dwExecuteCheckpointId = 1; - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - Assert::Equal(dwIndex, pPlan->cRollbackActions); - - Assert::Equal(0ul, pPlan->cExecutePackagesTotal); - Assert::Equal(0ul, pPlan->cOverallProgressTicksTotal); - - dwIndex = 0; - Assert::Equal(dwIndex, pPlan->cCleanActions); - - UINT uIndex = 0; - ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); - Assert::Equal(uIndex, pPlan->cPlannedProviders); - - Assert::Equal(1ul, pEngineState->packages.cPackages); - ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_IGNORED, BURN_PACKAGE_REGISTRATION_STATE_IGNORED); - } - - [Fact] - void SlipstreamInstallTest() - { - HRESULT hr = S_OK; - BURN_ENGINE_STATE engineState = { }; - BURN_ENGINE_STATE* pEngineState = &engineState; - BURN_PLAN* pPlan = &engineState.plan; - - InitializeEngineStateForCorePlan(wzSlipstreamManifestFileName, pEngineState); - DetectPermanentPackagesAsPresentAndCached(pEngineState); - PlanTestDetectPatchInitialize(pEngineState); - - hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL); - NativeAssert::Succeeded(hr, "CorePlan failed"); - - Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); - Assert::Equal(TRUE, pPlan->fPerMachine); - Assert::Equal(FALSE, pPlan->fDisableRollback); - - BOOL fRollback = FALSE; - DWORD dwIndex = 0; - ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PatchA"); - ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); - ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 2); - ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); - ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); - Assert::Equal(dwIndex, pPlan->cCacheActions); - - fRollback = TRUE; - dwIndex = 0; - ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 2); - Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); - - Assert::Equal(3055111ull, pPlan->qwEstimatedSize); - Assert::Equal(212992ull, pPlan->qwCacheSizeTotal); - - fRollback = FALSE; - dwIndex = 0; - DWORD dwExecuteCheckpointId = 3; - BURN_EXECUTE_ACTION* pExecuteAction = NULL; - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[5].syncpoint.hEvent); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_REGISTER); - pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_INSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE); - ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA"); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - Assert::Equal(dwIndex, pPlan->cExecuteActions); - - fRollback = TRUE; - dwIndex = 0; - dwExecuteCheckpointId = 3; - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PatchA"); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE); - ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA"); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - Assert::Equal(dwIndex, pPlan->cRollbackActions); - - Assert::Equal(2ul, pPlan->cExecutePackagesTotal); - Assert::Equal(4ul, pPlan->cOverallProgressTicksTotal); - - dwIndex = 0; - Assert::Equal(dwIndex, pPlan->cCleanActions); - - UINT uIndex = 0; - ValidatePlannedProvider(pPlan, uIndex++, L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", NULL); - Assert::Equal(uIndex, pPlan->cPlannedProviders); - - Assert::Equal(3ul, pEngineState->packages.cPackages); - ValidatePermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"NetFx48Web"); - ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); - ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PatchA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); - } - - [Fact] - void SlipstreamUninstallTest() - { - HRESULT hr = S_OK; - BURN_ENGINE_STATE engineState = { }; - BURN_ENGINE_STATE* pEngineState = &engineState; - BURN_PLAN* pPlan = &engineState.plan; - - InitializeEngineStateForCorePlan(wzSlipstreamManifestFileName, pEngineState); - DetectPackagesAsPresentAndCached(pEngineState); - - hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); - NativeAssert::Succeeded(hr, "CorePlan failed"); - - Assert::Equal(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action); - Assert::Equal(TRUE, pPlan->fPerMachine); - Assert::Equal(FALSE, pPlan->fDisableRollback); - - BOOL fRollback = FALSE; - DWORD dwIndex = 0; - Assert::Equal(dwIndex, pPlan->cCacheActions); - - fRollback = TRUE; - dwIndex = 0; - Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); - - Assert::Equal(0ull, pPlan->qwEstimatedSize); - Assert::Equal(0ull, pPlan->qwCacheSizeTotal); - - fRollback = FALSE; - dwIndex = 0; - DWORD dwExecuteCheckpointId = 1; - BURN_EXECUTE_ACTION* pExecuteAction = NULL; - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_UNREGISTER); - pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE); - ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA"); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - Assert::Equal(dwIndex, pPlan->cExecuteActions); - - fRollback = TRUE; - dwIndex = 0; - dwExecuteCheckpointId = 1; - ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_INSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE); - ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA"); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - Assert::Equal(dwIndex, pPlan->cRollbackActions); - - Assert::Equal(2ul, pPlan->cExecutePackagesTotal); - Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal); - - dwIndex = 0; - ValidateCleanAction(pPlan, dwIndex++, L"PatchA"); - ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); - Assert::Equal(dwIndex, pPlan->cCleanActions); - - UINT uIndex = 0; - ValidatePlannedProvider(pPlan, uIndex++, L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", NULL); - ValidatePlannedProvider(pPlan, uIndex++, L"{0A5113E3-06A5-4CE0-8E83-9EB42F6764A6}", NULL); - ValidatePlannedProvider(pPlan, uIndex++, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", NULL); - Assert::Equal(uIndex, pPlan->cPlannedProviders); - - Assert::Equal(3ul, pEngineState->packages.cPackages); - ValidatePermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"NetFx48Web"); - ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); - ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PatchA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); - } - - private: - // This doesn't initialize everything, just enough for CorePlan to work. - void InitializeEngineStateForCorePlan(LPCWSTR wzManifestFileName, BURN_ENGINE_STATE* pEngineState) - { - HRESULT hr = S_OK; - LPWSTR sczFilePath = NULL; - - ::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive); - - hr = VariableInitialize(&pEngineState->variables); - NativeAssert::Succeeded(hr, "Failed to initialize variables."); - - try - { - pin_ptr dataDirectory = PtrToStringChars(this->TestContext->TestDirectory); - hr = PathConcat(dataDirectory, L"TestData\\PlanTest", &sczFilePath); - NativeAssert::Succeeded(hr, "Failed to get path to test file directory."); - hr = PathConcat(sczFilePath, wzManifestFileName, &sczFilePath); - NativeAssert::Succeeded(hr, "Failed to get path to test file."); - Assert::True(FileExistsEx(sczFilePath, NULL), "Test file does not exist."); - - hr = ManifestLoadXmlFromFile(sczFilePath, pEngineState); - NativeAssert::Succeeded(hr, "Failed to load manifest."); - } - finally - { - ReleaseStr(sczFilePath); - } - - hr = CoreInitializeConstants(pEngineState); - NativeAssert::Succeeded(hr, "Failed to initialize core constants"); - - pEngineState->userExperience.pfnBAProc = PlanTestBAProc; - } - - void PlanTestDetect(BURN_ENGINE_STATE* pEngineState) - { - HRESULT hr = S_OK; - BURN_REGISTRATION* pRegistration = &pEngineState->registration; - - DetectReset(pRegistration, &pEngineState->packages); - PlanReset(&pEngineState->plan, &pEngineState->containers, &pEngineState->packages, &pEngineState->layoutPayloads); - - hr = DepDependencyArrayAlloc(&pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies, pRegistration->sczProviderKey, NULL); - NativeAssert::Succeeded(hr, "Failed to add the bundle provider key to the list of dependencies to ignore."); - - pEngineState->userExperience.fEngineActive = TRUE; - pEngineState->fDetected = TRUE; - } - - void PlanTestDetectPatchInitialize(BURN_ENGINE_STATE* pEngineState) - { - HRESULT hr = MsiEngineDetectInitialize(&pEngineState->packages); - NativeAssert::Succeeded(hr, "MsiEngineDetectInitialize failed"); - - for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) - { - BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; - - if (BURN_PACKAGE_TYPE_MSP == pPackage->type) - { - for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j) - { - BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + j; - - if (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN == pTargetProduct->patchPackageState) - { - pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; - } - } - } - } - } - - void DetectAttachedContainerAsAttached(BURN_ENGINE_STATE* pEngineState) - { - for (DWORD i = 0; i < pEngineState->containers.cContainers; ++i) - { - BURN_CONTAINER* pContainer = pEngineState->containers.rgContainers + i; - if (pContainer->fAttached) - { - pContainer->fActuallyAttached = TRUE; - } - } - } - - void DetectPackageAsAbsent(BURN_PACKAGE* pPackage) - { - pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; - if (pPackage->fCanAffectRegistration) - { - pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; - } - } - - void DetectPackageAsPresentAndCached(BURN_PACKAGE* pPackage) - { - pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; - pPackage->fCached = TRUE; - if (pPackage->fCanAffectRegistration) - { - pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } - } - - void DetectPackageDependent(BURN_PACKAGE* pPackage, LPCWSTR wzId) - { - HRESULT hr = S_OK; - - for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) - { - BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + i; - - hr = DepDependencyArrayAlloc(&pProvider->rgDependents, &pProvider->cDependents, wzId, NULL); - NativeAssert::Succeeded(hr, "Failed to add package dependent"); - } - } - - void DetectPackagesAsAbsent(BURN_ENGINE_STATE* pEngineState) - { - PlanTestDetect(pEngineState); - - for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) - { - BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; - DetectPackageAsAbsent(pPackage); - } - } - - void DetectPackagesAsPresentAndCached(BURN_ENGINE_STATE* pEngineState) - { - PlanTestDetect(pEngineState); - - pEngineState->registration.fInstalled = TRUE; - - for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) - { - BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; - DetectPackageAsPresentAndCached(pPackage); - DetectPackageDependent(pPackage, pEngineState->registration.sczId); - - if (BURN_PACKAGE_TYPE_MSI == pPackage->type) - { - for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) - { - pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; - - BURN_PACKAGE* pMspPackage = pPackage->Msi.rgSlipstreamMsps[j].pMspPackage; - MspEngineAddDetectedTargetProduct(&pEngineState->packages, pMspPackage, j, pPackage->Msi.sczProductCode, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED); - - BURN_MSPTARGETPRODUCT* pTargetProduct = pMspPackage->Msp.rgTargetProducts + (pMspPackage->Msp.cTargetProductCodes - 1); - pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; - pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; - } - } - } - } - - void DetectPermanentPackagesAsPresentAndCached(BURN_ENGINE_STATE* pEngineState) - { - PlanTestDetect(pEngineState); - - pEngineState->registration.fInstalled = TRUE; - - for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) - { - BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; - if (pPackage->fUninstallable) - { - DetectPackageAsAbsent(pPackage); - } - else - { - DetectPackageAsPresentAndCached(pPackage); - DetectPackageDependent(pPackage, pEngineState->registration.sczId); - } - } - } - - BURN_RELATED_BUNDLE* DetectUpgradeBundle( - __in BURN_ENGINE_STATE* pEngineState, - __in LPCWSTR wzId, - __in LPCWSTR wzVersion - ) - { - HRESULT hr = S_OK; - BURN_RELATED_BUNDLES* pRelatedBundles = &pEngineState->registration.relatedBundles; - BURN_DEPENDENCY_PROVIDER dependencyProvider = { }; - - hr = StrAllocString(&dependencyProvider.sczKey, wzId, 0); - NativeAssert::Succeeded(hr, "Failed to copy provider key"); - - dependencyProvider.fImported = TRUE; - - hr = StrAllocString(&dependencyProvider.sczVersion, wzVersion, 0); - NativeAssert::Succeeded(hr, "Failed to copy version"); - - hr = MemEnsureArraySize(reinterpret_cast(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5); - NativeAssert::Succeeded(hr, "Failed to ensure there is space for related bundles."); - - BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles; - - hr = VerParseVersion(wzVersion, 0, FALSE, &pRelatedBundle->pVersion); - NativeAssert::Succeeded(hr, "Failed to parse pseudo bundle version: %ls", wzVersion); - - pRelatedBundle->fPlannable = TRUE; - pRelatedBundle->relationType = BOOTSTRAPPER_RELATION_UPGRADE; - - hr = PseudoBundleInitialize(0, &pRelatedBundle->package, TRUE, wzId, pRelatedBundle->relationType, BOOTSTRAPPER_PACKAGE_STATE_PRESENT, TRUE, NULL, NULL, NULL, 0, FALSE, L"-quiet", L"-repair -quiet", L"-uninstall -quiet", &dependencyProvider, NULL, 0); - NativeAssert::Succeeded(hr, "Failed to initialize related bundle to represent bundle: %ls", wzId); - - ++pRelatedBundles->cRelatedBundles; - - return pRelatedBundle; - } - - void DetectAsRelatedUpgradeBundle( - __in BURN_ENGINE_STATE* pEngineState, - __in LPCWSTR wzId, - __in LPCWSTR wzVersion - ) - { - HRESULT hr = StrAllocString(&pEngineState->registration.sczAncestors, wzId, 0); - NativeAssert::Succeeded(hr, "Failed to set registration's ancestors"); - - pEngineState->command.relationType = BOOTSTRAPPER_RELATION_UPGRADE; - - DetectPackagesAsPresentAndCached(pEngineState); - DetectUpgradeBundle(pEngineState, wzId, wzVersion); - - for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) - { - BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; - DetectPackageDependent(pPackage, wzId); - } - } - - void ValidateCacheContainer( - __in BURN_PLAN* pPlan, - __in BOOL fRollback, - __in DWORD dwIndex, - __in LPCWSTR wzContainerId - ) - { - BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_CACHE_ACTION_TYPE_CONTAINER, pAction->type); - NativeAssert::StringEqual(wzContainerId, pAction->container.pContainer->sczId); - } - - BURN_CACHE_ACTION* ValidateCacheActionExists(BURN_PLAN* pPlan, BOOL fRollback, DWORD dwIndex) - { - Assert::InRange(dwIndex + 1ul, 1ul, (fRollback ? pPlan->cRollbackCacheActions : pPlan->cCacheActions)); - return (fRollback ? pPlan->rgRollbackCacheActions : pPlan->rgCacheActions) + dwIndex; - } - - void ValidateCacheCheckpoint( - __in BURN_PLAN* pPlan, - __in BOOL fRollback, - __in DWORD dwIndex, - __in DWORD dwId - ) - { - BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_CACHE_ACTION_TYPE_CHECKPOINT, pAction->type); - Assert::Equal(dwId, pAction->checkpoint.dwId); - } - - DWORD ValidateCachePackage( - __in BURN_PLAN* pPlan, - __in BOOL fRollback, - __in DWORD dwIndex, - __in LPCWSTR wzPackageId - ) - { - BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_CACHE_ACTION_TYPE_PACKAGE, pAction->type); - NativeAssert::StringEqual(wzPackageId, pAction->package.pPackage->sczId); - return dwIndex + 1; - } - - void ValidateCacheRollbackPackage( - __in BURN_PLAN* pPlan, - __in BOOL fRollback, - __in DWORD dwIndex, - __in LPCWSTR wzPackageId - ) - { - BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE, pAction->type); - NativeAssert::StringEqual(wzPackageId, pAction->rollbackPackage.pPackage->sczId); - } - - void ValidateCacheSignalSyncpoint( - __in BURN_PLAN* pPlan, - __in BOOL fRollback, - __in DWORD dwIndex - ) - { - BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT, pAction->type); - Assert::NotEqual((DWORD_PTR)NULL, (DWORD_PTR)pAction->syncpoint.hEvent); - } - - void ValidateCleanAction( - __in BURN_PLAN* pPlan, - __in DWORD dwIndex, - __in LPCWSTR wzPackageId - ) - { - Assert::InRange(dwIndex + 1ul, 1ul, pPlan->cCleanActions); - - BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + dwIndex; - Assert::NotEqual((DWORD_PTR)0, (DWORD_PTR)pCleanAction->pPackage); - NativeAssert::StringEqual(wzPackageId, pCleanAction->pPackage->sczId); - } - - BURN_EXECUTE_ACTION* ValidateExecuteActionExists(BURN_PLAN* pPlan, BOOL fRollback, DWORD dwIndex) - { - Assert::InRange(dwIndex + 1ul, 1ul, (fRollback ? pPlan->cRollbackActions : pPlan->cExecuteActions)); - return (fRollback ? pPlan->rgRollbackActions : pPlan->rgExecuteActions) + dwIndex; - } - - void ValidateExecuteBeginMsiTransaction( - __in BURN_PLAN* pPlan, - __in BOOL fRollback, - __in DWORD dwIndex, - __in LPCWSTR wzRollbackBoundaryId - ) - { - BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION, pAction->type); - NativeAssert::StringEqual(wzRollbackBoundaryId, pAction->msiTransaction.pRollbackBoundary->sczId); - Assert::Equal(FALSE, pAction->fDeleted); - } - - void ValidateExecuteCheckpoint( - __in BURN_PLAN* pPlan, - __in BOOL fRollback, - __in DWORD dwIndex, - __in DWORD dwId - ) - { - BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_EXECUTE_ACTION_TYPE_CHECKPOINT, pAction->type); - Assert::Equal(dwId, pAction->checkpoint.dwId); - Assert::Equal(FALSE, pAction->fDeleted); - } - - void ValidateExecuteCommitMsiTransaction( - __in BURN_PLAN* pPlan, - __in BOOL fRollback, - __in DWORD dwIndex, - __in LPCWSTR wzRollbackBoundaryId - ) - { - BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION, pAction->type); - NativeAssert::StringEqual(wzRollbackBoundaryId, pAction->msiTransaction.pRollbackBoundary->sczId); - Assert::Equal(FALSE, pAction->fDeleted); - } - - void ValidateExecuteExePackage( - __in BURN_PLAN* pPlan, - __in BOOL fRollback, - __in DWORD dwIndex, - __in LPCWSTR wzPackageId, - __in BOOTSTRAPPER_ACTION_STATE action, - __in LPCWSTR wzIgnoreDependencies - ) - { - BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE, pAction->type); - NativeAssert::StringEqual(wzPackageId, pAction->exePackage.pPackage->sczId); - Assert::Equal(action, pAction->exePackage.action); - NativeAssert::StringEqual(wzIgnoreDependencies, pAction->exePackage.sczIgnoreDependencies); - Assert::Equal(FALSE, pAction->fDeleted); - } - - void ValidateExecuteMsiPackage( - __in BURN_PLAN* pPlan, - __in BOOL fRollback, - __in DWORD dwIndex, - __in_z LPCWSTR wzPackageId, - __in BOOTSTRAPPER_ACTION_STATE action, - __in BURN_MSI_PROPERTY actionMsiProperty, - __in DWORD uiLevel, - __in BOOL fDisableExternalUiHandler, - __in DWORD dwLoggingAttributes - ) - { - BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE, pAction->type); - NativeAssert::StringEqual(wzPackageId, pAction->msiPackage.pPackage->sczId); - Assert::Equal(action, pAction->msiPackage.action); - Assert::Equal(actionMsiProperty, pAction->msiPackage.actionMsiProperty); - Assert::Equal(uiLevel, pAction->msiPackage.uiLevel); - Assert::Equal(fDisableExternalUiHandler, pAction->msiPackage.fDisableExternalUiHandler); - NativeAssert::NotNull(pAction->msiPackage.sczLogPath); - Assert::Equal(dwLoggingAttributes, pAction->msiPackage.dwLoggingAttributes); - Assert::Equal(FALSE, pAction->fDeleted); - } - - BURN_EXECUTE_ACTION* ValidateDeletedExecuteMspTarget( - __in BURN_PLAN* pPlan, - __in BOOL fRollback, - __in DWORD dwIndex, - __in_z LPCWSTR wzPackageId, - __in BOOTSTRAPPER_ACTION_STATE action, - __in_z LPCWSTR wzTargetProductCode, - __in BOOL fPerMachineTarget, - __in BURN_MSI_PROPERTY actionMsiProperty, - __in DWORD uiLevel, - __in BOOL fDisableExternalUiHandler, - __in BOOL fDeleted - ) - { - BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_EXECUTE_ACTION_TYPE_MSP_TARGET, pAction->type); - NativeAssert::StringEqual(wzPackageId, pAction->mspTarget.pPackage->sczId); - Assert::Equal(action, pAction->mspTarget.action); - NativeAssert::StringEqual(wzTargetProductCode, pAction->mspTarget.sczTargetProductCode); - Assert::Equal(fPerMachineTarget, pAction->mspTarget.fPerMachineTarget); - Assert::Equal(actionMsiProperty, pAction->mspTarget.actionMsiProperty); - Assert::Equal(uiLevel, pAction->mspTarget.uiLevel); - Assert::Equal(fDisableExternalUiHandler, pAction->mspTarget.fDisableExternalUiHandler); - NativeAssert::NotNull(pAction->mspTarget.sczLogPath); - Assert::Equal(fDeleted, pAction->fDeleted); - return pAction; - } - - void ValidateExecuteMspTargetPatch( - __in BURN_EXECUTE_ACTION* pAction, - __in DWORD dwIndex, - __in_z LPCWSTR wzPackageId - ) - { - Assert::InRange(dwIndex + 1ul, 1ul, pAction->mspTarget.cOrderedPatches); - BURN_ORDERED_PATCHES* pOrderedPatch = pAction->mspTarget.rgOrderedPatches + dwIndex; - NativeAssert::StringEqual(wzPackageId, pOrderedPatch->pPackage->sczId); - } - - void ValidateExecutePackageDependency( - __in BURN_PLAN* pPlan, - __in BOOL fRollback, - __in DWORD dwIndex, - __in LPCWSTR wzPackageId, - __in LPCWSTR wzBundleProviderKey, - __in BURN_DEPENDENCY_ACTION action - ) - { - BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY, pAction->type); - NativeAssert::StringEqual(wzPackageId, pAction->packageDependency.pPackage->sczId); - NativeAssert::StringEqual(wzBundleProviderKey, pAction->packageDependency.sczBundleProviderKey); - Assert::Equal(action, pAction->packageDependency.action); - Assert::Equal(FALSE, pAction->fDeleted); - } - - void ValidateExecutePackageProvider( - __in BURN_PLAN* pPlan, - __in BOOL fRollback, - __in DWORD dwIndex, - __in LPCWSTR wzPackageId, - __in BURN_DEPENDENCY_ACTION action - ) - { - BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER, pAction->type); - NativeAssert::StringEqual(wzPackageId, pAction->packageProvider.pPackage->sczId); - Assert::Equal(action, pAction->packageProvider.action); - Assert::Equal(FALSE, pAction->fDeleted); - } - - void ValidateExecuteRollbackBoundary( - __in BURN_PLAN* pPlan, - __in BOOL fRollback, - __in DWORD dwIndex, - __in LPCWSTR wzId, - __in BOOL fVital, - __in BOOL fTransaction - ) - { - BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY, pAction->type); - NativeAssert::StringEqual(wzId, pAction->rollbackBoundary.pRollbackBoundary->sczId); - Assert::Equal(fVital, pAction->rollbackBoundary.pRollbackBoundary->fVital); - Assert::Equal(fTransaction, pAction->rollbackBoundary.pRollbackBoundary->fTransaction); - Assert::Equal(FALSE, pAction->fDeleted); - } - - void ValidateExecuteUncachePackage( - __in BURN_PLAN* pPlan, - __in BOOL fRollback, - __in DWORD dwIndex, - __in LPCWSTR wzPackageId - ) - { - BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE, pAction->type); - NativeAssert::StringEqual(wzPackageId, pAction->uncachePackage.pPackage->sczId); - Assert::Equal(FALSE, pAction->fDeleted); - } - - void ValidateExecuteWaitSyncpoint( - __in BURN_PLAN* pPlan, - __in BOOL fRollback, - __in DWORD dwIndex, - __in HANDLE hEvent - ) - { - BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); - Assert::Equal(BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT, pAction->type); - Assert::Equal((DWORD_PTR)hEvent, (DWORD_PTR)pAction->syncpoint.hEvent); - Assert::Equal(FALSE, pAction->fDeleted); - } - - void ValidateNonPermanentPackageExpectedStates( - __in BURN_PACKAGE* pPackage, - __in_z LPCWSTR wzPackageId, - __in BURN_PACKAGE_REGISTRATION_STATE expectedCacheState, - __in BURN_PACKAGE_REGISTRATION_STATE expectedInstallState - ) - { - NativeAssert::StringEqual(wzPackageId, pPackage->sczId); - Assert::Equal(TRUE, pPackage->fCanAffectRegistration); - Assert::Equal(expectedCacheState, pPackage->expectedCacheRegistrationState); - Assert::Equal(expectedInstallState, pPackage->expectedInstallRegistrationState); - } - - void ValidatePermanentPackageExpectedStates( - __in BURN_PACKAGE* pPackage, - __in_z LPCWSTR wzPackageId - ) - { - NativeAssert::StringEqual(wzPackageId, pPackage->sczId); - Assert::Equal(FALSE, pPackage->fCanAffectRegistration); - Assert::Equal(BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, pPackage->expectedCacheRegistrationState); - Assert::Equal(BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, pPackage->expectedInstallRegistrationState); - } - - void ValidatePlannedProvider( - __in BURN_PLAN* pPlan, - __in UINT uIndex, - __in LPCWSTR wzKey, - __in LPCWSTR wzName - ) - { - Assert::InRange(uIndex + 1u, 1u, pPlan->cPlannedProviders); - - DEPENDENCY* pProvider = pPlan->rgPlannedProviders + uIndex; - NativeAssert::StringEqual(wzKey, pProvider->sczKey); - NativeAssert::StringEqual(wzName, pProvider->sczName); - } - }; -} -} -} -} -} - -static HRESULT WINAPI PlanTestBAProc( - __in BOOTSTRAPPER_APPLICATION_MESSAGE /*message*/, - __in const LPVOID /*pvArgs*/, - __inout LPVOID /*pvResults*/, - __in_opt LPVOID /*pvContext*/ - ) -{ - return S_OK; -} diff --git a/src/test/BurnUnitTest/RegistrationTest.cpp b/src/test/BurnUnitTest/RegistrationTest.cpp deleted file mode 100644 index 7b126f61..00000000 --- a/src/test/BurnUnitTest/RegistrationTest.cpp +++ /dev/null @@ -1,772 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -#define ROOT_PATH L"SOFTWARE\\WiX_Burn_UnitTest" -#define HKLM_PATH L"SOFTWARE\\WiX_Burn_UnitTest\\HKLM" -#define HKCU_PATH L"SOFTWARE\\WiX_Burn_UnitTest\\HKCU" -#define REGISTRY_UNINSTALL_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall" -#define REGISTRY_RUN_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce" - -#define TEST_UNINSTALL_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_UNINSTALL_KEY L"\\{D54F896D-1952-43e6-9C67-B5652240618C}" -#define TEST_RUN_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_RUN_KEY - - -static LSTATUS APIENTRY RegistrationTest_RegCreateKeyExW( - __in HKEY hKey, - __in LPCWSTR lpSubKey, - __reserved DWORD Reserved, - __in_opt LPWSTR lpClass, - __in DWORD dwOptions, - __in REGSAM samDesired, - __in_opt CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes, - __out PHKEY phkResult, - __out_opt LPDWORD lpdwDisposition - ); -static LSTATUS APIENTRY RegistrationTest_RegOpenKeyExW( - __in HKEY hKey, - __in_opt LPCWSTR lpSubKey, - __reserved DWORD ulOptions, - __in REGSAM samDesired, - __out PHKEY phkResult - ); -static LSTATUS APIENTRY RegistrationTest_RegDeleteKeyExW( - __in HKEY hKey, - __in LPCWSTR lpSubKey, - __in REGSAM samDesired, - __reserved DWORD Reserved - ); - -namespace Microsoft -{ -namespace Tools -{ -namespace WindowsInstallerXml -{ -namespace Test -{ -namespace Bootstrapper -{ - using namespace Microsoft::Win32; - using namespace System; - using namespace System::IO; - using namespace Xunit; - - public ref class RegistrationTest : BurnUnitTest - { - public: - RegistrationTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) - { - } - - [Fact] - void RegisterBasicTest() - { - HRESULT hr = S_OK; - IXMLDOMElement* pixeBundle = NULL; - LPWSTR sczCurrentProcess = NULL; - BURN_VARIABLES variables = { }; - BURN_USER_EXPERIENCE userExperience = { }; - BOOTSTRAPPER_COMMAND command = { }; - BURN_REGISTRATION registration = { }; - BURN_LOGGING logging = { }; - BURN_PACKAGES packages = { }; - String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); - - try - { - // set mock API's - RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); - - Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH)); - - logging.sczPath = L"BurnUnitTest.txt"; - - LPCWSTR wzDocument = - L"" - L" " - L" " - L" " - L" " - L" " - L" " - L""; - - // load XML document - LoadBundleXmlHelper(wzDocument, &pixeBundle); - - hr = VariableInitialize(&variables); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - hr = UserExperienceParseFromXml(&userExperience, pixeBundle); - TestThrowOnFailure(hr, L"Failed to parse UX from XML."); - - hr = RegistrationParseFromXml(®istration, pixeBundle); - TestThrowOnFailure(hr, L"Failed to parse registration from XML."); - - hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging); - TestThrowOnFailure(hr, L"Failed to set registration resume command."); - - hr = PathForCurrentProcess(&sczCurrentProcess, NULL); - TestThrowOnFailure(hr, L"Failed to get current process path."); - - // write registration - hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE | BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); - TestThrowOnFailure(hr, L"Failed to register bundle."); - - // verify that registration was created - Assert::True(Directory::Exists(cacheDirectory)); - Assert::True(File::Exists(Path::Combine(cacheDirectory, gcnew String(L"setup.exe")))); - - Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); - Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)(Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr))); - - // end session - hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); - TestThrowOnFailure(hr, L"Failed to unregister bundle."); - - // verify that registration was removed - Assert::False(Directory::Exists(cacheDirectory)); - - Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); - Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); - } - finally - { - ReleaseStr(sczCurrentProcess); - ReleaseObject(pixeBundle); - UserExperienceUninitialize(&userExperience); - RegistrationUninitialize(®istration); - VariablesUninitialize(&variables); - - Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH)); - if (Directory::Exists(cacheDirectory)) - { - Directory::Delete(cacheDirectory, true); - } - - RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - } - } - - [Fact] - void RegisterArpMinimumTest() - { - HRESULT hr = S_OK; - IXMLDOMElement* pixeBundle = NULL; - LPWSTR sczCurrentProcess = NULL; - BURN_VARIABLES variables = { }; - BURN_USER_EXPERIENCE userExperience = { }; - BOOTSTRAPPER_COMMAND command = { }; - BURN_REGISTRATION registration = { }; - BURN_LOGGING logging = { }; - BURN_PACKAGES packages = { }; - String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); - try - { - // set mock API's - RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); - - Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH)); - - logging.sczPath = L"BurnUnitTest.txt"; - - LPCWSTR wzDocument = - L"" - L" " - L" " - L" " - L" " - L" " - L" " - L""; - - // load XML document - LoadBundleXmlHelper(wzDocument, &pixeBundle); - - hr = VariableInitialize(&variables); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - hr = UserExperienceParseFromXml(&userExperience, pixeBundle); - TestThrowOnFailure(hr, L"Failed to parse UX from XML."); - - hr = RegistrationParseFromXml(®istration, pixeBundle); - TestThrowOnFailure(hr, L"Failed to parse registration from XML."); - - hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging); - TestThrowOnFailure(hr, L"Failed to set registration resume command."); - - hr = PathForCurrentProcess(&sczCurrentProcess, NULL); - TestThrowOnFailure(hr, L"Failed to get current process path."); - - // - // install - // - - // write registration - hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); - TestThrowOnFailure(hr, L"Failed to register bundle."); - - // verify that registration was created - Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); - Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); - - // complete registration - hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); - TestThrowOnFailure(hr, L"Failed to unregister bundle."); - - // verify that registration was updated - Assert::Equal(Int32(BURN_RESUME_MODE_ARP), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); - Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); - Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); - - // - // uninstall - // - - // write registration - hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, 0); - TestThrowOnFailure(hr, L"Failed to register bundle."); - - // verify that registration was updated - Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); - Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); - Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); - - // delete registration - hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); - TestThrowOnFailure(hr, L"Failed to unregister bundle."); - - // verify that registration was removed - Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); - Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); - Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); - } - finally - { - ReleaseStr(sczCurrentProcess); - ReleaseObject(pixeBundle); - UserExperienceUninitialize(&userExperience); - RegistrationUninitialize(®istration); - VariablesUninitialize(&variables); - - Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH)); - if (Directory::Exists(cacheDirectory)) - { - Directory::Delete(cacheDirectory, true); - } - - RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - } - } - - [Fact] - void RegisterVariablesTest() - { - HRESULT hr = S_OK; - IXMLDOMElement* pixeBundle = NULL; - LPWSTR sczCurrentProcess = NULL; - BURN_VARIABLES variables = { }; - BURN_USER_EXPERIENCE userExperience = { }; - BOOTSTRAPPER_COMMAND command = { }; - BURN_REGISTRATION registration = { }; - BURN_LOGGING logging = { }; - BURN_PACKAGES packages = { }; - String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); - try - { - // set mock API's - RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); - - Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH)); - - logging.sczPath = L"BurnUnitTest.txt"; - - LPCWSTR wzDocument = - L"" - L" " - L" " - L" " - L" " - L" " - L" " - L""; - - // load XML document - LoadBundleXmlHelper(wzDocument, &pixeBundle); - - hr = VariableInitialize(&variables); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - hr = UserExperienceParseFromXml(&userExperience, pixeBundle); - TestThrowOnFailure(hr, L"Failed to parse UX from XML."); - - hr = RegistrationParseFromXml(®istration, pixeBundle); - TestThrowOnFailure(hr, L"Failed to parse registration from XML."); - - hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging); - TestThrowOnFailure(hr, L"Failed to set registration resume command."); - - hr = PathForCurrentProcess(&sczCurrentProcess, NULL); - TestThrowOnFailure(hr, L"Failed to get current process path."); - - // - // install - // - - // write registration - hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); - TestThrowOnFailure(hr, L"Failed to register bundle."); - - // verify that registration was created - Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); - Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); - - // complete registration - hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_REQUIRED, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); - TestThrowOnFailure(hr, L"Failed to unregister bundle."); - - // verify that registration variables were updated - registration.fInstalled = TRUE; - - hr = RegistrationSetVariables(®istration, &variables); - TestThrowOnFailure(hr, L"Failed to set registration variables."); - - Assert::Equal(1ll, VariableGetNumericHelper(&variables, BURN_BUNDLE_INSTALLED)); - Assert::Equal(1ll, VariableGetNumericHelper(&variables, BURN_REBOOT_PENDING)); - Assert::Equal(gcnew String(L"foo"), VariableGetStringHelper(&variables, BURN_BUNDLE_TAG)); - Assert::Equal(gcnew String(L"bar"), VariableGetStringHelper(&variables, BURN_BUNDLE_PROVIDER_KEY)); - Assert::Equal(gcnew String(L"1.0.0.0"), VariableGetVersionHelper(&variables, BURN_BUNDLE_VERSION)); - - // - // uninstall - // - - // delete registration - hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); - TestThrowOnFailure(hr, L"Failed to unregister bundle."); - - // verify that registration was removed - Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); - Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); - Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); - } - finally - { - ReleaseStr(sczCurrentProcess); - ReleaseObject(pixeBundle); - UserExperienceUninitialize(&userExperience); - RegistrationUninitialize(®istration); - VariablesUninitialize(&variables); - - Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH)); - if (Directory::Exists(cacheDirectory)) - { - Directory::Delete(cacheDirectory, true); - } - - RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - } - } - - [Fact] - void RegisterArpFullTest() - { - HRESULT hr = S_OK; - IXMLDOMElement* pixeBundle = NULL; - LPWSTR sczCurrentProcess = NULL; - BURN_VARIABLES variables = { }; - BURN_USER_EXPERIENCE userExperience = { }; - BOOTSTRAPPER_COMMAND command = { }; - BURN_REGISTRATION registration = { }; - BURN_LOGGING logging = { }; - BURN_PACKAGES packages = { }; - String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); - try - { - // set mock API's - RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); - - Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH)); - - logging.sczPath = L"BurnUnitTest.txt"; - - LPCWSTR wzDocument = - L"" - L" " - L" " - L" " - L" " - L" " - L" " - L""; - - // load XML document - LoadBundleXmlHelper(wzDocument, &pixeBundle); - - hr = VariableInitialize(&variables); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - hr = UserExperienceParseFromXml(&userExperience, pixeBundle); - TestThrowOnFailure(hr, L"Failed to parse UX from XML."); - - hr = RegistrationParseFromXml(®istration, pixeBundle); - TestThrowOnFailure(hr, L"Failed to parse registration from XML."); - - hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging); - TestThrowOnFailure(hr, L"Failed to set registration resume command."); - - hr = PathForCurrentProcess(&sczCurrentProcess, NULL); - TestThrowOnFailure(hr, L"Failed to get current process path."); - - // - // install - // - - // write registration - hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); - TestThrowOnFailure(hr, L"Failed to register bundle."); - - // verify that registration was created - Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); - Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); - - // finish registration - hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); - TestThrowOnFailure(hr, L"Failed to register bundle."); - - // verify that registration was updated - Assert::Equal(Int32(BURN_RESUME_MODE_ARP), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); - Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); - Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); - - Assert::Equal(gcnew String(L"DisplayName1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"DisplayName"), nullptr)); - Assert::Equal(gcnew String(L"1.2.3.4"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"DisplayVersion"), nullptr)); - Assert::Equal(gcnew String(L"Publisher1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Publisher"), nullptr)); - Assert::Equal(gcnew String(L"http://www.microsoft.com/help"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"HelpLink"), nullptr)); - Assert::Equal(gcnew String(L"555-555-5555"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"HelpTelephone"), nullptr)); - Assert::Equal(gcnew String(L"http://www.microsoft.com/about"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"URLInfoAbout"), nullptr)); - Assert::Equal(gcnew String(L"http://www.microsoft.com/update"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"URLUpdateInfo"), nullptr)); - Assert::Equal(gcnew String(L"Comments1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Comments"), nullptr)); - Assert::Equal(gcnew String(L"Contact1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Contact"), nullptr)); - Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"NoModify"), nullptr)); - Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"NoRemove"), nullptr)); - - // - // uninstall - // - - // write registration - hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, 0); - TestThrowOnFailure(hr, L"Failed to register bundle."); - - // verify that registration was updated - Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); - Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); - - // delete registration - hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); - TestThrowOnFailure(hr, L"Failed to unregister bundle."); - - // verify that registration was removed - Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); - Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); - Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); - } - finally - { - ReleaseStr(sczCurrentProcess); - ReleaseObject(pixeBundle); - UserExperienceUninitialize(&userExperience); - RegistrationUninitialize(®istration); - VariablesUninitialize(&variables); - - Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH)); - if (Directory::Exists(cacheDirectory)) - { - Directory::Delete(cacheDirectory, true); - } - - RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - } - } - - [Fact(Skip = "Currently fails")] - void ResumeTest() - { - HRESULT hr = S_OK; - IXMLDOMElement* pixeBundle = NULL; - LPWSTR sczCurrentProcess = NULL; - BURN_VARIABLES variables = { }; - BURN_USER_EXPERIENCE userExperience = { }; - BOOTSTRAPPER_COMMAND command = { }; - BURN_REGISTRATION registration = { }; - BURN_LOGGING logging = { }; - BURN_PACKAGES packages = { }; - BYTE rgbData[256] = { }; - BOOTSTRAPPER_RESUME_TYPE resumeType = BOOTSTRAPPER_RESUME_TYPE_NONE; - BYTE* pbBuffer = NULL; - SIZE_T cbBuffer = 0; - String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); - try - { - for (DWORD i = 0; i < 256; ++i) - { - rgbData[i] = (BYTE)i; - } - - // set mock API's - RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); - - Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH)); - - logging.sczPath = L"BurnUnitTest.txt"; - - LPCWSTR wzDocument = - L"" - L" " - L" " - L" " - L" " - L" " - L" " - L""; - - // load XML document - LoadBundleXmlHelper(wzDocument, &pixeBundle); - - hr = VariableInitialize(&variables); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - hr = UserExperienceParseFromXml(&userExperience, pixeBundle); - TestThrowOnFailure(hr, L"Failed to parse UX from XML."); - - hr = RegistrationParseFromXml(®istration, pixeBundle); - TestThrowOnFailure(hr, L"Failed to parse registration from XML."); - - hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging); - TestThrowOnFailure(hr, L"Failed to set registration resume command."); - - hr = PathForCurrentProcess(&sczCurrentProcess, NULL); - TestThrowOnFailure(hr, L"Failed to get current process path."); - - // read resume type before session - hr = RegistrationDetectResumeType(®istration, &resumeType); - TestThrowOnFailure(hr, L"Failed to read resume type."); - - Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_NONE, (int)resumeType); - - // begin session - hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); - TestThrowOnFailure(hr, L"Failed to register bundle."); - - hr = RegistrationSaveState(®istration, rgbData, sizeof(rgbData)); - TestThrowOnFailure(hr, L"Failed to save state."); - - // read interrupted resume type - hr = RegistrationDetectResumeType(®istration, &resumeType); - TestThrowOnFailure(hr, L"Failed to read interrupted resume type."); - - Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_INTERRUPTED, (int)resumeType); - - // suspend session - hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_SUSPEND, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); - TestThrowOnFailure(hr, L"Failed to suspend session."); - - // verify that run key was removed - Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); - - // read suspend resume type - hr = RegistrationDetectResumeType(®istration, &resumeType); - TestThrowOnFailure(hr, L"Failed to read suspend resume type."); - - Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_SUSPEND, (int)resumeType); - - // read state back - hr = RegistrationLoadState(®istration, &pbBuffer, &cbBuffer); - TestThrowOnFailure(hr, L"Failed to load state."); - - Assert::Equal((SIZE_T)sizeof(rgbData), cbBuffer); - Assert::True(0 == memcmp(pbBuffer, rgbData, sizeof(rgbData))); - - // write active resume mode - hr = RegistrationSessionResume(®istration, &variables); - TestThrowOnFailure(hr, L"Failed to write active resume mode."); - - // verify that run key was put back - Assert::NotEqual((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); - - // end session - hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); - TestThrowOnFailure(hr, L"Failed to unregister bundle."); - - // read resume type after session - hr = RegistrationDetectResumeType(®istration, &resumeType); - TestThrowOnFailure(hr, L"Failed to read resume type."); - - Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_NONE, (int)resumeType); - } - finally - { - ReleaseStr(sczCurrentProcess); - ReleaseObject(pixeBundle); - UserExperienceUninitialize(&userExperience); - RegistrationUninitialize(®istration); - VariablesUninitialize(&variables); - - Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH)); - if (Directory::Exists(cacheDirectory)) - { - Directory::Delete(cacheDirectory, true); - } - - RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - } - } - - //BOOTSTRAPPER_RESUME_TYPE_NONE, - //BOOTSTRAPPER_RESUME_TYPE_INVALID, // resume information is present but invalid - //BOOTSTRAPPER_RESUME_TYPE_UNEXPECTED, // relaunched after an unexpected interruption - //BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING, // reboot has not taken place yet - //BOOTSTRAPPER_RESUME_TYPE_REBOOT, // relaunched after reboot - //BOOTSTRAPPER_RESUME_TYPE_SUSPEND, // relaunched after suspend - //BOOTSTRAPPER_RESUME_TYPE_ARP, // launched from ARP - }; -} -} -} -} -} - - -static LSTATUS APIENTRY RegistrationTest_RegCreateKeyExW( - __in HKEY hKey, - __in LPCWSTR lpSubKey, - __reserved DWORD Reserved, - __in_opt LPWSTR lpClass, - __in DWORD dwOptions, - __in REGSAM samDesired, - __in_opt CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes, - __out PHKEY phkResult, - __out_opt LPDWORD lpdwDisposition - ) -{ - LSTATUS ls = ERROR_SUCCESS; - LPCWSTR wzRoot = NULL; - HKEY hkRoot = NULL; - - if (HKEY_LOCAL_MACHINE == hKey) - { - wzRoot = HKLM_PATH; - } - else if (HKEY_CURRENT_USER == hKey) - { - wzRoot = HKCU_PATH; - } - else - { - hkRoot = hKey; - } - - if (wzRoot) - { - ls = ::RegOpenKeyExW(HKEY_CURRENT_USER, wzRoot, 0, KEY_WRITE, &hkRoot); - if (ERROR_SUCCESS != ls) - { - ExitFunction(); - } - } - - ls = ::RegCreateKeyExW(hkRoot, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition); - -LExit: - ReleaseRegKey(hkRoot); - - return ls; -} - -static LSTATUS APIENTRY RegistrationTest_RegOpenKeyExW( - __in HKEY hKey, - __in_opt LPCWSTR lpSubKey, - __reserved DWORD ulOptions, - __in REGSAM samDesired, - __out PHKEY phkResult - ) -{ - LSTATUS ls = ERROR_SUCCESS; - LPCWSTR wzRoot = NULL; - HKEY hkRoot = NULL; - - if (HKEY_LOCAL_MACHINE == hKey) - { - wzRoot = HKLM_PATH; - } - else if (HKEY_CURRENT_USER == hKey) - { - wzRoot = HKCU_PATH; - } - else - { - hkRoot = hKey; - } - - if (wzRoot) - { - ls = ::RegOpenKeyExW(HKEY_CURRENT_USER, wzRoot, 0, KEY_WRITE, &hkRoot); - if (ERROR_SUCCESS != ls) - { - ExitFunction(); - } - } - - ls = ::RegOpenKeyExW(hkRoot, lpSubKey, ulOptions, samDesired, phkResult); - -LExit: - ReleaseRegKey(hkRoot); - - return ls; -} - -static LSTATUS APIENTRY RegistrationTest_RegDeleteKeyExW( - __in HKEY hKey, - __in LPCWSTR lpSubKey, - __in REGSAM samDesired, - __reserved DWORD Reserved - ) -{ - LSTATUS ls = ERROR_SUCCESS; - LPCWSTR wzRoot = NULL; - HKEY hkRoot = NULL; - - if (HKEY_LOCAL_MACHINE == hKey) - { - wzRoot = HKLM_PATH; - } - else if (HKEY_CURRENT_USER == hKey) - { - wzRoot = HKCU_PATH; - } - else - { - hkRoot = hKey; - } - - if (wzRoot) - { - ls = ::RegOpenKeyExW(HKEY_CURRENT_USER, wzRoot, 0, KEY_WRITE | samDesired, &hkRoot); - if (ERROR_SUCCESS != ls) - { - ExitFunction(); - } - } - - ls = ::RegDeleteKeyExW(hkRoot, lpSubKey, samDesired, Reserved); - -LExit: - ReleaseRegKey(hkRoot); - - return ls; -} diff --git a/src/test/BurnUnitTest/SearchTest.cpp b/src/test/BurnUnitTest/SearchTest.cpp deleted file mode 100644 index eca01f5f..00000000 --- a/src/test/BurnUnitTest/SearchTest.cpp +++ /dev/null @@ -1,815 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiGetComponentPathW( - __in LPCWSTR szProduct, - __in LPCWSTR szComponent, - __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, - __inout_opt LPDWORD pcchBuf - ); -static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiLocateComponentW( - __in LPCWSTR szComponent, - __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, - __inout_opt LPDWORD pcchBuf - ); -static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoW( - __in LPCWSTR szProductCode, - __in LPCWSTR szProperty, - __out_ecount_opt(*pcchValue) LPWSTR szValue, - __inout_opt LPDWORD pcchValue - ); -static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoExW( - __in LPCWSTR szProductCode, - __in_opt LPCWSTR szUserSid, - __in MSIINSTALLCONTEXT dwContext, - __in LPCWSTR szProperty, - __out_ecount_opt(*pcchValue) LPWSTR szValue, - __inout_opt LPDWORD pcchValue - ); - -using namespace System; -using namespace Xunit; -using namespace Microsoft::Win32; - -namespace Microsoft -{ -namespace Tools -{ -namespace WindowsInstallerXml -{ -namespace Test -{ -namespace Bootstrapper -{ - public ref class SearchTest : BurnUnitTest - { - public: - SearchTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) - { - } - - [Fact] - void DirectorySearchTest() - { - HRESULT hr = S_OK; - IXMLDOMElement* pixeBundle = NULL; - BURN_VARIABLES variables = { }; - BURN_SEARCHES searches = { }; - BURN_EXTENSIONS burnExtensions = { }; - try - { - hr = VariableInitialize(&variables); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - pin_ptr wzDirectory1 = PtrToStringChars(this->TestContext->TestDirectory); - pin_ptr wzDirectory2 = PtrToStringChars(System::IO::Path::Combine(this->TestContext->TestDirectory, gcnew String(L"none"))); - - VariableSetStringHelper(&variables, L"Directory1", wzDirectory1, FALSE); - VariableSetStringHelper(&variables, L"Directory2", wzDirectory2, FALSE); - - LPCWSTR wzDocument = - L"" - L" " - L" " - L""; - - // load XML document - LoadBundleXmlHelper(wzDocument, &pixeBundle); - - hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); - TestThrowOnFailure(hr, L"Failed to parse searches from XML."); - - // execute searches - hr = SearchesExecute(&searches, &variables); - TestThrowOnFailure(hr, L"Failed to execute searches."); - - // check variable values - Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable1")); - Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable2")); - } - finally - { - ReleaseObject(pixeBundle); - VariablesUninitialize(&variables); - SearchesUninitialize(&searches); - } - } - - [Fact(Skip = "Currently fails")] - void FileSearchTest() - { - HRESULT hr = S_OK; - IXMLDOMElement* pixeBundle = NULL; - BURN_VARIABLES variables = { }; - BURN_SEARCHES searches = { }; - BURN_EXTENSIONS burnExtensions = { }; - ULARGE_INTEGER uliVersion = { }; - VERUTIL_VERSION* pVersion = NULL; - try - { - hr = VariableInitialize(&variables); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - pin_ptr wzFile1 = PtrToStringChars(System::IO::Path::Combine(this->TestContext->TestDirectory, gcnew String(L"none.txt"))); - pin_ptr wzFile2 = PtrToStringChars(System::Reflection::Assembly::GetExecutingAssembly()->Location); - - hr = FileVersion(wzFile2, &uliVersion.HighPart, &uliVersion.LowPart); - TestThrowOnFailure(hr, L"Failed to get DLL version."); - - hr = VerVersionFromQword(uliVersion.QuadPart, &pVersion); - NativeAssert::Succeeded(hr, "Failed to create version."); - - VariableSetStringHelper(&variables, L"File1", wzFile1, FALSE); - VariableSetStringHelper(&variables, L"File2", wzFile2, FALSE); - - LPCWSTR wzDocument = - L"" - L" " - L" " - L" " - L""; - - // load XML document - LoadBundleXmlHelper(wzDocument, &pixeBundle); - - hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); - TestThrowOnFailure(hr, L"Failed to parse searches from XML."); - - // execute searches - hr = SearchesExecute(&searches, &variables); - TestThrowOnFailure(hr, L"Failed to execute searches."); - - // check variable values - Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable1")); - Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable2")); - Assert::Equal(gcnew String(pVersion->sczVersion), VariableGetVersionHelper(&variables, L"Variable3")); - } - finally - { - ReleaseVerutilVersion(pVersion); - ReleaseObject(pixeBundle); - VariablesUninitialize(&variables); - SearchesUninitialize(&searches); - } - } - - [Fact] - void RegistrySearchTest() - { - HRESULT hr = S_OK; - IXMLDOMElement* pixeBundle = NULL; - BURN_VARIABLES variables = { }; - BURN_SEARCHES searches = { }; - BURN_EXTENSIONS burnExtensions = { }; - HKEY hkey32 = NULL; - HKEY hkey64 = NULL; - BOOL f64bitMachine = (nullptr != Environment::GetEnvironmentVariable("ProgramFiles(x86)")); - - try - { - hr = VariableInitialize(&variables); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"String"), gcnew String(L"String1 %TEMP%"), RegistryValueKind::String); - Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"StringExpand"), gcnew String(L"String1 %TEMP%"), RegistryValueKind::ExpandString); - Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"DWord"), 1, RegistryValueKind::DWord); - Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"QWord"), 1ll, RegistryValueKind::QWord); - Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"VersionString"), gcnew String(L"1.1.1.1"), RegistryValueKind::String); - Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"VersionQWord"), MAKEQWORDVERSION(1,1,1,1), RegistryValueKind::QWord); - Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\String"), nullptr, gcnew String(L"String1"), RegistryValueKind::String); - Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Numeric"), nullptr, 1ll, RegistryValueKind::DWord); - - if (f64bitMachine) - { - hr = RegCreate(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness\\", KEY_WRITE | KEY_WOW64_32KEY, &hkey32); - Assert::True(SUCCEEDED(hr)); - - hr = RegCreate(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness\\", KEY_WRITE | KEY_WOW64_64KEY, &hkey64); - Assert::True(SUCCEEDED(hr)); - - hr = RegWriteString(hkey64, L"TestStringSpecificToBitness", L"64-bit"); - Assert::True(SUCCEEDED(hr)); - - hr = RegWriteString(hkey32, L"TestStringSpecificToBitness", L"32-bit"); - Assert::True(SUCCEEDED(hr)); - } - - VariableSetStringHelper(&variables, L"MyKey", L"SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value", FALSE); - VariableSetStringHelper(&variables, L"MyValue", L"String", FALSE); - VariableSetStringHelper(&variables, L"Variable27", L"Default27", FALSE); - VariableSetStringHelper(&variables, L"Variable28", L"Default28", FALSE); - - LPCWSTR wzDocument = - L"" - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L""; - - // load XML document - LoadBundleXmlHelper(wzDocument, &pixeBundle); - - hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); - TestThrowOnFailure(hr, L"Failed to parse searches from XML."); - - // execute searches - hr = SearchesExecute(&searches, &variables); - TestThrowOnFailure(hr, L"Failed to execute searches."); - - // check variable values - Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable1")); - Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable2")); - Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable3")); - Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable4")); - Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable5")); - Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable6")); - Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable7")); - Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable8")); - Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable9")); - Assert::NotEqual(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable10")); - Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable11")); - Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable12")); - Assert::Equal(gcnew String(L"1.1.1.1"), VariableGetVersionHelper(&variables, L"Variable13")); - Assert::Equal(gcnew String(L"1.1.1.1"), VariableGetVersionHelper(&variables, L"Variable14")); - Assert::Equal(gcnew String(L"String1"), VariableGetStringHelper(&variables, L"Variable15")); - Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable16")); - Assert::False(VariableExistsHelper(&variables, L"Variable17")); - Assert::False(VariableExistsHelper(&variables, L"Variable18")); - Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable19")); - Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable20")); - if (f64bitMachine) - { - Assert::Equal(gcnew String(L"32-bit"), VariableGetStringHelper(&variables, L"Variable21")); - Assert::Equal(gcnew String(L"64-bit"), VariableGetStringHelper(&variables, L"Variable22")); - } - - Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable23")); - Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable24")); - Assert::Equal(gcnew String(L"Msi.Package"), VariableGetStringHelper(&variables, L"Variable25")); - Assert::Equal(gcnew String(L"Msi.Package"), VariableGetStringHelper(&variables, L"Variable26")); - Assert::Equal(gcnew String(L"Default27"), VariableGetStringHelper(&variables, L"Variable27")); - Assert::Equal(gcnew String(L"Default28"), VariableGetStringHelper(&variables, L"Variable28")); - } - finally - { - ReleaseRegKey(hkey32); - ReleaseRegKey(hkey64); - ReleaseObject(pixeBundle); - VariablesUninitialize(&variables); - SearchesUninitialize(&searches); - - Registry::CurrentUser->DeleteSubKeyTree(gcnew String(L"SOFTWARE\\Microsoft\\WiX_Burn_UnitTest")); - if (f64bitMachine) - { - RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness", REG_KEY_32BIT, FALSE); - RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest", REG_KEY_32BIT, FALSE); - RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness", REG_KEY_64BIT, FALSE); - RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest", REG_KEY_64BIT, FALSE); - } - } - } - - [Fact] - void MsiComponentSearchTest() - { - HRESULT hr = S_OK; - IXMLDOMElement* pixeBundle = NULL; - BURN_VARIABLES variables = { }; - BURN_SEARCHES searches = { }; - BURN_EXTENSIONS burnExtensions = { }; - try - { - hr = VariableInitialize(&variables); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - // set mock API's - WiuFunctionOverride(NULL, MsiComponentSearchTest_MsiGetComponentPathW, MsiComponentSearchTest_MsiLocateComponentW, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - - LPCWSTR wzDocument = - L"" - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " // todo: value key path - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L""; - - // load XML document - LoadBundleXmlHelper(wzDocument, &pixeBundle); - - hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); - TestThrowOnFailure(hr, L"Failed to parse searches from XML."); - - // execute searches - hr = SearchesExecute(&searches, &variables); - TestThrowOnFailure(hr, L"Failed to execute searches."); - - // check variable values - Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable1")); - Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable2")); - Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable3")); - Assert::Equal(gcnew String(L"C:\\directory\\file1.txt"), VariableGetStringHelper(&variables, L"Variable4")); - Assert::Equal(gcnew String(L"C:\\directory\\file2.txt"), VariableGetStringHelper(&variables, L"Variable5")); - Assert::Equal(gcnew String(L"C:\\directory\\file3.txt"), VariableGetStringHelper(&variables, L"Variable6")); - Assert::Equal(gcnew String(L"C:\\directory\\file4.txt"), VariableGetStringHelper(&variables, L"Variable7")); - Assert::Equal(gcnew String(L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\"), VariableGetStringHelper(&variables, L"Variable8")); - Assert::Equal(gcnew String(L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), VariableGetStringHelper(&variables, L"Variable9")); - Assert::Equal(3ll, VariableGetNumericHelper(&variables, L"Variable10")); - Assert::Equal(3ll, VariableGetNumericHelper(&variables, L"Variable11")); - Assert::Equal(4ll, VariableGetNumericHelper(&variables, L"Variable12")); - Assert::Equal(4ll, VariableGetNumericHelper(&variables, L"Variable13")); - Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable14")); - Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable15")); - Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable16")); - Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable17")); - Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable18")); - Assert::Equal(gcnew String(L"C:\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\file5.txt"), VariableGetStringHelper(&variables, L"Variable19")); - } - finally - { - ReleaseObject(pixeBundle); - VariablesUninitialize(&variables); - SearchesUninitialize(&searches); - } - } - - [Fact] - void MsiProductSearchTest() - { - HRESULT hr = S_OK; - IXMLDOMElement* pixeBundle = NULL; - BURN_VARIABLES variables = { }; - BURN_SEARCHES searches = { }; - BURN_EXTENSIONS burnExtensions = { }; - try - { - hr = VariableInitialize(&variables); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - // set mock API's - WiuFunctionOverride(NULL, NULL, NULL, NULL, MsiProductSearchTest_MsiGetProductInfoW, MsiProductSearchTest_MsiGetProductInfoExW, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - - LPCWSTR wzDocument = - L"" - L" " - L" " - L" " - L" " - L" " - L" " - L""; - - // load XML document - LoadBundleXmlHelper(wzDocument, &pixeBundle); - - hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); - TestThrowOnFailure(hr, L"Failed to parse searches from XML."); - - // execute searches - hr = SearchesExecute(&searches, &variables); - TestThrowOnFailure(hr, L"Failed to execute searches."); - - // check variable values - Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable1")); - Assert::Equal(gcnew String(L"1.0.0.0"), VariableGetVersionHelper(&variables, L"Variable2")); - Assert::Equal(1033ll, VariableGetNumericHelper(&variables, L"Variable3")); - Assert::Equal(5ll, VariableGetNumericHelper(&variables, L"Variable4")); - Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable5")); - Assert::Equal(gcnew String(L"1.0.0.0"), VariableGetVersionHelper(&variables, L"Variable6")); - } - finally - { - ReleaseObject(pixeBundle); - VariablesUninitialize(&variables); - SearchesUninitialize(&searches); - } - } - - [Fact] - void MsiFeatureSearchTest() - { - HRESULT hr = S_OK; - IXMLDOMElement* pixeBundle = NULL; - BURN_VARIABLES variables = { }; - BURN_SEARCHES searches = { }; - BURN_EXTENSIONS burnExtensions = { }; - try - { - LPCWSTR wzDocument = - L"" - L" " - L""; - - hr = VariableInitialize(&variables); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - // load XML document - LoadBundleXmlHelper(wzDocument, &pixeBundle); - - hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); - TestThrowOnFailure(hr, L"Failed to parse searches from XML."); - - // execute searches - hr = SearchesExecute(&searches, &variables); - TestThrowOnFailure(hr, L"Failed to execute searches."); - } - finally - { - ReleaseObject(pixeBundle); - VariablesUninitialize(&variables); - SearchesUninitialize(&searches); - } - } - - [Fact] - void ConditionalSearchTest() - { - HRESULT hr = S_OK; - IXMLDOMElement* pixeBundle = NULL; - BURN_VARIABLES variables = { }; - BURN_SEARCHES searches = { }; - BURN_EXTENSIONS burnExtensions = { }; - try - { - LPCWSTR wzDocument = - L"" - L" " - L" " - L" " - L""; - - hr = VariableInitialize(&variables); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - // load XML document - LoadBundleXmlHelper(wzDocument, &pixeBundle); - - hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); - TestThrowOnFailure(hr, L"Failed to parse searches from XML."); - - // execute searches - hr = SearchesExecute(&searches, &variables); - TestThrowOnFailure(hr, L"Failed to execute searches."); - - // check variable values - Assert::False(VariableExistsHelper(&variables, L"Variable1")); - Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable2")); - Assert::False(VariableExistsHelper(&variables, L"Variable3")); - } - finally - { - ReleaseObject(pixeBundle); - VariablesUninitialize(&variables); - SearchesUninitialize(&searches); - } - } - [Fact] - void NoSearchesTest() - { - HRESULT hr = S_OK; - IXMLDOMElement* pixeBundle = NULL; - BURN_VARIABLES variables = { }; - BURN_SEARCHES searches = { }; - BURN_EXTENSIONS burnExtensions = { }; - try - { - LPCWSTR wzDocument = - L"" - L""; - - hr = VariableInitialize(&variables); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - // load XML document - LoadBundleXmlHelper(wzDocument, &pixeBundle); - - hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); - TestThrowOnFailure(hr, L"Failed to parse searches from XML."); - - // execute searches - hr = SearchesExecute(&searches, &variables); - TestThrowOnFailure(hr, L"Failed to execute searches."); - } - finally - { - ReleaseObject(pixeBundle); - VariablesUninitialize(&variables); - SearchesUninitialize(&searches); - } - } - - [Fact] - void SetVariableSearchTest() - { - HRESULT hr = S_OK; - IXMLDOMElement* pixeBundle = NULL; - BURN_VARIABLES variables = { }; - BURN_SEARCHES searches = { }; - BURN_EXTENSIONS burnExtensions = { }; - try - { - LPCWSTR wzDocument = - L"" - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L" " - L""; - - hr = VariableInitialize(&variables); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - // set variables - VariableSetStringHelper(&variables, L"OVERWRITTEN_STRING", L"ORIGINAL", FALSE); - VariableSetNumericHelper(&variables, L"OVERWRITTEN_NUMBER", 5); - VariableSetNumericHelper(&variables, L"REMOVED_NUMBER", 22); - - // load XML document - LoadBundleXmlHelper(wzDocument, &pixeBundle); - - hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); - TestThrowOnFailure(hr, L"Failed to parse searches from XML."); - - // execute searches - hr = SearchesExecute(&searches, &variables); - TestThrowOnFailure(hr, L"Failed to execute searches."); - - // check variable values - Assert::Equal(gcnew String(L"VAL1"), VariableGetStringHelper(&variables, L"PROP1")); - Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"PROP2")); - Assert::Equal(gcnew String(L"2"), VariableGetStringHelper(&variables, L"PROP2")); - Assert::Equal(gcnew String(L"VAL3"), VariableGetStringHelper(&variables, L"PROP3")); - Assert::Equal(gcnew String(L"VAL4"), VariableGetStringHelper(&variables, L"PROP4")); - Assert::Equal(gcnew String(L"VAL5"), VariableGetStringHelper(&variables, L"PROP5")); - Assert::Equal(gcnew String(L"VAL6"), VariableGetStringHelper(&variables, L"PROP6")); - Assert::Equal(7ll, VariableGetNumericHelper(&variables, L"PROP7")); - Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetVersionHelper(&variables, L"PROP8")); - Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetStringHelper(&variables, L"PROP8")); - Assert::Equal(gcnew String(L"[VAL9]"), VariableGetStringHelper(&variables, L"PROP9")); - - Assert::Equal(42ll, VariableGetNumericHelper(&variables, L"OVERWRITTEN_STRING")); - Assert::Equal(gcnew String(L"NEW"), VariableGetStringHelper(&variables, L"OVERWRITTEN_NUMBER")); - Assert::Equal((int)BURN_VARIANT_TYPE_NONE, VariableGetTypeHelper(&variables, L"REMOVED_NUMBER")); - } - finally - { - ReleaseObject(pixeBundle); - VariablesUninitialize(&variables); - SearchesUninitialize(&searches); - } - } - }; -} -} -} -} -} - - -static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiGetComponentPathW( - __in LPCWSTR szProduct, - __in LPCWSTR szComponent, - __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, - __inout_opt LPDWORD pcchBuf - ) -{ - INSTALLSTATE is = INSTALLSTATE_INVALIDARG; - String^ product = gcnew String(szProduct); - - if (String::Equals(product, gcnew String(L"{BAD00000-0000-0000-0000-000000000000}"))) - { - is = INSTALLSTATE_UNKNOWN; - } - else if (String::Equals(product, gcnew String(L"{600D0000-0000-0000-0000-000000000000}"))) - { - is = MsiComponentSearchTest_MsiLocateComponentW(szComponent, lpPathBuf, pcchBuf); - } - - return is; -} - -static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiLocateComponentW( - __in LPCWSTR szComponent, - __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, - __inout_opt LPDWORD pcchBuf - ) -{ - HRESULT hr = S_OK; - INSTALLSTATE is = INSTALLSTATE_INVALIDARG; - String^ component = gcnew String(szComponent); - LPCWSTR wzValue = NULL; - - if (String::Equals(component, gcnew String(L"{BAD00000-1000-0000-0000-000000000000}"))) - { - is = INSTALLSTATE_UNKNOWN; - } - else if (String::Equals(component, gcnew String(L"{600D0000-1000-1000-0000-000000000000}"))) - { - wzValue = L"C:\\directory\\file1.txt"; - is = INSTALLSTATE_LOCAL; - } - else if (String::Equals(component, gcnew String(L"{600D0000-1000-2000-0000-000000000000}"))) - { - wzValue = L"C:\\directory\\file2.txt"; - is = INSTALLSTATE_SOURCE; - } - else if (String::Equals(component, gcnew String(L"{600D0000-1000-3000-0000-000000000000}"))) - { - wzValue = L"C:\\directory\\file3.txt"; - is = INSTALLSTATE_SOURCEABSENT; - } - else if (String::Equals(component, gcnew String(L"{600D0000-1000-4000-0000-000000000000}"))) - { - wzValue = L"C:\\directory\\file4.txt"; - is = INSTALLSTATE_ABSENT; - } - else if (String::Equals(component, gcnew String(L"{600D0000-1000-5000-0000-000000000000}"))) - { - wzValue = L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\"; - is = INSTALLSTATE_LOCAL; - } - else if (String::Equals(component, gcnew String(L"{600D0000-1000-6000-0000-000000000000}"))) - { - wzValue = L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"; - is = INSTALLSTATE_LOCAL; - } - else if (String::Equals(component, gcnew String(L"{600D0000-1000-7000-0000-000000000000}"))) - { - wzValue = L"C:\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\file5.txt"; - is = INSTALLSTATE_ABSENT; - } - - if (wzValue && lpPathBuf) - { - hr = ::StringCchCopyW(lpPathBuf, *pcchBuf, wzValue); - if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) - { - *pcchBuf = lstrlenW(wzValue); - is = INSTALLSTATE_MOREDATA; - } - else if (FAILED(hr)) - { - is = INSTALLSTATE_INVALIDARG; - } - } - - return is; -} - -static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoW( - __in LPCWSTR szProductCode, - __in LPCWSTR szProperty, - __out_ecount_opt(*pcchValue) LPWSTR szValue, - __inout_opt LPDWORD pcchValue - ) -{ - if (String::Equals(gcnew String(szProductCode), gcnew String(L"{600D0000-0000-0000-0000-000000000000}")) && - String::Equals(gcnew String(szProperty), gcnew String(INSTALLPROPERTY_PRODUCTSTATE))) - { - // force call to WiuGetProductInfoEx - return ERROR_UNKNOWN_PROPERTY; - } - - UINT er = MsiProductSearchTest_MsiGetProductInfoExW(szProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, szProperty, szValue, pcchValue); - return er; -} - -static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoExW( - __in LPCWSTR szProductCode, - __in_opt LPCWSTR /*szUserSid*/, - __in MSIINSTALLCONTEXT dwContext, - __in LPCWSTR szProperty, - __out_ecount_opt(*pcchValue) LPWSTR szValue, - __inout_opt LPDWORD pcchValue - ) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_FUNCTION_FAILED; - LPCWSTR wzValue = NULL; - - String^ productCode = gcnew String(szProductCode); - String^ _property = gcnew String(szProperty); - switch (dwContext) - { - case MSIINSTALLCONTEXT_USERMANAGED: - er = ERROR_UNKNOWN_PRODUCT; - break; - case MSIINSTALLCONTEXT_USERUNMANAGED: - if (String::Equals(productCode, gcnew String(L"{600D0000-0000-0000-0000-000000000000}"))) - { - if (String::Equals(_property, gcnew String(INSTALLPROPERTY_PRODUCTSTATE))) - { - wzValue = L"5"; - } - } - break; - case MSIINSTALLCONTEXT_MACHINE: - if (String::Equals(productCode, gcnew String(L"{BAD00000-0000-0000-0000-000000000000}"))) - { - er = ERROR_UNKNOWN_PRODUCT; - } - else if (String::Equals(productCode, gcnew String(L"{600D0000-0000-0000-0000-000000000000}"))) - { - if (String::Equals(_property, gcnew String(INSTALLPROPERTY_VERSIONSTRING))) - { - wzValue = L"1.0.0.0"; - } - else if (String::Equals(_property, gcnew String(INSTALLPROPERTY_LANGUAGE))) - { - wzValue = L"1033"; - } - else if (String::Equals(_property, gcnew String(INSTALLPROPERTY_ASSIGNMENTTYPE))) - { - wzValue = L"1"; - } - else if (String::Equals(_property, gcnew String(INSTALLPROPERTY_PRODUCTSTATE))) - { - // try again in per-user context - er = ERROR_UNKNOWN_PRODUCT; - } - } - else if (String::Equals(productCode, gcnew String(L"{600D0000-1000-0000-0000-000000000000}"))) - { - static BOOL fFlipp = FALSE; - if (fFlipp) - { - if (String::Equals(_property, gcnew String(INSTALLPROPERTY_VERSIONSTRING))) - { - wzValue = L"1.0.0.0"; - } - } - else - { - *pcchValue = MAX_PATH * 2; - er = ERROR_MORE_DATA; - } - fFlipp = !fFlipp; - } - break; - } - - if (wzValue) - { - hr = ::StringCchCopyW(szValue, *pcchValue, wzValue); - if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) - { - *pcchValue = lstrlenW(wzValue); - er = ERROR_MORE_DATA; - } - else if (SUCCEEDED(hr)) - { - er = ERROR_SUCCESS; - } - } - - return er; -} diff --git a/src/test/BurnUnitTest/TestData/CacheTest/CacheSignatureTest.File b/src/test/BurnUnitTest/TestData/CacheTest/CacheSignatureTest.File deleted file mode 100644 index 896ac017..00000000 --- a/src/test/BurnUnitTest/TestData/CacheTest/CacheSignatureTest.File +++ /dev/null @@ -1 +0,0 @@ -This file has a known hash. \ No newline at end of file diff --git a/src/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml b/src/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml deleted file mode 100644 index 65e3c63d..00000000 --- a/src/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml b/src/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml deleted file mode 100644 index cca9a982..00000000 --- a/src/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml b/src/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml deleted file mode 100644 index 996976b2..00000000 --- a/src/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/test/BurnUnitTest/VariableHelpers.cpp b/src/test/BurnUnitTest/VariableHelpers.cpp deleted file mode 100644 index 40f958f8..00000000 --- a/src/test/BurnUnitTest/VariableHelpers.cpp +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - - -using namespace System; -using namespace Xunit; - - -namespace Microsoft -{ -namespace Tools -{ -namespace WindowsInstallerXml -{ -namespace Test -{ -namespace Bootstrapper -{ - void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue, BOOL fFormatted) - { - HRESULT hr = S_OK; - - hr = VariableSetString(pVariables, wzVariable, wzValue, FALSE, fFormatted); - TestThrowOnFailure2(hr, L"Failed to set %s to: %s", wzVariable, wzValue); - } - - void VariableSetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LONGLONG llValue) - { - HRESULT hr = S_OK; - - hr = VariableSetNumeric(pVariables, wzVariable, llValue, FALSE); - TestThrowOnFailure2(hr, L"Failed to set %s to: %I64d", wzVariable, llValue); - } - - void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue) - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion = NULL; - - try - { - hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); - TestThrowOnFailure1(hr, L"Failed to parse version '%ls'", wzValue); - - hr = VariableSetVersion(pVariables, wzVariable, pVersion, FALSE); - TestThrowOnFailure2(hr, L"Failed to set %s to: '%ls'", wzVariable, wzValue); - } - finally - { - ReleaseVerutilVersion(pVersion); - } - } - - String^ VariableGetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) - { - HRESULT hr = S_OK; - LPWSTR scz = NULL; - try - { - hr = VariableGetString(pVariables, wzVariable, &scz); - TestThrowOnFailure1(hr, L"Failed to get: %s", wzVariable); - - return gcnew String(scz); - } - finally - { - ReleaseStr(scz); - } - } - - __int64 VariableGetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) - { - HRESULT hr = S_OK; - LONGLONG llValue = 0; - - hr = VariableGetNumeric(pVariables, wzVariable, &llValue); - TestThrowOnFailure1(hr, L"Failed to get: %s", wzVariable); - - return llValue; - } - - String^ VariableGetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pValue = NULL; - - try - { - hr = VariableGetVersion(pVariables, wzVariable, &pValue); - TestThrowOnFailure1(hr, L"Failed to get: %s", wzVariable); - - return gcnew String(pValue->sczVersion); - } - finally - { - ReleaseVerutilVersion(pValue); - } - } - - String^ VariableGetFormattedHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, BOOL* pfContainsHiddenVariable) - { - HRESULT hr = S_OK; - LPWSTR scz = NULL; - try - { - hr = VariableGetFormatted(pVariables, wzVariable, &scz, pfContainsHiddenVariable); - TestThrowOnFailure1(hr, L"Failed to get formatted: %s", wzVariable); - - return gcnew String(scz); - } - finally - { - ReleaseStr(scz); - } - } - - String^ VariableFormatStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzIn) - { - HRESULT hr = S_OK; - LPWSTR scz = NULL; - try - { - hr = VariableFormatString(pVariables, wzIn, &scz, NULL); - TestThrowOnFailure1(hr, L"Failed to format string: '%s'", wzIn); - - return gcnew String(scz); - } - finally - { - ReleaseStr(scz); - } - } - - String^ VariableEscapeStringHelper(LPCWSTR wzIn) - { - HRESULT hr = S_OK; - LPWSTR scz = NULL; - try - { - hr = VariableEscapeString(wzIn, &scz); - TestThrowOnFailure1(hr, L"Failed to escape string: '%s'", wzIn); - - return gcnew String(scz); - } - finally - { - ReleaseStr(scz); - } - } - - bool EvaluateConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition) - { - HRESULT hr = S_OK; - BOOL f = FALSE; - - hr = ConditionEvaluate(pVariables, wzCondition, &f); - TestThrowOnFailure1(hr, L"Failed to evaluate condition: '%s'", wzCondition); - - return f ? true : false; - } - - bool EvaluateFailureConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition) - { - HRESULT hr = S_OK; - BOOL f = FALSE; - - hr = ConditionEvaluate(pVariables, wzCondition, &f); - return E_INVALIDDATA == hr ? true : false; - } - - bool VariableExistsHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) - { - HRESULT hr = S_OK; - BURN_VARIANT value = { }; - - try - { - hr = VariableGetVariant(pVariables, wzVariable, &value); - if (E_NOTFOUND == hr || value.Type == BURN_VARIANT_TYPE_NONE) - { - return false; - } - else - { - TestThrowOnFailure1(hr, L"Failed to find variable: '%s'", wzVariable); - return true; - } - } - finally - { - BVariantUninitialize(&value); - } - } - - int VariableGetTypeHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) - { - HRESULT hr = S_OK; - BURN_VARIANT value = { }; - - try - { - hr = VariableGetVariant(pVariables, wzVariable, &value); - TestThrowOnFailure1(hr, L"Failed to find variable: '%s'", wzVariable); - - return (int)value.Type; - } - finally - { - BVariantUninitialize(&value); - } - } -} -} -} -} -} diff --git a/src/test/BurnUnitTest/VariableHelpers.h b/src/test/BurnUnitTest/VariableHelpers.h deleted file mode 100644 index d460c60f..00000000 --- a/src/test/BurnUnitTest/VariableHelpers.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -namespace Microsoft -{ -namespace Tools -{ -namespace WindowsInstallerXml -{ -namespace Test -{ -namespace Bootstrapper -{ - - -void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue, BOOL fFormatted); -void VariableSetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LONGLONG llValue); -void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue); -System::String^ VariableGetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); -__int64 VariableGetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); -System::String^ VariableGetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); -System::String^ VariableGetFormattedHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, BOOL* pfContainsHiddenVariable); -System::String^ VariableFormatStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzIn); -System::String^ VariableEscapeStringHelper(LPCWSTR wzIn); -bool EvaluateConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition); -bool EvaluateFailureConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition); -bool VariableExistsHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); -int VariableGetTypeHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); - - -} -} -} -} -} diff --git a/src/test/BurnUnitTest/VariableTest.cpp b/src/test/BurnUnitTest/VariableTest.cpp deleted file mode 100644 index 5c9dce03..00000000 --- a/src/test/BurnUnitTest/VariableTest.cpp +++ /dev/null @@ -1,532 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" -#undef GetTempPath -#undef GetEnvironmentVariable - -namespace Microsoft -{ -namespace Tools -{ -namespace WindowsInstallerXml -{ -namespace Test -{ -namespace Bootstrapper -{ - using namespace System; - using namespace Xunit; - - public ref class VariableTest : BurnUnitTest - { - public: - VariableTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) - { - } - - [Fact] - void VariablesBasicTest() - { - HRESULT hr = S_OK; - BURN_VARIABLES variables = { }; - try - { - hr = VariableInitialize(&variables); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - // set variables - VariableSetStringHelper(&variables, L"PROP1", L"VAL1", FALSE); - VariableSetNumericHelper(&variables, L"PROP2", 2); - VariableSetStringHelper(&variables, L"PROP5", L"VAL5", FALSE); - VariableSetStringHelper(&variables, L"PROP3", L"VAL3", FALSE); - VariableSetStringHelper(&variables, L"PROP4", L"VAL4", FALSE); - VariableSetStringHelper(&variables, L"PROP6", L"VAL6", FALSE); - VariableSetStringHelper(&variables, L"PROP7", L"7", FALSE); - VariableSetVersionHelper(&variables, L"PROP8", L"1.1.0.0"); - VariableSetStringHelper(&variables, L"PROP9", L"[VAL9]", TRUE); - - // set overwritten variables - VariableSetStringHelper(&variables, L"OVERWRITTEN_STRING", L"ORIGINAL", FALSE); - VariableSetNumericHelper(&variables, L"OVERWRITTEN_STRING", 42); - - VariableSetNumericHelper(&variables, L"OVERWRITTEN_NUMBER", 5); - VariableSetStringHelper(&variables, L"OVERWRITTEN_NUMBER", L"NEW", FALSE); - - // get and verify variable values - Assert::Equal(gcnew String(L"VAL1"), VariableGetStringHelper(&variables, L"PROP1")); - Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"PROP2")); - Assert::Equal(gcnew String(L"2"), VariableGetStringHelper(&variables, L"PROP2")); - Assert::Equal(gcnew String(L"VAL3"), VariableGetStringHelper(&variables, L"PROP3")); - Assert::Equal(gcnew String(L"VAL4"), VariableGetStringHelper(&variables, L"PROP4")); - Assert::Equal(gcnew String(L"VAL5"), VariableGetStringHelper(&variables, L"PROP5")); - Assert::Equal(gcnew String(L"VAL6"), VariableGetStringHelper(&variables, L"PROP6")); - Assert::Equal(7ll, VariableGetNumericHelper(&variables, L"PROP7")); - Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetVersionHelper(&variables, L"PROP8")); - Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetStringHelper(&variables, L"PROP8")); - Assert::Equal(gcnew String(L"[VAL9]"), VariableGetStringHelper(&variables, L"PROP9")); - - Assert::Equal(42ll, VariableGetNumericHelper(&variables, L"OVERWRITTEN_STRING")); - Assert::Equal(gcnew String(L"NEW"), VariableGetStringHelper(&variables, L"OVERWRITTEN_NUMBER")); - } - finally - { - VariablesUninitialize(&variables); - } - } - - [Fact] - void VariablesParseXmlTest() - { - HRESULT hr = S_OK; - IXMLDOMElement* pixeBundle = NULL; - BURN_VARIABLES variables = { }; - BOOL fContainsHiddenData = FALSE; - try - { - LPCWSTR wzDocument = - L"" - L" "; - - hr = VariableInitialize(&variables); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - // load XML document - LoadBundleXmlHelper(wzDocument, &pixeBundle); - - hr = VariablesParseFromXml(&variables, pixeBundle); - TestThrowOnFailure(hr, L"Failed to parse variables from XML."); - - // get and verify variable values - Assert::Equal((int)BURN_VARIANT_TYPE_NUMERIC, VariableGetTypeHelper(&variables, L"Var1")); - Assert::Equal((int)BURN_VARIANT_TYPE_STRING, VariableGetTypeHelper(&variables, L"Var2")); - Assert::Equal((int)BURN_VARIANT_TYPE_VERSION, VariableGetTypeHelper(&variables, L"Var3")); - Assert::Equal((int)BURN_VARIANT_TYPE_NONE, VariableGetTypeHelper(&variables, L"Var4")); - Assert::Equal((int)BURN_VARIANT_TYPE_FORMATTED, VariableGetTypeHelper(&variables, L"Var6")); - - Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Var1")); - Assert::Equal(gcnew String(L"String value."), VariableGetStringHelper(&variables, L"Var2")); - Assert::Equal(gcnew String(L"1.2.3.4"), VariableGetVersionHelper(&variables, L"Var3")); - Assert::Equal(gcnew String(L"[Formatted]"), VariableGetStringHelper(&variables, L"Var6")); - Assert::Equal(gcnew String(L"supersecret"), VariableGetFormattedHelper(&variables, L"Formatted", &fContainsHiddenData)); - Assert::Equal(TRUE, fContainsHiddenData); - Assert::Equal(gcnew String(L"supersecret"), VariableGetFormattedHelper(&variables, L"Var6", &fContainsHiddenData)); - Assert::Equal(TRUE, fContainsHiddenData); - Assert::Equal(gcnew String(L"String value."), VariableGetFormattedHelper(&variables, L"Var2", &fContainsHiddenData)); - Assert::Equal(FALSE, fContainsHiddenData); - } - finally - { - ReleaseObject(pixeBundle); - VariablesUninitialize(&variables); - } - } - - [Fact] - void VariablesFormatTest() - { - HRESULT hr = S_OK; - BURN_VARIABLES variables = { }; - LPWSTR scz = NULL; - SIZE_T cch = 0; - BOOL fContainsHiddenData = FALSE; - try - { - hr = VariableInitialize(&variables); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - // set variables - VariableSetStringHelper(&variables, L"PROP1", L"VAL1", FALSE); - VariableSetStringHelper(&variables, L"PROP2", L"VAL2", FALSE); - VariableSetNumericHelper(&variables, L"PROP3", 3); - VariableSetStringHelper(&variables, L"PROP4", L"[PROP1]", FALSE); - VariableSetStringHelper(&variables, L"PROP5", L"[PROP2]", FALSE); - VariableSetStringHelper(&variables, L"PROP6", L"[PROP4]", TRUE); - VariableSetStringHelper(&variables, L"PROP7", L"[PROP5]", TRUE); - - // test string formatting - Assert::Equal(gcnew String(L"NOPROP"), VariableFormatStringHelper(&variables, L"NOPROP")); - Assert::Equal(gcnew String(L"VAL1"), VariableFormatStringHelper(&variables, L"[PROP1]")); - Assert::Equal(gcnew String(L" VAL1 "), VariableFormatStringHelper(&variables, L" [PROP1] ")); - Assert::Equal(gcnew String(L"PRE VAL1"), VariableFormatStringHelper(&variables, L"PRE [PROP1]")); - Assert::Equal(gcnew String(L"VAL1 POST"), VariableFormatStringHelper(&variables, L"[PROP1] POST")); - Assert::Equal(gcnew String(L"PRE VAL1 POST"), VariableFormatStringHelper(&variables, L"PRE [PROP1] POST")); - Assert::Equal(gcnew String(L"VAL1 MID VAL2"), VariableFormatStringHelper(&variables, L"[PROP1] MID [PROP2]")); - Assert::Equal(gcnew String(L""), VariableFormatStringHelper(&variables, L"[NONE]")); - Assert::Equal(gcnew String(L""), VariableFormatStringHelper(&variables, L"[prop1]")); - Assert::Equal(gcnew String(L"["), VariableFormatStringHelper(&variables, L"[\\[]")); - Assert::Equal(gcnew String(L"]"), VariableFormatStringHelper(&variables, L"[\\]]")); - Assert::Equal(gcnew String(L"[]"), VariableFormatStringHelper(&variables, L"[]")); - Assert::Equal(gcnew String(L"[NONE"), VariableFormatStringHelper(&variables, L"[NONE")); - Assert::Equal(gcnew String(L"VAL2"), VariableGetFormattedHelper(&variables, L"PROP2", &fContainsHiddenData)); - Assert::Equal(FALSE, fContainsHiddenData); - Assert::Equal(gcnew String(L"3"), VariableGetFormattedHelper(&variables, L"PROP3", &fContainsHiddenData)); - Assert::Equal(FALSE, fContainsHiddenData); - Assert::Equal(gcnew String(L"[PROP1]"), VariableGetFormattedHelper(&variables, L"PROP4", &fContainsHiddenData)); - Assert::Equal(FALSE, fContainsHiddenData); - Assert::Equal(gcnew String(L"[PROP2]"), VariableGetFormattedHelper(&variables, L"PROP5", &fContainsHiddenData)); - Assert::Equal(FALSE, fContainsHiddenData); - Assert::Equal(gcnew String(L"[PROP1]"), VariableGetFormattedHelper(&variables, L"PROP6", &fContainsHiddenData)); - Assert::Equal(FALSE, fContainsHiddenData); - Assert::Equal(gcnew String(L"[PROP2]"), VariableGetFormattedHelper(&variables, L"PROP7", &fContainsHiddenData)); - Assert::Equal(FALSE, fContainsHiddenData); - - hr = VariableFormatString(&variables, L"PRE [PROP1] POST", &scz, &cch); - TestThrowOnFailure(hr, L"Failed to format string"); - - Assert::Equal((SIZE_T)lstrlenW(scz), cch); - - hr = VariableFormatString(&variables, L"PRE [PROP1] POST", NULL, &cch); - TestThrowOnFailure(hr, L"Failed to format string"); - - Assert::Equal((SIZE_T)lstrlenW(scz), cch); - } - finally - { - VariablesUninitialize(&variables); - ReleaseStr(scz); - } - } - - [Fact] - void VariablesEscapeTest() - { - // test string escaping - Assert::Equal(gcnew String(L"[\\[]"), VariableEscapeStringHelper(L"[")); - Assert::Equal(gcnew String(L"[\\]]"), VariableEscapeStringHelper(L"]")); - Assert::Equal(gcnew String(L" [\\[]TEXT[\\]] "), VariableEscapeStringHelper(L" [TEXT] ")); - } - - [Fact] - void VariablesConditionTest() - { - HRESULT hr = S_OK; - BURN_VARIABLES variables = { }; - try - { - hr = VariableInitialize(&variables); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - // set variables - VariableSetStringHelper(&variables, L"PROP1", L"VAL1", FALSE); - VariableSetStringHelper(&variables, L"PROP2", L"VAL2", FALSE); - VariableSetStringHelper(&variables, L"PROP3", L"VAL3", FALSE); - VariableSetStringHelper(&variables, L"PROP4", L"BEGIN MID END", FALSE); - VariableSetNumericHelper(&variables, L"PROP5", 5); - VariableSetNumericHelper(&variables, L"PROP6", 6); - VariableSetStringHelper(&variables, L"PROP7", L"", FALSE); - VariableSetNumericHelper(&variables, L"PROP8", 0); - VariableSetStringHelper(&variables, L"_PROP9", L"VAL9", FALSE); - VariableSetNumericHelper(&variables, L"PROP10", -10); - VariableSetNumericHelper(&variables, L"PROP11", 9223372036854775807ll); - VariableSetNumericHelper(&variables, L"PROP12", -9223372036854775808ll); - VariableSetNumericHelper(&variables, L"PROP13", 0x00010000); - VariableSetNumericHelper(&variables, L"PROP14", 0x00000001); - VariableSetNumericHelper(&variables, L"PROP15", 0x00010001); - VariableSetVersionHelper(&variables, L"PROP16", L"0.0.0.0"); - VariableSetVersionHelper(&variables, L"PROP17", L"1.0.0.0"); - VariableSetVersionHelper(&variables, L"PROP18", L"1.1.0.0"); - VariableSetVersionHelper(&variables, L"PROP19", L"1.1.1.0"); - VariableSetVersionHelper(&variables, L"PROP20", L"1.1.1.1"); - VariableSetNumericHelper(&variables, L"vPROP21", 1); - VariableSetVersionHelper(&variables, L"PROP22", L"65535.65535.65535.65535"); - VariableSetStringHelper(&variables, L"PROP23", L"1.1.1", FALSE); - VariableSetStringHelper(&variables, L"PROP24", L"[PROP1]", TRUE); - VariableSetStringHelper(&variables, L"PROP25", L"[PROP7]", TRUE); - VariableSetStringHelper(&variables, L"PROP26", L"[PROP8]", TRUE); - VariableSetStringHelper(&variables, L"PROP27", L"[PROP16]", TRUE); - - // test conditions - Assert::True(EvaluateConditionHelper(&variables, L"PROP1")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP5")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP7")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP8")); - Assert::True(EvaluateConditionHelper(&variables, L"_PROP9")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP16")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP17")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP24=\"VAL1\"")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP25")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP26")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP27")); - - Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\"")); - Assert::False(EvaluateConditionHelper(&variables, L"NONE = \"NOT\"")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP1 <> \"VAL1\"")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP1 ~<> \"VAL1\"")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP1 ~<> \"Val1\"")); - Assert::True(EvaluateConditionHelper(&variables, L"NONE <> \"NOT\"")); - Assert::True(EvaluateConditionHelper(&variables, L"NONE ~<> \"NOT\"")); - - Assert::True(EvaluateConditionHelper(&variables, L"PROP1 ~= \"val1\"")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"val1\"")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP1 ~<> \"val1\"")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP1 <> \"val1\"")); - - Assert::True(EvaluateConditionHelper(&variables, L"PROP5 = 5")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP5 = 0")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP5 <> 5")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP5 <> 0")); - - Assert::True(EvaluateConditionHelper(&variables, L"PROP10 = -10")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP10 <> -10")); - - Assert::True(EvaluateConditionHelper(&variables, L"PROP17 = v1")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP17 = v0")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP17 <> v1")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP17 <> v0")); - - Assert::True(EvaluateConditionHelper(&variables, L"PROP16 = v0")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP17 = v1")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP18 = v1.1")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP19 = v1.1.1")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP20 = v1.1.1.1")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP20 > v1.1.1.1.0")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP20 > v1.1.1.1.1")); - Assert::True(EvaluateConditionHelper(&variables, L"vPROP21 = 1")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP23 = v1.1.1")); - Assert::True(EvaluateConditionHelper(&variables, L"v1.1.1 = PROP23")); - Assert::False(EvaluateConditionHelper(&variables, L"v1.1.1<>PROP23")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP1 <> v1.1.1")); - Assert::True(EvaluateConditionHelper(&variables, L"v1.1.1 <> PROP1")); - - Assert::False(EvaluateConditionHelper(&variables, L"PROP11 = 9223372036854775806")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP11 = 9223372036854775807")); - Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP11 = 9223372036854775808")); - Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP11 = 92233720368547758070000")); - - Assert::False(EvaluateConditionHelper(&variables, L"PROP12 = -9223372036854775807")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP12 = -9223372036854775808")); - Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP12 = -9223372036854775809")); - Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP12 = -92233720368547758080000")); - - Assert::True(EvaluateConditionHelper(&variables, L"PROP22 = v65535.65535.65535.65535")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP22 < v65536.65535.65535.65535")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP22 < v65535.655350000.65535.65535")); - - Assert::True(EvaluateConditionHelper(&variables, L"PROP5 < 6")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP5 < 5")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP5 > 4")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP5 > 5")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP5 <= 6")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP5 <= 5")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP5 <= 4")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP5 >= 4")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP5 >= 5")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP5 >= 6")); - - Assert::True(EvaluateConditionHelper(&variables, L"PROP4 << \"BEGIN\"")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP4 << \"END\"")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP4 >> \"END\"")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP4 >> \"BEGIN\"")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP4 >< \"MID\"")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP4 >< \"NONE\"")); - - Assert::True(EvaluateConditionHelper(&variables, L"PROP16 < v1.1")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP16 < v0")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP17 > v0.12")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP17 > v1")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP18 >= v1.0")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP18 >= v1.1")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP18 >= v2.1")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP19 <= v1.1234.1")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP19 <= v1.1.1")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP19 <= v1.0.123")); - - Assert::True(EvaluateConditionHelper(&variables, L"PROP6 = \"6\"")); - Assert::True(EvaluateConditionHelper(&variables, L"\"6\" = PROP6")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP6 = \"ABC\"")); - Assert::False(EvaluateConditionHelper(&variables, L"\"ABC\" = PROP6")); - Assert::False(EvaluateConditionHelper(&variables, L"\"ABC\" = PROP6")); - - Assert::True(EvaluateConditionHelper(&variables, L"PROP13 << 1")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP13 << 0")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP14 >> 1")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP14 >> 0")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP15 >< 65537")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP15 >< 0")); - - Assert::False(EvaluateConditionHelper(&variables, L"NOT PROP1")); - Assert::True(EvaluateConditionHelper(&variables, L"NOT (PROP1 <> \"VAL1\")")); - - Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\"")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" AND PROP2 = \"VAL2\"")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" AND PROP2 = \"NOT\"")); - - Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" OR PROP2 = \"VAL2\"")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" OR PROP2 = \"NOT\"")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" OR PROP2 = \"VAL2\"")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" OR PROP2 = \"NOT\"")); - - Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\" OR PROP3 = \"NOT\"")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"VAL3\"")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"NOT\"")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND (PROP2 = \"NOT\" OR PROP3 = \"VAL3\")")); - Assert::True(EvaluateConditionHelper(&variables, L"(PROP1 = \"VAL1\" AND PROP2 = \"VAL2\") OR PROP3 = \"NOT\"")); - - Assert::True(EvaluateConditionHelper(&variables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"VAL2\"")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP3 = \"VAL3\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); - Assert::False(EvaluateConditionHelper(&variables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); - Assert::True(EvaluateConditionHelper(&variables, L"(PROP3 = \"NOT\" OR PROP1 = \"VAL1\") AND PROP2 = \"VAL2\"")); - Assert::True(EvaluateConditionHelper(&variables, L"PROP3 = \"NOT\" OR (PROP1 = \"VAL1\" AND PROP2 = \"VAL2\")")); - - Assert::True(EvaluateFailureConditionHelper(&variables, L"=")); - Assert::True(EvaluateFailureConditionHelper(&variables, L"(PROP1")); - Assert::True(EvaluateFailureConditionHelper(&variables, L"(PROP1 = \"")); - Assert::True(EvaluateFailureConditionHelper(&variables, L"1A")); - Assert::True(EvaluateFailureConditionHelper(&variables, L"*")); - - Assert::True(EvaluateFailureConditionHelper(&variables, L"1 == 1")); - } - finally - { - VariablesUninitialize(&variables); - } - } - - [Fact] - void VariablesSerializationTest() - { - HRESULT hr = S_OK; - BYTE* pbBuffer = NULL; - SIZE_T cbBuffer = 0; - SIZE_T iBuffer = 0; - BURN_VARIABLES variables1 = { }; - BURN_VARIABLES variables2 = { }; - try - { - // serialize - hr = VariableInitialize(&variables1); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - VariableSetStringHelper(&variables1, L"PROP1", L"VAL1", FALSE); - VariableSetNumericHelper(&variables1, L"PROP2", 2); - VariableSetVersionHelper(&variables1, L"PROP3", L"1.1.1.1"); - VariableSetStringHelper(&variables1, L"PROP4", L"VAL4", FALSE); - VariableSetStringHelper(&variables1, L"PROP5", L"[PROP1]", TRUE); - - hr = VariableSerialize(&variables1, FALSE, &pbBuffer, &cbBuffer); - TestThrowOnFailure(hr, L"Failed to serialize variables."); - - // deserialize - hr = VariableInitialize(&variables2); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - hr = VariableDeserialize(&variables2, FALSE, pbBuffer, cbBuffer, &iBuffer); - TestThrowOnFailure(hr, L"Failed to deserialize variables."); - - Assert::Equal(gcnew String(L"VAL1"), VariableGetStringHelper(&variables2, L"PROP1")); - Assert::Equal(2ll, VariableGetNumericHelper(&variables2, L"PROP2")); - Assert::Equal(gcnew String(L"1.1.1.1"), VariableGetVersionHelper(&variables2, L"PROP3")); - Assert::Equal(gcnew String(L"VAL4"), VariableGetStringHelper(&variables2, L"PROP4")); - Assert::Equal(gcnew String(L"[PROP1]"), VariableGetStringHelper(&variables2, L"PROP5")); - - Assert::Equal((int)BURN_VARIANT_TYPE_STRING, VariableGetTypeHelper(&variables2, L"PROP1")); - Assert::Equal((int)BURN_VARIANT_TYPE_NUMERIC, VariableGetTypeHelper(&variables2, L"PROP2")); - Assert::Equal((int)BURN_VARIANT_TYPE_VERSION, VariableGetTypeHelper(&variables2, L"PROP3")); - Assert::Equal((int)BURN_VARIANT_TYPE_STRING, VariableGetTypeHelper(&variables2, L"PROP4")); - Assert::Equal((int)BURN_VARIANT_TYPE_FORMATTED, VariableGetTypeHelper(&variables2, L"PROP5")); - } - finally - { - ReleaseBuffer(pbBuffer); - VariablesUninitialize(&variables1); - VariablesUninitialize(&variables2); - } - } - - [Fact] - void VariablesBuiltInTest() - { - HRESULT hr = S_OK; - BURN_VARIABLES variables = { }; - try - { - hr = VariableInitialize(&variables); - TestThrowOnFailure(hr, L"Failed to initialize variables."); - - // VersionMsi - Assert::True(EvaluateConditionHelper(&variables, L"VersionMsi >= v1.1")); - - // VersionNT - Assert::True(EvaluateConditionHelper(&variables, L"VersionNT <> v0.0.0.0")); - - // VersionNT64 - if (nullptr == Environment::GetEnvironmentVariable("ProgramFiles(x86)")) - { - Assert::False(EvaluateConditionHelper(&variables, L"VersionNT64")); - } - else - { - Assert::True(EvaluateConditionHelper(&variables, L"VersionNT64")); - } - - // attempt to set a built-in property - hr = VariableSetString(&variables, L"VersionNT", L"VAL", FALSE, FALSE); - Assert::Equal(E_INVALIDARG, hr); - Assert::False(EvaluateConditionHelper(&variables, L"VersionNT = \"VAL\"")); - - VariableGetNumericHelper(&variables, L"NTProductType"); - VariableGetNumericHelper(&variables, L"NTSuiteBackOffice"); - VariableGetNumericHelper(&variables, L"NTSuiteDataCenter"); - VariableGetNumericHelper(&variables, L"NTSuiteEnterprise"); - VariableGetNumericHelper(&variables, L"NTSuitePersonal"); - VariableGetNumericHelper(&variables, L"NTSuiteSmallBusiness"); - VariableGetNumericHelper(&variables, L"NTSuiteSmallBusinessRestricted"); - VariableGetNumericHelper(&variables, L"NTSuiteWebServer"); - VariableGetNumericHelper(&variables, L"CompatibilityMode"); - VariableGetNumericHelper(&variables, L"Privileged"); - VariableGetNumericHelper(&variables, L"SystemLanguageID"); - VariableGetNumericHelper(&variables, L"TerminalServer"); - VariableGetNumericHelper(&variables, L"UserUILanguageID"); - VariableGetNumericHelper(&variables, L"UserLanguageID"); - - // known folders - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::ApplicationData) + "\\", VariableGetStringHelper(&variables, L"AppDataFolder")); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::CommonApplicationData) + "\\", VariableGetStringHelper(&variables, L"CommonAppDataFolder")); - - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::ProgramFiles) + "\\", VariableGetStringHelper(&variables, L"ProgramFilesFolder")); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::DesktopDirectory) + "\\", VariableGetStringHelper(&variables, L"DesktopFolder")); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Favorites) + "\\", VariableGetStringHelper(&variables, L"FavoritesFolder")); - VariableGetStringHelper(&variables, L"FontsFolder"); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData) + "\\", VariableGetStringHelper(&variables, L"LocalAppDataFolder")); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Personal) + "\\", VariableGetStringHelper(&variables, L"PersonalFolder")); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Programs) + "\\", VariableGetStringHelper(&variables, L"ProgramMenuFolder")); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::SendTo) + "\\", VariableGetStringHelper(&variables, L"SendToFolder")); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::StartMenu) + "\\", VariableGetStringHelper(&variables, L"StartMenuFolder")); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Startup) + "\\", VariableGetStringHelper(&variables, L"StartupFolder")); - VariableGetStringHelper(&variables, L"SystemFolder"); - VariableGetStringHelper(&variables, L"WindowsFolder"); - VariableGetStringHelper(&variables, L"WindowsVolume"); - - Assert::Equal(System::IO::Path::GetTempPath(), System::IO::Path::GetFullPath(VariableGetStringHelper(&variables, L"TempFolder"))); - - VariableGetStringHelper(&variables, L"AdminToolsFolder"); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::CommonProgramFiles) + "\\", VariableGetStringHelper(&variables, L"CommonFilesFolder")); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::MyPictures) + "\\", VariableGetStringHelper(&variables, L"MyPicturesFolder")); - Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Templates) + "\\", VariableGetStringHelper(&variables, L"TemplateFolder")); - - if (Environment::Is64BitOperatingSystem) - { - VariableGetStringHelper(&variables, L"ProgramFiles64Folder"); - VariableGetStringHelper(&variables, L"CommonFiles64Folder"); - VariableGetStringHelper(&variables, L"System64Folder"); - } - } - finally - { - VariablesUninitialize(&variables); - } - } - }; -} -} -} -} -} diff --git a/src/test/BurnUnitTest/VariantTest.cpp b/src/test/BurnUnitTest/VariantTest.cpp deleted file mode 100644 index 43899a2b..00000000 --- a/src/test/BurnUnitTest/VariantTest.cpp +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -namespace Microsoft -{ -namespace Tools -{ -namespace WindowsInstallerXml -{ -namespace Test -{ -namespace Bootstrapper -{ - using namespace System; - using namespace Xunit; - - public ref class VariantTest : BurnUnitTest - { - public: - VariantTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) - { - } - - [Fact] - void VariantBasicTest() - { - BURN_VARIANT expectedVariants[10]; - BURN_VARIANT actualVariants[10]; - for (DWORD i = 0; i < 10; i++) - { - BVariantUninitialize(expectedVariants + i); - BVariantUninitialize(actualVariants + i); - } - - try - { - InitNumericValue(expectedVariants + 0, 2, FALSE, L"PROP1", actualVariants + 0); - InitStringValue(expectedVariants + 1, L"VAL2", FALSE, L"PROP2", actualVariants + 1); - InitVersionValue(expectedVariants + 2, L"1.1.0.0", FALSE, L"PROP3", actualVariants + 2); - InitNoneValue(expectedVariants + 3, FALSE, L"PROP4", actualVariants + 3); - InitNoneValue(expectedVariants + 4, TRUE, L"PROP5", actualVariants + 4); - InitVersionValue(expectedVariants + 5, L"1.1.1.0", TRUE, L"PROP6", actualVariants + 5); - InitStringValue(expectedVariants + 6, L"7", TRUE, L"PROP7", actualVariants + 6); - InitNumericValue(expectedVariants + 7, 11, TRUE, L"PROP8", actualVariants + 7); - InitFormattedValue(expectedVariants + 8, L"VAL9", FALSE, L"PROP9", actualVariants + 8); - InitFormattedValue(expectedVariants + 9, L"VAL10", TRUE, L"PROP10", actualVariants + 9); - - VerifyNumericValue(expectedVariants + 0, actualVariants + 0); - VerifyStringValue(expectedVariants + 1, actualVariants + 1); - VerifyVersionValue(expectedVariants + 2, actualVariants + 2); - VerifyNoneValue(expectedVariants + 3, actualVariants + 3); - VerifyNoneValue(expectedVariants + 4, actualVariants + 4); - VerifyVersionValue(expectedVariants + 5, actualVariants + 5); - VerifyStringValue(expectedVariants + 6, actualVariants + 6); - VerifyNumericValue(expectedVariants + 7, actualVariants + 7); - VerifyFormattedValue(expectedVariants + 8, actualVariants + 8); - VerifyFormattedValue(expectedVariants + 9, actualVariants + 9); - } - finally - { - for (DWORD i = 0; i < 10; i++) - { - BVariantUninitialize(expectedVariants + i); - BVariantUninitialize(actualVariants + i); - } - } - } - - private: - void InitFormattedValue(BURN_VARIANT* pValue, LPWSTR wzValue, BOOL /*fHidden*/, LPCWSTR wz, BURN_VARIANT* pActualValue) - { - HRESULT hr = S_OK; - pValue->Type = BURN_VARIANT_TYPE_FORMATTED; - - hr = StrAllocString(&pValue->sczValue, wzValue, 0); - NativeAssert::Succeeded(hr, "Failed to alloc string: {0}", wzValue); - - hr = BVariantCopy(pValue, pActualValue); - NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); - } - - void InitNoneValue(BURN_VARIANT* pValue, BOOL /*fHidden*/, LPCWSTR wz, BURN_VARIANT* pActualValue) - { - HRESULT hr = S_OK; - pValue->Type = BURN_VARIANT_TYPE_NONE; - - hr = BVariantCopy(pValue, pActualValue); - NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); - } - - void InitNumericValue(BURN_VARIANT* pValue, LONGLONG llValue, BOOL /*fHidden*/, LPCWSTR wz, BURN_VARIANT* pActualValue) - { - HRESULT hr = S_OK; - pValue->Type = BURN_VARIANT_TYPE_NUMERIC; - pValue->llValue = llValue; - - hr = BVariantCopy(pValue, pActualValue); - NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); - } - - void InitStringValue(BURN_VARIANT* pValue, LPWSTR wzValue, BOOL /*fHidden*/, LPCWSTR wz, BURN_VARIANT* pActualValue) - { - HRESULT hr = S_OK; - pValue->Type = BURN_VARIANT_TYPE_STRING; - - hr = StrAllocString(&pValue->sczValue, wzValue, 0); - NativeAssert::Succeeded(hr, "Failed to alloc string: {0}", wzValue); - - hr = BVariantCopy(pValue, pActualValue); - NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); - } - - void InitVersionValue(BURN_VARIANT* pValue, LPCWSTR wzValue, BOOL /*fHidden*/, LPCWSTR wz, BURN_VARIANT* pActualValue) - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pVersion = NULL; - - try - { - hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); - NativeAssert::Succeeded(hr, "Failed to parse version {0}", wzValue); - - pValue->Type = BURN_VARIANT_TYPE_VERSION; - pValue->pValue = pVersion; - pVersion = NULL; - - hr = BVariantCopy(pValue, pActualValue); - NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); - } - finally - { - ReleaseVerutilVersion(pVersion); - } - } - - void VerifyFormattedValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) - { - HRESULT hr = S_OK; - LPWSTR sczValue = NULL; - NativeAssert::Equal(BURN_VARIANT_TYPE_FORMATTED, pExpectedValue->Type); - NativeAssert::Equal(BURN_VARIANT_TYPE_FORMATTED, pActualValue->Type); - - try - { - hr = BVariantGetString(pActualValue, &sczValue); - NativeAssert::Succeeded(hr, "Failed to get string value"); - - NativeAssert::StringEqual(pExpectedValue->sczValue, sczValue); - } - finally - { - ReleaseStr(sczValue); - } - } - - void VerifyNumericValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) - { - HRESULT hr = S_OK; - LONGLONG llValue = 0; - NativeAssert::Equal(BURN_VARIANT_TYPE_NUMERIC, pExpectedValue->Type); - NativeAssert::Equal(BURN_VARIANT_TYPE_NUMERIC, pActualValue->Type); - - hr = BVariantGetNumeric(pActualValue, &llValue); - NativeAssert::Succeeded(hr, "Failed to get numeric value"); - - NativeAssert::Equal(pExpectedValue->llValue, llValue); - } - - void VerifyNoneValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) - { - NativeAssert::Equal(BURN_VARIANT_TYPE_NONE, pExpectedValue->Type); - NativeAssert::Equal(BURN_VARIANT_TYPE_NONE, pActualValue->Type); - NativeAssert::Equal(pExpectedValue->llValue, pActualValue->llValue); - } - - void VerifyStringValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) - { - HRESULT hr = S_OK; - LPWSTR sczValue = NULL; - NativeAssert::Equal(BURN_VARIANT_TYPE_STRING, pExpectedValue->Type); - NativeAssert::Equal(BURN_VARIANT_TYPE_STRING, pActualValue->Type); - - try - { - hr = BVariantGetString(pActualValue, &sczValue); - NativeAssert::Succeeded(hr, "Failed to get string value"); - - NativeAssert::StringEqual(pExpectedValue->sczValue, sczValue); - } - finally - { - ReleaseStr(sczValue); - } - } - - void VerifyVersionValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) - { - HRESULT hr = S_OK; - VERUTIL_VERSION* pValue = NULL; - NativeAssert::Equal(BURN_VARIANT_TYPE_VERSION, pExpectedValue->Type); - NativeAssert::Equal(BURN_VARIANT_TYPE_VERSION, pActualValue->Type); - - try - { - hr = BVariantGetVersion(pActualValue, &pValue); - NativeAssert::Succeeded(hr, "Failed to get version value"); - - NativeAssert::StringEqual(pExpectedValue->pValue->sczVersion, pActualValue->pValue->sczVersion); - } - finally - { - ReleaseVerutilVersion(pValue); - } - } - }; -} -} -} -} -} diff --git a/src/test/BurnUnitTest/packages.config b/src/test/BurnUnitTest/packages.config deleted file mode 100644 index 1d36c387..00000000 --- a/src/test/BurnUnitTest/packages.config +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/test/BurnUnitTest/precomp.cpp b/src/test/BurnUnitTest/precomp.cpp deleted file mode 100644 index 37664a1c..00000000 --- a/src/test/BurnUnitTest/precomp.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" diff --git a/src/test/BurnUnitTest/precomp.h b/src/test/BurnUnitTest/precomp.h deleted file mode 100644 index d2b57d61..00000000 --- a/src/test/BurnUnitTest/precomp.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#include -#include -#include -#include -#include -#include -#include -#include -#include "wininet.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "BootstrapperEngine.h" -#include "BootstrapperApplication.h" -#include "BundleExtensionEngine.h" -#include "BundleExtension.h" - -#include "platform.h" -#include "variant.h" -#include "variable.h" -#include "condition.h" -#include "section.h" -#include "approvedexe.h" -#include "container.h" -#include "payload.h" -#include "cabextract.h" -#include "burnextension.h" -#include "search.h" -#include "userexperience.h" -#include "package.h" -#include "update.h" -#include "pseudobundle.h" -#include "registration.h" -#include "plan.h" -#include "pipe.h" -#include "logging.h" -#include "core.h" -#include "cache.h" -#include "apply.h" -#include "exeengine.h" -#include "msiengine.h" -#include "mspengine.h" -#include "msuengine.h" -#include "dependency.h" -#include "elevation.h" -#include "embedded.h" -#include "manifest.h" -#include "splashscreen.h" -#include "detect.h" - -#pragma managed -#include - -#include "BurnTestException.h" -#include "BurnTestFixture.h" -#include "BurnUnitTest.h" -#include "VariableHelpers.h" -#include "ManifestHelpers.h" diff --git a/src/version.json b/src/version.json new file mode 100644 index 00000000..5f857771 --- /dev/null +++ b/src/version.json @@ -0,0 +1,11 @@ +{ + "version": "4.0", + "publicReleaseRefSpec": [ + "^refs/heads/master$" + ], + "cloudBuild": { + "buildNumber": { + "enabled": true + } + } +} diff --git a/version.json b/version.json deleted file mode 100644 index 5f857771..00000000 --- a/version.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": "4.0", - "publicReleaseRefSpec": [ - "^refs/heads/master$" - ], - "cloudBuild": { - "buildNumber": { - "enabled": true - } - } -} -- cgit v1.2.3-55-g6feb