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