From 386a3578413ba16b3c0615d47870ee44a0e461f6 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 30 Mar 2022 17:08:40 -0500 Subject: Implement BundlePackage. 3693 --- .../inc/BootstrapperApplication.h | 1 + .../IBootstrapperApplication.cs | 5 + .../WixToolset.Data/Symbols/SymbolDefinitions.cs | 8 + .../Symbols/WixBundleBundlePackagePayloadSymbol.cs | 36 +++ .../Symbols/WixBundleBundlePackageSymbol.cs | 118 +++++++++ .../Symbols/WixBundlePackageSymbol.cs | 3 +- src/burn/engine/apply.cpp | 102 ++++++++ src/burn/engine/bundlepackageengine.cpp | 272 ++++++++++++++++++++- src/burn/engine/bundlepackageengine.h | 26 ++ src/burn/engine/core.cpp | 16 +- src/burn/engine/core.h | 1 + src/burn/engine/elevation.cpp | 176 ++++++++++++- src/burn/engine/elevation.h | 9 + src/burn/engine/exeengine.cpp | 24 +- src/burn/engine/logging.cpp | 2 + src/burn/engine/package.cpp | 18 +- src/burn/engine/package.h | 4 +- src/burn/engine/plan.cpp | 24 +- src/burn/engine/plan.h | 8 + src/burn/engine/registration.cpp | 5 +- src/burn/engine/registration.h | 2 + src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj | 1 + src/burn/test/BurnUnitTest/PlanTest.cpp | 141 ++++++++++- .../PlanTest/BundlePackage_Multiple_manifest.xml | 1 + .../MultipleBundlePackagesBundle.wixproj | 19 ++ .../MultipleBundlePackagesBundle.wxs | 11 + .../WixToolsetTest.BurnE2E/BundlePackageTests.cs | 51 ++++ .../WixToolset.Core.Burn/Bind/BindBundleCommand.cs | 7 + .../Bind/GenerateManifestDataFromIRCommand.cs | 2 + src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs | 32 ++- src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs | 16 +- src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs | 17 +- .../Bundles/CreateBurnManifestCommand.cs | 11 +- .../Bundles/GetPackageFacadesCommand.cs | 27 +- .../PerformBundleBackendValidationCommand.cs | 11 +- .../Bundles/ProcessBundlePackageCommand.cs | 152 ++++++++++++ src/wix/WixToolset.Core/Compiler_Bundle.cs | 52 +++- src/wix/WixToolset.Core/Compiler_Dependency.cs | 2 +- .../BundlePackageFixture.cs | 114 +++++++++ .../TestData/BundlePackage/BundlePackage.wxs | 10 + 40 files changed, 1439 insertions(+), 98 deletions(-) create mode 100644 src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackagePayloadSymbol.cs create mode 100644 src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackageSymbol.cs create mode 100644 src/burn/test/BurnUnitTest/TestData/PlanTest/BundlePackage_Multiple_manifest.xml create mode 100644 src/test/burn/TestData/BundlePackageTests/MultipleBundlePackagesBundle/MultipleBundlePackagesBundle.wixproj create mode 100644 src/test/burn/TestData/BundlePackageTests/MultipleBundlePackagesBundle/MultipleBundlePackagesBundle.wxs create mode 100644 src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs create mode 100644 src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs create mode 100644 src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs create mode 100644 src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/BundlePackage.wxs (limited to 'src') diff --git a/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h index df8cac76..943f5ead 100644 --- a/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ b/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -106,6 +106,7 @@ enum BOOTSTRAPPER_RELATION_TYPE BOOTSTRAPPER_RELATION_DEPENDENT_ADDON, BOOTSTRAPPER_RELATION_DEPENDENT_PATCH, BOOTSTRAPPER_RELATION_UPDATE, + BOOTSTRAPPER_RELATION_CHAIN_PACKAGE, }; enum BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE diff --git a/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs b/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs index 1786eecd..7cf0957a 100644 --- a/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs +++ b/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs @@ -1714,6 +1714,11 @@ namespace WixToolset.Mba.Core /// /// Update, + + /// + /// + /// + ChainPackage, } /// diff --git a/src/api/wix/WixToolset.Data/Symbols/SymbolDefinitions.cs b/src/api/wix/WixToolset.Data/Symbols/SymbolDefinitions.cs index 21ca1b6d..d4a91343 100644 --- a/src/api/wix/WixToolset.Data/Symbols/SymbolDefinitions.cs +++ b/src/api/wix/WixToolset.Data/Symbols/SymbolDefinitions.cs @@ -125,6 +125,8 @@ namespace WixToolset.Data WixBundleCustomData, WixBundleCustomDataAttribute, WixBundleCustomDataCell, + WixBundleBundlePackage, + WixBundleBundlePackagePayload, WixBundleExePackage, WixBundleExePackagePayload, WixBundleExtension, @@ -553,6 +555,12 @@ namespace WixToolset.Data case SymbolDefinitionType.WixBundle: return SymbolDefinitions.WixBundle; + case SymbolDefinitionType.WixBundleBundlePackage: + return SymbolDefinitions.WixBundleBundlePackage; + + case SymbolDefinitionType.WixBundleBundlePackagePayload: + return SymbolDefinitions.WixBundleBundlePackagePayload; + case SymbolDefinitionType.WixBundleContainer: return SymbolDefinitions.WixBundleContainer; diff --git a/src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackagePayloadSymbol.cs b/src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackagePayloadSymbol.cs new file mode 100644 index 00000000..a171682d --- /dev/null +++ b/src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackagePayloadSymbol.cs @@ -0,0 +1,36 @@ +// Copyright (c) .NET 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 WixToolset.Data +{ + using WixToolset.Data.Symbols; + + public static partial class SymbolDefinitions + { + public static readonly IntermediateSymbolDefinition WixBundleBundlePackagePayload = new IntermediateSymbolDefinition( + SymbolDefinitionType.WixBundleBundlePackagePayload, + new IntermediateFieldDefinition[] + { + }, + typeof(WixBundleBundlePackagePayloadSymbol)); + } +} + +namespace WixToolset.Data.Symbols +{ + public enum WixBundleBundlePackagePayloadSymbolFields + { + } + + public class WixBundleBundlePackagePayloadSymbol : IntermediateSymbol + { + public WixBundleBundlePackagePayloadSymbol() : base(SymbolDefinitions.WixBundleBundlePackagePayload, null, null) + { + } + + public WixBundleBundlePackagePayloadSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(SymbolDefinitions.WixBundleBundlePackagePayload, sourceLineNumber, id) + { + } + + public IntermediateField this[WixBundleBundlePackagePayloadSymbolFields index] => this.Fields[(int)index]; + } +} diff --git a/src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackageSymbol.cs b/src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackageSymbol.cs new file mode 100644 index 00000000..36b9eb67 --- /dev/null +++ b/src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackageSymbol.cs @@ -0,0 +1,118 @@ +// Copyright (c) .NET 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 WixToolset.Data +{ + using WixToolset.Data.Symbols; + + public static partial class SymbolDefinitions + { + public static readonly IntermediateSymbolDefinition WixBundleBundlePackage = new IntermediateSymbolDefinition( + SymbolDefinitionType.WixBundleBundlePackage, + new[] + { + new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.Attributes), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.BundleId), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.InstallCommand), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.RepairCommand), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.UninstallCommand), IntermediateFieldType.String), + }, + typeof(WixBundleBundlePackageSymbol)); + } +} + +namespace WixToolset.Data.Symbols +{ + using System; + + public enum WixBundleBundlePackageSymbolFields + { + Attributes, + BundleId, + InstallCommand, + RepairCommand, + UninstallCommand, + } + + [Flags] + public enum WixBundleBundlePackageAttributes + { + None = 0, + SupportsBurnProtocol = 1, + Win64 = 2, + } + + public class WixBundleBundlePackageSymbol : IntermediateSymbol + { + public WixBundleBundlePackageSymbol() : base(SymbolDefinitions.WixBundleBundlePackage, null, null) + { + } + + public WixBundleBundlePackageSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(SymbolDefinitions.WixBundleBundlePackage, sourceLineNumber, id) + { + } + + public IntermediateField this[WixBundleBundlePackageSymbolFields index] => this.Fields[(int)index]; + + public WixBundleBundlePackageAttributes Attributes + { + get => (WixBundleBundlePackageAttributes)(int)this.Fields[(int)WixBundleBundlePackageSymbolFields.Attributes]; + set => this.Set((int)WixBundleBundlePackageSymbolFields.Attributes, (int)value); + } + + public string BundleId + { + get => (string)this.Fields[(int)WixBundleBundlePackageSymbolFields.BundleId]; + set => this.Set((int)WixBundleBundlePackageSymbolFields.BundleId, value); + } + + public string InstallCommand + { + get => (string)this.Fields[(int)WixBundleBundlePackageSymbolFields.InstallCommand]; + set => this.Set((int)WixBundleBundlePackageSymbolFields.InstallCommand, value); + } + + public string RepairCommand + { + get => (string)this.Fields[(int)WixBundleBundlePackageSymbolFields.RepairCommand]; + set => this.Set((int)WixBundleBundlePackageSymbolFields.RepairCommand, value); + } + + public string UninstallCommand + { + get => (string)this.Fields[(int)WixBundleBundlePackageSymbolFields.UninstallCommand]; + set => this.Set((int)WixBundleBundlePackageSymbolFields.UninstallCommand, value); + } + + public bool SupportsBurnProtocol + { + get { return this.Attributes.HasFlag(WixBundleBundlePackageAttributes.SupportsBurnProtocol); } + set + { + if (value) + { + this.Attributes |= WixBundleBundlePackageAttributes.SupportsBurnProtocol; + } + else + { + this.Attributes &= ~WixBundleBundlePackageAttributes.SupportsBurnProtocol; + } + } + } + + public bool Win64 + { + get { return this.Attributes.HasFlag(WixBundleBundlePackageAttributes.Win64); } + set + { + if (value) + { + this.Attributes |= WixBundleBundlePackageAttributes.Win64; + } + else + { + this.Attributes &= ~WixBundleBundlePackageAttributes.Win64; + } + } + } + } +} diff --git a/src/api/wix/WixToolset.Data/Symbols/WixBundlePackageSymbol.cs b/src/api/wix/WixToolset.Data/Symbols/WixBundlePackageSymbol.cs index 3d1a6ce8..e68a9d09 100644 --- a/src/api/wix/WixToolset.Data/Symbols/WixBundlePackageSymbol.cs +++ b/src/api/wix/WixToolset.Data/Symbols/WixBundlePackageSymbol.cs @@ -66,6 +66,7 @@ namespace WixToolset.Data.Symbols /// public enum WixBundlePackageType { + Bundle, Exe, Msi, Msp, @@ -209,4 +210,4 @@ namespace WixToolset.Data.Symbols public bool Permanent => (this.Attributes & WixBundlePackageAttributes.Permanent) == WixBundlePackageAttributes.Permanent; } -} \ No newline at end of file +} diff --git a/src/burn/engine/apply.cpp b/src/burn/engine/apply.cpp index 6af5c040..9ee7b58c 100644 --- a/src/burn/engine/apply.cpp +++ b/src/burn/engine/apply.cpp @@ -216,6 +216,15 @@ static HRESULT DoRestoreRelatedBundleActions( __in BURN_EXECUTE_CONTEXT* pContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ); +static HRESULT ExecuteBundlePackage( + __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 ExecuteExePackage( __in BURN_ENGINE_STATE* pEngineState, __in BURN_EXECUTE_ACTION* pExecuteAction, @@ -732,6 +741,9 @@ extern "C" HRESULT ApplyExecute( case BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE: wzId = pExecuteAction->relatedBundle.pRelatedBundle->package.sczId; break; + case BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE: + wzId = pExecuteAction->bundlePackage.pPackage->sczId; + break; case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: wzId = pExecuteAction->exePackage.pPackage->sczId; break; @@ -2352,6 +2364,11 @@ static HRESULT DoExecuteAction( ExitOnFailure(hr, "Failed to execute related bundle."); break; + case BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE: + hr = ExecuteBundlePackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart); + ExitOnFailure(hr, "Failed to execute BUNDLE package."); + break; + case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: hr = ExecuteExePackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart); ExitOnFailure(hr, "Failed to execute EXE package."); @@ -2479,6 +2496,11 @@ static HRESULT DoRollbackActions( ExitOnFailure(hr, "Failed to execute related bundle."); break; + case BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE: + hr = ExecuteBundlePackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); + IgnoreRollbackError(hr, "Failed to rollback BUNDLE package."); + break; + case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: hr = ExecuteExePackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); IgnoreRollbackError(hr, "Failed to rollback EXE package."); @@ -2682,6 +2704,86 @@ LExit: return hr; } +static HRESULT ExecuteBundlePackage( + __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->bundlePackage.pPackage; + + Assert(pContext->fRollback == fRollback); + + if (ShouldSkipPackage(pPackage, fRollback)) + { + ExitFunction1(hr = S_OK); + } + + pContext->wzExecutingPackageId = pPackage->sczId; + fBeginCalled = TRUE; + + // Send package execute begin to BA. + hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->bundlePackage.action, INSTALLUILEVEL_NOCHANGE, FALSE); + ExitOnRootFailure(hr, "BA aborted execute BUNDLE package begin."); + + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; + message.dwUIHint = MB_OKCANCEL; + message.progress.dwPercentage = fRollback ? 100 : 0; + nResult = GenericExecuteMessageHandler(&message, pContext); + hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwUIHint, nResult); + ExitOnRootFailure(hr, "BA aborted BUNDLE progress."); + + fExecuted = TRUE; + + // Execute package. + if (pPackage->fPerMachine) + { + hrExecute = ElevationExecuteBundlePackage(pEngineState->companionConnection.hPipe, pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart); + ExitOnFailure(hrExecute, "Failed to configure per-machine BUNDLE package."); + } + else + { + hrExecute = BundlePackageEngineExecutePackage(pExecuteAction, pContext->pCache, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart); + ExitOnFailure(hrExecute, "Failed to configure per-user BUNDLE package."); + } + + message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; + message.dwUIHint = MB_OKCANCEL; + message.progress.dwPercentage = fRollback ? 0 : 100; + nResult = GenericExecuteMessageHandler(&message, pContext); + hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwUIHint, nResult); + ExitOnRootFailure(hr, "BA aborted BUNDLE progress."); + + pContext->cExecutedPackages += fRollback ? -1 : 1; + + hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, pContext->pApplyContext); + ExitOnRootFailure(hr, "BA aborted BUNDLE package execute progress."); + +LExit: + if (fExecuted) + { + BundlePackageEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute); + } + + if (fBeginCalled) + { + pPackage->fAbandonedProcess = pContext->fAbandonedProcess; + hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage->sczId, pPackage->fVital, pPackage->fAbandonedProcess, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); + } + + return hr; +} + static HRESULT ExecuteExePackage( __in BURN_ENGINE_STATE* pEngineState, __in BURN_EXECUTE_ACTION* pExecuteAction, diff --git a/src/burn/engine/bundlepackageengine.cpp b/src/burn/engine/bundlepackageengine.cpp index 89488b91..f3badfc1 100644 --- a/src/burn/engine/bundlepackageengine.cpp +++ b/src/burn/engine/bundlepackageengine.cpp @@ -2,16 +2,80 @@ #include "precomp.h" +static HRESULT ExecuteBundle( + __in BURN_CACHE* pCache, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __in BOOTSTRAPPER_ACTION_STATE action, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in BURN_PACKAGE* pPackage, + __in_z_opt LPCWSTR wzIgnoreDependencies, + __in_z_opt LPCWSTR wzAncestors, + __in_z_opt LPCWSTR wzEngineWorkingDirectory, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); static BOOTSTRAPPER_RELATION_TYPE ConvertRelationType( __in BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE relationType ); // function definitions +extern "C" HRESULT BundlePackageEngineParsePackageFromXml( + __in IXMLDOMNode* pixnBundlePackage, + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + BOOL fFoundXml = FALSE; + LPWSTR scz = NULL; + + // @DetectCondition + hr = XmlGetAttributeEx(pixnBundlePackage, L"BundleId", &pPackage->Bundle.sczBundleId); + ExitOnRequiredXmlQueryFailure(hr, "Failed to get @BundleId."); + + // @InstallArguments + hr = XmlGetAttributeEx(pixnBundlePackage, L"InstallArguments", &pPackage->Bundle.sczInstallArguments); + ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @InstallArguments."); + + // @UninstallArguments + hr = XmlGetAttributeEx(pixnBundlePackage, L"UninstallArguments", &pPackage->Bundle.sczUninstallArguments); + ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @UninstallArguments."); + + // @RepairArguments + hr = XmlGetAttributeEx(pixnBundlePackage, L"RepairArguments", &pPackage->Bundle.sczRepairArguments); + ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @RepairArguments."); + + // @SupportsBurnProtocol + hr = XmlGetYesNoAttribute(pixnBundlePackage, L"SupportsBurnProtocol", &pPackage->Bundle.fSupportsBurnProtocol); + ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @SupportsBurnProtocol."); + + // @Win64 + hr = XmlGetYesNoAttribute(pixnBundlePackage, L"Win64", &pPackage->Bundle.fWin64); + ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Win64."); + + hr = ExeEngineParseExitCodesFromXml(pixnBundlePackage, &pPackage->Bundle.rgExitCodes, &pPackage->Bundle.cExitCodes); + ExitOnFailure(hr, "Failed to parse exit codes."); + + hr = ExeEngineParseCommandLineArgumentsFromXml(pixnBundlePackage, &pPackage->Bundle.rgCommandLineArguments, &pPackage->Bundle.cCommandLineArguments); + ExitOnFailure(hr, "Failed to parse command lines."); + + hr = StrAllocFormatted(&pPackage->Bundle.sczRegistrationKey, L"%ls\\%ls", BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, pPackage->Bundle.sczBundleId); + ExitOnFailure(hr, "Failed to build uninstall registry key path."); + +LExit: + ReleaseStr(scz); + + return hr; +} + extern "C" void BundlePackageEnginePackageUninitialize( __in BURN_PACKAGE* pPackage ) { + ReleaseStr(pPackage->Bundle.sczBundleId); + ReleaseStr(pPackage->Bundle.sczRegistrationKey); ReleaseStr(pPackage->Bundle.sczInstallArguments); ReleaseStr(pPackage->Bundle.sczRepairArguments); ReleaseStr(pPackage->Bundle.sczUninstallArguments); @@ -32,6 +96,44 @@ extern "C" void BundlePackageEnginePackageUninitialize( memset(&pPackage->Bundle, 0, sizeof(pPackage->Bundle)); } +extern "C" HRESULT BundlePackageEngineDetectPackage( + __in BURN_PACKAGE* pPackage + ) +{ + HRESULT hr = S_OK; + HKEY hkRegistration = NULL; + DWORD dwInstalled = 0; + BOOL fDetected = FALSE; + HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + REG_KEY_BITNESS bitness = pPackage->Bundle.fWin64 ? REG_KEY_64BIT : REG_KEY_32BIT; + + // TODO: detect all related bundles, so that the Obsolete state can be detected. + hr = RegOpenEx(hkRoot, pPackage->Bundle.sczRegistrationKey, KEY_QUERY_VALUE, bitness, &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; + } + + fDetected = (1 == dwInstalled); + + // 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; + } + + ReleaseRegKey(hkRegistration); + return hr; +} + // // PlanCalculate - calculates the execute and rollback state for the requested package state. // @@ -151,6 +253,79 @@ LExit: // // PlanAdd - adds the calculated execute and rollback actions for the package. // +extern "C" HRESULT BundlePackageEnginePlanAddPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + BURN_EXECUTE_ACTION* pAction = NULL; + + 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_BUNDLE_PACKAGE; + pAction->bundlePackage.pPackage = pPackage; + pAction->bundlePackage.action = pPackage->rollback; + + if (pPackage->Bundle.wzAncestors) + { + hr = StrAllocString(&pAction->bundlePackage.sczAncestors, pPackage->Bundle.wzAncestors, 0); + ExitOnFailure(hr, "Failed to allocate the list of ancestors."); + } + + if (pPackage->Bundle.wzEngineWorkingDirectory) + { + hr = StrAllocString(&pAction->bundlePackage.sczEngineWorkingDirectory, pPackage->Bundle.wzEngineWorkingDirectory, 0); + ExitOnFailure(hr, "Failed to allocate the custom working directory."); + } + + LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, NULL); // ignore errors. + + 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_BUNDLE_PACKAGE; + pAction->bundlePackage.pPackage = pPackage; + pAction->bundlePackage.action = pPackage->execute; + + if (pPackage->Bundle.wzAncestors) + { + hr = StrAllocString(&pAction->bundlePackage.sczAncestors, pPackage->Bundle.wzAncestors, 0); + ExitOnFailure(hr, "Failed to allocate the list of ancestors."); + } + + if (pPackage->Bundle.wzEngineWorkingDirectory) + { + hr = StrAllocString(&pAction->bundlePackage.sczEngineWorkingDirectory, pPackage->Bundle.wzEngineWorkingDirectory, 0); + ExitOnFailure(hr, "Failed to allocate the custom working directory."); + } + + LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, NULL); // ignore errors. + } + +LExit: + return hr; +} + +// +// PlanAdd - adds the calculated execute and rollback actions for the related bundle. +// extern "C" HRESULT BundlePackageEnginePlanAddRelatedBundle( __in_opt DWORD *pdwInsertSequence, __in BURN_RELATED_BUNDLE* pRelatedBundle, @@ -164,7 +339,7 @@ extern "C" HRESULT BundlePackageEnginePlanAddRelatedBundle( BURN_PACKAGE* pPackage = &pRelatedBundle->package; hr = DependencyPlanPackage(pdwInsertSequence, pPackage, pPlan); - ExitOnFailure(hr, "Failed to plan package dependency actions."); + ExitOnFailure(hr, "Failed to plan related bundle dependency actions."); // add execute action if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute) @@ -240,6 +415,26 @@ LExit: return hr; } +extern "C" HRESULT BundlePackageEngineExecutePackage( + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_CACHE* pCache, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) +{ + BOOTSTRAPPER_ACTION_STATE action = pExecuteAction->bundlePackage.action; + LPCWSTR wzIgnoreDependencies = pExecuteAction->bundlePackage.sczIgnoreDependencies; + LPCWSTR wzAncestors = pExecuteAction->bundlePackage.sczAncestors; + LPCWSTR wzEngineWorkingDirectory = pExecuteAction->bundlePackage.sczEngineWorkingDirectory; + BOOTSTRAPPER_RELATION_TYPE relationType = BOOTSTRAPPER_RELATION_CHAIN_PACKAGE; + BURN_PACKAGE* pPackage = pExecuteAction->bundlePackage.pPackage; + + return ExecuteBundle(pCache, pVariables, fRollback, pfnGenericMessageHandler, pvContext, action, relationType, pPackage, wzIgnoreDependencies, wzAncestors, wzEngineWorkingDirectory, pRestart); +} + extern "C" HRESULT BundlePackageEngineExecuteRelatedBundle( __in BURN_EXECUTE_ACTION* pExecuteAction, __in BURN_CACHE* pCache, @@ -249,6 +444,57 @@ extern "C" HRESULT BundlePackageEngineExecuteRelatedBundle( __in LPVOID pvContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ) +{ + BOOTSTRAPPER_ACTION_STATE action = pExecuteAction->relatedBundle.action; + LPCWSTR wzIgnoreDependencies = pExecuteAction->relatedBundle.sczIgnoreDependencies; + LPCWSTR wzAncestors = pExecuteAction->relatedBundle.sczAncestors; + LPCWSTR wzEngineWorkingDirectory = pExecuteAction->relatedBundle.sczEngineWorkingDirectory; + BURN_RELATED_BUNDLE* pRelatedBundle = pExecuteAction->relatedBundle.pRelatedBundle; + BOOTSTRAPPER_RELATION_TYPE relationType = ConvertRelationType(pRelatedBundle->planRelationType); + BURN_PACKAGE* pPackage = &pRelatedBundle->package; + + return ExecuteBundle(pCache, pVariables, fRollback, pfnGenericMessageHandler, pvContext, action, relationType, pPackage, wzIgnoreDependencies, wzAncestors, wzEngineWorkingDirectory, pRestart); +} + +extern "C" void BundlePackageEngineUpdateInstallRegistrationState( + __in BURN_EXECUTE_ACTION* pAction, + __in HRESULT hrExecute + ) +{ + BURN_PACKAGE* pPackage = pAction->bundlePackage.pPackage; + + if (FAILED(hrExecute) || !pPackage->fCanAffectRegistration) + { + ExitFunction(); + } + + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->bundlePackage.action) + { + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + else + { + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + +LExit: + return; +} + +static HRESULT ExecuteBundle( + __in BURN_CACHE* pCache, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __in BOOTSTRAPPER_ACTION_STATE action, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in BURN_PACKAGE* pPackage, + __in_z_opt LPCWSTR wzIgnoreDependencies, + __in_z_opt LPCWSTR wzAncestors, + __in_z_opt LPCWSTR wzEngineWorkingDirectory, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ) { HRESULT hr = S_OK; LPCWSTR wzArguments = NULL; @@ -264,10 +510,6 @@ extern "C" HRESULT BundlePackageEngineExecuteRelatedBundle( PROCESS_INFORMATION pi = { }; DWORD dwExitCode = 0; GENERIC_EXECUTE_MESSAGE message = { }; - BOOTSTRAPPER_ACTION_STATE action = pExecuteAction->relatedBundle.action; - BURN_RELATED_BUNDLE* pRelatedBundle = pExecuteAction->relatedBundle.pRelatedBundle; - BOOTSTRAPPER_RELATION_TYPE relationType = ConvertRelationType(pRelatedBundle->planRelationType); - BURN_PACKAGE* pPackage = &pRelatedBundle->package; BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload; LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType); LPCWSTR wzOperationCommandLine = NULL; @@ -376,21 +618,29 @@ extern "C" HRESULT BundlePackageEngineExecuteRelatedBundle( } // Add the list of dependencies to ignore, if any, to the burn command line. - if (pExecuteAction->relatedBundle.sczIgnoreDependencies) + if (BOOTSTRAPPER_RELATION_CHAIN_PACKAGE == relationType) { - hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls=%ls", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->relatedBundle.sczIgnoreDependencies); + hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls=ALL", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES); + ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line."); + } + else if (wzIgnoreDependencies) + { + hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls=%ls", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, wzIgnoreDependencies); ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line."); } // Add the list of ancestors, if any, to the burn command line. - if (pExecuteAction->relatedBundle.sczAncestors) + if (wzAncestors) { - hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls=%ls", BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->relatedBundle.sczAncestors); + hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls=%ls", BURN_COMMANDLINE_SWITCH_ANCESTORS, wzAncestors); ExitOnFailure(hr, "Failed to append the list of ancestors to the command line."); } - hr = CoreAppendEngineWorkingDirectoryToCommandLine(pExecuteAction->relatedBundle.sczEngineWorkingDirectory, &sczBaseCommand, NULL); - ExitOnFailure(hr, "Failed to append the custom working directory to the bundlepackage command line."); + if (wzEngineWorkingDirectory) + { + hr = CoreAppendEngineWorkingDirectoryToCommandLine(wzEngineWorkingDirectory, &sczBaseCommand, NULL); + ExitOnFailure(hr, "Failed to append the custom working directory to the bundlepackage command line."); + } hr = CoreAppendFileHandleSelfToCommandLine(sczExecutablePath, &hExecutableFile, &sczBaseCommand, NULL); ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF); diff --git a/src/burn/engine/bundlepackageengine.h b/src/burn/engine/bundlepackageengine.h index 0d59907d..9271ac6a 100644 --- a/src/burn/engine/bundlepackageengine.h +++ b/src/burn/engine/bundlepackageengine.h @@ -9,12 +9,25 @@ extern "C" { // function declarations +HRESULT BundlePackageEngineParsePackageFromXml( + __in IXMLDOMNode* pixnBundlePackage, + __in BURN_PACKAGE* pPackage + ); void BundlePackageEnginePackageUninitialize( __in BURN_PACKAGE* pPackage ); +HRESULT BundlePackageEngineDetectPackage( + __in BURN_PACKAGE* pPackage + ); HRESULT BundlePackageEnginePlanCalculatePackage( __in BURN_PACKAGE* pPackage ); +HRESULT BundlePackageEnginePlanAddPackage( + __in BURN_PACKAGE* pPackage, + __in BURN_PLAN* pPlan, + __in BURN_LOGGING* pLog, + __in BURN_VARIABLES* pVariables + ); HRESULT BundlePackageEnginePlanAddRelatedBundle( __in_opt DWORD *pdwInsertSequence, __in BURN_RELATED_BUNDLE* pRelatedBundle, @@ -22,6 +35,15 @@ HRESULT BundlePackageEnginePlanAddRelatedBundle( __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables ); +HRESULT BundlePackageEngineExecutePackage( + __in BURN_EXECUTE_ACTION* pExecuteAction, + __in BURN_CACHE* pCache, + __in BURN_VARIABLES* pVariables, + __in BOOL fRollback, + __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, + __in LPVOID pvContext, + __out BOOTSTRAPPER_APPLY_RESTART* pRestart + ); HRESULT BundlePackageEngineExecuteRelatedBundle( __in BURN_EXECUTE_ACTION* pExecuteAction, __in BURN_CACHE* pCache, @@ -31,6 +53,10 @@ HRESULT BundlePackageEngineExecuteRelatedBundle( __in LPVOID pvContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ); +void BundlePackageEngineUpdateInstallRegistrationState( + __in BURN_EXECUTE_ACTION* pAction, + __in HRESULT hrExecute + ); #if defined(__cplusplus) diff --git a/src/burn/engine/core.cpp b/src/burn/engine/core.cpp index 551843f2..3370ad05 100644 --- a/src/burn/engine/core.cpp +++ b/src/burn/engine/core.cpp @@ -956,6 +956,9 @@ extern "C" LPCWSTR CoreRelationTypeToCommandLineString( case BOOTSTRAPPER_RELATION_DEPENDENT_PATCH: wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_DEPENDENT_PATCH; break; + case BOOTSTRAPPER_RELATION_CHAIN_PACKAGE: + wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_CHAIN_PACKAGE; + break; case BOOTSTRAPPER_RELATION_NONE: __fallthrough; default: wzRelationTypeCommandLine = NULL; @@ -1709,6 +1712,12 @@ extern "C" HRESULT CoreParseCommandLine( 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_CHAIN_PACKAGE, -1)) + { + pCommand->relationType = BOOTSTRAPPER_RELATION_CHAIN_PACKAGE; + + 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; @@ -2111,6 +2120,10 @@ static HRESULT DetectPackage( // Use the correct engine to detect the package. switch (pPackage->type) { + case BURN_PACKAGE_TYPE_BUNDLE: + hr = BundlePackageEngineDetectPackage(pPackage); + break; + case BURN_PACKAGE_TYPE_EXE: hr = ExeEngineDetectPackage(pPackage, &pEngineState->registration, &pEngineState->variables); break; @@ -2128,8 +2141,7 @@ static HRESULT DetectPackage( break; default: - hr = E_NOTIMPL; - ExitOnRootFailure(hr, "Package type not supported by detect yet."); + ExitWithRootFailure(hr, E_NOTIMPL, "Package type not supported by detect yet."); } LExit: diff --git a/src/burn/engine/core.h b/src/burn/engine/core.h index cee71aec..556124d6 100644 --- a/src/burn/engine/core.h +++ b/src/burn/engine/core.h @@ -27,6 +27,7 @@ const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_DEPENDENT_ADDON = L"burn.related.d const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_PATCH = L"burn.related.patch"; const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_DEPENDENT_PATCH = L"burn.related.dependent.patch"; const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPDATE = L"burn.related.update"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_CHAIN_PACKAGE = L"burn.related.chain.package"; 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"; diff --git a/src/burn/engine/elevation.cpp b/src/burn/engine/elevation.cpp index 8488b649..504ddaea 100644 --- a/src/burn/engine/elevation.cpp +++ b/src/burn/engine/elevation.cpp @@ -20,6 +20,7 @@ typedef enum _BURN_ELEVATION_MESSAGE_TYPE BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP, BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_RELATED_BUNDLE, + BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_BUNDLE_PACKAGE, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE, @@ -250,6 +251,14 @@ static HRESULT OnExecuteRelatedBundle( __in BYTE* pbData, __in SIZE_T cbData ); +static HRESULT OnExecuteBundlePackage( + __in HANDLE hPipe, + __in BURN_CACHE* pCache, + __in BURN_PACKAGES* pPackages, + __in BURN_VARIABLES* pVariables, + __in BYTE* pbData, + __in SIZE_T cbData + ); static HRESULT OnExecuteExePackage( __in HANDLE hPipe, __in BURN_CACHE* pCache, @@ -910,6 +919,63 @@ LExit: return hr; } +/******************************************************************* + ElevationExecuteBundlePackage - + +*******************************************************************/ +extern "C" HRESULT ElevationExecuteBundlePackage( + __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->bundlePackage.pPackage->sczId); + ExitOnFailure(hr, "Failed to write package id to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->bundlePackage.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->bundlePackage.sczIgnoreDependencies); + ExitOnFailure(hr, "Failed to write the list of dependencies to ignore to the message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->bundlePackage.sczAncestors); + ExitOnFailure(hr, "Failed to write the list of ancestors to the message buffer."); + + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->bundlePackage.sczEngineWorkingDirectory); + ExitOnFailure(hr, "Failed to write the custom working directory 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_BUNDLE_PACKAGE, pbData, cbData, ProcessGenericExecuteMessages, &context, &dwResult); + ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_BUNDLE_PACKAGE message to per-machine process."); + + hr = ProcessResult(dwResult, pRestart); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + /******************************************************************* ElevationExecuteExePackage - @@ -940,9 +1006,6 @@ extern "C" HRESULT ElevationExecuteExePackage( 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."); @@ -2124,6 +2187,10 @@ static HRESULT ProcessElevatedChildMessage( hrResult = OnExecuteRelatedBundle(pContext->hPipe, pContext->pCache, &pContext->pRegistration->relatedBundles, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); break; + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_BUNDLE_PACKAGE: + hrResult = OnExecuteBundlePackage(pContext->hPipe, pContext->pCache, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); + break; + case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE: hrResult = OnExecuteExePackage(pContext->hPipe, pContext->pCache, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); break; @@ -2839,7 +2906,7 @@ LExit: return hr; } -static HRESULT OnExecuteExePackage( +static HRESULT OnExecuteBundlePackage( __in HANDLE hPipe, __in BURN_CACHE* pCache, __in BURN_PACKAGES* pPackages, @@ -2856,15 +2923,15 @@ static HRESULT OnExecuteExePackage( LPWSTR sczIgnoreDependencies = NULL; LPWSTR sczAncestors = NULL; LPWSTR sczEngineWorkingDirectory = NULL; - BOOTSTRAPPER_APPLY_RESTART exeRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; + BOOTSTRAPPER_APPLY_RESTART bundleRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; - executeAction.type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE; + executeAction.type = BURN_EXECUTE_ACTION_TYPE_BUNDLE_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); + hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.bundlePackage.action); ExitOnFailure(hr, "Failed to read action."); hr = BuffReadNumber(pbData, cbData, &iData, &dwRollback); @@ -2882,21 +2949,107 @@ static HRESULT OnExecuteExePackage( hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); ExitOnFailure(hr, "Failed to read variables."); - hr = PackageFindById(pPackages, sczPackage, &executeAction.exePackage.pPackage); + hr = PackageFindById(pPackages, sczPackage, &executeAction.bundlePackage.pPackage); ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); - if (BURN_PACKAGE_TYPE_EXE != executeAction.exePackage.pPackage->type) + if (BURN_PACKAGE_TYPE_BUNDLE != executeAction.bundlePackage.pPackage->type) { - ExitWithRootFailure(hr, E_INVALIDARG, "Package is not an EXE package: %ls", sczPackage); + ExitWithRootFailure(hr, E_INVALIDARG, "Package is not a BUNDLE 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); + hr = StrAllocString(&executeAction.bundlePackage.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.bundlePackage.sczAncestors, sczAncestors, 0); + ExitOnFailure(hr, "Failed to allocate the list of ancestors."); + } + + if (sczEngineWorkingDirectory && *sczEngineWorkingDirectory) + { + hr = StrAllocString(&executeAction.bundlePackage.sczEngineWorkingDirectory, sczEngineWorkingDirectory, 0); + ExitOnFailure(hr, "Failed to allocate the custom working directory."); + } + + // Execute BUNDLE package. + hr = BundlePackageEngineExecutePackage(&executeAction, pCache, pVariables, static_cast(dwRollback), GenericExecuteMessageHandler, hPipe, &bundleRestart); + ExitOnFailure(hr, "Failed to execute BUNDLE package."); + +LExit: + ReleaseStr(sczEngineWorkingDirectory); + ReleaseStr(sczAncestors); + ReleaseStr(sczIgnoreDependencies); + ReleaseStr(sczPackage); + PlanUninitializeExecuteAction(&executeAction); + + if (SUCCEEDED(hr)) + { + if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == bundleRestart) + { + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); + } + else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == bundleRestart) + { + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED); + } + } + + return hr; +} + +static HRESULT OnExecuteExePackage( + __in HANDLE hPipe, + __in BURN_CACHE* pCache, + __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; + BURN_EXECUTE_ACTION executeAction = { }; + LPWSTR sczAncestors = NULL; + LPWSTR sczEngineWorkingDirectory = 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, &sczAncestors); + ExitOnFailure(hr, "Failed to read the list of ancestors."); + + hr = BuffReadString(pbData, cbData, &iData, &sczEngineWorkingDirectory); + ExitOnFailure(hr, "Failed to read the custom working directory."); + + hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); + ExitOnFailure(hr, "Failed to read variables."); + + hr = PackageFindById(pPackages, sczPackage, &executeAction.exePackage.pPackage); + ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); + + if (BURN_PACKAGE_TYPE_EXE != executeAction.exePackage.pPackage->type) + { + ExitWithRootFailure(hr, E_INVALIDARG, "Package is not an EXE package: %ls", sczPackage); + } + // Pass the list of ancestors, if any, to the related bundle. if (sczAncestors && *sczAncestors) { @@ -2917,7 +3070,6 @@ static HRESULT OnExecuteExePackage( LExit: ReleaseStr(sczEngineWorkingDirectory); ReleaseStr(sczAncestors); - ReleaseStr(sczIgnoreDependencies); ReleaseStr(sczPackage); PlanUninitializeExecuteAction(&executeAction); diff --git a/src/burn/engine/elevation.h b/src/burn/engine/elevation.h index c2fa0627..3484057e 100644 --- a/src/burn/engine/elevation.h +++ b/src/burn/engine/elevation.h @@ -89,6 +89,15 @@ HRESULT ElevationExecuteRelatedBundle( __in LPVOID pvContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ); +HRESULT ElevationExecuteBundlePackage( + __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 ElevationExecuteExePackage( __in HANDLE hPipe, __in BURN_EXECUTE_ACTION* pExecuteAction, diff --git a/src/burn/engine/exeengine.cpp b/src/burn/engine/exeengine.cpp index 0a2084e5..a287d171 100644 --- a/src/burn/engine/exeengine.cpp +++ b/src/burn/engine/exeengine.cpp @@ -91,7 +91,6 @@ extern "C" void ExeEnginePackageUninitialize( ReleaseStr(pPackage->Exe.sczInstallArguments); ReleaseStr(pPackage->Exe.sczRepairArguments); ReleaseStr(pPackage->Exe.sczUninstallArguments); - ReleaseStr(pPackage->Exe.sczIgnoreDependencies); ReleaseMem(pPackage->Exe.rgExitCodes); // free command-line arguments @@ -291,12 +290,6 @@ extern "C" HRESULT ExeEnginePlanAddPackage( 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); @@ -325,12 +318,6 @@ extern "C" HRESULT ExeEnginePlanAddPackage( pAction->exePackage.pPackage = pPackage; 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); @@ -455,12 +442,11 @@ extern "C" HRESULT ExeEngineExecutePackage( hr = StrAllocConcat(&sczBaseCommand, L" -norestart", 0); ExitOnFailure(hr, "Failed to append norestart argument."); - // Add the list of dependencies to ignore, if any, to the burn command line. - if (pExecuteAction->exePackage.sczIgnoreDependencies) - { - hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls=%ls", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies); - ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line."); - } + hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls", BURN_COMMANDLINE_SWITCH_RELATED_CHAIN_PACKAGE); + ExitOnFailure(hr, "Failed to append the relation type to the command line."); + + hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls=ALL", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES); + ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line."); // Add the list of ancestors, if any, to the burn command line. if (pExecuteAction->exePackage.sczAncestors) diff --git a/src/burn/engine/logging.cpp b/src/burn/engine/logging.cpp index de332f84..a766b896 100644 --- a/src/burn/engine/logging.cpp +++ b/src/burn/engine/logging.cpp @@ -781,6 +781,8 @@ extern "C" LPCSTR LoggingRelationTypeToString( return "DependentPatch"; case BOOTSTRAPPER_RELATION_UPDATE: return "Update"; + case BOOTSTRAPPER_RELATION_CHAIN_PACKAGE: + return "ChainPackage"; default: return "Invalid"; } diff --git a/src/burn/engine/package.cpp b/src/burn/engine/package.cpp index 8a80194e..89203ada 100644 --- a/src/burn/engine/package.cpp +++ b/src/burn/engine/package.cpp @@ -87,7 +87,7 @@ extern "C" HRESULT PackagesParseFromXml( ReleaseNullObject(pixnNodes); // done with the RollbackBoundary elements. // select package nodes - hr = XmlSelectNodes(pixnBundle, L"Chain/ExePackage|Chain/MsiPackage|Chain/MspPackage|Chain/MsuPackage", &pixnNodes); + hr = XmlSelectNodes(pixnBundle, L"Chain/BundlePackage|Chain/ExePackage|Chain/MsiPackage|Chain/MspPackage|Chain/MsuPackage", &pixnNodes); ExitOnFailure(hr, "Failed to select package nodes."); // get package node count @@ -199,7 +199,14 @@ extern "C" HRESULT PackagesParseFromXml( } // read type specific attributes - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"ExePackage", -1)) + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"BundlePackage", -1)) + { + pPackage->type = BURN_PACKAGE_TYPE_BUNDLE; + + hr = BundlePackageEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization + ExitOnFailure(hr, "Failed to parse BUNDLE package."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"ExePackage", -1)) { pPackage->type = BURN_PACKAGE_TYPE_EXE; @@ -231,7 +238,7 @@ extern "C" HRESULT PackagesParseFromXml( } else { - // ignore other package types for now + ExitWithRootFailure(hr, E_UNEXPECTED, "Invalid package type: %ls", bstrNodeName); } if (!pPackage->fPermanent) @@ -371,7 +378,7 @@ extern "C" void PackageUninitialize( switch (pPackage->type) { case BURN_PACKAGE_TYPE_BUNDLE: - BundlePackageEnginePackageUninitialize(pPackage); + BundlePackageEnginePackageUninitialize(pPackage); // TODO: Modularization break; case BURN_PACKAGE_TYPE_EXE: ExeEnginePackageUninitialize(pPackage); // TODO: Modularization @@ -648,7 +655,6 @@ static HRESULT ParsePatchTargetCode( IXMLDOMNodeList* pixnNodes = NULL; IXMLDOMNode* pixnNode = NULL; DWORD cNodes = 0; - BSTR bstrNodeText = NULL; BOOL fProduct; hr = XmlSelectNodes(pixnBundle, L"PatchTargetCode", &pixnNodes); @@ -688,12 +694,10 @@ static HRESULT ParsePatchTargetCode( 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); diff --git a/src/burn/engine/package.h b/src/burn/engine/package.h index deab47b7..c13c651b 100644 --- a/src/burn/engine/package.h +++ b/src/burn/engine/package.h @@ -302,6 +302,8 @@ typedef struct _BURN_PACKAGE { struct { + LPWSTR sczBundleId; + LPWSTR sczRegistrationKey; LPWSTR sczInstallArguments; LPWSTR sczRepairArguments; LPWSTR sczUninstallArguments; @@ -310,6 +312,7 @@ typedef struct _BURN_PACKAGE LPCWSTR wzAncestors; // points directly into engine state. LPCWSTR wzEngineWorkingDirectory; // points directly into engine state. + BOOL fWin64; BOOL fSupportsBurnProtocol; BURN_EXE_EXIT_CODE* rgExitCodes; @@ -324,7 +327,6 @@ typedef struct _BURN_PACKAGE LPWSTR sczInstallArguments; LPWSTR sczRepairArguments; LPWSTR sczUninstallArguments; - LPWSTR sczIgnoreDependencies; LPCWSTR wzAncestors; // points directly into engine state. LPCWSTR wzEngineWorkingDirectory; // points directly into engine state. diff --git a/src/burn/engine/plan.cpp b/src/burn/engine/plan.cpp index cb50b0c9..1d008dba 100644 --- a/src/burn/engine/plan.cpp +++ b/src/burn/engine/plan.cpp @@ -292,7 +292,6 @@ extern "C" void PlanUninitializeExecuteAction( break; case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: - ReleaseStr(pExecuteAction->exePackage.sczIgnoreDependencies); ReleaseStr(pExecuteAction->exePackage.sczAncestors); ReleaseStr(pExecuteAction->exePackage.sczEngineWorkingDirectory); break; @@ -1181,6 +1180,10 @@ extern "C" HRESULT PlanExecutePackage( // Add execute actions. switch (pPackage->type) { + case BURN_PACKAGE_TYPE_BUNDLE: + hr = BundlePackageEnginePlanAddPackage(pPackage, pPlan, pLog, pVariables); + break; + case BURN_PACKAGE_TYPE_EXE: hr = ExeEnginePlanAddPackage(pPackage, pPlan, pLog, pVariables); break; @@ -1506,16 +1509,16 @@ extern "C" HRESULT PlanRelatedBundlesComplete( switch (pPlan->rgExecuteActions[i].type) { - case BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE: - packageAction = pPlan->rgExecuteActions[i].relatedBundle.action; - pPackage = &pPlan->rgExecuteActions[i].relatedBundle.pRelatedBundle->package; + case BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE: + packageAction = pPlan->rgExecuteActions[i].bundlePackage.action; + pPackage = pPlan->rgExecuteActions[i].bundlePackage.pPackage; fBundle = TRUE; break; case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: packageAction = pPlan->rgExecuteActions[i].exePackage.action; pPackage = pPlan->rgExecuteActions[i].exePackage.pPackage; - fBundle = TRUE; + fBundle = pPackage->Exe.fBundle; break; case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: @@ -2751,6 +2754,10 @@ static HRESULT CalculateExecuteActions( // Calculate execute actions. switch (pPackage->type) { + case BURN_PACKAGE_TYPE_BUNDLE: + hr = BundlePackageEnginePlanCalculatePackage(pPackage); + break; + case BURN_PACKAGE_TYPE_EXE: hr = ExeEnginePlanCalculatePackage(pPackage); break; @@ -2784,7 +2791,8 @@ static BOOL NeedsCache( ) { 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). + // TODO: bundles could theoretically use package cache + if (BURN_PACKAGE_TYPE_BUNDLE == pPackage->type || BURN_PACKAGE_TYPE_EXE == pPackage->type) // Bundle and Exe packages require the package for all operations (even uninstall). { return BOOTSTRAPPER_ACTION_STATE_NONE != action; } @@ -2918,6 +2926,10 @@ static void ExecuteActionLog( LogStringLine(PlanDumpLevel, "%ls action[%u]: RELATED_BUNDLE package id: %ls, action: %hs, ignore dependencies: %ls", wzBase, iAction, pAction->relatedBundle.pRelatedBundle->package.sczId, LoggingActionStateToString(pAction->relatedBundle.action), pAction->relatedBundle.sczIgnoreDependencies); break; + case BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE: + LogStringLine(PlanDumpLevel, "%ls action[%u]: BUNDLE_PACKAGE package id: %ls, action: %hs", wzBase, iAction, pAction->bundlePackage.pPackage->sczId, LoggingActionStateToString(pAction->bundlePackage.action)); + break; + case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: LogStringLine(PlanDumpLevel, "%ls action[%u]: EXE_PACKAGE package id: %ls, action: %hs", wzBase, iAction, pAction->exePackage.pPackage->sczId, LoggingActionStateToString(pAction->exePackage.action)); break; diff --git a/src/burn/engine/plan.h b/src/burn/engine/plan.h index 6e9a1ff5..5b6ee0fb 100644 --- a/src/burn/engine/plan.h +++ b/src/burn/engine/plan.h @@ -43,6 +43,7 @@ enum BURN_EXECUTE_ACTION_TYPE BURN_EXECUTE_ACTION_TYPE_WAIT_CACHE_PACKAGE, BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE, BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE, + BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE, BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE, BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE, BURN_EXECUTE_ACTION_TYPE_MSP_TARGET, @@ -167,6 +168,13 @@ typedef struct _BURN_EXECUTE_ACTION LPWSTR sczIgnoreDependencies; LPWSTR sczAncestors; LPWSTR sczEngineWorkingDirectory; + } bundlePackage; + struct + { + BURN_PACKAGE* pPackage; + BOOTSTRAPPER_ACTION_STATE action; + LPWSTR sczAncestors; + LPWSTR sczEngineWorkingDirectory; } exePackage; struct { diff --git a/src/burn/engine/registration.cpp b/src/burn/engine/registration.cpp index a65c30d3..78f8eeb1 100644 --- a/src/burn/engine/registration.cpp +++ b/src/burn/engine/registration.cpp @@ -7,7 +7,6 @@ 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_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"; @@ -1219,7 +1218,7 @@ static HRESULT SetPaths( 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); + hr = StrAllocFormatted(&pRegistration->sczRegistrationKey, L"%ls\\%ls", BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, pRegistration->sczId); ExitOnFailure(hr, "Failed to build uninstall registry key path."); // build cache directory @@ -1231,7 +1230,7 @@ static HRESULT SetPaths( ExitOnFailure(hr, "Failed to build cached executable path."); // build state file path - hr = StrAllocFormatted(&pRegistration->sczStateFile, L"%s\\state.rsm", sczCacheDirectory); + hr = StrAllocFormatted(&pRegistration->sczStateFile, L"%ls\\state.rsm", sczCacheDirectory); ExitOnFailure(hr, "Failed to build state file path."); LExit: diff --git a/src/burn/engine/registration.h b/src/burn/engine/registration.h index e4dd6f4a..58c883da 100644 --- a/src/burn/engine/registration.h +++ b/src/burn/engine/registration.h @@ -22,6 +22,8 @@ const LPCWSTR BURN_REGISTRATION_REGISTRY_ENGINE_PROTOCOL_VERSION = L"EngineProto const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey"; const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_TAG = L"BundleTag"; +const LPCWSTR REGISTRY_BUNDLE_INSTALLED = L"Installed"; + enum BURN_RESUME_MODE { BURN_RESUME_MODE_NONE, diff --git a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj index 35415dc3..b0159840 100644 --- a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -80,6 +80,7 @@ + diff --git a/src/burn/test/BurnUnitTest/PlanTest.cpp b/src/burn/test/BurnUnitTest/PlanTest.cpp index 143ca7d7..536e5351 100644 --- a/src/burn/test/BurnUnitTest/PlanTest.cpp +++ b/src/burn/test/BurnUnitTest/PlanTest.cpp @@ -10,6 +10,7 @@ static HRESULT WINAPI PlanTestBAProc( ); static LPCWSTR wzMsiTransactionManifestFileName = L"MsiTransaction_BundleAv1_manifest.xml"; +static LPCWSTR wzMultipleBundlePackageManifestFileName = L"BundlePackage_Multiple_manifest.xml"; static LPCWSTR wzSingleExeManifestFileName = L"Failure_BundleD_manifest.xml"; static LPCWSTR wzSingleMsiManifestFileName = L"BasicFunctionality_BundleA_manifest.xml"; static LPCWSTR wzSingleMsuManifestFileName = L"MsuPackageFixture_manifest.xml"; @@ -339,6 +340,121 @@ namespace Bootstrapper ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); } + [Fact] + void MultipleBundlePackageInstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzMultipleBundlePackageManifestFileName, pEngineState); + DetectAttachedContainerAsAttached(pEngineState); + DetectPermanentPackagesAsPresentAndCached(pEngineState); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); + NativeAssert::StringEqual(L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", pPlan->wzBundleId); + NativeAssert::StringEqual(L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", pPlan->wzBundleProviderKey); + Assert::Equal(FALSE, pPlan->fEnabledForwardCompatibleBundle); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(TRUE, pPlan->fCanAffectMachineState); + Assert::Equal(FALSE, pPlan->fDisableRollback); + Assert::Equal(FALSE, pPlan->fDisallowRemoval); + Assert::Equal(FALSE, pPlan->fDowngrade); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + ValidateDependentRegistrationAction(pPlan, fRollback, dwIndex++, TRUE, L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}"); + Assert::Equal(dwIndex, pPlan->cRegistrationActions); + + fRollback = TRUE; + dwIndex = 0; + ValidateDependentRegistrationAction(pPlan, fRollback, dwIndex++, FALSE, L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}"); + Assert::Equal(dwIndex, pPlan->cRollbackRegistrationActions); + + fRollback = FALSE; + dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 6); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageB"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(18575450ull, pPlan->qwEstimatedSize); + Assert::Equal(78462280ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteBundlePackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", registerActions1, 1); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + dwExecuteCheckpointId += 1; // cache checkpoints + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"PackageB"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteBundlePackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_INSTALL); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", registerActions1, 1); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteBundlePackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", unregisterActions1, 1); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + dwExecuteCheckpointId += 1; // cache checkpoints + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteBundlePackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_UNINSTALL); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", unregisterActions1, 1); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(2ul, pPlan->cExecutePackagesTotal); + Assert::Equal(4ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions); + + dwIndex = 0; + ValidateCleanAction(pPlan, dwIndex++, L"NetFx48Web"); + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(3ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + } + [Fact] void OrphanCompatiblePackageTest() { @@ -716,7 +832,7 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"ExeA"); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"ExeA", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"ExeA", BOOTSTRAPPER_ACTION_STATE_INSTALL); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++); @@ -727,7 +843,7 @@ namespace Bootstrapper dwExecuteCheckpointId = 2; ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"ExeA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"ExeA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -2125,7 +2241,7 @@ namespace Bootstrapper ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"NetFx48Web"); - ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"NetFx48Web", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"NetFx48Web", BOOTSTRAPPER_ACTION_STATE_INSTALL); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); dwExecuteCheckpointId += 1; // cache checkpoints ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); @@ -2678,20 +2794,33 @@ namespace Bootstrapper Assert::Equal(FALSE, pAction->fDeleted); } + void ValidateExecuteBundlePackage( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId, + __in BOOTSTRAPPER_ACTION_STATE action + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_BUNDLE_PACKAGE, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->bundlePackage.pPackage->sczId); + Assert::Equal(action, pAction->bundlePackage.action); + 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 + __in BOOTSTRAPPER_ACTION_STATE action ) { 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); } diff --git a/src/burn/test/BurnUnitTest/TestData/PlanTest/BundlePackage_Multiple_manifest.xml b/src/burn/test/BurnUnitTest/TestData/PlanTest/BundlePackage_Multiple_manifest.xml new file mode 100644 index 00000000..6c60085f --- /dev/null +++ b/src/burn/test/BurnUnitTest/TestData/PlanTest/BundlePackage_Multiple_manifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/test/burn/TestData/BundlePackageTests/MultipleBundlePackagesBundle/MultipleBundlePackagesBundle.wixproj b/src/test/burn/TestData/BundlePackageTests/MultipleBundlePackagesBundle/MultipleBundlePackagesBundle.wixproj new file mode 100644 index 00000000..a9639014 --- /dev/null +++ b/src/test/burn/TestData/BundlePackageTests/MultipleBundlePackagesBundle/MultipleBundlePackagesBundle.wixproj @@ -0,0 +1,19 @@ + + + + Bundle + {86D214FB-8D74-456C-99B3-6557ECA6159C} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/burn/TestData/BundlePackageTests/MultipleBundlePackagesBundle/MultipleBundlePackagesBundle.wxs b/src/test/burn/TestData/BundlePackageTests/MultipleBundlePackagesBundle/MultipleBundlePackagesBundle.wxs new file mode 100644 index 00000000..e0071874 --- /dev/null +++ b/src/test/burn/TestData/BundlePackageTests/MultipleBundlePackagesBundle/MultipleBundlePackagesBundle.wxs @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs new file mode 100644 index 00000000..2e95aedb --- /dev/null +++ b/src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs @@ -0,0 +1,51 @@ +// Copyright (c) .NET 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 WixToolsetTest.BurnE2E +{ + using System.IO; + using Xunit; + using Xunit.Abstractions; + + public class BundlePackageTests : BurnE2ETests + { + public BundlePackageTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { } + + [Fact] + public void CanInstallAndUninstallBundlePackages() + { + var packageA = this.CreatePackageInstaller(@"..\BasicFunctionalityTests\PackageA"); + var packageA_x64 = this.CreatePackageInstaller(@"..\BasicFunctionalityTests\PackageA_x64"); + var bundleA = this.CreateBundleInstaller(@"..\BasicFunctionalityTests\BundleA"); + var bundleB_x64 = this.CreateBundleInstaller(@"..\BasicFunctionalityTests\BundleB_x64"); + var multipleBundlePackagesBundle = this.CreateBundleInstaller(@"MultipleBundlePackagesBundle"); + + var packageA32SourceCodeFilePath = packageA.GetInstalledFilePath("Package.wxs"); + var packageA64SourceCodeFilePath = packageA_x64.GetInstalledFilePath("Package.wxs"); + + // Source file should *not* be installed + Assert.False(File.Exists(packageA32SourceCodeFilePath), $"PackageA payload should not be there on test start: {packageA32SourceCodeFilePath}"); + Assert.False(File.Exists(packageA64SourceCodeFilePath), $"PackageA_x64 payload should not be there on test start: {packageA64SourceCodeFilePath}"); + + multipleBundlePackagesBundle.Install(); + multipleBundlePackagesBundle.VerifyRegisteredAndInPackageCache(); + + bundleA.VerifyRegisteredAndInPackageCache(); + bundleB_x64.VerifyRegisteredAndInPackageCache(); + + // Source file should be installed + Assert.True(File.Exists(packageA32SourceCodeFilePath), $"Should have found PackageA payload installed at: {packageA32SourceCodeFilePath}"); + Assert.True(File.Exists(packageA64SourceCodeFilePath), $"Should have found PackageA_x64 payload installed at: {packageA64SourceCodeFilePath}"); + + multipleBundlePackagesBundle.Uninstall(); + multipleBundlePackagesBundle.VerifyUnregisteredAndRemovedFromPackageCache(); + + bundleA.VerifyUnregisteredAndRemovedFromPackageCache(); + bundleB_x64.VerifyUnregisteredAndRemovedFromPackageCache(); + + // Source file should *not* be installed + Assert.False(File.Exists(packageA32SourceCodeFilePath), $"PackageA payload should have been removed by uninstall from: {packageA32SourceCodeFilePath}"); + Assert.False(File.Exists(packageA64SourceCodeFilePath), $"PackageA_x64 payload should have been removed by uninstall from: {packageA64SourceCodeFilePath}"); + + } + } +} diff --git a/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs b/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs index a73992da..edcb0abf 100644 --- a/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs +++ b/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs @@ -178,6 +178,13 @@ namespace WixToolset.Core.Burn { switch (facade.PackageSymbol.Type) { + case WixBundlePackageType.Bundle: + { + var command = new ProcessBundlePackageCommand(this.ServiceProvider, section, facade, packagesPayloads[facade.PackageId], this.IntermediateFolder); + command.Execute(); + } + break; + case WixBundlePackageType.Exe: { var command = new ProcessExePackageCommand(facade, payloadSymbols); diff --git a/src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs b/src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs index a76f84ec..6b37580e 100644 --- a/src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs +++ b/src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs @@ -65,6 +65,8 @@ namespace WixToolset.Core.Burn.Bind case SymbolDefinitionType.WixBundle: case SymbolDefinitionType.WixBundleContainer: case SymbolDefinitionType.WixBundleCustomDataAttribute: + case SymbolDefinitionType.WixBundleBundlePackage: + case SymbolDefinitionType.WixBundleBundlePackagePayload: case SymbolDefinitionType.WixBundleExePackage: case SymbolDefinitionType.WixBundleExePackagePayload: case SymbolDefinitionType.WixBundleExtension: diff --git a/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs b/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs index 08183f32..7b34f4ae 100644 --- a/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs +++ b/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs @@ -37,6 +37,7 @@ namespace WixToolset.Core.Burn.Bundles protected const uint IMAGE_NT_HEADER_SIZE = 24; // signature DWORD (4) + IMAGE_FILE_HEADER (20) protected const uint IMAGE_NT_HEADER_OFFSET_SIGNATURE = 0; + protected const uint IMAGE_NT_HEADER_OFFSET_MACHINE = 4; protected const uint IMAGE_NT_HEADER_OFFSET_NUMBEROFSECTIONS = 6; protected const uint IMAGE_NT_HEADER_OFFSET_SIZEOFOPTIONALHEADER = 20; @@ -57,6 +58,12 @@ namespace WixToolset.Core.Burn.Bundles protected const uint IMAGE_NT_SIGNATURE = 0x00004550; protected const ulong IMAGE_SECTION_WIXBURN_NAME = 0x6E7275627869772E; // ".wixburn", as a qword. + public const ushort IMAGE_FILE_MACHINE_AMD64 = 0x8664; + public const ushort IMAGE_FILE_MACHINE_ARM = 0x1C0; + public const ushort IMAGE_FILE_MACHINE_ARM64 = 0xAA64; + public const ushort IMAGE_FILE_MACHINE_ARMNT = 0x1C4; + public const ushort IMAGE_FILE_MACHINE_I386 = 0x14C; + // The ".wixburn" section contains: // 0- 3: magic number // 4- 7: version @@ -119,10 +126,13 @@ namespace WixToolset.Core.Burn.Bundles this.AttachedContainers = new List(); } + public bool Invalid { get; protected set; } + public ushort MachineType { get; private set; } public uint Checksum { get; protected set; } public uint SignatureOffset { get; protected set; } public uint SignatureSize { get; protected set; } public uint Version { get; protected set; } + public Guid BundleId { get; protected set; } public uint StubSize { get; protected set; } public uint OriginalChecksum { get; protected set; } public uint OriginalSignatureOffset { get; protected set; } @@ -170,8 +180,12 @@ namespace WixToolset.Core.Burn.Bundles /// Initialize the common information about a Burn engine. /// /// Binary reader open against a Burn engine. - /// True if initialized. - protected bool Initialize(BinaryReader reader) + protected void Initialize(BinaryReader reader) + { + this.Invalid = !this.TryInitialize(reader); + } + + private bool TryInitialize(BinaryReader reader) { if (!this.GetWixburnSectionInfo(reader)) { @@ -202,6 +216,7 @@ namespace WixToolset.Core.Burn.Bundles return false; } + this.BundleId = BurnCommon.ReadGuid(bytes, BURN_SECTION_OFFSET_BUNDLEGUID); this.StubSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_STUBSIZE); this.OriginalChecksum = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALCHECKSUM); this.OriginalSignatureOffset = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALSIGNATUREOFFSET); @@ -321,6 +336,8 @@ namespace WixToolset.Core.Burn.Bundles return false; } + this.MachineType = BurnCommon.ReadUInt16(bytes, IMAGE_NT_HEADER_OFFSET_MACHINE); + var sizeOptionalHeader = BurnCommon.ReadUInt16(bytes, IMAGE_NT_HEADER_OFFSET_SIZEOFOPTIONALHEADER); this.sections = BurnCommon.ReadUInt16(bytes, IMAGE_NT_HEADER_OFFSET_NUMBEROFSECTIONS); @@ -362,6 +379,17 @@ namespace WixToolset.Core.Burn.Bundles return true; } + internal static Guid ReadGuid(byte[] bytes, uint offset) + { + var guidBytes = new byte[16]; + for (var i = 0; i < 16; ++i) + { + guidBytes[i] = bytes[offset + i]; + } + + return new Guid(guidBytes); + } + /// /// Reads a UInt16 value in little-endian format from an offset in an array of bytes. /// diff --git a/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs b/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs index b30ac48a..ac6a2b8f 100644 --- a/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs +++ b/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs @@ -25,7 +25,6 @@ namespace WixToolset.Core.Burn.Bundles { private bool disposed; - private bool invalidBundle; private BinaryReader binaryReader; private readonly List attachedContainerPayloadNames; @@ -53,13 +52,12 @@ namespace WixToolset.Core.Burn.Bundles /// Burn reader. public static BurnReader Open(IMessaging messaging, string fileExe) { - var reader = new BurnReader(messaging, fileExe); - - reader.binaryReader = new BinaryReader(File.Open(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)); - if (!reader.Initialize(reader.binaryReader)) + var binaryReader = new BinaryReader(File.Open(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)); + var reader = new BurnReader(messaging, fileExe) { - reader.invalidBundle = true; - } + binaryReader = binaryReader, + }; + reader.Initialize(reader.binaryReader); return reader; } @@ -78,7 +76,7 @@ namespace WixToolset.Core.Burn.Bundles return false; } - if (this.invalidBundle) + if (this.Invalid) { return false; } @@ -156,7 +154,7 @@ namespace WixToolset.Core.Burn.Bundles return false; } - if (this.invalidBundle) + if (this.Invalid) { return false; } diff --git a/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs b/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs index 5800f5c0..1f0a032e 100644 --- a/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs +++ b/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs @@ -23,7 +23,6 @@ namespace WixToolset.Core.Burn.Bundles internal class BurnWriter : BurnCommon { private bool disposed; - private bool invalidBundle; private BinaryWriter binaryWriter; /// @@ -48,13 +47,10 @@ namespace WixToolset.Core.Burn.Bundles using (var binaryReader = new BinaryReader(File.Open(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete))) { - if (!writer.Initialize(binaryReader)) - { - writer.invalidBundle = true; - } + writer.Initialize(binaryReader); } - if (!writer.invalidBundle) + if (!writer.Invalid) { writer.binaryWriter = new BinaryWriter(File.Open(fileExe, FileMode.Open, FileAccess.ReadWrite, FileShare.Read | FileShare.Delete)); } @@ -70,7 +66,7 @@ namespace WixToolset.Core.Burn.Bundles /// public bool InitializeBundleSectionData(long stubSize, string bundleId) { - if (this.invalidBundle) + if (this.Invalid) { return false; } @@ -84,6 +80,7 @@ namespace WixToolset.Core.Burn.Bundles this.binaryWriter.BaseStream.Seek(this.wixburnDataOffset + BURN_SECTION_OFFSET_BUNDLEGUID, SeekOrigin.Begin); this.binaryWriter.Write(bundleGuid.ToByteArray()); + this.BundleId = bundleGuid; this.StubSize = (uint)stubSize; this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_STUBSIZE, this.StubSize); @@ -191,7 +188,7 @@ namespace WixToolset.Core.Burn.Bundles public void RememberThenResetSignature() { - if (this.invalidBundle) + if (this.Invalid) { return; } @@ -241,14 +238,14 @@ namespace WixToolset.Core.Burn.Bundles /// true if the container data is successfully appended; false otherwise private bool AppendContainer(Stream containerStream, uint containerSize, uint burnSectionOffsetSize, uint burnSectionCount) { - if (this.invalidBundle) + if (this.Invalid) { return false; } if (burnSectionOffsetSize > (this.wixburnRawDataSize - sizeof(uint))) { - this.invalidBundle = true; + this.Invalid = true; this.Messaging.Write(BurnBackendErrors.TooManyAttachedContainers(this.wixburnMaxContainers)); return false; } diff --git a/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs index 6a684fe0..84923998 100644 --- a/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs +++ b/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs @@ -381,7 +381,16 @@ namespace WixToolset.Core.Burn.Bundles writer.WriteAttributeString("InstallCondition", package.PackageSymbol.InstallCondition); } - if (package.SpecificPackageSymbol is WixBundleExePackageSymbol exePackage) // EXE + if (package.SpecificPackageSymbol is WixBundleBundlePackageSymbol bundlePackage) // BUNDLE + { + writer.WriteAttributeString("BundleId", bundlePackage.BundleId); + writer.WriteAttributeString("InstallArguments", bundlePackage.InstallCommand); + writer.WriteAttributeString("UninstallArguments", bundlePackage.UninstallCommand); + writer.WriteAttributeString("RepairArguments", bundlePackage.RepairCommand); + writer.WriteAttributeString("SupportsBurnProtocol", bundlePackage.SupportsBurnProtocol ? "yes" : "no"); + writer.WriteAttributeString("Win64", bundlePackage.Win64 ? "yes" : "no"); + } + else if (package.SpecificPackageSymbol is WixBundleExePackageSymbol exePackage) // EXE { writer.WriteAttributeString("DetectCondition", exePackage.DetectCondition); writer.WriteAttributeString("InstallArguments", exePackage.InstallCommand); diff --git a/src/wix/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs index 2bf22a3d..19403631 100644 --- a/src/wix/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs +++ b/src/wix/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs @@ -28,10 +28,12 @@ namespace WixToolset.Core.Burn.Bundles public void Execute() { var wixGroupPackagesGroupedById = this.Section.Symbols.OfType().Where(g => g.ParentType == ComplexReferenceParentType.Package).ToLookup(g => g.ParentId); + var bundlePackages = this.Section.Symbols.OfType().ToDictionary(t => t.Id.Id); var exePackages = this.Section.Symbols.OfType().ToDictionary(t => t.Id.Id); var msiPackages = this.Section.Symbols.OfType().ToDictionary(t => t.Id.Id); var mspPackages = this.Section.Symbols.OfType().ToDictionary(t => t.Id.Id); var msuPackages = this.Section.Symbols.OfType().ToDictionary(t => t.Id.Id); + var bundlePackagePayloads = this.Section.Symbols.OfType().ToDictionary(t => t.Id.Id); var exePackagePayloads = this.Section.Symbols.OfType().ToDictionary(t => t.Id.Id); var msiPackagePayloads = this.Section.Symbols.OfType().ToDictionary(t => t.Id.Id); var mspPackagePayloads = this.Section.Symbols.OfType().ToDictionary(t => t.Id.Id); @@ -49,7 +51,19 @@ namespace WixToolset.Core.Burn.Bundles if (wixGroup.ChildType == ComplexReferenceChildType.PackagePayload) { IntermediateSymbol tempPackagePayload = null; - if (exePackagePayloads.TryGetValue(wixGroup.ChildId, out var exePackagePayload)) + if (bundlePackagePayloads.TryGetValue(wixGroup.ChildId, out var bundlePackagePayload)) + { + if (package.Type == WixBundlePackageType.Bundle) + { + tempPackagePayload = bundlePackagePayload; + } + else + { + this.Messaging.Write(ErrorMessages.PackagePayloadUnsupported(bundlePackagePayload.SourceLineNumbers, "Bundle")); + this.Messaging.Write(ErrorMessages.PackagePayloadUnsupported2(package.SourceLineNumbers)); + } + } + else if (exePackagePayloads.TryGetValue(wixGroup.ChildId, out var exePackagePayload)) { if (package.Type == WixBundlePackageType.Exe) { @@ -129,6 +143,17 @@ namespace WixToolset.Core.Burn.Bundles switch (package.Type) { + case WixBundlePackageType.Bundle: + if (bundlePackages.TryGetValue(id, out var bundlePackage)) + { + facades.Add(id, new PackageFacade(package, bundlePackage)); + } + else + { + this.Messaging.Write(ErrorMessages.IdentifierNotFound("WixBundleBundlePackage", id)); + } + break; + case WixBundlePackageType.Exe: if (exePackages.TryGetValue(id, out var exePackage)) { diff --git a/src/wix/WixToolset.Core.Burn/Bundles/PerformBundleBackendValidationCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/PerformBundleBackendValidationCommand.cs index 2db5841b..8a2f1c64 100644 --- a/src/wix/WixToolset.Core.Burn/Bundles/PerformBundleBackendValidationCommand.cs +++ b/src/wix/WixToolset.Core.Burn/Bundles/PerformBundleBackendValidationCommand.cs @@ -55,7 +55,11 @@ namespace WixToolset.Core.Burn.Bundles foreach (var packageFacade in this.PackageFacadesById.Values) { - if (packageFacade.SpecificPackageSymbol is WixBundleExePackageSymbol wixBundleExePackageSymbol) + if (packageFacade.SpecificPackageSymbol is WixBundleBundlePackageSymbol wixBundleBundlePackageSymbol) + { + this.ValidateBundlePackage(wixBundleBundlePackageSymbol, packageFacade.PackageSymbol); + } + else if (packageFacade.SpecificPackageSymbol is WixBundleExePackageSymbol wixBundleExePackageSymbol) { this.ValidateExePackage(wixBundleExePackageSymbol, packageFacade.PackageSymbol); } @@ -90,6 +94,11 @@ namespace WixToolset.Core.Burn.Bundles } } + private void ValidateBundlePackage(WixBundleBundlePackageSymbol symbol, WixBundlePackageSymbol packageSymbol) + { + this.ValidateChainPackage(packageSymbol, "BundlePackage"); + } + private void ValidateExePackage(WixBundleExePackageSymbol symbol, WixBundlePackageSymbol packageSymbol) { this.ValidateChainPackage(packageSymbol, "ExePackage"); diff --git a/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs new file mode 100644 index 00000000..fdb07a56 --- /dev/null +++ b/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs @@ -0,0 +1,152 @@ +// Copyright (c) .NET 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 WixToolset.Core.Burn.Bundles +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.IO; + using System.Xml; + using WixToolset.Data; + using WixToolset.Data.Symbols; + using WixToolset.Extensibility.Data; + using WixToolset.Extensibility.Services; + + /// + /// Initializes package state from the Bundle contents. + /// + internal class ProcessBundlePackageCommand + { + public ProcessBundlePackageCommand(IServiceProvider serviceProvider, IntermediateSection section, PackageFacade facade, Dictionary packagePayloads, string intermediateFolder) + { + this.Messaging = serviceProvider.GetService(); + this.BackendHelper = serviceProvider.GetService(); + this.PackagePayloads = packagePayloads; + this.Section = section; + this.Facade = facade; + this.IntermediateFolder = intermediateFolder; + } + + private IMessaging Messaging { get; } + + private IBackendHelper BackendHelper { get; } + + private Dictionary PackagePayloads { get; } + + private PackageFacade Facade { get; } + + private IntermediateSection Section { get; } + + private string IntermediateFolder { get; } + + public List TrackedFiles { get; } = new List(); + + /// + /// Processes the Bundle packages to add properties and payloads from the Bundle packages. + /// + public void Execute() + { + var bundlePackage = (WixBundleBundlePackageSymbol)this.Facade.SpecificPackageSymbol; + var packagePayload = this.PackagePayloads[this.Facade.PackageSymbol.PayloadRef]; + var sourcePath = packagePayload.SourceFile.Path; + + using (var burnReader = BurnReader.Open(this.Messaging, sourcePath)) + { + if (burnReader.Invalid) + { + return; + } + + var baFolderPath = Path.Combine(this.IntermediateFolder, burnReader.BundleId.ToString()); + + if (!burnReader.ExtractUXContainer(baFolderPath, baFolderPath)) + { + return; + } + + foreach (var filePath in Directory.EnumerateFiles(baFolderPath, "*.*", SearchOption.AllDirectories)) + { + this.TrackedFiles.Add(this.BackendHelper.TrackFile(filePath, TrackedFileType.Temporary, packagePayload.SourceLineNumbers)); + } + + switch (burnReader.MachineType) + { + case BurnCommon.IMAGE_FILE_MACHINE_ARM: + case BurnCommon.IMAGE_FILE_MACHINE_ARMNT: + case BurnCommon.IMAGE_FILE_MACHINE_I386: + break; + case BurnCommon.IMAGE_FILE_MACHINE_AMD64: + case BurnCommon.IMAGE_FILE_MACHINE_ARM64: + bundlePackage.Win64 = true; + break; + default: + Debug.Assert(false, "Unknown machine type"); + break; + } + + bundlePackage.BundleId = burnReader.BundleId.ToString("B").ToUpperInvariant(); + + // Assume that the .wixburn section version will change when the Burn protocol changes. + // This should be a safe assumption since we will need to add the protocol version to the section to support this harvesting. + bundlePackage.SupportsBurnProtocol = burnReader.Version == 2; + + var document = new XmlDocument(); + document.Load(Path.Combine(baFolderPath, "manifest.xml")); + var namespaceManager = new XmlNamespaceManager(document.NameTable); + namespaceManager.AddNamespace("burn", BurnCommon.BurnNamespace); // TODO: support v3 bundles + var registrationElement = document.SelectSingleNode("/burn:BurnManifest/burn:Registration", namespaceManager) as XmlElement; + var arpElement = document.SelectSingleNode("/burn:BurnManifest/burn:Registration/burn:Arp", namespaceManager) as XmlElement; + + var perMachine = registrationElement.GetAttribute("PerMachine") == "yes"; + this.Facade.PackageSymbol.PerMachine = perMachine ? YesNoDefaultType.Yes : YesNoDefaultType.No; + + var version = registrationElement.GetAttribute("Version"); + packagePayload.Version = version; + + if (String.IsNullOrEmpty(this.Facade.PackageSymbol.CacheId)) + { + this.Facade.PackageSymbol.CacheId = String.Format("{0}v{1}", bundlePackage.BundleId, version); + } + + var providerKey = registrationElement.GetAttribute("ProviderKey"); + var depId = new Identifier(AccessModifier.Section, this.BackendHelper.GenerateIdentifier("dep", bundlePackage.Id.Id, providerKey)); + this.Section.AddSymbol(new WixDependencyProviderSymbol(packagePayload.SourceLineNumbers, depId) + { + ParentRef = bundlePackage.Id.Id, + ProviderKey = providerKey, + Version = version, + Attributes = WixDependencyProviderAttributes.ProvidesAttributesImported, + }); + + if (String.IsNullOrEmpty(this.Facade.PackageSymbol.DisplayName)) + { + this.Facade.PackageSymbol.DisplayName = arpElement.GetAttribute("DisplayName"); + } + + this.ProcessPackages(document, namespaceManager); + + // TODO: Add payloads? + } + } + + private void ProcessPackages(XmlDocument document, XmlNamespaceManager namespaceManager) + { + long packageInstallSize = 0; + + foreach (XmlElement packageElement in document.SelectNodes("/burn:BurnManifest/burn:Chain/*", namespaceManager)) + { + if (!packageElement.Name.EndsWith("Package")) + { + continue; + } + + if (Int64.TryParse(packageElement.GetAttribute("InstallSize"), out var installSize)) + { + packageInstallSize += installSize; + } + } + + this.Facade.PackageSymbol.InstallSize = packageInstallSize; + } + } +} diff --git a/src/wix/WixToolset.Core/Compiler_Bundle.cs b/src/wix/WixToolset.Core/Compiler_Bundle.cs index f671e90b..835fa2c2 100644 --- a/src/wix/WixToolset.Core/Compiler_Bundle.cs +++ b/src/wix/WixToolset.Core/Compiler_Bundle.cs @@ -1517,6 +1517,9 @@ namespace WixToolset.Core WixBundlePackageType? packageType = null; switch (child.Name.LocalName) { + case "BundlePackagePayload": + packageType = WixBundlePackageType.Bundle; + break; case "ExePackagePayload": packageType = WixBundlePackageType.Exe; break; @@ -1751,6 +1754,10 @@ namespace WixToolset.Core previousId = this.ParseExePackageElement(child, ComplexReferenceParentType.PackageGroup, BurnConstants.BundleChainPackageGroupId, previousType, previousId); previousType = ComplexReferenceChildType.Package; break; + case "BundlePackage": + previousId = this.ParseBundlePackageElement(child, ComplexReferenceParentType.PackageGroup, BurnConstants.BundleChainPackageGroupId, previousType, previousId); + previousType = ComplexReferenceChildType.Package; + break; case "RollbackBoundary": previousId = this.ParseRollbackBoundaryElement(child, ComplexReferenceParentType.PackageGroup, BurnConstants.BundleChainPackageGroupId, previousType, previousId); previousType = ComplexReferenceChildType.Package; @@ -1841,6 +1848,20 @@ namespace WixToolset.Core return this.ParseChainPackage(node, WixBundlePackageType.Exe, parentType, parentId, previousType, previousId); } + /// + /// Parse BundlePackage element + /// + /// Element to parse + /// Type of parent group, if known. + /// Identifier of parent group, if known. + /// Type of previous item, if known. + /// Identifier of previous item, if known + /// Identifier for package element. + private string ParseBundlePackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId) + { + return this.ParseChainPackage(node, WixBundlePackageType.Bundle, parentType, parentId, previousType, previousId); + } + /// /// Parse RollbackBoundary element /// @@ -2096,15 +2117,15 @@ namespace WixToolset.Core break; case "InstallArguments": installArguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - allowed = (packageType == WixBundlePackageType.Exe); + allowed = (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe); break; case "RepairArguments": repairArguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); - allowed = (packageType == WixBundlePackageType.Exe); + allowed = (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe); break; case "UninstallArguments": uninstallArguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); - allowed = (packageType == WixBundlePackageType.Exe); + allowed = (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe); break; case "PerMachine": perMachine = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); @@ -2339,19 +2360,20 @@ namespace WixToolset.Core this.ParseProvidesElement(child, packageType, id.Id, out _); break; case "ExitCode": - allowed = (packageType == WixBundlePackageType.Exe); + allowed = (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe); if (allowed) { this.ParseExitCodeElement(child, id.Id); } break; case "CommandLine": - allowed = (packageType == WixBundlePackageType.Exe); + allowed = (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe); if (allowed) { this.ParseCommandLineElement(child, id.Id); } break; + case "BundlePackagePayload": case "ExePackagePayload": case "MsiPackagePayload": case "MspPackagePayload": @@ -2422,6 +2444,18 @@ namespace WixToolset.Core switch (packageType) { + case WixBundlePackageType.Bundle: + WixBundleBundlePackageAttributes bundleAttributes = 0; + + this.Core.AddSymbol(new WixBundleBundlePackageSymbol(sourceLineNumbers, id) + { + Attributes = bundleAttributes, + InstallCommand = installArguments, + RepairCommand = repairArguments, + UninstallCommand = uninstallArguments, + }); + break; + case WixBundlePackageType.Exe: WixBundleExePackageAttributes exeAttributes = 0; exeAttributes |= (YesNoType.Yes == bundle) ? WixBundleExePackageAttributes.Bundle : 0; @@ -2478,6 +2512,10 @@ namespace WixToolset.Core { switch (packageType) { + case WixBundlePackageType.Bundle: + this.Core.AddSymbol(new WixBundleBundlePackagePayloadSymbol(sourceLineNumbers, payloadId)); + break; + case WixBundlePackageType.Exe: this.Core.AddSymbol(new WixBundleExePackagePayloadSymbol(sourceLineNumbers, payloadId)); break; @@ -2741,6 +2779,10 @@ namespace WixToolset.Core previousId = this.ParseExePackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId); previousType = ComplexReferenceChildType.Package; break; + case "BundlePackage": + previousId = this.ParseBundlePackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId); + previousType = ComplexReferenceChildType.Package; + break; case "RollbackBoundary": previousId = this.ParseRollbackBoundaryElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId); previousType = ComplexReferenceChildType.Package; diff --git a/src/wix/WixToolset.Core/Compiler_Dependency.cs b/src/wix/WixToolset.Core/Compiler_Dependency.cs index 7c863883..664ac1be 100644 --- a/src/wix/WixToolset.Core/Compiler_Dependency.cs +++ b/src/wix/WixToolset.Core/Compiler_Dependency.cs @@ -129,7 +129,7 @@ namespace WixToolset.Core this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Property, "ProductCode"); key = "!(bind.property.ProductCode)"; } - else if (WixBundlePackageType.Exe == packageType || WixBundlePackageType.Msu == packageType) + else if (WixBundlePackageType.Bundle == packageType || WixBundlePackageType.Exe == packageType || WixBundlePackageType.Msu == packageType) { // Must specify the provider key when authored for a package. this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key")); diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs new file mode 100644 index 00000000..268920a6 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs @@ -0,0 +1,114 @@ +// Copyright (c) .NET 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 WixToolsetTest.CoreIntegration +{ + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Xml; + using WixBuildTools.TestSupport; + using WixToolset.Core.TestPackage; + using WixToolset.Data; + using WixToolset.Data.Symbols; + using Xunit; + + public class BundlePackageFixture + { + [Fact] + public void CanBuildBundleWithBundlePackage() + { + var folder = TestData.Get(@"TestData"); + + using (var fs = new DisposableFileSystem()) + { + var baseFolder = fs.GetFolder(); + var chainIntermediateFolder = Path.Combine(baseFolder, "obj", "Chain"); + var parentIntermediateFolder = Path.Combine(baseFolder, "obj", "Parent"); + var binFolder = Path.Combine(baseFolder, "bin"); + var chainBundlePath = Path.Combine(binFolder, "chain.exe"); + var chainPdbPath = Path.Combine(binFolder, "chain.wixpdb"); + var parentBundlePath = Path.Combine(binFolder, "parent.exe"); + var parentPdbPath = Path.Combine(binFolder, "parent.wixpdb"); + var baFolderPath = Path.Combine(baseFolder, "ba"); + var extractFolderPath = Path.Combine(baseFolder, "extract"); + + var result = WixRunner.Execute(new[] + { + "build", + Path.Combine(folder, "Dependency", "CustomProviderKeyBundle.wxs"), + Path.Combine(folder, "BundleWithPackageGroupRef", "MinimalPackageGroup.wxs"), + "-bindpath", Path.Combine(folder, "SimpleBundle", "data"), + "-intermediateFolder", chainIntermediateFolder, + "-o", chainBundlePath, + }); + + result.AssertSuccess(); + + Assert.True(File.Exists(chainBundlePath)); + + string chainBundleId; + using (var wixOutput = WixOutput.Read(chainPdbPath)) + { + + var intermediate = Intermediate.Load(wixOutput); + var section = intermediate.Sections.Single(); + + var bundleSymbol = section.Symbols.OfType().Single(); + chainBundleId = bundleSymbol.BundleId; + } + + result = WixRunner.Execute(new[] + { + "build", + Path.Combine(folder, "BundlePackage", "BundlePackage.wxs"), + "-bindpath", Path.Combine(folder, "SimpleBundle", "data"), + "-bindpath", binFolder, + "-intermediateFolder", parentIntermediateFolder, + "-o", parentBundlePath, + }); + + result.AssertSuccess(); + + Assert.True(File.Exists(parentBundlePath)); + + string parentBundleId; + using (var wixOutput = WixOutput.Read(parentPdbPath)) + { + + var intermediate = Intermediate.Load(wixOutput); + var section = intermediate.Sections.Single(); + + var bundleSymbol = section.Symbols.OfType().Single(); + parentBundleId = bundleSymbol.BundleId; + } + + var extractResult = BundleExtractor.ExtractBAContainer(null, parentBundlePath, baFolderPath, extractFolderPath); + extractResult.AssertSuccess(); + + var ignoreAttributesByElementName = new Dictionary> + { + { "BundlePackage", new List { "Size" } }, + }; + var bundlePackages = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Chain/burn:BundlePackage") + .Cast() + .Select(e => e.GetTestXml(ignoreAttributesByElementName)) + .ToArray(); + WixAssert.CompareLineByLine(new string[] + { + $"", + }, bundlePackages); + + var registrations = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Registration") + .Cast() + .Select(e => e.GetTestXml()) + .ToArray(); + WixAssert.CompareLineByLine(new string[] + { + $"" + + "" + + "" + }, registrations); + } + } + } +} diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/BundlePackage.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/BundlePackage.wxs new file mode 100644 index 00000000..0602a48b --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/BundlePackage.wxs @@ -0,0 +1,10 @@ + + + + + + + + + + -- cgit v1.2.3-55-g6feb