aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-05-11 07:42:44 -0700
committerRob Mensching <rob@firegiant.com>2021-05-11 07:42:44 -0700
commitafc20ecb53ad3ade524aceefa30d98a3d84fd479 (patch)
tree4fb762f65dc195ccb104a6c1fcce7c40e7693e98
parent8e1207c4c5e451ba1a087f0fa11b63964ac35f3c (diff)
parentaf10c45d7b3a44af0b461a557847fe03263dcc10 (diff)
downloadwix-afc20ecb53ad3ade524aceefa30d98a3d84fd479.tar.gz
wix-afc20ecb53ad3ade524aceefa30d98a3d84fd479.tar.bz2
wix-afc20ecb53ad3ade524aceefa30d98a3d84fd479.zip
Merge burn
-rw-r--r--src/burn/CustomizedNativeRecommendedRules.ruleset8
-rw-r--r--src/burn/Directory.Build.props26
-rw-r--r--src/burn/Directory.Build.targets73
-rw-r--r--src/burn/Directory.Packages.props20
-rw-r--r--src/burn/Directory.csproj.props13
-rw-r--r--src/burn/Directory.vcxproj.props118
-rw-r--r--src/burn/NativeMultiTargeting.Build.props10
-rw-r--r--src/burn/README.md2
-rw-r--r--src/burn/appveyor.cmd17
-rw-r--r--src/burn/appveyor.yml44
-rw-r--r--src/burn/burn.sln61
-rw-r--r--src/burn/engine/EngineForApplication.cpp529
-rw-r--r--src/burn/engine/EngineForApplication.h44
-rw-r--r--src/burn/engine/EngineForExtension.cpp263
-rw-r--r--src/burn/engine/EngineForExtension.h27
-rw-r--r--src/burn/engine/apply.cpp3096
-rw-r--r--src/burn/engine/apply.h104
-rw-r--r--src/burn/engine/approvedexe.cpp262
-rw-r--r--src/burn/engine/approvedexe.h67
-rw-r--r--src/burn/engine/burnextension.cpp264
-rw-r--r--src/burn/engine/burnextension.h61
-rw-r--r--src/burn/engine/cabextract.cpp974
-rw-r--r--src/burn/engine/cabextract.h40
-rw-r--r--src/burn/engine/cache.cpp2052
-rw-r--r--src/burn/engine/cache.h216
-rw-r--r--src/burn/engine/condition.cpp1057
-rw-r--r--src/burn/engine/condition.h39
-rw-r--r--src/burn/engine/container.cpp398
-rw-r--r--src/burn/engine/container.h191
-rw-r--r--src/burn/engine/core.cpp1856
-rw-r--r--src/burn/engine/core.h218
-rw-r--r--src/burn/engine/dependency.cpp1312
-rw-r--r--src/burn/engine/dependency.h168
-rw-r--r--src/burn/engine/detect.cpp469
-rw-r--r--src/burn/engine/detect.h44
-rw-r--r--src/burn/engine/elevation.cpp3239
-rw-r--r--src/burn/engine/elevation.h176
-rw-r--r--src/burn/engine/embedded.cpp197
-rw-r--r--src/burn/engine/embedded.h27
-rw-r--r--src/burn/engine/engine.cpp992
-rw-r--r--src/burn/engine/engine.mc1090
-rw-r--r--src/burn/engine/engine.vcxproj186
-rw-r--r--src/burn/engine/exeengine.cpp816
-rw-r--r--src/burn/engine/exeengine.h50
-rw-r--r--src/burn/engine/externalengine.cpp805
-rw-r--r--src/burn/engine/externalengine.h181
-rw-r--r--src/burn/engine/inc/burnsources.h4
-rw-r--r--src/burn/engine/inc/engine.h27
-rw-r--r--src/burn/engine/logging.cpp754
-rw-r--r--src/burn/engine/logging.h153
-rw-r--r--src/burn/engine/manifest.cpp164
-rw-r--r--src/burn/engine/manifest.h28
-rw-r--r--src/burn/engine/msiengine.cpp2035
-rw-r--r--src/burn/engine/msiengine.h104
-rw-r--r--src/burn/engine/mspengine.cpp1197
-rw-r--r--src/burn/engine/mspengine.h84
-rw-r--r--src/burn/engine/msuengine.cpp529
-rw-r--r--src/burn/engine/msuengine.h50
-rw-r--r--src/burn/engine/netfxchainer.cpp418
-rw-r--r--src/burn/engine/netfxchainer.h98
-rw-r--r--src/burn/engine/package.cpp692
-rw-r--r--src/burn/engine/package.h380
-rw-r--r--src/burn/engine/packages.config5
-rw-r--r--src/burn/engine/payload.cpp314
-rw-r--r--src/burn/engine/payload.h107
-rw-r--r--src/burn/engine/pipe.cpp821
-rw-r--r--src/burn/engine/pipe.h113
-rw-r--r--src/burn/engine/plan.cpp2699
-rw-r--r--src/burn/engine/plan.h456
-rw-r--r--src/burn/engine/platform.cpp16
-rw-r--r--src/burn/engine/platform.h34
-rw-r--r--src/burn/engine/precomp.cpp3
-rw-r--r--src/burn/engine/precomp.h102
-rw-r--r--src/burn/engine/pseudobundle.cpp241
-rw-r--r--src/burn/engine/pseudobundle.h40
-rw-r--r--src/burn/engine/registration.cpp1702
-rw-r--r--src/burn/engine/registration.h225
-rw-r--r--src/burn/engine/relatedbundle.cpp483
-rw-r--r--src/burn/engine/relatedbundle.h20
-rw-r--r--src/burn/engine/search.cpp1303
-rw-r--r--src/burn/engine/search.h163
-rw-r--r--src/burn/engine/section.cpp399
-rw-r--r--src/burn/engine/section.h54
-rw-r--r--src/burn/engine/splashscreen.cpp355
-rw-r--r--src/burn/engine/splashscreen.h31
-rw-r--r--src/burn/engine/uithread.cpp222
-rw-r--r--src/burn/engine/uithread.h23
-rw-r--r--src/burn/engine/update.cpp44
-rw-r--r--src/burn/engine/update.h33
-rw-r--r--src/burn/engine/userexperience.cpp2653
-rw-r--r--src/burn/engine/userexperience.h545
-rw-r--r--src/burn/engine/variable.cpp2323
-rw-r--r--src/burn/engine/variable.h185
-rw-r--r--src/burn/engine/variant.cpp321
-rw-r--r--src/burn/engine/variant.h100
-rw-r--r--src/burn/nuget.config10
-rw-r--r--src/burn/stub/StubSection.cpp23
-rw-r--r--src/burn/stub/WixToolset.Burn.props13
-rw-r--r--src/burn/stub/packages.config8
-rw-r--r--src/burn/stub/precomp.cpp3
-rw-r--r--src/burn/stub/precomp.h17
-rw-r--r--src/burn/stub/stub.cpp106
-rw-r--r--src/burn/stub/stub.icobin0 -> 2238 bytes
-rw-r--r--src/burn/stub/stub.nuspec25
-rw-r--r--src/burn/stub/stub.rc3
-rw-r--r--src/burn/stub/stub.vcxproj120
-rw-r--r--src/burn/test/BurnUnitTest/AssemblyInfo.cpp12
-rw-r--r--src/burn/test/BurnUnitTest/BurnTestException.h93
-rw-r--r--src/burn/test/BurnUnitTest/BurnTestFixture.h75
-rw-r--r--src/burn/test/BurnUnitTest/BurnUnitTest.h48
-rw-r--r--src/burn/test/BurnUnitTest/BurnUnitTest.rc6
-rw-r--r--src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj109
-rw-r--r--src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters80
-rw-r--r--src/burn/test/BurnUnitTest/CacheTest.cpp119
-rw-r--r--src/burn/test/BurnUnitTest/ElevationTest.cpp221
-rw-r--r--src/burn/test/BurnUnitTest/ManifestHelpers.cpp41
-rw-r--r--src/burn/test/BurnUnitTest/ManifestHelpers.h24
-rw-r--r--src/burn/test/BurnUnitTest/ManifestTest.cpp62
-rw-r--r--src/burn/test/BurnUnitTest/PlanTest.cpp1473
-rw-r--r--src/burn/test/BurnUnitTest/RegistrationTest.cpp772
-rw-r--r--src/burn/test/BurnUnitTest/SearchTest.cpp815
-rw-r--r--src/burn/test/BurnUnitTest/TestData/CacheTest/CacheSignatureTest.File1
-rw-r--r--src/burn/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml1
-rw-r--r--src/burn/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml1
-rw-r--r--src/burn/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml1
-rw-r--r--src/burn/test/BurnUnitTest/VariableHelpers.cpp217
-rw-r--r--src/burn/test/BurnUnitTest/VariableHelpers.h36
-rw-r--r--src/burn/test/BurnUnitTest/VariableTest.cpp532
-rw-r--r--src/burn/test/BurnUnitTest/VariantTest.cpp221
-rw-r--r--src/burn/test/BurnUnitTest/packages.config15
-rw-r--r--src/burn/test/BurnUnitTest/precomp.cpp3
-rw-r--r--src/burn/test/BurnUnitTest/precomp.h79
-rw-r--r--src/samples/burn/ManagedBundleRunner/BundleErrorEventArgs.cs33
-rw-r--r--src/samples/burn/ManagedBundleRunner/BundleProgressEventArgs.cs23
-rw-r--r--src/samples/burn/ManagedBundleRunner/BundleResult.cs24
-rw-r--r--src/samples/burn/ManagedBundleRunner/BundleRunner.cs212
-rw-r--r--src/samples/burn/runbundle/AssemblyInfo.cs12
-rw-r--r--src/samples/burn/runbundle/Program.cs47
138 files changed, 50490 insertions, 0 deletions
diff --git a/src/burn/CustomizedNativeRecommendedRules.ruleset b/src/burn/CustomizedNativeRecommendedRules.ruleset
new file mode 100644
index 00000000..142b141c
--- /dev/null
+++ b/src/burn/CustomizedNativeRecommendedRules.ruleset
@@ -0,0 +1,8 @@
1<?xml version="1.0" encoding="utf-8"?>
2<RuleSet Name="Customized Microsoft Native Recommended Rules" Description="Microsoft Native Recommended Rules, -C26812" ToolsVersion="16.0">
3 <Include Path="nativerecommendedrules.ruleset" Action="Default" />
4 <Rules AnalyzerId="Microsoft.Analyzers.NativeCodeAnalysis" RuleNamespace="Microsoft.Rules.Native">
5 <!-- We need C style enums since we support BAs written in C -->
6 <Rule Id="C26812" Action="None" />
7 </Rules>
8</RuleSet> \ No newline at end of file
diff --git a/src/burn/Directory.Build.props b/src/burn/Directory.Build.props
new file mode 100644
index 00000000..fb34d54e
--- /dev/null
+++ b/src/burn/Directory.Build.props
@@ -0,0 +1,26 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3<!--
4 Do NOT modify this file. Update the canonical version in Home\repo-template\src\Directory.Build.props
5 then update all of the repos.
6-->
7<Project>
8 <PropertyGroup>
9 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
10 <EnableSourceLink Condition=" '$(NCrunch)' == '1' ">false</EnableSourceLink>
11
12 <ProjectName Condition=" '$(ProjectName)' == '' ">$(MSBuildProjectName)</ProjectName>
13 <BaseOutputPath>$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\))</BaseOutputPath>
14 <BaseIntermediateOutputPath>$(BaseOutputPath)obj\$(ProjectName)\</BaseIntermediateOutputPath>
15 <OutputPath>$(BaseOutputPath)$(Configuration)\</OutputPath>
16
17 <Authors>WiX Toolset Team</Authors>
18 <Company>WiX Toolset</Company>
19 <Copyright>Copyright (c) .NET Foundation and contributors. All rights reserved.</Copyright>
20 <PackageLicenseExpression>MS-RL</PackageLicenseExpression>
21 <Product>WiX Toolset</Product>
22 </PropertyGroup>
23
24 <Import Project="Directory$(MSBuildProjectExtension).props" Condition=" Exists('Directory$(MSBuildProjectExtension).props') " />
25 <Import Project="Custom.Build.props" Condition=" Exists('Custom.Build.props') " />
26</Project>
diff --git a/src/burn/Directory.Build.targets b/src/burn/Directory.Build.targets
new file mode 100644
index 00000000..44701fb6
--- /dev/null
+++ b/src/burn/Directory.Build.targets
@@ -0,0 +1,73 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3<!--
4 Do NOT modify this file. Update the canonical version in Home\repo-template\src\Directory.Build.targets
5 then update all of the repos.
6-->
7<Project>
8 <PropertyGroup>
9 <SigningToolFolder>$(BaseOutputPath)obj\.tools</SigningToolFolder>
10 <SigningToolExe>$(SigningToolFolder)\SignClient.exe</SigningToolExe>
11 <SigningFilelist>$(SigningToolFolder)\empty-filelist.txt</SigningFilelist>
12 <SigningConfiguration>$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), signing.json))\signing.json</SigningConfiguration>
13 </PropertyGroup>
14
15 <PropertyGroup>
16 <CreateDocumentation Condition=" '$(CreateDocumentationFile)'!='true' ">false</CreateDocumentation>
17 <DocumentationFile Condition=" '$(CreateDocumentationFile)'=='true' ">$(OutputPath)\$(AssemblyName).xml</DocumentationFile>
18 </PropertyGroup>
19
20 <Target Name="SetNuspecProperties" DependsOnTargets="InitializeSourceControlInformation" AfterTargets="GetBuildVersion"
21 Condition=" Exists('$(MSBuildProjectName).nuspec') ">
22 <PropertyGroup>
23 <ProjectUrl Condition=" '$(ProjectUrl)'=='' and '$(PrivateRepositoryUrl)'!='' ">$(PrivateRepositoryUrl.Replace('.git',''))</ProjectUrl>
24
25 <NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
26 <NuspecBasePath Condition=" '$(NuspecBasePath)'=='' ">$(MSBuildProjectDirectory)</NuspecBasePath>
27 <NuspecProperties>$(NuspecProperties);Id=$(PackageId);Authors="$(Authors)";Configuration=$(Configuration);Copyright="$(Copyright)";Description="$(Description)";Title="$(Title)"</NuspecProperties>
28 <NuspecProperties>$(NuspecProperties);Version=$(NPMPackageVersion);RepositoryCommit=$(SourceRevisionId);RepositoryType=$(RepositoryType);RepositoryUrl=$(PrivateRepositoryUrl);ProjectFolder=$(MSBuildProjectDirectory)\;ProjectUrl=$(ProjectUrl)</NuspecProperties>
29 <PublishRepositoryUrl>true</PublishRepositoryUrl>
30 <SymbolPackageFormat>snupkg</SymbolPackageFormat>
31 </PropertyGroup>
32 </Target>
33
34 <Target Name="PackNative" DependsOnTargets="GetBuildVersion;SetNuspecProperties"
35 Condition=" Exists('$(MSBuildProjectName).nuspec') ">
36
37 <Exec Command='nuget pack $(NuspecFile) -OutputDirectory "$(BaseOutputPath)$(Configuration)" -BasePath "$(NuspecBasePath)" -Properties $(NuspecProperties)'
38 WorkingDirectory="$(MSBuildProjectDirectory)" />
39
40 <ItemGroup>
41 <NuGetPackOutput Include="$(BaseOutputPath)$(Configuration)\**\$(PackageId)*.nupkg" />
42 </ItemGroup>
43 </Target>
44
45 <Target Name="_GetSignClient"
46 Condition=" !Exists('$(SigningToolExe)') ">
47
48 <WriteLinesToFile File='$(SigningFilelist)' Lines='do-not-sign-files-in-nupkg' Overwrite='true' />
49
50 <Exec Command='dotnet.exe tool install --tool-path "$(SigningToolFolder)" SignClient' />
51 </Target>
52
53 <Target Name="SignOutput" DependsOnTargets="_GetSignClient" AfterTargets="AfterBuild"
54 Condition=" '$(SigningUser)'!='' and '$(SignOutput)'!='false' and
55 ('$(MSBuildProjectExtension)'=='.csproj' or ('$(MSBuildProjectExtension)'=='.vcxproj' and '$(ConfigurationType)'!='StaticLibrary'))">
56
57 <Exec Command='"$(SigningToolExe)" sign -i $(TargetPath) -c "$(SigningConfiguration)" -n "WiX Toolset" -d "WiX Toolset" -u https://wixtoolset.org/ -r "$(SigningUser)" -s "$(SigningSecret)"'
58 WorkingDirectory="$(MSBuildProjectDirectory)" EchoOff="true" />
59 </Target>
60
61 <Target Name="SignNupkg" DependsOnTargets="_GetSignClient" AfterTargets="Pack;PackNative"
62 Condition=" '$(SigningUser)'!='' and '@(NuGetPackOutput)'!='' and '$(SignNupkg)'!='false' ">
63 <ItemGroup>
64 <SigningNupkgs Include="@(NuGetPackOutput)" Condition=" '%(Extension)'=='.nupkg' " />
65 </ItemGroup>
66
67 <Exec Command='"$(SigningToolExe)" sign -i "@(SigningNupkgs->&apos;%(Identity)&apos;)" -c "$(SigningConfiguration)" -f "$(SigningFilelist)" -n "WiX Toolset" -d "WiX Toolset" -u https://wixtoolset.org/ -r "$(SigningUser)" -s "$(SigningSecret)"'
68 WorkingDirectory="$(MSBuildProjectDirectory)" EchoOff="true" />
69 </Target>
70
71 <Import Project="Directory$(MSBuildProjectExtension).targets" Condition=" Exists('Directory$(MSBuildProjectExtension).targets') " />
72 <Import Project="Custom.Build.targets" Condition=" Exists('Custom.Build.targets') " />
73</Project>
diff --git a/src/burn/Directory.Packages.props b/src/burn/Directory.Packages.props
new file mode 100644
index 00000000..369c51e7
--- /dev/null
+++ b/src/burn/Directory.Packages.props
@@ -0,0 +1,20 @@
1<Project>
2 <ItemGroup>
3 <PackageVersion Include="WixBuildTools.TestSupport" Version="4.0.47" />
4 <PackageVersion Include="WixBuildTools.TestSupport.Native" Version="4.0.47" />
5 <PackageVersion Include="WixToolset.DUtil" Version="4.0.70" targetFramework="native" />
6 <PackageVersion Include="WixToolset.BootstrapperCore.Native" Version="4.0.57" targetFramework="native" />
7 <PackageVersion Include="xunit.abstractions" Version="2.0.3" />
8 <PackageVersion Include="xunit.assert" Version="2.4.1" />
9 <PackageVersion Include="xunit.core" Version="2.4.1" />
10 <PackageVersion Include="xunit.extensibility.core" Version="2.4.1" />
11 <PackageVersion Include="xunit.extensibility.execution" Version="2.4.1" />
12 <PackageVersion Include="xunit.runner.msbuild" Version="2.4.1" />
13 <PackageVersion Include="xunit.runner.visualstudio" Version="2.4.1" />
14
15 <PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.0.0" />
16 <PackageVersion Include="Nerdbank.GitVersioning" Version="3.3.37" />
17 <PackageVersion Include="WixToolset.Dutil" Version="4.0.70" />
18 <PackageVersion Include="WixToolset.BootstrapperCore.Native" Version="4.0.57" />
19 </ItemGroup>
20</Project>
diff --git a/src/burn/Directory.csproj.props b/src/burn/Directory.csproj.props
new file mode 100644
index 00000000..81d24ad1
--- /dev/null
+++ b/src/burn/Directory.csproj.props
@@ -0,0 +1,13 @@
1<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2<!--
3 Do NOT modify this file. Update the canonical version in Home\repo-template\src\CSharp.Build.props
4 then update all of the repos.
5-->
6<Project>
7 <PropertyGroup>
8 <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
9 <SignAssembly>true</SignAssembly>
10 <AssemblyOriginatorKeyFile>$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)wix.snk))</AssemblyOriginatorKeyFile>
11 <NBGV_EmitThisAssemblyClass>false</NBGV_EmitThisAssemblyClass>
12 </PropertyGroup>
13</Project>
diff --git a/src/burn/Directory.vcxproj.props b/src/burn/Directory.vcxproj.props
new file mode 100644
index 00000000..63d73b36
--- /dev/null
+++ b/src/burn/Directory.vcxproj.props
@@ -0,0 +1,118 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project>
5 <PropertyGroup>
6 <Platform Condition=" '$(Platform)' == '' OR '$(Platform)' == 'AnyCPU' ">Win32</Platform>
7 <IntDir>$(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\</IntDir>
8 <OutDir>$(OutputPath)$(Platform)\</OutDir>
9
10 <!-- NBGV properties -->
11 <AssemblyCompany>$(Company)</AssemblyCompany>
12 <AssemblyCopyright>$(Copyright)</AssemblyCopyright>
13
14 <RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
15 <NuGetTargetMoniker>native,Version=v0.0</NuGetTargetMoniker>
16 </PropertyGroup>
17
18 <PropertyGroup Condition="'$(WindowsTargetPlatformVersion)'=='' AND '$(VisualStudioVersion)'>='15.0'">
19 <WindowsTargetPlatformVersion>$([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0'))</WindowsTargetPlatformVersion>
20 </PropertyGroup>
21
22 <PropertyGroup>
23 <CodeAnalysisRuleSet Condition=" Exists('$(MSBuildThisFileDirectory)CustomizedNativeRecommendedRules.ruleset') ">$(MSBuildThisFileDirectory)CustomizedNativeRecommendedRules.ruleset</CodeAnalysisRuleSet>
24 </PropertyGroup>
25
26 <ItemDefinitionGroup>
27 <ClCompile>
28 <DisableSpecificWarnings>$(DisableSpecificCompilerWarnings)</DisableSpecificWarnings>
29 <WarningLevel>Level4</WarningLevel>
30 <AdditionalIncludeDirectories>$(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
31 <PreprocessorDefinitions>WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0600;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
32 <PrecompiledHeader>Use</PrecompiledHeader>
33 <PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
34 <CallingConvention Condition="'$(Platform)'=='Win32'">StdCall</CallingConvention>
35 <TreatWarningAsError>true</TreatWarningAsError>
36 <ExceptionHandling>false</ExceptionHandling>
37 <ControlFlowGuard>Guard</ControlFlowGuard>
38 <AdditionalOptions>-YlprecompDefine</AdditionalOptions>
39 <AdditionalOptions Condition=" $(PlatformToolset.StartsWith('v14')) ">/Zc:threadSafeInit- %(AdditionalOptions)</AdditionalOptions>
40 <MultiProcessorCompilation Condition=" $(NUMBER_OF_PROCESSORS) &gt; 4 ">true</MultiProcessorCompilation>
41 </ClCompile>
42 <ResourceCompile>
43 <PreprocessorDefinitions>$(ArmPreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
44 <AdditionalIncludeDirectories>$(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
45 </ResourceCompile>
46 <Lib>
47 <AdditionalLibraryDirectories>$(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
48 </Lib>
49 <Link>
50 <SubSystem>$(ProjectSubSystem)</SubSystem>
51 <ModuleDefinitionFile>$(ProjectModuleDefinitionFile)</ModuleDefinitionFile>
52 <NoEntryPoint>$(ResourceOnlyDll)</NoEntryPoint>
53 <GenerateDebugInformation>true</GenerateDebugInformation>
54 <AdditionalDependencies>$(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
55 <AdditionalLibraryDirectories>$(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
56 <AdditionalOptions Condition=" $(PlatformToolset.StartsWith('v14')) ">/IGNORE:4099 %(AdditionalOptions)</AdditionalOptions>
57 </Link>
58 </ItemDefinitionGroup>
59
60 <ItemDefinitionGroup Condition=" '$(Platform)'=='Win32' and '$(PlatformToolset)'!='v100'">
61 <ClCompile>
62 <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
63 </ClCompile>
64 </ItemDefinitionGroup>
65 <ItemDefinitionGroup Condition=" '$(Platform)'=='arm' ">
66 <ClCompile>
67 <CallingConvention>CDecl</CallingConvention>
68 </ClCompile>
69 </ItemDefinitionGroup>
70 <ItemDefinitionGroup Condition=" '$(ConfigurationType)'=='StaticLibrary' ">
71 <ClCompile>
72 <DebugInformationFormat>OldStyle</DebugInformationFormat>
73 <OmitDefaultLibName>true</OmitDefaultLibName>
74 <IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
75 </ClCompile>
76 </ItemDefinitionGroup>
77 <ItemDefinitionGroup Condition=" '$(Configuration)'=='Debug' ">
78 <ClCompile>
79 <Optimization>Disabled</Optimization>
80 <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
81 <PreprocessorDefinitions>_DEBUG;DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
82 <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
83 </ClCompile>
84 </ItemDefinitionGroup>
85 <ItemDefinitionGroup Condition=" '$(Configuration)'=='Debug' and '$(CLRSupport)'=='true' ">
86 <ClCompile>
87 <ControlFlowGuard></ControlFlowGuard>
88 <BasicRuntimeChecks></BasicRuntimeChecks>
89 <RuntimeLibrary>MultiThreadedDebugDll</RuntimeLibrary>
90 </ClCompile>
91 </ItemDefinitionGroup>
92 <ItemDefinitionGroup Condition=" '$(Configuration)'=='Release' ">
93 <ClCompile>
94 <Optimization>MinSpace</Optimization>
95 <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
96 <FunctionLevelLinking>true</FunctionLevelLinking>
97 <IntrinsicFunctions>true</IntrinsicFunctions>
98 <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
99 </ClCompile>
100 <Link>
101 <EnableCOMDATFolding>true</EnableCOMDATFolding>
102 <OptimizeReferences>true</OptimizeReferences>
103 </Link>
104 </ItemDefinitionGroup>
105 <ItemDefinitionGroup Condition=" '$(Configuration)'=='Release' and '$(CLRSupport)'=='true' ">
106 <ClCompile>
107 <ControlFlowGuard></ControlFlowGuard>
108 <BasicRuntimeChecks></BasicRuntimeChecks>
109 <RuntimeLibrary>MultiThreadedDll</RuntimeLibrary>
110 </ClCompile>
111 </ItemDefinitionGroup>
112 <ItemDefinitionGroup Condition=" '$(CLRSupport)'=='true' ">
113 <Link>
114 <KeyFile>$(LinkKeyFile)</KeyFile>
115 <DelaySign>$(LinkDelaySign)</DelaySign>
116 </Link>
117 </ItemDefinitionGroup>
118</Project>
diff --git a/src/burn/NativeMultiTargeting.Build.props b/src/burn/NativeMultiTargeting.Build.props
new file mode 100644
index 00000000..1ff46559
--- /dev/null
+++ b/src/burn/NativeMultiTargeting.Build.props
@@ -0,0 +1,10 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project>
5 <!-- Overrides the standard Cpp.Build.props to include the PlatformToolset in the output path. -->
6 <PropertyGroup>
7 <IntDir>$(BaseIntermediateOutputPath)$(Configuration)\$(PlatformToolset)\$(PlatformTarget)\</IntDir>
8 <OutDir>$(OutputPath)$(PlatformToolset)\$(PlatformTarget)\</OutDir>
9 </PropertyGroup>
10</Project>
diff --git a/src/burn/README.md b/src/burn/README.md
new file mode 100644
index 00000000..a564d971
--- /dev/null
+++ b/src/burn/README.md
@@ -0,0 +1,2 @@
1# burn
2burn.lib - Burn engine
diff --git a/src/burn/appveyor.cmd b/src/burn/appveyor.cmd
new file mode 100644
index 00000000..a35e10d5
--- /dev/null
+++ b/src/burn/appveyor.cmd
@@ -0,0 +1,17 @@
1@setlocal
2@pushd %~dp0
3@set _C=Release
4@if /i "%1"=="debug" set _C=Debug
5
6nuget restore || exit /b
7
8msbuild -t:Test -p:Configuration=%_C% src\test\BurnUnitTest || exit /b
9
10msbuild -p:Configuration=%_C%;Platform=x86 || exit /b
11msbuild -p:Configuration=%_C%;Platform=x64 || exit /b
12msbuild -p:Configuration=%_C%;Platform=arm64 || exit /b
13
14msbuild -p:Configuration=%_C% -t:PackNative src\stub\stub.vcxproj || exit /b
15
16@popd
17@endlocal \ No newline at end of file
diff --git a/src/burn/appveyor.yml b/src/burn/appveyor.yml
new file mode 100644
index 00000000..364569cf
--- /dev/null
+++ b/src/burn/appveyor.yml
@@ -0,0 +1,44 @@
1# Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2#
3# Do NOT modify this file. Update the canonical version in Home\repo-template\src\appveyor.yml
4# then update all of the repos.
5
6branches:
7 only:
8 - master
9 - develop
10
11image: Visual Studio 2019
12
13version: 0.0.0.{build}
14configuration: Release
15
16environment:
17 DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
18 DOTNET_CLI_TELEMETRY_OPTOUT: 1
19 NUGET_XMLDOC_MODE: skip
20
21build_script:
22 - appveyor.cmd
23
24test: off
25
26pull_requests:
27 do_not_increment_build_number: true
28
29nuget:
30 disable_publish_on_pr: true
31
32skip_branch_with_pr: true
33skip_tags: true
34
35artifacts:
36- path: build\Release\**\*.nupkg
37 name: nuget
38- path: build\Release\**\*.snupkg
39 name: snupkg
40
41notifications:
42- provider: Slack
43 incoming_webhook:
44 secure: p5xuu+4x2JHfwGDMDe5KcG1k7gZxqYc4jWVwvyNZv5cvkubPD2waJs5yXMAXZNN7Z63/3PWHb7q4KoY/99AjauYa1nZ4c5qYqRPFRBKTHfA=
diff --git a/src/burn/burn.sln b/src/burn/burn.sln
new file mode 100644
index 00000000..6a64b8f0
--- /dev/null
+++ b/src/burn/burn.sln
@@ -0,0 +1,61 @@
1
2Microsoft Visual Studio Solution File, Format Version 12.00
3# Visual Studio Version 16
4VisualStudioVersion = 16.0.30711.63
5MinimumVisualStudioVersion = 15.0.26124.0
6Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "engine", "src\engine\engine.vcxproj", "{8119537D-E1D9-6591-D51A-49768A2F9C37}"
7EndProject
8Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stub", "src\stub\stub.vcxproj", "{C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}"
9EndProject
10Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BurnUnitTest", "src\test\BurnUnitTest\BurnUnitTest.vcxproj", "{9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}"
11EndProject
12Global
13 GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 Debug|ARM64 = Debug|ARM64
15 Debug|x64 = Debug|x64
16 Debug|x86 = Debug|x86
17 Release|ARM64 = Release|ARM64
18 Release|x64 = Release|x64
19 Release|x86 = Release|x86
20 EndGlobalSection
21 GlobalSection(ProjectConfigurationPlatforms) = postSolution
22 {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|ARM64.ActiveCfg = Debug|ARM64
23 {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|ARM64.Build.0 = Debug|ARM64
24 {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|x64.ActiveCfg = Debug|x64
25 {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|x64.Build.0 = Debug|x64
26 {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|x86.ActiveCfg = Debug|Win32
27 {8119537D-E1D9-6591-D51A-49768A2F9C37}.Debug|x86.Build.0 = Debug|Win32
28 {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|ARM64.ActiveCfg = Release|ARM64
29 {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|ARM64.Build.0 = Release|ARM64
30 {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|x64.ActiveCfg = Release|x64
31 {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|x64.Build.0 = Release|x64
32 {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|x86.ActiveCfg = Release|Win32
33 {8119537D-E1D9-6591-D51A-49768A2F9C37}.Release|x86.Build.0 = Release|Win32
34 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|ARM64.ActiveCfg = Debug|ARM64
35 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|ARM64.Build.0 = Debug|ARM64
36 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|x64.ActiveCfg = Debug|x64
37 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|x64.Build.0 = Debug|x64
38 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|x86.ActiveCfg = Debug|Win32
39 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Debug|x86.Build.0 = Debug|Win32
40 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|ARM64.ActiveCfg = Release|ARM64
41 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|ARM64.Build.0 = Release|ARM64
42 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x64.ActiveCfg = Release|x64
43 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x64.Build.0 = Release|x64
44 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x86.ActiveCfg = Release|Win32
45 {C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}.Release|x86.Build.0 = Release|Win32
46 {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Debug|ARM64.ActiveCfg = Debug|ARM64
47 {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Debug|x64.ActiveCfg = Debug|Win32
48 {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Debug|x86.ActiveCfg = Debug|Win32
49 {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Debug|x86.Build.0 = Debug|Win32
50 {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Release|ARM64.ActiveCfg = Release|ARM64
51 {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Release|x64.ActiveCfg = Release|Win32
52 {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Release|x86.ActiveCfg = Release|Win32
53 {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}.Release|x86.Build.0 = Release|Win32
54 EndGlobalSection
55 GlobalSection(SolutionProperties) = preSolution
56 HideSolutionNode = FALSE
57 EndGlobalSection
58 GlobalSection(ExtensibilityGlobals) = postSolution
59 SolutionGuid = {A35910C5-8A89-473E-9578-E084172DD3C9}
60 EndGlobalSection
61EndGlobal
diff --git a/src/burn/engine/EngineForApplication.cpp b/src/burn/engine/EngineForApplication.cpp
new file mode 100644
index 00000000..83d88ba1
--- /dev/null
+++ b/src/burn/engine/EngineForApplication.cpp
@@ -0,0 +1,529 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6static HRESULT BAEngineGetPackageCount(
7 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
8 __in const LPVOID pvArgs,
9 __inout LPVOID pvResults
10 )
11{
12 HRESULT hr = S_OK;
13 ValidateMessageArgs(hr, pvArgs, BAENGINE_GETPACKAGECOUNT_ARGS, pArgs);
14 ValidateMessageResults(hr, pvResults, BAENGINE_GETPACKAGECOUNT_RESULTS, pResults);
15
16 ExternalEngineGetPackageCount(pContext->pEngineState, &pResults->cPackages);
17
18LExit:
19 return hr;
20}
21
22static HRESULT BAEngineGetVariableNumeric(
23 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
24 __in const LPVOID pvArgs,
25 __inout LPVOID pvResults
26 )
27{
28 HRESULT hr = S_OK;
29 ValidateMessageArgs(hr, pvArgs, BAENGINE_GETVARIABLENUMERIC_ARGS, pArgs);
30 ValidateMessageResults(hr, pvResults, BAENGINE_GETVARIABLENUMERIC_RESULTS, pResults);
31
32 hr = ExternalEngineGetVariableNumeric(pContext->pEngineState, pArgs->wzVariable, &pResults->llValue);
33
34LExit:
35 return hr;
36}
37
38static HRESULT BAEngineGetVariableString(
39 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
40 __in const LPVOID pvArgs,
41 __inout LPVOID pvResults
42 )
43{
44 HRESULT hr = S_OK;
45 ValidateMessageArgs(hr, pvArgs, BAENGINE_GETVARIABLESTRING_ARGS, pArgs);
46 ValidateMessageResults(hr, pvResults, BAENGINE_GETVARIABLESTRING_RESULTS, pResults);
47
48 hr = ExternalEngineGetVariableString(pContext->pEngineState, pArgs->wzVariable, pResults->wzValue, &pResults->cchValue);
49
50LExit:
51 return hr;
52}
53
54static HRESULT BAEngineGetVariableVersion(
55 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
56 __in const LPVOID pvArgs,
57 __inout LPVOID pvResults
58 )
59{
60 HRESULT hr = S_OK;
61 ValidateMessageArgs(hr, pvArgs, BAENGINE_GETVARIABLEVERSION_ARGS, pArgs);
62 ValidateMessageResults(hr, pvResults, BAENGINE_GETVARIABLEVERSION_RESULTS, pResults);
63
64 hr = ExternalEngineGetVariableVersion(pContext->pEngineState, pArgs->wzVariable, pResults->wzValue, &pResults->cchValue);
65
66LExit:
67 return hr;
68}
69
70static HRESULT BAEngineFormatString(
71 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
72 __in const LPVOID pvArgs,
73 __inout LPVOID pvResults
74 )
75{
76 HRESULT hr = S_OK;
77 ValidateMessageArgs(hr, pvArgs, BAENGINE_FORMATSTRING_ARGS, pArgs);
78 ValidateMessageResults(hr, pvResults, BAENGINE_FORMATSTRING_RESULTS, pResults);
79
80 hr = ExternalEngineFormatString(pContext->pEngineState, pArgs->wzIn, pResults->wzOut, &pResults->cchOut);
81
82LExit:
83 return hr;
84}
85
86static HRESULT BAEngineEscapeString(
87 __in BOOTSTRAPPER_ENGINE_CONTEXT* /*pContext*/,
88 __in const LPVOID pvArgs,
89 __inout LPVOID pvResults
90 )
91{
92 HRESULT hr = S_OK;
93 ValidateMessageArgs(hr, pvArgs, BAENGINE_ESCAPESTRING_ARGS, pArgs);
94 ValidateMessageResults(hr, pvResults, BAENGINE_ESCAPESTRING_RESULTS, pResults);
95
96 hr = ExternalEngineEscapeString(pArgs->wzIn, pResults->wzOut, &pResults->cchOut);
97
98LExit:
99 return hr;
100}
101
102static HRESULT BAEngineEvaluateCondition(
103 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
104 __in const LPVOID pvArgs,
105 __inout LPVOID pvResults
106 )
107{
108 HRESULT hr = S_OK;
109 ValidateMessageArgs(hr, pvArgs, BAENGINE_EVALUATECONDITION_ARGS, pArgs);
110 ValidateMessageResults(hr, pvResults, BAENGINE_EVALUATECONDITION_RESULTS, pResults);
111
112 hr = ExternalEngineEvaluateCondition(pContext->pEngineState, pArgs->wzCondition, &pResults->f);
113
114LExit:
115 return hr;
116}
117
118static HRESULT BAEngineLog(
119 __in BOOTSTRAPPER_ENGINE_CONTEXT* /*pContext*/,
120 __in const LPVOID pvArgs,
121 __inout LPVOID pvResults
122 )
123{
124 HRESULT hr = S_OK;
125 REPORT_LEVEL rl = REPORT_NONE;
126 ValidateMessageArgs(hr, pvArgs, BAENGINE_LOG_ARGS, pArgs);
127 ValidateMessageResults(hr, pvResults, BAENGINE_LOG_RESULTS, pResults);
128
129 switch (pArgs->level)
130 {
131 case BOOTSTRAPPER_LOG_LEVEL_STANDARD:
132 rl = REPORT_STANDARD;
133 break;
134
135 case BOOTSTRAPPER_LOG_LEVEL_VERBOSE:
136 rl = REPORT_VERBOSE;
137 break;
138
139 case BOOTSTRAPPER_LOG_LEVEL_DEBUG:
140 rl = REPORT_DEBUG;
141 break;
142
143 case BOOTSTRAPPER_LOG_LEVEL_ERROR:
144 rl = REPORT_ERROR;
145 break;
146
147 default:
148 ExitFunction1(hr = E_INVALIDARG);
149 }
150
151 hr = ExternalEngineLog(rl, pArgs->wzMessage);
152 ExitOnFailure(hr, "Failed to log BA message.");
153
154LExit:
155 return hr;
156}
157
158static HRESULT BAEngineSendEmbeddedError(
159 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
160 __in const LPVOID pvArgs,
161 __inout LPVOID pvResults
162 )
163{
164 HRESULT hr = S_OK;
165 ValidateMessageArgs(hr, pvArgs, BAENGINE_SENDEMBEDDEDERROR_ARGS, pArgs);
166 ValidateMessageResults(hr, pvResults, BAENGINE_SENDEMBEDDEDERROR_RESULTS, pResults);
167
168 hr = ExternalEngineSendEmbeddedError(pContext->pEngineState, pArgs->dwErrorCode, pArgs->wzMessage, pArgs->dwUIHint, &pResults->nResult);
169
170LExit:
171 return hr;
172}
173
174static HRESULT BAEngineSendEmbeddedProgress(
175 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
176 __in const LPVOID pvArgs,
177 __inout LPVOID pvResults
178 )
179{
180 HRESULT hr = S_OK;
181 ValidateMessageArgs(hr, pvArgs, BAENGINE_SENDEMBEDDEDPROGRESS_ARGS, pArgs);
182 ValidateMessageResults(hr, pvResults, BAENGINE_SENDEMBEDDEDPROGRESS_RESULTS, pResults);
183
184 hr = ExternalEngineSendEmbeddedProgress(pContext->pEngineState, pArgs->dwProgressPercentage, pArgs->dwOverallProgressPercentage, &pResults->nResult);
185
186LExit:
187 return hr;
188}
189
190static HRESULT BAEngineSetUpdate(
191 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
192 __in const LPVOID pvArgs,
193 __inout LPVOID pvResults
194 )
195{
196 HRESULT hr = S_OK;
197 ValidateMessageArgs(hr, pvArgs, BAENGINE_SETUPDATE_ARGS, pArgs);
198 ValidateMessageResults(hr, pvResults, BAENGINE_SETUPDATE_RESULTS, pResults);
199
200 hr = ExternalEngineSetUpdate(pContext->pEngineState, pArgs->wzLocalSource, pArgs->wzDownloadSource, pArgs->qwSize, pArgs->hashType, pArgs->rgbHash, pArgs->cbHash);
201
202LExit:
203 return hr;
204}
205
206static HRESULT BAEngineSetLocalSource(
207 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
208 __in const LPVOID pvArgs,
209 __inout LPVOID pvResults
210 )
211{
212 HRESULT hr = S_OK;
213 ValidateMessageArgs(hr, pvArgs, BAENGINE_SETLOCALSOURCE_ARGS, pArgs);
214 ValidateMessageResults(hr, pvResults, BAENGINE_SETLOCALSOURCE_RESULTS, pResults);
215
216 hr = ExternalEngineSetLocalSource(pContext->pEngineState, pArgs->wzPackageOrContainerId, pArgs->wzPayloadId, pArgs->wzPath);
217
218LExit:
219 return hr;
220}
221
222static HRESULT BAEngineSetDownloadSource(
223 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
224 __in const LPVOID pvArgs,
225 __inout LPVOID pvResults
226 )
227{
228 HRESULT hr = S_OK;
229 ValidateMessageArgs(hr, pvArgs, BAENGINE_SETDOWNLOADSOURCE_ARGS, pArgs);
230 ValidateMessageResults(hr, pvResults, BAENGINE_SETDOWNLOADSOURCE_RESULTS, pResults);
231
232 hr = ExternalEngineSetDownloadSource(pContext->pEngineState, pArgs->wzPackageOrContainerId, pArgs->wzPayloadId, pArgs->wzUrl, pArgs->wzUser, pArgs->wzPassword);
233
234LExit:
235 return hr;
236}
237
238static HRESULT BAEngineSetVariableNumeric(
239 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
240 __in const LPVOID pvArgs,
241 __inout LPVOID pvResults
242 )
243{
244 HRESULT hr = S_OK;
245 ValidateMessageArgs(hr, pvArgs, BAENGINE_SETVARIABLENUMERIC_ARGS, pArgs);
246 ValidateMessageResults(hr, pvResults, BAENGINE_SETVARIABLENUMERIC_RESULTS, pResults);
247
248 hr = ExternalEngineSetVariableNumeric(pContext->pEngineState, pArgs->wzVariable, pArgs->llValue);
249
250LExit:
251 return hr;
252}
253
254static HRESULT BAEngineSetVariableString(
255 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
256 __in const LPVOID pvArgs,
257 __inout LPVOID pvResults
258 )
259{
260 HRESULT hr = S_OK;
261 ValidateMessageArgs(hr, pvArgs, BAENGINE_SETVARIABLESTRING_ARGS, pArgs);
262 ValidateMessageResults(hr, pvResults, BAENGINE_SETVARIABLESTRING_RESULTS, pResults);
263
264 hr = ExternalEngineSetVariableString(pContext->pEngineState, pArgs->wzVariable, pArgs->wzValue, pArgs->fFormatted);
265
266LExit:
267 return hr;
268}
269
270static HRESULT BAEngineSetVariableVersion(
271 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
272 __in const LPVOID pvArgs,
273 __inout LPVOID pvResults
274 )
275{
276 HRESULT hr = S_OK;
277 ValidateMessageArgs(hr, pvArgs, BAENGINE_SETVARIABLEVERSION_ARGS, pArgs);
278 ValidateMessageResults(hr, pvResults, BAENGINE_SETVARIABLEVERSION_RESULTS, pResults);
279
280 hr = ExternalEngineSetVariableVersion(pContext->pEngineState, pArgs->wzVariable, pArgs->wzValue);
281
282LExit:
283 return hr;
284}
285
286static HRESULT BAEngineCloseSplashScreen(
287 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
288 __in const LPVOID pvArgs,
289 __inout LPVOID pvResults
290 )
291{
292 HRESULT hr = S_OK;
293 ValidateMessageArgs(hr, pvArgs, BAENGINE_CLOSESPLASHSCREEN_ARGS, pArgs);
294 ValidateMessageResults(hr, pvResults, BAENGINE_CLOSESPLASHSCREEN_RESULTS, pResults);
295
296 ExternalEngineCloseSplashScreen(pContext->pEngineState);
297
298LExit:
299 return hr;
300}
301
302static HRESULT BAEngineCompareVersions(
303 __in BOOTSTRAPPER_ENGINE_CONTEXT* /*pContext*/,
304 __in const LPVOID pvArgs,
305 __inout LPVOID pvResults
306 )
307{
308 HRESULT hr = S_OK;
309 ValidateMessageArgs(hr, pvArgs, BAENGINE_COMPAREVERSIONS_ARGS, pArgs);
310 ValidateMessageResults(hr, pvResults, BAENGINE_COMPAREVERSIONS_RESULTS, pResults);
311
312 hr = ExternalEngineCompareVersions(pArgs->wzVersion1, pArgs->wzVersion2, &pResults->nResult);
313
314LExit:
315 return hr;
316}
317
318static HRESULT BAEngineDetect(
319 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
320 __in const LPVOID pvArgs,
321 __inout LPVOID pvResults
322 )
323{
324 HRESULT hr = S_OK;
325 ValidateMessageArgs(hr, pvArgs, BAENGINE_DETECT_ARGS, pArgs);
326 ValidateMessageResults(hr, pvResults, BAENGINE_DETECT_RESULTS, pResults);
327
328 hr = ExternalEngineDetect(pContext->dwThreadId, pArgs->hwndParent);
329
330LExit:
331 return hr;
332}
333
334static HRESULT BAEnginePlan(
335 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
336 __in const LPVOID pvArgs,
337 __inout LPVOID pvResults
338 )
339{
340 HRESULT hr = S_OK;
341 ValidateMessageArgs(hr, pvArgs, BAENGINE_PLAN_ARGS, pArgs);
342 ValidateMessageResults(hr, pvResults, BAENGINE_PLAN_RESULTS, pResults);
343
344 hr = ExternalEnginePlan(pContext->dwThreadId, pArgs->action);
345
346LExit:
347 return hr;
348}
349
350static HRESULT BAEngineElevate(
351 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
352 __in const LPVOID pvArgs,
353 __inout LPVOID pvResults
354 )
355{
356 HRESULT hr = S_OK;
357 ValidateMessageArgs(hr, pvArgs, BAENGINE_ELEVATE_ARGS, pArgs);
358 ValidateMessageResults(hr, pvResults, BAENGINE_ELEVATE_RESULTS, pResults);
359
360 hr = ExternalEngineElevate(pContext->pEngineState, pContext->dwThreadId, pArgs->hwndParent);
361
362LExit:
363 return hr;
364}
365
366static HRESULT BAEngineApply(
367 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
368 __in const LPVOID pvArgs,
369 __inout LPVOID pvResults
370 )
371{
372 HRESULT hr = S_OK;
373 ValidateMessageArgs(hr, pvArgs, BAENGINE_APPLY_ARGS, pArgs);
374 ValidateMessageResults(hr, pvResults, BAENGINE_APPLY_RESULTS, pResults);
375
376 hr = ExternalEngineApply(pContext->dwThreadId, pArgs->hwndParent);
377
378LExit:
379 return hr;
380}
381
382static HRESULT BAEngineQuit(
383 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
384 __in const LPVOID pvArgs,
385 __inout LPVOID pvResults
386 )
387{
388 HRESULT hr = S_OK;
389 ValidateMessageArgs(hr, pvArgs, BAENGINE_QUIT_ARGS, pArgs);
390 ValidateMessageResults(hr, pvResults, BAENGINE_QUIT_RESULTS, pResults);
391
392 hr = ExternalEngineQuit(pContext->dwThreadId, pArgs->dwExitCode);
393
394LExit:
395 return hr;
396}
397
398static HRESULT BAEngineLaunchApprovedExe(
399 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
400 __in const LPVOID pvArgs,
401 __inout LPVOID pvResults
402 )
403{
404 HRESULT hr = S_OK;
405 ValidateMessageArgs(hr, pvArgs, BAENGINE_LAUNCHAPPROVEDEXE_ARGS, pArgs);
406 ValidateMessageResults(hr, pvResults, BAENGINE_LAUNCHAPPROVEDEXE_RESULTS, pResults);
407
408 hr = ExternalEngineLaunchApprovedExe(pContext->pEngineState, pContext->dwThreadId, pArgs->hwndParent, pArgs->wzApprovedExeForElevationId, pArgs->wzArguments, pArgs->dwWaitForInputIdleTimeout);
409
410LExit:
411 return hr;
412}
413
414static HRESULT BAEngineSetUpdateSource(
415 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
416 __in const LPVOID pvArgs,
417 __inout LPVOID pvResults
418 )
419{
420 HRESULT hr = S_OK;
421 ValidateMessageArgs(hr, pvArgs, BAENGINE_SETUPDATESOURCE_ARGS, pArgs);
422 ValidateMessageResults(hr, pvResults, BAENGINE_SETUPDATESOURCE_RESULTS, pResults);
423
424 hr = ExternalEngineSetUpdateSource(pContext->pEngineState, pArgs->wzUrl);
425
426LExit:
427 return hr;
428}
429
430HRESULT WINAPI EngineForApplicationProc(
431 __in BOOTSTRAPPER_ENGINE_MESSAGE message,
432 __in const LPVOID pvArgs,
433 __inout LPVOID pvResults,
434 __in_opt LPVOID pvContext
435 )
436{
437 HRESULT hr = S_OK;
438 BOOTSTRAPPER_ENGINE_CONTEXT* pContext = reinterpret_cast<BOOTSTRAPPER_ENGINE_CONTEXT*>(pvContext);
439
440 if (!pContext || !pvArgs || !pvResults)
441 {
442 ExitFunction1(hr = E_INVALIDARG);
443 }
444
445 switch (message)
446 {
447 case BOOTSTRAPPER_ENGINE_MESSAGE_GETPACKAGECOUNT:
448 hr = BAEngineGetPackageCount(pContext, pvArgs, pvResults);
449 break;
450 case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLENUMERIC:
451 hr = BAEngineGetVariableNumeric(pContext, pvArgs, pvResults);
452 break;
453 case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLESTRING:
454 hr = BAEngineGetVariableString(pContext, pvArgs, pvResults);
455 break;
456 case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLEVERSION:
457 hr = BAEngineGetVariableVersion(pContext, pvArgs, pvResults);
458 break;
459 case BOOTSTRAPPER_ENGINE_MESSAGE_FORMATSTRING:
460 hr = BAEngineFormatString(pContext, pvArgs, pvResults);
461 break;
462 case BOOTSTRAPPER_ENGINE_MESSAGE_ESCAPESTRING:
463 hr = BAEngineEscapeString(pContext, pvArgs, pvResults);
464 break;
465 case BOOTSTRAPPER_ENGINE_MESSAGE_EVALUATECONDITION:
466 hr = BAEngineEvaluateCondition(pContext, pvArgs, pvResults);
467 break;
468 case BOOTSTRAPPER_ENGINE_MESSAGE_LOG:
469 hr = BAEngineLog(pContext, pvArgs, pvResults);
470 break;
471 case BOOTSTRAPPER_ENGINE_MESSAGE_SENDEMBEDDEDERROR:
472 hr = BAEngineSendEmbeddedError(pContext, pvArgs, pvResults);
473 break;
474 case BOOTSTRAPPER_ENGINE_MESSAGE_SENDEMBEDDEDPROGRESS:
475 hr = BAEngineSendEmbeddedProgress(pContext, pvArgs, pvResults);
476 break;
477 case BOOTSTRAPPER_ENGINE_MESSAGE_SETUPDATE:
478 hr = BAEngineSetUpdate(pContext, pvArgs, pvResults);
479 break;
480 case BOOTSTRAPPER_ENGINE_MESSAGE_SETLOCALSOURCE:
481 hr = BAEngineSetLocalSource(pContext, pvArgs, pvResults);
482 break;
483 case BOOTSTRAPPER_ENGINE_MESSAGE_SETDOWNLOADSOURCE:
484 hr = BAEngineSetDownloadSource(pContext, pvArgs, pvResults);
485 break;
486 case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLENUMERIC:
487 hr = BAEngineSetVariableNumeric(pContext, pvArgs, pvResults);
488 break;
489 case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLESTRING:
490 hr = BAEngineSetVariableString(pContext, pvArgs, pvResults);
491 break;
492 case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLEVERSION:
493 hr = BAEngineSetVariableVersion(pContext, pvArgs, pvResults);
494 break;
495 case BOOTSTRAPPER_ENGINE_MESSAGE_CLOSESPLASHSCREEN:
496 hr = BAEngineCloseSplashScreen(pContext, pvArgs, pvResults);
497 break;
498 case BOOTSTRAPPER_ENGINE_MESSAGE_DETECT:
499 hr = BAEngineDetect(pContext, pvArgs, pvResults);
500 break;
501 case BOOTSTRAPPER_ENGINE_MESSAGE_PLAN:
502 hr = BAEnginePlan(pContext, pvArgs, pvResults);
503 break;
504 case BOOTSTRAPPER_ENGINE_MESSAGE_ELEVATE:
505 hr = BAEngineElevate(pContext, pvArgs, pvResults);
506 break;
507 case BOOTSTRAPPER_ENGINE_MESSAGE_APPLY:
508 hr = BAEngineApply(pContext, pvArgs, pvResults);
509 break;
510 case BOOTSTRAPPER_ENGINE_MESSAGE_QUIT:
511 hr = BAEngineQuit(pContext, pvArgs, pvResults);
512 break;
513 case BOOTSTRAPPER_ENGINE_MESSAGE_LAUNCHAPPROVEDEXE:
514 hr = BAEngineLaunchApprovedExe(pContext, pvArgs, pvResults);
515 break;
516 case BOOTSTRAPPER_ENGINE_MESSAGE_SETUPDATESOURCE:
517 hr = BAEngineSetUpdateSource(pContext, pvArgs, pvResults);
518 break;
519 case BOOTSTRAPPER_ENGINE_MESSAGE_COMPAREVERSIONS:
520 hr = BAEngineCompareVersions(pContext, pvArgs, pvResults);
521 break;
522 default:
523 hr = E_NOTIMPL;
524 break;
525 }
526
527LExit:
528 return hr;
529}
diff --git a/src/burn/engine/EngineForApplication.h b/src/burn/engine/EngineForApplication.h
new file mode 100644
index 00000000..e5e8f6d7
--- /dev/null
+++ b/src/burn/engine/EngineForApplication.h
@@ -0,0 +1,44 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9// constants
10
11enum WM_BURN
12{
13 WM_BURN_FIRST = WM_APP + 0xFFF, // this enum value must always be first.
14
15 WM_BURN_DETECT,
16 WM_BURN_PLAN,
17 WM_BURN_ELEVATE,
18 WM_BURN_APPLY,
19 WM_BURN_LAUNCH_APPROVED_EXE,
20 WM_BURN_QUIT,
21
22 WM_BURN_LAST, // this enum value must always be last.
23};
24
25// structs
26
27typedef struct _BOOTSTRAPPER_ENGINE_CONTEXT
28{
29 BURN_ENGINE_STATE* pEngineState;
30 DWORD dwThreadId;
31} BOOTSTRAPPER_ENGINE_CONTEXT;
32
33// function declarations
34
35HRESULT WINAPI EngineForApplicationProc(
36 __in BOOTSTRAPPER_ENGINE_MESSAGE message,
37 __in const LPVOID pvArgs,
38 __inout LPVOID pvResults,
39 __in_opt LPVOID pvContext
40 );
41
42#if defined(__cplusplus)
43}
44#endif
diff --git a/src/burn/engine/EngineForExtension.cpp b/src/burn/engine/EngineForExtension.cpp
new file mode 100644
index 00000000..2e1c98fd
--- /dev/null
+++ b/src/burn/engine/EngineForExtension.cpp
@@ -0,0 +1,263 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6static HRESULT BEEngineEscapeString(
7 __in BURN_EXTENSION_ENGINE_CONTEXT* /*pContext*/,
8 __in const LPVOID pvArgs,
9 __inout LPVOID pvResults
10 )
11{
12 HRESULT hr = S_OK;
13 ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_ARGS, pArgs);
14 ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_ESCAPESTRING_RESULTS, pResults);
15
16 hr = ExternalEngineEscapeString(pArgs->wzIn, pResults->wzOut, &pResults->cchOut);
17
18LExit:
19 return hr;
20}
21
22static HRESULT BEEngineEvaluateCondition(
23 __in BURN_EXTENSION_ENGINE_CONTEXT* pContext,
24 __in const LPVOID pvArgs,
25 __inout LPVOID pvResults
26 )
27{
28 HRESULT hr = S_OK;
29 ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_ARGS, pArgs);
30 ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_EVALUATECONDITION_RESULTS, pResults);
31
32 hr = ExternalEngineEvaluateCondition(pContext->pEngineState, pArgs->wzCondition, &pResults->f);
33
34LExit:
35 return hr;
36}
37
38static HRESULT BEEngineFormatString(
39 __in BURN_EXTENSION_ENGINE_CONTEXT* pContext,
40 __in const LPVOID pvArgs,
41 __inout LPVOID pvResults
42 )
43{
44 HRESULT hr = S_OK;
45 ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_FORMATSTRING_ARGS, pArgs);
46 ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_FORMATSTRING_RESULTS, pResults);
47
48 hr = ExternalEngineFormatString(pContext->pEngineState, pArgs->wzIn, pResults->wzOut, &pResults->cchOut);
49
50LExit:
51 return hr;
52}
53
54static HRESULT BEEngineGetVariableNumeric(
55 __in BURN_EXTENSION_ENGINE_CONTEXT* pContext,
56 __in const LPVOID pvArgs,
57 __inout LPVOID pvResults
58 )
59{
60 HRESULT hr = S_OK;
61 ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_ARGS, pArgs);
62 ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_GETVARIABLENUMERIC_RESULTS, pResults);
63
64 hr = ExternalEngineGetVariableNumeric(pContext->pEngineState, pArgs->wzVariable, &pResults->llValue);
65
66LExit:
67 return hr;
68}
69
70static HRESULT BEEngineGetVariableString(
71 __in BURN_EXTENSION_ENGINE_CONTEXT* pContext,
72 __in const LPVOID pvArgs,
73 __inout LPVOID pvResults
74 )
75{
76 HRESULT hr = S_OK;
77 ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_ARGS, pArgs);
78 ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_GETVARIABLESTRING_RESULTS, pResults);
79
80 hr = ExternalEngineGetVariableString(pContext->pEngineState, pArgs->wzVariable, pResults->wzValue, &pResults->cchValue);
81
82LExit:
83 return hr;
84}
85
86static HRESULT BEEngineGetVariableVersion(
87 __in BURN_EXTENSION_ENGINE_CONTEXT* pContext,
88 __in const LPVOID pvArgs,
89 __inout LPVOID pvResults
90 )
91{
92 HRESULT hr = S_OK;
93 ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_ARGS, pArgs);
94 ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_GETVARIABLEVERSION_RESULTS, pResults);
95
96 hr = ExternalEngineGetVariableVersion(pContext->pEngineState, pArgs->wzVariable, pResults->wzValue, &pResults->cchValue);
97
98LExit:
99 return hr;
100}
101
102static HRESULT BEEngineLog(
103 __in BURN_EXTENSION_ENGINE_CONTEXT* /*pContext*/,
104 __in const LPVOID pvArgs,
105 __inout LPVOID pvResults
106 )
107{
108 HRESULT hr = S_OK;
109 REPORT_LEVEL rl = REPORT_NONE;
110 ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_LOG_ARGS, pArgs);
111 ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_LOG_RESULTS, pResults);
112
113 switch (pArgs->level)
114 {
115 case BUNDLE_EXTENSION_LOG_LEVEL_STANDARD:
116 rl = REPORT_STANDARD;
117 break;
118
119 case BUNDLE_EXTENSION_LOG_LEVEL_VERBOSE:
120 rl = REPORT_VERBOSE;
121 break;
122
123 case BUNDLE_EXTENSION_LOG_LEVEL_DEBUG:
124 rl = REPORT_DEBUG;
125 break;
126
127 case BUNDLE_EXTENSION_LOG_LEVEL_ERROR:
128 rl = REPORT_ERROR;
129 break;
130
131 default:
132 ExitFunction1(hr = E_INVALIDARG);
133 }
134
135 hr = ExternalEngineLog(rl, pArgs->wzMessage);
136 ExitOnFailure(hr, "Failed to log Bundle Extension message.");
137
138LExit:
139 return hr;
140}
141
142static HRESULT BEEngineSetVariableNumeric(
143 __in BURN_EXTENSION_ENGINE_CONTEXT* pContext,
144 __in const LPVOID pvArgs,
145 __inout LPVOID pvResults
146 )
147{
148 HRESULT hr = S_OK;
149 ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_ARGS, pArgs);
150 ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_RESULTS, pResults);
151
152 hr = ExternalEngineSetVariableNumeric(pContext->pEngineState, pArgs->wzVariable, pArgs->llValue);
153
154LExit:
155 return hr;
156}
157
158static HRESULT BEEngineSetVariableString(
159 __in BURN_EXTENSION_ENGINE_CONTEXT* pContext,
160 __in const LPVOID pvArgs,
161 __inout LPVOID pvResults
162 )
163{
164 HRESULT hr = S_OK;
165 ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_ARGS, pArgs);
166 ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_RESULTS, pResults);
167
168 hr = ExternalEngineSetVariableString(pContext->pEngineState, pArgs->wzVariable, pArgs->wzValue, pArgs->fFormatted);
169
170LExit:
171 return hr;
172}
173
174static HRESULT BEEngineSetVariableVersion(
175 __in BURN_EXTENSION_ENGINE_CONTEXT* pContext,
176 __in const LPVOID pvArgs,
177 __inout LPVOID pvResults
178 )
179{
180 HRESULT hr = S_OK;
181 ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_ARGS, pArgs);
182 ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_SETVARIABLEVERSION_RESULTS, pResults);
183
184 hr = ExternalEngineSetVariableVersion(pContext->pEngineState, pArgs->wzVariable, pArgs->wzValue);
185
186LExit:
187 return hr;
188}
189
190static HRESULT BEEngineCompareVersions(
191 __in BURN_EXTENSION_ENGINE_CONTEXT* /*pContext*/,
192 __in const LPVOID pvArgs,
193 __inout LPVOID pvResults
194 )
195{
196 HRESULT hr = S_OK;
197 ValidateMessageArgs(hr, pvArgs, BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_ARGS, pArgs);
198 ValidateMessageResults(hr, pvResults, BUNDLE_EXTENSION_ENGINE_COMPAREVERSIONS_RESULTS, pResults);
199
200 hr = ExternalEngineCompareVersions(pArgs->wzVersion1, pArgs->wzVersion2, &pResults->nResult);
201
202LExit:
203 return hr;
204}
205
206HRESULT WINAPI EngineForExtensionProc(
207 __in BUNDLE_EXTENSION_ENGINE_MESSAGE message,
208 __in const LPVOID pvArgs,
209 __inout LPVOID pvResults,
210 __in_opt LPVOID pvContext
211 )
212{
213 HRESULT hr = S_OK;
214 BURN_EXTENSION_ENGINE_CONTEXT* pContext = reinterpret_cast<BURN_EXTENSION_ENGINE_CONTEXT*>(pvContext);
215
216 if (!pContext || !pvArgs || !pvResults)
217 {
218 ExitFunction1(hr = E_INVALIDARG);
219 }
220
221 switch (message)
222 {
223 case BUNDLE_EXTENSION_ENGINE_MESSAGE_ESCAPESTRING:
224 hr = BEEngineEscapeString(pContext, pvArgs, pvResults);
225 break;
226 case BUNDLE_EXTENSION_ENGINE_MESSAGE_EVALUATECONDITION:
227 hr = BEEngineEvaluateCondition(pContext, pvArgs, pvResults);
228 break;
229 case BUNDLE_EXTENSION_ENGINE_MESSAGE_FORMATSTRING:
230 hr = BEEngineFormatString(pContext, pvArgs, pvResults);
231 break;
232 case BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLENUMERIC:
233 hr = BEEngineGetVariableNumeric(pContext, pvArgs, pvResults);
234 break;
235 case BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLESTRING:
236 hr = BEEngineGetVariableString(pContext, pvArgs, pvResults);
237 break;
238 case BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLEVERSION:
239 hr = BEEngineGetVariableVersion(pContext, pvArgs, pvResults);
240 break;
241 case BUNDLE_EXTENSION_ENGINE_MESSAGE_LOG:
242 hr = BEEngineLog(pContext, pvArgs, pvResults);
243 break;
244 case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLENUMERIC:
245 hr = BEEngineSetVariableNumeric(pContext, pvArgs, pvResults);
246 break;
247 case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLESTRING:
248 hr = BEEngineSetVariableString(pContext, pvArgs, pvResults);
249 break;
250 case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLEVERSION:
251 hr = BEEngineSetVariableVersion(pContext, pvArgs, pvResults);
252 break;
253 case BUNDLE_EXTENSION_ENGINE_MESSAGE_COMPAREVERSIONS:
254 hr = BEEngineCompareVersions(pContext, pvArgs, pvResults);
255 break;
256 default:
257 hr = E_NOTIMPL;
258 break;
259 }
260
261LExit:
262 return hr;
263}
diff --git a/src/burn/engine/EngineForExtension.h b/src/burn/engine/EngineForExtension.h
new file mode 100644
index 00000000..bad5f08a
--- /dev/null
+++ b/src/burn/engine/EngineForExtension.h
@@ -0,0 +1,27 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9// structs
10
11typedef struct _BURN_EXTENSION_ENGINE_CONTEXT
12{
13 BURN_ENGINE_STATE* pEngineState;
14} BURN_EXTENSION_ENGINE_CONTEXT;
15
16// function declarations
17
18HRESULT WINAPI EngineForExtensionProc(
19 __in BUNDLE_EXTENSION_ENGINE_MESSAGE message,
20 __in const LPVOID pvArgs,
21 __inout LPVOID pvResults,
22 __in_opt LPVOID pvContext
23 );
24
25#if defined(__cplusplus)
26}
27#endif
diff --git a/src/burn/engine/apply.cpp b/src/burn/engine/apply.cpp
new file mode 100644
index 00000000..58d41b28
--- /dev/null
+++ b/src/burn/engine/apply.cpp
@@ -0,0 +1,3096 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6#ifdef DEBUG
7 #define IgnoreRollbackError(x, f, ...) if (FAILED(x)) { TraceError(x, f, __VA_ARGS__); }
8#else
9 #define IgnoreRollbackError(x, f, ...)
10#endif
11
12const DWORD BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS = 2;
13
14enum BURN_CACHE_PROGRESS_TYPE
15{
16 BURN_CACHE_PROGRESS_TYPE_ACQUIRE,
17 BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY,
18 BURN_CACHE_PROGRESS_TYPE_EXTRACT,
19 BURN_CACHE_PROGRESS_TYPE_FINALIZE,
20 BURN_CACHE_PROGRESS_TYPE_HASH,
21 BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY,
22 BURN_CACHE_PROGRESS_TYPE_STAGE,
23};
24
25// structs
26
27typedef struct _BURN_CACHE_CONTEXT
28{
29 BURN_USER_EXPERIENCE* pUX;
30 BURN_VARIABLES* pVariables;
31 BURN_PAYLOADS* pPayloads;
32 HANDLE hPipe;
33 HANDLE hSourceEngineFile;
34 DWORD64 qwTotalCacheSize;
35 DWORD64 qwSuccessfulCacheProgress;
36 LPCWSTR wzLayoutDirectory;
37 LPWSTR* rgSearchPaths;
38 DWORD cSearchPaths;
39 DWORD cSearchPathsMax;
40 LPWSTR sczLastUsedFolderCandidate;
41} BURN_CACHE_CONTEXT;
42
43typedef struct _BURN_CACHE_PROGRESS_CONTEXT
44{
45 BURN_CACHE_CONTEXT* pCacheContext;
46 BURN_CACHE_PROGRESS_TYPE type;
47 BURN_CONTAINER* pContainer;
48 BURN_PACKAGE* pPackage;
49 BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem;
50 BURN_PAYLOAD* pPayload;
51
52 BOOL fCancel;
53 HRESULT hrError;
54} BURN_CACHE_PROGRESS_CONTEXT;
55
56typedef struct _BURN_EXECUTE_CONTEXT
57{
58 BURN_USER_EXPERIENCE* pUX;
59 BOOL fRollback;
60 BURN_PACKAGE* pExecutingPackage;
61 DWORD cExecutedPackages;
62 DWORD cExecutePackagesTotal;
63 DWORD* pcOverallProgressTicks;
64} BURN_EXECUTE_CONTEXT;
65
66
67// internal function declarations
68static HRESULT WINAPI AuthenticationRequired(
69 __in LPVOID pData,
70 __in HINTERNET hUrl,
71 __in long lHttpCode,
72 __out BOOL* pfRetrySend,
73 __out BOOL* pfRetry
74 );
75
76static void CalculateKeepRegistration(
77 __in BURN_ENGINE_STATE* pEngineState,
78 __inout BOOL* pfKeepRegistration
79 );
80static HRESULT ExecuteDependentRegistrationActions(
81 __in HANDLE hPipe,
82 __in const BURN_REGISTRATION* pRegistration,
83 __in_ecount(cActions) const BURN_DEPENDENT_REGISTRATION_ACTION* rgActions,
84 __in DWORD cActions
85 );
86static HRESULT ApplyCachePackage(
87 __in BURN_CACHE_CONTEXT* pContext,
88 __in BURN_PACKAGE* pPackage
89 );
90static HRESULT ApplyExtractContainer(
91 __in BURN_CACHE_CONTEXT* pContext,
92 __in BURN_CONTAINER* pContainer
93 );
94static HRESULT ApplyLayoutBundle(
95 __in BURN_CACHE_CONTEXT* pContext,
96 __in BURN_PAYLOAD_GROUP* pPayloads,
97 __in_z LPCWSTR wzExecutableName,
98 __in_z LPCWSTR wzUnverifiedPath,
99 __in DWORD64 qwBundleSize
100 );
101static HRESULT ApplyLayoutContainer(
102 __in BURN_CACHE_CONTEXT* pContext,
103 __in BURN_CONTAINER* pContainer
104 );
105static HRESULT ApplyProcessPayload(
106 __in BURN_CACHE_CONTEXT* pContext,
107 __in_opt BURN_PACKAGE* pPackage,
108 __in BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem
109 );
110static HRESULT ApplyCacheVerifyContainerOrPayload(
111 __in BURN_CACHE_CONTEXT* pContext,
112 __in_opt BURN_CONTAINER* pContainer,
113 __in_opt BURN_PACKAGE* pPackage,
114 __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem
115 );
116static HRESULT ExtractContainer(
117 __in BURN_CACHE_CONTEXT* pContext,
118 __in BURN_CONTAINER* pContainer
119 );
120static HRESULT LayoutBundle(
121 __in BURN_CACHE_CONTEXT* pContext,
122 __in_z LPCWSTR wzExecutableName,
123 __in_z LPCWSTR wzUnverifiedPath,
124 __in DWORD64 qwBundleSize
125 );
126static HRESULT ApplyAcquireContainerOrPayload(
127 __in BURN_CACHE_CONTEXT* pContext,
128 __in_opt BURN_CONTAINER* pContainer,
129 __in_opt BURN_PACKAGE* pPackage,
130 __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem
131 );
132static HRESULT AcquireContainerOrPayload(
133 __in BURN_CACHE_PROGRESS_CONTEXT* pProgress,
134 __out BOOL* pfRetry
135 );
136static BOOL IsValidLocalFile(
137 __in_z LPCWSTR wzFilePath,
138 __in DWORD64 qwFileSize,
139 __in BOOL fMinimumFileSize
140 );
141static HRESULT LayoutOrCacheContainerOrPayload(
142 __in BURN_CACHE_CONTEXT* pContext,
143 __in_opt BURN_CONTAINER* pContainer,
144 __in_opt BURN_PACKAGE* pPackage,
145 __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem,
146 __in DWORD cTryAgainAttempts,
147 __out BOOL* pfRetry
148 );
149static HRESULT PreparePayloadDestinationPath(
150 __in_z LPCWSTR wzDestinationPath
151 );
152static HRESULT CopyPayload(
153 __in BURN_CACHE_PROGRESS_CONTEXT* pProgress,
154 __in HANDLE hSourceFile,
155 __in_z LPCWSTR wzSourcePath,
156 __in_z LPCWSTR wzDestinationPath
157 );
158static HRESULT DownloadPayload(
159 __in BURN_CACHE_PROGRESS_CONTEXT* pProgress,
160 __in_z LPCWSTR wzDestinationPath
161 );
162static HRESULT CALLBACK CacheMessageHandler(
163 __in BURN_CACHE_MESSAGE* pMessage,
164 __in LPVOID pvContext
165 );
166static HRESULT CompleteCacheProgress(
167 __in BURN_CACHE_PROGRESS_CONTEXT* pContext,
168 __in DWORD64 qwFileSize
169 );
170static DWORD CALLBACK CacheProgressRoutine(
171 __in LARGE_INTEGER TotalFileSize,
172 __in LARGE_INTEGER TotalBytesTransferred,
173 __in LARGE_INTEGER StreamSize,
174 __in LARGE_INTEGER StreamBytesTransferred,
175 __in DWORD dwStreamNumber,
176 __in DWORD dwCallbackReason,
177 __in HANDLE hSourceFile,
178 __in HANDLE hDestinationFile,
179 __in_opt LPVOID lpData
180 );
181static void DoRollbackCache(
182 __in BURN_USER_EXPERIENCE* pUX,
183 __in BURN_PLAN* pPlan,
184 __in HANDLE hPipe,
185 __in DWORD dwCheckpoint
186 );
187static HRESULT DoExecuteAction(
188 __in BURN_ENGINE_STATE* pEngineState,
189 __in BURN_EXECUTE_ACTION* pExecuteAction,
190 __in_opt HANDLE hCacheThread,
191 __in BURN_EXECUTE_CONTEXT* pContext,
192 __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary,
193 __inout BURN_EXECUTE_ACTION_CHECKPOINT** ppCheckpoint,
194 __out BOOL* pfSuspend,
195 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
196 );
197static HRESULT DoRollbackActions(
198 __in BURN_ENGINE_STATE* pEngineState,
199 __in BURN_EXECUTE_CONTEXT* pContext,
200 __in DWORD dwCheckpoint,
201 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
202 );
203static HRESULT ExecuteExePackage(
204 __in BURN_ENGINE_STATE* pEngineState,
205 __in BURN_EXECUTE_ACTION* pExecuteAction,
206 __in BURN_EXECUTE_CONTEXT* pContext,
207 __in BOOL fRollback,
208 __out BOOL* pfRetry,
209 __out BOOL* pfSuspend,
210 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
211 );
212static HRESULT ExecuteMsiPackage(
213 __in BURN_ENGINE_STATE* pEngineState,
214 __in BURN_EXECUTE_ACTION* pExecuteAction,
215 __in BURN_EXECUTE_CONTEXT* pContext,
216 __in BOOL fInsideMsiTransaction,
217 __in BOOL fRollback,
218 __out BOOL* pfRetry,
219 __out BOOL* pfSuspend,
220 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
221 );
222static HRESULT ExecuteMspPackage(
223 __in BURN_ENGINE_STATE* pEngineState,
224 __in BURN_EXECUTE_ACTION* pExecuteAction,
225 __in BURN_EXECUTE_CONTEXT* pContext,
226 __in BOOL fInsideMsiTransaction,
227 __in BOOL fRollback,
228 __out BOOL* pfRetry,
229 __out BOOL* pfSuspend,
230 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
231 );
232static HRESULT ExecuteMsuPackage(
233 __in BURN_ENGINE_STATE* pEngineState,
234 __in BURN_EXECUTE_ACTION* pExecuteAction,
235 __in BURN_EXECUTE_CONTEXT* pContext,
236 __in BOOL fRollback,
237 __in BOOL fStopWusaService,
238 __out BOOL* pfRetry,
239 __out BOOL* pfSuspend,
240 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
241 );
242static HRESULT ExecutePackageProviderAction(
243 __in BURN_ENGINE_STATE* pEngineState,
244 __in BURN_EXECUTE_ACTION* pAction,
245 __in BURN_EXECUTE_CONTEXT* pContext
246 );
247static HRESULT ExecuteDependencyAction(
248 __in BURN_ENGINE_STATE* pEngineState,
249 __in BURN_EXECUTE_ACTION* pAction,
250 __in BURN_EXECUTE_CONTEXT* pContext
251 );
252static HRESULT ExecuteMsiBeginTransaction(
253 __in BURN_ENGINE_STATE* pEngineState,
254 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary,
255 __in BURN_EXECUTE_CONTEXT* pContext
256 );
257static HRESULT ExecuteMsiCommitTransaction(
258 __in BURN_ENGINE_STATE* pEngineState,
259 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary,
260 __in BURN_EXECUTE_CONTEXT* pContext
261 );
262static HRESULT ExecuteMsiRollbackTransaction(
263 __in BURN_ENGINE_STATE* pEngineState,
264 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary,
265 __in BURN_EXECUTE_CONTEXT* pContext
266 );
267static void ResetTransactionRegistrationState(
268 __in BURN_ENGINE_STATE* pEngineState,
269 __in BOOL fCommit
270 );
271static HRESULT CleanPackage(
272 __in HANDLE hElevatedPipe,
273 __in BURN_PACKAGE* pPackage
274 );
275static int GenericExecuteMessageHandler(
276 __in GENERIC_EXECUTE_MESSAGE* pMessage,
277 __in LPVOID pvContext
278 );
279static int MsiExecuteMessageHandler(
280 __in WIU_MSI_EXECUTE_MESSAGE* pMessage,
281 __in_opt LPVOID pvContext
282 );
283static HRESULT ReportOverallProgressTicks(
284 __in BURN_USER_EXPERIENCE* pUX,
285 __in BOOL fRollback,
286 __in DWORD cOverallProgressTicksTotal,
287 __in DWORD cOverallProgressTicks
288 );
289static HRESULT ExecutePackageComplete(
290 __in BURN_USER_EXPERIENCE* pUX,
291 __in BURN_VARIABLES* pVariables,
292 __in BURN_PACKAGE* pPackage,
293 __in HRESULT hrOverall,
294 __in HRESULT hrExecute,
295 __in BOOL fRollback,
296 __out BOOTSTRAPPER_APPLY_RESTART* pRestart,
297 __out BOOL* pfRetry,
298 __out BOOL* pfSuspend
299 );
300
301
302// function definitions
303
304extern "C" void ApplyInitialize()
305{
306 // Prevent the system from sleeping.
307 ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
308}
309
310extern "C" void ApplyUninitialize()
311{
312 ::SetThreadExecutionState(ES_CONTINUOUS);
313}
314
315extern "C" HRESULT ApplySetVariables(
316 __in BURN_VARIABLES* pVariables
317 )
318{
319 HRESULT hr = S_OK;
320
321 hr = VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, NULL, TRUE, FALSE);
322 ExitOnFailure(hr, "Failed to set the bundle forced restart package built-in variable.");
323
324LExit:
325 return hr;
326}
327
328extern "C" void ApplyReset(
329 __in BURN_USER_EXPERIENCE* pUX,
330 __in BURN_PACKAGES* pPackages
331 )
332{
333 UserExperienceExecuteReset(pUX);
334
335 for (DWORD i = 0; i < pPackages->cPackages; ++i)
336 {
337 BURN_PACKAGE* pPackage = pPackages->rgPackages + i;
338 pPackage->hrCacheResult = S_OK;
339 pPackage->transactionRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
340 }
341}
342
343extern "C" HRESULT ApplyLock(
344 __in BOOL /*fPerMachine*/,
345 __out HANDLE* phLock
346 )
347{
348 HRESULT hr = S_OK;
349 *phLock = NULL;
350
351#if 0 // eventually figure out the correct way to support this. In its current form, embedded bundles (including related bundles) are hosed.
352 DWORD er = ERROR_SUCCESS;
353 HANDLE hLock = NULL;
354
355 hLock = ::CreateMutexW(NULL, TRUE, fPerMachine ? L"Global\\WixBurnExecutionLock" : L"Local\\WixBurnExecutionLock");
356 ExitOnNullWithLastError(hLock, hr, "Failed to create lock.");
357
358 er = ::GetLastError();
359 if (ERROR_ALREADY_EXISTS == er)
360 {
361 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSTALL_ALREADY_RUNNING));
362 }
363
364 *phLock = hLock;
365 hLock = NULL;
366
367LExit:
368 ReleaseHandle(hLock);
369#endif
370 return hr;
371}
372
373extern "C" HRESULT ApplyRegister(
374 __in BURN_ENGINE_STATE* pEngineState
375 )
376{
377 HRESULT hr = S_OK;
378 LPWSTR sczEngineWorkingPath = NULL;
379
380 hr = UserExperienceOnRegisterBegin(&pEngineState->userExperience);
381 ExitOnRootFailure(hr, "BA aborted register begin.");
382
383 // If we have a resume mode that suggests the bundle is on the machine.
384 if (BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING < pEngineState->command.resumeType)
385 {
386 // resume previous session
387 if (pEngineState->registration.fPerMachine)
388 {
389 hr = ElevationSessionResume(pEngineState->companionConnection.hPipe, pEngineState->registration.sczResumeCommandLine, pEngineState->registration.fDisableResume, &pEngineState->variables);
390 ExitOnFailure(hr, "Failed to resume registration session in per-machine process.");
391 }
392 else
393 {
394 hr = RegistrationSessionResume(&pEngineState->registration, &pEngineState->variables);
395 ExitOnFailure(hr, "Failed to resume registration session.");
396 }
397 }
398 else // need to complete registration on the machine.
399 {
400 hr = CacheCalculateBundleWorkingPath(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &sczEngineWorkingPath);
401 ExitOnFailure(hr, "Failed to calculate working path for engine.");
402
403 // begin new session
404 if (pEngineState->registration.fPerMachine)
405 {
406 hr = ElevationSessionBegin(pEngineState->companionConnection.hPipe, sczEngineWorkingPath, pEngineState->registration.sczResumeCommandLine, pEngineState->registration.fDisableResume, &pEngineState->variables, pEngineState->plan.dwRegistrationOperations, pEngineState->plan.dependencyRegistrationAction, pEngineState->plan.qwEstimatedSize);
407 ExitOnFailure(hr, "Failed to begin registration session in per-machine process.");
408 }
409 else
410 {
411 hr = RegistrationSessionBegin(sczEngineWorkingPath, &pEngineState->registration, &pEngineState->variables, pEngineState->plan.dwRegistrationOperations, pEngineState->plan.dependencyRegistrationAction, pEngineState->plan.qwEstimatedSize);
412 ExitOnFailure(hr, "Failed to begin registration session.");
413 }
414 }
415
416 // Apply any registration actions.
417 HRESULT hrExecuteRegistration = ExecuteDependentRegistrationActions(pEngineState->companionConnection.hPipe, &pEngineState->registration, pEngineState->plan.rgRegistrationActions, pEngineState->plan.cRegistrationActions);
418 UNREFERENCED_PARAMETER(hrExecuteRegistration);
419
420 // Try to save engine state.
421 hr = CoreSaveEngineState(pEngineState);
422 if (FAILED(hr))
423 {
424 LogErrorId(hr, MSG_STATE_NOT_SAVED);
425 hr = S_OK;
426 }
427
428LExit:
429 UserExperienceOnRegisterComplete(&pEngineState->userExperience, hr);
430 ReleaseStr(sczEngineWorkingPath);
431
432 return hr;
433}
434
435extern "C" HRESULT ApplyUnregister(
436 __in BURN_ENGINE_STATE* pEngineState,
437 __in BOOL fFailedOrRollback,
438 __in BOOL fSuspend,
439 __in BOOTSTRAPPER_APPLY_RESTART restart
440 )
441{
442 HRESULT hr = S_OK;
443 BURN_RESUME_MODE resumeMode = BURN_RESUME_MODE_NONE;
444 BOOL fKeepRegistration = pEngineState->plan.fDisallowRemoval;
445
446 CalculateKeepRegistration(pEngineState, &fKeepRegistration);
447
448 hr = UserExperienceOnUnregisterBegin(&pEngineState->userExperience, &fKeepRegistration);
449 ExitOnRootFailure(hr, "BA aborted unregister begin.");
450
451 // Calculate the correct resume mode. If a restart has been initiated, that trumps all other
452 // modes. If the user chose to suspend the install then we'll use that as the resume mode.
453 // Barring those special cases, if it was determined that we should keep the registration then
454 // do that, otherwise the resume mode was initialized to none and registration will be removed.
455 if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart)
456 {
457 resumeMode = BURN_RESUME_MODE_REBOOT_PENDING;
458 }
459 else if (fSuspend)
460 {
461 resumeMode = BURN_RESUME_MODE_SUSPEND;
462 }
463 else if (fKeepRegistration)
464 {
465 resumeMode = BURN_RESUME_MODE_ARP;
466 }
467
468 // If apply failed in any way and we're going to be keeping the bundle registered then
469 // execute any rollback dependency registration actions.
470 if (fFailedOrRollback && fKeepRegistration)
471 {
472 // Execute any rollback registration actions.
473 HRESULT hrRegistrationRollback = ExecuteDependentRegistrationActions(pEngineState->companionConnection.hPipe, &pEngineState->registration, pEngineState->plan.rgRollbackRegistrationActions, pEngineState->plan.cRollbackRegistrationActions);
474 UNREFERENCED_PARAMETER(hrRegistrationRollback);
475 }
476
477 if (pEngineState->registration.fPerMachine)
478 {
479 hr = ElevationSessionEnd(pEngineState->companionConnection.hPipe, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction);
480 ExitOnFailure(hr, "Failed to end session in per-machine process.");
481 }
482 else
483 {
484 hr = RegistrationSessionEnd(&pEngineState->registration, &pEngineState->variables, &pEngineState->packages, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction);
485 ExitOnFailure(hr, "Failed to end session in per-user process.");
486 }
487
488 pEngineState->resumeMode = resumeMode;
489
490LExit:
491 UserExperienceOnUnregisterComplete(&pEngineState->userExperience, hr);
492
493 return hr;
494}
495
496extern "C" HRESULT ApplyCache(
497 __in HANDLE hSourceEngineFile,
498 __in BURN_USER_EXPERIENCE* pUX,
499 __in BURN_VARIABLES* pVariables,
500 __in BURN_PLAN* pPlan,
501 __in HANDLE hPipe,
502 __inout DWORD* pcOverallProgressTicks,
503 __inout BOOL* pfRollback
504 )
505{
506 HRESULT hr = S_OK;
507 DWORD dwCheckpoint = 0;
508 BURN_CACHE_CONTEXT cacheContext = { };
509 BURN_PACKAGE* pPackage = NULL;
510
511 *pfRollback = FALSE;
512
513 hr = UserExperienceOnCacheBegin(pUX);
514 ExitOnRootFailure(hr, "BA aborted cache.");
515
516 cacheContext.hSourceEngineFile = hSourceEngineFile;
517 cacheContext.pPayloads = pPlan->pPayloads;
518 cacheContext.pUX = pUX;
519 cacheContext.pVariables = pVariables;
520 cacheContext.qwTotalCacheSize = pPlan->qwCacheSizeTotal;
521 cacheContext.wzLayoutDirectory = pPlan->sczLayoutDirectory;
522
523 hr = MemAllocArray(reinterpret_cast<LPVOID*>(&cacheContext.rgSearchPaths), sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
524 ExitOnNull(cacheContext.rgSearchPaths, hr, E_OUTOFMEMORY, "Failed to allocate cache search paths array.");
525
526 for (DWORD i = 0; i < pPlan->cCacheActions; ++i)
527 {
528 BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + i;
529 cacheContext.hPipe = hPipe;
530 pPackage = NULL;
531
532 switch (pCacheAction->type)
533 {
534 case BURN_CACHE_ACTION_TYPE_CHECKPOINT:
535 dwCheckpoint = pCacheAction->checkpoint.dwId;
536 break;
537
538 case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE:
539 hr = ApplyLayoutBundle(&cacheContext, pCacheAction->bundleLayout.pPayloadGroup, pCacheAction->bundleLayout.sczExecutableName, pCacheAction->bundleLayout.sczUnverifiedPath, pCacheAction->bundleLayout.qwBundleSize);
540 ExitOnFailure(hr, "Failed cache action: %ls", L"layout bundle");
541
542 ++(*pcOverallProgressTicks);
543
544 hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks);
545 LogExitOnFailure(hr, MSG_USER_CANCELED, "Cancel during cache: %ls", L"layout bundle");
546
547 break;
548
549 case BURN_CACHE_ACTION_TYPE_PACKAGE:
550 pPackage = pCacheAction->package.pPackage;
551
552 if (!pPackage->fPerMachine && !cacheContext.wzLayoutDirectory)
553 {
554 hr = CacheGetCompletedPath(FALSE, pPackage->sczCacheId, &pPackage->sczCacheFolder);
555 ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", pPackage->sczCacheId);
556
557 cacheContext.hPipe = INVALID_HANDLE_VALUE;
558 }
559
560 hr = ApplyCachePackage(&cacheContext, pPackage);
561 ExitOnFailure(hr, "Failed cache action: %ls", L"cache package");
562
563 ++(*pcOverallProgressTicks);
564
565 hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks);
566 LogExitOnFailure(hr, MSG_USER_CANCELED, "Cancel during cache: %ls", L"cache package");
567
568 break;
569
570 case BURN_CACHE_ACTION_TYPE_CONTAINER:
571 Assert(pPlan->sczLayoutDirectory);
572 hr = ApplyLayoutContainer(&cacheContext, pCacheAction->container.pContainer);
573 ExitOnFailure(hr, "Failed cache action: %ls", L"layout container");
574
575 break;
576
577 case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT:
578 if (!::SetEvent(pCacheAction->syncpoint.hEvent))
579 {
580 ExitWithLastError(hr, "Failed to set syncpoint event.");
581 }
582 break;
583
584 default:
585 AssertSz(FALSE, "Unknown cache action.");
586 break;
587 }
588 }
589
590LExit:
591 if (FAILED(hr))
592 {
593 DoRollbackCache(pUX, pPlan, hPipe, dwCheckpoint);
594 *pfRollback = TRUE;
595 }
596
597 // Clean up any remanents in the cache.
598 if (INVALID_HANDLE_VALUE != hPipe)
599 {
600 ElevationCacheCleanup(hPipe);
601 }
602
603 CacheCleanup(FALSE, pPlan->wzBundleId);
604
605 for (DWORD i = 0; i < cacheContext.cSearchPathsMax; ++i)
606 {
607 ReleaseNullStr(cacheContext.rgSearchPaths[i]);
608 }
609 ReleaseMem(cacheContext.rgSearchPaths);
610 ReleaseStr(cacheContext.sczLastUsedFolderCandidate);
611
612 UserExperienceOnCacheComplete(pUX, hr);
613 return hr;
614}
615
616extern "C" HRESULT ApplyExecute(
617 __in BURN_ENGINE_STATE* pEngineState,
618 __in_opt HANDLE hCacheThread,
619 __inout DWORD* pcOverallProgressTicks,
620 __out BOOL* pfRollback,
621 __out BOOL* pfSuspend,
622 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
623 )
624{
625 HRESULT hr = S_OK;
626 HRESULT hrRollback = S_OK;
627 BURN_EXECUTE_ACTION_CHECKPOINT* pCheckpoint = NULL;
628 BURN_EXECUTE_CONTEXT context = { };
629 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL;
630 BOOL fSeekNextRollbackBoundary = FALSE;
631
632 context.pUX = &pEngineState->userExperience;
633 context.cExecutePackagesTotal = pEngineState->plan.cExecutePackagesTotal;
634 context.pcOverallProgressTicks = pcOverallProgressTicks;
635
636 *pfRollback = FALSE;
637 *pfSuspend = FALSE;
638
639 // Send execute begin to BA.
640 hr = UserExperienceOnExecuteBegin(&pEngineState->userExperience, pEngineState->plan.cExecutePackagesTotal);
641 ExitOnRootFailure(hr, "BA aborted execute begin.");
642
643 // Do execute actions.
644 for (DWORD i = 0; i < pEngineState->plan.cExecuteActions; ++i)
645 {
646 BURN_EXECUTE_ACTION* pExecuteAction = &pEngineState->plan.rgExecuteActions[i];
647 if (pExecuteAction->fDeleted)
648 {
649 continue;
650 }
651
652 // If we are seeking the next rollback boundary, skip if this action wasn't it.
653 if (fSeekNextRollbackBoundary)
654 {
655 if (BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY == pExecuteAction->type)
656 {
657 continue;
658 }
659 else
660 {
661 fSeekNextRollbackBoundary = FALSE;
662 }
663 }
664
665 // Execute the action.
666 hr = DoExecuteAction(pEngineState, pExecuteAction, hCacheThread, &context, &pRollbackBoundary, &pCheckpoint, pfSuspend, pRestart);
667
668 if (*pfSuspend || BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart)
669 {
670 if (pCheckpoint && pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction)
671 {
672 hr = E_INVALIDSTATE;
673 LogId(REPORT_ERROR, MSG_RESTART_REQUEST_DURING_MSI_TRANSACTION, pCheckpoint->pActiveRollbackBoundary->sczId);
674 }
675 else
676 {
677 ExitFunction();
678 }
679 }
680
681 if (FAILED(hr))
682 {
683 // If rollback is disabled, keep what we have and always end execution here.
684 if (pEngineState->plan.fDisableRollback)
685 {
686 LogId(REPORT_WARNING, MSG_PLAN_ROLLBACK_DISABLED);
687
688 if (pCheckpoint && pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction)
689 {
690 hrRollback = ExecuteMsiCommitTransaction(pEngineState, pCheckpoint->pActiveRollbackBoundary, &context);
691 IgnoreRollbackError(hrRollback, "Failed commit transaction from disable rollback");
692 }
693
694 *pfRollback = TRUE;
695 break;
696 }
697
698 if (pCheckpoint)
699 {
700 // If inside a MSI transaction, roll it back.
701 if (pCheckpoint->pActiveRollbackBoundary && pCheckpoint->pActiveRollbackBoundary->fActiveTransaction)
702 {
703 hrRollback = ExecuteMsiRollbackTransaction(pEngineState, pCheckpoint->pActiveRollbackBoundary, &context);
704 IgnoreRollbackError(hrRollback, "Failed rolling back transaction");
705 }
706
707 // The action failed, roll back to previous rollback boundary.
708 hrRollback = DoRollbackActions(pEngineState, &context, pCheckpoint->dwId, pRestart);
709 IgnoreRollbackError(hrRollback, "Failed rollback actions");
710 }
711
712 // If the rollback boundary is vital, end execution here.
713 if (pRollbackBoundary && pRollbackBoundary->fVital)
714 {
715 *pfRollback = TRUE;
716 break;
717 }
718
719 // Move forward to next rollback boundary.
720 fSeekNextRollbackBoundary = TRUE;
721 }
722 }
723
724LExit:
725 // Send execute complete to BA.
726 UserExperienceOnExecuteComplete(&pEngineState->userExperience, hr);
727
728 return hr;
729}
730
731extern "C" void ApplyClean(
732 __in BURN_USER_EXPERIENCE* /*pUX*/,
733 __in BURN_PLAN* pPlan,
734 __in HANDLE hPipe
735 )
736{
737 HRESULT hr = S_OK;
738
739 for (DWORD i = 0; i < pPlan->cCleanActions; ++i)
740 {
741 BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + i;
742 BURN_PACKAGE* pPackage = pCleanAction->pPackage;
743
744 hr = CleanPackage(hPipe, pPackage);
745 }
746}
747
748
749// internal helper functions
750
751static void CalculateKeepRegistration(
752 __in BURN_ENGINE_STATE* pEngineState,
753 __inout BOOL* pfKeepRegistration
754 )
755{
756 LogId(REPORT_STANDARD, MSG_POST_APPLY_CALCULATE_REGISTRATION);
757
758 for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i)
759 {
760 BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i;
761
762 if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
763 {
764 MspEngineFinalizeInstallRegistrationState(pPackage);
765 }
766
767 LogId(REPORT_STANDARD, MSG_POST_APPLY_PACKAGE, pPackage->sczId, LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->installRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->cacheRegistrationState));
768
769 if (!pPackage->fCanAffectRegistration)
770 {
771 continue;
772 }
773
774 if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState ||
775 BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState)
776 {
777 *pfKeepRegistration = TRUE;
778 }
779 }
780}
781
782static HRESULT ExecuteDependentRegistrationActions(
783 __in HANDLE hPipe,
784 __in const BURN_REGISTRATION* pRegistration,
785 __in_ecount(cActions) const BURN_DEPENDENT_REGISTRATION_ACTION* rgActions,
786 __in DWORD cActions
787 )
788{
789 HRESULT hr = S_OK;
790
791 for (DWORD iAction = 0; iAction < cActions; ++iAction)
792 {
793 const BURN_DEPENDENT_REGISTRATION_ACTION* pAction = rgActions + iAction;
794
795 if (pRegistration->fPerMachine)
796 {
797 hr = ElevationProcessDependentRegistration(hPipe, pAction);
798 ExitOnFailure(hr, "Failed to execute dependent registration action.");
799 }
800 else
801 {
802 hr = DependencyProcessDependentRegistration(pRegistration, pAction);
803 ExitOnFailure(hr, "Failed to process dependency registration action.");
804 }
805 }
806
807LExit:
808 return hr;
809}
810
811static HRESULT ApplyCachePackage(
812 __in BURN_CACHE_CONTEXT* pContext,
813 __in BURN_PACKAGE* pPackage
814 )
815{
816 HRESULT hr = S_OK;
817 BOOL fCanceledBegin = FALSE;
818 BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION cachePackageCompleteAction = BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE;
819
820 for (;;)
821 {
822 fCanceledBegin = FALSE;
823
824 hr = UserExperienceOnCachePackageBegin(pContext->pUX, pPackage->sczId, pPackage->payloads.cItems, pPackage->payloads.qwTotalSize);
825 if (FAILED(hr))
826 {
827 fCanceledBegin = TRUE;
828 }
829 else
830 {
831 for (DWORD i = 0; i < pPackage->payloads.cItems; ++i)
832 {
833 BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem = pPackage->payloads.rgItems + i;
834
835 hr = ApplyProcessPayload(pContext, pPackage, pPayloadGroupItem);
836 if (FAILED(hr))
837 {
838 break;
839 }
840 }
841 }
842
843 pPackage->hrCacheResult = hr;
844 cachePackageCompleteAction = SUCCEEDED(hr) || pPackage->fVital || fCanceledBegin ? BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE;
845 UserExperienceOnCachePackageComplete(pContext->pUX, pPackage->sczId, hr, &cachePackageCompleteAction);
846
847 if (SUCCEEDED(hr))
848 {
849 break;
850 }
851
852 if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_RETRY == cachePackageCompleteAction)
853 {
854 for (DWORD i = 0; i < pPackage->payloads.cItems; ++i)
855 {
856 BURN_PAYLOAD_GROUP_ITEM* pItem = pPackage->payloads.rgItems + i;
857 if (pItem->fCached)
858 {
859 pItem->pPayload->cRemainingInstances += 1;
860 pItem->fCached = FALSE;
861 }
862
863 if (pItem->qwCommittedCacheProgress)
864 {
865 pContext->qwSuccessfulCacheProgress -= pItem->qwCommittedCacheProgress;
866 pItem->qwCommittedCacheProgress = 0;
867 }
868 }
869
870 LogErrorId(hr, MSG_CACHE_RETRYING_PACKAGE, pPackage->sczId, NULL, NULL);
871
872 continue;
873 }
874 else if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE == cachePackageCompleteAction && !pPackage->fVital) // ignore non-vital download failures.
875 {
876 LogId(REPORT_STANDARD, MSG_CACHE_CONTINUING_NONVITAL_PACKAGE, pPackage->sczId, hr);
877 hr = S_OK;
878 }
879 else if (fCanceledBegin)
880 {
881 LogExitOnFailure(hr, MSG_USER_CANCELED, "Cancel during cache: %ls: %ls", L"begin cache package", pPackage->sczId);
882 }
883
884 break;
885 }
886
887LExit:
888 return hr;
889}
890
891static HRESULT ApplyExtractContainer(
892 __in BURN_CACHE_CONTEXT* pContext,
893 __in BURN_CONTAINER* pContainer
894 )
895{
896 HRESULT hr = S_OK;
897
898 if (pContainer->qwCommittedCacheProgress)
899 {
900 pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedCacheProgress;
901 pContainer->qwCommittedCacheProgress = 0;
902 }
903
904 if (pContainer->qwCommittedExtractProgress)
905 {
906 pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedExtractProgress;
907 pContainer->qwCommittedExtractProgress = 0;
908 }
909
910 if (!pContainer->fActuallyAttached)
911 {
912 hr = ApplyAcquireContainerOrPayload(pContext, pContainer, NULL, NULL);
913 LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_CONTAINER, "Failed to acquire container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath);
914 }
915
916 hr = ExtractContainer(pContext, pContainer);
917 LogExitOnFailure(hr, MSG_FAILED_EXTRACT_CONTAINER, "Failed to extract payloads from container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath);
918
919 if (pContext->sczLastUsedFolderCandidate)
920 {
921 // We successfully copied from a source location, set that as the last used source.
922 CacheSetLastUsedSource(pContext->pVariables, pContext->sczLastUsedFolderCandidate, pContainer->sczFilePath);
923 }
924
925 if (pContainer->qwExtractSizeTotal < pContainer->qwCommittedExtractProgress)
926 {
927 AssertSz(FALSE, "Container extracted more than planned.");
928 pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedExtractProgress;
929 pContext->qwSuccessfulCacheProgress += pContainer->qwExtractSizeTotal;
930 }
931 else
932 {
933 pContext->qwSuccessfulCacheProgress += pContainer->qwExtractSizeTotal - pContainer->qwCommittedExtractProgress;
934 }
935
936 pContainer->qwCommittedExtractProgress = pContainer->qwExtractSizeTotal;
937
938LExit:
939 ReleaseNullStr(pContext->sczLastUsedFolderCandidate);
940
941 return hr;
942}
943
944static HRESULT ApplyLayoutBundle(
945 __in BURN_CACHE_CONTEXT* pContext,
946 __in BURN_PAYLOAD_GROUP* pPayloads,
947 __in_z LPCWSTR wzExecutableName,
948 __in_z LPCWSTR wzUnverifiedPath,
949 __in DWORD64 qwBundleSize
950 )
951{
952 HRESULT hr = S_OK;
953
954 hr = LayoutBundle(pContext, wzExecutableName, wzUnverifiedPath, qwBundleSize);
955 ExitOnFailure(hr, "Failed to layout bundle.");
956
957 for (DWORD i = 0; i < pPayloads->cItems; ++i)
958 {
959 BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem = pPayloads->rgItems + i;
960
961 hr = ApplyProcessPayload(pContext, NULL, pPayloadGroupItem);
962 ExitOnFailure(hr, "Failed to layout bundle payload: %ls", pPayloadGroupItem->pPayload->sczKey);
963 }
964
965LExit:
966 return hr;
967}
968
969static HRESULT ApplyLayoutContainer(
970 __in BURN_CACHE_CONTEXT* pContext,
971 __in BURN_CONTAINER* pContainer
972 )
973{
974 HRESULT hr = S_OK;
975 DWORD cTryAgainAttempts = 0;
976 BOOL fRetry = FALSE;
977
978 Assert(!pContainer->fAttached);
979
980 hr = ApplyCacheVerifyContainerOrPayload(pContext, pContainer, NULL, NULL);
981 if (SUCCEEDED(hr))
982 {
983 ExitFunction();
984 }
985
986 for (;;)
987 {
988 fRetry = FALSE;
989
990 hr = ApplyAcquireContainerOrPayload(pContext, pContainer, NULL, NULL);
991 LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_CONTAINER, "Failed to acquire container: %ls to working path: %ls", pContainer->sczId, pContainer->sczUnverifiedPath);
992
993 hr = LayoutOrCacheContainerOrPayload(pContext, pContainer, NULL, NULL, cTryAgainAttempts, &fRetry);
994 if (SUCCEEDED(hr))
995 {
996 break;
997 }
998 else
999 {
1000 LogErrorId(hr, MSG_FAILED_LAYOUT_CONTAINER, pContainer->sczId, pContext->wzLayoutDirectory, pContainer->sczUnverifiedPath);
1001
1002 if (!fRetry)
1003 {
1004 ExitFunction();
1005 }
1006
1007 ++cTryAgainAttempts;
1008 pContext->qwSuccessfulCacheProgress -= pContainer->qwCommittedCacheProgress;
1009 pContainer->qwCommittedCacheProgress = 0;
1010 ReleaseNullStr(pContext->sczLastUsedFolderCandidate);
1011 LogErrorId(hr, MSG_CACHE_RETRYING_CONTAINER, pContainer->sczId, NULL, NULL);
1012 }
1013 }
1014
1015LExit:
1016 ReleaseNullStr(pContext->sczLastUsedFolderCandidate);
1017
1018 return hr;
1019}
1020
1021static HRESULT ApplyProcessPayload(
1022 __in BURN_CACHE_CONTEXT* pContext,
1023 __in_opt BURN_PACKAGE* pPackage,
1024 __in BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem
1025 )
1026{
1027 HRESULT hr = S_OK;
1028 DWORD cTryAgainAttempts = 0;
1029 BOOL fRetry = FALSE;
1030 BURN_PAYLOAD* pPayload = pPayloadGroupItem->pPayload;
1031
1032 Assert(pContext->pPayloads && pPackage || pContext->wzLayoutDirectory);
1033
1034 if (pPayload->pContainer && pContext->wzLayoutDirectory)
1035 {
1036 ExitFunction();
1037 }
1038
1039 hr = ApplyCacheVerifyContainerOrPayload(pContext, NULL, pPackage, pPayloadGroupItem);
1040 if (SUCCEEDED(hr))
1041 {
1042 ExitFunction();
1043 }
1044
1045 for (;;)
1046 {
1047 fRetry = FALSE;
1048
1049 hr = ApplyAcquireContainerOrPayload(pContext, NULL, pPackage, pPayloadGroupItem);
1050 LogExitOnFailure(hr, MSG_FAILED_ACQUIRE_PAYLOAD, "Failed to acquire payload: %ls to working path: %ls", pPayload->sczKey, pPayload->sczUnverifiedPath);
1051
1052 hr = LayoutOrCacheContainerOrPayload(pContext, NULL, pPackage, pPayloadGroupItem, cTryAgainAttempts, &fRetry);
1053 if (SUCCEEDED(hr))
1054 {
1055 break;
1056 }
1057 else
1058 {
1059 LogErrorId(hr, pContext->wzLayoutDirectory ? MSG_FAILED_LAYOUT_PAYLOAD : MSG_FAILED_CACHE_PAYLOAD, pPayload->sczKey, pContext->wzLayoutDirectory, pPayload->sczUnverifiedPath);
1060
1061 if (!fRetry)
1062 {
1063 ExitFunction();
1064 }
1065
1066 ++cTryAgainAttempts;
1067 pContext->qwSuccessfulCacheProgress -= pPayloadGroupItem->qwCommittedCacheProgress;
1068 pPayloadGroupItem->qwCommittedCacheProgress = 0;
1069 ReleaseNullStr(pContext->sczLastUsedFolderCandidate);
1070 LogErrorId(hr, MSG_CACHE_RETRYING_PAYLOAD, pPayload->sczKey, NULL, NULL);
1071 }
1072 }
1073
1074LExit:
1075 ReleaseNullStr(pContext->sczLastUsedFolderCandidate);
1076
1077 return hr;
1078}
1079
1080static HRESULT ApplyCacheVerifyContainerOrPayload(
1081 __in BURN_CACHE_CONTEXT* pContext,
1082 __in_opt BURN_CONTAINER* pContainer,
1083 __in_opt BURN_PACKAGE* pPackage,
1084 __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem
1085 )
1086{
1087 AssertSz(pContainer || pPayloadGroupItem, "Must provide a container or a payload.");
1088
1089 HRESULT hr = S_OK;
1090 BURN_CACHE_PROGRESS_CONTEXT progress = { };
1091
1092 progress.pCacheContext = pContext;
1093 progress.pContainer = pContainer;
1094 progress.pPackage = pPackage;
1095 progress.pPayloadGroupItem = pPayloadGroupItem;
1096
1097 if (pContainer)
1098 {
1099 hr = CacheVerifyContainer(pContainer, pContext->wzLayoutDirectory, CacheMessageHandler, CacheProgressRoutine, &progress);
1100 }
1101 else if (!pContext->wzLayoutDirectory && INVALID_HANDLE_VALUE != pContext->hPipe)
1102 {
1103 hr = ElevationCacheVerifyPayload(pContext->hPipe, pPackage, pPayloadGroupItem->pPayload, CacheMessageHandler, CacheProgressRoutine, &progress);
1104 }
1105 else
1106 {
1107 hr = CacheVerifyPayload(pPayloadGroupItem->pPayload, pContext->wzLayoutDirectory ? pContext->wzLayoutDirectory : pPackage->sczCacheFolder, CacheMessageHandler, CacheProgressRoutine, &progress);
1108 }
1109
1110 return hr;
1111}
1112
1113static HRESULT ExtractContainer(
1114 __in BURN_CACHE_CONTEXT* pContext,
1115 __in BURN_CONTAINER* pContainer
1116 )
1117{
1118 HRESULT hr = S_OK;
1119 BURN_CONTAINER_CONTEXT context = { };
1120 HANDLE hContainerHandle = INVALID_HANDLE_VALUE;
1121 LPWSTR sczStreamName = NULL;
1122 BURN_PAYLOAD* pExtract = NULL;
1123 BURN_CACHE_PROGRESS_CONTEXT progress = { };
1124
1125 progress.pCacheContext = pContext;
1126 progress.pContainer = pContainer;
1127 progress.type = BURN_CACHE_PROGRESS_TYPE_EXTRACT;
1128
1129 // If the container is actually attached, then it was planned to be acquired through hSourceEngineFile.
1130 if (pContainer->fActuallyAttached)
1131 {
1132 hContainerHandle = pContext->hSourceEngineFile;
1133 }
1134
1135 hr = ContainerOpen(&context, pContainer, hContainerHandle, pContainer->sczUnverifiedPath);
1136 ExitOnFailure(hr, "Failed to open container: %ls.", pContainer->sczId);
1137
1138 while (S_OK == (hr = ContainerNextStream(&context, &sczStreamName)))
1139 {
1140 BOOL fExtracted = FALSE;
1141
1142 hr = PayloadFindEmbeddedBySourcePath(pContext->pPayloads, sczStreamName, &pExtract);
1143 if (E_NOTFOUND != hr)
1144 {
1145 ExitOnFailure(hr, "Failed to find embedded payload by source path: %ls container: %ls", sczStreamName, pContainer->sczId);
1146
1147 // Skip payloads that weren't planned or have already been cached.
1148 if (pExtract->sczUnverifiedPath && pExtract->cRemainingInstances)
1149 {
1150 progress.pPayload = pExtract;
1151
1152 hr = PreparePayloadDestinationPath(pExtract->sczUnverifiedPath);
1153 ExitOnFailure(hr, "Failed to prepare payload destination path: %ls", pExtract->sczUnverifiedPath);
1154
1155 hr = UserExperienceOnCachePayloadExtractBegin(pContext->pUX, pContainer->sczId, pExtract->sczKey);
1156 if (FAILED(hr))
1157 {
1158 UserExperienceOnCachePayloadExtractComplete(pContext->pUX, pContainer->sczId, pExtract->sczKey, hr);
1159 ExitOnRootFailure(hr, "BA aborted cache payload extract begin.");
1160 }
1161
1162 // TODO: Send progress when extracting stream to file.
1163 hr = ContainerStreamToFile(&context, pExtract->sczUnverifiedPath);
1164 // Error handling happens after sending complete message to BA.
1165
1166 // If succeeded, send 100% complete here to make sure progress was sent to the BA.
1167 if (SUCCEEDED(hr))
1168 {
1169 hr = CompleteCacheProgress(&progress, pExtract->qwFileSize);
1170 }
1171
1172 UserExperienceOnCachePayloadExtractComplete(pContext->pUX, pContainer->sczId, pExtract->sczKey, hr);
1173 ExitOnFailure(hr, "Failed to extract payload: %ls from container: %ls", sczStreamName, pContainer->sczId);
1174
1175 fExtracted = TRUE;
1176 }
1177 }
1178
1179 if (!fExtracted)
1180 {
1181 hr = ContainerSkipStream(&context);
1182 ExitOnFailure(hr, "Failed to skip the extraction of payload: %ls from container: %ls", sczStreamName, pContainer->sczId);
1183 }
1184 }
1185
1186 if (E_NOMOREITEMS == hr)
1187 {
1188 hr = S_OK;
1189 }
1190 ExitOnFailure(hr, "Failed to extract all payloads from container: %ls", pContainer->sczId);
1191
1192LExit:
1193 ReleaseStr(sczStreamName);
1194 ContainerClose(&context);
1195
1196 return hr;
1197}
1198
1199static HRESULT LayoutBundle(
1200 __in BURN_CACHE_CONTEXT* pContext,
1201 __in_z LPCWSTR wzExecutableName,
1202 __in_z LPCWSTR wzUnverifiedPath,
1203 __in DWORD64 qwBundleSize
1204 )
1205{
1206 HRESULT hr = S_OK;
1207 LPWSTR sczBundlePath = NULL;
1208 LPWSTR sczBundleDownloadUrl = NULL;
1209 LPWSTR sczDestinationPath = NULL;
1210 int nEquivalentPaths = 0;
1211 BOOTSTRAPPER_CACHE_OPERATION cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE;
1212 BURN_CACHE_PROGRESS_CONTEXT progress = { };
1213 BOOL fRetry = FALSE;
1214 BOOL fRetryAcquire = FALSE;
1215 BOOL fCanceledBegin = FALSE;
1216
1217 progress.pCacheContext = pContext;
1218
1219 hr = VariableGetString(pContext->pVariables, BURN_BUNDLE_SOURCE_PROCESS_PATH, &sczBundlePath);
1220 if (FAILED(hr))
1221 {
1222 if (E_NOTFOUND != hr)
1223 {
1224 ExitOnFailure(hr, "Failed to get path to bundle source process path to layout.");
1225 }
1226
1227 hr = PathForCurrentProcess(&sczBundlePath, NULL);
1228 ExitOnFailure(hr, "Failed to get path to bundle to layout.");
1229 }
1230
1231 hr = PathConcat(pContext->wzLayoutDirectory, wzExecutableName, &sczDestinationPath);
1232 ExitOnFailure(hr, "Failed to concat layout path for bundle.");
1233
1234 // If the destination path is the currently running bundle, bail.
1235 hr = PathCompare(sczBundlePath, sczDestinationPath, &nEquivalentPaths);
1236 ExitOnFailure(hr, "Failed to determine if layout bundle path was equivalent with current process path.");
1237
1238 if (CSTR_EQUAL == nEquivalentPaths && FileExistsEx(sczDestinationPath, NULL))
1239 {
1240 hr = UserExperienceOnCacheContainerOrPayloadVerifyBegin(pContext->pUX, NULL, NULL);
1241 if (FAILED(hr))
1242 {
1243 UserExperienceOnCacheContainerOrPayloadVerifyComplete(pContext->pUX, NULL, NULL, hr);
1244 ExitOnRootFailure(hr, "BA aborted cache payload verify begin.");
1245 }
1246
1247 progress.type = BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY;
1248 hr = CompleteCacheProgress(&progress, qwBundleSize);
1249
1250 UserExperienceOnCacheContainerOrPayloadVerifyComplete(pContext->pUX, NULL, NULL, hr);
1251
1252 ExitFunction();
1253 }
1254
1255 do
1256 {
1257 hr = S_OK;
1258 fRetry = FALSE;
1259 progress.type = BURN_CACHE_PROGRESS_TYPE_ACQUIRE;
1260
1261 for (;;)
1262 {
1263 fRetryAcquire = FALSE;
1264 progress.fCancel = FALSE;
1265 fCanceledBegin = FALSE;
1266
1267 hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, NULL, NULL, &sczBundlePath, &sczBundleDownloadUrl, NULL, &cacheOperation);
1268
1269 if (FAILED(hr))
1270 {
1271 fCanceledBegin = TRUE;
1272 }
1273 else
1274 {
1275 hr = CopyPayload(&progress, pContext->hSourceEngineFile, sczBundlePath, wzUnverifiedPath);
1276 // Error handling happens after sending complete message to BA.
1277
1278 // If succeeded, send 100% complete here to make sure progress was sent to the BA.
1279 if (SUCCEEDED(hr))
1280 {
1281 hr = CompleteCacheProgress(&progress, qwBundleSize);
1282 }
1283 }
1284
1285 UserExperienceOnCacheAcquireComplete(pContext->pUX, NULL, NULL, hr, &fRetryAcquire);
1286 if (fRetryAcquire)
1287 {
1288 continue;
1289 }
1290 else if (fCanceledBegin)
1291 {
1292 ExitOnRootFailure(hr, "BA aborted cache acquire begin.");
1293 }
1294
1295 ExitOnFailure(hr, "Failed to copy bundle from: '%ls' to: '%ls'", sczBundlePath, wzUnverifiedPath);
1296 break;
1297 }
1298
1299 do
1300 {
1301 fCanceledBegin = FALSE;
1302
1303 hr = UserExperienceOnCacheVerifyBegin(pContext->pUX, NULL, NULL);
1304
1305 if (FAILED(hr))
1306 {
1307 fCanceledBegin = TRUE;
1308 }
1309 else
1310 {
1311 hr = CacheLayoutBundle(wzExecutableName, pContext->wzLayoutDirectory, wzUnverifiedPath, qwBundleSize, CacheMessageHandler, CacheProgressRoutine, &progress);
1312 }
1313
1314 BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE;
1315 UserExperienceOnCacheVerifyComplete(pContext->pUX, NULL, NULL, hr, &action);
1316 if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action)
1317 {
1318 hr = S_FALSE; // retry verify.
1319 }
1320 else if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION == action)
1321 {
1322 fRetry = TRUE; // go back and retry acquire.
1323 }
1324 else if (fCanceledBegin)
1325 {
1326 ExitOnRootFailure(hr, "BA aborted cache verify begin.");
1327 }
1328 } while (S_FALSE == hr);
1329
1330 if (fRetry)
1331 {
1332 pContext->qwSuccessfulCacheProgress -= qwBundleSize; // Acquire
1333 }
1334 } while (fRetry);
1335 LogExitOnFailure(hr, MSG_FAILED_LAYOUT_BUNDLE, "Failed to layout bundle: %ls to layout directory: %ls", sczBundlePath, pContext->wzLayoutDirectory);
1336
1337LExit:
1338 ReleaseStr(sczDestinationPath);
1339 ReleaseStr(sczBundleDownloadUrl);
1340 ReleaseStr(sczBundlePath);
1341
1342 return hr;
1343}
1344
1345static HRESULT ApplyAcquireContainerOrPayload(
1346 __in BURN_CACHE_CONTEXT* pContext,
1347 __in_opt BURN_CONTAINER* pContainer,
1348 __in_opt BURN_PACKAGE* pPackage,
1349 __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem
1350 )
1351{
1352 AssertSz(pContainer || pPayloadGroupItem, "Must provide a container or a payload.");
1353
1354 HRESULT hr = S_OK;
1355 BURN_CACHE_PROGRESS_CONTEXT progress = { };
1356 BOOL fRetry = FALSE;
1357
1358 progress.pCacheContext = pContext;
1359 progress.type = BURN_CACHE_PROGRESS_TYPE_ACQUIRE;
1360 progress.pContainer = pContainer;
1361 progress.pPackage = pPackage;
1362 progress.pPayloadGroupItem = pPayloadGroupItem;
1363
1364 do
1365 {
1366 hr = AcquireContainerOrPayload(&progress, &fRetry);
1367
1368 if (fRetry)
1369 {
1370 LogErrorId(hr, pContainer ? MSG_APPLY_RETRYING_ACQUIRE_CONTAINER : MSG_APPLY_RETRYING_ACQUIRE_PAYLOAD, pContainer ? pContainer->sczId : pPayloadGroupItem->pPayload->sczKey, NULL, NULL);
1371 hr = S_OK;
1372 }
1373
1374 ExitOnFailure(hr, "Failed to acquire %hs: %ls", pContainer ? "container" : "payload", pContainer ? pContainer->sczId : pPayloadGroupItem->pPayload->sczKey);
1375 } while (fRetry);
1376
1377LExit:
1378 return hr;
1379}
1380
1381static HRESULT AcquireContainerOrPayload(
1382 __in BURN_CACHE_PROGRESS_CONTEXT* pProgress,
1383 __out BOOL* pfRetry
1384 )
1385{
1386 BURN_CACHE_CONTEXT* pContext = pProgress->pCacheContext;
1387 BURN_CONTAINER* pContainer = pProgress->pContainer;
1388 BURN_PACKAGE* pPackage = pProgress->pPackage;
1389 BURN_PAYLOAD* pPayload = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload : NULL;
1390 AssertSz(pContainer || pPayload, "Must provide a container or a payload.");
1391
1392 HRESULT hr = S_OK;
1393 int nEquivalentPaths = 0;
1394 LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : NULL;
1395 LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : NULL;
1396 LPCWSTR wzPayloadContainerId = pPayload && pPayload->pContainer ? pPayload->pContainer->sczId : NULL;
1397 LPCWSTR wzDestinationPath = pContainer ? pContainer->sczUnverifiedPath: pPayload->sczUnverifiedPath;
1398 LPCWSTR wzRelativePath = pContainer ? pContainer->sczFilePath : pPayload->sczFilePath;
1399 DWORD dwChosenSearchPath = 0;
1400 DWORD dwDestinationSearchPath = 0;
1401 BOOTSTRAPPER_CACHE_OPERATION cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE;
1402 BOOTSTRAPPER_CACHE_RESOLVE_OPERATION resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_NONE;
1403 LPWSTR* pwzDownloadUrl = pContainer ? &pContainer->downloadSource.sczUrl : &pPayload->downloadSource.sczUrl;
1404 LPWSTR* pwzSourcePath = pContainer ? &pContainer->sczSourcePath : &pPayload->sczSourcePath;
1405 BOOL fFoundLocal = FALSE;
1406 BOOL fPreferExtract = FALSE;
1407 DWORD64 qwFileSize = 0;
1408 BOOL fMinimumFileSize = FALSE;
1409
1410 if (pContainer)
1411 {
1412 if (pContainer->fAttached)
1413 {
1414 fMinimumFileSize = TRUE;
1415 qwFileSize = pContainer->qwAttachedOffset + pContainer->qwFileSize;
1416 }
1417 else if (pContainer->pbHash && pContext->wzLayoutDirectory)
1418 {
1419 qwFileSize = pContainer->qwFileSize;
1420 }
1421 }
1422 else if (pPayload->pbHash)
1423 {
1424 qwFileSize = pPayload->qwFileSize;
1425 }
1426
1427 pContext->cSearchPaths = 0;
1428 *pfRetry = FALSE;
1429 pProgress->fCancel = FALSE;
1430
1431 hr = UserExperienceOnCacheAcquireBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId, pwzSourcePath, pwzDownloadUrl, wzPayloadContainerId, &cacheOperation);
1432 ExitOnRootFailure(hr, "BA aborted cache acquire begin.");
1433
1434 // Skip the Resolving event and probing local paths if the BA already knew it wanted to download or extract.
1435 if (BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD != cacheOperation &&
1436 BOOTSTRAPPER_CACHE_OPERATION_EXTRACT != cacheOperation)
1437 {
1438 do
1439 {
1440 fFoundLocal = FALSE;
1441 fPreferExtract = FALSE;
1442 resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_NONE;
1443 dwChosenSearchPath = 0;
1444 dwDestinationSearchPath = 0;
1445
1446 hr = CacheGetLocalSourcePaths(wzRelativePath, *pwzSourcePath, wzDestinationPath, pContext->wzLayoutDirectory, pContext->pVariables, &pContext->rgSearchPaths, &pContext->cSearchPaths, &dwChosenSearchPath, &dwDestinationSearchPath);
1447 ExitOnFailure(hr, "Failed to search local source.");
1448
1449 if (wzPayloadContainerId)
1450 {
1451 // When a payload comes from a container, the container has the highest chance of being correct.
1452 // But we want to avoid extracting the container multiple times.
1453 // So only consider the destination path, which means the container was already extracted.
1454 if (IsValidLocalFile(pContext->rgSearchPaths[dwDestinationSearchPath], qwFileSize, fMinimumFileSize))
1455 {
1456 fFoundLocal = TRUE;
1457 dwChosenSearchPath = dwDestinationSearchPath;
1458 }
1459 else // don't prefer the container if extracting it already failed.
1460 {
1461 fPreferExtract = SUCCEEDED(pPayload->pContainer->hrExtract);
1462 }
1463 }
1464
1465 if (!fFoundLocal)
1466 {
1467 for (DWORD i = 0; i < pContext->cSearchPaths; ++i)
1468 {
1469 // If the file exists locally with the correct size, choose it.
1470 if (IsValidLocalFile(pContext->rgSearchPaths[i], qwFileSize, fMinimumFileSize))
1471 {
1472 dwChosenSearchPath = i;
1473
1474 fFoundLocal = TRUE;
1475 break;
1476 }
1477 }
1478 }
1479
1480 if (BOOTSTRAPPER_CACHE_OPERATION_COPY == cacheOperation)
1481 {
1482 if (fFoundLocal)
1483 {
1484 resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_LOCAL;
1485 }
1486 }
1487 else
1488 {
1489 if (fPreferExtract) // the file comes from a container which hasn't been extracted yet, so extract it.
1490 {
1491 resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER;
1492 }
1493 else if (fFoundLocal) // the file exists locally, so copy it.
1494 {
1495 resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_LOCAL;
1496 }
1497 else if (*pwzDownloadUrl && **pwzDownloadUrl)
1498 {
1499 resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_DOWNLOAD;
1500 }
1501 else if (wzPayloadContainerId)
1502 {
1503 resolveOperation = BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER;
1504 }
1505 }
1506
1507 // Let the BA have a chance to override the source.
1508 hr = UserExperienceOnCacheAcquireResolving(pContext->pUX, wzPackageOrContainerId, wzPayloadId, pContext->rgSearchPaths, pContext->cSearchPaths, fFoundLocal, &dwChosenSearchPath, pwzDownloadUrl, wzPayloadContainerId, &resolveOperation);
1509 ExitOnRootFailure(hr, "BA aborted cache acquire resolving.");
1510
1511 switch (resolveOperation)
1512 {
1513 case BOOTSTRAPPER_CACHE_RESOLVE_LOCAL:
1514 cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_COPY;
1515 break;
1516 case BOOTSTRAPPER_CACHE_RESOLVE_DOWNLOAD:
1517 cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD;
1518 break;
1519 case BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER:
1520 cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_EXTRACT;
1521 break;
1522 case BOOTSTRAPPER_CACHE_RESOLVE_RETRY:
1523 pContext->cSearchPathsMax = max(pContext->cSearchPaths, pContext->cSearchPathsMax);
1524 break;
1525 }
1526 } while (BOOTSTRAPPER_CACHE_RESOLVE_RETRY == resolveOperation);
1527 }
1528
1529 switch (cacheOperation)
1530 {
1531 case BOOTSTRAPPER_CACHE_OPERATION_COPY:
1532 // If the source path and destination path are different, do the copy (otherwise there's no point).
1533 hr = PathCompare(pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath, &nEquivalentPaths);
1534 ExitOnFailure(hr, "Failed to determine if payload paths were equivalent, source: %ls, destination: %ls.", pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath);
1535
1536 if (CSTR_EQUAL != nEquivalentPaths)
1537 {
1538 hr = CopyPayload(pProgress, INVALID_HANDLE_VALUE, pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath);
1539 ExitOnFailure(hr, "Failed to copy payload: %ls", wzPayloadId);
1540
1541 // Store the source path so it can be used as the LastUsedFolder if it passes verification.
1542 pContext->sczLastUsedFolderCandidate = pContext->rgSearchPaths[dwChosenSearchPath];
1543 pContext->rgSearchPaths[dwChosenSearchPath] = NULL;
1544 }
1545
1546 break;
1547 case BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD:
1548 hr = DownloadPayload(pProgress, wzDestinationPath);
1549 ExitOnFailure(hr, "Failed to download payload: %ls", wzPayloadId);
1550
1551 break;
1552 case BOOTSTRAPPER_CACHE_OPERATION_EXTRACT:
1553 Assert(pPayload && pPayload->pContainer);
1554
1555 hr = ApplyExtractContainer(pContext, pPayload->pContainer);
1556 ExitOnFailure(hr, "Failed to extract container for payload: %ls", wzPayloadId);
1557
1558 break;
1559 default:
1560 hr = E_FILENOTFOUND;
1561 LogExitOnFailure(hr, MSG_RESOLVE_SOURCE_FAILED, "Failed to resolve source, payload: %ls, package: %ls, container: %ls", wzPayloadId, pPackage ? pPackage->sczId : NULL, pContainer ? pContainer->sczId : NULL);
1562 }
1563
1564 // Send 100% complete here. This is sometimes the only progress sent to the BA.
1565 hr = CompleteCacheProgress(pProgress, pContainer ? pContainer->qwFileSize : pPayload->qwFileSize);
1566
1567LExit:
1568 if (BOOTSTRAPPER_CACHE_OPERATION_EXTRACT == cacheOperation)
1569 {
1570 if (FAILED(hr) && SUCCEEDED(pPayload->pContainer->hrExtract) &&
1571 (fFoundLocal || pPayload->downloadSource.sczUrl && *pPayload->downloadSource.sczUrl))
1572 {
1573 *pfRetry = TRUE;
1574 }
1575 pPayload->pContainer->hrExtract = hr;
1576 }
1577 UserExperienceOnCacheAcquireComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, pfRetry);
1578
1579 pContext->cSearchPathsMax = max(pContext->cSearchPaths, pContext->cSearchPathsMax);
1580
1581 return hr;
1582}
1583
1584static BOOL IsValidLocalFile(
1585 __in_z LPCWSTR wzFilePath,
1586 __in DWORD64 qwFileSize,
1587 __in BOOL fMinimumFileSize
1588 )
1589{
1590 LONGLONG llFileSize = 0;
1591
1592 if (!qwFileSize)
1593 {
1594 return FileExistsEx(wzFilePath, NULL);
1595 }
1596 else
1597 {
1598 return SUCCEEDED(FileSize(wzFilePath, &llFileSize)) &&
1599 (static_cast<DWORD64>(llFileSize) == qwFileSize ||
1600 fMinimumFileSize && static_cast<DWORD64>(llFileSize) > qwFileSize);
1601 }
1602}
1603
1604static HRESULT LayoutOrCacheContainerOrPayload(
1605 __in BURN_CACHE_CONTEXT* pContext,
1606 __in_opt BURN_CONTAINER* pContainer,
1607 __in_opt BURN_PACKAGE* pPackage,
1608 __in_opt BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem,
1609 __in DWORD cTryAgainAttempts,
1610 __out BOOL* pfRetry
1611 )
1612{
1613 HRESULT hr = S_OK;
1614 BURN_PAYLOAD* pPayload = pPayloadGroupItem ? pPayloadGroupItem->pPayload : NULL;
1615 LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : L"";
1616 LPCWSTR wzUnverifiedPath = pContainer ? pContainer->sczUnverifiedPath : pPayload->sczUnverifiedPath;
1617 LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : L"";
1618 BOOL fCanAffectRegistration = FALSE;
1619 BURN_CACHE_PROGRESS_CONTEXT progress = { };
1620 BOOL fMove = !pPayload || 1 == pPayload->cRemainingInstances;
1621 BOOL fCanceledBegin = FALSE;
1622
1623 if (pContainer)
1624 {
1625 Assert(!pPayloadGroupItem);
1626 }
1627 else
1628 {
1629 Assert(pPayload);
1630 AssertSz(0 < pPayload->cRemainingInstances, "Laying out payload more times than planned.");
1631 AssertSz(!pPayloadGroupItem->fCached, "Laying out payload group item that was already cached.");
1632 }
1633
1634 if (!pContext->wzLayoutDirectory)
1635 {
1636 Assert(!pContainer);
1637 Assert(pPackage);
1638
1639 fCanAffectRegistration = pPackage->fCanAffectRegistration;
1640 }
1641
1642 *pfRetry = FALSE;
1643 progress.pCacheContext = pContext;
1644 progress.pContainer = pContainer;
1645 progress.pPackage = pPackage;
1646 progress.pPayloadGroupItem = pPayloadGroupItem;
1647
1648 do
1649 {
1650 fCanceledBegin = FALSE;
1651
1652 hr = UserExperienceOnCacheVerifyBegin(pContext->pUX, wzPackageOrContainerId, wzPayloadId);
1653
1654 if (FAILED(hr))
1655 {
1656 fCanceledBegin = TRUE;
1657 }
1658 else
1659 {
1660 if (pContext->wzLayoutDirectory) // layout the container or payload.
1661 {
1662 if (pContainer)
1663 {
1664 hr = CacheLayoutContainer(pContainer, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress);
1665 }
1666 else
1667 {
1668 hr = CacheLayoutPayload(pPayload, pContext->wzLayoutDirectory, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress);
1669 }
1670 }
1671 else if (INVALID_HANDLE_VALUE != pContext->hPipe) // pass the decision off to the elevated process.
1672 {
1673 hr = ElevationCacheCompletePayload(pContext->hPipe, pPackage, pPayload, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress);
1674 }
1675 else // complete the payload.
1676 {
1677 hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, wzUnverifiedPath, fMove, CacheMessageHandler, CacheProgressRoutine, &progress);
1678 }
1679 }
1680
1681 if (SUCCEEDED(hr) && fCanAffectRegistration)
1682 {
1683 pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
1684 }
1685
1686 BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = FAILED(hr) && !fCanceledBegin && cTryAgainAttempts < BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS ? BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION : BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE;
1687 UserExperienceOnCacheVerifyComplete(pContext->pUX, wzPackageOrContainerId, wzPayloadId, hr, &action);
1688 if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action)
1689 {
1690 hr = S_FALSE; // retry verify.
1691 }
1692 else if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION == action)
1693 {
1694 *pfRetry = TRUE; // go back and retry acquire.
1695 }
1696 else if (fCanceledBegin)
1697 {
1698 ExitOnRootFailure(hr, "BA aborted cache verify begin.");
1699 }
1700 } while (S_FALSE == hr);
1701
1702 if (SUCCEEDED(hr) && pPayloadGroupItem)
1703 {
1704 pPayload->cRemainingInstances -= 1;
1705 pPayloadGroupItem->fCached = TRUE;
1706 }
1707
1708LExit:
1709 return hr;
1710}
1711
1712static HRESULT PreparePayloadDestinationPath(
1713 __in_z LPCWSTR wzDestinationPath
1714 )
1715{
1716 HRESULT hr = S_OK;
1717 DWORD dwFileAttributes = 0;
1718
1719 // If the destination file already exists, clear the readonly bit to avoid E_ACCESSDENIED.
1720 if (FileExistsEx(wzDestinationPath, &dwFileAttributes))
1721 {
1722 if (FILE_ATTRIBUTE_READONLY & dwFileAttributes)
1723 {
1724 dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY;
1725 if (!::SetFileAttributes(wzDestinationPath, dwFileAttributes))
1726 {
1727 ExitWithLastError(hr, "Failed to clear readonly bit on payload destination path: %ls", wzDestinationPath);
1728 }
1729 }
1730 }
1731
1732LExit:
1733 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
1734 {
1735 hr = S_OK;
1736 }
1737
1738 return hr;
1739}
1740
1741static HRESULT CopyPayload(
1742 __in BURN_CACHE_PROGRESS_CONTEXT* pProgress,
1743 __in HANDLE hSourceFile,
1744 __in_z LPCWSTR wzSourcePath,
1745 __in_z LPCWSTR wzDestinationPath
1746 )
1747{
1748 HRESULT hr = S_OK;
1749 LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L"";
1750 LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : L"";
1751 HANDLE hDestinationFile = INVALID_HANDLE_VALUE;
1752 HANDLE hSourceOpenedFile = INVALID_HANDLE_VALUE;
1753
1754 DWORD dwLogId = pProgress->pContainer ? MSG_ACQUIRE_CONTAINER : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD;
1755 LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "copy", wzSourcePath);
1756
1757 hr = PreparePayloadDestinationPath(wzDestinationPath);
1758 ExitOnFailure(hr, "Failed to prepare payload destination path: %ls", wzDestinationPath);
1759
1760 if (INVALID_HANDLE_VALUE == hSourceFile)
1761 {
1762 hSourceOpenedFile = ::CreateFileW(wzSourcePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1763 if (INVALID_HANDLE_VALUE == hSourceOpenedFile)
1764 {
1765 ExitWithLastError(hr, "Failed to open source file to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath);
1766 }
1767
1768 hSourceFile = hSourceOpenedFile;
1769 }
1770 else
1771 {
1772 hr = FileSetPointer(hSourceFile, 0, NULL, FILE_BEGIN);
1773 ExitOnRootFailure(hr, "Failed to read from start of source file to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath);
1774 }
1775
1776 hDestinationFile = ::CreateFileW(wzDestinationPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1777 if (INVALID_HANDLE_VALUE == hDestinationFile)
1778 {
1779 ExitWithLastError(hr, "Failed to open destination file to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath);
1780 }
1781
1782 hr = FileCopyUsingHandlesWithProgress(hSourceFile, hDestinationFile, 0, CacheProgressRoutine, pProgress);
1783 if (FAILED(hr))
1784 {
1785 if (pProgress->fCancel)
1786 {
1787 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1788 ExitOnRootFailure(hr, "BA aborted copy of payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath);
1789 }
1790 else
1791 {
1792 ExitOnRootFailure(hr, "Failed attempt to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath);
1793 }
1794 }
1795
1796LExit:
1797 ReleaseFileHandle(hDestinationFile);
1798 ReleaseFileHandle(hSourceOpenedFile);
1799
1800 return hr;
1801}
1802
1803static HRESULT DownloadPayload(
1804 __in BURN_CACHE_PROGRESS_CONTEXT* pProgress,
1805 __in_z LPCWSTR wzDestinationPath
1806 )
1807{
1808 HRESULT hr = S_OK;
1809 LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L"";
1810 LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : L"";
1811 DOWNLOAD_SOURCE* pDownloadSource = pProgress->pContainer ? &pProgress->pContainer->downloadSource : &pProgress->pPayloadGroupItem->pPayload->downloadSource;
1812 DWORD64 qwDownloadSize = pProgress->pContainer ? pProgress->pContainer->qwFileSize : pProgress->pPayloadGroupItem->pPayload->qwFileSize;
1813 DOWNLOAD_CACHE_CALLBACK cacheCallback = { };
1814 DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { };
1815 APPLY_AUTHENTICATION_REQUIRED_DATA authenticationData = { };
1816
1817 DWORD dwLogId = pProgress->pContainer ? MSG_ACQUIRE_CONTAINER : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD;
1818 LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "download", pDownloadSource->sczUrl);
1819
1820 hr = PreparePayloadDestinationPath(wzDestinationPath);
1821 ExitOnFailure(hr, "Failed to prepare payload destination path: %ls", wzDestinationPath);
1822
1823 cacheCallback.pfnProgress = CacheProgressRoutine;
1824 cacheCallback.pfnCancel = NULL; // TODO: set this
1825 cacheCallback.pv = pProgress;
1826
1827 authenticationData.pUX = pProgress->pCacheContext->pUX;
1828 authenticationData.wzPackageOrContainerId = wzPackageOrContainerId;
1829 authenticationData.wzPayloadId = wzPayloadId;
1830 authenticationCallback.pv = static_cast<LPVOID>(&authenticationData);
1831 authenticationCallback.pfnAuthenticate = &AuthenticationRequired;
1832
1833 hr = DownloadUrl(pDownloadSource, qwDownloadSize, wzDestinationPath, &cacheCallback, &authenticationCallback);
1834 ExitOnFailure(hr, "Failed attempt to download URL: '%ls' to: '%ls'", pDownloadSource->sczUrl, wzDestinationPath);
1835
1836LExit:
1837 return hr;
1838}
1839
1840static HRESULT WINAPI AuthenticationRequired(
1841 __in LPVOID pData,
1842 __in HINTERNET hUrl,
1843 __in long lHttpCode,
1844 __out BOOL* pfRetrySend,
1845 __out BOOL* pfRetry
1846 )
1847{
1848 Assert(401 == lHttpCode || 407 == lHttpCode);
1849
1850 HRESULT hr = S_OK;
1851 DWORD er = ERROR_SUCCESS;
1852 BOOTSTRAPPER_ERROR_TYPE errorType = (401 == lHttpCode) ? BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER : BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY;
1853 LPWSTR sczError = NULL;
1854 int nResult = IDNOACTION;
1855
1856 *pfRetrySend = FALSE;
1857 *pfRetry = FALSE;
1858
1859 hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL);
1860 ExitOnFailure(hr, "Failed to allocation error string.");
1861
1862 APPLY_AUTHENTICATION_REQUIRED_DATA* authenticationData = reinterpret_cast<APPLY_AUTHENTICATION_REQUIRED_DATA*>(pData);
1863
1864 UserExperienceOnError(authenticationData->pUX, errorType, authenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value;
1865 nResult = UserExperienceCheckExecuteResult(authenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult);
1866 if (IDTRYAGAIN == nResult && authenticationData->pUX->hwndApply)
1867 {
1868 er = ::InternetErrorDlg(authenticationData->pUX->hwndApply, hUrl, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL);
1869 if (ERROR_SUCCESS == er || ERROR_CANCELLED == er)
1870 {
1871 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1872 }
1873 else if (ERROR_INTERNET_FORCE_RETRY == er)
1874 {
1875 *pfRetrySend = TRUE;
1876 hr = S_OK;
1877 }
1878 else
1879 {
1880 hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
1881 }
1882 }
1883 else if (IDRETRY == nResult)
1884 {
1885 *pfRetry = TRUE;
1886 hr = S_OK;
1887 }
1888 else
1889 {
1890 hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
1891 }
1892
1893LExit:
1894 ReleaseStr(sczError);
1895
1896 return hr;
1897}
1898
1899static HRESULT CALLBACK CacheMessageHandler(
1900 __in BURN_CACHE_MESSAGE* pMessage,
1901 __in LPVOID pvContext
1902 )
1903{
1904 HRESULT hr = S_OK;
1905 BURN_CACHE_PROGRESS_CONTEXT* pProgress = static_cast<BURN_CACHE_PROGRESS_CONTEXT*>(pvContext);
1906 LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : NULL;
1907 LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : pProgress->pPayload ? pProgress->pPayload->sczKey : NULL;
1908
1909 switch (pMessage->type)
1910 {
1911 case BURN_CACHE_MESSAGE_BEGIN:
1912 switch (pMessage->begin.cacheStep)
1913 {
1914 case BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE:
1915 pProgress->type = BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY;
1916 hr = UserExperienceOnCacheContainerOrPayloadVerifyBegin(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId);
1917 break;
1918 case BURN_CACHE_STEP_HASH_TO_SKIP_VERIFY:
1919 pProgress->type = BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY;
1920 break;
1921 case BURN_CACHE_STEP_STAGE:
1922 pProgress->type = BURN_CACHE_PROGRESS_TYPE_STAGE;
1923 break;
1924 case BURN_CACHE_STEP_HASH:
1925 pProgress->type = BURN_CACHE_PROGRESS_TYPE_HASH;
1926 break;
1927 case BURN_CACHE_STEP_FINALIZE:
1928 pProgress->type = BURN_CACHE_PROGRESS_TYPE_FINALIZE;
1929 break;
1930 }
1931 break;
1932 case BURN_CACHE_MESSAGE_SUCCESS:
1933 hr = CompleteCacheProgress(pProgress, pMessage->success.qwFileSize);
1934 break;
1935 case BURN_CACHE_MESSAGE_COMPLETE:
1936 switch (pProgress->type)
1937 {
1938 case BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY:
1939 hr = UserExperienceOnCacheContainerOrPayloadVerifyComplete(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, hr);
1940 break;
1941 }
1942 }
1943
1944 return hr;
1945}
1946
1947static HRESULT CompleteCacheProgress(
1948 __in BURN_CACHE_PROGRESS_CONTEXT* pContext,
1949 __in DWORD64 qwFileSize
1950 )
1951{
1952 HRESULT hr = S_OK;
1953 LARGE_INTEGER liContainerOrPayloadSize = { };
1954 LARGE_INTEGER liZero = { };
1955 DWORD dwResult = 0;
1956 DWORD64 qwCommitSize = 0;
1957
1958 liContainerOrPayloadSize.QuadPart = qwFileSize;
1959
1960 // Need to commit the steps that were skipped.
1961 if (BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY == pContext->type || BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY == pContext->type)
1962 {
1963 Assert(!pContext->pPayload);
1964
1965 qwCommitSize = qwFileSize * (pContext->pCacheContext->wzLayoutDirectory ? 2 : 3); // Acquire (+ Stage) + Hash + Finalize - 1 (that's added later)
1966
1967 pContext->pCacheContext->qwSuccessfulCacheProgress += qwCommitSize;
1968
1969 if (pContext->pContainer)
1970 {
1971 pContext->pContainer->qwCommittedCacheProgress += qwCommitSize;
1972 }
1973 else if (pContext->pPayloadGroupItem)
1974 {
1975 pContext->pPayloadGroupItem->qwCommittedCacheProgress += qwCommitSize;
1976 }
1977 }
1978
1979 dwResult = CacheProgressRoutine(liContainerOrPayloadSize, liContainerOrPayloadSize, liZero, liZero, 0, 0, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, pContext);
1980
1981 if (PROGRESS_CONTINUE == dwResult)
1982 {
1983 pContext->pCacheContext->qwSuccessfulCacheProgress += qwFileSize;
1984
1985 if (pContext->pPayload)
1986 {
1987 pContext->pContainer->qwCommittedExtractProgress += qwFileSize;
1988 }
1989 else if (pContext->pContainer)
1990 {
1991 pContext->pContainer->qwCommittedCacheProgress += qwFileSize;
1992 }
1993 else if (pContext->pPayloadGroupItem)
1994 {
1995 pContext->pPayloadGroupItem->qwCommittedCacheProgress += qwFileSize;
1996 }
1997
1998 if (BURN_CACHE_PROGRESS_TYPE_FINALIZE == pContext->type && pContext->pCacheContext->sczLastUsedFolderCandidate)
1999 {
2000 // We successfully copied from a source location, set that as the last used source.
2001 CacheSetLastUsedSource(pContext->pCacheContext->pVariables, pContext->pCacheContext->sczLastUsedFolderCandidate, pContext->pContainer ? pContext->pContainer->sczFilePath : pContext->pPayloadGroupItem->pPayload->sczFilePath);
2002 }
2003 }
2004 else if (PROGRESS_CANCEL == dwResult)
2005 {
2006 if (pContext->fCancel)
2007 {
2008 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
2009 }
2010 else
2011 {
2012 hr = pContext->hrError;
2013 }
2014
2015 if (qwCommitSize)
2016 {
2017 pContext->pCacheContext->qwSuccessfulCacheProgress -= qwCommitSize;
2018
2019 if (pContext->pContainer)
2020 {
2021 pContext->pContainer->qwCommittedCacheProgress -= qwCommitSize;
2022 }
2023 else if (pContext->pPayloadGroupItem)
2024 {
2025 pContext->pPayloadGroupItem->qwCommittedCacheProgress -= qwCommitSize;
2026 }
2027 }
2028 }
2029
2030 return hr;
2031}
2032
2033static DWORD CALLBACK CacheProgressRoutine(
2034 __in LARGE_INTEGER TotalFileSize,
2035 __in LARGE_INTEGER TotalBytesTransferred,
2036 __in LARGE_INTEGER /*StreamSize*/,
2037 __in LARGE_INTEGER /*StreamBytesTransferred*/,
2038 __in DWORD /*dwStreamNumber*/,
2039 __in DWORD /*dwCallbackReason*/,
2040 __in HANDLE /*hSourceFile*/,
2041 __in HANDLE /*hDestinationFile*/,
2042 __in_opt LPVOID lpData
2043 )
2044{
2045 HRESULT hr = S_OK;
2046 DWORD dwResult = PROGRESS_CONTINUE;
2047 BURN_CACHE_PROGRESS_CONTEXT* pProgress = static_cast<BURN_CACHE_PROGRESS_CONTEXT*>(lpData);
2048 LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : NULL;
2049 LPCWSTR wzPayloadId = pProgress->pPayloadGroupItem ? pProgress->pPayloadGroupItem->pPayload->sczKey : pProgress->pPayload ? pProgress->pPayload->sczKey : NULL;
2050 DWORD64 qwCacheProgress = pProgress->pCacheContext->qwSuccessfulCacheProgress + TotalBytesTransferred.QuadPart;
2051 if (qwCacheProgress > pProgress->pCacheContext->qwTotalCacheSize)
2052 {
2053 //AssertSz(FALSE, "Apply has cached more than Plan envisioned.");
2054 qwCacheProgress = pProgress->pCacheContext->qwTotalCacheSize;
2055 }
2056 DWORD dwOverallPercentage = pProgress->pCacheContext->qwTotalCacheSize ? static_cast<DWORD>(qwCacheProgress * 100 / pProgress->pCacheContext->qwTotalCacheSize) : 0;
2057
2058 switch (pProgress->type)
2059 {
2060 case BURN_CACHE_PROGRESS_TYPE_ACQUIRE:
2061 hr = UserExperienceOnCacheAcquireProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage);
2062 ExitOnRootFailure(hr, "BA aborted acquire of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId);
2063 break;
2064 case BURN_CACHE_PROGRESS_TYPE_PAYLOAD_VERIFY:
2065 hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_HASH);
2066 ExitOnRootFailure(hr, "BA aborted payload verify step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId);
2067 break;
2068 case BURN_CACHE_PROGRESS_TYPE_STAGE:
2069 hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_STAGE);
2070 ExitOnRootFailure(hr, "BA aborted stage step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId);
2071 break;
2072 case BURN_CACHE_PROGRESS_TYPE_HASH:
2073 hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_HASH);
2074 ExitOnRootFailure(hr, "BA aborted hash step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId);
2075 break;
2076 case BURN_CACHE_PROGRESS_TYPE_FINALIZE:
2077 hr = UserExperienceOnCacheVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage, BOOTSTRAPPER_CACHE_VERIFY_STEP_FINALIZE);
2078 ExitOnRootFailure(hr, "BA aborted finalize step during verify of %hs: %ls", pProgress->pContainer ? "container" : "payload", pProgress->pContainer ? wzPackageOrContainerId : wzPayloadId);
2079 break;
2080 case BURN_CACHE_PROGRESS_TYPE_CONTAINER_OR_PAYLOAD_VERIFY:
2081 hr = UserExperienceOnCacheContainerOrPayloadVerifyProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage);
2082 ExitOnRootFailure(hr, "BA aborted container or payload verify: %ls", wzPayloadId);
2083 break;
2084 case BURN_CACHE_PROGRESS_TYPE_EXTRACT:
2085 hr = UserExperienceOnCachePayloadExtractProgress(pProgress->pCacheContext->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage);
2086 ExitOnRootFailure(hr, "BA aborted extract container: %ls, payload: %ls", wzPackageOrContainerId, wzPayloadId);
2087 break;
2088 }
2089
2090LExit:
2091 if (HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr)
2092 {
2093 dwResult = PROGRESS_CANCEL;
2094 pProgress->fCancel = TRUE;
2095 }
2096 else if (FAILED(hr))
2097 {
2098 dwResult = PROGRESS_CANCEL;
2099 pProgress->hrError = hr;
2100 }
2101 else
2102 {
2103 dwResult = PROGRESS_CONTINUE;
2104 }
2105
2106 return dwResult;
2107}
2108
2109static void DoRollbackCache(
2110 __in BURN_USER_EXPERIENCE* /*pUX*/,
2111 __in BURN_PLAN* pPlan,
2112 __in HANDLE hPipe,
2113 __in DWORD dwCheckpoint
2114 )
2115{
2116 HRESULT hr = S_OK;
2117 DWORD iCheckpoint = 0;
2118
2119 // Scan to last checkpoint.
2120 for (DWORD i = 0; i < pPlan->cRollbackCacheActions; ++i)
2121 {
2122 BURN_CACHE_ACTION* pRollbackCacheAction = &pPlan->rgRollbackCacheActions[i];
2123
2124 if (BURN_CACHE_ACTION_TYPE_CHECKPOINT == pRollbackCacheAction->type && pRollbackCacheAction->checkpoint.dwId == dwCheckpoint)
2125 {
2126 iCheckpoint = i;
2127 break;
2128 }
2129 }
2130
2131 // Rollback cache actions.
2132 if (iCheckpoint)
2133 {
2134 // i has to be a signed integer so it doesn't get decremented to 0xFFFFFFFF.
2135 for (int i = iCheckpoint - 1; i >= 0; --i)
2136 {
2137 BURN_CACHE_ACTION* pRollbackCacheAction = &pPlan->rgRollbackCacheActions[i];
2138
2139 switch (pRollbackCacheAction->type)
2140 {
2141 case BURN_CACHE_ACTION_TYPE_CHECKPOINT:
2142 break;
2143
2144 case BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE:
2145 hr = CleanPackage(hPipe, pRollbackCacheAction->rollbackPackage.pPackage);
2146 break;
2147
2148 default:
2149 AssertSz(FALSE, "Invalid rollback cache action.");
2150 break;
2151 }
2152 }
2153 }
2154}
2155
2156static HRESULT DoExecuteAction(
2157 __in BURN_ENGINE_STATE* pEngineState,
2158 __in BURN_EXECUTE_ACTION* pExecuteAction,
2159 __in_opt HANDLE hCacheThread,
2160 __in BURN_EXECUTE_CONTEXT* pContext,
2161 __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary,
2162 __inout BURN_EXECUTE_ACTION_CHECKPOINT** ppCheckpoint,
2163 __out BOOL* pfSuspend,
2164 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2165 )
2166{
2167 Assert(!pExecuteAction->fDeleted);
2168
2169 HRESULT hr = S_OK;
2170 HANDLE rghWait[2] = { };
2171 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2172 BOOL fRetry = FALSE;
2173 BOOL fStopWusaService = FALSE;
2174 BOOL fInsideMsiTransaction = FALSE;
2175
2176 pContext->fRollback = FALSE;
2177
2178 do
2179 {
2180 fInsideMsiTransaction = *ppRollbackBoundary && (*ppRollbackBoundary)->fActiveTransaction;
2181
2182 switch (pExecuteAction->type)
2183 {
2184 case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT:
2185 *ppCheckpoint = &pExecuteAction->checkpoint;
2186 break;
2187
2188 case BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT:
2189 // wait for cache sync-point
2190 rghWait[0] = pExecuteAction->syncpoint.hEvent;
2191 rghWait[1] = hCacheThread;
2192 switch (::WaitForMultipleObjects(rghWait[1] ? 2 : 1, rghWait, FALSE, INFINITE))
2193 {
2194 case WAIT_OBJECT_0:
2195 break;
2196
2197 case WAIT_OBJECT_0 + 1:
2198 if (!::GetExitCodeThread(hCacheThread, (DWORD*)&hr))
2199 {
2200 ExitWithLastError(hr, "Failed to get cache thread exit code.");
2201 }
2202
2203 if (SUCCEEDED(hr))
2204 {
2205 hr = E_UNEXPECTED;
2206 }
2207 ExitOnFailure(hr, "Cache thread exited unexpectedly.");
2208
2209 case WAIT_FAILED: __fallthrough;
2210 default:
2211 ExitWithLastError(hr, "Failed to wait for cache check-point.");
2212 }
2213 break;
2214
2215 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
2216 hr = ExecuteExePackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart);
2217 ExitOnFailure(hr, "Failed to execute EXE package.");
2218 break;
2219
2220 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
2221 hr = ExecuteMsiPackage(pEngineState, pExecuteAction, pContext, fInsideMsiTransaction, FALSE, &fRetry, pfSuspend, &restart);
2222 ExitOnFailure(hr, "Failed to execute MSI package.");
2223 break;
2224
2225 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
2226 hr = ExecuteMspPackage(pEngineState, pExecuteAction, pContext, fInsideMsiTransaction, FALSE, &fRetry, pfSuspend, &restart);
2227 ExitOnFailure(hr, "Failed to execute MSP package.");
2228 break;
2229
2230 case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
2231 hr = ExecuteMsuPackage(pEngineState, pExecuteAction, pContext, FALSE, fStopWusaService, &fRetry, pfSuspend, &restart);
2232 fStopWusaService = fRetry;
2233 ExitOnFailure(hr, "Failed to execute MSU package.");
2234 break;
2235
2236 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER:
2237 hr = ExecutePackageProviderAction(pEngineState, pExecuteAction, pContext);
2238 ExitOnFailure(hr, "Failed to execute package provider registration action.");
2239 break;
2240
2241 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY:
2242 hr = ExecuteDependencyAction(pEngineState, pExecuteAction, pContext);
2243 ExitOnFailure(hr, "Failed to execute dependency action.");
2244 break;
2245
2246 break;
2247
2248 case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY:
2249 *ppRollbackBoundary = pExecuteAction->rollbackBoundary.pRollbackBoundary;
2250 break;
2251
2252 case BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION:
2253 hr = ExecuteMsiBeginTransaction(pEngineState, pExecuteAction->msiTransaction.pRollbackBoundary, pContext);
2254 ExitOnFailure(hr, "Failed to execute begin MSI transaction action.");
2255 break;
2256
2257 case BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION:
2258 hr = ExecuteMsiCommitTransaction(pEngineState, pExecuteAction->msiTransaction.pRollbackBoundary, pContext);
2259 ExitOnFailure(hr, "Failed to execute commit MSI transaction action.");
2260 break;
2261
2262 default:
2263 hr = E_UNEXPECTED;
2264 ExitOnFailure(hr, "Invalid execute action.");
2265 }
2266
2267 if (*pRestart < restart)
2268 {
2269 *pRestart = restart;
2270 }
2271 } while (fRetry && *pRestart < BOOTSTRAPPER_APPLY_RESTART_INITIATED);
2272
2273LExit:
2274 return hr;
2275}
2276
2277static HRESULT DoRollbackActions(
2278 __in BURN_ENGINE_STATE* pEngineState,
2279 __in BURN_EXECUTE_CONTEXT* pContext,
2280 __in DWORD dwCheckpoint,
2281 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2282 )
2283{
2284 HRESULT hr = S_OK;
2285 DWORD iCheckpoint = 0;
2286 BOOL fRetryIgnored = FALSE;
2287 BOOL fSuspendIgnored = FALSE;
2288
2289 pContext->fRollback = TRUE;
2290
2291 // scan to last checkpoint
2292 for (DWORD i = 0; i < pEngineState->plan.cRollbackActions; ++i)
2293 {
2294 BURN_EXECUTE_ACTION* pRollbackAction = &pEngineState->plan.rgRollbackActions[i];
2295 if (pRollbackAction->fDeleted)
2296 {
2297 continue;
2298 }
2299
2300 if (BURN_EXECUTE_ACTION_TYPE_CHECKPOINT == pRollbackAction->type)
2301 {
2302 if (pRollbackAction->checkpoint.dwId == dwCheckpoint)
2303 {
2304 iCheckpoint = i;
2305 break;
2306 }
2307 }
2308 }
2309
2310 // execute rollback actions
2311 if (iCheckpoint)
2312 {
2313 // i has to be a signed integer so it doesn't get decremented to 0xFFFFFFFF.
2314 for (int i = iCheckpoint - 1; i >= 0; --i)
2315 {
2316 BURN_EXECUTE_ACTION* pRollbackAction = &pEngineState->plan.rgRollbackActions[i];
2317 if (pRollbackAction->fDeleted)
2318 {
2319 continue;
2320 }
2321
2322 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2323 switch (pRollbackAction->type)
2324 {
2325 case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT:
2326 break;
2327
2328 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
2329 hr = ExecuteExePackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart);
2330 IgnoreRollbackError(hr, "Failed to rollback EXE package.");
2331 break;
2332
2333 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
2334 hr = ExecuteMsiPackage(pEngineState, pRollbackAction, pContext, FALSE, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart);
2335 IgnoreRollbackError(hr, "Failed to rollback MSI package.");
2336 break;
2337
2338 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
2339 hr = ExecuteMspPackage(pEngineState, pRollbackAction, pContext, FALSE, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart);
2340 IgnoreRollbackError(hr, "Failed to rollback MSP package.");
2341 break;
2342
2343 case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
2344 hr = ExecuteMsuPackage(pEngineState, pRollbackAction, pContext, TRUE, FALSE, &fRetryIgnored, &fSuspendIgnored, &restart);
2345 IgnoreRollbackError(hr, "Failed to rollback MSU package.");
2346 break;
2347
2348 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER:
2349 hr = ExecutePackageProviderAction(pEngineState, pRollbackAction, pContext);
2350 IgnoreRollbackError(hr, "Failed to rollback package provider action.");
2351 break;
2352
2353 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY:
2354 hr = ExecuteDependencyAction(pEngineState, pRollbackAction, pContext);
2355 IgnoreRollbackError(hr, "Failed to rollback dependency action.");
2356 break;
2357
2358 case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY:
2359 ExitFunction1(hr = S_OK);
2360
2361 case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE:
2362 // TODO: This used to be skipped if the package was already cached.
2363 // Need to figure out new logic for when (if?) to skip it.
2364 hr = CleanPackage(pEngineState->companionConnection.hPipe, pRollbackAction->uncachePackage.pPackage);
2365 IgnoreRollbackError(hr, "Failed to uncache package for rollback.");
2366 break;
2367
2368 default:
2369 hr = E_UNEXPECTED;
2370 ExitOnFailure(hr, "Invalid rollback action: %d.", pRollbackAction->type);
2371 }
2372
2373 if (*pRestart < restart)
2374 {
2375 *pRestart = restart;
2376 }
2377 }
2378 }
2379
2380LExit:
2381 return hr;
2382}
2383
2384static HRESULT ExecuteExePackage(
2385 __in BURN_ENGINE_STATE* pEngineState,
2386 __in BURN_EXECUTE_ACTION* pExecuteAction,
2387 __in BURN_EXECUTE_CONTEXT* pContext,
2388 __in BOOL fRollback,
2389 __out BOOL* pfRetry,
2390 __out BOOL* pfSuspend,
2391 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2392 )
2393{
2394 HRESULT hr = S_OK;
2395 HRESULT hrExecute = S_OK;
2396 GENERIC_EXECUTE_MESSAGE message = { };
2397 int nResult = 0;
2398 BOOL fBeginCalled = FALSE;
2399 BOOL fExecuted = FALSE;
2400 BURN_PACKAGE* pPackage = pExecuteAction->exePackage.pPackage;
2401
2402 if (FAILED(pPackage->hrCacheResult))
2403 {
2404 LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult);
2405 ExitFunction1(hr = S_OK);
2406 }
2407
2408 Assert(pContext->fRollback == fRollback);
2409 pContext->pExecutingPackage = pPackage;
2410 fBeginCalled = TRUE;
2411
2412 // Send package execute begin to BA.
2413 hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->exePackage.action, INSTALLUILEVEL_NOCHANGE, FALSE);
2414 ExitOnRootFailure(hr, "BA aborted execute EXE package begin.");
2415
2416 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2417 message.dwAllowedResults = MB_OKCANCEL;
2418 message.progress.dwPercentage = fRollback ? 100 : 0;
2419 nResult = GenericExecuteMessageHandler(&message, pContext);
2420 hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult);
2421 ExitOnRootFailure(hr, "BA aborted EXE progress.");
2422
2423 fExecuted = TRUE;
2424
2425 // Execute package.
2426 if (pPackage->fPerMachine)
2427 {
2428 hrExecute = ElevationExecuteExePackage(pEngineState->companionConnection.hPipe, pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart);
2429 ExitOnFailure(hrExecute, "Failed to configure per-machine EXE package.");
2430 }
2431 else
2432 {
2433 hrExecute = ExeEngineExecutePackage(pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart);
2434 ExitOnFailure(hrExecute, "Failed to configure per-user EXE package.");
2435 }
2436
2437 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2438 message.dwAllowedResults = MB_OKCANCEL;
2439 message.progress.dwPercentage = fRollback ? 0 : 100;
2440 nResult = GenericExecuteMessageHandler(&message, pContext);
2441 hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult);
2442 ExitOnRootFailure(hr, "BA aborted EXE progress.");
2443
2444 pContext->cExecutedPackages += fRollback ? -1 : 1;
2445 (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1;
2446
2447 hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks);
2448 ExitOnRootFailure(hr, "BA aborted EXE package execute progress.");
2449
2450LExit:
2451 if (fExecuted)
2452 {
2453 ExeEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute);
2454 }
2455
2456 if (fBeginCalled)
2457 {
2458 hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2459 }
2460
2461 return hr;
2462}
2463
2464static HRESULT ExecuteMsiPackage(
2465 __in BURN_ENGINE_STATE* pEngineState,
2466 __in BURN_EXECUTE_ACTION* pExecuteAction,
2467 __in BURN_EXECUTE_CONTEXT* pContext,
2468 __in BOOL fInsideMsiTransaction,
2469 __in BOOL fRollback,
2470 __out BOOL* pfRetry,
2471 __out BOOL* pfSuspend,
2472 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2473 )
2474{
2475 HRESULT hr = S_OK;
2476 HRESULT hrExecute = S_OK;
2477 BOOL fBeginCalled = FALSE;
2478 BOOL fExecuted = FALSE;
2479 BURN_PACKAGE* pPackage = pExecuteAction->msiPackage.pPackage;
2480
2481 if (FAILED(pPackage->hrCacheResult))
2482 {
2483 LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult);
2484 ExitFunction1(hr = S_OK);
2485 }
2486
2487 Assert(pContext->fRollback == fRollback);
2488 pContext->pExecutingPackage = pPackage;
2489 fBeginCalled = TRUE;
2490
2491 // Send package execute begin to BA.
2492 hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->msiPackage.action, pExecuteAction->msiPackage.uiLevel, pExecuteAction->msiPackage.fDisableExternalUiHandler);
2493 ExitOnRootFailure(hr, "BA aborted execute MSI package begin.");
2494
2495 fExecuted = TRUE;
2496
2497 // execute package
2498 if (pPackage->fPerMachine)
2499 {
2500 hrExecute = ElevationExecuteMsiPackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart);
2501 ExitOnFailure(hrExecute, "Failed to configure per-machine MSI package.");
2502 }
2503 else
2504 {
2505 hrExecute = MsiEngineExecutePackage(pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart);
2506 ExitOnFailure(hrExecute, "Failed to configure per-user MSI package.");
2507 }
2508
2509 pContext->cExecutedPackages += fRollback ? -1 : 1;
2510 (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1;
2511
2512 hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks);
2513 ExitOnRootFailure(hr, "BA aborted MSI package execute progress.");
2514
2515LExit:
2516 if (fExecuted)
2517 {
2518 MsiEngineUpdateInstallRegistrationState(pExecuteAction, fRollback, hrExecute, fInsideMsiTransaction);
2519 }
2520
2521 if (fBeginCalled)
2522 {
2523 hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2524 }
2525
2526 return hr;
2527}
2528
2529static HRESULT ExecuteMspPackage(
2530 __in BURN_ENGINE_STATE* pEngineState,
2531 __in BURN_EXECUTE_ACTION* pExecuteAction,
2532 __in BURN_EXECUTE_CONTEXT* pContext,
2533 __in BOOL fInsideMsiTransaction,
2534 __in BOOL fRollback,
2535 __out BOOL* pfRetry,
2536 __out BOOL* pfSuspend,
2537 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2538 )
2539{
2540 HRESULT hr = S_OK;
2541 HRESULT hrExecute = S_OK;
2542 BOOL fBeginCalled = FALSE;
2543 BOOL fExecuted = FALSE;
2544 BURN_PACKAGE* pPackage = pExecuteAction->mspTarget.pPackage;
2545
2546 if (FAILED(pPackage->hrCacheResult))
2547 {
2548 LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult);
2549 ExitFunction1(hr = S_OK);
2550 }
2551
2552 Assert(pContext->fRollback == fRollback);
2553 pContext->pExecutingPackage = pPackage;
2554 fBeginCalled = TRUE;
2555
2556 // Send package execute begin to BA.
2557 hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->mspTarget.action, pExecuteAction->mspTarget.uiLevel, pExecuteAction->mspTarget.fDisableExternalUiHandler);
2558 ExitOnRootFailure(hr, "BA aborted execute MSP package begin.");
2559
2560 // Now send all the patches that target this product code.
2561 for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i)
2562 {
2563 BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage;
2564
2565 hr = UserExperienceOnExecutePatchTarget(&pEngineState->userExperience, pMspPackage->sczId, pExecuteAction->mspTarget.sczTargetProductCode);
2566 ExitOnRootFailure(hr, "BA aborted execute MSP target.");
2567 }
2568
2569 fExecuted = TRUE;
2570
2571 // execute package
2572 if (pExecuteAction->mspTarget.fPerMachineTarget)
2573 {
2574 hrExecute = ElevationExecuteMspPackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart);
2575 ExitOnFailure(hrExecute, "Failed to configure per-machine MSP package.");
2576 }
2577 else
2578 {
2579 hrExecute = MspEngineExecutePackage(pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart);
2580 ExitOnFailure(hrExecute, "Failed to configure per-user MSP package.");
2581 }
2582
2583 pContext->cExecutedPackages += fRollback ? -1 : 1;
2584 (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1;
2585
2586 hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks);
2587 ExitOnRootFailure(hr, "BA aborted MSP package execute progress.");
2588
2589LExit:
2590 if (fExecuted)
2591 {
2592 MspEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute, fInsideMsiTransaction);
2593 }
2594
2595 if (fBeginCalled)
2596 {
2597 hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2598 }
2599
2600 return hr;
2601}
2602
2603static HRESULT ExecuteMsuPackage(
2604 __in BURN_ENGINE_STATE* pEngineState,
2605 __in BURN_EXECUTE_ACTION* pExecuteAction,
2606 __in BURN_EXECUTE_CONTEXT* pContext,
2607 __in BOOL fRollback,
2608 __in BOOL fStopWusaService,
2609 __out BOOL* pfRetry,
2610 __out BOOL* pfSuspend,
2611 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2612 )
2613{
2614 HRESULT hr = S_OK;
2615 HRESULT hrExecute = S_OK;
2616 GENERIC_EXECUTE_MESSAGE message = { };
2617 int nResult = 0;
2618 BOOL fBeginCalled = FALSE;
2619 BOOL fExecuted = FALSE;
2620 BURN_PACKAGE* pPackage = pExecuteAction->msuPackage.pPackage;
2621
2622 if (FAILED(pPackage->hrCacheResult))
2623 {
2624 LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pPackage->sczId, pPackage->hrCacheResult);
2625 ExitFunction1(hr = S_OK);
2626 }
2627
2628 Assert(pContext->fRollback == fRollback);
2629 pContext->pExecutingPackage = pPackage;
2630 fBeginCalled = TRUE;
2631
2632 // Send package execute begin to BA.
2633 hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pPackage->sczId, !fRollback, pExecuteAction->msuPackage.action, INSTALLUILEVEL_NOCHANGE, FALSE);
2634 ExitOnRootFailure(hr, "BA aborted execute MSU package begin.");
2635
2636 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2637 message.dwAllowedResults = MB_OKCANCEL;
2638 message.progress.dwPercentage = fRollback ? 100 : 0;
2639 nResult = GenericExecuteMessageHandler(&message, pContext);
2640 hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult);
2641 ExitOnRootFailure(hr, "BA aborted MSU progress.");
2642
2643 fExecuted = TRUE;
2644
2645 // execute package
2646 if (pPackage->fPerMachine)
2647 {
2648 hrExecute = ElevationExecuteMsuPackage(pEngineState->companionConnection.hPipe, pExecuteAction, fRollback, fStopWusaService, GenericExecuteMessageHandler, pContext, pRestart);
2649 ExitOnFailure(hrExecute, "Failed to configure per-machine MSU package.");
2650 }
2651 else
2652 {
2653 hrExecute = E_UNEXPECTED;
2654 ExitOnFailure(hr, "MSU packages cannot be per-user.");
2655 }
2656
2657 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2658 message.dwAllowedResults = MB_OKCANCEL;
2659 message.progress.dwPercentage = fRollback ? 0 : 100;
2660 nResult = GenericExecuteMessageHandler(&message, pContext);
2661 hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult);
2662 ExitOnRootFailure(hr, "BA aborted MSU progress.");
2663
2664 pContext->cExecutedPackages += fRollback ? -1 : 1;
2665 (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1;
2666
2667 hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks);
2668 ExitOnRootFailure(hr, "BA aborted MSU package execute progress.");
2669
2670LExit:
2671 if (fExecuted)
2672 {
2673 MsuEngineUpdateInstallRegistrationState(pExecuteAction, hrExecute);
2674 }
2675
2676 if (fBeginCalled)
2677 {
2678 hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2679 }
2680
2681 return hr;
2682}
2683
2684static HRESULT ExecutePackageProviderAction(
2685 __in BURN_ENGINE_STATE* pEngineState,
2686 __in BURN_EXECUTE_ACTION* pAction,
2687 __in BURN_EXECUTE_CONTEXT* /*pContext*/
2688 )
2689{
2690 HRESULT hr = S_OK;
2691
2692 if (pAction->packageProvider.pPackage->fPerMachine)
2693 {
2694 hr = ElevationExecutePackageProviderAction(pEngineState->companionConnection.hPipe, pAction);
2695 ExitOnFailure(hr, "Failed to register the package provider on per-machine package.");
2696 }
2697 else
2698 {
2699 hr = DependencyExecutePackageProviderAction(pAction);
2700 ExitOnFailure(hr, "Failed to register the package provider on per-user package.");
2701 }
2702
2703LExit:
2704 return hr;
2705}
2706
2707static HRESULT ExecuteDependencyAction(
2708 __in BURN_ENGINE_STATE* pEngineState,
2709 __in BURN_EXECUTE_ACTION* pAction,
2710 __in BURN_EXECUTE_CONTEXT* /*pContext*/
2711 )
2712{
2713 HRESULT hr = S_OK;
2714
2715 if (pAction->packageDependency.pPackage->fPerMachine)
2716 {
2717 hr = ElevationExecutePackageDependencyAction(pEngineState->companionConnection.hPipe, pAction);
2718 ExitOnFailure(hr, "Failed to register the dependency on per-machine package.");
2719 }
2720 else
2721 {
2722 hr = DependencyExecutePackageDependencyAction(FALSE, pAction);
2723 ExitOnFailure(hr, "Failed to register the dependency on per-user package.");
2724 }
2725
2726 if (pAction->packageDependency.pPackage->fCanAffectRegistration)
2727 {
2728 if (BURN_DEPENDENCY_ACTION_REGISTER == pAction->packageDependency.action)
2729 {
2730 if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pAction->packageDependency.pPackage->cacheRegistrationState)
2731 {
2732 pAction->packageDependency.pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
2733 }
2734
2735 if (BURN_PACKAGE_TYPE_MSP == pAction->packageDependency.pPackage->type)
2736 {
2737 for (DWORD i = 0; i < pAction->packageDependency.pPackage->Msp.cTargetProductCodes; ++i)
2738 {
2739 BURN_MSPTARGETPRODUCT* pTargetProduct = pAction->packageDependency.pPackage->Msp.rgTargetProducts + i;
2740
2741 if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pTargetProduct->registrationState)
2742 {
2743 pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
2744 }
2745 }
2746 }
2747 else if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pAction->packageDependency.pPackage->installRegistrationState)
2748 {
2749 pAction->packageDependency.pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
2750 }
2751 }
2752 else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pAction->packageDependency.action)
2753 {
2754 if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pAction->packageDependency.pPackage->cacheRegistrationState)
2755 {
2756 pAction->packageDependency.pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED;
2757 }
2758
2759 if (BURN_PACKAGE_TYPE_MSP == pAction->packageDependency.pPackage->type)
2760 {
2761 for (DWORD i = 0; i < pAction->packageDependency.pPackage->Msp.cTargetProductCodes; ++i)
2762 {
2763 BURN_MSPTARGETPRODUCT* pTargetProduct = pAction->packageDependency.pPackage->Msp.rgTargetProducts + i;
2764
2765 if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pTargetProduct->registrationState)
2766 {
2767 pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED;
2768 }
2769 }
2770 }
2771 else if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pAction->packageDependency.pPackage->installRegistrationState)
2772 {
2773 pAction->packageDependency.pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED;
2774 }
2775 }
2776 }
2777
2778LExit:
2779 return hr;
2780}
2781
2782static HRESULT ExecuteMsiBeginTransaction(
2783 __in BURN_ENGINE_STATE* pEngineState,
2784 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary,
2785 __in BURN_EXECUTE_CONTEXT* /*pContext*/
2786 )
2787{
2788 HRESULT hr = S_OK;
2789 BOOL fBeginCalled = FALSE;
2790
2791 if (pRollbackBoundary->fActiveTransaction)
2792 {
2793 ExitFunction1(hr = E_INVALIDSTATE);
2794 }
2795
2796 fBeginCalled = TRUE;
2797 hr = UserExperienceOnBeginMsiTransactionBegin(&pEngineState->userExperience, pRollbackBoundary->sczId);
2798 ExitOnRootFailure(hr, "BA aborted execute begin MSI transaction.");
2799
2800 if (pEngineState->plan.fPerMachine)
2801 {
2802 hr = ElevationMsiBeginTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary);
2803 ExitOnFailure(hr, "Failed to begin an elevated MSI transaction.");
2804 }
2805 else
2806 {
2807 hr = MsiEngineBeginTransaction(pRollbackBoundary);
2808 }
2809
2810 if (SUCCEEDED(hr))
2811 {
2812 pRollbackBoundary->fActiveTransaction = TRUE;
2813
2814 ResetTransactionRegistrationState(pEngineState, FALSE);
2815 }
2816
2817LExit:
2818 if (fBeginCalled)
2819 {
2820 UserExperienceOnBeginMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr);
2821 }
2822
2823 return hr;
2824}
2825
2826static HRESULT ExecuteMsiCommitTransaction(
2827 __in BURN_ENGINE_STATE* pEngineState,
2828 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary,
2829 __in BURN_EXECUTE_CONTEXT* /*pContext*/
2830 )
2831{
2832 HRESULT hr = S_OK;
2833 BOOL fCommitBeginCalled = FALSE;
2834
2835 if (!pRollbackBoundary->fActiveTransaction)
2836 {
2837 ExitFunction1(hr = E_INVALIDSTATE);
2838 }
2839
2840 fCommitBeginCalled = TRUE;
2841 hr = UserExperienceOnCommitMsiTransactionBegin(&pEngineState->userExperience, pRollbackBoundary->sczId);
2842 ExitOnRootFailure(hr, "BA aborted execute commit MSI transaction.");
2843
2844 if (pEngineState->plan.fPerMachine)
2845 {
2846 hr = ElevationMsiCommitTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary);
2847 ExitOnFailure(hr, "Failed to commit an elevated MSI transaction.");
2848 }
2849 else
2850 {
2851 hr = MsiEngineCommitTransaction(pRollbackBoundary);
2852 }
2853
2854 if (SUCCEEDED(hr))
2855 {
2856 pRollbackBoundary->fActiveTransaction = FALSE;
2857
2858 ResetTransactionRegistrationState(pEngineState, TRUE);
2859 }
2860
2861LExit:
2862 if (fCommitBeginCalled)
2863 {
2864 UserExperienceOnCommitMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr);
2865 }
2866
2867 return hr;
2868}
2869
2870static HRESULT ExecuteMsiRollbackTransaction(
2871 __in BURN_ENGINE_STATE* pEngineState,
2872 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary,
2873 __in BURN_EXECUTE_CONTEXT* /*pContext*/
2874 )
2875{
2876 HRESULT hr = S_OK;
2877 BOOL fRollbackBeginCalled = FALSE;
2878
2879 if (!pRollbackBoundary->fActiveTransaction)
2880 {
2881 ExitFunction();
2882 }
2883
2884 fRollbackBeginCalled = TRUE;
2885 UserExperienceOnRollbackMsiTransactionBegin(&pEngineState->userExperience, pRollbackBoundary->sczId);
2886
2887 if (pEngineState->plan.fPerMachine)
2888 {
2889 hr = ElevationMsiRollbackTransaction(pEngineState->companionConnection.hPipe, pRollbackBoundary);
2890 ExitOnFailure(hr, "Failed to rollback an elevated MSI transaction.");
2891 }
2892 else
2893 {
2894 hr = MsiEngineRollbackTransaction(pRollbackBoundary);
2895 }
2896
2897LExit:
2898 pRollbackBoundary->fActiveTransaction = FALSE;
2899
2900 ResetTransactionRegistrationState(pEngineState, FALSE);
2901
2902 if (fRollbackBeginCalled)
2903 {
2904 UserExperienceOnRollbackMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr);
2905 }
2906
2907 return hr;
2908}
2909
2910static void ResetTransactionRegistrationState(
2911 __in BURN_ENGINE_STATE* pEngineState,
2912 __in BOOL fCommit
2913 )
2914{
2915 for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i)
2916 {
2917 BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i;
2918
2919 if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
2920 {
2921 for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j)
2922 {
2923 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + j;
2924
2925 if (fCommit && BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN != pTargetProduct->transactionRegistrationState)
2926 {
2927 pTargetProduct->registrationState = pTargetProduct->transactionRegistrationState;
2928 }
2929
2930 pTargetProduct->transactionRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
2931 }
2932 }
2933 else if (fCommit && BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN != pPackage->transactionRegistrationState)
2934 {
2935 pPackage->installRegistrationState = pPackage->transactionRegistrationState;
2936 }
2937
2938 pPackage->transactionRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
2939 }
2940}
2941
2942static HRESULT CleanPackage(
2943 __in HANDLE hElevatedPipe,
2944 __in BURN_PACKAGE* pPackage
2945 )
2946{
2947 HRESULT hr = S_OK;
2948
2949 if (pPackage->fPerMachine)
2950 {
2951 hr = ElevationCleanPackage(hElevatedPipe, pPackage);
2952 }
2953 else
2954 {
2955 hr = CacheRemovePackage(FALSE, pPackage->sczId, pPackage->sczCacheId);
2956 }
2957
2958 if (pPackage->fCanAffectRegistration)
2959 {
2960 pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
2961 }
2962
2963 return hr;
2964}
2965
2966static int GenericExecuteMessageHandler(
2967 __in GENERIC_EXECUTE_MESSAGE* pMessage,
2968 __in LPVOID pvContext
2969 )
2970{
2971 BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext;
2972 int nResult = IDNOACTION;
2973
2974 switch (pMessage->type)
2975 {
2976 case GENERIC_EXECUTE_MESSAGE_PROGRESS:
2977 {
2978 DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0;
2979 UserExperienceOnExecuteProgress(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value.
2980 }
2981 break;
2982
2983 case GENERIC_EXECUTE_MESSAGE_ERROR:
2984 UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_EXE_PACKAGE, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwAllowedResults, 0, NULL, &nResult); // ignore return value.
2985 break;
2986
2987 case GENERIC_EXECUTE_MESSAGE_FILES_IN_USE:
2988 UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->filesInUse.cFiles, pMessage->filesInUse.rgwzFiles, &nResult); // ignore return value.
2989 break;
2990 }
2991
2992 nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult);
2993 return nResult;
2994}
2995
2996static int MsiExecuteMessageHandler(
2997 __in WIU_MSI_EXECUTE_MESSAGE* pMessage,
2998 __in_opt LPVOID pvContext
2999 )
3000{
3001 BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext;
3002 int nResult = IDNOACTION;
3003
3004 switch (pMessage->type)
3005 {
3006 case WIU_MSI_EXECUTE_MESSAGE_PROGRESS:
3007 {
3008 DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0;
3009 UserExperienceOnExecuteProgress(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value.
3010 }
3011 break;
3012
3013 case WIU_MSI_EXECUTE_MESSAGE_ERROR:
3014 nResult = pMessage->nResultRecommendation;
3015 UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwAllowedResults, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value.
3016 break;
3017
3018 case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE:
3019 nResult = pMessage->nResultRecommendation;
3020 UserExperienceOnExecuteMsiMessage(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiMessage.mt, pMessage->dwAllowedResults, pMessage->msiMessage.wzMessage, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value.
3021 break;
3022
3023 case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE:
3024 UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiFilesInUse.cFiles, pMessage->msiFilesInUse.rgwzFiles, &nResult); // ignore return value.
3025 break;
3026 }
3027
3028 nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult);
3029 return nResult;
3030}
3031
3032static HRESULT ReportOverallProgressTicks(
3033 __in BURN_USER_EXPERIENCE* pUX,
3034 __in BOOL fRollback,
3035 __in DWORD cOverallProgressTicksTotal,
3036 __in DWORD cOverallProgressTicks
3037 )
3038{
3039 HRESULT hr = S_OK;
3040 DWORD dwProgress = cOverallProgressTicksTotal ? (cOverallProgressTicks * 100 / cOverallProgressTicksTotal) : 0;
3041
3042 // TODO: consider sending different progress numbers in the future.
3043 hr = UserExperienceOnProgress(pUX, fRollback, dwProgress, dwProgress);
3044
3045 return hr;
3046}
3047
3048static HRESULT ExecutePackageComplete(
3049 __in BURN_USER_EXPERIENCE* pUX,
3050 __in BURN_VARIABLES* pVariables,
3051 __in BURN_PACKAGE* pPackage,
3052 __in HRESULT hrOverall,
3053 __in HRESULT hrExecute,
3054 __in BOOL fRollback,
3055 __out BOOTSTRAPPER_APPLY_RESTART* pRestart,
3056 __out BOOL* pfRetry,
3057 __out BOOL* pfSuspend
3058 )
3059{
3060 HRESULT hr = FAILED(hrOverall) ? hrOverall : hrExecute; // if the overall function failed use that otherwise use the execution result.
3061 BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION executePackageCompleteAction = FAILED(hrOverall) || SUCCEEDED(hrExecute) || pPackage->fVital ? BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE;
3062
3063 // Send package execute complete to BA.
3064 UserExperienceOnExecutePackageComplete(pUX, pPackage->sczId, hr, *pRestart, &executePackageCompleteAction);
3065 if (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RESTART == executePackageCompleteAction)
3066 {
3067 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
3068 }
3069 *pfRetry = (FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RETRY == executePackageCompleteAction); // allow retry only on failures.
3070 *pfSuspend = (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_SUSPEND == executePackageCompleteAction);
3071
3072 // Remember this package as the package that initiated the forced restart.
3073 if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart)
3074 {
3075 // Best effort to set the forced restart package variable.
3076 VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, pPackage->sczId, TRUE, FALSE);
3077 }
3078
3079 // If we're retrying, leave a message in the log file and say everything is okay.
3080 if (*pfRetry)
3081 {
3082 LogId(REPORT_STANDARD, MSG_APPLY_RETRYING_PACKAGE, pPackage->sczId, hrExecute);
3083 hr = S_OK;
3084 }
3085 else if (SUCCEEDED(hrOverall) && FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE == executePackageCompleteAction && !pPackage->fVital) // If we *only* failed to execute and the BA ignored this *not-vital* package, say everything is okay.
3086 {
3087 LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, pPackage->sczId, hrExecute);
3088 hr = S_OK;
3089 }
3090 else
3091 {
3092 LogId(REPORT_STANDARD, MSG_APPLY_COMPLETED_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, hr, LoggingRestartToString(*pRestart));
3093 }
3094
3095 return hr;
3096}
diff --git a/src/burn/engine/apply.h b/src/burn/engine/apply.h
new file mode 100644
index 00000000..548e147d
--- /dev/null
+++ b/src/burn/engine/apply.h
@@ -0,0 +1,104 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9
10enum GENERIC_EXECUTE_MESSAGE_TYPE
11{
12 GENERIC_EXECUTE_MESSAGE_NONE,
13 GENERIC_EXECUTE_MESSAGE_ERROR,
14 GENERIC_EXECUTE_MESSAGE_PROGRESS,
15 GENERIC_EXECUTE_MESSAGE_FILES_IN_USE,
16};
17
18typedef struct _APPLY_AUTHENTICATION_REQUIRED_DATA
19{
20 BURN_USER_EXPERIENCE* pUX;
21 LPCWSTR wzPackageOrContainerId;
22 LPCWSTR wzPayloadId;
23} APPLY_AUTHENTICATION_REQUIRED_DATA;
24
25typedef struct _GENERIC_EXECUTE_MESSAGE
26{
27 GENERIC_EXECUTE_MESSAGE_TYPE type;
28 DWORD dwAllowedResults;
29
30 union
31 {
32 struct
33 {
34 DWORD dwErrorCode;
35 LPCWSTR wzMessage;
36 } error;
37 struct
38 {
39 DWORD dwPercentage;
40 } progress;
41 struct
42 {
43 DWORD cFiles;
44 LPCWSTR* rgwzFiles;
45 } filesInUse;
46 };
47} GENERIC_EXECUTE_MESSAGE;
48
49
50typedef int (*PFN_GENERICMESSAGEHANDLER)(
51 __in GENERIC_EXECUTE_MESSAGE* pMessage,
52 __in LPVOID pvContext
53 );
54
55
56void ApplyInitialize();
57void ApplyUninitialize();
58HRESULT ApplySetVariables(
59 __in BURN_VARIABLES* pVariables
60 );
61void ApplyReset(
62 __in BURN_USER_EXPERIENCE* pUX,
63 __in BURN_PACKAGES* pPackages
64 );
65HRESULT ApplyLock(
66 __in BOOL fPerMachine,
67 __out HANDLE* phLock
68 );
69HRESULT ApplyRegister(
70 __in BURN_ENGINE_STATE* pEngineState
71 );
72HRESULT ApplyUnregister(
73 __in BURN_ENGINE_STATE* pEngineState,
74 __in BOOL fFailedOrRollback,
75 __in BOOL fSuspend,
76 __in BOOTSTRAPPER_APPLY_RESTART restart
77 );
78HRESULT ApplyCache(
79 __in HANDLE hSourceEngineFile,
80 __in BURN_USER_EXPERIENCE* pUX,
81 __in BURN_VARIABLES* pVariables,
82 __in BURN_PLAN* pPlan,
83 __in HANDLE hPipe,
84 __inout DWORD* pcOverallProgressTicks,
85 __inout BOOL* pfRollback
86 );
87HRESULT ApplyExecute(
88 __in BURN_ENGINE_STATE* pEngineState,
89 __in_opt HANDLE hCacheThread,
90 __inout DWORD* pcOverallProgressTicks,
91 __out BOOL* pfRollback,
92 __out BOOL* pfSuspend,
93 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
94 );
95void ApplyClean(
96 __in BURN_USER_EXPERIENCE* pUX,
97 __in BURN_PLAN* pPlan,
98 __in HANDLE hPipe
99 );
100
101
102#ifdef __cplusplus
103}
104#endif
diff --git a/src/burn/engine/approvedexe.cpp b/src/burn/engine/approvedexe.cpp
new file mode 100644
index 00000000..55518519
--- /dev/null
+++ b/src/burn/engine/approvedexe.cpp
@@ -0,0 +1,262 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// function definitions
7
8extern "C" HRESULT ApprovedExesParseFromXml(
9 __in BURN_APPROVED_EXES* pApprovedExes,
10 __in IXMLDOMNode* pixnBundle
11 )
12{
13 HRESULT hr = S_OK;
14 IXMLDOMNodeList* pixnNodes = NULL;
15 IXMLDOMNode* pixnNode = NULL;
16 DWORD cNodes = 0;
17 LPWSTR scz = NULL;
18
19 // select approved exe nodes
20 hr = XmlSelectNodes(pixnBundle, L"ApprovedExeForElevation", &pixnNodes);
21 ExitOnFailure(hr, "Failed to select approved exe nodes.");
22
23 // get approved exe node count
24 hr = pixnNodes->get_length((long*)&cNodes);
25 ExitOnFailure(hr, "Failed to get approved exe node count.");
26
27 if (!cNodes)
28 {
29 ExitFunction();
30 }
31
32 // allocate memory for approved exes
33 pApprovedExes->rgApprovedExes = (BURN_APPROVED_EXE*)MemAlloc(sizeof(BURN_APPROVED_EXE) * cNodes, TRUE);
34 ExitOnNull(pApprovedExes->rgApprovedExes, hr, E_OUTOFMEMORY, "Failed to allocate memory for approved exe structs.");
35
36 pApprovedExes->cApprovedExes = cNodes;
37
38 // parse approved exe elements
39 for (DWORD i = 0; i < cNodes; ++i)
40 {
41 BURN_APPROVED_EXE* pApprovedExe = &pApprovedExes->rgApprovedExes[i];
42
43 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
44 ExitOnFailure(hr, "Failed to get next node.");
45
46 // @Id
47 hr = XmlGetAttributeEx(pixnNode, L"Id", &pApprovedExe->sczId);
48 ExitOnFailure(hr, "Failed to get @Id.");
49
50 // @Key
51 hr = XmlGetAttributeEx(pixnNode, L"Key", &pApprovedExe->sczKey);
52 ExitOnFailure(hr, "Failed to get @Key.");
53
54 // @ValueName
55 hr = XmlGetAttributeEx(pixnNode, L"ValueName", &pApprovedExe->sczValueName);
56 if (E_NOTFOUND != hr)
57 {
58 ExitOnFailure(hr, "Failed to get @ValueName.");
59 }
60
61 // @Win64
62 hr = XmlGetYesNoAttribute(pixnNode, L"Win64", &pApprovedExe->fWin64);
63 if (E_NOTFOUND != hr)
64 {
65 ExitOnFailure(hr, "Failed to get @Win64.");
66 }
67
68 // prepare next iteration
69 ReleaseNullObject(pixnNode);
70 ReleaseNullStr(scz);
71 }
72
73 hr = S_OK;
74
75LExit:
76 ReleaseObject(pixnNodes);
77 ReleaseObject(pixnNode);
78 ReleaseStr(scz);
79 return hr;
80}
81
82extern "C" void ApprovedExesUninitialize(
83 __in BURN_APPROVED_EXES* pApprovedExes
84 )
85{
86 if (pApprovedExes->rgApprovedExes)
87 {
88 for (DWORD i = 0; i < pApprovedExes->cApprovedExes; ++i)
89 {
90 BURN_APPROVED_EXE* pApprovedExe = &pApprovedExes->rgApprovedExes[i];
91
92 ReleaseStr(pApprovedExe->sczId);
93 ReleaseStr(pApprovedExe->sczKey);
94 ReleaseStr(pApprovedExe->sczValueName);
95 }
96 MemFree(pApprovedExes->rgApprovedExes);
97 }
98}
99
100extern "C" void ApprovedExesUninitializeLaunch(
101 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe
102 )
103{
104 if (pLaunchApprovedExe)
105 {
106 ReleaseStr(pLaunchApprovedExe->sczArguments);
107 ReleaseStr(pLaunchApprovedExe->sczExecutablePath);
108 ReleaseStr(pLaunchApprovedExe->sczId);
109 MemFree(pLaunchApprovedExe);
110 }
111}
112
113extern "C" HRESULT ApprovedExesFindById(
114 __in BURN_APPROVED_EXES* pApprovedExes,
115 __in_z LPCWSTR wzId,
116 __out BURN_APPROVED_EXE** ppApprovedExe
117 )
118{
119 HRESULT hr = S_OK;
120 BURN_APPROVED_EXE* pApprovedExe = NULL;
121
122 for (DWORD i = 0; i < pApprovedExes->cApprovedExes; ++i)
123 {
124 pApprovedExe = &pApprovedExes->rgApprovedExes[i];
125
126 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pApprovedExe->sczId, -1, wzId, -1))
127 {
128 *ppApprovedExe = pApprovedExe;
129 ExitFunction1(hr = S_OK);
130 }
131 }
132
133 hr = E_NOTFOUND;
134
135LExit:
136 return hr;
137}
138
139extern "C" HRESULT ApprovedExesLaunch(
140 __in BURN_VARIABLES* pVariables,
141 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe,
142 __out DWORD* pdwProcessId
143 )
144{
145 HRESULT hr = S_OK;
146 LPWSTR sczArgumentsFormatted = NULL;
147 LPWSTR sczArgumentsObfuscated = NULL;
148 LPWSTR sczCommand = NULL;
149 LPWSTR sczCommandObfuscated = NULL;
150 LPWSTR sczExecutableDirectory = NULL;
151 STARTUPINFOW si = { };
152 PROCESS_INFORMATION pi = { };
153
154 // build command
155 if (pLaunchApprovedExe->sczArguments && *pLaunchApprovedExe->sczArguments)
156 {
157 hr = VariableFormatString(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsFormatted, NULL);
158 ExitOnFailure(hr, "Failed to format argument string.");
159
160 hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsFormatted);
161 ExitOnFailure(hr, "Failed to create executable command.");
162
163 hr = VariableFormatStringObfuscated(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsObfuscated, NULL);
164 ExitOnFailure(hr, "Failed to format obfuscated argument string.");
165
166 hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsObfuscated);
167 }
168 else
169 {
170 hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath);
171 ExitOnFailure(hr, "Failed to create executable command.");
172
173 hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath);
174 }
175 ExitOnFailure(hr, "Failed to create obfuscated executable command.");
176
177 // Try to get the directory of the executable so we can set the current directory of the process to help those executables
178 // that expect stuff to be relative to them. Best effort only.
179 hr = PathGetDirectory(pLaunchApprovedExe->sczExecutablePath, &sczExecutableDirectory);
180 if (FAILED(hr))
181 {
182 ReleaseNullStr(sczExecutableDirectory);
183 }
184
185 LogId(REPORT_STANDARD, MSG_LAUNCHING_APPROVED_EXE, pLaunchApprovedExe->sczExecutablePath, sczCommandObfuscated);
186
187 si.cb = sizeof(si);
188 if (!::CreateProcessW(pLaunchApprovedExe->sczExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NEW_PROCESS_GROUP, NULL, sczExecutableDirectory, &si, &pi))
189 {
190 ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", pLaunchApprovedExe->sczExecutablePath);
191 }
192
193 *pdwProcessId = pi.dwProcessId;
194
195 if (pLaunchApprovedExe->dwWaitForInputIdleTimeout)
196 {
197 ::WaitForInputIdle(pi.hProcess, pLaunchApprovedExe->dwWaitForInputIdleTimeout);
198 }
199
200LExit:
201 StrSecureZeroFreeString(sczArgumentsFormatted);
202 ReleaseStr(sczArgumentsObfuscated);
203 StrSecureZeroFreeString(sczCommand);
204 ReleaseStr(sczCommandObfuscated);
205 ReleaseStr(sczExecutableDirectory);
206
207 ReleaseHandle(pi.hThread);
208 ReleaseHandle(pi.hProcess);
209
210 return hr;
211}
212
213extern "C" HRESULT ApprovedExesVerifySecureLocation(
214 __in BURN_VARIABLES* pVariables,
215 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe
216 )
217{
218 HRESULT hr = S_OK;
219 LPWSTR scz = NULL;
220
221 const LPCWSTR vrgSecureFolderVariables[] = {
222 L"ProgramFiles64Folder",
223 L"ProgramFilesFolder",
224 };
225
226 for (DWORD i = 0; i < countof(vrgSecureFolderVariables); ++i)
227 {
228 LPCWSTR wzSecureFolderVariable = vrgSecureFolderVariables[i];
229
230 hr = VariableGetString(pVariables, wzSecureFolderVariable, &scz);
231 if (SUCCEEDED(hr))
232 {
233 hr = PathDirectoryContainsPath(scz, pLaunchApprovedExe->sczExecutablePath);
234 if (S_OK == hr)
235 {
236 ExitFunction();
237 }
238 }
239 else if (E_NOTFOUND != hr)
240 {
241 ExitOnFailure(hr, "Failed to get the variable: %ls", wzSecureFolderVariable);
242 }
243 }
244
245 // The problem with using a Variable for the root package cache folder is that it might not have been secured yet.
246 // Getting it through CacheGetRootCompletedPath makes sure it has been secured.
247 hr = CacheGetRootCompletedPath(TRUE, TRUE, &scz);
248 ExitOnFailure(hr, "Failed to get the root package cache folder.");
249
250 hr = PathDirectoryContainsPath(scz, pLaunchApprovedExe->sczExecutablePath);
251 if (S_OK == hr)
252 {
253 ExitFunction();
254 }
255
256 hr = S_FALSE;
257
258LExit:
259 ReleaseStr(scz);
260
261 return hr;
262}
diff --git a/src/burn/engine/approvedexe.h b/src/burn/engine/approvedexe.h
new file mode 100644
index 00000000..23f3b1bb
--- /dev/null
+++ b/src/burn/engine/approvedexe.h
@@ -0,0 +1,67 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// structs
11
12typedef struct _BURN_APPROVED_EXE
13{
14 LPWSTR sczId;
15 LPWSTR sczKey;
16 LPWSTR sczValueName;
17 BOOL fWin64;
18} BURN_APPROVED_EXE;
19
20typedef struct _BURN_APPROVED_EXES
21{
22 BURN_APPROVED_EXE* rgApprovedExes;
23 DWORD cApprovedExes;
24} BURN_APPROVED_EXES;
25
26typedef struct _BURN_LAUNCH_APPROVED_EXE
27{
28 HWND hwndParent;
29 LPWSTR sczId;
30 LPWSTR sczExecutablePath;
31 LPWSTR sczArguments;
32 DWORD dwWaitForInputIdleTimeout;
33} BURN_LAUNCH_APPROVED_EXE;
34
35
36// function declarations
37
38HRESULT ApprovedExesParseFromXml(
39 __in BURN_APPROVED_EXES* pApprovedExes,
40 __in IXMLDOMNode* pixnBundle
41 );
42
43void ApprovedExesUninitialize(
44 __in BURN_APPROVED_EXES* pApprovedExes
45 );
46void ApprovedExesUninitializeLaunch(
47 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe
48 );
49HRESULT ApprovedExesFindById(
50 __in BURN_APPROVED_EXES* pApprovedExes,
51 __in_z LPCWSTR wzId,
52 __out BURN_APPROVED_EXE** ppApprovedExe
53 );
54HRESULT ApprovedExesLaunch(
55 __in BURN_VARIABLES* pVariables,
56 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe,
57 __out DWORD* pdwProcessId
58 );
59HRESULT ApprovedExesVerifySecureLocation(
60 __in BURN_VARIABLES* pVariables,
61 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe
62 );
63
64
65#if defined(__cplusplus)
66}
67#endif
diff --git a/src/burn/engine/burnextension.cpp b/src/burn/engine/burnextension.cpp
new file mode 100644
index 00000000..475df1c5
--- /dev/null
+++ b/src/burn/engine/burnextension.cpp
@@ -0,0 +1,264 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6static HRESULT SendRequiredBextMessage(
7 __in BURN_EXTENSION* pExtension,
8 __in BUNDLE_EXTENSION_MESSAGE message,
9 __in const LPVOID pvArgs,
10 __inout LPVOID pvResults
11 );
12
13// function definitions
14
15/*******************************************************************
16 BurnExtensionParseFromXml -
17
18*******************************************************************/
19EXTERN_C HRESULT BurnExtensionParseFromXml(
20 __in BURN_EXTENSIONS* pBurnExtensions,
21 __in BURN_PAYLOADS* pBaPayloads,
22 __in IXMLDOMNode* pixnBundle
23 )
24{
25 HRESULT hr = S_OK;
26 IXMLDOMNodeList* pixnNodes = NULL;
27 IXMLDOMNode* pixnNode = NULL;
28 DWORD cNodes = 0;
29
30 // Select BundleExtension nodes.
31 hr = XmlSelectNodes(pixnBundle, L"BundleExtension", &pixnNodes);
32 ExitOnFailure(hr, "Failed to select BundleExtension nodes.");
33
34 // Get BundleExtension node count.
35 hr = pixnNodes->get_length((long*)&cNodes);
36 ExitOnFailure(hr, "Failed to get BundleExtension node count.");
37
38 if (!cNodes)
39 {
40 ExitFunction();
41 }
42
43 // Allocate memory for BundleExtensions.
44 pBurnExtensions->rgExtensions = (BURN_EXTENSION*)MemAlloc(sizeof(BURN_EXTENSION) * cNodes, TRUE);
45 ExitOnNull(pBurnExtensions->rgExtensions, hr, E_OUTOFMEMORY, "Failed to allocate memory for BundleExtension structs.");
46
47 pBurnExtensions->cExtensions = cNodes;
48
49 // parse search elements
50 for (DWORD i = 0; i < cNodes; ++i)
51 {
52 BURN_EXTENSION* pExtension = &pBurnExtensions->rgExtensions[i];
53
54 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
55 ExitOnFailure(hr, "Failed to get next node.");
56
57 // @Id
58 hr = XmlGetAttributeEx(pixnNode, L"Id", &pExtension->sczId);
59 ExitOnFailure(hr, "Failed to get @Id.");
60
61 // @EntryPayloadId
62 hr = XmlGetAttributeEx(pixnNode, L"EntryPayloadId", &pExtension->sczEntryPayloadId);
63 ExitOnFailure(hr, "Failed to get @EntryPayloadId.");
64
65 hr = PayloadFindById(pBaPayloads, pExtension->sczEntryPayloadId, &pExtension->pEntryPayload);
66 ExitOnFailure(hr, "Failed to find BundleExtension EntryPayload '%ls'.", pExtension->sczEntryPayloadId);
67
68 // prepare next iteration
69 ReleaseNullObject(pixnNode);
70 }
71
72 hr = S_OK;
73
74LExit:
75 ReleaseObject(pixnNode);
76 ReleaseObject(pixnNodes);
77
78 return hr;
79}
80
81/*******************************************************************
82 BurnExtensionUninitialize -
83
84*******************************************************************/
85EXTERN_C void BurnExtensionUninitialize(
86 __in BURN_EXTENSIONS* pBurnExtensions
87 )
88{
89 if (pBurnExtensions->rgExtensions)
90 {
91 for (DWORD i = 0; i < pBurnExtensions->cExtensions; ++i)
92 {
93 BURN_EXTENSION* pExtension = &pBurnExtensions->rgExtensions[i];
94
95 ReleaseStr(pExtension->sczEntryPayloadId);
96 ReleaseStr(pExtension->sczId);
97 }
98 MemFree(pBurnExtensions->rgExtensions);
99 }
100
101 // clear struct
102 memset(pBurnExtensions, 0, sizeof(BURN_EXTENSIONS));
103}
104
105/*******************************************************************
106 BurnExtensionLoad -
107
108*******************************************************************/
109EXTERN_C HRESULT BurnExtensionLoad(
110 __in BURN_EXTENSIONS * pBurnExtensions,
111 __in BURN_EXTENSION_ENGINE_CONTEXT* pEngineContext
112 )
113{
114 HRESULT hr = S_OK;
115 LPWSTR sczBundleExtensionDataPath = NULL;
116 BUNDLE_EXTENSION_CREATE_ARGS args = { };
117 BUNDLE_EXTENSION_CREATE_RESULTS results = { };
118
119 if (!pBurnExtensions->rgExtensions || !pBurnExtensions->cExtensions)
120 {
121 ExitFunction();
122 }
123
124 hr = PathConcat(pEngineContext->pEngineState->userExperience.sczTempDirectory, L"BundleExtensionData.xml", &sczBundleExtensionDataPath);
125 ExitOnFailure(hr, "Failed to get BundleExtensionDataPath.");
126
127 for (DWORD i = 0; i < pBurnExtensions->cExtensions; ++i)
128 {
129 BURN_EXTENSION* pExtension = &pBurnExtensions->rgExtensions[i];
130
131 memset(&args, 0, sizeof(BUNDLE_EXTENSION_CREATE_ARGS));
132 memset(&results, 0, sizeof(BUNDLE_EXTENSION_CREATE_RESULTS));
133
134 args.cbSize = sizeof(BUNDLE_EXTENSION_CREATE_ARGS);
135 args.pfnBundleExtensionEngineProc = EngineForExtensionProc;
136 args.pvBundleExtensionEngineProcContext = pEngineContext;
137 args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 4, 27, 0);
138 args.wzBootstrapperWorkingFolder = pEngineContext->pEngineState->userExperience.sczTempDirectory;
139 args.wzBundleExtensionDataPath = sczBundleExtensionDataPath;
140 args.wzExtensionId = pExtension->sczId;
141
142 results.cbSize = sizeof(BUNDLE_EXTENSION_CREATE_RESULTS);
143
144 // Load BundleExtension DLL.
145 pExtension->hBextModule = ::LoadLibraryExW(pExtension->pEntryPayload->sczLocalFilePath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
146 ExitOnNullWithLastError(pExtension->hBextModule, hr, "Failed to load BundleExtension DLL '%ls': '%ls'.", pExtension->sczId, pExtension->pEntryPayload->sczLocalFilePath);
147
148 // Get BundleExtensionCreate entry-point.
149 PFN_BUNDLE_EXTENSION_CREATE pfnCreate = (PFN_BUNDLE_EXTENSION_CREATE)::GetProcAddress(pExtension->hBextModule, "BundleExtensionCreate");
150 ExitOnNullWithLastError(pfnCreate, hr, "Failed to get BundleExtensionCreate entry-point '%ls'.", pExtension->sczId);
151
152 // Create BundleExtension.
153 hr = pfnCreate(&args, &results);
154 ExitOnFailure(hr, "Failed to create BundleExtension '%ls'.", pExtension->sczId);
155
156 pExtension->pfnBurnExtensionProc = results.pfnBundleExtensionProc;
157 pExtension->pvBurnExtensionProcContext = results.pvBundleExtensionProcContext;
158 }
159
160LExit:
161 ReleaseStr(sczBundleExtensionDataPath);
162
163 return hr;
164}
165
166/*******************************************************************
167 BurnExtensionUnload -
168
169*******************************************************************/
170EXTERN_C void BurnExtensionUnload(
171 __in BURN_EXTENSIONS * pBurnExtensions
172 )
173{
174 HRESULT hr = S_OK;
175
176 if (pBurnExtensions->rgExtensions)
177 {
178 for (DWORD i = 0; i < pBurnExtensions->cExtensions; ++i)
179 {
180 BURN_EXTENSION* pExtension = &pBurnExtensions->rgExtensions[i];
181
182 if (pExtension->hBextModule)
183 {
184 // Get BundleExtensionDestroy entry-point and call it if it exists.
185 PFN_BUNDLE_EXTENSION_DESTROY pfnDestroy = (PFN_BUNDLE_EXTENSION_DESTROY)::GetProcAddress(pExtension->hBextModule, "BundleExtensionDestroy");
186 if (pfnDestroy)
187 {
188 pfnDestroy();
189 }
190
191 // Free BundleExtension DLL.
192 if (!::FreeLibrary(pExtension->hBextModule))
193 {
194 hr = HRESULT_FROM_WIN32(::GetLastError());
195 TraceError(hr, "Failed to unload BundleExtension DLL.");
196 }
197 pExtension->hBextModule = NULL;
198 }
199 }
200 }
201}
202
203EXTERN_C HRESULT BurnExtensionFindById(
204 __in BURN_EXTENSIONS* pBurnExtensions,
205 __in_z LPCWSTR wzId,
206 __out BURN_EXTENSION** ppExtension
207 )
208{
209 HRESULT hr = S_OK;
210 BURN_EXTENSION* pExtension = NULL;
211
212 for (DWORD i = 0; i < pBurnExtensions->cExtensions; ++i)
213 {
214 pExtension = &pBurnExtensions->rgExtensions[i];
215
216 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pExtension->sczId, -1, wzId, -1))
217 {
218 *ppExtension = pExtension;
219 ExitFunction1(hr = S_OK);
220 }
221 }
222
223 hr = E_NOTFOUND;
224
225LExit:
226 return hr;
227}
228
229EXTERN_C BEEAPI BurnExtensionPerformSearch(
230 __in BURN_EXTENSION* pExtension,
231 __in LPWSTR wzSearchId,
232 __in LPWSTR wzVariable
233 )
234{
235 HRESULT hr = S_OK;
236 BUNDLE_EXTENSION_SEARCH_ARGS args = { };
237 BUNDLE_EXTENSION_SEARCH_RESULTS results = { };
238
239 args.cbSize = sizeof(args);
240 args.wzId = wzSearchId;
241 args.wzVariable = wzVariable;
242
243 results.cbSize = sizeof(results);
244
245 hr = SendRequiredBextMessage(pExtension, BUNDLE_EXTENSION_MESSAGE_SEARCH, &args, &results);
246 ExitOnFailure(hr, "BundleExtension '%ls' Search '%ls' failed.", pExtension->sczId, wzSearchId);
247
248LExit:
249 return hr;
250}
251
252static HRESULT SendRequiredBextMessage(
253 __in BURN_EXTENSION* pExtension,
254 __in BUNDLE_EXTENSION_MESSAGE message,
255 __in const LPVOID pvArgs,
256 __inout LPVOID pvResults
257 )
258{
259 HRESULT hr = S_OK;
260
261 hr = pExtension->pfnBurnExtensionProc(message, pvArgs, pvResults, pExtension->pvBurnExtensionProcContext);
262
263 return hr;
264}
diff --git a/src/burn/engine/burnextension.h b/src/burn/engine/burnextension.h
new file mode 100644
index 00000000..370ddd2d
--- /dev/null
+++ b/src/burn/engine/burnextension.h
@@ -0,0 +1,61 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4#define BEEAPI HRESULT __stdcall
5
6#if defined(__cplusplus)
7extern "C" {
8#endif
9
10// structs
11
12typedef struct _BURN_EXTENSION_ENGINE_CONTEXT BURN_EXTENSION_ENGINE_CONTEXT;
13
14typedef struct _BURN_EXTENSION
15{
16 LPWSTR sczEntryPayloadId;
17 LPWSTR sczId;
18
19 BURN_PAYLOAD* pEntryPayload;
20
21 HMODULE hBextModule;
22 PFN_BUNDLE_EXTENSION_PROC pfnBurnExtensionProc;
23 LPVOID pvBurnExtensionProcContext;
24} BURN_EXTENSION;
25
26typedef struct _BURN_EXTENSIONS
27{
28 BURN_EXTENSION* rgExtensions;
29 DWORD cExtensions;
30} BURN_EXTENSIONS;
31
32// functions
33
34HRESULT BurnExtensionParseFromXml(
35 __in BURN_EXTENSIONS* pBurnExtensions,
36 __in BURN_PAYLOADS* pBaPayloads,
37 __in IXMLDOMNode* pixnBundle
38 );
39void BurnExtensionUninitialize(
40 __in BURN_EXTENSIONS* pBurnExtensions
41 );
42HRESULT BurnExtensionLoad(
43 __in BURN_EXTENSIONS* pBurnExtensions,
44 __in BURN_EXTENSION_ENGINE_CONTEXT* pEngineContext
45 );
46void BurnExtensionUnload(
47 __in BURN_EXTENSIONS* pBurnExtensions
48 );
49HRESULT BurnExtensionFindById(
50 __in BURN_EXTENSIONS* pBurnExtensions,
51 __in_z LPCWSTR wzId,
52 __out BURN_EXTENSION** ppExtension
53 );
54BEEAPI BurnExtensionPerformSearch(
55 __in BURN_EXTENSION* pExtension,
56 __in LPWSTR wzSearchId,
57 __in LPWSTR wzVariable
58 );
59#if defined(__cplusplus)
60}
61#endif
diff --git a/src/burn/engine/cabextract.cpp b/src/burn/engine/cabextract.cpp
new file mode 100644
index 00000000..5a02ff8a
--- /dev/null
+++ b/src/burn/engine/cabextract.cpp
@@ -0,0 +1,974 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5#include <fdi.h>
6
7#define ARRAY_GROWTH_SIZE 2
8
9const LPSTR INVALID_CAB_NAME = "<the>.cab";
10
11// structs
12
13typedef struct _BURN_CAB_CONTEXT
14{
15 HANDLE hFile;
16 DWORD64 qwOffset;
17 DWORD64 qwSize;
18
19 HANDLE hThread;
20 HANDLE hBeginOperationEvent;
21 HANDLE hOperationCompleteEvent;
22
23 BURN_CAB_OPERATION operation;
24 HRESULT hrError;
25
26 LPWSTR* psczStreamName;
27 LPCWSTR wzTargetFile;
28 HANDLE hTargetFile;
29 BYTE* pbTargetBuffer;
30 DWORD cbTargetBuffer;
31 DWORD iTargetBuffer;
32} BURN_CAB_CONTEXT;
33
34
35// internal function declarations
36
37static HRESULT BeginAndWaitForOperation(
38 __in BURN_CONTAINER_CONTEXT* pContext
39 );
40static HRESULT WaitForOperation(
41 __in BURN_CONTAINER_CONTEXT* pContext
42 );
43static DWORD WINAPI ExtractThreadProc(
44 __in LPVOID lpThreadParameter
45 );
46static INT_PTR DIAMONDAPI CabNotifyCallback(
47 __in FDINOTIFICATIONTYPE iNotification,
48 __inout FDINOTIFICATION *pFDINotify
49 );
50static INT_PTR CopyFileCallback(
51 __in BURN_CONTAINER_CONTEXT* pContext,
52 __inout FDINOTIFICATION *pFDINotify
53 );
54static INT_PTR CloseFileInfoCallback(
55 __in BURN_CONTAINER_CONTEXT* pContext,
56 __inout FDINOTIFICATION *pFDINotify
57 );
58static LPVOID DIAMONDAPI CabAlloc(
59 __in DWORD dwSize
60 );
61static void DIAMONDAPI CabFree(
62 __in LPVOID pvData
63 );
64static INT_PTR FAR DIAMONDAPI CabOpen(
65 __in char FAR *pszFile,
66 __in int /* oflag */,
67 __in int /* pmode */
68 );
69static UINT FAR DIAMONDAPI CabRead(
70 __in INT_PTR hf,
71 __out void FAR *pv,
72 __in UINT cb
73 );
74static UINT FAR DIAMONDAPI CabWrite(
75 __in INT_PTR hf,
76 __in void FAR *pv,
77 __in UINT cb
78 );
79static long FAR DIAMONDAPI CabSeek(
80 __in INT_PTR hf,
81 __in long dist,
82 __in int seektype
83 );
84static int FAR DIAMONDAPI CabClose(
85 __in INT_PTR hf
86 );
87static HRESULT AddVirtualFilePointer(
88 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
89 __in HANDLE hFile,
90 __in LONGLONG llInitialFilePointer
91 );
92static HRESULT ReadIfVirtualFilePointer(
93 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
94 __in HANDLE hFile,
95 __in DWORD cbRead
96 );
97static BOOL SetIfVirtualFilePointer(
98 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
99 __in HANDLE hFile,
100 __in LONGLONG llDistance,
101 __out LONGLONG* pllNewPostion,
102 __in DWORD dwSeekType
103 );
104static HRESULT CloseIfVirturalFilePointer(
105 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
106 __in HANDLE hFile
107 );
108static BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* GetVirtualFilePointer(
109 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
110 __in HANDLE hFile
111 );
112
113
114// internal variables
115
116__declspec(thread) static BURN_CONTAINER_CONTEXT* vpContext;
117
118
119// function definitions
120
121extern "C" void CabExtractInitialize()
122{
123}
124
125extern "C" HRESULT CabExtractOpen(
126 __in BURN_CONTAINER_CONTEXT* pContext,
127 __in LPCWSTR wzFilePath
128 )
129{
130 HRESULT hr = S_OK;
131
132 // initialize context
133 pContext->Cabinet.hTargetFile = INVALID_HANDLE_VALUE;
134
135 hr = StrAllocString(&pContext->Cabinet.sczFile, wzFilePath, 0);
136 ExitOnFailure(hr, "Failed to copy file name.");
137
138 // create events
139 pContext->Cabinet.hBeginOperationEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
140 ExitOnNullWithLastError(pContext->Cabinet.hBeginOperationEvent, hr, "Failed to create begin operation event.");
141
142 pContext->Cabinet.hOperationCompleteEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
143 ExitOnNullWithLastError(pContext->Cabinet.hOperationCompleteEvent, hr, "Failed to create operation complete event.");
144
145 // create extraction thread
146 pContext->Cabinet.hThread = ::CreateThread(NULL, 0, ExtractThreadProc, pContext, 0, NULL);
147 ExitOnNullWithLastError(pContext->Cabinet.hThread, hr, "Failed to create extraction thread.");
148
149 // wait for operation to complete
150 hr = WaitForOperation(pContext);
151 ExitOnFailure(hr, "Failed to wait for operation complete.");
152
153LExit:
154 return hr;
155}
156
157extern "C" HRESULT CabExtractNextStream(
158 __in BURN_CONTAINER_CONTEXT* pContext,
159 __inout_z LPWSTR* psczStreamName
160 )
161{
162 HRESULT hr = S_OK;
163
164 // set operation to move to next stream
165 pContext->Cabinet.operation = BURN_CAB_OPERATION_NEXT_STREAM;
166 pContext->Cabinet.psczStreamName = psczStreamName;
167
168 // begin operation and wait
169 hr = BeginAndWaitForOperation(pContext);
170 if (E_ABORT != hr && E_NOMOREITEMS != hr)
171 {
172 ExitOnFailure(hr, "Failed to begin and wait for operation.");
173 }
174
175LExit:
176 return hr;
177}
178
179extern "C" HRESULT CabExtractStreamToFile(
180 __in BURN_CONTAINER_CONTEXT* pContext,
181 __in_z LPCWSTR wzFileName
182 )
183{
184 HRESULT hr = S_OK;
185
186 // set operation to move to next stream
187 pContext->Cabinet.operation = BURN_CAB_OPERATION_STREAM_TO_FILE;
188 pContext->Cabinet.wzTargetFile = wzFileName;
189
190 // begin operation and wait
191 hr = BeginAndWaitForOperation(pContext);
192 ExitOnFailure(hr, "Failed to begin and wait for operation.");
193
194 // clear file name
195 pContext->Cabinet.wzTargetFile = NULL;
196
197LExit:
198 return hr;
199}
200
201extern "C" HRESULT CabExtractStreamToBuffer(
202 __in BURN_CONTAINER_CONTEXT* pContext,
203 __out BYTE** ppbBuffer,
204 __out SIZE_T* pcbBuffer
205 )
206{
207 HRESULT hr = S_OK;
208
209 // set operation to move to next stream
210 pContext->Cabinet.operation = BURN_CAB_OPERATION_STREAM_TO_BUFFER;
211
212 // begin operation and wait
213 hr = BeginAndWaitForOperation(pContext);
214 ExitOnFailure(hr, "Failed to begin and wait for operation.");
215
216 // return values
217 *ppbBuffer = pContext->Cabinet.pbTargetBuffer;
218 *pcbBuffer = pContext->Cabinet.cbTargetBuffer;
219
220 // clear buffer variables
221 pContext->Cabinet.pbTargetBuffer = NULL;
222 pContext->Cabinet.cbTargetBuffer = 0;
223 pContext->Cabinet.iTargetBuffer = 0;
224
225LExit:
226 return hr;
227}
228
229extern "C" HRESULT CabExtractSkipStream(
230 __in BURN_CONTAINER_CONTEXT* pContext
231 )
232{
233 HRESULT hr = S_OK;
234
235 // set operation to move to next stream
236 pContext->Cabinet.operation = BURN_CAB_OPERATION_SKIP_STREAM;
237
238 // begin operation and wait
239 hr = BeginAndWaitForOperation(pContext);
240 ExitOnFailure(hr, "Failed to begin and wait for operation.");
241
242LExit:
243 return hr;
244}
245
246extern "C" HRESULT CabExtractClose(
247 __in BURN_CONTAINER_CONTEXT* pContext
248 )
249{
250 HRESULT hr = S_OK;
251
252 // terminate worker thread
253 if (pContext->Cabinet.hThread)
254 {
255 // set operation to move to close
256 pContext->Cabinet.operation = BURN_CAB_OPERATION_CLOSE;
257
258 // set begin operation event
259 if (!::SetEvent(pContext->Cabinet.hBeginOperationEvent))
260 {
261 ExitWithLastError(hr, "Failed to set begin operation event.");
262 }
263
264 // wait for thread to terminate
265 if (WAIT_OBJECT_0 != ::WaitForSingleObject(pContext->Cabinet.hThread, INFINITE))
266 {
267 ExitWithLastError(hr, "Failed to wait for thread to terminate.");
268 }
269 }
270
271LExit:
272 ReleaseHandle(pContext->Cabinet.hThread);
273 ReleaseHandle(pContext->Cabinet.hBeginOperationEvent);
274 ReleaseHandle(pContext->Cabinet.hOperationCompleteEvent);
275 ReleaseMem(pContext->Cabinet.rgVirtualFilePointers);
276 ReleaseStr(pContext->Cabinet.sczFile);
277
278 return hr;
279}
280
281
282// internal helper functions
283
284static HRESULT BeginAndWaitForOperation(
285 __in BURN_CONTAINER_CONTEXT* pContext
286 )
287{
288 HRESULT hr = S_OK;
289
290 // set begin operation event
291 if (!::SetEvent(pContext->Cabinet.hBeginOperationEvent))
292 {
293 ExitWithLastError(hr, "Failed to set begin operation event.");
294 }
295
296 // wait for operation to complete
297 hr = WaitForOperation(pContext);
298
299LExit:
300 return hr;
301}
302
303static HRESULT WaitForOperation(
304 __in BURN_CONTAINER_CONTEXT* pContext
305 )
306{
307 HRESULT hr = S_OK;
308 HANDLE rghWait[2] = { };
309
310 // wait for operation complete event
311 rghWait[0] = pContext->Cabinet.hOperationCompleteEvent;
312 rghWait[1] = pContext->Cabinet.hThread;
313 switch (::WaitForMultipleObjects(countof(rghWait), rghWait, FALSE, INFINITE))
314 {
315 case WAIT_OBJECT_0:
316 if (!::ResetEvent(pContext->Cabinet.hOperationCompleteEvent))
317 {
318 ExitWithLastError(hr, "Failed to reset operation complete event.");
319 }
320 break;
321
322 case WAIT_OBJECT_0 + 1:
323 if (!::GetExitCodeThread(pContext->Cabinet.hThread, (DWORD*)&hr))
324 {
325 ExitWithLastError(hr, "Failed to get extraction thread exit code.");
326 }
327 ExitFunction();
328
329 case WAIT_FAILED: __fallthrough;
330 default:
331 ExitWithLastError(hr, "Failed to wait for operation complete event.");
332 }
333
334 // clear operation
335 pContext->Cabinet.operation = BURN_CAB_OPERATION_NONE;
336
337LExit:
338 return hr;
339}
340
341static DWORD WINAPI ExtractThreadProc(
342 __in LPVOID lpThreadParameter
343 )
344{
345 HRESULT hr = S_OK;
346 BURN_CONTAINER_CONTEXT* pContext = (BURN_CONTAINER_CONTEXT*)lpThreadParameter;
347 BOOL fComInitialized = FALSE;
348 HFDI hfdi = NULL;
349 ERF erf = { };
350
351 // initialize COM
352 hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
353 ExitOnFailure(hr, "Failed to initialize COM.");
354 fComInitialized = TRUE;
355
356 // save context in TLS storage
357 vpContext = pContext;
358
359 // create FDI context
360 hfdi = ::FDICreate(CabAlloc, CabFree, CabOpen, CabRead, CabWrite, CabClose, CabSeek, cpuUNKNOWN, &erf);
361 ExitOnNull(hfdi, hr, E_FAIL, "Failed to initialize cabinet.dll.");
362
363 // begin CAB extraction
364 if (!::FDICopy(hfdi, INVALID_CAB_NAME, "", 0, CabNotifyCallback, NULL, NULL))
365 {
366 hr = pContext->Cabinet.hrError;
367 if (E_ABORT == hr || E_NOMOREITEMS == hr)
368 {
369 ExitFunction();
370 }
371 else if (SUCCEEDED(hr))
372 {
373 if (ERROR_SUCCESS != erf.erfType)
374 {
375 hr = HRESULT_FROM_WIN32(erf.erfType);
376 }
377 else
378 {
379 switch (erf.erfOper)
380 {
381 case FDIERROR_NONE:
382 hr = E_UNEXPECTED;
383 break;
384 case FDIERROR_CABINET_NOT_FOUND:
385 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
386 break;
387 case FDIERROR_NOT_A_CABINET:
388 hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
389 break;
390 case FDIERROR_UNKNOWN_CABINET_VERSION:
391 hr = HRESULT_FROM_WIN32(ERROR_VERSION_PARSE_ERROR);
392 break;
393 case FDIERROR_CORRUPT_CABINET:
394 hr = HRESULT_FROM_WIN32(ERROR_FILE_CORRUPT);
395 break;
396 case FDIERROR_ALLOC_FAIL:
397 hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
398 break;
399 case FDIERROR_BAD_COMPR_TYPE:
400 hr = HRESULT_FROM_WIN32(ERROR_UNSUPPORTED_COMPRESSION);
401 break;
402 case FDIERROR_MDI_FAIL:
403 hr = HRESULT_FROM_WIN32(ERROR_BAD_COMPRESSION_BUFFER);
404 break;
405 case FDIERROR_TARGET_FILE:
406 hr = HRESULT_FROM_WIN32(ERROR_WRITE_FAULT);
407 break;
408 case FDIERROR_RESERVE_MISMATCH:
409 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
410 break;
411 case FDIERROR_WRONG_CABINET:
412 hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
413 break;
414 case FDIERROR_USER_ABORT:
415 hr = E_ABORT;
416 break;
417 default:
418 hr = E_FAIL;
419 break;
420 }
421 }
422 }
423 ExitOnFailure(hr, "Failed to extract all files from container, erf: %d:%X:%d", erf.fError, erf.erfOper, erf.erfType);
424 }
425
426 // set operation complete event
427 if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent))
428 {
429 ExitWithLastError(hr, "Failed to set operation complete event.");
430 }
431
432 // wait for begin operation event
433 if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE))
434 {
435 ExitWithLastError(hr, "Failed to wait for begin operation event.");
436 }
437
438 if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent))
439 {
440 ExitWithLastError(hr, "Failed to reset begin operation event.");
441 }
442
443 // read operation
444 switch (pContext->Cabinet.operation)
445 {
446 case BURN_CAB_OPERATION_NEXT_STREAM:
447 ExitFunction1(hr = E_NOMOREITEMS);
448 break;
449
450 case BURN_CAB_OPERATION_CLOSE:
451 ExitFunction1(hr = S_OK);
452
453 default:
454 hr = E_INVALIDSTATE;
455 ExitOnRootFailure(hr, "Invalid operation for this state.");
456 }
457
458LExit:
459 if (hfdi)
460 {
461 ::FDIDestroy(hfdi);
462 }
463 if (fComInitialized)
464 {
465 ::CoUninitialize();
466 }
467
468 return (DWORD)hr;
469}
470
471static INT_PTR DIAMONDAPI CabNotifyCallback(
472 __in FDINOTIFICATIONTYPE iNotification,
473 __inout FDINOTIFICATION *pFDINotify
474 )
475{
476 BURN_CONTAINER_CONTEXT* pContext = vpContext;
477 INT_PTR ipResult = 0; // result to return on success
478
479 switch (iNotification)
480 {
481 case fdintCOPY_FILE:
482 ipResult = CopyFileCallback(pContext, pFDINotify);
483 break;
484
485 case fdintCLOSE_FILE_INFO: // resource extraction complete
486 ipResult = CloseFileInfoCallback(pContext, pFDINotify);
487 break;
488
489 case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages
490 case fdintNEXT_CABINET: __fallthrough;
491 case fdintENUMERATE: __fallthrough;
492 case fdintCABINET_INFO:
493 break;
494
495 default:
496 AssertSz(FALSE, "CabExtractCallback() - unknown FDI notification command");
497 };
498
499//LExit:
500 return ipResult;
501}
502
503static INT_PTR CopyFileCallback(
504 __in BURN_CONTAINER_CONTEXT* pContext,
505 __inout FDINOTIFICATION* pFDINotify
506 )
507{
508 HRESULT hr = S_OK;
509 INT_PTR ipResult = 1; // result to return on success
510 LPWSTR pwzPath = NULL;
511 LARGE_INTEGER li = { };
512
513 // set operation complete event
514 if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent))
515 {
516 ExitWithLastError(hr, "Failed to set operation complete event.");
517 }
518
519 // wait for begin operation event
520 if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE))
521 {
522 ExitWithLastError(hr, "Failed to wait for begin operation event.");
523 }
524
525 if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent))
526 {
527 ExitWithLastError(hr, "Failed to reset begin operation event.");
528 }
529
530 // read operation
531 switch (pContext->Cabinet.operation)
532 {
533 case BURN_CAB_OPERATION_NEXT_STREAM:
534 break;
535
536 case BURN_CAB_OPERATION_CLOSE:
537 ExitFunction1(hr = E_ABORT);
538
539 default:
540 hr = E_INVALIDSTATE;
541 ExitOnRootFailure(hr, "Invalid operation for this state.");
542 }
543
544 // copy stream name
545 hr = StrAllocStringAnsi(pContext->Cabinet.psczStreamName, pFDINotify->psz1, 0, CP_UTF8);
546 ExitOnFailure(hr, "Failed to copy stream name: %hs", pFDINotify->psz1);
547
548 // set operation complete event
549 if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent))
550 {
551 ExitWithLastError(hr, "Failed to set operation complete event.");
552 }
553
554 // wait for begin operation event
555 if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE))
556 {
557 ExitWithLastError(hr, "Failed to wait for begin operation event.");
558 }
559
560 if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent))
561 {
562 ExitWithLastError(hr, "Failed to reset begin operation event.");
563 }
564
565 // read operation
566 switch (pContext->Cabinet.operation)
567 {
568 case BURN_CAB_OPERATION_STREAM_TO_FILE:
569 // create file
570 pContext->Cabinet.hTargetFile = ::CreateFileW(pContext->Cabinet.wzTargetFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
571 if (INVALID_HANDLE_VALUE == pContext->Cabinet.hTargetFile)
572 {
573 ExitWithLastError(hr, "Failed to create file: %ls", pContext->Cabinet.wzTargetFile);
574 }
575
576 // set file size
577 li.QuadPart = pFDINotify->cb;
578 if (!::SetFilePointerEx(pContext->Cabinet.hTargetFile, li, NULL, FILE_BEGIN))
579 {
580 ExitWithLastError(hr, "Failed to set file pointer to end of file.");
581 }
582
583 if (!::SetEndOfFile(pContext->Cabinet.hTargetFile))
584 {
585 ExitWithLastError(hr, "Failed to set end of file.");
586 }
587
588 li.QuadPart = 0;
589 if (!::SetFilePointerEx(pContext->Cabinet.hTargetFile, li, NULL, FILE_BEGIN))
590 {
591 ExitWithLastError(hr, "Failed to set file pointer to beginning of file.");
592 }
593
594 break;
595
596 case BURN_CAB_OPERATION_STREAM_TO_BUFFER:
597 // allocate buffer for stream
598 pContext->Cabinet.pbTargetBuffer = (BYTE*)MemAlloc(pFDINotify->cb, TRUE);
599 ExitOnNull(pContext->Cabinet.pbTargetBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for stream.");
600
601 // set buffer size and write position
602 pContext->Cabinet.cbTargetBuffer = pFDINotify->cb;
603 pContext->Cabinet.iTargetBuffer = 0;
604
605 break;
606
607 case BURN_CAB_OPERATION_SKIP_STREAM:
608 ipResult = 0;
609 break;
610
611 case BURN_CAB_OPERATION_CLOSE:
612 ExitFunction1(hr = E_ABORT);
613
614 default:
615 hr = E_INVALIDSTATE;
616 ExitOnRootFailure(hr, "Invalid operation for this state.");
617 }
618
619LExit:
620 ReleaseStr(pwzPath);
621
622 pContext->Cabinet.hrError = hr;
623 return SUCCEEDED(hr) ? ipResult : -1;
624}
625
626static INT_PTR CloseFileInfoCallback(
627 __in BURN_CONTAINER_CONTEXT* pContext,
628 __inout FDINOTIFICATION *pFDINotify
629 )
630{
631 HRESULT hr = S_OK;
632 INT_PTR ipResult = 1; // result to return on success
633 FILETIME ftLocal = { };
634 FILETIME ft = { };
635
636 // read operation
637 switch (pContext->Cabinet.operation)
638 {
639 case BURN_CAB_OPERATION_STREAM_TO_FILE:
640 // Make a best effort to set the time on the new file before
641 // we close it.
642 if (::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal))
643 {
644 if (::LocalFileTimeToFileTime(&ftLocal, &ft))
645 {
646 ::SetFileTime(pContext->Cabinet.hTargetFile, &ft, &ft, &ft);
647 }
648 }
649
650 // close file
651 ReleaseFile(pContext->Cabinet.hTargetFile);
652 break;
653
654 case BURN_CAB_OPERATION_STREAM_TO_BUFFER:
655 break;
656
657 case BURN_CAB_OPERATION_CLOSE:
658 ExitFunction1(hr = E_ABORT);
659
660 default:
661 hr = E_INVALIDSTATE;
662 ExitOnRootFailure(hr, "Invalid operation for this state.");
663 }
664
665 //if (pContext->pfnProgress)
666 //{
667 // hr = StrAllocFormatted(&pwzPath, L"%s%ls", pContext->wzRootPath, pFDINotify->psz1);
668 // ExitOnFailure(hr, "Failed to calculate file path from: %ls and %s", pContext->wzRootPath, pFDINotify->psz1);
669 // if (SUCCEEDED(hr))
670 // {
671 // hr = pContext->pfnProgress(BOX_PROGRESS_DECOMPRESSION_END, pwzPath, 0, pContext->pvContext);
672 // if (S_OK != hr)
673 // {
674 // pContext->hrError = hr;
675 // ExitFunction();
676 // }
677 // }
678 //}
679
680LExit:
681 pContext->Cabinet.hrError = hr;
682 return SUCCEEDED(hr) ? ipResult : -1;
683}
684
685static LPVOID DIAMONDAPI CabAlloc(
686 __in DWORD dwSize
687 )
688{
689 return MemAlloc(dwSize, FALSE);
690}
691
692static void DIAMONDAPI CabFree(
693 __in LPVOID pvData
694 )
695{
696 MemFree(pvData);
697}
698
699static INT_PTR FAR DIAMONDAPI CabOpen(
700 __in char FAR * pszFile,
701 __in int /* oflag */,
702 __in int /* pmode */
703 )
704{
705 HRESULT hr = S_OK;
706 BURN_CONTAINER_CONTEXT* pContext = vpContext;
707 HANDLE hFile = INVALID_HANDLE_VALUE;
708
709 // If this is the invalid cab name, use our file handle.
710 if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, INVALID_CAB_NAME, -1, pszFile, -1))
711 {
712 if (!::DuplicateHandle(::GetCurrentProcess(), pContext->hFile, ::GetCurrentProcess(), &hFile, 0, FALSE, DUPLICATE_SAME_ACCESS))
713 {
714 ExitWithLastError(hr, "Failed to duplicate handle to cab container.");
715 }
716
717 // Use a virtual file pointer since duplicated file handles share their file pointer. Seek to container offset
718 // to start.
719 hr = AddVirtualFilePointer(&pContext->Cabinet, hFile, pContext->qwOffset);
720 ExitOnFailure(hr, "Failed to add virtual file pointer for cab container.");
721 }
722 else // open file requested. This is used in the rare cases where the CAB API wants to create a temp file.
723 {
724 hFile = ::CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
725 ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open cabinet file: %hs", pszFile);
726 }
727
728LExit:
729 pContext->Cabinet.hrError = hr;
730 return FAILED(hr) ? -1 : (INT_PTR)hFile;
731}
732
733static UINT FAR DIAMONDAPI CabRead(
734 __in INT_PTR hf,
735 __out void FAR *pv,
736 __in UINT cb
737 )
738{
739 HRESULT hr = S_OK;
740 BURN_CONTAINER_CONTEXT* pContext = vpContext;
741 HANDLE hFile = (HANDLE)hf;
742 DWORD cbRead = 0;
743
744 ReadIfVirtualFilePointer(&pContext->Cabinet, hFile, cb);
745
746 if (!::ReadFile(hFile, pv, cb, &cbRead, NULL))
747 {
748 ExitWithLastError(hr, "Failed to read during cabinet extraction.");
749 }
750
751LExit:
752 pContext->Cabinet.hrError = hr;
753 return FAILED(hr) ? -1 : cbRead;
754}
755
756static UINT FAR DIAMONDAPI CabWrite(
757 __in INT_PTR /* hf */,
758 __in void FAR *pv,
759 __in UINT cb
760 )
761{
762 HRESULT hr = S_OK;
763 BURN_CONTAINER_CONTEXT* pContext = vpContext;
764 DWORD cbWrite = 0;
765
766 switch (pContext->Cabinet.operation)
767 {
768 case BURN_CAB_OPERATION_STREAM_TO_FILE:
769 // write file
770 if (!::WriteFile(pContext->Cabinet.hTargetFile, pv, cb, &cbWrite, NULL))
771 {
772 ExitWithLastError(hr, "Failed to write during cabinet extraction.");
773 }
774 break;
775
776 case BURN_CAB_OPERATION_STREAM_TO_BUFFER:
777 // copy to target buffer
778 memcpy_s(pContext->Cabinet.pbTargetBuffer + pContext->Cabinet.iTargetBuffer, pContext->Cabinet.cbTargetBuffer - pContext->Cabinet.iTargetBuffer, pv, cb);
779 pContext->Cabinet.iTargetBuffer += cb;
780
781 cbWrite = cb;
782 break;
783
784 default:
785 hr = E_INVALIDSTATE;
786 ExitOnFailure(hr, "Unexpected call to CabWrite().");
787 }
788
789LExit:
790 pContext->Cabinet.hrError = hr;
791 return FAILED(hr) ? -1 : cbWrite;
792}
793
794static long FAR DIAMONDAPI CabSeek(
795 __in INT_PTR hf,
796 __in long dist,
797 __in int seektype
798 )
799{
800 HRESULT hr = S_OK;
801 BURN_CONTAINER_CONTEXT* pContext = vpContext;
802 HANDLE hFile = (HANDLE)hf;
803 LARGE_INTEGER liDistance = { };
804 LARGE_INTEGER liNewPointer = { };
805 DWORD dwSeekType = 0;
806
807 // We assume that CabSeek() will only be called to seek the
808 // cabinet itself so we have to offset the seek operations to
809 // where the internal cabinet starts.
810 switch (seektype)
811 {
812 case FILE_BEGIN:
813 liDistance.QuadPart = pContext->qwOffset + dist;
814 dwSeekType = FILE_BEGIN;
815 break;
816
817 case FILE_CURRENT:
818 liDistance.QuadPart = dist;
819 dwSeekType = FILE_CURRENT;
820 break;
821
822 case FILE_END:
823 liDistance.QuadPart = pContext->qwOffset + pContext->qwSize + dist;
824 dwSeekType = FILE_BEGIN;
825 break;
826
827 default:
828 hr = E_INVALIDARG;
829 ExitOnFailure(hr, "Invalid seek type.");;
830 }
831
832 if (SetIfVirtualFilePointer(&pContext->Cabinet, hFile, liDistance.QuadPart, &liNewPointer.QuadPart, seektype))
833 {
834 // set file pointer
835 if (!::SetFilePointerEx(hFile, liDistance, &liNewPointer, seektype))
836 {
837 ExitWithLastError(hr, "Failed to move file pointer 0x%x bytes.", dist);
838 }
839 }
840
841 liNewPointer.QuadPart -= pContext->qwOffset;
842
843LExit:
844 pContext->Cabinet.hrError = hr;
845 return FAILED(hr) ? -1 : liNewPointer.LowPart;
846}
847
848static int FAR DIAMONDAPI CabClose(
849 __in INT_PTR hf
850 )
851{
852 BURN_CONTAINER_CONTEXT* pContext = vpContext;
853 HANDLE hFile = (HANDLE)hf;
854
855 CloseIfVirturalFilePointer(&pContext->Cabinet, hFile);
856 ReleaseFileHandle(hFile);
857
858 return 0;
859}
860
861static HRESULT AddVirtualFilePointer(
862 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
863 __in HANDLE hFile,
864 __in LONGLONG llInitialFilePointer
865 )
866{
867 HRESULT hr = S_OK;
868
869 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pCabinetContext->rgVirtualFilePointers), pCabinetContext->cVirtualFilePointers, sizeof(BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER), ARRAY_GROWTH_SIZE);
870 ExitOnFailure(hr, "Failed to allocate memory for the virtual file pointer array.");
871
872 pCabinetContext->rgVirtualFilePointers[pCabinetContext->cVirtualFilePointers].hFile = hFile;
873 pCabinetContext->rgVirtualFilePointers[pCabinetContext->cVirtualFilePointers].liPosition.QuadPart = llInitialFilePointer;
874 ++pCabinetContext->cVirtualFilePointers;
875
876LExit:
877 return hr;
878}
879
880static HRESULT ReadIfVirtualFilePointer(
881 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
882 __in HANDLE hFile,
883 __in DWORD cbRead
884 )
885{
886 HRESULT hr = E_NOTFOUND;
887
888 BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile);
889 if (pVfp)
890 {
891 // Set the file handle to the virtual file pointer.
892 if (!::SetFilePointerEx(hFile, pVfp->liPosition, NULL, FILE_BEGIN))
893 {
894 ExitWithLastError(hr, "Failed to move to virtual file pointer.");
895 }
896
897 pVfp->liPosition.QuadPart += cbRead; // add the amount that will be read to advance the pointer.
898 hr = S_OK;
899 }
900
901LExit:
902 return hr;
903}
904
905static BOOL SetIfVirtualFilePointer(
906 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
907 __in HANDLE hFile,
908 __in LONGLONG llDistance,
909 __out LONGLONG* pllNewPostion,
910 __in DWORD dwSeekType
911 )
912{
913 BOOL fFound = FALSE;
914
915 BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile);
916 if (pVfp)
917 {
918 switch (dwSeekType)
919 {
920 case FILE_BEGIN:
921 pVfp->liPosition.QuadPart = llDistance;
922 break;
923
924 case FILE_CURRENT:
925 pVfp->liPosition.QuadPart += llDistance;
926 break;
927
928 case FILE_END: __fallthrough;
929 default:
930 AssertSz(FALSE, "Unsupported seek type.");
931 break;
932 }
933
934 *pllNewPostion = pVfp->liPosition.QuadPart;
935 fFound = TRUE;
936 }
937
938 return fFound;
939}
940
941static HRESULT CloseIfVirturalFilePointer(
942 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
943 __in HANDLE hFile
944 )
945{
946 HRESULT hr = E_NOTFOUND;
947
948 BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile);
949 if (pVfp)
950 {
951 pVfp->hFile = INVALID_HANDLE_VALUE;
952 pVfp->liPosition.QuadPart = 0;
953 hr = S_OK;
954 }
955
956 return hr;
957}
958
959static BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* GetVirtualFilePointer(
960 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
961 __in HANDLE hFile
962 )
963{
964 for (DWORD i = 0; i < pCabinetContext->cVirtualFilePointers; ++i)
965 {
966 BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = pCabinetContext->rgVirtualFilePointers + i;
967 if (pVfp->hFile == hFile)
968 {
969 return pVfp;
970 }
971 }
972
973 return NULL;
974}
diff --git a/src/burn/engine/cabextract.h b/src/burn/engine/cabextract.h
new file mode 100644
index 00000000..31667f2b
--- /dev/null
+++ b/src/burn/engine/cabextract.h
@@ -0,0 +1,40 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// function declarations
11
12void CabExtractInitialize();
13HRESULT CabExtractOpen(
14 __in BURN_CONTAINER_CONTEXT* pContext,
15 __in LPCWSTR wzFilePath
16 );
17HRESULT CabExtractNextStream(
18 __in BURN_CONTAINER_CONTEXT* pContext,
19 __inout_z LPWSTR* psczStreamName
20 );
21HRESULT CabExtractStreamToFile(
22 __in BURN_CONTAINER_CONTEXT* pContext,
23 __in_z LPCWSTR wzFileName
24 );
25HRESULT CabExtractStreamToBuffer(
26 __in BURN_CONTAINER_CONTEXT* pContext,
27 __out BYTE** ppbBuffer,
28 __out SIZE_T* pcbBuffer
29 );
30HRESULT CabExtractSkipStream(
31 __in BURN_CONTAINER_CONTEXT* pContext
32 );
33HRESULT CabExtractClose(
34 __in BURN_CONTAINER_CONTEXT* pContext
35 );
36
37
38#if defined(__cplusplus)
39}
40#endif
diff --git a/src/burn/engine/cache.cpp b/src/burn/engine/cache.cpp
new file mode 100644
index 00000000..59daf139
--- /dev/null
+++ b/src/burn/engine/cache.cpp
@@ -0,0 +1,2052 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5static const LPCWSTR BUNDLE_CLEAN_ROOM_WORKING_FOLDER_NAME = L".cr";
6static const LPCWSTR BUNDLE_WORKING_FOLDER_NAME = L".be";
7static const LPCWSTR UNVERIFIED_CACHE_FOLDER_NAME = L".unverified";
8static const LPCWSTR PACKAGE_CACHE_FOLDER_NAME = L"Package Cache";
9static const DWORD FILE_OPERATION_RETRY_COUNT = 3;
10static const DWORD FILE_OPERATION_RETRY_WAIT = 2000;
11
12static BOOL vfInitializedCache = FALSE;
13static BOOL vfRunningFromCache = FALSE;
14static LPWSTR vsczSourceProcessFolder = NULL;
15static LPWSTR vsczWorkingFolder = NULL;
16static LPWSTR vsczDefaultUserPackageCache = NULL;
17static LPWSTR vsczDefaultMachinePackageCache = NULL;
18static LPWSTR vsczCurrentMachinePackageCache = NULL;
19
20static HRESULT CalculateWorkingFolder(
21 __in_z LPCWSTR wzBundleId,
22 __deref_out_z LPWSTR* psczWorkingFolder
23 );
24static HRESULT GetLastUsedSourceFolder(
25 __in BURN_VARIABLES* pVariables,
26 __out_z LPWSTR* psczLastSource
27 );
28static HRESULT CreateCompletedPath(
29 __in BOOL fPerMachine,
30 __in LPCWSTR wzCacheId,
31 __out LPWSTR* psczCacheDirectory
32 );
33static HRESULT CreateUnverifiedPath(
34 __in BOOL fPerMachine,
35 __in_z LPCWSTR wzPayloadId,
36 __out_z LPWSTR* psczUnverifiedPayloadPath
37 );
38static HRESULT GetRootPath(
39 __in BOOL fPerMachine,
40 __in BOOL fAllowRedirect,
41 __deref_out_z LPWSTR* psczRootPath
42 );
43static HRESULT VerifyThenTransferContainer(
44 __in BURN_CONTAINER* pContainer,
45 __in_z LPCWSTR wzCachedPath,
46 __in_z LPCWSTR wzUnverifiedContainerPath,
47 __in BOOL fMove,
48 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
49 __in LPPROGRESS_ROUTINE pfnProgress,
50 __in LPVOID pContext
51 );
52static HRESULT VerifyThenTransferPayload(
53 __in BURN_PAYLOAD* pPayload,
54 __in_z LPCWSTR wzCachedPath,
55 __in_z LPCWSTR wzUnverifiedPayloadPath,
56 __in BOOL fMove,
57 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
58 __in LPPROGRESS_ROUTINE pfnProgress,
59 __in LPVOID pContext
60 );
61static HRESULT CacheTransferFileWithRetry(
62 __in_z LPCWSTR wzSourcePath,
63 __in_z LPCWSTR wzDestinationPath,
64 __in BOOL fMove,
65 __in BURN_CACHE_STEP cacheStep,
66 __in DWORD64 qwFileSize,
67 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
68 __in LPPROGRESS_ROUTINE pfnProgress,
69 __in LPVOID pContext
70 );
71static HRESULT VerifyFileAgainstContainer(
72 __in BURN_CONTAINER* pContainer,
73 __in_z LPCWSTR wzVerifyPath,
74 __in BOOL fAlreadyCached,
75 __in BURN_CACHE_STEP cacheStep,
76 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
77 __in LPPROGRESS_ROUTINE pfnProgress,
78 __in LPVOID pContext
79 );
80static HRESULT VerifyFileAgainstPayload(
81 __in BURN_PAYLOAD* pPayload,
82 __in_z LPCWSTR wzVerifyPath,
83 __in BOOL fAlreadyCached,
84 __in BURN_CACHE_STEP cacheStep,
85 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
86 __in LPPROGRESS_ROUTINE pfnProgress,
87 __in LPVOID pContext
88 );
89static HRESULT ResetPathPermissions(
90 __in BOOL fPerMachine,
91 __in_z LPCWSTR wzPath
92 );
93static HRESULT SecurePath(
94 __in LPCWSTR wzPath
95 );
96static HRESULT CopyEngineToWorkingFolder(
97 __in_z LPCWSTR wzSourcePath,
98 __in_z LPCWSTR wzWorkingFolderName,
99 __in_z LPCWSTR wzExecutableName,
100 __in BURN_SECTION* pSection,
101 __deref_out_z_opt LPWSTR* psczEngineWorkingPath
102 );
103static HRESULT CopyEngineWithSignatureFixup(
104 __in HANDLE hEngineFile,
105 __in_z LPCWSTR wzEnginePath,
106 __in_z LPCWSTR wzTargetPath,
107 __in BURN_SECTION* pSection
108 );
109static HRESULT RemoveBundleOrPackage(
110 __in BOOL fBundle,
111 __in BOOL fPerMachine,
112 __in_z LPCWSTR wzBundleOrPackageId,
113 __in_z LPCWSTR wzCacheId
114 );
115static HRESULT VerifyHash(
116 __in BYTE* pbHash,
117 __in DWORD cbHash,
118 __in DWORD64 qwFileSize,
119 __in_z LPCWSTR wzUnverifiedPayloadPath,
120 __in HANDLE hFile,
121 __in BURN_CACHE_STEP cacheStep,
122 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
123 __in LPPROGRESS_ROUTINE pfnProgress,
124 __in LPVOID pContext
125 );
126static HRESULT SendCacheBeginMessage(
127 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
128 __in LPVOID pContext,
129 __in BURN_CACHE_STEP cacheStep
130 );
131static HRESULT SendCacheSuccessMessage(
132 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
133 __in LPVOID pContext,
134 __in DWORD64 qwFileSize
135 );
136static HRESULT SendCacheCompleteMessage(
137 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
138 __in LPVOID pContext,
139 __in HRESULT hrStatus
140 );
141
142
143extern "C" HRESULT CacheInitialize(
144 __in BURN_REGISTRATION* pRegistration,
145 __in BURN_VARIABLES* pVariables,
146 __in_z_opt LPCWSTR wzSourceProcessPath
147 )
148{
149 HRESULT hr = S_OK;
150 LPWSTR sczCurrentPath = NULL;
151 LPWSTR sczCompletedFolder = NULL;
152 LPWSTR sczCompletedPath = NULL;
153 LPWSTR sczOriginalSource = NULL;
154 LPWSTR sczOriginalSourceFolder = NULL;
155 int nCompare = 0;
156
157 if (!vfInitializedCache)
158 {
159 hr = PathForCurrentProcess(&sczCurrentPath, NULL);
160 ExitOnFailure(hr, "Failed to get current process path.");
161
162 // Determine if we are running from the package cache or not.
163 hr = CacheGetCompletedPath(pRegistration->fPerMachine, pRegistration->sczId, &sczCompletedFolder);
164 ExitOnFailure(hr, "Failed to get completed path for bundle.");
165
166 hr = PathConcat(sczCompletedFolder, pRegistration->sczExecutableName, &sczCompletedPath);
167 ExitOnFailure(hr, "Failed to combine working path with engine file name.");
168
169 hr = PathCompare(sczCurrentPath, sczCompletedPath, &nCompare);
170 ExitOnFailure(hr, "Failed to compare current path for bundle: %ls", sczCurrentPath);
171
172 vfRunningFromCache = (CSTR_EQUAL == nCompare);
173
174 // If a source process path was not provided (e.g. we are not being
175 // run in a clean room) then use the current process path as the
176 // source process path.
177 if (!wzSourceProcessPath)
178 {
179 wzSourceProcessPath = sczCurrentPath;
180 }
181
182 hr = PathGetDirectory(wzSourceProcessPath, &vsczSourceProcessFolder);
183 ExitOnFailure(hr, "Failed to initialize cache source folder.");
184
185 // If we're not running from the cache, ensure the original source is set.
186 if (!vfRunningFromCache)
187 {
188 // If the original source has not been set already then set it where the bundle is
189 // running from right now. This value will be persisted and we'll use it when launched
190 // from the clean room or package cache since none of our packages will be relative to
191 // those locations.
192 hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource);
193 if (E_NOTFOUND == hr)
194 {
195 hr = VariableSetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, wzSourceProcessPath, FALSE, FALSE);
196 ExitOnFailure(hr, "Failed to set original source variable.");
197
198 hr = StrAllocString(&sczOriginalSource, wzSourceProcessPath, 0);
199 ExitOnFailure(hr, "Failed to copy current path to original source.");
200 }
201
202 hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, &sczOriginalSourceFolder);
203 if (E_NOTFOUND == hr)
204 {
205 hr = PathGetDirectory(sczOriginalSource, &sczOriginalSourceFolder);
206 ExitOnFailure(hr, "Failed to get directory from original source path.");
207
208 hr = VariableSetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, sczOriginalSourceFolder, FALSE, FALSE);
209 ExitOnFailure(hr, "Failed to set original source directory variable.");
210 }
211 }
212
213 vfInitializedCache = TRUE;
214 }
215
216LExit:
217 ReleaseStr(sczCurrentPath);
218 ReleaseStr(sczCompletedFolder);
219 ReleaseStr(sczCompletedPath);
220 ReleaseStr(sczOriginalSource);
221 ReleaseStr(sczOriginalSourceFolder);
222
223 return hr;
224}
225
226extern "C" HRESULT CacheEnsureWorkingFolder(
227 __in_z_opt LPCWSTR wzBundleId,
228 __deref_out_z_opt LPWSTR* psczWorkingFolder
229 )
230{
231 HRESULT hr = S_OK;
232 LPWSTR sczWorkingFolder = NULL;
233
234 hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder);
235 ExitOnFailure(hr, "Failed to calculate working folder to ensure it exists.");
236
237 hr = DirEnsureExists(sczWorkingFolder, NULL);
238 ExitOnFailure(hr, "Failed create working folder.");
239
240 // Best effort to ensure our working folder is not encrypted.
241 ::DecryptFileW(sczWorkingFolder, 0);
242
243 if (psczWorkingFolder)
244 {
245 hr = StrAllocString(psczWorkingFolder, sczWorkingFolder, 0);
246 ExitOnFailure(hr, "Failed to copy working folder.");
247 }
248
249LExit:
250 ReleaseStr(sczWorkingFolder);
251
252 return hr;
253}
254
255extern "C" HRESULT CacheCalculateBundleWorkingPath(
256 __in_z LPCWSTR wzBundleId,
257 __in LPCWSTR wzExecutableName,
258 __deref_out_z LPWSTR* psczWorkingPath
259 )
260{
261 Assert(vfInitializedCache);
262
263 HRESULT hr = S_OK;
264 LPWSTR sczWorkingFolder = NULL;
265
266 // If the bundle is running out of the package cache then we use that as the
267 // working folder since we feel safe in the package cache.
268 if (vfRunningFromCache)
269 {
270 hr = PathForCurrentProcess(psczWorkingPath, NULL);
271 ExitOnFailure(hr, "Failed to get current process path.");
272 }
273 else // Otherwise, use the real working folder.
274 {
275 hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder);
276 ExitOnFailure(hr, "Failed to get working folder for bundle.");
277
278 hr = StrAllocFormatted(psczWorkingPath, L"%ls%ls\\%ls", sczWorkingFolder, BUNDLE_WORKING_FOLDER_NAME, wzExecutableName);
279 ExitOnFailure(hr, "Failed to calculate the bundle working path.");
280 }
281
282LExit:
283 ReleaseStr(sczWorkingFolder);
284
285 return hr;
286}
287
288extern "C" HRESULT CacheCalculateBundleLayoutWorkingPath(
289 __in_z LPCWSTR wzBundleId,
290 __deref_out_z LPWSTR* psczWorkingPath
291 )
292{
293 HRESULT hr = S_OK;
294 LPWSTR sczWorkingFolder = NULL;
295
296 hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath);
297 ExitOnFailure(hr, "Failed to get working folder for bundle layout.");
298
299 hr = StrAllocConcat(psczWorkingPath, wzBundleId, 0);
300 ExitOnFailure(hr, "Failed to append bundle id for bundle layout working path.");
301
302LExit:
303 ReleaseStr(sczWorkingFolder);
304
305 return hr;
306}
307
308extern "C" HRESULT CacheCalculatePayloadWorkingPath(
309 __in_z LPCWSTR wzBundleId,
310 __in BURN_PAYLOAD* pPayload,
311 __deref_out_z LPWSTR* psczWorkingPath
312 )
313{
314 HRESULT hr = S_OK;
315
316 hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath);
317 ExitOnFailure(hr, "Failed to get working folder for payload.");
318
319 hr = StrAllocConcat(psczWorkingPath, pPayload->sczKey, 0);
320 ExitOnFailure(hr, "Failed to append Id as payload unverified path.");
321
322LExit:
323 return hr;
324}
325
326extern "C" HRESULT CacheCalculateContainerWorkingPath(
327 __in_z LPCWSTR wzBundleId,
328 __in BURN_CONTAINER* pContainer,
329 __deref_out_z LPWSTR* psczWorkingPath
330 )
331{
332 HRESULT hr = S_OK;
333
334 hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath);
335 ExitOnFailure(hr, "Failed to get working folder for container.");
336
337 hr = StrAllocConcat(psczWorkingPath, pContainer->sczHash, 0);
338 ExitOnFailure(hr, "Failed to append hash as container unverified path.");
339
340LExit:
341 return hr;
342}
343
344extern "C" HRESULT CacheGetRootCompletedPath(
345 __in BOOL fPerMachine,
346 __in BOOL fForceInitialize,
347 __deref_out_z LPWSTR* psczRootCompletedPath
348 )
349{
350 HRESULT hr = S_OK;
351
352 if (fForceInitialize)
353 {
354 hr = CreateCompletedPath(fPerMachine, L"", psczRootCompletedPath);
355 }
356 else
357 {
358 hr = GetRootPath(fPerMachine, TRUE, psczRootCompletedPath);
359 }
360
361 return hr;
362}
363
364extern "C" HRESULT CacheGetCompletedPath(
365 __in BOOL fPerMachine,
366 __in_z LPCWSTR wzCacheId,
367 __deref_out_z LPWSTR* psczCompletedPath
368 )
369{
370 HRESULT hr = S_OK;
371 BOOL fRedirected = FALSE;
372 LPWSTR sczRootPath = NULL;
373 LPWSTR sczCurrentCompletedPath = NULL;
374 LPWSTR sczDefaultCompletedPath = NULL;
375
376 hr = GetRootPath(fPerMachine, TRUE, &sczRootPath);
377 ExitOnFailure(hr, "Failed to get %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user");
378
379 // GetRootPath returns S_FALSE if the package cache is redirected elsewhere.
380 fRedirected = S_FALSE == hr;
381
382 hr = PathConcat(sczRootPath, wzCacheId, &sczCurrentCompletedPath);
383 ExitOnFailure(hr, "Failed to construct cache path.");
384
385 hr = PathBackslashTerminate(&sczCurrentCompletedPath);
386 ExitOnFailure(hr, "Failed to ensure cache path was backslash terminated.");
387
388 // Return the old package cache directory if the new directory does not exist but the old directory does.
389 // If neither package cache directory exists return the (possibly) redirected package cache directory.
390 if (fRedirected && !DirExists(sczCurrentCompletedPath, NULL))
391 {
392 hr = GetRootPath(fPerMachine, FALSE, &sczRootPath);
393 ExitOnFailure(hr, "Failed to get old %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user");
394
395 hr = PathConcat(sczRootPath, wzCacheId, &sczDefaultCompletedPath);
396 ExitOnFailure(hr, "Failed to construct cache path.");
397
398 hr = PathBackslashTerminate(&sczDefaultCompletedPath);
399 ExitOnFailure(hr, "Failed to ensure cache path was backslash terminated.");
400
401 if (DirExists(sczDefaultCompletedPath, NULL))
402 {
403 *psczCompletedPath = sczDefaultCompletedPath;
404 sczDefaultCompletedPath = NULL;
405
406 ExitFunction();
407 }
408 }
409
410 *psczCompletedPath = sczCurrentCompletedPath;
411 sczCurrentCompletedPath = NULL;
412
413LExit:
414 ReleaseNullStr(sczDefaultCompletedPath);
415 ReleaseNullStr(sczCurrentCompletedPath);
416 ReleaseNullStr(sczRootPath);
417
418 return hr;
419}
420
421extern "C" HRESULT CacheGetResumePath(
422 __in_z LPCWSTR wzPayloadWorkingPath,
423 __deref_out_z LPWSTR* psczResumePath
424 )
425{
426 HRESULT hr = S_OK;
427
428 hr = StrAllocFormatted(psczResumePath, L"%ls.R", wzPayloadWorkingPath);
429 ExitOnFailure(hr, "Failed to create resume path.");
430
431LExit:
432 return hr;
433}
434
435extern "C" HRESULT CacheGetLocalSourcePaths(
436 __in_z LPCWSTR wzRelativePath,
437 __in_z LPCWSTR wzSourcePath,
438 __in_z LPCWSTR wzDestinationPath,
439 __in_z_opt LPCWSTR wzLayoutDirectory,
440 __in BURN_VARIABLES* pVariables,
441 __inout LPWSTR** prgSearchPaths,
442 __out DWORD* pcSearchPaths,
443 __out DWORD* pdwLikelySearchPath,
444 __out DWORD* pdwDestinationSearchPath
445 )
446{
447 HRESULT hr = S_OK;
448 LPWSTR sczCurrentPath = NULL;
449 LPWSTR sczLastSourceFolder = NULL;
450 LPWSTR* psczPath = NULL;
451 BOOL fPreferSourcePathLocation = FALSE;
452 BOOL fTryLastFolder = FALSE;
453 BOOL fTryRelativePath = FALSE;
454 BOOL fSourceIsAbsolute = FALSE;
455 DWORD cSearchPaths = 0;
456 DWORD dwLikelySearchPath = 0;
457 DWORD dwDestinationSearchPath = 0;
458
459 AssertSz(vfInitializedCache, "Cache wasn't initialized");
460
461 hr = GetLastUsedSourceFolder(pVariables, &sczLastSourceFolder);
462 fPreferSourcePathLocation = !vfRunningFromCache || FAILED(hr);
463 fTryLastFolder = SUCCEEDED(hr) && sczLastSourceFolder && *sczLastSourceFolder && CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, vsczSourceProcessFolder, -1, sczLastSourceFolder, -1);
464 fTryRelativePath = CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzSourcePath, -1, wzRelativePath, -1);
465 fSourceIsAbsolute = PathIsAbsolute(wzSourcePath);
466
467 // If the source path provided is a full path, try that first.
468 if (fSourceIsAbsolute)
469 {
470 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
471 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
472
473 psczPath = *prgSearchPaths + cSearchPaths;
474 ++cSearchPaths;
475
476 hr = StrAllocString(psczPath, wzSourcePath, 0);
477 ExitOnFailure(hr, "Failed to copy absolute source path.");
478 }
479 else
480 {
481 // If none of the paths exist, then most BAs will want to prompt the user with a possible path.
482 // The destination path is a temporary location and so not really a possible path.
483 dwLikelySearchPath = 1;
484 }
485
486 // Try the destination path next.
487 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
488 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
489
490 dwDestinationSearchPath = cSearchPaths;
491 psczPath = *prgSearchPaths + cSearchPaths;
492 ++cSearchPaths;
493
494 hr = StrAllocString(psczPath, wzDestinationPath, 0);
495 ExitOnFailure(hr, "Failed to copy absolute source path.");
496
497 if (!fSourceIsAbsolute)
498 {
499 // Calculate the source path location.
500 // In the case where we are in the bundle's package cache and
501 // couldn't find a last used source that will be the package cache path
502 // which isn't likely to have what we are looking for.
503 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
504 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
505
506 hr = PathConcat(vsczSourceProcessFolder, wzSourcePath, &sczCurrentPath);
507 ExitOnFailure(hr, "Failed to combine source process folder with source.");
508
509 // If we're not running from cache or we couldn't get the last source,
510 // try the source path location next.
511 if (fPreferSourcePathLocation)
512 {
513 (*prgSearchPaths)[cSearchPaths] = sczCurrentPath;
514 ++cSearchPaths;
515 sczCurrentPath = NULL;
516 }
517
518 // If we have a last used source and it is not the source path location,
519 // add the last used source to the search path next.
520 if (fTryLastFolder)
521 {
522 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
523 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
524
525 psczPath = *prgSearchPaths + cSearchPaths;
526 ++cSearchPaths;
527
528 hr = PathConcat(sczLastSourceFolder, wzSourcePath, psczPath);
529 ExitOnFailure(hr, "Failed to combine last source with source.");
530 }
531
532 if (!fPreferSourcePathLocation)
533 {
534 (*prgSearchPaths)[cSearchPaths] = sczCurrentPath;
535 ++cSearchPaths;
536 sczCurrentPath = NULL;
537 }
538
539 // Also consider the layout directory if doing Layout.
540 if (wzLayoutDirectory)
541 {
542 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
543 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
544
545 psczPath = *prgSearchPaths + cSearchPaths;
546 ++cSearchPaths;
547
548 hr = PathConcat(wzLayoutDirectory, wzSourcePath, psczPath);
549 ExitOnFailure(hr, "Failed to combine layout source with source.");
550 }
551 }
552
553 if (fTryRelativePath)
554 {
555 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
556 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
557
558 hr = PathConcat(vsczSourceProcessFolder, wzRelativePath, &sczCurrentPath);
559 ExitOnFailure(hr, "Failed to combine source process folder with relative.");
560
561 if (fPreferSourcePathLocation)
562 {
563 (*prgSearchPaths)[cSearchPaths] = sczCurrentPath;
564 ++cSearchPaths;
565 sczCurrentPath = NULL;
566 }
567
568 if (fTryLastFolder)
569 {
570 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
571 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
572
573 psczPath = *prgSearchPaths + cSearchPaths;
574 ++cSearchPaths;
575
576 hr = PathConcat(sczLastSourceFolder, wzRelativePath, psczPath);
577 ExitOnFailure(hr, "Failed to combine last source with relative.");
578 }
579
580 if (!fPreferSourcePathLocation)
581 {
582 (*prgSearchPaths)[cSearchPaths] = sczCurrentPath;
583 ++cSearchPaths;
584 sczCurrentPath = NULL;
585 }
586
587 if (wzLayoutDirectory)
588 {
589 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
590 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
591
592 psczPath = *prgSearchPaths + cSearchPaths;
593 ++cSearchPaths;
594
595 hr = PathConcat(wzLayoutDirectory, wzSourcePath, psczPath);
596 ExitOnFailure(hr, "Failed to combine layout source with relative.");
597 }
598 }
599
600LExit:
601 ReleaseStr(sczCurrentPath);
602 ReleaseStr(sczLastSourceFolder);
603
604 AssertSz(cSearchPaths <= BURN_CACHE_MAX_SEARCH_PATHS, "Got more than BURN_CACHE_MAX_SEARCH_PATHS search paths");
605 *pcSearchPaths = cSearchPaths;
606 *pdwLikelySearchPath = dwLikelySearchPath;
607 *pdwDestinationSearchPath = dwDestinationSearchPath;
608
609 return hr;
610}
611
612extern "C" HRESULT CacheSetLastUsedSource(
613 __in BURN_VARIABLES* pVariables,
614 __in_z LPCWSTR wzSourcePath,
615 __in_z LPCWSTR wzRelativePath
616 )
617{
618 HRESULT hr = S_OK;
619 size_t cchSourcePath = 0;
620 size_t cchRelativePath = 0;
621 size_t iSourceRelativePath = 0;
622 LPWSTR sczSourceFolder = NULL;
623 LPWSTR sczLastSourceFolder = NULL;
624 int nCompare = 0;
625
626 hr = ::StringCchLengthW(wzSourcePath, STRSAFE_MAX_CCH, &cchSourcePath);
627 ExitOnFailure(hr, "Failed to determine length of source path.");
628
629 hr = ::StringCchLengthW(wzRelativePath, STRSAFE_MAX_CCH, &cchRelativePath);
630 ExitOnFailure(hr, "Failed to determine length of relative path.");
631
632 // If the source path is smaller than the relative path (plus space for "X:\") then we know they
633 // are not relative to each other.
634 if (cchSourcePath < cchRelativePath + 3)
635 {
636 ExitFunction();
637 }
638
639 // If the source path ends with the relative path then this source could be a new path.
640 iSourceRelativePath = cchSourcePath - cchRelativePath;
641 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzSourcePath + iSourceRelativePath, -1, wzRelativePath, -1))
642 {
643 hr = StrAllocString(&sczSourceFolder, wzSourcePath, iSourceRelativePath);
644 ExitOnFailure(hr, "Failed to trim source folder.");
645
646 hr = VariableGetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, &sczLastSourceFolder);
647 if (SUCCEEDED(hr))
648 {
649 nCompare = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczSourceFolder, -1, sczLastSourceFolder, -1);
650 }
651 else if (E_NOTFOUND == hr)
652 {
653 nCompare = CSTR_GREATER_THAN;
654 hr = S_OK;
655 }
656
657 if (CSTR_EQUAL != nCompare)
658 {
659 hr = VariableSetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, sczSourceFolder, FALSE, FALSE);
660 ExitOnFailure(hr, "Failed to set last source.");
661 }
662 }
663
664LExit:
665 ReleaseStr(sczLastSourceFolder);
666 ReleaseStr(sczSourceFolder);
667
668 return hr;
669}
670
671extern "C" HRESULT CacheSendProgressCallback(
672 __in DOWNLOAD_CACHE_CALLBACK* pCallback,
673 __in DWORD64 dw64Progress,
674 __in DWORD64 dw64Total,
675 __in HANDLE hDestinationFile
676 )
677{
678 static LARGE_INTEGER LARGE_INTEGER_ZERO = { };
679
680 HRESULT hr = S_OK;
681 DWORD dwResult = PROGRESS_CONTINUE;
682 LARGE_INTEGER liTotalSize = { };
683 LARGE_INTEGER liTotalTransferred = { };
684
685 if (pCallback->pfnProgress)
686 {
687 liTotalSize.QuadPart = dw64Total;
688 liTotalTransferred.QuadPart = dw64Progress;
689
690 dwResult = (*pCallback->pfnProgress)(liTotalSize, liTotalTransferred, LARGE_INTEGER_ZERO, LARGE_INTEGER_ZERO, 1, CALLBACK_CHUNK_FINISHED, INVALID_HANDLE_VALUE, hDestinationFile, pCallback->pv);
691 switch (dwResult)
692 {
693 case PROGRESS_CONTINUE:
694 hr = S_OK;
695 break;
696
697 case PROGRESS_CANCEL: __fallthrough; // TODO: should cancel and stop be treated differently?
698 case PROGRESS_STOP:
699 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
700 ExitOnRootFailure(hr, "UX aborted on download progress.");
701
702 case PROGRESS_QUIET: // Not actually an error, just an indication to the caller to stop requesting progress.
703 pCallback->pfnProgress = NULL;
704 hr = S_OK;
705 break;
706
707 default:
708 hr = E_UNEXPECTED;
709 ExitOnRootFailure(hr, "Invalid return code from progress routine.");
710 }
711 }
712
713LExit:
714 return hr;
715}
716
717extern "C" void CacheSendErrorCallback(
718 __in DOWNLOAD_CACHE_CALLBACK* pCallback,
719 __in HRESULT hrError,
720 __in_z_opt LPCWSTR wzError,
721 __out_opt BOOL* pfRetry
722 )
723{
724 if (pfRetry)
725 {
726 *pfRetry = FALSE;
727 }
728
729 if (pCallback->pfnCancel)
730 {
731 int nResult = (*pCallback->pfnCancel)(hrError, wzError, pfRetry != NULL, pCallback->pv);
732 if (pfRetry && IDRETRY == nResult)
733 {
734 *pfRetry = TRUE;
735 }
736 }
737}
738
739extern "C" BOOL CacheBundleRunningFromCache()
740{
741 return vfRunningFromCache;
742}
743
744extern "C" HRESULT CacheBundleToCleanRoom(
745 __in BURN_SECTION* pSection,
746 __deref_out_z_opt LPWSTR* psczCleanRoomBundlePath
747 )
748{
749 HRESULT hr = S_OK;
750 LPWSTR sczSourcePath = NULL;
751 LPWSTR wzExecutableName = NULL;
752
753 hr = PathForCurrentProcess(&sczSourcePath, NULL);
754 ExitOnFailure(hr, "Failed to get current path for process to cache to clean room.");
755
756 wzExecutableName = PathFile(sczSourcePath);
757
758 hr = CopyEngineToWorkingFolder(sczSourcePath, BUNDLE_CLEAN_ROOM_WORKING_FOLDER_NAME, wzExecutableName, pSection, psczCleanRoomBundlePath);
759 ExitOnFailure(hr, "Failed to cache bundle to clean room.");
760
761LExit:
762 ReleaseStr(sczSourcePath);
763
764 return hr;
765}
766
767extern "C" HRESULT CacheBundleToWorkingDirectory(
768 __in_z LPCWSTR /*wzBundleId*/,
769 __in_z LPCWSTR wzExecutableName,
770 __in BURN_SECTION* pSection,
771 __deref_out_z_opt LPWSTR* psczEngineWorkingPath
772 )
773{
774 Assert(vfInitializedCache);
775
776 HRESULT hr = S_OK;
777 LPWSTR sczSourcePath = NULL;
778
779 // Initialize the source.
780 hr = PathForCurrentProcess(&sczSourcePath, NULL);
781 ExitOnFailure(hr, "Failed to get current process path.");
782
783 // If the bundle is running out of the package cache then we don't need to copy it to
784 // the working folder since we feel safe in the package cache and will run from there.
785 if (vfRunningFromCache)
786 {
787 hr = StrAllocString(psczEngineWorkingPath, sczSourcePath, 0);
788 ExitOnFailure(hr, "Failed to use current process path as target path.");
789 }
790 else // otherwise, carry on putting the bundle in the working folder.
791 {
792 hr = CopyEngineToWorkingFolder(sczSourcePath, BUNDLE_WORKING_FOLDER_NAME, wzExecutableName, pSection, psczEngineWorkingPath);
793 ExitOnFailure(hr, "Failed to copy engine to working folder.");
794 }
795
796LExit:
797 ReleaseStr(sczSourcePath);
798
799 return hr;
800}
801
802extern "C" HRESULT CacheLayoutBundle(
803 __in_z LPCWSTR wzExecutableName,
804 __in_z LPCWSTR wzLayoutDirectory,
805 __in_z LPCWSTR wzSourceBundlePath,
806 __in DWORD64 qwBundleSize,
807 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
808 __in LPPROGRESS_ROUTINE pfnProgress,
809 __in LPVOID pContext
810 )
811{
812 HRESULT hr = S_OK;
813 LPWSTR sczTargetPath = NULL;
814
815 hr = PathConcat(wzLayoutDirectory, wzExecutableName, &sczTargetPath);
816 ExitOnFailure(hr, "Failed to combine completed path with engine file name for layout.");
817
818 LogStringLine(REPORT_STANDARD, "Layout bundle from: '%ls' to: '%ls'", wzSourceBundlePath, sczTargetPath);
819
820 hr = CacheTransferFileWithRetry(wzSourceBundlePath, sczTargetPath, TRUE, BURN_CACHE_STEP_FINALIZE, qwBundleSize, pfnCacheMessageHandler, pfnProgress, pContext);
821 ExitOnFailure(hr, "Failed to layout bundle from: '%ls' to '%ls'", wzSourceBundlePath, sczTargetPath);
822
823LExit:
824 ReleaseStr(sczTargetPath);
825
826 return hr;
827}
828
829extern "C" HRESULT CacheCompleteBundle(
830 __in BOOL fPerMachine,
831 __in_z LPCWSTR wzExecutableName,
832 __in_z LPCWSTR wzBundleId,
833 __in_z LPCWSTR wzSourceBundlePath
834#ifdef DEBUG
835 , __in_z LPCWSTR wzExecutablePath
836#endif
837 )
838{
839 HRESULT hr = S_OK;
840 int nCompare = 0;
841 LPWSTR sczTargetDirectory = NULL;
842 LPWSTR sczTargetPath = NULL;
843 LPWSTR sczSourceDirectory = NULL;
844 LPWSTR sczPayloadSourcePath = NULL;
845
846 hr = CreateCompletedPath(fPerMachine, wzBundleId, &sczTargetDirectory);
847 ExitOnFailure(hr, "Failed to create completed cache path for bundle.");
848
849 hr = PathConcat(sczTargetDirectory, wzExecutableName, &sczTargetPath);
850 ExitOnFailure(hr, "Failed to combine completed path with engine file name.");
851
852 // We can't just use wzExecutablePath because we needed to call CreateCompletedPath to ensure that the destination was secured.
853 Assert(CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzExecutablePath, -1, sczTargetPath, -1));
854
855 // If the bundle is running out of the package cache then we don't need to copy it there
856 // (and don't want to since it'll be in use) so bail.
857 hr = PathCompare(wzSourceBundlePath, sczTargetPath, &nCompare);
858 ExitOnFailure(hr, "Failed to compare completed cache path for bundle: %ls", wzSourceBundlePath);
859
860 if (CSTR_EQUAL == nCompare)
861 {
862 ExitFunction();
863 }
864
865 // Otherwise, carry on putting the bundle in the cache.
866 LogStringLine(REPORT_STANDARD, "Caching bundle from: '%ls' to: '%ls'", wzSourceBundlePath, sczTargetPath);
867
868 FileRemoveFromPendingRename(sczTargetPath); // best effort to ensure bundle is not deleted from cache post restart.
869
870 hr = FileEnsureCopyWithRetry(wzSourceBundlePath, sczTargetPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
871 ExitOnFailure(hr, "Failed to cache bundle from: '%ls' to '%ls'", wzSourceBundlePath, sczTargetPath);
872
873 // Reset the path permissions in the cache.
874 hr = ResetPathPermissions(fPerMachine, sczTargetPath);
875 ExitOnFailure(hr, "Failed to reset permissions on cached bundle: '%ls'", sczTargetPath);
876
877 hr = PathGetDirectory(wzSourceBundlePath, &sczSourceDirectory);
878 ExitOnFailure(hr, "Failed to get directory from engine working path: %ls", wzSourceBundlePath);
879
880LExit:
881 ReleaseStr(sczPayloadSourcePath);
882 ReleaseStr(sczSourceDirectory);
883 ReleaseStr(sczTargetPath);
884 ReleaseStr(sczTargetDirectory);
885
886 return hr;
887}
888
889extern "C" HRESULT CacheLayoutContainer(
890 __in BURN_CONTAINER* pContainer,
891 __in_z_opt LPCWSTR wzLayoutDirectory,
892 __in_z LPCWSTR wzUnverifiedContainerPath,
893 __in BOOL fMove,
894 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
895 __in LPPROGRESS_ROUTINE pfnProgress,
896 __in LPVOID pContext
897 )
898{
899 HRESULT hr = S_OK;
900 LPWSTR sczCachedPath = NULL;
901
902 hr = PathConcat(wzLayoutDirectory, pContainer->sczFilePath, &sczCachedPath);
903 ExitOnFailure(hr, "Failed to concat complete cached path.");
904
905 hr = VerifyThenTransferContainer(pContainer, sczCachedPath, wzUnverifiedContainerPath, fMove, pfnCacheMessageHandler, pfnProgress, pContext);
906 ExitOnFailure(hr, "Failed to layout container from cached path: %ls", sczCachedPath);
907
908LExit:
909 ReleaseStr(sczCachedPath);
910
911 return hr;
912}
913
914extern "C" HRESULT CacheLayoutPayload(
915 __in BURN_PAYLOAD* pPayload,
916 __in_z_opt LPCWSTR wzLayoutDirectory,
917 __in_z LPCWSTR wzUnverifiedPayloadPath,
918 __in BOOL fMove,
919 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
920 __in LPPROGRESS_ROUTINE pfnProgress,
921 __in LPVOID pContext
922 )
923{
924 HRESULT hr = S_OK;
925 LPWSTR sczCachedPath = NULL;
926
927 hr = PathConcat(wzLayoutDirectory, pPayload->sczFilePath, &sczCachedPath);
928 ExitOnFailure(hr, "Failed to concat complete cached path.");
929
930 hr = VerifyThenTransferPayload(pPayload, sczCachedPath, wzUnverifiedPayloadPath, fMove, pfnCacheMessageHandler, pfnProgress, pContext);
931 ExitOnFailure(hr, "Failed to layout payload from cached payload: %ls", sczCachedPath);
932
933LExit:
934 ReleaseStr(sczCachedPath);
935
936 return hr;
937}
938
939extern "C" HRESULT CacheCompletePayload(
940 __in BOOL fPerMachine,
941 __in BURN_PAYLOAD* pPayload,
942 __in_z LPCWSTR wzCacheId,
943 __in_z LPCWSTR wzWorkingPayloadPath,
944 __in BOOL fMove,
945 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
946 __in LPPROGRESS_ROUTINE pfnProgress,
947 __in LPVOID pContext
948 )
949{
950 HRESULT hr = S_OK;
951 LPWSTR sczCachedDirectory = NULL;
952 LPWSTR sczCachedPath = NULL;
953 LPWSTR sczUnverifiedPayloadPath = NULL;
954
955 hr = CreateCompletedPath(fPerMachine, wzCacheId, &sczCachedDirectory);
956 ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", wzCacheId);
957
958 hr = PathConcat(sczCachedDirectory, pPayload->sczFilePath, &sczCachedPath);
959 ExitOnFailure(hr, "Failed to concat complete cached path.");
960
961 // If the cached file matches what we expected, we're good.
962 hr = VerifyFileAgainstPayload(pPayload, sczCachedPath, TRUE, BURN_CACHE_STEP_HASH_TO_SKIP_VERIFY, pfnCacheMessageHandler, pfnProgress, pContext);
963 if (SUCCEEDED(hr))
964 {
965 ExitFunction();
966 }
967
968 hr = CreateUnverifiedPath(fPerMachine, pPayload->sczKey, &sczUnverifiedPayloadPath);
969 ExitOnFailure(hr, "Failed to create unverified path.");
970
971 // If the working path exists, let's get it into the unverified path so we can reset the ACLs and verify the file.
972 if (FileExistsEx(wzWorkingPayloadPath, NULL))
973 {
974 hr = CacheTransferFileWithRetry(wzWorkingPayloadPath, sczUnverifiedPayloadPath, fMove, BURN_CACHE_STEP_STAGE, pPayload->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext);
975 ExitOnFailure(hr, "Failed to transfer working path to unverified path for payload: %ls.", pPayload->sczKey);
976 }
977 else if (FileExistsEx(sczUnverifiedPayloadPath, NULL))
978 {
979 // Make sure the staging progress is sent even though there was nothing to do.
980 hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, BURN_CACHE_STEP_STAGE);
981 if (SUCCEEDED(hr))
982 {
983 hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, pPayload->qwFileSize);
984 }
985 SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr);
986 ExitOnFailure(hr, "Aborted transferring working path to unverified path for payload: %ls.", pPayload->sczKey);
987 }
988 else // if the working path and unverified path do not exist, nothing we can do.
989 {
990 hr = E_FILENOTFOUND;
991 ExitOnFailure(hr, "Failed to find payload: %ls in working path: %ls and unverified path: %ls", pPayload->sczKey, wzWorkingPayloadPath, sczUnverifiedPayloadPath);
992 }
993
994 hr = ResetPathPermissions(fPerMachine, sczUnverifiedPayloadPath);
995 ExitOnFailure(hr, "Failed to reset permissions on unverified cached payload: %ls", pPayload->sczKey);
996
997 hr = VerifyFileAgainstPayload(pPayload, sczUnverifiedPayloadPath, FALSE, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext);
998 LogExitOnFailure(hr, MSG_FAILED_VERIFY_PAYLOAD, "Failed to verify payload: %ls at path: %ls", pPayload->sczKey, sczUnverifiedPayloadPath, NULL);
999
1000 LogId(REPORT_STANDARD, MSG_VERIFIED_ACQUIRED_PAYLOAD, pPayload->sczKey, sczUnverifiedPayloadPath, fMove ? "moving" : "copying", sczCachedPath);
1001
1002 hr = CacheTransferFileWithRetry(sczUnverifiedPayloadPath, sczCachedPath, TRUE, BURN_CACHE_STEP_FINALIZE, pPayload->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext);
1003 ExitOnFailure(hr, "Failed to move verified file to complete payload path: %ls", sczCachedPath);
1004
1005 ::DecryptFileW(sczCachedPath, 0); // Let's try to make sure it's not encrypted.
1006
1007LExit:
1008 ReleaseStr(sczUnverifiedPayloadPath);
1009 ReleaseStr(sczCachedPath);
1010 ReleaseStr(sczCachedDirectory);
1011
1012 return hr;
1013}
1014
1015extern "C" HRESULT CacheVerifyContainer(
1016 __in BURN_CONTAINER* pContainer,
1017 __in_z LPCWSTR wzCachedDirectory,
1018 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1019 __in LPPROGRESS_ROUTINE pfnProgress,
1020 __in LPVOID pContext
1021 )
1022{
1023 HRESULT hr = S_OK;
1024 LPWSTR sczCachedPath = NULL;
1025
1026 hr = PathConcat(wzCachedDirectory, pContainer->sczFilePath, &sczCachedPath);
1027 ExitOnFailure(hr, "Failed to concat complete cached path.");
1028
1029 hr = VerifyFileAgainstContainer(pContainer, sczCachedPath, TRUE, BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE, pfnCacheMessageHandler, pfnProgress, pContext);
1030
1031LExit:
1032 ReleaseStr(sczCachedPath);
1033
1034 return hr;
1035}
1036
1037extern "C" HRESULT CacheVerifyPayload(
1038 __in BURN_PAYLOAD* pPayload,
1039 __in_z LPCWSTR wzCachedDirectory,
1040 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1041 __in LPPROGRESS_ROUTINE pfnProgress,
1042 __in LPVOID pContext
1043 )
1044{
1045 HRESULT hr = S_OK;
1046 LPWSTR sczCachedPath = NULL;
1047
1048 hr = PathConcat(wzCachedDirectory, pPayload->sczFilePath, &sczCachedPath);
1049 ExitOnFailure(hr, "Failed to concat complete cached path.");
1050
1051 hr = VerifyFileAgainstPayload(pPayload, sczCachedPath, TRUE, BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE, pfnCacheMessageHandler, pfnProgress, pContext);
1052
1053LExit:
1054 ReleaseStr(sczCachedPath);
1055
1056 return hr;
1057}
1058
1059extern "C" HRESULT CacheRemoveWorkingFolder(
1060 __in_z_opt LPCWSTR wzBundleId
1061 )
1062{
1063 HRESULT hr = S_OK;
1064 LPWSTR sczWorkingFolder = NULL;
1065
1066 if (vfInitializedCache)
1067 {
1068 hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder);
1069 ExitOnFailure(hr, "Failed to calculate the working folder to remove it.");
1070
1071 // Try to clean out everything in the working folder.
1072 hr = DirEnsureDeleteEx(sczWorkingFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE);
1073 TraceError(hr, "Could not delete bundle engine working folder.");
1074 }
1075
1076LExit:
1077 ReleaseStr(sczWorkingFolder);
1078
1079 return hr;
1080}
1081
1082extern "C" HRESULT CacheRemoveBundle(
1083 __in BOOL fPerMachine,
1084 __in_z LPCWSTR wzBundleId
1085 )
1086{
1087 HRESULT hr = S_OK;
1088
1089 hr = RemoveBundleOrPackage(TRUE, fPerMachine, wzBundleId, wzBundleId);
1090 ExitOnFailure(hr, "Failed to remove bundle id: %ls.", wzBundleId);
1091
1092LExit:
1093 return hr;
1094}
1095
1096extern "C" HRESULT CacheRemovePackage(
1097 __in BOOL fPerMachine,
1098 __in_z LPCWSTR wzPackageId,
1099 __in_z LPCWSTR wzCacheId
1100 )
1101{
1102 HRESULT hr = S_OK;
1103
1104 hr = RemoveBundleOrPackage(FALSE, fPerMachine, wzPackageId, wzCacheId);
1105 ExitOnFailure(hr, "Failed to remove package id: %ls.", wzPackageId);
1106
1107LExit:
1108 return hr;
1109}
1110
1111extern "C" void CacheCleanup(
1112 __in BOOL fPerMachine,
1113 __in_z LPCWSTR wzBundleId
1114 )
1115{
1116 HRESULT hr = S_OK;
1117 LPWSTR sczFolder = NULL;
1118 LPWSTR sczFiles = NULL;
1119 LPWSTR sczDelete = NULL;
1120 HANDLE hFind = INVALID_HANDLE_VALUE;
1121 WIN32_FIND_DATAW wfd = { };
1122 size_t cchFileName = 0;
1123
1124 hr = CacheGetCompletedPath(fPerMachine, UNVERIFIED_CACHE_FOLDER_NAME, &sczFolder);
1125 if (SUCCEEDED(hr))
1126 {
1127 hr = DirEnsureDeleteEx(sczFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE);
1128 }
1129
1130 if (!fPerMachine)
1131 {
1132 hr = CalculateWorkingFolder(wzBundleId, &sczFolder);
1133 if (SUCCEEDED(hr))
1134 {
1135 hr = PathConcat(sczFolder, L"*.*", &sczFiles);
1136 if (SUCCEEDED(hr))
1137 {
1138 hFind = ::FindFirstFileW(sczFiles, &wfd);
1139 if (INVALID_HANDLE_VALUE != hFind)
1140 {
1141 do
1142 {
1143 // Skip directories.
1144 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1145 {
1146 continue;
1147 }
1148
1149 // Skip resume files (they end with ".R").
1150 hr = ::StringCchLengthW(wfd.cFileName, MAX_PATH, &cchFileName);
1151 if (FAILED(hr) ||
1152 2 < cchFileName && L'.' == wfd.cFileName[cchFileName - 2] && (L'R' == wfd.cFileName[cchFileName - 1] || L'r' == wfd.cFileName[cchFileName - 1]))
1153 {
1154 continue;
1155 }
1156
1157 hr = PathConcatCch(sczFolder, 0, wfd.cFileName, cchFileName, &sczDelete);
1158 if (SUCCEEDED(hr))
1159 {
1160 hr = FileEnsureDelete(sczDelete);
1161 }
1162 } while (::FindNextFileW(hFind, &wfd));
1163 }
1164 }
1165 }
1166 }
1167
1168 if (INVALID_HANDLE_VALUE != hFind)
1169 {
1170 ::FindClose(hFind);
1171 }
1172
1173 ReleaseStr(sczDelete);
1174 ReleaseStr(sczFiles);
1175 ReleaseStr(sczFolder);
1176}
1177
1178extern "C" void CacheUninitialize()
1179{
1180 ReleaseNullStr(vsczCurrentMachinePackageCache);
1181 ReleaseNullStr(vsczDefaultMachinePackageCache);
1182 ReleaseNullStr(vsczDefaultUserPackageCache);
1183 ReleaseNullStr(vsczWorkingFolder);
1184 ReleaseNullStr(vsczSourceProcessFolder);
1185
1186 vfRunningFromCache = FALSE;
1187 vfInitializedCache = FALSE;
1188}
1189
1190// Internal functions.
1191
1192static HRESULT CalculateWorkingFolder(
1193 __in_z_opt LPCWSTR /*wzBundleId*/,
1194 __deref_out_z LPWSTR* psczWorkingFolder
1195 )
1196{
1197 HRESULT hr = S_OK;
1198 RPC_STATUS rs = RPC_S_OK;
1199 BOOL fElevated = FALSE;
1200 WCHAR wzTempPath[MAX_PATH] = { };
1201 UUID guid = {};
1202 WCHAR wzGuid[39];
1203
1204 if (!vsczWorkingFolder)
1205 {
1206 ProcElevated(::GetCurrentProcess(), &fElevated);
1207
1208 if (fElevated)
1209 {
1210 if (!::GetWindowsDirectoryW(wzTempPath, countof(wzTempPath)))
1211 {
1212 ExitWithLastError(hr, "Failed to get windows path for working folder.");
1213 }
1214
1215 hr = PathFixedBackslashTerminate(wzTempPath, countof(wzTempPath));
1216 ExitOnFailure(hr, "Failed to ensure windows path for working folder ended in backslash.");
1217
1218 hr = ::StringCchCatW(wzTempPath, countof(wzTempPath), L"Temp\\");
1219 ExitOnFailure(hr, "Failed to concat Temp directory on windows path for working folder.");
1220 }
1221 else if (0 == ::GetTempPathW(countof(wzTempPath), wzTempPath))
1222 {
1223 ExitWithLastError(hr, "Failed to get temp path for working folder.");
1224 }
1225
1226 rs = ::UuidCreate(&guid);
1227 hr = HRESULT_FROM_RPC(rs);
1228 ExitOnFailure(hr, "Failed to create working folder guid.");
1229
1230 if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid)))
1231 {
1232 hr = E_OUTOFMEMORY;
1233 ExitOnRootFailure(hr, "Failed to convert working folder guid into string.");
1234 }
1235
1236 hr = StrAllocFormatted(&vsczWorkingFolder, L"%ls%ls\\", wzTempPath, wzGuid);
1237 ExitOnFailure(hr, "Failed to append bundle id on to temp path for working folder.");
1238 }
1239
1240 hr = StrAllocString(psczWorkingFolder, vsczWorkingFolder, 0);
1241 ExitOnFailure(hr, "Failed to copy working folder path.");
1242
1243LExit:
1244 return hr;
1245}
1246
1247static HRESULT GetRootPath(
1248 __in BOOL fPerMachine,
1249 __in BOOL fAllowRedirect,
1250 __deref_out_z LPWSTR* psczRootPath
1251 )
1252{
1253 HRESULT hr = S_OK;
1254 LPWSTR sczAppData = NULL;
1255 int nCompare = 0;
1256
1257 // Cache paths are initialized once so they cannot be changed while the engine is caching payloads.
1258 if (fPerMachine)
1259 {
1260 // Always construct the default machine package cache path so we can determine if we're redirected.
1261 if (!vsczDefaultMachinePackageCache)
1262 {
1263 hr = PathGetKnownFolder(CSIDL_COMMON_APPDATA, &sczAppData);
1264 ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-machine");
1265
1266 hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &vsczDefaultMachinePackageCache);
1267 ExitOnFailure(hr, "Failed to construct %hs package cache directory name.", "per-machine");
1268
1269 hr = PathBackslashTerminate(&vsczDefaultMachinePackageCache);
1270 ExitOnFailure(hr, "Failed to backslash terminate default %hs package cache directory name.", "per-machine");
1271 }
1272
1273 if (!vsczCurrentMachinePackageCache)
1274 {
1275 hr = PolcReadString(POLICY_BURN_REGISTRY_PATH, L"PackageCache", NULL, &vsczCurrentMachinePackageCache);
1276 ExitOnFailure(hr, "Failed to read PackageCache policy directory.");
1277
1278 if (vsczCurrentMachinePackageCache)
1279 {
1280 hr = PathBackslashTerminate(&vsczCurrentMachinePackageCache);
1281 ExitOnFailure(hr, "Failed to backslash terminate redirected per-machine package cache directory name.");
1282 }
1283 else
1284 {
1285 hr = StrAllocString(&vsczCurrentMachinePackageCache, vsczDefaultMachinePackageCache, 0);
1286 ExitOnFailure(hr, "Failed to copy default package cache directory to current package cache directory.");
1287 }
1288 }
1289
1290 hr = StrAllocString(psczRootPath, fAllowRedirect ? vsczCurrentMachinePackageCache : vsczDefaultMachinePackageCache, 0);
1291 ExitOnFailure(hr, "Failed to copy %hs package cache root directory.", "per-machine");
1292
1293 hr = PathCompare(vsczDefaultMachinePackageCache, *psczRootPath, &nCompare);
1294 ExitOnFailure(hr, "Failed to compare default and current package cache directories.");
1295
1296 // Return S_FALSE if the current location is not the default location (redirected).
1297 hr = CSTR_EQUAL == nCompare ? S_OK : S_FALSE;
1298 }
1299 else
1300 {
1301 if (!vsczDefaultUserPackageCache)
1302 {
1303 hr = PathGetKnownFolder(CSIDL_LOCAL_APPDATA, &sczAppData);
1304 ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-user");
1305
1306 hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &vsczDefaultUserPackageCache);
1307 ExitOnFailure(hr, "Failed to construct %hs package cache directory name.", "per-user");
1308
1309 hr = PathBackslashTerminate(&vsczDefaultUserPackageCache);
1310 ExitOnFailure(hr, "Failed to backslash terminate default %hs package cache directory name.", "per-user");
1311 }
1312
1313 hr = StrAllocString(psczRootPath, vsczDefaultUserPackageCache, 0);
1314 ExitOnFailure(hr, "Failed to copy %hs package cache root directory.", "per-user");
1315 }
1316
1317LExit:
1318 ReleaseStr(sczAppData);
1319
1320 return hr;
1321}
1322
1323static HRESULT GetLastUsedSourceFolder(
1324 __in BURN_VARIABLES* pVariables,
1325 __out_z LPWSTR* psczLastSource
1326 )
1327{
1328 HRESULT hr = S_OK;
1329
1330 hr = VariableGetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, psczLastSource);
1331 if (E_NOTFOUND == hr)
1332 {
1333 // Try the original source folder.
1334 hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, psczLastSource);
1335 }
1336
1337 return hr;
1338}
1339
1340static HRESULT CreateCompletedPath(
1341 __in BOOL fPerMachine,
1342 __in LPCWSTR wzId,
1343 __out LPWSTR* psczCacheDirectory
1344 )
1345{
1346 static BOOL fPerMachineCacheRootVerified = FALSE;
1347
1348 HRESULT hr = S_OK;
1349 LPWSTR sczCacheDirectory = NULL;
1350
1351 // If we are doing a permachine install but have not yet verified that the root cache folder
1352 // was created with the correct ACLs yet, do that now.
1353 if (fPerMachine && !fPerMachineCacheRootVerified)
1354 {
1355 hr = GetRootPath(fPerMachine, TRUE, &sczCacheDirectory);
1356 ExitOnFailure(hr, "Failed to get cache directory.");
1357
1358 hr = DirEnsureExists(sczCacheDirectory, NULL);
1359 ExitOnFailure(hr, "Failed to create cache directory: %ls", sczCacheDirectory);
1360
1361 hr = SecurePath(sczCacheDirectory);
1362 ExitOnFailure(hr, "Failed to secure cache directory: %ls", sczCacheDirectory);
1363
1364 fPerMachineCacheRootVerified = TRUE;
1365 }
1366
1367 // Get the cache completed path, ensure it exists, and reset any permissions people
1368 // might have tried to set on the directory so we inherit the (correct!) security
1369 // permissions from the parent directory.
1370 hr = CacheGetCompletedPath(fPerMachine, wzId, &sczCacheDirectory);
1371 ExitOnFailure(hr, "Failed to get cache directory.");
1372
1373 hr = DirEnsureExists(sczCacheDirectory, NULL);
1374 ExitOnFailure(hr, "Failed to create cache directory: %ls", sczCacheDirectory);
1375
1376 ResetPathPermissions(fPerMachine, sczCacheDirectory);
1377
1378 *psczCacheDirectory = sczCacheDirectory;
1379 sczCacheDirectory = NULL;
1380
1381LExit:
1382 ReleaseStr(sczCacheDirectory);
1383 return hr;
1384}
1385
1386static HRESULT CreateUnverifiedPath(
1387 __in BOOL fPerMachine,
1388 __in_z LPCWSTR wzPayloadId,
1389 __out_z LPWSTR* psczUnverifiedPayloadPath
1390 )
1391{
1392 static BOOL fUnverifiedCacheFolderCreated = FALSE;
1393
1394 HRESULT hr = S_OK;
1395 LPWSTR sczUnverifiedCacheFolder = NULL;
1396
1397 hr = CacheGetCompletedPath(fPerMachine, UNVERIFIED_CACHE_FOLDER_NAME, &sczUnverifiedCacheFolder);
1398 ExitOnFailure(hr, "Failed to get cache directory.");
1399
1400 if (!fUnverifiedCacheFolderCreated)
1401 {
1402 hr = DirEnsureExists(sczUnverifiedCacheFolder, NULL);
1403 ExitOnFailure(hr, "Failed to create unverified cache directory: %ls", sczUnverifiedCacheFolder);
1404
1405 ResetPathPermissions(fPerMachine, sczUnverifiedCacheFolder);
1406 }
1407
1408 hr = PathConcat(sczUnverifiedCacheFolder, wzPayloadId, psczUnverifiedPayloadPath);
1409 ExitOnFailure(hr, "Failed to concat payload id to unverified folder path.");
1410
1411LExit:
1412 ReleaseStr(sczUnverifiedCacheFolder);
1413
1414 return hr;
1415}
1416
1417static HRESULT VerifyThenTransferContainer(
1418 __in BURN_CONTAINER* pContainer,
1419 __in_z LPCWSTR wzCachedPath,
1420 __in_z LPCWSTR wzUnverifiedContainerPath,
1421 __in BOOL fMove,
1422 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1423 __in LPPROGRESS_ROUTINE pfnProgress,
1424 __in LPVOID pContext
1425 )
1426{
1427 HRESULT hr = S_OK;
1428 HANDLE hFile = INVALID_HANDLE_VALUE;
1429
1430 // Get the container on disk actual hash.
1431 hFile = ::CreateFileW(wzUnverifiedContainerPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1432 if (INVALID_HANDLE_VALUE == hFile)
1433 {
1434 ExitWithLastError(hr, "Failed to open container in working path: %ls", wzUnverifiedContainerPath);
1435 }
1436
1437 // Container should have a hash we can use to verify with.
1438 if (pContainer->pbHash)
1439 {
1440 hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, pContainer->qwFileSize, wzUnverifiedContainerPath, hFile, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext);
1441 ExitOnFailure(hr, "Failed to verify container hash: %ls", wzCachedPath);
1442 }
1443
1444 LogStringLine(REPORT_STANDARD, "%ls container from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedContainerPath, wzCachedPath);
1445
1446 hr = CacheTransferFileWithRetry(wzUnverifiedContainerPath, wzCachedPath, fMove, BURN_CACHE_STEP_FINALIZE, pContainer->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext);
1447
1448LExit:
1449 ReleaseFileHandle(hFile);
1450
1451 return hr;
1452}
1453
1454static HRESULT VerifyThenTransferPayload(
1455 __in BURN_PAYLOAD* pPayload,
1456 __in_z LPCWSTR wzCachedPath,
1457 __in_z LPCWSTR wzUnverifiedPayloadPath,
1458 __in BOOL fMove,
1459 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1460 __in LPPROGRESS_ROUTINE pfnProgress,
1461 __in LPVOID pContext
1462 )
1463{
1464 HRESULT hr = S_OK;
1465 HANDLE hFile = INVALID_HANDLE_VALUE;
1466
1467 // Get the payload on disk actual hash.
1468 hFile = ::CreateFileW(wzUnverifiedPayloadPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1469 if (INVALID_HANDLE_VALUE == hFile)
1470 {
1471 ExitWithLastError(hr, "Failed to open payload in working path: %ls", wzUnverifiedPayloadPath);
1472 }
1473
1474 if (pPayload->pbHash) // the payload should have a hash we can use to verify it.
1475 {
1476 hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, wzUnverifiedPayloadPath, hFile, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext);
1477 ExitOnFailure(hr, "Failed to verify payload hash: %ls", wzCachedPath);
1478 }
1479
1480 LogStringLine(REPORT_STANDARD, "%ls payload from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedPayloadPath, wzCachedPath);
1481
1482 hr = CacheTransferFileWithRetry(wzUnverifiedPayloadPath, wzCachedPath, fMove, BURN_CACHE_STEP_FINALIZE, pPayload->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext);
1483
1484LExit:
1485 ReleaseFileHandle(hFile);
1486
1487 return hr;
1488}
1489
1490static HRESULT CacheTransferFileWithRetry(
1491 __in_z LPCWSTR wzSourcePath,
1492 __in_z LPCWSTR wzDestinationPath,
1493 __in BOOL fMove,
1494 __in BURN_CACHE_STEP cacheStep,
1495 __in DWORD64 qwFileSize,
1496 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1497 __in LPPROGRESS_ROUTINE /*pfnProgress*/,
1498 __in LPVOID pContext
1499 )
1500{
1501 HRESULT hr = S_OK;
1502
1503 hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, cacheStep);
1504 ExitOnFailure(hr, "Aborted cache file transfer begin.");
1505
1506 // TODO: send progress during the file transfer.
1507 if (fMove)
1508 {
1509 hr = FileEnsureMoveWithRetry(wzSourcePath, wzDestinationPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1510 ExitOnFailure(hr, "Failed to move %ls to %ls", wzSourcePath, wzDestinationPath);
1511 }
1512 else
1513 {
1514 hr = FileEnsureCopyWithRetry(wzSourcePath, wzDestinationPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1515 ExitOnFailure(hr, "Failed to copy %ls to %ls", wzSourcePath, wzDestinationPath);
1516 }
1517
1518 hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, qwFileSize);
1519
1520LExit:
1521 SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr);
1522
1523 return hr;
1524}
1525
1526static HRESULT VerifyFileAgainstContainer(
1527 __in BURN_CONTAINER* pContainer,
1528 __in_z LPCWSTR wzVerifyPath,
1529 __in BOOL fAlreadyCached,
1530 __in BURN_CACHE_STEP cacheStep,
1531 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1532 __in LPPROGRESS_ROUTINE pfnProgress,
1533 __in LPVOID pContext
1534 )
1535{
1536 HRESULT hr = S_OK;
1537 HANDLE hFile = INVALID_HANDLE_VALUE;
1538
1539 // Get the container on disk actual hash.
1540 hFile = ::CreateFileW(wzVerifyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1541 if (INVALID_HANDLE_VALUE == hFile)
1542 {
1543 hr = HRESULT_FROM_WIN32(::GetLastError());
1544 if (E_PATHNOTFOUND == hr || E_FILENOTFOUND == hr)
1545 {
1546 ExitFunction(); // do not log error when the file was not found.
1547 }
1548 ExitOnRootFailure(hr, "Failed to open container at path: %ls", wzVerifyPath);
1549 }
1550
1551 if (pContainer->pbHash) // the container should have a hash we can use to verify it.
1552 {
1553 hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, pContainer->qwFileSize, wzVerifyPath, hFile, cacheStep, pfnCacheMessageHandler, pfnProgress, pContext);
1554 ExitOnFailure(hr, "Failed to verify hash of container: %ls", pContainer->sczId);
1555 }
1556
1557 if (fAlreadyCached)
1558 {
1559 LogId(REPORT_STANDARD, MSG_VERIFIED_EXISTING_CONTAINER, pContainer->sczId, wzVerifyPath);
1560 ::DecryptFileW(wzVerifyPath, 0); // Let's try to make sure it's not encrypted.
1561 }
1562
1563LExit:
1564 ReleaseFileHandle(hFile);
1565
1566 if (FAILED(hr) && E_PATHNOTFOUND != hr && E_FILENOTFOUND != hr)
1567 {
1568 if (fAlreadyCached)
1569 {
1570 LogErrorId(hr, MSG_FAILED_VERIFY_CONTAINER, pContainer->sczId, wzVerifyPath, NULL);
1571 }
1572
1573 FileEnsureDelete(wzVerifyPath); // if the file existed but did not verify correctly, make it go away.
1574 }
1575
1576 return hr;
1577}
1578
1579static HRESULT VerifyFileAgainstPayload(
1580 __in BURN_PAYLOAD* pPayload,
1581 __in_z LPCWSTR wzVerifyPath,
1582 __in BOOL fAlreadyCached,
1583 __in BURN_CACHE_STEP cacheStep,
1584 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1585 __in LPPROGRESS_ROUTINE pfnProgress,
1586 __in LPVOID pContext
1587 )
1588{
1589 HRESULT hr = S_OK;
1590 HANDLE hFile = INVALID_HANDLE_VALUE;
1591
1592 // Get the payload on disk actual hash.
1593 hFile = ::CreateFileW(wzVerifyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1594 if (INVALID_HANDLE_VALUE == hFile)
1595 {
1596 hr = HRESULT_FROM_WIN32(::GetLastError());
1597 if (E_PATHNOTFOUND == hr || E_FILENOTFOUND == hr)
1598 {
1599 ExitFunction(); // do not log error when the file was not found.
1600 }
1601 ExitOnRootFailure(hr, "Failed to open payload at path: %ls", wzVerifyPath);
1602 }
1603
1604 if (pPayload->pbHash) // the payload should have a hash we can use to verify it.
1605 {
1606 hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, wzVerifyPath, hFile, cacheStep, pfnCacheMessageHandler, pfnProgress, pContext);
1607 ExitOnFailure(hr, "Failed to verify hash of payload: %ls", pPayload->sczKey);
1608 }
1609
1610 if (fAlreadyCached)
1611 {
1612 LogId(REPORT_STANDARD, MSG_VERIFIED_EXISTING_PAYLOAD, pPayload->sczKey, wzVerifyPath);
1613 ::DecryptFileW(wzVerifyPath, 0); // Let's try to make sure it's not encrypted.
1614 }
1615
1616LExit:
1617 ReleaseFileHandle(hFile);
1618
1619 if (FAILED(hr) && E_PATHNOTFOUND != hr && E_FILENOTFOUND != hr)
1620 {
1621 if (fAlreadyCached)
1622 {
1623 LogErrorId(hr, MSG_FAILED_VERIFY_PAYLOAD, pPayload->sczKey, wzVerifyPath, NULL);
1624 }
1625
1626 FileEnsureDelete(wzVerifyPath); // if the file existed but did not verify correctly, make it go away.
1627 }
1628
1629 return hr;
1630}
1631
1632static HRESULT AllocateSid(
1633 __in WELL_KNOWN_SID_TYPE type,
1634 __out PSID* ppSid
1635 )
1636{
1637 HRESULT hr = S_OK;
1638 PSID pAllocSid = NULL;
1639 DWORD cbSid = SECURITY_MAX_SID_SIZE;
1640
1641 pAllocSid = static_cast<PSID>(MemAlloc(cbSid, TRUE));
1642 ExitOnNull(pAllocSid, hr, E_OUTOFMEMORY, "Failed to allocate memory for well known SID.");
1643
1644 if (!::CreateWellKnownSid(type, NULL, pAllocSid, &cbSid))
1645 {
1646 ExitWithLastError(hr, "Failed to create well known SID.");
1647 }
1648
1649 *ppSid = pAllocSid;
1650 pAllocSid = NULL;
1651
1652LExit:
1653 ReleaseMem(pAllocSid);
1654 return hr;
1655}
1656
1657
1658static HRESULT ResetPathPermissions(
1659 __in BOOL fPerMachine,
1660 __in_z LPCWSTR wzPath
1661 )
1662{
1663 HRESULT hr = S_OK;
1664 DWORD er = ERROR_SUCCESS;
1665 DWORD dwSetSecurity = DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION;
1666 ACL acl = { };
1667 PSID pSid = NULL;
1668
1669 if (fPerMachine)
1670 {
1671 hr = AllocateSid(WinBuiltinAdministratorsSid, &pSid);
1672 ExitOnFailure(hr, "Failed to allocate administrator SID.");
1673
1674 // Create an empty (not NULL!) ACL to reset the permissions on the file to purely inherit from parent.
1675 if (!::InitializeAcl(&acl, sizeof(acl), ACL_REVISION))
1676 {
1677 ExitWithLastError(hr, "Failed to initialize ACL.");
1678 }
1679
1680 dwSetSecurity |= OWNER_SECURITY_INFORMATION;
1681 }
1682
1683 hr = AclSetSecurityWithRetry(wzPath, SE_FILE_OBJECT, dwSetSecurity, pSid, NULL, &acl, NULL, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1684 ExitOnWin32Error(er, hr, "Failed to reset the ACL on cached file: %ls", wzPath);
1685
1686 ::SetFileAttributesW(wzPath, FILE_ATTRIBUTE_NORMAL); // Let's try to reset any possible read-only/system bits.
1687
1688LExit:
1689 ReleaseMem(pSid);
1690 return hr;
1691}
1692
1693
1694static HRESULT GrantAccessAndAllocateSid(
1695 __in WELL_KNOWN_SID_TYPE type,
1696 __in DWORD dwGrantAccess,
1697 __in EXPLICIT_ACCESS* pAccess
1698 )
1699{
1700 HRESULT hr = S_OK;
1701
1702 hr = AllocateSid(type, reinterpret_cast<PSID*>(&pAccess->Trustee.ptstrName));
1703 ExitOnFailure(hr, "Failed to allocate SID to grate access.");
1704
1705 pAccess->grfAccessMode = GRANT_ACCESS;
1706 pAccess->grfAccessPermissions = dwGrantAccess;
1707 pAccess->grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
1708 pAccess->Trustee.TrusteeForm = TRUSTEE_IS_SID;
1709 pAccess->Trustee.TrusteeType = TRUSTEE_IS_GROUP;
1710
1711LExit:
1712 return hr;
1713}
1714
1715
1716static HRESULT SecurePath(
1717 __in LPCWSTR wzPath
1718 )
1719{
1720 HRESULT hr = S_OK;
1721 DWORD er = ERROR_SUCCESS;
1722 EXPLICIT_ACCESSW access[4] = { };
1723 PACL pAcl = NULL;
1724
1725 // Administrators must be the first one in the array so we can reuse the allocated SID below.
1726 hr = GrantAccessAndAllocateSid(WinBuiltinAdministratorsSid, FILE_ALL_ACCESS, &access[0]);
1727 ExitOnFailure(hr, "Failed to allocate access for Administrators group to path: %ls", wzPath);
1728
1729 hr = GrantAccessAndAllocateSid(WinLocalSystemSid, FILE_ALL_ACCESS, &access[1]);
1730 ExitOnFailure(hr, "Failed to allocate access for SYSTEM group to path: %ls", wzPath);
1731
1732 hr = GrantAccessAndAllocateSid(WinWorldSid, GENERIC_READ | GENERIC_EXECUTE, &access[2]);
1733 ExitOnFailure(hr, "Failed to allocate access for Everyone group to path: %ls", wzPath);
1734
1735 hr = GrantAccessAndAllocateSid(WinBuiltinUsersSid, GENERIC_READ | GENERIC_EXECUTE, &access[3]);
1736 ExitOnFailure(hr, "Failed to allocate access for Users group to path: %ls", wzPath);
1737
1738 er = ::SetEntriesInAclW(countof(access), access, NULL, &pAcl);
1739 ExitOnWin32Error(er, hr, "Failed to create ACL to secure cache path: %ls", wzPath);
1740
1741 // Set the ACL and ensure the Administrators group ends up the owner
1742 hr = AclSetSecurityWithRetry(wzPath, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
1743 reinterpret_cast<PSID>(access[0].Trustee.ptstrName), NULL, pAcl, NULL, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1744 ExitOnFailure(hr, "Failed to secure cache path: %ls", wzPath);
1745
1746LExit:
1747 if (pAcl)
1748 {
1749 ::LocalFree(pAcl);
1750 }
1751
1752 for (DWORD i = 0; i < countof(access); ++i)
1753 {
1754 ReleaseMem(access[i].Trustee.ptstrName);
1755 }
1756
1757 return hr;
1758}
1759
1760
1761static HRESULT CopyEngineToWorkingFolder(
1762 __in_z LPCWSTR wzSourcePath,
1763 __in_z LPCWSTR wzWorkingFolderName,
1764 __in_z LPCWSTR wzExecutableName,
1765 __in BURN_SECTION* pSection,
1766 __deref_out_z_opt LPWSTR* psczEngineWorkingPath
1767 )
1768{
1769 HRESULT hr = S_OK;
1770 LPWSTR sczWorkingFolder = NULL;
1771 LPWSTR sczTargetDirectory = NULL;
1772 LPWSTR sczTargetPath = NULL;
1773 LPWSTR sczSourceDirectory = NULL;
1774 LPWSTR sczPayloadSourcePath = NULL;
1775 LPWSTR sczPayloadTargetPath = NULL;
1776
1777 hr = CacheEnsureWorkingFolder(NULL, &sczWorkingFolder);
1778 ExitOnFailure(hr, "Failed to create working path to copy engine.");
1779
1780 hr = PathConcat(sczWorkingFolder, wzWorkingFolderName, &sczTargetDirectory);
1781 ExitOnFailure(hr, "Failed to calculate the bundle working folder target name.");
1782
1783 hr = DirEnsureExists(sczTargetDirectory, NULL);
1784 ExitOnFailure(hr, "Failed create bundle working folder.");
1785
1786 hr = PathConcat(sczTargetDirectory, wzExecutableName, &sczTargetPath);
1787 ExitOnFailure(hr, "Failed to combine working path with engine file name.");
1788
1789 // Copy the engine without any attached containers to the working path.
1790 hr = CopyEngineWithSignatureFixup(pSection->hEngineFile, wzSourcePath, sczTargetPath, pSection);
1791 ExitOnFailure(hr, "Failed to copy engine: '%ls' to working path: %ls", wzSourcePath, sczTargetPath);
1792
1793 if (psczEngineWorkingPath)
1794 {
1795 hr = StrAllocString(psczEngineWorkingPath, sczTargetPath, 0);
1796 ExitOnFailure(hr, "Failed to copy target path for engine working path.");
1797 }
1798
1799LExit:
1800 ReleaseStr(sczPayloadTargetPath);
1801 ReleaseStr(sczPayloadSourcePath);
1802 ReleaseStr(sczSourceDirectory);
1803 ReleaseStr(sczTargetPath);
1804 ReleaseStr(sczTargetDirectory);
1805 ReleaseStr(sczWorkingFolder);
1806
1807 return hr;
1808}
1809
1810
1811static HRESULT CopyEngineWithSignatureFixup(
1812 __in HANDLE hEngineFile,
1813 __in_z LPCWSTR wzEnginePath,
1814 __in_z LPCWSTR wzTargetPath,
1815 __in BURN_SECTION* pSection
1816 )
1817{
1818 HRESULT hr = S_OK;
1819 HANDLE hTarget = INVALID_HANDLE_VALUE;
1820 LARGE_INTEGER li = { };
1821 DWORD dwZeroOriginals[3] = { };
1822
1823 hTarget = ::CreateFileW(wzTargetPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1824 if (INVALID_HANDLE_VALUE == hTarget)
1825 {
1826 ExitWithLastError(hr, "Failed to create engine file at path: %ls", wzTargetPath);
1827 }
1828
1829 hr = FileSetPointer(hEngineFile, 0, NULL, FILE_BEGIN);
1830 ExitOnFailure(hr, "Failed to seek to beginning of engine file: %ls", wzEnginePath);
1831
1832 hr = FileCopyUsingHandles(hEngineFile, hTarget, pSection->cbEngineSize, NULL);
1833 ExitOnFailure(hr, "Failed to copy engine from: %ls to: %ls", wzEnginePath, wzTargetPath);
1834
1835 // If the original executable was signed, let's put back the checksum and signature.
1836 if (pSection->dwOriginalSignatureOffset)
1837 {
1838 // Fix up the checksum.
1839 li.QuadPart = pSection->dwChecksumOffset;
1840 if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN))
1841 {
1842 ExitWithLastError(hr, "Failed to seek to checksum in exe header.");
1843 }
1844
1845 hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&pSection->dwOriginalChecksum), sizeof(pSection->dwOriginalChecksum));
1846 ExitOnFailure(hr, "Failed to update signature offset.");
1847
1848 // Fix up the signature information.
1849 li.QuadPart = pSection->dwCertificateTableOffset;
1850 if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN))
1851 {
1852 ExitWithLastError(hr, "Failed to seek to signature table in exe header.");
1853 }
1854
1855 hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&pSection->dwOriginalSignatureOffset), sizeof(pSection->dwOriginalSignatureOffset));
1856 ExitOnFailure(hr, "Failed to update signature offset.");
1857
1858 hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&pSection->dwOriginalSignatureSize), sizeof(pSection->dwOriginalSignatureSize));
1859 ExitOnFailure(hr, "Failed to update signature offset.");
1860
1861 // Zero out the original information since that is how it was when the file was originally signed.
1862 li.QuadPart = pSection->dwOriginalChecksumAndSignatureOffset;
1863 if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN))
1864 {
1865 ExitWithLastError(hr, "Failed to seek to original data in exe burn section header.");
1866 }
1867
1868 hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&dwZeroOriginals), sizeof(dwZeroOriginals));
1869 ExitOnFailure(hr, "Failed to zero out original data offset.");
1870 }
1871
1872LExit:
1873 ReleaseFileHandle(hTarget);
1874
1875 return hr;
1876}
1877
1878
1879static HRESULT RemoveBundleOrPackage(
1880 __in BOOL fBundle,
1881 __in BOOL fPerMachine,
1882 __in_z LPCWSTR wzBundleOrPackageId,
1883 __in_z LPCWSTR wzCacheId
1884 )
1885{
1886 HRESULT hr = S_OK;
1887 LPWSTR sczRootCacheDirectory = NULL;
1888 LPWSTR sczDirectory = NULL;
1889
1890 hr = CacheGetCompletedPath(fPerMachine, wzCacheId, &sczDirectory);
1891 ExitOnFailure(hr, "Failed to calculate cache path.");
1892
1893 LogId(REPORT_STANDARD, fBundle ? MSG_UNCACHE_BUNDLE : MSG_UNCACHE_PACKAGE, wzBundleOrPackageId, sczDirectory);
1894
1895 // Try really hard to remove the cache directory.
1896 hr = E_FAIL;
1897 for (DWORD iRetry = 0; FAILED(hr) && iRetry < FILE_OPERATION_RETRY_COUNT; ++iRetry)
1898 {
1899 if (0 < iRetry)
1900 {
1901 ::Sleep(FILE_OPERATION_RETRY_WAIT);
1902 }
1903
1904 hr = DirEnsureDeleteEx(sczDirectory, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE);
1905 if (E_PATHNOTFOUND == hr)
1906 {
1907 break;
1908 }
1909 }
1910
1911 if (E_PATHNOTFOUND != hr && FAILED(hr))
1912 {
1913 LogId(REPORT_STANDARD, fBundle ? MSG_UNABLE_UNCACHE_BUNDLE : MSG_UNABLE_UNCACHE_PACKAGE, wzBundleOrPackageId, sczDirectory, hr);
1914 hr = S_OK;
1915 }
1916 else
1917 {
1918 // Try to remove root package cache in the off chance it is now empty.
1919 hr = GetRootPath(fPerMachine, TRUE, &sczRootCacheDirectory);
1920 ExitOnFailure(hr, "Failed to get %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user");
1921 DirEnsureDeleteEx(sczRootCacheDirectory, DIR_DELETE_SCHEDULE);
1922
1923 // GetRootPath returns S_FALSE if the package cache is redirected elsewhere.
1924 if (S_FALSE == hr)
1925 {
1926 hr = GetRootPath(fPerMachine, FALSE, &sczRootCacheDirectory);
1927 ExitOnFailure(hr, "Failed to get old %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user");
1928 DirEnsureDeleteEx(sczRootCacheDirectory, DIR_DELETE_SCHEDULE);
1929 }
1930 }
1931
1932LExit:
1933 ReleaseStr(sczDirectory);
1934 ReleaseStr(sczRootCacheDirectory);
1935
1936 return hr;
1937}
1938
1939static HRESULT VerifyHash(
1940 __in BYTE* pbHash,
1941 __in DWORD cbHash,
1942 __in DWORD64 qwFileSize,
1943 __in_z LPCWSTR wzUnverifiedPayloadPath,
1944 __in HANDLE hFile,
1945 __in BURN_CACHE_STEP cacheStep,
1946 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1947 __in LPPROGRESS_ROUTINE /*pfnProgress*/,
1948 __in LPVOID pContext
1949 )
1950{
1951 UNREFERENCED_PARAMETER(wzUnverifiedPayloadPath);
1952
1953 HRESULT hr = S_OK;
1954 BYTE rgbActualHash[SHA512_HASH_LEN] = { };
1955 DWORD64 qwHashedBytes = 0;
1956 LONGLONG llSize = 0;
1957 LPWSTR pszExpected = NULL;
1958 LPWSTR pszActual = NULL;
1959
1960 hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, cacheStep);
1961 ExitOnFailure(hr, "Aborted cache verify hash begin.");
1962
1963 hr = FileSizeByHandle(hFile, &llSize);
1964 ExitOnFailure(hr, "Failed to get file size for path: %ls", wzUnverifiedPayloadPath);
1965
1966 if (static_cast<DWORD64>(llSize) != qwFileSize)
1967 {
1968 ExitOnFailure(hr = ERROR_FILE_CORRUPT, "File size mismatch for path: %ls, expected: %llu, actual: %lld", wzUnverifiedPayloadPath, qwFileSize, llSize);
1969 }
1970
1971 // TODO: create a cryp hash file that sends progress.
1972 hr = CrypHashFileHandle(hFile, PROV_RSA_AES, CALG_SHA_512, rgbActualHash, sizeof(rgbActualHash), &qwHashedBytes);
1973 ExitOnFailure(hr, "Failed to calculate hash for path: %ls", wzUnverifiedPayloadPath);
1974
1975 // Compare hashes.
1976 if (cbHash != sizeof(rgbActualHash) || 0 != memcmp(pbHash, rgbActualHash, sizeof(rgbActualHash)))
1977 {
1978 hr = CRYPT_E_HASH_VALUE;
1979
1980 // Best effort to log the expected and actual hash value strings.
1981 if (SUCCEEDED(StrAllocHexEncode(pbHash, cbHash, &pszExpected)) &&
1982 SUCCEEDED(StrAllocHexEncode(rgbActualHash, sizeof(rgbActualHash), &pszActual)))
1983 {
1984 ExitOnFailure(hr, "Hash mismatch for path: %ls, expected: %ls, actual: %ls", wzUnverifiedPayloadPath, pszExpected, pszActual);
1985 }
1986 else
1987 {
1988 ExitOnFailure(hr, "Hash mismatch for path: %ls", wzUnverifiedPayloadPath);
1989 }
1990 }
1991
1992 hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, qwFileSize);
1993
1994LExit:
1995 SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr);
1996
1997 ReleaseStr(pszActual);
1998 ReleaseStr(pszExpected);
1999
2000 return hr;
2001}
2002
2003static HRESULT SendCacheBeginMessage(
2004 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
2005 __in LPVOID pContext,
2006 __in BURN_CACHE_STEP cacheStep
2007 )
2008{
2009 HRESULT hr = S_OK;
2010 BURN_CACHE_MESSAGE message = { };
2011
2012 message.type = BURN_CACHE_MESSAGE_BEGIN;
2013 message.begin.cacheStep = cacheStep;
2014
2015 hr = pfnCacheMessageHandler(&message, pContext);
2016
2017 return hr;
2018}
2019
2020static HRESULT SendCacheSuccessMessage(
2021 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
2022 __in LPVOID pContext,
2023 __in DWORD64 qwFileSize
2024 )
2025{
2026 HRESULT hr = S_OK;
2027 BURN_CACHE_MESSAGE message = { };
2028
2029 message.type = BURN_CACHE_MESSAGE_SUCCESS;
2030 message.success.qwFileSize = qwFileSize;
2031
2032 hr = pfnCacheMessageHandler(&message, pContext);
2033
2034 return hr;
2035}
2036
2037static HRESULT SendCacheCompleteMessage(
2038 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
2039 __in LPVOID pContext,
2040 __in HRESULT hrStatus
2041 )
2042{
2043 HRESULT hr = S_OK;
2044 BURN_CACHE_MESSAGE message = { };
2045
2046 message.type = BURN_CACHE_MESSAGE_COMPLETE;
2047 message.complete.hrStatus = hrStatus;
2048
2049 hr = pfnCacheMessageHandler(&message, pContext);
2050
2051 return hr;
2052}
diff --git a/src/burn/engine/cache.h b/src/burn/engine/cache.h
new file mode 100644
index 00000000..0152d33b
--- /dev/null
+++ b/src/burn/engine/cache.h
@@ -0,0 +1,216 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4#define BURN_CACHE_MAX_SEARCH_PATHS 7
5
6#ifdef __cplusplus
7extern "C" {
8#endif
9
10
11enum BURN_CACHE_MESSAGE_TYPE
12{
13 BURN_CACHE_MESSAGE_BEGIN,
14 BURN_CACHE_MESSAGE_SUCCESS,
15 BURN_CACHE_MESSAGE_COMPLETE,
16};
17
18enum BURN_CACHE_STEP
19{
20 BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE,
21 BURN_CACHE_STEP_HASH_TO_SKIP_VERIFY,
22 BURN_CACHE_STEP_STAGE,
23 BURN_CACHE_STEP_HASH,
24 BURN_CACHE_STEP_FINALIZE,
25};
26
27typedef struct _BURN_CACHE_MESSAGE
28{
29 BURN_CACHE_MESSAGE_TYPE type;
30
31 union
32 {
33 struct
34 {
35 BURN_CACHE_STEP cacheStep;
36 } begin;
37 struct
38 {
39 DWORD64 qwFileSize;
40 } success;
41 struct
42 {
43 HRESULT hrStatus;
44 } complete;
45 };
46} BURN_CACHE_MESSAGE;
47
48typedef HRESULT(CALLBACK* PFN_BURNCACHEMESSAGEHANDLER)(
49 __in BURN_CACHE_MESSAGE* pMessage,
50 __in LPVOID pvContext
51 );
52
53// functions
54
55HRESULT CacheInitialize(
56 __in BURN_REGISTRATION* pRegistration,
57 __in BURN_VARIABLES* pVariables,
58 __in_z_opt LPCWSTR wzSourceProcessPath
59 );
60HRESULT CacheEnsureWorkingFolder(
61 __in_z_opt LPCWSTR wzBundleId,
62 __deref_out_z_opt LPWSTR* psczWorkingFolder
63 );
64HRESULT CacheCalculateBundleWorkingPath(
65 __in_z LPCWSTR wzBundleId,
66 __in LPCWSTR wzExecutableName,
67 __deref_out_z LPWSTR* psczWorkingPath
68 );
69HRESULT CacheCalculateBundleLayoutWorkingPath(
70 __in_z LPCWSTR wzBundleId,
71 __deref_out_z LPWSTR* psczWorkingPath
72 );
73HRESULT CacheCalculatePayloadWorkingPath(
74 __in_z LPCWSTR wzBundleId,
75 __in BURN_PAYLOAD* pPayload,
76 __deref_out_z LPWSTR* psczWorkingPath
77 );
78HRESULT CacheCalculateContainerWorkingPath(
79 __in_z LPCWSTR wzBundleId,
80 __in BURN_CONTAINER* pContainer,
81 __deref_out_z LPWSTR* psczWorkingPath
82 );
83HRESULT CacheGetRootCompletedPath(
84 __in BOOL fPerMachine,
85 __in BOOL fForceInitialize,
86 __deref_out_z LPWSTR* psczRootCompletedPath
87 );
88HRESULT CacheGetCompletedPath(
89 __in BOOL fPerMachine,
90 __in_z LPCWSTR wzCacheId,
91 __deref_out_z LPWSTR* psczCompletedPath
92 );
93HRESULT CacheGetResumePath(
94 __in_z LPCWSTR wzPayloadWorkingPath,
95 __deref_out_z LPWSTR* psczResumePath
96 );
97HRESULT CacheGetLocalSourcePaths(
98 __in_z LPCWSTR wzRelativePath,
99 __in_z LPCWSTR wzSourcePath,
100 __in_z LPCWSTR wzDestinationPath,
101 __in_z_opt LPCWSTR wzLayoutDirectory,
102 __in BURN_VARIABLES* pVariables,
103 __inout LPWSTR** prgSearchPaths,
104 __out DWORD* pcSearchPaths,
105 __out DWORD* pdwLikelySearchPath,
106 __out DWORD* pdwDestinationSearchPath
107 );
108HRESULT CacheSetLastUsedSource(
109 __in BURN_VARIABLES* pVariables,
110 __in_z LPCWSTR wzSourcePath,
111 __in_z LPCWSTR wzRelativePath
112 );
113HRESULT CacheSendProgressCallback(
114 __in DOWNLOAD_CACHE_CALLBACK* pCallback,
115 __in DWORD64 dw64Progress,
116 __in DWORD64 dw64Total,
117 __in HANDLE hDestinationFile
118 );
119void CacheSendErrorCallback(
120 __in DOWNLOAD_CACHE_CALLBACK* pCallback,
121 __in HRESULT hrError,
122 __in_z_opt LPCWSTR wzError,
123 __out_opt BOOL* pfRetry
124 );
125BOOL CacheBundleRunningFromCache();
126HRESULT CacheBundleToCleanRoom(
127 __in BURN_SECTION* pSection,
128 __deref_out_z_opt LPWSTR* psczCleanRoomBundlePath
129 );
130HRESULT CacheBundleToWorkingDirectory(
131 __in_z LPCWSTR wzBundleId,
132 __in_z LPCWSTR wzExecutableName,
133 __in BURN_SECTION* pSection,
134 __deref_out_z_opt LPWSTR* psczEngineWorkingPath
135 );
136HRESULT CacheLayoutBundle(
137 __in_z LPCWSTR wzExecutableName,
138 __in_z LPCWSTR wzLayoutDirectory,
139 __in_z LPCWSTR wzSourceBundlePath,
140 __in DWORD64 qwBundleSize,
141 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
142 __in LPPROGRESS_ROUTINE pfnProgress,
143 __in LPVOID pContext
144 );
145HRESULT CacheCompleteBundle(
146 __in BOOL fPerMachine,
147 __in_z LPCWSTR wzExecutableName,
148 __in_z LPCWSTR wzBundleId,
149 __in_z LPCWSTR wzSourceBundlePath
150#ifdef DEBUG
151 , __in_z LPCWSTR wzExecutablePath
152#endif
153 );
154HRESULT CacheLayoutContainer(
155 __in BURN_CONTAINER* pContainer,
156 __in_z_opt LPCWSTR wzLayoutDirectory,
157 __in_z LPCWSTR wzUnverifiedContainerPath,
158 __in BOOL fMove,
159 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
160 __in LPPROGRESS_ROUTINE pfnProgress,
161 __in LPVOID pContext
162 );
163HRESULT CacheLayoutPayload(
164 __in BURN_PAYLOAD* pPayload,
165 __in_z_opt LPCWSTR wzLayoutDirectory,
166 __in_z LPCWSTR wzUnverifiedPayloadPath,
167 __in BOOL fMove,
168 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
169 __in LPPROGRESS_ROUTINE pfnProgress,
170 __in LPVOID pContext
171 );
172HRESULT CacheCompletePayload(
173 __in BOOL fPerMachine,
174 __in BURN_PAYLOAD* pPayload,
175 __in_z LPCWSTR wzCacheId,
176 __in_z LPCWSTR wzUnverifiedPayloadPath,
177 __in BOOL fMove,
178 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
179 __in LPPROGRESS_ROUTINE pfnProgress,
180 __in LPVOID pContext
181 );
182HRESULT CacheVerifyContainer(
183 __in BURN_CONTAINER* pContainer,
184 __in_z LPCWSTR wzCachedDirectory,
185 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
186 __in LPPROGRESS_ROUTINE pfnProgress,
187 __in LPVOID pContext
188 );
189HRESULT CacheVerifyPayload(
190 __in BURN_PAYLOAD* pPayload,
191 __in_z LPCWSTR wzCachedDirectory,
192 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
193 __in LPPROGRESS_ROUTINE pfnProgress,
194 __in LPVOID pContext
195 );
196HRESULT CacheRemoveWorkingFolder(
197 __in_z_opt LPCWSTR wzBundleId
198 );
199HRESULT CacheRemoveBundle(
200 __in BOOL fPerMachine,
201 __in_z LPCWSTR wzPackageId
202 );
203HRESULT CacheRemovePackage(
204 __in BOOL fPerMachine,
205 __in_z LPCWSTR wzPackageId,
206 __in_z LPCWSTR wzCacheId
207 );
208void CacheCleanup(
209 __in BOOL fPerMachine,
210 __in_z LPCWSTR wzBundleId
211 );
212void CacheUninitialize();
213
214#ifdef __cplusplus
215}
216#endif
diff --git a/src/burn/engine/condition.cpp b/src/burn/engine/condition.cpp
new file mode 100644
index 00000000..b7cd7413
--- /dev/null
+++ b/src/burn/engine/condition.cpp
@@ -0,0 +1,1057 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6//
7// parse rules
8//
9// value variable | literal | integer | version
10// comparison-operator < | > | <= | >= | = | <> | >< | << | >>
11// term value | value comparison-operator value | ( expression )
12// boolean-factor term | NOT term
13// boolean-term boolean-factor | boolean-factor AND boolean-term
14// expression boolean-term | boolean-term OR expression
15//
16
17
18// constants
19
20#define COMPARISON 0x00010000
21#define INSENSITIVE 0x00020000
22
23enum BURN_SYMBOL_TYPE
24{
25 // terminals
26 BURN_SYMBOL_TYPE_NONE = 0,
27 BURN_SYMBOL_TYPE_END = 1,
28 BURN_SYMBOL_TYPE_OR = 2, // OR
29 BURN_SYMBOL_TYPE_AND = 3, // AND
30 BURN_SYMBOL_TYPE_NOT = 4, // NOT
31 BURN_SYMBOL_TYPE_LT = 5 | COMPARISON, // <
32 BURN_SYMBOL_TYPE_GT = 6 | COMPARISON, // >
33 BURN_SYMBOL_TYPE_LE = 7 | COMPARISON, // <=
34 BURN_SYMBOL_TYPE_GE = 8 | COMPARISON, // >=
35 BURN_SYMBOL_TYPE_EQ = 9 | COMPARISON, // =
36 BURN_SYMBOL_TYPE_NE = 10 | COMPARISON, // <>
37 BURN_SYMBOL_TYPE_BAND = 11 | COMPARISON, // ><
38 BURN_SYMBOL_TYPE_HIEQ = 12 | COMPARISON, // <<
39 BURN_SYMBOL_TYPE_LOEQ = 13 | COMPARISON, // >>
40 BURN_SYMBOL_TYPE_LT_I = 5 | COMPARISON | INSENSITIVE, // ~<
41 BURN_SYMBOL_TYPE_GT_I = 6 | COMPARISON | INSENSITIVE, // ~>
42 BURN_SYMBOL_TYPE_LE_I = 7 | COMPARISON | INSENSITIVE, // ~<=
43 BURN_SYMBOL_TYPE_GE_I = 8 | COMPARISON | INSENSITIVE, // ~>=
44 BURN_SYMBOL_TYPE_EQ_I = 9 | COMPARISON | INSENSITIVE, // ~=
45 BURN_SYMBOL_TYPE_NE_I = 10 | COMPARISON | INSENSITIVE, // ~<>
46 BURN_SYMBOL_TYPE_BAND_I = 11 | COMPARISON | INSENSITIVE, // ~><
47 BURN_SYMBOL_TYPE_HIEQ_I = 12 | COMPARISON | INSENSITIVE, // ~<<
48 BURN_SYMBOL_TYPE_LOEQ_I = 13 | COMPARISON | INSENSITIVE, // ~>>
49 BURN_SYMBOL_TYPE_LPAREN = 14, // (
50 BURN_SYMBOL_TYPE_RPAREN = 15, // )
51 BURN_SYMBOL_TYPE_NUMBER = 16,
52 BURN_SYMBOL_TYPE_IDENTIFIER = 17,
53 BURN_SYMBOL_TYPE_LITERAL = 18,
54 BURN_SYMBOL_TYPE_VERSION = 19,
55};
56
57
58// structs
59
60struct BURN_SYMBOL
61{
62 BURN_SYMBOL_TYPE Type;
63 DWORD iPosition;
64 BURN_VARIANT Value;
65};
66
67struct BURN_CONDITION_PARSE_CONTEXT
68{
69 BURN_VARIABLES* pVariables;
70 LPCWSTR wzCondition;
71 LPCWSTR wzRead;
72 BURN_SYMBOL NextSymbol;
73 BOOL fError;
74};
75
76struct BURN_CONDITION_OPERAND
77{
78 BOOL fHidden;
79 BURN_VARIANT Value;
80};
81
82
83// internal function declarations
84
85static HRESULT ParseExpression(
86 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
87 __out BOOL* pf
88 );
89static HRESULT ParseBooleanTerm(
90 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
91 __out BOOL* pf
92 );
93static HRESULT ParseBooleanFactor(
94 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
95 __out BOOL* pf
96 );
97static HRESULT ParseTerm(
98 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
99 __out BOOL* pf
100 );
101static HRESULT ParseOperand(
102 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
103 __out BURN_CONDITION_OPERAND* pOperand
104 );
105static HRESULT Expect(
106 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
107 __in BURN_SYMBOL_TYPE symbolType
108 );
109static HRESULT NextSymbol(
110 __in BURN_CONDITION_PARSE_CONTEXT* pContext
111 );
112static HRESULT CompareOperands(
113 __in BURN_SYMBOL_TYPE comparison,
114 __in BURN_CONDITION_OPERAND* pLeftOperand,
115 __in BURN_CONDITION_OPERAND* pRightOperand,
116 __out BOOL* pfResult
117 );
118static HRESULT CompareStringValues(
119 __in BURN_SYMBOL_TYPE comparison,
120 __in_z LPCWSTR wzLeftOperand,
121 __in_z LPCWSTR wzRightOperand,
122 __out BOOL* pfResult
123 );
124static HRESULT CompareIntegerValues(
125 __in BURN_SYMBOL_TYPE comparison,
126 __in LONGLONG llLeftOperand,
127 __in LONGLONG llRightOperand,
128 __out BOOL* pfResult
129 );
130static HRESULT CompareVersionValues(
131 __in BURN_SYMBOL_TYPE comparison,
132 __in VERUTIL_VERSION* pLeftOperand,
133 __in VERUTIL_VERSION* pRightOperand,
134 __out BOOL* pfResult
135 );
136
137
138// function definitions
139
140extern "C" HRESULT ConditionEvaluate(
141 __in BURN_VARIABLES* pVariables,
142 __in_z LPCWSTR wzCondition,
143 __out BOOL* pf
144 )
145{
146 HRESULT hr = S_OK;
147 BURN_CONDITION_PARSE_CONTEXT context = { };
148 BOOL f = FALSE;
149
150 context.pVariables = pVariables;
151 context.wzCondition = wzCondition;
152 context.wzRead = wzCondition;
153
154 hr = NextSymbol(&context);
155 ExitOnFailure(hr, "Failed to read next symbol.");
156
157 hr = ParseExpression(&context, &f);
158 ExitOnFailure(hr, "Failed to parse expression.");
159
160 hr = Expect(&context, BURN_SYMBOL_TYPE_END);
161 ExitOnFailure(hr, "Failed to expect end symbol.");
162
163 LogId(REPORT_VERBOSE, MSG_CONDITION_RESULT, wzCondition, LoggingTrueFalseToString(f));
164
165 *pf = f;
166 hr = S_OK;
167
168LExit:
169 if (context.fError)
170 {
171 Assert(FAILED(hr));
172 LogErrorId(hr, MSG_FAILED_PARSE_CONDITION, wzCondition, NULL, NULL);
173 }
174
175 return hr;
176}
177
178extern "C" HRESULT ConditionGlobalCheck(
179 __in BURN_VARIABLES* pVariables,
180 __in BURN_CONDITION* pCondition,
181 __in BOOTSTRAPPER_DISPLAY display,
182 __in_z LPCWSTR wzBundleName,
183 __out DWORD *pdwExitCode,
184 __out BOOL *pfContinueExecution
185 )
186{
187 HRESULT hr = S_OK;
188 BOOL fSuccess = TRUE;
189 HRESULT hrError = HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION);
190
191 // Only run on Windows Vista SP2 or newer, or Windows Server 2008 SP2 or newer.
192 if (!::IsWindowsVistaSP2OrGreater())
193 {
194 fSuccess = FALSE;
195 }
196 else
197 {
198 if (NULL != pCondition->sczConditionString)
199 {
200 hr = ConditionEvaluate(pVariables, pCondition->sczConditionString, &fSuccess);
201 ExitOnFailure(hr, "Failed to evaluate condition: %ls", pCondition->sczConditionString);
202 }
203 }
204
205 if (!fSuccess)
206 {
207 // Display the error messagebox, as long as we're in an appropriate display mode
208 hr = SplashScreenDisplayError(display, wzBundleName, hrError);
209 ExitOnFailure(hr, "Failed to display error dialog");
210
211 *pdwExitCode = static_cast<DWORD>(hrError);
212 *pfContinueExecution = FALSE;
213 }
214
215LExit:
216 return hr;
217}
218
219HRESULT ConditionGlobalParseFromXml(
220 __in BURN_CONDITION* pCondition,
221 __in IXMLDOMNode* pixnBundle
222 )
223{
224 HRESULT hr = S_OK;
225 IXMLDOMNode* pixnNode = NULL;
226 BSTR bstrExpression = NULL;
227
228 // select variable nodes
229 hr = XmlSelectSingleNode(pixnBundle, L"Condition", &pixnNode);
230 if (S_FALSE == hr)
231 {
232 ExitFunction1(hr = S_OK);
233 }
234 ExitOnFailure(hr, "Failed to select condition node.");
235
236 // @Condition
237 hr = XmlGetText(pixnNode, &bstrExpression);
238 ExitOnFailure(hr, "Failed to get Condition inner text.");
239
240 hr = StrAllocString(&pCondition->sczConditionString, bstrExpression, 0);
241 ExitOnFailure(hr, "Failed to copy condition string from BSTR");
242
243LExit:
244 ReleaseBSTR(bstrExpression);
245 ReleaseObject(pixnNode);
246
247 return hr;
248}
249
250
251// internal function definitions
252
253static HRESULT ParseExpression(
254 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
255 __out BOOL* pf
256 )
257{
258 HRESULT hr = S_OK;
259 BOOL fFirst = FALSE;
260 BOOL fSecond = FALSE;
261
262 hr = ParseBooleanTerm(pContext, &fFirst);
263 ExitOnFailure(hr, "Failed to parse boolean-term.");
264
265 if (BURN_SYMBOL_TYPE_OR == pContext->NextSymbol.Type)
266 {
267 hr = NextSymbol(pContext);
268 ExitOnFailure(hr, "Failed to read next symbol.");
269
270 hr = ParseExpression(pContext, &fSecond);
271 ExitOnFailure(hr, "Failed to parse expression.");
272
273 *pf = fFirst || fSecond;
274 }
275 else
276 {
277 *pf = fFirst;
278 }
279
280LExit:
281 return hr;
282}
283
284static HRESULT ParseBooleanTerm(
285 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
286 __out BOOL* pf
287 )
288{
289 HRESULT hr = S_OK;
290 BOOL fFirst = FALSE;
291 BOOL fSecond = FALSE;
292
293 hr = ParseBooleanFactor(pContext, &fFirst);
294 ExitOnFailure(hr, "Failed to parse boolean-factor.");
295
296 if (BURN_SYMBOL_TYPE_AND == pContext->NextSymbol.Type)
297 {
298 hr = NextSymbol(pContext);
299 ExitOnFailure(hr, "Failed to read next symbol.");
300
301 hr = ParseBooleanTerm(pContext, &fSecond);
302 ExitOnFailure(hr, "Failed to parse boolean-term.");
303
304 *pf = fFirst && fSecond;
305 }
306 else
307 {
308 *pf = fFirst;
309 }
310
311LExit:
312 return hr;
313}
314
315static HRESULT ParseBooleanFactor(
316 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
317 __out BOOL* pf
318 )
319{
320 HRESULT hr = S_OK;
321 BOOL fNot = FALSE;
322 BOOL f = FALSE;
323
324 if (BURN_SYMBOL_TYPE_NOT == pContext->NextSymbol.Type)
325 {
326 hr = NextSymbol(pContext);
327 ExitOnFailure(hr, "Failed to read next symbol.");
328
329 fNot = TRUE;
330 }
331
332 hr = ParseTerm(pContext, &f);
333 ExitOnFailure(hr, "Failed to parse term.");
334
335 *pf = fNot ? !f : f;
336
337LExit:
338 return hr;
339}
340
341static HRESULT ParseTerm(
342 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
343 __out BOOL* pf
344 )
345{
346 HRESULT hr = S_OK;
347 BURN_CONDITION_OPERAND firstOperand = { };
348 BURN_CONDITION_OPERAND secondOperand = { };
349
350 if (BURN_SYMBOL_TYPE_LPAREN == pContext->NextSymbol.Type)
351 {
352 hr = NextSymbol(pContext);
353 ExitOnFailure(hr, "Failed to read next symbol.");
354
355 hr = ParseExpression(pContext, pf);
356 ExitOnFailure(hr, "Failed to parse expression.");
357
358 hr = Expect(pContext, BURN_SYMBOL_TYPE_RPAREN);
359 ExitOnFailure(hr, "Failed to expect right parenthesis.");
360
361 ExitFunction1(hr = S_OK);
362 }
363
364 hr = ParseOperand(pContext, &firstOperand);
365 ExitOnFailure(hr, "Failed to parse operand.");
366
367 if (COMPARISON & pContext->NextSymbol.Type)
368 {
369 BURN_SYMBOL_TYPE comparison = pContext->NextSymbol.Type;
370
371 hr = NextSymbol(pContext);
372 ExitOnFailure(hr, "Failed to read next symbol.");
373
374 hr = ParseOperand(pContext, &secondOperand);
375 ExitOnFailure(hr, "Failed to parse operand.");
376
377 hr = CompareOperands(comparison, &firstOperand, &secondOperand, pf);
378 ExitOnFailure(hr, "Failed to compare operands.");
379 }
380 else
381 {
382 LONGLONG llValue = 0;
383 LPWSTR sczValue = NULL;
384 VERUTIL_VERSION* pVersion = NULL;
385 switch (firstOperand.Value.Type)
386 {
387 case BURN_VARIANT_TYPE_NONE:
388 *pf = FALSE;
389 break;
390 case BURN_VARIANT_TYPE_STRING:
391 hr = BVariantGetString(&firstOperand.Value, &sczValue);
392 if (SUCCEEDED(hr))
393 {
394 *pf = sczValue && *sczValue;
395 }
396 StrSecureZeroFreeString(sczValue);
397 break;
398 case BURN_VARIANT_TYPE_NUMERIC:
399 hr = BVariantGetNumeric(&firstOperand.Value, &llValue);
400 if (SUCCEEDED(hr))
401 {
402 *pf = 0 != llValue;
403 }
404 SecureZeroMemory(&llValue, sizeof(llValue));
405 break;
406 case BURN_VARIANT_TYPE_VERSION:
407 hr = BVariantGetVersionHidden(&firstOperand.Value, firstOperand.fHidden, &pVersion);
408 if (SUCCEEDED(hr))
409 {
410 *pf = 0 != *pVersion->sczVersion;
411 }
412 ReleaseVerutilVersion(pVersion);
413 break;
414 default:
415 ExitFunction1(hr = E_UNEXPECTED);
416 }
417 }
418
419LExit:
420 BVariantUninitialize(&firstOperand.Value);
421 BVariantUninitialize(&secondOperand.Value);
422 return hr;
423}
424
425static HRESULT ParseOperand(
426 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
427 __out BURN_CONDITION_OPERAND* pOperand
428 )
429{
430 HRESULT hr = S_OK;
431 LPWSTR sczFormatted = NULL;
432
433 switch (pContext->NextSymbol.Type)
434 {
435 case BURN_SYMBOL_TYPE_IDENTIFIER:
436 Assert(BURN_VARIANT_TYPE_STRING == pContext->NextSymbol.Value.Type);
437
438 // find variable
439 hr = VariableGetVariant(pContext->pVariables, pContext->NextSymbol.Value.sczValue, &pOperand->Value);
440 if (E_NOTFOUND != hr)
441 {
442 ExitOnRootFailure(hr, "Failed to find variable.");
443
444 hr = VariableIsHidden(pContext->pVariables, pContext->NextSymbol.Value.sczValue, &pOperand->fHidden);
445 ExitOnRootFailure(hr, "Failed to get if variable is hidden.");
446 }
447
448 if (BURN_VARIANT_TYPE_FORMATTED == pOperand->Value.Type)
449 {
450 hr = VariableGetFormatted(pContext->pVariables, pContext->NextSymbol.Value.sczValue, &sczFormatted, &pOperand->fHidden);
451 ExitOnRootFailure(hr, "Failed to format variable '%ls' for condition '%ls'", pContext->NextSymbol.Value.sczValue, pContext->wzCondition);
452
453 hr = BVariantSetString(&pOperand->Value, sczFormatted, 0, FALSE);
454 ExitOnRootFailure(hr, "Failed to store formatted value for variable '%ls' for condition '%ls'", pContext->NextSymbol.Value.sczValue, pContext->wzCondition);
455 }
456 break;
457
458 case BURN_SYMBOL_TYPE_NUMBER: __fallthrough;
459 case BURN_SYMBOL_TYPE_LITERAL: __fallthrough;
460 case BURN_SYMBOL_TYPE_VERSION:
461 pOperand->fHidden = FALSE;
462 // steal value of symbol
463 memcpy_s(&pOperand->Value, sizeof(BURN_VARIANT), &pContext->NextSymbol.Value, sizeof(BURN_VARIANT));
464 memset(&pContext->NextSymbol.Value, 0, sizeof(BURN_VARIANT));
465 break;
466
467 default:
468 pContext->fError = TRUE;
469 hr = E_INVALIDDATA;
470 ExitOnRootFailure(hr, "Failed to parse condition '%ls' at position: %u", pContext->wzCondition, pContext->NextSymbol.iPosition);
471 }
472
473 // get next symbol
474 hr = NextSymbol(pContext);
475 ExitOnFailure(hr, "Failed to read next symbol.");
476
477LExit:
478 StrSecureZeroFreeString(sczFormatted);
479
480 return hr;
481}
482
483//
484// Expect - expects a symbol.
485//
486static HRESULT Expect(
487 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
488 __in BURN_SYMBOL_TYPE symbolType
489 )
490{
491 HRESULT hr = S_OK;
492
493 if (pContext->NextSymbol.Type != symbolType)
494 {
495 pContext->fError = TRUE;
496 hr = E_INVALIDDATA;
497 ExitOnRootFailure(hr, "Failed to parse condition '%ls' at position: %u", pContext->wzCondition, pContext->NextSymbol.iPosition);
498 }
499
500 hr = NextSymbol(pContext);
501 ExitOnFailure(hr, "Failed to read next symbol.");
502
503LExit:
504 return hr;
505}
506
507//
508// NextSymbol - finds the next symbol in an expression string.
509//
510static HRESULT NextSymbol(
511 __in BURN_CONDITION_PARSE_CONTEXT* pContext
512 )
513{
514 HRESULT hr = S_OK;
515 WORD charType = 0;
516 ptrdiff_t cchPosition = 0;
517 DWORD iPosition = 0;
518 DWORD n = 0;
519
520 // free existing symbol
521 BVariantUninitialize(&pContext->NextSymbol.Value);
522 memset(&pContext->NextSymbol, 0, sizeof(BURN_SYMBOL));
523
524 // skip past blanks
525 while (L'\0' != pContext->wzRead[0])
526 {
527 ::GetStringTypeW(CT_CTYPE1, pContext->wzRead, 1, &charType);
528 if (0 == (C1_BLANK & charType))
529 {
530 break; // no blank, done
531 }
532 ++pContext->wzRead;
533 }
534
535 cchPosition = pContext->wzRead - pContext->wzCondition;
536 if (DWORD_MAX < cchPosition || 0 > cchPosition)
537 {
538 ExitOnFailure(hr = E_INVALIDARG, "Symbol was too long: %ls", pContext->wzCondition);
539 }
540 iPosition = (DWORD)cchPosition;
541
542 // read depending on first character type
543 switch (pContext->wzRead[0])
544 {
545 case L'\0':
546 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_END;
547 break;
548 case L'~':
549 switch (pContext->wzRead[1])
550 {
551 case L'=':
552 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_EQ_I;
553 n = 2;
554 break;
555 case L'>':
556 switch (pContext->wzRead[2])
557 {
558 case '=':
559 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GE_I;
560 n = 3;
561 break;
562 case L'>':
563 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LOEQ_I;
564 n = 3;
565 break;
566 case L'<':
567 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_BAND_I;
568 n = 3;
569 break;
570 default:
571 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GT_I;
572 n = 2;
573 }
574 break;
575 case L'<':
576 switch (pContext->wzRead[2])
577 {
578 case '=':
579 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LE_I;
580 n = 3;
581 break;
582 case L'<':
583 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_HIEQ_I;
584 n = 3;
585 break;
586 case '>':
587 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NE_I;
588 n = 3;
589 break;
590 default:
591 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LT_I;
592 n = 2;
593 }
594 break;
595 default:
596 // error
597 pContext->fError = TRUE;
598 hr = E_INVALIDDATA;
599 ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unexpected '~' operator at position %d.", pContext->wzCondition, iPosition);
600 }
601 break;
602 case L'>':
603 switch (pContext->wzRead[1])
604 {
605 case L'=':
606 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GE;
607 n = 2;
608 break;
609 case L'>':
610 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LOEQ;
611 n = 2;
612 break;
613 case L'<':
614 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_BAND;
615 n = 2;
616 break;
617 default:
618 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GT;
619 n = 1;
620 }
621 break;
622 case L'<':
623 switch (pContext->wzRead[1])
624 {
625 case L'=':
626 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LE;
627 n = 2;
628 break;
629 case L'<':
630 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_HIEQ;
631 n = 2;
632 break;
633 case L'>':
634 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NE;
635 n = 2;
636 break;
637 default:
638 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LT;
639 n = 1;
640 }
641 break;
642 case L'=':
643 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_EQ;
644 n = 1;
645 break;
646 case L'(':
647 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LPAREN;
648 n = 1;
649 break;
650 case L')':
651 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_RPAREN;
652 n = 1;
653 break;
654 case L'"': // literal
655 do
656 {
657 ++n;
658 if (L'\0' == pContext->wzRead[n])
659 {
660 // error
661 pContext->fError = TRUE;
662 hr = E_INVALIDDATA;
663 ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unterminated literal at position %d.", pContext->wzCondition, iPosition);
664 }
665 } while (L'"' != pContext->wzRead[n]);
666 ++n; // terminating '"'
667
668 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LITERAL;
669 hr = BVariantSetString(&pContext->NextSymbol.Value, &pContext->wzRead[1], n - 2, FALSE);
670 ExitOnFailure(hr, "Failed to set symbol value.");
671 break;
672 default:
673 if (C1_DIGIT & charType || L'-' == pContext->wzRead[0])
674 {
675 do
676 {
677 ++n;
678 ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType);
679 if (C1_ALPHA & charType || L'_' == pContext->wzRead[n])
680 {
681 // error, identifier cannot start with a digit
682 pContext->fError = TRUE;
683 hr = E_INVALIDDATA;
684 ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Identifier cannot start at a digit, at position %d.", pContext->wzCondition, iPosition);
685 }
686 } while (C1_DIGIT & charType);
687
688 // number
689 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NUMBER;
690
691 LONGLONG ll = 0;
692 hr = StrStringToInt64(pContext->wzRead, n, &ll);
693 if (FAILED(hr))
694 {
695 pContext->fError = TRUE;
696 hr = E_INVALIDDATA;
697 ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Constant too big, at position %d.", pContext->wzCondition, iPosition);
698 }
699
700 hr = BVariantSetNumeric(&pContext->NextSymbol.Value, ll);
701 ExitOnFailure(hr, "Failed to set symbol value.");
702 }
703 else if (C1_ALPHA & charType || L'_' == pContext->wzRead[0])
704 {
705 ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[1], 1, &charType);
706 if (L'v' == pContext->wzRead[0] && C1_DIGIT & charType)
707 {
708 // version
709 do
710 {
711 ++n;
712 } while (pContext->wzRead[n] >= L'0' && pContext->wzRead[n] <= L'9' ||
713 pContext->wzRead[n] >= L'A' && pContext->wzRead[n] <= L'Z' ||
714 pContext->wzRead[n] >= L'a' && pContext->wzRead[n] <= L'z' ||
715 pContext->wzRead[n] == L'_' ||
716 pContext->wzRead[n] == L'+' ||
717 pContext->wzRead[n] == L'-' ||
718 pContext->wzRead[n] == L'.');
719
720 hr = VerParseVersion(&pContext->wzRead[1], n - 1, FALSE, &pContext->NextSymbol.Value.pValue);
721 if (FAILED(hr))
722 {
723 pContext->fError = TRUE;
724 hr = E_INVALIDDATA;
725 ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Invalid version format, at position %d.", pContext->wzCondition, iPosition);
726 }
727 else if (pContext->NextSymbol.Value.pValue->fInvalid)
728 {
729 LogId(REPORT_WARNING, MSG_CONDITION_INVALID_VERSION, pContext->wzCondition, pContext->NextSymbol.Value.pValue->sczVersion);
730 }
731
732 pContext->NextSymbol.Value.Type = BURN_VARIANT_TYPE_VERSION;
733 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_VERSION;
734 }
735 else
736 {
737 do
738 {
739 ++n;
740 ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType);
741 } while (C1_ALPHA & charType || C1_DIGIT & charType || L'_' == pContext->wzRead[n]);
742
743 if (2 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 2, L"OR", 2))
744 {
745 // OR
746 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_OR;
747 }
748 else if (3 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 3, L"AND", 3))
749 {
750 // AND
751 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_AND;
752 }
753 else if (3 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 3, L"NOT", 3))
754 {
755 // NOT
756 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NOT;
757 }
758 else
759 {
760 // identifier
761 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_IDENTIFIER;
762 hr = BVariantSetString(&pContext->NextSymbol.Value, pContext->wzRead, n, FALSE);
763 ExitOnFailure(hr, "Failed to set symbol value.");
764 }
765 }
766 }
767 else
768 {
769 // error, unexpected character
770 pContext->fError = TRUE;
771 hr = E_INVALIDDATA;
772 ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unexpected character at position %d.", pContext->wzCondition, iPosition);
773 }
774 }
775 pContext->NextSymbol.iPosition = iPosition;
776 pContext->wzRead += n;
777
778LExit:
779 return hr;
780}
781
782//
783// CompareOperands - compares two variant values using a given comparison.
784//
785static HRESULT CompareOperands(
786 __in BURN_SYMBOL_TYPE comparison,
787 __in BURN_CONDITION_OPERAND* pLeftOperand,
788 __in BURN_CONDITION_OPERAND* pRightOperand,
789 __out BOOL* pfResult
790 )
791{
792 HRESULT hr = S_OK;
793 LONGLONG llLeft = 0;
794 VERUTIL_VERSION* pVersionLeft = 0;
795 LPWSTR sczLeft = NULL;
796 LONGLONG llRight = 0;
797 VERUTIL_VERSION* pVersionRight = 0;
798 LPWSTR sczRight = NULL;
799 BURN_VARIANT* pLeftValue = &pLeftOperand->Value;
800 BURN_VARIANT* pRightValue = &pRightOperand->Value;
801
802 // get values to compare based on type
803 if (BURN_VARIANT_TYPE_STRING == pLeftValue->Type && BURN_VARIANT_TYPE_STRING == pRightValue->Type)
804 {
805 hr = BVariantGetString(pLeftValue, &sczLeft);
806 ExitOnFailure(hr, "Failed to get the left string");
807 hr = BVariantGetString(pRightValue, &sczRight);
808 ExitOnFailure(hr, "Failed to get the right string");
809 hr = CompareStringValues(comparison, sczLeft, sczRight, pfResult);
810 }
811 else if (BURN_VARIANT_TYPE_NUMERIC == pLeftValue->Type && BURN_VARIANT_TYPE_NUMERIC == pRightValue->Type)
812 {
813 hr = BVariantGetNumeric(pLeftValue, &llLeft);
814 ExitOnFailure(hr, "Failed to get the left numeric");
815 hr = BVariantGetNumeric(pRightValue, &llRight);
816 ExitOnFailure(hr, "Failed to get the right numeric");
817 hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult);
818 }
819 else if (BURN_VARIANT_TYPE_VERSION == pLeftValue->Type && BURN_VARIANT_TYPE_VERSION == pRightValue->Type)
820 {
821 hr = BVariantGetVersionHidden(pLeftValue, pLeftOperand->fHidden, &pVersionLeft);
822 ExitOnFailure(hr, "Failed to get the left version");
823 hr = BVariantGetVersionHidden(pRightValue, pRightOperand->fHidden, &pVersionRight);
824 ExitOnFailure(hr, "Failed to get the right version");
825 hr = CompareVersionValues(comparison, pVersionLeft, pVersionRight, pfResult);
826 }
827 else if (BURN_VARIANT_TYPE_VERSION == pLeftValue->Type && BURN_VARIANT_TYPE_STRING == pRightValue->Type)
828 {
829 hr = BVariantGetVersionHidden(pLeftValue, pLeftOperand->fHidden, &pVersionLeft);
830 ExitOnFailure(hr, "Failed to get the left version");
831 hr = BVariantGetVersionHidden(pRightValue, pRightOperand->fHidden, &pVersionRight);
832 if (FAILED(hr))
833 {
834 if (DISP_E_TYPEMISMATCH != hr)
835 {
836 ExitOnFailure(hr, "Failed to get the right version");
837 }
838 *pfResult = (BURN_SYMBOL_TYPE_NE == comparison);
839 hr = S_OK;
840 }
841 else
842 {
843 hr = CompareVersionValues(comparison, pVersionLeft, pVersionRight, pfResult);
844 }
845 }
846 else if (BURN_VARIANT_TYPE_STRING == pLeftValue->Type && BURN_VARIANT_TYPE_VERSION == pRightValue->Type)
847 {
848 hr = BVariantGetVersionHidden(pRightValue, pRightOperand->fHidden, &pVersionRight);
849 ExitOnFailure(hr, "Failed to get the right version");
850 hr = BVariantGetVersionHidden(pLeftValue, pLeftOperand->fHidden, &pVersionLeft);
851 if (FAILED(hr))
852 {
853 if (DISP_E_TYPEMISMATCH != hr)
854 {
855 ExitOnFailure(hr, "Failed to get the left version");
856 }
857 *pfResult = (BURN_SYMBOL_TYPE_NE == comparison);
858 hr = S_OK;
859 }
860 else
861 {
862 hr = CompareVersionValues(comparison, pVersionLeft, pVersionRight, pfResult);
863 }
864 }
865 else if (BURN_VARIANT_TYPE_NUMERIC == pLeftValue->Type && BURN_VARIANT_TYPE_STRING == pRightValue->Type)
866 {
867 hr = BVariantGetNumeric(pLeftValue, &llLeft);
868 ExitOnFailure(hr, "Failed to get the left numeric");
869 hr = BVariantGetNumeric(pRightValue, &llRight);
870 if (FAILED(hr))
871 {
872 if (DISP_E_TYPEMISMATCH != hr)
873 {
874 ExitOnFailure(hr, "Failed to get the right numeric");
875 }
876 *pfResult = (BURN_SYMBOL_TYPE_NE == comparison);
877 hr = S_OK;
878 }
879 else
880 {
881 hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult);
882 }
883 }
884 else if (BURN_VARIANT_TYPE_STRING == pLeftValue->Type && BURN_VARIANT_TYPE_NUMERIC == pRightValue->Type)
885 {
886 hr = BVariantGetNumeric(pRightValue, &llRight);
887 ExitOnFailure(hr, "Failed to get the right numeric");
888 hr = BVariantGetNumeric(pLeftValue, &llLeft);
889 if (FAILED(hr))
890 {
891 if (DISP_E_TYPEMISMATCH != hr)
892 {
893 ExitOnFailure(hr, "Failed to get the left numeric");
894 }
895 *pfResult = (BURN_SYMBOL_TYPE_NE == comparison);
896 hr = S_OK;
897 }
898 else
899 {
900 hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult);
901 }
902 }
903 else
904 {
905 // not a combination that can be compared
906 *pfResult = (BURN_SYMBOL_TYPE_NE == comparison || BURN_SYMBOL_TYPE_NE_I == comparison);
907 }
908
909LExit:
910 ReleaseVerutilVersion(pVersionLeft);
911 SecureZeroMemory(&llLeft, sizeof(LONGLONG));
912 StrSecureZeroFreeString(sczLeft);
913 ReleaseVerutilVersion(pVersionRight);
914 SecureZeroMemory(&llRight, sizeof(LONGLONG));
915 StrSecureZeroFreeString(sczRight);
916
917 return hr;
918}
919
920//
921// CompareStringValues - compares two string values using a given comparison.
922//
923static HRESULT CompareStringValues(
924 __in BURN_SYMBOL_TYPE comparison,
925 __in_z LPCWSTR wzLeftOperand,
926 __in_z LPCWSTR wzRightOperand,
927 __out BOOL* pfResult
928 )
929{
930 HRESULT hr = S_OK;
931 DWORD dwCompareString = (comparison & INSENSITIVE) ? NORM_IGNORECASE : 0;
932 size_t cchLeftSize = 0;
933 size_t cchRightSize = 0;
934 int cchLeft = 0;
935 int cchRight = 0;
936
937 hr = ::StringCchLengthW(wzLeftOperand, STRSAFE_MAX_CCH, &cchLeftSize);
938 ExitOnRootFailure(hr, "Failed to get length of left string: %ls", wzLeftOperand);
939
940 hr = ::StringCchLengthW(wzRightOperand, STRSAFE_MAX_CCH, &cchRightSize);
941 ExitOnRootFailure(hr, "Failed to get length of right string: %ls", wzRightOperand);
942
943 cchLeft = static_cast<int>(cchLeftSize);
944 cchRight = static_cast<int>(cchRightSize);
945
946 switch (comparison)
947 {
948 case BURN_SYMBOL_TYPE_LT:
949 case BURN_SYMBOL_TYPE_GT:
950 case BURN_SYMBOL_TYPE_LE:
951 case BURN_SYMBOL_TYPE_GE:
952 case BURN_SYMBOL_TYPE_EQ:
953 case BURN_SYMBOL_TYPE_NE:
954 case BURN_SYMBOL_TYPE_LT_I:
955 case BURN_SYMBOL_TYPE_GT_I:
956 case BURN_SYMBOL_TYPE_LE_I:
957 case BURN_SYMBOL_TYPE_GE_I:
958 case BURN_SYMBOL_TYPE_EQ_I:
959 case BURN_SYMBOL_TYPE_NE_I:
960 {
961 int i = ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand, cchLeft, wzRightOperand, cchRight);
962 hr = CompareIntegerValues(comparison, i, CSTR_EQUAL, pfResult);
963 }
964 break;
965 case BURN_SYMBOL_TYPE_BAND:
966 case BURN_SYMBOL_TYPE_BAND_I:
967 // test if left string contains right string
968 for (int i = 0; (i + cchRight) <= cchLeft; ++i)
969 {
970 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand + i, cchRight, wzRightOperand, cchRight))
971 {
972 *pfResult = TRUE;
973 ExitFunction();
974 }
975 }
976 *pfResult = FALSE;
977 break;
978 case BURN_SYMBOL_TYPE_HIEQ:
979 case BURN_SYMBOL_TYPE_HIEQ_I:
980 // test if left string starts with right string
981 *pfResult = cchLeft >= cchRight && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand, cchRight, wzRightOperand, cchRight);
982 break;
983 case BURN_SYMBOL_TYPE_LOEQ:
984 case BURN_SYMBOL_TYPE_LOEQ_I:
985 // test if left string ends with right string
986 *pfResult = cchLeft >= cchRight && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand + (cchLeft - cchRight), cchRight, wzRightOperand, cchRight);
987 break;
988 default:
989 ExitFunction1(hr = E_INVALIDARG);
990 }
991
992LExit:
993 return hr;
994}
995
996//
997// CompareIntegerValues - compares two integer values using a given comparison.
998//
999static HRESULT CompareIntegerValues(
1000 __in BURN_SYMBOL_TYPE comparison,
1001 __in LONGLONG llLeftOperand,
1002 __in LONGLONG llRightOperand,
1003 __out BOOL* pfResult
1004 )
1005{
1006 HRESULT hr = S_OK;
1007
1008 switch (comparison)
1009 {
1010 case BURN_SYMBOL_TYPE_LT: case BURN_SYMBOL_TYPE_LT_I: *pfResult = llLeftOperand < llRightOperand; break;
1011 case BURN_SYMBOL_TYPE_GT: case BURN_SYMBOL_TYPE_GT_I: *pfResult = llLeftOperand > llRightOperand; break;
1012 case BURN_SYMBOL_TYPE_LE: case BURN_SYMBOL_TYPE_LE_I: *pfResult = llLeftOperand <= llRightOperand; break;
1013 case BURN_SYMBOL_TYPE_GE: case BURN_SYMBOL_TYPE_GE_I: *pfResult = llLeftOperand >= llRightOperand; break;
1014 case BURN_SYMBOL_TYPE_EQ: case BURN_SYMBOL_TYPE_EQ_I: *pfResult = llLeftOperand == llRightOperand; break;
1015 case BURN_SYMBOL_TYPE_NE: case BURN_SYMBOL_TYPE_NE_I: *pfResult = llLeftOperand != llRightOperand; break;
1016 case BURN_SYMBOL_TYPE_BAND: case BURN_SYMBOL_TYPE_BAND_I: *pfResult = (llLeftOperand & llRightOperand) ? TRUE : FALSE; break;
1017 case BURN_SYMBOL_TYPE_HIEQ: case BURN_SYMBOL_TYPE_HIEQ_I: *pfResult = ((llLeftOperand >> 16) & 0xFFFF) == llRightOperand; break;
1018 case BURN_SYMBOL_TYPE_LOEQ: case BURN_SYMBOL_TYPE_LOEQ_I: *pfResult = (llLeftOperand & 0xFFFF) == llRightOperand; break;
1019 default:
1020 ExitFunction1(hr = E_INVALIDARG);
1021 }
1022
1023LExit:
1024 return hr;
1025}
1026
1027//
1028// CompareVersionValues - compares two quad-word version values using a given comparison.
1029//
1030static HRESULT CompareVersionValues(
1031 __in BURN_SYMBOL_TYPE comparison,
1032 __in VERUTIL_VERSION* pLeftOperand,
1033 __in VERUTIL_VERSION* pRightOperand,
1034 __out BOOL* pfResult
1035 )
1036{
1037 HRESULT hr = S_OK;
1038 int nResult = 0;
1039
1040 hr = VerCompareParsedVersions(pLeftOperand, pRightOperand, &nResult);
1041 ExitOnFailure(hr, "Failed to compare condition versions: '%ls', '%ls'", pLeftOperand->sczVersion, pRightOperand->sczVersion);
1042
1043 switch (comparison)
1044 {
1045 case BURN_SYMBOL_TYPE_LT: *pfResult = nResult < 0; break;
1046 case BURN_SYMBOL_TYPE_GT: *pfResult = nResult > 0; break;
1047 case BURN_SYMBOL_TYPE_LE: *pfResult = nResult <= 0; break;
1048 case BURN_SYMBOL_TYPE_GE: *pfResult = nResult >= 0; break;
1049 case BURN_SYMBOL_TYPE_EQ: *pfResult = nResult == 0; break;
1050 case BURN_SYMBOL_TYPE_NE: *pfResult = nResult != 0; break;
1051 default:
1052 ExitFunction1(hr = E_INVALIDARG);
1053 }
1054
1055LExit:
1056 return hr;
1057}
diff --git a/src/burn/engine/condition.h b/src/burn/engine/condition.h
new file mode 100644
index 00000000..91627f3c
--- /dev/null
+++ b/src/burn/engine/condition.h
@@ -0,0 +1,39 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10typedef struct _BURN_CONDITION
11{
12 // The is an expression a condition string to fire the built-in "need newer OS" message
13 LPWSTR sczConditionString;
14} BURN_CONDITION;
15
16
17// function declarations
18
19HRESULT ConditionEvaluate(
20 __in BURN_VARIABLES* pVariables,
21 __in_z LPCWSTR wzCondition,
22 __out BOOL* pf
23 );
24HRESULT ConditionGlobalCheck(
25 __in BURN_VARIABLES* pVariables,
26 __in BURN_CONDITION* pBlock,
27 __in BOOTSTRAPPER_DISPLAY display,
28 __in_z LPCWSTR wzBundleName,
29 __out DWORD *pdwExitCode,
30 __out BOOL *pfContinueExecution
31 );
32HRESULT ConditionGlobalParseFromXml(
33 __in BURN_CONDITION* pBlock,
34 __in IXMLDOMNode* pixnBundle
35 );
36
37#if defined(__cplusplus)
38}
39#endif
diff --git a/src/burn/engine/container.cpp b/src/burn/engine/container.cpp
new file mode 100644
index 00000000..0cce3131
--- /dev/null
+++ b/src/burn/engine/container.cpp
@@ -0,0 +1,398 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// function definitions
7
8extern "C" HRESULT ContainersParseFromXml(
9 __in BURN_CONTAINERS* pContainers,
10 __in IXMLDOMNode* pixnBundle
11 )
12{
13 HRESULT hr = S_OK;
14 IXMLDOMNodeList* pixnNodes = NULL;
15 IXMLDOMNode* pixnNode = NULL;
16 DWORD cNodes = 0;
17 LPWSTR scz = NULL;
18
19 // select container nodes
20 hr = XmlSelectNodes(pixnBundle, L"Container", &pixnNodes);
21 ExitOnFailure(hr, "Failed to select container nodes.");
22
23 // get container node count
24 hr = pixnNodes->get_length((long*)&cNodes);
25 ExitOnFailure(hr, "Failed to get container node count.");
26
27 if (!cNodes)
28 {
29 ExitFunction();
30 }
31
32 // allocate memory for searches
33 pContainers->rgContainers = (BURN_CONTAINER*)MemAlloc(sizeof(BURN_CONTAINER) * cNodes, TRUE);
34 ExitOnNull(pContainers->rgContainers, hr, E_OUTOFMEMORY, "Failed to allocate memory for container structs.");
35
36 pContainers->cContainers = cNodes;
37
38 // parse search elements
39 for (DWORD i = 0; i < cNodes; ++i)
40 {
41 BURN_CONTAINER* pContainer = &pContainers->rgContainers[i];
42
43 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
44 ExitOnFailure(hr, "Failed to get next node.");
45
46 // TODO: Read type from manifest. Today only CABINET is supported.
47 pContainer->type = BURN_CONTAINER_TYPE_CABINET;
48
49 // @Id
50 hr = XmlGetAttributeEx(pixnNode, L"Id", &pContainer->sczId);
51 ExitOnFailure(hr, "Failed to get @Id.");
52
53 // @Primary
54 hr = XmlGetYesNoAttribute(pixnNode, L"Primary", &pContainer->fPrimary);
55 if (E_NOTFOUND != hr)
56 {
57 ExitOnFailure(hr, "Failed to get @Primary.");
58 }
59
60 // @Attached
61 hr = XmlGetYesNoAttribute(pixnNode, L"Attached", &pContainer->fAttached);
62 if (E_NOTFOUND != hr || pContainer->fPrimary) // if it is a primary container, it has to be attached
63 {
64 ExitOnFailure(hr, "Failed to get @Attached.");
65 }
66
67 // @AttachedIndex
68 hr = XmlGetAttributeNumber(pixnNode, L"AttachedIndex", &pContainer->dwAttachedIndex);
69 if (E_NOTFOUND != hr || pContainer->fAttached) // if it is an attached container it must have an index
70 {
71 ExitOnFailure(hr, "Failed to get @AttachedIndex.");
72 }
73
74 // Attached containers are always found attached to the current process, so use the current proccess's
75 // name instead of what may be in the manifest.
76 if (pContainer->fAttached)
77 {
78 hr = PathForCurrentProcess(&scz, NULL);
79 ExitOnFailure(hr, "Failed to get path to current process for attached container.");
80
81 LPCWSTR wzFileName = PathFile(scz);
82
83 hr = StrAllocString(&pContainer->sczFilePath, wzFileName, 0);
84 ExitOnFailure(hr, "Failed to set attached container file path.");
85 }
86 else
87 {
88 // @FilePath
89 hr = XmlGetAttributeEx(pixnNode, L"FilePath", &pContainer->sczFilePath);
90 if (E_NOTFOUND != hr)
91 {
92 ExitOnFailure(hr, "Failed to get @FilePath.");
93 }
94 }
95
96 // The source path starts as the file path.
97 hr = StrAllocString(&pContainer->sczSourcePath, pContainer->sczFilePath, 0);
98 ExitOnFailure(hr, "Failed to copy @FilePath");
99
100 // @DownloadUrl
101 hr = XmlGetAttributeEx(pixnNode, L"DownloadUrl", &pContainer->downloadSource.sczUrl);
102 if (E_NOTFOUND != hr || (!pContainer->fPrimary && !pContainer->sczSourcePath)) // if the package is not a primary package, it must have a source path or a download url
103 {
104 ExitOnFailure(hr, "Failed to get @DownloadUrl. Either @SourcePath or @DownloadUrl needs to be provided.");
105 }
106
107 // @Hash
108 hr = XmlGetAttributeEx(pixnNode, L"Hash", &pContainer->sczHash);
109 if (SUCCEEDED(hr))
110 {
111 hr = StrAllocHexDecode(pContainer->sczHash, &pContainer->pbHash, &pContainer->cbHash);
112 ExitOnFailure(hr, "Failed to hex decode the Container/@Hash.");
113 }
114 else if (E_NOTFOUND != hr)
115 {
116 ExitOnFailure(hr, "Failed to get @Hash.");
117 }
118
119 // prepare next iteration
120 ReleaseNullObject(pixnNode);
121 }
122
123 hr = S_OK;
124
125LExit:
126 ReleaseObject(pixnNodes);
127 ReleaseObject(pixnNode);
128 ReleaseStr(scz);
129
130 return hr;
131}
132
133extern "C" HRESULT ContainersInitialize(
134 __in BURN_CONTAINERS* pContainers,
135 __in BURN_SECTION* pSection
136 )
137{
138 HRESULT hr = S_OK;
139
140 if (pContainers->rgContainers)
141 {
142 for (DWORD i = 0; i < pContainers->cContainers; ++i)
143 {
144 BURN_CONTAINER* pContainer = &pContainers->rgContainers[i];
145
146 // If the container is attached, make sure the information in the section matches what the
147 // manifest contained and get the offset to the container.
148 if (pContainer->fAttached)
149 {
150 hr = SectionGetAttachedContainerInfo(pSection, pContainer->dwAttachedIndex, pContainer->type, &pContainer->qwAttachedOffset, &pContainer->qwFileSize, &pContainer->fActuallyAttached);
151 ExitOnFailure(hr, "Failed to get attached container information.");
152 }
153 }
154 }
155
156LExit:
157 return hr;
158}
159
160extern "C" void ContainersUninitialize(
161 __in BURN_CONTAINERS* pContainers
162 )
163{
164 if (pContainers->rgContainers)
165 {
166 for (DWORD i = 0; i < pContainers->cContainers; ++i)
167 {
168 BURN_CONTAINER* pContainer = &pContainers->rgContainers[i];
169
170 ReleaseStr(pContainer->sczId);
171 ReleaseStr(pContainer->sczHash);
172 ReleaseStr(pContainer->sczSourcePath);
173 ReleaseStr(pContainer->sczFilePath);
174 ReleaseMem(pContainer->pbHash);
175 ReleaseStr(pContainer->downloadSource.sczUrl);
176 ReleaseStr(pContainer->downloadSource.sczUser);
177 ReleaseStr(pContainer->downloadSource.sczPassword);
178 ReleaseStr(pContainer->sczUnverifiedPath);
179 }
180 MemFree(pContainers->rgContainers);
181 }
182
183 // clear struct
184 memset(pContainers, 0, sizeof(BURN_CONTAINERS));
185}
186
187extern "C" HRESULT ContainerOpenUX(
188 __in BURN_SECTION* pSection,
189 __in BURN_CONTAINER_CONTEXT* pContext
190 )
191{
192 HRESULT hr = S_OK;
193 BURN_CONTAINER container = { };
194 LPWSTR sczExecutablePath = NULL;
195
196 // open attached container
197 container.type = BURN_CONTAINER_TYPE_CABINET;
198 container.fPrimary = TRUE;
199 container.fAttached = TRUE;
200 container.dwAttachedIndex = 0;
201
202 hr = SectionGetAttachedContainerInfo(pSection, container.dwAttachedIndex, container.type, &container.qwAttachedOffset, &container.qwFileSize, &container.fActuallyAttached);
203 ExitOnFailure(hr, "Failed to get container information for UX container.");
204
205 AssertSz(container.fActuallyAttached, "The BA container must always be found attached.");
206
207 hr = PathForCurrentProcess(&sczExecutablePath, NULL);
208 ExitOnFailure(hr, "Failed to get path for executing module.");
209
210 hr = ContainerOpen(pContext, &container, pSection->hEngineFile, sczExecutablePath);
211 ExitOnFailure(hr, "Failed to open attached container.");
212
213LExit:
214 ReleaseStr(sczExecutablePath);
215
216 return hr;
217}
218
219extern "C" HRESULT ContainerOpen(
220 __in BURN_CONTAINER_CONTEXT* pContext,
221 __in BURN_CONTAINER* pContainer,
222 __in HANDLE hContainerFile,
223 __in_z LPCWSTR wzFilePath
224 )
225{
226 HRESULT hr = S_OK;
227 LARGE_INTEGER li = { };
228
229 // initialize context
230 pContext->type = pContainer->type;
231 pContext->qwSize = pContainer->qwFileSize;
232 pContext->qwOffset = pContainer->qwAttachedOffset;
233
234 // If the handle to the container is not open already, open container file
235 if (INVALID_HANDLE_VALUE == hContainerFile)
236 {
237 pContext->hFile = ::CreateFileW(wzFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
238 ExitOnInvalidHandleWithLastError(pContext->hFile, hr, "Failed to open file: %ls", wzFilePath);
239 }
240 else // use the container file handle.
241 {
242 if (!::DuplicateHandle(::GetCurrentProcess(), hContainerFile, ::GetCurrentProcess(), &pContext->hFile, 0, FALSE, DUPLICATE_SAME_ACCESS))
243 {
244 ExitWithLastError(hr, "Failed to duplicate handle to container: %ls", wzFilePath);
245 }
246 }
247
248 // If it is a container attached to an executable, seek to the container offset.
249 if (pContainer->fAttached)
250 {
251 li.QuadPart = (LONGLONG)pContext->qwOffset;
252 }
253
254 if (!::SetFilePointerEx(pContext->hFile, li, NULL, FILE_BEGIN))
255 {
256 ExitWithLastError(hr, "Failed to move file pointer to container offset.");
257 }
258
259 // open the archive
260 switch (pContext->type)
261 {
262 case BURN_CONTAINER_TYPE_CABINET:
263 hr = CabExtractOpen(pContext, wzFilePath);
264 break;
265 }
266 ExitOnFailure(hr, "Failed to open container.");
267
268LExit:
269 return hr;
270}
271
272extern "C" HRESULT ContainerNextStream(
273 __in BURN_CONTAINER_CONTEXT* pContext,
274 __inout_z LPWSTR* psczStreamName
275 )
276{
277 HRESULT hr = S_OK;
278
279 switch (pContext->type)
280 {
281 case BURN_CONTAINER_TYPE_CABINET:
282 hr = CabExtractNextStream(pContext, psczStreamName);
283 break;
284 }
285
286//LExit:
287 return hr;
288}
289
290extern "C" HRESULT ContainerStreamToFile(
291 __in BURN_CONTAINER_CONTEXT* pContext,
292 __in_z LPCWSTR wzFileName
293 )
294{
295 HRESULT hr = S_OK;
296
297 switch (pContext->type)
298 {
299 case BURN_CONTAINER_TYPE_CABINET:
300 hr = CabExtractStreamToFile(pContext, wzFileName);
301 break;
302 }
303
304//LExit:
305 return hr;
306}
307
308extern "C" HRESULT ContainerStreamToBuffer(
309 __in BURN_CONTAINER_CONTEXT* pContext,
310 __out BYTE** ppbBuffer,
311 __out SIZE_T* pcbBuffer
312 )
313{
314 HRESULT hr = S_OK;
315
316 switch (pContext->type)
317 {
318 case BURN_CONTAINER_TYPE_CABINET:
319 hr = CabExtractStreamToBuffer(pContext, ppbBuffer, pcbBuffer);
320 break;
321
322 default:
323 *ppbBuffer = NULL;
324 *pcbBuffer = 0;
325 }
326
327//LExit:
328 return hr;
329}
330
331extern "C" HRESULT ContainerSkipStream(
332 __in BURN_CONTAINER_CONTEXT* pContext
333 )
334{
335 HRESULT hr = S_OK;
336
337 switch (pContext->type)
338 {
339 case BURN_CONTAINER_TYPE_CABINET:
340 hr = CabExtractSkipStream(pContext);
341 break;
342 }
343
344//LExit:
345 return hr;
346}
347
348extern "C" HRESULT ContainerClose(
349 __in BURN_CONTAINER_CONTEXT* pContext
350 )
351{
352 HRESULT hr = S_OK;
353
354 // close container
355 switch (pContext->type)
356 {
357 case BURN_CONTAINER_TYPE_CABINET:
358 hr = CabExtractClose(pContext);
359 ExitOnFailure(hr, "Failed to close cabinet.");
360 break;
361 }
362
363LExit:
364 ReleaseFile(pContext->hFile);
365
366 if (SUCCEEDED(hr))
367 {
368 memset(pContext, 0, sizeof(BURN_CONTAINER_CONTEXT));
369 }
370
371 return hr;
372}
373
374extern "C" HRESULT ContainerFindById(
375 __in BURN_CONTAINERS* pContainers,
376 __in_z LPCWSTR wzId,
377 __out BURN_CONTAINER** ppContainer
378 )
379{
380 HRESULT hr = S_OK;
381 BURN_CONTAINER* pContainer = NULL;
382
383 for (DWORD i = 0; i < pContainers->cContainers; ++i)
384 {
385 pContainer = &pContainers->rgContainers[i];
386
387 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pContainer->sczId, -1, wzId, -1))
388 {
389 *ppContainer = pContainer;
390 ExitFunction1(hr = S_OK);
391 }
392 }
393
394 hr = E_NOTFOUND;
395
396LExit:
397 return hr;
398}
diff --git a/src/burn/engine/container.h b/src/burn/engine/container.h
new file mode 100644
index 00000000..c2c1c9a8
--- /dev/null
+++ b/src/burn/engine/container.h
@@ -0,0 +1,191 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// typedefs
11
12//typedef HRESULT (*PFN_EXTRACTOPEN)(
13// __in HANDLE hFile,
14// __in DWORD64 qwOffset,
15// __in DWORD64 qwSize,
16// __out void** ppCookie
17// );
18//typedef HRESULT (*PFN_EXTRACTNEXTSTREAM)(
19// __in void* pCookie,
20// __inout_z LPWSTR* psczStreamName
21// );
22//typedef HRESULT (*PFN_EXTRACTSTREAMTOFILE)(
23// __in void* pCookie,
24// __in_z LPCWSTR wzFileName
25// );
26//typedef HRESULT (*PFN_EXTRACTSTREAMTOBUFFER)(
27// __in void* pCookie,
28// __out BYTE** ppbBuffer,
29// __out SIZE_T* pcbBuffer
30// );
31//typedef HRESULT (*PFN_EXTRACTCLOSE)(
32// __in void* pCookie
33// );
34
35
36// constants
37
38enum BURN_CONTAINER_TYPE
39{
40 BURN_CONTAINER_TYPE_NONE,
41 BURN_CONTAINER_TYPE_CABINET,
42 BURN_CONTAINER_TYPE_SEVENZIP,
43};
44
45enum BURN_CAB_OPERATION
46{
47 BURN_CAB_OPERATION_NONE,
48 BURN_CAB_OPERATION_NEXT_STREAM,
49 BURN_CAB_OPERATION_STREAM_TO_FILE,
50 BURN_CAB_OPERATION_STREAM_TO_BUFFER,
51 BURN_CAB_OPERATION_SKIP_STREAM,
52 BURN_CAB_OPERATION_CLOSE,
53};
54
55
56// structs
57
58typedef struct _BURN_CONTAINER
59{
60 LPWSTR sczId;
61 BURN_CONTAINER_TYPE type;
62 BOOL fPrimary;
63 BOOL fAttached;
64 DWORD dwAttachedIndex;
65 DWORD64 qwFileSize;
66 LPWSTR sczHash;
67 LPWSTR sczFilePath; // relative path to container.
68 DOWNLOAD_SOURCE downloadSource;
69
70 BYTE* pbHash;
71 DWORD cbHash;
72 DWORD64 qwAttachedOffset;
73 BOOL fActuallyAttached; // indicates whether an attached container is attached or missing.
74
75 // mutable members
76 BOOL fPlanned;
77 LPWSTR sczSourcePath;
78 LPWSTR sczUnverifiedPath;
79 DWORD64 qwExtractSizeTotal;
80 DWORD64 qwCommittedCacheProgress;
81 DWORD64 qwCommittedExtractProgress;
82 HRESULT hrExtract;
83} BURN_CONTAINER;
84
85typedef struct _BURN_CONTAINERS
86{
87 BURN_CONTAINER* rgContainers;
88 DWORD cContainers;
89} BURN_CONTAINERS;
90
91typedef struct _BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER
92{
93 HANDLE hFile;
94 LARGE_INTEGER liPosition;
95} BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER;
96
97typedef struct _BURN_CONTAINER_CONTEXT_CABINET
98{
99 LPWSTR sczFile;
100
101 HANDLE hThread;
102 HANDLE hBeginOperationEvent;
103 HANDLE hOperationCompleteEvent;
104
105 BURN_CAB_OPERATION operation;
106 HRESULT hrError;
107
108 LPWSTR* psczStreamName;
109 LPCWSTR wzTargetFile;
110 HANDLE hTargetFile;
111 BYTE* pbTargetBuffer;
112 DWORD cbTargetBuffer;
113 DWORD iTargetBuffer;
114
115 BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* rgVirtualFilePointers;
116 DWORD cVirtualFilePointers;
117} BURN_CONTAINER_CONTEXT_CABINET;
118
119typedef struct _BURN_CONTAINER_CONTEXT
120{
121 HANDLE hFile;
122 DWORD64 qwOffset;
123 DWORD64 qwSize;
124
125 //PFN_EXTRACTOPEN pfnExtractOpen;
126 //PFN_EXTRACTNEXTSTREAM pfnExtractNextStream;
127 //PFN_EXTRACTSTREAMTOFILE pfnExtractStreamToFile;
128 //PFN_EXTRACTSTREAMTOBUFFER pfnExtractStreamToBuffer;
129 //PFN_EXTRACTCLOSE pfnExtractClose;
130 //void* pCookie;
131 BURN_CONTAINER_TYPE type;
132 union
133 {
134 BURN_CONTAINER_CONTEXT_CABINET Cabinet;
135 };
136
137} BURN_CONTAINER_CONTEXT;
138
139
140// functions
141
142HRESULT ContainersParseFromXml(
143 __in BURN_CONTAINERS* pContainers,
144 __in IXMLDOMNode* pixnBundle
145 );
146HRESULT ContainersInitialize(
147 __in BURN_CONTAINERS* pContainers,
148 __in BURN_SECTION* pSection
149 );
150void ContainersUninitialize(
151 __in BURN_CONTAINERS* pContainers
152 );
153HRESULT ContainerOpenUX(
154 __in BURN_SECTION* pSection,
155 __in BURN_CONTAINER_CONTEXT* pContext
156 );
157HRESULT ContainerOpen(
158 __in BURN_CONTAINER_CONTEXT* pContext,
159 __in BURN_CONTAINER* pContainer,
160 __in HANDLE hContainerFile,
161 __in_z LPCWSTR wzFilePath
162 );
163HRESULT ContainerNextStream(
164 __in BURN_CONTAINER_CONTEXT* pContext,
165 __inout_z LPWSTR* psczStreamName
166 );
167HRESULT ContainerStreamToFile(
168 __in BURN_CONTAINER_CONTEXT* pContext,
169 __in_z LPCWSTR wzFileName
170 );
171HRESULT ContainerStreamToBuffer(
172 __in BURN_CONTAINER_CONTEXT* pContext,
173 __out BYTE** ppbBuffer,
174 __out SIZE_T* pcbBuffer
175 );
176HRESULT ContainerSkipStream(
177 __in BURN_CONTAINER_CONTEXT* pContext
178 );
179HRESULT ContainerClose(
180 __in BURN_CONTAINER_CONTEXT* pContext
181 );
182HRESULT ContainerFindById(
183 __in BURN_CONTAINERS* pContainers,
184 __in_z LPCWSTR wzId,
185 __out BURN_CONTAINER** ppContainer
186 );
187
188
189#if defined(__cplusplus)
190}
191#endif
diff --git a/src/burn/engine/core.cpp b/src/burn/engine/core.cpp
new file mode 100644
index 00000000..535043af
--- /dev/null
+++ b/src/burn/engine/core.cpp
@@ -0,0 +1,1856 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// structs
7
8struct BURN_CACHE_THREAD_CONTEXT
9{
10 BURN_ENGINE_STATE* pEngineState;
11 DWORD* pcOverallProgressTicks;
12 BOOL* pfRollback;
13};
14
15
16// internal function declarations
17
18static HRESULT ParseCommandLine(
19 __in int argc,
20 __in LPWSTR* argv,
21 __in BOOTSTRAPPER_COMMAND* pCommand,
22 __in BURN_PIPE_CONNECTION* pCompanionConnection,
23 __in BURN_PIPE_CONNECTION* pEmbeddedConnection,
24 __in BURN_VARIABLES* pVariables,
25 __out BURN_MODE* pMode,
26 __out BURN_AU_PAUSE_ACTION* pAutomaticUpdates,
27 __out BOOL* pfDisableSystemRestore,
28 __out_z LPWSTR* psczSourceProcessPath,
29 __out_z LPWSTR* psczOriginalSource,
30 __out BOOL* pfDisableUnelevate,
31 __out DWORD *pdwLoggingAttributes,
32 __out_z LPWSTR* psczLogFile,
33 __out_z LPWSTR* psczActiveParent,
34 __out_z LPWSTR* psczIgnoreDependencies,
35 __out_z LPWSTR* psczAncestors,
36 __out_z LPWSTR* psczSanitizedCommandLine
37 );
38static HRESULT ParsePipeConnection(
39 __in_ecount(3) LPWSTR* rgArgs,
40 __in BURN_PIPE_CONNECTION* pConnection
41 );
42static HRESULT DetectPackage(
43 __in BURN_ENGINE_STATE* pEngineState,
44 __in BURN_PACKAGE* pPackage
45 );
46static HRESULT DetectPackagePayloadsCached(
47 __in BURN_PACKAGE* pPackage
48 );
49static DWORD WINAPI CacheThreadProc(
50 __in LPVOID lpThreadParameter
51 );
52static HRESULT WaitForCacheThread(
53 __in HANDLE hCacheThread
54 );
55static void LogPackages(
56 __in_opt const BURN_PACKAGE* pUpgradeBundlePackage,
57 __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage,
58 __in const BURN_PACKAGES* pPackages,
59 __in const BURN_RELATED_BUNDLES* pRelatedBundles,
60 __in const BOOTSTRAPPER_ACTION action
61 );
62static void LogRelatedBundles(
63 __in const BURN_RELATED_BUNDLES* pRelatedBundles,
64 __in BOOL fReverse
65 );
66
67
68// function definitions
69
70extern "C" HRESULT CoreInitialize(
71 __in BURN_ENGINE_STATE* pEngineState
72 )
73{
74 HRESULT hr = S_OK;
75 LPWSTR sczSanitizedCommandLine = NULL;
76 LPWSTR sczStreamName = NULL;
77 BYTE* pbBuffer = NULL;
78 SIZE_T cbBuffer = 0;
79 BURN_CONTAINER_CONTEXT containerContext = { };
80 BOOL fElevated = FALSE;
81 LPWSTR sczSourceProcessPath = NULL;
82 LPWSTR sczSourceProcessFolder = NULL;
83 LPWSTR sczOriginalSource = NULL;
84
85 // Initialize variables.
86 hr = VariableInitialize(&pEngineState->variables);
87 ExitOnFailure(hr, "Failed to initialize variables.");
88
89 // Open attached UX container.
90 hr = ContainerOpenUX(&pEngineState->section, &containerContext);
91 ExitOnFailure(hr, "Failed to open attached UX container.");
92
93 // Load manifest.
94 hr = ContainerNextStream(&containerContext, &sczStreamName);
95 ExitOnFailure(hr, "Failed to open manifest stream.");
96
97 hr = ContainerStreamToBuffer(&containerContext, &pbBuffer, &cbBuffer);
98 ExitOnFailure(hr, "Failed to get manifest stream from container.");
99
100 hr = ManifestLoadXmlFromBuffer(pbBuffer, cbBuffer, pEngineState);
101 ExitOnFailure(hr, "Failed to load manifest.");
102
103 hr = ContainersInitialize(&pEngineState->containers, &pEngineState->section);
104 ExitOnFailure(hr, "Failed to initialize containers.");
105
106 // Parse command line.
107 hr = ParseCommandLine(pEngineState->argc, pEngineState->argv, &pEngineState->command, &pEngineState->companionConnection, &pEngineState->embeddedConnection, &pEngineState->variables, &pEngineState->mode, &pEngineState->automaticUpdates, &pEngineState->fDisableSystemRestore, &sczSourceProcessPath, &sczOriginalSource, &pEngineState->fDisableUnelevate, &pEngineState->log.dwAttributes, &pEngineState->log.sczPath, &pEngineState->registration.sczActiveParent, &pEngineState->sczIgnoreDependencies, &pEngineState->registration.sczAncestors, &sczSanitizedCommandLine);
108 ExitOnFailure(hr, "Failed to parse command line.");
109
110 LogId(REPORT_STANDARD, MSG_BURN_COMMAND_LINE, sczSanitizedCommandLine ? sczSanitizedCommandLine : L"");
111
112 hr = CoreInitializeConstants(pEngineState);
113 ExitOnFailure(hr, "Failed to initialize contants.");
114
115 // Retain whether bundle was initially run elevated.
116 ProcElevated(::GetCurrentProcess(), &fElevated);
117
118 hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, fElevated, TRUE);
119 ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_ELEVATED);
120
121 hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_UILEVEL, pEngineState->command.display, TRUE);
122 ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_UILEVEL);
123
124 if (sczSourceProcessPath)
125 {
126 hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_PATH, sczSourceProcessPath, TRUE, FALSE);
127 ExitOnFailure(hr, "Failed to set source process path variable.");
128
129 hr = PathGetDirectory(sczSourceProcessPath, &sczSourceProcessFolder);
130 ExitOnFailure(hr, "Failed to get source process folder from path.");
131
132 hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, sczSourceProcessFolder, TRUE, FALSE);
133 ExitOnFailure(hr, "Failed to set source process folder variable.");
134 }
135
136 // Set BURN_BUNDLE_ORIGINAL_SOURCE, if it was passed in on the command line.
137 // Needs to be done after ManifestLoadXmlFromBuffer.
138 if (sczOriginalSource)
139 {
140 hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_ORIGINAL_SOURCE, sczOriginalSource, FALSE, FALSE);
141 ExitOnFailure(hr, "Failed to set original source variable.");
142 }
143
144 if (BURN_MODE_UNTRUSTED == pEngineState->mode || BURN_MODE_NORMAL == pEngineState->mode || BURN_MODE_EMBEDDED == pEngineState->mode)
145 {
146 hr = CacheInitialize(&pEngineState->registration, &pEngineState->variables, sczSourceProcessPath);
147 ExitOnFailure(hr, "Failed to initialize internal cache functionality.");
148 }
149
150 // If we're not elevated then we'll be loading the bootstrapper application, so extract
151 // the payloads from the BA container.
152 if (BURN_MODE_NORMAL == pEngineState->mode || BURN_MODE_EMBEDDED == pEngineState->mode)
153 {
154 // Extract all UX payloads to working folder.
155 hr = UserExperienceEnsureWorkingFolder(pEngineState->registration.sczId, &pEngineState->userExperience.sczTempDirectory);
156 ExitOnFailure(hr, "Failed to get unique temporary folder for bootstrapper application.");
157
158 hr = PayloadExtractUXContainer(&pEngineState->userExperience.payloads, &containerContext, pEngineState->userExperience.sczTempDirectory);
159 ExitOnFailure(hr, "Failed to extract bootstrapper application payloads.");
160
161 hr = PathConcat(pEngineState->userExperience.sczTempDirectory, L"BootstrapperApplicationData.xml", &pEngineState->command.wzBootstrapperApplicationDataPath);
162 ExitOnFailure(hr, "Failed to get BootstrapperApplicationDataPath.");
163
164 hr = StrAllocString(&pEngineState->command.wzBootstrapperWorkingFolder, pEngineState->userExperience.sczTempDirectory, 0);
165 ExitOnFailure(hr, "Failed to copy sczBootstrapperWorkingFolder.");
166 }
167
168LExit:
169 ReleaseStr(sczOriginalSource);
170 ReleaseStr(sczSourceProcessFolder);
171 ReleaseStr(sczSourceProcessPath);
172 ContainerClose(&containerContext);
173 ReleaseStr(sczStreamName);
174 ReleaseStr(sczSanitizedCommandLine);
175 ReleaseMem(pbBuffer);
176
177 return hr;
178}
179
180extern "C" HRESULT CoreInitializeConstants(
181 __in BURN_ENGINE_STATE* pEngineState
182 )
183{
184 HRESULT hr = S_OK;
185 BURN_REGISTRATION* pRegistration = &pEngineState->registration;
186
187 hr = DependencyInitialize(pRegistration, pEngineState->sczIgnoreDependencies);
188 ExitOnFailure(hr, "Failed to initialize dependency data.");
189
190 // Support passing Ancestors to embedded burn bundles.
191 if (pRegistration->sczAncestors && *pRegistration->sczAncestors)
192 {
193 hr = StrAllocFormatted(&pRegistration->sczBundlePackageAncestors, L"%ls;%ls", pRegistration->sczAncestors, pRegistration->sczId);
194 ExitOnFailure(hr, "Failed to copy ancestors and self to bundle package ancestors.");
195 }
196 else
197 {
198 hr = StrAllocString(&pRegistration->sczBundlePackageAncestors, pRegistration->sczId, 0);
199 ExitOnFailure(hr, "Failed to copy self to bundle package ancestors.");
200 }
201
202 for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i)
203 {
204 BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i;
205
206 if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) // TODO: Don't assume exePackages with burn protocol are bundles.
207 {
208 // Pass along any ancestors and ourself to prevent infinite loops.
209 pPackage->Exe.wzAncestors = pRegistration->sczBundlePackageAncestors;
210 }
211 }
212
213LExit:
214 return hr;
215}
216
217extern "C" HRESULT CoreSerializeEngineState(
218 __in BURN_ENGINE_STATE* pEngineState,
219 __inout BYTE** ppbBuffer,
220 __inout SIZE_T* piBuffer
221 )
222{
223 HRESULT hr = S_OK;
224
225 hr = VariableSerialize(&pEngineState->variables, TRUE, ppbBuffer, piBuffer);
226 ExitOnFailure(hr, "Failed to serialize variables.");
227
228LExit:
229 return hr;
230}
231
232extern "C" HRESULT CoreQueryRegistration(
233 __in BURN_ENGINE_STATE* pEngineState
234 )
235{
236 HRESULT hr = S_OK;
237 BYTE* pbBuffer = NULL;
238 SIZE_T cbBuffer = 0;
239 SIZE_T iBuffer = 0;
240
241 // Detect if bundle is already installed.
242 hr = RegistrationDetectInstalled(&pEngineState->registration);
243 ExitOnFailure(hr, "Failed to detect bundle install state.");
244
245 // detect resume type
246 hr = RegistrationDetectResumeType(&pEngineState->registration, &pEngineState->command.resumeType);
247 ExitOnFailure(hr, "Failed to detect resume type.");
248
249 // If we have a resume mode that suggests the bundle might already be present, try to load any
250 // previously stored state.
251 if (BOOTSTRAPPER_RESUME_TYPE_INVALID < pEngineState->command.resumeType)
252 {
253 // load resume state
254 hr = RegistrationLoadState(&pEngineState->registration, &pbBuffer, &cbBuffer);
255 if (SUCCEEDED(hr))
256 {
257 hr = VariableDeserialize(&pEngineState->variables, TRUE, pbBuffer, cbBuffer, &iBuffer);
258 }
259
260 // Log any failures and continue.
261 if (FAILED(hr))
262 {
263 LogId(REPORT_STANDARD, MSG_CANNOT_LOAD_STATE_FILE, hr, pEngineState->registration.sczStateFile);
264 hr = S_OK;
265 }
266 }
267
268LExit:
269 ReleaseBuffer(pbBuffer);
270
271 return hr;
272}
273
274extern "C" HRESULT CoreDetect(
275 __in BURN_ENGINE_STATE* pEngineState,
276 __in_opt HWND hwndParent
277 )
278{
279 HRESULT hr = S_OK;
280 BOOL fDetectBegan = FALSE;
281 BURN_PACKAGE* pPackage = NULL;
282 HRESULT hrFirstPackageFailure = S_OK;
283
284 LogId(REPORT_STANDARD, MSG_DETECT_BEGIN, pEngineState->packages.cPackages);
285
286 // Always reset the detect state which means the plan should be reset too.
287 pEngineState->fDetected = FALSE;
288 pEngineState->fPlanned = FALSE;
289 DetectReset(&pEngineState->registration, &pEngineState->packages);
290 PlanReset(&pEngineState->plan, &pEngineState->containers, &pEngineState->packages, &pEngineState->layoutPayloads);
291
292 // Detect if bundle installed state has changed since start up. This
293 // only happens if Apply() changed the state of bundle (installed or
294 // uninstalled). In that case, Detect() can be used here to reset
295 // the installed state.
296 hr = RegistrationDetectInstalled(&pEngineState->registration);
297 ExitOnFailure(hr, "Failed to detect bundle install state.");
298
299 if (pEngineState->registration.fInstalled)
300 {
301 hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_INSTALLED, 1, TRUE);
302 ExitOnFailure(hr, "Failed to set the bundle installed built-in variable.");
303 }
304 else
305 {
306 hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_INSTALLED, NULL, TRUE, FALSE);
307 ExitOnFailure(hr, "Failed to unset the bundle installed built-in variable.");
308 }
309
310 fDetectBegan = TRUE;
311 hr = UserExperienceOnDetectBegin(&pEngineState->userExperience, pEngineState->registration.fCached, pEngineState->registration.fInstalled, pEngineState->packages.cPackages);
312 ExitOnRootFailure(hr, "UX aborted detect begin.");
313
314 pEngineState->userExperience.hwndDetect = hwndParent;
315
316 hr = SearchesExecute(&pEngineState->searches, &pEngineState->variables);
317 ExitOnFailure(hr, "Failed to execute searches.");
318
319 // Load all of the related bundles.
320 hr = RegistrationDetectRelatedBundles(&pEngineState->registration);
321 ExitOnFailure(hr, "Failed to detect related bundles.");
322
323 hr = DependencyDetectProviderKeyBundleId(&pEngineState->registration);
324 if (SUCCEEDED(hr))
325 {
326 hr = DetectForwardCompatibleBundles(&pEngineState->userExperience, &pEngineState->registration);
327 ExitOnFailure(hr, "Failed to detect forward compatible bundle.");
328 }
329 else if (E_NOTFOUND == hr)
330 {
331 hr = S_OK;
332 }
333 ExitOnFailure(hr, "Failed to detect provider key bundle id.");
334
335 // Report the related bundles.
336 hr = DetectReportRelatedBundles(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, pEngineState->command.action, &pEngineState->registration.fEligibleForCleanup);
337 ExitOnFailure(hr, "Failed to report detected related bundles.");
338
339 // Do update detection.
340 hr = DetectUpdate(pEngineState->registration.sczId, &pEngineState->userExperience, &pEngineState->update);
341 ExitOnFailure(hr, "Failed to detect update.");
342
343 // Detecting MSPs requires special initialization before processing each package but
344 // only do the detection if there are actually patch packages to detect because it
345 // can be expensive.
346 if (pEngineState->packages.cPatchInfo)
347 {
348 hr = MspEngineDetectInitialize(&pEngineState->packages);
349 ExitOnFailure(hr, "Failed to initialize MSP engine detection.");
350
351 hr = MsiEngineDetectInitialize(&pEngineState->packages);
352 ExitOnFailure(hr, "Failed to initialize MSI engine detection.");
353 }
354
355 for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i)
356 {
357 pPackage = pEngineState->packages.rgPackages + i;
358
359 hr = DetectPackage(pEngineState, pPackage);
360
361 // If the package detection failed, ensure the package state is set to unknown.
362 if (FAILED(hr))
363 {
364 if (SUCCEEDED(hrFirstPackageFailure))
365 {
366 hrFirstPackageFailure = hr;
367 }
368
369 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN;
370 pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
371 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
372 }
373 }
374
375 hr = DependencyDetect(pEngineState);
376 ExitOnFailure(hr, "Failed to detect the dependencies.");
377
378 // Log the detected states.
379 for (DWORD iPackage = 0; iPackage < pEngineState->packages.cPackages; ++iPackage)
380 {
381 pPackage = pEngineState->packages.rgPackages + iPackage;
382
383 // If any packages that can affect registration are present, then the bundle should not automatically be uninstalled.
384 if (pEngineState->registration.fEligibleForCleanup && pPackage->fCanAffectRegistration &&
385 (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState ||
386 BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState))
387 {
388 pEngineState->registration.fEligibleForCleanup = FALSE;
389 }
390
391 LogId(REPORT_STANDARD, MSG_DETECTED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingBoolToString(pPackage->fCached), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->installRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->cacheRegistrationState));
392
393 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
394 {
395 for (DWORD iFeature = 0; iFeature < pPackage->Msi.cFeatures; ++iFeature)
396 {
397 const BURN_MSIFEATURE* pFeature = pPackage->Msi.rgFeatures + iFeature;
398 LogId(REPORT_STANDARD, MSG_DETECTED_MSI_FEATURE, pPackage->sczId, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState));
399 }
400 }
401 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
402 {
403 for (DWORD iTargetProduct = 0; iTargetProduct < pPackage->Msp.cTargetProductCodes; ++iTargetProduct)
404 {
405 const BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + iTargetProduct;
406 LogId(REPORT_STANDARD, MSG_DETECTED_MSP_TARGET, pPackage->sczId, pTargetProduct->wzTargetProductCode, LoggingPackageStateToString(pTargetProduct->patchPackageState));
407 }
408 }
409 }
410
411LExit:
412 if (SUCCEEDED(hr))
413 {
414 hr = hrFirstPackageFailure;
415 }
416
417 if (SUCCEEDED(hr))
418 {
419 pEngineState->fDetected = TRUE;
420 }
421
422 if (fDetectBegan)
423 {
424 UserExperienceOnDetectComplete(&pEngineState->userExperience, hr, pEngineState->registration.fEligibleForCleanup);
425 }
426
427 pEngineState->userExperience.hwndDetect = NULL;
428
429 LogId(REPORT_STANDARD, MSG_DETECT_COMPLETE, hr, !fDetectBegan ? "(failed)" : LoggingBoolToString(pEngineState->registration.fInstalled), !fDetectBegan ? "(failed)" : LoggingBoolToString(pEngineState->registration.fCached), FAILED(hr) ? "(failed)" : LoggingBoolToString(pEngineState->registration.fEligibleForCleanup));
430
431 return hr;
432}
433
434extern "C" HRESULT CorePlan(
435 __in BURN_ENGINE_STATE* pEngineState,
436 __in BOOTSTRAPPER_ACTION action
437 )
438{
439 HRESULT hr = S_OK;
440 BOOL fPlanBegan = FALSE;
441 BURN_PACKAGE* pUpgradeBundlePackage = NULL;
442 BURN_PACKAGE* pForwardCompatibleBundlePackage = NULL;
443 BOOL fContinuePlanning = TRUE; // assume we won't skip planning due to dependencies.
444
445 LogId(REPORT_STANDARD, MSG_PLAN_BEGIN, pEngineState->packages.cPackages, LoggingBurnActionToString(action));
446
447 fPlanBegan = TRUE;
448 hr = UserExperienceOnPlanBegin(&pEngineState->userExperience, pEngineState->packages.cPackages);
449 ExitOnRootFailure(hr, "BA aborted plan begin.");
450
451 if (!pEngineState->fDetected)
452 {
453 ExitOnFailure(hr = E_INVALIDSTATE, "Plan cannot be done without a successful Detect.");
454 }
455 else if (pEngineState->plan.fAffectedMachineState)
456 {
457 ExitOnFailure(hr = E_INVALIDSTATE, "Plan requires a new successful Detect after calling Apply.");
458 }
459
460 // Always reset the plan.
461 pEngineState->fPlanned = FALSE;
462 PlanReset(&pEngineState->plan, &pEngineState->containers, &pEngineState->packages, &pEngineState->layoutPayloads);
463
464 // Remember the overall action state in the plan since it shapes the changes
465 // we make everywhere.
466 pEngineState->plan.action = action;
467 pEngineState->plan.pPayloads = &pEngineState->payloads;
468 pEngineState->plan.wzBundleId = pEngineState->registration.sczId;
469 pEngineState->plan.wzBundleProviderKey = pEngineState->registration.sczId;
470 pEngineState->plan.fDisableRollback = pEngineState->fDisableRollback;
471
472 hr = PlanSetVariables(action, &pEngineState->variables);
473 ExitOnFailure(hr, "Failed to update action.");
474
475 // Set resume commandline
476 hr = PlanSetResumeCommand(&pEngineState->registration, action, &pEngineState->command, &pEngineState->log);
477 ExitOnFailure(hr, "Failed to set resume command");
478
479 hr = DependencyPlanInitialize(&pEngineState->registration, &pEngineState->plan);
480 ExitOnFailure(hr, "Failed to initialize the dependencies for the plan.");
481
482 if (BOOTSTRAPPER_ACTION_LAYOUT == action)
483 {
484 Assert(!pEngineState->plan.fPerMachine);
485
486 // Plan the bundle's layout.
487 hr = PlanLayoutBundle(&pEngineState->plan, pEngineState->registration.sczExecutableName, pEngineState->section.qwBundleSize, &pEngineState->variables, &pEngineState->layoutPayloads);
488 ExitOnFailure(hr, "Failed to plan the layout of the bundle.");
489
490 // Plan the packages' layout.
491 hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType);
492 ExitOnFailure(hr, "Failed to plan packages.");
493 }
494 else if (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == action || BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED == action)
495 {
496 Assert(!pEngineState->plan.fPerMachine);
497
498 pUpgradeBundlePackage = &pEngineState->update.package;
499
500 hr = PlanUpdateBundle(&pEngineState->userExperience, pUpgradeBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType);
501 ExitOnFailure(hr, "Failed to plan update.");
502 }
503 else
504 {
505 hr = PlanForwardCompatibleBundles(&pEngineState->userExperience, &pEngineState->command, &pEngineState->plan, &pEngineState->registration, action);
506 ExitOnFailure(hr, "Failed to plan forward compatible bundles.");
507
508 if (pEngineState->plan.fEnabledForwardCompatibleBundle)
509 {
510 Assert(!pEngineState->plan.fPerMachine);
511
512 pForwardCompatibleBundlePackage = &pEngineState->plan.forwardCompatibleBundle;
513
514 hr = PlanPassThroughBundle(&pEngineState->userExperience, pForwardCompatibleBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType);
515 ExitOnFailure(hr, "Failed to plan passthrough.");
516 }
517 else // doing an action that modifies the machine state.
518 {
519 pEngineState->plan.fPerMachine = pEngineState->registration.fPerMachine; // default the scope of the plan to the per-machine state of the bundle.
520
521 hr = PlanRegistration(&pEngineState->plan, &pEngineState->registration, pEngineState->command.resumeType, pEngineState->command.relationType, &fContinuePlanning);
522 ExitOnFailure(hr, "Failed to plan registration.");
523
524 if (fContinuePlanning)
525 {
526 // Remember the early index, because we want to be able to insert some related bundles
527 // into the plan before other executed packages. This particularly occurs for uninstallation
528 // of addons and patches, which should be uninstalled before the main product.
529 DWORD dwExecuteActionEarlyIndex = pEngineState->plan.cExecuteActions;
530
531 // Plan the related bundles first to support downgrades with ref-counting.
532 hr = PlanRelatedBundlesBegin(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->plan);
533 ExitOnFailure(hr, "Failed to plan related bundles.");
534
535 hr = PlanPackages(&pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType);
536 ExitOnFailure(hr, "Failed to plan packages.");
537
538 // Schedule the update of related bundles last.
539 hr = PlanRelatedBundlesComplete(&pEngineState->registration, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, dwExecuteActionEarlyIndex);
540 ExitOnFailure(hr, "Failed to schedule related bundles.");
541 }
542 }
543 }
544
545 if (fContinuePlanning)
546 {
547 // Finally, display all packages and related bundles in the log.
548 LogPackages(pUpgradeBundlePackage, pForwardCompatibleBundlePackage, &pEngineState->packages, &pEngineState->registration.relatedBundles, action);
549 }
550
551 PlanDump(&pEngineState->plan);
552
553LExit:
554 if (SUCCEEDED(hr))
555 {
556 pEngineState->fPlanned = TRUE;
557 }
558
559 if (fPlanBegan)
560 {
561 UserExperienceOnPlanComplete(&pEngineState->userExperience, hr);
562 }
563
564 LogId(REPORT_STANDARD, MSG_PLAN_COMPLETE, hr);
565
566 return hr;
567}
568
569extern "C" HRESULT CoreElevate(
570 __in BURN_ENGINE_STATE* pEngineState,
571 __in_opt HWND hwndParent
572 )
573{
574 HRESULT hr = S_OK;
575 DWORD cAVRetryAttempts = 0;
576
577 while (INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe)
578 {
579 // If the elevated companion pipe isn't created yet, let's make that happen.
580 if (!pEngineState->sczBundleEngineWorkingPath)
581 {
582 hr = CacheBundleToWorkingDirectory(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath);
583 ExitOnFailure(hr, "Failed to cache engine to working directory.");
584 }
585
586 hr = ElevationElevate(pEngineState, hwndParent);
587 if (E_SUSPECTED_AV_INTERFERENCE == hr && 1 > cAVRetryAttempts)
588 {
589 ++cAVRetryAttempts;
590 continue;
591 }
592 ExitOnFailure(hr, "Failed to actually elevate.");
593
594 hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, TRUE, TRUE);
595 ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_ELEVATED);
596 }
597
598LExit:
599 return hr;
600}
601
602extern "C" HRESULT CoreApply(
603 __in BURN_ENGINE_STATE* pEngineState,
604 __in_opt HWND hwndParent
605 )
606{
607 HRESULT hr = S_OK;
608 HANDLE hLock = NULL;
609 DWORD cOverallProgressTicks = 0;
610 HANDLE hCacheThread = NULL;
611 BOOL fApplyInitialize = FALSE;
612 BOOL fElevated = FALSE;
613 BOOL fRegistered = FALSE;
614 BOOL fRollback = FALSE;
615 BOOL fSuspend = FALSE;
616 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
617 BURN_CACHE_THREAD_CONTEXT cacheThreadContext = { };
618 DWORD dwPhaseCount = 0;
619 BOOTSTRAPPER_APPLYCOMPLETE_ACTION applyCompleteAction = BOOTSTRAPPER_APPLYCOMPLETE_ACTION_NONE;
620
621 LogId(REPORT_STANDARD, MSG_APPLY_BEGIN);
622
623 if (!pEngineState->fPlanned)
624 {
625 ExitOnFailure(hr = E_INVALIDSTATE, "Apply cannot be done without a successful Plan.");
626 }
627 else if (pEngineState->plan.fAffectedMachineState)
628 {
629 ExitOnFailure(hr = E_INVALIDSTATE, "Plans cannot be applied multiple times.");
630 }
631
632 // Ensure any previous attempts to execute are reset.
633 ApplyReset(&pEngineState->userExperience, &pEngineState->packages);
634
635 if (pEngineState->plan.cCacheActions)
636 {
637 ++dwPhaseCount;
638 }
639 if (pEngineState->plan.cExecuteActions)
640 {
641 ++dwPhaseCount;
642 }
643
644 hr = UserExperienceOnApplyBegin(&pEngineState->userExperience, dwPhaseCount);
645 ExitOnRootFailure(hr, "BA aborted apply begin.");
646
647 pEngineState->plan.fAffectedMachineState = pEngineState->plan.fCanAffectMachineState;
648
649 // Abort if this bundle already requires a restart.
650 if (BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING == pEngineState->command.resumeType)
651 {
652 restart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
653
654 hr = HRESULT_FROM_WIN32(ERROR_FAIL_NOACTION_REBOOT);
655 UserExperienceSendError(&pEngineState->userExperience, BOOTSTRAPPER_ERROR_TYPE_APPLY, NULL, hr, NULL, MB_ICONERROR | MB_OK, IDNOACTION); // ignore return value.
656 ExitFunction();
657 }
658
659 hr = ApplyLock(FALSE, &hLock);
660 ExitOnFailure(hr, "Another per-user setup is already executing.");
661
662 // Initialize only after getting a lock.
663 fApplyInitialize = TRUE;
664 ApplyInitialize();
665
666 pEngineState->userExperience.hwndApply = hwndParent;
667
668 hr = ApplySetVariables(&pEngineState->variables);
669 ExitOnFailure(hr, "Failed to set initial apply variables.");
670
671 // If the plan is empty of work to do, skip everything.
672 if (!(pEngineState->plan.cRegistrationActions || pEngineState->plan.cCacheActions || pEngineState->plan.cExecuteActions || pEngineState->plan.cCleanActions))
673 {
674 LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED);
675 ExitFunction();
676 }
677
678 // Ensure the engine is cached to the working path.
679 if (!pEngineState->sczBundleEngineWorkingPath)
680 {
681 hr = CacheBundleToWorkingDirectory(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath);
682 ExitOnFailure(hr, "Failed to cache engine to working directory.");
683 }
684
685 // Elevate.
686 if (pEngineState->plan.fPerMachine)
687 {
688 hr = CoreElevate(pEngineState, pEngineState->userExperience.hwndApply);
689 ExitOnFailure(hr, "Failed to elevate.");
690
691 hr = ElevationApplyInitialize(pEngineState->companionConnection.hPipe, &pEngineState->userExperience, &pEngineState->variables, pEngineState->plan.action, pEngineState->automaticUpdates, !pEngineState->fDisableSystemRestore);
692 ExitOnFailure(hr, "Failed to initialize apply in elevated process.");
693
694 fElevated = TRUE;
695 }
696
697 // Register.
698 if (pEngineState->plan.fCanAffectMachineState)
699 {
700 fRegistered = TRUE;
701 hr = ApplyRegister(pEngineState);
702 ExitOnFailure(hr, "Failed to register bundle.");
703 }
704
705 // Cache.
706 if (pEngineState->plan.cCacheActions)
707 {
708 // Launch the cache thread.
709 cacheThreadContext.pEngineState = pEngineState;
710 cacheThreadContext.pcOverallProgressTicks = &cOverallProgressTicks;
711 cacheThreadContext.pfRollback = &fRollback;
712
713 hCacheThread = ::CreateThread(NULL, 0, CacheThreadProc, &cacheThreadContext, 0, NULL);
714 ExitOnNullWithLastError(hCacheThread, hr, "Failed to create cache thread.");
715
716 // If we're not caching in parallel, wait for the cache thread to terminate.
717 if (!pEngineState->fParallelCacheAndExecute)
718 {
719 hr = WaitForCacheThread(hCacheThread);
720 ExitOnFailure(hr, "Failed while caching, aborting execution.");
721
722 ReleaseHandle(hCacheThread);
723 }
724 }
725
726 // Execute.
727 if (pEngineState->plan.cExecuteActions)
728 {
729 hr = ApplyExecute(pEngineState, hCacheThread, &cOverallProgressTicks, &fRollback, &fSuspend, &restart);
730 UserExperienceExecutePhaseComplete(&pEngineState->userExperience, hr); // signal that execute completed.
731 }
732
733 // Wait for cache thread to terminate, this should return immediately unless we're waiting for layout to complete.
734 if (hCacheThread)
735 {
736 HRESULT hrCached = WaitForCacheThread(hCacheThread);
737 if (SUCCEEDED(hr))
738 {
739 hr = hrCached;
740 }
741 }
742
743 // If something went wrong or force restarted, skip cleaning.
744 if (FAILED(hr) || fRollback || fSuspend || BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart)
745 {
746 ExitFunction();
747 }
748
749 // Clean.
750 if (pEngineState->plan.cCleanActions)
751 {
752 ApplyClean(&pEngineState->userExperience, &pEngineState->plan, pEngineState->companionConnection.hPipe);
753 }
754
755LExit:
756 // Unregister.
757 if (fRegistered)
758 {
759 ApplyUnregister(pEngineState, FAILED(hr) || fRollback, fSuspend, restart);
760 }
761
762 if (fElevated)
763 {
764 ElevationApplyUninitialize(pEngineState->companionConnection.hPipe);
765 }
766
767 pEngineState->userExperience.hwndApply = NULL;
768
769 if (fApplyInitialize)
770 {
771 ApplyUninitialize();
772 }
773
774 if (hLock)
775 {
776 ::ReleaseMutex(hLock);
777 ::CloseHandle(hLock);
778 }
779
780 ReleaseHandle(hCacheThread);
781
782 UserExperienceOnApplyComplete(&pEngineState->userExperience, hr, restart, &applyCompleteAction);
783 if (BOOTSTRAPPER_APPLYCOMPLETE_ACTION_RESTART == applyCompleteAction)
784 {
785 pEngineState->fRestart = TRUE;
786 }
787
788 LogId(REPORT_STANDARD, MSG_APPLY_COMPLETE, hr, LoggingRestartToString(restart), LoggingBoolToString(pEngineState->fRestart));
789
790 return hr;
791}
792
793extern "C" HRESULT CoreLaunchApprovedExe(
794 __in BURN_ENGINE_STATE* pEngineState,
795 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe
796 )
797{
798 HRESULT hr = S_OK;
799 DWORD dwProcessId = 0;
800
801 LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_BEGIN, pLaunchApprovedExe->sczId);
802
803 hr = UserExperienceOnLaunchApprovedExeBegin(&pEngineState->userExperience);
804 ExitOnRootFailure(hr, "BA aborted LaunchApprovedExe begin.");
805
806 // Elevate.
807 hr = CoreElevate(pEngineState, pLaunchApprovedExe->hwndParent);
808 ExitOnFailure(hr, "Failed to elevate.");
809
810 // Launch.
811 hr = ElevationLaunchApprovedExe(pEngineState->companionConnection.hPipe, pLaunchApprovedExe, &dwProcessId);
812
813LExit:
814 UserExperienceOnLaunchApprovedExeComplete(&pEngineState->userExperience, hr, dwProcessId);
815
816 LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_COMPLETE, hr, dwProcessId);
817
818 ApprovedExesUninitializeLaunch(pLaunchApprovedExe);
819
820 return hr;
821}
822
823extern "C" HRESULT CoreQuit(
824 __in BURN_ENGINE_STATE* pEngineState,
825 __in int nExitCode
826 )
827{
828 HRESULT hr = S_OK;
829
830 // Save engine state if resume mode is unequal to "none".
831 if (BURN_RESUME_MODE_NONE != pEngineState->resumeMode)
832 {
833 hr = CoreSaveEngineState(pEngineState);
834 if (FAILED(hr))
835 {
836 LogErrorId(hr, MSG_STATE_NOT_SAVED);
837 hr = S_OK;
838 }
839 }
840
841 LogId(REPORT_STANDARD, MSG_QUIT, nExitCode);
842
843 pEngineState->fQuit = TRUE;
844
845 ::PostQuitMessage(nExitCode); // go bye-bye.
846
847 return hr;
848}
849
850extern "C" HRESULT CoreSaveEngineState(
851 __in BURN_ENGINE_STATE* pEngineState
852 )
853{
854 HRESULT hr = S_OK;
855 BYTE* pbBuffer = NULL;
856 SIZE_T cbBuffer = 0;
857
858 // serialize engine state
859 hr = CoreSerializeEngineState(pEngineState, &pbBuffer, &cbBuffer);
860 ExitOnFailure(hr, "Failed to serialize engine state.");
861
862 // write to registration store
863 if (pEngineState->registration.fPerMachine)
864 {
865 hr = ElevationSaveState(pEngineState->companionConnection.hPipe, pbBuffer, cbBuffer);
866 ExitOnFailure(hr, "Failed to save engine state in per-machine process.");
867 }
868 else
869 {
870 hr = RegistrationSaveState(&pEngineState->registration, pbBuffer, cbBuffer);
871 ExitOnFailure(hr, "Failed to save engine state.");
872 }
873
874LExit:
875 ReleaseBuffer(pbBuffer);
876
877 return hr;
878}
879
880extern "C" LPCWSTR CoreRelationTypeToCommandLineString(
881 __in BOOTSTRAPPER_RELATION_TYPE relationType
882 )
883{
884 LPCWSTR wzRelationTypeCommandLine = NULL;
885 switch (relationType)
886 {
887 case BOOTSTRAPPER_RELATION_DETECT:
888 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_DETECT;
889 break;
890 case BOOTSTRAPPER_RELATION_UPGRADE:
891 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE;
892 break;
893 case BOOTSTRAPPER_RELATION_ADDON:
894 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_ADDON;
895 break;
896 case BOOTSTRAPPER_RELATION_PATCH:
897 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_PATCH;
898 break;
899 case BOOTSTRAPPER_RELATION_UPDATE:
900 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_UPDATE;
901 break;
902 case BOOTSTRAPPER_RELATION_DEPENDENT:
903 break;
904 case BOOTSTRAPPER_RELATION_NONE: __fallthrough;
905 default:
906 wzRelationTypeCommandLine = NULL;
907 break;
908 }
909
910 return wzRelationTypeCommandLine;
911}
912
913extern "C" HRESULT CoreRecreateCommandLine(
914 __deref_inout_z LPWSTR* psczCommandLine,
915 __in BOOTSTRAPPER_ACTION action,
916 __in BOOTSTRAPPER_DISPLAY display,
917 __in BOOTSTRAPPER_RESTART restart,
918 __in BOOTSTRAPPER_RELATION_TYPE relationType,
919 __in BOOL fPassthrough,
920 __in_z_opt LPCWSTR wzActiveParent,
921 __in_z_opt LPCWSTR wzAncestors,
922 __in_z_opt LPCWSTR wzAppendLogPath,
923 __in_z_opt LPCWSTR wzAdditionalCommandLineArguments
924 )
925{
926 HRESULT hr = S_OK;
927 LPWSTR scz = NULL;
928 LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType);
929
930 hr = StrAllocString(psczCommandLine, L"", 0);
931 ExitOnFailure(hr, "Failed to empty command line.");
932
933 switch (display)
934 {
935 case BOOTSTRAPPER_DISPLAY_NONE:
936 hr = StrAllocConcat(psczCommandLine, L" /quiet", 0);
937 break;
938 case BOOTSTRAPPER_DISPLAY_PASSIVE:
939 hr = StrAllocConcat(psczCommandLine, L" /passive", 0);
940 break;
941 }
942 ExitOnFailure(hr, "Failed to append display state to command-line");
943
944 switch (action)
945 {
946 case BOOTSTRAPPER_ACTION_MODIFY:
947 hr = StrAllocConcat(psczCommandLine, L" /modify", 0);
948 break;
949 case BOOTSTRAPPER_ACTION_REPAIR:
950 hr = StrAllocConcat(psczCommandLine, L" /repair", 0);
951 break;
952 case BOOTSTRAPPER_ACTION_UNINSTALL:
953 hr = StrAllocConcat(psczCommandLine, L" /uninstall", 0);
954 break;
955 }
956 ExitOnFailure(hr, "Failed to append action state to command-line");
957
958 switch (restart)
959 {
960 case BOOTSTRAPPER_RESTART_ALWAYS:
961 hr = StrAllocConcat(psczCommandLine, L" /forcerestart", 0);
962 break;
963 case BOOTSTRAPPER_RESTART_NEVER:
964 hr = StrAllocConcat(psczCommandLine, L" /norestart", 0);
965 break;
966 }
967 ExitOnFailure(hr, "Failed to append restart state to command-line");
968
969 if (wzActiveParent)
970 {
971 if (*wzActiveParent)
972 {
973 hr = StrAllocFormatted(&scz, L" /%ls \"%ls\"", BURN_COMMANDLINE_SWITCH_PARENT, wzActiveParent);
974 ExitOnFailure(hr, "Failed to format active parent command-line for command-line.");
975 }
976 else
977 {
978 hr = StrAllocFormatted(&scz, L" /%ls", BURN_COMMANDLINE_SWITCH_PARENT_NONE);
979 ExitOnFailure(hr, "Failed to format parent:none command-line for command-line.");
980 }
981
982 hr = StrAllocConcat(psczCommandLine, scz, 0);
983 ExitOnFailure(hr, "Failed to append active parent command-line to command-line.");
984 }
985
986 if (wzAncestors)
987 {
988 hr = StrAllocFormatted(&scz, L" /%ls=%ls", BURN_COMMANDLINE_SWITCH_ANCESTORS, wzAncestors);
989 ExitOnFailure(hr, "Failed to format ancestors for command-line.");
990
991 hr = StrAllocConcat(psczCommandLine, scz, 0);
992 ExitOnFailure(hr, "Failed to append ancestors to command-line.");
993 }
994
995 if (wzRelationTypeCommandLine)
996 {
997 hr = StrAllocFormatted(&scz, L" /%ls", wzRelationTypeCommandLine);
998 ExitOnFailure(hr, "Failed to format relation type for command-line.");
999
1000 hr = StrAllocConcat(psczCommandLine, scz, 0);
1001 ExitOnFailure(hr, "Failed to append relation type to command-line.");
1002 }
1003
1004 if (fPassthrough)
1005 {
1006 hr = StrAllocFormatted(&scz, L" /%ls", BURN_COMMANDLINE_SWITCH_PASSTHROUGH);
1007 ExitOnFailure(hr, "Failed to format passthrough for command-line.");
1008
1009 hr = StrAllocConcat(psczCommandLine, scz, 0);
1010 ExitOnFailure(hr, "Failed to append passthrough to command-line.");
1011 }
1012
1013 if (wzAppendLogPath && *wzAppendLogPath)
1014 {
1015 hr = StrAllocFormatted(&scz, L" /%ls \"%ls\"", BURN_COMMANDLINE_SWITCH_LOG_APPEND, wzAppendLogPath);
1016 ExitOnFailure(hr, "Failed to format append log command-line for command-line.");
1017
1018 hr = StrAllocConcat(psczCommandLine, scz, 0);
1019 ExitOnFailure(hr, "Failed to append log command-line to command-line");
1020 }
1021
1022 if (wzAdditionalCommandLineArguments && *wzAdditionalCommandLineArguments)
1023 {
1024 hr = StrAllocConcat(psczCommandLine, L" ", 0);
1025 ExitOnFailure(hr, "Failed to append space to command-line.");
1026
1027 hr = StrAllocConcat(psczCommandLine, wzAdditionalCommandLineArguments, 0);
1028 ExitOnFailure(hr, "Failed to append command-line to command-line.");
1029 }
1030
1031LExit:
1032 ReleaseStr(scz);
1033
1034 return hr;
1035}
1036
1037extern "C" HRESULT CoreAppendFileHandleAttachedToCommandLine(
1038 __in HANDLE hFileWithAttachedContainer,
1039 __out HANDLE* phExecutableFile,
1040 __deref_inout_z LPWSTR* psczCommandLine
1041 )
1042{
1043 HRESULT hr = S_OK;
1044 HANDLE hExecutableFile = INVALID_HANDLE_VALUE;
1045
1046 *phExecutableFile = INVALID_HANDLE_VALUE;
1047
1048 if (!::DuplicateHandle(::GetCurrentProcess(), hFileWithAttachedContainer, ::GetCurrentProcess(), &hExecutableFile, 0, TRUE, DUPLICATE_SAME_ACCESS))
1049 {
1050 ExitWithLastError(hr, "Failed to duplicate file handle for attached container.");
1051 }
1052
1053 hr = StrAllocFormattedSecure(psczCommandLine, L"%ls -%ls=%Iu", *psczCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, reinterpret_cast<size_t>(hExecutableFile));
1054 ExitOnFailure(hr, "Failed to append the file handle to the command line.");
1055
1056 *phExecutableFile = hExecutableFile;
1057 hExecutableFile = INVALID_HANDLE_VALUE;
1058
1059LExit:
1060 ReleaseFileHandle(hExecutableFile);
1061
1062 return hr;
1063}
1064
1065extern "C" HRESULT CoreAppendFileHandleSelfToCommandLine(
1066 __in LPCWSTR wzExecutablePath,
1067 __out HANDLE* phExecutableFile,
1068 __deref_inout_z LPWSTR* psczCommandLine,
1069 __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine
1070 )
1071{
1072 HRESULT hr = S_OK;
1073 HANDLE hExecutableFile = INVALID_HANDLE_VALUE;
1074 SECURITY_ATTRIBUTES securityAttributes = { };
1075 securityAttributes.bInheritHandle = TRUE;
1076 *phExecutableFile = INVALID_HANDLE_VALUE;
1077
1078 hExecutableFile = ::CreateFileW(wzExecutablePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, &securityAttributes, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1079 if (INVALID_HANDLE_VALUE != hExecutableFile)
1080 {
1081 hr = StrAllocFormattedSecure(psczCommandLine, L"%ls -%ls=%Iu", *psczCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, reinterpret_cast<size_t>(hExecutableFile));
1082 ExitOnFailure(hr, "Failed to append the file handle to the command line.");
1083
1084 if (psczObfuscatedCommandLine)
1085 {
1086 hr = StrAllocFormatted(psczObfuscatedCommandLine, L"%ls -%ls=%Iu", *psczObfuscatedCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, reinterpret_cast<size_t>(hExecutableFile));
1087 ExitOnFailure(hr, "Failed to append the file handle to the obfuscated command line.");
1088 }
1089
1090 *phExecutableFile = hExecutableFile;
1091 hExecutableFile = INVALID_HANDLE_VALUE;
1092 }
1093
1094LExit:
1095 ReleaseFileHandle(hExecutableFile);
1096
1097 return hr;
1098}
1099
1100extern "C" void CoreCleanup(
1101 __in BURN_ENGINE_STATE* pEngineState
1102 )
1103{
1104 HRESULT hr = S_OK;
1105 LONGLONG llValue = 0;
1106 BOOL fNeedsElevation = pEngineState->registration.fPerMachine && INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe;
1107
1108 LogId(REPORT_STANDARD, MSG_CLEANUP_BEGIN);
1109
1110 if (pEngineState->plan.fAffectedMachineState)
1111 {
1112 LogId(REPORT_STANDARD, MSG_CLEANUP_SKIPPED_APPLY);
1113 ExitFunction();
1114 }
1115
1116 if (fNeedsElevation)
1117 {
1118 hr = VariableGetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, &llValue);
1119 ExitOnFailure(hr, "Failed to get value of WixBundleElevated variable during cleanup");
1120
1121 if (llValue)
1122 {
1123 fNeedsElevation = FALSE;
1124 }
1125 else
1126 {
1127 LogId(REPORT_STANDARD, MSG_CLEANUP_SKIPPED_ELEVATION_REQUIRED);
1128 ExitFunction();
1129 }
1130 }
1131
1132 if (!pEngineState->fDetected)
1133 {
1134 hr = CoreDetect(pEngineState, pEngineState->hMessageWindow);
1135 ExitOnFailure(hr, "Detect during cleanup failed");
1136 }
1137
1138 if (!pEngineState->registration.fEligibleForCleanup)
1139 {
1140 ExitFunction();
1141 }
1142
1143 hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL);
1144 ExitOnFailure(hr, "Plan during cleanup failed");
1145
1146 hr = CoreApply(pEngineState, pEngineState->hMessageWindow);
1147 ExitOnFailure(hr, "Apply during cleanup failed");
1148
1149LExit:
1150 LogId(REPORT_STANDARD, MSG_CLEANUP_COMPLETE, hr);
1151}
1152
1153// internal helper functions
1154
1155static HRESULT ParseCommandLine(
1156 __in int argc,
1157 __in LPWSTR* argv,
1158 __in BOOTSTRAPPER_COMMAND* pCommand,
1159 __in BURN_PIPE_CONNECTION* pCompanionConnection,
1160 __in BURN_PIPE_CONNECTION* pEmbeddedConnection,
1161 __in BURN_VARIABLES* pVariables,
1162 __out BURN_MODE* pMode,
1163 __out BURN_AU_PAUSE_ACTION* pAutomaticUpdates,
1164 __out BOOL* pfDisableSystemRestore,
1165 __out_z LPWSTR* psczSourceProcessPath,
1166 __out_z LPWSTR* psczOriginalSource,
1167 __out BOOL* pfDisableUnelevate,
1168 __out DWORD *pdwLoggingAttributes,
1169 __out_z LPWSTR* psczLogFile,
1170 __out_z LPWSTR* psczActiveParent,
1171 __out_z LPWSTR* psczIgnoreDependencies,
1172 __out_z LPWSTR* psczAncestors,
1173 __out_z LPWSTR* psczSanitizedCommandLine
1174 )
1175{
1176 HRESULT hr = S_OK;
1177 BOOL fUnknownArg = FALSE;
1178 BOOL fHidden = FALSE;
1179 LPWSTR sczCommandLine = NULL;
1180 LPWSTR sczSanitizedArgument = NULL;
1181 LPWSTR sczVariableName = NULL;
1182
1183 for (int i = 0; i < argc; ++i)
1184 {
1185 fUnknownArg = FALSE;
1186 int originalIndex = i;
1187 ReleaseNullStr(sczSanitizedArgument);
1188
1189 if (argv[i][0] == L'-' || argv[i][0] == L'/')
1190 {
1191 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"l", -1) ||
1192 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"log", -1) ||
1193 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"xlog", -1))
1194 {
1195 *pdwLoggingAttributes &= ~BURN_LOGGING_ATTRIBUTE_APPEND;
1196
1197 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], 1, L"x", 1))
1198 {
1199 *pdwLoggingAttributes |= BURN_LOGGING_ATTRIBUTE_VERBOSE | BURN_LOGGING_ATTRIBUTE_EXTRADEBUG;
1200 }
1201
1202 if (i + 1 >= argc)
1203 {
1204 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for log.");
1205 }
1206
1207 ++i;
1208
1209 hr = StrAllocString(psczLogFile, argv[i], 0);
1210 ExitOnFailure(hr, "Failed to copy log file path.");
1211 }
1212 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"?", -1) ||
1213 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"h", -1) ||
1214 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"help", -1))
1215 {
1216 pCommand->action = BOOTSTRAPPER_ACTION_HELP;
1217 }
1218 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"q", -1) ||
1219 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"quiet", -1) ||
1220 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"s", -1) ||
1221 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"silent", -1))
1222 {
1223 pCommand->display = BOOTSTRAPPER_DISPLAY_NONE;
1224
1225 if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart)
1226 {
1227 pCommand->restart = BOOTSTRAPPER_RESTART_AUTOMATIC;
1228 }
1229 }
1230 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"passive", -1))
1231 {
1232 pCommand->display = BOOTSTRAPPER_DISPLAY_PASSIVE;
1233
1234 if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart)
1235 {
1236 pCommand->restart = BOOTSTRAPPER_RESTART_AUTOMATIC;
1237 }
1238 }
1239 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"norestart", -1))
1240 {
1241 pCommand->restart = BOOTSTRAPPER_RESTART_NEVER;
1242 }
1243 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"forcerestart", -1))
1244 {
1245 pCommand->restart = BOOTSTRAPPER_RESTART_ALWAYS;
1246 }
1247 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"promptrestart", -1))
1248 {
1249 pCommand->restart = BOOTSTRAPPER_RESTART_PROMPT;
1250 }
1251 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"layout", -1))
1252 {
1253 if (BOOTSTRAPPER_ACTION_HELP != pCommand->action)
1254 {
1255 pCommand->action = BOOTSTRAPPER_ACTION_LAYOUT;
1256 }
1257
1258 // If there is another command line argument and it is not a switch, use that as the layout directory.
1259 if (i + 1 < argc && argv[i + 1][0] != L'-' && argv[i + 1][0] != L'/')
1260 {
1261 ++i;
1262
1263 hr = PathExpand(&pCommand->wzLayoutDirectory, argv[i], PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH);
1264 ExitOnFailure(hr, "Failed to copy path for layout directory.");
1265 }
1266 }
1267 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"uninstall", -1))
1268 {
1269 if (BOOTSTRAPPER_ACTION_HELP != pCommand->action)
1270 {
1271 pCommand->action = BOOTSTRAPPER_ACTION_UNINSTALL;
1272 }
1273 }
1274 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"repair", -1))
1275 {
1276 if (BOOTSTRAPPER_ACTION_HELP != pCommand->action)
1277 {
1278 pCommand->action = BOOTSTRAPPER_ACTION_REPAIR;
1279 }
1280 }
1281 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"modify", -1))
1282 {
1283 if (BOOTSTRAPPER_ACTION_HELP != pCommand->action)
1284 {
1285 pCommand->action = BOOTSTRAPPER_ACTION_MODIFY;
1286 }
1287 }
1288 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"package", -1) ||
1289 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"update", -1))
1290 {
1291 if (BOOTSTRAPPER_ACTION_UNKNOWN == pCommand->action)
1292 {
1293 pCommand->action = BOOTSTRAPPER_ACTION_INSTALL;
1294 }
1295 }
1296 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"noaupause", -1))
1297 {
1298 *pAutomaticUpdates = BURN_AU_PAUSE_ACTION_NONE;
1299 }
1300 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"keepaupaused", -1))
1301 {
1302 // Switch /noaupause takes precedence.
1303 if (BURN_AU_PAUSE_ACTION_NONE != *pAutomaticUpdates)
1304 {
1305 *pAutomaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME;
1306 }
1307 }
1308 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"disablesystemrestore", -1))
1309 {
1310 *pfDisableSystemRestore = TRUE;
1311 }
1312 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"originalsource", -1))
1313 {
1314 if (i + 1 >= argc)
1315 {
1316 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for original source.");
1317 }
1318
1319 ++i;
1320 hr = StrAllocString(psczOriginalSource, argv[i], 0);
1321 ExitOnFailure(hr, "Failed to copy last used source.");
1322 }
1323 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PARENT, -1))
1324 {
1325 if (i + 1 >= argc)
1326 {
1327 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a value for parent.");
1328 }
1329
1330 ++i;
1331
1332 hr = StrAllocString(psczActiveParent, argv[i], 0);
1333 ExitOnFailure(hr, "Failed to copy parent.");
1334 }
1335 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PARENT_NONE, -1))
1336 {
1337 hr = StrAllocString(psczActiveParent, L"", 0);
1338 ExitOnFailure(hr, "Failed to initialize parent to none.");
1339 }
1340 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_LOG_APPEND, -1))
1341 {
1342 if (i + 1 >= argc)
1343 {
1344 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for append log.");
1345 }
1346
1347 ++i;
1348
1349 hr = StrAllocString(psczLogFile, argv[i], 0);
1350 ExitOnFailure(hr, "Failed to copy append log file path.");
1351
1352 *pdwLoggingAttributes |= BURN_LOGGING_ATTRIBUTE_APPEND;
1353 }
1354 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_ELEVATED, -1))
1355 {
1356 if (i + 3 >= argc)
1357 {
1358 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify the elevated name, token and parent process id.");
1359 }
1360
1361 if (BURN_MODE_UNTRUSTED != *pMode)
1362 {
1363 ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided.");
1364 }
1365
1366 *pMode = BURN_MODE_ELEVATED;
1367
1368 ++i;
1369
1370 hr = ParsePipeConnection(argv + i, pCompanionConnection);
1371 ExitOnFailure(hr, "Failed to parse elevated connection.");
1372
1373 i += 2;
1374 }
1375 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM), BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM)))
1376 {
1377 // Get a pointer to the next character after the switch.
1378 LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM)];
1379 if (L'=' != wzParam[0] || L'\0' == wzParam[1])
1380 {
1381 ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_CLEAN_ROOM);
1382 }
1383
1384 if (BURN_MODE_UNTRUSTED != *pMode)
1385 {
1386 ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided.");
1387 }
1388
1389 *pMode = BURN_MODE_NORMAL;
1390
1391 hr = StrAllocString(psczSourceProcessPath, wzParam + 1, 0);
1392 ExitOnFailure(hr, "Failed to copy source process path.");
1393 }
1394 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_EMBEDDED, -1))
1395 {
1396 if (i + 3 >= argc)
1397 {
1398 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify the embedded name, token and parent process id.");
1399 }
1400
1401 switch (*pMode)
1402 {
1403 case BURN_MODE_UNTRUSTED:
1404 // Leave mode as UNTRUSTED to launch the clean room process.
1405 break;
1406 case BURN_MODE_NORMAL:
1407 // The initialization code already assumes that the
1408 // clean room switch is at the beginning of the command line,
1409 // so it's safe to assume that the mode is NORMAL in the clean room.
1410 *pMode = BURN_MODE_EMBEDDED;
1411 break;
1412 default:
1413 ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided.");
1414 }
1415
1416 ++i;
1417
1418 hr = ParsePipeConnection(argv + i, pEmbeddedConnection);
1419 ExitOnFailure(hr, "Failed to parse embedded connection.");
1420
1421 i += 2;
1422 }
1423 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_DETECT, -1))
1424 {
1425 pCommand->relationType = BOOTSTRAPPER_RELATION_DETECT;
1426
1427 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1428 }
1429 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE, -1))
1430 {
1431 pCommand->relationType = BOOTSTRAPPER_RELATION_UPGRADE;
1432
1433 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1434 }
1435 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_ADDON, -1))
1436 {
1437 pCommand->relationType = BOOTSTRAPPER_RELATION_ADDON;
1438
1439 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1440 }
1441 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_PATCH, -1))
1442 {
1443 pCommand->relationType = BOOTSTRAPPER_RELATION_PATCH;
1444
1445 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1446 }
1447 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_UPDATE, -1))
1448 {
1449 pCommand->relationType = BOOTSTRAPPER_RELATION_UPDATE;
1450
1451 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1452 }
1453 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PASSTHROUGH, -1))
1454 {
1455 pCommand->fPassthrough = TRUE;
1456 }
1457 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_DISABLE_UNELEVATE, -1))
1458 {
1459 *pfDisableUnelevate = TRUE;
1460 }
1461 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RUNONCE, -1))
1462 {
1463 if (BURN_MODE_UNTRUSTED != *pMode)
1464 {
1465 ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided.");
1466 }
1467
1468 *pMode = BURN_MODE_RUNONCE;
1469 }
1470 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES), BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES)))
1471 {
1472 // Get a pointer to the next character after the switch.
1473 LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES)];
1474 if (L'=' != wzParam[0] || L'\0' == wzParam[1])
1475 {
1476 ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES);
1477 }
1478
1479 hr = StrAllocString(psczIgnoreDependencies, &wzParam[1], 0);
1480 ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
1481 }
1482 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS), BURN_COMMANDLINE_SWITCH_ANCESTORS, lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS)))
1483 {
1484 // Get a pointer to the next character after the switch.
1485 LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS)];
1486 if (L'=' != wzParam[0] || L'\0' == wzParam[1])
1487 {
1488 ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_ANCESTORS);
1489 }
1490
1491 hr = StrAllocString(psczAncestors, &wzParam[1], 0);
1492 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
1493 }
1494 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED), BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED)))
1495 {
1496 // Already processed in InitializeEngineState.
1497 }
1498 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF), BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF)))
1499 {
1500 // Already processed in InitializeEngineState.
1501 }
1502 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX), BURN_COMMANDLINE_SWITCH_PREFIX, lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX)))
1503 {
1504 // Skip (but log) any other private burn switches we don't recognize, so that
1505 // adding future private variables doesn't break old bundles
1506 LogId(REPORT_STANDARD, MSG_BURN_UNKNOWN_PRIVATE_SWITCH, &argv[i][1]);
1507 }
1508 else
1509 {
1510 fUnknownArg = TRUE;
1511 }
1512 }
1513 else
1514 {
1515 fUnknownArg = TRUE;
1516
1517 const wchar_t* pwc = wcschr(argv[i], L'=');
1518 if (pwc)
1519 {
1520 hr = StrAllocString(&sczVariableName, argv[i], pwc - argv[i]);
1521 ExitOnFailure(hr, "Failed to copy variable name.");
1522
1523 hr = VariableIsHidden(pVariables, sczVariableName, &fHidden);
1524 ExitOnFailure(hr, "Failed to determine whether variable is hidden.");
1525
1526 if (fHidden)
1527 {
1528 hr = StrAllocFormatted(&sczSanitizedArgument, L"%ls=*****", sczVariableName);
1529 ExitOnFailure(hr, "Failed to copy sanitized argument.");
1530 }
1531 }
1532 }
1533
1534 // Remember command-line switch to pass off to UX.
1535 if (fUnknownArg)
1536 {
1537 PathCommandLineAppend(&pCommand->wzCommandLine, argv[i]);
1538 }
1539
1540 if (sczSanitizedArgument)
1541 {
1542 PathCommandLineAppend(psczSanitizedCommandLine, sczSanitizedArgument);
1543 }
1544 else
1545 {
1546 for (; originalIndex <= i; ++originalIndex)
1547 {
1548 PathCommandLineAppend(psczSanitizedCommandLine, argv[originalIndex]);
1549 }
1550 }
1551 }
1552
1553 // If embedded, ensure the display goes embedded as well.
1554 if (BURN_MODE_EMBEDDED == *pMode)
1555 {
1556 pCommand->display = BOOTSTRAPPER_DISPLAY_EMBEDDED;
1557 }
1558
1559 // Set the defaults if nothing was set above.
1560 if (BOOTSTRAPPER_ACTION_UNKNOWN == pCommand->action)
1561 {
1562 pCommand->action = BOOTSTRAPPER_ACTION_INSTALL;
1563 }
1564
1565 if (BOOTSTRAPPER_DISPLAY_UNKNOWN == pCommand->display)
1566 {
1567 pCommand->display = BOOTSTRAPPER_DISPLAY_FULL;
1568 }
1569
1570 if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart)
1571 {
1572 pCommand->restart = BOOTSTRAPPER_RESTART_PROMPT;
1573 }
1574
1575LExit:
1576 ReleaseStr(sczVariableName);
1577 ReleaseStr(sczSanitizedArgument);
1578 ReleaseStr(sczCommandLine);
1579
1580 return hr;
1581}
1582
1583static HRESULT ParsePipeConnection(
1584 __in_ecount(3) LPWSTR* rgArgs,
1585 __in BURN_PIPE_CONNECTION* pConnection
1586 )
1587{
1588 HRESULT hr = S_OK;
1589
1590 hr = StrAllocString(&pConnection->sczName, rgArgs[0], 0);
1591 ExitOnFailure(hr, "Failed to copy connection name from command line.");
1592
1593 hr = StrAllocString(&pConnection->sczSecret, rgArgs[1], 0);
1594 ExitOnFailure(hr, "Failed to copy connection secret from command line.");
1595
1596 hr = StrStringToUInt32(rgArgs[2], 0, reinterpret_cast<UINT*>(&pConnection->dwProcessId));
1597 ExitOnFailure(hr, "Failed to copy parent process id from command line.");
1598
1599LExit:
1600 return hr;
1601}
1602
1603static HRESULT DetectPackage(
1604 __in BURN_ENGINE_STATE* pEngineState,
1605 __in BURN_PACKAGE* pPackage
1606 )
1607{
1608 HRESULT hr = S_OK;
1609 BOOL fBegan = FALSE;
1610
1611 fBegan = TRUE;
1612 hr = UserExperienceOnDetectPackageBegin(&pEngineState->userExperience, pPackage->sczId);
1613 ExitOnRootFailure(hr, "BA aborted detect package begin.");
1614
1615 // Detect the cache state of the package.
1616 hr = DetectPackagePayloadsCached(pPackage);
1617 ExitOnFailure(hr, "Failed to detect if payloads are all cached for package: %ls", pPackage->sczId);
1618
1619 // Use the correct engine to detect the package.
1620 switch (pPackage->type)
1621 {
1622 case BURN_PACKAGE_TYPE_EXE:
1623 hr = ExeEngineDetectPackage(pPackage, &pEngineState->variables);
1624 break;
1625
1626 case BURN_PACKAGE_TYPE_MSI:
1627 hr = MsiEngineDetectPackage(pPackage, &pEngineState->userExperience);
1628 break;
1629
1630 case BURN_PACKAGE_TYPE_MSP:
1631 hr = MspEngineDetectPackage(pPackage, &pEngineState->userExperience);
1632 break;
1633
1634 case BURN_PACKAGE_TYPE_MSU:
1635 hr = MsuEngineDetectPackage(pPackage, &pEngineState->variables);
1636 break;
1637
1638 default:
1639 hr = E_NOTIMPL;
1640 ExitOnRootFailure(hr, "Package type not supported by detect yet.");
1641 }
1642
1643LExit:
1644 if (FAILED(hr))
1645 {
1646 LogErrorId(hr, MSG_FAILED_DETECT_PACKAGE, pPackage->sczId, NULL, NULL);
1647 }
1648
1649 if (fBegan)
1650 {
1651 UserExperienceOnDetectPackageComplete(&pEngineState->userExperience, pPackage->sczId, hr, pPackage->currentState, pPackage->fCached);
1652 }
1653
1654 return hr;
1655}
1656
1657static HRESULT DetectPackagePayloadsCached(
1658 __in BURN_PACKAGE* pPackage
1659 )
1660{
1661 HRESULT hr = S_OK;
1662 LPWSTR sczCachePath = NULL;
1663 BOOL fCached = FALSE; // assume the package is not cached.
1664 LPWSTR sczPayloadCachePath = NULL;
1665
1666 if (pPackage->sczCacheId && *pPackage->sczCacheId)
1667 {
1668 hr = CacheGetCompletedPath(pPackage->fPerMachine, pPackage->sczCacheId, &sczCachePath);
1669 ExitOnFailure(hr, "Failed to get completed cache path.");
1670
1671 // If the cached directory exists, we have something.
1672 if (DirExists(sczCachePath, NULL))
1673 {
1674 // Check all payloads to see if any exist.
1675 for (DWORD i = 0; i < pPackage->payloads.cItems; ++i)
1676 {
1677 BURN_PAYLOAD* pPayload = pPackage->payloads.rgItems[i].pPayload;
1678
1679 hr = PathConcat(sczCachePath, pPayload->sczFilePath, &sczPayloadCachePath);
1680 ExitOnFailure(hr, "Failed to concat payload cache path.");
1681
1682 if (FileExistsEx(sczPayloadCachePath, NULL))
1683 {
1684 fCached = TRUE;
1685 break;
1686 }
1687 else
1688 {
1689 LogId(REPORT_STANDARD, MSG_DETECT_PACKAGE_NOT_FULLY_CACHED, pPackage->sczId, pPayload->sczKey);
1690 }
1691 }
1692 }
1693 }
1694
1695 pPackage->fCached = fCached;
1696
1697 if (pPackage->fCanAffectRegistration)
1698 {
1699 pPackage->cacheRegistrationState = pPackage->fCached ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
1700 }
1701
1702LExit:
1703 ReleaseStr(sczPayloadCachePath);
1704 ReleaseStr(sczCachePath);
1705 return hr;
1706}
1707
1708static DWORD WINAPI CacheThreadProc(
1709 __in LPVOID lpThreadParameter
1710 )
1711{
1712 HRESULT hr = S_OK;
1713 BURN_CACHE_THREAD_CONTEXT* pContext = reinterpret_cast<BURN_CACHE_THREAD_CONTEXT*>(lpThreadParameter);
1714 BURN_ENGINE_STATE* pEngineState = pContext->pEngineState;
1715 DWORD* pcOverallProgressTicks = pContext->pcOverallProgressTicks;
1716 BOOL* pfRollback = pContext->pfRollback;
1717 BOOL fComInitialized = FALSE;
1718
1719 // initialize COM
1720 hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
1721 ExitOnFailure(hr, "Failed to initialize COM on cache thread.");
1722 fComInitialized = TRUE;
1723
1724 // cache packages
1725 hr = ApplyCache(pEngineState->section.hSourceEngineFile, &pEngineState->userExperience, &pEngineState->variables, &pEngineState->plan, pEngineState->companionConnection.hCachePipe, pcOverallProgressTicks, pfRollback);
1726
1727LExit:
1728 UserExperienceExecutePhaseComplete(&pEngineState->userExperience, hr); // signal that cache completed.
1729
1730 if (fComInitialized)
1731 {
1732 ::CoUninitialize();
1733 }
1734
1735 return (DWORD)hr;
1736}
1737
1738static HRESULT WaitForCacheThread(
1739 __in HANDLE hCacheThread
1740 )
1741{
1742 HRESULT hr = S_OK;
1743
1744 if (WAIT_OBJECT_0 != ::WaitForSingleObject(hCacheThread, INFINITE))
1745 {
1746 ExitWithLastError(hr, "Failed to wait for cache thread to terminate.");
1747 }
1748
1749 if (!::GetExitCodeThread(hCacheThread, (DWORD*)&hr))
1750 {
1751 ExitWithLastError(hr, "Failed to get cache thread exit code.");
1752 }
1753
1754LExit:
1755 return hr;
1756}
1757
1758static void LogPackages(
1759 __in_opt const BURN_PACKAGE* pUpgradeBundlePackage,
1760 __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage,
1761 __in const BURN_PACKAGES* pPackages,
1762 __in const BURN_RELATED_BUNDLES* pRelatedBundles,
1763 __in const BOOTSTRAPPER_ACTION action
1764 )
1765{
1766 if (pUpgradeBundlePackage)
1767 {
1768 LogId(REPORT_STANDARD, MSG_PLANNED_UPGRADE_BUNDLE, pUpgradeBundlePackage->sczId, LoggingRequestStateToString(pUpgradeBundlePackage->defaultRequested), LoggingRequestStateToString(pUpgradeBundlePackage->requested), LoggingActionStateToString(pUpgradeBundlePackage->execute), LoggingActionStateToString(pUpgradeBundlePackage->rollback), LoggingDependencyActionToString(pUpgradeBundlePackage->dependencyExecute));
1769 }
1770 else if (pForwardCompatibleBundlePackage)
1771 {
1772 LogId(REPORT_STANDARD, MSG_PLANNED_FORWARD_COMPATIBLE_BUNDLE, pForwardCompatibleBundlePackage->sczId, LoggingRequestStateToString(pForwardCompatibleBundlePackage->defaultRequested), LoggingRequestStateToString(pForwardCompatibleBundlePackage->requested), LoggingActionStateToString(pForwardCompatibleBundlePackage->execute), LoggingActionStateToString(pForwardCompatibleBundlePackage->rollback), LoggingDependencyActionToString(pForwardCompatibleBundlePackage->dependencyExecute));
1773 }
1774 else
1775 {
1776 // Display related bundles first if uninstalling.
1777 if (BOOTSTRAPPER_ACTION_UNINSTALL == action)
1778 {
1779 LogRelatedBundles(pRelatedBundles, TRUE);
1780 }
1781
1782 // Display all the packages in the log.
1783 for (DWORD i = 0; i < pPackages->cPackages; ++i)
1784 {
1785 const DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? pPackages->cPackages - 1 - i : i;
1786 const BURN_PACKAGE* pPackage = &pPackages->rgPackages[iPackage];
1787
1788 LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingBoolToString(pPackage->fPlannedCache), LoggingBoolToString(pPackage->fPlannedUncache), LoggingDependencyActionToString(pPackage->dependencyExecute), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedInstallRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedCacheRegistrationState));
1789
1790 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
1791 {
1792 if (pPackage->Msi.cFeatures)
1793 {
1794 LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURES, pPackage->Msi.cFeatures, pPackage->sczId);
1795
1796 for (DWORD j = 0; j < pPackage->Msi.cFeatures; ++j)
1797 {
1798 const BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[j];
1799
1800 LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURE, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState), LoggingMsiFeatureStateToString(pFeature->defaultRequested), LoggingMsiFeatureStateToString(pFeature->requested), LoggingMsiFeatureActionToString(pFeature->execute), LoggingMsiFeatureActionToString(pFeature->rollback));
1801 }
1802 }
1803
1804 if (pPackage->Msi.cSlipstreamMspPackages)
1805 {
1806 LogId(REPORT_STANDARD, MSG_PLANNED_SLIPSTREAMED_MSP_TARGETS, pPackage->Msi.cSlipstreamMspPackages, pPackage->sczId);
1807
1808 for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j)
1809 {
1810 const BURN_SLIPSTREAM_MSP* pSlipstreamMsp = &pPackage->Msi.rgSlipstreamMsps[j];
1811
1812 LogId(REPORT_STANDARD, MSG_PLANNED_SLIPSTREAMED_MSP_TARGET, pSlipstreamMsp->pMspPackage->sczId, LoggingActionStateToString(pSlipstreamMsp->execute), LoggingActionStateToString(pSlipstreamMsp->rollback));
1813 }
1814 }
1815 }
1816 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.cTargetProductCodes)
1817 {
1818 LogId(REPORT_STANDARD, MSG_PLANNED_MSP_TARGETS, pPackage->Msp.cTargetProductCodes, pPackage->sczId);
1819
1820 for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j)
1821 {
1822 const BURN_MSPTARGETPRODUCT* pTargetProduct = &pPackage->Msp.rgTargetProducts[j];
1823
1824 LogId(REPORT_STANDARD, MSG_PLANNED_MSP_TARGET, pTargetProduct->wzTargetProductCode, LoggingPackageStateToString(pTargetProduct->patchPackageState), LoggingRequestStateToString(pTargetProduct->defaultRequested), LoggingRequestStateToString(pTargetProduct->requested), LoggingMspTargetActionToString(pTargetProduct->execute, pTargetProduct->executeSkip), LoggingMspTargetActionToString(pTargetProduct->rollback, pTargetProduct->rollbackSkip));
1825 }
1826 }
1827 }
1828
1829 // Display related bundles last if caching, installing, modifying, or repairing.
1830 if (BOOTSTRAPPER_ACTION_UNINSTALL < action)
1831 {
1832 LogRelatedBundles(pRelatedBundles, FALSE);
1833 }
1834 }
1835}
1836
1837static void LogRelatedBundles(
1838 __in const BURN_RELATED_BUNDLES* pRelatedBundles,
1839 __in BOOL fReverse
1840 )
1841{
1842 if (0 < pRelatedBundles->cRelatedBundles)
1843 {
1844 for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i)
1845 {
1846 const DWORD iRelatedBundle = fReverse ? pRelatedBundles->cRelatedBundles - 1 - i : i;
1847 const BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + iRelatedBundle;
1848 const BURN_PACKAGE* pPackage = &pRelatedBundle->package;
1849
1850 if (pRelatedBundle->fPlannable)
1851 {
1852 LogId(REPORT_STANDARD, MSG_PLANNED_RELATED_BUNDLE, pPackage->sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingDependencyActionToString(pPackage->dependencyExecute));
1853 }
1854 }
1855 }
1856}
diff --git a/src/burn/engine/core.h b/src/burn/engine/core.h
new file mode 100644
index 00000000..e96440bb
--- /dev/null
+++ b/src/burn/engine/core.h
@@ -0,0 +1,218 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// constants
11
12const LPCWSTR BURN_POLICY_REGISTRY_PATH = L"WiX\\Burn";
13
14const LPCWSTR BURN_COMMANDLINE_SWITCH_PARENT = L"parent";
15const LPCWSTR BURN_COMMANDLINE_SWITCH_PARENT_NONE = L"parent:none";
16const LPCWSTR BURN_COMMANDLINE_SWITCH_CLEAN_ROOM = L"burn.clean.room";
17const LPCWSTR BURN_COMMANDLINE_SWITCH_ELEVATED = L"burn.elevated";
18const LPCWSTR BURN_COMMANDLINE_SWITCH_EMBEDDED = L"burn.embedded";
19const LPCWSTR BURN_COMMANDLINE_SWITCH_RUNONCE = L"burn.runonce";
20const LPCWSTR BURN_COMMANDLINE_SWITCH_LOG_APPEND = L"burn.log.append";
21const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_DETECT = L"burn.related.detect";
22const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE = L"burn.related.upgrade";
23const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_ADDON = L"burn.related.addon";
24const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_PATCH = L"burn.related.patch";
25const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPDATE = L"burn.related.update";
26const LPCWSTR BURN_COMMANDLINE_SWITCH_PASSTHROUGH = L"burn.passthrough";
27const LPCWSTR BURN_COMMANDLINE_SWITCH_DISABLE_UNELEVATE = L"burn.disable.unelevate";
28const LPCWSTR BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES = L"burn.ignoredependencies";
29const LPCWSTR BURN_COMMANDLINE_SWITCH_ANCESTORS = L"burn.ancestors";
30const LPCWSTR BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED = L"burn.filehandle.attached";
31const LPCWSTR BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF = L"burn.filehandle.self";
32const LPCWSTR BURN_COMMANDLINE_SWITCH_PREFIX = L"burn.";
33
34const LPCWSTR BURN_BUNDLE_LAYOUT_DIRECTORY = L"WixBundleLayoutDirectory";
35const LPCWSTR BURN_BUNDLE_ACTION = L"WixBundleAction";
36const LPCWSTR BURN_BUNDLE_ACTIVE_PARENT = L"WixBundleActiveParent";
37const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER = L"WixBundleExecutePackageCacheFolder";
38const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_ACTION = L"WixBundleExecutePackageAction";
39const LPCWSTR BURN_BUNDLE_FORCED_RESTART_PACKAGE = L"WixBundleForcedRestartPackage";
40const LPCWSTR BURN_BUNDLE_INSTALLED = L"WixBundleInstalled";
41const LPCWSTR BURN_BUNDLE_ELEVATED = L"WixBundleElevated";
42const LPCWSTR BURN_BUNDLE_PROVIDER_KEY = L"WixBundleProviderKey";
43const LPCWSTR BURN_BUNDLE_MANUFACTURER = L"WixBundleManufacturer";
44const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_PATH = L"WixBundleSourceProcessPath";
45const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_FOLDER = L"WixBundleSourceProcessFolder";
46const LPCWSTR BURN_BUNDLE_TAG = L"WixBundleTag";
47const LPCWSTR BURN_BUNDLE_UILEVEL = L"WixBundleUILevel";
48const LPCWSTR BURN_BUNDLE_VERSION = L"WixBundleVersion";
49const LPCWSTR BURN_REBOOT_PENDING = L"RebootPending";
50
51// The following constants must stay in sync with src\wix\Binder.cs
52const LPCWSTR BURN_BUNDLE_NAME = L"WixBundleName";
53const LPCWSTR BURN_BUNDLE_ORIGINAL_SOURCE = L"WixBundleOriginalSource";
54const LPCWSTR BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER = L"WixBundleOriginalSourceFolder";
55const LPCWSTR BURN_BUNDLE_LAST_USED_SOURCE = L"WixBundleLastUsedSource";
56
57
58// enums
59
60enum BURN_MODE
61{
62 BURN_MODE_UNTRUSTED,
63 BURN_MODE_NORMAL,
64 BURN_MODE_ELEVATED,
65 BURN_MODE_EMBEDDED,
66 BURN_MODE_RUNONCE,
67};
68
69enum BURN_AU_PAUSE_ACTION
70{
71 BURN_AU_PAUSE_ACTION_NONE,
72 BURN_AU_PAUSE_ACTION_IFELEVATED,
73 BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME,
74};
75
76
77// structs
78
79typedef struct _BURN_ENGINE_STATE
80{
81 // UX flow control
82 BOOL fDetected;
83 BOOL fPlanned;
84 BOOL fQuit;
85 //BOOL fSuspend; // Is TRUE when UX made Suspend() call on core.
86 //BOOL fForcedReboot; // Is TRUE when UX made Reboot() call on core.
87 //BOOL fCancelled; // Is TRUE when UX return cancel on UX OnXXX() methods.
88 //BOOL fReboot; // Is TRUE when UX confirms OnRestartRequried().
89 BOOL fRestart; // Set TRUE when UX returns IDRESTART during Apply().
90
91 // engine data
92 BOOTSTRAPPER_COMMAND command;
93 BURN_SECTION section;
94 BURN_VARIABLES variables;
95 BURN_CONDITION condition;
96 BURN_SEARCHES searches;
97 BURN_USER_EXPERIENCE userExperience;
98 BURN_REGISTRATION registration;
99 BURN_CONTAINERS containers;
100 BURN_PAYLOADS payloads;
101 BURN_PACKAGES packages;
102 BURN_UPDATE update;
103 BURN_APPROVED_EXES approvedExes;
104 BURN_EXTENSIONS extensions;
105
106 HWND hMessageWindow;
107 HANDLE hMessageWindowThread;
108
109 BOOL fDisableRollback;
110 BOOL fDisableSystemRestore;
111 BOOL fParallelCacheAndExecute;
112
113 BURN_LOGGING log;
114
115 BURN_PAYLOAD_GROUP layoutPayloads;
116
117 BURN_PLAN plan;
118
119 BURN_MODE mode;
120 BURN_AU_PAUSE_ACTION automaticUpdates;
121
122 DWORD dwElevatedLoggingTlsId;
123
124 LPWSTR sczBundleEngineWorkingPath;
125 BURN_PIPE_CONNECTION companionConnection;
126 BURN_PIPE_CONNECTION embeddedConnection;
127
128 BURN_RESUME_MODE resumeMode;
129 BOOL fDisableUnelevate;
130
131 LPWSTR sczIgnoreDependencies;
132
133 int argc;
134 LPWSTR* argv;
135} BURN_ENGINE_STATE;
136
137
138// function declarations
139
140HRESULT CoreInitialize(
141 __in BURN_ENGINE_STATE* pEngineState
142 );
143HRESULT CoreInitializeConstants(
144 __in BURN_ENGINE_STATE* pEngineState
145 );
146HRESULT CoreSerializeEngineState(
147 __in BURN_ENGINE_STATE* pEngineState,
148 __inout BYTE** ppbBuffer,
149 __inout SIZE_T* piBuffer
150 );
151HRESULT CoreQueryRegistration(
152 __in BURN_ENGINE_STATE* pEngineState
153 );
154//HRESULT CoreDeserializeEngineState(
155// __in BURN_ENGINE_STATE* pEngineState,
156// __in_bcount(cbBuffer) BYTE* pbBuffer,
157// __in SIZE_T cbBuffer
158// );
159HRESULT CoreDetect(
160 __in BURN_ENGINE_STATE* pEngineState,
161 __in_opt HWND hwndParent
162 );
163HRESULT CorePlan(
164 __in BURN_ENGINE_STATE* pEngineState,
165 __in BOOTSTRAPPER_ACTION action
166 );
167HRESULT CoreElevate(
168 __in BURN_ENGINE_STATE* pEngineState,
169 __in_opt HWND hwndParent
170 );
171HRESULT CoreApply(
172 __in BURN_ENGINE_STATE* pEngineState,
173 __in_opt HWND hwndParent
174 );
175HRESULT CoreLaunchApprovedExe(
176 __in BURN_ENGINE_STATE* pEngineState,
177 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe
178 );
179HRESULT CoreQuit(
180 __in BURN_ENGINE_STATE* pEngineState,
181 __in int nExitCode
182 );
183HRESULT CoreSaveEngineState(
184 __in BURN_ENGINE_STATE* pEngineState
185 );
186LPCWSTR CoreRelationTypeToCommandLineString(
187 __in BOOTSTRAPPER_RELATION_TYPE relationType
188 );
189HRESULT CoreRecreateCommandLine(
190 __deref_inout_z LPWSTR* psczCommandLine,
191 __in BOOTSTRAPPER_ACTION action,
192 __in BOOTSTRAPPER_DISPLAY display,
193 __in BOOTSTRAPPER_RESTART restart,
194 __in BOOTSTRAPPER_RELATION_TYPE relationType,
195 __in BOOL fPassthrough,
196 __in_z_opt LPCWSTR wzActiveParent,
197 __in_z_opt LPCWSTR wzAncestors,
198 __in_z_opt LPCWSTR wzAppendLogPath,
199 __in_z_opt LPCWSTR wzAdditionalCommandLineArguments
200 );
201HRESULT CoreAppendFileHandleAttachedToCommandLine(
202 __in HANDLE hFileWithAttachedContainer,
203 __out HANDLE* phExecutableFile,
204 __deref_inout_z LPWSTR* psczCommandLine
205 );
206HRESULT CoreAppendFileHandleSelfToCommandLine(
207 __in LPCWSTR wzExecutablePath,
208 __out HANDLE* phExecutableFile,
209 __deref_inout_z LPWSTR* psczCommandLine,
210 __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine
211 );
212void CoreCleanup(
213 __in BURN_ENGINE_STATE* pEngineState
214 );
215
216#if defined(__cplusplus)
217}
218#endif
diff --git a/src/burn/engine/dependency.cpp b/src/burn/engine/dependency.cpp
new file mode 100644
index 00000000..876cd8b3
--- /dev/null
+++ b/src/burn/engine/dependency.cpp
@@ -0,0 +1,1312 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5// constants
6
7#define INITIAL_STRINGDICT_SIZE 48
8const LPCWSTR vcszIgnoreDependenciesDelim = L";";
9
10
11// internal function declarations
12
13static HRESULT DetectPackageDependents(
14 __in BURN_PACKAGE* pPackage,
15 __in STRINGDICT_HANDLE sdIgnoredDependents,
16 __in const BURN_REGISTRATION* pRegistration
17 );
18
19static HRESULT SplitIgnoreDependencies(
20 __in_z LPCWSTR wzIgnoreDependencies,
21 __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies,
22 __inout LPUINT pcDependencies,
23 __out BOOL* pfIgnoreAll
24 );
25
26static HRESULT JoinIgnoreDependencies(
27 __out_z LPWSTR* psczIgnoreDependencies,
28 __in_ecount(cDependencies) const DEPENDENCY* rgDependencies,
29 __in UINT cDependencies
30 );
31
32static HRESULT GetIgnoredDependents(
33 __in const BURN_PACKAGE* pPackage,
34 __in const BURN_PLAN* pPlan,
35 __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents
36 );
37
38static BOOL GetProviderExists(
39 __in HKEY hkRoot,
40 __in_z LPCWSTR wzProviderKey
41 );
42
43static void CalculateDependencyActionStates(
44 __in const BURN_PACKAGE* pPackage,
45 __in const BOOTSTRAPPER_ACTION action,
46 __out BURN_DEPENDENCY_ACTION* pDependencyExecuteAction,
47 __out BURN_DEPENDENCY_ACTION* pDependencyRollbackAction
48 );
49
50static HRESULT AddPackageDependencyActions(
51 __in_opt DWORD *pdwInsertSequence,
52 __in const BURN_PACKAGE* pPackage,
53 __in BURN_PLAN* pPlan,
54 __in const BURN_DEPENDENCY_ACTION dependencyExecuteAction,
55 __in const BURN_DEPENDENCY_ACTION dependencyRollbackAction
56 );
57
58static HRESULT RegisterPackageProvider(
59 __in const BURN_PACKAGE* pPackage
60 );
61
62static void UnregisterPackageProvider(
63 __in const BURN_PACKAGE* pPackage
64 );
65
66static HRESULT RegisterPackageDependency(
67 __in BOOL fPerMachine,
68 __in const BURN_PACKAGE* pPackage,
69 __in_z LPCWSTR wzDependentProviderKey
70 );
71
72static void UnregisterPackageDependency(
73 __in BOOL fPerMachine,
74 __in const BURN_PACKAGE* pPackage,
75 __in_z LPCWSTR wzDependentProviderKey
76 );
77
78
79// functions
80
81extern "C" void DependencyUninitializeProvider(
82 __in BURN_DEPENDENCY_PROVIDER* pProvider
83 )
84{
85 ReleaseStr(pProvider->sczKey);
86 ReleaseStr(pProvider->sczVersion);
87 ReleaseStr(pProvider->sczDisplayName);
88 ReleaseDependencyArray(pProvider->rgDependents, pProvider->cDependents);
89
90 memset(pProvider, 0, sizeof(BURN_DEPENDENCY_PROVIDER));
91}
92
93extern "C" HRESULT DependencyParseProvidersFromXml(
94 __in BURN_PACKAGE* pPackage,
95 __in IXMLDOMNode* pixnPackage
96 )
97{
98 HRESULT hr = S_OK;
99 IXMLDOMNodeList* pixnNodes = NULL;
100 DWORD cNodes = 0;
101 IXMLDOMNode* pixnNode = NULL;
102
103 // Select dependency provider nodes.
104 hr = XmlSelectNodes(pixnPackage, L"Provides", &pixnNodes);
105 ExitOnFailure(hr, "Failed to select dependency provider nodes.");
106
107 // Get dependency provider node count.
108 hr = pixnNodes->get_length((long*)&cNodes);
109 ExitOnFailure(hr, "Failed to get the dependency provider node count.");
110
111 if (!cNodes)
112 {
113 ExitFunction1(hr = S_OK);
114 }
115
116 // Allocate memory for dependency provider pointers.
117 pPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER) * cNodes, TRUE);
118 ExitOnNull(pPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers.");
119
120 pPackage->cDependencyProviders = cNodes;
121
122 // Parse dependency provider elements.
123 for (DWORD i = 0; i < cNodes; i++)
124 {
125 BURN_DEPENDENCY_PROVIDER* pDependencyProvider = &pPackage->rgDependencyProviders[i];
126
127 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
128 ExitOnFailure(hr, "Failed to get the next dependency provider node.");
129
130 // @Key
131 hr = XmlGetAttributeEx(pixnNode, L"Key", &pDependencyProvider->sczKey);
132 ExitOnFailure(hr, "Failed to get the Key attribute.");
133
134 // @Version
135 hr = XmlGetAttributeEx(pixnNode, L"Version", &pDependencyProvider->sczVersion);
136 if (E_NOTFOUND != hr)
137 {
138 ExitOnFailure(hr, "Failed to get the Version attribute.");
139 }
140
141 // @DisplayName
142 hr = XmlGetAttributeEx(pixnNode, L"DisplayName", &pDependencyProvider->sczDisplayName);
143 if (E_NOTFOUND != hr)
144 {
145 ExitOnFailure(hr, "Failed to get the DisplayName attribute.");
146 }
147
148 // @Imported
149 hr = XmlGetYesNoAttribute(pixnNode, L"Imported", &pDependencyProvider->fImported);
150 if (E_NOTFOUND != hr)
151 {
152 ExitOnFailure(hr, "Failed to get the Imported attribute.");
153 }
154 else
155 {
156 pDependencyProvider->fImported = FALSE;
157 hr = S_OK;
158 }
159
160 // Prepare next iteration.
161 ReleaseNullObject(pixnNode);
162 }
163
164 hr = S_OK;
165
166LExit:
167 ReleaseObject(pixnNode);
168 ReleaseObject(pixnNodes);
169
170 return hr;
171}
172
173extern "C" HRESULT DependencyInitialize(
174 __in BURN_REGISTRATION* pRegistration,
175 __in_z_opt LPCWSTR wzIgnoreDependencies
176 )
177{
178 HRESULT hr = S_OK;
179
180 // If no parent was specified at all, use the bundle id as the self dependent.
181 if (!pRegistration->sczActiveParent)
182 {
183 pRegistration->wzSelfDependent = pRegistration->sczId;
184 }
185 else if (*pRegistration->sczActiveParent) // if parent was specified use that as the self dependent.
186 {
187 pRegistration->wzSelfDependent = pRegistration->sczActiveParent;
188 }
189 // else parent:none was used which means we should not register a dependency on ourself.
190
191 // The current bundle provider key should always be ignored for dependency checks.
192 hr = DepDependencyArrayAlloc(&pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies, pRegistration->sczProviderKey, NULL);
193 ExitOnFailure(hr, "Failed to add the bundle provider key to the list of dependencies to ignore.");
194
195 // Add the list of dependencies to ignore.
196 if (wzIgnoreDependencies)
197 {
198 hr = SplitIgnoreDependencies(wzIgnoreDependencies, &pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies, &pRegistration->fIgnoreAllDependents);
199 ExitOnFailure(hr, "Failed to split the list of dependencies to ignore.");
200 }
201
202LExit:
203 return hr;
204}
205
206extern "C" HRESULT DependencyDetectProviderKeyBundleId(
207 __in BURN_REGISTRATION* pRegistration
208 )
209{
210 HRESULT hr = S_OK;
211
212 hr = DepGetProviderInformation(pRegistration->hkRoot, pRegistration->sczProviderKey, &pRegistration->sczDetectedProviderKeyBundleId, NULL, NULL);
213 if (E_NOTFOUND == hr)
214 {
215 ExitFunction();
216 }
217 ExitOnFailure(hr, "Failed to get provider key bundle id.");
218
219 // If a bundle id was not explicitly set, default the provider key bundle id to this bundle's provider key.
220 if (!pRegistration->sczDetectedProviderKeyBundleId || !*pRegistration->sczDetectedProviderKeyBundleId)
221 {
222 hr = StrAllocString(&pRegistration->sczDetectedProviderKeyBundleId, pRegistration->sczProviderKey, 0);
223 ExitOnFailure(hr, "Failed to initialize provider key bundle id.");
224 }
225
226LExit:
227 return hr;
228}
229
230extern "C" HRESULT DependencyDetect(
231 __in BURN_ENGINE_STATE* pEngineState
232 )
233{
234 HRESULT hr = S_OK;
235 BURN_REGISTRATION* pRegistration = &pEngineState->registration;
236 STRINGDICT_HANDLE sdIgnoredDependents = NULL;
237 BURN_PACKAGE* pPackage = NULL;
238 BOOL fSelfDependent = NULL != pRegistration->wzSelfDependent;
239 BOOL fActiveParent = NULL != pRegistration->sczActiveParent && NULL != *pRegistration->sczActiveParent;
240
241 // Always leave this empty so that all dependents get detected. Plan will ignore dependents based on its own logic.
242 hr = DictCreateStringList(&sdIgnoredDependents, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE);
243 ExitOnFailure(hr, "Failed to create the string dictionary.");
244
245 hr = DepCheckDependents(pRegistration->hkRoot, pRegistration->sczProviderKey, 0, sdIgnoredDependents, &pRegistration->rgDependents, &pRegistration->cDependents);
246 if (E_FILENOTFOUND != hr)
247 {
248 ExitOnFailure(hr, "Failed dependents check on bundle");
249 }
250 else
251 {
252 hr = S_OK;
253 }
254
255 for (DWORD iPackage = 0; iPackage < pEngineState->packages.cPackages; ++iPackage)
256 {
257 pPackage = pEngineState->packages.rgPackages + iPackage;
258 hr = DetectPackageDependents(pPackage, sdIgnoredDependents, pRegistration);
259 ExitOnFailure(hr, "Failed to detect dependents for package '%ls'", pPackage->sczId);
260 }
261
262 for (DWORD iRelatedBundle = 0; iRelatedBundle < pEngineState->registration.relatedBundles.cRelatedBundles; ++iRelatedBundle)
263 {
264 BURN_RELATED_BUNDLE* pRelatedBundle = pEngineState->registration.relatedBundles.rgRelatedBundles + iRelatedBundle;
265 if (!pRelatedBundle->fPlannable)
266 {
267 continue;
268 }
269
270 pPackage = &pRelatedBundle->package;
271 hr = DetectPackageDependents(pPackage, sdIgnoredDependents, pRegistration);
272 ExitOnFailure(hr, "Failed to detect dependents for related bundle '%ls'", pPackage->sczId);
273 }
274
275 if (fSelfDependent || fActiveParent)
276 {
277 for (DWORD i = 0; i < pRegistration->cDependents; ++i)
278 {
279 DEPENDENCY* pDependent = pRegistration->rgDependents + i;
280
281 if (fActiveParent && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczActiveParent, -1, pDependent->sczKey, -1))
282 {
283 pRegistration->fParentRegisteredAsDependent = TRUE;
284 }
285
286 if (fSelfDependent && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->wzSelfDependent, -1, pDependent->sczKey, -1))
287 {
288 pRegistration->fSelfRegisteredAsDependent = TRUE;
289 }
290 }
291 }
292
293LExit:
294 ReleaseDict(sdIgnoredDependents);
295
296 return hr;
297}
298
299extern "C" HRESULT DependencyPlanInitialize(
300 __in const BURN_REGISTRATION* pRegistration,
301 __in BURN_PLAN* pPlan
302 )
303{
304 HRESULT hr = S_OK;
305
306 // TODO: After adding enumeration to STRINGDICT, a single STRINGDICT_HANDLE can be used everywhere.
307 for (DWORD i = 0; i < pRegistration->cIgnoredDependencies; ++i)
308 {
309 DEPENDENCY* pDependency = pRegistration->rgIgnoredDependencies + i;
310
311 hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pDependency->sczKey, pDependency->sczName);
312 ExitOnFailure(hr, "Failed to add the detected provider to the list of dependencies to ignore.");
313 }
314
315LExit:
316 return hr;
317}
318
319extern "C" HRESULT DependencyAllocIgnoreDependencies(
320 __in const BURN_PLAN *pPlan,
321 __out_z LPWSTR* psczIgnoreDependencies
322 )
323{
324 HRESULT hr = S_OK;
325
326 // Join the list of dependencies to ignore for each related bundle.
327 if (0 < pPlan->cPlannedProviders)
328 {
329 hr = JoinIgnoreDependencies(psczIgnoreDependencies, pPlan->rgPlannedProviders, pPlan->cPlannedProviders);
330 ExitOnFailure(hr, "Failed to join the list of dependencies to ignore.");
331 }
332
333LExit:
334 return hr;
335}
336
337extern "C" HRESULT DependencyAddIgnoreDependencies(
338 __in STRINGDICT_HANDLE sdIgnoreDependencies,
339 __in_z LPCWSTR wzAddIgnoreDependencies
340 )
341{
342 HRESULT hr = S_OK;
343 LPWSTR wzContext = NULL;
344
345 // Parse through the semicolon-delimited tokens and add to the array.
346 for (LPCWSTR wzToken = ::wcstok_s(const_cast<LPWSTR>(wzAddIgnoreDependencies), vcszIgnoreDependenciesDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, vcszIgnoreDependenciesDelim, &wzContext))
347 {
348 hr = DictKeyExists(sdIgnoreDependencies, wzToken);
349 if (E_NOTFOUND != hr)
350 {
351 ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies.");
352 }
353 else
354 {
355 hr = DictAddKey(sdIgnoreDependencies, wzToken);
356 ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzToken);
357 }
358 }
359
360LExit:
361 return hr;
362}
363
364extern "C" HRESULT DependencyPlanPackageBegin(
365 __in BOOL fPerMachine,
366 __in BURN_PACKAGE* pPackage,
367 __in BURN_PLAN* pPlan
368 )
369{
370 HRESULT hr = S_OK;
371 STRINGDICT_HANDLE sdIgnoredDependents = NULL;
372 BURN_DEPENDENCY_ACTION dependencyExecuteAction = BURN_DEPENDENCY_ACTION_NONE;
373 BURN_DEPENDENCY_ACTION dependencyRollbackAction = BURN_DEPENDENCY_ACTION_NONE;
374
375 pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE;
376 pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE;
377
378 // Make sure the package defines at least one provider.
379 if (0 == pPackage->cDependencyProviders)
380 {
381 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_SKIP_NOPROVIDERS, pPackage->sczId);
382 ExitFunction1(hr = S_OK);
383 }
384
385 // Make sure the package is in the same scope as the bundle.
386 if (fPerMachine != pPackage->fPerMachine)
387 {
388 LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine));
389 ExitFunction1(hr = S_OK);
390 }
391
392 // If we're uninstalling the package, check if any dependents are registered.
393 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute)
394 {
395 // Build up a list of dependents to ignore, including the current bundle.
396 hr = GetIgnoredDependents(pPackage, pPlan, &sdIgnoredDependents);
397 ExitOnFailure(hr, "Failed to build the list of ignored dependents.");
398
399 // Skip the dependency check if "ALL" was authored for IGNOREDEPENDENCIES.
400 hr = DictKeyExists(sdIgnoredDependents, L"ALL");
401 if (E_NOTFOUND != hr)
402 {
403 ExitOnFailure(hr, "Failed to check if \"ALL\" was set in IGNOREDEPENDENCIES.");
404 }
405 else
406 {
407 hr = S_OK;
408
409 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
410 {
411 const BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + i;
412
413 for (DWORD j = 0; j < pProvider->cDependents; ++j)
414 {
415 const DEPENDENCY* pDependency = pProvider->rgDependents + j;
416
417 hr = DictKeyExists(sdIgnoredDependents, pDependency->sczKey);
418 if (E_NOTFOUND == hr)
419 {
420 hr = S_OK;
421
422 if (!pPackage->fDependencyManagerWasHere)
423 {
424 pPackage->fDependencyManagerWasHere = TRUE;
425
426 LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS, pPackage->sczId);
427 }
428
429 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_DEPENDENT, pDependency->sczKey, LoggingStringOrUnknownIfNull(pDependency->sczName));
430 }
431 ExitOnFailure(hr, "Failed to check the dictionary of ignored dependents.");
432 }
433 }
434 }
435 }
436
437 // Calculate the dependency actions before the package itself is planned.
438 CalculateDependencyActionStates(pPackage, pPlan->action, &dependencyExecuteAction, &dependencyRollbackAction);
439
440 // If dependents were found, change the action to not uninstall the package.
441 if (pPackage->fDependencyManagerWasHere)
442 {
443 pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
444 pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
445 }
446 else
447 {
448 // Use the calculated dependency actions as the provider actions if there
449 // are any non-imported providers that need to be registered and the package
450 // is current (not obsolete).
451 if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE != pPackage->currentState)
452 {
453 BOOL fAllImportedProviders = TRUE; // assume all providers were imported.
454 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
455 {
456 const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
457 if (!pProvider->fImported)
458 {
459 fAllImportedProviders = FALSE;
460 break;
461 }
462 }
463
464 if (!fAllImportedProviders)
465 {
466 pPackage->providerExecute = dependencyExecuteAction;
467 pPackage->providerRollback = dependencyRollbackAction;
468 }
469 }
470
471 // If the package will be removed, add its providers to the growing list in the plan.
472 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute)
473 {
474 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
475 {
476 const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
477
478 hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, NULL);
479 ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey);
480 }
481 }
482 }
483
484 pPackage->dependencyExecute = dependencyExecuteAction;
485 pPackage->dependencyRollback = dependencyRollbackAction;
486
487LExit:
488 ReleaseDict(sdIgnoredDependents);
489
490 return hr;
491}
492
493extern "C" HRESULT DependencyPlanPackage(
494 __in_opt DWORD *pdwInsertSequence,
495 __in const BURN_PACKAGE* pPackage,
496 __in BURN_PLAN* pPlan
497 )
498{
499 HRESULT hr = S_OK;
500 BURN_EXECUTE_ACTION* pAction = NULL;
501
502 // If the dependency execution action is to unregister, add the dependency actions to the plan
503 // *before* the provider key is potentially removed.
504 if (BURN_DEPENDENCY_ACTION_UNREGISTER == pPackage->dependencyExecute)
505 {
506 hr = AddPackageDependencyActions(pdwInsertSequence, pPackage, pPlan, pPackage->dependencyExecute, pPackage->dependencyRollback);
507 ExitOnFailure(hr, "Failed to plan the dependency actions for package: %ls", pPackage->sczId);
508 }
509
510 // Add the provider rollback plan.
511 if (BURN_DEPENDENCY_ACTION_NONE != pPackage->providerRollback)
512 {
513 hr = PlanAppendRollbackAction(pPlan, &pAction);
514 ExitOnFailure(hr, "Failed to append provider rollback action.");
515
516 pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER;
517 pAction->packageProvider.pPackage = const_cast<BURN_PACKAGE*>(pPackage);
518 pAction->packageProvider.action = pPackage->providerRollback;
519
520 // Put a checkpoint before the execute action so that rollback happens
521 // if execute fails.
522 hr = PlanExecuteCheckpoint(pPlan);
523 ExitOnFailure(hr, "Failed to plan provider checkpoint action.");
524 }
525
526 // Add the provider execute plan. This comes after rollback so if something goes wrong
527 // rollback will try to clean up after us.
528 if (BURN_DEPENDENCY_ACTION_NONE != pPackage->providerExecute)
529 {
530 if (NULL != pdwInsertSequence)
531 {
532 hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction);
533 ExitOnFailure(hr, "Failed to insert provider execute action.");
534
535 // Always move the sequence after this dependency action so the provider registration
536 // stays in front of the inserted actions.
537 ++(*pdwInsertSequence);
538 }
539 else
540 {
541 hr = PlanAppendExecuteAction(pPlan, &pAction);
542 ExitOnFailure(hr, "Failed to append provider execute action.");
543 }
544
545 pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER;
546 pAction->packageProvider.pPackage = const_cast<BURN_PACKAGE*>(pPackage);
547 pAction->packageProvider.action = pPackage->providerExecute;
548 }
549
550LExit:
551 return hr;
552}
553
554extern "C" HRESULT DependencyPlanPackageComplete(
555 __in BURN_PACKAGE* pPackage,
556 __in BURN_PLAN* pPlan
557 )
558{
559 HRESULT hr = S_OK;
560
561 // Registration of dependencies happens here, after the package is planned to be
562 // installed and all that good stuff.
563 if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute)
564 {
565 // Recalculate the dependency actions in case other operations may have changed
566 // the package execution state.
567 CalculateDependencyActionStates(pPackage, pPlan->action, &pPackage->dependencyExecute, &pPackage->dependencyRollback);
568
569 // If the dependency execution action is *still* to register, add the dependency actions to the plan.
570 if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute)
571 {
572 hr = AddPackageDependencyActions(NULL, pPackage, pPlan, pPackage->dependencyExecute, pPackage->dependencyRollback);
573 ExitOnFailure(hr, "Failed to plan the dependency actions for package: %ls", pPackage->sczId);
574 }
575 }
576
577LExit:
578 return hr;
579}
580
581extern "C" HRESULT DependencyExecutePackageProviderAction(
582 __in const BURN_EXECUTE_ACTION* pAction
583 )
584{
585 AssertSz(BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER == pAction->type, "Execute action type not supported by this function.");
586
587 HRESULT hr = S_OK;
588 const BURN_PACKAGE* pPackage = pAction->packageProvider.pPackage;
589
590 // Register or unregister the package provider(s).
591 if (BURN_DEPENDENCY_ACTION_REGISTER == pAction->packageProvider.action)
592 {
593 hr = RegisterPackageProvider(pPackage);
594 ExitOnFailure(hr, "Failed to register the package providers.");
595 }
596 else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pAction->packageProvider.action)
597 {
598 UnregisterPackageProvider(pPackage);
599 }
600
601LExit:
602 if (!pPackage->fVital)
603 {
604 hr = S_OK;
605 }
606
607 return hr;
608}
609
610extern "C" HRESULT DependencyExecutePackageDependencyAction(
611 __in BOOL fPerMachine,
612 __in const BURN_EXECUTE_ACTION* pAction
613 )
614{
615 AssertSz(BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY == pAction->type, "Execute action type not supported by this function.");
616
617 HRESULT hr = S_OK;
618 const BURN_PACKAGE* pPackage = pAction->packageDependency.pPackage;
619
620 // Register or unregister the bundle as a dependent of each package dependency provider.
621 if (BURN_DEPENDENCY_ACTION_REGISTER == pAction->packageDependency.action)
622 {
623 hr = RegisterPackageDependency(fPerMachine, pPackage, pAction->packageDependency.sczBundleProviderKey);
624 ExitOnFailure(hr, "Failed to register the dependency on the package provider.");
625 }
626 else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pAction->packageDependency.action)
627 {
628 UnregisterPackageDependency(fPerMachine, pPackage, pAction->packageDependency.sczBundleProviderKey);
629 }
630
631LExit:
632 if (!pPackage->fVital)
633 {
634 hr = S_OK;
635 }
636
637 return hr;
638}
639
640extern "C" HRESULT DependencyRegisterBundle(
641 __in const BURN_REGISTRATION* pRegistration
642 )
643{
644 HRESULT hr = S_OK;
645
646 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_REGISTER, pRegistration->sczProviderKey, pRegistration->pVersion->sczVersion);
647
648 // Register the bundle provider key.
649 hr = DepRegisterDependency(pRegistration->hkRoot, pRegistration->sczProviderKey, pRegistration->pVersion->sczVersion, pRegistration->sczDisplayName, pRegistration->sczId, 0);
650 ExitOnFailure(hr, "Failed to register the bundle dependency provider.");
651
652LExit:
653 return hr;
654}
655
656extern "C" HRESULT DependencyProcessDependentRegistration(
657 __in const BURN_REGISTRATION* pRegistration,
658 __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction
659 )
660{
661 HRESULT hr = S_OK;
662
663 switch (pAction->type)
664 {
665 case BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER:
666 hr = DepRegisterDependent(pRegistration->hkRoot, pRegistration->sczProviderKey, pAction->sczDependentProviderKey, NULL, NULL, 0);
667 ExitOnFailure(hr, "Failed to register dependent: %ls", pAction->sczDependentProviderKey);
668 break;
669
670 case BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER:
671 hr = DepUnregisterDependent(pRegistration->hkRoot, pRegistration->sczProviderKey, pAction->sczDependentProviderKey);
672 ExitOnFailure(hr, "Failed to unregister dependent: %ls", pAction->sczDependentProviderKey);
673 break;
674
675 default:
676 hr = E_INVALIDARG;
677 ExitOnRootFailure(hr, "Unrecognized registration action type: %d", pAction->type);
678 }
679
680LExit:
681 return hr;
682}
683
684extern "C" void DependencyUnregisterBundle(
685 __in const BURN_REGISTRATION* pRegistration,
686 __in const BURN_PACKAGES* pPackages
687 )
688{
689 HRESULT hr = S_OK;
690 LPCWSTR wzDependentProviderKey = pRegistration->sczId;
691
692 // Remove the bundle provider key.
693 hr = DepUnregisterDependency(pRegistration->hkRoot, pRegistration->sczProviderKey);
694 if (SUCCEEDED(hr))
695 {
696 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_UNREGISTERED, pRegistration->sczProviderKey);
697 }
698 else if (FAILED(hr) && E_FILENOTFOUND != hr)
699 {
700 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_UNREGISTERED_FAILED, pRegistration->sczProviderKey, hr);
701 }
702
703 // Best effort to make sure this bundle is not registered as a dependent for anything.
704 for (DWORD i = 0; i < pPackages->cPackages; ++i)
705 {
706 const BURN_PACKAGE* pPackage = pPackages->rgPackages + i;
707 UnregisterPackageDependency(pPackage->fPerMachine, pPackage, wzDependentProviderKey);
708 }
709
710 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
711 {
712 const BURN_PACKAGE* pPackage = &pRegistration->relatedBundles.rgRelatedBundles[i].package;
713 UnregisterPackageDependency(pPackage->fPerMachine, pPackage, wzDependentProviderKey);
714 }
715}
716
717// internal functions
718
719
720static HRESULT DetectPackageDependents(
721 __in BURN_PACKAGE* pPackage,
722 __in STRINGDICT_HANDLE sdIgnoredDependents,
723 __in const BURN_REGISTRATION* pRegistration
724 )
725{
726 HRESULT hr = S_OK;
727 HKEY hkHive = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
728 BOOL fCanIgnorePresence = pPackage->fCanAffectRegistration && 0 < pPackage->cDependencyProviders &&
729 (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState || BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState);
730 BOOL fBundleRegisteredAsDependent = FALSE;
731
732 // There's currently no point in getting the dependents if the scope doesn't match,
733 // because they will just get ignored.
734 if (pRegistration->fPerMachine != pPackage->fPerMachine)
735 {
736 ExitFunction();
737 }
738
739 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
740 {
741 BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
742
743 hr = DepCheckDependents(hkHive, pProvider->sczKey, 0, sdIgnoredDependents, &pProvider->rgDependents, &pProvider->cDependents);
744 if (E_FILENOTFOUND != hr)
745 {
746 ExitOnFailure(hr, "Failed dependents check on package provider: %ls", pProvider->sczKey);
747
748 if (!pPackage->fPackageProviderExists && (0 < pProvider->cDependents || GetProviderExists(hkHive, pProvider->sczKey)))
749 {
750 pPackage->fPackageProviderExists = TRUE;
751 }
752
753 if (fCanIgnorePresence && !fBundleRegisteredAsDependent)
754 {
755 for (DWORD iDependent = 0; iDependent < pProvider->cDependents; ++iDependent)
756 {
757 DEPENDENCY* pDependent = pProvider->rgDependents + iDependent;
758
759 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczId, -1, pDependent->sczKey, -1))
760 {
761 fBundleRegisteredAsDependent = TRUE;
762 break;
763 }
764 }
765 }
766 }
767 else
768 {
769 hr = S_OK;
770
771 if (!pPackage->fPackageProviderExists && GetProviderExists(hkHive, pProvider->sczKey))
772 {
773 pPackage->fPackageProviderExists = TRUE;
774 }
775 }
776 }
777
778 // Older bundles may not have written the id so try the default.
779 if (!pPackage->fPackageProviderExists && BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.sczProductCode && GetProviderExists(hkHive, pPackage->Msi.sczProductCode))
780 {
781 pPackage->fPackageProviderExists = TRUE;
782 }
783
784 if (fCanIgnorePresence && !fBundleRegisteredAsDependent)
785 {
786 if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState)
787 {
788 pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED;
789 }
790 if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState)
791 {
792 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED;
793 }
794 if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
795 {
796 for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
797 {
798 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i;
799
800 if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pTargetProduct->registrationState)
801 {
802 pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED;
803 }
804 }
805 }
806 }
807
808LExit:
809 return hr;
810}
811
812/********************************************************************
813 SplitIgnoreDependencies - Splits a semicolon-delimited
814 string into a list of unique dependencies to ignore.
815
816*********************************************************************/
817static HRESULT SplitIgnoreDependencies(
818 __in_z LPCWSTR wzIgnoreDependencies,
819 __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies,
820 __inout LPUINT pcDependencies,
821 __out BOOL* pfIgnoreAll
822 )
823{
824 HRESULT hr = S_OK;
825 LPWSTR wzContext = NULL;
826 STRINGDICT_HANDLE sdIgnoreDependencies = NULL;
827 *pfIgnoreAll = FALSE;
828
829 // Create a dictionary to hold unique dependencies.
830 hr = DictCreateStringList(&sdIgnoreDependencies, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE);
831 ExitOnFailure(hr, "Failed to create the string dictionary.");
832
833 // Parse through the semicolon-delimited tokens and add to the array.
834 for (LPCWSTR wzToken = ::wcstok_s(const_cast<LPWSTR>(wzIgnoreDependencies), vcszIgnoreDependenciesDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, vcszIgnoreDependenciesDelim, &wzContext))
835 {
836 hr = DictKeyExists(sdIgnoreDependencies, wzToken);
837 if (E_NOTFOUND != hr)
838 {
839 ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies.");
840 }
841 else
842 {
843 hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzToken, NULL);
844 ExitOnFailure(hr, "Failed to add \"%ls\" to the list of dependencies to ignore.", wzToken);
845
846 hr = DictAddKey(sdIgnoreDependencies, wzToken);
847 ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzToken);
848
849 if (!*pfIgnoreAll && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, L"ALL", -1, wzToken, -1))
850 {
851 *pfIgnoreAll = TRUE;
852 }
853 }
854 }
855
856LExit:
857 ReleaseDict(sdIgnoreDependencies);
858
859 return hr;
860}
861
862/********************************************************************
863 JoinIgnoreDependencies - Joins a list of dependencies
864 to ignore into a semicolon-delimited string of unique values.
865
866*********************************************************************/
867static HRESULT JoinIgnoreDependencies(
868 __out_z LPWSTR* psczIgnoreDependencies,
869 __in_ecount(cDependencies) const DEPENDENCY* rgDependencies,
870 __in UINT cDependencies
871 )
872{
873 HRESULT hr = S_OK;
874 STRINGDICT_HANDLE sdIgnoreDependencies = NULL;
875
876 // Make sure we pass back an empty string if there are no dependencies.
877 if (0 == cDependencies)
878 {
879 ExitFunction1(hr = S_OK);
880 }
881
882 // Create a dictionary to hold unique dependencies.
883 hr = DictCreateStringList(&sdIgnoreDependencies, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE);
884 ExitOnFailure(hr, "Failed to create the string dictionary.");
885
886 for (UINT i = 0; i < cDependencies; ++i)
887 {
888 const DEPENDENCY* pDependency = &rgDependencies[i];
889
890 hr = DictKeyExists(sdIgnoreDependencies, pDependency->sczKey);
891 if (E_NOTFOUND != hr)
892 {
893 ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies.");
894 }
895 else
896 {
897 if (0 < i)
898 {
899 hr = StrAllocConcat(psczIgnoreDependencies, vcszIgnoreDependenciesDelim, 1);
900 ExitOnFailure(hr, "Failed to append the string delimiter.");
901 }
902
903 hr = StrAllocConcat(psczIgnoreDependencies, pDependency->sczKey, 0);
904 ExitOnFailure(hr, "Failed to append the key \"%ls\".", pDependency->sczKey);
905
906 hr = DictAddKey(sdIgnoreDependencies, pDependency->sczKey);
907 ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", pDependency->sczKey);
908 }
909 }
910
911LExit:
912 ReleaseDict(sdIgnoreDependencies);
913
914 return hr;
915}
916
917/********************************************************************
918 GetIgnoredDependents - Combines the current bundle's
919 provider key, packages' provider keys that are being uninstalled,
920 and any ignored dependencies authored for packages into a string
921 list to pass to deputil.
922
923*********************************************************************/
924static HRESULT GetIgnoredDependents(
925 __in const BURN_PACKAGE* pPackage,
926 __in const BURN_PLAN* pPlan,
927 __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents
928 )
929{
930 HRESULT hr = S_OK;
931 LPWSTR sczIgnoreDependencies = NULL;
932
933 // Create the dictionary and add the bundle provider key initially.
934 hr = DictCreateStringList(psdIgnoredDependents, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE);
935 ExitOnFailure(hr, "Failed to create the string dictionary.");
936
937 hr = DictAddKey(*psdIgnoredDependents, pPlan->wzBundleProviderKey);
938 ExitOnFailure(hr, "Failed to add the bundle provider key \"%ls\" to the list of ignored dependencies.", pPlan->wzBundleProviderKey);
939
940 // Add previously planned package providers to the dictionary.
941 for (DWORD i = 0; i < pPlan->cPlannedProviders; ++i)
942 {
943 const DEPENDENCY* pDependency = &pPlan->rgPlannedProviders[i];
944
945 hr = DictAddKey(*psdIgnoredDependents, pDependency->sczKey);
946 ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the list of ignored dependencies.", pDependency->sczKey);
947 }
948
949 // Get the IGNOREDEPENDENCIES property if defined.
950 hr = PackageGetProperty(pPackage, DEPENDENCY_IGNOREDEPENDENCIES, &sczIgnoreDependencies);
951 if (E_NOTFOUND != hr)
952 {
953 ExitOnFailure(hr, "Failed to get the package property: %ls", DEPENDENCY_IGNOREDEPENDENCIES);
954
955 hr = DependencyAddIgnoreDependencies(*psdIgnoredDependents, sczIgnoreDependencies);
956 ExitOnFailure(hr, "Failed to add the authored ignored dependencies to the cumulative list of ignored dependencies.");
957 }
958 else
959 {
960 hr = S_OK;
961 }
962
963LExit:
964 ReleaseStr(sczIgnoreDependencies);
965
966 return hr;
967}
968
969/********************************************************************
970 GetProviderExists - Gets whether the provider key is registered.
971
972*********************************************************************/
973static BOOL GetProviderExists(
974 __in HKEY hkRoot,
975 __in_z LPCWSTR wzProviderKey
976 )
977{
978 HRESULT hr = DepGetProviderInformation(hkRoot, wzProviderKey, NULL, NULL, NULL);
979 return SUCCEEDED(hr);
980}
981
982/********************************************************************
983 CalculateDependencyActionStates - Calculates the dependency execute and
984 rollback actions for a package.
985
986*********************************************************************/
987static void CalculateDependencyActionStates(
988 __in const BURN_PACKAGE* pPackage,
989 __in const BOOTSTRAPPER_ACTION action,
990 __out BURN_DEPENDENCY_ACTION* pDependencyExecuteAction,
991 __out BURN_DEPENDENCY_ACTION* pDependencyRollbackAction
992 )
993{
994 switch (action)
995 {
996 case BOOTSTRAPPER_ACTION_UNINSTALL:
997 // Always remove the dependency when uninstalling a bundle even if the package is absent.
998 *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_UNREGISTER;
999 break;
1000 case BOOTSTRAPPER_ACTION_INSTALL: __fallthrough;
1001 case BOOTSTRAPPER_ACTION_CACHE:
1002 // Always remove the dependency during rollback when installing a bundle.
1003 *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_UNREGISTER;
1004 __fallthrough;
1005 case BOOTSTRAPPER_ACTION_MODIFY: __fallthrough;
1006 case BOOTSTRAPPER_ACTION_REPAIR:
1007 switch (pPackage->execute)
1008 {
1009 case BOOTSTRAPPER_ACTION_STATE_NONE:
1010 switch (pPackage->requested)
1011 {
1012 case BOOTSTRAPPER_REQUEST_STATE_NONE:
1013 // Register if a newer, compatible package is already installed.
1014 switch (pPackage->currentState)
1015 {
1016 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE:
1017 if (!pPackage->fPackageProviderExists)
1018 {
1019 break;
1020 }
1021 __fallthrough;
1022 case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
1023 *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER;
1024 break;
1025 }
1026 break;
1027 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
1028 case BOOTSTRAPPER_REQUEST_STATE_MEND: __fallthrough;
1029 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
1030 // Register if the package is requested but already installed.
1031 switch (pPackage->currentState)
1032 {
1033 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE:
1034 if (!pPackage->fPackageProviderExists)
1035 {
1036 break;
1037 }
1038 __fallthrough;
1039 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
1040 case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
1041 *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER;
1042 break;
1043 }
1044 break;
1045 }
1046 break;
1047 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
1048 *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_UNREGISTER;
1049 break;
1050 case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough;
1051 case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough;
1052 case BOOTSTRAPPER_ACTION_STATE_MEND: __fallthrough;
1053 case BOOTSTRAPPER_ACTION_STATE_REPAIR: __fallthrough;
1054 case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: __fallthrough;
1055 *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER;
1056 break;
1057 }
1058 break;
1059 }
1060
1061 switch (*pDependencyExecuteAction)
1062 {
1063 case BURN_DEPENDENCY_ACTION_REGISTER:
1064 switch (pPackage->currentState)
1065 {
1066 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
1067 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough;
1068 *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_UNREGISTER;
1069 break;
1070 }
1071 break;
1072 case BURN_DEPENDENCY_ACTION_UNREGISTER:
1073 switch (pPackage->currentState)
1074 {
1075 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
1076 case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
1077 *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_REGISTER;
1078 break;
1079 }
1080 break;
1081 }
1082}
1083
1084/********************************************************************
1085 AddPackageDependencyActions - Adds the dependency execute and rollback
1086 actions to the plan.
1087
1088*********************************************************************/
1089static HRESULT AddPackageDependencyActions(
1090 __in_opt DWORD *pdwInsertSequence,
1091 __in const BURN_PACKAGE* pPackage,
1092 __in BURN_PLAN* pPlan,
1093 __in const BURN_DEPENDENCY_ACTION dependencyExecuteAction,
1094 __in const BURN_DEPENDENCY_ACTION dependencyRollbackAction
1095 )
1096{
1097 HRESULT hr = S_OK;
1098 BURN_EXECUTE_ACTION* pAction = NULL;
1099
1100 // Add the rollback plan.
1101 if (BURN_DEPENDENCY_ACTION_NONE != dependencyRollbackAction)
1102 {
1103 hr = PlanAppendRollbackAction(pPlan, &pAction);
1104 ExitOnFailure(hr, "Failed to append rollback action.");
1105
1106 pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY;
1107 pAction->packageDependency.pPackage = const_cast<BURN_PACKAGE*>(pPackage);
1108 pAction->packageDependency.action = dependencyRollbackAction;
1109
1110 hr = StrAllocString(&pAction->packageDependency.sczBundleProviderKey, pPlan->wzBundleProviderKey, 0);
1111 ExitOnFailure(hr, "Failed to copy the bundle dependency provider.");
1112
1113 // Put a checkpoint before the execute action so that rollback happens
1114 // if execute fails.
1115 hr = PlanExecuteCheckpoint(pPlan);
1116 ExitOnFailure(hr, "Failed to plan dependency checkpoint action.");
1117 }
1118
1119 // Add the execute plan. This comes after rollback so if something goes wrong
1120 // rollback will try to clean up after us correctly.
1121 if (BURN_DEPENDENCY_ACTION_NONE != dependencyExecuteAction)
1122 {
1123 if (NULL != pdwInsertSequence)
1124 {
1125 hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction);
1126 ExitOnFailure(hr, "Failed to insert execute action.");
1127
1128 // Always move the sequence after this dependency action so the dependency registration
1129 // stays in front of the inserted actions.
1130 ++(*pdwInsertSequence);
1131 }
1132 else
1133 {
1134 hr = PlanAppendExecuteAction(pPlan, &pAction);
1135 ExitOnFailure(hr, "Failed to append execute action.");
1136 }
1137
1138 pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY;
1139 pAction->packageDependency.pPackage = const_cast<BURN_PACKAGE*>(pPackage);
1140 pAction->packageDependency.action = dependencyExecuteAction;
1141
1142 hr = StrAllocString(&pAction->packageDependency.sczBundleProviderKey, pPlan->wzBundleProviderKey, 0);
1143 ExitOnFailure(hr, "Failed to copy the bundle dependency provider.");
1144 }
1145
1146LExit:
1147 return hr;
1148}
1149
1150static HRESULT RegisterPackageProvider(
1151 __in const BURN_PACKAGE* pPackage
1152 )
1153{
1154 HRESULT hr = S_OK;
1155 LPWSTR wzId = NULL;
1156 HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1157
1158 if (pPackage->rgDependencyProviders)
1159 {
1160 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
1161 {
1162 wzId = pPackage->Msi.sczProductCode;
1163 }
1164 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
1165 {
1166 wzId = pPackage->Msp.sczPatchCode;
1167 }
1168
1169 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
1170 {
1171 const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
1172
1173 if (!pProvider->fImported)
1174 {
1175 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_REGISTER, pProvider->sczKey, pProvider->sczVersion, pPackage->sczId);
1176
1177 hr = DepRegisterDependency(hkRoot, pProvider->sczKey, pProvider->sczVersion, pProvider->sczDisplayName, wzId, 0);
1178 ExitOnFailure(hr, "Failed to register the package dependency provider: %ls", pProvider->sczKey);
1179 }
1180 }
1181 }
1182
1183LExit:
1184 if (!pPackage->fVital)
1185 {
1186 hr = S_OK;
1187 }
1188
1189 return hr;
1190}
1191
1192/********************************************************************
1193 UnregisterPackageProvider - Removes each dependency provider
1194 for the package (if not imported from the package itself).
1195
1196 Note: Does not check for existing dependents before removing the key.
1197*********************************************************************/
1198static void UnregisterPackageProvider(
1199 __in const BURN_PACKAGE* pPackage
1200 )
1201{
1202 HRESULT hr = S_OK;
1203 HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1204
1205 if (pPackage->rgDependencyProviders)
1206 {
1207 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
1208 {
1209 const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
1210
1211 if (!pProvider->fImported)
1212 {
1213 hr = DepUnregisterDependency(hkRoot, pProvider->sczKey);
1214 if (SUCCEEDED(hr))
1215 {
1216 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED, pProvider->sczKey, pPackage->sczId);
1217 }
1218 else if (FAILED(hr) && E_FILENOTFOUND != hr)
1219 {
1220 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_FAILED, pProvider->sczKey, pPackage->sczId, hr);
1221 }
1222 }
1223 }
1224 }
1225}
1226
1227/********************************************************************
1228 RegisterPackageDependency - Registers the provider key
1229 as a dependent of a package.
1230
1231*********************************************************************/
1232static HRESULT RegisterPackageDependency(
1233 __in BOOL fPerMachine,
1234 __in const BURN_PACKAGE* pPackage,
1235 __in_z LPCWSTR wzDependentProviderKey
1236 )
1237{
1238 HRESULT hr = S_OK;
1239 HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1240
1241 // Do not register a dependency on a package in a different install context.
1242 if (fPerMachine != pPackage->fPerMachine)
1243 {
1244 LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine));
1245 ExitFunction1(hr = S_OK);
1246 }
1247
1248 if (pPackage->rgDependencyProviders)
1249 {
1250 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
1251 {
1252 const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
1253
1254 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_REGISTER_DEPENDENCY, wzDependentProviderKey, pProvider->sczKey, pPackage->sczId);
1255
1256 hr = DepRegisterDependent(hkRoot, pProvider->sczKey, wzDependentProviderKey, NULL, NULL, 0);
1257 if (E_FILENOTFOUND != hr || pPackage->fVital)
1258 {
1259 ExitOnFailure(hr, "Failed to register the dependency on package dependency provider: %ls", pProvider->sczKey);
1260 }
1261 else
1262 {
1263 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_SKIP_MISSING, pProvider->sczKey, pPackage->sczId);
1264 hr = S_OK;
1265 }
1266 }
1267 }
1268
1269LExit:
1270 return hr;
1271}
1272
1273/********************************************************************
1274 UnregisterPackageDependency - Unregisters the provider key
1275 as a dependent of a package.
1276
1277*********************************************************************/
1278static void UnregisterPackageDependency(
1279 __in BOOL fPerMachine,
1280 __in const BURN_PACKAGE* pPackage,
1281 __in_z LPCWSTR wzDependentProviderKey
1282 )
1283{
1284 HRESULT hr = S_OK;
1285 HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1286
1287 // Should be no registration to remove since we don't write keys across contexts.
1288 if (fPerMachine != pPackage->fPerMachine)
1289 {
1290 LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine));
1291 return;
1292 }
1293
1294 // Loop through each package provider and remove the bundle dependency key.
1295 if (pPackage->rgDependencyProviders)
1296 {
1297 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
1298 {
1299 const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
1300
1301 hr = DepUnregisterDependent(hkRoot, pProvider->sczKey, wzDependentProviderKey);
1302 if (SUCCEEDED(hr))
1303 {
1304 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY, wzDependentProviderKey, pProvider->sczKey, pPackage->sczId);
1305 }
1306 else if (FAILED(hr) && E_FILENOTFOUND != hr)
1307 {
1308 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY_FAILED, wzDependentProviderKey, pProvider->sczKey, pPackage->sczId, hr);
1309 }
1310 }
1311 }
1312}
diff --git a/src/burn/engine/dependency.h b/src/burn/engine/dependency.h
new file mode 100644
index 00000000..06a01a20
--- /dev/null
+++ b/src/burn/engine/dependency.h
@@ -0,0 +1,168 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9// constants
10
11const LPCWSTR DEPENDENCY_IGNOREDEPENDENCIES = L"IGNOREDEPENDENCIES";
12
13
14// function declarations
15
16/********************************************************************
17 DependencyUninitializeProvider - Frees and zeros memory allocated in
18 the dependency provider.
19
20*********************************************************************/
21void DependencyUninitializeProvider(
22 __in BURN_DEPENDENCY_PROVIDER* pProvider
23 );
24
25/********************************************************************
26 DependencyParseProvidersFromXml - Parses dependency information
27 from the manifest for the specified package.
28
29*********************************************************************/
30HRESULT DependencyParseProvidersFromXml(
31 __in BURN_PACKAGE* pPackage,
32 __in IXMLDOMNode* pixnPackage
33 );
34
35HRESULT DependencyInitialize(
36 __in BURN_REGISTRATION* pRegistration,
37 __in_z_opt LPCWSTR wzIgnoreDependencies
38 );
39
40/********************************************************************
41 DependencyDetectProviderKeyBundleId - Detect if the provider key is
42 registered and if so what bundle is registered.
43
44 Note: Returns E_NOTFOUND if the provider key is not registered.
45*********************************************************************/
46HRESULT DependencyDetectProviderKeyBundleId(
47 __in BURN_REGISTRATION* pRegistration
48 );
49
50/********************************************************************
51 DependencyDetect - Detects dependency information.
52
53*********************************************************************/
54HRESULT DependencyDetect(
55 __in BURN_ENGINE_STATE* pEngineState
56 );
57
58/********************************************************************
59 DependencyPlanInitialize - Initializes the plan.
60
61*********************************************************************/
62HRESULT DependencyPlanInitialize(
63 __in const BURN_REGISTRATION* pRegistration,
64 __in BURN_PLAN* pPlan
65 );
66
67/********************************************************************
68 DependencyAllocIgnoreDependencies - Allocates the dependencies to
69 ignore as a semicolon-delimited string.
70
71*********************************************************************/
72HRESULT DependencyAllocIgnoreDependencies(
73 __in const BURN_PLAN *pPlan,
74 __out_z LPWSTR* psczIgnoreDependencies
75 );
76
77/********************************************************************
78 DependencyAddIgnoreDependencies - Populates the ignore dependency
79 names.
80
81*********************************************************************/
82HRESULT DependencyAddIgnoreDependencies(
83 __in STRINGDICT_HANDLE sdIgnoreDependencies,
84 __in_z LPCWSTR wzAddIgnoreDependencies
85 );
86
87/********************************************************************
88 DependencyPlanPackageBegin - Updates the dependency registration
89 action depending on the calculated state for the package.
90
91*********************************************************************/
92HRESULT DependencyPlanPackageBegin(
93 __in BOOL fPerMachine,
94 __in BURN_PACKAGE* pPackage,
95 __in BURN_PLAN* pPlan
96 );
97
98/********************************************************************
99 DependencyPlanPackage - adds dependency related actions to the plan
100 for this package.
101
102*********************************************************************/
103HRESULT DependencyPlanPackage(
104 __in_opt DWORD *pdwInsertSequence,
105 __in const BURN_PACKAGE* pPackage,
106 __in BURN_PLAN* pPlan
107 );
108
109/********************************************************************
110 DependencyPlanPackageComplete - Updates the dependency registration
111 action depending on the planned action for the package.
112
113*********************************************************************/
114HRESULT DependencyPlanPackageComplete(
115 __in BURN_PACKAGE* pPackage,
116 __in BURN_PLAN* pPlan
117 );
118
119/********************************************************************
120 DependencyExecutePackageProviderAction - Registers or unregisters
121 provider information for the package contained within the action.
122
123*********************************************************************/
124HRESULT DependencyExecutePackageProviderAction(
125 __in const BURN_EXECUTE_ACTION* pAction
126 );
127
128/********************************************************************
129 DependencyExecutePackageDependencyAction - Registers or unregisters
130 dependency information for the package contained within the action.
131
132*********************************************************************/
133HRESULT DependencyExecutePackageDependencyAction(
134 __in BOOL fPerMachine,
135 __in const BURN_EXECUTE_ACTION* pAction
136 );
137
138/********************************************************************
139 DependencyRegisterBundle - Registers the bundle dependency provider.
140
141*********************************************************************/
142HRESULT DependencyRegisterBundle(
143 __in const BURN_REGISTRATION* pRegistration
144 );
145
146/********************************************************************
147 DependencyProcessDependentRegistration - Registers or unregisters dependents
148 on the bundle based on the action.
149
150*********************************************************************/
151HRESULT DependencyProcessDependentRegistration(
152 __in const BURN_REGISTRATION* pRegistration,
153 __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction
154 );
155
156/********************************************************************
157 DependencyUnregisterBundle - Removes the bundle dependency provider.
158
159 Note: Does not check for existing dependents before removing the key.
160*********************************************************************/
161void DependencyUnregisterBundle(
162 __in const BURN_REGISTRATION* pRegistration,
163 __in const BURN_PACKAGES* pPackages
164 );
165
166#if defined(__cplusplus)
167}
168#endif
diff --git a/src/burn/engine/detect.cpp b/src/burn/engine/detect.cpp
new file mode 100644
index 00000000..dc35e747
--- /dev/null
+++ b/src/burn/engine/detect.cpp
@@ -0,0 +1,469 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5typedef struct _DETECT_AUTHENTICATION_REQUIRED_DATA
6{
7 BURN_USER_EXPERIENCE* pUX;
8 LPCWSTR wzPackageOrContainerId;
9} DETECT_AUTHENTICATION_REQUIRED_DATA;
10
11// internal function definitions
12static HRESULT WINAPI AuthenticationRequired(
13 __in LPVOID pData,
14 __in HINTERNET hUrl,
15 __in long lHttpCode,
16 __out BOOL* pfRetrySend,
17 __out BOOL* pfRetry
18 );
19
20static HRESULT DetectAtomFeedUpdate(
21 __in_z LPCWSTR wzBundleId,
22 __in BURN_USER_EXPERIENCE* pUX,
23 __in BURN_UPDATE* pUpdate
24 );
25
26static HRESULT DownloadUpdateFeed(
27 __in_z LPCWSTR wzBundleId,
28 __in BURN_USER_EXPERIENCE* pUX,
29 __in BURN_UPDATE* pUpdate,
30 __deref_inout_z LPWSTR* psczTempFile
31 );
32
33// function definitions
34
35extern "C" void DetectReset(
36 __in BURN_REGISTRATION* pRegistration,
37 __in BURN_PACKAGES* pPackages
38 )
39{
40 RelatedBundlesUninitialize(&pRegistration->relatedBundles);
41 ReleaseNullStr(pRegistration->sczDetectedProviderKeyBundleId);
42 pRegistration->fSelfRegisteredAsDependent = FALSE;
43 pRegistration->fParentRegisteredAsDependent = FALSE;
44 pRegistration->fForwardCompatibleBundleExists = FALSE;
45 pRegistration->fEligibleForCleanup = FALSE;
46
47 if (pRegistration->rgIgnoredDependencies)
48 {
49 ReleaseDependencyArray(pRegistration->rgIgnoredDependencies, pRegistration->cIgnoredDependencies);
50 }
51 pRegistration->rgIgnoredDependencies = NULL;
52 pRegistration->cIgnoredDependencies = 0;
53
54 if (pRegistration->rgDependents)
55 {
56 ReleaseDependencyArray(pRegistration->rgDependents, pRegistration->cDependents);
57 }
58 pRegistration->rgDependents = NULL;
59 pRegistration->cDependents = 0;
60
61 for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage)
62 {
63 BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage;
64
65 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN;
66 pPackage->fPackageProviderExists = FALSE;
67 pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
68 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
69
70 pPackage->fCached = FALSE;
71
72 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
73 {
74 for (DWORD iFeature = 0; iFeature < pPackage->Msi.cFeatures; ++iFeature)
75 {
76 BURN_MSIFEATURE* pFeature = pPackage->Msi.rgFeatures + iFeature;
77
78 pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
79 }
80
81 for (DWORD iSlipstreamMsp = 0; iSlipstreamMsp < pPackage->Msi.cSlipstreamMspPackages; ++iSlipstreamMsp)
82 {
83 BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + iSlipstreamMsp;
84
85 pSlipstreamMsp->dwMsiChainedPatchIndex = BURN_PACKAGE_INVALID_PATCH_INDEX;
86 }
87
88 ReleaseNullMem(pPackage->Msi.rgChainedPatches);
89 pPackage->Msi.cChainedPatches = 0;
90 }
91 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
92 {
93 ReleaseNullMem(pPackage->Msp.rgTargetProducts);
94 pPackage->Msp.cTargetProductCodes = 0;
95 }
96
97 for (DWORD iProvider = 0; iProvider < pPackage->cDependencyProviders; ++iProvider)
98 {
99 BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + iProvider;
100
101 if (pProvider->rgDependents)
102 {
103 ReleaseDependencyArray(pProvider->rgDependents, pProvider->cDependents);
104 }
105 pProvider->rgDependents = NULL;
106 pProvider->cDependents = 0;
107 }
108 }
109
110 for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo)
111 {
112 MSIPATCHSEQUENCEINFOW* pPatchInfo = pPackages->rgPatchInfo + iPatchInfo;
113 pPatchInfo->dwOrder = 0;
114 pPatchInfo->uStatus = 0;
115 }
116}
117
118extern "C" HRESULT DetectForwardCompatibleBundles(
119 __in BURN_USER_EXPERIENCE* pUX,
120 __in BURN_REGISTRATION* pRegistration
121 )
122{
123 HRESULT hr = S_OK;
124 int nCompareResult = 0;
125
126 if (pRegistration->sczDetectedProviderKeyBundleId &&
127 CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRegistration->sczId, -1))
128 {
129 for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle)
130 {
131 BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle;
132
133 if (BOOTSTRAPPER_RELATION_UPGRADE == pRelatedBundle->relationType &&
134 CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRelatedBundle->package.sczId, -1))
135 {
136 hr = VerCompareParsedVersions(pRegistration->pVersion, pRelatedBundle->pVersion, &nCompareResult);
137 ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistration->pVersion->sczVersion, pRelatedBundle->pVersion->sczVersion);
138
139 if (nCompareResult <= 0)
140 {
141 if (pRelatedBundle->fPlannable)
142 {
143 pRelatedBundle->fForwardCompatible = TRUE;
144 pRegistration->fForwardCompatibleBundleExists = TRUE;
145 }
146
147 hr = UserExperienceOnDetectForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, !pRelatedBundle->package.fCached);
148 ExitOnRootFailure(hr, "BA aborted detect forward compatible bundle.");
149
150 LogId(REPORT_STANDARD, MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingBoolToString(pRelatedBundle->package.fCached));
151 }
152 }
153 }
154 }
155
156LExit:
157 return hr;
158}
159
160extern "C" HRESULT DetectReportRelatedBundles(
161 __in BURN_USER_EXPERIENCE* pUX,
162 __in BURN_REGISTRATION* pRegistration,
163 __in BOOTSTRAPPER_RELATION_TYPE relationType,
164 __in BOOTSTRAPPER_ACTION action,
165 __out BOOL* pfEligibleForCleanup
166 )
167{
168 HRESULT hr = S_OK;
169 int nCompareResult = 0;
170 BOOTSTRAPPER_REQUEST_STATE uninstallRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
171 *pfEligibleForCleanup = pRegistration->fInstalled || pRegistration->fCached;
172
173 for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle)
174 {
175 const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle;
176 BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
177
178 switch (pRelatedBundle->relationType)
179 {
180 case BOOTSTRAPPER_RELATION_UPGRADE:
181 if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < action)
182 {
183 hr = VerCompareParsedVersions(pRegistration->pVersion, pRelatedBundle->pVersion, &nCompareResult);
184 ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistration->pVersion->sczVersion, pRelatedBundle->pVersion->sczVersion);
185
186 if (nCompareResult < 0)
187 {
188 operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE;
189 }
190 else
191 {
192 operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE;
193 }
194 }
195 break;
196
197 case BOOTSTRAPPER_RELATION_PATCH: __fallthrough;
198 case BOOTSTRAPPER_RELATION_ADDON:
199 if (BOOTSTRAPPER_ACTION_UNINSTALL == action)
200 {
201 operation = BOOTSTRAPPER_RELATED_OPERATION_REMOVE;
202 }
203 else if (BOOTSTRAPPER_ACTION_INSTALL == action || BOOTSTRAPPER_ACTION_MODIFY == action)
204 {
205 operation = BOOTSTRAPPER_RELATED_OPERATION_INSTALL;
206 }
207 else if (BOOTSTRAPPER_ACTION_REPAIR == action)
208 {
209 operation = BOOTSTRAPPER_RELATED_OPERATION_REPAIR;
210 }
211 break;
212
213 case BOOTSTRAPPER_RELATION_DETECT: __fallthrough;
214 case BOOTSTRAPPER_RELATION_DEPENDENT:
215 break;
216
217 default:
218 hr = E_FAIL;
219 ExitOnRootFailure(hr, "Unexpected relation type encountered: %d", pRelatedBundle->relationType);
220 break;
221 }
222
223 LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingRelatedOperationToString(operation), LoggingBoolToString(pRelatedBundle->package.fCached));
224
225 hr = UserExperienceOnDetectRelatedBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, operation, !pRelatedBundle->package.fCached);
226 ExitOnRootFailure(hr, "BA aborted detect related bundle.");
227
228 // For now, if any related bundles will be executed during uninstall by default then never automatically clean up the bundle.
229 if (*pfEligibleForCleanup && pRelatedBundle->fPlannable)
230 {
231 uninstallRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
232 hr = PlanDefaultRelatedBundleRequestState(relationType, pRelatedBundle->relationType, BOOTSTRAPPER_ACTION_UNINSTALL, pRegistration->pVersion, pRelatedBundle->pVersion, &uninstallRequestState);
233 ExitOnFailure(hr, "Failed to get the default request state for related bundle for calculating fEligibleForCleanup");
234
235 if (BOOTSTRAPPER_REQUEST_STATE_NONE != uninstallRequestState)
236 {
237 *pfEligibleForCleanup = FALSE;
238 }
239 }
240 }
241
242LExit:
243 return hr;
244}
245
246extern "C" HRESULT DetectUpdate(
247 __in_z LPCWSTR wzBundleId,
248 __in BURN_USER_EXPERIENCE* pUX,
249 __in BURN_UPDATE* pUpdate
250 )
251{
252 HRESULT hr = S_OK;
253 BOOL fBeginCalled = FALSE;
254 BOOL fSkip = TRUE;
255 BOOL fIgnoreError = FALSE;
256 LPWSTR sczOriginalSource = NULL;
257
258 // If no update source was specified, skip update detection.
259 if (!pUpdate->sczUpdateSource || !*pUpdate->sczUpdateSource)
260 {
261 ExitFunction();
262 }
263
264 fBeginCalled = TRUE;
265
266 hr = StrAllocString(&sczOriginalSource, pUpdate->sczUpdateSource, 0);
267 ExitOnFailure(hr, "Failed to duplicate update feed source.");
268
269 hr = UserExperienceOnDetectUpdateBegin(pUX, sczOriginalSource, &fSkip);
270 ExitOnRootFailure(hr, "BA aborted detect update begin.");
271
272 if (!fSkip)
273 {
274 hr = DetectAtomFeedUpdate(wzBundleId, pUX, pUpdate);
275 ExitOnFailure(hr, "Failed to detect atom feed update.");
276 }
277
278LExit:
279 ReleaseStr(sczOriginalSource);
280
281 if (fBeginCalled)
282 {
283 UserExperienceOnDetectUpdateComplete(pUX, hr, &fIgnoreError);
284 if (fIgnoreError)
285 {
286 hr = S_OK;
287 }
288 }
289
290 return hr;
291}
292
293static HRESULT WINAPI AuthenticationRequired(
294 __in LPVOID pData,
295 __in HINTERNET hUrl,
296 __in long lHttpCode,
297 __out BOOL* pfRetrySend,
298 __out BOOL* pfRetry
299 )
300{
301 Assert(401 == lHttpCode || 407 == lHttpCode);
302
303 HRESULT hr = S_OK;
304 DWORD er = ERROR_SUCCESS;
305 BOOTSTRAPPER_ERROR_TYPE errorType = (401 == lHttpCode) ? BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER : BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY;
306 LPWSTR sczError = NULL;
307 DETECT_AUTHENTICATION_REQUIRED_DATA* pAuthenticationData = reinterpret_cast<DETECT_AUTHENTICATION_REQUIRED_DATA*>(pData);
308 int nResult = IDNOACTION;
309
310 *pfRetrySend = FALSE;
311 *pfRetry = FALSE;
312
313 hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL);
314 ExitOnFailure(hr, "Failed to allocation error string.");
315
316 UserExperienceOnError(pAuthenticationData->pUX, errorType, pAuthenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value.
317 nResult = UserExperienceCheckExecuteResult(pAuthenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult);
318 if (IDTRYAGAIN == nResult && pAuthenticationData->pUX->hwndDetect)
319 {
320 er = ::InternetErrorDlg(pAuthenticationData->pUX->hwndDetect, hUrl, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL);
321 if (ERROR_SUCCESS == er || ERROR_CANCELLED == er)
322 {
323 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
324 }
325 else if (ERROR_INTERNET_FORCE_RETRY == er)
326 {
327 *pfRetrySend = TRUE;
328 hr = S_OK;
329 }
330 else
331 {
332 hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
333 }
334 }
335 else if (IDRETRY == nResult)
336 {
337 *pfRetry = TRUE;
338 hr = S_OK;
339 }
340 else
341 {
342 hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
343 }
344
345LExit:
346 ReleaseStr(sczError);
347
348 return hr;
349}
350
351static HRESULT DownloadUpdateFeed(
352 __in_z LPCWSTR wzBundleId,
353 __in BURN_USER_EXPERIENCE* pUX,
354 __in BURN_UPDATE* pUpdate,
355 __deref_inout_z LPWSTR* psczTempFile
356 )
357{
358 HRESULT hr = S_OK;
359 DOWNLOAD_SOURCE downloadSource = { };
360 DOWNLOAD_CACHE_CALLBACK cacheCallback = { };
361 DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { };
362 DETECT_AUTHENTICATION_REQUIRED_DATA authenticationData = { };
363 LPWSTR sczUpdateId = NULL;
364 LPWSTR sczError = NULL;
365 DWORD64 qwDownloadSize = 0;
366
367 // Always do our work in the working folder, even if cached.
368 hr = PathCreateTimeBasedTempFile(NULL, L"UpdateFeed", NULL, L"xml", psczTempFile, NULL);
369 ExitOnFailure(hr, "Failed to create UpdateFeed based on current system time.");
370
371 // Do we need a means of the BA to pass in a user name and password? If so, we should copy it to downloadSource here
372 hr = StrAllocString(&downloadSource.sczUrl, pUpdate->sczUpdateSource, 0);
373 ExitOnFailure(hr, "Failed to copy update url.");
374
375 cacheCallback.pfnProgress = NULL; //UpdateProgressRoutine;
376 cacheCallback.pfnCancel = NULL; // TODO: set this
377 cacheCallback.pv = NULL; //pProgress;
378
379 authenticationData.pUX = pUX;
380 authenticationData.wzPackageOrContainerId = wzBundleId;
381
382 authenticationCallback.pv = static_cast<LPVOID>(&authenticationData);
383 authenticationCallback.pfnAuthenticate = &AuthenticationRequired;
384
385 hr = DownloadUrl(&downloadSource, qwDownloadSize, *psczTempFile, &cacheCallback, &authenticationCallback);
386 ExitOnFailure(hr, "Failed attempt to download update feed from URL: '%ls' to: '%ls'", downloadSource.sczUrl, *psczTempFile);
387
388LExit:
389 if (FAILED(hr))
390 {
391 if (*psczTempFile)
392 {
393 FileEnsureDelete(*psczTempFile);
394 }
395
396 ReleaseNullStr(*psczTempFile);
397 }
398
399 ReleaseStr(downloadSource.sczUrl);
400 ReleaseStr(downloadSource.sczUser);
401 ReleaseStr(downloadSource.sczPassword);
402 ReleaseStr(sczUpdateId);
403 ReleaseStr(sczError);
404 return hr;
405}
406
407
408static HRESULT DetectAtomFeedUpdate(
409 __in_z LPCWSTR wzBundleId,
410 __in BURN_USER_EXPERIENCE* pUX,
411 __in BURN_UPDATE* pUpdate
412 )
413{
414 Assert(pUpdate && pUpdate->sczUpdateSource && *pUpdate->sczUpdateSource);
415#ifdef DEBUG
416 LogStringLine(REPORT_STANDARD, "DetectAtomFeedUpdate() - update location: %ls", pUpdate->sczUpdateSource);
417#endif
418
419
420 HRESULT hr = S_OK;
421 LPWSTR sczUpdateFeedTempFile = NULL;
422 ATOM_FEED* pAtomFeed = NULL;
423 APPLICATION_UPDATE_CHAIN* pApupChain = NULL;
424 BOOL fStopProcessingUpdates = FALSE;
425
426 hr = AtomInitialize();
427 ExitOnFailure(hr, "Failed to initialize Atom.");
428
429 hr = DownloadUpdateFeed(wzBundleId, pUX, pUpdate, &sczUpdateFeedTempFile);
430 ExitOnFailure(hr, "Failed to download update feed.");
431
432 hr = AtomParseFromFile(sczUpdateFeedTempFile, &pAtomFeed);
433 ExitOnFailure(hr, "Failed to parse update atom feed: %ls.", sczUpdateFeedTempFile);
434
435 hr = ApupAllocChainFromAtom(pAtomFeed, &pApupChain);
436 ExitOnFailure(hr, "Failed to allocate update chain from atom feed.");
437
438 if (0 < pApupChain->cEntries)
439 {
440 for (DWORD i = 0; i < pApupChain->cEntries; ++i)
441 {
442 APPLICATION_UPDATE_ENTRY* pAppUpdateEntry = &pApupChain->rgEntries[i];
443
444 hr = UserExperienceOnDetectUpdate(pUX, pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->wzUrl : NULL,
445 pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->dw64Size : 0,
446 pAppUpdateEntry->pVersion, pAppUpdateEntry->wzTitle,
447 pAppUpdateEntry->wzSummary, pAppUpdateEntry->wzContentType, pAppUpdateEntry->wzContent, &fStopProcessingUpdates);
448 ExitOnRootFailure(hr, "BA aborted detect update.");
449
450 if (fStopProcessingUpdates)
451 {
452 break;
453 }
454 }
455 }
456
457LExit:
458 if (sczUpdateFeedTempFile && *sczUpdateFeedTempFile)
459 {
460 FileEnsureDelete(sczUpdateFeedTempFile);
461 }
462
463 ApupFreeChain(pApupChain);
464 AtomFreeFeed(pAtomFeed);
465 ReleaseStr(sczUpdateFeedTempFile);
466 AtomUninitialize();
467
468 return hr;
469}
diff --git a/src/burn/engine/detect.h b/src/burn/engine/detect.h
new file mode 100644
index 00000000..9bc34882
--- /dev/null
+++ b/src/burn/engine/detect.h
@@ -0,0 +1,44 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// constants
11
12
13// structs
14
15
16// functions
17
18void DetectReset(
19 __in BURN_REGISTRATION* pRegistration,
20 __in BURN_PACKAGES* pPackages
21 );
22
23HRESULT DetectForwardCompatibleBundles(
24 __in BURN_USER_EXPERIENCE* pUX,
25 __in BURN_REGISTRATION* pRegistration
26 );
27
28HRESULT DetectReportRelatedBundles(
29 __in BURN_USER_EXPERIENCE* pUX,
30 __in BURN_REGISTRATION* pRegistration,
31 __in BOOTSTRAPPER_RELATION_TYPE relationType,
32 __in BOOTSTRAPPER_ACTION action,
33 __out BOOL* pfEligibleForCleanup
34 );
35
36HRESULT DetectUpdate(
37 __in_z LPCWSTR wzBundleId,
38 __in BURN_USER_EXPERIENCE* pUX,
39 __in BURN_UPDATE* pUpdate
40 );
41
42#if defined(__cplusplus)
43}
44#endif
diff --git a/src/burn/engine/elevation.cpp b/src/burn/engine/elevation.cpp
new file mode 100644
index 00000000..9d1b8fc7
--- /dev/null
+++ b/src/burn/engine/elevation.cpp
@@ -0,0 +1,3239 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6const DWORD BURN_TIMEOUT = 5 * 60 * 1000; // TODO: is 5 minutes good?
7
8typedef enum _BURN_ELEVATION_MESSAGE_TYPE
9{
10 BURN_ELEVATION_MESSAGE_TYPE_UNKNOWN,
11 BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE,
12 BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE,
13 BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN,
14 BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME,
15 BURN_ELEVATION_MESSAGE_TYPE_SESSION_END,
16 BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE,
17 BURN_ELEVATION_MESSAGE_TYPE_CACHE_COMPLETE_PAYLOAD,
18 BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_PAYLOAD,
19 BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP,
20 BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION,
21 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE,
22 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE,
23 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE,
24 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE,
25 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER,
26 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY,
27 BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_EMBEDDED_CHILD,
28 BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE,
29 BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE,
30 BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION,
31 BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION,
32 BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION,
33
34 BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_BEGIN,
35 BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_COMPLETE,
36 BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_BEGIN,
37 BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_COMPLETE,
38 BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_BEGIN,
39 BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_COMPLETE,
40 BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_SUCCESS,
41 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS,
42 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR,
43 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE,
44 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE,
45 BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID,
46 BURN_ELEVATION_MESSAGE_TYPE_PROGRESS_ROUTINE,
47} BURN_ELEVATION_MESSAGE_TYPE;
48
49
50// struct
51
52typedef struct _BURN_ELEVATION_APPLY_INITIALIZE_MESSAGE_CONTEXT
53{
54 BURN_USER_EXPERIENCE* pBA;
55 BOOL fPauseCompleteNeeded;
56 BOOL fSrpCompleteNeeded;
57} BURN_ELEVATION_APPLY_INITIALIZE_MESSAGE_CONTEXT;
58
59typedef struct _BURN_ELEVATION_CACHE_MESSAGE_CONTEXT
60{
61 PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler;
62 LPPROGRESS_ROUTINE pfnProgress;
63 LPVOID pvContext;
64} BURN_ELEVATION_CACHE_MESSAGE_CONTEXT;
65
66typedef struct _BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT
67{
68 PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler;
69 LPVOID pvContext;
70} BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT;
71
72typedef struct _BURN_ELEVATION_MSI_MESSAGE_CONTEXT
73{
74 PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler;
75 LPVOID pvContext;
76} BURN_ELEVATION_MSI_MESSAGE_CONTEXT;
77
78typedef struct _BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT
79{
80 DWORD dwProcessId;
81} BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT;
82
83typedef struct _BURN_ELEVATION_CHILD_MESSAGE_CONTEXT
84{
85 DWORD dwLoggingTlsId;
86 HANDLE hPipe;
87 HANDLE* phLock;
88 BOOL* pfDisabledAutomaticUpdates;
89 BURN_APPROVED_EXES* pApprovedExes;
90 BURN_CONTAINERS* pContainers;
91 BURN_PACKAGES* pPackages;
92 BURN_PAYLOADS* pPayloads;
93 BURN_VARIABLES* pVariables;
94 BURN_REGISTRATION* pRegistration;
95 BURN_USER_EXPERIENCE* pUserExperience;
96} BURN_ELEVATION_CHILD_MESSAGE_CONTEXT;
97
98
99// internal function declarations
100
101static DWORD WINAPI ElevatedChildCacheThreadProc(
102 __in LPVOID lpThreadParameter
103 );
104static HRESULT WaitForElevatedChildCacheThread(
105 __in HANDLE hCacheThread,
106 __in DWORD dwExpectedExitCode
107 );
108static HRESULT ProcessApplyInitializeMessages(
109 __in BURN_PIPE_MESSAGE* pMsg,
110 __in_opt LPVOID pvContext,
111 __out DWORD* pdwResult
112 );
113static HRESULT ProcessBurnCacheMessages(
114 __in BURN_PIPE_MESSAGE* pMsg,
115 __in LPVOID pvContext,
116 __out DWORD* pdwResult
117 );
118static HRESULT ProcessGenericExecuteMessages(
119 __in BURN_PIPE_MESSAGE* pMsg,
120 __in LPVOID pvContext,
121 __out DWORD* pdwResult
122 );
123static HRESULT ProcessMsiPackageMessages(
124 __in BURN_PIPE_MESSAGE* pMsg,
125 __in_opt LPVOID pvContext,
126 __out DWORD* pdwResult
127 );
128static HRESULT ProcessLaunchApprovedExeMessages(
129 __in BURN_PIPE_MESSAGE* pMsg,
130 __in_opt LPVOID pvContext,
131 __out DWORD* pdwResult
132 );
133static HRESULT ProcessProgressRoutineMessage(
134 __in BURN_PIPE_MESSAGE* pMsg,
135 __in LPPROGRESS_ROUTINE pfnProgress,
136 __in LPVOID pvContext,
137 __out DWORD* pdwResult
138 );
139static HRESULT ProcessElevatedChildMessage(
140 __in BURN_PIPE_MESSAGE* pMsg,
141 __in_opt LPVOID pvContext,
142 __out DWORD* pdwResult
143 );
144static HRESULT ProcessElevatedChildCacheMessage(
145 __in BURN_PIPE_MESSAGE* pMsg,
146 __in_opt LPVOID pvContext,
147 __out DWORD* pdwResult
148 );
149static HRESULT ProcessResult(
150 __in DWORD dwResult,
151 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
152 );
153static HRESULT OnApplyInitialize(
154 __in HANDLE hPipe,
155 __in BURN_VARIABLES* pVariables,
156 __in BURN_REGISTRATION* pRegistration,
157 __in HANDLE* phLock,
158 __in BOOL* pfDisabledWindowsUpdate,
159 __in BYTE* pbData,
160 __in SIZE_T cbData
161 );
162static HRESULT OnApplyUninitialize(
163 __in HANDLE* phLock
164 );
165static HRESULT OnSessionBegin(
166 __in BURN_REGISTRATION* pRegistration,
167 __in BURN_VARIABLES* pVariables,
168 __in BYTE* pbData,
169 __in SIZE_T cbData
170 );
171static HRESULT OnSessionResume(
172 __in BURN_REGISTRATION* pRegistration,
173 __in BURN_VARIABLES* pVariables,
174 __in BYTE* pbData,
175 __in SIZE_T cbData
176 );
177static HRESULT OnSessionEnd(
178 __in BURN_PACKAGES* pPackages,
179 __in BURN_REGISTRATION* pRegistration,
180 __in BURN_VARIABLES* pVariables,
181 __in BYTE* pbData,
182 __in SIZE_T cbData
183 );
184static HRESULT OnSaveState(
185 __in BURN_REGISTRATION* pRegistration,
186 __in BYTE* pbData,
187 __in SIZE_T cbData
188 );
189static HRESULT OnCacheCompletePayload(
190 __in HANDLE hPipe,
191 __in BURN_PACKAGES* pPackages,
192 __in BURN_PAYLOADS* pPayloads,
193 __in BYTE* pbData,
194 __in SIZE_T cbData
195 );
196static HRESULT OnCacheVerifyPayload(
197 __in HANDLE hPipe,
198 __in BURN_PACKAGES* pPackages,
199 __in BURN_PAYLOADS* pPayloads,
200 __in BYTE* pbData,
201 __in SIZE_T cbData
202 );
203static void OnCacheCleanup(
204 __in_z LPCWSTR wzBundleId
205 );
206static HRESULT OnProcessDependentRegistration(
207 __in const BURN_REGISTRATION* pRegistration,
208 __in BYTE* pbData,
209 __in SIZE_T cbData
210 );
211static HRESULT OnExecuteExePackage(
212 __in HANDLE hPipe,
213 __in BURN_PACKAGES* pPackages,
214 __in BURN_RELATED_BUNDLES* pRelatedBundles,
215 __in BURN_VARIABLES* pVariables,
216 __in BYTE* pbData,
217 __in SIZE_T cbData
218 );
219static HRESULT OnExecuteMsiPackage(
220 __in HANDLE hPipe,
221 __in BURN_PACKAGES* pPackages,
222 __in BURN_VARIABLES* pVariables,
223 __in BYTE* pbData,
224 __in SIZE_T cbData
225 );
226static HRESULT OnExecuteMspPackage(
227 __in HANDLE hPipe,
228 __in BURN_PACKAGES* pPackages,
229 __in BURN_VARIABLES* pVariables,
230 __in BYTE* pbData,
231 __in SIZE_T cbData
232 );
233static HRESULT OnExecuteMsuPackage(
234 __in HANDLE hPipe,
235 __in BURN_PACKAGES* pPackages,
236 __in BURN_VARIABLES* pVariables,
237 __in BYTE* pbData,
238 __in SIZE_T cbData
239 );
240static HRESULT OnExecutePackageProviderAction(
241 __in BURN_PACKAGES* pPackages,
242 __in BURN_RELATED_BUNDLES* pRelatedBundles,
243 __in BYTE* pbData,
244 __in SIZE_T cbData
245 );
246static HRESULT OnExecutePackageDependencyAction(
247 __in BURN_PACKAGES* pPackages,
248 __in BURN_RELATED_BUNDLES* pRelatedBundles,
249 __in BYTE* pbData,
250 __in SIZE_T cbData
251 );
252static HRESULT CALLBACK BurnCacheMessageHandler(
253 __in BURN_CACHE_MESSAGE* pMessage,
254 __in LPVOID pvContext
255 );
256static DWORD CALLBACK ElevatedProgressRoutine(
257 __in LARGE_INTEGER TotalFileSize,
258 __in LARGE_INTEGER TotalBytesTransferred,
259 __in LARGE_INTEGER StreamSize,
260 __in LARGE_INTEGER StreamBytesTransferred,
261 __in DWORD dwStreamNumber,
262 __in DWORD dwCallbackReason,
263 __in HANDLE hSourceFile,
264 __in HANDLE hDestinationFile,
265 __in_opt LPVOID lpData
266 );
267static int GenericExecuteMessageHandler(
268 __in GENERIC_EXECUTE_MESSAGE* pMessage,
269 __in LPVOID pvContext
270 );
271static int MsiExecuteMessageHandler(
272 __in WIU_MSI_EXECUTE_MESSAGE* pMessage,
273 __in_opt LPVOID pvContext
274 );
275static HRESULT OnCleanPackage(
276 __in BURN_PACKAGES* pPackages,
277 __in BYTE* pbData,
278 __in SIZE_T cbData
279 );
280static HRESULT OnLaunchApprovedExe(
281 __in HANDLE hPipe,
282 __in BURN_APPROVED_EXES* pApprovedExes,
283 __in BURN_VARIABLES* pVariables,
284 __in BYTE* pbData,
285 __in SIZE_T cbData
286 );
287static HRESULT OnMsiBeginTransaction(
288 __in BURN_PACKAGES* pPackages,
289 __in BYTE* pbData,
290 __in SIZE_T cbData
291 );
292static HRESULT OnMsiCommitTransaction(
293 __in BURN_PACKAGES* pPackages,
294 __in BYTE* pbData,
295 __in SIZE_T cbData
296 );
297static HRESULT OnMsiRollbackTransaction(
298 __in BURN_PACKAGES* pPackages,
299 __in BYTE* pbData,
300 __in SIZE_T cbData
301 );
302static HRESULT ElevatedOnPauseAUBegin(
303 __in HANDLE hPipe
304 );
305static HRESULT ElevatedOnPauseAUComplete(
306 __in HANDLE hPipe,
307 __in HRESULT hrStatus
308 );
309static HRESULT ElevatedOnSystemRestorePointBegin(
310 __in HANDLE hPipe
311 );
312static HRESULT ElevatedOnSystemRestorePointComplete(
313 __in HANDLE hPipe,
314 __in HRESULT hrStatus
315 );
316
317
318// function definitions
319
320extern "C" HRESULT ElevationElevate(
321 __in BURN_ENGINE_STATE* pEngineState,
322 __in_opt HWND hwndParent
323 )
324{
325 Assert(BURN_MODE_ELEVATED != pEngineState->mode);
326 Assert(!pEngineState->companionConnection.sczName);
327 Assert(!pEngineState->companionConnection.sczSecret);
328 Assert(!pEngineState->companionConnection.hProcess);
329 Assert(!pEngineState->companionConnection.dwProcessId);
330 Assert(INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe);
331 Assert(INVALID_HANDLE_VALUE == pEngineState->companionConnection.hCachePipe);
332
333 HRESULT hr = S_OK;
334 int nResult = IDOK;
335 HANDLE hPipesCreatedEvent = INVALID_HANDLE_VALUE;
336
337 hr = UserExperienceOnElevateBegin(&pEngineState->userExperience);
338 ExitOnRootFailure(hr, "BA aborted elevation requirement.");
339
340 hr = PipeCreateNameAndSecret(&pEngineState->companionConnection.sczName, &pEngineState->companionConnection.sczSecret);
341 ExitOnFailure(hr, "Failed to create pipe name and client token.");
342
343 hr = PipeCreatePipes(&pEngineState->companionConnection, TRUE, &hPipesCreatedEvent);
344 ExitOnFailure(hr, "Failed to create pipe and cache pipe.");
345
346 LogId(REPORT_STANDARD, MSG_LAUNCH_ELEVATED_ENGINE_STARTING);
347
348 do
349 {
350 nResult = IDOK;
351
352 // Create the elevated process and if successful, wait for it to connect.
353 hr = PipeLaunchChildProcess(pEngineState->sczBundleEngineWorkingPath, &pEngineState->companionConnection, TRUE, hwndParent);
354 if (SUCCEEDED(hr))
355 {
356 LogId(REPORT_STANDARD, MSG_LAUNCH_ELEVATED_ENGINE_SUCCESS);
357
358 hr = PipeWaitForChildConnect(&pEngineState->companionConnection);
359 if (HRESULT_FROM_WIN32(ERROR_NO_DATA) == hr)
360 {
361 hr = E_SUSPECTED_AV_INTERFERENCE;
362 }
363 ExitOnFailure(hr, "Failed to connect to elevated child process.");
364
365 LogId(REPORT_STANDARD, MSG_CONNECT_TO_ELEVATED_ENGINE_SUCCESS);
366 }
367 else if (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr)
368 {
369 // The user clicked "Cancel" on the elevation prompt or the elevation prompt timed out, provide the notification with the option to retry.
370 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
371 nResult = UserExperienceSendError(&pEngineState->userExperience, BOOTSTRAPPER_ERROR_TYPE_ELEVATE, NULL, hr, NULL, MB_ICONERROR | MB_RETRYCANCEL, IDNOACTION);
372 }
373 } while (IDRETRY == nResult);
374 ExitOnFailure(hr, "Failed to elevate.");
375
376LExit:
377 ReleaseHandle(hPipesCreatedEvent);
378
379 if (FAILED(hr))
380 {
381 PipeConnectionUninitialize(&pEngineState->companionConnection);
382 }
383
384 UserExperienceOnElevateComplete(&pEngineState->userExperience, hr);
385
386 return hr;
387}
388
389extern "C" HRESULT ElevationApplyInitialize(
390 __in HANDLE hPipe,
391 __in BURN_USER_EXPERIENCE* pBA,
392 __in BURN_VARIABLES* pVariables,
393 __in BOOTSTRAPPER_ACTION action,
394 __in BURN_AU_PAUSE_ACTION auAction,
395 __in BOOL fTakeSystemRestorePoint
396 )
397{
398 HRESULT hr = S_OK;
399 BYTE* pbData = NULL;
400 SIZE_T cbData = 0;
401 DWORD dwResult = 0;
402 BURN_ELEVATION_APPLY_INITIALIZE_MESSAGE_CONTEXT context = { };
403
404 context.pBA = pBA;
405
406 // serialize message data
407 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)action);
408 ExitOnFailure(hr, "Failed to write action to message buffer.");
409
410 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)auAction);
411 ExitOnFailure(hr, "Failed to write update action to message buffer.");
412
413 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fTakeSystemRestorePoint);
414 ExitOnFailure(hr, "Failed to write system restore point action to message buffer.");
415
416 hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData);
417 ExitOnFailure(hr, "Failed to write variables.");
418
419 // send message
420 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE, pbData, cbData, ProcessApplyInitializeMessages, &context, &dwResult);
421 ExitOnFailure(hr, "Failed to send message to per-machine process.");
422
423 hr = (HRESULT)dwResult;
424
425 // Best effort to keep the sequence of BA events sane.
426 if (context.fPauseCompleteNeeded)
427 {
428 UserExperienceOnPauseAUComplete(pBA, hr);
429 }
430 if (context.fSrpCompleteNeeded)
431 {
432 UserExperienceOnSystemRestorePointComplete(pBA, hr);
433 }
434
435LExit:
436 ReleaseBuffer(pbData);
437
438 return hr;
439}
440
441extern "C" HRESULT ElevationApplyUninitialize(
442 __in HANDLE hPipe
443 )
444{
445 HRESULT hr = S_OK;
446 BYTE* pbData = NULL;
447 SIZE_T cbData = 0;
448 DWORD dwResult = 0;
449
450 // send message
451 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE, pbData, cbData, NULL, NULL, &dwResult);
452 ExitOnFailure(hr, "Failed to send message to per-machine process.");
453
454 hr = (HRESULT)dwResult;
455
456LExit:
457 ReleaseBuffer(pbData);
458
459 return hr;
460}
461
462/*******************************************************************
463 ElevationSessionBegin -
464
465*******************************************************************/
466extern "C" HRESULT ElevationSessionBegin(
467 __in HANDLE hPipe,
468 __in_z LPCWSTR wzEngineWorkingPath,
469 __in_z LPCWSTR wzResumeCommandLine,
470 __in BOOL fDisableResume,
471 __in BURN_VARIABLES* pVariables,
472 __in DWORD dwRegistrationOperations,
473 __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction,
474 __in DWORD64 qwEstimatedSize
475 )
476{
477 HRESULT hr = S_OK;
478 BYTE* pbData = NULL;
479 SIZE_T cbData = 0;
480 DWORD dwResult = 0;
481
482 // serialize message data
483 hr = BuffWriteString(&pbData, &cbData, wzEngineWorkingPath);
484 ExitOnFailure(hr, "Failed to write engine working path to message buffer.");
485
486 hr = BuffWriteString(&pbData, &cbData, wzResumeCommandLine);
487 ExitOnFailure(hr, "Failed to write resume command line to message buffer.");
488
489 hr = BuffWriteNumber(&pbData, &cbData, fDisableResume);
490 ExitOnFailure(hr, "Failed to write resume flag.");
491
492 hr = BuffWriteNumber(&pbData, &cbData, dwRegistrationOperations);
493 ExitOnFailure(hr, "Failed to write registration operations to message buffer.");
494
495 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)dependencyRegistrationAction);
496 ExitOnFailure(hr, "Failed to write dependency registration action to message buffer.");
497
498 hr = BuffWriteNumber64(&pbData, &cbData, qwEstimatedSize);
499 ExitOnFailure(hr, "Failed to write estimated size to message buffer.");
500
501 hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData);
502 ExitOnFailure(hr, "Failed to write variables.");
503
504 // send message
505 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN, pbData, cbData, NULL, NULL, &dwResult);
506 ExitOnFailure(hr, "Failed to send message to per-machine process.");
507
508 hr = (HRESULT)dwResult;
509
510LExit:
511 ReleaseBuffer(pbData);
512
513 return hr;
514}
515
516/*******************************************************************
517 ElevationSessionResume -
518
519*******************************************************************/
520extern "C" HRESULT ElevationSessionResume(
521 __in HANDLE hPipe,
522 __in_z LPCWSTR wzResumeCommandLine,
523 __in BOOL fDisableResume,
524 __in BURN_VARIABLES* pVariables
525 )
526{
527 HRESULT hr = S_OK;
528 BYTE* pbData = NULL;
529 SIZE_T cbData = 0;
530 DWORD dwResult = 0;
531
532 // serialize message data
533 hr = BuffWriteString(&pbData, &cbData, wzResumeCommandLine);
534 ExitOnFailure(hr, "Failed to write resume command line to message buffer.");
535
536 hr = BuffWriteNumber(&pbData, &cbData, fDisableResume);
537 ExitOnFailure(hr, "Failed to write resume flag.");
538
539 hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData);
540 ExitOnFailure(hr, "Failed to write variables.");
541
542 // send message
543 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME, pbData, cbData, NULL, NULL, &dwResult);
544 ExitOnFailure(hr, "Failed to send message to per-machine process.");
545
546 hr = (HRESULT)dwResult;
547
548LExit:
549 ReleaseBuffer(pbData);
550
551 return hr;
552}
553
554/*******************************************************************
555 ElevationSessionEnd -
556
557*******************************************************************/
558extern "C" HRESULT ElevationSessionEnd(
559 __in HANDLE hPipe,
560 __in BURN_RESUME_MODE resumeMode,
561 __in BOOTSTRAPPER_APPLY_RESTART restart,
562 __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction
563 )
564{
565 HRESULT hr = S_OK;
566 BYTE* pbData = NULL;
567 SIZE_T cbData = 0;
568 DWORD dwResult = 0;
569
570 // serialize message data
571 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)resumeMode);
572 ExitOnFailure(hr, "Failed to write resume mode to message buffer.");
573
574 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)restart);
575 ExitOnFailure(hr, "Failed to write restart enum to message buffer.");
576
577 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)dependencyRegistrationAction);
578 ExitOnFailure(hr, "Failed to write dependency registration action to message buffer.");
579
580 // send message
581 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SESSION_END, pbData, cbData, NULL, NULL, &dwResult);
582 ExitOnFailure(hr, "Failed to send message to per-machine process.");
583
584 hr = (HRESULT)dwResult;
585
586LExit:
587 ReleaseBuffer(pbData);
588
589 return hr;
590}
591
592/*******************************************************************
593 ElevationSaveState -
594
595*******************************************************************/
596HRESULT ElevationSaveState(
597 __in HANDLE hPipe,
598 __in_bcount(cbBuffer) BYTE* pbBuffer,
599 __in SIZE_T cbBuffer
600 )
601{
602 HRESULT hr = S_OK;
603 DWORD dwResult = 0;
604
605 // send message
606 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE, pbBuffer, cbBuffer, NULL, NULL, &dwResult);
607 ExitOnFailure(hr, "Failed to send message to per-machine process.");
608
609 hr = (HRESULT)dwResult;
610
611LExit:
612 return hr;
613}
614
615/*******************************************************************
616 ElevationCacheCompletePayload -
617
618*******************************************************************/
619extern "C" HRESULT ElevationCacheCompletePayload(
620 __in HANDLE hPipe,
621 __in BURN_PACKAGE* pPackage,
622 __in BURN_PAYLOAD* pPayload,
623 __in_z LPCWSTR wzUnverifiedPath,
624 __in BOOL fMove,
625 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
626 __in LPPROGRESS_ROUTINE pfnProgress,
627 __in LPVOID pContext
628 )
629{
630 HRESULT hr = S_OK;
631 BYTE* pbData = NULL;
632 SIZE_T cbData = 0;
633 DWORD dwResult = 0;
634 BURN_ELEVATION_CACHE_MESSAGE_CONTEXT context = { };
635
636 context.pfnCacheMessageHandler = pfnCacheMessageHandler;
637 context.pfnProgress = pfnProgress;
638 context.pvContext = pContext;
639
640 // serialize message data
641 hr = BuffWriteString(&pbData, &cbData, pPackage->sczId);
642 ExitOnFailure(hr, "Failed to write package id to message buffer.");
643
644 hr = BuffWriteString(&pbData, &cbData, pPayload->sczKey);
645 ExitOnFailure(hr, "Failed to write payload id to message buffer.");
646
647 hr = BuffWriteString(&pbData, &cbData, wzUnverifiedPath);
648 ExitOnFailure(hr, "Failed to write unverified path to message buffer.");
649
650 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fMove);
651 ExitOnFailure(hr, "Failed to write move flag to message buffer.");
652
653 // send message
654 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_COMPLETE_PAYLOAD, pbData, cbData, ProcessBurnCacheMessages, &context, &dwResult);
655 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_COMPLETE_PAYLOAD message to per-machine process.");
656
657 hr = (HRESULT)dwResult;
658
659LExit:
660 ReleaseBuffer(pbData);
661
662 return hr;
663}
664
665extern "C" HRESULT ElevationCacheVerifyPayload(
666 __in HANDLE hPipe,
667 __in BURN_PACKAGE* pPackage,
668 __in BURN_PAYLOAD* pPayload,
669 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
670 __in LPPROGRESS_ROUTINE pfnProgress,
671 __in LPVOID pContext
672 )
673{
674 HRESULT hr = S_OK;
675 BYTE* pbData = NULL;
676 SIZE_T cbData = 0;
677 DWORD dwResult = 0;
678 BURN_ELEVATION_CACHE_MESSAGE_CONTEXT context = { };
679
680 context.pfnCacheMessageHandler = pfnCacheMessageHandler;
681 context.pfnProgress = pfnProgress;
682 context.pvContext = pContext;
683
684 // serialize message data
685 hr = BuffWriteString(&pbData, &cbData, pPackage->sczId);
686 ExitOnFailure(hr, "Failed to write package id to message buffer.");
687
688 hr = BuffWriteString(&pbData, &cbData, pPayload->sczKey);
689 ExitOnFailure(hr, "Failed to write payload id to message buffer.");
690
691 // send message
692 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_PAYLOAD, pbData, cbData, ProcessBurnCacheMessages, &context, &dwResult);
693 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_PAYLOAD message to per-machine process.");
694
695 hr = (HRESULT)dwResult;
696
697LExit:
698 ReleaseBuffer(pbData);
699
700 return hr;
701}
702
703/*******************************************************************
704 ElevationCacheCleanup -
705
706*******************************************************************/
707extern "C" HRESULT ElevationCacheCleanup(
708 __in HANDLE hPipe
709 )
710{
711 HRESULT hr = S_OK;
712 DWORD dwResult = 0;
713
714 // send message
715 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP, NULL, 0, NULL, NULL, &dwResult);
716 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP message to per-machine process.");
717
718 hr = (HRESULT)dwResult;
719
720LExit:
721 return hr;
722}
723
724extern "C" HRESULT ElevationProcessDependentRegistration(
725 __in HANDLE hPipe,
726 __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction
727 )
728{
729 HRESULT hr = S_OK;
730 BYTE* pbData = NULL;
731 SIZE_T cbData = 0;
732 DWORD dwResult = 0;
733
734 // serialize message data
735 hr = BuffWriteNumber(&pbData, &cbData, pAction->type);
736 ExitOnFailure(hr, "Failed to write action type to message buffer.");
737
738 hr = BuffWriteString(&pbData, &cbData, pAction->sczBundleId);
739 ExitOnFailure(hr, "Failed to write bundle id to message buffer.");
740
741 hr = BuffWriteString(&pbData, &cbData, pAction->sczDependentProviderKey);
742 ExitOnFailure(hr, "Failed to write dependent provider key to message buffer.");
743
744 // send message
745 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION, pbData, cbData, NULL, NULL, &dwResult);
746 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION message to per-machine process.");
747
748 hr = (HRESULT)dwResult;
749
750LExit:
751 ReleaseBuffer(pbData);
752
753 return hr;
754}
755
756/*******************************************************************
757 ElevationExecuteExePackage -
758
759*******************************************************************/
760extern "C" HRESULT ElevationExecuteExePackage(
761 __in HANDLE hPipe,
762 __in BURN_EXECUTE_ACTION* pExecuteAction,
763 __in BURN_VARIABLES* pVariables,
764 __in BOOL fRollback,
765 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
766 __in LPVOID pvContext,
767 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
768 )
769{
770 HRESULT hr = S_OK;
771 BYTE* pbData = NULL;
772 SIZE_T cbData = 0;
773 BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT context = { };
774 DWORD dwResult = 0;
775
776 // serialize message data
777 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.pPackage->sczId);
778 ExitOnFailure(hr, "Failed to write package id to message buffer.");
779
780 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->exePackage.action);
781 ExitOnFailure(hr, "Failed to write action to message buffer.");
782
783 hr = BuffWriteNumber(&pbData, &cbData, fRollback);
784 ExitOnFailure(hr, "Failed to write rollback.");
785
786 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.sczIgnoreDependencies);
787 ExitOnFailure(hr, "Failed to write the list of dependencies to ignore to the message buffer.");
788
789 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.sczAncestors);
790 ExitOnFailure(hr, "Failed to write the list of ancestors to the message buffer.");
791
792 hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData);
793 ExitOnFailure(hr, "Failed to write variables.");
794
795 // send message
796 context.pfnGenericMessageHandler = pfnGenericMessageHandler;
797 context.pvContext = pvContext;
798
799 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE, pbData, cbData, ProcessGenericExecuteMessages, &context, &dwResult);
800 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE message to per-machine process.");
801
802 hr = ProcessResult(dwResult, pRestart);
803
804LExit:
805 ReleaseBuffer(pbData);
806
807 return hr;
808}
809
810extern "C" HRESULT ElevationMsiBeginTransaction(
811 __in HANDLE hPipe,
812 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
813 )
814{
815 HRESULT hr = S_OK;
816 BYTE* pbData = NULL;
817 SIZE_T cbData = 0;
818 DWORD dwResult = ERROR_SUCCESS;
819
820 // serialize message data
821 hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczId);
822 ExitOnFailure(hr, "Failed to write transaction name to message buffer.");
823
824 hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczLogPath);
825 ExitOnFailure(hr, "Failed to write transaction log path to message buffer.");
826
827 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION, pbData, cbData, NULL, NULL, &dwResult);
828 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION message to per-machine process.");
829
830 hr = static_cast<HRESULT>(dwResult);
831
832LExit:
833 ReleaseBuffer(pbData);
834
835 return hr;
836}
837
838extern "C" HRESULT ElevationMsiCommitTransaction(
839 __in HANDLE hPipe,
840 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
841 )
842{
843 HRESULT hr = S_OK;
844 BYTE* pbData = NULL;
845 SIZE_T cbData = 0;
846 DWORD dwResult = ERROR_SUCCESS;
847
848 // serialize message data
849 hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczId);
850 ExitOnFailure(hr, "Failed to write transaction name to message buffer.");
851
852 hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczLogPath);
853 ExitOnFailure(hr, "Failed to write transaction log path to message buffer.");
854
855 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION, pbData, cbData, NULL, NULL, &dwResult);
856 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION message to per-machine process.");
857
858 hr = static_cast<HRESULT>(dwResult);
859
860LExit:
861 ReleaseBuffer(pbData);
862
863 return hr;
864}
865
866extern "C" HRESULT ElevationMsiRollbackTransaction(
867 __in HANDLE hPipe,
868 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
869 )
870{
871 HRESULT hr = S_OK;
872 BYTE* pbData = NULL;
873 SIZE_T cbData = 0;
874 DWORD dwResult = ERROR_SUCCESS;
875
876 // serialize message data
877 hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczId);
878 ExitOnFailure(hr, "Failed to write transaction name to message buffer.");
879
880 hr = BuffWriteString(&pbData, &cbData, pRollbackBoundary->sczLogPath);
881 ExitOnFailure(hr, "Failed to write transaction log path to message buffer.");
882
883 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION, pbData, cbData, NULL, NULL, &dwResult);
884 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION message to per-machine process.");
885
886 hr = static_cast<HRESULT>(dwResult);
887
888LExit:
889 ReleaseBuffer(pbData);
890
891 return hr;
892}
893
894
895
896/*******************************************************************
897 ElevationExecuteMsiPackage -
898
899*******************************************************************/
900extern "C" HRESULT ElevationExecuteMsiPackage(
901 __in HANDLE hPipe,
902 __in_opt HWND hwndParent,
903 __in BURN_EXECUTE_ACTION* pExecuteAction,
904 __in BURN_VARIABLES* pVariables,
905 __in BOOL fRollback,
906 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
907 __in LPVOID pvContext,
908 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
909 )
910{
911 HRESULT hr = S_OK;
912 BYTE* pbData = NULL;
913 SIZE_T cbData = 0;
914 BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = { };
915 DWORD dwResult = 0;
916
917 // serialize message data
918 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fRollback);
919 ExitOnFailure(hr, "Failed to write rollback flag to message buffer.");
920
921 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msiPackage.pPackage->sczId);
922 ExitOnFailure(hr, "Failed to write package id to message buffer.");
923
924 hr = BuffWritePointer(&pbData, &cbData, (DWORD_PTR)hwndParent);
925 ExitOnFailure(hr, "Failed to write parent hwnd to message buffer.");
926
927 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msiPackage.sczLogPath);
928 ExitOnFailure(hr, "Failed to write package log to message buffer.");
929
930 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.actionMsiProperty);
931 ExitOnFailure(hr, "Failed to write actionMsiProperty to message buffer.");
932
933 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.uiLevel);
934 ExitOnFailure(hr, "Failed to write UI level to message buffer.");
935
936 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.fDisableExternalUiHandler);
937 ExitOnFailure(hr, "Failed to write fDisableExternalUiHandler to message buffer.");
938
939 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.action);
940 ExitOnFailure(hr, "Failed to write action to message buffer.");
941
942 // Feature actions.
943 for (DWORD i = 0; i < pExecuteAction->msiPackage.pPackage->Msi.cFeatures; ++i)
944 {
945 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.rgFeatures[i]);
946 ExitOnFailure(hr, "Failed to write feature action to message buffer.");
947 }
948
949 // Slipstream patches actions.
950 for (DWORD i = 0; i < pExecuteAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++i)
951 {
952 BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pExecuteAction->msiPackage.pPackage->Msi.rgSlipstreamMsps + i;
953 BOOTSTRAPPER_ACTION_STATE* pAction = fRollback ? &pSlipstreamMsp->rollback : &pSlipstreamMsp->execute;
954 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)*pAction);
955 ExitOnFailure(hr, "Failed to write slipstream patch action to message buffer.");
956 }
957
958 hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData);
959 ExitOnFailure(hr, "Failed to write variables.");
960
961
962 // send message
963 context.pfnMessageHandler = pfnMessageHandler;
964 context.pvContext = pvContext;
965
966 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE, pbData, cbData, ProcessMsiPackageMessages, &context, &dwResult);
967 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process.");
968
969 hr = ProcessResult(dwResult, pRestart);
970
971LExit:
972 ReleaseBuffer(pbData);
973
974 return hr;
975}
976
977/*******************************************************************
978 ElevationExecuteMspPackage -
979
980*******************************************************************/
981extern "C" HRESULT ElevationExecuteMspPackage(
982 __in HANDLE hPipe,
983 __in_opt HWND hwndParent,
984 __in BURN_EXECUTE_ACTION* pExecuteAction,
985 __in BURN_VARIABLES* pVariables,
986 __in BOOL fRollback,
987 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
988 __in LPVOID pvContext,
989 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
990 )
991{
992 HRESULT hr = S_OK;
993 BYTE* pbData = NULL;
994 SIZE_T cbData = 0;
995 BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = { };
996 DWORD dwResult = 0;
997
998 // serialize message data
999 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.pPackage->sczId);
1000 ExitOnFailure(hr, "Failed to write package id to message buffer.");
1001
1002 hr = BuffWritePointer(&pbData, &cbData, (DWORD_PTR)hwndParent);
1003 ExitOnFailure(hr, "Failed to write parent hwnd to message buffer.");
1004
1005 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.sczTargetProductCode);
1006 ExitOnFailure(hr, "Failed to write target product code to message buffer.");
1007
1008 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.sczLogPath);
1009 ExitOnFailure(hr, "Failed to write package log to message buffer.");
1010
1011 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.actionMsiProperty);
1012 ExitOnFailure(hr, "Failed to write actionMsiProperty to message buffer.");
1013
1014 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.uiLevel);
1015 ExitOnFailure(hr, "Failed to write UI level to message buffer.");
1016
1017 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.fDisableExternalUiHandler);
1018 ExitOnFailure(hr, "Failed to write fDisableExternalUiHandler to message buffer.");
1019
1020 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.action);
1021 ExitOnFailure(hr, "Failed to write action to message buffer.");
1022
1023 hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->mspTarget.cOrderedPatches);
1024 ExitOnFailure(hr, "Failed to write count of ordered patches to message buffer.");
1025
1026 for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i)
1027 {
1028 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage->sczId);
1029 ExitOnFailure(hr, "Failed to write ordered patch id to message buffer.");
1030 }
1031
1032 hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData);
1033 ExitOnFailure(hr, "Failed to write variables.");
1034
1035 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fRollback);
1036 ExitOnFailure(hr, "Failed to write rollback flag to message buffer.");
1037
1038 // send message
1039 context.pfnMessageHandler = pfnMessageHandler;
1040 context.pvContext = pvContext;
1041
1042 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE, pbData, cbData, ProcessMsiPackageMessages, &context, &dwResult);
1043 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE message to per-machine process.");
1044
1045 hr = ProcessResult(dwResult, pRestart);
1046
1047LExit:
1048 ReleaseBuffer(pbData);
1049
1050 return hr;
1051}
1052
1053/*******************************************************************
1054 ElevationExecuteMsuPackage -
1055
1056*******************************************************************/
1057extern "C" HRESULT ElevationExecuteMsuPackage(
1058 __in HANDLE hPipe,
1059 __in BURN_EXECUTE_ACTION* pExecuteAction,
1060 __in BOOL fRollback,
1061 __in BOOL fStopWusaService,
1062 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
1063 __in LPVOID pvContext,
1064 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
1065 )
1066{
1067 HRESULT hr = S_OK;
1068 BYTE* pbData = NULL;
1069 SIZE_T cbData = 0;
1070 BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT context = { };
1071 DWORD dwResult = 0;
1072
1073 // serialize message data
1074 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msuPackage.pPackage->sczId);
1075 ExitOnFailure(hr, "Failed to write package id to message buffer.");
1076
1077 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msuPackage.sczLogPath);
1078 ExitOnFailure(hr, "Failed to write package log to message buffer.");
1079
1080 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msuPackage.action);
1081 ExitOnFailure(hr, "Failed to write action to message buffer.");
1082
1083 hr = BuffWriteNumber(&pbData, &cbData, fRollback);
1084 ExitOnFailure(hr, "Failed to write rollback.");
1085
1086 hr = BuffWriteNumber(&pbData, &cbData, fStopWusaService);
1087 ExitOnFailure(hr, "Failed to write StopWusaService.");
1088
1089 // send message
1090 context.pfnGenericMessageHandler = pfnGenericMessageHandler;
1091 context.pvContext = pvContext;
1092
1093 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE, pbData, cbData, ProcessGenericExecuteMessages, &context, &dwResult);
1094 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE message to per-machine process.");
1095
1096 hr = ProcessResult(dwResult, pRestart);
1097
1098LExit:
1099 ReleaseBuffer(pbData);
1100
1101 return hr;
1102}
1103
1104extern "C" HRESULT ElevationExecutePackageProviderAction(
1105 __in HANDLE hPipe,
1106 __in BURN_EXECUTE_ACTION* pExecuteAction
1107 )
1108{
1109 HRESULT hr = S_OK;
1110 BYTE* pbData = NULL;
1111 SIZE_T cbData = 0;
1112 DWORD dwResult = 0;
1113 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
1114
1115 // Serialize the message data.
1116 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->packageProvider.pPackage->sczId);
1117 ExitOnFailure(hr, "Failed to write package id to message buffer.");
1118
1119 hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->packageProvider.action);
1120 ExitOnFailure(hr, "Failed to write action to message buffer.");
1121
1122 // Send the message.
1123 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER, pbData, cbData, NULL, NULL, &dwResult);
1124 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER message to per-machine process.");
1125
1126 // Ignore the restart since this action only results in registry writes.
1127 hr = ProcessResult(dwResult, &restart);
1128
1129LExit:
1130 ReleaseBuffer(pbData);
1131
1132 return hr;
1133}
1134
1135extern "C" HRESULT ElevationExecutePackageDependencyAction(
1136 __in HANDLE hPipe,
1137 __in BURN_EXECUTE_ACTION* pExecuteAction
1138 )
1139{
1140 HRESULT hr = S_OK;
1141 BYTE* pbData = NULL;
1142 SIZE_T cbData = 0;
1143 DWORD dwResult = 0;
1144 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
1145
1146 // Serialize the message data.
1147 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->packageDependency.pPackage->sczId);
1148 ExitOnFailure(hr, "Failed to write package id to message buffer.");
1149
1150 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->packageDependency.sczBundleProviderKey);
1151 ExitOnFailure(hr, "Failed to write bundle dependency key to message buffer.");
1152
1153 hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->packageDependency.action);
1154 ExitOnFailure(hr, "Failed to write action to message buffer.");
1155
1156 // Send the message.
1157 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY, pbData, cbData, NULL, NULL, &dwResult);
1158 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY message to per-machine process.");
1159
1160 // Ignore the restart since this action only results in registry writes.
1161 hr = ProcessResult(dwResult, &restart);
1162
1163LExit:
1164 ReleaseBuffer(pbData);
1165
1166 return hr;
1167}
1168
1169/*******************************************************************
1170 ElevationCleanPackage -
1171
1172*******************************************************************/
1173extern "C" HRESULT ElevationCleanPackage(
1174 __in HANDLE hPipe,
1175 __in BURN_PACKAGE* pPackage
1176 )
1177{
1178 HRESULT hr = S_OK;
1179 BYTE* pbData = NULL;
1180 SIZE_T cbData = 0;
1181 DWORD dwResult = 0;
1182
1183 // serialize message data
1184 hr = BuffWriteString(&pbData, &cbData, pPackage->sczId);
1185 ExitOnFailure(hr, "Failed to write clean package id to message buffer.");
1186
1187 // send message
1188 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE, pbData, cbData, NULL, NULL, &dwResult);
1189 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE message to per-machine process.");
1190
1191 hr = (HRESULT)dwResult;
1192
1193LExit:
1194 ReleaseBuffer(pbData);
1195
1196 return hr;
1197}
1198
1199extern "C" HRESULT ElevationLaunchApprovedExe(
1200 __in HANDLE hPipe,
1201 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe,
1202 __out DWORD* pdwProcessId
1203 )
1204{
1205 HRESULT hr = S_OK;
1206 BYTE* pbData = NULL;
1207 SIZE_T cbData = 0;
1208 DWORD dwResult = 0;
1209 BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT context = { };
1210
1211 // Serialize message data.
1212 hr = BuffWriteString(&pbData, &cbData, pLaunchApprovedExe->sczId);
1213 ExitOnFailure(hr, "Failed to write approved exe id to message buffer.");
1214
1215 hr = BuffWriteString(&pbData, &cbData, pLaunchApprovedExe->sczArguments);
1216 ExitOnFailure(hr, "Failed to write approved exe arguments to message buffer.");
1217
1218 hr = BuffWriteNumber(&pbData, &cbData, pLaunchApprovedExe->dwWaitForInputIdleTimeout);
1219 ExitOnFailure(hr, "Failed to write approved exe WaitForInputIdle timeout to message buffer.");
1220
1221 // Send the message.
1222 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE, pbData, cbData, ProcessLaunchApprovedExeMessages, &context, &dwResult);
1223 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE message to per-machine process.");
1224
1225 hr = (HRESULT)dwResult;
1226 *pdwProcessId = context.dwProcessId;
1227
1228LExit:
1229 ReleaseBuffer(pbData);
1230
1231 return hr;
1232}
1233
1234/*******************************************************************
1235 ElevationChildPumpMessages -
1236
1237*******************************************************************/
1238extern "C" HRESULT ElevationChildPumpMessages(
1239 __in DWORD dwLoggingTlsId,
1240 __in HANDLE hPipe,
1241 __in HANDLE hCachePipe,
1242 __in BURN_APPROVED_EXES* pApprovedExes,
1243 __in BURN_CONTAINERS* pContainers,
1244 __in BURN_PACKAGES* pPackages,
1245 __in BURN_PAYLOADS* pPayloads,
1246 __in BURN_VARIABLES* pVariables,
1247 __in BURN_REGISTRATION* pRegistration,
1248 __in BURN_USER_EXPERIENCE* pUserExperience,
1249 __out HANDLE* phLock,
1250 __out BOOL* pfDisabledAutomaticUpdates,
1251 __out DWORD* pdwChildExitCode,
1252 __out BOOL* pfRestart
1253 )
1254{
1255 HRESULT hr = S_OK;
1256 BURN_ELEVATION_CHILD_MESSAGE_CONTEXT cacheContext = { };
1257 BURN_ELEVATION_CHILD_MESSAGE_CONTEXT context = { };
1258 HANDLE hCacheThread = NULL;
1259 BURN_PIPE_RESULT result = { };
1260
1261 cacheContext.dwLoggingTlsId = dwLoggingTlsId;
1262 cacheContext.hPipe = hCachePipe;
1263 cacheContext.pContainers = pContainers;
1264 cacheContext.pPackages = pPackages;
1265 cacheContext.pPayloads = pPayloads;
1266 cacheContext.pVariables = pVariables;
1267 cacheContext.pRegistration = pRegistration;
1268 cacheContext.pUserExperience = pUserExperience;
1269
1270 context.dwLoggingTlsId = dwLoggingTlsId;
1271 context.hPipe = hPipe;
1272 context.phLock = phLock;
1273 context.pfDisabledAutomaticUpdates = pfDisabledAutomaticUpdates;
1274 context.pApprovedExes = pApprovedExes;
1275 context.pContainers = pContainers;
1276 context.pPackages = pPackages;
1277 context.pPayloads = pPayloads;
1278 context.pVariables = pVariables;
1279 context.pRegistration = pRegistration;
1280 context.pUserExperience = pUserExperience;
1281
1282 hCacheThread = ::CreateThread(NULL, 0, ElevatedChildCacheThreadProc, &cacheContext, 0, NULL);
1283 ExitOnNullWithLastError(hCacheThread, hr, "Failed to create elevated cache thread.");
1284
1285 hr = PipePumpMessages(hPipe, ProcessElevatedChildMessage, &context, &result);
1286 ExitOnFailure(hr, "Failed to pump messages in child process.");
1287
1288 // Wait for the cache thread and verify it gets the right result but don't fail if things
1289 // don't work out.
1290 WaitForElevatedChildCacheThread(hCacheThread, result.dwResult);
1291
1292 *pdwChildExitCode = result.dwResult;
1293 *pfRestart = result.fRestart;
1294
1295LExit:
1296 ReleaseHandle(hCacheThread);
1297
1298 return hr;
1299}
1300
1301extern "C" HRESULT ElevationChildResumeAutomaticUpdates()
1302{
1303 HRESULT hr = S_OK;
1304
1305 LogId(REPORT_STANDARD, MSG_RESUME_AU_STARTING);
1306
1307 hr = WuaResumeAutomaticUpdates();
1308 ExitOnFailure(hr, "Failed to resume automatic updates after pausing them, continuing...");
1309
1310 LogId(REPORT_STANDARD, MSG_RESUME_AU_SUCCEEDED);
1311
1312LExit:
1313 return hr;
1314}
1315
1316// internal function definitions
1317
1318static DWORD WINAPI ElevatedChildCacheThreadProc(
1319 __in LPVOID lpThreadParameter
1320 )
1321{
1322 HRESULT hr = S_OK;
1323 BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = reinterpret_cast<BURN_ELEVATION_CHILD_MESSAGE_CONTEXT*>(lpThreadParameter);
1324 BOOL fComInitialized = FALSE;
1325 BURN_PIPE_RESULT result = { };
1326
1327 if (!::TlsSetValue(pContext->dwLoggingTlsId, pContext->hPipe))
1328 {
1329 ExitWithLastError(hr, "Failed to set elevated cache pipe into thread local storage for logging.");
1330 }
1331
1332 // initialize COM
1333 hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
1334 ExitOnFailure(hr, "Failed to initialize COM.");
1335 fComInitialized = TRUE;
1336
1337 hr = PipePumpMessages(pContext->hPipe, ProcessElevatedChildCacheMessage, pContext, &result);
1338 ExitOnFailure(hr, "Failed to pump messages in child process.");
1339
1340 hr = (HRESULT)result.dwResult;
1341
1342LExit:
1343 if (fComInitialized)
1344 {
1345 ::CoUninitialize();
1346 }
1347
1348 return (DWORD)hr;
1349}
1350
1351static HRESULT WaitForElevatedChildCacheThread(
1352 __in HANDLE hCacheThread,
1353 __in DWORD dwExpectedExitCode
1354 )
1355{
1356 UNREFERENCED_PARAMETER(dwExpectedExitCode);
1357
1358 HRESULT hr = S_OK;
1359 DWORD dwExitCode = ERROR_SUCCESS;
1360
1361 if (WAIT_OBJECT_0 != ::WaitForSingleObject(hCacheThread, BURN_TIMEOUT))
1362 {
1363 ExitWithLastError(hr, "Failed to wait for cache thread to terminate.");
1364 }
1365
1366 if (!::GetExitCodeThread(hCacheThread, &dwExitCode))
1367 {
1368 ExitWithLastError(hr, "Failed to get cache thread exit code.");
1369 }
1370
1371 AssertSz(dwExitCode == dwExpectedExitCode, "Cache thread should have exited with the expected exit code.");
1372
1373LExit:
1374 return hr;
1375}
1376
1377static HRESULT ProcessApplyInitializeMessages(
1378 __in BURN_PIPE_MESSAGE* pMsg,
1379 __in_opt LPVOID pvContext,
1380 __out DWORD* pdwResult
1381 )
1382{
1383 HRESULT hr = S_OK;
1384 BURN_ELEVATION_APPLY_INITIALIZE_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_APPLY_INITIALIZE_MESSAGE_CONTEXT*>(pvContext);
1385 BYTE* pbData = (BYTE*)pMsg->pvData;
1386 SIZE_T iData = 0;
1387 HRESULT hrStatus = S_OK;
1388 HRESULT hrBA = S_OK;
1389
1390 // Process the message.
1391 switch (pMsg->dwMessage)
1392 {
1393 case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_BEGIN:
1394 pContext->fPauseCompleteNeeded = TRUE;
1395 hrBA = UserExperienceOnPauseAUBegin(pContext->pBA);
1396 break;
1397
1398 case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_COMPLETE:
1399 // read hrStatus
1400 hr = BuffReadNumber(pbData, pMsg->cbData, &iData, reinterpret_cast<DWORD*>(&hrStatus));
1401 ExitOnFailure(hr, "Failed to read pause AU hrStatus.");
1402
1403 pContext->fPauseCompleteNeeded = FALSE;
1404 hrBA = UserExperienceOnPauseAUComplete(pContext->pBA, hrStatus);
1405 break;
1406
1407 case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_BEGIN:
1408 if (pContext->fPauseCompleteNeeded)
1409 {
1410 pContext->fPauseCompleteNeeded = FALSE;
1411 hrBA = UserExperienceOnPauseAUComplete(pContext->pBA, E_INVALIDSTATE);
1412 }
1413
1414 pContext->fSrpCompleteNeeded = TRUE;
1415 hrBA = UserExperienceOnSystemRestorePointBegin(pContext->pBA);
1416 break;
1417
1418 case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_COMPLETE:
1419 // read hrStatus
1420 hr = BuffReadNumber(pbData, pMsg->cbData, &iData, reinterpret_cast<DWORD*>(&hrStatus));
1421 ExitOnFailure(hr, "Failed to read system restore point hrStatus.");
1422
1423 pContext->fSrpCompleteNeeded = FALSE;
1424 hrBA = UserExperienceOnSystemRestorePointComplete(pContext->pBA, hrStatus);
1425 break;
1426
1427 default:
1428 hr = E_INVALIDARG;
1429 ExitOnRootFailure(hr, "Invalid apply initialize message.");
1430 break;
1431 }
1432
1433 *pdwResult = static_cast<DWORD>(hrBA);
1434
1435LExit:
1436 return hr;
1437}
1438
1439static HRESULT ProcessBurnCacheMessages(
1440 __in BURN_PIPE_MESSAGE* pMsg,
1441 __in LPVOID pvContext,
1442 __out DWORD* pdwResult
1443 )
1444{
1445 HRESULT hr = S_OK;
1446 SIZE_T iData = 0;
1447 BURN_ELEVATION_CACHE_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_CACHE_MESSAGE_CONTEXT*>(pvContext);
1448 BURN_CACHE_MESSAGE message = { };
1449 BOOL fProgressRoutine = FALSE;
1450
1451 // Process the message.
1452 switch (pMsg->dwMessage)
1453 {
1454 case BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_BEGIN:
1455 // read message parameters
1456 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, reinterpret_cast<DWORD*>(&message.begin.cacheStep));
1457 ExitOnFailure(hr, "Failed to read begin cache step.");
1458
1459 message.type = BURN_CACHE_MESSAGE_BEGIN;
1460 break;
1461
1462 case BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_COMPLETE:
1463 // read message parameters
1464 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, reinterpret_cast<DWORD*>(&message.complete.hrStatus));
1465 ExitOnFailure(hr, "Failed to read complete hresult.");
1466
1467 message.type = BURN_CACHE_MESSAGE_COMPLETE;
1468 break;
1469
1470 case BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_SUCCESS:
1471 // read message parameters
1472 hr = BuffReadNumber64((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.success.qwFileSize);
1473 ExitOnFailure(hr, "Failed to read begin cache step.");
1474
1475 message.type = BURN_CACHE_MESSAGE_SUCCESS;
1476 break;
1477
1478 case BURN_ELEVATION_MESSAGE_TYPE_PROGRESS_ROUTINE:
1479 fProgressRoutine = TRUE;
1480 break;
1481
1482 default:
1483 hr = E_INVALIDARG;
1484 ExitOnRootFailure(hr, "Invalid burn cache message.");
1485 break;
1486 }
1487
1488 if (fProgressRoutine)
1489 {
1490 hr = ProcessProgressRoutineMessage(pMsg, pContext->pfnProgress, pContext->pvContext, pdwResult);
1491 }
1492 else
1493 {
1494 hr = pContext->pfnCacheMessageHandler(&message, pContext->pvContext);
1495 *pdwResult = static_cast<DWORD>(hr);
1496 }
1497
1498LExit:
1499 return hr;
1500}
1501
1502static HRESULT ProcessGenericExecuteMessages(
1503 __in BURN_PIPE_MESSAGE* pMsg,
1504 __in LPVOID pvContext,
1505 __out DWORD* pdwResult
1506 )
1507{
1508 HRESULT hr = S_OK;
1509 SIZE_T iData = 0;
1510 BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT*>(pvContext);
1511 LPWSTR sczMessage = NULL;
1512 DWORD cFiles = 0;
1513 LPWSTR* rgwzFiles = NULL;
1514 GENERIC_EXECUTE_MESSAGE message = { };
1515
1516 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.dwAllowedResults);
1517 ExitOnFailure(hr, "Failed to allowed results.");
1518
1519 // Process the message.
1520 switch (pMsg->dwMessage)
1521 {
1522 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS:
1523 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
1524
1525 // read message parameters
1526 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.progress.dwPercentage);
1527 ExitOnFailure(hr, "Failed to progress.");
1528 break;
1529
1530 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR:
1531 message.type = GENERIC_EXECUTE_MESSAGE_ERROR;
1532
1533 // read message parameters
1534 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.error.dwErrorCode);
1535 ExitOnFailure(hr, "Failed to read error code.");
1536
1537 hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &sczMessage);
1538 ExitOnFailure(hr, "Failed to read message.");
1539
1540 message.error.wzMessage = sczMessage;
1541 break;
1542
1543 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE:
1544 message.type = GENERIC_EXECUTE_MESSAGE_FILES_IN_USE;
1545
1546 // read message parameters
1547 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &cFiles);
1548 ExitOnFailure(hr, "Failed to read file count.");
1549
1550 rgwzFiles = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cFiles, TRUE);
1551 ExitOnNull(rgwzFiles, hr, E_OUTOFMEMORY, "Failed to allocate buffer for files in use.");
1552
1553 for (DWORD i = 0; i < cFiles; ++i)
1554 {
1555 hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &rgwzFiles[i]);
1556 ExitOnFailure(hr, "Failed to read file name: %u", i);
1557 }
1558
1559 message.filesInUse.cFiles = cFiles;
1560 message.filesInUse.rgwzFiles = (LPCWSTR*)rgwzFiles;
1561 break;
1562
1563 default:
1564 hr = E_INVALIDARG;
1565 ExitOnRootFailure(hr, "Invalid package message.");
1566 break;
1567 }
1568
1569 // send message
1570 *pdwResult = (DWORD)pContext->pfnGenericMessageHandler(&message, pContext->pvContext);
1571
1572LExit:
1573 ReleaseStr(sczMessage);
1574
1575 if (rgwzFiles)
1576 {
1577 for (DWORD i = 0; i < cFiles; ++i)
1578 {
1579 ReleaseStr(rgwzFiles[i]);
1580 }
1581 MemFree(rgwzFiles);
1582 }
1583 return hr;
1584}
1585
1586static HRESULT ProcessMsiPackageMessages(
1587 __in BURN_PIPE_MESSAGE* pMsg,
1588 __in_opt LPVOID pvContext,
1589 __out DWORD* pdwResult
1590 )
1591{
1592 HRESULT hr = S_OK;
1593 SIZE_T iData = 0;
1594 WIU_MSI_EXECUTE_MESSAGE message = { };
1595 DWORD cMsiData = 0;
1596 LPWSTR* rgwzMsiData = NULL;
1597 BURN_ELEVATION_MSI_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_MSI_MESSAGE_CONTEXT*>(pvContext);
1598 LPWSTR sczMessage = NULL;
1599
1600 // Read MSI extended message data.
1601 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &cMsiData);
1602 ExitOnFailure(hr, "Failed to read MSI data count.");
1603
1604 if (cMsiData)
1605 {
1606 rgwzMsiData = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cMsiData, TRUE);
1607 ExitOnNull(rgwzMsiData, hr, E_OUTOFMEMORY, "Failed to allocate buffer to read MSI data.");
1608
1609 for (DWORD i = 0; i < cMsiData; ++i)
1610 {
1611 hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &rgwzMsiData[i]);
1612 ExitOnFailure(hr, "Failed to read MSI data: %u", i);
1613 }
1614
1615 message.cData = cMsiData;
1616 message.rgwzData = (LPCWSTR*)rgwzMsiData;
1617 }
1618
1619 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.dwAllowedResults);
1620 ExitOnFailure(hr, "Failed to read UI flags.");
1621
1622 // Process the rest of the message.
1623 switch (pMsg->dwMessage)
1624 {
1625 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS:
1626 // read message parameters
1627 message.type = WIU_MSI_EXECUTE_MESSAGE_PROGRESS;
1628
1629 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.progress.dwPercentage);
1630 ExitOnFailure(hr, "Failed to read progress.");
1631 break;
1632
1633 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR:
1634 // read message parameters
1635 message.type = WIU_MSI_EXECUTE_MESSAGE_ERROR;
1636
1637 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.error.dwErrorCode);
1638 ExitOnFailure(hr, "Failed to read error code.");
1639
1640 hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &sczMessage);
1641 ExitOnFailure(hr, "Failed to read message.");
1642 message.error.wzMessage = sczMessage;
1643 break;
1644
1645 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE:
1646 // read message parameters
1647 message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE;
1648
1649 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, (DWORD*)&message.msiMessage.mt);
1650 ExitOnFailure(hr, "Failed to read message type.");
1651
1652 hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &sczMessage);
1653 ExitOnFailure(hr, "Failed to read message.");
1654 message.msiMessage.wzMessage = sczMessage;
1655 break;
1656
1657 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE:
1658 message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE;
1659 message.msiFilesInUse.cFiles = cMsiData;
1660 message.msiFilesInUse.rgwzFiles = (LPCWSTR*)rgwzMsiData;
1661 break;
1662
1663 default:
1664 hr = E_INVALIDARG;
1665 ExitOnRootFailure(hr, "Invalid package message.");
1666 break;
1667 }
1668
1669 // send message
1670 *pdwResult = (DWORD)pContext->pfnMessageHandler(&message, pContext->pvContext);
1671
1672LExit:
1673 ReleaseStr(sczMessage);
1674
1675 if (rgwzMsiData)
1676 {
1677 for (DWORD i = 0; i < cMsiData; ++i)
1678 {
1679 ReleaseStr(rgwzMsiData[i]);
1680 }
1681
1682 MemFree(rgwzMsiData);
1683 }
1684
1685 return hr;
1686}
1687
1688static HRESULT ProcessLaunchApprovedExeMessages(
1689 __in BURN_PIPE_MESSAGE* pMsg,
1690 __in_opt LPVOID pvContext,
1691 __out DWORD* pdwResult
1692 )
1693{
1694 HRESULT hr = S_OK;
1695 SIZE_T iData = 0;
1696 BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT*>(pvContext);
1697 DWORD dwProcessId = 0;
1698
1699 // Process the message.
1700 switch (pMsg->dwMessage)
1701 {
1702 case BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID:
1703 // read message parameters
1704 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &dwProcessId);
1705 ExitOnFailure(hr, "Failed to read approved exe process id.");
1706 pContext->dwProcessId = dwProcessId;
1707 break;
1708
1709 default:
1710 hr = E_INVALIDARG;
1711 ExitOnRootFailure(hr, "Invalid launch approved exe message.");
1712 break;
1713 }
1714
1715 *pdwResult = static_cast<DWORD>(hr);
1716
1717LExit:
1718 return hr;
1719}
1720
1721static HRESULT ProcessProgressRoutineMessage(
1722 __in BURN_PIPE_MESSAGE* pMsg,
1723 __in LPPROGRESS_ROUTINE pfnProgress,
1724 __in LPVOID pvContext,
1725 __out DWORD* pdwResult
1726 )
1727{
1728 HRESULT hr = S_OK;
1729 SIZE_T iData = 0;
1730 LARGE_INTEGER liTotalFileSize = { };
1731 LARGE_INTEGER liTotalBytesTransferred = { };
1732 LARGE_INTEGER liStreamSize = { };
1733 LARGE_INTEGER liStreamBytesTransferred = { };
1734 DWORD dwStreamNumber = 0;
1735 DWORD dwCallbackReason = CALLBACK_CHUNK_FINISHED;
1736 HANDLE hSourceFile = INVALID_HANDLE_VALUE;
1737 HANDLE hDestinationFile = INVALID_HANDLE_VALUE;
1738
1739 hr = BuffReadNumber64((BYTE*)pMsg->pvData, pMsg->cbData, &iData, reinterpret_cast<DWORD64*>(&liTotalFileSize.QuadPart));
1740 ExitOnFailure(hr, "Failed to read total file size for progress.");
1741
1742 hr = BuffReadNumber64((BYTE*)pMsg->pvData, pMsg->cbData, &iData, reinterpret_cast<DWORD64*>(&liTotalBytesTransferred.QuadPart));
1743 ExitOnFailure(hr, "Failed to read total bytes transferred for progress.");
1744
1745 *pdwResult = pfnProgress(liTotalFileSize, liTotalBytesTransferred, liStreamSize, liStreamBytesTransferred, dwStreamNumber, dwCallbackReason, hSourceFile, hDestinationFile, pvContext);
1746
1747LExit:
1748 return hr;
1749}
1750
1751static HRESULT ProcessElevatedChildMessage(
1752 __in BURN_PIPE_MESSAGE* pMsg,
1753 __in_opt LPVOID pvContext,
1754 __out DWORD* pdwResult
1755 )
1756{
1757 HRESULT hr = S_OK;
1758 BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_CHILD_MESSAGE_CONTEXT*>(pvContext);
1759 HRESULT hrResult = S_OK;
1760 DWORD dwPid = 0;
1761
1762 switch (pMsg->dwMessage)
1763 {
1764 case BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION:
1765 hrResult = OnMsiBeginTransaction(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData);
1766 break;
1767
1768 case BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION:
1769 hrResult = OnMsiCommitTransaction(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData);
1770 break;
1771
1772 case BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION:
1773 hrResult = OnMsiRollbackTransaction(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData);
1774 break;
1775
1776 case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE:
1777 hrResult = OnApplyInitialize(pContext->hPipe, pContext->pVariables, pContext->pRegistration, pContext->phLock, pContext->pfDisabledAutomaticUpdates, (BYTE*)pMsg->pvData, pMsg->cbData);
1778 break;
1779
1780 case BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE:
1781 hrResult = OnApplyUninitialize(pContext->phLock);
1782 break;
1783
1784 case BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN:
1785 hrResult = OnSessionBegin(pContext->pRegistration, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
1786 break;
1787
1788 case BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME:
1789 hrResult = OnSessionResume(pContext->pRegistration, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
1790 break;
1791
1792 case BURN_ELEVATION_MESSAGE_TYPE_SESSION_END:
1793 hrResult = OnSessionEnd(pContext->pPackages, pContext->pRegistration, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
1794 break;
1795
1796 case BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE:
1797 hrResult = OnSaveState(pContext->pRegistration, (BYTE*)pMsg->pvData, pMsg->cbData);
1798 break;
1799
1800 case BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION:
1801 hrResult = OnProcessDependentRegistration(pContext->pRegistration, (BYTE*)pMsg->pvData, pMsg->cbData);
1802 break;
1803
1804 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE:
1805 hrResult = OnExecuteExePackage(pContext->hPipe, pContext->pPackages, &pContext->pRegistration->relatedBundles, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
1806 break;
1807
1808 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE:
1809 hrResult = OnExecuteMsiPackage(pContext->hPipe, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
1810 break;
1811
1812 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE:
1813 hrResult = OnExecuteMspPackage(pContext->hPipe, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
1814 break;
1815
1816 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE:
1817 hrResult = OnExecuteMsuPackage(pContext->hPipe, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
1818 break;
1819
1820 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER:
1821 hrResult = OnExecutePackageProviderAction(pContext->pPackages, &pContext->pRegistration->relatedBundles, (BYTE*)pMsg->pvData, pMsg->cbData);
1822 break;
1823
1824 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY:
1825 hrResult = OnExecutePackageDependencyAction(pContext->pPackages, &pContext->pRegistration->relatedBundles, (BYTE*)pMsg->pvData, pMsg->cbData);
1826 break;
1827
1828 case BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE:
1829 hrResult = OnCleanPackage(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData);
1830 break;
1831
1832 case BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE:
1833 hrResult = OnLaunchApprovedExe(pContext->hPipe, pContext->pApprovedExes, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
1834 break;
1835
1836 default:
1837 hr = E_INVALIDARG;
1838 ExitOnRootFailure(hr, "Unexpected elevated message sent to child process, msg: %u", pMsg->dwMessage);
1839 }
1840
1841 *pdwResult = dwPid ? dwPid : (DWORD)hrResult;
1842
1843LExit:
1844 return hr;
1845}
1846
1847static HRESULT ProcessElevatedChildCacheMessage(
1848 __in BURN_PIPE_MESSAGE* pMsg,
1849 __in_opt LPVOID pvContext,
1850 __out DWORD* pdwResult
1851 )
1852{
1853 HRESULT hr = S_OK;
1854 BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_CHILD_MESSAGE_CONTEXT*>(pvContext);
1855 HRESULT hrResult = S_OK;
1856
1857 switch (pMsg->dwMessage)
1858 {
1859 case BURN_ELEVATION_MESSAGE_TYPE_CACHE_COMPLETE_PAYLOAD:
1860 hrResult = OnCacheCompletePayload(pContext->hPipe, pContext->pPackages, pContext->pPayloads, (BYTE*)pMsg->pvData, pMsg->cbData);
1861 break;
1862
1863 case BURN_ELEVATION_MESSAGE_TYPE_CACHE_VERIFY_PAYLOAD:
1864 hrResult = OnCacheVerifyPayload(pContext->hPipe, pContext->pPackages, pContext->pPayloads, (BYTE*)pMsg->pvData, pMsg->cbData);
1865 break;
1866
1867 case BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP:
1868 OnCacheCleanup(pContext->pRegistration->sczId);
1869 hrResult = S_OK;
1870 break;
1871
1872 case BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE:
1873 hrResult = OnCleanPackage(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData);
1874 break;
1875
1876 default:
1877 hr = E_INVALIDARG;
1878 ExitOnRootFailure(hr, "Unexpected elevated cache message sent to child process, msg: %u", pMsg->dwMessage);
1879 }
1880
1881 *pdwResult = (DWORD)hrResult;
1882
1883LExit:
1884 return hr;
1885}
1886
1887static HRESULT ProcessResult(
1888 __in DWORD dwResult,
1889 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
1890 )
1891{
1892 HRESULT hr = static_cast<HRESULT>(dwResult);
1893 if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == hr)
1894 {
1895 *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
1896 hr = S_OK;
1897 }
1898 else if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED) == hr)
1899 {
1900 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
1901 hr = S_OK;
1902 }
1903
1904 return hr;
1905}
1906
1907static HRESULT OnApplyInitialize(
1908 __in HANDLE hPipe,
1909 __in BURN_VARIABLES* pVariables,
1910 __in BURN_REGISTRATION* pRegistration,
1911 __in HANDLE* phLock,
1912 __in BOOL* pfDisabledWindowsUpdate,
1913 __in BYTE* pbData,
1914 __in SIZE_T cbData
1915 )
1916{
1917 HRESULT hr = S_OK;
1918 SIZE_T iData = 0;
1919 DWORD dwAction = 0;
1920 DWORD dwAUAction = 0;
1921 DWORD dwTakeSystemRestorePoint = 0;
1922 LPWSTR sczBundleName = NULL;
1923 HRESULT hrStatus = S_OK;
1924
1925 // Deserialize message data.
1926 hr = BuffReadNumber(pbData, cbData, &iData, &dwAction);
1927 ExitOnFailure(hr, "Failed to read action.");
1928
1929 hr = BuffReadNumber(pbData, cbData, &iData, &dwAUAction);
1930 ExitOnFailure(hr, "Failed to read update action.");
1931
1932 hr = BuffReadNumber(pbData, cbData, &iData, &dwTakeSystemRestorePoint);
1933 ExitOnFailure(hr, "Failed to read system restore point action.");
1934
1935 hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
1936 ExitOnFailure(hr, "Failed to read variables.");
1937
1938 // Initialize.
1939 hr = ApplyLock(TRUE, phLock);
1940 ExitOnFailure(hr, "Failed to acquire lock due to setup in other session.");
1941
1942 // Reset and reload the related bundles.
1943 RelatedBundlesUninitialize(&pRegistration->relatedBundles);
1944
1945 hr = RelatedBundlesInitializeForScope(TRUE, pRegistration, &pRegistration->relatedBundles);
1946 ExitOnFailure(hr, "Failed to initialize per-machine related bundles.");
1947
1948 // Attempt to pause AU with best effort.
1949 if (BURN_AU_PAUSE_ACTION_IFELEVATED == dwAUAction || BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME == dwAUAction)
1950 {
1951 hr = ElevatedOnPauseAUBegin(hPipe);
1952 ExitOnFailure(hr, "ElevatedOnPauseAUBegin failed.");
1953
1954 LogId(REPORT_STANDARD, MSG_PAUSE_AU_STARTING);
1955
1956 hrStatus = hr = WuaPauseAutomaticUpdates();
1957 if (FAILED(hr))
1958 {
1959 LogId(REPORT_STANDARD, MSG_FAILED_PAUSE_AU, hr);
1960 hr = S_OK;
1961 }
1962 else
1963 {
1964 LogId(REPORT_STANDARD, MSG_PAUSE_AU_SUCCEEDED);
1965 if (BURN_AU_PAUSE_ACTION_IFELEVATED == dwAUAction)
1966 {
1967 *pfDisabledWindowsUpdate = TRUE;
1968 }
1969 }
1970
1971 hr = ElevatedOnPauseAUComplete(hPipe, hrStatus);
1972 ExitOnFailure(hr, "ElevatedOnPauseAUComplete failed.");
1973 }
1974
1975 if (dwTakeSystemRestorePoint)
1976 {
1977 hr = VariableGetString(pVariables, BURN_BUNDLE_NAME, &sczBundleName);
1978 if (FAILED(hr))
1979 {
1980 hr = S_OK;
1981 ExitFunction();
1982 }
1983
1984 hr = ElevatedOnSystemRestorePointBegin(hPipe);
1985 ExitOnFailure(hr, "ElevatedOnSystemRestorePointBegin failed.");
1986
1987 LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_STARTING);
1988
1989 BOOTSTRAPPER_ACTION action = static_cast<BOOTSTRAPPER_ACTION>(dwAction);
1990 SRP_ACTION restoreAction = (BOOTSTRAPPER_ACTION_INSTALL == action) ? SRP_ACTION_INSTALL : (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? SRP_ACTION_UNINSTALL : SRP_ACTION_MODIFY;
1991 hrStatus = hr = SrpCreateRestorePoint(sczBundleName, restoreAction);
1992 if (SUCCEEDED(hr))
1993 {
1994 LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_SUCCEEDED);
1995 }
1996 else if (E_NOTIMPL == hr)
1997 {
1998 LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_DISABLED);
1999 hr = S_OK;
2000 }
2001 else
2002 {
2003 LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_FAILED, hr);
2004 hr = S_OK;
2005 }
2006
2007 hr = ElevatedOnSystemRestorePointComplete(hPipe, hrStatus);
2008 ExitOnFailure(hr, "ElevatedOnSystemRestorePointComplete failed.");
2009 }
2010
2011LExit:
2012 ReleaseStr(sczBundleName);
2013 return hr;
2014}
2015
2016static HRESULT OnApplyUninitialize(
2017 __in HANDLE* phLock
2018 )
2019{
2020 Assert(phLock);
2021
2022 // TODO: end system restore point.
2023
2024 if (*phLock)
2025 {
2026 ::ReleaseMutex(*phLock);
2027 ::CloseHandle(*phLock);
2028 *phLock = NULL;
2029 }
2030
2031 return S_OK;
2032}
2033
2034static HRESULT OnSessionBegin(
2035 __in BURN_REGISTRATION* pRegistration,
2036 __in BURN_VARIABLES* pVariables,
2037 __in BYTE* pbData,
2038 __in SIZE_T cbData
2039 )
2040{
2041 HRESULT hr = S_OK;
2042 SIZE_T iData = 0;
2043 LPWSTR sczEngineWorkingPath = NULL;
2044 DWORD dwRegistrationOperations = 0;
2045 DWORD dwDependencyRegistrationAction = 0;
2046 DWORD64 qwEstimatedSize = 0;
2047
2048 // Deserialize message data.
2049 hr = BuffReadString(pbData, cbData, &iData, &sczEngineWorkingPath);
2050 ExitOnFailure(hr, "Failed to read engine working path.");
2051
2052 hr = BuffReadString(pbData, cbData, &iData, &pRegistration->sczResumeCommandLine);
2053 ExitOnFailure(hr, "Failed to read resume command line.");
2054
2055 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&pRegistration->fDisableResume);
2056 ExitOnFailure(hr, "Failed to read resume flag.");
2057
2058 hr = BuffReadNumber(pbData, cbData, &iData, &dwRegistrationOperations);
2059 ExitOnFailure(hr, "Failed to read registration operations.");
2060
2061 hr = BuffReadNumber(pbData, cbData, &iData, &dwDependencyRegistrationAction);
2062 ExitOnFailure(hr, "Failed to read dependency registration action.");
2063
2064 hr = BuffReadNumber64(pbData, cbData, &iData, &qwEstimatedSize);
2065 ExitOnFailure(hr, "Failed to read estimated size.");
2066
2067 hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
2068 ExitOnFailure(hr, "Failed to read variables.");
2069
2070 // Begin session in per-machine process.
2071 hr = RegistrationSessionBegin(sczEngineWorkingPath, pRegistration, pVariables, dwRegistrationOperations, (BURN_DEPENDENCY_REGISTRATION_ACTION)dwDependencyRegistrationAction, qwEstimatedSize);
2072 ExitOnFailure(hr, "Failed to begin registration session.");
2073
2074LExit:
2075 ReleaseStr(sczEngineWorkingPath);
2076
2077 return hr;
2078}
2079
2080static HRESULT OnSessionResume(
2081 __in BURN_REGISTRATION* pRegistration,
2082 __in BURN_VARIABLES* pVariables,
2083 __in BYTE* pbData,
2084 __in SIZE_T cbData
2085 )
2086{
2087 HRESULT hr = S_OK;
2088 SIZE_T iData = 0;
2089
2090 // Deserialize message data.
2091 hr = BuffReadString(pbData, cbData, &iData, &pRegistration->sczResumeCommandLine);
2092 ExitOnFailure(hr, "Failed to read resume command line.");
2093
2094 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&pRegistration->fDisableResume);
2095 ExitOnFailure(hr, "Failed to read resume flag.");
2096
2097 hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
2098 ExitOnFailure(hr, "Failed to read variables.");
2099
2100 // resume session in per-machine process
2101 hr = RegistrationSessionResume(pRegistration, pVariables);
2102 ExitOnFailure(hr, "Failed to resume registration session.");
2103
2104LExit:
2105 return hr;
2106}
2107
2108static HRESULT OnSessionEnd(
2109 __in BURN_PACKAGES* pPackages,
2110 __in BURN_REGISTRATION* pRegistration,
2111 __in BURN_VARIABLES* pVariables,
2112 __in BYTE* pbData,
2113 __in SIZE_T cbData
2114 )
2115{
2116 HRESULT hr = S_OK;
2117 SIZE_T iData = 0;
2118 DWORD dwResumeMode = 0;
2119 DWORD dwRestart = 0;
2120 DWORD dwDependencyRegistrationAction = 0;
2121
2122 // Deserialize message data.
2123 hr = BuffReadNumber(pbData, cbData, &iData, &dwResumeMode);
2124 ExitOnFailure(hr, "Failed to read resume mode enum.");
2125
2126 hr = BuffReadNumber(pbData, cbData, &iData, &dwRestart);
2127 ExitOnFailure(hr, "Failed to read restart enum.");
2128
2129 hr = BuffReadNumber(pbData, cbData, &iData, &dwDependencyRegistrationAction);
2130 ExitOnFailure(hr, "Failed to read dependency registration action.");
2131
2132 // suspend session in per-machine process
2133 hr = RegistrationSessionEnd(pRegistration, pVariables, pPackages, (BURN_RESUME_MODE)dwResumeMode, (BOOTSTRAPPER_APPLY_RESTART)dwRestart, (BURN_DEPENDENCY_REGISTRATION_ACTION)dwDependencyRegistrationAction);
2134 ExitOnFailure(hr, "Failed to suspend registration session.");
2135
2136LExit:
2137 return hr;
2138}
2139
2140static HRESULT OnSaveState(
2141 __in BURN_REGISTRATION* pRegistration,
2142 __in BYTE* pbData,
2143 __in SIZE_T cbData
2144 )
2145{
2146 HRESULT hr = S_OK;
2147
2148 // save state in per-machine process
2149 hr = RegistrationSaveState(pRegistration, pbData, cbData);
2150 ExitOnFailure(hr, "Failed to save state.");
2151
2152LExit:
2153 return hr;
2154}
2155
2156static HRESULT OnCacheCompletePayload(
2157 __in HANDLE hPipe,
2158 __in BURN_PACKAGES* pPackages,
2159 __in BURN_PAYLOADS* pPayloads,
2160 __in BYTE* pbData,
2161 __in SIZE_T cbData
2162 )
2163{
2164 HRESULT hr = S_OK;
2165 SIZE_T iData = 0;
2166 LPWSTR scz = NULL;
2167 BURN_PACKAGE* pPackage = NULL;
2168 BURN_PAYLOAD* pPayload = NULL;
2169 LPWSTR sczUnverifiedPath = NULL;
2170 BOOL fMove = FALSE;
2171
2172 // Deserialize message data.
2173 hr = BuffReadString(pbData, cbData, &iData, &scz);
2174 ExitOnFailure(hr, "Failed to read package id.");
2175
2176 if (scz && *scz)
2177 {
2178 hr = PackageFindById(pPackages, scz, &pPackage);
2179 ExitOnFailure(hr, "Failed to find package: %ls", scz);
2180 }
2181
2182 hr = BuffReadString(pbData, cbData, &iData, &scz);
2183 ExitOnFailure(hr, "Failed to read payload id.");
2184
2185 if (scz && *scz)
2186 {
2187 hr = PayloadFindById(pPayloads, scz, &pPayload);
2188 ExitOnFailure(hr, "Failed to find payload: %ls", scz);
2189 }
2190
2191 hr = BuffReadString(pbData, cbData, &iData, &sczUnverifiedPath);
2192 ExitOnFailure(hr, "Failed to read unverified path.");
2193
2194 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fMove);
2195 ExitOnFailure(hr, "Failed to read move flag.");
2196
2197 if (pPackage && pPayload) // complete payload.
2198 {
2199 hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, sczUnverifiedPath, fMove, BurnCacheMessageHandler, ElevatedProgressRoutine, hPipe);
2200 ExitOnFailure(hr, "Failed to cache payload: %ls", pPayload->sczKey);
2201 }
2202 else
2203 {
2204 hr = E_INVALIDARG;
2205 ExitOnRootFailure(hr, "Invalid data passed to cache complete payload.");
2206 }
2207
2208LExit:
2209 ReleaseStr(sczUnverifiedPath);
2210 ReleaseStr(scz);
2211
2212 return hr;
2213}
2214
2215static HRESULT OnCacheVerifyPayload(
2216 __in HANDLE hPipe,
2217 __in BURN_PACKAGES* pPackages,
2218 __in BURN_PAYLOADS* pPayloads,
2219 __in BYTE* pbData,
2220 __in SIZE_T cbData
2221 )
2222{
2223 HRESULT hr = S_OK;
2224 SIZE_T iData = 0;
2225 LPWSTR scz = NULL;
2226 BURN_PACKAGE* pPackage = NULL;
2227 BURN_PAYLOAD* pPayload = NULL;
2228 LPWSTR sczCacheDirectory = NULL;
2229
2230 // Deserialize message data.
2231 hr = BuffReadString(pbData, cbData, &iData, &scz);
2232 ExitOnFailure(hr, "Failed to read package id.");
2233
2234 if (scz && *scz)
2235 {
2236 hr = PackageFindById(pPackages, scz, &pPackage);
2237 ExitOnFailure(hr, "Failed to find package: %ls", scz);
2238 }
2239
2240 hr = BuffReadString(pbData, cbData, &iData, &scz);
2241 ExitOnFailure(hr, "Failed to read payload id.");
2242
2243 if (scz && *scz)
2244 {
2245 hr = PayloadFindById(pPayloads, scz, &pPayload);
2246 ExitOnFailure(hr, "Failed to find payload: %ls", scz);
2247 }
2248
2249 if (pPackage && pPayload)
2250 {
2251 hr = CacheGetCompletedPath(TRUE, pPackage->sczCacheId, &sczCacheDirectory);
2252 ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", pPackage->sczCacheId);
2253
2254 hr = CacheVerifyPayload(pPayload, sczCacheDirectory, BurnCacheMessageHandler, ElevatedProgressRoutine, hPipe);
2255 }
2256 else
2257 {
2258 hr = E_INVALIDARG;
2259 ExitOnRootFailure(hr, "Invalid data passed to cache verify payload.");
2260 }
2261 // Nothing should be logged on failure.
2262
2263LExit:
2264 ReleaseStr(sczCacheDirectory);
2265 ReleaseStr(scz);
2266
2267 return hr;
2268}
2269
2270static void OnCacheCleanup(
2271 __in_z LPCWSTR wzBundleId
2272 )
2273{
2274 CacheCleanup(TRUE, wzBundleId);
2275}
2276
2277static HRESULT OnProcessDependentRegistration(
2278 __in const BURN_REGISTRATION* pRegistration,
2279 __in BYTE* pbData,
2280 __in SIZE_T cbData
2281 )
2282{
2283 HRESULT hr = S_OK;
2284 SIZE_T iData = 0;
2285 BURN_DEPENDENT_REGISTRATION_ACTION action = { };
2286
2287 // Deserialize message data.
2288 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&action.type);
2289 ExitOnFailure(hr, "Failed to read action type.");
2290
2291 hr = BuffReadString(pbData, cbData, &iData, &action.sczBundleId);
2292 ExitOnFailure(hr, "Failed to read bundle id.");
2293
2294 hr = BuffReadString(pbData, cbData, &iData, &action.sczDependentProviderKey);
2295 ExitOnFailure(hr, "Failed to read dependent provider key.");
2296
2297 // Execute the registration action.
2298 hr = DependencyProcessDependentRegistration(pRegistration, &action);
2299 ExitOnFailure(hr, "Failed to execute dependent registration action for provider key: %ls", action.sczDependentProviderKey);
2300
2301LExit:
2302 // TODO: do the right thing here.
2303 //DependencyUninitializeRegistrationAction(&action);
2304 ReleaseStr(action.sczDependentProviderKey);
2305 ReleaseStr(action.sczBundleId)
2306
2307 return hr;
2308}
2309
2310static HRESULT OnExecuteExePackage(
2311 __in HANDLE hPipe,
2312 __in BURN_PACKAGES* pPackages,
2313 __in BURN_RELATED_BUNDLES* pRelatedBundles,
2314 __in BURN_VARIABLES* pVariables,
2315 __in BYTE* pbData,
2316 __in SIZE_T cbData
2317 )
2318{
2319 HRESULT hr = S_OK;
2320 SIZE_T iData = 0;
2321 LPWSTR sczPackage = NULL;
2322 DWORD dwRollback = 0;
2323 BURN_EXECUTE_ACTION executeAction = { };
2324 LPWSTR sczIgnoreDependencies = NULL;
2325 LPWSTR sczAncestors = NULL;
2326 BOOTSTRAPPER_APPLY_RESTART exeRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2327
2328 executeAction.type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE;
2329
2330 // Deserialize message data.
2331 hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2332 ExitOnFailure(hr, "Failed to read EXE package id.");
2333
2334 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.exePackage.action);
2335 ExitOnFailure(hr, "Failed to read action.");
2336
2337 hr = BuffReadNumber(pbData, cbData, &iData, &dwRollback);
2338 ExitOnFailure(hr, "Failed to read rollback.");
2339
2340 hr = BuffReadString(pbData, cbData, &iData, &sczIgnoreDependencies);
2341 ExitOnFailure(hr, "Failed to read the list of dependencies to ignore.");
2342
2343 hr = BuffReadString(pbData, cbData, &iData, &sczAncestors);
2344 ExitOnFailure(hr, "Failed to read the list of ancestors.");
2345
2346 hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
2347 ExitOnFailure(hr, "Failed to read variables.");
2348
2349 hr = PackageFindById(pPackages, sczPackage, &executeAction.exePackage.pPackage);
2350 if (E_NOTFOUND == hr)
2351 {
2352 hr = PackageFindRelatedById(pRelatedBundles, sczPackage, &executeAction.exePackage.pPackage);
2353 }
2354 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2355
2356 // Pass the list of dependencies to ignore, if any, to the related bundle.
2357 if (sczIgnoreDependencies && *sczIgnoreDependencies)
2358 {
2359 hr = StrAllocString(&executeAction.exePackage.sczIgnoreDependencies, sczIgnoreDependencies, 0);
2360 ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
2361 }
2362
2363 // Pass the list of ancestors, if any, to the related bundle.
2364 if (sczAncestors && *sczAncestors)
2365 {
2366 hr = StrAllocString(&executeAction.exePackage.sczAncestors, sczAncestors, 0);
2367 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
2368 }
2369
2370 // Execute EXE package.
2371 hr = ExeEngineExecutePackage(&executeAction, pVariables, static_cast<BOOL>(dwRollback), GenericExecuteMessageHandler, hPipe, &exeRestart);
2372 ExitOnFailure(hr, "Failed to execute EXE package.");
2373
2374LExit:
2375 ReleaseStr(sczAncestors);
2376 ReleaseStr(sczIgnoreDependencies);
2377 ReleaseStr(sczPackage);
2378 PlanUninitializeExecuteAction(&executeAction);
2379
2380 if (SUCCEEDED(hr))
2381 {
2382 if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == exeRestart)
2383 {
2384 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED);
2385 }
2386 else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == exeRestart)
2387 {
2388 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED);
2389 }
2390 }
2391
2392 return hr;
2393}
2394
2395static HRESULT OnExecuteMsiPackage(
2396 __in HANDLE hPipe,
2397 __in BURN_PACKAGES* pPackages,
2398 __in BURN_VARIABLES* pVariables,
2399 __in BYTE* pbData,
2400 __in SIZE_T cbData
2401 )
2402{
2403 HRESULT hr = S_OK;
2404 SIZE_T iData = 0;
2405 LPWSTR sczPackage = NULL;
2406 HWND hwndParent = NULL;
2407 BOOL fRollback = 0;
2408 BURN_EXECUTE_ACTION executeAction = { };
2409 BOOTSTRAPPER_APPLY_RESTART msiRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2410
2411 executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE;
2412
2413 // Deserialize message data.
2414 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback);
2415 ExitOnFailure(hr, "Failed to read rollback flag.");
2416
2417 hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2418 ExitOnFailure(hr, "Failed to read MSI package id.");
2419
2420 hr = PackageFindById(pPackages, sczPackage, &executeAction.msiPackage.pPackage);
2421 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2422
2423 hr = BuffReadPointer(pbData, cbData, &iData, (DWORD_PTR*)&hwndParent);
2424 ExitOnFailure(hr, "Failed to read parent hwnd.");
2425
2426 hr = BuffReadString(pbData, cbData, &iData, &executeAction.msiPackage.sczLogPath);
2427 ExitOnFailure(hr, "Failed to read package log.");
2428
2429 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.actionMsiProperty);
2430 ExitOnFailure(hr, "Failed to read actionMsiProperty.");
2431
2432 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.uiLevel);
2433 ExitOnFailure(hr, "Failed to read UI level.");
2434
2435 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.fDisableExternalUiHandler);
2436 ExitOnFailure(hr, "Failed to read fDisableExternalUiHandler.");
2437
2438 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.action);
2439 ExitOnFailure(hr, "Failed to read action.");
2440
2441 // Read feature actions.
2442 if (executeAction.msiPackage.pPackage->Msi.cFeatures)
2443 {
2444 executeAction.msiPackage.rgFeatures = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(executeAction.msiPackage.pPackage->Msi.cFeatures * sizeof(BOOTSTRAPPER_FEATURE_ACTION), TRUE);
2445 ExitOnNull(executeAction.msiPackage.rgFeatures, hr, E_OUTOFMEMORY, "Failed to allocate memory for feature actions.");
2446
2447 for (DWORD i = 0; i < executeAction.msiPackage.pPackage->Msi.cFeatures; ++i)
2448 {
2449 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.rgFeatures[i]);
2450 ExitOnFailure(hr, "Failed to read feature action.");
2451 }
2452 }
2453
2454 // Read slipstream patches actions.
2455 if (executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages)
2456 {
2457 for (DWORD i = 0; i < executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++i)
2458 {
2459 BURN_SLIPSTREAM_MSP* pSlipstreamMsp = executeAction.msiPackage.pPackage->Msi.rgSlipstreamMsps + i;
2460 BOOTSTRAPPER_ACTION_STATE* pAction = fRollback ? &pSlipstreamMsp->rollback : &pSlipstreamMsp->execute;
2461 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)pAction);
2462 ExitOnFailure(hr, "Failed to read slipstream action.");
2463 }
2464 }
2465
2466 hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
2467 ExitOnFailure(hr, "Failed to read variables.");
2468
2469 // Execute MSI package.
2470 hr = MsiEngineExecutePackage(hwndParent, &executeAction, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &msiRestart);
2471 ExitOnFailure(hr, "Failed to execute MSI package.");
2472
2473LExit:
2474 ReleaseStr(sczPackage);
2475 PlanUninitializeExecuteAction(&executeAction);
2476
2477 if (SUCCEEDED(hr))
2478 {
2479 if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == msiRestart)
2480 {
2481 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED);
2482 }
2483 else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == msiRestart)
2484 {
2485 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED);
2486 }
2487 }
2488
2489 return hr;
2490}
2491
2492static HRESULT OnExecuteMspPackage(
2493 __in HANDLE hPipe,
2494 __in BURN_PACKAGES* pPackages,
2495 __in BURN_VARIABLES* pVariables,
2496 __in BYTE* pbData,
2497 __in SIZE_T cbData
2498 )
2499{
2500 HRESULT hr = S_OK;
2501 SIZE_T iData = 0;
2502 LPWSTR sczPackage = NULL;
2503 HWND hwndParent = NULL;
2504 BOOL fRollback = 0;
2505 BURN_EXECUTE_ACTION executeAction = { };
2506 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2507
2508 executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSP_TARGET;
2509
2510 // Deserialize message data.
2511 hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2512 ExitOnFailure(hr, "Failed to read MSP package id.");
2513
2514 hr = PackageFindById(pPackages, sczPackage, &executeAction.mspTarget.pPackage);
2515 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2516
2517 hr = BuffReadPointer(pbData, cbData, &iData, (DWORD_PTR*)&hwndParent);
2518 ExitOnFailure(hr, "Failed to read parent hwnd.");
2519
2520 executeAction.mspTarget.fPerMachineTarget = TRUE; // we're in the elevated process, clearly we're targeting a per-machine product.
2521
2522 hr = BuffReadString(pbData, cbData, &iData, &executeAction.mspTarget.sczTargetProductCode);
2523 ExitOnFailure(hr, "Failed to read target product code.");
2524
2525 hr = BuffReadString(pbData, cbData, &iData, &executeAction.mspTarget.sczLogPath);
2526 ExitOnFailure(hr, "Failed to read package log.");
2527
2528 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.actionMsiProperty);
2529 ExitOnFailure(hr, "Failed to read actionMsiProperty.");
2530
2531 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.uiLevel);
2532 ExitOnFailure(hr, "Failed to read UI level.");
2533
2534 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.fDisableExternalUiHandler);
2535 ExitOnFailure(hr, "Failed to read fDisableExternalUiHandler.");
2536
2537 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.action);
2538 ExitOnFailure(hr, "Failed to read action.");
2539
2540 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.cOrderedPatches);
2541 ExitOnFailure(hr, "Failed to read count of ordered patches.");
2542
2543 if (executeAction.mspTarget.cOrderedPatches)
2544 {
2545 executeAction.mspTarget.rgOrderedPatches = (BURN_ORDERED_PATCHES*)MemAlloc(executeAction.mspTarget.cOrderedPatches * sizeof(BURN_ORDERED_PATCHES), TRUE);
2546 ExitOnNull(executeAction.mspTarget.rgOrderedPatches, hr, E_OUTOFMEMORY, "Failed to allocate memory for ordered patches.");
2547
2548 for (DWORD i = 0; i < executeAction.mspTarget.cOrderedPatches; ++i)
2549 {
2550 hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2551 ExitOnFailure(hr, "Failed to read ordered patch package id.");
2552
2553 hr = PackageFindById(pPackages, sczPackage, &executeAction.mspTarget.rgOrderedPatches[i].pPackage);
2554 ExitOnFailure(hr, "Failed to find ordered patch package: %ls", sczPackage);
2555 }
2556 }
2557
2558 hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
2559 ExitOnFailure(hr, "Failed to read variables.");
2560
2561 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback);
2562 ExitOnFailure(hr, "Failed to read rollback flag.");
2563
2564 // Execute MSP package.
2565 hr = MspEngineExecutePackage(hwndParent, &executeAction, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &restart);
2566 ExitOnFailure(hr, "Failed to execute MSP package.");
2567
2568LExit:
2569 ReleaseStr(sczPackage);
2570 PlanUninitializeExecuteAction(&executeAction);
2571
2572 if (SUCCEEDED(hr))
2573 {
2574 if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == restart)
2575 {
2576 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED);
2577 }
2578 else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart)
2579 {
2580 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED);
2581 }
2582 }
2583
2584 return hr;
2585}
2586
2587static HRESULT OnExecuteMsuPackage(
2588 __in HANDLE hPipe,
2589 __in BURN_PACKAGES* pPackages,
2590 __in BURN_VARIABLES* pVariables,
2591 __in BYTE* pbData,
2592 __in SIZE_T cbData
2593 )
2594{
2595 HRESULT hr = S_OK;
2596 SIZE_T iData = 0;
2597 LPWSTR sczPackage = NULL;
2598 DWORD dwRollback = 0;
2599 DWORD dwStopWusaService = 0;
2600 BURN_EXECUTE_ACTION executeAction = { };
2601 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2602
2603 executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE;
2604
2605 // Deserialize message data.
2606 hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2607 ExitOnFailure(hr, "Failed to read MSU package id.");
2608
2609 hr = BuffReadString(pbData, cbData, &iData, &executeAction.msuPackage.sczLogPath);
2610 ExitOnFailure(hr, "Failed to read package log.");
2611
2612 hr = BuffReadNumber(pbData, cbData, &iData, reinterpret_cast<DWORD*>(&executeAction.msuPackage.action));
2613 ExitOnFailure(hr, "Failed to read action.");
2614
2615 hr = BuffReadNumber(pbData, cbData, &iData, &dwRollback);
2616 ExitOnFailure(hr, "Failed to read rollback.");
2617
2618 hr = BuffReadNumber(pbData, cbData, &iData, &dwStopWusaService);
2619 ExitOnFailure(hr, "Failed to read StopWusaService.");
2620
2621 hr = PackageFindById(pPackages, sczPackage, &executeAction.msuPackage.pPackage);
2622 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2623
2624 // execute MSU package
2625 hr = MsuEngineExecutePackage(&executeAction, pVariables, static_cast<BOOL>(dwRollback), static_cast<BOOL>(dwStopWusaService), GenericExecuteMessageHandler, hPipe, &restart);
2626 ExitOnFailure(hr, "Failed to execute MSU package.");
2627
2628LExit:
2629 ReleaseStr(sczPackage);
2630 PlanUninitializeExecuteAction(&executeAction);
2631
2632 if (SUCCEEDED(hr))
2633 {
2634 if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == restart)
2635 {
2636 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED);
2637 }
2638 else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart)
2639 {
2640 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED);
2641 }
2642 }
2643
2644 return hr;
2645}
2646
2647static HRESULT OnExecutePackageProviderAction(
2648 __in BURN_PACKAGES* pPackages,
2649 __in BURN_RELATED_BUNDLES* pRelatedBundles,
2650 __in BYTE* pbData,
2651 __in SIZE_T cbData
2652 )
2653{
2654 HRESULT hr = S_OK;
2655 SIZE_T iData = 0;
2656 LPWSTR sczPackage = NULL;
2657 BURN_EXECUTE_ACTION executeAction = { };
2658
2659 executeAction.type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER;
2660
2661 // Deserialize the message data.
2662 hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2663 ExitOnFailure(hr, "Failed to read package id from message buffer.");
2664
2665 hr = BuffReadNumber(pbData, cbData, &iData, reinterpret_cast<DWORD*>(&executeAction.packageProvider.action));
2666 ExitOnFailure(hr, "Failed to read action.");
2667
2668 // Find the package again.
2669 hr = PackageFindById(pPackages, sczPackage, &executeAction.packageProvider.pPackage);
2670 if (E_NOTFOUND == hr)
2671 {
2672 hr = PackageFindRelatedById(pRelatedBundles, sczPackage, &executeAction.packageProvider.pPackage);
2673 }
2674 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2675
2676 // Execute the package provider action.
2677 hr = DependencyExecutePackageProviderAction(&executeAction);
2678 ExitOnFailure(hr, "Failed to execute package provider action.");
2679
2680LExit:
2681 ReleaseStr(sczPackage);
2682 PlanUninitializeExecuteAction(&executeAction);
2683
2684 return hr;
2685}
2686
2687static HRESULT OnExecutePackageDependencyAction(
2688 __in BURN_PACKAGES* pPackages,
2689 __in BURN_RELATED_BUNDLES* pRelatedBundles,
2690 __in BYTE* pbData,
2691 __in SIZE_T cbData
2692 )
2693{
2694 HRESULT hr = S_OK;
2695 SIZE_T iData = 0;
2696 LPWSTR sczPackage = NULL;
2697 BURN_EXECUTE_ACTION executeAction = { };
2698
2699 executeAction.type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY;
2700
2701 // Deserialize the message data.
2702 hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2703 ExitOnFailure(hr, "Failed to read package id from message buffer.");
2704
2705 hr = BuffReadString(pbData, cbData, &iData, &executeAction.packageDependency.sczBundleProviderKey);
2706 ExitOnFailure(hr, "Failed to read bundle dependency key from message buffer.");
2707
2708 hr = BuffReadNumber(pbData, cbData, &iData, reinterpret_cast<DWORD*>(&executeAction.packageDependency.action));
2709 ExitOnFailure(hr, "Failed to read action.");
2710
2711 // Find the package again.
2712 hr = PackageFindById(pPackages, sczPackage, &executeAction.packageDependency.pPackage);
2713 if (E_NOTFOUND == hr)
2714 {
2715 hr = PackageFindRelatedById(pRelatedBundles, sczPackage, &executeAction.packageDependency.pPackage);
2716 }
2717 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2718
2719 // Execute the package dependency action.
2720 hr = DependencyExecutePackageDependencyAction(TRUE, &executeAction);
2721 ExitOnFailure(hr, "Failed to execute package dependency action.");
2722
2723LExit:
2724 ReleaseStr(sczPackage);
2725 PlanUninitializeExecuteAction(&executeAction);
2726
2727 return hr;
2728}
2729
2730static HRESULT CALLBACK BurnCacheMessageHandler(
2731 __in BURN_CACHE_MESSAGE* pMessage,
2732 __in LPVOID pvContext
2733 )
2734{
2735 HRESULT hr = S_OK;
2736 DWORD dwResult = 0;
2737 HANDLE hPipe = (HANDLE)pvContext;
2738 BYTE* pbData = NULL;
2739 SIZE_T cbData = 0;
2740 DWORD dwMessage = 0;
2741
2742 switch (pMessage->type)
2743 {
2744 case BURN_CACHE_MESSAGE_BEGIN:
2745 // serialize message data
2746 hr = BuffWriteNumber(&pbData, &cbData, pMessage->begin.cacheStep);
2747 ExitOnFailure(hr, "Failed to write progress percentage to message buffer.");
2748
2749 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_BEGIN;
2750 break;
2751
2752 case BURN_CACHE_MESSAGE_COMPLETE:
2753 // serialize message data
2754 hr = BuffWriteNumber(&pbData, &cbData, pMessage->complete.hrStatus);
2755 ExitOnFailure(hr, "Failed to write error code to message buffer.");
2756
2757 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_COMPLETE;
2758 break;
2759
2760 case BURN_CACHE_MESSAGE_SUCCESS:
2761 hr = BuffWriteNumber64(&pbData, &cbData, pMessage->success.qwFileSize);
2762 ExitOnFailure(hr, "Failed to count of files in use to message buffer.");
2763
2764 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_BURN_CACHE_SUCCESS;
2765 break;
2766 }
2767
2768 // send message
2769 hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, &dwResult);
2770 ExitOnFailure(hr, "Failed to send burn cache message to per-user process.");
2771
2772 hr = dwResult;
2773
2774LExit:
2775 ReleaseBuffer(pbData);
2776
2777 return hr;
2778}
2779
2780static DWORD CALLBACK ElevatedProgressRoutine(
2781 __in LARGE_INTEGER TotalFileSize,
2782 __in LARGE_INTEGER TotalBytesTransferred,
2783 __in LARGE_INTEGER /*StreamSize*/,
2784 __in LARGE_INTEGER /*StreamBytesTransferred*/,
2785 __in DWORD /*dwStreamNumber*/,
2786 __in DWORD /*dwCallbackReason*/,
2787 __in HANDLE /*hSourceFile*/,
2788 __in HANDLE /*hDestinationFile*/,
2789 __in_opt LPVOID lpData
2790 )
2791{
2792 HRESULT hr = S_OK;
2793 DWORD dwResult = 0;
2794 HANDLE hPipe = (HANDLE)lpData;
2795 BYTE* pbData = NULL;
2796 SIZE_T cbData = 0;
2797 DWORD dwMessage = BURN_ELEVATION_MESSAGE_TYPE_PROGRESS_ROUTINE;
2798
2799 hr = BuffWriteNumber64(&pbData, &cbData, TotalFileSize.QuadPart);
2800 ExitOnFailure(hr, "Failed to write total file size progress to message buffer.");
2801
2802 hr = BuffWriteNumber64(&pbData, &cbData, TotalBytesTransferred.QuadPart);
2803 ExitOnFailure(hr, "Failed to write total bytes transferred progress to message buffer.");
2804
2805 // send message
2806 hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, &dwResult);
2807 ExitOnFailure(hr, "Failed to send progress routine message to per-user process.");
2808
2809LExit:
2810 ReleaseBuffer(pbData);
2811
2812 return dwResult;
2813}
2814
2815static int GenericExecuteMessageHandler(
2816 __in GENERIC_EXECUTE_MESSAGE* pMessage,
2817 __in LPVOID pvContext
2818 )
2819{
2820 HRESULT hr = S_OK;
2821 int nResult = IDOK;
2822 HANDLE hPipe = (HANDLE)pvContext;
2823 BYTE* pbData = NULL;
2824 SIZE_T cbData = 0;
2825 DWORD dwMessage = 0;
2826
2827 hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwAllowedResults);
2828 ExitOnFailure(hr, "Failed to write UI flags.");
2829
2830 switch(pMessage->type)
2831 {
2832 case GENERIC_EXECUTE_MESSAGE_PROGRESS:
2833 // serialize message data
2834 hr = BuffWriteNumber(&pbData, &cbData, pMessage->progress.dwPercentage);
2835 ExitOnFailure(hr, "Failed to write progress percentage to message buffer.");
2836
2837 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS;
2838 break;
2839
2840 case GENERIC_EXECUTE_MESSAGE_ERROR:
2841 // serialize message data
2842 hr = BuffWriteNumber(&pbData, &cbData, pMessage->error.dwErrorCode);
2843 ExitOnFailure(hr, "Failed to write error code to message buffer.");
2844
2845 hr = BuffWriteString(&pbData, &cbData, pMessage->error.wzMessage);
2846 ExitOnFailure(hr, "Failed to write message to message buffer.");
2847
2848 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR;
2849 break;
2850
2851 case GENERIC_EXECUTE_MESSAGE_FILES_IN_USE:
2852 hr = BuffWriteNumber(&pbData, &cbData, pMessage->filesInUse.cFiles);
2853 ExitOnFailure(hr, "Failed to count of files in use to message buffer.");
2854
2855 for (DWORD i = 0; i < pMessage->filesInUse.cFiles; ++i)
2856 {
2857 hr = BuffWriteString(&pbData, &cbData, pMessage->filesInUse.rgwzFiles[i]);
2858 ExitOnFailure(hr, "Failed to write file in use to message buffer.");
2859 }
2860
2861 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE;
2862 break;
2863 }
2864
2865 // send message
2866 hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, reinterpret_cast<DWORD*>(&nResult));
2867 ExitOnFailure(hr, "Failed to send message to per-user process.");
2868
2869LExit:
2870 ReleaseBuffer(pbData);
2871
2872 return nResult;
2873}
2874
2875static int MsiExecuteMessageHandler(
2876 __in WIU_MSI_EXECUTE_MESSAGE* pMessage,
2877 __in_opt LPVOID pvContext
2878 )
2879{
2880 HRESULT hr = S_OK;
2881 int nResult = IDOK;
2882 HANDLE hPipe = (HANDLE)pvContext;
2883 BYTE* pbData = NULL;
2884 SIZE_T cbData = 0;
2885 DWORD dwMessage = 0;
2886
2887 // Always send any extra data via the struct first.
2888 hr = BuffWriteNumber(&pbData, &cbData, pMessage->cData);
2889 ExitOnFailure(hr, "Failed to write MSI data count to message buffer.");
2890
2891 for (DWORD i = 0; i < pMessage->cData; ++i)
2892 {
2893 hr = BuffWriteString(&pbData, &cbData, pMessage->rgwzData[i]);
2894 ExitOnFailure(hr, "Failed to write MSI data to message buffer.");
2895 }
2896
2897 hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwAllowedResults);
2898 ExitOnFailure(hr, "Failed to write UI flags.");
2899
2900 switch (pMessage->type)
2901 {
2902 case WIU_MSI_EXECUTE_MESSAGE_PROGRESS:
2903 // serialize message data
2904 hr = BuffWriteNumber(&pbData, &cbData, pMessage->progress.dwPercentage);
2905 ExitOnFailure(hr, "Failed to write progress percentage to message buffer.");
2906
2907 // set message id
2908 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS;
2909 break;
2910
2911 case WIU_MSI_EXECUTE_MESSAGE_ERROR:
2912 // serialize message data
2913 hr = BuffWriteNumber(&pbData, &cbData, pMessage->error.dwErrorCode);
2914 ExitOnFailure(hr, "Failed to write error code to message buffer.");
2915
2916 hr = BuffWriteString(&pbData, &cbData, pMessage->error.wzMessage);
2917 ExitOnFailure(hr, "Failed to write message to message buffer.");
2918
2919 // set message id
2920 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR;
2921 break;
2922
2923 case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE:
2924 // serialize message data
2925 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pMessage->msiMessage.mt);
2926 ExitOnFailure(hr, "Failed to write MSI message type to message buffer.");
2927
2928 hr = BuffWriteString(&pbData, &cbData, pMessage->msiMessage.wzMessage);
2929 ExitOnFailure(hr, "Failed to write message to message buffer.");
2930
2931 // set message id
2932 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE;
2933 break;
2934
2935 case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE:
2936 // NOTE: we do not serialize other message data here because all the "files in use" are in the data above.
2937
2938 // set message id
2939 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE;
2940 break;
2941
2942 default:
2943 hr = E_UNEXPECTED;
2944 ExitOnFailure(hr, "Invalid message type: %d", pMessage->type);
2945 }
2946
2947 // send message
2948 hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, (DWORD*)&nResult);
2949 ExitOnFailure(hr, "Failed to send msi message to per-user process.");
2950
2951LExit:
2952 ReleaseBuffer(pbData);
2953
2954 return nResult;
2955}
2956
2957static HRESULT OnCleanPackage(
2958 __in BURN_PACKAGES* pPackages,
2959 __in BYTE* pbData,
2960 __in SIZE_T cbData
2961 )
2962{
2963 HRESULT hr = S_OK;
2964 SIZE_T iData = 0;
2965 LPWSTR sczPackage = NULL;
2966 BURN_PACKAGE* pPackage = NULL;
2967
2968 // Deserialize message data.
2969 hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2970 ExitOnFailure(hr, "Failed to read package id.");
2971
2972 hr = PackageFindById(pPackages, sczPackage, &pPackage);
2973 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2974
2975 // Remove the package from the cache.
2976 hr = CacheRemovePackage(TRUE, pPackage->sczId, pPackage->sczCacheId);
2977 ExitOnFailure(hr, "Failed to remove from cache package: %ls", pPackage->sczId);
2978
2979LExit:
2980 ReleaseStr(sczPackage);
2981 return hr;
2982}
2983
2984static HRESULT OnLaunchApprovedExe(
2985 __in HANDLE hPipe,
2986 __in BURN_APPROVED_EXES* pApprovedExes,
2987 __in BURN_VARIABLES* pVariables,
2988 __in BYTE* pbData,
2989 __in SIZE_T cbData
2990 )
2991{
2992 HRESULT hr = S_OK;
2993 SIZE_T iData = 0;
2994 BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe = NULL;
2995 BURN_APPROVED_EXE* pApprovedExe = NULL;
2996 REGSAM samDesired = KEY_QUERY_VALUE;
2997 HKEY hKey = NULL;
2998 DWORD dwProcessId = 0;
2999 BYTE* pbSendData = NULL;
3000 SIZE_T cbSendData = 0;
3001 DWORD dwResult = 0;
3002
3003 pLaunchApprovedExe = (BURN_LAUNCH_APPROVED_EXE*)MemAlloc(sizeof(BURN_LAUNCH_APPROVED_EXE), TRUE);
3004
3005 // Deserialize message data.
3006 hr = BuffReadString(pbData, cbData, &iData, &pLaunchApprovedExe->sczId);
3007 ExitOnFailure(hr, "Failed to read approved exe id.");
3008
3009 hr = BuffReadString(pbData, cbData, &iData, &pLaunchApprovedExe->sczArguments);
3010 ExitOnFailure(hr, "Failed to read approved exe arguments.");
3011
3012 hr = BuffReadNumber(pbData, cbData, &iData, &pLaunchApprovedExe->dwWaitForInputIdleTimeout);
3013 ExitOnFailure(hr, "Failed to read approved exe WaitForInputIdle timeout.");
3014
3015 hr = ApprovedExesFindById(pApprovedExes, pLaunchApprovedExe->sczId, &pApprovedExe);
3016 ExitOnFailure(hr, "The per-user process requested unknown approved exe with id: %ls", pLaunchApprovedExe->sczId);
3017
3018 LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_SEARCH, pApprovedExe->sczKey, pApprovedExe->sczValueName ? pApprovedExe->sczValueName : L"", pApprovedExe->fWin64 ? L"yes" : L"no");
3019
3020 if (pApprovedExe->fWin64)
3021 {
3022 samDesired |= KEY_WOW64_64KEY;
3023 }
3024
3025 hr = RegOpen(HKEY_LOCAL_MACHINE, pApprovedExe->sczKey, samDesired, &hKey);
3026 ExitOnFailure(hr, "Failed to open the registry key for the approved exe path.");
3027
3028 hr = RegReadString(hKey, pApprovedExe->sczValueName, &pLaunchApprovedExe->sczExecutablePath);
3029 ExitOnFailure(hr, "Failed to read the value for the approved exe path.");
3030
3031 hr = ApprovedExesVerifySecureLocation(pVariables, pLaunchApprovedExe);
3032 ExitOnFailure(hr, "Failed to verify the executable path is in a secure location: %ls", pLaunchApprovedExe->sczExecutablePath);
3033 if (S_FALSE == hr)
3034 {
3035 LogStringLine(REPORT_STANDARD, "The executable path is not in a secure location: %ls", pLaunchApprovedExe->sczExecutablePath);
3036 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED));
3037 }
3038
3039 hr = ApprovedExesLaunch(pVariables, pLaunchApprovedExe, &dwProcessId);
3040 ExitOnFailure(hr, "Failed to launch approved exe: %ls", pLaunchApprovedExe->sczExecutablePath);
3041
3042 //send process id over pipe
3043 hr = BuffWriteNumber(&pbSendData, &cbSendData, dwProcessId);
3044 ExitOnFailure(hr, "Failed to write the approved exe process id to message buffer.");
3045
3046 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID, pbSendData, cbSendData, NULL, NULL, &dwResult);
3047 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID message to per-user process.");
3048
3049LExit:
3050 ReleaseBuffer(pbSendData);
3051 ApprovedExesUninitializeLaunch(pLaunchApprovedExe);
3052 return hr;
3053}
3054
3055static HRESULT OnMsiBeginTransaction(
3056 __in BURN_PACKAGES* pPackages,
3057 __in BYTE* pbData,
3058 __in SIZE_T cbData
3059 )
3060{
3061 HRESULT hr = S_OK;
3062 SIZE_T iData = 0;
3063 LPWSTR sczId = NULL;
3064 LPWSTR sczLogPath = NULL;
3065 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL;
3066
3067 // Deserialize message data.
3068 hr = BuffReadString(pbData, cbData, &iData, &sczId);
3069 ExitOnFailure(hr, "Failed to read rollback boundary id.");
3070
3071 hr = BuffReadString(pbData, cbData, &iData, &sczLogPath);
3072 ExitOnFailure(hr, "Failed to read transaction log path.");
3073
3074 hr = PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary);
3075 ExitOnFailure(hr, "Failed to find rollback boundary: %ls", sczId);
3076
3077 pRollbackBoundary->sczLogPath = sczLogPath;
3078
3079 hr = MsiEngineBeginTransaction(pRollbackBoundary);
3080
3081LExit:
3082 ReleaseStr(sczId);
3083 ReleaseStr(sczLogPath);
3084
3085 if (pRollbackBoundary)
3086 {
3087 pRollbackBoundary->sczLogPath = NULL;
3088 }
3089
3090 return hr;
3091}
3092
3093static HRESULT OnMsiCommitTransaction(
3094 __in BURN_PACKAGES* pPackages,
3095 __in BYTE* pbData,
3096 __in SIZE_T cbData
3097 )
3098{
3099 HRESULT hr = S_OK;
3100 SIZE_T iData = 0;
3101 LPWSTR sczId = NULL;
3102 LPWSTR sczLogPath = NULL;
3103 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL;
3104
3105 // Deserialize message data.
3106 hr = BuffReadString(pbData, cbData, &iData, &sczId);
3107 ExitOnFailure(hr, "Failed to read rollback boundary id.");
3108
3109 hr = BuffReadString(pbData, cbData, &iData, &sczLogPath);
3110 ExitOnFailure(hr, "Failed to read transaction log path.");
3111
3112 hr = PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary);
3113 ExitOnFailure(hr, "Failed to find rollback boundary: %ls", sczId);
3114
3115 pRollbackBoundary->sczLogPath = sczLogPath;
3116
3117 hr = MsiEngineCommitTransaction(pRollbackBoundary);
3118
3119LExit:
3120 ReleaseStr(sczId);
3121 ReleaseStr(sczLogPath);
3122
3123 if (pRollbackBoundary)
3124 {
3125 pRollbackBoundary->sczLogPath = NULL;
3126 }
3127
3128 return hr;
3129}
3130
3131static HRESULT OnMsiRollbackTransaction(
3132 __in BURN_PACKAGES* pPackages,
3133 __in BYTE* pbData,
3134 __in SIZE_T cbData
3135 )
3136{
3137 HRESULT hr = S_OK;
3138 SIZE_T iData = 0;
3139 LPWSTR sczId = NULL;
3140 LPWSTR sczLogPath = NULL;
3141 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL;
3142
3143 // Deserialize message data.
3144 hr = BuffReadString(pbData, cbData, &iData, &sczId);
3145 ExitOnFailure(hr, "Failed to read rollback boundary id.");
3146
3147 hr = BuffReadString(pbData, cbData, &iData, &sczLogPath);
3148 ExitOnFailure(hr, "Failed to read transaction log path.");
3149
3150 hr = PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary);
3151 ExitOnFailure(hr, "Failed to find rollback boundary: %ls", sczId);
3152
3153 pRollbackBoundary->sczLogPath = sczLogPath;
3154
3155 hr = MsiEngineRollbackTransaction(pRollbackBoundary);
3156
3157LExit:
3158 ReleaseStr(sczId);
3159 ReleaseStr(sczLogPath);
3160
3161 if (pRollbackBoundary)
3162 {
3163 pRollbackBoundary->sczLogPath = NULL;
3164 }
3165
3166 return hr;
3167}
3168
3169static HRESULT ElevatedOnPauseAUBegin(
3170 __in HANDLE hPipe
3171 )
3172{
3173 HRESULT hr = S_OK;
3174 DWORD dwResult = 0;
3175
3176 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_BEGIN, NULL, 0, NULL, NULL, &dwResult);
3177 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_BEGIN message to per-user process.");
3178
3179LExit:
3180 return hr;
3181}
3182
3183static HRESULT ElevatedOnPauseAUComplete(
3184 __in HANDLE hPipe,
3185 __in HRESULT hrStatus
3186 )
3187{
3188 HRESULT hr = S_OK;
3189 BYTE* pbSendData = NULL;
3190 SIZE_T cbSendData = 0;
3191 DWORD dwResult = 0;
3192
3193 hr = BuffWriteNumber(&pbSendData, &cbSendData, hrStatus);
3194 ExitOnFailure(hr, "Failed to write the pause au status to message buffer.");
3195
3196 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_COMPLETE, pbSendData, cbSendData, NULL, NULL, &dwResult);
3197 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_COMPLETE message to per-user process.");
3198
3199LExit:
3200 ReleaseBuffer(pbSendData);
3201
3202 return hr;
3203}
3204
3205static HRESULT ElevatedOnSystemRestorePointBegin(
3206 __in HANDLE hPipe
3207 )
3208{
3209 HRESULT hr = S_OK;
3210 DWORD dwResult = 0;
3211
3212 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_BEGIN, NULL, 0, NULL, NULL, &dwResult);
3213 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_BEGIN message to per-user process.");
3214
3215LExit:
3216 return hr;
3217}
3218
3219static HRESULT ElevatedOnSystemRestorePointComplete(
3220 __in HANDLE hPipe,
3221 __in HRESULT hrStatus
3222 )
3223{
3224 HRESULT hr = S_OK;
3225 BYTE* pbSendData = NULL;
3226 SIZE_T cbSendData = 0;
3227 DWORD dwResult = 0;
3228
3229 hr = BuffWriteNumber(&pbSendData, &cbSendData, hrStatus);
3230 ExitOnFailure(hr, "Failed to write the system restore point status to message buffer.");
3231
3232 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_COMPLETE, pbSendData, cbSendData, NULL, NULL, &dwResult);
3233 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_SYSTEM_RESTORE_POINT_COMPLETE message to per-user process.");
3234
3235LExit:
3236 ReleaseBuffer(pbSendData);
3237
3238 return hr;
3239}
diff --git a/src/burn/engine/elevation.h b/src/burn/engine/elevation.h
new file mode 100644
index 00000000..9244f36c
--- /dev/null
+++ b/src/burn/engine/elevation.h
@@ -0,0 +1,176 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9
10// Parent (per-user process) side functions.
11HRESULT ElevationElevate(
12 __in BURN_ENGINE_STATE* pEngineState,
13 __in_opt HWND hwndParent
14 );
15HRESULT ElevationApplyInitialize(
16 __in HANDLE hPipe,
17 __in BURN_USER_EXPERIENCE* pBA,
18 __in BURN_VARIABLES* pVariables,
19 __in BOOTSTRAPPER_ACTION action,
20 __in BURN_AU_PAUSE_ACTION auAction,
21 __in BOOL fTakeSystemRestorePoint
22 );
23HRESULT ElevationApplyUninitialize(
24 __in HANDLE hPipe
25 );
26HRESULT ElevationSessionBegin(
27 __in HANDLE hPipe,
28 __in_z LPCWSTR wzEngineWorkingPath,
29 __in_z LPCWSTR wzResumeCommandLine,
30 __in BOOL fDisableResume,
31 __in BURN_VARIABLES* pVariables,
32 __in DWORD dwRegistrationOperations,
33 __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction,
34 __in DWORD64 qwEstimatedSize
35 );
36HRESULT ElevationSessionResume(
37 __in HANDLE hPipe,
38 __in_z LPCWSTR wzResumeCommandLine,
39 __in BOOL fDisableResume,
40 __in BURN_VARIABLES* pVariables
41 );
42HRESULT ElevationSessionEnd(
43 __in HANDLE hPipe,
44 __in BURN_RESUME_MODE resumeMode,
45 __in BOOTSTRAPPER_APPLY_RESTART restart,
46 __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction
47 );
48HRESULT ElevationSaveState(
49 __in HANDLE hPipe,
50 __in_bcount(cbBuffer) BYTE* pbBuffer,
51 __in SIZE_T cbBuffer
52 );
53HRESULT ElevationCacheCompletePayload(
54 __in HANDLE hPipe,
55 __in BURN_PACKAGE* pPackage,
56 __in BURN_PAYLOAD* pPayload,
57 __in_z LPCWSTR wzUnverifiedPath,
58 __in BOOL fMove,
59 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
60 __in LPPROGRESS_ROUTINE pfnProgress,
61 __in LPVOID pContext
62 );
63HRESULT ElevationCacheVerifyPayload(
64 __in HANDLE hPipe,
65 __in BURN_PACKAGE* pPackage,
66 __in BURN_PAYLOAD* pPayload,
67 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
68 __in LPPROGRESS_ROUTINE pfnProgress,
69 __in LPVOID pContext
70 );
71HRESULT ElevationCacheCleanup(
72 __in HANDLE hPipe
73 );
74HRESULT ElevationProcessDependentRegistration(
75 __in HANDLE hPipe,
76 __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction
77 );
78HRESULT ElevationExecuteExePackage(
79 __in HANDLE hPipe,
80 __in BURN_EXECUTE_ACTION* pExecuteAction,
81 __in BURN_VARIABLES* pVariables,
82 __in BOOL fRollback,
83 __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress,
84 __in LPVOID pvContext,
85 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
86 );
87HRESULT ElevationExecuteMsiPackage(
88 __in HANDLE hPipe,
89 __in_opt HWND hwndParent,
90 __in BURN_EXECUTE_ACTION* pExecuteAction,
91 __in BURN_VARIABLES* pVariables,
92 __in BOOL fRollback,
93 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
94 __in LPVOID pvContext,
95 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
96 );
97HRESULT ElevationExecuteMspPackage(
98 __in HANDLE hPipe,
99 __in_opt HWND hwndParent,
100 __in BURN_EXECUTE_ACTION* pExecuteAction,
101 __in BURN_VARIABLES* pVariables,
102 __in BOOL fRollback,
103 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
104 __in LPVOID pvContext,
105 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
106 );
107HRESULT ElevationExecuteMsuPackage(
108 __in HANDLE hPipe,
109 __in BURN_EXECUTE_ACTION* pExecuteAction,
110 __in BOOL fRollback,
111 __in BOOL fStopWusaService,
112 __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress,
113 __in LPVOID pvContext,
114 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
115 );
116HRESULT ElevationExecutePackageProviderAction(
117 __in HANDLE hPipe,
118 __in BURN_EXECUTE_ACTION* pExecuteAction
119 );
120HRESULT ElevationExecutePackageDependencyAction(
121 __in HANDLE hPipe,
122 __in BURN_EXECUTE_ACTION* pExecuteAction
123 );
124HRESULT ElevationLaunchElevatedChild(
125 __in HANDLE hPipe,
126 __in BURN_PACKAGE* pPackage,
127 __in LPCWSTR wzPipeName,
128 __in LPCWSTR wzPipeToken,
129 __out DWORD* pdwChildPid
130 );
131HRESULT ElevationCleanPackage(
132 __in HANDLE hPipe,
133 __in BURN_PACKAGE* pPackage
134 );
135HRESULT ElevationLaunchApprovedExe(
136 __in HANDLE hPipe,
137 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe,
138 __out DWORD* pdwProcessId
139 );
140
141// Child (per-machine process) side functions.
142HRESULT ElevationChildPumpMessages(
143 __in DWORD dwLoggingTlsId,
144 __in HANDLE hPipe,
145 __in HANDLE hCachePipe,
146 __in BURN_APPROVED_EXES* pApprovedExes,
147 __in BURN_CONTAINERS* pContainers,
148 __in BURN_PACKAGES* pPackages,
149 __in BURN_PAYLOADS* pPayloads,
150 __in BURN_VARIABLES* pVariables,
151 __in BURN_REGISTRATION* pRegistration,
152 __in BURN_USER_EXPERIENCE* pUserExperience,
153 __out HANDLE* phLock,
154 __out BOOL* pfDisabledAutomaticUpdates,
155 __out DWORD* pdwChildExitCode,
156 __out BOOL* pfRestart
157 );
158HRESULT ElevationChildResumeAutomaticUpdates();
159
160
161HRESULT ElevationMsiBeginTransaction(
162 __in HANDLE hPipe,
163 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
164 );
165HRESULT ElevationMsiCommitTransaction(
166 __in HANDLE hPipe,
167 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
168 );
169HRESULT ElevationMsiRollbackTransaction(
170 __in HANDLE hPipe,
171 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
172 );
173
174#ifdef __cplusplus
175}
176#endif
diff --git a/src/burn/engine/embedded.cpp b/src/burn/engine/embedded.cpp
new file mode 100644
index 00000000..03898ebd
--- /dev/null
+++ b/src/burn/engine/embedded.cpp
@@ -0,0 +1,197 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// struct
7
8struct BURN_EMBEDDED_CALLBACK_CONTEXT
9{
10 PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler;
11 LPVOID pvContext;
12};
13
14// internal function declarations
15
16static HRESULT ProcessEmbeddedMessages(
17 __in BURN_PIPE_MESSAGE* pMsg,
18 __in_opt LPVOID pvContext,
19 __out DWORD* pdwResult
20 );
21static HRESULT OnEmbeddedErrorMessage(
22 __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler,
23 __in LPVOID pvContext,
24 __in_bcount(cbData) BYTE* pbData,
25 __in SIZE_T cbData,
26 __out DWORD* pdwResult
27 );
28static HRESULT OnEmbeddedProgress(
29 __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler,
30 __in LPVOID pvContext,
31 __in_bcount(cbData) BYTE* pbData,
32 __in SIZE_T cbData,
33 __out DWORD* pdwResult
34 );
35
36// function definitions
37
38/*******************************************************************
39 EmbeddedLaunchChildProcess -
40
41*******************************************************************/
42extern "C" HRESULT EmbeddedRunBundle(
43 __in LPCWSTR wzExecutablePath,
44 __in LPCWSTR wzArguments,
45 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
46 __in LPVOID pvContext,
47 __out DWORD* pdwExitCode
48 )
49{
50 HRESULT hr = S_OK;
51 DWORD dwCurrentProcessId = ::GetCurrentProcessId();
52 HANDLE hCreatedPipesEvent = NULL;
53 LPWSTR sczCommand = NULL;
54 STARTUPINFOW si = { };
55 PROCESS_INFORMATION pi = { };
56 BURN_PIPE_RESULT result = { };
57
58 BURN_PIPE_CONNECTION connection = { };
59 PipeConnectionInitialize(&connection);
60
61 BURN_EMBEDDED_CALLBACK_CONTEXT context = { };
62 context.pfnGenericMessageHandler = pfnGenericMessageHandler;
63 context.pvContext = pvContext;
64
65 hr = PipeCreateNameAndSecret(&connection.sczName, &connection.sczSecret);
66 ExitOnFailure(hr, "Failed to create embedded pipe name and client token.");
67
68 hr = PipeCreatePipes(&connection, FALSE, &hCreatedPipesEvent);
69 ExitOnFailure(hr, "Failed to create embedded pipe.");
70
71 hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls %ls %ls %u", wzArguments, BURN_COMMANDLINE_SWITCH_EMBEDDED, connection.sczName, connection.sczSecret, dwCurrentProcessId);
72 ExitOnFailure(hr, "Failed to allocate embedded command.");
73
74 if (!::CreateProcessW(wzExecutablePath, sczCommand, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
75 {
76 ExitWithLastError(hr, "Failed to create embedded process at path: %ls", wzExecutablePath);
77 }
78
79 connection.dwProcessId = ::GetProcessId(pi.hProcess);
80 connection.hProcess = pi.hProcess;
81 pi.hProcess = NULL;
82
83 hr = PipeWaitForChildConnect(&connection);
84 ExitOnFailure(hr, "Failed to wait for embedded process to connect to pipe.");
85
86 hr = PipePumpMessages(connection.hPipe, ProcessEmbeddedMessages, &context, &result);
87 ExitOnFailure(hr, "Failed to process messages from embedded message.");
88
89 // Get the return code from the embedded process.
90 hr = ProcWaitForCompletion(connection.hProcess, INFINITE, pdwExitCode);
91 ExitOnFailure(hr, "Failed to wait for embedded executable: %ls", wzExecutablePath);
92
93LExit:
94 ReleaseHandle(pi.hThread);
95 ReleaseHandle(pi.hProcess);
96
97 StrSecureZeroFreeString(sczCommand);
98 ReleaseHandle(hCreatedPipesEvent);
99 PipeConnectionUninitialize(&connection);
100
101 return hr;
102}
103
104
105// internal function definitions
106
107static HRESULT ProcessEmbeddedMessages(
108 __in BURN_PIPE_MESSAGE* pMsg,
109 __in_opt LPVOID pvContext,
110 __out DWORD* pdwResult
111 )
112{
113 HRESULT hr = S_OK;
114 BURN_EMBEDDED_CALLBACK_CONTEXT* pContext = static_cast<BURN_EMBEDDED_CALLBACK_CONTEXT*>(pvContext);
115 DWORD dwResult = 0;
116
117 // Process the message.
118 switch (pMsg->dwMessage)
119 {
120 case BURN_EMBEDDED_MESSAGE_TYPE_ERROR:
121 hr = OnEmbeddedErrorMessage(pContext->pfnGenericMessageHandler, pContext->pvContext, static_cast<BYTE*>(pMsg->pvData), pMsg->cbData, &dwResult);
122 ExitOnFailure(hr, "Failed to process embedded error message.");
123 break;
124
125 case BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS:
126 hr = OnEmbeddedProgress(pContext->pfnGenericMessageHandler, pContext->pvContext, static_cast<BYTE*>(pMsg->pvData), pMsg->cbData, &dwResult);
127 ExitOnFailure(hr, "Failed to process embedded progress message.");
128 break;
129
130 default:
131 hr = E_INVALIDARG;
132 ExitOnRootFailure(hr, "Unexpected embedded message sent to child process, msg: %u", pMsg->dwMessage);
133 }
134
135 *pdwResult = dwResult;
136
137LExit:
138 return hr;
139}
140
141static HRESULT OnEmbeddedErrorMessage(
142 __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler,
143 __in LPVOID pvContext,
144 __in_bcount(cbData) BYTE* pbData,
145 __in SIZE_T cbData,
146 __out DWORD* pdwResult
147 )
148{
149 HRESULT hr = S_OK;
150 SIZE_T iData = 0;
151 GENERIC_EXECUTE_MESSAGE message = { };
152 LPWSTR sczMessage = NULL;
153
154 message.type = GENERIC_EXECUTE_MESSAGE_ERROR;
155
156 hr = BuffReadNumber(pbData, cbData, &iData, &message.error.dwErrorCode);
157 ExitOnFailure(hr, "Failed to read error code from buffer.");
158
159 hr = BuffReadString(pbData, cbData, &iData, &sczMessage);
160 ExitOnFailure(hr, "Failed to read error message from buffer.");
161
162 message.error.wzMessage = sczMessage;
163
164 hr = BuffReadNumber(pbData, cbData, &iData, &message.dwAllowedResults);
165 ExitOnFailure(hr, "Failed to read UI hint from buffer.");
166
167 *pdwResult = (DWORD)pfnMessageHandler(&message, pvContext);
168
169LExit:
170 ReleaseStr(sczMessage);
171
172 return hr;
173}
174
175static HRESULT OnEmbeddedProgress(
176 __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler,
177 __in LPVOID pvContext,
178 __in_bcount(cbData) BYTE* pbData,
179 __in SIZE_T cbData,
180 __out DWORD* pdwResult
181 )
182{
183 HRESULT hr = S_OK;
184 SIZE_T iData = 0;
185 GENERIC_EXECUTE_MESSAGE message = { };
186
187 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
188 message.dwAllowedResults = MB_OKCANCEL;
189
190 hr = BuffReadNumber(pbData, cbData, &iData, &message.progress.dwPercentage);
191 ExitOnFailure(hr, "Failed to read progress from buffer.");
192
193 *pdwResult = (DWORD)pfnMessageHandler(&message, pvContext);
194
195LExit:
196 return hr;
197}
diff --git a/src/burn/engine/embedded.h b/src/burn/engine/embedded.h
new file mode 100644
index 00000000..08adeae0
--- /dev/null
+++ b/src/burn/engine/embedded.h
@@ -0,0 +1,27 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9typedef enum _BURN_EMBEDDED_MESSAGE_TYPE
10{
11 BURN_EMBEDDED_MESSAGE_TYPE_UNKNOWN,
12 BURN_EMBEDDED_MESSAGE_TYPE_ERROR,
13 BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS,
14} BURN_EMBEDDED_MESSAGE_TYPE;
15
16
17HRESULT EmbeddedRunBundle(
18 __in LPCWSTR wzExecutablePath,
19 __in LPCWSTR wzArguments,
20 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
21 __in LPVOID pvContext,
22 __out DWORD* pdwExitCode
23 );
24
25#ifdef __cplusplus
26}
27#endif
diff --git a/src/burn/engine/engine.cpp b/src/burn/engine/engine.cpp
new file mode 100644
index 00000000..8f024e98
--- /dev/null
+++ b/src/burn/engine/engine.cpp
@@ -0,0 +1,992 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// constants
7
8const DWORD RESTART_RETRIES = 10;
9
10// internal function declarations
11
12static HRESULT InitializeEngineState(
13 __in BURN_ENGINE_STATE* pEngineState,
14 __in HANDLE hEngineFile
15 );
16static void UninitializeEngineState(
17 __in BURN_ENGINE_STATE* pEngineState
18 );
19static HRESULT RunUntrusted(
20 __in LPCWSTR wzCommandLine,
21 __in BURN_ENGINE_STATE* pEngineState
22 );
23static HRESULT RunNormal(
24 __in HINSTANCE hInstance,
25 __in BURN_ENGINE_STATE* pEngineState
26 );
27static HRESULT RunElevated(
28 __in HINSTANCE hInstance,
29 __in LPCWSTR wzCommandLine,
30 __in BURN_ENGINE_STATE* pEngineState
31 );
32static HRESULT RunEmbedded(
33 __in HINSTANCE hInstance,
34 __in BURN_ENGINE_STATE* pEngineState
35 );
36static HRESULT RunRunOnce(
37 __in const BURN_REGISTRATION* pRegistration,
38 __in int nCmdShow
39 );
40static HRESULT RunApplication(
41 __in BURN_ENGINE_STATE* pEngineState,
42 __out BOOL* pfReloadApp,
43 __out BOOL* pfSkipCleanup
44 );
45static HRESULT ProcessMessage(
46 __in BURN_ENGINE_STATE* pEngineState,
47 __in const MSG* pmsg
48 );
49static HRESULT DAPI RedirectLoggingOverPipe(
50 __in_z LPCSTR szString,
51 __in_opt LPVOID pvContext
52 );
53static HRESULT Restart();
54
55
56// function definitions
57
58extern "C" BOOL EngineInCleanRoom(
59 __in_z_opt LPCWSTR wzCommandLine
60 )
61{
62 // Be very careful with the functions you call from here.
63 // This function will be called before ::SetDefaultDllDirectories()
64 // has been called so dependencies outside of kernel32.dll are
65 // very likely to introduce DLL hijacking opportunities.
66
67 static DWORD cchCleanRoomSwitch = lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM);
68
69 // This check is wholly dependent on the clean room command line switch being
70 // present at the beginning of the command line. Since Burn is the only thing
71 // that should be setting this command line option, that is in our control.
72 BOOL fInCleanRoom = (wzCommandLine &&
73 (wzCommandLine[0] == L'-' || wzCommandLine[0] == L'/') &&
74 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzCommandLine + 1, cchCleanRoomSwitch, BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, cchCleanRoomSwitch) &&
75 wzCommandLine[1 + cchCleanRoomSwitch] == L'='
76 );
77
78 return fInCleanRoom;
79}
80
81extern "C" HRESULT EngineRun(
82 __in HINSTANCE hInstance,
83 __in HANDLE hEngineFile,
84 __in_z_opt LPCWSTR wzCommandLine,
85 __in int nCmdShow,
86 __out DWORD* pdwExitCode
87 )
88{
89 HRESULT hr = S_OK;
90 BOOL fComInitialized = FALSE;
91 BOOL fLogInitialized = FALSE;
92 BOOL fCrypInitialized = FALSE;
93 BOOL fDpiuInitialized = FALSE;
94 BOOL fRegInitialized = FALSE;
95 BOOL fWiuInitialized = FALSE;
96 BOOL fXmlInitialized = FALSE;
97 SYSTEM_INFO si = { };
98 RTL_OSVERSIONINFOEXW ovix = { };
99 LPWSTR sczExePath = NULL;
100 BOOL fRunNormal = FALSE;
101 BOOL fRestart = FALSE;
102
103 BURN_ENGINE_STATE engineState = { };
104 engineState.command.cbSize = sizeof(BOOTSTRAPPER_COMMAND);
105
106 // Always initialize logging first
107 LogInitialize(::GetModuleHandleW(NULL));
108 fLogInitialized = TRUE;
109
110 // Ensure that log contains approriate level of information
111#ifdef _DEBUG
112 LogSetLevel(REPORT_DEBUG, FALSE);
113#else
114 LogSetLevel(REPORT_VERBOSE, FALSE); // FALSE means don't write an additional text line to the log saying the level changed
115#endif
116
117 hr = AppParseCommandLine(wzCommandLine, &engineState.argc, &engineState.argv);
118 ExitOnFailure(hr, "Failed to parse command line.");
119
120 hr = InitializeEngineState(&engineState, hEngineFile);
121 ExitOnFailure(hr, "Failed to initialize engine state.");
122
123 engineState.command.nCmdShow = nCmdShow;
124
125 // initialize platform layer
126 PlatformInitialize();
127
128 // initialize COM
129 hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
130 ExitOnFailure(hr, "Failed to initialize COM.");
131 fComInitialized = TRUE;
132
133 // Initialize dutil.
134 hr = CrypInitialize();
135 ExitOnFailure(hr, "Failed to initialize Cryputil.");
136 fCrypInitialized = TRUE;
137
138 DpiuInitialize();
139 fDpiuInitialized = TRUE;
140
141 hr = RegInitialize();
142 ExitOnFailure(hr, "Failed to initialize Regutil.");
143 fRegInitialized = TRUE;
144
145 hr = WiuInitialize();
146 ExitOnFailure(hr, "Failed to initialize Wiutil.");
147 fWiuInitialized = TRUE;
148
149 hr = XmlInitialize();
150 ExitOnFailure(hr, "Failed to initialize XML util.");
151 fXmlInitialized = TRUE;
152
153 hr = OsRtlGetVersion(&ovix);
154 ExitOnFailure(hr, "Failed to get OS info.");
155
156#if defined(_M_ARM64)
157 LPCSTR szBurnPlatform = "ARM64";
158#elif defined(_M_AMD64)
159 LPCSTR szBurnPlatform = "x64";
160#else
161 LPCSTR szBurnPlatform = "x86";
162#endif
163
164 LPCSTR szMachinePlatform = "unknown architecture";
165 ::GetNativeSystemInfo(&si);
166 switch (si.wProcessorArchitecture)
167 {
168 case PROCESSOR_ARCHITECTURE_AMD64:
169 szMachinePlatform = "x64";
170 break;
171 case PROCESSOR_ARCHITECTURE_ARM:
172 szMachinePlatform = "ARM";
173 break;
174 case PROCESSOR_ARCHITECTURE_ARM64:
175 szMachinePlatform = "ARM64";
176 break;
177 case PROCESSOR_ARCHITECTURE_INTEL:
178 szMachinePlatform = "x86";
179 break;
180 }
181
182 PathForCurrentProcess(&sczExePath, NULL); // Ignore failure.
183 LogId(REPORT_STANDARD, MSG_BURN_INFO, szVerMajorMinorBuild, ovix.dwMajorVersion, ovix.dwMinorVersion, ovix.dwBuildNumber, ovix.wServicePackMajor, sczExePath, szBurnPlatform, szMachinePlatform);
184 ReleaseNullStr(sczExePath);
185
186 // initialize core
187 hr = CoreInitialize(&engineState);
188 ExitOnFailure(hr, "Failed to initialize core.");
189
190 // Select run mode.
191 switch (engineState.mode)
192 {
193 case BURN_MODE_UNTRUSTED:
194 hr = RunUntrusted(wzCommandLine, &engineState);
195 ExitOnFailure(hr, "Failed to run untrusted mode.");
196 break;
197
198 case BURN_MODE_NORMAL:
199 fRunNormal = TRUE;
200
201 hr = RunNormal(hInstance, &engineState);
202 ExitOnFailure(hr, "Failed to run per-user mode.");
203 break;
204
205 case BURN_MODE_ELEVATED:
206 hr = RunElevated(hInstance, wzCommandLine, &engineState);
207 ExitOnFailure(hr, "Failed to run per-machine mode.");
208 break;
209
210 case BURN_MODE_EMBEDDED:
211 fRunNormal = TRUE;
212
213 hr = RunEmbedded(hInstance, &engineState);
214 ExitOnFailure(hr, "Failed to run embedded mode.");
215 break;
216
217 case BURN_MODE_RUNONCE:
218 hr = RunRunOnce(&engineState.registration, nCmdShow);
219 ExitOnFailure(hr, "Failed to run RunOnce mode.");
220 break;
221
222 default:
223 hr = E_UNEXPECTED;
224 ExitOnFailure(hr, "Invalid run mode.");
225 }
226
227 // set exit code and remember if we are supposed to restart.
228 *pdwExitCode = engineState.userExperience.dwExitCode;
229 fRestart = engineState.fRestart;
230
231LExit:
232 ReleaseStr(sczExePath);
233
234 // If anything went wrong but the log was never open, try to open a "failure" log
235 // and that will dump anything captured in the log memory buffer to the log.
236 if (FAILED(hr) && BURN_LOGGING_STATE_CLOSED == engineState.log.state)
237 {
238 LoggingOpenFailed();
239 }
240
241 UserExperienceRemove(&engineState.userExperience);
242
243 CacheRemoveWorkingFolder(engineState.registration.sczId);
244 CacheUninitialize();
245
246 // If this is a related bundle (but not an update) suppress restart and return the standard restart error code.
247 if (fRestart && BOOTSTRAPPER_RELATION_NONE != engineState.command.relationType && BOOTSTRAPPER_RELATION_UPDATE != engineState.command.relationType)
248 {
249 LogId(REPORT_STANDARD, MSG_RESTART_ABORTED, LoggingRelationTypeToString(engineState.command.relationType));
250
251 fRestart = FALSE;
252 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED);
253 }
254
255 UninitializeEngineState(&engineState);
256
257 if (fXmlInitialized)
258 {
259 XmlUninitialize();
260 }
261
262 if (fWiuInitialized)
263 {
264 WiuUninitialize();
265 }
266
267 if (fRegInitialized)
268 {
269 RegUninitialize();
270 }
271
272 if (fDpiuInitialized)
273 {
274 DpiuUninitialize();
275 }
276
277 if (fCrypInitialized)
278 {
279 CrypUninitialize();
280 }
281
282 if (fComInitialized)
283 {
284 ::CoUninitialize();
285 }
286
287 if (fRunNormal)
288 {
289 LogId(REPORT_STANDARD, MSG_EXITING, FAILED(hr) ? (int)hr : *pdwExitCode, LoggingBoolToString(fRestart));
290
291 if (fRestart)
292 {
293 LogId(REPORT_STANDARD, MSG_RESTARTING);
294 }
295 }
296
297 if (fLogInitialized)
298 {
299 LogClose(FALSE);
300 }
301
302 if (fRestart)
303 {
304 Restart();
305 }
306
307 if (fLogInitialized)
308 {
309 LogUninitialize(FALSE);
310 }
311
312 return hr;
313}
314
315
316// internal function definitions
317
318static HRESULT InitializeEngineState(
319 __in BURN_ENGINE_STATE* pEngineState,
320 __in HANDLE hEngineFile
321 )
322{
323 HRESULT hr = S_OK;
324 LPCWSTR wzParam = NULL;
325 HANDLE hSectionFile = hEngineFile;
326 HANDLE hSourceEngineFile = INVALID_HANDLE_VALUE;
327 DWORD64 qw = 0;
328
329 pEngineState->automaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED;
330 pEngineState->dwElevatedLoggingTlsId = TLS_OUT_OF_INDEXES;
331 ::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive);
332 PipeConnectionInitialize(&pEngineState->companionConnection);
333 PipeConnectionInitialize(&pEngineState->embeddedConnection);
334
335 for (int i = 0; i < pEngineState->argc; ++i)
336 {
337 if (pEngineState->argv[i][0] == L'-')
338 {
339 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &pEngineState->argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED), BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED)))
340 {
341 wzParam = &pEngineState->argv[i][2 + lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED)];
342 if (L'=' != wzParam[-1] || L'\0' == wzParam[0])
343 {
344 ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED);
345 }
346
347 hr = StrStringToUInt64(wzParam, 0, &qw);
348 ExitOnFailure(hr, "Failed to parse file handle: '%ls'", (wzParam));
349
350 hSourceEngineFile = (HANDLE)qw;
351 }
352 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &pEngineState->argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF), BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF)))
353 {
354 wzParam = &pEngineState->argv[i][2 + lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF)];
355 if (L'=' != wzParam[-1] || L'\0' == wzParam[0])
356 {
357 ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF);
358 }
359
360 hr = StrStringToUInt64(wzParam, 0, &qw);
361 ExitOnFailure(hr, "Failed to parse file handle: '%ls'", (wzParam));
362
363 hSectionFile = (HANDLE)qw;
364 }
365 }
366 }
367
368 hr = SectionInitialize(&pEngineState->section, hSectionFile, hSourceEngineFile);
369 ExitOnFailure(hr, "Failed to initialize engine section.");
370
371LExit:
372 return hr;
373}
374
375static void UninitializeEngineState(
376 __in BURN_ENGINE_STATE* pEngineState
377 )
378{
379 if (pEngineState->argv)
380 {
381 AppFreeCommandLineArgs(pEngineState->argv);
382 }
383
384 ReleaseStr(pEngineState->sczIgnoreDependencies);
385
386 PipeConnectionUninitialize(&pEngineState->embeddedConnection);
387 PipeConnectionUninitialize(&pEngineState->companionConnection);
388 ReleaseStr(pEngineState->sczBundleEngineWorkingPath)
389
390 ReleaseHandle(pEngineState->hMessageWindowThread);
391
392 BurnExtensionUninitialize(&pEngineState->extensions);
393
394 ::DeleteCriticalSection(&pEngineState->userExperience.csEngineActive);
395 UserExperienceUninitialize(&pEngineState->userExperience);
396
397 ApprovedExesUninitialize(&pEngineState->approvedExes);
398 UpdateUninitialize(&pEngineState->update);
399 VariablesUninitialize(&pEngineState->variables);
400 SearchesUninitialize(&pEngineState->searches);
401 RegistrationUninitialize(&pEngineState->registration);
402 PayloadsUninitialize(&pEngineState->payloads);
403 PackagesUninitialize(&pEngineState->packages);
404 SectionUninitialize(&pEngineState->section);
405 ContainersUninitialize(&pEngineState->containers);
406
407 ReleaseStr(pEngineState->command.wzBootstrapperApplicationDataPath);
408 ReleaseStr(pEngineState->command.wzBootstrapperWorkingFolder);
409 ReleaseStr(pEngineState->command.wzLayoutDirectory);
410 ReleaseStr(pEngineState->command.wzCommandLine);
411
412 ReleaseStr(pEngineState->log.sczExtension);
413 ReleaseStr(pEngineState->log.sczPrefix);
414 ReleaseStr(pEngineState->log.sczPath);
415 ReleaseStr(pEngineState->log.sczPathVariable);
416
417 if (TLS_OUT_OF_INDEXES != pEngineState->dwElevatedLoggingTlsId)
418 {
419 ::TlsFree(pEngineState->dwElevatedLoggingTlsId);
420 }
421
422 // clear struct
423 memset(pEngineState, 0, sizeof(BURN_ENGINE_STATE));
424}
425
426static HRESULT RunUntrusted(
427 __in LPCWSTR wzCommandLine,
428 __in BURN_ENGINE_STATE* pEngineState
429 )
430{
431 HRESULT hr = S_OK;
432 LPWSTR sczCurrentProcessPath = NULL;
433 LPWSTR wzCleanRoomBundlePath = NULL;
434 LPWSTR sczCachedCleanRoomBundlePath = NULL;
435 LPWSTR sczParameters = NULL;
436 LPWSTR sczFullCommandLine = NULL;
437 STARTUPINFOW si = { };
438 PROCESS_INFORMATION pi = { };
439 HANDLE hFileAttached = NULL;
440 HANDLE hFileSelf = NULL;
441 HANDLE hProcess = NULL;
442
443 hr = PathForCurrentProcess(&sczCurrentProcessPath, NULL);
444 ExitOnFailure(hr, "Failed to get path for current process.");
445
446 BOOL fRunningFromCache = CacheBundleRunningFromCache();
447
448 // If we're running from the package cache, we're in a secure
449 // folder (DLLs cannot be inserted here for hijacking purposes)
450 // so just launch the current process's path as the clean room
451 // process. Technically speaking, we'd be able to skip creating
452 // a clean room process at all (since we're already running from
453 // a secure folder) but it makes the code that only wants to run
454 // in clean room more complicated if we don't launch an explicit
455 // clean room process.
456 if (fRunningFromCache)
457 {
458 wzCleanRoomBundlePath = sczCurrentProcessPath;
459 }
460 else
461 {
462 hr = CacheBundleToCleanRoom(&pEngineState->section, &sczCachedCleanRoomBundlePath);
463 ExitOnFailure(hr, "Failed to cache to clean room.");
464
465 wzCleanRoomBundlePath = sczCachedCleanRoomBundlePath;
466 }
467
468 // The clean room switch must always be at the front of the command line so
469 // the EngineInCleanRoom function will operate correctly.
470 hr = StrAllocFormatted(&sczParameters, L"-%ls=\"%ls\"", BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, sczCurrentProcessPath);
471 ExitOnFailure(hr, "Failed to allocate parameters for unelevated process.");
472
473 // Send a file handle for the child Burn process to access the attached container.
474 hr = CoreAppendFileHandleAttachedToCommandLine(pEngineState->section.hEngineFile, &hFileAttached, &sczParameters);
475 ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED);
476
477 // Grab a file handle for the child Burn process.
478 hr = CoreAppendFileHandleSelfToCommandLine(wzCleanRoomBundlePath, &hFileSelf, &sczParameters, NULL);
479 ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF);
480
481 hr = StrAllocFormattedSecure(&sczParameters, L"%ls %ls", sczParameters, wzCommandLine);
482 ExitOnFailure(hr, "Failed to append original command line.");
483
484#ifdef ENABLE_UNELEVATE
485 // TODO: Pass file handle to unelevated process if this ever gets reenabled.
486 if (!pEngineState->fDisableUnelevate)
487 {
488 // Try to launch unelevated and if that fails for any reason, we'll launch our process normally (even though that may make it elevated).
489 hr = ProcExecuteAsInteractiveUser(wzCleanRoomBundlePath, sczParameters, &hProcess);
490 }
491#endif
492
493 if (!hProcess)
494 {
495 hr = StrAllocFormattedSecure(&sczFullCommandLine, L"\"%ls\" %ls", wzCleanRoomBundlePath, sczParameters);
496 ExitOnFailure(hr, "Failed to allocate full command-line.");
497
498 si.cb = sizeof(si);
499 si.wShowWindow = static_cast<WORD>(pEngineState->command.nCmdShow);
500 if (!::CreateProcessW(wzCleanRoomBundlePath, sczFullCommandLine, NULL, NULL, TRUE, 0, 0, NULL, &si, &pi))
501 {
502 ExitWithLastError(hr, "Failed to launch clean room process: %ls", sczFullCommandLine);
503 }
504
505 hProcess = pi.hProcess;
506 pi.hProcess = NULL;
507 }
508
509 hr = ProcWaitForCompletion(hProcess, INFINITE, &pEngineState->userExperience.dwExitCode);
510 ExitOnFailure(hr, "Failed to wait for clean room process: %ls", wzCleanRoomBundlePath);
511
512LExit:
513 ReleaseHandle(pi.hThread);
514 ReleaseFileHandle(hFileSelf);
515 ReleaseFileHandle(hFileAttached);
516 ReleaseHandle(hProcess);
517 StrSecureZeroFreeString(sczFullCommandLine);
518 StrSecureZeroFreeString(sczParameters);
519 ReleaseStr(sczCachedCleanRoomBundlePath);
520 ReleaseStr(sczCurrentProcessPath);
521
522 return hr;
523}
524
525static HRESULT RunNormal(
526 __in HINSTANCE hInstance,
527 __in BURN_ENGINE_STATE* pEngineState
528 )
529{
530 HRESULT hr = S_OK;
531 LPWSTR sczOriginalSource = NULL;
532 LPWSTR sczCopiedOriginalSource = NULL;
533 BOOL fContinueExecution = TRUE;
534 BOOL fReloadApp = FALSE;
535 BOOL fSkipCleanup = FALSE;
536 BURN_EXTENSION_ENGINE_CONTEXT extensionEngineContext = { };
537
538 // Initialize logging.
539 hr = LoggingOpen(&pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->registration.sczDisplayName);
540 ExitOnFailure(hr, "Failed to open log.");
541
542 // Ensure we're on a supported operating system.
543 hr = ConditionGlobalCheck(&pEngineState->variables, &pEngineState->condition, pEngineState->command.display, pEngineState->registration.sczDisplayName, &pEngineState->userExperience.dwExitCode, &fContinueExecution);
544 ExitOnFailure(hr, "Failed to check global conditions");
545
546 if (!fContinueExecution)
547 {
548 LogId(REPORT_STANDARD, MSG_FAILED_CONDITION_CHECK);
549
550 // If the block told us to abort, abort!
551 ExitFunction1(hr = S_OK);
552 }
553
554 if (pEngineState->userExperience.fSplashScreen && BOOTSTRAPPER_DISPLAY_NONE < pEngineState->command.display)
555 {
556 SplashScreenCreate(hInstance, NULL, &pEngineState->command.hwndSplashScreen);
557 }
558
559 // Create a top-level window to handle system messages.
560 hr = UiCreateMessageWindow(hInstance, pEngineState);
561 ExitOnFailure(hr, "Failed to create the message window.");
562
563 // Query registration state.
564 hr = CoreQueryRegistration(pEngineState);
565 ExitOnFailure(hr, "Failed to query registration.");
566
567 // Best effort to set the source of attached containers to BURN_BUNDLE_ORIGINAL_SOURCE.
568 hr = VariableGetString(&pEngineState->variables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource);
569 if (SUCCEEDED(hr))
570 {
571 for (DWORD i = 0; i < pEngineState->containers.cContainers; ++i)
572 {
573 BURN_CONTAINER* pContainer = pEngineState->containers.rgContainers + i;
574 if (pContainer->fAttached)
575 {
576 hr = StrAllocString(&sczCopiedOriginalSource, sczOriginalSource, 0);
577 if (SUCCEEDED(hr))
578 {
579 ReleaseNullStr(pContainer->sczSourcePath);
580 pContainer->sczSourcePath = sczCopiedOriginalSource;
581 sczCopiedOriginalSource = NULL;
582 }
583 }
584 }
585 }
586 hr = S_OK;
587
588 // Set some built-in variables before loading the BA.
589 hr = PlanSetVariables(pEngineState->command.action, &pEngineState->variables);
590 ExitOnFailure(hr, "Failed to set action variables.");
591
592 hr = RegistrationSetVariables(&pEngineState->registration, &pEngineState->variables);
593 ExitOnFailure(hr, "Failed to set registration variables.");
594
595 // If a layout directory was specified on the command-line, set it as a well-known variable.
596 if (pEngineState->command.wzLayoutDirectory && *pEngineState->command.wzLayoutDirectory)
597 {
598 hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_LAYOUT_DIRECTORY, pEngineState->command.wzLayoutDirectory, FALSE, FALSE);
599 ExitOnFailure(hr, "Failed to set layout directory variable to value provided from command-line.");
600 }
601
602 // Setup the extension engine.
603 extensionEngineContext.pEngineState = pEngineState;
604
605 // Load the extensions.
606 hr = BurnExtensionLoad(&pEngineState->extensions, &extensionEngineContext);
607 ExitOnFailure(hr, "Failed to load BundleExtensions.");
608
609 do
610 {
611 fReloadApp = FALSE;
612 pEngineState->fQuit = FALSE;
613
614 hr = RunApplication(pEngineState, &fReloadApp, &fSkipCleanup);
615 ExitOnFailure(hr, "Failed while running ");
616 } while (fReloadApp);
617
618LExit:
619 if (!fSkipCleanup)
620 {
621 CoreCleanup(pEngineState);
622 }
623
624 BurnExtensionUnload(&pEngineState->extensions);
625
626 // If the message window is still around, close it.
627 UiCloseMessageWindow(pEngineState);
628
629 VariablesDump(&pEngineState->variables);
630
631 // end per-machine process if running
632 if (INVALID_HANDLE_VALUE != pEngineState->companionConnection.hPipe)
633 {
634 PipeTerminateChildProcess(&pEngineState->companionConnection, pEngineState->userExperience.dwExitCode, FALSE);
635 }
636
637 // If the splash screen is still around, close it.
638 if (::IsWindow(pEngineState->command.hwndSplashScreen))
639 {
640 ::PostMessageW(pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0);
641 }
642
643 ReleaseStr(sczOriginalSource);
644 ReleaseStr(sczCopiedOriginalSource);
645
646 return hr;
647}
648
649static HRESULT RunElevated(
650 __in HINSTANCE hInstance,
651 __in LPCWSTR /*wzCommandLine*/,
652 __in BURN_ENGINE_STATE* pEngineState
653 )
654{
655 HRESULT hr = S_OK;
656 HANDLE hLock = NULL;
657 BOOL fDisabledAutomaticUpdates = FALSE;
658
659 // connect to per-user process
660 hr = PipeChildConnect(&pEngineState->companionConnection, TRUE);
661 ExitOnFailure(hr, "Failed to connect to unelevated process.");
662
663 // Set up the thread local storage to store the correct pipe to communicate logging then
664 // override logging to write over the pipe.
665 pEngineState->dwElevatedLoggingTlsId = ::TlsAlloc();
666 if (TLS_OUT_OF_INDEXES == pEngineState->dwElevatedLoggingTlsId)
667 {
668 ExitWithLastError(hr, "Failed to allocate thread local storage for logging.");
669 }
670
671 if (!::TlsSetValue(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe))
672 {
673 ExitWithLastError(hr, "Failed to set elevated pipe into thread local storage for logging.");
674 }
675
676 LogRedirect(RedirectLoggingOverPipe, pEngineState);
677
678 // Create a top-level window to prevent shutting down the elevated process.
679 hr = UiCreateMessageWindow(hInstance, pEngineState);
680 ExitOnFailure(hr, "Failed to create the message window.");
681
682 SrpInitialize(TRUE);
683
684 // Pump messages from parent process.
685 hr = ElevationChildPumpMessages(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe, pEngineState->companionConnection.hCachePipe, &pEngineState->approvedExes, &pEngineState->containers, &pEngineState->packages, &pEngineState->payloads, &pEngineState->variables, &pEngineState->registration, &pEngineState->userExperience, &hLock, &fDisabledAutomaticUpdates, &pEngineState->userExperience.dwExitCode, &pEngineState->fRestart);
686 LogRedirect(NULL, NULL); // reset logging so the next failure gets written to "log buffer" for the failure log.
687 ExitOnFailure(hr, "Failed to pump messages from parent process.");
688
689LExit:
690 LogRedirect(NULL, NULL); // we're done talking to the child so always reset logging now.
691
692 // If the message window is still around, close it.
693 UiCloseMessageWindow(pEngineState);
694
695 if (fDisabledAutomaticUpdates)
696 {
697 ElevationChildResumeAutomaticUpdates();
698 }
699
700 if (hLock)
701 {
702 ::ReleaseMutex(hLock);
703 ::CloseHandle(hLock);
704 }
705
706 return hr;
707}
708
709static HRESULT RunEmbedded(
710 __in HINSTANCE hInstance,
711 __in BURN_ENGINE_STATE* pEngineState
712 )
713{
714 HRESULT hr = S_OK;
715
716 // Disable system restore since the parent bundle may have done it.
717 pEngineState->fDisableSystemRestore = TRUE;
718
719 // Connect to parent process.
720 hr = PipeChildConnect(&pEngineState->embeddedConnection, FALSE);
721 ExitOnFailure(hr, "Failed to connect to parent of embedded process.");
722
723 // Do not register the bundle to automatically restart if embedded.
724 if (BOOTSTRAPPER_DISPLAY_EMBEDDED == pEngineState->command.display)
725 {
726 pEngineState->registration.fDisableResume = TRUE;
727 }
728
729 // Now run the application like normal.
730 hr = RunNormal(hInstance, pEngineState);
731 ExitOnFailure(hr, "Failed to run bootstrapper application embedded.");
732
733LExit:
734 return hr;
735}
736
737static HRESULT RunRunOnce(
738 __in const BURN_REGISTRATION* pRegistration,
739 __in int nCmdShow
740 )
741{
742 HRESULT hr = S_OK;
743 LPWSTR sczNewCommandLine = NULL;
744 LPWSTR sczBurnPath = NULL;
745 HANDLE hProcess = NULL;
746
747 hr = RegistrationGetResumeCommandLine(pRegistration, &sczNewCommandLine);
748 ExitOnFailure(hr, "Unable to get resume command line from the registry");
749
750 // and re-launch
751 hr = PathForCurrentProcess(&sczBurnPath, NULL);
752 ExitOnFailure(hr, "Failed to get current process path.");
753
754 hr = ProcExec(sczBurnPath, 0 < sczNewCommandLine ? sczNewCommandLine : L"", nCmdShow, &hProcess);
755 ExitOnFailure(hr, "Failed to re-launch bundle process after RunOnce: %ls", sczBurnPath);
756
757LExit:
758 ReleaseHandle(hProcess);
759 ReleaseStr(sczNewCommandLine);
760 ReleaseStr(sczBurnPath);
761
762 return hr;
763}
764
765static HRESULT RunApplication(
766 __in BURN_ENGINE_STATE* pEngineState,
767 __out BOOL* pfReloadApp,
768 __out BOOL* pfSkipCleanup
769 )
770{
771 HRESULT hr = S_OK;
772 BOOTSTRAPPER_ENGINE_CONTEXT engineContext = { };
773 BOOL fStartupCalled = FALSE;
774 BOOL fRet = FALSE;
775 MSG msg = { };
776 BOOTSTRAPPER_SHUTDOWN_ACTION shutdownAction = BOOTSTRAPPER_SHUTDOWN_ACTION_NONE;
777
778 ::PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
779
780 // Setup the bootstrapper engine.
781 engineContext.dwThreadId = ::GetCurrentThreadId();
782 engineContext.pEngineState = pEngineState;
783
784 // Load the bootstrapper application.
785 hr = UserExperienceLoad(&pEngineState->userExperience, &engineContext, &pEngineState->command);
786 ExitOnFailure(hr, "Failed to load BA.");
787
788 fStartupCalled = TRUE;
789 hr = UserExperienceOnStartup(&pEngineState->userExperience);
790 ExitOnFailure(hr, "Failed to start bootstrapper application.");
791
792 // Enter the message pump.
793 while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
794 {
795 if (-1 == fRet)
796 {
797 hr = E_UNEXPECTED;
798 ExitOnRootFailure(hr, "Unexpected return value from message pump.");
799 }
800 else
801 {
802 // When the BA makes a request from its own thread, it's common for the PostThreadMessage in externalengine.cpp
803 // to block until this thread waits on something. It's also common for Detect and Plan to never wait on something.
804 // In the extreme case, the engine could be elevating in Apply before the Detect call returned to the BA.
805 // This helps to avoid that situation, which could be blocking a UI thread.
806 ::Sleep(0);
807
808 ProcessMessage(pEngineState, &msg);
809 }
810 }
811
812 // Get exit code.
813 pEngineState->userExperience.dwExitCode = (DWORD)msg.wParam;
814
815LExit:
816 if (fStartupCalled)
817 {
818 UserExperienceOnShutdown(&pEngineState->userExperience, &shutdownAction);
819 if (BOOTSTRAPPER_SHUTDOWN_ACTION_RESTART == shutdownAction)
820 {
821 LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RESTART, LoggingBoolToString(pEngineState->fRestart));
822 pEngineState->fRestart = TRUE;
823 }
824 else if (BOOTSTRAPPER_SHUTDOWN_ACTION_RELOAD_BOOTSTRAPPER == shutdownAction)
825 {
826 LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RELOAD);
827 *pfReloadApp = TRUE;
828 }
829 else if (BOOTSTRAPPER_SHUTDOWN_ACTION_SKIP_CLEANUP == shutdownAction)
830 {
831 LogId(REPORT_STANDARD, MSG_BA_REQUESTED_SKIP_CLEANUP);
832 *pfSkipCleanup = TRUE;
833 }
834 }
835
836 // Unload BA.
837 UserExperienceUnload(&pEngineState->userExperience);
838
839 return hr;
840}
841
842static HRESULT ProcessMessage(
843 __in BURN_ENGINE_STATE* pEngineState,
844 __in const MSG* pmsg
845 )
846{
847 HRESULT hr = S_OK;
848
849 UserExperienceActivateEngine(&pEngineState->userExperience);
850
851 if (pEngineState->fQuit)
852 {
853 LogId(REPORT_WARNING, MSG_IGNORE_OPERATION_AFTER_QUIT, LoggingBurnMessageToString(pmsg->message));
854 ExitFunction1(hr = E_INVALIDSTATE);
855 }
856
857 switch (pmsg->message)
858 {
859 case WM_BURN_DETECT:
860 hr = CoreDetect(pEngineState, reinterpret_cast<HWND>(pmsg->lParam));
861 break;
862
863 case WM_BURN_PLAN:
864 hr = CorePlan(pEngineState, static_cast<BOOTSTRAPPER_ACTION>(pmsg->lParam));
865 break;
866
867 case WM_BURN_ELEVATE:
868 hr = CoreElevate(pEngineState, reinterpret_cast<HWND>(pmsg->lParam));
869 break;
870
871 case WM_BURN_APPLY:
872 hr = CoreApply(pEngineState, reinterpret_cast<HWND>(pmsg->lParam));
873 break;
874
875 case WM_BURN_LAUNCH_APPROVED_EXE:
876 hr = CoreLaunchApprovedExe(pEngineState, reinterpret_cast<BURN_LAUNCH_APPROVED_EXE*>(pmsg->lParam));
877 break;
878
879 case WM_BURN_QUIT:
880 hr = CoreQuit(pEngineState, static_cast<int>(pmsg->wParam));
881 break;
882 }
883
884LExit:
885 UserExperienceDeactivateEngine(&pEngineState->userExperience);
886
887 return hr;
888}
889
890static HRESULT DAPI RedirectLoggingOverPipe(
891 __in_z LPCSTR szString,
892 __in_opt LPVOID pvContext
893 )
894{
895 static BOOL s_fCurrentlyLoggingToPipe = FALSE;
896
897 HRESULT hr = S_OK;
898 BURN_ENGINE_STATE* pEngineState = static_cast<BURN_ENGINE_STATE*>(pvContext);
899 BOOL fStartedLogging = FALSE;
900 HANDLE hPipe = INVALID_HANDLE_VALUE;
901 BYTE* pbData = NULL;
902 SIZE_T cbData = 0;
903 DWORD dwResult = 0;
904
905 // Prevent this function from being called recursively.
906 if (s_fCurrentlyLoggingToPipe)
907 {
908 ExitFunction();
909 }
910
911 s_fCurrentlyLoggingToPipe = TRUE;
912 fStartedLogging = TRUE;
913
914 // Make sure the current thread set the pipe in TLS.
915 hPipe = ::TlsGetValue(pEngineState->dwElevatedLoggingTlsId);
916 if (!hPipe || INVALID_HANDLE_VALUE == hPipe)
917 {
918 hr = HRESULT_FROM_WIN32(ERROR_PIPE_NOT_CONNECTED);
919 ExitFunction();
920 }
921
922 // Do not log or use ExitOnFailure() macro here because they will be discarded
923 // by the recursive block at the top of this function.
924 hr = BuffWriteStringAnsi(&pbData, &cbData, szString);
925 if (SUCCEEDED(hr))
926 {
927 hr = PipeSendMessage(hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_LOG), pbData, cbData, NULL, NULL, &dwResult);
928 if (SUCCEEDED(hr))
929 {
930 hr = (HRESULT)dwResult;
931 }
932 }
933
934LExit:
935 ReleaseBuffer(pbData);
936
937 // We started logging so remember to say we are no longer logging.
938 if (fStartedLogging)
939 {
940 s_fCurrentlyLoggingToPipe = FALSE;
941 }
942
943 return hr;
944}
945
946static HRESULT Restart()
947{
948 HRESULT hr = S_OK;
949 HANDLE hProcessToken = NULL;
950 TOKEN_PRIVILEGES priv = { };
951 DWORD dwRetries = 0;
952
953 if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken))
954 {
955 ExitWithLastError(hr, "Failed to get process token.");
956 }
957
958 priv.PrivilegeCount = 1;
959 priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
960 if (!::LookupPrivilegeValueW(NULL, L"SeShutdownPrivilege", &priv.Privileges[0].Luid))
961 {
962 ExitWithLastError(hr, "Failed to get shutdown privilege LUID.");
963 }
964
965 if (!::AdjustTokenPrivileges(hProcessToken, FALSE, &priv, sizeof(TOKEN_PRIVILEGES), NULL, 0))
966 {
967 ExitWithLastError(hr, "Failed to adjust token to add shutdown privileges.");
968 }
969
970 do
971 {
972 hr = S_OK;
973
974 // Wait a second to let the companion process (assuming we did an elevated install) to get to the
975 // point where it too is thinking about restarting the computer. Only one will schedule the restart
976 // but both will have their log files closed and otherwise be ready to exit.
977 //
978 // On retry, we'll also wait a second to let the OS try to get to a place where the restart can
979 // be initiated.
980 ::Sleep(1000);
981
982 if (!vpfnInitiateSystemShutdownExW(NULL, NULL, 0, FALSE, TRUE, SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_INSTALLATION | SHTDN_REASON_FLAG_PLANNED))
983 {
984 hr = HRESULT_FROM_WIN32(::GetLastError());
985 }
986 } while (dwRetries++ < RESTART_RETRIES && (HRESULT_FROM_WIN32(ERROR_MACHINE_LOCKED) == hr || HRESULT_FROM_WIN32(ERROR_NOT_READY) == hr));
987 ExitOnRootFailure(hr, "Failed to schedule restart.");
988
989LExit:
990 ReleaseHandle(hProcessToken);
991 return hr;
992}
diff --git a/src/burn/engine/engine.mc b/src/burn/engine/engine.mc
new file mode 100644
index 00000000..25d5b4e4
--- /dev/null
+++ b/src/burn/engine/engine.mc
@@ -0,0 +1,1090 @@
1; // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3
4MessageIdTypedef=DWORD
5
6LanguageNames=(English=0x409:MSG00409)
7
8
9; // message definitions
10
11; // MessageId=#
12; // Severity=Success
13; // SymbolicName=MSG_SUCCESS
14; // Language=English
15; // Success %1.
16; // .
17;
18; // MessageId=#
19; // Severity=Warning
20; // SymbolicName=MSG_WARNING
21; // Language=English
22; // Warning %1.
23; // .
24;
25; // MessageId=#
26; // Severity=Error
27; // SymbolicName=MSG_ERROR
28; // Language=English
29; // Error %1.
30; // .
31
32MessageId=1
33Severity=Success
34SymbolicName=MSG_BURN_INFO
35Language=English
36Burn %7!hs! v%1!hs!, Windows v%2!d!.%3!d! %8!hs! (Build %4!d!: Service Pack %5!d!), path: %6!ls!
37.
38
39MessageId=2
40Severity=Warning
41SymbolicName=MSG_BURN_UNKNOWN_PRIVATE_SWITCH
42Language=English
43Unknown burn internal command-line switch encountered: '%1!ls!'.
44.
45
46MessageId=3
47Severity=Success
48SymbolicName=MSG_BURN_RUN_BY_RELATED_BUNDLE
49Language=English
50This bundle is being run by a related bundle as type '%1!hs!'.
51.
52
53MessageId=4
54Severity=Success
55SymbolicName=MSG_BA_REQUESTED_RESTART
56Language=English
57Bootstrapper application requested restart at shutdown. Planned to restart already: %1!hs!.
58.
59
60MessageId=5
61Severity=Warning
62SymbolicName=MSG_RESTARTING
63Language=English
64Restarting computer...
65=======================================
66.
67
68MessageId=6
69Severity=Success
70SymbolicName=MSG_BA_REQUESTED_RELOAD
71Language=English
72Bootstrapper application requested to be reloaded.
73.
74
75MessageId=7
76Severity=Success
77SymbolicName=MSG_EXITING
78Language=English
79Exit code: 0x%1!x!, restarting: %2!hs!
80.
81
82MessageId=8
83Severity=Warning
84SymbolicName=MSG_RESTART_ABORTED
85Language=English
86Preventing requested restart because bundle is related: '%1!hs!'. Returning restart requested to parent bundle.
87.
88
89MessageId=9
90Severity=Success
91SymbolicName=MSG_BURN_COMMAND_LINE
92Language=English
93Command Line: '%1!ls!'
94.
95
96MessageId=10
97Severity=Success
98SymbolicName=MSG_LAUNCH_ELEVATED_ENGINE_STARTING
99Language=English
100Launching elevated engine process.
101.
102
103MessageId=11
104Severity=Success
105SymbolicName=MSG_LAUNCH_ELEVATED_ENGINE_SUCCESS
106Language=English
107Launched elevated engine process.
108.
109
110MessageId=12
111Severity=Success
112SymbolicName=MSG_CONNECT_TO_ELEVATED_ENGINE_SUCCESS
113Language=English
114Connected to elevated engine.
115.
116
117MessageId=13
118Severity=Warning
119SymbolicName=MSG_MANIFEST_INVALID_VERSION
120Language=English
121The manifest contains an invalid version string: '%1!ls!'
122.
123
124MessageId=14
125Severity=Success
126SymbolicName=MSG_BA_REQUESTED_SKIP_CLEANUP
127Language=English
128Bootstrapper application opted out of any engine behavior to automatically uninstall the bundle during shutdown.
129.
130
131MessageId=51
132Severity=Error
133SymbolicName=MSG_FAILED_PARSE_CONDITION
134Language=English
135Error %1!hs!. Failed to parse condition %2!ls!. Unexpected symbol at position %3!hs!
136.
137
138MessageId=52
139Severity=Success
140SymbolicName=MSG_CONDITION_RESULT
141Language=English
142Condition '%1!ls!' evaluates to %2!hs!.
143.
144
145MessageId=53
146Severity=Error
147SymbolicName=MSG_FAILED_CONDITION_CHECK
148Language=English
149Bundle global condition check didn't succeed - aborting without loading application.
150.
151
152MessageId=54
153Severity=Error
154SymbolicName=MSG_RESOLVE_SOURCE_FAILED
155Language=English
156Failed to resolve source for payload: %2!ls!, package: %3!ls!, container: %4!ls!, error: %1!ls!.
157.
158
159MessageId=55
160Severity=Warning
161SymbolicName=MSG_CANNOT_LOAD_STATE_FILE
162Language=English
163Could not load or read state file: %2!ls!, error: 0x%1!x!.
164.
165
166MessageId=56
167Severity=Error
168SymbolicName=MSG_USER_CANCELED
169Language=English
170Application canceled operation: %2!ls!, error: %1!ls!
171.
172
173MessageId=57
174Severity=Warning
175SymbolicName=MSG_CONDITION_INVALID_VERSION
176Language=English
177Condition '%1!ls!' contains invalid version string '%2!ls!'.
178.
179
180MessageId=58
181Severity=Warning
182SymbolicName=MSG_IGNORE_OPERATION_AFTER_QUIT
183Language=English
184Bootstrapper application already requested to quit, ignoring request: '%1!hs!'.
185.
186
187MessageId=100
188Severity=Success
189SymbolicName=MSG_DETECT_BEGIN
190Language=English
191Detect begin, %1!u! packages
192.
193
194MessageId=101
195Severity=Success
196SymbolicName=MSG_DETECTED_PACKAGE
197Language=English
198Detected package: %1!ls!, state: %2!hs!, cached: %3!hs!, install registration state: %4!hs!, cache registration state: %5!hs!
199.
200
201MessageId=102
202Severity=Success
203SymbolicName=MSG_DETECTED_RELATED_BUNDLE
204Language=English
205Detected related bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!ls!, operation: %5!hs!, cached: %6!hs!
206.
207
208MessageId=103
209Severity=Success
210SymbolicName=MSG_DETECTED_RELATED_PACKAGE
211Language=English
212Detected related package: %1!ls!, scope: %2!hs!, version: %3!ls!, language: %4!u! operation: %5!hs!
213.
214
215MessageId=104
216Severity=Success
217SymbolicName=MSG_DETECTED_MSI_FEATURE
218Language=English
219Detected package: %1!ls!, feature: %2!ls!, state: %3!hs!
220.
221
222MessageId=105
223Severity=Success
224SymbolicName=MSG_DETECTED_MSP_TARGET
225Language=English
226Detected package: %1!ls! target: %2!ls!, state: %3!hs!
227.
228
229MessageId=106
230Severity=Success
231SymbolicName=MSG_DETECT_CALCULATE_PATCH_APPLICABILITY
232Language=English
233Calculating patch applicability for target product code: %1!ls!, context: %2!hs!
234.
235
236MessageId=107
237Severity=Success
238SymbolicName=MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE
239Language=English
240Detected forward compatible bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!ls!, cached: %5!hs!
241.
242
243MessageId=108
244Severity=Warning
245SymbolicName=MSG_DETECT_RELATED_BUNDLE_NOT_CACHED
246Language=English
247Detected related bundle missing from cache: %1!ls!, cache path: %2!ls!
248.
249
250MessageId=120
251Severity=Warning
252SymbolicName=MSG_DETECT_PACKAGE_NOT_FULLY_CACHED
253Language=English
254Detected partially cached package: %1!ls!, missing payload: %2!ls!
255.
256
257MessageId=121
258Severity=Warning
259SymbolicName=MSG_DETECT_FAILED_CALCULATE_PATCH_APPLICABILITY
260Language=English
261Could not calculate patch applicability for target product code: %1!ls!, context: %2!hs!, reason: 0x%3!x!
262.
263
264MessageId=122
265Severity=Warning
266SymbolicName=MSG_RELATED_PACKAGE_INVALID_VERSION
267Language=English
268Related package: '%1!ls!' has invalid version: %2!ls!
269.
270
271MessageId=123
272Severity=Warning
273SymbolicName=MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION
274Language=English
275Detected msi package with invalid version, product code: '%1!ls!', version: '%2!ls!'
276.
277
278MessageId=151
279Severity=Error
280SymbolicName=MSG_FAILED_DETECT_PACKAGE
281Language=English
282Detect failed for package: %2!ls!, error: %1!ls!
283.
284
285MessageId=152
286Severity=Error
287SymbolicName=MSG_FAILED_READ_RELATED_PACKAGE_LANGUAGE
288Language=English
289Detected related package: %2!ls!, but failed to read language: %3!hs!, error: 0x%1!x!
290.
291
292MessageId=170
293Severity=Warning
294SymbolicName=MSG_DETECT_BAD_PRODUCT_CONFIGURATION
295Language=English
296Detected bad configuration for product: %1!ls!
297.
298
299MessageId=199
300Severity=Success
301SymbolicName=MSG_DETECT_COMPLETE
302Language=English
303Detect complete, result: 0x%1!x!, installed: %2!hs!, cached: %3!hs!, eligible for cleanup: %4!hs!
304.
305
306MessageId=200
307Severity=Success
308SymbolicName=MSG_PLAN_BEGIN
309Language=English
310Plan begin, %1!u! packages, action: %2!hs!
311.
312
313MessageId=201
314Severity=Success
315SymbolicName=MSG_PLANNED_PACKAGE
316Language=English
317Planned package: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, cache: %7!hs!, uncache: %8!hs!, dependency: %9!hs!, expected install registration state: %10!hs!, expected cache registration state: %11!hs!
318.
319
320MessageId=202
321Severity=Success
322SymbolicName=MSG_PLANNED_BUNDLE_UX_CHANGED_REQUEST
323Language=English
324Planned bundle: %1!ls!, ba requested state: %2!hs! over default: %3!hs!
325.
326
327MessageId=203
328Severity=Success
329SymbolicName=MSG_PLANNED_MSI_FEATURE
330Language=English
331 Planned feature: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute action: %5!hs!, rollback action: %6!hs!
332.
333
334MessageId=204
335Severity=Success
336SymbolicName=MSG_PLANNED_MSI_FEATURES
337Language=English
338 Plan %1!u! msi features for package: %2!ls!
339.
340
341MessageId=205
342Severity=Warning
343SymbolicName=MSG_PLAN_SKIP_PATCH_ACTION
344Language=English
345Plan %5!hs! skipped patch: %1!ls!, action: %2!hs! because chained target package: %3!ls! being uninstalled
346.
347
348MessageId=206
349Severity=Warning
350SymbolicName=MSG_PLAN_SKIP_SLIPSTREAM_ACTION
351Language=English
352Plan %5!hs! skipped patch: %1!ls!, action: %2!hs! because slipstreamed into chained target package: %3!ls!, action: %4!hs!
353.
354
355MessageId=207
356Severity=Success
357SymbolicName=MSG_PLANNED_RELATED_BUNDLE
358Language=English
359Planned related bundle: %1!ls!, type: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, dependency: %7!hs!
360.
361
362MessageId=208
363Severity=Warning
364SymbolicName=MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE
365Language=English
366Plan disabled rollback due to incomplete cache for package: %1!ls!, original rollback action: %2!hs!
367.
368
369MessageId=209
370Severity=Warning
371SymbolicName=MSG_PLAN_SKIPPED_PROVIDER_KEY_REMOVAL
372Language=English
373Plan skipped removal of provider key: %1!ls! because it is registered to a different bundle: %2!ls!
374.
375
376MessageId=210
377Severity=Warning
378SymbolicName=MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS
379Language=English
380Plan skipped due to remaining dependents:
381.
382
383MessageId=211
384Severity=Success
385SymbolicName=MSG_PLANNED_UPGRADE_BUNDLE
386Language=English
387Planned upgrade bundle: %1!ls!, default requested: %2!hs!, ba requested: %3!hs!, execute: %4!hs!, rollback: %5!hs!, dependency: %6!hs!
388.
389
390MessageId=212
391Severity=Success
392SymbolicName=MSG_PLANNED_FORWARD_COMPATIBLE_BUNDLE
393Language=English
394Planned forward compatible bundle: %1!ls!, default requested: %2!hs!, ba requested: %3!hs!, execute: %4!hs!, rollback: %5!hs!, dependency: %6!hs!
395.
396
397MessageId=213
398Severity=Success
399SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_DEPENDENT
400Language=English
401Plan skipped related bundle: %1!ls!, type: %2!hs!, because it was dependent and the current bundle is being executed as type: %3!hs!.
402.
403
404MessageId=214
405Severity=Success
406SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_SCHEDULED
407Language=English
408Plan skipped related bundle: %1!ls!, type: %2!hs!, because it was previously scheduled.
409.
410
411MessageId=216
412Severity=Success
413SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER
414Language=English
415Plan skipped related bundle: %1!ls!, type: %2!hs!, provider key: %3!ls!, because an embedded bundle with the same provider key is being installed.
416.
417
418MessageId=217
419Severity=Success
420SymbolicName=MSG_PLAN_SKIPPED_DEPENDENT_BUNDLE_REPAIR
421Language=English
422Plan skipped dependent bundle repair: %1!ls!, type: %2!hs!, because no packages are being executed during this uninstall operation.
423.
424
425MessageId=218
426Severity=Success
427SymbolicName=MSG_PLANNED_MSP_TARGETS
428Language=English
429 Plan %1!u! patch targets for package: %2!ls!
430.
431
432MessageId=219
433Severity=Success
434SymbolicName=MSG_PLANNED_MSP_TARGET
435Language=English
436 Planned patch target: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!
437.
438
439MessageId=220
440Severity=Success
441SymbolicName=MSG_PLANNED_SLIPSTREAMED_MSP_TARGETS
442Language=English
443 Plan %1!u! slipstream patches for package: %2!ls!
444.
445
446MessageId=221
447Severity=Success
448SymbolicName=MSG_PLANNED_SLIPSTREAMED_MSP_TARGET
449Language=English
450 Planned slipstreamed patch: %1!ls!, execute: %2!hs!, rollback: %3!hs!
451.
452
453MessageId=299
454Severity=Success
455SymbolicName=MSG_PLAN_COMPLETE
456Language=English
457Plan complete, result: 0x%1!x!
458.
459
460MessageId=300
461Severity=Success
462SymbolicName=MSG_APPLY_BEGIN
463Language=English
464Apply begin
465.
466
467MessageId=301
468Severity=Success
469SymbolicName=MSG_APPLYING_PACKAGE
470Language=English
471Applying %1!hs! package: %2!ls!, action: %3!hs!, path: %4!ls!, arguments: '%5!ls!'
472.
473
474MessageId=302
475Severity=Success
476SymbolicName=MSG_ACQUIRED_PAYLOAD
477Language=English
478Acquired payload: %1!ls! to working path: %2!ls! from: %4!ls!.
479.
480
481MessageId=303
482Severity=Success
483SymbolicName=MSG_VERIFIED_EXISTING_CONTAINER
484Language=English
485Verified existing container: %1!ls! at path: %2!ls!.
486.
487
488MessageId=304
489Severity=Success
490SymbolicName=MSG_VERIFIED_EXISTING_PAYLOAD
491Language=English
492Verified existing payload: %1!ls! at path: %2!ls!.
493.
494
495MessageId=305
496Severity=Success
497SymbolicName=MSG_VERIFIED_ACQUIRED_PAYLOAD
498Language=English
499Verified acquired payload: %1!ls! at path: %2!ls!, %3!hs! to: %4!ls!.
500.
501
502MessageId=306
503Severity=Success
504SymbolicName=MSG_APPLYING_PATCH_PACKAGE
505Language=English
506Applying package: %1!ls!, target: %5!ls!, action: %2!hs!, path: %3!ls!, arguments: '%4!ls!'
507.
508
509MessageId=307
510Severity=Warning
511SymbolicName=MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE
512Language=English
513Attempted to uninstall absent package: %1!ls!. Continuing...
514.
515
516MessageId=308
517Severity=Warning
518SymbolicName=MSG_FAILED_PAUSE_AU
519Language=English
520Automatic updates could not be paused due to error: 0x%1!x!. Continuing...
521.
522
523MessageId=309
524Severity=Warning
525SymbolicName=MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE
526Language=English
527Skipping apply of package: %1!ls! due to cache error: 0x%2!x!. Continuing...
528.
529
530MessageId=310
531Severity=Error
532SymbolicName=MSG_FAILED_VERIFY_PAYLOAD
533Language=English
534Failed to verify payload: %2!ls! at path: %3!ls!, error: %1!ls!. Deleting file.
535.
536
537MessageId=311
538Severity=Error
539SymbolicName=MSG_FAILED_ACQUIRE_CONTAINER
540Language=English
541Failed to acquire container: %2!ls! to working path: %3!ls!, error: %1!ls!.
542.
543
544MessageId=312
545Severity=Error
546SymbolicName=MSG_FAILED_EXTRACT_CONTAINER
547Language=English
548Failed to extract payloads from container: %2!ls! to working path: %3!ls!, error: %1!ls!.
549.
550
551MessageId=313
552Severity=Error
553SymbolicName=MSG_FAILED_ACQUIRE_PAYLOAD
554Language=English
555Failed to acquire payload: %2!ls! to working path: %3!ls!, error: %1!ls!.
556.
557
558MessageId=314
559Severity=Error
560SymbolicName=MSG_FAILED_CACHE_PAYLOAD
561Language=English
562Failed to cache payload: %2!ls! from working path: %4!ls!, error: %1!ls!.
563.
564
565MessageId=315
566Severity=Error
567SymbolicName=MSG_FAILED_LAYOUT_BUNDLE
568Language=English
569Failed to layout bundle: %2!ls! to layout directory: %3!ls!, error: %1!ls!.
570.
571
572MessageId=316
573Severity=Error
574SymbolicName=MSG_FAILED_LAYOUT_CONTAINER
575Language=English
576Failed to layout container: %2!ls! to layout directory: %3!ls!, error: %1!ls!.
577.
578
579
580MessageId=317
581Severity=Error
582SymbolicName=MSG_FAILED_LAYOUT_PAYLOAD
583Language=English
584Failed to layout payload: %2!ls! from working path: %4!ls! to layout directory: %3!ls!, error: %1!ls!.
585.
586
587MessageId=318
588Severity=Success
589SymbolicName=MSG_ROLLBACK_PACKAGE_SKIPPED
590Language=English
591Skipped rollback of package: %1!ls!, action: %2!hs!, already: %3!hs!
592.
593
594MessageId=319
595Severity=Success
596SymbolicName=MSG_APPLY_COMPLETED_PACKAGE
597Language=English
598Applied %1!hs! package: %2!ls!, result: 0x%3!x!, restart: %4!hs!
599.
600
601MessageId=320
602Severity=Success
603SymbolicName=MSG_DEPENDENCY_BUNDLE_REGISTER
604Language=English
605Registering bundle dependency provider: %1!ls!, version: %2!ls!
606.
607
608MessageId=321
609Severity=Warning
610SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_NOPROVIDERS
611Language=English
612Skipping dependency registration on package with no dependency providers: %1!ls!
613.
614
615MessageId=322
616Severity=Warning
617SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE
618Language=English
619Skipping cross-scope dependency registration on package: %1!ls!, bundle scope: %2!hs!, package scope: %3!hs!
620.
621
622MessageId=323
623Severity=Success
624SymbolicName=MSG_DEPENDENCY_PACKAGE_REGISTER
625Language=English
626Registering package dependency provider: %1!ls!, version: %2!ls!, package: %3!ls!
627.
628
629MessageId=324
630Severity=Warning
631SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_MISSING
632Language=English
633Skipping dependency registration on missing package provider: %1!ls!, package: %2!ls!
634.
635
636MessageId=325
637Severity=Success
638SymbolicName=MSG_DEPENDENCY_PACKAGE_REGISTER_DEPENDENCY
639Language=English
640Registering dependency: %1!ls! on package provider: %2!ls!, package: %3!ls!
641.
642
643MessageId=326
644Severity=Success
645SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY
646Language=English
647Removed dependency: %1!ls! on package provider: %2!ls!, package %3!ls!
648.
649
650MessageId=327
651Severity=Warning
652SymbolicName=MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS
653Language=English
654Will not uninstall package: %1!ls!, found dependents:
655.
656
657MessageId=328
658Severity=Warning
659SymbolicName=MSG_DEPENDENCY_PACKAGE_DEPENDENT
660Language=English
661Found dependent: %1!ls!, name: %2!ls!
662.
663
664MessageId=329
665Severity=Success
666SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED
667Language=English
668Removed package dependency provider: %1!ls!, package: %2!ls!
669.
670
671MessageId=330
672Severity=Success
673SymbolicName=MSG_DEPENDENCY_BUNDLE_UNREGISTERED
674Language=English
675Removed bundle dependency provider: %1!ls!
676.
677
678MessageId=331
679Severity=Warning
680SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY_FAILED
681Language=English
682Could not remove dependency: %1!ls! on package provider: %2!ls!, package %3!ls!, error: 0x%4!x!
683.
684
685MessageId=332
686Severity=Warning
687SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_FAILED
688Language=English
689Could not remove package dependency provider: %1!ls!, package: %2!ls!, error: 0x%3!x!
690.
691
692MessageId=333
693Severity=Warning
694SymbolicName=MSG_DEPENDENCY_BUNDLE_UNREGISTERED_FAILED
695Language=English
696Could not remove bundle dependency provider: %1!ls!, error: 0x%2!x!
697.
698
699MessageId=334
700Severity=Warning
701SymbolicName=MSG_DEPENDENCY_BUNDLE_DEPENDENT
702Language=English
703Found dependent: %1!ls!, name: %2!ls!
704.
705
706MessageId=335
707Severity=Success
708SymbolicName=MSG_ACQUIRE_BUNDLE_PAYLOAD
709Language=English
710Acquiring bundle payload: %2!ls!, %3!hs! from: %4!ls!
711.
712
713MessageId=336
714Severity=Success
715SymbolicName=MSG_ACQUIRE_CONTAINER
716Language=English
717Acquiring container: %1!ls!, %3!hs! from: %4!ls!
718.
719
720MessageId=338
721Severity=Success
722SymbolicName=MSG_ACQUIRE_PACKAGE_PAYLOAD
723Language=English
724Acquiring package: %1!ls!, payload: %2!ls!, %3!hs! from: %4!ls!
725.
726
727MessageId=339
728Severity=Error
729SymbolicName=MSG_FAILED_VERIFY_CONTAINER
730Language=English
731Failed to verify container: %2!ls! at path: %3!ls!, error: %1!ls!. Deleting file.
732.
733
734MessageId=340
735Severity=Warning
736SymbolicName=MSG_CACHE_CONTINUING_NONVITAL_PACKAGE
737Language=English
738Cached non-vital package: %1!ls!, encountered error: 0x%2!x!. Continuing...
739.
740
741MessageId=346
742Severity=Warning
743SymbolicName=MSG_CACHE_RETRYING_PACKAGE
744Language=English
745Application requested retry of caching package: %1!ls!, encountered error: 0x%2!x!. Retrying...
746.
747
748MessageId=347
749Severity=Warning
750SymbolicName=MSG_CACHE_RETRYING_CONTAINER
751Language=English
752Application requested retry of caching container: %2!ls!, encountered error: %1!ls!. Retrying...
753.
754
755MessageId=348
756Severity=Warning
757SymbolicName=MSG_APPLY_RETRYING_PACKAGE
758Language=English
759Application requested retry of executing package: %1!ls!, encountered error: 0x%2!x!. Retrying...
760.
761
762MessageId=349
763Severity=Warning
764SymbolicName=MSG_CACHE_RETRYING_PAYLOAD
765Language=English
766Application requested retry of caching payload: %2!ls!, encountered error: %1!ls!. Retrying...
767.
768
769MessageId=350
770Severity=Warning
771SymbolicName=MSG_APPLY_CONTINUING_NONVITAL_PACKAGE
772Language=English
773Applied non-vital package: %1!ls!, encountered error: 0x%2!x!. Continuing...
774.
775
776MessageId=351
777Severity=Success
778SymbolicName=MSG_UNCACHE_PACKAGE
779Language=English
780Removing cached package: %1!ls!, from path: %2!ls!
781.
782
783MessageId=352
784Severity=Success
785SymbolicName=MSG_UNCACHE_BUNDLE
786Language=English
787Removing cached bundle: %1!ls!, from path: %2!ls!
788.
789
790MessageId=353
791Severity=Warning
792SymbolicName=MSG_UNABLE_UNCACHE_PACKAGE
793Language=English
794Unable to remove cached package: %1!ls!, from path: %2!ls!, reason: 0x%3!x!. Continuing...
795.
796
797MessageId=354
798Severity=Warning
799SymbolicName=MSG_UNABLE_UNCACHE_BUNDLE
800Language=English
801Unable to remove cached bundle: %1!ls!, from path: %2!ls!, reason: 0x%3!x!. Continuing...
802.
803
804MessageId=355
805Severity=Warning
806SymbolicName=MSG_SOURCELIST_REGISTER
807Language=English
808Unable to register source directory: %1!ls!, product: %2!ls!, reason: 0x%3!x!. Continuing...
809.
810
811MessageId=356
812Severity=Warning
813SymbolicName=MSG_APPLY_RETRYING_ACQUIRE_CONTAINER
814Language=English
815Application requested retry acquire of container: %2!ls!, encountered error: %1!ls!. Retrying...
816.
817
818MessageId=357
819Severity=Warning
820SymbolicName=MSG_APPLY_RETRYING_ACQUIRE_PAYLOAD
821Language=English
822Application requested retry acquire of payload: %2!ls!, encountered error: %1!ls!. Retrying...
823.
824
825MessageId=358
826Severity=Success
827SymbolicName=MSG_PAUSE_AU_STARTING
828Language=English
829Pausing automatic updates.
830.
831
832MessageId=359
833Severity=Success
834SymbolicName=MSG_PAUSE_AU_SUCCEEDED
835Language=English
836Paused automatic updates.
837.
838
839MessageId=360
840Severity=Success
841SymbolicName=MSG_SYSTEM_RESTORE_POINT_STARTING
842Language=English
843Creating a system restore point.
844.
845
846MessageId=361
847Severity=Success
848SymbolicName=MSG_SYSTEM_RESTORE_POINT_SUCCEEDED
849Language=English
850Created a system restore point.
851.
852
853MessageId=362
854Severity=Success
855SymbolicName=MSG_SYSTEM_RESTORE_POINT_DISABLED
856Language=English
857System restore disabled, system restore point not created.
858.
859
860MessageId=363
861Severity=Warning
862SymbolicName=MSG_SYSTEM_RESTORE_POINT_FAILED
863Language=English
864Could not create system restore point, error: 0x%1!x!. Continuing...
865.
866
867MessageId=370
868Severity=Success
869SymbolicName=MSG_SESSION_BEGIN
870Language=English
871Session begin, registration key: %1!ls!, options: 0x%2!x!, disable resume: %3!hs!
872.
873
874MessageId=371
875Severity=Success
876SymbolicName=MSG_SESSION_UPDATE
877Language=English
878Updating session, registration key: %1!ls!, resume: %2!hs!, restart initiated: %3!hs!, disable resume: %4!hs!
879.
880
881MessageId=372
882Severity=Success
883SymbolicName=MSG_SESSION_END
884Language=English
885Session end, registration key: %1!ls!, resume: %2!hs!, restart: %3!hs!, disable resume: %4!hs!
886.
887
888MessageId=373
889Severity=Success
890SymbolicName=MSG_POST_APPLY_CALCULATE_REGISTRATION
891Language=English
892Calculating whether to keep registration
893.
894
895
896MessageId=374
897Severity=Success
898SymbolicName=MSG_POST_APPLY_PACKAGE
899Language=English
900 package: %1!ls!, install registration state: %2!hs!, cache registration state: %3!hs!
901.
902
903MessageId=380
904Severity=Warning
905SymbolicName=MSG_APPLY_SKIPPED
906Language=English
907Apply skipped, no planned actions
908.
909
910MessageId=381
911Severity=Warning
912SymbolicName=MSG_APPLY_CANCEL_IGNORED_DURING_ROLLBACK
913Language=English
914Ignoring application request to cancel from %1!ls! during rollback.
915.
916
917MessageId=382
918Severity=Warning
919SymbolicName=MSG_PLAN_ROLLBACK_DISABLED
920Language=English
921Rollback is disabled for this bundle.
922.
923
924MessageId=383
925Severity=Error
926SymbolicName=MSG_MSI_TRANSACTIONS_DISABLED
927Language=English
928Windows Installer rollback is disabled on this computer. It must be enabled for this bundle to proceed.
929.
930
931MessageId=384
932Severity=Success
933SymbolicName=MSG_MSI_TRANSACTION_BEGIN
934Language=English
935Starting a new MSI transaction, id: %1!ls!
936.
937
938MessageId=385
939Severity=Success
940SymbolicName=MSG_MSI_TRANSACTION_COMMIT
941Language=English
942Committing MSI transaction, id: %1!ls!
943.
944
945MessageId=386
946Severity=Warning
947SymbolicName=MSG_MSI_TRANSACTION_ROLLBACK
948Language=English
949Rolling back MSI transaction, id: %1!ls!
950.
951
952MessageId=387
953Severity=Error
954SymbolicName=MSG_RESTART_REQUEST_DURING_MSI_TRANSACTION
955Language=English
956Illegal state: Reboot requested within an MSI transaction, id: %1!ls!
957.
958
959MessageId=399
960Severity=Success
961SymbolicName=MSG_APPLY_COMPLETE
962Language=English
963Apply complete, result: 0x%1!x!, restart: %2!hs!, ba requested restart: %3!hs!
964.
965
966MessageId=400
967Severity=Success
968SymbolicName=MSG_SYSTEM_SHUTDOWN
969Language=English
970Received system request to shut down the process: critical: %1!hs!, elevated: %2!hs!, allowed: %3!hs!
971.
972
973MessageId=410
974Severity=Success
975SymbolicName=MSG_VARIABLE_DUMP
976Language=English
977Variable: %1!ls!
978.
979
980MessageId=411
981Severity=Warning
982SymbolicName=MSG_VARIABLE_INVALID_VERSION
983Language=English
984The variable '%1!ls!' is being set with an invalid version string.
985.
986
987MessageId=412
988Severity=Warning
989SymbolicName=MSG_INVALID_VERSION_COERSION
990Language=English
991The string '%1!ls!' could not be coerced to a valid version.
992.
993
994MessageId=420
995Severity=Success
996SymbolicName=MSG_RESUME_AU_STARTING
997Language=English
998Resuming automatic updates.
999.
1000
1001MessageId=421
1002Severity=Success
1003SymbolicName=MSG_RESUME_AU_SUCCEEDED
1004Language=English
1005Resumed automatic updates.
1006.
1007
1008MessageId=500
1009Severity=Success
1010SymbolicName=MSG_QUIT
1011Language=English
1012Shutting down, exit code: 0x%1!x!
1013.
1014
1015MessageId=501
1016Severity=Warning
1017SymbolicName=MSG_STATE_NOT_SAVED
1018Language=English
1019The state file could not be saved, error: %1!ls!. Continuing...
1020.
1021
1022MessageId=502
1023Severity=Success
1024SymbolicName=MSG_CLEANUP_BEGIN
1025Language=English
1026Cleanup begin.
1027.
1028
1029MessageId=503
1030Severity=Success
1031SymbolicName=MSG_CLEANUP_SKIPPED_APPLY
1032Language=English
1033Cleanup not required due to running Apply.
1034.
1035
1036MessageId=504
1037Severity=Success
1038SymbolicName=MSG_CLEANUP_SKIPPED_ELEVATION_REQUIRED
1039Language=English
1040Cleanup check skipped since this per-machine bundle would require elevation.
1041.
1042
1043MessageId=599
1044Severity=Success
1045SymbolicName=MSG_CLEANUP_COMPLETE
1046Language=English
1047Cleanup complete, result: 0x%1!x!
1048.
1049
1050MessageId=600
1051Severity=Success
1052SymbolicName=MSG_LAUNCH_APPROVED_EXE_BEGIN
1053Language=English
1054LaunchApprovedExe begin, id: %1!ls!
1055.
1056
1057MessageId=601
1058Severity=Success
1059SymbolicName=MSG_LAUNCH_APPROVED_EXE_SEARCH
1060Language=English
1061Searching registry for approved exe path, key: %1!ls!, value: '%2!ls!', win64: %3!ls!
1062.
1063
1064MessageId=602
1065Severity=Success
1066SymbolicName=MSG_LAUNCHING_APPROVED_EXE
1067Language=English
1068Launching approved exe, path: '%1!ls!', 'command: %2!ls!'
1069.
1070
1071MessageId=699
1072Severity=Success
1073SymbolicName=MSG_LAUNCH_APPROVED_EXE_COMPLETE
1074Language=English
1075LaunchApprovedExe complete, result: 0x%1!x!, processId: %2!lu!
1076.
1077
1078MessageId=700
1079Severity=Success
1080SymbolicName=MSG_MSI_PROPERTY_CONDITION_FAILED
1081Language=English
1082Skipping MSI property '%1!ls!' because condition '%2!ls!' evaluates to %3!hs!.
1083.
1084
1085MessageId=701
1086Severity=Warning
1087SymbolicName=MSG_PENDING_REBOOT_DETECTED
1088Language=English
1089A reboot is pending from a prior execution of this bundle: %1!ls!. Apply will be blocked. Continuing...
1090.
diff --git a/src/burn/engine/engine.vcxproj b/src/burn/engine/engine.vcxproj
new file mode 100644
index 00000000..b3a0f81b
--- /dev/null
+++ b/src/burn/engine/engine.vcxproj
@@ -0,0 +1,186 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5 <Import Project="..\..\packages\WixToolset.BootstrapperCore.Native.4.0.57\build\WixToolset.BootstrapperCore.Native.props" Condition="Exists('..\..\packages\WixToolset.BootstrapperCore.Native.4.0.57\build\WixToolset.BootstrapperCore.Native.props')" />
6 <Import Project="..\..\packages\WixToolset.DUtil.4.0.70\build\WixToolset.DUtil.props" Condition="Exists('..\..\packages\WixToolset.DUtil.4.0.70\build\WixToolset.DUtil.props')" />
7
8 <ItemGroup Label="ProjectConfigurations">
9 <ProjectConfiguration Include="Debug|Win32">
10 <Configuration>Debug</Configuration>
11 <Platform>Win32</Platform>
12 </ProjectConfiguration>
13 <ProjectConfiguration Include="Release|Win32">
14 <Configuration>Release</Configuration>
15 <Platform>Win32</Platform>
16 </ProjectConfiguration>
17 <ProjectConfiguration Include="Debug|x64">
18 <Configuration>Debug</Configuration>
19 <Platform>x64</Platform>
20 </ProjectConfiguration>
21 <ProjectConfiguration Include="Release|x64">
22 <Configuration>Release</Configuration>
23 <Platform>x64</Platform>
24 </ProjectConfiguration>
25 <ProjectConfiguration Include="Debug|ARM64">
26 <Configuration>Debug</Configuration>
27 <Platform>ARM64</Platform>
28 </ProjectConfiguration>
29 <ProjectConfiguration Include="Release|ARM64">
30 <Configuration>Release</Configuration>
31 <Platform>ARM64</Platform>
32 </ProjectConfiguration>
33 </ItemGroup>
34
35 <PropertyGroup Label="Globals">
36 <ProjectGuid>{8119537D-E1D9-6591-D51A-49768A2F9C37}</ProjectGuid>
37 <ConfigurationType>StaticLibrary</ConfigurationType>
38 <TargetName>engine</TargetName>
39 <PlatformToolset>v142</PlatformToolset>
40 <CharacterSet>Unicode</CharacterSet>
41 <Description>Native component of WixToolset.Burn</Description>
42 </PropertyGroup>
43
44 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
45 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
46
47 <PropertyGroup>
48 <ProjectAdditionalIncludeDirectories Condition=" '$(DirectReference)'=='true' ">$(ProjectDir)..\..\..\balutil\src\WixToolset.BootstrapperCore.Native\inc;$(ProjectAdditionalIncludeDirectories)</ProjectAdditionalIncludeDirectories>
49 </PropertyGroup>
50
51 <ImportGroup Label="ExtensionSettings">
52 </ImportGroup>
53 <ImportGroup Label="Shared">
54 </ImportGroup>
55
56 <ItemGroup>
57 <ClCompile Include="apply.cpp" />
58 <ClCompile Include="approvedexe.cpp" />
59 <ClCompile Include="burnextension.cpp" />
60 <ClCompile Include="detect.cpp" />
61 <ClCompile Include="embedded.cpp" />
62 <ClCompile Include="EngineForApplication.cpp" />
63 <ClCompile Include="EngineForExtension.cpp" />
64 <ClCompile Include="externalengine.cpp" />
65 <ClCompile Include="cabextract.cpp" />
66 <ClCompile Include="cache.cpp" />
67 <ClCompile Include="condition.cpp" />
68 <ClCompile Include="container.cpp" />
69 <ClCompile Include="core.cpp" />
70 <ClCompile Include="dependency.cpp" />
71 <ClCompile Include="elevation.cpp" />
72 <ClCompile Include="engine.cpp" />
73 <ClCompile Include="exeengine.cpp" />
74 <ClCompile Include="logging.cpp" />
75 <ClCompile Include="manifest.cpp" />
76 <ClCompile Include="msiengine.cpp" />
77 <ClCompile Include="mspengine.cpp" />
78 <ClCompile Include="msuengine.cpp" />
79 <ClCompile Include="NetFxChainer.cpp" />
80 <ClCompile Include="package.cpp" />
81 <ClCompile Include="payload.cpp" />
82 <ClCompile Include="pipe.cpp" />
83 <ClCompile Include="plan.cpp" />
84 <ClCompile Include="platform.cpp" />
85 <ClCompile Include="precomp.cpp">
86 <PrecompiledHeader>Create</PrecompiledHeader>
87 </ClCompile>
88 <ClCompile Include="pseudobundle.cpp" />
89 <ClCompile Include="registration.cpp" />
90 <ClCompile Include="relatedbundle.cpp" />
91 <ClCompile Include="search.cpp" />
92 <ClCompile Include="section.cpp" />
93 <ClCompile Include="splashscreen.cpp" />
94 <ClCompile Include="uithread.cpp" />
95 <ClCompile Include="update.cpp" />
96 <ClCompile Include="userexperience.cpp" />
97 <ClCompile Include="variable.cpp" />
98 <ClCompile Include="variant.cpp" />
99 </ItemGroup>
100 <ItemGroup>
101 <ClInclude Include="apply.h" />
102 <ClInclude Include="approvedexe.h" />
103 <ClInclude Include="..\WixToolset.BootstrapperCore.Native\inc\BootstrapperApplication.h" />
104 <ClInclude Include="..\WixToolset.BootstrapperCore.Native\inc\BootstrapperEngine.h" />
105 <ClInclude Include="..\WixToolset.BootstrapperCore.Native\inc\BundleExtension.h" />
106 <ClInclude Include="..\WixToolset.BootstrapperCore.Native\inc\BundleExtensionEngine.h" />
107 <ClInclude Include="burnextension.h" />
108 <ClInclude Include="cabextract.h" />
109 <ClInclude Include="cache.h" />
110 <ClInclude Include="condition.h" />
111 <ClInclude Include="container.h" />
112 <ClInclude Include="core.h" />
113 <ClInclude Include="dependency.h" />
114 <ClInclude Include="detect.h" />
115 <ClInclude Include="elevation.h" />
116 <ClInclude Include="embedded.h" />
117 <ClInclude Include="EngineForApplication.h" />
118 <ClInclude Include="EngineForExtension.h" />
119 <ClInclude Include="exeengine.h" />
120 <ClInclude Include="externalengine.h" />
121 <ClInclude Include="inc\burnsources.h" />
122 <ClInclude Include="inc\engine.h" />
123 <ClInclude Include="logging.h" />
124 <ClInclude Include="manifest.h" />
125 <ClInclude Include="msiengine.h" />
126 <ClInclude Include="mspengine.h" />
127 <ClInclude Include="msuengine.h" />
128 <ClInclude Include="netfxchainer.h" />
129 <ClInclude Include="package.h" />
130 <ClInclude Include="payload.h" />
131 <ClInclude Include="pipe.h" />
132 <ClInclude Include="plan.h" />
133 <ClInclude Include="platform.h" />
134 <ClInclude Include="precomp.h" />
135 <ClInclude Include="pseudobundle.h" />
136 <ClInclude Include="registration.h" />
137 <ClInclude Include="relatedbundle.h" />
138 <ClInclude Include="search.h" />
139 <ClInclude Include="section.h" />
140 <ClInclude Include="splashscreen.h" />
141 <ClInclude Include="uithread.h" />
142 <ClInclude Include="update.h" />
143 <ClInclude Include="userexperience.h" />
144 <ClInclude Include="variable.h" />
145 <ClInclude Include="variant.h" />
146 </ItemGroup>
147 <ItemGroup>
148 <None Include="packages.config" />
149 </ItemGroup>
150
151 <ItemGroup>
152 <CustomBuild Include="engine.mc">
153 <Message>Compiling message file...</Message>
154 <Command>mc.exe -h "$(IntDir)." -r "$(IntDir)." -A -c -z engine.messages "$(InputDir)engine.mc"
155rc.exe -fo "$(OutDir)engine.res" "$(IntDir)engine.messages.rc"</Command>
156 <Outputs>$(IntDir)engine.messages.h;$(IntDir)engine.messages.rc;$(OutDir)engine.res</Outputs>
157 </CustomBuild>
158 </ItemGroup>
159
160 <Target Name="SetWixVersion" DependsOnTargets="GetBuildVersion" BeforeTargets="ClCompile">
161 <PropertyGroup>
162 <rmj>$(MajorMinorVersion.Split(`.`)[0])</rmj>
163 <rmm>$(MajorMinorVersion.Split(`.`)[1])</rmm>
164 <rup>0</rup>
165 <rpr>$(BuildNumber)</rpr>
166 <szVerMajorMinorBuild>$(rmj).$(rmm).$(rup).$(rpr)</szVerMajorMinorBuild>
167 <wixver>rmj=$(rmj);rmm=$(rmm);rup=$(rup);rpr=$(rpr);szVerMajorMinorBuild="$(szVerMajorMinorBuild)"</wixver>
168 </PropertyGroup>
169
170 <ItemGroup>
171 <ClCompile>
172 <PreprocessorDefinitions>$(wixver);%(PreprocessorDefinitions)</PreprocessorDefinitions>
173 </ClCompile>
174 </ItemGroup>
175 </Target>
176 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
177 <Import Project="..\..\packages\Nerdbank.GitVersioning.3.3.37\build\Nerdbank.GitVersioning.targets" Condition="Exists('..\..\packages\Nerdbank.GitVersioning.3.3.37\build\Nerdbank.GitVersioning.targets')" />
178 <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
179 <PropertyGroup>
180 <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
181 </PropertyGroup>
182 <Error Condition="!Exists('..\..\packages\Nerdbank.GitVersioning.3.3.37\build\Nerdbank.GitVersioning.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Nerdbank.GitVersioning.3.3.37\build\Nerdbank.GitVersioning.targets'))" />
183 <Error Condition="!Exists('..\..\packages\WixToolset.DUtil.4.0.70\build\WixToolset.DUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.DUtil.4.0.70\build\WixToolset.DUtil.props'))" />
184 <Error Condition="!Exists('..\..\packages\WixToolset.BootstrapperCore.Native.4.0.57\build\WixToolset.BootstrapperCore.Native.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\WixToolset.BootstrapperCore.Native.4.0.57\build\WixToolset.BootstrapperCore.Native.props'))" />
185 </Target>
186</Project>
diff --git a/src/burn/engine/exeengine.cpp b/src/burn/engine/exeengine.cpp
new file mode 100644
index 00000000..c0ba93e0
--- /dev/null
+++ b/src/burn/engine/exeengine.cpp
@@ -0,0 +1,816 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// internal function declarations
7
8static HRESULT HandleExitCode(
9 __in BURN_PACKAGE* pPackage,
10 __in DWORD dwExitCode,
11 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
12 );
13static HRESULT ParseCommandLineArgumentsFromXml(
14 __in IXMLDOMNode* pixnExePackage,
15 __in BURN_PACKAGE* pPackage
16 );
17static HRESULT ParseExitCodesFromXml(
18 __in IXMLDOMNode* pixnExePackage,
19 __in BURN_PACKAGE* pPackage
20 );
21
22
23// function definitions
24
25extern "C" HRESULT ExeEngineParsePackageFromXml(
26 __in IXMLDOMNode* pixnExePackage,
27 __in BURN_PACKAGE* pPackage
28 )
29{
30 HRESULT hr = S_OK;
31 IXMLDOMNodeList* pixnNodes = NULL;
32 IXMLDOMNode* pixnNode = NULL;
33 LPWSTR scz = NULL;
34
35 // @DetectCondition
36 hr = XmlGetAttributeEx(pixnExePackage, L"DetectCondition", &pPackage->Exe.sczDetectCondition);
37 ExitOnFailure(hr, "Failed to get @DetectCondition.");
38
39 // @InstallArguments
40 hr = XmlGetAttributeEx(pixnExePackage, L"InstallArguments", &pPackage->Exe.sczInstallArguments);
41 ExitOnFailure(hr, "Failed to get @InstallArguments.");
42
43 // @UninstallArguments
44 hr = XmlGetAttributeEx(pixnExePackage, L"UninstallArguments", &pPackage->Exe.sczUninstallArguments);
45 ExitOnFailure(hr, "Failed to get @UninstallArguments.");
46
47 // @RepairArguments
48 hr = XmlGetAttributeEx(pixnExePackage, L"RepairArguments", &pPackage->Exe.sczRepairArguments);
49 ExitOnFailure(hr, "Failed to get @RepairArguments.");
50
51 // @Repairable
52 hr = XmlGetYesNoAttribute(pixnExePackage, L"Repairable", &pPackage->Exe.fRepairable);
53 if (E_NOTFOUND != hr)
54 {
55 ExitOnFailure(hr, "Failed to get @Repairable.");
56 }
57
58 // @Protocol
59 hr = XmlGetAttributeEx(pixnExePackage, L"Protocol", &scz);
60 if (SUCCEEDED(hr))
61 {
62 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"burn", -1))
63 {
64 pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_BURN;
65 }
66 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"netfx4", -1))
67 {
68 pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_NETFX4;
69 }
70 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"none", -1))
71 {
72 pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_NONE;
73 }
74 else
75 {
76 hr = E_UNEXPECTED;
77 ExitOnFailure(hr, "Invalid protocol type: %ls", scz);
78 }
79 }
80 else if (E_NOTFOUND != hr)
81 {
82 ExitOnFailure(hr, "Failed to get @Protocol.");
83 }
84
85 hr = ParseExitCodesFromXml(pixnExePackage, pPackage);
86 ExitOnFailure(hr, "Failed to parse exit codes.");
87
88 hr = ParseCommandLineArgumentsFromXml(pixnExePackage, pPackage);
89 ExitOnFailure(hr, "Failed to parse command lines.");
90
91LExit:
92 ReleaseObject(pixnNodes);
93 ReleaseObject(pixnNode);
94 ReleaseStr(scz);
95
96 return hr;
97}
98
99extern "C" void ExeEnginePackageUninitialize(
100 __in BURN_PACKAGE* pPackage
101 )
102{
103 ReleaseStr(pPackage->Exe.sczDetectCondition);
104 ReleaseStr(pPackage->Exe.sczInstallArguments);
105 ReleaseStr(pPackage->Exe.sczRepairArguments);
106 ReleaseStr(pPackage->Exe.sczUninstallArguments);
107 ReleaseStr(pPackage->Exe.sczIgnoreDependencies);
108 //ReleaseStr(pPackage->Exe.sczProgressSwitch);
109 ReleaseMem(pPackage->Exe.rgExitCodes);
110
111 // free command-line arguments
112 if (pPackage->Exe.rgCommandLineArguments)
113 {
114 for (DWORD i = 0; i < pPackage->Exe.cCommandLineArguments; ++i)
115 {
116 BURN_EXE_COMMAND_LINE_ARGUMENT* pCommandLineArgument = &pPackage->Exe.rgCommandLineArguments[i];
117 ReleaseStr(pCommandLineArgument->sczInstallArgument);
118 ReleaseStr(pCommandLineArgument->sczUninstallArgument);
119 ReleaseStr(pCommandLineArgument->sczRepairArgument);
120 ReleaseStr(pCommandLineArgument->sczCondition);
121 }
122 MemFree(pPackage->Exe.rgCommandLineArguments);
123 }
124
125 // clear struct
126 memset(&pPackage->Exe, 0, sizeof(pPackage->Exe));
127}
128
129extern "C" HRESULT ExeEngineDetectPackage(
130 __in BURN_PACKAGE* pPackage,
131 __in BURN_VARIABLES* pVariables
132 )
133{
134 HRESULT hr = S_OK;
135 BOOL fDetected = FALSE;
136
137 // evaluate detect condition
138 if (pPackage->Exe.sczDetectCondition && *pPackage->Exe.sczDetectCondition)
139 {
140 hr = ConditionEvaluate(pVariables, pPackage->Exe.sczDetectCondition, &fDetected);
141 ExitOnFailure(hr, "Failed to evaluate executable package detect condition.");
142 }
143
144 // update detect state
145 pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
146
147 if (pPackage->fCanAffectRegistration)
148 {
149 pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
150 }
151
152LExit:
153 return hr;
154}
155
156//
157// PlanCalculate - calculates the execute and rollback state for the requested package state.
158//
159extern "C" HRESULT ExeEnginePlanCalculatePackage(
160 __in BURN_PACKAGE* pPackage
161 )
162{
163 HRESULT hr = S_OK;
164 BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE;
165 BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
166
167 // execute action
168 switch (pPackage->currentState)
169 {
170 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
171 switch (pPackage->requested)
172 {
173 case BOOTSTRAPPER_REQUEST_STATE_PRESENT:
174 execute = pPackage->Exe.fPseudoBundle ? BOOTSTRAPPER_ACTION_STATE_INSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
175 break;
176 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
177 execute = pPackage->Exe.fRepairable ? BOOTSTRAPPER_ACTION_STATE_REPAIR : BOOTSTRAPPER_ACTION_STATE_NONE;
178 break;
179 case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough;
180 case BOOTSTRAPPER_REQUEST_STATE_CACHE:
181 execute = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
182 break;
183 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
184 execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
185 break;
186 default:
187 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
188 break;
189 }
190 break;
191
192 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
193 switch (pPackage->requested)
194 {
195 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
196 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
197 execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
198 break;
199 default:
200 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
201 break;
202 }
203 break;
204
205 default:
206 hr = E_INVALIDARG;
207 ExitOnRootFailure(hr, "Invalid package current state: %d.", pPackage->currentState);
208 }
209
210 // Calculate the rollback action if there is an execute action.
211 if (BOOTSTRAPPER_ACTION_STATE_NONE != execute)
212 {
213 switch (pPackage->currentState)
214 {
215 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
216 switch (pPackage->requested)
217 {
218 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
219 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
220 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
221 break;
222 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
223 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
224 rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL;
225 break;
226 default:
227 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
228 break;
229 }
230 break;
231
232 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
233 switch (pPackage->requested)
234 {
235 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
236 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
237 rollback = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
238 break;
239 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
240 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
241 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
242 break;
243 default:
244 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
245 break;
246 }
247 break;
248
249 default:
250 hr = E_INVALIDARG;
251 ExitOnRootFailure(hr, "Invalid package expected state.");
252 }
253 }
254
255 // return values
256 pPackage->execute = execute;
257 pPackage->rollback = rollback;
258
259LExit:
260 return hr;
261}
262
263//
264// PlanAdd - adds the calculated execute and rollback actions for the package.
265//
266extern "C" HRESULT ExeEnginePlanAddPackage(
267 __in_opt DWORD *pdwInsertSequence,
268 __in BURN_PACKAGE* pPackage,
269 __in BURN_PLAN* pPlan,
270 __in BURN_LOGGING* pLog,
271 __in BURN_VARIABLES* pVariables,
272 __in_opt HANDLE hCacheEvent
273 )
274{
275 HRESULT hr = S_OK;
276 BURN_EXECUTE_ACTION* pAction = NULL;
277
278 // add wait for cache
279 if (hCacheEvent)
280 {
281 hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent);
282 ExitOnFailure(hr, "Failed to plan package cache syncpoint");
283 }
284
285 hr = DependencyPlanPackage(pdwInsertSequence, pPackage, pPlan);
286 ExitOnFailure(hr, "Failed to plan package dependency actions.");
287
288 // add execute action
289 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
290 {
291 if (NULL != pdwInsertSequence)
292 {
293 hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction);
294 ExitOnFailure(hr, "Failed to insert execute action.");
295 }
296 else
297 {
298 hr = PlanAppendExecuteAction(pPlan, &pAction);
299 ExitOnFailure(hr, "Failed to append execute action.");
300 }
301
302 pAction->type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE;
303 pAction->exePackage.pPackage = pPackage;
304 pAction->exePackage.fFireAndForget = (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == pPlan->action);
305 pAction->exePackage.action = pPackage->execute;
306
307 if (pPackage->Exe.sczIgnoreDependencies)
308 {
309 hr = StrAllocString(&pAction->exePackage.sczIgnoreDependencies, pPackage->Exe.sczIgnoreDependencies, 0);
310 ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
311 }
312
313 if (pPackage->Exe.wzAncestors)
314 {
315 hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.wzAncestors, 0);
316 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
317 }
318
319 LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, NULL); // ignore errors.
320 }
321
322 // add rollback action
323 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
324 {
325 hr = PlanAppendRollbackAction(pPlan, &pAction);
326 ExitOnFailure(hr, "Failed to append rollback action.");
327
328 pAction->type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE;
329 pAction->exePackage.pPackage = pPackage;
330 pAction->exePackage.action = pPackage->rollback;
331
332 if (pPackage->Exe.sczIgnoreDependencies)
333 {
334 hr = StrAllocString(&pAction->exePackage.sczIgnoreDependencies, pPackage->Exe.sczIgnoreDependencies, 0);
335 ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
336 }
337
338 if (pPackage->Exe.wzAncestors)
339 {
340 hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.wzAncestors, 0);
341 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
342 }
343
344 LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, NULL); // ignore errors.
345 }
346
347LExit:
348 return hr;
349}
350
351extern "C" HRESULT ExeEngineExecutePackage(
352 __in BURN_EXECUTE_ACTION* pExecuteAction,
353 __in BURN_VARIABLES* pVariables,
354 __in BOOL fRollback,
355 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
356 __in LPVOID pvContext,
357 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
358 )
359{
360 HRESULT hr = S_OK;
361 int nResult = IDNOACTION;
362 LPCWSTR wzArguments = NULL;
363 LPWSTR sczArguments = NULL;
364 LPWSTR sczArgumentsFormatted = NULL;
365 LPWSTR sczArgumentsObfuscated = NULL;
366 LPWSTR sczCachedDirectory = NULL;
367 LPWSTR sczExecutablePath = NULL;
368 LPWSTR sczCommand = NULL;
369 LPWSTR sczCommandObfuscated = NULL;
370 HANDLE hExecutableFile = INVALID_HANDLE_VALUE;
371 STARTUPINFOW si = { };
372 PROCESS_INFORMATION pi = { };
373 DWORD dwExitCode = 0;
374 GENERIC_EXECUTE_MESSAGE message = { };
375 BURN_PACKAGE* pPackage = pExecuteAction->exePackage.pPackage;
376 BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload;
377
378 // get cached executable path
379 hr = CacheGetCompletedPath(pPackage->fPerMachine, pPackage->sczCacheId, &sczCachedDirectory);
380 ExitOnFailure(hr, "Failed to get cached path for package: %ls", pPackage->sczId);
381
382 // Best effort to set the execute package cache folder and action variables.
383 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE);
384 VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->exePackage.action, TRUE);
385
386 hr = PathConcat(sczCachedDirectory, pPackagePayload->sczFilePath, &sczExecutablePath);
387 ExitOnFailure(hr, "Failed to build executable path.");
388
389 // pick arguments
390 switch (pExecuteAction->exePackage.action)
391 {
392 case BOOTSTRAPPER_ACTION_STATE_INSTALL:
393 wzArguments = pPackage->Exe.sczInstallArguments;
394 break;
395
396 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
397 wzArguments = pPackage->Exe.sczUninstallArguments;
398 break;
399
400 case BOOTSTRAPPER_ACTION_STATE_REPAIR:
401 wzArguments = pPackage->Exe.sczRepairArguments;
402 break;
403
404 default:
405 hr = E_INVALIDARG;
406 ExitOnFailure(hr, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action);
407 }
408
409 // now add optional arguments
410 hr = StrAllocString(&sczArguments, wzArguments && *wzArguments ? wzArguments : L"", 0);
411 ExitOnFailure(hr, "Failed to copy package arguments.");
412
413 for (DWORD i = 0; i < pPackage->Exe.cCommandLineArguments; ++i)
414 {
415 BURN_EXE_COMMAND_LINE_ARGUMENT* commandLineArgument = &pPackage->Exe.rgCommandLineArguments[i];
416 BOOL fCondition = FALSE;
417
418 hr = ConditionEvaluate(pVariables, commandLineArgument->sczCondition, &fCondition);
419 ExitOnFailure(hr, "Failed to evaluate executable package command-line condition.");
420
421 if (fCondition)
422 {
423 hr = StrAllocConcat(&sczArguments, L" ", 0);
424 ExitOnFailure(hr, "Failed to separate command-line arguments.");
425
426 switch (pExecuteAction->exePackage.action)
427 {
428 case BOOTSTRAPPER_ACTION_STATE_INSTALL:
429 hr = StrAllocConcat(&sczArguments, commandLineArgument->sczInstallArgument, 0);
430 ExitOnFailure(hr, "Failed to get command-line argument for install.");
431 break;
432
433 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
434 hr = StrAllocConcat(&sczArguments, commandLineArgument->sczUninstallArgument, 0);
435 ExitOnFailure(hr, "Failed to get command-line argument for uninstall.");
436 break;
437
438 case BOOTSTRAPPER_ACTION_STATE_REPAIR:
439 hr = StrAllocConcat(&sczArguments, commandLineArgument->sczRepairArgument, 0);
440 ExitOnFailure(hr, "Failed to get command-line argument for repair.");
441 break;
442
443 default:
444 hr = E_INVALIDARG;
445 ExitOnFailure(hr, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action);
446 }
447 }
448 }
449
450 // build command
451 if (*sczArguments)
452 {
453 hr = VariableFormatString(pVariables, sczArguments, &sczArgumentsFormatted, NULL);
454 ExitOnFailure(hr, "Failed to format argument string.");
455
456 hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", sczExecutablePath, sczArgumentsFormatted);
457 ExitOnFailure(hr, "Failed to create executable command.");
458
459 hr = VariableFormatStringObfuscated(pVariables, sczArguments, &sczArgumentsObfuscated, NULL);
460 ExitOnFailure(hr, "Failed to format obfuscated argument string.");
461
462 hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", sczExecutablePath, sczArgumentsObfuscated);
463 }
464 else
465 {
466 hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", sczExecutablePath);
467 ExitOnFailure(hr, "Failed to create executable command.");
468
469 hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", sczExecutablePath);
470 }
471 ExitOnFailure(hr, "Failed to create obfuscated executable command.");
472
473 if (pPackage->Exe.fSupportsAncestors)
474 {
475 // Add the list of dependencies to ignore, if any, to the burn command line.
476 if (pExecuteAction->exePackage.sczIgnoreDependencies && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol)
477 {
478 hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies);
479 ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line.");
480
481 hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies);
482 ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the obfuscated command line.");
483 }
484
485 // Add the list of ancestors, if any, to the burn command line.
486 if (pExecuteAction->exePackage.sczAncestors)
487 {
488 hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors);
489 ExitOnFailure(hr, "Failed to append the list of ancestors to the command line.");
490
491 hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors);
492 ExitOnFailure(hr, "Failed to append the list of ancestors to the obfuscated command line.");
493 }
494 }
495
496 if (BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol)
497 {
498 hr = CoreAppendFileHandleSelfToCommandLine(sczExecutablePath, &hExecutableFile, &sczCommand, &sczCommandObfuscated);
499 ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF);
500 }
501
502 // Log before we add the secret pipe name and client token for embedded processes.
503 LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, LoggingActionStateToString(pExecuteAction->exePackage.action), sczExecutablePath, sczCommandObfuscated);
504
505 if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol)
506 {
507 hr = EmbeddedRunBundle(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode);
508 ExitOnFailure(hr, "Failed to run bundle as embedded from path: %ls", sczExecutablePath);
509 }
510 else if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_NETFX4 == pPackage->Exe.protocol)
511 {
512 hr = NetFxRunChainer(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode);
513 ExitOnFailure(hr, "Failed to run netfx chainer: %ls", sczExecutablePath);
514 }
515 else // create and wait for the executable process while sending fake progress to allow cancel.
516 {
517 // Make the cache location of the executable the current directory to help those executables
518 // that expect stuff to be relative to them.
519 si.cb = sizeof(si);
520 if (!::CreateProcessW(sczExecutablePath, sczCommand, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, sczCachedDirectory, &si, &pi))
521 {
522 ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", sczExecutablePath);
523 }
524
525 if (pExecuteAction->exePackage.fFireAndForget)
526 {
527 ::WaitForInputIdle(pi.hProcess, 5000);
528 ExitFunction();
529 }
530
531 do
532 {
533 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
534 message.dwAllowedResults = MB_OKCANCEL;
535 message.progress.dwPercentage = 50;
536 nResult = pfnGenericMessageHandler(&message, pvContext);
537 hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
538 ExitOnRootFailure(hr, "Bootstrapper application aborted during EXE progress.");
539
540 hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode);
541 if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr)
542 {
543 ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", sczExecutablePath);
544 }
545 } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr);
546 }
547
548 hr = HandleExitCode(pPackage, dwExitCode, pRestart);
549 ExitOnRootFailure(hr, "Process returned error: 0x%x", dwExitCode);
550
551LExit:
552 StrSecureZeroFreeString(sczArguments);
553 StrSecureZeroFreeString(sczArgumentsFormatted);
554 ReleaseStr(sczArgumentsObfuscated);
555 ReleaseStr(sczCachedDirectory);
556 ReleaseStr(sczExecutablePath);
557 StrSecureZeroFreeString(sczCommand);
558 ReleaseStr(sczCommandObfuscated);
559
560 ReleaseHandle(pi.hThread);
561 ReleaseHandle(pi.hProcess);
562 ReleaseFileHandle(hExecutableFile);
563
564 // Best effort to clear the execute package cache folder and action variables.
565 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE);
566 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE, FALSE);
567
568 return hr;
569}
570
571extern "C" void ExeEngineUpdateInstallRegistrationState(
572 __in BURN_EXECUTE_ACTION* pAction,
573 __in HRESULT hrExecute
574 )
575{
576 BURN_PACKAGE* pPackage = pAction->exePackage.pPackage;
577
578 if (FAILED(hrExecute) || !pPackage->fCanAffectRegistration)
579 {
580 ExitFunction();
581 }
582
583 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->exePackage.action)
584 {
585 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
586 }
587 else
588 {
589 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
590 }
591
592LExit:
593 return;
594}
595
596
597// internal helper functions
598
599static HRESULT ParseExitCodesFromXml(
600 __in IXMLDOMNode* pixnExePackage,
601 __in BURN_PACKAGE* pPackage
602 )
603{
604 HRESULT hr = S_OK;
605 IXMLDOMNodeList* pixnNodes = NULL;
606 IXMLDOMNode* pixnNode = NULL;
607 DWORD cNodes = 0;
608 LPWSTR scz = NULL;
609
610 // select exit code nodes
611 hr = XmlSelectNodes(pixnExePackage, L"ExitCode", &pixnNodes);
612 ExitOnFailure(hr, "Failed to select exit code nodes.");
613
614 // get exit code node count
615 hr = pixnNodes->get_length((long*) &cNodes);
616 ExitOnFailure(hr, "Failed to get exit code node count.");
617
618 if (cNodes)
619 {
620 // allocate memory for exit codes
621 pPackage->Exe.rgExitCodes = (BURN_EXE_EXIT_CODE*) MemAlloc(sizeof(BURN_EXE_EXIT_CODE) * cNodes, TRUE);
622 ExitOnNull(pPackage->Exe.rgExitCodes, hr, E_OUTOFMEMORY, "Failed to allocate memory for exit code structs.");
623
624 pPackage->Exe.cExitCodes = cNodes;
625
626 // parse package elements
627 for (DWORD i = 0; i < cNodes; ++i)
628 {
629 BURN_EXE_EXIT_CODE* pExitCode = &pPackage->Exe.rgExitCodes[i];
630
631 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
632 ExitOnFailure(hr, "Failed to get next node.");
633
634 // @Type
635 hr = XmlGetAttributeNumber(pixnNode, L"Type", (DWORD*)&pExitCode->type);
636 ExitOnFailure(hr, "Failed to get @Type.");
637
638 // @Code
639 hr = XmlGetAttributeEx(pixnNode, L"Code", &scz);
640 ExitOnFailure(hr, "Failed to get @Code.");
641
642 if (L'*' == scz[0])
643 {
644 pExitCode->fWildcard = TRUE;
645 }
646 else
647 {
648 hr = StrStringToUInt32(scz, 0, (UINT*) &pExitCode->dwCode);
649 ExitOnFailure(hr, "Failed to parse @Code value: %ls", scz);
650 }
651
652 // prepare next iteration
653 ReleaseNullObject(pixnNode);
654 }
655 }
656
657 hr = S_OK;
658
659LExit:
660 ReleaseObject(pixnNodes);
661 ReleaseObject(pixnNode);
662 ReleaseStr(scz);
663
664 return hr;
665}
666
667static HRESULT ParseCommandLineArgumentsFromXml(
668 __in IXMLDOMNode* pixnExePackage,
669 __in BURN_PACKAGE* pPackage
670 )
671{
672 HRESULT hr = S_OK;
673 IXMLDOMNodeList* pixnNodes = NULL;
674 IXMLDOMNode* pixnNode = NULL;
675 DWORD cNodes = 0;
676 LPWSTR scz = NULL;
677
678 // Select command-line argument nodes.
679 hr = XmlSelectNodes(pixnExePackage, L"CommandLine", &pixnNodes);
680 ExitOnFailure(hr, "Failed to select command-line argument nodes.");
681
682 // Get command-line argument node count.
683 hr = pixnNodes->get_length((long*) &cNodes);
684 ExitOnFailure(hr, "Failed to get command-line argument count.");
685
686 if (cNodes)
687 {
688 pPackage->Exe.rgCommandLineArguments = (BURN_EXE_COMMAND_LINE_ARGUMENT*) MemAlloc(sizeof(BURN_EXE_COMMAND_LINE_ARGUMENT) * cNodes, TRUE);
689 ExitOnNull(pPackage->Exe.rgCommandLineArguments, hr, E_OUTOFMEMORY, "Failed to allocate memory for command-line argument structs.");
690
691 pPackage->Exe.cCommandLineArguments = cNodes;
692
693 // Parse command-line argument elements.
694 for (DWORD i = 0; i < cNodes; ++i)
695 {
696 BURN_EXE_COMMAND_LINE_ARGUMENT* pCommandLineArgument = &pPackage->Exe.rgCommandLineArguments[i];
697
698 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
699 ExitOnFailure(hr, "Failed to get next command-line argument node.");
700
701 // @InstallArgument
702 hr = XmlGetAttributeEx(pixnNode, L"InstallArgument", &pCommandLineArgument->sczInstallArgument);
703 ExitOnFailure(hr, "Failed to get @InstallArgument.");
704
705 // @UninstallArgument
706 hr = XmlGetAttributeEx(pixnNode, L"UninstallArgument", &pCommandLineArgument->sczUninstallArgument);
707 ExitOnFailure(hr, "Failed to get @UninstallArgument.");
708
709 // @RepairArgument
710 hr = XmlGetAttributeEx(pixnNode, L"RepairArgument", &pCommandLineArgument->sczRepairArgument);
711 ExitOnFailure(hr, "Failed to get @RepairArgument.");
712
713 // @Condition
714 hr = XmlGetAttributeEx(pixnNode, L"Condition", &pCommandLineArgument->sczCondition);
715 ExitOnFailure(hr, "Failed to get @Condition.");
716
717 // Prepare next iteration.
718 ReleaseNullObject(pixnNode);
719 }
720 }
721
722 hr = S_OK;
723
724LExit:
725 ReleaseObject(pixnNodes);
726 ReleaseObject(pixnNode);
727 ReleaseStr(scz);
728
729 return hr;
730}
731
732static HRESULT HandleExitCode(
733 __in BURN_PACKAGE* pPackage,
734 __in DWORD dwExitCode,
735 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
736 )
737{
738 HRESULT hr = S_OK;
739 BURN_EXE_EXIT_CODE_TYPE typeCode = BURN_EXE_EXIT_CODE_TYPE_NONE;
740
741 for (DWORD i = 0; i < pPackage->Exe.cExitCodes; ++i)
742 {
743 BURN_EXE_EXIT_CODE* pExitCode = &pPackage->Exe.rgExitCodes[i];
744
745 // If this is a wildcard, use the last one we come across.
746 if (pExitCode->fWildcard)
747 {
748 typeCode = pExitCode->type;
749 }
750 else if (dwExitCode == pExitCode->dwCode) // If we have an exact match on the error code use that and stop looking.
751 {
752 typeCode = pExitCode->type;
753 break;
754 }
755 }
756
757 // If we didn't find a matching code then treat 0 as success, the standard restarts codes as restarts
758 // and everything else as an error.
759 if (BURN_EXE_EXIT_CODE_TYPE_NONE == typeCode)
760 {
761 if (0 == dwExitCode)
762 {
763 typeCode = BURN_EXE_EXIT_CODE_TYPE_SUCCESS;
764 }
765 else if (ERROR_SUCCESS_REBOOT_REQUIRED == dwExitCode ||
766 HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast<HRESULT>(dwExitCode) ||
767 ERROR_SUCCESS_RESTART_REQUIRED == dwExitCode ||
768 HRESULT_FROM_WIN32(ERROR_SUCCESS_RESTART_REQUIRED) == static_cast<HRESULT>(dwExitCode))
769 {
770 typeCode = BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT;
771 }
772 else if (ERROR_SUCCESS_REBOOT_INITIATED == dwExitCode ||
773 HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED) == static_cast<HRESULT>(dwExitCode))
774 {
775 typeCode = BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT;
776 }
777 else
778 {
779 typeCode = BURN_EXE_EXIT_CODE_TYPE_ERROR;
780 }
781 }
782
783 switch (typeCode)
784 {
785 case BURN_EXE_EXIT_CODE_TYPE_SUCCESS:
786 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
787 hr = S_OK;
788 break;
789
790 case BURN_EXE_EXIT_CODE_TYPE_ERROR:
791 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
792 hr = HRESULT_FROM_WIN32(dwExitCode);
793 if (SUCCEEDED(hr))
794 {
795 hr = E_FAIL;
796 }
797 break;
798
799 case BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT:
800 *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
801 hr = S_OK;
802 break;
803
804 case BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT:
805 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
806 hr = S_OK;
807 break;
808
809 default:
810 hr = E_UNEXPECTED;
811 break;
812 }
813
814//LExit:
815 return hr;
816}
diff --git a/src/burn/engine/exeengine.h b/src/burn/engine/exeengine.h
new file mode 100644
index 00000000..e032ea01
--- /dev/null
+++ b/src/burn/engine/exeengine.h
@@ -0,0 +1,50 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// function declarations
11
12HRESULT ExeEngineParsePackageFromXml(
13 __in IXMLDOMNode* pixnExePackage,
14 __in BURN_PACKAGE* pPackage
15 );
16void ExeEnginePackageUninitialize(
17 __in BURN_PACKAGE* pPackage
18 );
19HRESULT ExeEngineDetectPackage(
20 __in BURN_PACKAGE* pPackage,
21 __in BURN_VARIABLES* pVariables
22 );
23HRESULT ExeEnginePlanCalculatePackage(
24 __in BURN_PACKAGE* pPackage
25 );
26HRESULT ExeEnginePlanAddPackage(
27 __in_opt DWORD *pdwInsertSequence,
28 __in BURN_PACKAGE* pPackage,
29 __in BURN_PLAN* pPlan,
30 __in BURN_LOGGING* pLog,
31 __in BURN_VARIABLES* pVariables,
32 __in_opt HANDLE hCacheEvent
33 );
34HRESULT ExeEngineExecutePackage(
35 __in BURN_EXECUTE_ACTION* pExecuteAction,
36 __in BURN_VARIABLES* pVariables,
37 __in BOOL fRollback,
38 __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress,
39 __in LPVOID pvContext,
40 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
41 );
42void ExeEngineUpdateInstallRegistrationState(
43 __in BURN_EXECUTE_ACTION* pAction,
44 __in HRESULT hrExecute
45 );
46
47
48#if defined(__cplusplus)
49}
50#endif
diff --git a/src/burn/engine/externalengine.cpp b/src/burn/engine/externalengine.cpp
new file mode 100644
index 00000000..409353e4
--- /dev/null
+++ b/src/burn/engine/externalengine.cpp
@@ -0,0 +1,805 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6static HRESULT CopyStringToExternal(
7 __in_z LPWSTR wzValue,
8 __in_z_opt LPWSTR wzBuffer,
9 __inout SIZE_T* pcchBuffer
10 );
11
12// function definitions
13
14void ExternalEngineGetPackageCount(
15 __in BURN_ENGINE_STATE* pEngineState,
16 __out DWORD* pcPackages
17 )
18{
19 *pcPackages = pEngineState->packages.cPackages;
20}
21
22HRESULT ExternalEngineGetVariableNumeric(
23 __in BURN_ENGINE_STATE* pEngineState,
24 __in_z LPCWSTR wzVariable,
25 __out LONGLONG* pllValue
26 )
27{
28 HRESULT hr = S_OK;
29
30 if (wzVariable && *wzVariable)
31 {
32 hr = VariableGetNumeric(&pEngineState->variables, wzVariable, pllValue);
33 }
34 else
35 {
36 *pllValue = 0;
37 hr = E_INVALIDARG;
38 }
39
40 return hr;
41}
42
43HRESULT ExternalEngineGetVariableString(
44 __in BURN_ENGINE_STATE* pEngineState,
45 __in_z LPCWSTR wzVariable,
46 __out_ecount_opt(*pcchValue) LPWSTR wzValue,
47 __inout SIZE_T* pcchValue
48 )
49{
50 HRESULT hr = S_OK;
51 LPWSTR sczValue = NULL;
52
53 if (wzVariable && *wzVariable)
54 {
55 hr = VariableGetString(&pEngineState->variables, wzVariable, &sczValue);
56 if (SUCCEEDED(hr))
57 {
58 hr = CopyStringToExternal(sczValue, wzValue, pcchValue);
59 }
60 }
61 else
62 {
63 hr = E_INVALIDARG;
64 }
65
66 StrSecureZeroFreeString(sczValue);
67
68 return hr;
69}
70
71HRESULT ExternalEngineGetVariableVersion(
72 __in BURN_ENGINE_STATE* pEngineState,
73 __in_z LPCWSTR wzVariable,
74 __out_ecount_opt(*pcchValue) LPWSTR wzValue,
75 __inout SIZE_T* pcchValue
76 )
77{
78 HRESULT hr = S_OK;
79 VERUTIL_VERSION* pVersion = NULL;
80
81 if (wzVariable && *wzVariable)
82 {
83 hr = VariableGetVersion(&pEngineState->variables, wzVariable, &pVersion);
84 if (SUCCEEDED(hr))
85 {
86 hr = CopyStringToExternal(pVersion->sczVersion, wzValue, pcchValue);
87 }
88 }
89 else
90 {
91 hr = E_INVALIDARG;
92 }
93
94 ReleaseVerutilVersion(pVersion);
95
96 return hr;
97}
98
99HRESULT ExternalEngineFormatString(
100 __in BURN_ENGINE_STATE* pEngineState,
101 __in_z LPCWSTR wzIn,
102 __out_ecount_opt(*pcchOut) LPWSTR wzOut,
103 __inout SIZE_T* pcchOut
104 )
105{
106 HRESULT hr = S_OK;
107 LPWSTR sczValue = NULL;
108
109 if (wzIn && *wzIn)
110 {
111 hr = VariableFormatString(&pEngineState->variables, wzIn, &sczValue, NULL);
112 if (SUCCEEDED(hr))
113 {
114 hr = CopyStringToExternal(sczValue, wzOut, pcchOut);
115 }
116 }
117 else
118 {
119 hr = E_INVALIDARG;
120 }
121
122 StrSecureZeroFreeString(sczValue);
123
124 return hr;
125}
126
127HRESULT ExternalEngineEscapeString(
128 __in_z LPCWSTR wzIn,
129 __out_ecount_opt(*pcchOut) LPWSTR wzOut,
130 __inout SIZE_T* pcchOut
131 )
132{
133 HRESULT hr = S_OK;
134 LPWSTR sczValue = NULL;
135
136 if (wzIn && *wzIn)
137 {
138 hr = VariableEscapeString(wzIn, &sczValue);
139 if (SUCCEEDED(hr))
140 {
141 hr = CopyStringToExternal(sczValue, wzOut, pcchOut);
142 }
143 }
144 else
145 {
146 hr = E_INVALIDARG;
147 }
148
149 StrSecureZeroFreeString(sczValue);
150
151 return hr;
152}
153
154HRESULT ExternalEngineEvaluateCondition(
155 __in BURN_ENGINE_STATE* pEngineState,
156 __in_z LPCWSTR wzCondition,
157 __out BOOL* pf
158 )
159{
160 HRESULT hr = S_OK;
161
162 if (wzCondition && *wzCondition)
163 {
164 hr = ConditionEvaluate(&pEngineState->variables, wzCondition, pf);
165 }
166 else
167 {
168 *pf = FALSE;
169 hr = E_INVALIDARG;
170 }
171
172 return hr;
173}
174
175HRESULT ExternalEngineLog(
176 __in REPORT_LEVEL rl,
177 __in_z LPCWSTR wzMessage
178 )
179{
180 HRESULT hr = S_OK;
181
182 hr = LogStringLine(rl, "%ls", wzMessage);
183
184 return hr;
185}
186
187HRESULT ExternalEngineSendEmbeddedError(
188 __in BURN_ENGINE_STATE* pEngineState,
189 __in const DWORD dwErrorCode,
190 __in_z LPCWSTR wzMessage,
191 __in const DWORD dwUIHint,
192 __out int* pnResult
193 )
194{
195 HRESULT hr = S_OK;
196 BYTE* pbData = NULL;
197 SIZE_T cbData = 0;
198 DWORD dwResult = *pnResult = 0;
199
200 if (BURN_MODE_EMBEDDED != pEngineState->mode)
201 {
202 hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
203 ExitOnRootFailure(hr, "BA requested to send embedded message when not in embedded mode.");
204 }
205
206 hr = BuffWriteNumber(&pbData, &cbData, dwErrorCode);
207 ExitOnFailure(hr, "Failed to write error code to message buffer.");
208
209 hr = BuffWriteString(&pbData, &cbData, wzMessage ? wzMessage : L"");
210 ExitOnFailure(hr, "Failed to write message string to message buffer.");
211
212 hr = BuffWriteNumber(&pbData, &cbData, dwUIHint);
213 ExitOnFailure(hr, "Failed to write UI hint to message buffer.");
214
215 hr = PipeSendMessage(pEngineState->embeddedConnection.hPipe, BURN_EMBEDDED_MESSAGE_TYPE_ERROR, pbData, cbData, NULL, NULL, &dwResult);
216 ExitOnFailure(hr, "Failed to send embedded message over pipe.");
217
218 *pnResult = static_cast<int>(dwResult);
219
220LExit:
221 ReleaseBuffer(pbData);
222
223 return hr;
224}
225
226HRESULT ExternalEngineSendEmbeddedProgress(
227 __in BURN_ENGINE_STATE* pEngineState,
228 __in const DWORD dwProgressPercentage,
229 __in const DWORD dwOverallProgressPercentage,
230 __out int* pnResult
231 )
232{
233 HRESULT hr = S_OK;
234 BYTE* pbData = NULL;
235 SIZE_T cbData = 0;
236 DWORD dwResult = *pnResult = 0;
237
238 if (BURN_MODE_EMBEDDED != pEngineState->mode)
239 {
240 hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
241 ExitOnRootFailure(hr, "BA requested to send embedded progress message when not in embedded mode.");
242 }
243
244 hr = BuffWriteNumber(&pbData, &cbData, dwProgressPercentage);
245 ExitOnFailure(hr, "Failed to write progress percentage to message buffer.");
246
247 hr = BuffWriteNumber(&pbData, &cbData, dwOverallProgressPercentage);
248 ExitOnFailure(hr, "Failed to write overall progress percentage to message buffer.");
249
250 hr = PipeSendMessage(pEngineState->embeddedConnection.hPipe, BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS, pbData, cbData, NULL, NULL, &dwResult);
251 ExitOnFailure(hr, "Failed to send embedded progress message over pipe.");
252
253 *pnResult = static_cast<int>(dwResult);
254
255LExit:
256 ReleaseBuffer(pbData);
257
258 return hr;
259}
260
261HRESULT ExternalEngineSetUpdate(
262 __in BURN_ENGINE_STATE* pEngineState,
263 __in_z_opt LPCWSTR wzLocalSource,
264 __in_z_opt LPCWSTR wzDownloadSource,
265 __in const DWORD64 qwSize,
266 __in const BOOTSTRAPPER_UPDATE_HASH_TYPE hashType,
267 __in_opt const BYTE* rgbHash,
268 __in const DWORD cbHash
269 )
270{
271 HRESULT hr = S_OK;
272 LPWSTR sczFilePath = NULL;
273 LPWSTR sczCommandline = NULL;
274 UUID guid = { };
275 WCHAR wzGuid[39];
276 RPC_STATUS rs = RPC_S_OK;
277
278 ::EnterCriticalSection(&pEngineState->userExperience.csEngineActive);
279 hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience);
280 ExitOnFailure(hr, "Engine is active, cannot change engine state.");
281
282 if ((!wzLocalSource || !*wzLocalSource) && (!wzDownloadSource || !*wzDownloadSource))
283 {
284 UpdateUninitialize(&pEngineState->update);
285 }
286 else if (BOOTSTRAPPER_UPDATE_HASH_TYPE_NONE == hashType && (0 != cbHash || rgbHash))
287 {
288 hr = E_INVALIDARG;
289 }
290 else if (BOOTSTRAPPER_UPDATE_HASH_TYPE_SHA512 == hashType && (SHA512_HASH_LEN != cbHash || !rgbHash))
291 {
292 hr = E_INVALIDARG;
293 }
294 else
295 {
296 UpdateUninitialize(&pEngineState->update);
297
298 hr = CoreRecreateCommandLine(&sczCommandline, BOOTSTRAPPER_ACTION_INSTALL, pEngineState->command.display, pEngineState->command.restart, BOOTSTRAPPER_RELATION_NONE, FALSE, pEngineState->registration.sczActiveParent, pEngineState->registration.sczAncestors, NULL, pEngineState->command.wzCommandLine);
299 ExitOnFailure(hr, "Failed to recreate command-line for update bundle.");
300
301 // Bundles would fail to use the downloaded update bundle, as the running bundle would be one of the search paths.
302 // Here I am generating a random guid, but in the future it would be nice if the feed would provide the ID of the update.
303 rs = ::UuidCreate(&guid);
304 hr = HRESULT_FROM_RPC(rs);
305 ExitOnFailure(hr, "Failed to create bundle update guid.");
306
307 if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid)))
308 {
309 hr = E_OUTOFMEMORY;
310 ExitOnRootFailure(hr, "Failed to convert bundle update guid into string.");
311 }
312
313 hr = StrAllocFormatted(&sczFilePath, L"%ls\\%ls", wzGuid, pEngineState->registration.sczExecutableName);
314 ExitOnFailure(hr, "Failed to build bundle update file path.");
315
316 if (!wzLocalSource || !*wzLocalSource)
317 {
318 wzLocalSource = sczFilePath;
319 }
320
321 hr = PseudoBundleInitialize(FILEMAKEVERSION(rmj, rmm, rup, rpr), &pEngineState->update.package, FALSE, pEngineState->registration.sczId, BOOTSTRAPPER_RELATION_UPDATE, BOOTSTRAPPER_PACKAGE_STATE_ABSENT, FALSE, sczFilePath, wzLocalSource, wzDownloadSource, qwSize, TRUE, sczCommandline, NULL, NULL, NULL, rgbHash, cbHash);
322 ExitOnFailure(hr, "Failed to set update bundle.");
323
324 pEngineState->update.fUpdateAvailable = TRUE;
325 }
326
327LExit:
328 ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive);
329
330 ReleaseStr(sczCommandline);
331 ReleaseStr(sczFilePath);
332
333 return hr;
334}
335
336HRESULT ExternalEngineSetLocalSource(
337 __in BURN_ENGINE_STATE* pEngineState,
338 __in_z_opt LPCWSTR wzPackageOrContainerId,
339 __in_z_opt LPCWSTR wzPayloadId,
340 __in_z LPCWSTR wzPath
341 )
342{
343 HRESULT hr = S_OK;
344 BURN_CONTAINER* pContainer = NULL;
345 BURN_PAYLOAD* pPayload = NULL;
346
347 ::EnterCriticalSection(&pEngineState->userExperience.csEngineActive);
348 hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience);
349 ExitOnFailure(hr, "Engine is active, cannot change engine state.");
350
351 if (!wzPath || !*wzPath)
352 {
353 hr = E_INVALIDARG;
354 }
355 else if (wzPayloadId && *wzPayloadId)
356 {
357 hr = PayloadFindById(&pEngineState->payloads, wzPayloadId, &pPayload);
358 ExitOnFailure(hr, "BA requested unknown payload with id: %ls", wzPayloadId);
359
360 hr = StrAllocString(&pPayload->sczSourcePath, wzPath, 0);
361 ExitOnFailure(hr, "Failed to set source path for payload.");
362 }
363 else if (wzPackageOrContainerId && *wzPackageOrContainerId)
364 {
365 hr = ContainerFindById(&pEngineState->containers, wzPackageOrContainerId, &pContainer);
366 ExitOnFailure(hr, "BA requested unknown container with id: %ls", wzPackageOrContainerId);
367
368 hr = StrAllocString(&pContainer->sczSourcePath, wzPath, 0);
369 ExitOnFailure(hr, "Failed to set source path for container.");
370 }
371 else
372 {
373 hr = E_INVALIDARG;
374 }
375
376LExit:
377 ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive);
378
379 return hr;
380}
381
382HRESULT ExternalEngineSetDownloadSource(
383 __in BURN_ENGINE_STATE* pEngineState,
384 __in_z_opt LPCWSTR wzPackageOrContainerId,
385 __in_z_opt LPCWSTR wzPayloadId,
386 __in_z_opt LPCWSTR wzUrl,
387 __in_z_opt LPCWSTR wzUser,
388 __in_z_opt LPCWSTR wzPassword
389 )
390{
391 HRESULT hr = S_OK;
392 BURN_CONTAINER* pContainer = NULL;
393 BURN_PAYLOAD* pPayload = NULL;
394 DOWNLOAD_SOURCE* pDownloadSource = NULL;
395
396 ::EnterCriticalSection(&pEngineState->userExperience.csEngineActive);
397 hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience);
398 ExitOnFailure(hr, "Engine is active, cannot change engine state.");
399
400 if (wzPayloadId && *wzPayloadId)
401 {
402 hr = PayloadFindById(&pEngineState->payloads, wzPayloadId, &pPayload);
403 ExitOnFailure(hr, "BA requested unknown payload with id: %ls", wzPayloadId);
404
405 pDownloadSource = &pPayload->downloadSource;
406 }
407 else if (wzPackageOrContainerId && *wzPackageOrContainerId)
408 {
409 hr = ContainerFindById(&pEngineState->containers, wzPackageOrContainerId, &pContainer);
410 ExitOnFailure(hr, "BA requested unknown container with id: %ls", wzPackageOrContainerId);
411
412 pDownloadSource = &pContainer->downloadSource;
413 }
414 else
415 {
416 hr = E_INVALIDARG;
417 ExitOnFailure(hr, "BA did not provide container or payload id.");
418 }
419
420 if (wzUrl && *wzUrl)
421 {
422 hr = StrAllocString(&pDownloadSource->sczUrl, wzUrl, 0);
423 ExitOnFailure(hr, "Failed to set download URL.");
424
425 if (wzUser && *wzUser)
426 {
427 hr = StrAllocString(&pDownloadSource->sczUser, wzUser, 0);
428 ExitOnFailure(hr, "Failed to set download user.");
429
430 if (wzPassword && *wzPassword)
431 {
432 hr = StrAllocString(&pDownloadSource->sczPassword, wzPassword, 0);
433 ExitOnFailure(hr, "Failed to set download password.");
434 }
435 else // no password.
436 {
437 ReleaseNullStr(pDownloadSource->sczPassword);
438 }
439 }
440 else // no user means no password either.
441 {
442 ReleaseNullStr(pDownloadSource->sczUser);
443 ReleaseNullStr(pDownloadSource->sczPassword);
444 }
445 }
446 else // no URL provided means clear out the whole download source.
447 {
448 ReleaseNullStr(pDownloadSource->sczUrl);
449 ReleaseNullStr(pDownloadSource->sczUser);
450 ReleaseNullStr(pDownloadSource->sczPassword);
451 }
452
453LExit:
454 ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive);
455
456 return hr;
457}
458
459HRESULT ExternalEngineSetVariableNumeric(
460 __in BURN_ENGINE_STATE* pEngineState,
461 __in_z LPCWSTR wzVariable,
462 __in const LONGLONG llValue
463 )
464{
465 HRESULT hr = S_OK;
466
467 if (wzVariable && *wzVariable)
468 {
469 hr = VariableSetNumeric(&pEngineState->variables, wzVariable, llValue, FALSE);
470 ExitOnFailure(hr, "Failed to set numeric variable.");
471 }
472 else
473 {
474 hr = E_INVALIDARG;
475 ExitOnFailure(hr, "SetVariableNumeric did not provide variable name.");
476 }
477
478LExit:
479 return hr;
480}
481
482HRESULT ExternalEngineSetVariableString(
483 __in BURN_ENGINE_STATE* pEngineState,
484 __in_z LPCWSTR wzVariable,
485 __in_z_opt LPCWSTR wzValue,
486 __in const BOOL fFormatted
487 )
488{
489 HRESULT hr = S_OK;
490
491 if (wzVariable && *wzVariable)
492 {
493 hr = VariableSetString(&pEngineState->variables, wzVariable, wzValue, FALSE, fFormatted);
494 ExitOnFailure(hr, "Failed to set string variable.");
495 }
496 else
497 {
498 hr = E_INVALIDARG;
499 ExitOnFailure(hr, "SetVariableString did not provide variable name.");
500 }
501
502LExit:
503 return hr;
504}
505
506HRESULT ExternalEngineSetVariableVersion(
507 __in BURN_ENGINE_STATE* pEngineState,
508 __in_z LPCWSTR wzVariable,
509 __in_z_opt LPCWSTR wzValue
510 )
511{
512 HRESULT hr = S_OK;
513 VERUTIL_VERSION* pVersion = NULL;
514
515 if (wzVariable && *wzVariable)
516 {
517 if (wzValue)
518 {
519 hr = VerParseVersion(wzValue, 0, FALSE, &pVersion);
520 ExitOnFailure(hr, "Failed to parse new version value.");
521 }
522
523 hr = VariableSetVersion(&pEngineState->variables, wzVariable, pVersion, FALSE);
524 ExitOnFailure(hr, "Failed to set version variable.");
525 }
526 else
527 {
528 hr = E_INVALIDARG;
529 ExitOnFailure(hr, "SetVariableVersion did not provide variable name.");
530 }
531
532LExit:
533 ReleaseVerutilVersion(pVersion);
534
535 return hr;
536}
537
538void ExternalEngineCloseSplashScreen(
539 __in BURN_ENGINE_STATE* pEngineState
540 )
541{
542 // If the splash screen is still around, close it.
543 if (::IsWindow(pEngineState->command.hwndSplashScreen))
544 {
545 ::PostMessageW(pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0);
546 }
547}
548
549HRESULT ExternalEngineCompareVersions(
550 __in_z LPCWSTR wzVersion1,
551 __in_z LPCWSTR wzVersion2,
552 __out int* pnResult
553 )
554{
555 HRESULT hr = S_OK;
556
557 hr = VerCompareStringVersions(wzVersion1, wzVersion2, FALSE, pnResult);
558
559 return hr;
560}
561
562HRESULT ExternalEngineDetect(
563 __in const DWORD dwThreadId,
564 __in_opt const HWND hwndParent
565 )
566{
567 HRESULT hr = S_OK;
568
569 if (!::PostThreadMessageW(dwThreadId, WM_BURN_DETECT, 0, reinterpret_cast<LPARAM>(hwndParent)))
570 {
571 ExitWithLastError(hr, "Failed to post detect message.");
572 }
573
574LExit:
575 return hr;
576}
577
578HRESULT ExternalEnginePlan(
579 __in const DWORD dwThreadId,
580 __in const BOOTSTRAPPER_ACTION action
581 )
582{
583 HRESULT hr = S_OK;
584
585 if (BOOTSTRAPPER_ACTION_LAYOUT > action || BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED < action)
586 {
587 ExitOnRootFailure(hr = E_INVALIDARG, "BA passed invalid action to Plan: %u.", action);
588 }
589
590 if (!::PostThreadMessageW(dwThreadId, WM_BURN_PLAN, 0, action))
591 {
592 ExitWithLastError(hr, "Failed to post plan message.");
593 }
594
595LExit:
596 return hr;
597}
598
599HRESULT ExternalEngineElevate(
600 __in BURN_ENGINE_STATE* pEngineState,
601 __in const DWORD dwThreadId,
602 __in_opt const HWND hwndParent
603 )
604{
605 HRESULT hr = S_OK;
606
607 if (INVALID_HANDLE_VALUE != pEngineState->companionConnection.hPipe)
608 {
609 hr = HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED);
610 }
611 else if (!::PostThreadMessageW(dwThreadId, WM_BURN_ELEVATE, 0, reinterpret_cast<LPARAM>(hwndParent)))
612 {
613 ExitWithLastError(hr, "Failed to post elevate message.");
614 }
615
616LExit:
617 return hr;
618}
619
620HRESULT ExternalEngineApply(
621 __in const DWORD dwThreadId,
622 __in_opt const HWND hwndParent
623 )
624{
625 HRESULT hr = S_OK;
626
627 ExitOnNull(hwndParent, hr, E_INVALIDARG, "BA passed NULL hwndParent to Apply.");
628 if (!::IsWindow(hwndParent))
629 {
630 ExitOnRootFailure(hr = E_INVALIDARG, "BA passed invalid hwndParent to Apply.");
631 }
632
633 if (!::PostThreadMessageW(dwThreadId, WM_BURN_APPLY, 0, reinterpret_cast<LPARAM>(hwndParent)))
634 {
635 ExitWithLastError(hr, "Failed to post apply message.");
636 }
637
638LExit:
639 return hr;
640}
641
642HRESULT ExternalEngineQuit(
643 __in const DWORD dwThreadId,
644 __in const DWORD dwExitCode
645 )
646{
647 HRESULT hr = S_OK;
648
649 if (!::PostThreadMessageW(dwThreadId, WM_BURN_QUIT, static_cast<WPARAM>(dwExitCode), 0))
650 {
651 ExitWithLastError(hr, "Failed to post shutdown message.");
652 }
653
654LExit:
655 return hr;
656}
657
658HRESULT ExternalEngineLaunchApprovedExe(
659 __in BURN_ENGINE_STATE* pEngineState,
660 __in const DWORD dwThreadId,
661 __in_opt const HWND hwndParent,
662 __in_z LPCWSTR wzApprovedExeForElevationId,
663 __in_z_opt LPCWSTR wzArguments,
664 __in const DWORD dwWaitForInputIdleTimeout
665 )
666{
667 HRESULT hr = S_OK;
668 BURN_APPROVED_EXE* pApprovedExe = NULL;
669 BOOL fLeaveCriticalSection = FALSE;
670 BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe = NULL;
671
672 pLaunchApprovedExe = (BURN_LAUNCH_APPROVED_EXE*)MemAlloc(sizeof(BURN_LAUNCH_APPROVED_EXE), TRUE);
673 ExitOnNull(pLaunchApprovedExe, hr, E_OUTOFMEMORY, "Failed to alloc BURN_LAUNCH_APPROVED_EXE");
674
675 ::EnterCriticalSection(&pEngineState->userExperience.csEngineActive);
676 fLeaveCriticalSection = TRUE;
677 hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience);
678 ExitOnFailure(hr, "Engine is active, cannot change engine state.");
679
680 if (!wzApprovedExeForElevationId || !*wzApprovedExeForElevationId)
681 {
682 ExitFunction1(hr = E_INVALIDARG);
683 }
684
685 hr = ApprovedExesFindById(&pEngineState->approvedExes, wzApprovedExeForElevationId, &pApprovedExe);
686 ExitOnFailure(hr, "BA requested unknown approved exe with id: %ls", wzApprovedExeForElevationId);
687
688 hr = StrAllocString(&pLaunchApprovedExe->sczId, wzApprovedExeForElevationId, NULL);
689 ExitOnFailure(hr, "Failed to copy the id.");
690
691 if (wzArguments)
692 {
693 hr = StrAllocString(&pLaunchApprovedExe->sczArguments, wzArguments, NULL);
694 ExitOnFailure(hr, "Failed to copy the arguments.");
695 }
696
697 pLaunchApprovedExe->dwWaitForInputIdleTimeout = dwWaitForInputIdleTimeout;
698
699 pLaunchApprovedExe->hwndParent = hwndParent;
700
701 if (!::PostThreadMessageW(dwThreadId, WM_BURN_LAUNCH_APPROVED_EXE, 0, reinterpret_cast<LPARAM>(pLaunchApprovedExe)))
702 {
703 ExitWithLastError(hr, "Failed to post launch approved exe message.");
704 }
705
706LExit:
707 if (fLeaveCriticalSection)
708 {
709 ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive);
710 }
711
712 if (FAILED(hr))
713 {
714 ApprovedExesUninitializeLaunch(pLaunchApprovedExe);
715 }
716
717 return hr;
718}
719
720HRESULT ExternalEngineSetUpdateSource(
721 __in BURN_ENGINE_STATE* pEngineState,
722 __in_z LPCWSTR wzUrl
723 )
724{
725 HRESULT hr = S_OK;
726 BOOL fLeaveCriticalSection = FALSE;
727
728 ::EnterCriticalSection(&pEngineState->userExperience.csEngineActive);
729 fLeaveCriticalSection = TRUE;
730 hr = UserExperienceEnsureEngineInactive(&pEngineState->userExperience);
731 ExitOnFailure(hr, "Engine is active, cannot change engine state.");
732
733 if (wzUrl && *wzUrl)
734 {
735 hr = StrAllocString(&pEngineState->update.sczUpdateSource, wzUrl, 0);
736 ExitOnFailure(hr, "Failed to set feed download URL.");
737 }
738 else // no URL provided means clear out the whole download source.
739 {
740 ReleaseNullStr(pEngineState->update.sczUpdateSource);
741 }
742
743LExit:
744 if (fLeaveCriticalSection)
745 {
746 ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive);
747 }
748
749 return hr;
750}
751
752// TODO: callers need to provide the original size (at the time of first public release) of the struct instead of the current size.
753HRESULT WINAPI ExternalEngineValidateMessageParameter(
754 __in_opt const LPVOID pv,
755 __in SIZE_T cbSizeOffset,
756 __in DWORD dwMinimumSize
757 )
758{
759 HRESULT hr = S_OK;
760
761 if (!pv)
762 {
763 ExitFunction1(hr = E_INVALIDARG);
764 }
765
766 DWORD cbSize = *(DWORD*)((BYTE*)pv + cbSizeOffset);
767 if (dwMinimumSize < cbSize)
768 {
769 ExitFunction1(hr = E_INVALIDARG);
770 }
771
772LExit:
773 return hr;
774}
775
776static HRESULT CopyStringToExternal(
777 __in_z LPWSTR wzValue,
778 __in_z_opt LPWSTR wzBuffer,
779 __inout SIZE_T* pcchBuffer
780 )
781{
782 HRESULT hr = S_OK;
783 BOOL fTooSmall = !wzBuffer;
784
785 if (!fTooSmall)
786 {
787 hr = ::StringCchCopyExW(wzBuffer, *pcchBuffer, wzValue, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
788 if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
789 {
790 fTooSmall = TRUE;
791 }
792 }
793
794 if (fTooSmall)
795 {
796 hr = ::StringCchLengthW(wzValue, STRSAFE_MAX_LENGTH, reinterpret_cast<size_t*>(pcchBuffer));
797 if (SUCCEEDED(hr))
798 {
799 hr = E_MOREDATA;
800 *pcchBuffer += 1; // null terminator.
801 }
802 }
803
804 return hr;
805}
diff --git a/src/burn/engine/externalengine.h b/src/burn/engine/externalengine.h
new file mode 100644
index 00000000..2903615d
--- /dev/null
+++ b/src/burn/engine/externalengine.h
@@ -0,0 +1,181 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#define ValidateMessageParameter(x, pv, type) { x = ExternalEngineValidateMessageParameter(pv, offsetof(type, cbSize), sizeof(type)); if (FAILED(x)) { goto LExit; }}
6#define ValidateMessageArgs(x, pv, type, identifier) ValidateMessageParameter(x, pv, type); const type* identifier = reinterpret_cast<type*>(pv); UNREFERENCED_PARAMETER(identifier)
7#define ValidateMessageResults(x, pv, type, identifier) ValidateMessageParameter(x, pv, type); type* identifier = reinterpret_cast<type*>(pv); UNREFERENCED_PARAMETER(identifier)
8
9
10#if defined(__cplusplus)
11extern "C" {
12#endif
13
14void ExternalEngineGetPackageCount(
15 __in BURN_ENGINE_STATE* pEngineState,
16 __out DWORD* pcPackages
17 );
18
19HRESULT ExternalEngineGetVariableNumeric(
20 __in BURN_ENGINE_STATE* pEngineState,
21 __in_z LPCWSTR wzVariable,
22 __out LONGLONG* pllValue
23 );
24
25HRESULT ExternalEngineGetVariableString(
26 __in BURN_ENGINE_STATE* pEngineState,
27 __in_z LPCWSTR wzVariable,
28 __out_ecount_opt(*pcchValue) LPWSTR wzValue,
29 __inout SIZE_T* pcchValue
30 );
31
32HRESULT ExternalEngineGetVariableVersion(
33 __in BURN_ENGINE_STATE* pEngineState,
34 __in_z LPCWSTR wzVariable,
35 __out_ecount_opt(*pcchValue) LPWSTR wzValue,
36 __inout SIZE_T* pcchValue
37 );
38
39HRESULT ExternalEngineFormatString(
40 __in BURN_ENGINE_STATE* pEngineState,
41 __in_z LPCWSTR wzIn,
42 __out_ecount_opt(*pcchOut) LPWSTR wzOut,
43 __inout SIZE_T* pcchOut
44 );
45
46HRESULT ExternalEngineEscapeString(
47 __in_z LPCWSTR wzIn,
48 __out_ecount_opt(*pcchOut) LPWSTR wzOut,
49 __inout SIZE_T* pcchOut
50 );
51
52HRESULT ExternalEngineEvaluateCondition(
53 __in BURN_ENGINE_STATE* pEngineState,
54 __in_z LPCWSTR wzCondition,
55 __out BOOL* pf
56 );
57
58HRESULT ExternalEngineLog(
59 __in REPORT_LEVEL rl,
60 __in_z LPCWSTR wzMessage
61 );
62
63HRESULT ExternalEngineSendEmbeddedError(
64 __in BURN_ENGINE_STATE* pEngineState,
65 __in const DWORD dwErrorCode,
66 __in_z LPCWSTR wzMessage,
67 __in const DWORD dwUIHint,
68 __out int* pnResult
69 );
70
71HRESULT ExternalEngineSendEmbeddedProgress(
72 __in BURN_ENGINE_STATE* pEngineState,
73 __in const DWORD dwProgressPercentage,
74 __in const DWORD dwOverallProgressPercentage,
75 __out int* pnResult
76 );
77
78HRESULT ExternalEngineSetUpdate(
79 __in BURN_ENGINE_STATE* pEngineState,
80 __in_z_opt LPCWSTR wzLocalSource,
81 __in_z_opt LPCWSTR wzDownloadSource,
82 __in const DWORD64 qwSize,
83 __in const BOOTSTRAPPER_UPDATE_HASH_TYPE hashType,
84 __in_opt const BYTE* rgbHash,
85 __in const DWORD cbHash
86 );
87
88HRESULT ExternalEngineSetLocalSource(
89 __in BURN_ENGINE_STATE* pEngineState,
90 __in_z_opt LPCWSTR wzPackageOrContainerId,
91 __in_z_opt LPCWSTR wzPayloadId,
92 __in_z LPCWSTR wzPath
93 );
94
95HRESULT ExternalEngineSetDownloadSource(
96 __in BURN_ENGINE_STATE* pEngineState,
97 __in_z_opt LPCWSTR wzPackageOrContainerId,
98 __in_z_opt LPCWSTR wzPayloadId,
99 __in_z_opt LPCWSTR wzUrl,
100 __in_z_opt LPCWSTR wzUser,
101 __in_z_opt LPCWSTR wzPassword
102 );
103
104HRESULT ExternalEngineSetVariableNumeric(
105 __in BURN_ENGINE_STATE* pEngineState,
106 __in_z LPCWSTR wzVariable,
107 __in const LONGLONG llValue
108 );
109
110HRESULT ExternalEngineSetVariableString(
111 __in BURN_ENGINE_STATE* pEngineState,
112 __in_z LPCWSTR wzVariable,
113 __in_z_opt LPCWSTR wzValue,
114 __in const BOOL fFormatted
115 );
116
117HRESULT ExternalEngineSetVariableVersion(
118 __in BURN_ENGINE_STATE* pEngineState,
119 __in_z LPCWSTR wzVariable,
120 __in_z_opt LPCWSTR wzValue
121 );
122
123void ExternalEngineCloseSplashScreen(
124 __in BURN_ENGINE_STATE* pEngineState
125 );
126
127HRESULT ExternalEngineCompareVersions(
128 __in_z LPCWSTR wzVersion1,
129 __in_z LPCWSTR wzVersion2,
130 __out int* pnResult
131 );
132
133HRESULT ExternalEngineDetect(
134 __in const DWORD dwThreadId,
135 __in_opt const HWND hwndParent
136 );
137
138HRESULT ExternalEnginePlan(
139 __in const DWORD dwThreadId,
140 __in const BOOTSTRAPPER_ACTION action
141 );
142
143HRESULT ExternalEngineElevate(
144 __in BURN_ENGINE_STATE* pEngineState,
145 __in const DWORD dwThreadId,
146 __in_opt const HWND hwndParent
147 );
148
149HRESULT ExternalEngineApply(
150 __in const DWORD dwThreadId,
151 __in_opt const HWND hwndParent
152 );
153
154HRESULT ExternalEngineQuit(
155 __in const DWORD dwThreadId,
156 __in const DWORD dwExitCode
157 );
158
159HRESULT ExternalEngineLaunchApprovedExe(
160 __in BURN_ENGINE_STATE* pEngineState,
161 __in const DWORD dwThreadId,
162 __in_opt const HWND hwndParent,
163 __in_z LPCWSTR wzApprovedExeForElevationId,
164 __in_z_opt LPCWSTR wzArguments,
165 __in const DWORD dwWaitForInputIdleTimeout
166 );
167
168HRESULT ExternalEngineSetUpdateSource(
169 __in BURN_ENGINE_STATE* pEngineState,
170 __in_z LPCWSTR wzUrl
171 );
172
173HRESULT WINAPI ExternalEngineValidateMessageParameter(
174 __in_opt const LPVOID pv,
175 __in SIZE_T cbSizeOffset,
176 __in DWORD dwMinimumSize
177 );
178
179#if defined(__cplusplus)
180}
181#endif
diff --git a/src/burn/engine/inc/burnsources.h b/src/burn/engine/inc/burnsources.h
new file mode 100644
index 00000000..bff79ed5
--- /dev/null
+++ b/src/burn/engine/inc/burnsources.h
@@ -0,0 +1,4 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4#define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_EXTERNAL
diff --git a/src/burn/engine/inc/engine.h b/src/burn/engine/inc/engine.h
new file mode 100644
index 00000000..808bb91a
--- /dev/null
+++ b/src/burn/engine/inc/engine.h
@@ -0,0 +1,27 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// function declarations
11
12BOOL EngineInCleanRoom(
13 __in_z_opt LPCWSTR wzCommandLine
14 );
15
16HRESULT EngineRun(
17 __in HINSTANCE hInstance,
18 __in HANDLE hEngineFile,
19 __in_z_opt LPCWSTR wzCommandLine,
20 __in int nCmdShow,
21 __out DWORD* pdwExitCode
22 );
23
24
25#if defined(__cplusplus)
26}
27#endif
diff --git a/src/burn/engine/logging.cpp b/src/burn/engine/logging.cpp
new file mode 100644
index 00000000..065ef907
--- /dev/null
+++ b/src/burn/engine/logging.cpp
@@ -0,0 +1,754 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6static DWORD vdwPackageSequence = 0;
7static const DWORD LOG_OPEN_RETRY_COUNT = 3;
8static const DWORD LOG_OPEN_RETRY_WAIT = 2000;
9static CONST LPWSTR LOG_FAILED_EVENT_LOG_MESSAGE = L"Burn Engine Fatal Error: failed to open log file.";
10
11// structs
12
13
14
15// internal function declarations
16
17static void CheckLoggingPolicy(
18 __out DWORD *pdwAttributes
19 );
20static HRESULT GetNonSessionSpecificTempFolder(
21 __deref_out_z LPWSTR* psczNonSessionTempFolder
22 );
23
24
25// function definitions
26
27extern "C" HRESULT LoggingOpen(
28 __in BURN_LOGGING* pLog,
29 __in BURN_VARIABLES* pVariables,
30 __in BOOTSTRAPPER_DISPLAY display,
31 __in_z LPCWSTR wzBundleName
32 )
33{
34 HRESULT hr = S_OK;
35 LPWSTR sczLoggingBaseFolder = NULL;
36 LPWSTR sczPrefixFormatted = NULL;
37
38 // Check if the logging policy is set and configure the logging appropriately.
39 CheckLoggingPolicy(&pLog->dwAttributes);
40
41 if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_VERBOSE || pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG)
42 {
43 if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG)
44 {
45 LogSetLevel(REPORT_DEBUG, FALSE);
46 }
47 else if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_VERBOSE)
48 {
49 LogSetLevel(REPORT_VERBOSE, FALSE);
50 }
51
52 if ((!pLog->sczPath || !*pLog->sczPath) && (!pLog->sczPrefix || !*pLog->sczPrefix))
53 {
54 PathCreateTimeBasedTempFile(NULL, L"Setup", NULL, L"log", &pLog->sczPath, NULL);
55 }
56 }
57
58 // Open the log approriately.
59 if (pLog->sczPath && *pLog->sczPath)
60 {
61 DWORD cRetry = 0;
62
63 hr = DirGetCurrent(&sczLoggingBaseFolder);
64 ExitOnFailure(hr, "Failed to get current directory.");
65
66 // Try pretty hard to open the log file when appending.
67 do
68 {
69 if (0 < cRetry)
70 {
71 ::Sleep(LOG_OPEN_RETRY_WAIT);
72 }
73
74 hr = LogOpen(sczLoggingBaseFolder, pLog->sczPath, NULL, NULL, pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND, FALSE, &pLog->sczPath);
75 if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND && HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) == hr)
76 {
77 ++cRetry;
78 }
79 } while (cRetry > 0 && cRetry <= LOG_OPEN_RETRY_COUNT);
80
81 if (FAILED(hr))
82 {
83 // Log is not open, so note that.
84 LogDisable();
85 pLog->state = BURN_LOGGING_STATE_DISABLED;
86
87 if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND)
88 {
89 // If appending, ignore the failure and continue.
90 hr = S_OK;
91 }
92 else // specifically tried to create a log file so show an error if appropriate and bail.
93 {
94 HRESULT hrOriginal = hr;
95
96 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_LOG_FAILURE);
97 SplashScreenDisplayError(display, wzBundleName, hr);
98
99 ExitOnFailure(hrOriginal, "Failed to open log: %ls", pLog->sczPath);
100 }
101 }
102 else
103 {
104 pLog->state = BURN_LOGGING_STATE_OPEN;
105 }
106 }
107 else
108 {
109 if (pLog->sczPrefix && *pLog->sczPrefix)
110 {
111 hr = VariableFormatString(pVariables, pLog->sczPrefix, &sczPrefixFormatted, NULL);
112 }
113
114 if (sczPrefixFormatted && *sczPrefixFormatted)
115 {
116 LPCWSTR wzPrefix = sczPrefixFormatted;
117
118 // Best effort to open default logging.
119 if (PathIsAbsolute(sczPrefixFormatted))
120 {
121 hr = PathGetDirectory(sczPrefixFormatted, &sczLoggingBaseFolder);
122 ExitOnFailure(hr, "Failed to get parent directory from '%ls'.", sczPrefixFormatted);
123
124 wzPrefix = PathFile(sczPrefixFormatted);
125 }
126 else
127 {
128 hr = GetNonSessionSpecificTempFolder(&sczLoggingBaseFolder);
129 ExitOnFailure(hr, "Failed to get non-session specific TEMP folder.");
130 }
131
132 hr = LogOpen(sczLoggingBaseFolder, wzPrefix, NULL, pLog->sczExtension, FALSE, FALSE, &pLog->sczPath);
133 if (FAILED(hr))
134 {
135 LogDisable();
136 pLog->state = BURN_LOGGING_STATE_DISABLED;
137
138 hr = S_OK;
139 }
140 else
141 {
142 pLog->state = BURN_LOGGING_STATE_OPEN;
143 }
144 }
145 else // no logging enabled.
146 {
147 LogDisable();
148 pLog->state = BURN_LOGGING_STATE_DISABLED;
149 }
150 }
151
152 // If the log was opened, write the header info and update the prefix and extension to match
153 // the log name so future logs are opened with the same pattern.
154 if (BURN_LOGGING_STATE_OPEN == pLog->state)
155 {
156 LPCWSTR wzExtension = PathExtension(pLog->sczPath);
157 if (wzExtension && *wzExtension)
158 {
159 hr = StrAllocString(&pLog->sczPrefix, pLog->sczPath, wzExtension - pLog->sczPath);
160 ExitOnFailure(hr, "Failed to copy log path to prefix.");
161
162 hr = StrAllocString(&pLog->sczExtension, wzExtension + 1, 0);
163 ExitOnFailure(hr, "Failed to copy log extension to extension.");
164 }
165 else
166 {
167 hr = StrAllocString(&pLog->sczPrefix, pLog->sczPath, 0);
168 ExitOnFailure(hr, "Failed to copy full log path to prefix.");
169 }
170
171 if (pLog->sczPathVariable && *pLog->sczPathVariable)
172 {
173 VariableSetString(pVariables, pLog->sczPathVariable, pLog->sczPath, FALSE, FALSE); // Ignore failure.
174 }
175 }
176
177LExit:
178 ReleaseStr(sczLoggingBaseFolder);
179 StrSecureZeroFreeString(sczPrefixFormatted);
180
181 return hr;
182}
183
184extern "C" void LoggingOpenFailed()
185{
186 HRESULT hr = S_OK;
187 HANDLE hEventLog = NULL;
188 LPCWSTR* lpStrings = const_cast<LPCWSTR*>(&LOG_FAILED_EVENT_LOG_MESSAGE);
189 WORD wNumStrings = 1;
190
191 hr = LogOpen(NULL, L"Setup", L"_Failed", L"txt", FALSE, FALSE, NULL);
192 if (SUCCEEDED(hr))
193 {
194 ExitFunction();
195 }
196
197 // If opening the "failure" log failed, then attempt to record that in the Application event log.
198 hEventLog = ::OpenEventLogW(NULL, L"Application");
199 ExitOnNullWithLastError(hEventLog, hr, "Failed to open Application event log");
200
201 hr = ::ReportEventW(hEventLog, EVENTLOG_ERROR_TYPE, 1, 1, NULL, wNumStrings, 0, lpStrings, NULL);
202 ExitOnNullWithLastError(hEventLog, hr, "Failed to write event log entry");
203
204LExit:
205 if (hEventLog)
206 {
207 ::CloseEventLog(hEventLog);
208 }
209}
210
211extern "C" void LoggingIncrementPackageSequence()
212{
213 ++vdwPackageSequence;
214}
215
216extern "C" HRESULT LoggingSetPackageVariable(
217 __in BURN_PACKAGE* pPackage,
218 __in_z_opt LPCWSTR wzSuffix,
219 __in BOOL fRollback,
220 __in BURN_LOGGING* pLog,
221 __in BURN_VARIABLES* pVariables,
222 __out_opt LPWSTR* psczLogPath
223 )
224{
225 HRESULT hr = S_OK;
226 LPWSTR sczLogPath = NULL;
227
228 // Make sure that no package log files are created when logging has been disabled via Log element.
229 if (BURN_LOGGING_STATE_DISABLED == pLog->state)
230 {
231 if (psczLogPath)
232 {
233 *psczLogPath = NULL;
234 }
235
236 ExitFunction();
237 }
238
239 if ((!fRollback && pPackage->sczLogPathVariable && *pPackage->sczLogPathVariable) ||
240 (fRollback && pPackage->sczRollbackLogPathVariable && *pPackage->sczRollbackLogPathVariable))
241 {
242 hr = StrAllocFormatted(&sczLogPath, L"%ls%hs%ls_%03u_%ls%ls.%ls", pLog->sczPrefix, wzSuffix && *wzSuffix ? "_" : "", wzSuffix && *wzSuffix ? wzSuffix : L"", vdwPackageSequence, pPackage->sczId, fRollback ? L"_rollback" : L"", pLog->sczExtension);
243 ExitOnFailure(hr, "Failed to allocate path for package log.");
244
245 hr = VariableSetString(pVariables, fRollback ? pPackage->sczRollbackLogPathVariable : pPackage->sczLogPathVariable, sczLogPath, FALSE, FALSE);
246 ExitOnFailure(hr, "Failed to set log path into variable.");
247
248 if (psczLogPath)
249 {
250 hr = StrAllocString(psczLogPath, sczLogPath, 0);
251 ExitOnFailure(hr, "Failed to copy package log path.");
252 }
253 }
254
255LExit:
256 ReleaseStr(sczLogPath);
257
258 return hr;
259}
260
261extern "C" LPCSTR LoggingBurnActionToString(
262 __in BOOTSTRAPPER_ACTION action
263 )
264{
265 switch (action)
266 {
267 case BOOTSTRAPPER_ACTION_UNKNOWN:
268 return "Unknown";
269 case BOOTSTRAPPER_ACTION_HELP:
270 return "Help";
271 case BOOTSTRAPPER_ACTION_LAYOUT:
272 return "Layout";
273 case BOOTSTRAPPER_ACTION_CACHE:
274 return "Cache";
275 case BOOTSTRAPPER_ACTION_UNINSTALL:
276 return "Uninstall";
277 case BOOTSTRAPPER_ACTION_INSTALL:
278 return "Install";
279 case BOOTSTRAPPER_ACTION_MODIFY:
280 return "Modify";
281 case BOOTSTRAPPER_ACTION_REPAIR:
282 return "Repair";
283 case BOOTSTRAPPER_ACTION_UPDATE_REPLACE:
284 return "UpdateReplace";
285 case BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED:
286 return "UpdateReplaceEmbedded";
287 default:
288 return "Invalid";
289 }
290}
291
292LPCSTR LoggingBurnMessageToString(
293 __in UINT message
294 )
295{
296 switch (message)
297 {
298 case WM_BURN_APPLY:
299 return "Apply";
300 case WM_BURN_DETECT:
301 return "Detect";
302 case WM_BURN_ELEVATE:
303 return "Elevate";
304 case WM_BURN_LAUNCH_APPROVED_EXE:
305 return "LaunchApprovedExe";
306 case WM_BURN_PLAN:
307 return "Plan";
308 case WM_BURN_QUIT:
309 return "Quit";
310 default:
311 return "Invalid";
312 }
313}
314
315extern "C" LPCSTR LoggingActionStateToString(
316 __in BOOTSTRAPPER_ACTION_STATE actionState
317 )
318{
319 switch (actionState)
320 {
321 case BOOTSTRAPPER_ACTION_STATE_NONE:
322 return "None";
323 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
324 return "Uninstall";
325 case BOOTSTRAPPER_ACTION_STATE_INSTALL:
326 return "Install";
327 case BOOTSTRAPPER_ACTION_STATE_MODIFY:
328 return "Modify";
329 case BOOTSTRAPPER_ACTION_STATE_MEND:
330 return "Mend";
331 case BOOTSTRAPPER_ACTION_STATE_REPAIR:
332 return "Repair";
333 case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE:
334 return "MinorUpgrade";
335 default:
336 return "Invalid";
337 }
338}
339
340extern "C" LPCSTR LoggingDependencyActionToString(
341 BURN_DEPENDENCY_ACTION action
342 )
343{
344 switch (action)
345 {
346 case BURN_DEPENDENCY_ACTION_NONE:
347 return "None";
348 case BURN_DEPENDENCY_ACTION_REGISTER:
349 return "Register";
350 case BURN_DEPENDENCY_ACTION_UNREGISTER:
351 return "Unregister";
352 default:
353 return "Invalid";
354 }
355}
356
357extern "C" LPCSTR LoggingBoolToString(
358 __in BOOL f
359 )
360{
361 if (f)
362 {
363 return "Yes";
364 }
365
366 return "No";
367}
368
369extern "C" LPCSTR LoggingTrueFalseToString(
370 __in BOOL f
371 )
372{
373 if (f)
374 {
375 return "true";
376 }
377
378 return "false";
379}
380
381extern "C" LPCSTR LoggingPackageStateToString(
382 __in BOOTSTRAPPER_PACKAGE_STATE packageState
383 )
384{
385 switch (packageState)
386 {
387 case BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN:
388 return "Unknown";
389 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE:
390 return "Obsolete";
391 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
392 return "Absent";
393 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
394 return "Present";
395 case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
396 return "Superseded";
397 default:
398 return "Invalid";
399 }
400}
401
402extern "C" LPCSTR LoggingPackageRegistrationStateToString(
403 __in BOOL fCanAffectRegistration,
404 __in BURN_PACKAGE_REGISTRATION_STATE registrationState
405 )
406{
407 if (!fCanAffectRegistration)
408 {
409 return "(permanent)";
410 }
411
412 switch (registrationState)
413 {
414 case BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN:
415 return "Unknown";
416 case BURN_PACKAGE_REGISTRATION_STATE_IGNORED:
417 return "Ignored";
418 case BURN_PACKAGE_REGISTRATION_STATE_ABSENT:
419 return "Absent";
420 case BURN_PACKAGE_REGISTRATION_STATE_PRESENT:
421 return "Present";
422 default:
423 return "Invalid";
424 }
425}
426
427extern "C" LPCSTR LoggingMsiFeatureStateToString(
428 __in BOOTSTRAPPER_FEATURE_STATE featureState
429 )
430{
431 switch (featureState)
432 {
433 case BOOTSTRAPPER_FEATURE_STATE_UNKNOWN:
434 return "Unknown";
435 case BOOTSTRAPPER_FEATURE_STATE_ABSENT:
436 return "Absent";
437 case BOOTSTRAPPER_FEATURE_STATE_ADVERTISED:
438 return "Advertised";
439 case BOOTSTRAPPER_FEATURE_STATE_LOCAL:
440 return "Local";
441 case BOOTSTRAPPER_FEATURE_STATE_SOURCE:
442 return "Source";
443 default:
444 return "Invalid";
445 }
446}
447
448extern "C" LPCSTR LoggingMsiFeatureActionToString(
449 __in BOOTSTRAPPER_FEATURE_ACTION featureAction
450 )
451{
452 switch (featureAction)
453 {
454 case BOOTSTRAPPER_FEATURE_ACTION_NONE:
455 return "None";
456 case BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL:
457 return "AddLocal";
458 case BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE:
459 return "AddSource";
460 case BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT:
461 return "AddDefault";
462 case BOOTSTRAPPER_FEATURE_ACTION_REINSTALL:
463 return "Reinstall";
464 case BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE:
465 return "Advertise";
466 case BOOTSTRAPPER_FEATURE_ACTION_REMOVE:
467 return "Remove";
468 default:
469 return "Invalid";
470 }
471}
472
473extern "C" LPCSTR LoggingMsiInstallContext(
474 __in MSIINSTALLCONTEXT context
475 )
476{
477 switch (context)
478 {
479 case MSIINSTALLCONTEXT_ALL:
480 return "All";
481 case MSIINSTALLCONTEXT_ALLUSERMANAGED:
482 return "AllUserManaged";
483 case MSIINSTALLCONTEXT_MACHINE:
484 return "Machine";
485 case MSIINSTALLCONTEXT_NONE:
486 return "None";
487 case MSIINSTALLCONTEXT_USERMANAGED:
488 return "UserManaged";
489 case MSIINSTALLCONTEXT_USERUNMANAGED:
490 return "UserUnmanaged";
491 default:
492 return "Invalid";
493 }
494}
495
496extern "C" LPCWSTR LoggingBurnMsiPropertyToString(
497 __in BURN_MSI_PROPERTY burnMsiProperty
498 )
499{
500 switch (burnMsiProperty)
501 {
502 case BURN_MSI_PROPERTY_INSTALL:
503 return BURNMSIINSTALL_PROPERTY_NAME;
504 case BURN_MSI_PROPERTY_MODIFY:
505 return BURNMSIMODIFY_PROPERTY_NAME;
506 case BURN_MSI_PROPERTY_NONE:
507 return L"(none)";
508 case BURN_MSI_PROPERTY_REPAIR:
509 return BURNMSIREPAIR_PROPERTY_NAME;
510 case BURN_MSI_PROPERTY_UNINSTALL:
511 return BURNMSIUNINSTALL_PROPERTY_NAME;
512 default:
513 return L"Invalid";
514 }
515}
516
517extern "C" LPCSTR LoggingMspTargetActionToString(
518 __in BOOTSTRAPPER_ACTION_STATE action,
519 __in BURN_PATCH_SKIP_STATE skipState
520 )
521{
522 switch (skipState)
523 {
524 case BURN_PATCH_SKIP_STATE_NONE:
525 return LoggingActionStateToString(action);
526 case BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL:
527 return "Skipped (target uninstall)";
528 case BURN_PATCH_SKIP_STATE_SLIPSTREAM:
529 return "Skipped (slipstream)";
530 default:
531 return "Invalid";
532 }
533}
534
535extern "C" LPCSTR LoggingPerMachineToString(
536 __in BOOL fPerMachine
537 )
538{
539 if (fPerMachine)
540 {
541 return "PerMachine";
542 }
543
544 return "PerUser";
545}
546
547extern "C" LPCSTR LoggingRestartToString(
548 __in BOOTSTRAPPER_APPLY_RESTART restart
549 )
550{
551 switch (restart)
552 {
553 case BOOTSTRAPPER_APPLY_RESTART_NONE:
554 return "None";
555 case BOOTSTRAPPER_APPLY_RESTART_REQUIRED:
556 return "Required";
557 case BOOTSTRAPPER_APPLY_RESTART_INITIATED:
558 return "Initiated";
559 default:
560 return "Invalid";
561 }
562}
563
564extern "C" LPCSTR LoggingResumeModeToString(
565 __in BURN_RESUME_MODE resumeMode
566 )
567{
568 switch (resumeMode)
569 {
570 case BURN_RESUME_MODE_NONE:
571 return "None";
572 case BURN_RESUME_MODE_ACTIVE:
573 return "Active";
574 case BURN_RESUME_MODE_SUSPEND:
575 return "Suspend";
576 case BURN_RESUME_MODE_ARP:
577 return "ARP";
578 case BURN_RESUME_MODE_REBOOT_PENDING:
579 return "Reboot Pending";
580 default:
581 return "Invalid";
582 }
583}
584
585extern "C" LPCSTR LoggingRelationTypeToString(
586 __in BOOTSTRAPPER_RELATION_TYPE type
587 )
588{
589 switch (type)
590 {
591 case BOOTSTRAPPER_RELATION_NONE:
592 return "None";
593 case BOOTSTRAPPER_RELATION_DETECT:
594 return "Detect";
595 case BOOTSTRAPPER_RELATION_UPGRADE:
596 return "Upgrade";
597 case BOOTSTRAPPER_RELATION_ADDON:
598 return "Addon";
599 case BOOTSTRAPPER_RELATION_PATCH:
600 return "Patch";
601 case BOOTSTRAPPER_RELATION_DEPENDENT:
602 return "Dependent";
603 case BOOTSTRAPPER_RELATION_UPDATE:
604 return "Update";
605 default:
606 return "Invalid";
607 }
608}
609
610extern "C" LPCSTR LoggingRelatedOperationToString(
611 __in BOOTSTRAPPER_RELATED_OPERATION operation
612 )
613{
614 switch (operation)
615 {
616 case BOOTSTRAPPER_RELATED_OPERATION_NONE:
617 return "None";
618 case BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE:
619 return "Downgrade";
620 case BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE:
621 return "MinorUpdate";
622 case BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE:
623 return "MajorUpgrade";
624 case BOOTSTRAPPER_RELATED_OPERATION_REMOVE:
625 return "Remove";
626 case BOOTSTRAPPER_RELATED_OPERATION_INSTALL:
627 return "Install";
628 case BOOTSTRAPPER_RELATED_OPERATION_REPAIR:
629 return "Repair";
630 default:
631 return "Invalid";
632 }
633}
634
635extern "C" LPCSTR LoggingRequestStateToString(
636 __in BOOTSTRAPPER_REQUEST_STATE requestState
637 )
638{
639 switch (requestState)
640 {
641 case BOOTSTRAPPER_REQUEST_STATE_NONE:
642 return "None";
643 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
644 return "ForceAbsent";
645 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
646 return "Absent";
647 case BOOTSTRAPPER_REQUEST_STATE_CACHE:
648 return "Cache";
649 case BOOTSTRAPPER_REQUEST_STATE_PRESENT:
650 return "Present";
651 case BOOTSTRAPPER_REQUEST_STATE_MEND:
652 return "Mend";
653 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
654 return "Repair";
655 default:
656 return "Invalid";
657 }
658}
659
660extern "C" LPCSTR LoggingRollbackOrExecute(
661 __in BOOL fRollback
662 )
663{
664 return fRollback ? "rollback" : "execute";
665}
666
667extern "C" LPWSTR LoggingStringOrUnknownIfNull(
668 __in LPCWSTR wz
669 )
670{
671 return wz ? wz : L"Unknown";
672}
673
674
675// internal function declarations
676
677static void CheckLoggingPolicy(
678 __out DWORD *pdwAttributes
679 )
680{
681 HRESULT hr = S_OK;
682 HKEY hk = NULL;
683 LPWSTR sczLoggingPolicy = NULL;
684
685 hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Policies\\Microsoft\\Windows\\Installer", KEY_READ, &hk);
686 if (SUCCEEDED(hr))
687 {
688 hr = RegReadString(hk, L"Logging", &sczLoggingPolicy);
689 if (SUCCEEDED(hr))
690 {
691 LPCWSTR wz = sczLoggingPolicy;
692 while (*wz)
693 {
694 if (L'v' == *wz || L'V' == *wz)
695 {
696 *pdwAttributes |= BURN_LOGGING_ATTRIBUTE_VERBOSE;
697 }
698 else if (L'x' == *wz || L'X' == *wz)
699 {
700 *pdwAttributes |= BURN_LOGGING_ATTRIBUTE_EXTRADEBUG;
701 }
702
703 ++wz;
704 }
705 }
706 }
707
708 ReleaseStr(sczLoggingPolicy);
709 ReleaseRegKey(hk);
710}
711
712static HRESULT GetNonSessionSpecificTempFolder(
713 __deref_out_z LPWSTR* psczNonSessionTempFolder
714 )
715{
716 HRESULT hr = S_OK;
717 WCHAR wzTempFolder[MAX_PATH] = { };
718 SIZE_T cchTempFolder = 0;
719 DWORD dwSessionId = 0;
720 LPWSTR sczSessionId = 0;
721 SIZE_T cchSessionId = 0;
722
723 if (!::GetTempPathW(countof(wzTempFolder), wzTempFolder))
724 {
725 ExitWithLastError(hr, "Failed to get temp folder.");
726 }
727
728 hr = ::StringCchLengthW(wzTempFolder, countof(wzTempFolder), reinterpret_cast<size_t*>(&cchTempFolder));
729 ExitOnFailure(hr, "Failed to get length of temp folder.");
730
731 // If our session id is in the TEMP path then remove that part so we get the non-session
732 // specific temporary folder.
733 if (::ProcessIdToSessionId(::GetCurrentProcessId(), &dwSessionId))
734 {
735 hr = StrAllocFormatted(&sczSessionId, L"%u\\", dwSessionId);
736 ExitOnFailure(hr, "Failed to format session id as a string.");
737
738 hr = ::StringCchLengthW(sczSessionId, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchSessionId));
739 ExitOnFailure(hr, "Failed to get length of session id string.");
740
741 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzTempFolder + cchTempFolder - cchSessionId, static_cast<DWORD>(cchSessionId), sczSessionId, static_cast<DWORD>(cchSessionId)))
742 {
743 cchTempFolder -= cchSessionId;
744 }
745 }
746
747 hr = StrAllocString(psczNonSessionTempFolder, wzTempFolder, cchTempFolder);
748 ExitOnFailure(hr, "Failed to copy temp folder.");
749
750LExit:
751 ReleaseStr(sczSessionId);
752
753 return hr;
754}
diff --git a/src/burn/engine/logging.h b/src/burn/engine/logging.h
new file mode 100644
index 00000000..601039f9
--- /dev/null
+++ b/src/burn/engine/logging.h
@@ -0,0 +1,153 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// constants
11
12enum BURN_LOGGING_STATE
13{
14 BURN_LOGGING_STATE_CLOSED,
15 BURN_LOGGING_STATE_OPEN,
16 BURN_LOGGING_STATE_DISABLED,
17};
18
19enum BURN_LOGGING_ATTRIBUTE
20{
21 BURN_LOGGING_ATTRIBUTE_APPEND = 0x1,
22 BURN_LOGGING_ATTRIBUTE_VERBOSE = 0x2,
23 BURN_LOGGING_ATTRIBUTE_EXTRADEBUG = 0x4,
24};
25
26
27// structs
28
29typedef struct _BURN_LOGGING
30{
31 BURN_LOGGING_STATE state;
32 LPWSTR sczPathVariable;
33
34 DWORD dwAttributes;
35 LPWSTR sczPath;
36 LPWSTR sczPrefix;
37 LPWSTR sczExtension;
38} BURN_LOGGING;
39
40
41
42// function declarations
43
44HRESULT LoggingOpen(
45 __in BURN_LOGGING* pLog,
46 __in BURN_VARIABLES* pVariables,
47 __in BOOTSTRAPPER_DISPLAY display,
48 __in_z LPCWSTR wzBundleName
49 );
50
51void LoggingOpenFailed();
52
53void LoggingIncrementPackageSequence();
54
55HRESULT LoggingSetPackageVariable(
56 __in BURN_PACKAGE* pPackage,
57 __in_z_opt LPCWSTR wzSuffix,
58 __in BOOL fRollback,
59 __in BURN_LOGGING* pLog,
60 __in BURN_VARIABLES* pVariables,
61 __out_opt LPWSTR* psczLogPath
62 );
63
64LPCSTR LoggingBurnActionToString(
65 __in BOOTSTRAPPER_ACTION action
66 );
67
68LPCSTR LoggingBurnMessageToString(
69 __in UINT message
70 );
71
72LPCSTR LoggingActionStateToString(
73 __in BOOTSTRAPPER_ACTION_STATE actionState
74 );
75
76LPCSTR LoggingDependencyActionToString(
77 BURN_DEPENDENCY_ACTION action
78 );
79
80LPCSTR LoggingBoolToString(
81 __in BOOL f
82 );
83
84LPCSTR LoggingTrueFalseToString(
85 __in BOOL f
86 );
87
88LPCSTR LoggingPackageStateToString(
89 __in BOOTSTRAPPER_PACKAGE_STATE packageState
90 );
91
92LPCSTR LoggingPackageRegistrationStateToString(
93 __in BOOL fCanAffectRegistration,
94 __in BURN_PACKAGE_REGISTRATION_STATE registrationState
95 );
96
97LPCSTR LoggingMsiFeatureStateToString(
98 __in BOOTSTRAPPER_FEATURE_STATE featureState
99 );
100
101LPCSTR LoggingMsiFeatureActionToString(
102 __in BOOTSTRAPPER_FEATURE_ACTION featureAction
103 );
104
105LPCSTR LoggingMsiInstallContext(
106 __in MSIINSTALLCONTEXT context
107 );
108
109LPCWSTR LoggingBurnMsiPropertyToString(
110 __in BURN_MSI_PROPERTY burnMsiProperty
111 );
112
113LPCSTR LoggingMspTargetActionToString(
114 __in BOOTSTRAPPER_ACTION_STATE action,
115 __in BURN_PATCH_SKIP_STATE skipState
116 );
117
118LPCSTR LoggingPerMachineToString(
119 __in BOOL fPerMachine
120 );
121
122LPCSTR LoggingRestartToString(
123 __in BOOTSTRAPPER_APPLY_RESTART restart
124 );
125
126LPCSTR LoggingResumeModeToString(
127 __in BURN_RESUME_MODE resumeMode
128 );
129
130LPCSTR LoggingRelationTypeToString(
131 __in BOOTSTRAPPER_RELATION_TYPE type
132 );
133
134LPCSTR LoggingRelatedOperationToString(
135 __in BOOTSTRAPPER_RELATED_OPERATION operation
136 );
137
138LPCSTR LoggingRequestStateToString(
139 __in BOOTSTRAPPER_REQUEST_STATE requestState
140 );
141
142LPCSTR LoggingRollbackOrExecute(
143 __in BOOL fRollback
144 );
145
146LPWSTR LoggingStringOrUnknownIfNull(
147 __in LPCWSTR wz
148 );
149
150
151#if defined(__cplusplus)
152}
153#endif
diff --git a/src/burn/engine/manifest.cpp b/src/burn/engine/manifest.cpp
new file mode 100644
index 00000000..b1740083
--- /dev/null
+++ b/src/burn/engine/manifest.cpp
@@ -0,0 +1,164 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6static HRESULT ParseFromXml(
7 __in IXMLDOMDocument* pixdDocument,
8 __in BURN_ENGINE_STATE* pEngineState
9 );
10
11// function definitions
12
13extern "C" HRESULT ManifestLoadXmlFromFile(
14 __in LPCWSTR wzPath,
15 __in BURN_ENGINE_STATE* pEngineState
16 )
17{
18 HRESULT hr = S_OK;
19 IXMLDOMDocument* pixdDocument = NULL;
20
21 // load xml document
22 hr = XmlLoadDocumentFromFile(wzPath, &pixdDocument);
23 ExitOnFailure(hr, "Failed to load manifest as XML document.");
24
25 hr = ParseFromXml(pixdDocument, pEngineState);
26
27LExit:
28 ReleaseObject(pixdDocument);
29
30 return hr;
31}
32
33extern "C" HRESULT ManifestLoadXmlFromBuffer(
34 __in_bcount(cbBuffer) BYTE* pbBuffer,
35 __in SIZE_T cbBuffer,
36 __in BURN_ENGINE_STATE* pEngineState
37 )
38{
39 HRESULT hr = S_OK;
40 IXMLDOMDocument* pixdDocument = NULL;
41
42 // load xml document
43 hr = XmlLoadDocumentFromBuffer(pbBuffer, cbBuffer, &pixdDocument);
44 ExitOnFailure(hr, "Failed to load manifest as XML document.");
45
46 hr = ParseFromXml(pixdDocument, pEngineState);
47
48LExit:
49 ReleaseObject(pixdDocument);
50
51 return hr;
52}
53
54static HRESULT ParseFromXml(
55 __in IXMLDOMDocument* pixdDocument,
56 __in BURN_ENGINE_STATE* pEngineState
57 )
58{
59 HRESULT hr = S_OK;
60 IXMLDOMElement* pixeBundle = NULL;
61 IXMLDOMNode* pixnLog = NULL;
62 IXMLDOMNode* pixnChain = NULL;
63
64 // get bundle element
65 hr = pixdDocument->get_documentElement(&pixeBundle);
66 ExitOnFailure(hr, "Failed to get bundle element.");
67
68 // parse the log element, if present.
69 hr = XmlSelectSingleNode(pixeBundle, L"Log", &pixnLog);
70 ExitOnFailure(hr, "Failed to get Log element.");
71
72 if (S_OK == hr)
73 {
74 hr = XmlGetAttributeEx(pixnLog, L"PathVariable", &pEngineState->log.sczPathVariable);
75 if (E_NOTFOUND != hr)
76 {
77 ExitOnFailure(hr, "Failed to get Log/@PathVariable.");
78 }
79
80 hr = XmlGetAttributeEx(pixnLog, L"Prefix", &pEngineState->log.sczPrefix);
81 ExitOnFailure(hr, "Failed to get Log/@Prefix attribute.");
82
83 hr = XmlGetAttributeEx(pixnLog, L"Extension", &pEngineState->log.sczExtension);
84 ExitOnFailure(hr, "Failed to get Log/@Extension attribute.");
85 }
86
87 // get the chain element
88 hr = XmlSelectSingleNode(pixeBundle, L"Chain", &pixnChain);
89 ExitOnFailure(hr, "Failed to get chain element.");
90
91 if (S_OK == hr)
92 {
93 // parse disable rollback
94 hr = XmlGetYesNoAttribute(pixnChain, L"DisableRollback", &pEngineState->fDisableRollback);
95 if (E_NOTFOUND != hr)
96 {
97 ExitOnFailure(hr, "Failed to get Chain/@DisableRollback");
98 }
99
100 // parse disable system restore
101 hr = XmlGetYesNoAttribute(pixnChain, L"DisableSystemRestore", &pEngineState->fDisableSystemRestore);
102 if (E_NOTFOUND != hr)
103 {
104 ExitOnFailure(hr, "Failed to get Chain/@DisableSystemRestore");
105 }
106
107 // parse parallel cache
108 hr = XmlGetYesNoAttribute(pixnChain, L"ParallelCache", &pEngineState->fParallelCacheAndExecute);
109 if (E_NOTFOUND != hr)
110 {
111 ExitOnFailure(hr, "Failed to get Chain/@ParallelCache");
112 }
113 }
114
115 // parse built-in condition
116 hr = ConditionGlobalParseFromXml(&pEngineState->condition, pixeBundle);
117 ExitOnFailure(hr, "Failed to parse global condition.");
118
119 // parse variables
120 hr = VariablesParseFromXml(&pEngineState->variables, pixeBundle);
121 ExitOnFailure(hr, "Failed to parse variables.");
122
123 // parse user experience
124 hr = UserExperienceParseFromXml(&pEngineState->userExperience, pixeBundle);
125 ExitOnFailure(hr, "Failed to parse user experience.");
126
127 // parse extensions
128 hr = BurnExtensionParseFromXml(&pEngineState->extensions, &pEngineState->userExperience.payloads, pixeBundle);
129 ExitOnFailure(hr, "Failed to parse extensions.");
130
131 // parse searches
132 hr = SearchesParseFromXml(&pEngineState->searches, &pEngineState->extensions, pixeBundle);
133 ExitOnFailure(hr, "Failed to parse searches.");
134
135 // parse registration
136 hr = RegistrationParseFromXml(&pEngineState->registration, pixeBundle);
137 ExitOnFailure(hr, "Failed to parse registration.");
138
139 // parse update
140 hr = UpdateParseFromXml(&pEngineState->update, pixeBundle);
141 ExitOnFailure(hr, "Failed to parse update.");
142
143 // parse containers
144 hr = ContainersParseFromXml(&pEngineState->containers, pixeBundle);
145 ExitOnFailure(hr, "Failed to parse containers.");
146
147 // parse payloads
148 hr = PayloadsParseFromXml(&pEngineState->payloads, &pEngineState->containers, &pEngineState->layoutPayloads, pixeBundle);
149 ExitOnFailure(hr, "Failed to parse payloads.");
150
151 // parse packages
152 hr = PackagesParseFromXml(&pEngineState->packages, &pEngineState->payloads, pixeBundle);
153 ExitOnFailure(hr, "Failed to parse packages.");
154
155 // parse approved exes for elevation
156 hr = ApprovedExesParseFromXml(&pEngineState->approvedExes, pixeBundle);
157 ExitOnFailure(hr, "Failed to parse approved exes.");
158
159LExit:
160 ReleaseObject(pixnChain);
161 ReleaseObject(pixnLog);
162 ReleaseObject(pixeBundle);
163 return hr;
164}
diff --git a/src/burn/engine/manifest.h b/src/burn/engine/manifest.h
new file mode 100644
index 00000000..8c527279
--- /dev/null
+++ b/src/burn/engine/manifest.h
@@ -0,0 +1,28 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5interface IBurnPayload; // forward declare.
6
7#if defined(__cplusplus)
8extern "C" {
9#endif
10
11
12// function declarations
13
14HRESULT ManifestLoadXmlFromFile(
15 __in LPCWSTR wzPath,
16 __in BURN_ENGINE_STATE* pEngineState
17 );
18
19HRESULT ManifestLoadXmlFromBuffer(
20 __in_bcount(cbBuffer) BYTE* pbBuffer,
21 __in SIZE_T cbBuffer,
22 __in BURN_ENGINE_STATE* pEngineState
23 );
24
25
26#if defined(__cplusplus)
27}
28#endif
diff --git a/src/burn/engine/msiengine.cpp b/src/burn/engine/msiengine.cpp
new file mode 100644
index 00000000..3e96e5f9
--- /dev/null
+++ b/src/burn/engine/msiengine.cpp
@@ -0,0 +1,2035 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// constants
7
8
9// structs
10
11
12
13// internal function declarations
14
15static HRESULT ParseRelatedMsiFromXml(
16 __in IXMLDOMNode* pixnRelatedMsi,
17 __in BURN_RELATED_MSI* pRelatedMsi
18 );
19static HRESULT EvaluateActionStateConditions(
20 __in BURN_VARIABLES* pVariables,
21 __in_z_opt LPCWSTR sczAddLocalCondition,
22 __in_z_opt LPCWSTR sczAddSourceCondition,
23 __in_z_opt LPCWSTR sczAdvertiseCondition,
24 __out BOOTSTRAPPER_FEATURE_STATE* pState
25 );
26static HRESULT CalculateFeatureAction(
27 __in BOOTSTRAPPER_FEATURE_STATE currentState,
28 __in BOOTSTRAPPER_FEATURE_STATE requestedState,
29 __in BOOL fRepair,
30 __out BOOTSTRAPPER_FEATURE_ACTION* pFeatureAction,
31 __inout BOOL* pfDelta
32 );
33static HRESULT EscapePropertyArgumentString(
34 __in LPCWSTR wzProperty,
35 __inout_z LPWSTR* psczEscapedValue,
36 __in BOOL fZeroOnRealloc
37 );
38static HRESULT ConcatFeatureActionProperties(
39 __in BURN_PACKAGE* pPackage,
40 __in BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions,
41 __inout_z LPWSTR* psczArguments
42 );
43static HRESULT ConcatPatchProperty(
44 __in BURN_PACKAGE* pPackage,
45 __in BOOL fRollback,
46 __inout_z LPWSTR* psczArguments
47 );
48static void RegisterSourceDirectory(
49 __in BURN_PACKAGE* pPackage,
50 __in_z LPCWSTR wzCacheDirectory
51 );
52
53
54// function definitions
55
56extern "C" HRESULT MsiEngineParsePackageFromXml(
57 __in IXMLDOMNode* pixnMsiPackage,
58 __in BURN_PACKAGE* pPackage
59 )
60{
61 HRESULT hr = S_OK;
62 IXMLDOMNodeList* pixnNodes = NULL;
63 IXMLDOMNode* pixnNode = NULL;
64 DWORD cNodes = 0;
65 LPWSTR scz = NULL;
66
67 // @ProductCode
68 hr = XmlGetAttributeEx(pixnMsiPackage, L"ProductCode", &pPackage->Msi.sczProductCode);
69 ExitOnFailure(hr, "Failed to get @ProductCode.");
70
71 // @Language
72 hr = XmlGetAttributeNumber(pixnMsiPackage, L"Language", &pPackage->Msi.dwLanguage);
73 ExitOnFailure(hr, "Failed to get @Language.");
74
75 // @Version
76 hr = XmlGetAttributeEx(pixnMsiPackage, L"Version", &scz);
77 ExitOnFailure(hr, "Failed to get @Version.");
78
79 hr = VerParseVersion(scz, 0, FALSE, &pPackage->Msi.pVersion);
80 ExitOnFailure(hr, "Failed to parse @Version: %ls", scz);
81
82 if (pPackage->Msi.pVersion->fInvalid)
83 {
84 LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz);
85 }
86
87 // @UpgradeCode
88 hr = XmlGetAttributeEx(pixnMsiPackage, L"UpgradeCode", &pPackage->Msi.sczUpgradeCode);
89 if (E_NOTFOUND != hr)
90 {
91 ExitOnFailure(hr, "Failed to get @UpgradeCode.");
92 }
93
94 // select feature nodes
95 hr = XmlSelectNodes(pixnMsiPackage, L"MsiFeature", &pixnNodes);
96 ExitOnFailure(hr, "Failed to select feature nodes.");
97
98 // get feature node count
99 hr = pixnNodes->get_length((long*)&cNodes);
100 ExitOnFailure(hr, "Failed to get feature node count.");
101
102 if (cNodes)
103 {
104 // allocate memory for features
105 pPackage->Msi.rgFeatures = (BURN_MSIFEATURE*)MemAlloc(sizeof(BURN_MSIFEATURE) * cNodes, TRUE);
106 ExitOnNull(pPackage->Msi.rgFeatures, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSI feature structs.");
107
108 pPackage->Msi.cFeatures = cNodes;
109
110 // parse feature elements
111 for (DWORD i = 0; i < cNodes; ++i)
112 {
113 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
114
115 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
116 ExitOnFailure(hr, "Failed to get next node.");
117
118 // @Id
119 hr = XmlGetAttributeEx(pixnNode, L"Id", &pFeature->sczId);
120 ExitOnFailure(hr, "Failed to get @Id.");
121
122 // @AddLocalCondition
123 hr = XmlGetAttributeEx(pixnNode, L"AddLocalCondition", &pFeature->sczAddLocalCondition);
124 if (E_NOTFOUND != hr)
125 {
126 ExitOnFailure(hr, "Failed to get @AddLocalCondition.");
127 }
128
129 // @AddSourceCondition
130 hr = XmlGetAttributeEx(pixnNode, L"AddSourceCondition", &pFeature->sczAddSourceCondition);
131 if (E_NOTFOUND != hr)
132 {
133 ExitOnFailure(hr, "Failed to get @AddSourceCondition.");
134 }
135
136 // @AdvertiseCondition
137 hr = XmlGetAttributeEx(pixnNode, L"AdvertiseCondition", &pFeature->sczAdvertiseCondition);
138 if (E_NOTFOUND != hr)
139 {
140 ExitOnFailure(hr, "Failed to get @AdvertiseCondition.");
141 }
142
143 // @RollbackAddLocalCondition
144 hr = XmlGetAttributeEx(pixnNode, L"RollbackAddLocalCondition", &pFeature->sczRollbackAddLocalCondition);
145 if (E_NOTFOUND != hr)
146 {
147 ExitOnFailure(hr, "Failed to get @RollbackAddLocalCondition.");
148 }
149
150 // @RollbackAddSourceCondition
151 hr = XmlGetAttributeEx(pixnNode, L"RollbackAddSourceCondition", &pFeature->sczRollbackAddSourceCondition);
152 if (E_NOTFOUND != hr)
153 {
154 ExitOnFailure(hr, "Failed to get @RollbackAddSourceCondition.");
155 }
156
157 // @RollbackAdvertiseCondition
158 hr = XmlGetAttributeEx(pixnNode, L"RollbackAdvertiseCondition", &pFeature->sczRollbackAdvertiseCondition);
159 if (E_NOTFOUND != hr)
160 {
161 ExitOnFailure(hr, "Failed to get @RollbackAdvertiseCondition.");
162 }
163
164 // prepare next iteration
165 ReleaseNullObject(pixnNode);
166 }
167 }
168
169 ReleaseNullObject(pixnNodes); // done with the MsiFeature elements.
170
171 hr = MsiEngineParsePropertiesFromXml(pixnMsiPackage, &pPackage->Msi.rgProperties, &pPackage->Msi.cProperties);
172 ExitOnFailure(hr, "Failed to parse properties from XML.");
173
174 // select related MSI nodes
175 hr = XmlSelectNodes(pixnMsiPackage, L"RelatedPackage", &pixnNodes);
176 ExitOnFailure(hr, "Failed to select related MSI nodes.");
177
178 // get related MSI node count
179 hr = pixnNodes->get_length((long*)&cNodes);
180 ExitOnFailure(hr, "Failed to get related MSI node count.");
181
182 if (cNodes)
183 {
184 // allocate memory for related MSIs
185 pPackage->Msi.rgRelatedMsis = (BURN_RELATED_MSI*)MemAlloc(sizeof(BURN_RELATED_MSI) * cNodes, TRUE);
186 ExitOnNull(pPackage->Msi.rgRelatedMsis, hr, E_OUTOFMEMORY, "Failed to allocate memory for related MSI structs.");
187
188 pPackage->Msi.cRelatedMsis = cNodes;
189
190 // parse related MSI elements
191 for (DWORD i = 0; i < cNodes; ++i)
192 {
193 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
194 ExitOnFailure(hr, "Failed to get next node.");
195
196 // parse related MSI element
197 hr = ParseRelatedMsiFromXml(pixnNode, &pPackage->Msi.rgRelatedMsis[i]);
198 ExitOnFailure(hr, "Failed to parse related MSI element.");
199
200 // prepare next iteration
201 ReleaseNullObject(pixnNode);
202 }
203 }
204
205 ReleaseNullObject(pixnNodes); // done with the RelatedPackage elements.
206
207 // Select slipstream MSP nodes.
208 hr = XmlSelectNodes(pixnMsiPackage, L"SlipstreamMsp", &pixnNodes);
209 ExitOnFailure(hr, "Failed to select related MSI nodes.");
210
211 hr = pixnNodes->get_length((long*)&cNodes);
212 ExitOnFailure(hr, "Failed to get related MSI node count.");
213
214 if (cNodes)
215 {
216 pPackage->Msi.rgSlipstreamMsps = reinterpret_cast<BURN_SLIPSTREAM_MSP*>(MemAlloc(sizeof(BURN_SLIPSTREAM_MSP) * cNodes, TRUE));
217 ExitOnNull(pPackage->Msi.rgSlipstreamMsps, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP packages.");
218
219 pPackage->Msi.rgsczSlipstreamMspPackageIds = reinterpret_cast<LPWSTR*>(MemAlloc(sizeof(LPWSTR*) * cNodes, TRUE));
220 ExitOnNull(pPackage->Msi.rgsczSlipstreamMspPackageIds, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP ids.");
221
222 pPackage->Msi.cSlipstreamMspPackages = cNodes;
223
224 // Parse slipstream MSP Ids.
225 for (DWORD i = 0; i < cNodes; ++i)
226 {
227 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
228 ExitOnFailure(hr, "Failed to get next slipstream MSP node.");
229
230 hr = XmlGetAttributeEx(pixnNode, L"Id", pPackage->Msi.rgsczSlipstreamMspPackageIds + i);
231 ExitOnFailure(hr, "Failed to parse slipstream MSP ids.");
232
233 ReleaseNullObject(pixnNode);
234 }
235 }
236
237 hr = S_OK;
238
239LExit:
240 ReleaseObject(pixnNodes);
241 ReleaseObject(pixnNode);
242 ReleaseStr(scz);
243
244 return hr;
245}
246
247extern "C" HRESULT MsiEngineParsePropertiesFromXml(
248 __in IXMLDOMNode* pixnPackage,
249 __out BURN_MSIPROPERTY** prgProperties,
250 __out DWORD* pcProperties
251 )
252{
253 HRESULT hr = S_OK;
254 IXMLDOMNodeList* pixnNodes = NULL;
255 IXMLDOMNode* pixnNode = NULL;
256 DWORD cNodes = 0;
257
258 BURN_MSIPROPERTY* pProperties = NULL;
259
260 // select property nodes
261 hr = XmlSelectNodes(pixnPackage, L"MsiProperty", &pixnNodes);
262 ExitOnFailure(hr, "Failed to select property nodes.");
263
264 // get property node count
265 hr = pixnNodes->get_length((long*)&cNodes);
266 ExitOnFailure(hr, "Failed to get property node count.");
267
268 if (cNodes)
269 {
270 // allocate memory for properties
271 pProperties = (BURN_MSIPROPERTY*)MemAlloc(sizeof(BURN_MSIPROPERTY) * cNodes, TRUE);
272 ExitOnNull(pProperties, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSI property structs.");
273
274 // parse property elements
275 for (DWORD i = 0; i < cNodes; ++i)
276 {
277 BURN_MSIPROPERTY* pProperty = &pProperties[i];
278
279 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
280 ExitOnFailure(hr, "Failed to get next node.");
281
282 // @Id
283 hr = XmlGetAttributeEx(pixnNode, L"Id", &pProperty->sczId);
284 ExitOnFailure(hr, "Failed to get @Id.");
285
286 // @Value
287 hr = XmlGetAttributeEx(pixnNode, L"Value", &pProperty->sczValue);
288 ExitOnFailure(hr, "Failed to get @Value.");
289
290 // @RollbackValue
291 hr = XmlGetAttributeEx(pixnNode, L"RollbackValue", &pProperty->sczRollbackValue);
292 if (E_NOTFOUND != hr)
293 {
294 ExitOnFailure(hr, "Failed to get @RollbackValue.");
295 }
296
297 // @Condition
298 hr = XmlGetAttributeEx(pixnNode, L"Condition", &pProperty->sczCondition);
299 if (E_NOTFOUND != hr)
300 {
301 ExitOnFailure(hr, "Failed to get @Condition.");
302 }
303
304 // prepare next iteration
305 ReleaseNullObject(pixnNode);
306 }
307 }
308
309 *pcProperties = cNodes;
310 *prgProperties = pProperties;
311 pProperties = NULL;
312
313 hr = S_OK;
314
315LExit:
316 ReleaseNullObject(pixnNodes);
317 ReleaseMem(pProperties);
318
319 return hr;
320}
321
322extern "C" void MsiEnginePackageUninitialize(
323 __in BURN_PACKAGE* pPackage
324 )
325{
326 ReleaseStr(pPackage->Msi.sczProductCode);
327 ReleaseStr(pPackage->Msi.sczUpgradeCode);
328
329 // free features
330 if (pPackage->Msi.rgFeatures)
331 {
332 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
333 {
334 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
335
336 ReleaseStr(pFeature->sczId);
337 ReleaseStr(pFeature->sczAddLocalCondition);
338 ReleaseStr(pFeature->sczAddSourceCondition);
339 ReleaseStr(pFeature->sczAdvertiseCondition);
340 ReleaseStr(pFeature->sczRollbackAddLocalCondition);
341 ReleaseStr(pFeature->sczRollbackAddSourceCondition);
342 ReleaseStr(pFeature->sczRollbackAdvertiseCondition);
343 }
344 MemFree(pPackage->Msi.rgFeatures);
345 }
346
347 // free properties
348 if (pPackage->Msi.rgProperties)
349 {
350 for (DWORD i = 0; i < pPackage->Msi.cProperties; ++i)
351 {
352 BURN_MSIPROPERTY* pProperty = &pPackage->Msi.rgProperties[i];
353
354 ReleaseStr(pProperty->sczId);
355 ReleaseStr(pProperty->sczValue);
356 ReleaseStr(pProperty->sczRollbackValue);
357 ReleaseStr(pProperty->sczCondition);
358 }
359 MemFree(pPackage->Msi.rgProperties);
360 }
361
362 // free related MSIs
363 if (pPackage->Msi.rgRelatedMsis)
364 {
365 for (DWORD i = 0; i < pPackage->Msi.cRelatedMsis; ++i)
366 {
367 BURN_RELATED_MSI* pRelatedMsi = &pPackage->Msi.rgRelatedMsis[i];
368
369 ReleaseStr(pRelatedMsi->sczUpgradeCode);
370 ReleaseMem(pRelatedMsi->rgdwLanguages);
371 }
372 MemFree(pPackage->Msi.rgRelatedMsis);
373 }
374
375 // free slipstream MSPs
376 if (pPackage->Msi.rgsczSlipstreamMspPackageIds)
377 {
378 for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i)
379 {
380 ReleaseStr(pPackage->Msi.rgsczSlipstreamMspPackageIds[i]);
381 }
382
383 MemFree(pPackage->Msi.rgsczSlipstreamMspPackageIds);
384 }
385
386 if (pPackage->Msi.rgSlipstreamMsps)
387 {
388 MemFree(pPackage->Msi.rgSlipstreamMsps);
389 }
390
391 if (pPackage->Msi.rgChainedPatches)
392 {
393 MemFree(pPackage->Msi.rgChainedPatches);
394 }
395
396 // clear struct
397 memset(&pPackage->Msi, 0, sizeof(pPackage->Msi));
398}
399
400extern "C" HRESULT MsiEngineDetectInitialize(
401 __in BURN_PACKAGES* pPackages
402 )
403{
404 AssertSz(pPackages->cPatchInfo, "MsiEngineDetectInitialize() should only be called if there are MSP packages.");
405
406 HRESULT hr = S_OK;
407
408 // Add target products for slipstream MSIs that weren't detected.
409 for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage)
410 {
411 BURN_PACKAGE* pMsiPackage = pPackages->rgPackages + iPackage;
412 if (BURN_PACKAGE_TYPE_MSI == pMsiPackage->type)
413 {
414 for (DWORD j = 0; j < pMsiPackage->Msi.cSlipstreamMspPackages; ++j)
415 {
416 BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pMsiPackage->Msi.rgSlipstreamMsps + j;
417 Assert(pSlipstreamMsp->pMspPackage && BURN_PACKAGE_TYPE_MSP == pSlipstreamMsp->pMspPackage->type);
418
419 if (pSlipstreamMsp->pMspPackage && BURN_PACKAGE_INVALID_PATCH_INDEX == pSlipstreamMsp->dwMsiChainedPatchIndex)
420 {
421 hr = MspEngineAddMissingSlipstreamTarget(pMsiPackage, pSlipstreamMsp);
422 ExitOnFailure(hr, "Failed to add slipstreamed target product code to package: %ls", pSlipstreamMsp->pMspPackage->sczId);
423 }
424 }
425 }
426 }
427
428LExit:
429 return hr;
430}
431
432extern "C" HRESULT MsiEngineDetectPackage(
433 __in BURN_PACKAGE* pPackage,
434 __in BURN_USER_EXPERIENCE* pUserExperience
435 )
436{
437 Trace(REPORT_STANDARD, "Detecting MSI package 0x%p", pPackage);
438
439 HRESULT hr = S_OK;
440 int nCompareResult = 0;
441 LPWSTR sczInstalledVersion = NULL;
442 LPWSTR sczInstalledLanguage = NULL;
443 INSTALLSTATE installState = INSTALLSTATE_UNKNOWN;
444 BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
445 BOOTSTRAPPER_RELATED_OPERATION relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
446 WCHAR wzProductCode[MAX_GUID_CHARS + 1] = { };
447 VERUTIL_VERSION* pVersion = NULL;
448 UINT uLcid = 0;
449 BOOL fPerMachine = FALSE;
450
451 // detect self by product code
452 // TODO: what to do about MSIINSTALLCONTEXT_USERMANAGED?
453 hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
454 if (SUCCEEDED(hr))
455 {
456 hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pPackage->Msi.pInstalledVersion);
457 ExitOnFailure(hr, "Failed to parse installed version: '%ls' for ProductCode: %ls", sczInstalledVersion, pPackage->Msi.sczProductCode);
458
459 if (pPackage->Msi.pInstalledVersion->fInvalid)
460 {
461 LogId(REPORT_WARNING, MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION, pPackage->Msi.sczProductCode, sczInstalledVersion);
462 }
463
464 // compare versions
465 hr = VerCompareParsedVersions(pPackage->Msi.pVersion, pPackage->Msi.pInstalledVersion, &nCompareResult);
466 ExitOnFailure(hr, "Failed to compare version '%ls' to installed version: '%ls'", pPackage->Msi.pVersion->sczVersion, pPackage->Msi.pInstalledVersion->sczVersion);
467
468 if (nCompareResult < 0)
469 {
470 operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE;
471 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED;
472 }
473 else
474 {
475 if (nCompareResult > 0)
476 {
477 operation = BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE;
478 }
479
480 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT;
481 }
482
483 // Report related MSI package to BA.
484 if (BOOTSTRAPPER_RELATED_OPERATION_NONE != operation)
485 {
486 LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, pPackage->Msi.sczProductCode, LoggingPerMachineToString(pPackage->fPerMachine), pPackage->Msi.pInstalledVersion->sczVersion, pPackage->Msi.dwLanguage, LoggingRelatedOperationToString(operation));
487
488 hr = UserExperienceOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pPackage->Msi.sczUpgradeCode, pPackage->Msi.sczProductCode, pPackage->fPerMachine, pPackage->Msi.pInstalledVersion, operation);
489 ExitOnRootFailure(hr, "BA aborted detect related MSI package.");
490 }
491 }
492 else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr || HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr) // package not present.
493 {
494 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
495 hr = S_OK;
496 }
497 else
498 {
499 ExitOnFailure(hr, "Failed to get product information for ProductCode: %ls", pPackage->Msi.sczProductCode);
500 }
501
502 // detect related packages by upgrade code
503 for (DWORD i = 0; i < pPackage->Msi.cRelatedMsis; ++i)
504 {
505 BURN_RELATED_MSI* pRelatedMsi = &pPackage->Msi.rgRelatedMsis[i];
506
507 for (DWORD iProduct = 0; ; ++iProduct)
508 {
509 // get product
510 hr = WiuEnumRelatedProducts(pRelatedMsi->sczUpgradeCode, iProduct, wzProductCode);
511 if (E_NOMOREITEMS == hr)
512 {
513 hr = S_OK;
514 break;
515 }
516 ExitOnFailure(hr, "Failed to enum related products.");
517
518 // If we found ourselves, skip because saying that a package is related to itself is nonsensical.
519 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pPackage->Msi.sczProductCode, -1, wzProductCode, -1))
520 {
521 continue;
522 }
523
524 // get product version
525 hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
526 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr)
527 {
528 ExitOnFailure(hr, "Failed to get version for product in machine context: %ls", wzProductCode);
529 fPerMachine = TRUE;
530 }
531 else
532 {
533 hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
534 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr)
535 {
536 ExitOnFailure(hr, "Failed to get version for product in user unmanaged context: %ls", wzProductCode);
537 fPerMachine = FALSE;
538 }
539 else
540 {
541 hr = S_OK;
542 continue;
543 }
544 }
545
546 hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pVersion);
547 ExitOnFailure(hr, "Failed to parse related installed version: '%ls' for ProductCode: %ls", sczInstalledVersion, wzProductCode);
548
549 if (pVersion->fInvalid)
550 {
551 LogId(REPORT_WARNING, MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION, wzProductCode, sczInstalledVersion);
552 }
553
554 // compare versions
555 if (pRelatedMsi->fMinProvided)
556 {
557 hr = VerCompareParsedVersions(pVersion, pRelatedMsi->pMinVersion, &nCompareResult);
558 ExitOnFailure(hr, "Failed to compare related installed version '%ls' to related min version: '%ls'", pVersion->sczVersion, pRelatedMsi->pMinVersion->sczVersion);
559
560 if (pRelatedMsi->fMinInclusive ? (nCompareResult < 0) : (nCompareResult <= 0))
561 {
562 continue;
563 }
564 }
565
566 if (pRelatedMsi->fMaxProvided)
567 {
568 hr = VerCompareParsedVersions(pVersion, pRelatedMsi->pMaxVersion, &nCompareResult);
569 ExitOnFailure(hr, "Failed to compare related installed version '%ls' to related max version: '%ls'", pVersion->sczVersion, pRelatedMsi->pMaxVersion->sczVersion);
570
571 if (pRelatedMsi->fMaxInclusive ? (nCompareResult > 0) : (nCompareResult >= 0))
572 {
573 continue;
574 }
575 }
576
577 // Filter by language if necessary.
578 uLcid = 0; // always reset the found language.
579 if (pRelatedMsi->cLanguages)
580 {
581 // If there is a language to get, convert it into an LCID.
582 hr = WiuGetProductInfoEx(wzProductCode, NULL, fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_LANGUAGE, &sczInstalledLanguage);
583 if (SUCCEEDED(hr))
584 {
585 hr = StrStringToUInt32(sczInstalledLanguage, 0, &uLcid);
586 }
587
588 // Ignore related product where we can't read the language.
589 if (FAILED(hr))
590 {
591 LogErrorId(hr, MSG_FAILED_READ_RELATED_PACKAGE_LANGUAGE, wzProductCode, sczInstalledLanguage, NULL);
592
593 hr = S_OK;
594 continue;
595 }
596
597 BOOL fMatchedLcid = FALSE;
598 for (DWORD iLanguage = 0; iLanguage < pRelatedMsi->cLanguages; ++iLanguage)
599 {
600 if (uLcid == pRelatedMsi->rgdwLanguages[iLanguage])
601 {
602 fMatchedLcid = TRUE;
603 break;
604 }
605 }
606
607 // Skip the product if the language did not meet the inclusive/exclusive criteria.
608 if ((pRelatedMsi->fLangInclusive && !fMatchedLcid) || (!pRelatedMsi->fLangInclusive && fMatchedLcid))
609 {
610 continue;
611 }
612 }
613
614 // If this is a detect-only related package and we're not installed yet, then we'll assume a downgrade
615 // would take place since that is the overwhelmingly common use of detect-only related packages. If
616 // not detect-only then it's easy; we're clearly doing a major upgrade.
617 if (pRelatedMsi->fOnlyDetect)
618 {
619 // If we've already detected a major upgrade that trumps any guesses that the detect is a downgrade
620 // or even something else.
621 if (BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE == operation)
622 {
623 relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
624 }
625 // It can't be a downgrade if the upgrade codes aren't the same.
626 else if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == pPackage->currentState &&
627 pPackage->Msi.sczUpgradeCode && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pPackage->Msi.sczUpgradeCode, -1, pRelatedMsi->sczUpgradeCode, -1))
628 {
629 relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE;
630 operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE;
631 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE;
632 }
633 else // we're already on the machine so the detect-only *must* be for detection purposes only.
634 {
635 relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
636 }
637 }
638 else
639 {
640 relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE;
641 operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE;
642 }
643
644 LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, wzProductCode, LoggingPerMachineToString(fPerMachine), pVersion->sczVersion, uLcid, LoggingRelatedOperationToString(relatedMsiOperation));
645
646 // Pass to BA.
647 hr = UserExperienceOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pRelatedMsi->sczUpgradeCode, wzProductCode, fPerMachine, pVersion, relatedMsiOperation);
648 ExitOnRootFailure(hr, "BA aborted detect related MSI package.");
649 }
650 }
651
652 // detect features
653 if (pPackage->Msi.cFeatures)
654 {
655 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
656 {
657 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
658
659 // Try to detect features state if the product is present on the machine.
660 if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT <= pPackage->currentState)
661 {
662 hr = WiuQueryFeatureState(pPackage->Msi.sczProductCode, pFeature->sczId, &installState);
663 ExitOnFailure(hr, "Failed to query feature state.");
664
665 if (INSTALLSTATE_UNKNOWN == installState) // in case of an upgrade a feature could be removed.
666 {
667 installState = INSTALLSTATE_ABSENT;
668 }
669 }
670 else // MSI not installed then the features can't be either.
671 {
672 installState = INSTALLSTATE_ABSENT;
673 }
674
675 // set current state
676 switch (installState)
677 {
678 case INSTALLSTATE_ABSENT:
679 pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_ABSENT;
680 break;
681 case INSTALLSTATE_ADVERTISED:
682 pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_ADVERTISED;
683 break;
684 case INSTALLSTATE_LOCAL:
685 pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_LOCAL;
686 break;
687 case INSTALLSTATE_SOURCE:
688 pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_SOURCE;
689 break;
690 default:
691 hr = E_UNEXPECTED;
692 ExitOnRootFailure(hr, "Invalid state value.");
693 }
694
695 // Pass to BA.
696 hr = UserExperienceOnDetectMsiFeature(pUserExperience, pPackage->sczId, pFeature->sczId, pFeature->currentState);
697 ExitOnRootFailure(hr, "BA aborted detect MSI feature.");
698 }
699 }
700
701 if (pPackage->fCanAffectRegistration)
702 {
703 pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
704 }
705
706LExit:
707 ReleaseStr(sczInstalledLanguage);
708 ReleaseStr(sczInstalledVersion);
709 ReleaseVerutilVersion(pVersion);
710
711 return hr;
712}
713
714extern "C" HRESULT MsiEnginePlanInitializePackage(
715 __in BURN_PACKAGE* pPackage,
716 __in BURN_VARIABLES* pVariables,
717 __in BURN_USER_EXPERIENCE* pUserExperience
718 )
719{
720 HRESULT hr = S_OK;
721
722 if (pPackage->Msi.cFeatures)
723 {
724 // get feature request states
725 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
726 {
727 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
728
729 // Evaluate feature conditions.
730 hr = EvaluateActionStateConditions(pVariables, pFeature->sczAddLocalCondition, pFeature->sczAddSourceCondition, pFeature->sczAdvertiseCondition, &pFeature->defaultRequested);
731 ExitOnFailure(hr, "Failed to evaluate requested state conditions.");
732
733 hr = EvaluateActionStateConditions(pVariables, pFeature->sczRollbackAddLocalCondition, pFeature->sczRollbackAddSourceCondition, pFeature->sczRollbackAdvertiseCondition, &pFeature->expectedState);
734 ExitOnFailure(hr, "Failed to evaluate expected state conditions.");
735
736 // Remember the default feature requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it.
737 pFeature->requested = pFeature->defaultRequested;
738
739 // Send plan MSI feature message to BA.
740 hr = UserExperienceOnPlanMsiFeature(pUserExperience, pPackage->sczId, pFeature->sczId, &pFeature->requested);
741 ExitOnRootFailure(hr, "BA aborted plan MSI feature.");
742 }
743 }
744
745LExit:
746 return hr;
747}
748
749//
750// PlanCalculate - calculates the execute and rollback state for the requested package state.
751//
752extern "C" HRESULT MsiEnginePlanCalculatePackage(
753 __in BURN_PACKAGE* pPackage,
754 __in BOOL fInsideMsiTransaction
755 )
756{
757 Trace(REPORT_STANDARD, "Planning MSI package 0x%p", pPackage);
758
759 HRESULT hr = S_OK;
760 VERUTIL_VERSION* pVersion = pPackage->Msi.pVersion;
761 VERUTIL_VERSION* pInstalledVersion = pPackage->Msi.pInstalledVersion;
762 int nCompareResult = 0;
763 BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE;
764 BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
765 BOOL fFeatureActionDelta = FALSE;
766 BOOL fRollbackFeatureActionDelta = FALSE;
767
768 if (pPackage->Msi.cFeatures)
769 {
770 // If the package is present and we're repairing it.
771 BOOL fRepairingPackage = (BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested);
772
773 // plan features
774 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
775 {
776 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
777
778 // Calculate feature actions.
779 hr = CalculateFeatureAction(pFeature->currentState, pFeature->requested, fRepairingPackage, &pFeature->execute, &fFeatureActionDelta);
780 ExitOnFailure(hr, "Failed to calculate execute feature state.");
781
782 hr = CalculateFeatureAction(pFeature->requested, BOOTSTRAPPER_FEATURE_ACTION_NONE == pFeature->execute ? pFeature->expectedState : pFeature->currentState, FALSE, &pFeature->rollback, &fRollbackFeatureActionDelta);
783 ExitOnFailure(hr, "Failed to calculate rollback feature state.");
784 }
785 }
786
787 // execute action
788 switch (pPackage->currentState)
789 {
790 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
791 case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
792 if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_MEND == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested)
793 {
794 hr = VerCompareParsedVersions(pVersion, pInstalledVersion, &nCompareResult);
795 ExitOnFailure(hr, "Failed to compare '%ls' to '%ls' for planning.", pVersion->sczVersion, pInstalledVersion->sczVersion);
796
797 // Take a look at the version and determine if this is a potential
798 // minor upgrade (same ProductCode newer ProductVersion), otherwise,
799 // there is a newer version so no work necessary.
800 if (nCompareResult > 0)
801 {
802 execute = BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE;
803 }
804 else if (BOOTSTRAPPER_REQUEST_STATE_MEND == pPackage->requested)
805 {
806 execute = BOOTSTRAPPER_ACTION_STATE_MEND;
807 }
808 else if (BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested)
809 {
810 execute = BOOTSTRAPPER_ACTION_STATE_REPAIR;
811 }
812 else
813 {
814 execute = fFeatureActionDelta ? BOOTSTRAPPER_ACTION_STATE_MODIFY : BOOTSTRAPPER_ACTION_STATE_NONE;
815 }
816 }
817 else if ((BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_CACHE == pPackage->requested) &&
818 pPackage->fUninstallable) // removing a package that can be removed.
819 {
820 execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
821 }
822 else if (BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested)
823 {
824 execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
825 }
826 else
827 {
828 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
829 }
830 break;
831
832 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
833 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
834 switch (pPackage->requested)
835 {
836 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
837 case BOOTSTRAPPER_REQUEST_STATE_MEND: __fallthrough;
838 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
839 execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
840 break;
841
842 default:
843 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
844 break;
845 }
846 break;
847
848 default:
849 hr = E_INVALIDARG;
850 ExitOnRootFailure(hr, "Invalid package current state result encountered during plan: %d", pPackage->currentState);
851 }
852
853 // Calculate the rollback action if there is an execute action.
854 if (BOOTSTRAPPER_ACTION_STATE_NONE != execute && !fInsideMsiTransaction)
855 {
856 switch (pPackage->currentState)
857 {
858 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
859 case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
860 switch (pPackage->requested)
861 {
862 case BOOTSTRAPPER_REQUEST_STATE_PRESENT:
863 rollback = fRollbackFeatureActionDelta ? BOOTSTRAPPER_ACTION_STATE_MODIFY : BOOTSTRAPPER_ACTION_STATE_NONE;
864 break;
865 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
866 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
867 break;
868 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
869 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
870 rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL;
871 break;
872 default:
873 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
874 break;
875 }
876 break;
877
878 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
879 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough;
880 // If the package is uninstallable and we requested to put the package on the machine then
881 // remove the package during rollback.
882 if (pPackage->fUninstallable &&
883 (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested ||
884 BOOTSTRAPPER_REQUEST_STATE_MEND == pPackage->requested ||
885 BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested))
886 {
887 rollback = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
888 }
889 else
890 {
891 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
892 }
893 break;
894
895 default:
896 hr = E_INVALIDARG;
897 ExitOnRootFailure(hr, "Invalid package detection result encountered.");
898 }
899 }
900
901 // return values
902 pPackage->execute = execute;
903 pPackage->rollback = rollback;
904
905LExit:
906 return hr;
907}
908
909//
910// PlanAdd - adds the calculated execute and rollback actions for the package.
911//
912extern "C" HRESULT MsiEnginePlanAddPackage(
913 __in BOOTSTRAPPER_DISPLAY display,
914 __in BURN_USER_EXPERIENCE* pUserExperience,
915 __in BURN_PACKAGE* pPackage,
916 __in BURN_PLAN* pPlan,
917 __in BURN_LOGGING* pLog,
918 __in BURN_VARIABLES* pVariables,
919 __in_opt HANDLE hCacheEvent
920 )
921{
922 HRESULT hr = S_OK;
923 BURN_EXECUTE_ACTION* pAction = NULL;
924 BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions = NULL;
925 BOOTSTRAPPER_FEATURE_ACTION* rgRollbackFeatureActions = NULL;
926
927 if (pPackage->Msi.cFeatures)
928 {
929 // Allocate and populate array for feature actions.
930 rgFeatureActions = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(sizeof(BOOTSTRAPPER_FEATURE_ACTION) * pPackage->Msi.cFeatures, TRUE);
931 ExitOnNull(rgFeatureActions, hr, E_OUTOFMEMORY, "Failed to allocate memory for feature actions.");
932
933 rgRollbackFeatureActions = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(sizeof(BOOTSTRAPPER_FEATURE_ACTION) * pPackage->Msi.cFeatures, TRUE);
934 ExitOnNull(rgRollbackFeatureActions, hr, E_OUTOFMEMORY, "Failed to allocate memory for rollback feature actions.");
935
936 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
937 {
938 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
939
940 // calculate feature actions
941 rgFeatureActions[i] = pFeature->execute;
942 rgRollbackFeatureActions[i] = pFeature->rollback;
943 }
944 }
945
946 // add wait for cache
947 if (hCacheEvent)
948 {
949 hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent);
950 ExitOnFailure(hr, "Failed to plan package cache syncpoint");
951 }
952
953 hr = DependencyPlanPackage(NULL, pPackage, pPlan);
954 ExitOnFailure(hr, "Failed to plan package dependency actions.");
955
956 // add rollback action
957 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
958 {
959 hr = PlanAppendRollbackAction(pPlan, &pAction);
960 ExitOnFailure(hr, "Failed to append rollback action.");
961
962 pAction->type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE;
963 pAction->msiPackage.pPackage = pPackage;
964 pAction->msiPackage.action = pPackage->rollback;
965 pAction->msiPackage.rgFeatures = rgRollbackFeatureActions;
966 rgRollbackFeatureActions = NULL;
967
968 hr = MsiEngineCalculateInstallUiLevel(display, pUserExperience, pPackage->sczId, FALSE, pAction->msiPackage.action,
969 &pAction->msiPackage.actionMsiProperty, &pAction->msiPackage.uiLevel, &pAction->msiPackage.fDisableExternalUiHandler);
970 ExitOnFailure(hr, "Failed to get msi ui options.");
971
972 LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors.
973 pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes;
974
975 // Plan a checkpoint between rollback and execute so that we always attempt
976 // rollback in the case that the MSI was not able to rollback itself (e.g.
977 // user pushes cancel after InstallFinalize).
978 hr = PlanExecuteCheckpoint(pPlan);
979 ExitOnFailure(hr, "Failed to append execute checkpoint.");
980 }
981
982 // add execute action
983 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
984 {
985 hr = PlanAppendExecuteAction(pPlan, &pAction);
986 ExitOnFailure(hr, "Failed to append execute action.");
987
988 pAction->type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE;
989 pAction->msiPackage.pPackage = pPackage;
990 pAction->msiPackage.action = pPackage->execute;
991 pAction->msiPackage.rgFeatures = rgFeatureActions;
992 rgFeatureActions = NULL;
993
994 hr = MsiEngineCalculateInstallUiLevel(display, pUserExperience, pPackage->sczId, TRUE, pAction->msiPackage.action,
995 &pAction->msiPackage.actionMsiProperty, &pAction->msiPackage.uiLevel, &pAction->msiPackage.fDisableExternalUiHandler);
996 ExitOnFailure(hr, "Failed to get msi ui options.");
997
998 LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors.
999 pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes;
1000 }
1001
1002LExit:
1003 ReleaseMem(rgFeatureActions);
1004 ReleaseMem(rgRollbackFeatureActions);
1005
1006 return hr;
1007}
1008
1009extern "C" HRESULT MsiEngineBeginTransaction(
1010 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
1011 )
1012{
1013 HRESULT hr = S_OK;
1014 MSIHANDLE hTransactionHandle = NULL;
1015 HANDLE hChangeOfOwnerEvent = NULL;
1016
1017 LogId(REPORT_STANDARD, MSG_MSI_TRANSACTION_BEGIN, pRollbackBoundary->sczId);
1018
1019 hr = WiuBeginTransaction(pRollbackBoundary->sczId, 0, &hTransactionHandle, &hChangeOfOwnerEvent, WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE, pRollbackBoundary->sczLogPath);
1020
1021 if (HRESULT_FROM_WIN32(ERROR_ROLLBACK_DISABLED) == hr)
1022 {
1023 LogId(REPORT_ERROR, MSG_MSI_TRANSACTIONS_DISABLED);
1024 }
1025
1026 ExitOnFailure(hr, "Failed to begin an MSI transaction");
1027
1028LExit:
1029 ReleaseMsi(hTransactionHandle);
1030 ReleaseHandle(hChangeOfOwnerEvent);
1031
1032 return hr;
1033}
1034
1035extern "C" HRESULT MsiEngineCommitTransaction(
1036 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
1037 )
1038{
1039 HRESULT hr = S_OK;
1040
1041 LogId(REPORT_STANDARD, MSG_MSI_TRANSACTION_COMMIT, pRollbackBoundary->sczId);
1042
1043 hr = WiuEndTransaction(MSITRANSACTIONSTATE_COMMIT, WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE, pRollbackBoundary->sczLogPath);
1044 ExitOnFailure(hr, "Failed to commit the MSI transaction");
1045
1046LExit:
1047
1048 return hr;
1049}
1050
1051extern "C" HRESULT MsiEngineRollbackTransaction(
1052 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
1053 )
1054{
1055 HRESULT hr = S_OK;
1056
1057 LogId(REPORT_WARNING, MSG_MSI_TRANSACTION_ROLLBACK, pRollbackBoundary->sczId);
1058
1059 hr = WiuEndTransaction(MSITRANSACTIONSTATE_ROLLBACK, WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE, pRollbackBoundary->sczLogPath);
1060 ExitOnFailure(hr, "Failed to rollback the MSI transaction");
1061
1062LExit:
1063
1064 return hr;
1065}
1066
1067extern "C" HRESULT MsiEngineExecutePackage(
1068 __in_opt HWND hwndParent,
1069 __in BURN_EXECUTE_ACTION* pExecuteAction,
1070 __in BURN_VARIABLES* pVariables,
1071 __in BOOL fRollback,
1072 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
1073 __in LPVOID pvContext,
1074 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
1075 )
1076{
1077 HRESULT hr = S_OK;
1078 WIU_MSI_EXECUTE_CONTEXT context = { };
1079 WIU_RESTART restart = WIU_RESTART_NONE;
1080
1081 LPWSTR sczInstalledVersion = NULL;
1082 LPWSTR sczCachedDirectory = NULL;
1083 LPWSTR sczMsiPath = NULL;
1084 LPWSTR sczProperties = NULL;
1085 LPWSTR sczObfuscatedProperties = NULL;
1086 BURN_PACKAGE* pPackage = pExecuteAction->msiPackage.pPackage;
1087 BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload;
1088
1089 // During rollback, if the package is already in the rollback state we expect don't
1090 // touch it again.
1091 if (fRollback)
1092 {
1093 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->msiPackage.action)
1094 {
1095 hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
1096 if (FAILED(hr)) // package not present.
1097 {
1098 LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_ABSENT));
1099
1100 hr = S_OK;
1101 ExitFunction();
1102 }
1103 }
1104 else if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->msiPackage.action)
1105 {
1106 hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
1107 if (SUCCEEDED(hr)) // package present.
1108 {
1109 LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_PRESENT));
1110
1111 hr = S_OK;
1112 ExitFunction();
1113 }
1114
1115 hr = S_OK;
1116 }
1117 }
1118
1119 // Default to "verbose" logging and set extra debug mode only if explicitly required.
1120 DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE;
1121
1122 if (pExecuteAction->msiPackage.dwLoggingAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG)
1123 {
1124 dwLogMode |= INSTALLLOGMODE_EXTRADEBUG;
1125 }
1126
1127 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL != pExecuteAction->msiPackage.action)
1128 {
1129 // get cached MSI path
1130 hr = CacheGetCompletedPath(pPackage->fPerMachine, pPackage->sczCacheId, &sczCachedDirectory);
1131 ExitOnFailure(hr, "Failed to get cached path for package: %ls", pPackage->sczId);
1132
1133 // Best effort to set the execute package cache folder variable.
1134 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE);
1135
1136 hr = PathConcat(sczCachedDirectory, pPackagePayload->sczFilePath, &sczMsiPath);
1137 ExitOnFailure(hr, "Failed to build MSI path.");
1138 }
1139
1140 // Best effort to set the execute package action variable.
1141 VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->msiPackage.action, TRUE);
1142
1143 // Wire up the external UI handler and logging.
1144 if (pExecuteAction->msiPackage.fDisableExternalUiHandler)
1145 {
1146 hr = WiuInitializeInternalUI(pExecuteAction->msiPackage.uiLevel, hwndParent, &context);
1147 ExitOnFailure(hr, "Failed to initialize internal UI for MSI package.");
1148 }
1149 else
1150 {
1151 hr = WiuInitializeExternalUI(pfnMessageHandler, pExecuteAction->msiPackage.uiLevel, hwndParent, pvContext, fRollback, &context);
1152 ExitOnFailure(hr, "Failed to initialize external UI handler.");
1153 }
1154
1155 if (pExecuteAction->msiPackage.sczLogPath && *pExecuteAction->msiPackage.sczLogPath)
1156 {
1157 hr = WiuEnableLog(dwLogMode, pExecuteAction->msiPackage.sczLogPath, 0);
1158 ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pPackage->sczId, pExecuteAction->msiPackage.sczLogPath);
1159 }
1160
1161 // set up properties
1162 hr = MsiEngineConcatProperties(pPackage->Msi.rgProperties, pPackage->Msi.cProperties, pVariables, fRollback, &sczProperties, FALSE);
1163 ExitOnFailure(hr, "Failed to add properties to argument string.");
1164
1165 hr = MsiEngineConcatProperties(pPackage->Msi.rgProperties, pPackage->Msi.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE);
1166 ExitOnFailure(hr, "Failed to add obfuscated properties to argument string.");
1167
1168 // add feature action properties
1169 hr = ConcatFeatureActionProperties(pPackage, pExecuteAction->msiPackage.rgFeatures, &sczProperties);
1170 ExitOnFailure(hr, "Failed to add feature action properties to argument string.");
1171
1172 hr = ConcatFeatureActionProperties(pPackage, pExecuteAction->msiPackage.rgFeatures, &sczObfuscatedProperties);
1173 ExitOnFailure(hr, "Failed to add feature action properties to obfuscated argument string.");
1174
1175 // add slipstream patch properties
1176 hr = ConcatPatchProperty(pPackage, fRollback, &sczProperties);
1177 ExitOnFailure(hr, "Failed to add patch properties to argument string.");
1178
1179 hr = ConcatPatchProperty(pPackage, fRollback, &sczObfuscatedProperties);
1180 ExitOnFailure(hr, "Failed to add patch properties to obfuscated argument string.");
1181
1182 hr = MsiEngineConcatActionProperty(pExecuteAction->msiPackage.actionMsiProperty, &sczProperties);
1183 ExitOnFailure(hr, "Failed to add action property to argument string.");
1184
1185 hr = MsiEngineConcatActionProperty(pExecuteAction->msiPackage.actionMsiProperty, &sczObfuscatedProperties);
1186 ExitOnFailure(hr, "Failed to add action property to obfuscated argument string.");
1187
1188 LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), sczMsiPath, sczObfuscatedProperties ? sczObfuscatedProperties : L"");
1189
1190 //
1191 // Do the actual action.
1192 //
1193 switch (pExecuteAction->msiPackage.action)
1194 {
1195 case BOOTSTRAPPER_ACTION_STATE_INSTALL:
1196 hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0);
1197 ExitOnFailure(hr, "Failed to add reboot suppression property on install.");
1198
1199 hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart);
1200 ExitOnFailure(hr, "Failed to install MSI package.");
1201
1202 RegisterSourceDirectory(pPackage, sczMsiPath);
1203 break;
1204
1205 case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE:
1206 // If feature selection is not enabled, then reinstall the existing features to ensure they get
1207 // updated.
1208 if (0 == pPackage->Msi.cFeatures)
1209 {
1210 hr = StrAllocConcatSecure(&sczProperties, L" REINSTALL=ALL", 0);
1211 ExitOnFailure(hr, "Failed to add reinstall all property on minor upgrade.");
1212 }
1213
1214 hr = StrAllocConcatSecure(&sczProperties, L" REINSTALLMODE=\"vomus\" REBOOT=ReallySuppress", 0);
1215 ExitOnFailure(hr, "Failed to add reinstall mode and reboot suppression properties on minor upgrade.");
1216
1217 hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart);
1218 ExitOnFailure(hr, "Failed to perform minor upgrade of MSI package.");
1219
1220 RegisterSourceDirectory(pPackage, sczMsiPath);
1221 break;
1222
1223 case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough;
1224 case BOOTSTRAPPER_ACTION_STATE_MEND: __fallthrough;
1225 case BOOTSTRAPPER_ACTION_STATE_REPAIR:
1226 {
1227 LPCWSTR wzReinstallAll = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action ||
1228 pPackage->Msi.cFeatures) ? L"" : L" REINSTALL=ALL";
1229 LPCWSTR wzReinstallMode = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action || BOOTSTRAPPER_ACTION_STATE_MEND == pExecuteAction->msiPackage.action) ? L"o" : L"e";
1230
1231 hr = StrAllocFormattedSecure(&sczProperties, L"%ls%ls REINSTALLMODE=\"cmus%ls\" REBOOT=ReallySuppress", sczProperties ? sczProperties : L"", wzReinstallAll, wzReinstallMode);
1232 ExitOnFailure(hr, "Failed to add reinstall mode and reboot suppression properties on repair.");
1233 }
1234
1235 // Ignore all dependencies, since the Burn engine already performed the check.
1236 hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES);
1237 ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties.");
1238
1239 hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart);
1240 ExitOnFailure(hr, "Failed to run maintenance mode for MSI package.");
1241 break;
1242
1243 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
1244 hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0);
1245 ExitOnFailure(hr, "Failed to add reboot suppression property on uninstall.");
1246
1247 // Ignore all dependencies, since the Burn engine already performed the check.
1248 hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES);
1249 ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties.");
1250
1251 hr = WiuConfigureProductEx(pPackage->Msi.sczProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, sczProperties, &restart);
1252 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr)
1253 {
1254 LogId(REPORT_STANDARD, MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE, pPackage->sczId);
1255 hr = S_OK;
1256 }
1257 ExitOnFailure(hr, "Failed to uninstall MSI package.");
1258 break;
1259 }
1260
1261LExit:
1262 WiuUninitializeExternalUI(&context);
1263
1264 StrSecureZeroFreeString(sczProperties);
1265 ReleaseStr(sczObfuscatedProperties);
1266 ReleaseStr(sczMsiPath);
1267 ReleaseStr(sczCachedDirectory);
1268 ReleaseStr(sczInstalledVersion);
1269
1270 switch (restart)
1271 {
1272 case WIU_RESTART_NONE:
1273 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
1274 break;
1275
1276 case WIU_RESTART_REQUIRED:
1277 *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
1278 break;
1279
1280 case WIU_RESTART_INITIATED:
1281 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
1282 break;
1283 }
1284
1285 // Best effort to clear the execute package cache folder and action variables.
1286 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE);
1287 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE, FALSE);
1288
1289 return hr;
1290}
1291
1292extern "C" HRESULT MsiEngineConcatActionProperty(
1293 __in BURN_MSI_PROPERTY actionMsiProperty,
1294 __deref_out_z LPWSTR* psczProperties
1295 )
1296{
1297 HRESULT hr = S_OK;
1298 LPCWSTR wzPropertyName = NULL;
1299
1300 switch (actionMsiProperty)
1301 {
1302 case BURN_MSI_PROPERTY_INSTALL:
1303 wzPropertyName = BURNMSIINSTALL_PROPERTY_NAME;
1304 break;
1305 case BURN_MSI_PROPERTY_MODIFY:
1306 wzPropertyName = BURNMSIMODIFY_PROPERTY_NAME;
1307 break;
1308 case BURN_MSI_PROPERTY_REPAIR:
1309 wzPropertyName = BURNMSIREPAIR_PROPERTY_NAME;
1310 break;
1311 case BURN_MSI_PROPERTY_UNINSTALL:
1312 wzPropertyName = BURNMSIUNINSTALL_PROPERTY_NAME;
1313 break;
1314 }
1315
1316 if (wzPropertyName)
1317 {
1318 hr = StrAllocConcatFormattedSecure(psczProperties, L" %ls=1", wzPropertyName);
1319 ExitOnFailure(hr, "Failed to add burn action property.");
1320 }
1321
1322LExit:
1323 return hr;
1324}
1325
1326extern "C" HRESULT MsiEngineConcatProperties(
1327 __in_ecount(cProperties) BURN_MSIPROPERTY* rgProperties,
1328 __in DWORD cProperties,
1329 __in BURN_VARIABLES* pVariables,
1330 __in BOOL fRollback,
1331 __deref_out_z LPWSTR* psczProperties,
1332 __in BOOL fObfuscateHiddenVariables
1333 )
1334{
1335 HRESULT hr = S_OK;
1336 LPWSTR sczValue = NULL;
1337 LPWSTR sczEscapedValue = NULL;
1338 LPWSTR sczProperty = NULL;
1339
1340 for (DWORD i = 0; i < cProperties; ++i)
1341 {
1342 BURN_MSIPROPERTY* pProperty = &rgProperties[i];
1343
1344 if (pProperty->sczCondition && *pProperty->sczCondition)
1345 {
1346 BOOL fCondition = FALSE;
1347
1348 hr = ConditionEvaluate(pVariables, pProperty->sczCondition, &fCondition);
1349 if (FAILED(hr) || !fCondition)
1350 {
1351 LogId(REPORT_VERBOSE, MSG_MSI_PROPERTY_CONDITION_FAILED, pProperty->sczId, pProperty->sczCondition, LoggingTrueFalseToString(fCondition));
1352 continue;
1353 }
1354 }
1355
1356 // format property value
1357 if (fObfuscateHiddenVariables)
1358 {
1359 hr = VariableFormatStringObfuscated(pVariables, (fRollback && pProperty->sczRollbackValue) ? pProperty->sczRollbackValue : pProperty->sczValue, &sczValue, NULL);
1360 }
1361 else
1362 {
1363 hr = VariableFormatString(pVariables, (fRollback && pProperty->sczRollbackValue) ? pProperty->sczRollbackValue : pProperty->sczValue, &sczValue, NULL);
1364 ExitOnFailure(hr, "Failed to format property value.");
1365 }
1366 ExitOnFailure(hr, "Failed to format property value.");
1367
1368 // escape property value
1369 hr = EscapePropertyArgumentString(sczValue, &sczEscapedValue, !fObfuscateHiddenVariables);
1370 ExitOnFailure(hr, "Failed to escape string.");
1371
1372 // build part
1373 hr = VariableStrAllocFormatted(!fObfuscateHiddenVariables, &sczProperty, L" %s%=\"%s\"", pProperty->sczId, sczEscapedValue);
1374 ExitOnFailure(hr, "Failed to format property string part.");
1375
1376 // append to property string
1377 hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, psczProperties, sczProperty, 0);
1378 ExitOnFailure(hr, "Failed to append property string part.");
1379 }
1380
1381LExit:
1382 StrSecureZeroFreeString(sczValue);
1383 StrSecureZeroFreeString(sczEscapedValue);
1384 StrSecureZeroFreeString(sczProperty);
1385 return hr;
1386}
1387
1388extern "C" HRESULT MsiEngineCalculateInstallUiLevel(
1389 __in BOOTSTRAPPER_DISPLAY display,
1390 __in BURN_USER_EXPERIENCE* pUserExperience,
1391 __in LPCWSTR wzPackageId,
1392 __in BOOL fExecute,
1393 __in BOOTSTRAPPER_ACTION_STATE actionState,
1394 __out BURN_MSI_PROPERTY* pActionMsiProperty,
1395 __out INSTALLUILEVEL* pUiLevel,
1396 __out BOOL* pfDisableExternalUiHandler
1397 )
1398{
1399 *pUiLevel = INSTALLUILEVEL_NONE;
1400 *pfDisableExternalUiHandler = FALSE;
1401
1402 if (BOOTSTRAPPER_DISPLAY_FULL == display ||
1403 BOOTSTRAPPER_DISPLAY_PASSIVE == display)
1404 {
1405 *pUiLevel = static_cast<INSTALLUILEVEL>(*pUiLevel | INSTALLUILEVEL_SOURCERESONLY);
1406 }
1407
1408 switch (actionState)
1409 {
1410 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
1411 *pActionMsiProperty = BURN_MSI_PROPERTY_UNINSTALL;
1412 break;
1413 case BOOTSTRAPPER_ACTION_STATE_REPAIR:
1414 *pActionMsiProperty = BURN_MSI_PROPERTY_REPAIR;
1415 break;
1416 case BOOTSTRAPPER_ACTION_STATE_MODIFY:
1417 *pActionMsiProperty = BURN_MSI_PROPERTY_MODIFY;
1418 break;
1419 default:
1420 *pActionMsiProperty = BURN_MSI_PROPERTY_INSTALL;
1421 break;
1422 }
1423
1424 return UserExperienceOnPlanMsiPackage(pUserExperience, wzPackageId, fExecute, actionState, pActionMsiProperty, pUiLevel, pfDisableExternalUiHandler);
1425}
1426
1427extern "C" void MsiEngineUpdateInstallRegistrationState(
1428 __in BURN_EXECUTE_ACTION* pAction,
1429 __in BOOL fRollback,
1430 __in HRESULT hrExecute,
1431 __in BOOL fInsideMsiTransaction
1432 )
1433{
1434 BURN_PACKAGE_REGISTRATION_STATE newState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
1435 BURN_PACKAGE* pPackage = pAction->msiPackage.pPackage;
1436
1437 if (FAILED(hrExecute) || !pPackage->fCanAffectRegistration)
1438 {
1439 ExitFunction();
1440 }
1441
1442 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->msiPackage.action)
1443 {
1444 newState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
1445 }
1446 else
1447 {
1448 newState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
1449 }
1450
1451 if (fInsideMsiTransaction)
1452 {
1453 pPackage->transactionRegistrationState = newState;
1454 }
1455 else
1456 {
1457 pPackage->installRegistrationState = newState;
1458 }
1459
1460 if (BURN_PACKAGE_REGISTRATION_STATE_ABSENT == newState)
1461 {
1462 for (DWORD i = 0; i < pPackage->Msi.cChainedPatches; ++i)
1463 {
1464 BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + i;
1465 BURN_MSPTARGETPRODUCT* pTargetProduct = pChainedPatch->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex;
1466
1467 if (fInsideMsiTransaction)
1468 {
1469 pTargetProduct->transactionRegistrationState = newState;
1470 }
1471 else
1472 {
1473 pTargetProduct->registrationState = newState;
1474 }
1475 }
1476 }
1477 else
1478 {
1479 for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i)
1480 {
1481 BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + i;
1482 BOOTSTRAPPER_ACTION_STATE patchExecuteAction = fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute;
1483
1484 if (BOOTSTRAPPER_ACTION_STATE_INSTALL > patchExecuteAction)
1485 {
1486 continue;
1487 }
1488
1489 BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + pSlipstreamMsp->dwMsiChainedPatchIndex;
1490 BURN_MSPTARGETPRODUCT* pTargetProduct = pChainedPatch->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex;
1491
1492 if (fInsideMsiTransaction)
1493 {
1494 pTargetProduct->transactionRegistrationState = newState;
1495 }
1496 else
1497 {
1498 pTargetProduct->registrationState = newState;
1499 }
1500 }
1501 }
1502
1503LExit:
1504 return;
1505}
1506
1507
1508// internal helper functions
1509
1510static HRESULT ParseRelatedMsiFromXml(
1511 __in IXMLDOMNode* pixnRelatedMsi,
1512 __in BURN_RELATED_MSI* pRelatedMsi
1513 )
1514{
1515 HRESULT hr = S_OK;
1516 IXMLDOMNodeList* pixnNodes = NULL;
1517 IXMLDOMNode* pixnNode = NULL;
1518 DWORD cNodes = 0;
1519 LPWSTR scz = NULL;
1520
1521 // @Id
1522 hr = XmlGetAttributeEx(pixnRelatedMsi, L"Id", &pRelatedMsi->sczUpgradeCode);
1523 ExitOnFailure(hr, "Failed to get @Id.");
1524
1525 // @MinVersion
1526 hr = XmlGetAttributeEx(pixnRelatedMsi, L"MinVersion", &scz);
1527 if (E_NOTFOUND != hr)
1528 {
1529 ExitOnFailure(hr, "Failed to get @MinVersion.");
1530
1531 hr = VerParseVersion(scz, 0, FALSE, &pRelatedMsi->pMinVersion);
1532 ExitOnFailure(hr, "Failed to parse @MinVersion: %ls", scz);
1533
1534 if (pRelatedMsi->pMinVersion->fInvalid)
1535 {
1536 LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz);
1537 }
1538
1539 // flag that we have a min version
1540 pRelatedMsi->fMinProvided = TRUE;
1541
1542 // @MinInclusive
1543 hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"MinInclusive", &pRelatedMsi->fMinInclusive);
1544 ExitOnFailure(hr, "Failed to get @MinInclusive.");
1545 }
1546
1547 // @MaxVersion
1548 hr = XmlGetAttributeEx(pixnRelatedMsi, L"MaxVersion", &scz);
1549 if (E_NOTFOUND != hr)
1550 {
1551 ExitOnFailure(hr, "Failed to get @MaxVersion.");
1552
1553 hr = VerParseVersion(scz, 0, FALSE, &pRelatedMsi->pMaxVersion);
1554 ExitOnFailure(hr, "Failed to parse @MaxVersion: %ls", scz);
1555
1556 if (pRelatedMsi->pMaxVersion->fInvalid)
1557 {
1558 LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz);
1559 }
1560
1561 // flag that we have a max version
1562 pRelatedMsi->fMaxProvided = TRUE;
1563
1564 // @MaxInclusive
1565 hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"MaxInclusive", &pRelatedMsi->fMaxInclusive);
1566 ExitOnFailure(hr, "Failed to get @MaxInclusive.");
1567 }
1568
1569 // @OnlyDetect
1570 hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"OnlyDetect", &pRelatedMsi->fOnlyDetect);
1571 ExitOnFailure(hr, "Failed to get @OnlyDetect.");
1572
1573 // select language nodes
1574 hr = XmlSelectNodes(pixnRelatedMsi, L"Language", &pixnNodes);
1575 ExitOnFailure(hr, "Failed to select language nodes.");
1576
1577 // get language node count
1578 hr = pixnNodes->get_length((long*)&cNodes);
1579 ExitOnFailure(hr, "Failed to get language node count.");
1580
1581 if (cNodes)
1582 {
1583 // @LangInclusive
1584 hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"LangInclusive", &pRelatedMsi->fLangInclusive);
1585 ExitOnFailure(hr, "Failed to get @LangInclusive.");
1586
1587 // allocate memory for language IDs
1588 pRelatedMsi->rgdwLanguages = (DWORD*)MemAlloc(sizeof(DWORD) * cNodes, TRUE);
1589 ExitOnNull(pRelatedMsi->rgdwLanguages, hr, E_OUTOFMEMORY, "Failed to allocate memory for language IDs.");
1590
1591 pRelatedMsi->cLanguages = cNodes;
1592
1593 // parse language elements
1594 for (DWORD i = 0; i < cNodes; ++i)
1595 {
1596 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
1597 ExitOnFailure(hr, "Failed to get next node.");
1598
1599 // @Id
1600 hr = XmlGetAttributeNumber(pixnNode, L"Id", &pRelatedMsi->rgdwLanguages[i]);
1601 ExitOnFailure(hr, "Failed to get Language/@Id.");
1602
1603 // prepare next iteration
1604 ReleaseNullObject(pixnNode);
1605 }
1606 }
1607
1608 hr = S_OK;
1609
1610LExit:
1611 ReleaseObject(pixnNodes);
1612 ReleaseObject(pixnNode);
1613 ReleaseStr(scz);
1614
1615 return hr;
1616}
1617
1618static HRESULT EvaluateActionStateConditions(
1619 __in BURN_VARIABLES* pVariables,
1620 __in_z_opt LPCWSTR sczAddLocalCondition,
1621 __in_z_opt LPCWSTR sczAddSourceCondition,
1622 __in_z_opt LPCWSTR sczAdvertiseCondition,
1623 __out BOOTSTRAPPER_FEATURE_STATE* pState
1624 )
1625{
1626 HRESULT hr = S_OK;
1627 BOOL fCondition = FALSE;
1628
1629 // if no condition was set, return no feature state
1630 if (!sczAddLocalCondition && !sczAddSourceCondition && !sczAdvertiseCondition)
1631 {
1632 *pState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
1633 ExitFunction();
1634 }
1635
1636 if (sczAddLocalCondition)
1637 {
1638 hr = ConditionEvaluate(pVariables, sczAddLocalCondition, &fCondition);
1639 ExitOnFailure(hr, "Failed to evaluate add local condition.");
1640
1641 if (fCondition)
1642 {
1643 *pState = BOOTSTRAPPER_FEATURE_STATE_LOCAL;
1644 ExitFunction();
1645 }
1646 }
1647
1648 if (sczAddSourceCondition)
1649 {
1650 hr = ConditionEvaluate(pVariables, sczAddSourceCondition, &fCondition);
1651 ExitOnFailure(hr, "Failed to evaluate add source condition.");
1652
1653 if (fCondition)
1654 {
1655 *pState = BOOTSTRAPPER_FEATURE_STATE_SOURCE;
1656 ExitFunction();
1657 }
1658 }
1659
1660 if (sczAdvertiseCondition)
1661 {
1662 hr = ConditionEvaluate(pVariables, sczAdvertiseCondition, &fCondition);
1663 ExitOnFailure(hr, "Failed to evaluate advertise condition.");
1664
1665 if (fCondition)
1666 {
1667 *pState = BOOTSTRAPPER_FEATURE_STATE_ADVERTISED;
1668 ExitFunction();
1669 }
1670 }
1671
1672 // if no condition was true, set to absent
1673 *pState = BOOTSTRAPPER_FEATURE_STATE_ABSENT;
1674
1675LExit:
1676 return hr;
1677}
1678
1679static HRESULT CalculateFeatureAction(
1680 __in BOOTSTRAPPER_FEATURE_STATE currentState,
1681 __in BOOTSTRAPPER_FEATURE_STATE requestedState,
1682 __in BOOL fRepair,
1683 __out BOOTSTRAPPER_FEATURE_ACTION* pFeatureAction,
1684 __inout BOOL* pfDelta
1685 )
1686{
1687 HRESULT hr = S_OK;
1688
1689 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_NONE;
1690 switch (requestedState)
1691 {
1692 case BOOTSTRAPPER_FEATURE_STATE_UNKNOWN:
1693 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_NONE;
1694 break;
1695
1696 case BOOTSTRAPPER_FEATURE_STATE_ABSENT:
1697 if (BOOTSTRAPPER_FEATURE_STATE_ABSENT != currentState)
1698 {
1699 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REMOVE;
1700 }
1701 break;
1702
1703 case BOOTSTRAPPER_FEATURE_STATE_ADVERTISED:
1704 if (BOOTSTRAPPER_FEATURE_STATE_ADVERTISED != currentState)
1705 {
1706 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE;
1707 }
1708 else if (fRepair)
1709 {
1710 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL;
1711 }
1712 break;
1713
1714 case BOOTSTRAPPER_FEATURE_STATE_LOCAL:
1715 if (BOOTSTRAPPER_FEATURE_STATE_LOCAL != currentState)
1716 {
1717 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL;
1718 }
1719 else if (fRepair)
1720 {
1721 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL;
1722 }
1723 break;
1724
1725 case BOOTSTRAPPER_FEATURE_STATE_SOURCE:
1726 if (BOOTSTRAPPER_FEATURE_STATE_SOURCE != currentState)
1727 {
1728 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE;
1729 }
1730 else if (fRepair)
1731 {
1732 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL;
1733 }
1734 break;
1735
1736 default:
1737 hr = E_UNEXPECTED;
1738 ExitOnRootFailure(hr, "Invalid state value.");
1739 }
1740
1741 if (BOOTSTRAPPER_FEATURE_ACTION_NONE != *pFeatureAction)
1742 {
1743 *pfDelta = TRUE;
1744 }
1745
1746LExit:
1747 return hr;
1748}
1749
1750static HRESULT EscapePropertyArgumentString(
1751 __in LPCWSTR wzProperty,
1752 __inout_z LPWSTR* psczEscapedValue,
1753 __in BOOL fZeroOnRealloc
1754 )
1755{
1756 HRESULT hr = S_OK;
1757 DWORD cch = 0;
1758 DWORD cchEscape = 0;
1759 LPCWSTR wzSource = NULL;
1760 LPWSTR wzTarget = NULL;
1761
1762 // count characters to escape
1763 wzSource = wzProperty;
1764 while (*wzSource)
1765 {
1766 ++cch;
1767 if (L'\"' == *wzSource)
1768 {
1769 ++cchEscape;
1770 }
1771 ++wzSource;
1772 }
1773
1774 // allocate target buffer
1775 hr = VariableStrAlloc(fZeroOnRealloc, psczEscapedValue, cch + cchEscape + 1); // character count, plus escape character count, plus null terminator
1776 ExitOnFailure(hr, "Failed to allocate string buffer.");
1777
1778 // write to target buffer
1779 wzSource = wzProperty;
1780 wzTarget = *psczEscapedValue;
1781 while (*wzSource)
1782 {
1783 *wzTarget = *wzSource;
1784 if (L'\"' == *wzTarget)
1785 {
1786 ++wzTarget;
1787 *wzTarget = L'\"';
1788 }
1789
1790 ++wzSource;
1791 ++wzTarget;
1792 }
1793
1794 *wzTarget = L'\0'; // add null terminator
1795
1796LExit:
1797 return hr;
1798}
1799
1800static HRESULT ConcatFeatureActionProperties(
1801 __in BURN_PACKAGE* pPackage,
1802 __in BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions,
1803 __inout_z LPWSTR* psczArguments
1804 )
1805{
1806 HRESULT hr = S_OK;
1807 LPWSTR scz = NULL;
1808 LPWSTR sczAddLocal = NULL;
1809 LPWSTR sczAddSource = NULL;
1810 LPWSTR sczAddDefault = NULL;
1811 LPWSTR sczReinstall = NULL;
1812 LPWSTR sczAdvertise = NULL;
1813 LPWSTR sczRemove = NULL;
1814
1815 // features
1816 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
1817 {
1818 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
1819
1820 switch (rgFeatureActions[i])
1821 {
1822 case BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL:
1823 if (sczAddLocal)
1824 {
1825 hr = StrAllocConcat(&sczAddLocal, L",", 0);
1826 ExitOnFailure(hr, "Failed to concat separator.");
1827 }
1828 hr = StrAllocConcat(&sczAddLocal, pFeature->sczId, 0);
1829 ExitOnFailure(hr, "Failed to concat feature.");
1830 break;
1831
1832 case BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE:
1833 if (sczAddSource)
1834 {
1835 hr = StrAllocConcat(&sczAddSource, L",", 0);
1836 ExitOnFailure(hr, "Failed to concat separator.");
1837 }
1838 hr = StrAllocConcat(&sczAddSource, pFeature->sczId, 0);
1839 ExitOnFailure(hr, "Failed to concat feature.");
1840 break;
1841
1842 case BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT:
1843 if (sczAddDefault)
1844 {
1845 hr = StrAllocConcat(&sczAddDefault, L",", 0);
1846 ExitOnFailure(hr, "Failed to concat separator.");
1847 }
1848 hr = StrAllocConcat(&sczAddDefault, pFeature->sczId, 0);
1849 ExitOnFailure(hr, "Failed to concat feature.");
1850 break;
1851
1852 case BOOTSTRAPPER_FEATURE_ACTION_REINSTALL:
1853 if (sczReinstall)
1854 {
1855 hr = StrAllocConcat(&sczReinstall, L",", 0);
1856 ExitOnFailure(hr, "Failed to concat separator.");
1857 }
1858 hr = StrAllocConcat(&sczReinstall, pFeature->sczId, 0);
1859 ExitOnFailure(hr, "Failed to concat feature.");
1860 break;
1861
1862 case BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE:
1863 if (sczAdvertise)
1864 {
1865 hr = StrAllocConcat(&sczAdvertise, L",", 0);
1866 ExitOnFailure(hr, "Failed to concat separator.");
1867 }
1868 hr = StrAllocConcat(&sczAdvertise, pFeature->sczId, 0);
1869 ExitOnFailure(hr, "Failed to concat feature.");
1870 break;
1871
1872 case BOOTSTRAPPER_FEATURE_ACTION_REMOVE:
1873 if (sczRemove)
1874 {
1875 hr = StrAllocConcat(&sczRemove, L",", 0);
1876 ExitOnFailure(hr, "Failed to concat separator.");
1877 }
1878 hr = StrAllocConcat(&sczRemove, pFeature->sczId, 0);
1879 ExitOnFailure(hr, "Failed to concat feature.");
1880 break;
1881 }
1882 }
1883
1884 if (sczAddLocal)
1885 {
1886 hr = StrAllocFormatted(&scz, L" ADDLOCAL=\"%s\"", sczAddLocal, 0);
1887 ExitOnFailure(hr, "Failed to format ADDLOCAL string.");
1888
1889 hr = StrAllocConcatSecure(psczArguments, scz, 0);
1890 ExitOnFailure(hr, "Failed to concat argument string.");
1891 }
1892
1893 if (sczAddSource)
1894 {
1895 hr = StrAllocFormatted(&scz, L" ADDSOURCE=\"%s\"", sczAddSource, 0);
1896 ExitOnFailure(hr, "Failed to format ADDSOURCE string.");
1897
1898 hr = StrAllocConcatSecure(psczArguments, scz, 0);
1899 ExitOnFailure(hr, "Failed to concat argument string.");
1900 }
1901
1902 if (sczAddDefault)
1903 {
1904 hr = StrAllocFormatted(&scz, L" ADDDEFAULT=\"%s\"", sczAddDefault, 0);
1905 ExitOnFailure(hr, "Failed to format ADDDEFAULT string.");
1906
1907 hr = StrAllocConcatSecure(psczArguments, scz, 0);
1908 ExitOnFailure(hr, "Failed to concat argument string.");
1909 }
1910
1911 if (sczReinstall)
1912 {
1913 hr = StrAllocFormatted(&scz, L" REINSTALL=\"%s\"", sczReinstall, 0);
1914 ExitOnFailure(hr, "Failed to format REINSTALL string.");
1915
1916 hr = StrAllocConcatSecure(psczArguments, scz, 0);
1917 ExitOnFailure(hr, "Failed to concat argument string.");
1918 }
1919
1920 if (sczAdvertise)
1921 {
1922 hr = StrAllocFormatted(&scz, L" ADVERTISE=\"%s\"", sczAdvertise, 0);
1923 ExitOnFailure(hr, "Failed to format ADVERTISE string.");
1924
1925 hr = StrAllocConcatSecure(psczArguments, scz, 0);
1926 ExitOnFailure(hr, "Failed to concat argument string.");
1927 }
1928
1929 if (sczRemove)
1930 {
1931 hr = StrAllocFormatted(&scz, L" REMOVE=\"%s\"", sczRemove, 0);
1932 ExitOnFailure(hr, "Failed to format REMOVE string.");
1933
1934 hr = StrAllocConcatSecure(psczArguments, scz, 0);
1935 ExitOnFailure(hr, "Failed to concat argument string.");
1936 }
1937
1938LExit:
1939 ReleaseStr(scz);
1940 ReleaseStr(sczAddLocal);
1941 ReleaseStr(sczAddSource);
1942 ReleaseStr(sczAddDefault);
1943 ReleaseStr(sczReinstall);
1944 ReleaseStr(sczAdvertise);
1945 ReleaseStr(sczRemove);
1946
1947 return hr;
1948}
1949
1950static HRESULT ConcatPatchProperty(
1951 __in BURN_PACKAGE* pPackage,
1952 __in BOOL fRollback,
1953 __inout_z LPWSTR* psczArguments
1954 )
1955{
1956 HRESULT hr = S_OK;
1957 LPWSTR sczCachedDirectory = NULL;
1958 LPWSTR sczMspPath = NULL;
1959 LPWSTR sczPatches = NULL;
1960
1961 // If there are slipstream patch actions, build up their patch action.
1962 if (pPackage->Msi.cSlipstreamMspPackages)
1963 {
1964 for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i)
1965 {
1966 BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + i;
1967 BURN_PACKAGE* pMspPackage = pSlipstreamMsp->pMspPackage;
1968 BURN_PAYLOAD* pMspPackagePayload = pMspPackage->payloads.rgItems[0].pPayload;
1969 BOOTSTRAPPER_ACTION_STATE patchExecuteAction = fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute;
1970
1971 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < patchExecuteAction)
1972 {
1973 hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory);
1974 ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId);
1975
1976 hr = PathConcat(sczCachedDirectory, pMspPackagePayload->sczFilePath, &sczMspPath);
1977 ExitOnFailure(hr, "Failed to build MSP path.");
1978
1979 if (!sczPatches)
1980 {
1981 hr = StrAllocConcat(&sczPatches, L" PATCH=\"", 0);
1982 ExitOnFailure(hr, "Failed to prefix with PATCH property.");
1983 }
1984 else
1985 {
1986 hr = StrAllocConcat(&sczPatches, L";", 0);
1987 ExitOnFailure(hr, "Failed to semi-colon delimit patches.");
1988 }
1989
1990 hr = StrAllocConcat(&sczPatches, sczMspPath, 0);
1991 ExitOnFailure(hr, "Failed to append patch path.");
1992 }
1993 }
1994
1995 if (sczPatches)
1996 {
1997 hr = StrAllocConcat(&sczPatches, L"\"", 0);
1998 ExitOnFailure(hr, "Failed to close the quoted PATCH property.");
1999
2000 hr = StrAllocConcatSecure(psczArguments, sczPatches, 0);
2001 ExitOnFailure(hr, "Failed to append PATCH property.");
2002 }
2003 }
2004
2005LExit:
2006 ReleaseStr(sczMspPath);
2007 ReleaseStr(sczCachedDirectory);
2008 ReleaseStr(sczPatches);
2009 return hr;
2010}
2011
2012static void RegisterSourceDirectory(
2013 __in BURN_PACKAGE* pPackage,
2014 __in_z LPCWSTR wzMsiPath
2015 )
2016{
2017 HRESULT hr = S_OK;
2018 LPWSTR sczMsiDirectory = NULL;
2019 MSIINSTALLCONTEXT dwContext = pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED;
2020
2021 hr = PathGetDirectory(wzMsiPath, &sczMsiDirectory);
2022 ExitOnFailure(hr, "Failed to get directory for path: %ls", wzMsiPath);
2023
2024 hr = WiuSourceListAddSourceEx(pPackage->Msi.sczProductCode, NULL, dwContext, MSICODE_PRODUCT, sczMsiDirectory, 1);
2025 if (FAILED(hr))
2026 {
2027 LogId(REPORT_VERBOSE, MSG_SOURCELIST_REGISTER, sczMsiDirectory, pPackage->Msi.sczProductCode, hr);
2028 ExitFunction();
2029 }
2030
2031LExit:
2032 ReleaseStr(sczMsiDirectory);
2033
2034 return;
2035}
diff --git a/src/burn/engine/msiengine.h b/src/burn/engine/msiengine.h
new file mode 100644
index 00000000..8b5bcdd0
--- /dev/null
+++ b/src/burn/engine/msiengine.h
@@ -0,0 +1,104 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4// constants
5#define BURNMSIINSTALL_PROPERTY_NAME L"BURNMSIINSTALL"
6#define BURNMSIMODIFY_PROPERTY_NAME L"BURNMSIMODIFY"
7#define BURNMSIREPAIR_PROPERTY_NAME L"BURNMSIREPAIR"
8#define BURNMSIUNINSTALL_PROPERTY_NAME L"BURNMSIUNINSTALL"
9
10
11#if defined(__cplusplus)
12extern "C" {
13#endif
14
15
16// function declarations
17
18HRESULT MsiEngineParsePackageFromXml(
19 __in IXMLDOMNode* pixnBundle,
20 __in BURN_PACKAGE* pPackage
21 );
22HRESULT MsiEngineParsePropertiesFromXml(
23 __in IXMLDOMNode* pixnPackage,
24 __out BURN_MSIPROPERTY** prgProperties,
25 __out DWORD* pcProperties
26 );
27void MsiEnginePackageUninitialize(
28 __in BURN_PACKAGE* pPackage
29 );
30HRESULT MsiEngineDetectInitialize(
31 __in BURN_PACKAGES* pPackages
32 );
33HRESULT MsiEngineDetectPackage(
34 __in BURN_PACKAGE* pPackage,
35 __in BURN_USER_EXPERIENCE* pUserExperience
36 );
37HRESULT MsiEnginePlanInitializePackage(
38 __in BURN_PACKAGE* pPackage,
39 __in BURN_VARIABLES* pVariables,
40 __in BURN_USER_EXPERIENCE* pUserExperience
41 );
42HRESULT MsiEnginePlanCalculatePackage(
43 __in BURN_PACKAGE* pPackage,
44 __in BOOL fInsideMsiTransaction
45 );
46HRESULT MsiEnginePlanAddPackage(
47 __in BOOTSTRAPPER_DISPLAY display,
48 __in BURN_USER_EXPERIENCE* pUserExperience,
49 __in BURN_PACKAGE* pPackage,
50 __in BURN_PLAN* pPlan,
51 __in BURN_LOGGING* pLog,
52 __in BURN_VARIABLES* pVariables,
53 __in_opt HANDLE hCacheEvent
54 );
55HRESULT MsiEngineBeginTransaction(
56 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
57 );
58HRESULT MsiEngineCommitTransaction(
59 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
60 );
61HRESULT MsiEngineRollbackTransaction(
62 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
63 );
64HRESULT MsiEngineExecutePackage(
65 __in_opt HWND hwndParent,
66 __in BURN_EXECUTE_ACTION* pExecuteAction,
67 __in BURN_VARIABLES* pVariables,
68 __in BOOL fRollback,
69 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
70 __in LPVOID pvContext,
71 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
72 );
73HRESULT MsiEngineConcatActionProperty(
74 __in BURN_MSI_PROPERTY actionMsiProperty,
75 __deref_out_z LPWSTR* psczProperties
76 );
77HRESULT MsiEngineConcatProperties(
78 __in_ecount(cProperties) BURN_MSIPROPERTY* rgProperties,
79 __in DWORD cProperties,
80 __in BURN_VARIABLES* pVariables,
81 __in BOOL fRollback,
82 __deref_out_z LPWSTR* psczProperties,
83 __in BOOL fObfuscateHiddenVariables
84 );
85HRESULT MsiEngineCalculateInstallUiLevel(
86 __in BOOTSTRAPPER_DISPLAY display,
87 __in BURN_USER_EXPERIENCE* pUserExperience,
88 __in LPCWSTR wzPackageId,
89 __in BOOL fExecute,
90 __in BOOTSTRAPPER_ACTION_STATE actionState,
91 __out BURN_MSI_PROPERTY* pActionMsiProperty,
92 __out INSTALLUILEVEL* pUiLevel,
93 __out BOOL* pfDisableExternalUiHandler
94 );
95void MsiEngineUpdateInstallRegistrationState(
96 __in BURN_EXECUTE_ACTION* pAction,
97 __in BOOL fRollback,
98 __in HRESULT hrExecute,
99 __in BOOL fInsideMsiTransaction
100 );
101
102#if defined(__cplusplus)
103}
104#endif
diff --git a/src/burn/engine/mspengine.cpp b/src/burn/engine/mspengine.cpp
new file mode 100644
index 00000000..6d58d324
--- /dev/null
+++ b/src/burn/engine/mspengine.cpp
@@ -0,0 +1,1197 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// constants
7
8
9// structs
10
11struct POSSIBLE_TARGETPRODUCT
12{
13 WCHAR wzProductCode[39];
14 LPWSTR pszLocalPackage;
15 MSIINSTALLCONTEXT context;
16};
17
18// internal function declarations
19
20static HRESULT GetPossibleTargetProductCodes(
21 __in BURN_PACKAGES* pPackages,
22 __deref_inout_ecount_opt(*pcPossibleTargetProductCodes) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProductCodes,
23 __inout DWORD* pcPossibleTargetProductCodes
24 );
25static HRESULT AddPossibleTargetProduct(
26 __in STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes,
27 __in_z LPCWSTR wzPossibleTargetProductCode,
28 __in MSIINSTALLCONTEXT context,
29 __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts,
30 __inout DWORD* pcPossibleTargetProducts
31 );
32static HRESULT AddDetectedTargetProduct(
33 __in BURN_PACKAGE* pPackage,
34 __in DWORD dwOrder,
35 __in_z LPCWSTR wzProductCode,
36 __in MSIINSTALLCONTEXT context,
37 __out DWORD* pdwTargetProductIndex
38 );
39static HRESULT AddMsiChainedPatch(
40 __in BURN_PACKAGE* pPackage,
41 __in BURN_PACKAGE* pMspPackage,
42 __in DWORD dwMspTargetProductIndex,
43 __out DWORD* pdwChainedPatchIndex
44 );
45static HRESULT DeterminePatchChainedTarget(
46 __in BURN_PACKAGES* pPackages,
47 __in BURN_PACKAGE* pMspPackage,
48 __in LPCWSTR wzTargetProductCode,
49 __in DWORD dwMspTargetProductIndex
50 );
51static HRESULT PlanTargetProduct(
52 __in BOOTSTRAPPER_DISPLAY display,
53 __in BURN_USER_EXPERIENCE* pUserExperience,
54 __in BOOL fRollback,
55 __in BURN_PLAN* pPlan,
56 __in BURN_LOGGING* pLog,
57 __in BURN_VARIABLES* pVariables,
58 __in BOOTSTRAPPER_ACTION_STATE actionState,
59 __in BURN_PACKAGE* pPackage,
60 __in BURN_MSPTARGETPRODUCT* pTargetProduct,
61 __in_opt HANDLE hCacheEvent
62 );
63
64
65// function definitions
66
67extern "C" HRESULT MspEngineParsePackageFromXml(
68 __in IXMLDOMNode* pixnMspPackage,
69 __in BURN_PACKAGE* pPackage
70 )
71{
72 HRESULT hr = S_OK;
73
74 // @PatchCode
75 hr = XmlGetAttributeEx(pixnMspPackage, L"PatchCode", &pPackage->Msp.sczPatchCode);
76 ExitOnFailure(hr, "Failed to get @PatchCode.");
77
78 // @PatchXml
79 hr = XmlGetAttributeEx(pixnMspPackage, L"PatchXml", &pPackage->Msp.sczApplicabilityXml);
80 ExitOnFailure(hr, "Failed to get @PatchXml.");
81
82 // Read properties.
83 hr = MsiEngineParsePropertiesFromXml(pixnMspPackage, &pPackage->Msp.rgProperties, &pPackage->Msp.cProperties);
84 ExitOnFailure(hr, "Failed to parse properties from XML.");
85
86LExit:
87
88 return hr;
89}
90
91extern "C" void MspEnginePackageUninitialize(
92 __in BURN_PACKAGE* pPackage
93 )
94{
95 ReleaseStr(pPackage->Msp.sczPatchCode);
96 ReleaseStr(pPackage->Msp.sczApplicabilityXml);
97
98 // free properties
99 if (pPackage->Msp.rgProperties)
100 {
101 for (DWORD i = 0; i < pPackage->Msp.cProperties; ++i)
102 {
103 BURN_MSIPROPERTY* pProperty = &pPackage->Msp.rgProperties[i];
104
105 ReleaseStr(pProperty->sczId);
106 ReleaseStr(pProperty->sczValue);
107 ReleaseStr(pProperty->sczRollbackValue);
108 }
109 MemFree(pPackage->Msp.rgProperties);
110 }
111
112 // free target products
113 ReleaseMem(pPackage->Msp.rgTargetProducts);
114
115 // clear struct
116 memset(&pPackage->Msp, 0, sizeof(pPackage->Msp));
117}
118
119extern "C" HRESULT MspEngineDetectInitialize(
120 __in BURN_PACKAGES* pPackages
121 )
122{
123 AssertSz(pPackages->cPatchInfo, "MspEngineDetectInitialize() should only be called if there are MSP packages.");
124
125 HRESULT hr = S_OK;
126 POSSIBLE_TARGETPRODUCT* rgPossibleTargetProducts = NULL;
127 DWORD cPossibleTargetProducts = 0;
128
129#ifdef DEBUG
130 // All patch info should be initialized to zero.
131 for (DWORD i = 0; i < pPackages->cPatchInfo; ++i)
132 {
133 BURN_PACKAGE* pPackage = pPackages->rgPatchInfoToPackage[i];
134 Assert(!pPackage->Msp.cTargetProductCodes);
135 Assert(!pPackage->Msp.rgTargetProducts);
136 }
137#endif
138
139 // Figure out which product codes to target on the machine. In the worst case all products on the machine
140 // will be returned.
141 hr = GetPossibleTargetProductCodes(pPackages, &rgPossibleTargetProducts, &cPossibleTargetProducts);
142 ExitOnFailure(hr, "Failed to get possible target product codes.");
143
144 // Loop through possible target products, testing the collective patch applicability against each product in
145 // the appropriate context. Store the result with the appropriate patch package.
146 for (DWORD iSearch = 0; iSearch < cPossibleTargetProducts; ++iSearch)
147 {
148 const POSSIBLE_TARGETPRODUCT* pPossibleTargetProduct = rgPossibleTargetProducts + iSearch;
149
150 LogId(REPORT_STANDARD, MSG_DETECT_CALCULATE_PATCH_APPLICABILITY, pPossibleTargetProduct->wzProductCode, LoggingMsiInstallContext(pPossibleTargetProduct->context));
151
152 if (pPossibleTargetProduct->pszLocalPackage)
153 {
154 // Ignores current machine state to determine just patch applicability.
155 // Superseded and obsolesced patches will be planned separately.
156 hr = WiuDetermineApplicablePatches(pPossibleTargetProduct->pszLocalPackage, pPackages->rgPatchInfo, pPackages->cPatchInfo);
157 }
158 else
159 {
160 hr = WiuDeterminePatchSequence(pPossibleTargetProduct->wzProductCode, NULL, pPossibleTargetProduct->context, pPackages->rgPatchInfo, pPackages->cPatchInfo);
161 }
162
163 if (SUCCEEDED(hr))
164 {
165 for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo)
166 {
167 hr = HRESULT_FROM_WIN32(pPackages->rgPatchInfo[iPatchInfo].uStatus);
168 BURN_PACKAGE* pMspPackage = pPackages->rgPatchInfoToPackage[iPatchInfo];
169 Assert(BURN_PACKAGE_TYPE_MSP == pMspPackage->type);
170
171 if (S_OK == hr)
172 {
173 // Note that we do add superseded and obsolete MSP packages. Package Detect and Plan will sort them out later.
174 hr = MspEngineAddDetectedTargetProduct(pPackages, pMspPackage, pPackages->rgPatchInfo[iPatchInfo].dwOrder, pPossibleTargetProduct->wzProductCode, pPossibleTargetProduct->context);
175 ExitOnFailure(hr, "Failed to add target product code to package: %ls", pMspPackage->sczId);
176 }
177 else
178 {
179 LogStringLine(REPORT_DEBUG, " 0x%x: Patch applicability failed for package: %ls", hr, pMspPackage->sczId);
180 }
181 }
182 }
183 else
184 {
185 LogId(REPORT_STANDARD, MSG_DETECT_FAILED_CALCULATE_PATCH_APPLICABILITY, pPossibleTargetProduct->wzProductCode, LoggingMsiInstallContext(pPossibleTargetProduct->context), hr);
186 }
187
188 hr = S_OK; // always reset so we test all possible target products.
189 }
190
191LExit:
192 if (rgPossibleTargetProducts)
193 {
194 for (DWORD i = 0; i < cPossibleTargetProducts; ++i)
195 {
196 ReleaseStr(rgPossibleTargetProducts[i].pszLocalPackage);
197 }
198 MemFree(rgPossibleTargetProducts);
199 }
200
201 return hr;
202}
203
204extern "C" HRESULT MspEngineAddDetectedTargetProduct(
205 __in BURN_PACKAGES* pPackages,
206 __in BURN_PACKAGE* pPackage,
207 __in DWORD dwOrder,
208 __in_z LPCWSTR wzProductCode,
209 __in MSIINSTALLCONTEXT context
210 )
211{
212 HRESULT hr = S_OK;
213 DWORD dwTargetProductIndex = 0;
214
215 hr = AddDetectedTargetProduct(pPackage, dwOrder, wzProductCode, context, &dwTargetProductIndex);
216 ExitOnFailure(hr, "Failed to add detected target product.");
217
218 hr = DeterminePatchChainedTarget(pPackages, pPackage, wzProductCode, dwTargetProductIndex);
219 ExitOnFailure(hr, "Failed to determine patch chained target.");
220
221LExit:
222 return hr;
223}
224
225extern "C" HRESULT MspEngineAddMissingSlipstreamTarget(
226 __in BURN_PACKAGE* pMsiPackage,
227 __in BURN_SLIPSTREAM_MSP* pSlipstreamMsp
228 )
229{
230 HRESULT hr = S_OK;
231 DWORD dwTargetProductIndex = 0;
232 BURN_MSPTARGETPRODUCT* pTargetProduct = NULL;
233 DWORD dwChainedPatchIndex = 0;
234
235 hr = AddDetectedTargetProduct(pSlipstreamMsp->pMspPackage, 0, pMsiPackage->Msi.sczProductCode, pMsiPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, &dwTargetProductIndex);
236 ExitOnFailure(hr, "Failed to add missing slipstream target.");
237
238 pTargetProduct = pSlipstreamMsp->pMspPackage->Msp.rgTargetProducts + dwTargetProductIndex;
239 pTargetProduct->fSlipstream = TRUE;
240 pTargetProduct->fSlipstreamRequired = TRUE;
241 pTargetProduct->pChainedTargetPackage = pMsiPackage;
242
243 hr = AddMsiChainedPatch(pMsiPackage, pSlipstreamMsp->pMspPackage, dwTargetProductIndex, &dwChainedPatchIndex);
244 ExitOnFailure(hr, "Failed to add chained patch.");
245
246 pSlipstreamMsp->dwMsiChainedPatchIndex = dwChainedPatchIndex;
247
248LExit:
249 return hr;
250}
251
252extern "C" HRESULT MspEngineDetectPackage(
253 __in BURN_PACKAGE* pPackage,
254 __in BURN_USER_EXPERIENCE* pUserExperience
255 )
256{
257 HRESULT hr = S_OK;
258 LPWSTR sczState = NULL;
259
260 if (pPackage->fCanAffectRegistration)
261 {
262 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
263 }
264
265 if (0 == pPackage->Msp.cTargetProductCodes)
266 {
267 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
268 }
269 else
270 {
271 // Start the package state at the highest state then loop through all the
272 // target product codes and end up setting the current state to the lowest
273 // package state applied to the target product codes.
274 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED;
275
276 for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
277 {
278 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i;
279
280 hr = WiuGetPatchInfoEx(pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode, NULL, pTargetProduct->context, INSTALLPROPERTY_PATCHSTATE, &sczState);
281 if (SUCCEEDED(hr))
282 {
283 switch (*sczState)
284 {
285 case '1':
286 pTargetProduct->fInstalled = TRUE;
287 pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT;
288 break;
289
290 case '2':
291 pTargetProduct->fInstalled = TRUE;
292 pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED;
293 break;
294
295 case '4':
296 pTargetProduct->fInstalled = TRUE;
297 pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE;
298 break;
299
300 default:
301 pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
302 break;
303 }
304 }
305 else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PATCH) == hr || HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr)
306 {
307 pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
308 hr = S_OK;
309 }
310 ExitOnFailure(hr, "Failed to get patch information for patch code: %ls, target product code: %ls", pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode);
311
312 if (pPackage->currentState > pTargetProduct->patchPackageState)
313 {
314 pPackage->currentState = pTargetProduct->patchPackageState;
315 }
316
317 if (pPackage->fCanAffectRegistration)
318 {
319 pTargetProduct->registrationState = pTargetProduct->fInstalled ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
320
321 if (pTargetProduct->fInstalled)
322 {
323 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
324 }
325 }
326
327 hr = UserExperienceOnDetectPatchTarget(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, pTargetProduct->patchPackageState);
328 ExitOnRootFailure(hr, "BA aborted detect patch target.");
329 }
330 }
331
332LExit:
333 ReleaseStr(sczState);
334
335 return hr;
336}
337
338extern "C" HRESULT MspEnginePlanInitializePackage(
339 __in BURN_PACKAGE* pPackage,
340 __in BURN_USER_EXPERIENCE* pUserExperience
341 )
342{
343 HRESULT hr = S_OK;
344
345 for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
346 {
347 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i;
348
349 if (!pTargetProduct->fInstalled && pTargetProduct->fSlipstreamRequired && BOOTSTRAPPER_REQUEST_STATE_PRESENT > pTargetProduct->pChainedTargetPackage->requested)
350 {
351 // There's no way to apply the patch if the target isn't installed.
352 pTargetProduct->defaultRequested = pTargetProduct->requested = BOOTSTRAPPER_REQUEST_STATE_NONE;
353 continue;
354 }
355
356 pTargetProduct->defaultRequested = pTargetProduct->requested = pPackage->requested;
357
358 hr = UserExperienceOnPlanPatchTarget(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, &pTargetProduct->requested);
359 ExitOnRootFailure(hr, "BA aborted plan patch target.");
360 }
361
362LExit:
363 return hr;
364}
365
366//
367// PlanCalculate - calculates the execute and rollback state for the requested package state.
368//
369extern "C" HRESULT MspEnginePlanCalculatePackage(
370 __in BURN_PACKAGE* pPackage,
371 __in BOOL fInsideMsiTransaction
372 )
373{
374 HRESULT hr = S_OK;
375 BOOL fWillUninstallAll = TRUE;
376
377 for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
378 {
379 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i;
380
381 BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE;
382 BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
383
384 // Calculate the execute action.
385 switch (pTargetProduct->patchPackageState)
386 {
387 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
388 switch (pTargetProduct->requested)
389 {
390 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
391 execute = BOOTSTRAPPER_ACTION_STATE_REPAIR;
392 fWillUninstallAll = FALSE;
393 break;
394
395 case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough;
396 case BOOTSTRAPPER_REQUEST_STATE_CACHE:
397 execute = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
398 break;
399
400 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
401 execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
402 break;
403
404 default:
405 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
406 fWillUninstallAll = FALSE;
407 break;
408 }
409 break;
410
411 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
412 switch (pTargetProduct->requested)
413 {
414 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
415 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
416 execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
417 fWillUninstallAll = FALSE;
418 break;
419
420 default:
421 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
422 break;
423 }
424 break;
425
426 default:
427 if (pTargetProduct->fInstalled)
428 {
429 fWillUninstallAll = FALSE;
430 }
431 break;
432 }
433
434 // Calculate the rollback action if there is an execute action.
435 if (BOOTSTRAPPER_ACTION_STATE_NONE != execute && !fInsideMsiTransaction)
436 {
437 switch (pPackage->currentState)
438 {
439 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
440 switch (pTargetProduct->requested)
441 {
442 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
443 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
444 rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL;
445 break;
446
447 default:
448 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
449 break;
450 }
451 break;
452
453 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough;
454 switch (pTargetProduct->requested)
455 {
456 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
457 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
458 rollback = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
459 break;
460
461 default:
462 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
463 break;
464 }
465 break;
466
467 default:
468 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
469 break;
470 }
471 }
472
473 pTargetProduct->execute = execute;
474 pTargetProduct->rollback = rollback;
475
476 // The highest aggregate action state found will be returned.
477 if (pPackage->execute < execute)
478 {
479 pPackage->execute = execute;
480 }
481
482 if (pPackage->rollback < rollback)
483 {
484 pPackage->rollback = rollback;
485 }
486 }
487
488 // The dependency manager will do the wrong thing if the package level action is UNINSTALL
489 // when the patch will still be applied to at least one product.
490 if (!fWillUninstallAll && BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute)
491 {
492 pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
493 }
494
495 return hr;
496}
497
498//
499// PlanAdd - adds the calculated execute and rollback actions for the package.
500//
501extern "C" HRESULT MspEnginePlanAddPackage(
502 __in BOOTSTRAPPER_DISPLAY display,
503 __in BURN_USER_EXPERIENCE* pUserExperience,
504 __in BURN_PACKAGE* pPackage,
505 __in BURN_PLAN* pPlan,
506 __in BURN_LOGGING* pLog,
507 __in BURN_VARIABLES* pVariables,
508 __in_opt HANDLE hCacheEvent
509 )
510{
511 HRESULT hr = S_OK;
512
513 // TODO: need to handle the case where this patch adds itself to an earlier patch's list of target products. That would
514 // essentially bump this patch earlier in the plan and we need to make sure this patch is downloaded.
515 // add wait for cache
516 if (hCacheEvent)
517 {
518 hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent);
519 ExitOnFailure(hr, "Failed to plan package cache syncpoint");
520 }
521
522 hr = DependencyPlanPackage(NULL, pPackage, pPlan);
523 ExitOnFailure(hr, "Failed to plan package dependency actions.");
524
525 // Plan the actions for each target product code.
526 for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
527 {
528 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i;
529
530 // If the dependency manager changed the action state for the patch, change the target product actions.
531 if (pPackage->fDependencyManagerWasHere)
532 {
533 pTargetProduct->execute = pPackage->execute;
534 pTargetProduct->rollback = pPackage->rollback;
535 }
536
537 if (BOOTSTRAPPER_ACTION_STATE_NONE != pTargetProduct->execute)
538 {
539 hr = PlanTargetProduct(display, pUserExperience, FALSE, pPlan, pLog, pVariables, pTargetProduct->execute, pPackage, pTargetProduct, hCacheEvent);
540 ExitOnFailure(hr, "Failed to plan target product.");
541 }
542
543 if (BOOTSTRAPPER_ACTION_STATE_NONE != pTargetProduct->rollback)
544 {
545 hr = PlanTargetProduct(display, pUserExperience, TRUE, pPlan, pLog, pVariables, pTargetProduct->rollback, pPackage, pTargetProduct, hCacheEvent);
546 ExitOnFailure(hr, "Failed to plan rollback target product.");
547 }
548 }
549
550LExit:
551
552 return hr;
553}
554
555extern "C" HRESULT MspEngineExecutePackage(
556 __in_opt HWND hwndParent,
557 __in BURN_EXECUTE_ACTION* pExecuteAction,
558 __in BURN_VARIABLES* pVariables,
559 __in BOOL fRollback,
560 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
561 __in LPVOID pvContext,
562 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
563 )
564{
565 HRESULT hr = S_OK;
566 WIU_MSI_EXECUTE_CONTEXT context = { };
567 WIU_RESTART restart = WIU_RESTART_NONE;
568
569 LPWSTR sczCachedDirectory = NULL;
570 LPWSTR sczMspPath = NULL;
571 LPWSTR sczPatches = NULL;
572 LPWSTR sczProperties = NULL;
573 LPWSTR sczObfuscatedProperties = NULL;
574
575 // default to "verbose" logging
576 DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE;
577
578 // get cached MSP paths
579 for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i)
580 {
581 LPCWSTR wzAppend = NULL;
582 BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage;
583 BURN_PAYLOAD* pMspPackagePayload = pMspPackage->payloads.rgItems[0].pPayload;
584 AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Invalid package type added to ordered patches.");
585
586 if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->mspTarget.action)
587 {
588 hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory);
589 ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId);
590
591 // TODO: Figure out if this makes sense -- the variable is set to the last patch's path only
592 // Best effort to set the execute package cache folder variable.
593 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE);
594
595 hr = PathConcat(sczCachedDirectory, pMspPackagePayload->sczFilePath, &sczMspPath);
596 ExitOnFailure(hr, "Failed to build MSP path.");
597
598 wzAppend = sczMspPath;
599 }
600 else // uninstall
601 {
602 wzAppend = pMspPackage->Msp.sczPatchCode;
603 }
604
605 if (NULL != sczPatches)
606 {
607 hr = StrAllocConcat(&sczPatches, L";", 0);
608 ExitOnFailure(hr, "Failed to semi-colon delimit patches.");
609 }
610
611 hr = StrAllocConcat(&sczPatches, wzAppend, 0);
612 ExitOnFailure(hr, "Failed to append patch.");
613 }
614
615 // Best effort to set the execute package action variable.
616 VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->mspTarget.action, TRUE);
617
618 // Wire up the external UI handler and logging.
619 if (pExecuteAction->mspTarget.fDisableExternalUiHandler)
620 {
621 hr = WiuInitializeInternalUI(pExecuteAction->mspTarget.uiLevel, hwndParent, &context);
622 ExitOnFailure(hr, "Failed to initialize internal UI for MSP package.");
623 }
624 else
625 {
626 hr = WiuInitializeExternalUI(pfnMessageHandler, pExecuteAction->mspTarget.uiLevel, hwndParent, pvContext, fRollback, &context);
627 ExitOnFailure(hr, "Failed to initialize external UI handler.");
628 }
629
630 //if (BURN_LOGGING_LEVEL_DEBUG == logLevel)
631 //{
632 // dwLogMode | INSTALLLOGMODE_EXTRADEBUG;
633 //}
634
635 if (pExecuteAction->mspTarget.sczLogPath && *pExecuteAction->mspTarget.sczLogPath)
636 {
637 hr = WiuEnableLog(dwLogMode, pExecuteAction->mspTarget.sczLogPath, 0);
638 ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pExecuteAction->mspTarget.pPackage->sczId, pExecuteAction->mspTarget.sczLogPath);
639 }
640
641 // set up properties
642 hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczProperties, FALSE);
643 ExitOnFailure(hr, "Failed to add properties to argument string.");
644
645 hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE);
646 ExitOnFailure(hr, "Failed to add properties to obfuscated argument string.");
647
648 hr = MsiEngineConcatActionProperty(pExecuteAction->mspTarget.actionMsiProperty, &sczProperties);
649 ExitOnFailure(hr, "Failed to add action property to argument string.");
650
651 hr = MsiEngineConcatActionProperty(pExecuteAction->mspTarget.actionMsiProperty, &sczObfuscatedProperties);
652 ExitOnFailure(hr, "Failed to add action property to obfuscated argument string.");
653
654 LogId(REPORT_STANDARD, MSG_APPLYING_PATCH_PACKAGE, pExecuteAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pExecuteAction->mspTarget.action), sczPatches, sczObfuscatedProperties, pExecuteAction->mspTarget.sczTargetProductCode);
655
656 //
657 // Do the actual action.
658 //
659 switch (pExecuteAction->mspTarget.action)
660 {
661 case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough;
662 case BOOTSTRAPPER_ACTION_STATE_REPAIR:
663 hr = StrAllocConcatSecure(&sczProperties, L" PATCH=\"", 0);
664 ExitOnFailure(hr, "Failed to add PATCH property on install.");
665
666 hr = StrAllocConcatSecure(&sczProperties, sczPatches, 0);
667 ExitOnFailure(hr, "Failed to add patches to PATCH property on install.");
668
669 hr = StrAllocConcatSecure(&sczProperties, L"\" REBOOT=ReallySuppress", 0);
670 ExitOnFailure(hr, "Failed to add reboot suppression property on install.");
671
672 hr = WiuConfigureProductEx(pExecuteAction->mspTarget.sczTargetProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, sczProperties, &restart);
673 ExitOnFailure(hr, "Failed to install MSP package.");
674 break;
675
676 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
677 hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0);
678 ExitOnFailure(hr, "Failed to add reboot suppression property on uninstall.");
679
680 // Ignore all dependencies, since the Burn engine already performed the check.
681 hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES);
682 ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties.");
683
684 hr = WiuRemovePatches(sczPatches, pExecuteAction->mspTarget.sczTargetProductCode, sczProperties, &restart);
685 ExitOnFailure(hr, "Failed to uninstall MSP package.");
686 break;
687 }
688
689LExit:
690 WiuUninitializeExternalUI(&context);
691
692 ReleaseStr(sczCachedDirectory);
693 ReleaseStr(sczMspPath);
694 StrSecureZeroFreeString(sczProperties);
695 ReleaseStr(sczObfuscatedProperties);
696 ReleaseStr(sczPatches);
697
698 switch (restart)
699 {
700 case WIU_RESTART_NONE:
701 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
702 break;
703
704 case WIU_RESTART_REQUIRED:
705 *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
706 break;
707
708 case WIU_RESTART_INITIATED:
709 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
710 break;
711 }
712
713 // Best effort to clear the execute package cache folder and action variables.
714 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE);
715 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE, FALSE);
716
717 return hr;
718}
719
720extern "C" void MspEngineUpdateInstallRegistrationState(
721 __in BURN_EXECUTE_ACTION* pAction,
722 __in HRESULT hrExecute,
723 __in BOOL fInsideMsiTransaction
724 )
725{
726 BURN_PACKAGE_REGISTRATION_STATE newState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
727
728 if (FAILED(hrExecute))
729 {
730 ExitFunction();
731 }
732
733 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->mspTarget.action)
734 {
735 newState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
736 }
737 else
738 {
739 newState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
740 }
741
742 for (DWORD i = 0; i < pAction->mspTarget.cOrderedPatches; ++i)
743 {
744 BURN_ORDERED_PATCHES* pOrderedPatches = pAction->mspTarget.rgOrderedPatches + i;
745 BURN_PACKAGE* pPackage = pOrderedPatches->pPackage;
746 BURN_MSPTARGETPRODUCT* pTargetProduct = NULL;
747
748 Assert(BURN_PACKAGE_TYPE_MSP == pPackage->type);
749
750 if (!pPackage->fCanAffectRegistration)
751 {
752 continue;
753 }
754
755 for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j)
756 {
757 pTargetProduct = pPackage->Msp.rgTargetProducts + j;
758 if (pAction->mspTarget.fPerMachineTarget == (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context) &&
759 CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pAction->mspTarget.sczTargetProductCode, -1, pTargetProduct->wzTargetProductCode, -1))
760 {
761 break;
762 }
763
764 pTargetProduct = NULL;
765 }
766
767 if (!pTargetProduct)
768 {
769 AssertSz(pTargetProduct, "Ordered patch didn't have corresponding target product");
770 continue;
771 }
772
773 if (fInsideMsiTransaction)
774 {
775 pTargetProduct->transactionRegistrationState = newState;
776 }
777 else
778 {
779 pTargetProduct->registrationState = newState;
780 }
781 }
782
783LExit:
784 return;
785}
786
787extern "C" void MspEngineFinalizeInstallRegistrationState(
788 __in BURN_PACKAGE* pPackage
789 )
790{
791 if (!pPackage->fCanAffectRegistration)
792 {
793 ExitFunction();
794 }
795
796 if (!pPackage->Msp.cTargetProductCodes)
797 {
798 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
799 }
800 else
801 {
802 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
803
804 for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
805 {
806 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i;
807
808 if (pPackage->installRegistrationState < pTargetProduct->registrationState)
809 {
810 pPackage->installRegistrationState = pTargetProduct->registrationState;
811 }
812 }
813 }
814
815LExit:
816 return;
817}
818
819
820// internal helper functions
821
822static HRESULT GetPossibleTargetProductCodes(
823 __in BURN_PACKAGES* pPackages,
824 __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts,
825 __inout DWORD* pcPossibleTargetProducts
826 )
827{
828 HRESULT hr = S_OK;
829 STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes = NULL;
830 BOOL fCheckAll = FALSE;
831 WCHAR wzPossibleTargetProductCode[MAX_GUID_CHARS + 1];
832
833 // Use a dictionary to ensure we capture unique product codes. Otherwise, we could end up
834 // doing patch applicability for the same product code multiple times and that would confuse
835 // everything down stream.
836 hr = DictCreateStringList(&sdUniquePossibleTargetProductCodes, 5, DICT_FLAG_NONE);
837 ExitOnFailure(hr, "Failed to create unique target product codes.");
838
839 // If the patches target a specific set of product/upgrade codes, search only those. This
840 // should be much faster than searching all packages on the machine.
841 if (pPackages->rgPatchTargetCodes)
842 {
843 for (DWORD i = 0; i < pPackages->cPatchTargetCodes; ++i)
844 {
845 BURN_PATCH_TARGETCODE* pTargetCode = pPackages->rgPatchTargetCodes + i;
846
847 // If targeting a product, add the unique product code to the list.
848 if (BURN_PATCH_TARGETCODE_TYPE_PRODUCT == pTargetCode->type)
849 {
850 hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, pTargetCode->sczTargetCode, MSIINSTALLCONTEXT_NONE, prgPossibleTargetProducts, pcPossibleTargetProducts);
851 ExitOnFailure(hr, "Failed to add product code to possible target product codes.");
852 }
853 else if (BURN_PATCH_TARGETCODE_TYPE_UPGRADE == pTargetCode->type)
854 {
855 // Enumerate all unique related products to the target upgrade code.
856 for (DWORD iProduct = 0; SUCCEEDED(hr); ++iProduct)
857 {
858 hr = WiuEnumRelatedProducts(pTargetCode->sczTargetCode, iProduct, wzPossibleTargetProductCode);
859 if (SUCCEEDED(hr))
860 {
861 hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode, MSIINSTALLCONTEXT_NONE, prgPossibleTargetProducts, pcPossibleTargetProducts);
862 ExitOnFailure(hr, "Failed to add upgrade product code to possible target product codes.");
863 }
864 else if (E_BADCONFIGURATION == hr)
865 {
866 // Skip product's with bad configuration and continue.
867 LogId(REPORT_STANDARD, MSG_DETECT_BAD_PRODUCT_CONFIGURATION, wzPossibleTargetProductCode);
868
869 hr = S_OK;
870 }
871 }
872
873 if (E_NOMOREITEMS == hr)
874 {
875 hr = S_OK;
876 }
877 ExitOnFailure(hr, "Failed to enumerate all products to patch related to upgrade code: %ls", pTargetCode->sczTargetCode);
878 }
879 else
880 {
881 // The element does not target a specific product.
882 fCheckAll = TRUE;
883
884 break;
885 }
886 }
887 }
888 else
889 {
890 fCheckAll = TRUE;
891 }
892
893 // One or more of the patches do not target a specific product so search everything on the machine.
894 if (fCheckAll)
895 {
896 for (DWORD iProduct = 0; SUCCEEDED(hr); ++iProduct)
897 {
898 MSIINSTALLCONTEXT context = MSIINSTALLCONTEXT_NONE;
899
900 hr = WiuEnumProductsEx(NULL, NULL, MSIINSTALLCONTEXT_ALL, iProduct, wzPossibleTargetProductCode, &context, NULL, NULL);
901 if (SUCCEEDED(hr))
902 {
903 hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode, context, prgPossibleTargetProducts, pcPossibleTargetProducts);
904 ExitOnFailure(hr, "Failed to add product code to search product codes.");
905 }
906 else if (E_BADCONFIGURATION == hr)
907 {
908 // Skip products with bad configuration and continue.
909 LogId(REPORT_STANDARD, MSG_DETECT_BAD_PRODUCT_CONFIGURATION, wzPossibleTargetProductCode);
910
911 hr = S_OK;
912 }
913 }
914
915 if (E_NOMOREITEMS == hr)
916 {
917 hr = S_OK;
918 }
919 ExitOnFailure(hr, "Failed to enumerate all products on the machine for patches applicability.");
920 }
921
922LExit:
923 ReleaseDict(sdUniquePossibleTargetProductCodes);
924
925 return hr;
926}
927
928static HRESULT AddPossibleTargetProduct(
929 __in STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes,
930 __in_z LPCWSTR wzPossibleTargetProductCode,
931 __in MSIINSTALLCONTEXT context,
932 __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts,
933 __inout DWORD* pcPossibleTargetProducts
934 )
935{
936 HRESULT hr = S_OK;
937 LPWSTR pszLocalPackage = NULL;
938
939 // Only add this possible target code if we haven't queried for it already.
940 if (E_NOTFOUND == DictKeyExists(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode))
941 {
942 // If the install context is not known, ask the Windows Installer for it. If we can't get the context
943 // then bail.
944 if (MSIINSTALLCONTEXT_NONE == context)
945 {
946 hr = WiuEnumProductsEx(wzPossibleTargetProductCode, NULL, MSIINSTALLCONTEXT_ALL, 0, NULL, &context, NULL, NULL);
947 if (FAILED(hr))
948 {
949 ExitFunction1(hr = S_OK);
950 }
951 }
952
953 hr = DictAddKey(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode);
954 ExitOnFailure(hr, "Failed to add possible target code to unique product codes.");
955
956 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgPossibleTargetProducts), *pcPossibleTargetProducts + 1, sizeof(POSSIBLE_TARGETPRODUCT), 3);
957 ExitOnFailure(hr, "Failed to grow array of possible target products.");
958
959 POSSIBLE_TARGETPRODUCT *const pPossibleTargetProduct = *prgPossibleTargetProducts + *pcPossibleTargetProducts;
960
961 hr = ::StringCchCopyW(pPossibleTargetProduct->wzProductCode, countof(pPossibleTargetProduct->wzProductCode), wzPossibleTargetProductCode);
962 ExitOnFailure(hr, "Failed to copy possible target product code.");
963
964 // Attempt to get the local package path so we can more quickly determine patch applicability later.
965 hr = WiuGetProductInfoEx(wzPossibleTargetProductCode, NULL, context, INSTALLPROPERTY_LOCALPACKAGE, &pszLocalPackage);
966 if (SUCCEEDED(hr))
967 {
968 pPossibleTargetProduct->pszLocalPackage = pszLocalPackage;
969 pszLocalPackage = NULL;
970 }
971 else
972 {
973 // Will instead call MsiDeterminePatchSequence later.
974 hr = S_OK;
975 }
976
977 pPossibleTargetProduct->context = context;
978
979 ++(*pcPossibleTargetProducts);
980 }
981
982LExit:
983 ReleaseStr(pszLocalPackage);
984
985 return hr;
986}
987
988static HRESULT AddDetectedTargetProduct(
989 __in BURN_PACKAGE* pPackage,
990 __in DWORD dwOrder,
991 __in_z LPCWSTR wzProductCode,
992 __in MSIINSTALLCONTEXT context,
993 __out DWORD* pdwTargetProductIndex
994 )
995{
996 HRESULT hr = S_OK;
997 BURN_MSPTARGETPRODUCT* pTargetProduct = NULL;
998
999 *pdwTargetProductIndex = BURN_PACKAGE_INVALID_PATCH_INDEX;
1000
1001 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPackage->Msp.rgTargetProducts), pPackage->Msp.cTargetProductCodes + 1, sizeof(BURN_MSPTARGETPRODUCT), 5);
1002 ExitOnFailure(hr, "Failed to ensure enough target product codes were allocated.");
1003
1004 pTargetProduct = pPackage->Msp.rgTargetProducts + pPackage->Msp.cTargetProductCodes;
1005
1006 hr = ::StringCchCopyW(pTargetProduct->wzTargetProductCode, countof(pTargetProduct->wzTargetProductCode), wzProductCode);
1007 ExitOnFailure(hr, "Failed to copy target product code.");
1008
1009 pTargetProduct->context = context;
1010 pTargetProduct->dwOrder = dwOrder;
1011
1012 *pdwTargetProductIndex = pPackage->Msp.cTargetProductCodes;
1013 ++pPackage->Msp.cTargetProductCodes;
1014
1015LExit:
1016 return hr;
1017}
1018
1019static HRESULT AddMsiChainedPatch(
1020 __in BURN_PACKAGE* pPackage,
1021 __in BURN_PACKAGE* pMspPackage,
1022 __in DWORD dwMspTargetProductIndex,
1023 __out DWORD* pdwChainedPatchIndex
1024 )
1025{
1026 HRESULT hr = S_OK;
1027
1028 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPackage->Msi.rgChainedPatches), pPackage->Msi.cChainedPatches + 1, sizeof(BURN_CHAINED_PATCH), 5);
1029 ExitOnFailure(hr, "Failed to ensure enough chained patches were allocated.");
1030
1031 BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + pPackage->Msi.cChainedPatches;
1032 pChainedPatch->pMspPackage = pMspPackage;
1033 pChainedPatch->dwMspTargetProductIndex = dwMspTargetProductIndex;
1034
1035 *pdwChainedPatchIndex = pPackage->Msi.cChainedPatches;
1036 ++pPackage->Msi.cChainedPatches;
1037LExit:
1038 return hr;
1039}
1040
1041static HRESULT DeterminePatchChainedTarget(
1042 __in BURN_PACKAGES* pPackages,
1043 __in BURN_PACKAGE* pMspPackage,
1044 __in LPCWSTR wzTargetProductCode,
1045 __in DWORD dwMspTargetProductIndex
1046 )
1047{
1048 HRESULT hr = S_OK;
1049 DWORD dwChainedPatchIndex = 0;
1050 BURN_MSPTARGETPRODUCT* pTargetProduct = pMspPackage->Msp.rgTargetProducts + dwMspTargetProductIndex;
1051
1052 for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage)
1053 {
1054 BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage;
1055
1056 if (BURN_PACKAGE_TYPE_MSI == pPackage->type && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzTargetProductCode, -1, pPackage->Msi.sczProductCode, -1))
1057 {
1058 pTargetProduct->pChainedTargetPackage = pPackage;
1059
1060 hr = AddMsiChainedPatch(pPackage, pMspPackage, dwMspTargetProductIndex, &dwChainedPatchIndex);
1061 ExitOnFailure(hr, "Failed to add chained patch.");
1062
1063 for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j)
1064 {
1065 BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + j;
1066 if (pSlipstreamMsp->pMspPackage == pMspPackage)
1067 {
1068 AssertSz(BURN_PACKAGE_INVALID_PATCH_INDEX == pSlipstreamMsp->dwMsiChainedPatchIndex, "An MSP should only show up as a slipstreamed patch in an MSI once.");
1069 pTargetProduct->fSlipstream = TRUE;
1070 pSlipstreamMsp->dwMsiChainedPatchIndex = dwChainedPatchIndex;
1071 break;
1072 }
1073 }
1074
1075 break;
1076 }
1077 }
1078
1079LExit:
1080 return hr;
1081}
1082
1083static HRESULT PlanTargetProduct(
1084 __in BOOTSTRAPPER_DISPLAY display,
1085 __in BURN_USER_EXPERIENCE* pUserExperience,
1086 __in BOOL fRollback,
1087 __in BURN_PLAN* pPlan,
1088 __in BURN_LOGGING* pLog,
1089 __in BURN_VARIABLES* pVariables,
1090 __in BOOTSTRAPPER_ACTION_STATE actionState,
1091 __in BURN_PACKAGE* pPackage,
1092 __in BURN_MSPTARGETPRODUCT* pTargetProduct,
1093 __in_opt HANDLE hCacheEvent
1094 )
1095{
1096 HRESULT hr = S_OK;
1097 BURN_EXECUTE_ACTION* rgActions = fRollback ? pPlan->rgRollbackActions : pPlan->rgExecuteActions;
1098 DWORD cActions = fRollback ? pPlan->cRollbackActions : pPlan->cExecuteActions;
1099 BURN_EXECUTE_ACTION* pAction = NULL;
1100 DWORD dwInsertSequence = 0;
1101
1102 // Try to find another MSP action with the exact same action (install or uninstall) targeting
1103 // the same product in the same machine context (per-user or per-machine).
1104 for (DWORD i = 0; i < cActions; ++i)
1105 {
1106 pAction = rgActions + i;
1107
1108 if (BURN_EXECUTE_ACTION_TYPE_MSP_TARGET == pAction->type &&
1109 pAction->mspTarget.action == actionState &&
1110 pAction->mspTarget.fPerMachineTarget == (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context) &&
1111 CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pAction->mspTarget.sczTargetProductCode, -1, pTargetProduct->wzTargetProductCode, -1))
1112 {
1113 dwInsertSequence = i;
1114 break;
1115 }
1116
1117 pAction = NULL;
1118 }
1119
1120 // If we didn't find an MSP target action already updating the product, create a new action.
1121 if (!pAction)
1122 {
1123 if (fRollback)
1124 {
1125 hr = PlanAppendRollbackAction(pPlan, &pAction);
1126 }
1127 else
1128 {
1129 hr = PlanAppendExecuteAction(pPlan, &pAction);
1130 }
1131 ExitOnFailure(hr, "Failed to plan action for target product.");
1132
1133 pAction->type = BURN_EXECUTE_ACTION_TYPE_MSP_TARGET;
1134 pAction->mspTarget.action = actionState;
1135 pAction->mspTarget.pPackage = pPackage;
1136 pAction->mspTarget.fPerMachineTarget = (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context);
1137 pAction->mspTarget.pChainedTargetPackage = pTargetProduct->pChainedTargetPackage;
1138 pAction->mspTarget.fSlipstream = pTargetProduct->fSlipstream;
1139 hr = StrAllocString(&pAction->mspTarget.sczTargetProductCode, pTargetProduct->wzTargetProductCode, 0);
1140 ExitOnFailure(hr, "Failed to copy target product code.");
1141
1142 hr = MsiEngineCalculateInstallUiLevel(display, pUserExperience, pPackage->sczId, !fRollback, pAction->mspTarget.action,
1143 &pAction->mspTarget.actionMsiProperty, &pAction->mspTarget.uiLevel, &pAction->mspTarget.fDisableExternalUiHandler);
1144 ExitOnFailure(hr, "Failed to get msp ui options.");
1145
1146 // If this is a per-machine target product, then the plan needs to be per-machine as well.
1147 if (pAction->mspTarget.fPerMachineTarget)
1148 {
1149 pPlan->fPerMachine = TRUE;
1150 }
1151
1152 LoggingSetPackageVariable(pPackage, pAction->mspTarget.sczTargetProductCode, fRollback, pLog, pVariables, &pAction->mspTarget.sczLogPath); // ignore errors.
1153 }
1154 else
1155 {
1156 if (!fRollback && hCacheEvent)
1157 {
1158 // Since a previouse MSP target action is being updated with the new MSP,
1159 // insert a wait syncpoint to before this action since we need to cache the current MSI before using it.
1160 BURN_EXECUTE_ACTION* pWaitSyncPointAction = NULL;
1161 hr = PlanInsertExecuteAction(dwInsertSequence, pPlan, &pWaitSyncPointAction);
1162 ExitOnFailure(hr, "Failed to insert execute action.");
1163
1164 pWaitSyncPointAction->type = BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT;
1165 pWaitSyncPointAction->syncpoint.hEvent = hCacheEvent;
1166
1167 // Since we inserted an action before the MSP target action that we will be updating, need to update the pointer.
1168 pAction = pPlan->rgExecuteActions + (dwInsertSequence + 1);
1169 }
1170 }
1171
1172 // Add our target product to the array and sort based on their order determined during detection.
1173 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pAction->mspTarget.rgOrderedPatches), pAction->mspTarget.cOrderedPatches + 1, sizeof(BURN_ORDERED_PATCHES), 2);
1174 ExitOnFailure(hr, "Failed grow array of ordered patches.");
1175
1176 pAction->mspTarget.rgOrderedPatches[pAction->mspTarget.cOrderedPatches].pTargetProduct = pTargetProduct;
1177 pAction->mspTarget.rgOrderedPatches[pAction->mspTarget.cOrderedPatches].pPackage = pPackage;
1178 ++pAction->mspTarget.cOrderedPatches;
1179
1180 // Insertion sort to keep the patches ordered.
1181 for (DWORD i = pAction->mspTarget.cOrderedPatches - 1; i > 0; --i)
1182 {
1183 if (pAction->mspTarget.rgOrderedPatches[i].pTargetProduct->dwOrder < pAction->mspTarget.rgOrderedPatches[i - 1].pTargetProduct->dwOrder)
1184 {
1185 BURN_ORDERED_PATCHES temp = pAction->mspTarget.rgOrderedPatches[i - 1];
1186 pAction->mspTarget.rgOrderedPatches[i - 1] = pAction->mspTarget.rgOrderedPatches[i];
1187 pAction->mspTarget.rgOrderedPatches[i] = temp;
1188 }
1189 else // no swap necessary, we're done.
1190 {
1191 break;
1192 }
1193 }
1194
1195LExit:
1196 return hr;
1197}
diff --git a/src/burn/engine/mspengine.h b/src/burn/engine/mspengine.h
new file mode 100644
index 00000000..79998030
--- /dev/null
+++ b/src/burn/engine/mspengine.h
@@ -0,0 +1,84 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// constants
11
12
13// structures
14
15
16// typedefs
17
18
19// function declarations
20
21HRESULT MspEngineParsePackageFromXml(
22 __in IXMLDOMNode* pixnBundle,
23 __in BURN_PACKAGE* pPackage
24 );
25void MspEnginePackageUninitialize(
26 __in BURN_PACKAGE* pPackage
27 );
28HRESULT MspEngineDetectInitialize(
29 __in BURN_PACKAGES* pPackages
30 );
31HRESULT MspEngineAddDetectedTargetProduct(
32 __in BURN_PACKAGES* pPackages,
33 __in BURN_PACKAGE* pPackage,
34 __in DWORD dwOrder,
35 __in_z LPCWSTR wzProductCode,
36 __in MSIINSTALLCONTEXT context
37 );
38HRESULT MspEngineAddMissingSlipstreamTarget(
39 __in BURN_PACKAGE* pMsiPackage,
40 __in BURN_SLIPSTREAM_MSP* pSlipstreamMsp
41 );
42HRESULT MspEngineDetectPackage(
43 __in BURN_PACKAGE* pPackage,
44 __in BURN_USER_EXPERIENCE* pUserExperience
45 );
46HRESULT MspEnginePlanInitializePackage(
47 __in BURN_PACKAGE* pPackage,
48 __in BURN_USER_EXPERIENCE* pUserExperience
49 );
50HRESULT MspEnginePlanCalculatePackage(
51 __in BURN_PACKAGE* pPackage,
52 __in BOOL fInsideMsiTransaction
53 );
54HRESULT MspEnginePlanAddPackage(
55 __in BOOTSTRAPPER_DISPLAY display,
56 __in BURN_USER_EXPERIENCE* pUserExperience,
57 __in BURN_PACKAGE* pPackage,
58 __in BURN_PLAN* pPlan,
59 __in BURN_LOGGING* pLog,
60 __in BURN_VARIABLES* pVariables,
61 __in_opt HANDLE hCacheEvent
62 );
63HRESULT MspEngineExecutePackage(
64 __in_opt HWND hwndParent,
65 __in BURN_EXECUTE_ACTION* pExecuteAction,
66 __in BURN_VARIABLES* pVariables,
67 __in BOOL fRollback,
68 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
69 __in LPVOID pvContext,
70 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
71 );
72void MspEngineUpdateInstallRegistrationState(
73 __in BURN_EXECUTE_ACTION* pAction,
74 __in HRESULT hrExecute,
75 __in BOOL fInsideMsiTransaction
76 );
77void MspEngineFinalizeInstallRegistrationState(
78 __in BURN_PACKAGE* pPackage
79 );
80
81
82#if defined(__cplusplus)
83}
84#endif
diff --git a/src/burn/engine/msuengine.cpp b/src/burn/engine/msuengine.cpp
new file mode 100644
index 00000000..6003123b
--- /dev/null
+++ b/src/burn/engine/msuengine.cpp
@@ -0,0 +1,529 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// constants
7
8#define WU_S_REBOOT_REQUIRED 0x00240005L
9#define WU_S_ALREADY_INSTALLED 0x00240006L
10
11
12// function definitions
13static HRESULT EnsureWUServiceEnabled(
14 __in BOOL fStopWusaService,
15 __out SC_HANDLE* pschWu,
16 __out BOOL* pfPreviouslyDisabled
17 );
18static HRESULT SetServiceStartType(
19 __in SC_HANDLE sch,
20 __in DWORD stratType
21 );
22static HRESULT StopWUService(
23 __in SC_HANDLE schWu
24 );
25
26
27extern "C" HRESULT MsuEngineParsePackageFromXml(
28 __in IXMLDOMNode* pixnMsuPackage,
29 __in BURN_PACKAGE* pPackage
30 )
31{
32 HRESULT hr = S_OK;
33
34 // @KB
35 hr = XmlGetAttributeEx(pixnMsuPackage, L"KB", &pPackage->Msu.sczKB);
36 ExitOnFailure(hr, "Failed to get @KB.");
37
38 // @DetectCondition
39 hr = XmlGetAttributeEx(pixnMsuPackage, L"DetectCondition", &pPackage->Msu.sczDetectCondition);
40 ExitOnFailure(hr, "Failed to get @DetectCondition.");
41
42LExit:
43 return hr;
44}
45
46extern "C" void MsuEnginePackageUninitialize(
47 __in BURN_PACKAGE* pPackage
48 )
49{
50 ReleaseNullStr(pPackage->Msu.sczKB);
51 ReleaseNullStr(pPackage->Msu.sczDetectCondition);
52}
53
54extern "C" HRESULT MsuEngineDetectPackage(
55 __in BURN_PACKAGE* pPackage,
56 __in BURN_VARIABLES* pVariables
57 )
58{
59 HRESULT hr = S_OK;
60 BOOL fDetected = FALSE;
61
62 // evaluate detect condition
63 if (pPackage->Msu.sczDetectCondition && *pPackage->Msu.sczDetectCondition)
64 {
65 hr = ConditionEvaluate(pVariables, pPackage->Msu.sczDetectCondition, &fDetected);
66 ExitOnFailure(hr, "Failed to evaluate MSU package detect condition.");
67 }
68
69 // update detect state
70 pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
71
72 if (pPackage->fCanAffectRegistration)
73 {
74 pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
75 }
76
77LExit:
78 return hr;
79}
80
81//
82// PlanCalculate - calculates the execute and rollback state for the requested package state.
83//
84extern "C" HRESULT MsuEnginePlanCalculatePackage(
85 __in BURN_PACKAGE* pPackage
86 )
87{
88 HRESULT hr = S_OK;
89 BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE;
90 BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
91 BOOL fAllowUninstall = FALSE;
92
93 // We can only uninstall MSU packages if they have a KB and we are on Win7 or newer.
94 fAllowUninstall = pPackage->Msu.sczKB && *pPackage->Msu.sczKB && ::IsWindows7OrGreater();
95
96 // execute action
97 switch (pPackage->currentState)
98 {
99 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
100 switch (pPackage->requested)
101 {
102 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
103 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
104 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
105 break;
106
107 case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough;
108 case BOOTSTRAPPER_REQUEST_STATE_CACHE:
109 execute = fAllowUninstall && pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
110 break;
111
112 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
113 execute = fAllowUninstall ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
114 break;
115
116 default:
117 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
118 break;
119 }
120 break;
121
122 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
123 switch (pPackage->requested)
124 {
125 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
126 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
127 execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
128 break;
129
130 default:
131 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
132 break;
133 }
134 break;
135
136 default:
137 hr = E_INVALIDARG;
138 ExitOnRootFailure(hr, "Invalid package state.");
139 }
140
141 // Calculate the rollback action if there is an execute action.
142 if (BOOTSTRAPPER_ACTION_STATE_NONE != execute)
143 {
144 switch (pPackage->currentState)
145 {
146 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
147 switch (pPackage->requested)
148 {
149 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
150 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
151 rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL;
152 break;
153
154 default:
155 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
156 break;
157 }
158 break;
159
160 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
161 switch (pPackage->requested)
162 {
163 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
164 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
165 rollback = fAllowUninstall ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
166 break;
167
168 default:
169 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
170 break;
171 }
172 break;
173
174 default:
175 hr = E_INVALIDARG;
176 ExitOnRootFailure(hr, "Invalid package expected state.");
177 }
178 }
179
180 // return values
181 pPackage->execute = execute;
182 pPackage->rollback = rollback;
183
184LExit:
185 return hr;
186}
187
188//
189// PlanAdd - adds the calculated execute and rollback actions for the package.
190//
191extern "C" HRESULT MsuEnginePlanAddPackage(
192 __in BURN_PACKAGE* pPackage,
193 __in BURN_PLAN* pPlan,
194 __in BURN_LOGGING* pLog,
195 __in BURN_VARIABLES* pVariables,
196 __in HANDLE hCacheEvent
197 )
198{
199 HRESULT hr = S_OK;
200 BURN_EXECUTE_ACTION* pAction = NULL;
201
202 // add wait for cache
203 if (hCacheEvent)
204 {
205 hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent);
206 ExitOnFailure(hr, "Failed to plan package cache syncpoint");
207 }
208
209 hr = DependencyPlanPackage(NULL, pPackage, pPlan);
210 ExitOnFailure(hr, "Failed to plan package dependency actions.");
211
212 // add execute action
213 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
214 {
215 hr = PlanAppendExecuteAction(pPlan, &pAction);
216 ExitOnFailure(hr, "Failed to append execute action.");
217
218 pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE;
219 pAction->msuPackage.pPackage = pPackage;
220 pAction->msuPackage.action = pPackage->execute;
221
222 LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors.
223 }
224
225 // add rollback action
226 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
227 {
228 hr = PlanAppendRollbackAction(pPlan, &pAction);
229 ExitOnFailure(hr, "Failed to append rollback action.");
230
231 pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE;
232 pAction->msuPackage.pPackage = pPackage;
233 pAction->msuPackage.action = pPackage->rollback;
234
235 LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors.
236 }
237
238LExit:
239 return hr;
240}
241
242extern "C" HRESULT MsuEngineExecutePackage(
243 __in BURN_EXECUTE_ACTION* pExecuteAction,
244 __in BURN_VARIABLES* pVariables,
245 __in BOOL fRollback,
246 __in BOOL fStopWusaService,
247 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
248 __in LPVOID pvContext,
249 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
250 )
251{
252 HRESULT hr = S_OK;
253 int nResult = IDNOACTION;
254 LPWSTR sczCachedDirectory = NULL;
255 LPWSTR sczMsuPath = NULL;
256 LPWSTR sczWindowsPath = NULL;
257 LPWSTR sczSystemPath = NULL;
258 LPWSTR sczWusaPath = NULL;
259 LPWSTR sczCommand = NULL;
260 SC_HANDLE schWu = NULL;
261 BOOL fWuWasDisabled = FALSE;
262 STARTUPINFOW si = { };
263 PROCESS_INFORMATION pi = { };
264 GENERIC_EXECUTE_MESSAGE message = { };
265 DWORD dwExitCode = 0;
266 BOOL fUseSysNativePath = FALSE;
267 BURN_PACKAGE* pPackage = pExecuteAction->msuPackage.pPackage;
268 BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload;
269
270#if !defined(_WIN64)
271 hr = ProcWow64(::GetCurrentProcess(), &fUseSysNativePath);
272 ExitOnFailure(hr, "Failed to determine WOW64 status.");
273#endif
274
275 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
276
277 // get wusa.exe path
278 if (fUseSysNativePath)
279 {
280 hr = PathGetKnownFolder(CSIDL_WINDOWS, &sczWindowsPath);
281 ExitOnFailure(hr, "Failed to find Windows directory.");
282
283 hr = PathConcat(sczWindowsPath, L"SysNative\\", &sczSystemPath);
284 ExitOnFailure(hr, "Failed to append SysNative directory.");
285 }
286 else
287 {
288 hr = PathGetKnownFolder(CSIDL_SYSTEM, &sczSystemPath);
289 ExitOnFailure(hr, "Failed to find System32 directory.");
290 }
291
292 hr = PathConcat(sczSystemPath, L"wusa.exe", &sczWusaPath);
293 ExitOnFailure(hr, "Failed to allocate WUSA.exe path.");
294
295 // build command
296 switch (pExecuteAction->msuPackage.action)
297 {
298 case BOOTSTRAPPER_ACTION_STATE_INSTALL:
299 // get cached MSU path
300 hr = CacheGetCompletedPath(TRUE, pPackage->sczCacheId, &sczCachedDirectory);
301 ExitOnFailure(hr, "Failed to get cached path for package: %ls", pPackage->sczId);
302
303 // Best effort to set the execute package cache folder variable.
304 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE);
305
306 hr = PathConcat(sczCachedDirectory, pPackagePayload->sczFilePath, &sczMsuPath);
307 ExitOnFailure(hr, "Failed to build MSU path.");
308
309 // format command
310 hr = StrAllocFormatted(&sczCommand, L"\"%ls\" \"%ls\" /quiet /norestart", sczWusaPath, sczMsuPath);
311 ExitOnFailure(hr, "Failed to format MSU install command.");
312 break;
313
314 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
315 // format command
316 hr = StrAllocFormatted(&sczCommand, L"\"%ls\" /uninstall /kb:%ls /quiet /norestart", sczWusaPath, pPackage->Msu.sczKB);
317 ExitOnFailure(hr, "Failed to format MSU uninstall command.");
318 break;
319
320 default:
321 hr = E_UNEXPECTED;
322 ExitOnFailure(hr, "Failed to get action arguments for MSU package.");
323 }
324
325 if (pExecuteAction->msuPackage.sczLogPath && *pExecuteAction->msuPackage.sczLogPath)
326 {
327 hr = StrAllocConcat(&sczCommand, L" /log:", 0);
328 ExitOnFailure(hr, "Failed to append log switch to MSU command-line.");
329
330 hr = StrAllocConcat(&sczCommand, pExecuteAction->msuPackage.sczLogPath, 0);
331 ExitOnFailure(hr, "Failed to append log path to MSU command-line.");
332 }
333
334 LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, LoggingActionStateToString(pExecuteAction->msuPackage.action), sczMsuPath ? sczMsuPath : pPackage->Msu.sczKB, sczCommand);
335
336 hr = EnsureWUServiceEnabled(fStopWusaService, &schWu, &fWuWasDisabled);
337 ExitOnFailure(hr, "Failed to ensure WU service was enabled to install MSU package.");
338
339 // create process
340 si.cb = sizeof(si);
341 if (!::CreateProcessW(sczWusaPath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
342 {
343 ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", sczWusaPath);
344 }
345
346 do
347 {
348 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
349 message.dwAllowedResults = MB_OKCANCEL;
350 message.progress.dwPercentage = 50;
351 nResult = pfnGenericMessageHandler(&message, pvContext);
352 hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
353 ExitOnRootFailure(hr, "Bootstrapper application aborted during MSU progress.");
354
355 // wait for process to terminate
356 hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode);
357 if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr)
358 {
359 ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", sczWusaPath);
360 }
361 } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr);
362
363 // get process exit code
364 if (!::GetExitCodeProcess(pi.hProcess, &dwExitCode))
365 {
366 ExitWithLastError(hr, "Failed to get process exit code.");
367 }
368
369 // We'll normalize the restart required error code from wusa.exe just in case. Most likely
370 // that on reboot we'll actually get WU_S_REBOOT_REQUIRED.
371 if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast<HRESULT>(dwExitCode))
372 {
373 dwExitCode = ERROR_SUCCESS_REBOOT_REQUIRED;
374 }
375
376 // handle exit code
377 switch (dwExitCode)
378 {
379 case S_OK: __fallthrough;
380 case S_FALSE: __fallthrough;
381 case WU_S_ALREADY_INSTALLED:
382 hr = S_OK;
383 break;
384
385 case ERROR_SUCCESS_REBOOT_REQUIRED: __fallthrough;
386 case WU_S_REBOOT_REQUIRED:
387 *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
388 hr = S_OK;
389 break;
390
391 default:
392 hr = static_cast<HRESULT>(dwExitCode);
393 break;
394 }
395
396LExit:
397 ReleaseStr(sczCachedDirectory);
398 ReleaseStr(sczMsuPath);
399 ReleaseStr(sczSystemPath);
400 ReleaseStr(sczWindowsPath);
401 ReleaseStr(sczWusaPath);
402 ReleaseStr(sczCommand);
403
404 ReleaseHandle(pi.hProcess);
405 ReleaseHandle(pi.hThread);
406
407 if (fWuWasDisabled)
408 {
409 SetServiceStartType(schWu, SERVICE_DISABLED);
410 }
411
412 // Best effort to clear the execute package cache folder variable.
413 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE);
414
415 return hr;
416}
417
418extern "C" void MsuEngineUpdateInstallRegistrationState(
419 __in BURN_EXECUTE_ACTION* pAction,
420 __in HRESULT hrExecute
421 )
422{
423 BURN_PACKAGE* pPackage = pAction->msuPackage.pPackage;
424
425 if (FAILED(hrExecute) || !pPackage->fCanAffectRegistration)
426 {
427 ExitFunction();
428 }
429
430 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->msuPackage.action)
431 {
432 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
433 }
434 else
435 {
436 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
437 }
438
439LExit:
440 return;
441}
442
443static HRESULT EnsureWUServiceEnabled(
444 __in BOOL fStopWusaService,
445 __out SC_HANDLE* pschWu,
446 __out BOOL* pfPreviouslyDisabled
447 )
448{
449 HRESULT hr = S_OK;
450 SC_HANDLE schSCM = NULL;
451 SC_HANDLE schWu = NULL;
452 SERVICE_STATUS serviceStatus = { };
453 QUERY_SERVICE_CONFIGW* pConfig = NULL;
454
455 schSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
456 ExitOnNullWithLastError(schSCM, hr, "Failed to open service control manager.");
457
458 schWu = ::OpenServiceW(schSCM, L"wuauserv", SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_STOP );
459 ExitOnNullWithLastError(schWu, hr, "Failed to open WU service.");
460
461 if (!::QueryServiceStatus(schWu, &serviceStatus) )
462 {
463 ExitWithLastError(hr, "Failed to query status of WU service.");
464 }
465
466 // Stop service if requested to.
467 if (SERVICE_STOPPED != serviceStatus.dwCurrentState && fStopWusaService)
468 {
469 hr = StopWUService(schWu);
470 }
471
472 // If the service is not running then it might be disabled so let's check.
473 if (SERVICE_RUNNING != serviceStatus.dwCurrentState)
474 {
475 hr = SvcQueryConfig(schWu, &pConfig);
476 ExitOnFailure(hr, "Failed to read configuration for WU service.");
477
478 // If WU is disabled, change it to a demand start service (but touch nothing else).
479 if (SERVICE_DISABLED == pConfig->dwStartType)
480 {
481 hr = SetServiceStartType(schWu, SERVICE_DEMAND_START);
482 ExitOnFailure(hr, "Failed to mark WU service to start on demand.");
483
484 *pfPreviouslyDisabled = TRUE;
485 }
486 }
487
488 *pschWu = schWu;
489 schWu = NULL;
490
491LExit:
492 ReleaseMem(pConfig);
493 ReleaseServiceHandle(schWu);
494 ReleaseServiceHandle(schSCM);
495
496 return hr;
497}
498
499static HRESULT SetServiceStartType(
500 __in SC_HANDLE sch,
501 __in DWORD startType
502 )
503{
504 HRESULT hr = S_OK;
505
506 if (!::ChangeServiceConfigW(sch, SERVICE_NO_CHANGE, startType, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
507 {
508 ExitWithLastError(hr, "Failed to set service start type.");
509 }
510
511LExit:
512 return hr;
513}
514
515static HRESULT StopWUService(
516 __in SC_HANDLE schWu
517 )
518{
519 HRESULT hr = S_OK;
520 SERVICE_STATUS serviceStatus = { };
521
522 if(!::ControlService(schWu, SERVICE_CONTROL_STOP, &serviceStatus))
523 {
524 ExitWithLastError(hr, "Failed to stop wusa service.");
525 }
526
527LExit:
528 return hr;
529}
diff --git a/src/burn/engine/msuengine.h b/src/burn/engine/msuengine.h
new file mode 100644
index 00000000..fda7a5ab
--- /dev/null
+++ b/src/burn/engine/msuengine.h
@@ -0,0 +1,50 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// function declarations
11
12HRESULT MsuEngineParsePackageFromXml(
13 __in IXMLDOMNode* pixnMsiPackage,
14 __in BURN_PACKAGE* pPackage
15 );
16void MsuEnginePackageUninitialize(
17 __in BURN_PACKAGE* pPackage
18 );
19HRESULT MsuEngineDetectPackage(
20 __in BURN_PACKAGE* pPackage,
21 __in BURN_VARIABLES* pVariables
22 );
23HRESULT MsuEnginePlanCalculatePackage(
24 __in BURN_PACKAGE* pPackage
25 );
26HRESULT MsuEnginePlanAddPackage(
27 __in BURN_PACKAGE* pPackage,
28 __in BURN_PLAN* pPlan,
29 __in BURN_LOGGING* pLog,
30 __in BURN_VARIABLES* pVariables,
31 __in HANDLE hCacheEvent
32 );
33HRESULT MsuEngineExecutePackage(
34 __in BURN_EXECUTE_ACTION* pExecuteAction,
35 __in BURN_VARIABLES* pVariables,
36 __in BOOL fRollback,
37 __in BOOL fStopWusaService,
38 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
39 __in LPVOID pvContext,
40 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
41 );
42void MsuEngineUpdateInstallRegistrationState(
43 __in BURN_EXECUTE_ACTION* pAction,
44 __in HRESULT hrExecute
45 );
46
47
48#if defined(__cplusplus)
49}
50#endif
diff --git a/src/burn/engine/netfxchainer.cpp b/src/burn/engine/netfxchainer.cpp
new file mode 100644
index 00000000..4e7a7720
--- /dev/null
+++ b/src/burn/engine/netfxchainer.cpp
@@ -0,0 +1,418 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5static VOID DestroyNetFxChainer(
6 __in NetFxChainer* pChainer
7 )
8{
9 if (pChainer)
10 {
11 ReleaseHandle(pChainer->hSection);
12 ReleaseHandle(pChainer->hEventChaineeSend);
13 ReleaseHandle(pChainer->hEventChainerSend);
14 ReleaseHandle(pChainer->hMutex);
15
16 if (pChainer->pData)
17 {
18 ::UnmapViewOfFile(pChainer->pData);
19 }
20
21 MemFree(pChainer);
22 }
23}
24
25static HRESULT CreateNetFxChainer(
26 __in LPCWSTR wzSectionName,
27 __in LPCWSTR wzEventName,
28 __out NetFxChainer** ppChainer
29 )
30{
31 HRESULT hr = S_OK;
32 LPWSTR sczName = NULL;
33 NetFxChainer* pChainer = NULL;
34
35 pChainer = (NetFxChainer*)MemAlloc(sizeof(NetFxChainer), TRUE);
36 ExitOnNull(pChainer, hr, E_OUTOFMEMORY, "Failed to allocate memory for NetFxChainer struct.");
37
38 pChainer->hEventChaineeSend = ::CreateEvent(NULL, FALSE, FALSE, wzEventName);
39 ExitOnNullWithLastError(pChainer->hEventChaineeSend, hr, "Failed to create event: %ls", wzEventName);
40
41 hr = StrAllocFormatted(&sczName, L"%ls_send", wzEventName);
42 ExitOnFailure(hr, "failed to allocate memory for event name");
43
44 pChainer->hEventChainerSend = ::CreateEvent(NULL, FALSE, FALSE, sczName);
45 ExitOnNullWithLastError(pChainer->hEventChainerSend, hr, "Failed to create event: %ls", sczName);
46
47 hr = StrAllocFormatted(&sczName, L"%ls_mutex", wzEventName);
48 ExitOnFailure(hr, "failed to allocate memory for mutex name");
49
50 // Create the mutex, we initially own
51 pChainer->hMutex = ::CreateMutex(NULL, TRUE, sczName);
52 ExitOnNullWithLastError(pChainer->hMutex, hr, "Failed to create mutex: %ls", sczName);
53
54 pChainer->hSection = ::CreateFileMapping(INVALID_HANDLE_VALUE,
55 NULL, // security attributes
56 PAGE_READWRITE,
57 0, // high-order DWORD of maximum size
58 NETFXDATA_SIZE, // low-order DWORD of maximum size
59 wzSectionName);
60 ExitOnNullWithLastError(pChainer->hSection, hr, "Failed to memory map cabinet file: %ls", wzSectionName);
61
62 pChainer->pData = reinterpret_cast<NetFxDataStructure*>(::MapViewOfFile(pChainer->hSection,
63 FILE_MAP_WRITE,
64 0, 0, // offsets
65 0 // map entire file
66 ));
67 ExitOnNullWithLastError(pChainer->pData, hr, "Failed to MapViewOfFile for %ls.", wzSectionName);
68
69 // Initialize the shared memory
70 hr = ::StringCchCopyW(pChainer->pData->szEventName, countof(pChainer->pData->szEventName), wzEventName);
71 ExitOnFailure(hr, "failed to copy event name to shared memory structure.");
72 pChainer->pData->downloadFinished = false;
73 pChainer->pData->downloadSoFar = 0;
74 pChainer->pData->hrDownloadFinished = E_PENDING;
75 pChainer->pData->downloadAbort = false;
76 pChainer->pData->installFinished = false;
77 pChainer->pData->installSoFar = 0;
78 pChainer->pData->hrInstallFinished = E_PENDING;
79 pChainer->pData->installAbort = false;
80 pChainer->pData->hrInternalError = S_OK;
81 pChainer->pData->version = NETFXDATA_VERSION;
82 pChainer->pData->messageCode = 0;
83 pChainer->pData->messageResponse = 0;
84 pChainer->pData->messageDataLength = 0;
85
86 // Done with initialization, allow others to access.
87 ::ReleaseMutex(pChainer->hMutex);
88
89 *ppChainer = pChainer;
90 pChainer = NULL;
91
92LExit:
93 ReleaseStr(sczName);
94
95 if (pChainer)
96 {
97 // Something failed, release the mutex and destroy the object
98 if (pChainer->hMutex)
99 {
100 ::ReleaseMutex(pChainer->hMutex);
101 }
102
103 DestroyNetFxChainer(pChainer);
104 }
105
106 return hr;
107}
108
109
110static VOID NetFxAbort(
111 __in NetFxChainer* pChainer
112 )
113{
114 ::WaitForSingleObject(pChainer->hMutex, INFINITE);
115
116 pChainer->pData->downloadAbort = true;
117 pChainer->pData->installAbort = true;
118
119 ::ReleaseMutex(pChainer->hMutex);
120
121 ::SetEvent(pChainer->hEventChainerSend);
122}
123
124static BYTE NetFxGetProgress(
125 __in NetFxChainer* pChainer
126 )
127{
128 BYTE bProgress = 0;
129 ::WaitForSingleObject(pChainer->hMutex, INFINITE);
130
131 bProgress = (pChainer->pData->installSoFar + pChainer->pData->downloadSoFar) / 2;
132
133 ::ReleaseMutex(pChainer->hMutex);
134
135 return bProgress;
136}
137
138static HRESULT NetFxGetMessage(
139 __in NetFxChainer* pChainer,
140 __out DWORD* pdwMessage,
141 __out LPVOID* ppBuffer,
142 __out DWORD* pdwBufferSize
143 )
144{
145 HRESULT hr = S_OK;
146 ::WaitForSingleObject(pChainer->hMutex, INFINITE);
147
148 *pdwMessage = pChainer->pData->messageCode;
149 *ppBuffer = NULL;
150 *pdwBufferSize = 0;
151
152 if (NETFX_NO_MESSAGE != *pdwMessage)
153 {
154 *ppBuffer = MemAlloc(pChainer->pData->messageDataLength, TRUE);
155 ExitOnNull(*ppBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for message data");
156
157 memcpy(*ppBuffer, pChainer->pData->messageData, pChainer->pData->messageDataLength);
158 *pdwBufferSize = pChainer->pData->messageDataLength;
159 }
160
161LExit:
162 ::ReleaseMutex(pChainer->hMutex);
163
164 return hr;
165}
166
167static void NetFxRespond(
168 __in NetFxChainer* pChainer,
169 __in DWORD dwResponse
170 )
171{
172 ::WaitForSingleObject(pChainer->hMutex, INFINITE);
173
174 pChainer->pData->messageCode = NETFX_NO_MESSAGE;
175 pChainer->pData->messageResponse = dwResponse;
176 if (IDCANCEL == dwResponse)
177 {
178 pChainer->pData->downloadAbort = true;
179 pChainer->pData->installAbort = true;
180 }
181
182 ::ReleaseMutex(pChainer->hMutex);
183
184 ::SetEvent(pChainer->hEventChainerSend);
185}
186
187static HRESULT NetFxGetResult(
188 __in NetFxChainer* pChainer,
189 __out HRESULT* phrInternalError
190 )
191{
192 HRESULT hr = S_OK;
193 ::WaitForSingleObject(pChainer->hMutex, INFINITE);
194
195 hr = pChainer->pData->hrInstallFinished;
196
197 if (FAILED(pChainer->pData->hrDownloadFinished) && // Download failed
198 (S_OK == hr || E_ABORT == hr)) // Install succeeded or was aborted
199 {
200 hr = pChainer->pData->hrDownloadFinished;
201 }
202
203 if (phrInternalError)
204 {
205 *phrInternalError = pChainer->pData->hrInternalError;
206 }
207
208 ::ReleaseMutex(pChainer->hMutex);
209
210 return hr;
211}
212
213static HRESULT OnNetFxFilesInUse(
214 __in NetFxChainer* pNetfxChainer,
215 __in NetFxCloseApplications* pCloseApps,
216 __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler,
217 __in LPVOID pvContext
218 )
219{
220 HRESULT hr = S_OK;
221 DWORD cFiles = 0;
222 LPWSTR* rgwzFiles = NULL;
223 GENERIC_EXECUTE_MESSAGE message = { };
224 DWORD dwResponse = 0;
225
226 cFiles = pCloseApps->dwApplicationsSize;
227 rgwzFiles = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cFiles, TRUE);
228 ExitOnNull(rgwzFiles, hr, E_OUTOFMEMORY, "Failed to allocate buffer.");
229
230 for (DWORD i = 0; i < pCloseApps->dwApplicationsSize; ++i)
231 {
232 rgwzFiles[i] = pCloseApps->applications[i].szName;
233 }
234
235 // send message
236 message.type = GENERIC_EXECUTE_MESSAGE_FILES_IN_USE;
237 message.dwAllowedResults = MB_ABORTRETRYIGNORE;
238 message.filesInUse.cFiles = cFiles;
239 message.filesInUse.rgwzFiles = (LPCWSTR*)rgwzFiles;
240 dwResponse = (DWORD)pfnMessageHandler(&message, pvContext);
241
242 NetFxRespond(pNetfxChainer, dwResponse);
243
244LExit:
245 ReleaseMem(rgwzFiles);
246
247 return hr;
248}
249
250static HRESULT OnNetFxProgress(
251 __in NetFxChainer* pNetfxChainer,
252 __in BYTE bProgress,
253 __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler,
254 __in LPVOID pvContext
255 )
256{
257 GENERIC_EXECUTE_MESSAGE message = { };
258 DWORD dwResponse = 0;
259
260 // send message
261 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
262 message.dwAllowedResults = MB_OKCANCEL;
263 message.progress.dwPercentage = 100 * (DWORD)bProgress / BYTE_MAX;
264 dwResponse = (DWORD)pfnMessageHandler(&message, pvContext);
265
266 if (IDCANCEL == dwResponse)
267 {
268 NetFxAbort(pNetfxChainer);
269 }
270
271 return S_OK;
272}
273
274static HRESULT OnNetFxError(
275 __in NetFxChainer* /*pNetfxChainer*/,
276 __in HRESULT hrError,
277 __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler,
278 __in LPVOID pvContext
279 )
280{
281 GENERIC_EXECUTE_MESSAGE message = { };
282 DWORD dwResponse = 0;
283
284 // send message
285 message.type = GENERIC_EXECUTE_MESSAGE_ERROR;
286 message.dwAllowedResults = MB_OK;
287 message.error.dwErrorCode = hrError;
288 message.error.wzMessage = NULL;
289 dwResponse = (DWORD)pfnMessageHandler(&message, pvContext);
290
291 return S_OK;
292}
293
294static HRESULT ProcessNetFxMessage(
295 __in NetFxChainer* pNetfxChainer,
296 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
297 __in LPVOID pvContext
298 )
299{
300 HRESULT hr = S_OK;
301 DWORD dwMessage = NETFX_NO_MESSAGE;
302 DWORD dwBufferSize = 0;
303 LPVOID pBuffer = NULL;
304
305 // send progress
306 hr = OnNetFxProgress(pNetfxChainer, NetFxGetProgress(pNetfxChainer), pfnGenericMessageHandler, pvContext);
307 ExitOnFailure(hr, "Failed to send progress from netfx chainer.");
308
309 // Check for message
310 hr = NetFxGetMessage(pNetfxChainer, &dwMessage, &pBuffer, &dwBufferSize);
311 ExitOnFailure(hr, "Failed to get message from netfx chainer.");
312
313 switch(dwMessage)
314 {
315 case NETFX_CLOSE_APPS:
316 hr = OnNetFxFilesInUse(pNetfxChainer, (NetFxCloseApplications*)pBuffer, pfnGenericMessageHandler, pvContext);
317 ExitOnFailure(hr, "Failed to send files in use message from netfx chainer.");
318 break;
319
320 default:
321 // No message we understand.
322 break;
323 }
324
325LExit:
326 ReleaseMem(pBuffer);
327
328 return hr;
329}
330
331extern "C" HRESULT NetFxRunChainer(
332 __in LPCWSTR wzExecutablePath,
333 __in LPCWSTR wzArguments,
334 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
335 __in LPVOID pvContext,
336 __out DWORD* pdwExitCode
337 )
338{
339 HRESULT hr = S_OK;
340 DWORD er = 0;
341 WCHAR wzGuid[GUID_STRING_LENGTH];
342 LPWSTR sczEventName = NULL;
343 LPWSTR sczSectionName = NULL;
344 LPWSTR sczCommand = NULL;
345 NetFxChainer* pNetfxChainer = NULL;
346 STARTUPINFOW si = { };
347 PROCESS_INFORMATION pi = { };
348 HRESULT hrInternalError = 0;
349
350 // Create the unique name suffix.
351 hr = GuidFixedCreate(wzGuid);
352 ExitOnRootFailure(hr, "Failed to create netfx chainer guid.");
353
354 hr = StrAllocFormatted(&sczSectionName, L"NetFxSection.%ls", wzGuid);
355 ExitOnFailure(hr, "Failed to allocate section name.");
356
357 hr = StrAllocFormatted(&sczEventName, L"NetFxEvent.%ls", wzGuid);
358 ExitOnFailure(hr, "Failed to allocate event name.");
359
360 hr = CreateNetFxChainer(sczSectionName, sczEventName, &pNetfxChainer);
361 ExitOnFailure(hr, "Failed to create netfx chainer.");
362
363 hr = StrAllocFormattedSecure(&sczCommand, L"%ls /pipe %ls", wzArguments, sczSectionName);
364 ExitOnFailure(hr, "Failed to allocate netfx chainer arguments.");
365
366 si.cb = sizeof(si);
367 if (!::CreateProcessW(wzExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
368 {
369 ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", wzExecutablePath);
370 }
371
372 HANDLE handles[2] = { pi.hProcess, pNetfxChainer->hEventChaineeSend };
373
374 for (;;)
375 {
376 er = ::WaitForMultipleObjects(2, handles, FALSE, 100);
377 if (WAIT_OBJECT_0 == er)
378 {
379 // Process has exited
380 *pdwExitCode = NetFxGetResult(pNetfxChainer, &hrInternalError);
381 if (E_PENDING == *pdwExitCode)
382 {
383 if (!::GetExitCodeProcess(pi.hProcess, pdwExitCode))
384 {
385 ExitWithLastError(hr, "Failed to get netfx return code.");
386 }
387 }
388 else if (FAILED(hrInternalError))
389 {
390 // push internal error message
391 OnNetFxError(pNetfxChainer, hrInternalError, pfnGenericMessageHandler, pvContext);
392 ExitOnFailure(hr, "Failed to send internal error message from netfx chainer.");
393 }
394
395 break;
396 }
397 else if (WAIT_OBJECT_0 + 1 == er)
398 {
399 // Chainee has notified us of a change.
400 hr = ProcessNetFxMessage(pNetfxChainer, pfnGenericMessageHandler, pvContext);
401 ExitOnFailure(hr, "Failed to process netfx chainer message.");
402 }
403 else if (WAIT_FAILED == er)
404 {
405 ExitWithLastError(hr, "Failed to wait for netfx chainer process to complete");
406 }
407 }
408
409LExit:
410 ReleaseStr(sczSectionName);
411 ReleaseStr(sczEventName);
412 StrSecureZeroFreeString(sczCommand);
413 DestroyNetFxChainer(pNetfxChainer);
414 ReleaseHandle(pi.hThread);
415 ReleaseHandle(pi.hProcess);
416
417 return hr;
418}
diff --git a/src/burn/engine/netfxchainer.h b/src/burn/engine/netfxchainer.h
new file mode 100644
index 00000000..7d3aff1c
--- /dev/null
+++ b/src/burn/engine/netfxchainer.h
@@ -0,0 +1,98 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9struct NetFxDataStructure
10{
11 bool downloadFinished; // download done yet?
12 bool installFinished; // install done yet?
13 bool downloadAbort; // set downloader to abort
14 bool installAbort; // set installer to abort
15 HRESULT hrDownloadFinished; // resultant HRESULT for download
16 HRESULT hrInstallFinished; // resultant HRESULT for install
17 HRESULT hrInternalError;
18 WCHAR szCurrentItemStep[MAX_PATH];
19 BYTE downloadSoFar; // download progress 0 - 255 (0 to 100% done)
20 BYTE installSoFar; // install progress 0 - 255 (0 to 100% done)
21 WCHAR szEventName[MAX_PATH]; // event that chainer 'creates' and chainee 'opens'to sync communications
22
23 BYTE version; // version of the data structure, set by chainer.
24
25 DWORD messageCode; // current message being sent by the chainee, 0 if no message is active
26 DWORD messageResponse; // chainer's response to current message, 0 if not yet handled
27 DWORD messageDataLength; // length of the m_messageData field in bytes
28 BYTE messageData[1]; // variable length buffer, content depends on m_messageCode
29};
30
31struct NetFxChainer
32{
33 HANDLE hSection;
34
35 HANDLE hEventChaineeSend;
36 HANDLE hEventChainerSend;
37 HANDLE hMutex;
38
39 NetFxDataStructure* pData;
40 DWORD dwDataSize;
41};
42
43#define NETFXDATA_SIZE 65536
44
45#define NETFXDATA_VERSION 1
46
47#define NETFX_MESSAGE(version, defaultResponse, messageCode) \
48 ((((DWORD)version & 0xFF) << 24) | (((DWORD)defaultResponse & 0xFF) << 16) | ((DWORD)messageCode & 0xFFFF))
49#define NETFX_MESSAGE_CODE(messageId) \
50 (messageId & 0xFFFF)
51#define NETFX_MESSAGE_DEFAULT_RESPONSE(messageId) \
52 ((messageId >> 16) & 0xFF)
53#define NETFX_MESSAGE_VERSION(messageId) \
54 ((messageId >>24) & 0xFF)
55
56#define NETFX_NO_MESSAGE 0
57
58
59//------------------------------------------------------------------------------
60// NETFX_CLOSE_APPS
61//
62// Sent by the chainee when it detects that applications are holding files in
63// use. Respond to this message in order to tell the chainee to close the
64// applications to prevent a reboot.
65//
66// pData : NetFxCloseApplications : The list of applications
67// Acceptable responses:
68// IDYES : Indicates that the chainee should attempt to shutdown the apps.
69// If all apps do not successfully close the message may be sent again.
70// IDNO : Indicates that the chainee should not attempt to close apps.
71// IDRETRY : Indicates that the chainee should refresh the list of apps.
72// Another NETFX_CLOSE_APPS message will be sent asynchronously with
73// the new list of apps.
74//------------------------------------------------------------------------------
75#define NETFX_CLOSE_APPS NETFX_MESSAGE(NETFXDATA_VERSION, IDNO, 1)
76
77struct NetFxApplication
78{
79 WCHAR szName[MAX_PATH];
80 DWORD dwPid;
81};
82
83struct NetFxCloseApplications
84{
85 DWORD dwApplicationsSize;
86 NetFxApplication applications[1];
87};
88
89HRESULT NetFxRunChainer(
90 __in LPCWSTR wzExecutablePath,
91 __in LPCWSTR wzArguments,
92 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
93 __in LPVOID pvContext,
94 __out DWORD* pdwExitCode
95 );
96#if defined(__cplusplus)
97}
98#endif
diff --git a/src/burn/engine/package.cpp b/src/burn/engine/package.cpp
new file mode 100644
index 00000000..3f8c8b0f
--- /dev/null
+++ b/src/burn/engine/package.cpp
@@ -0,0 +1,692 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// internal function declarations
7
8static HRESULT ParsePayloadRefsFromXml(
9 __in BURN_PACKAGE* pPackage,
10 __in BURN_PAYLOADS* pPayloads,
11 __in IXMLDOMNode* pixnPackage
12 );
13static HRESULT ParsePatchTargetCode(
14 __in BURN_PACKAGES* pPackages,
15 __in IXMLDOMNode* pixnBundle
16 );
17static HRESULT FindRollbackBoundaryById(
18 __in BURN_PACKAGES* pPackages,
19 __in_z LPCWSTR wzId,
20 __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
21 );
22
23
24// function definitions
25
26extern "C" HRESULT PackagesParseFromXml(
27 __in BURN_PACKAGES* pPackages,
28 __in BURN_PAYLOADS* pPayloads,
29 __in IXMLDOMNode* pixnBundle
30 )
31{
32 HRESULT hr = S_OK;
33 IXMLDOMNodeList* pixnNodes = NULL;
34 IXMLDOMNode* pixnNode = NULL;
35 DWORD cNodes = 0;
36 BSTR bstrNodeName = NULL;
37 DWORD cMspPackages = 0;
38 LPWSTR scz = NULL;
39
40 // select rollback boundary nodes
41 hr = XmlSelectNodes(pixnBundle, L"RollbackBoundary", &pixnNodes);
42 ExitOnFailure(hr, "Failed to select rollback boundary nodes.");
43
44 // get rollback boundary node count
45 hr = pixnNodes->get_length((long*)&cNodes);
46 ExitOnFailure(hr, "Failed to get rollback bundary node count.");
47
48 if (cNodes)
49 {
50 // allocate memory for rollback boundaries
51 pPackages->rgRollbackBoundaries = (BURN_ROLLBACK_BOUNDARY*)MemAlloc(sizeof(BURN_ROLLBACK_BOUNDARY) * cNodes, TRUE);
52 ExitOnNull(pPackages->rgRollbackBoundaries, hr, E_OUTOFMEMORY, "Failed to allocate memory for rollback boundary structs.");
53
54 pPackages->cRollbackBoundaries = cNodes;
55
56 // parse rollback boundary elements
57 for (DWORD i = 0; i < cNodes; ++i)
58 {
59 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = &pPackages->rgRollbackBoundaries[i];
60
61 hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName);
62 ExitOnFailure(hr, "Failed to get next node.");
63
64 // @Id
65 hr = XmlGetAttributeEx(pixnNode, L"Id", &pRollbackBoundary->sczId);
66 ExitOnFailure(hr, "Failed to get @Id.");
67
68 // @Vital
69 hr = XmlGetYesNoAttribute(pixnNode, L"Vital", &pRollbackBoundary->fVital);
70 ExitOnFailure(hr, "Failed to get @Vital.");
71
72 // @Transaction
73 hr = XmlGetYesNoAttribute(pixnNode, L"Transaction", &pRollbackBoundary->fTransaction);
74 ExitOnFailure(hr, "Failed to get @Transaction.");
75
76 // prepare next iteration
77 ReleaseNullObject(pixnNode);
78 ReleaseNullBSTR(bstrNodeName);
79 }
80 }
81
82 ReleaseNullObject(pixnNodes); // done with the RollbackBoundary elements.
83
84 // select package nodes
85 hr = XmlSelectNodes(pixnBundle, L"Chain/ExePackage|Chain/MsiPackage|Chain/MspPackage|Chain/MsuPackage", &pixnNodes);
86 ExitOnFailure(hr, "Failed to select package nodes.");
87
88 // get package node count
89 hr = pixnNodes->get_length((long*)&cNodes);
90 ExitOnFailure(hr, "Failed to get package node count.");
91
92 if (!cNodes)
93 {
94 ExitFunction1(hr = S_OK);
95 }
96
97 // allocate memory for packages
98 pPackages->rgPackages = (BURN_PACKAGE*)MemAlloc(sizeof(BURN_PACKAGE) * cNodes, TRUE);
99 ExitOnNull(pPackages->rgPackages, hr, E_OUTOFMEMORY, "Failed to allocate memory for package structs.");
100
101 pPackages->cPackages = cNodes;
102
103 // parse package elements
104 for (DWORD i = 0; i < cNodes; ++i)
105 {
106 BURN_PACKAGE* pPackage = &pPackages->rgPackages[i];
107
108 hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName);
109 ExitOnFailure(hr, "Failed to get next node.");
110
111 // @Id
112 hr = XmlGetAttributeEx(pixnNode, L"Id", &pPackage->sczId);
113 ExitOnFailure(hr, "Failed to get @Id.");
114
115 // @Cache
116 hr = XmlGetAttributeEx(pixnNode, L"Cache", &scz);
117 if (SUCCEEDED(hr))
118 {
119 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"remove", -1))
120 {
121 pPackage->authoredCacheType = BOOTSTRAPPER_CACHE_TYPE_REMOVE;
122 }
123 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"keep", -1))
124 {
125 pPackage->authoredCacheType = BOOTSTRAPPER_CACHE_TYPE_KEEP;
126 }
127 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"force", -1))
128 {
129 pPackage->authoredCacheType = BOOTSTRAPPER_CACHE_TYPE_FORCE;
130 }
131 else
132 {
133 hr = E_UNEXPECTED;
134 ExitOnRootFailure(hr, "Invalid cache type: %ls", scz);
135 }
136 }
137 ExitOnFailure(hr, "Failed to get @Cache.");
138
139 // @CacheId
140 hr = XmlGetAttributeEx(pixnNode, L"CacheId", &pPackage->sczCacheId);
141 ExitOnFailure(hr, "Failed to get @CacheId.");
142
143 // @Size
144 hr = XmlGetAttributeLargeNumber(pixnNode, L"Size", &pPackage->qwSize);
145 ExitOnFailure(hr, "Failed to get @Size.");
146
147 // @InstallSize
148 hr = XmlGetAttributeLargeNumber(pixnNode, L"InstallSize", &pPackage->qwInstallSize);
149 ExitOnFailure(hr, "Failed to get @InstallSize.");
150
151 // @PerMachine
152 hr = XmlGetYesNoAttribute(pixnNode, L"PerMachine", &pPackage->fPerMachine);
153 ExitOnFailure(hr, "Failed to get @PerMachine.");
154
155 // @Permanent
156 hr = XmlGetYesNoAttribute(pixnNode, L"Permanent", &pPackage->fUninstallable);
157 ExitOnFailure(hr, "Failed to get @Permanent.");
158 pPackage->fUninstallable = !pPackage->fUninstallable; // TODO: change "Uninstallable" variable name to permanent, until then Uninstallable is the opposite of Permanent so fix the variable.
159 pPackage->fCanAffectRegistration = pPackage->fUninstallable;
160
161 // @Vital
162 hr = XmlGetYesNoAttribute(pixnNode, L"Vital", &pPackage->fVital);
163 ExitOnFailure(hr, "Failed to get @Vital.");
164
165 // @LogPathVariable
166 hr = XmlGetAttributeEx(pixnNode, L"LogPathVariable", &pPackage->sczLogPathVariable);
167 if (E_NOTFOUND != hr)
168 {
169 ExitOnFailure(hr, "Failed to get @LogPathVariable.");
170 }
171
172 // @RollbackLogPathVariable
173 hr = XmlGetAttributeEx(pixnNode, L"RollbackLogPathVariable", &pPackage->sczRollbackLogPathVariable);
174 if (E_NOTFOUND != hr)
175 {
176 ExitOnFailure(hr, "Failed to get @RollbackLogPathVariable.");
177 }
178
179 // @InstallCondition
180 hr = XmlGetAttributeEx(pixnNode, L"InstallCondition", &pPackage->sczInstallCondition);
181 if (E_NOTFOUND != hr)
182 {
183 ExitOnFailure(hr, "Failed to get @InstallCondition.");
184 }
185
186 // @RollbackBoundaryForward
187 hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryForward", &scz);
188 if (E_NOTFOUND != hr)
189 {
190 ExitOnFailure(hr, "Failed to get @RollbackBoundaryForward.");
191
192 hr = FindRollbackBoundaryById(pPackages, scz, &pPackage->pRollbackBoundaryForward);
193 ExitOnFailure(hr, "Failed to find forward transaction boundary: %ls", scz);
194 }
195
196 // @RollbackBoundaryBackward
197 hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryBackward", &scz);
198 if (E_NOTFOUND != hr)
199 {
200 ExitOnFailure(hr, "Failed to get @RollbackBoundaryBackward.");
201
202 hr = FindRollbackBoundaryById(pPackages, scz, &pPackage->pRollbackBoundaryBackward);
203 ExitOnFailure(hr, "Failed to find backward transaction boundary: %ls", scz);
204 }
205
206 // read type specific attributes
207 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"ExePackage", -1))
208 {
209 pPackage->type = BURN_PACKAGE_TYPE_EXE;
210
211 hr = ExeEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization
212 ExitOnFailure(hr, "Failed to parse EXE package.");
213 }
214 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiPackage", -1))
215 {
216 pPackage->type = BURN_PACKAGE_TYPE_MSI;
217
218 hr = MsiEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization
219 ExitOnFailure(hr, "Failed to parse MSI package.");
220 }
221 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MspPackage", -1))
222 {
223 pPackage->type = BURN_PACKAGE_TYPE_MSP;
224
225 hr = MspEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization
226 ExitOnFailure(hr, "Failed to parse MSP package.");
227
228 ++cMspPackages;
229 }
230 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsuPackage", -1))
231 {
232 pPackage->type = BURN_PACKAGE_TYPE_MSU;
233
234 hr = MsuEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization
235 ExitOnFailure(hr, "Failed to parse MSU package.");
236 }
237 else
238 {
239 // ignore other package types for now
240 }
241
242 // parse payload references
243 hr = ParsePayloadRefsFromXml(pPackage, pPayloads, pixnNode);
244 ExitOnFailure(hr, "Failed to parse payload references.");
245
246 // parse dependency providers
247 hr = DependencyParseProvidersFromXml(pPackage, pixnNode);
248 ExitOnFailure(hr, "Failed to parse dependency providers.");
249
250 // prepare next iteration
251 ReleaseNullObject(pixnNode);
252 ReleaseNullBSTR(bstrNodeName);
253 }
254
255 if (cMspPackages)
256 {
257 pPackages->rgPatchInfo = static_cast<MSIPATCHSEQUENCEINFOW*>(MemAlloc(sizeof(MSIPATCHSEQUENCEINFOW) * cMspPackages, TRUE));
258 ExitOnNull(pPackages->rgPatchInfo, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSP patch sequence information.");
259
260 pPackages->rgPatchInfoToPackage = static_cast<BURN_PACKAGE**>(MemAlloc(sizeof(BURN_PACKAGE*) * cMspPackages, TRUE));
261 ExitOnNull(pPackages->rgPatchInfoToPackage, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch sequence information to package lookup.");
262
263 for (DWORD i = 0; i < pPackages->cPackages; ++i)
264 {
265 BURN_PACKAGE* pPackage = &pPackages->rgPackages[i];
266
267 if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
268 {
269 pPackages->rgPatchInfo[pPackages->cPatchInfo].szPatchData = pPackage->Msp.sczApplicabilityXml;
270 pPackages->rgPatchInfo[pPackages->cPatchInfo].ePatchDataType = MSIPATCH_DATATYPE_XMLBLOB;
271 pPackages->rgPatchInfoToPackage[pPackages->cPatchInfo] = pPackage;
272 ++pPackages->cPatchInfo;
273
274 // Loop through all MSI packages seeing if any of them slipstream this MSP.
275 for (DWORD j = 0; j < pPackages->cPackages; ++j)
276 {
277 BURN_PACKAGE* pMsiPackage = &pPackages->rgPackages[j];
278
279 if (BURN_PACKAGE_TYPE_MSI == pMsiPackage->type)
280 {
281 for (DWORD k = 0; k < pMsiPackage->Msi.cSlipstreamMspPackages; ++k)
282 {
283 if (pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k] && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k], -1))
284 {
285 BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pMsiPackage->Msi.rgSlipstreamMsps + k;
286 pSlipstreamMsp->pMspPackage = pPackage;
287 pSlipstreamMsp->dwMsiChainedPatchIndex = BURN_PACKAGE_INVALID_PATCH_INDEX;
288
289 ReleaseNullStr(pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k]); // we don't need the slipstream package id any longer so free it.
290 }
291 }
292 }
293 }
294 }
295 }
296 }
297
298 AssertSz(pPackages->cPatchInfo == cMspPackages, "Count of packages patch info should be equal to the number of MSP packages.");
299
300#if DEBUG
301 // Loop through all MSI packages seeing if any of them are missing their slipstream MSP.
302 for (DWORD i = 0; i < pPackages->cPackages; ++i)
303 {
304 BURN_PACKAGE* pPackage = &pPackages->rgPackages[i];
305
306 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
307 {
308 for (DWORD k = 0; k < pPackage->Msi.cSlipstreamMspPackages; ++k)
309 {
310 if (pPackage->Msi.rgsczSlipstreamMspPackageIds[k])
311 {
312 AssertSz(FALSE, "MSI slipstream MSP package doesn't exist.");
313 }
314 }
315 }
316 }
317#endif
318
319 hr = ParsePatchTargetCode(pPackages, pixnBundle);
320 ExitOnFailure(hr, "Failed to parse target product codes.");
321
322 hr = S_OK;
323
324LExit:
325 ReleaseObject(pixnNodes);
326 ReleaseObject(pixnNode);
327 ReleaseBSTR(bstrNodeName);
328 ReleaseStr(scz);
329
330 return hr;
331}
332
333extern "C" void PackageUninitialize(
334 __in BURN_PACKAGE* pPackage
335 )
336{
337 ReleaseStr(pPackage->sczId);
338 ReleaseStr(pPackage->sczLogPathVariable);
339 ReleaseStr(pPackage->sczRollbackLogPathVariable);
340 ReleaseStr(pPackage->sczInstallCondition);
341 ReleaseStr(pPackage->sczCacheId);
342
343 if (pPackage->rgDependencyProviders)
344 {
345 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
346 {
347 DependencyUninitializeProvider(pPackage->rgDependencyProviders + i);
348 }
349 MemFree(pPackage->rgDependencyProviders);
350 }
351
352 ReleaseMem(pPackage->payloads.rgItems);
353
354 switch (pPackage->type)
355 {
356 case BURN_PACKAGE_TYPE_EXE:
357 ExeEnginePackageUninitialize(pPackage); // TODO: Modularization
358 break;
359 case BURN_PACKAGE_TYPE_MSI:
360 MsiEnginePackageUninitialize(pPackage); // TODO: Modularization
361 break;
362 case BURN_PACKAGE_TYPE_MSP:
363 MspEnginePackageUninitialize(pPackage); // TODO: Modularization
364 break;
365 case BURN_PACKAGE_TYPE_MSU:
366 MsuEnginePackageUninitialize(pPackage); // TODO: Modularization
367 break;
368 }
369}
370
371extern "C" void PackagesUninitialize(
372 __in BURN_PACKAGES* pPackages
373 )
374{
375 if (pPackages->rgRollbackBoundaries)
376 {
377 for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i)
378 {
379 ReleaseStr(pPackages->rgRollbackBoundaries[i].sczId);
380 ReleaseStr(pPackages->rgRollbackBoundaries[i].sczLogPath);
381 }
382 MemFree(pPackages->rgRollbackBoundaries);
383 }
384
385 if (pPackages->rgPackages)
386 {
387 for (DWORD i = 0; i < pPackages->cPackages; ++i)
388 {
389 PackageUninitialize(pPackages->rgPackages + i);
390 }
391 MemFree(pPackages->rgPackages);
392 }
393
394 if (pPackages->rgPatchTargetCodes)
395 {
396 for (DWORD i = 0; i < pPackages->cPatchTargetCodes; ++i)
397 {
398 ReleaseStr(pPackages->rgPatchTargetCodes[i].sczTargetCode);
399 }
400 MemFree(pPackages->rgPatchTargetCodes);
401 }
402
403 ReleaseMem(pPackages->rgPatchInfo);
404 ReleaseMem(pPackages->rgPatchInfoToPackage);
405
406 // clear struct
407 memset(pPackages, 0, sizeof(BURN_PACKAGES));
408}
409
410extern "C" HRESULT PackageFindById(
411 __in BURN_PACKAGES* pPackages,
412 __in_z LPCWSTR wzId,
413 __out BURN_PACKAGE** ppPackage
414 )
415{
416 HRESULT hr = S_OK;
417 BURN_PACKAGE* pPackage = NULL;
418
419 for (DWORD i = 0; i < pPackages->cPackages; ++i)
420 {
421 pPackage = &pPackages->rgPackages[i];
422
423 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, wzId, -1))
424 {
425 *ppPackage = pPackage;
426 ExitFunction1(hr = S_OK);
427 }
428 }
429
430 hr = E_NOTFOUND;
431
432LExit:
433 return hr;
434}
435
436
437extern "C" HRESULT PackageFindRelatedById(
438 __in BURN_RELATED_BUNDLES* pRelatedBundles,
439 __in_z LPCWSTR wzId,
440 __out BURN_PACKAGE** ppPackage
441 )
442{
443 HRESULT hr = S_OK;
444 BURN_PACKAGE* pPackage = NULL;
445
446 for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i)
447 {
448 pPackage = &pRelatedBundles->rgRelatedBundles[i].package;
449
450 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, wzId, -1))
451 {
452 *ppPackage = pPackage;
453 ExitFunction1(hr = S_OK);
454 }
455 }
456
457 hr = E_NOTFOUND;
458
459LExit:
460 return hr;
461}
462
463/********************************************************************
464 PackageGetProperty - Determines if the property is defined
465 and optionally copies the property value.
466
467 Note: The caller must free psczValue if requested.
468
469 Note: Returns E_NOTFOUND if the property was not defined or if the
470 package does not support properties.
471
472*********************************************************************/
473extern "C" HRESULT PackageGetProperty(
474 __in const BURN_PACKAGE* pPackage,
475 __in_z LPCWSTR wzProperty,
476 __out_z_opt LPWSTR* psczValue
477 )
478{
479 HRESULT hr = E_NOTFOUND;
480 BURN_MSIPROPERTY* rgProperties = NULL;
481 DWORD cProperties = 0;
482
483 // For MSIs and MSPs, enumerate the properties looking for wzProperty.
484 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
485 {
486 rgProperties = pPackage->Msi.rgProperties;
487 cProperties = pPackage->Msi.cProperties;
488 }
489 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
490 {
491 rgProperties = pPackage->Msp.rgProperties;
492 cProperties = pPackage->Msp.cProperties;
493 }
494
495 for (DWORD i = 0; i < cProperties; ++i)
496 {
497 const BURN_MSIPROPERTY* pProperty = &rgProperties[i];
498
499 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pProperty->sczId, -1, wzProperty, -1))
500 {
501 if (psczValue)
502 {
503 hr = StrAllocString(psczValue, pProperty->sczValue, 0);
504 ExitOnFailure(hr, "Failed to copy the property value.");
505 }
506
507 ExitFunction1(hr = S_OK);
508 }
509 }
510
511LExit:
512 return hr;
513}
514
515extern "C" HRESULT PackageFindRollbackBoundaryById(
516 __in BURN_PACKAGES* pPackages,
517 __in_z LPCWSTR wzId,
518 __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
519 )
520{
521 HRESULT hr = S_OK;
522 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL;
523
524 for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i)
525 {
526 pRollbackBoundary = &pPackages->rgRollbackBoundaries[i];
527
528 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pRollbackBoundary->sczId, -1, wzId, -1))
529 {
530 *ppRollbackBoundary = pRollbackBoundary;
531 ExitFunction1(hr = S_OK);
532 }
533 }
534
535 hr = E_NOTFOUND;
536
537LExit:
538 return hr;
539}
540
541
542// internal function declarations
543
544static HRESULT ParsePayloadRefsFromXml(
545 __in BURN_PACKAGE* pPackage,
546 __in BURN_PAYLOADS* pPayloads,
547 __in IXMLDOMNode* pixnPackage
548 )
549{
550 HRESULT hr = S_OK;
551 IXMLDOMNodeList* pixnNodes = NULL;
552 IXMLDOMNode* pixnNode = NULL;
553 DWORD cNodes = 0;
554 LPWSTR sczId = NULL;
555
556 // select package nodes
557 hr = XmlSelectNodes(pixnPackage, L"PayloadRef", &pixnNodes);
558 ExitOnFailure(hr, "Failed to select package nodes.");
559
560 // get package node count
561 hr = pixnNodes->get_length((long*)&cNodes);
562 ExitOnFailure(hr, "Failed to get package node count.");
563
564 if (!cNodes)
565 {
566 ExitFunction1(hr = S_OK);
567 }
568
569 // allocate memory for payload pointers
570 pPackage->payloads.rgItems = (BURN_PAYLOAD_GROUP_ITEM*)MemAlloc(sizeof(BURN_PAYLOAD_GROUP_ITEM) * cNodes, TRUE);
571 ExitOnNull(pPackage->payloads.rgItems, hr, E_OUTOFMEMORY, "Failed to allocate memory for package payloads.");
572
573 pPackage->payloads.cItems = cNodes;
574
575 // parse package elements
576 for (DWORD i = 0; i < cNodes; ++i)
577 {
578 BURN_PAYLOAD_GROUP_ITEM* pPackagePayload = pPackage->payloads.rgItems + i;
579
580 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
581 ExitOnFailure(hr, "Failed to get next node.");
582
583 // @Id
584 hr = XmlGetAttributeEx(pixnNode, L"Id", &sczId);
585 ExitOnFailure(hr, "Failed to get Id attribute.");
586
587 // find payload
588 hr = PayloadFindById(pPayloads, sczId, &pPackagePayload->pPayload);
589 ExitOnFailure(hr, "Failed to find payload.");
590
591 pPackage->payloads.qwTotalSize += pPackagePayload->pPayload->qwFileSize;
592
593 // prepare next iteration
594 ReleaseNullObject(pixnNode);
595 }
596
597 hr = S_OK;
598
599LExit:
600 ReleaseObject(pixnNodes);
601 ReleaseObject(pixnNode);
602 ReleaseStr(sczId);
603
604 return hr;
605}
606
607static HRESULT ParsePatchTargetCode(
608 __in BURN_PACKAGES* pPackages,
609 __in IXMLDOMNode* pixnBundle
610 )
611{
612 HRESULT hr = S_OK;
613 IXMLDOMNodeList* pixnNodes = NULL;
614 IXMLDOMNode* pixnNode = NULL;
615 DWORD cNodes = 0;
616 BSTR bstrNodeText = NULL;
617 BOOL fProduct;
618
619 hr = XmlSelectNodes(pixnBundle, L"PatchTargetCode", &pixnNodes);
620 ExitOnFailure(hr, "Failed to select PatchTargetCode nodes.");
621
622 hr = pixnNodes->get_length((long*)&cNodes);
623 ExitOnFailure(hr, "Failed to get PatchTargetCode node count.");
624
625 if (!cNodes)
626 {
627 ExitFunction1(hr = S_OK);
628 }
629
630 pPackages->rgPatchTargetCodes = (BURN_PATCH_TARGETCODE*)MemAlloc(sizeof(BURN_PATCH_TARGETCODE) * cNodes, TRUE);
631 ExitOnNull(pPackages->rgPatchTargetCodes, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch targetcodes.");
632
633 pPackages->cPatchTargetCodes = cNodes;
634
635 for (DWORD i = 0; i < cNodes; ++i)
636 {
637 BURN_PATCH_TARGETCODE* pTargetCode = pPackages->rgPatchTargetCodes + i;
638
639 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
640 ExitOnFailure(hr, "Failed to get next node.");
641
642 hr = XmlGetAttributeEx(pixnNode, L"TargetCode", &pTargetCode->sczTargetCode);
643 ExitOnFailure(hr, "Failed to get @TargetCode attribute.");
644
645 hr = XmlGetYesNoAttribute(pixnNode, L"Product", &fProduct);
646 if (E_NOTFOUND == hr)
647 {
648 fProduct = FALSE;
649 hr = S_OK;
650 }
651 ExitOnFailure(hr, "Failed to get @Product.");
652
653 pTargetCode->type = fProduct ? BURN_PATCH_TARGETCODE_TYPE_PRODUCT : BURN_PATCH_TARGETCODE_TYPE_UPGRADE;
654
655 // prepare next iteration
656 ReleaseNullBSTR(bstrNodeText);
657 ReleaseNullObject(pixnNode);
658 }
659
660LExit:
661 ReleaseBSTR(bstrNodeText);
662 ReleaseObject(pixnNode);
663 ReleaseObject(pixnNodes);
664
665 return hr;
666}
667
668static HRESULT FindRollbackBoundaryById(
669 __in BURN_PACKAGES* pPackages,
670 __in_z LPCWSTR wzId,
671 __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
672 )
673{
674 HRESULT hr = S_OK;
675 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL;
676
677 for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i)
678 {
679 pRollbackBoundary = &pPackages->rgRollbackBoundaries[i];
680
681 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pRollbackBoundary->sczId, -1, wzId, -1))
682 {
683 *ppRollbackBoundary = pRollbackBoundary;
684 ExitFunction1(hr = S_OK);
685 }
686 }
687
688 hr = E_NOTFOUND;
689
690LExit:
691 return hr;
692}
diff --git a/src/burn/engine/package.h b/src/burn/engine/package.h
new file mode 100644
index 00000000..89a3d6e9
--- /dev/null
+++ b/src/burn/engine/package.h
@@ -0,0 +1,380 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9struct _BURN_RELATED_BUNDLES;
10typedef _BURN_RELATED_BUNDLES BURN_RELATED_BUNDLES;
11
12struct _BURN_PACKAGE;
13typedef _BURN_PACKAGE BURN_PACKAGE;
14
15// constants
16
17const DWORD BURN_PACKAGE_INVALID_PATCH_INDEX = 0x80000000;
18
19enum BURN_EXE_EXIT_CODE_TYPE
20{
21 BURN_EXE_EXIT_CODE_TYPE_NONE,
22 BURN_EXE_EXIT_CODE_TYPE_SUCCESS,
23 BURN_EXE_EXIT_CODE_TYPE_ERROR,
24 BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT,
25 BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT,
26};
27
28enum BURN_EXE_PROTOCOL_TYPE
29{
30 BURN_EXE_PROTOCOL_TYPE_NONE,
31 BURN_EXE_PROTOCOL_TYPE_BURN,
32 BURN_EXE_PROTOCOL_TYPE_NETFX4,
33};
34
35enum BURN_PACKAGE_TYPE
36{
37 BURN_PACKAGE_TYPE_NONE,
38 BURN_PACKAGE_TYPE_EXE,
39 BURN_PACKAGE_TYPE_MSI,
40 BURN_PACKAGE_TYPE_MSP,
41 BURN_PACKAGE_TYPE_MSU,
42};
43
44enum BURN_DEPENDENCY_ACTION
45{
46 BURN_DEPENDENCY_ACTION_NONE,
47 BURN_DEPENDENCY_ACTION_REGISTER,
48 BURN_DEPENDENCY_ACTION_UNREGISTER,
49};
50
51enum BURN_PATCH_TARGETCODE_TYPE
52{
53 BURN_PATCH_TARGETCODE_TYPE_UNKNOWN,
54 BURN_PATCH_TARGETCODE_TYPE_PRODUCT,
55 BURN_PATCH_TARGETCODE_TYPE_UPGRADE,
56};
57
58enum BOOTSTRAPPER_FEATURE_ACTION
59{
60 BOOTSTRAPPER_FEATURE_ACTION_NONE,
61 BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL,
62 BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE,
63 BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT,
64 BOOTSTRAPPER_FEATURE_ACTION_REINSTALL,
65 BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE,
66 BOOTSTRAPPER_FEATURE_ACTION_REMOVE,
67};
68
69enum BURN_PACKAGE_REGISTRATION_STATE
70{
71 BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN,
72 BURN_PACKAGE_REGISTRATION_STATE_ABSENT,
73 BURN_PACKAGE_REGISTRATION_STATE_IGNORED,
74 BURN_PACKAGE_REGISTRATION_STATE_PRESENT,
75};
76
77enum BURN_PATCH_SKIP_STATE
78{
79 BURN_PATCH_SKIP_STATE_NONE,
80 BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL,
81 BURN_PATCH_SKIP_STATE_SLIPSTREAM,
82};
83
84// structs
85
86typedef struct _BURN_EXE_EXIT_CODE
87{
88 BURN_EXE_EXIT_CODE_TYPE type;
89 DWORD dwCode;
90 BOOL fWildcard;
91} BURN_EXE_EXIT_CODE;
92
93typedef struct _BURN_EXE_COMMAND_LINE_ARGUMENT
94{
95 LPWSTR sczInstallArgument;
96 LPWSTR sczUninstallArgument;
97 LPWSTR sczRepairArgument;
98 LPWSTR sczCondition;
99} BURN_EXE_COMMAND_LINE_ARGUMENT;
100
101typedef struct _BURN_MSPTARGETPRODUCT
102{
103 MSIINSTALLCONTEXT context;
104 DWORD dwOrder;
105 WCHAR wzTargetProductCode[39];
106 BURN_PACKAGE* pChainedTargetPackage;
107 BOOL fInstalled;
108 BOOL fSlipstream;
109 BOOL fSlipstreamRequired; // this means the target product is not present on the machine, but is available in the chain as a slipstream target.
110
111 BOOTSTRAPPER_PACKAGE_STATE patchPackageState; // only valid after Detect.
112 BOOTSTRAPPER_REQUEST_STATE defaultRequested; // only valid during Plan.
113 BOOTSTRAPPER_REQUEST_STATE requested; // only valid during Plan.
114 BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan.
115 BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan.
116 BURN_PATCH_SKIP_STATE executeSkip; // only valid during Plan.
117 BURN_PATCH_SKIP_STATE rollbackSkip; // only valid during Plan.
118
119 BURN_PACKAGE_REGISTRATION_STATE registrationState; // initialized during Detect, updated during Apply.
120 BURN_PACKAGE_REGISTRATION_STATE transactionRegistrationState;// only valid during Apply inside an MSI transaction.
121} BURN_MSPTARGETPRODUCT;
122
123typedef struct _BURN_MSIPROPERTY
124{
125 LPWSTR sczId;
126 LPWSTR sczValue; // used during forward execution
127 LPWSTR sczRollbackValue; // used during rollback
128 LPWSTR sczCondition;
129} BURN_MSIPROPERTY;
130
131typedef struct _BURN_MSIFEATURE
132{
133 LPWSTR sczId;
134 LPWSTR sczAddLocalCondition;
135 LPWSTR sczAddSourceCondition;
136 LPWSTR sczAdvertiseCondition;
137 LPWSTR sczRollbackAddLocalCondition;
138 LPWSTR sczRollbackAddSourceCondition;
139 LPWSTR sczRollbackAdvertiseCondition;
140
141 BOOTSTRAPPER_FEATURE_STATE currentState; // only valid after Detect.
142 BOOTSTRAPPER_FEATURE_STATE expectedState; // only valid during Plan.
143 BOOTSTRAPPER_FEATURE_STATE defaultRequested; // only valid during Plan.
144 BOOTSTRAPPER_FEATURE_STATE requested; // only valid during Plan.
145 BOOTSTRAPPER_FEATURE_ACTION execute; // only valid during Plan.
146 BOOTSTRAPPER_FEATURE_ACTION rollback; // only valid during Plan.
147} BURN_MSIFEATURE;
148
149typedef struct _BURN_RELATED_MSI
150{
151 LPWSTR sczUpgradeCode;
152 VERUTIL_VERSION* pMinVersion;
153 VERUTIL_VERSION* pMaxVersion;
154 BOOL fMinProvided;
155 BOOL fMaxProvided;
156 BOOL fMinInclusive;
157 BOOL fMaxInclusive;
158 BOOL fOnlyDetect;
159 BOOL fLangInclusive;
160
161 DWORD* rgdwLanguages;
162 DWORD cLanguages;
163} BURN_RELATED_MSI;
164
165typedef struct _BURN_CHAINED_PATCH
166{
167 BURN_PACKAGE* pMspPackage;
168 DWORD dwMspTargetProductIndex; // index into the Msp.rgTargetProducts
169} BURN_CHAINED_PATCH;
170
171typedef struct _BURN_SLIPSTREAM_MSP
172{
173 BURN_PACKAGE* pMspPackage;
174 DWORD dwMsiChainedPatchIndex; // index into the Msi.rgChainedPatches
175
176 BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan.
177 BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan.
178} BURN_SLIPSTREAM_MSP;
179
180typedef struct _BURN_DEPENDENCY_PROVIDER
181{
182 LPWSTR sczKey;
183 LPWSTR sczVersion;
184 LPWSTR sczDisplayName;
185 BOOL fImported;
186
187 DEPENDENCY* rgDependents; // only valid after Detect.
188 UINT cDependents; // only valid after Detect.
189} BURN_DEPENDENCY_PROVIDER;
190
191typedef struct _BURN_ROLLBACK_BOUNDARY
192{
193 LPWSTR sczId;
194 BOOL fVital;
195 BOOL fTransaction;
196 BOOL fActiveTransaction; // only valid during Apply.
197 LPWSTR sczLogPath;
198} BURN_ROLLBACK_BOUNDARY;
199
200typedef struct _BURN_PATCH_TARGETCODE
201{
202 LPWSTR sczTargetCode;
203 BURN_PATCH_TARGETCODE_TYPE type;
204} BURN_PATCH_TARGETCODE;
205
206typedef struct _BURN_PACKAGE
207{
208 LPWSTR sczId;
209
210 LPWSTR sczLogPathVariable; // name of the variable that will be set to the log path.
211 LPWSTR sczRollbackLogPathVariable; // name of the variable that will be set to the rollback path.
212
213 LPWSTR sczInstallCondition;
214 BOOL fPerMachine;
215 BOOL fUninstallable;
216 BOOL fVital;
217 BOOL fCanAffectRegistration;
218
219 BOOTSTRAPPER_CACHE_TYPE authoredCacheType;
220 LPWSTR sczCacheId;
221
222 DWORD64 qwInstallSize;
223 DWORD64 qwSize;
224
225 BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryForward; // used during install and repair.
226 BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryBackward; // used during uninstall.
227
228 BOOTSTRAPPER_PACKAGE_STATE currentState; // only valid after Detect.
229 BOOL fCached; // only valid after Detect.
230 BOOL fPackageProviderExists; // only valid after Detect.
231 BOOTSTRAPPER_CACHE_TYPE cacheType; // only valid during Plan.
232 BOOTSTRAPPER_REQUEST_STATE defaultRequested;// only valid during Plan.
233 BOOTSTRAPPER_REQUEST_STATE requested; // only valid during Plan.
234 BOOL fPlannedCache; // only valid during Plan.
235 BOOL fPlannedUncache; // only valid during Plan.
236 BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan.
237 BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan.
238 BURN_DEPENDENCY_ACTION providerExecute; // only valid during Plan.
239 BURN_DEPENDENCY_ACTION providerRollback; // only valid during Plan.
240 BURN_DEPENDENCY_ACTION dependencyExecute; // only valid during Plan.
241 BURN_DEPENDENCY_ACTION dependencyRollback; // only valid during Plan.
242 BOOL fDependencyManagerWasHere; // only valid during Plan.
243 LPWSTR sczCacheFolder; // only valid during Apply.
244 HRESULT hrCacheResult; // only valid during Apply.
245
246 BURN_PACKAGE_REGISTRATION_STATE cacheRegistrationState; // initialized during Detect, updated during Apply.
247 BURN_PACKAGE_REGISTRATION_STATE installRegistrationState; // initialized during Detect, updated during Apply.
248 BURN_PACKAGE_REGISTRATION_STATE expectedCacheRegistrationState; // only valid after Plan.
249 BURN_PACKAGE_REGISTRATION_STATE expectedInstallRegistrationState;// only valid after Plan.
250 BURN_PACKAGE_REGISTRATION_STATE transactionRegistrationState; // only valid during Apply inside an MSI transaction.
251
252 BURN_PAYLOAD_GROUP payloads;
253
254 BURN_DEPENDENCY_PROVIDER* rgDependencyProviders;
255 DWORD cDependencyProviders;
256
257 BURN_PACKAGE_TYPE type;
258 union
259 {
260 struct
261 {
262 LPWSTR sczDetectCondition;
263 LPWSTR sczInstallArguments;
264 LPWSTR sczRepairArguments;
265 LPWSTR sczUninstallArguments;
266 LPWSTR sczIgnoreDependencies;
267 LPCWSTR wzAncestors; // points directly into engine state.
268
269 BOOL fPseudoBundle;
270
271 BOOL fRepairable;
272 BURN_EXE_PROTOCOL_TYPE protocol;
273
274 BOOL fSupportsAncestors;
275
276 BURN_EXE_EXIT_CODE* rgExitCodes;
277 DWORD cExitCodes;
278
279 BURN_EXE_COMMAND_LINE_ARGUMENT* rgCommandLineArguments;
280 DWORD cCommandLineArguments;
281 } Exe;
282 struct
283 {
284 LPWSTR sczProductCode;
285 DWORD dwLanguage;
286 VERUTIL_VERSION* pVersion;
287 VERUTIL_VERSION* pInstalledVersion;
288 LPWSTR sczUpgradeCode;
289
290 BURN_MSIPROPERTY* rgProperties;
291 DWORD cProperties;
292
293 BURN_MSIFEATURE* rgFeatures;
294 DWORD cFeatures;
295
296 BURN_RELATED_MSI* rgRelatedMsis;
297 DWORD cRelatedMsis;
298
299 BURN_SLIPSTREAM_MSP* rgSlipstreamMsps;
300 LPWSTR* rgsczSlipstreamMspPackageIds;
301 DWORD cSlipstreamMspPackages;
302
303 BURN_CHAINED_PATCH* rgChainedPatches;
304 DWORD cChainedPatches;
305 } Msi;
306 struct
307 {
308 LPWSTR sczPatchCode;
309 LPWSTR sczApplicabilityXml;
310
311 BURN_MSIPROPERTY* rgProperties;
312 DWORD cProperties;
313
314 BURN_MSPTARGETPRODUCT* rgTargetProducts;
315 DWORD cTargetProductCodes;
316 } Msp;
317 struct
318 {
319 LPWSTR sczDetectCondition;
320 LPWSTR sczKB;
321 } Msu;
322 };
323} BURN_PACKAGE;
324
325typedef struct _BURN_PACKAGES
326{
327 BURN_ROLLBACK_BOUNDARY* rgRollbackBoundaries;
328 DWORD cRollbackBoundaries;
329
330 BURN_PACKAGE* rgPackages;
331 DWORD cPackages;
332
333 BURN_PATCH_TARGETCODE* rgPatchTargetCodes;
334 DWORD cPatchTargetCodes;
335
336 MSIPATCHSEQUENCEINFOW* rgPatchInfo;
337 BURN_PACKAGE** rgPatchInfoToPackage; // direct lookup from patch information to the (MSP) package it describes.
338 // Thus this array is the exact same size as rgPatchInfo.
339 DWORD cPatchInfo;
340} BURN_PACKAGES;
341
342
343// function declarations
344
345HRESULT PackagesParseFromXml(
346 __in BURN_PACKAGES* pPackages,
347 __in BURN_PAYLOADS* pPayloads,
348 __in IXMLDOMNode* pixnBundle
349 );
350void PackageUninitialize(
351 __in BURN_PACKAGE* pPackage
352 );
353void PackagesUninitialize(
354 __in BURN_PACKAGES* pPackages
355 );
356HRESULT PackageFindById(
357 __in BURN_PACKAGES* pPackages,
358 __in_z LPCWSTR wzId,
359 __out BURN_PACKAGE** ppPackage
360 );
361HRESULT PackageFindRelatedById(
362 __in BURN_RELATED_BUNDLES* pRelatedBundles,
363 __in_z LPCWSTR wzId,
364 __out BURN_PACKAGE** ppPackage
365 );
366HRESULT PackageGetProperty(
367 __in const BURN_PACKAGE* pPackage,
368 __in_z LPCWSTR wzProperty,
369 __out_z_opt LPWSTR* psczValue
370 );
371HRESULT PackageFindRollbackBoundaryById(
372 __in BURN_PACKAGES* pPackages,
373 __in_z LPCWSTR wzId,
374 __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
375 );
376
377
378#if defined(__cplusplus)
379}
380#endif
diff --git a/src/burn/engine/packages.config b/src/burn/engine/packages.config
new file mode 100644
index 00000000..7219a3da
--- /dev/null
+++ b/src/burn/engine/packages.config
@@ -0,0 +1,5 @@
1<?xml version="1.0" encoding="utf-8"?>
2<packages>
3 <package id="Nerdbank.GitVersioning" version="3.3.37" targetFramework="native" developmentDependency="true" />
4 <package id="WixToolset.DUtil" version="4.0.72" targetFramework="native" />
5</packages> \ No newline at end of file
diff --git a/src/burn/engine/payload.cpp b/src/burn/engine/payload.cpp
new file mode 100644
index 00000000..72eb3476
--- /dev/null
+++ b/src/burn/engine/payload.cpp
@@ -0,0 +1,314 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// internal function declarations
7
8static HRESULT FindEmbeddedBySourcePath(
9 __in BURN_PAYLOADS* pPayloads,
10 __in_opt BURN_CONTAINER* pContainer,
11 __in_z LPCWSTR wzStreamName,
12 __out BURN_PAYLOAD** ppPayload
13 );
14
15
16// function definitions
17
18extern "C" HRESULT PayloadsParseFromXml(
19 __in BURN_PAYLOADS* pPayloads,
20 __in_opt BURN_CONTAINERS* pContainers,
21 __in_opt BURN_PAYLOAD_GROUP* pLayoutPayloads,
22 __in IXMLDOMNode* pixnBundle
23 )
24{
25 HRESULT hr = S_OK;
26 IXMLDOMNodeList* pixnNodes = NULL;
27 IXMLDOMNode* pixnNode = NULL;
28 DWORD cNodes = 0;
29 LPWSTR scz = NULL;
30
31 // select payload nodes
32 hr = XmlSelectNodes(pixnBundle, L"Payload", &pixnNodes);
33 ExitOnFailure(hr, "Failed to select payload nodes.");
34
35 // get payload node count
36 hr = pixnNodes->get_length((long*)&cNodes);
37 ExitOnFailure(hr, "Failed to get payload node count.");
38
39 if (!cNodes)
40 {
41 ExitFunction();
42 }
43
44 // allocate memory for payloads
45 pPayloads->rgPayloads = (BURN_PAYLOAD*)MemAlloc(sizeof(BURN_PAYLOAD) * cNodes, TRUE);
46 ExitOnNull(pPayloads->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate memory for payload structs.");
47
48 pPayloads->cPayloads = cNodes;
49
50 // parse search elements
51 for (DWORD i = 0; i < cNodes; ++i)
52 {
53 BURN_PAYLOAD* pPayload = &pPayloads->rgPayloads[i];
54
55 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
56 ExitOnFailure(hr, "Failed to get next node.");
57
58 // @Id
59 hr = XmlGetAttributeEx(pixnNode, L"Id", &pPayload->sczKey);
60 ExitOnFailure(hr, "Failed to get @Id.");
61
62 // @FilePath
63 hr = XmlGetAttributeEx(pixnNode, L"FilePath", &pPayload->sczFilePath);
64 ExitOnFailure(hr, "Failed to get @FilePath.");
65
66 // @Packaging
67 hr = XmlGetAttributeEx(pixnNode, L"Packaging", &scz);
68 ExitOnFailure(hr, "Failed to get @Packaging.");
69
70 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"embedded", -1))
71 {
72 pPayload->packaging = BURN_PAYLOAD_PACKAGING_EMBEDDED;
73 }
74 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"external", -1))
75 {
76 pPayload->packaging = BURN_PAYLOAD_PACKAGING_EXTERNAL;
77 }
78 else
79 {
80 hr = E_INVALIDARG;
81 ExitOnFailure(hr, "Invalid value for @Packaging: %ls", scz);
82 }
83
84 // @Container
85 if (pContainers)
86 {
87 hr = XmlGetAttributeEx(pixnNode, L"Container", &scz);
88 if (E_NOTFOUND != hr || BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging)
89 {
90 ExitOnFailure(hr, "Failed to get @Container.");
91
92 // find container
93 hr = ContainerFindById(pContainers, scz, &pPayload->pContainer);
94 ExitOnFailure(hr, "Failed to to find container: %ls", scz);
95 }
96 }
97
98 // @LayoutOnly
99 hr = XmlGetYesNoAttribute(pixnNode, L"LayoutOnly", &pPayload->fLayoutOnly);
100 if (E_NOTFOUND != hr)
101 {
102 ExitOnFailure(hr, "Failed to get @LayoutOnly.");
103 }
104
105 // @SourcePath
106 hr = XmlGetAttributeEx(pixnNode, L"SourcePath", &pPayload->sczSourcePath);
107 ExitOnFailure(hr, "Failed to get @SourcePath.");
108
109 // @DownloadUrl
110 hr = XmlGetAttributeEx(pixnNode, L"DownloadUrl", &pPayload->downloadSource.sczUrl);
111 if (E_NOTFOUND != hr)
112 {
113 ExitOnFailure(hr, "Failed to get @DownloadUrl.");
114 }
115
116 // @FileSize
117 hr = XmlGetAttributeEx(pixnNode, L"FileSize", &scz);
118 if (E_NOTFOUND != hr)
119 {
120 ExitOnFailure(hr, "Failed to get @FileSize.");
121
122 hr = StrStringToUInt64(scz, 0, &pPayload->qwFileSize);
123 ExitOnFailure(hr, "Failed to parse @FileSize.");
124 }
125
126 // @Hash
127 hr = XmlGetAttributeEx(pixnNode, L"Hash", &scz);
128 ExitOnFailure(hr, "Failed to get @Hash.");
129
130 hr = StrAllocHexDecode(scz, &pPayload->pbHash, &pPayload->cbHash);
131 ExitOnFailure(hr, "Failed to hex decode the Payload/@Hash.");
132
133 if (pPayload->fLayoutOnly && pLayoutPayloads)
134 {
135 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pLayoutPayloads->rgItems), pLayoutPayloads->cItems + 1, sizeof(BURN_PAYLOAD_GROUP_ITEM), 5);
136 ExitOnFailure(hr, "Failed to allocate memory for layout payloads.");
137
138 pLayoutPayloads->rgItems[pLayoutPayloads->cItems].pPayload = pPayload;
139 ++pLayoutPayloads->cItems;
140
141 pLayoutPayloads->qwTotalSize += pPayload->qwFileSize;
142 }
143
144 // prepare next iteration
145 ReleaseNullObject(pixnNode);
146 }
147
148 hr = S_OK;
149
150LExit:
151 ReleaseObject(pixnNodes);
152 ReleaseObject(pixnNode);
153 ReleaseStr(scz);
154
155 return hr;
156}
157
158extern "C" void PayloadUninitialize(
159 __in BURN_PAYLOAD* pPayload
160 )
161{
162 if (pPayload)
163 {
164 ReleaseStr(pPayload->sczKey);
165 ReleaseStr(pPayload->sczFilePath);
166 ReleaseMem(pPayload->pbHash);
167 ReleaseStr(pPayload->sczSourcePath);
168 ReleaseStr(pPayload->sczLocalFilePath);
169 ReleaseStr(pPayload->downloadSource.sczUrl);
170 ReleaseStr(pPayload->downloadSource.sczUser);
171 ReleaseStr(pPayload->downloadSource.sczPassword);
172 ReleaseStr(pPayload->sczUnverifiedPath);
173 }
174}
175
176extern "C" void PayloadsUninitialize(
177 __in BURN_PAYLOADS* pPayloads
178 )
179{
180 if (pPayloads->rgPayloads)
181 {
182 for (DWORD i = 0; i < pPayloads->cPayloads; ++i)
183 {
184 PayloadUninitialize(pPayloads->rgPayloads + i);
185 }
186 MemFree(pPayloads->rgPayloads);
187 }
188
189 // clear struct
190 memset(pPayloads, 0, sizeof(BURN_PAYLOADS));
191}
192
193extern "C" HRESULT PayloadExtractUXContainer(
194 __in BURN_PAYLOADS* pPayloads,
195 __in BURN_CONTAINER_CONTEXT* pContainerContext,
196 __in_z LPCWSTR wzTargetDir
197 )
198{
199 HRESULT hr = S_OK;
200 LPWSTR sczStreamName = NULL;
201 LPWSTR sczDirectory = NULL;
202 BURN_PAYLOAD* pPayload = NULL;
203
204 // extract all payloads
205 for (;;)
206 {
207 // get next stream
208 hr = ContainerNextStream(pContainerContext, &sczStreamName);
209 if (E_NOMOREITEMS == hr)
210 {
211 hr = S_OK;
212 break;
213 }
214 ExitOnFailure(hr, "Failed to get next stream.");
215
216 // find payload by stream name
217 hr = PayloadFindEmbeddedBySourcePath(pPayloads, sczStreamName, &pPayload);
218 ExitOnFailure(hr, "Failed to find embedded payload: %ls", sczStreamName);
219
220 // make file path
221 hr = PathConcat(wzTargetDir, pPayload->sczFilePath, &pPayload->sczLocalFilePath);
222 ExitOnFailure(hr, "Failed to concat file paths.");
223
224 // extract file
225 hr = PathGetDirectory(pPayload->sczLocalFilePath, &sczDirectory);
226 ExitOnFailure(hr, "Failed to get directory portion of local file path");
227
228 hr = DirEnsureExists(sczDirectory, NULL);
229 ExitOnFailure(hr, "Failed to ensure directory exists");
230
231 hr = ContainerStreamToFile(pContainerContext, pPayload->sczLocalFilePath);
232 ExitOnFailure(hr, "Failed to extract file.");
233
234 // flag that the payload has been acquired
235 pPayload->state = BURN_PAYLOAD_STATE_ACQUIRED;
236 }
237
238 // locate any payloads that were not extracted
239 for (DWORD i = 0; i < pPayloads->cPayloads; ++i)
240 {
241 pPayload = &pPayloads->rgPayloads[i];
242
243 // if the payload has not been acquired
244 if (BURN_PAYLOAD_STATE_ACQUIRED > pPayload->state)
245 {
246 hr = E_INVALIDDATA;
247 ExitOnRootFailure(hr, "Payload was not found in container: %ls", pPayload->sczKey);
248 }
249 }
250
251LExit:
252 ReleaseStr(sczStreamName);
253 ReleaseStr(sczDirectory);
254
255 return hr;
256}
257
258extern "C" HRESULT PayloadFindById(
259 __in BURN_PAYLOADS* pPayloads,
260 __in_z LPCWSTR wzId,
261 __out BURN_PAYLOAD** ppPayload
262 )
263{
264 HRESULT hr = S_OK;
265 BURN_PAYLOAD* pPayload = NULL;
266
267 for (DWORD i = 0; i < pPayloads->cPayloads; ++i)
268 {
269 pPayload = &pPayloads->rgPayloads[i];
270
271 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPayload->sczKey, -1, wzId, -1))
272 {
273 *ppPayload = pPayload;
274 ExitFunction1(hr = S_OK);
275 }
276 }
277
278 hr = E_NOTFOUND;
279
280LExit:
281 return hr;
282}
283
284extern "C" HRESULT PayloadFindEmbeddedBySourcePath(
285 __in BURN_PAYLOADS* pPayloads,
286 __in_z LPCWSTR wzStreamName,
287 __out BURN_PAYLOAD** ppPayload
288 )
289{
290 HRESULT hr = S_OK;
291 BURN_PAYLOAD* pPayload = NULL;
292
293 for (DWORD i = 0; i < pPayloads->cPayloads; ++i)
294 {
295 pPayload = &pPayloads->rgPayloads[i];
296
297 if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging)
298 {
299 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPayload->sczSourcePath, -1, wzStreamName, -1))
300 {
301 *ppPayload = pPayload;
302 ExitFunction1(hr = S_OK);
303 }
304 }
305 }
306
307 hr = E_NOTFOUND;
308
309LExit:
310 return hr;
311}
312
313
314// internal function definitions
diff --git a/src/burn/engine/payload.h b/src/burn/engine/payload.h
new file mode 100644
index 00000000..f28b437f
--- /dev/null
+++ b/src/burn/engine/payload.h
@@ -0,0 +1,107 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// constants
11
12enum BURN_PAYLOAD_PACKAGING
13{
14 BURN_PAYLOAD_PACKAGING_NONE,
15 BURN_PAYLOAD_PACKAGING_EMBEDDED,
16 BURN_PAYLOAD_PACKAGING_EXTERNAL,
17};
18
19enum BURN_PAYLOAD_STATE
20{
21 BURN_PAYLOAD_STATE_NONE,
22 BURN_PAYLOAD_STATE_ACQUIRED,
23 BURN_PAYLOAD_STATE_CACHED,
24};
25
26
27// structs
28
29typedef struct _BURN_PAYLOAD
30{
31 LPWSTR sczKey;
32 BURN_PAYLOAD_PACKAGING packaging;
33 BOOL fLayoutOnly;
34 DWORD64 qwFileSize;
35 LPWSTR sczFilePath; // file path relative to the execute location
36
37 BYTE* pbHash;
38 DWORD cbHash;
39
40 LPWSTR sczSourcePath;
41 BURN_CONTAINER* pContainer;
42 DOWNLOAD_SOURCE downloadSource;
43
44 // mutable members
45 BURN_PAYLOAD_STATE state;
46 LPWSTR sczLocalFilePath; // location of extracted or downloaded copy
47
48 LPWSTR sczUnverifiedPath;
49 DWORD cRemainingInstances;
50} BURN_PAYLOAD;
51
52typedef struct _BURN_PAYLOADS
53{
54 BURN_PAYLOAD* rgPayloads;
55 DWORD cPayloads;
56} BURN_PAYLOADS;
57
58typedef struct _BURN_PAYLOAD_GROUP_ITEM
59{
60 BURN_PAYLOAD* pPayload;
61
62 // mutable members
63 BOOL fCached;
64 DWORD64 qwCommittedCacheProgress;
65} BURN_PAYLOAD_GROUP_ITEM;
66
67typedef struct _BURN_PAYLOAD_GROUP
68{
69 BURN_PAYLOAD_GROUP_ITEM* rgItems;
70 DWORD cItems;
71 DWORD64 qwTotalSize;
72} BURN_PAYLOAD_GROUP;
73
74// functions
75
76HRESULT PayloadsParseFromXml(
77 __in BURN_PAYLOADS* pPayloads,
78 __in_opt BURN_CONTAINERS* pContainers,
79 __in_opt BURN_PAYLOAD_GROUP* pLayoutPayloads,
80 __in IXMLDOMNode* pixnBundle
81 );
82void PayloadUninitialize(
83 __in BURN_PAYLOAD* pPayload
84 );
85void PayloadsUninitialize(
86 __in BURN_PAYLOADS* pPayloads
87 );
88HRESULT PayloadExtractUXContainer(
89 __in BURN_PAYLOADS* pPayloads,
90 __in BURN_CONTAINER_CONTEXT* pContainerContext,
91 __in_z LPCWSTR wzTargetDir
92 );
93HRESULT PayloadFindById(
94 __in BURN_PAYLOADS* pPayloads,
95 __in_z LPCWSTR wzId,
96 __out BURN_PAYLOAD** ppPayload
97 );
98HRESULT PayloadFindEmbeddedBySourcePath(
99 __in BURN_PAYLOADS* pPayloads,
100 __in_z LPCWSTR wzStreamName,
101 __out BURN_PAYLOAD** ppPayload
102 );
103
104
105#if defined(__cplusplus)
106}
107#endif
diff --git a/src/burn/engine/pipe.cpp b/src/burn/engine/pipe.cpp
new file mode 100644
index 00000000..a9fd24e8
--- /dev/null
+++ b/src/burn/engine/pipe.cpp
@@ -0,0 +1,821 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5static const DWORD PIPE_64KB = 64 * 1024;
6static const DWORD PIPE_WAIT_FOR_CONNECTION = 100; // wait a 10th of a second,
7static const DWORD PIPE_RETRY_FOR_CONNECTION = 1800; // for up to 3 minutes.
8
9static const LPCWSTR PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls";
10static const LPCWSTR CACHE_PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls.Cache";
11
12static HRESULT AllocatePipeMessage(
13 __in DWORD dwMessage,
14 __in_bcount_opt(cbData) LPVOID pvData,
15 __in SIZE_T cbData,
16 __out_bcount(cb) LPVOID* ppvMessage,
17 __out SIZE_T* cbMessage
18 );
19static void FreePipeMessage(
20 __in BURN_PIPE_MESSAGE *pMsg
21 );
22static HRESULT WritePipeMessage(
23 __in HANDLE hPipe,
24 __in DWORD dwMessage,
25 __in_bcount_opt(cbData) LPVOID pvData,
26 __in SIZE_T cbData
27 );
28static HRESULT GetPipeMessage(
29 __in HANDLE hPipe,
30 __in BURN_PIPE_MESSAGE* pMsg
31 );
32static HRESULT ChildPipeConnected(
33 __in HANDLE hPipe,
34 __in_z LPCWSTR wzSecret,
35 __inout DWORD* pdwProcessId
36 );
37
38
39
40/*******************************************************************
41 PipeConnectionInitialize - initialize pipe connection data.
42
43*******************************************************************/
44void PipeConnectionInitialize(
45 __in BURN_PIPE_CONNECTION* pConnection
46 )
47{
48 memset(pConnection, 0, sizeof(BURN_PIPE_CONNECTION));
49 pConnection->hPipe = INVALID_HANDLE_VALUE;
50 pConnection->hCachePipe = INVALID_HANDLE_VALUE;
51}
52
53/*******************************************************************
54 PipeConnectionUninitialize - free data in a pipe connection.
55
56*******************************************************************/
57void PipeConnectionUninitialize(
58 __in BURN_PIPE_CONNECTION* pConnection
59 )
60{
61 ReleaseFileHandle(pConnection->hCachePipe);
62 ReleaseFileHandle(pConnection->hPipe);
63 ReleaseHandle(pConnection->hProcess);
64 ReleaseStr(pConnection->sczSecret);
65 ReleaseStr(pConnection->sczName);
66
67 memset(pConnection, 0, sizeof(BURN_PIPE_CONNECTION));
68 pConnection->hPipe = INVALID_HANDLE_VALUE;
69 pConnection->hCachePipe = INVALID_HANDLE_VALUE;
70}
71
72/*******************************************************************
73 PipeSendMessage -
74
75*******************************************************************/
76extern "C" HRESULT PipeSendMessage(
77 __in HANDLE hPipe,
78 __in DWORD dwMessage,
79 __in_bcount_opt(cbData) LPVOID pvData,
80 __in SIZE_T cbData,
81 __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback,
82 __in_opt LPVOID pvContext,
83 __out DWORD* pdwResult
84 )
85{
86 HRESULT hr = S_OK;
87 BURN_PIPE_RESULT result = { };
88
89 hr = WritePipeMessage(hPipe, dwMessage, pvData, cbData);
90 ExitOnFailure(hr, "Failed to write send message to pipe.");
91
92 hr = PipePumpMessages(hPipe, pfnCallback, pvContext, &result);
93 ExitOnFailure(hr, "Failed to pump messages during send message to pipe.");
94
95 *pdwResult = result.dwResult;
96
97LExit:
98 return hr;
99}
100
101/*******************************************************************
102 PipePumpMessages -
103
104*******************************************************************/
105extern "C" HRESULT PipePumpMessages(
106 __in HANDLE hPipe,
107 __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback,
108 __in_opt LPVOID pvContext,
109 __in BURN_PIPE_RESULT* pResult
110 )
111{
112 HRESULT hr = S_OK;
113 BURN_PIPE_MESSAGE msg = { };
114 SIZE_T iData = 0;
115 LPSTR sczMessage = NULL;
116 DWORD dwResult = 0;
117
118 // Pump messages from child process.
119 while (S_OK == (hr = GetPipeMessage(hPipe, &msg)))
120 {
121 switch (msg.dwMessage)
122 {
123 case BURN_PIPE_MESSAGE_TYPE_LOG:
124 iData = 0;
125
126 hr = BuffReadStringAnsi((BYTE*)msg.pvData, msg.cbData, &iData, &sczMessage);
127 ExitOnFailure(hr, "Failed to read log message.");
128
129 hr = LogStringWorkRaw(sczMessage);
130 ExitOnFailure(hr, "Failed to write log message:'%hs'.", sczMessage);
131
132 dwResult = static_cast<DWORD>(hr);
133 break;
134
135 case BURN_PIPE_MESSAGE_TYPE_COMPLETE:
136 if (!msg.pvData || sizeof(DWORD) != msg.cbData)
137 {
138 hr = E_INVALIDARG;
139 ExitOnRootFailure(hr, "No status returned to PipePumpMessages()");
140 }
141
142 pResult->dwResult = *static_cast<DWORD*>(msg.pvData);
143 ExitFunction1(hr = S_OK); // exit loop.
144
145 case BURN_PIPE_MESSAGE_TYPE_TERMINATE:
146 iData = 0;
147
148 hr = BuffReadNumber(static_cast<BYTE*>(msg.pvData), msg.cbData, &iData, &pResult->dwResult);
149 ExitOnFailure(hr, "Failed to read returned result to PipePumpMessages()");
150
151 if (sizeof(DWORD) * 2 == msg.cbData)
152 {
153 hr = BuffReadNumber(static_cast<BYTE*>(msg.pvData), msg.cbData, &iData, (DWORD*)&pResult->fRestart);
154 ExitOnFailure(hr, "Failed to read returned restart to PipePumpMessages()");
155 }
156
157 ExitFunction1(hr = S_OK); // exit loop.
158
159 default:
160 if (pfnCallback)
161 {
162 hr = pfnCallback(&msg, pvContext, &dwResult);
163 }
164 else
165 {
166 hr = E_INVALIDARG;
167 }
168 ExitOnFailure(hr, "Failed to process message: %u", msg.dwMessage);
169 break;
170 }
171
172 // post result
173 hr = WritePipeMessage(hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_COMPLETE), &dwResult, sizeof(dwResult));
174 ExitOnFailure(hr, "Failed to post result to child process.");
175
176 FreePipeMessage(&msg);
177 }
178 ExitOnFailure(hr, "Failed to get message over pipe");
179
180 if (S_FALSE == hr)
181 {
182 hr = S_OK;
183 }
184
185LExit:
186 ReleaseStr(sczMessage);
187 FreePipeMessage(&msg);
188
189 return hr;
190}
191
192/*******************************************************************
193 PipeCreateNameAndSecret -
194
195*******************************************************************/
196extern "C" HRESULT PipeCreateNameAndSecret(
197 __out_z LPWSTR *psczConnectionName,
198 __out_z LPWSTR *psczSecret
199 )
200{
201 HRESULT hr = S_OK;
202 WCHAR wzGuid[GUID_STRING_LENGTH];
203 LPWSTR sczConnectionName = NULL;
204 LPWSTR sczSecret = NULL;
205
206 // Create the unique pipe name.
207 hr = GuidFixedCreate(wzGuid);
208 ExitOnRootFailure(hr, "Failed to create pipe guid.");
209
210 hr = StrAllocFormatted(&sczConnectionName, L"BurnPipe.%s", wzGuid);
211 ExitOnFailure(hr, "Failed to allocate pipe name.");
212
213 // Create the unique client secret.
214 hr = GuidFixedCreate(wzGuid);
215 ExitOnRootFailure(hr, "Failed to create pipe secret.");
216
217 hr = StrAllocString(&sczSecret, wzGuid, 0);
218 ExitOnFailure(hr, "Failed to allocate pipe secret.");
219
220 *psczConnectionName = sczConnectionName;
221 sczConnectionName = NULL;
222 *psczSecret = sczSecret;
223 sczSecret = NULL;
224
225LExit:
226 ReleaseStr(sczSecret);
227 ReleaseStr(sczConnectionName);
228
229 return hr;
230}
231
232/*******************************************************************
233 PipeCreatePipes - create the pipes and event to signal child process.
234
235*******************************************************************/
236extern "C" HRESULT PipeCreatePipes(
237 __in BURN_PIPE_CONNECTION* pConnection,
238 __in BOOL fCreateCachePipe,
239 __out HANDLE* phEvent
240 )
241{
242 Assert(pConnection->sczName);
243 Assert(INVALID_HANDLE_VALUE == pConnection->hPipe);
244 Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe);
245
246 HRESULT hr = S_OK;
247 PSECURITY_DESCRIPTOR psd = NULL;
248 SECURITY_ATTRIBUTES sa = { };
249 LPWSTR sczFullPipeName = NULL;
250 HANDLE hPipe = INVALID_HANDLE_VALUE;
251 HANDLE hCachePipe = INVALID_HANDLE_VALUE;
252
253 // Only the grant special rights when the pipe is being used for "embedded"
254 // scenarios (aka: there is no cache pipe).
255 if (!fCreateCachePipe)
256 {
257 // Create the security descriptor that grants read/write/sync access to Everyone.
258 // TODO: consider locking down "WD" to LogonIds (logon session)
259 LPCWSTR wzSddl = L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW0x00100000;;;WD)";
260 if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(wzSddl, SDDL_REVISION_1, &psd, NULL))
261 {
262 ExitWithLastError(hr, "Failed to create the security descriptor for the connection event and pipe.");
263 }
264
265 sa.nLength = sizeof(sa);
266 sa.lpSecurityDescriptor = psd;
267 sa.bInheritHandle = FALSE;
268 }
269
270 // Create the pipe.
271 hr = StrAllocFormatted(&sczFullPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName);
272 ExitOnFailure(hr, "Failed to allocate full name of pipe: %ls", pConnection->sczName);
273
274 // TODO: consider using overlapped IO to do waits on the pipe and still be able to cancel and such.
275 hPipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, psd ? &sa : NULL);
276 if (INVALID_HANDLE_VALUE == hPipe)
277 {
278 ExitWithLastError(hr, "Failed to create pipe: %ls", sczFullPipeName);
279 }
280
281 if (fCreateCachePipe)
282 {
283 // Create the cache pipe.
284 hr = StrAllocFormatted(&sczFullPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName);
285 ExitOnFailure(hr, "Failed to allocate full name of cache pipe: %ls", pConnection->sczName);
286
287 hCachePipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, NULL);
288 if (INVALID_HANDLE_VALUE == hCachePipe)
289 {
290 ExitWithLastError(hr, "Failed to create pipe: %ls", sczFullPipeName);
291 }
292 }
293
294 pConnection->hCachePipe = hCachePipe;
295 hCachePipe = INVALID_HANDLE_VALUE;
296
297 pConnection->hPipe = hPipe;
298 hPipe = INVALID_HANDLE_VALUE;
299
300 // TODO: remove the following
301 *phEvent = NULL;
302
303LExit:
304 ReleaseFileHandle(hCachePipe);
305 ReleaseFileHandle(hPipe);
306 ReleaseStr(sczFullPipeName);
307
308 if (psd)
309 {
310 ::LocalFree(psd);
311 }
312
313 return hr;
314}
315
316/*******************************************************************
317 PipeLaunchParentProcess - Called from the per-machine process to create
318 a per-user process and set up the
319 communication pipe.
320
321*******************************************************************/
322const LPCWSTR BURN_COMMANDLINE_SWITCH_UNELEVATED = L"burn.unelevated";
323HRESULT PipeLaunchParentProcess(
324 __in_z LPCWSTR wzCommandLine,
325 __in int nCmdShow,
326 __in_z LPWSTR sczConnectionName,
327 __in_z LPWSTR sczSecret,
328 __in BOOL /*fDisableUnelevate*/
329 )
330{
331 HRESULT hr = S_OK;
332 DWORD dwProcessId = 0;
333 LPWSTR sczBurnPath = NULL;
334 LPWSTR sczParameters = NULL;
335 HANDLE hProcess = NULL;
336
337 dwProcessId = ::GetCurrentProcessId();
338
339 hr = PathForCurrentProcess(&sczBurnPath, NULL);
340 ExitOnFailure(hr, "Failed to get current process path.");
341
342 hr = StrAllocFormatted(&sczParameters, L"-%ls %ls %ls %u %ls", BURN_COMMANDLINE_SWITCH_UNELEVATED, sczConnectionName, sczSecret, dwProcessId, wzCommandLine);
343 ExitOnFailure(hr, "Failed to allocate parameters for unelevated process.");
344
345#ifdef ENABLE_UNELEVATE
346 if (fDisableUnelevate)
347 {
348 hr = ProcExec(sczBurnPath, sczParameters, nCmdShow, &hProcess);
349 ExitOnFailure(hr, "Failed to launch parent process with unelevate disabled: %ls", sczBurnPath);
350 }
351 else
352 {
353 // Try to launch unelevated and if that fails for any reason, try launch our process normally (even though that may make it elevated).
354 hr = ProcExecuteAsInteractiveUser(sczBurnPath, sczParameters, &hProcess);
355 if (FAILED(hr))
356 {
357 hr = ShelExecUnelevated(sczBurnPath, sczParameters, L"open", NULL, nCmdShow);
358 if (FAILED(hr))
359 {
360 hr = ShelExec(sczBurnPath, sczParameters, L"open", NULL, nCmdShow, NULL, NULL);
361 ExitOnFailure(hr, "Failed to launch parent process: %ls", sczBurnPath);
362 }
363 }
364 }
365#else
366 hr = ProcExec(sczBurnPath, sczParameters, nCmdShow, &hProcess);
367 ExitOnFailure(hr, "Failed to launch parent process with unelevate disabled: %ls", sczBurnPath);
368#endif
369
370LExit:
371 ReleaseHandle(hProcess);
372 ReleaseStr(sczParameters);
373 ReleaseStr(sczBurnPath);
374
375 return hr;
376}
377
378/*******************************************************************
379 PipeLaunchChildProcess - Called from the per-user process to create
380 the per-machine process and set up the
381 communication pipe.
382
383*******************************************************************/
384extern "C" HRESULT PipeLaunchChildProcess(
385 __in_z LPCWSTR wzExecutablePath,
386 __in BURN_PIPE_CONNECTION* pConnection,
387 __in BOOL fElevate,
388 __in_opt HWND hwndParent
389 )
390{
391 HRESULT hr = S_OK;
392 DWORD dwCurrentProcessId = ::GetCurrentProcessId();
393 LPWSTR sczParameters = NULL;
394 LPCWSTR wzVerb = NULL;
395 HANDLE hProcess = NULL;
396
397 hr = StrAllocFormatted(&sczParameters, L"-q -%ls %ls %ls %u", BURN_COMMANDLINE_SWITCH_ELEVATED, pConnection->sczName, pConnection->sczSecret, dwCurrentProcessId);
398 ExitOnFailure(hr, "Failed to allocate parameters for elevated process.");
399
400 wzVerb = !fElevate ? L"open" : L"runas";
401
402 // Since ShellExecuteEx doesn't support passing inherited handles, don't bother with CoreAppendFileHandleSelfToCommandLine.
403 // We could fallback to using ::DuplicateHandle to inject the file handle later if necessary.
404 hr = ShelExec(wzExecutablePath, sczParameters, wzVerb, NULL, SW_SHOWNA, hwndParent, &hProcess);
405 ExitOnFailure(hr, "Failed to launch elevated child process: %ls", wzExecutablePath);
406
407 pConnection->dwProcessId = ::GetProcessId(hProcess);
408 pConnection->hProcess = hProcess;
409 hProcess = NULL;
410
411LExit:
412 ReleaseHandle(hProcess);
413 ReleaseStr(sczParameters);
414
415 return hr;
416}
417
418/*******************************************************************
419 PipeWaitForChildConnect -
420
421*******************************************************************/
422extern "C" HRESULT PipeWaitForChildConnect(
423 __in BURN_PIPE_CONNECTION* pConnection
424 )
425{
426 HRESULT hr = S_OK;
427 HANDLE hPipes[2] = { pConnection->hPipe, pConnection->hCachePipe};
428 LPCWSTR wzSecret = pConnection->sczSecret;
429 DWORD cbSecret = lstrlenW(wzSecret) * sizeof(WCHAR);
430 DWORD dwCurrentProcessId = ::GetCurrentProcessId();
431 DWORD dwAck = 0;
432
433 for (DWORD i = 0; i < countof(hPipes) && INVALID_HANDLE_VALUE != hPipes[i]; ++i)
434 {
435 HANDLE hPipe = hPipes[i];
436 DWORD dwPipeState = PIPE_READMODE_BYTE | PIPE_NOWAIT;
437
438 // Temporarily make the pipe non-blocking so we will not get stuck in ::ConnectNamedPipe() forever
439 // if the child decides not to show up.
440 if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL))
441 {
442 ExitWithLastError(hr, "Failed to set pipe to non-blocking.");
443 }
444
445 // Loop for a while waiting for a connection from child process.
446 DWORD cRetry = 0;
447 do
448 {
449 if (!::ConnectNamedPipe(hPipe, NULL))
450 {
451 DWORD er = ::GetLastError();
452 if (ERROR_PIPE_CONNECTED == er)
453 {
454 hr = S_OK;
455 break;
456 }
457 else if (ERROR_PIPE_LISTENING == er)
458 {
459 if (cRetry < PIPE_RETRY_FOR_CONNECTION)
460 {
461 hr = HRESULT_FROM_WIN32(er);
462 }
463 else
464 {
465 hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
466 break;
467 }
468
469 ++cRetry;
470 ::Sleep(PIPE_WAIT_FOR_CONNECTION);
471 }
472 else
473 {
474 hr = HRESULT_FROM_WIN32(er);
475 break;
476 }
477 }
478 } while (HRESULT_FROM_WIN32(ERROR_PIPE_LISTENING) == hr);
479 ExitOnRootFailure(hr, "Failed to wait for child to connect to pipe.");
480
481 // Put the pipe back in blocking mode.
482 dwPipeState = PIPE_READMODE_BYTE | PIPE_WAIT;
483 if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL))
484 {
485 ExitWithLastError(hr, "Failed to reset pipe to blocking.");
486 }
487
488 // Prove we are the one that created the elevated process by passing the secret.
489 hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(&cbSecret), sizeof(cbSecret));
490 ExitOnFailure(hr, "Failed to write secret length to pipe.");
491
492 hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(wzSecret), cbSecret);
493 ExitOnFailure(hr, "Failed to write secret to pipe.");
494
495 hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(&dwCurrentProcessId), sizeof(dwCurrentProcessId));
496 ExitOnFailure(hr, "Failed to write our process id to pipe.");
497
498 // Wait until the elevated process responds that it is ready to go.
499 hr = FileReadHandle(hPipe, reinterpret_cast<LPBYTE>(&dwAck), sizeof(dwAck));
500 ExitOnFailure(hr, "Failed to read ACK from pipe.");
501
502 // The ACK should match out expected child process id.
503 //if (pConnection->dwProcessId != dwAck)
504 //{
505 // hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
506 // ExitOnRootFailure(hr, "Incorrect ACK from elevated pipe: %u", dwAck);
507 //}
508 }
509
510LExit:
511 return hr;
512}
513
514/*******************************************************************
515 PipeTerminateChildProcess -
516
517*******************************************************************/
518extern "C" HRESULT PipeTerminateChildProcess(
519 __in BURN_PIPE_CONNECTION* pConnection,
520 __in DWORD dwParentExitCode,
521 __in BOOL fRestart
522 )
523{
524 HRESULT hr = S_OK;
525 BYTE* pbData = NULL;
526 SIZE_T cbData = 0;
527
528 // Prepare the exit message.
529 hr = BuffWriteNumber(&pbData, &cbData, dwParentExitCode);
530 ExitOnFailure(hr, "Failed to write exit code to message buffer.");
531
532 hr = BuffWriteNumber(&pbData, &cbData, fRestart);
533 ExitOnFailure(hr, "Failed to write restart to message buffer.");
534
535 // Send the messages.
536 if (INVALID_HANDLE_VALUE != pConnection->hCachePipe)
537 {
538 hr = WritePipeMessage(pConnection->hCachePipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData);
539 ExitOnFailure(hr, "Failed to post terminate message to child process cache thread.");
540 }
541
542 hr = WritePipeMessage(pConnection->hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData);
543 ExitOnFailure(hr, "Failed to post terminate message to child process.");
544
545 // If we were able to get a handle to the other process, wait for it to exit.
546 if (pConnection->hProcess)
547 {
548 if (WAIT_FAILED == ::WaitForSingleObject(pConnection->hProcess, PIPE_WAIT_FOR_CONNECTION * PIPE_RETRY_FOR_CONNECTION))
549 {
550 ExitWithLastError(hr, "Failed to wait for child process exit.");
551 }
552
553#ifdef DEBUG
554 DWORD dwChildExitCode = 0;
555 DWORD dwErrorCode = ERROR_SUCCESS;
556 BOOL fReturnedExitCode = ::GetExitCodeProcess(pConnection->hProcess, &dwChildExitCode);
557 if (!fReturnedExitCode)
558 {
559 dwErrorCode = ::GetLastError(); // if the other process is elevated and we are not, then we'll get ERROR_ACCESS_DENIED.
560
561 // The unit test use a thread instead of a process so try to get the exit code from
562 // the thread because we failed to get it from the process.
563 if (ERROR_INVALID_HANDLE == dwErrorCode)
564 {
565 fReturnedExitCode = ::GetExitCodeThread(pConnection->hProcess, &dwChildExitCode);
566 }
567 }
568 AssertSz((fReturnedExitCode && dwChildExitCode == dwParentExitCode) ||
569 (!fReturnedExitCode && ERROR_ACCESS_DENIED == dwErrorCode),
570 "Child elevated process did not return matching exit code to parent process.");
571#endif
572 }
573
574LExit:
575 return hr;
576}
577
578/*******************************************************************
579 PipeChildConnect - Called from the child process to connect back
580 to the pipe provided by the parent process.
581
582*******************************************************************/
583extern "C" HRESULT PipeChildConnect(
584 __in BURN_PIPE_CONNECTION* pConnection,
585 __in BOOL fConnectCachePipe
586 )
587{
588 Assert(pConnection->sczName);
589 Assert(pConnection->sczSecret);
590 Assert(!pConnection->hProcess);
591 Assert(INVALID_HANDLE_VALUE == pConnection->hPipe);
592 Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe);
593
594 HRESULT hr = S_OK;
595 LPWSTR sczPipeName = NULL;
596
597 // Try to connect to the parent.
598 hr = StrAllocFormatted(&sczPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName);
599 ExitOnFailure(hr, "Failed to allocate name of parent pipe.");
600
601 hr = E_UNEXPECTED;
602 for (DWORD cRetry = 0; FAILED(hr) && cRetry < PIPE_RETRY_FOR_CONNECTION; ++cRetry)
603 {
604 pConnection->hPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
605 if (INVALID_HANDLE_VALUE == pConnection->hPipe)
606 {
607 hr = HRESULT_FROM_WIN32(::GetLastError());
608 if (E_FILENOTFOUND == hr) // if the pipe isn't created, call it a timeout waiting on the parent.
609 {
610 hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
611 }
612
613 ::Sleep(PIPE_WAIT_FOR_CONNECTION);
614 }
615 else // we have a connection, go with it.
616 {
617 hr = S_OK;
618 }
619 }
620 ExitOnRootFailure(hr, "Failed to open parent pipe: %ls", sczPipeName)
621
622 // Verify the parent and notify it that the child connected.
623 hr = ChildPipeConnected(pConnection->hPipe, pConnection->sczSecret, &pConnection->dwProcessId);
624 ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName);
625
626 if (fConnectCachePipe)
627 {
628 // Connect to the parent for the cache pipe.
629 hr = StrAllocFormatted(&sczPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName);
630 ExitOnFailure(hr, "Failed to allocate name of parent cache pipe.");
631
632 pConnection->hCachePipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
633 if (INVALID_HANDLE_VALUE == pConnection->hCachePipe)
634 {
635 ExitWithLastError(hr, "Failed to open parent pipe: %ls", sczPipeName)
636 }
637
638 // Verify the parent and notify it that the child connected.
639 hr = ChildPipeConnected(pConnection->hCachePipe, pConnection->sczSecret, &pConnection->dwProcessId);
640 ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName);
641 }
642
643 pConnection->hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, pConnection->dwProcessId);
644 ExitOnNullWithLastError(pConnection->hProcess, hr, "Failed to open companion process with PID: %u", pConnection->dwProcessId);
645
646LExit:
647 ReleaseStr(sczPipeName);
648
649 return hr;
650}
651
652
653static HRESULT AllocatePipeMessage(
654 __in DWORD dwMessage,
655 __in_bcount_opt(cbData) LPVOID pvData,
656 __in SIZE_T cbData,
657 __out_bcount(cb) LPVOID* ppvMessage,
658 __out SIZE_T* cbMessage
659 )
660{
661 HRESULT hr = S_OK;
662 LPVOID pv = NULL;
663 SIZE_T cb = 0;
664
665 // If no data was provided, ensure the count of bytes is zero.
666 if (!pvData)
667 {
668 cbData = 0;
669 }
670
671 // Allocate the message.
672 cb = sizeof(dwMessage) + sizeof(cbData) + cbData;
673 pv = MemAlloc(cb, FALSE);
674 ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for message.");
675
676 memcpy_s(pv, cb, &dwMessage, sizeof(dwMessage));
677 memcpy_s(static_cast<BYTE*>(pv) + sizeof(dwMessage), cb - sizeof(dwMessage), &cbData, sizeof(cbData));
678 if (cbData)
679 {
680 memcpy_s(static_cast<BYTE*>(pv) + sizeof(dwMessage) + sizeof(cbData), cb - sizeof(dwMessage) - sizeof(cbData), pvData, cbData);
681 }
682
683 *cbMessage = cb;
684 *ppvMessage = pv;
685 pv = NULL;
686
687LExit:
688 ReleaseMem(pv);
689 return hr;
690}
691
692static void FreePipeMessage(
693 __in BURN_PIPE_MESSAGE *pMsg
694 )
695{
696 if (pMsg->fAllocatedData)
697 {
698 ReleaseNullMem(pMsg->pvData);
699 pMsg->fAllocatedData = FALSE;
700 }
701}
702
703static HRESULT WritePipeMessage(
704 __in HANDLE hPipe,
705 __in DWORD dwMessage,
706 __in_bcount_opt(cbData) LPVOID pvData,
707 __in SIZE_T cbData
708 )
709{
710 HRESULT hr = S_OK;
711 LPVOID pv = NULL;
712 SIZE_T cb = 0;
713
714 hr = AllocatePipeMessage(dwMessage, pvData, cbData, &pv, &cb);
715 ExitOnFailure(hr, "Failed to allocate message to write.");
716
717 // Write the message.
718 hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(pv), cb);
719 ExitOnFailure(hr, "Failed to write message type to pipe.");
720
721LExit:
722 ReleaseMem(pv);
723 return hr;
724}
725
726static HRESULT GetPipeMessage(
727 __in HANDLE hPipe,
728 __in BURN_PIPE_MESSAGE* pMsg
729 )
730{
731 HRESULT hr = S_OK;
732 BYTE pbMessageAndByteCount[sizeof(DWORD) + sizeof(SIZE_T)] = { };
733
734 hr = FileReadHandle(hPipe, pbMessageAndByteCount, sizeof(pbMessageAndByteCount));
735 if (HRESULT_FROM_WIN32(ERROR_BROKEN_PIPE) == hr)
736 {
737 memset(pbMessageAndByteCount, 0, sizeof(pbMessageAndByteCount));
738 hr = S_FALSE;
739 }
740 ExitOnFailure(hr, "Failed to read message from pipe.");
741
742 pMsg->dwMessage = *(DWORD*)(pbMessageAndByteCount);
743 pMsg->cbData = *(SIZE_T*)(pbMessageAndByteCount + sizeof(DWORD));
744 if (pMsg->cbData)
745 {
746 pMsg->pvData = MemAlloc(pMsg->cbData, FALSE);
747 ExitOnNull(pMsg->pvData, hr, E_OUTOFMEMORY, "Failed to allocate data for message.");
748
749 hr = FileReadHandle(hPipe, reinterpret_cast<LPBYTE>(pMsg->pvData), pMsg->cbData);
750 ExitOnFailure(hr, "Failed to read data for message.");
751
752 pMsg->fAllocatedData = TRUE;
753 }
754
755LExit:
756 if (!pMsg->fAllocatedData && pMsg->pvData)
757 {
758 MemFree(pMsg->pvData);
759 }
760
761 return hr;
762}
763
764static HRESULT ChildPipeConnected(
765 __in HANDLE hPipe,
766 __in_z LPCWSTR wzSecret,
767 __inout DWORD* pdwProcessId
768 )
769{
770 HRESULT hr = S_OK;
771 LPWSTR sczVerificationSecret = NULL;
772 DWORD cbVerificationSecret = 0;
773 DWORD dwVerificationProcessId = 0;
774 DWORD dwAck = ::GetCurrentProcessId(); // send our process id as the ACK.
775
776 // Read the verification secret.
777 hr = FileReadHandle(hPipe, reinterpret_cast<LPBYTE>(&cbVerificationSecret), sizeof(cbVerificationSecret));
778 ExitOnFailure(hr, "Failed to read size of verification secret from parent pipe.");
779
780 if (255 < cbVerificationSecret / sizeof(WCHAR))
781 {
782 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
783 ExitOnRootFailure(hr, "Verification secret from parent is too big.");
784 }
785
786 hr = StrAlloc(&sczVerificationSecret, cbVerificationSecret / sizeof(WCHAR) + 1);
787 ExitOnFailure(hr, "Failed to allocate buffer for verification secret.");
788
789 FileReadHandle(hPipe, reinterpret_cast<LPBYTE>(sczVerificationSecret), cbVerificationSecret);
790 ExitOnFailure(hr, "Failed to read verification secret from parent pipe.");
791
792 // Verify the secrets match.
793 if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, 0, sczVerificationSecret, -1, wzSecret, -1))
794 {
795 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
796 ExitOnRootFailure(hr, "Verification secret from parent does not match.");
797 }
798
799 // Read the verification process id.
800 hr = FileReadHandle(hPipe, reinterpret_cast<LPBYTE>(&dwVerificationProcessId), sizeof(dwVerificationProcessId));
801 ExitOnFailure(hr, "Failed to read verification process id from parent pipe.");
802
803 // If a process id was not provided, we'll trust the process id from the parent.
804 if (*pdwProcessId == 0)
805 {
806 *pdwProcessId = dwVerificationProcessId;
807 }
808 else if (*pdwProcessId != dwVerificationProcessId) // verify the ids match.
809 {
810 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
811 ExitOnRootFailure(hr, "Verification process id from parent does not match.");
812 }
813
814 // All is well, tell the parent process.
815 hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(&dwAck), sizeof(dwAck));
816 ExitOnFailure(hr, "Failed to inform parent process that child is running.");
817
818LExit:
819 ReleaseStr(sczVerificationSecret);
820 return hr;
821}
diff --git a/src/burn/engine/pipe.h b/src/burn/engine/pipe.h
new file mode 100644
index 00000000..429cd824
--- /dev/null
+++ b/src/burn/engine/pipe.h
@@ -0,0 +1,113 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9typedef struct _BURN_PIPE_CONNECTION
10{
11 LPWSTR sczName;
12 LPWSTR sczSecret;
13 DWORD dwProcessId;
14
15 HANDLE hProcess;
16 HANDLE hPipe;
17 HANDLE hCachePipe;
18} BURN_PIPE_CONNECTION;
19
20typedef enum _BURN_PIPE_MESSAGE_TYPE : DWORD
21{
22 BURN_PIPE_MESSAGE_TYPE_LOG = 0xF0000001,
23 BURN_PIPE_MESSAGE_TYPE_COMPLETE = 0xF0000002,
24 BURN_PIPE_MESSAGE_TYPE_TERMINATE = 0xF0000003,
25} BURN_PIPE_MESSAGE_TYPE;
26
27typedef struct _BURN_PIPE_MESSAGE
28{
29 DWORD dwMessage;
30 SIZE_T cbData;
31
32 BOOL fAllocatedData;
33 LPVOID pvData;
34} BURN_PIPE_MESSAGE;
35
36typedef struct _BURN_PIPE_RESULT
37{
38 DWORD dwResult;
39 BOOL fRestart;
40} BURN_PIPE_RESULT;
41
42
43typedef HRESULT (*PFN_PIPE_MESSAGE_CALLBACK)(
44 __in BURN_PIPE_MESSAGE* pMsg,
45 __in_opt LPVOID pvContext,
46 __out DWORD* pdwResult
47 );
48
49
50// Common functions.
51void PipeConnectionInitialize(
52 __in BURN_PIPE_CONNECTION* pConnection
53 );
54void PipeConnectionUninitialize(
55 __in BURN_PIPE_CONNECTION* pConnection
56 );
57HRESULT PipeSendMessage(
58 __in HANDLE hPipe,
59 __in DWORD dwMessage,
60 __in_bcount_opt(cbData) LPVOID pvData,
61 __in SIZE_T cbData,
62 __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback,
63 __in_opt LPVOID pvContext,
64 __out DWORD* pdwResult
65 );
66HRESULT PipePumpMessages(
67 __in HANDLE hPipe,
68 __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback,
69 __in_opt LPVOID pvContext,
70 __in BURN_PIPE_RESULT* pResult
71 );
72
73// Parent functions.
74HRESULT PipeCreateNameAndSecret(
75 __out_z LPWSTR *psczConnectionName,
76 __out_z LPWSTR *psczSecret
77 );
78HRESULT PipeCreatePipes(
79 __in BURN_PIPE_CONNECTION* pConnection,
80 __in BOOL fCreateCachePipe,
81 __out HANDLE* phEvent
82 );
83HRESULT PipeLaunchParentProcess(
84 __in LPCWSTR wzCommandLine,
85 __in int nCmdShow,
86 __in_z LPWSTR sczConnectionName,
87 __in_z LPWSTR sczSecret,
88 __in BOOL fDisableUnelevate
89 );
90HRESULT PipeLaunchChildProcess(
91 __in_z LPCWSTR wzExecutablePath,
92 __in BURN_PIPE_CONNECTION* pConnection,
93 __in BOOL fElevate,
94 __in_opt HWND hwndParent
95 );
96HRESULT PipeWaitForChildConnect(
97 __in BURN_PIPE_CONNECTION* pConnection
98 );
99HRESULT PipeTerminateChildProcess(
100 __in BURN_PIPE_CONNECTION* pConnection,
101 __in DWORD dwParentExitCode,
102 __in BOOL fRestart
103 );
104
105// Child functions.
106HRESULT PipeChildConnect(
107 __in BURN_PIPE_CONNECTION* pConnection,
108 __in BOOL fConnectCachePipe
109 );
110
111#ifdef __cplusplus
112}
113#endif
diff --git a/src/burn/engine/plan.cpp b/src/burn/engine/plan.cpp
new file mode 100644
index 00000000..9a4aa5f1
--- /dev/null
+++ b/src/burn/engine/plan.cpp
@@ -0,0 +1,2699 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5#define PlanDumpLevel REPORT_DEBUG
6
7// internal struct definitions
8
9
10// internal function definitions
11
12static void UninitializeRegistrationAction(
13 __in BURN_DEPENDENT_REGISTRATION_ACTION* pAction
14 );
15static void UninitializeCacheAction(
16 __in BURN_CACHE_ACTION* pCacheAction
17 );
18static void ResetPlannedContainerState(
19 __in BURN_CONTAINER* pContainer
20 );
21static void ResetPlannedPayloadsState(
22 __in BURN_PAYLOADS* pPayloads
23 );
24static void ResetPlannedPayloadGroupState(
25 __in BURN_PAYLOAD_GROUP* pPayloadGroup
26 );
27static void ResetPlannedPackageState(
28 __in BURN_PACKAGE* pPackage
29 );
30static void ResetPlannedRollbackBoundaryState(
31 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
32 );
33static HRESULT PlanPackagesHelper(
34 __in BURN_PACKAGE* rgPackages,
35 __in DWORD cPackages,
36 __in BOOL fPlanCleanPackages,
37 __in BURN_USER_EXPERIENCE* pUX,
38 __in BURN_PLAN* pPlan,
39 __in BURN_LOGGING* pLog,
40 __in BURN_VARIABLES* pVariables,
41 __in BOOTSTRAPPER_DISPLAY display,
42 __in BOOTSTRAPPER_RELATION_TYPE relationType
43 );
44static HRESULT InitializePackage(
45 __in BURN_PLAN* pPlan,
46 __in BURN_USER_EXPERIENCE* pUX,
47 __in BURN_VARIABLES* pVariables,
48 __in BURN_PACKAGE* pPackage,
49 __in BOOTSTRAPPER_RELATION_TYPE relationType
50 );
51static HRESULT ProcessPackage(
52 __in BOOL fBundlePerMachine,
53 __in BURN_USER_EXPERIENCE* pUX,
54 __in BURN_PLAN* pPlan,
55 __in BURN_PACKAGE* pPackage,
56 __in BURN_LOGGING* pLog,
57 __in BURN_VARIABLES* pVariables,
58 __in BOOTSTRAPPER_DISPLAY display,
59 __inout HANDLE* phSyncpointEvent,
60 __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
61 );
62static HRESULT ProcessPackageRollbackBoundary(
63 __in BURN_PLAN* pPlan,
64 __in_opt BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary,
65 __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
66 );
67static HRESULT GetActionDefaultRequestState(
68 __in BOOTSTRAPPER_ACTION action,
69 __in BOOL fPermanent,
70 __in BOOTSTRAPPER_PACKAGE_STATE currentState,
71 __out BOOTSTRAPPER_REQUEST_STATE* pRequestState
72 );
73static HRESULT AddRegistrationAction(
74 __in BURN_PLAN* pPlan,
75 __in BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type,
76 __in_z LPCWSTR wzDependentProviderKey,
77 __in_z LPCWSTR wzOwnerBundleId
78 );
79static HRESULT AddCachePackage(
80 __in BURN_PLAN* pPlan,
81 __in BURN_PACKAGE* pPackage,
82 __out HANDLE* phSyncpointEvent
83 );
84static HRESULT AddCachePackageHelper(
85 __in BURN_PLAN* pPlan,
86 __in BURN_PACKAGE* pPackage,
87 __out HANDLE* phSyncpointEvent
88 );
89static HRESULT AddCacheSlipstreamMsps(
90 __in BURN_PLAN* pPlan,
91 __in BURN_PACKAGE* pPackage
92 );
93static BOOL AlreadyPlannedCachePackage(
94 __in BURN_PLAN* pPlan,
95 __in_z LPCWSTR wzPackageId,
96 __out HANDLE* phSyncpointEvent
97 );
98static DWORD GetNextCheckpointId(
99 __in BURN_PLAN* pPlan
100 );
101static HRESULT AppendCacheAction(
102 __in BURN_PLAN* pPlan,
103 __out BURN_CACHE_ACTION** ppCacheAction
104 );
105static HRESULT AppendRollbackCacheAction(
106 __in BURN_PLAN* pPlan,
107 __out BURN_CACHE_ACTION** ppCacheAction
108 );
109static HRESULT ProcessPayloadGroup(
110 __in BURN_PLAN* pPlan,
111 __in BURN_PAYLOAD_GROUP* pPayloadGroup
112 );
113static void RemoveUnnecessaryActions(
114 __in BOOL fExecute,
115 __in BURN_EXECUTE_ACTION* rgActions,
116 __in DWORD cActions
117 );
118static void FinalizePatchActions(
119 __in BOOL fExecute,
120 __in BURN_EXECUTE_ACTION* rgActions,
121 __in DWORD cActions
122 );
123static void CalculateExpectedRegistrationStates(
124 __in BURN_PACKAGE* rgPackages,
125 __in DWORD cPackages
126 );
127static HRESULT PlanDependencyActions(
128 __in BOOL fBundlePerMachine,
129 __in BURN_PLAN* pPlan,
130 __in BURN_PACKAGE* pPackage
131 );
132static HRESULT CalculateExecuteActions(
133 __in BURN_PACKAGE* pPackage,
134 __in_opt BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary
135 );
136static BOOL NeedsCache(
137 __in BURN_PACKAGE* pPackage,
138 __in BOOL fExecute
139 );
140static BOOL ForceCache(
141 __in BURN_PLAN* pPlan,
142 __in BURN_PACKAGE* pPackage
143 );
144
145// function definitions
146
147extern "C" void PlanReset(
148 __in BURN_PLAN* pPlan,
149 __in BURN_CONTAINERS* pContainers,
150 __in BURN_PACKAGES* pPackages,
151 __in BURN_PAYLOAD_GROUP* pLayoutPayloads
152 )
153{
154 ReleaseNullStr(pPlan->sczLayoutDirectory);
155 PackageUninitialize(&pPlan->forwardCompatibleBundle);
156
157 if (pPlan->rgRegistrationActions)
158 {
159 for (DWORD i = 0; i < pPlan->cRegistrationActions; ++i)
160 {
161 UninitializeRegistrationAction(&pPlan->rgRegistrationActions[i]);
162 }
163 MemFree(pPlan->rgRegistrationActions);
164 }
165
166 if (pPlan->rgRollbackRegistrationActions)
167 {
168 for (DWORD i = 0; i < pPlan->cRollbackRegistrationActions; ++i)
169 {
170 UninitializeRegistrationAction(&pPlan->rgRollbackRegistrationActions[i]);
171 }
172 MemFree(pPlan->rgRollbackRegistrationActions);
173 }
174
175 if (pPlan->rgCacheActions)
176 {
177 for (DWORD i = 0; i < pPlan->cCacheActions; ++i)
178 {
179 UninitializeCacheAction(&pPlan->rgCacheActions[i]);
180 }
181 MemFree(pPlan->rgCacheActions);
182 }
183
184 if (pPlan->rgExecuteActions)
185 {
186 for (DWORD i = 0; i < pPlan->cExecuteActions; ++i)
187 {
188 PlanUninitializeExecuteAction(&pPlan->rgExecuteActions[i]);
189 }
190 MemFree(pPlan->rgExecuteActions);
191 }
192
193 if (pPlan->rgRollbackActions)
194 {
195 for (DWORD i = 0; i < pPlan->cRollbackActions; ++i)
196 {
197 PlanUninitializeExecuteAction(&pPlan->rgRollbackActions[i]);
198 }
199 MemFree(pPlan->rgRollbackActions);
200 }
201
202 if (pPlan->rgCleanActions)
203 {
204 // Nothing needs to be freed inside clean actions today.
205 MemFree(pPlan->rgCleanActions);
206 }
207
208 if (pPlan->rgPlannedProviders)
209 {
210 ReleaseDependencyArray(pPlan->rgPlannedProviders, pPlan->cPlannedProviders);
211 }
212
213 if (pPlan->rgContainerProgress)
214 {
215 MemFree(pPlan->rgContainerProgress);
216 }
217
218 if (pPlan->shContainerProgress)
219 {
220 ReleaseDict(pPlan->shContainerProgress);
221 }
222
223 if (pPlan->rgPayloadProgress)
224 {
225 MemFree(pPlan->rgPayloadProgress);
226 }
227
228 if (pPlan->shPayloadProgress)
229 {
230 ReleaseDict(pPlan->shPayloadProgress);
231 }
232
233 if (pPlan->pPayloads)
234 {
235 ResetPlannedPayloadsState(pPlan->pPayloads);
236 }
237
238 memset(pPlan, 0, sizeof(BURN_PLAN));
239
240 if (pContainers->rgContainers)
241 {
242 for (DWORD i = 0; i < pContainers->cContainers; ++i)
243 {
244 ResetPlannedContainerState(&pContainers->rgContainers[i]);
245 }
246 }
247
248 // Reset the planned actions for each package.
249 if (pPackages->rgPackages)
250 {
251 for (DWORD i = 0; i < pPackages->cPackages; ++i)
252 {
253 ResetPlannedPackageState(&pPackages->rgPackages[i]);
254 }
255 }
256
257 ResetPlannedPayloadGroupState(pLayoutPayloads);
258
259 // Reset the planned state for each rollback boundary.
260 if (pPackages->rgRollbackBoundaries)
261 {
262 for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i)
263 {
264 ResetPlannedRollbackBoundaryState(&pPackages->rgRollbackBoundaries[i]);
265 }
266 }
267}
268
269extern "C" void PlanUninitializeExecuteAction(
270 __in BURN_EXECUTE_ACTION* pExecuteAction
271 )
272{
273 switch (pExecuteAction->type)
274 {
275 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
276 ReleaseStr(pExecuteAction->exePackage.sczIgnoreDependencies);
277 ReleaseStr(pExecuteAction->exePackage.sczAncestors);
278 break;
279
280 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
281 ReleaseStr(pExecuteAction->msiPackage.sczLogPath);
282 ReleaseMem(pExecuteAction->msiPackage.rgFeatures);
283 break;
284
285 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
286 ReleaseStr(pExecuteAction->mspTarget.sczTargetProductCode);
287 ReleaseStr(pExecuteAction->mspTarget.sczLogPath);
288 ReleaseMem(pExecuteAction->mspTarget.rgOrderedPatches);
289 break;
290
291 case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
292 ReleaseStr(pExecuteAction->msuPackage.sczLogPath);
293 break;
294
295 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY:
296 ReleaseStr(pExecuteAction->packageDependency.sczBundleProviderKey);
297 break;
298 }
299}
300
301extern "C" HRESULT PlanSetVariables(
302 __in BOOTSTRAPPER_ACTION action,
303 __in BURN_VARIABLES* pVariables
304 )
305{
306 HRESULT hr = S_OK;
307
308 hr = VariableSetNumeric(pVariables, BURN_BUNDLE_ACTION, action, TRUE);
309 ExitOnFailure(hr, "Failed to set the bundle action built-in variable.");
310
311LExit:
312 return hr;
313}
314
315extern "C" HRESULT PlanDefaultPackageRequestState(
316 __in BURN_PACKAGE_TYPE packageType,
317 __in BOOTSTRAPPER_PACKAGE_STATE currentState,
318 __in BOOL fPermanent,
319 __in BOOTSTRAPPER_ACTION action,
320 __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition,
321 __in BOOTSTRAPPER_RELATION_TYPE relationType,
322 __out BOOTSTRAPPER_REQUEST_STATE* pRequestState
323 )
324{
325 HRESULT hr = S_OK;
326 BOOTSTRAPPER_REQUEST_STATE defaultRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
327
328 // If doing layout, then always default to requesting the package be cached.
329 if (BOOTSTRAPPER_ACTION_LAYOUT == action)
330 {
331 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE;
332 }
333 else if (BOOTSTRAPPER_RELATION_PATCH == relationType && BURN_PACKAGE_TYPE_MSP == packageType)
334 {
335 // For patch related bundles, only install a patch if currently absent during install, modify, or repair.
336 if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == currentState && BOOTSTRAPPER_ACTION_INSTALL <= action)
337 {
338 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
339 }
340 else
341 {
342 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
343 }
344 }
345 else if (BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED == currentState && BOOTSTRAPPER_ACTION_UNINSTALL != action)
346 {
347 // Superseded means the package is on the machine but not active, so only uninstall operations are allowed.
348 // All other operations do nothing.
349 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
350 }
351 else if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == currentState && !(BOOTSTRAPPER_ACTION_UNINSTALL == action && BURN_PACKAGE_TYPE_MSP == packageType))
352 {
353 // Obsolete means the package is not on the machine and should not be installed, *except* patches can be obsolete
354 // and present so allow them to be removed during uninstall. Everyone else, gets nothing.
355 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
356 }
357 else // pick the best option for the action state and install condition.
358 {
359 hr = GetActionDefaultRequestState(action, fPermanent, currentState, &defaultRequestState);
360 ExitOnFailure(hr, "Failed to get default request state for action.");
361
362 // If we're doing an install, use the install condition
363 // to determine whether to use the default request state or make the package absent.
364 if (BOOTSTRAPPER_ACTION_UNINSTALL != action && BOOTSTRAPPER_PACKAGE_CONDITION_FALSE == installCondition)
365 {
366 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT;
367 }
368 else // just set the package to the default request state.
369 {
370 *pRequestState = defaultRequestState;
371 }
372 }
373
374LExit:
375 return hr;
376}
377
378extern "C" HRESULT PlanLayoutBundle(
379 __in BURN_PLAN* pPlan,
380 __in_z LPCWSTR wzExecutableName,
381 __in DWORD64 qwBundleSize,
382 __in BURN_VARIABLES* pVariables,
383 __in BURN_PAYLOAD_GROUP* pLayoutPayloads
384 )
385{
386 HRESULT hr = S_OK;
387 BURN_CACHE_ACTION* pCacheAction = NULL;
388 LPWSTR sczExecutablePath = NULL;
389
390 // Get the layout directory.
391 hr = VariableGetString(pVariables, BURN_BUNDLE_LAYOUT_DIRECTORY, &pPlan->sczLayoutDirectory);
392 if (E_NOTFOUND == hr) // if not set, use the current directory as the layout directory.
393 {
394 hr = VariableGetString(pVariables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, &pPlan->sczLayoutDirectory);
395 if (E_NOTFOUND == hr) // if not set, use the current directory as the layout directory.
396 {
397 hr = PathForCurrentProcess(&sczExecutablePath, NULL);
398 ExitOnFailure(hr, "Failed to get path for current executing process as layout directory.");
399
400 hr = PathGetDirectory(sczExecutablePath, &pPlan->sczLayoutDirectory);
401 ExitOnFailure(hr, "Failed to get executing process as layout directory.");
402 }
403 }
404 ExitOnFailure(hr, "Failed to get bundle layout directory property.");
405
406 hr = PathBackslashTerminate(&pPlan->sczLayoutDirectory);
407 ExitOnFailure(hr, "Failed to ensure layout directory is backslash terminated.");
408
409 hr = ProcessPayloadGroup(pPlan, pLayoutPayloads);
410 ExitOnFailure(hr, "Failed to process payload group for bundle.");
411
412 // Plan the layout of the bundle engine itself.
413 hr = AppendCacheAction(pPlan, &pCacheAction);
414 ExitOnFailure(hr, "Failed to append bundle start action.");
415
416 pCacheAction->type = BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE;
417
418 hr = StrAllocString(&pCacheAction->bundleLayout.sczExecutableName, wzExecutableName, 0);
419 ExitOnFailure(hr, "Failed to to copy executable name for bundle.");
420
421 hr = CacheCalculateBundleLayoutWorkingPath(pPlan->wzBundleId, &pCacheAction->bundleLayout.sczUnverifiedPath);
422 ExitOnFailure(hr, "Failed to calculate bundle layout working path.");
423
424 pCacheAction->bundleLayout.qwBundleSize = qwBundleSize;
425 pCacheAction->bundleLayout.pPayloadGroup = pLayoutPayloads;
426
427 // Acquire + Verify + Finalize
428 pPlan->qwCacheSizeTotal += 3 * qwBundleSize;
429
430 ++pPlan->cOverallProgressTicksTotal;
431
432LExit:
433 ReleaseStr(sczExecutablePath);
434
435 return hr;
436}
437
438extern "C" HRESULT PlanForwardCompatibleBundles(
439 __in BURN_USER_EXPERIENCE* pUX,
440 __in BOOTSTRAPPER_COMMAND* pCommand,
441 __in BURN_PLAN* pPlan,
442 __in BURN_REGISTRATION* pRegistration,
443 __in BOOTSTRAPPER_ACTION action
444 )
445{
446 HRESULT hr = S_OK;
447 BOOL fRecommendIgnore = TRUE;
448 BOOL fIgnoreBundle = FALSE;
449
450 if (!pRegistration->fForwardCompatibleBundleExists)
451 {
452 ExitFunction();
453 }
454
455 // Only change the recommendation if an active parent was provided.
456 if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent)
457 {
458 // On install, recommend running the forward compatible bundle because there is an active parent. This
459 // will essentially register the parent with the forward compatible bundle.
460 if (BOOTSTRAPPER_ACTION_INSTALL == action)
461 {
462 fRecommendIgnore = FALSE;
463 }
464 else if (BOOTSTRAPPER_ACTION_UNINSTALL == action ||
465 BOOTSTRAPPER_ACTION_MODIFY == action ||
466 BOOTSTRAPPER_ACTION_REPAIR == action)
467 {
468 // When modifying the bundle, only recommend running the forward compatible bundle if the parent
469 // is already registered as a dependent of the provider key.
470 if (pRegistration->fParentRegisteredAsDependent)
471 {
472 fRecommendIgnore = FALSE;
473 }
474 }
475 }
476
477 for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle)
478 {
479 BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle;
480 if (!pRelatedBundle->fForwardCompatible)
481 {
482 continue;
483 }
484
485 fIgnoreBundle = fRecommendIgnore;
486
487 hr = UserExperienceOnPlanForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, &fIgnoreBundle);
488 ExitOnRootFailure(hr, "BA aborted plan forward compatible bundle.");
489
490 if (!fIgnoreBundle)
491 {
492 hr = PseudoBundleInitializePassthrough(&pPlan->forwardCompatibleBundle, pCommand, NULL, pRegistration->sczActiveParent, pRegistration->sczAncestors, &pRelatedBundle->package);
493 ExitOnFailure(hr, "Failed to initialize pass through bundle.");
494
495 pPlan->fEnabledForwardCompatibleBundle = TRUE;
496 break;
497 }
498 }
499
500LExit:
501 return hr;
502}
503
504extern "C" HRESULT PlanPackages(
505 __in BURN_USER_EXPERIENCE* pUX,
506 __in BURN_PACKAGES* pPackages,
507 __in BURN_PLAN* pPlan,
508 __in BURN_LOGGING* pLog,
509 __in BURN_VARIABLES* pVariables,
510 __in BOOTSTRAPPER_DISPLAY display,
511 __in BOOTSTRAPPER_RELATION_TYPE relationType
512 )
513{
514 HRESULT hr = S_OK;
515
516 hr = PlanPackagesHelper(pPackages->rgPackages, pPackages->cPackages, TRUE, pUX, pPlan, pLog, pVariables, display, relationType);
517
518 return hr;
519}
520
521extern "C" HRESULT PlanRegistration(
522 __in BURN_PLAN* pPlan,
523 __in BURN_REGISTRATION* pRegistration,
524 __in BOOTSTRAPPER_RESUME_TYPE /*resumeType*/,
525 __in BOOTSTRAPPER_RELATION_TYPE relationType,
526 __inout BOOL* pfContinuePlanning
527 )
528{
529 HRESULT hr = S_OK;
530 STRINGDICT_HANDLE sdBundleDependents = NULL;
531 STRINGDICT_HANDLE sdIgnoreDependents = NULL;
532
533 pPlan->fCanAffectMachineState = TRUE; // register the bundle since we're modifying machine state.
534 pPlan->fDisallowRemoval = FALSE; // by default the bundle can be planned to be removed
535 pPlan->fIgnoreAllDependents = pRegistration->fIgnoreAllDependents;
536
537 // Ensure the bundle is cached if not running from the cache.
538 if (!CacheBundleRunningFromCache())
539 {
540 pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE;
541 }
542
543 // Always write registration since things may have changed or it just needs to be "fixed up".
544 pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION;
545
546 // Always update our estimated size registration when installing/modify/repair since things
547 // may have been added or removed or it just needs to be "fixed up".
548 pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE;
549
550 if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
551 {
552 // If our provider key was detected and it points to our current bundle then we can
553 // unregister the bundle dependency.
554 if (pRegistration->sczDetectedProviderKeyBundleId &&
555 CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczId, -1, pRegistration->sczDetectedProviderKeyBundleId, -1))
556 {
557 pPlan->dependencyRegistrationAction = BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER;
558 }
559 else // log that another bundle already owned our registration, hopefully this only happens when a newer version
560 { // of a bundle installed and is in the process of upgrading us.
561 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_PROVIDER_KEY_REMOVAL, pRegistration->sczProviderKey, pRegistration->sczDetectedProviderKeyBundleId);
562 }
563
564 // Create the dictionary of dependents that should be ignored.
565 hr = DictCreateStringList(&sdIgnoreDependents, 5, DICT_FLAG_CASEINSENSITIVE);
566 ExitOnFailure(hr, "Failed to create the string dictionary.");
567
568 // If the self-dependent dependent exists, plan its removal. If we did not do this, we
569 // would prevent self-removal.
570 if (pRegistration->fSelfRegisteredAsDependent)
571 {
572 hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER, pRegistration->wzSelfDependent, pRegistration->sczId);
573 ExitOnFailure(hr, "Failed to allocate registration action.");
574
575 hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, pRegistration->wzSelfDependent);
576 ExitOnFailure(hr, "Failed to add self-dependent to ignore dependents.");
577 }
578
579 if (!pPlan->fIgnoreAllDependents)
580 {
581 // If we are not doing an upgrade, we check to see if there are still dependents on us and if so we skip planning.
582 // However, when being upgraded, we always execute our uninstall because a newer version of us is probably
583 // already on the machine and we need to clean up the stuff specific to this bundle.
584 if (BOOTSTRAPPER_RELATION_UPGRADE != relationType)
585 {
586 // If there were other dependencies to ignore, add them.
587 for (DWORD iDependency = 0; iDependency < pRegistration->cIgnoredDependencies; ++iDependency)
588 {
589 DEPENDENCY* pDependency = pRegistration->rgIgnoredDependencies + iDependency;
590
591 hr = DictKeyExists(sdIgnoreDependents, pDependency->sczKey);
592 if (E_NOTFOUND != hr)
593 {
594 ExitOnFailure(hr, "Failed to check the dictionary of ignored dependents.");
595 }
596 else
597 {
598 hr = DictAddKey(sdIgnoreDependents, pDependency->sczKey);
599 ExitOnFailure(hr, "Failed to add dependent key to ignored dependents.");
600 }
601 }
602
603 // For addon or patch bundles, dependent related bundles should be ignored. This allows
604 // that addon or patch to be removed even though bundles it targets still are registered.
605 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
606 {
607 const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
608
609 if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType)
610 {
611 for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j)
612 {
613 const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j;
614
615 hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, pProvider->sczKey);
616 ExitOnFailure(hr, "Failed to add dependent bundle provider key to ignore dependents.");
617 }
618 }
619 }
620
621 // If there are any (non-ignored and not-planned-to-be-removed) dependents left, skip planning.
622 for (DWORD iDependent = 0; iDependent < pRegistration->cDependents; ++iDependent)
623 {
624 DEPENDENCY* pDependent = pRegistration->rgDependents + iDependent;
625
626 hr = DictKeyExists(sdIgnoreDependents, pDependent->sczKey);
627 if (E_NOTFOUND == hr)
628 {
629 hr = S_OK;
630
631 // TODO: callback to the BA and let it have the option to ignore this dependent?
632 if (!pPlan->fDisallowRemoval)
633 {
634 pPlan->fDisallowRemoval = TRUE; // ensure the registration stays
635 *pfContinuePlanning = FALSE; // skip the rest of planning.
636
637 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS);
638 }
639
640 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_DEPENDENT, pDependent->sczKey, LoggingStringOrUnknownIfNull(pDependent->sczName));
641 }
642 ExitOnFailure(hr, "Failed to check for remaining dependents during planning.");
643 }
644 }
645 }
646 }
647 else
648 {
649 BOOL fAddonOrPatchBundle = (pRegistration->cAddonCodes || pRegistration->cPatchCodes);
650
651 // Always plan to write our provider key registration when installing/modify/repair to "fix it"
652 // if broken.
653 pPlan->dependencyRegistrationAction = BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER;
654
655 // Create the dictionary of bundle dependents.
656 hr = DictCreateStringList(&sdBundleDependents, 5, DICT_FLAG_CASEINSENSITIVE);
657 ExitOnFailure(hr, "Failed to create the string dictionary.");
658
659 for (DWORD iDependent = 0; iDependent < pRegistration->cDependents; ++iDependent)
660 {
661 DEPENDENCY* pDependent = pRegistration->rgDependents + iDependent;
662
663 hr = DictKeyExists(sdBundleDependents, pDependent->sczKey);
664 if (E_NOTFOUND == hr)
665 {
666 hr = DictAddKey(sdBundleDependents, pDependent->sczKey);
667 ExitOnFailure(hr, "Failed to add dependent key to bundle dependents.");
668 }
669 ExitOnFailure(hr, "Failed to check the dictionary of bundle dependents.");
670 }
671
672 // Register each dependent related bundle. The ensures that addons and patches are reference
673 // counted and stick around until the last targeted bundle is removed.
674 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
675 {
676 const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
677
678 if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType)
679 {
680 for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j)
681 {
682 const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j;
683
684 hr = DictKeyExists(sdBundleDependents, pProvider->sczKey);
685 if (E_NOTFOUND == hr)
686 {
687 hr = DictAddKey(sdBundleDependents, pProvider->sczKey);
688 ExitOnFailure(hr, "Failed to add new dependent key to bundle dependents.");
689
690 hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, pProvider->sczKey, pRelatedBundle->package.sczId);
691 ExitOnFailure(hr, "Failed to add registration action for dependent related bundle.");
692 }
693 ExitOnFailure(hr, "Failed to check the dictionary of bundle dependents.");
694 }
695 }
696 }
697
698 // Only do the following if we decided there was a dependent self to register. If so and and an explicit parent was
699 // provided, register dependent self. Otherwise, if this bundle is not an addon or patch bundle then self-regisiter
700 // as our own dependent.
701 if (pRegistration->wzSelfDependent && !pRegistration->fSelfRegisteredAsDependent && (pRegistration->sczActiveParent || !fAddonOrPatchBundle))
702 {
703 hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, pRegistration->wzSelfDependent, pRegistration->sczId);
704 ExitOnFailure(hr, "Failed to add registration action for self dependent.");
705 }
706 }
707
708LExit:
709 ReleaseDict(sdBundleDependents);
710 ReleaseDict(sdIgnoreDependents);
711
712 return hr;
713}
714
715extern "C" HRESULT PlanPassThroughBundle(
716 __in BURN_USER_EXPERIENCE* pUX,
717 __in BURN_PACKAGE* pPackage,
718 __in BURN_PLAN* pPlan,
719 __in BURN_LOGGING* pLog,
720 __in BURN_VARIABLES* pVariables,
721 __in BOOTSTRAPPER_DISPLAY display,
722 __in BOOTSTRAPPER_RELATION_TYPE relationType
723 )
724{
725 HRESULT hr = S_OK;
726
727 // Plan passthrough package.
728 // Passthrough packages are never cleaned up by the calling bundle (they delete themselves when appropriate)
729 // so we don't need to plan clean up.
730 hr = PlanPackagesHelper(pPackage, 1, FALSE, pUX, pPlan, pLog, pVariables, display, relationType);
731 ExitOnFailure(hr, "Failed to process passthrough package.");
732
733LExit:
734 return hr;
735}
736
737extern "C" HRESULT PlanUpdateBundle(
738 __in BURN_USER_EXPERIENCE* pUX,
739 __in BURN_PACKAGE* pPackage,
740 __in BURN_PLAN* pPlan,
741 __in BURN_LOGGING* pLog,
742 __in BURN_VARIABLES* pVariables,
743 __in BOOTSTRAPPER_DISPLAY display,
744 __in BOOTSTRAPPER_RELATION_TYPE relationType
745 )
746{
747 HRESULT hr = S_OK;
748
749 // Plan update package.
750 hr = PlanPackagesHelper(pPackage, 1, TRUE, pUX, pPlan, pLog, pVariables, display, relationType);
751 ExitOnFailure(hr, "Failed to process update package.");
752
753LExit:
754 return hr;
755}
756
757static HRESULT PlanPackagesHelper(
758 __in BURN_PACKAGE* rgPackages,
759 __in DWORD cPackages,
760 __in BOOL fPlanCleanPackages,
761 __in BURN_USER_EXPERIENCE* pUX,
762 __in BURN_PLAN* pPlan,
763 __in BURN_LOGGING* pLog,
764 __in BURN_VARIABLES* pVariables,
765 __in BOOTSTRAPPER_DISPLAY display,
766 __in BOOTSTRAPPER_RELATION_TYPE relationType
767 )
768{
769 HRESULT hr = S_OK;
770 BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine.
771 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL;
772 HANDLE hSyncpointEvent = NULL;
773
774 // Initialize the packages.
775 for (DWORD i = 0; i < cPackages; ++i)
776 {
777 DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i;
778 BURN_PACKAGE* pPackage = rgPackages + iPackage;
779
780 hr = InitializePackage(pPlan, pUX, pVariables, pPackage, relationType);
781 ExitOnFailure(hr, "Failed to initialize package.");
782 }
783
784 // Initialize the patch targets after all packages, since they could rely on the requested state of packages that are after the patch's package in the chain.
785 for (DWORD i = 0; i < cPackages; ++i)
786 {
787 DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i;
788 BURN_PACKAGE* pPackage = rgPackages + iPackage;
789
790 if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
791 {
792 hr = MspEnginePlanInitializePackage(pPackage, pUX);
793 ExitOnFailure(hr, "Failed to initialize plan package: %ls", pPackage->sczId);
794 }
795 }
796
797 // Plan the packages.
798 for (DWORD i = 0; i < cPackages; ++i)
799 {
800 DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i;
801 BURN_PACKAGE* pPackage = rgPackages + iPackage;
802
803 hr = ProcessPackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables, display, &hSyncpointEvent, &pRollbackBoundary);
804 ExitOnFailure(hr, "Failed to process package.");
805 }
806
807 // If we still have an open rollback boundary, complete it.
808 if (pRollbackBoundary)
809 {
810 hr = PlanRollbackBoundaryComplete(pPlan);
811 ExitOnFailure(hr, "Failed to plan final rollback boundary complete.");
812
813 pRollbackBoundary = NULL;
814 }
815
816 if (fPlanCleanPackages)
817 {
818 // Plan clean up of packages.
819 for (DWORD i = 0; i < cPackages; ++i)
820 {
821 DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i;
822 BURN_PACKAGE* pPackage = rgPackages + iPackage;
823
824 hr = PlanCleanPackage(pPlan, pPackage);
825 ExitOnFailure(hr, "Failed to plan clean package.");
826 }
827 }
828
829 // Remove unnecessary actions.
830 hr = PlanFinalizeActions(pPlan);
831 ExitOnFailure(hr, "Failed to remove unnecessary actions from plan.");
832
833 CalculateExpectedRegistrationStates(rgPackages, cPackages);
834
835 // Let the BA know the actions that were planned.
836 for (DWORD i = 0; i < cPackages; ++i)
837 {
838 DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? cPackages - 1 - i : i;
839 BURN_PACKAGE* pPackage = rgPackages + iPackage;
840
841 UserExperienceOnPlannedPackage(pUX, pPackage->sczId, pPackage->execute, pPackage->rollback, pPackage->fPlannedCache, pPackage->fPlannedUncache);
842 }
843
844LExit:
845 return hr;
846}
847
848static HRESULT InitializePackage(
849 __in BURN_PLAN* pPlan,
850 __in BURN_USER_EXPERIENCE* pUX,
851 __in BURN_VARIABLES* pVariables,
852 __in BURN_PACKAGE* pPackage,
853 __in BOOTSTRAPPER_RELATION_TYPE relationType
854 )
855{
856 HRESULT hr = S_OK;
857 BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition = BOOTSTRAPPER_PACKAGE_CONDITION_DEFAULT;
858 BOOL fInstallCondition = FALSE;
859 BOOL fBeginCalled = FALSE;
860
861 if (pPackage->fCanAffectRegistration)
862 {
863 pPackage->expectedCacheRegistrationState = pPackage->cacheRegistrationState;
864 pPackage->expectedInstallRegistrationState = pPackage->installRegistrationState;
865 }
866
867 if (pPackage->sczInstallCondition && *pPackage->sczInstallCondition)
868 {
869 hr = ConditionEvaluate(pVariables, pPackage->sczInstallCondition, &fInstallCondition);
870 ExitOnFailure(hr, "Failed to evaluate install condition.");
871
872 installCondition = fInstallCondition ? BOOTSTRAPPER_PACKAGE_CONDITION_TRUE : BOOTSTRAPPER_PACKAGE_CONDITION_FALSE;
873 }
874
875 // Remember the default requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it.
876 hr = PlanDefaultPackageRequestState(pPackage->type, pPackage->currentState, !pPackage->fUninstallable, pPlan->action, installCondition, relationType, &pPackage->defaultRequested);
877 ExitOnFailure(hr, "Failed to set default package state.");
878
879 pPackage->requested = pPackage->defaultRequested;
880 fBeginCalled = TRUE;
881
882 hr = UserExperienceOnPlanPackageBegin(pUX, pPackage->sczId, pPackage->currentState, pPackage->fCached, installCondition, &pPackage->requested, &pPackage->cacheType);
883 ExitOnRootFailure(hr, "BA aborted plan package begin.");
884
885 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
886 {
887 hr = MsiEnginePlanInitializePackage(pPackage, pVariables, pUX);
888 ExitOnFailure(hr, "Failed to initialize plan package: %ls", pPackage->sczId);
889 }
890
891LExit:
892 if (fBeginCalled)
893 {
894 UserExperienceOnPlanPackageComplete(pUX, pPackage->sczId, hr, pPackage->requested);
895 }
896
897 return hr;
898}
899
900static HRESULT ProcessPackage(
901 __in BOOL fBundlePerMachine,
902 __in BURN_USER_EXPERIENCE* pUX,
903 __in BURN_PLAN* pPlan,
904 __in BURN_PACKAGE* pPackage,
905 __in BURN_LOGGING* pLog,
906 __in BURN_VARIABLES* pVariables,
907 __in BOOTSTRAPPER_DISPLAY display,
908 __inout HANDLE* phSyncpointEvent,
909 __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
910 )
911{
912 HRESULT hr = S_OK;
913 BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary = NULL;
914
915 pEffectiveRollbackBoundary = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackage->pRollbackBoundaryBackward : pPackage->pRollbackBoundaryForward;
916 hr = ProcessPackageRollbackBoundary(pPlan, pEffectiveRollbackBoundary, ppRollbackBoundary);
917 ExitOnFailure(hr, "Failed to process package rollback boundary.");
918
919 if (BOOTSTRAPPER_ACTION_LAYOUT == pPlan->action)
920 {
921 if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested)
922 {
923 hr = PlanLayoutPackage(pPlan, pPackage);
924 ExitOnFailure(hr, "Failed to plan layout package.");
925 }
926 }
927 else
928 {
929 if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested)
930 {
931 // If the package is in a requested state, plan it.
932 hr = PlanExecutePackage(fBundlePerMachine, display, pUX, pPlan, pPackage, pLog, pVariables, phSyncpointEvent);
933 ExitOnFailure(hr, "Failed to plan execute package.");
934 }
935 else
936 {
937 if (ForceCache(pPlan, pPackage))
938 {
939 hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent);
940 ExitOnFailure(hr, "Failed to plan cache package.");
941
942 if (pPackage->fPerMachine)
943 {
944 pPlan->fPerMachine = TRUE;
945 }
946 }
947
948 // Make sure the package is properly ref-counted even if no plan is requested.
949 hr = PlanDependencyActions(fBundlePerMachine, pPlan, pPackage);
950 ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId);
951 }
952 }
953
954 // Add the checkpoint after each package and dependency registration action.
955 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute || BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback || BURN_DEPENDENCY_ACTION_NONE != pPackage->dependencyExecute)
956 {
957 hr = PlanExecuteCheckpoint(pPlan);
958 ExitOnFailure(hr, "Failed to append execute checkpoint.");
959 }
960
961LExit:
962 return hr;
963}
964
965static HRESULT ProcessPackageRollbackBoundary(
966 __in BURN_PLAN* pPlan,
967 __in_opt BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary,
968 __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
969 )
970{
971 HRESULT hr = S_OK;
972
973 // If the package marks the start of a rollback boundary, start a new one.
974 if (pEffectiveRollbackBoundary)
975 {
976 // Complete previous rollback boundary.
977 if (*ppRollbackBoundary)
978 {
979 hr = PlanRollbackBoundaryComplete(pPlan);
980 ExitOnFailure(hr, "Failed to plan rollback boundary complete.");
981 }
982
983 // Start new rollback boundary.
984 hr = PlanRollbackBoundaryBegin(pPlan, pEffectiveRollbackBoundary);
985 ExitOnFailure(hr, "Failed to plan rollback boundary begin.");
986
987 *ppRollbackBoundary = pEffectiveRollbackBoundary;
988 }
989
990LExit:
991 return hr;
992}
993
994extern "C" HRESULT PlanLayoutContainer(
995 __in BURN_PLAN* pPlan,
996 __in BURN_CONTAINER* pContainer
997 )
998{
999 HRESULT hr = S_OK;
1000 BURN_CACHE_ACTION* pCacheAction = NULL;
1001
1002 Assert(!pContainer->fPlanned);
1003 pContainer->fPlanned = TRUE;
1004
1005 if (pPlan->sczLayoutDirectory)
1006 {
1007 if (!pContainer->fAttached)
1008 {
1009 hr = AppendCacheAction(pPlan, &pCacheAction);
1010 ExitOnFailure(hr, "Failed to append package start action.");
1011
1012 pCacheAction->type = BURN_CACHE_ACTION_TYPE_CONTAINER;
1013 pCacheAction->container.pContainer = pContainer;
1014
1015 // Acquire + Verify + Finalize
1016 pPlan->qwCacheSizeTotal += 3 * pContainer->qwFileSize;
1017 }
1018 }
1019 else
1020 {
1021 if (!pContainer->fActuallyAttached)
1022 {
1023 // Acquire
1024 pPlan->qwCacheSizeTotal += pContainer->qwFileSize;
1025 }
1026 }
1027
1028 if (!pContainer->sczUnverifiedPath)
1029 {
1030 if (pContainer->fActuallyAttached)
1031 {
1032 hr = PathForCurrentProcess(&pContainer->sczUnverifiedPath, NULL);
1033 ExitOnFailure(hr, "Failed to get path for executing module as attached container working path.");
1034 }
1035 else
1036 {
1037 hr = CacheCalculateContainerWorkingPath(pPlan->wzBundleId, pContainer, &pContainer->sczUnverifiedPath);
1038 ExitOnFailure(hr, "Failed to calculate unverified path for container.");
1039 }
1040 }
1041
1042LExit:
1043 return hr;
1044}
1045
1046extern "C" HRESULT PlanLayoutPackage(
1047 __in BURN_PLAN* pPlan,
1048 __in BURN_PACKAGE* pPackage
1049 )
1050{
1051 HRESULT hr = S_OK;
1052 BURN_CACHE_ACTION* pCacheAction = NULL;
1053
1054 hr = ProcessPayloadGroup(pPlan, &pPackage->payloads);
1055 ExitOnFailure(hr, "Failed to process payload group for package: %ls.", pPackage->sczId);
1056
1057 hr = AppendCacheAction(pPlan, &pCacheAction);
1058 ExitOnFailure(hr, "Failed to append package start action.");
1059
1060 pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE;
1061 pCacheAction->package.pPackage = pPackage;
1062
1063 ++pPlan->cOverallProgressTicksTotal;
1064
1065LExit:
1066 return hr;
1067}
1068
1069extern "C" HRESULT PlanExecutePackage(
1070 __in BOOL fPerMachine,
1071 __in BOOTSTRAPPER_DISPLAY display,
1072 __in BURN_USER_EXPERIENCE* pUserExperience,
1073 __in BURN_PLAN* pPlan,
1074 __in BURN_PACKAGE* pPackage,
1075 __in BURN_LOGGING* pLog,
1076 __in BURN_VARIABLES* pVariables,
1077 __inout HANDLE* phSyncpointEvent
1078 )
1079{
1080 HRESULT hr = S_OK;
1081 BOOL fRequestedCache = BOOTSTRAPPER_REQUEST_STATE_CACHE == pPackage->requested || ForceCache(pPlan, pPackage);
1082
1083 hr = CalculateExecuteActions(pPackage, pPlan->pActiveRollbackBoundary);
1084 ExitOnFailure(hr, "Failed to calculate plan actions for package: %ls", pPackage->sczId);
1085
1086 // Calculate package states based on reference count and plan certain dependency actions prior to planning the package execute action.
1087 hr = DependencyPlanPackageBegin(fPerMachine, pPackage, pPlan);
1088 ExitOnFailure(hr, "Failed to begin plan dependency actions for package: %ls", pPackage->sczId);
1089
1090 if (fRequestedCache || NeedsCache(pPackage, TRUE))
1091 {
1092 hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent);
1093 ExitOnFailure(hr, "Failed to plan cache package.");
1094 }
1095 else if (!pPackage->fCached && NeedsCache(pPackage, FALSE))
1096 {
1097 // TODO: this decision should be made during apply instead of plan based on whether the package is actually cached.
1098 // If the package is not in the cache, disable any rollback that would require the package from the cache.
1099 LogId(REPORT_STANDARD, MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE, pPackage->sczId, LoggingActionStateToString(pPackage->rollback));
1100 pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
1101 }
1102
1103 // Add the cache and install size to estimated size if it will be on the machine at the end of the install
1104 if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested ||
1105 fRequestedCache ||
1106 (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_ABSENT < pPackage->requested)
1107 )
1108 {
1109 // If the package will remain in the cache, add the package size to the estimated size
1110 if (BOOTSTRAPPER_CACHE_TYPE_REMOVE < pPackage->cacheType)
1111 {
1112 pPlan->qwEstimatedSize += pPackage->qwSize;
1113 }
1114
1115 // If the package will end up installed on the machine, add the install size to the estimated size.
1116 if (BOOTSTRAPPER_REQUEST_STATE_CACHE < pPackage->requested)
1117 {
1118 // MSP packages get cached automatically by windows installer with any embedded cabs, so include that in the size as well
1119 if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
1120 {
1121 pPlan->qwEstimatedSize += pPackage->qwSize;
1122 }
1123
1124 pPlan->qwEstimatedSize += pPackage->qwInstallSize;
1125 }
1126 }
1127
1128 // Add execute actions.
1129 switch (pPackage->type)
1130 {
1131 case BURN_PACKAGE_TYPE_EXE:
1132 hr = ExeEnginePlanAddPackage(NULL, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent);
1133 break;
1134
1135 case BURN_PACKAGE_TYPE_MSI:
1136 hr = MsiEnginePlanAddPackage(display, pUserExperience, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent);
1137 break;
1138
1139 case BURN_PACKAGE_TYPE_MSP:
1140 hr = MspEnginePlanAddPackage(display, pUserExperience, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent);
1141 break;
1142
1143 case BURN_PACKAGE_TYPE_MSU:
1144 hr = MsuEnginePlanAddPackage(pPackage, pPlan, pLog, pVariables, *phSyncpointEvent);
1145 break;
1146
1147 default:
1148 hr = E_UNEXPECTED;
1149 ExitOnFailure(hr, "Invalid package type.");
1150 }
1151 ExitOnFailure(hr, "Failed to add plan actions for package: %ls", pPackage->sczId);
1152
1153 // Plan certain dependency actions after planning the package execute action.
1154 hr = DependencyPlanPackageComplete(pPackage, pPlan);
1155 ExitOnFailure(hr, "Failed to complete plan dependency actions for package: %ls", pPackage->sczId);
1156
1157 // If we are going to take any action on this package, add progress for it.
1158 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute || BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
1159 {
1160 LoggingIncrementPackageSequence();
1161
1162 ++pPlan->cExecutePackagesTotal;
1163 ++pPlan->cOverallProgressTicksTotal;
1164
1165 // If package is per-machine and is being executed, flag the plan to be per-machine as well.
1166 if (pPackage->fPerMachine)
1167 {
1168 pPlan->fPerMachine = TRUE;
1169 }
1170 }
1171
1172LExit:
1173 return hr;
1174}
1175
1176extern "C" HRESULT PlanDefaultRelatedBundleRequestState(
1177 __in BOOTSTRAPPER_RELATION_TYPE commandRelationType,
1178 __in BOOTSTRAPPER_RELATION_TYPE relatedBundleRelationType,
1179 __in BOOTSTRAPPER_ACTION action,
1180 __in VERUTIL_VERSION* pRegistrationVersion,
1181 __in VERUTIL_VERSION* pRelatedBundleVersion,
1182 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestState
1183 )
1184{
1185 HRESULT hr = S_OK;
1186 int nCompareResult = 0;
1187
1188 // Never touch related bundles during Cache.
1189 if (BOOTSTRAPPER_ACTION_CACHE == action)
1190 {
1191 ExitFunction1(*pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE);
1192 }
1193
1194 switch (relatedBundleRelationType)
1195 {
1196 case BOOTSTRAPPER_RELATION_UPGRADE:
1197 if (BOOTSTRAPPER_RELATION_UPGRADE != commandRelationType && BOOTSTRAPPER_ACTION_UNINSTALL < action)
1198 {
1199 hr = VerCompareParsedVersions(pRegistrationVersion, pRelatedBundleVersion, &nCompareResult);
1200 ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistrationVersion ? pRegistrationVersion->sczVersion : NULL, pRelatedBundleVersion ? pRelatedBundleVersion->sczVersion : NULL);
1201
1202 *pRequestState = (nCompareResult < 0) ? BOOTSTRAPPER_REQUEST_STATE_NONE : BOOTSTRAPPER_REQUEST_STATE_ABSENT;
1203 }
1204 break;
1205 case BOOTSTRAPPER_RELATION_PATCH: __fallthrough;
1206 case BOOTSTRAPPER_RELATION_ADDON:
1207 if (BOOTSTRAPPER_ACTION_UNINSTALL == action)
1208 {
1209 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT;
1210 }
1211 else if (BOOTSTRAPPER_ACTION_INSTALL == action || BOOTSTRAPPER_ACTION_MODIFY == action)
1212 {
1213 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
1214 }
1215 else if (BOOTSTRAPPER_ACTION_REPAIR == action)
1216 {
1217 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_REPAIR;
1218 }
1219 break;
1220 case BOOTSTRAPPER_RELATION_DEPENDENT:
1221 // Automatically repair dependent bundles to restore missing
1222 // packages after uninstall unless we're being upgraded with the
1223 // assumption that upgrades are cumulative (as intended).
1224 if (BOOTSTRAPPER_RELATION_UPGRADE != commandRelationType && BOOTSTRAPPER_ACTION_UNINSTALL == action)
1225 {
1226 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_REPAIR;
1227 }
1228 break;
1229 case BOOTSTRAPPER_RELATION_DETECT:
1230 break;
1231 default:
1232 hr = E_UNEXPECTED;
1233 ExitOnFailure(hr, "Unexpected relation type encountered during plan: %d", relatedBundleRelationType);
1234 break;
1235 }
1236
1237LExit:
1238 return hr;
1239}
1240
1241extern "C" HRESULT PlanRelatedBundlesBegin(
1242 __in BURN_USER_EXPERIENCE* pUserExperience,
1243 __in BURN_REGISTRATION* pRegistration,
1244 __in BOOTSTRAPPER_RELATION_TYPE relationType,
1245 __in BURN_PLAN* pPlan
1246 )
1247{
1248 HRESULT hr = S_OK;
1249 LPWSTR* rgsczAncestors = NULL;
1250 UINT cAncestors = 0;
1251 STRINGDICT_HANDLE sdAncestors = NULL;
1252
1253 if (pRegistration->sczAncestors)
1254 {
1255 hr = StrSplitAllocArray(&rgsczAncestors, &cAncestors, pRegistration->sczAncestors, L";");
1256 ExitOnFailure(hr, "Failed to create string array from ancestors.");
1257
1258 hr = DictCreateStringListFromArray(&sdAncestors, rgsczAncestors, cAncestors, DICT_FLAG_CASEINSENSITIVE);
1259 ExitOnFailure(hr, "Failed to create dictionary from ancestors array.");
1260 }
1261
1262 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
1263 {
1264 BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
1265
1266 if (!pRelatedBundle->fPlannable)
1267 {
1268 continue;
1269 }
1270
1271 pRelatedBundle->package.defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1272 pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1273
1274 // Do not execute the same bundle twice.
1275 if (sdAncestors)
1276 {
1277 hr = DictKeyExists(sdAncestors, pRelatedBundle->package.sczId);
1278 if (SUCCEEDED(hr))
1279 {
1280 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_SCHEDULED, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType));
1281 continue;
1282 }
1283 else if (E_NOTFOUND != hr)
1284 {
1285 ExitOnFailure(hr, "Failed to lookup the bundle ID in the ancestors dictionary.");
1286 }
1287 }
1288 else if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_RELATION_NONE != relationType)
1289 {
1290 // Avoid repair loops for older bundles that do not handle ancestors.
1291 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_DEPENDENT, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRelationTypeToString(relationType));
1292 continue;
1293 }
1294
1295 // Pass along any ancestors and ourself to prevent infinite loops.
1296 pRelatedBundle->package.Exe.wzAncestors = pRegistration->sczBundlePackageAncestors;
1297
1298 hr = PlanDefaultRelatedBundleRequestState(relationType, pRelatedBundle->relationType, pPlan->action, pRegistration->pVersion, pRelatedBundle->pVersion, &pRelatedBundle->package.requested);
1299 ExitOnFailure(hr, "Failed to get default request state for related bundle.");
1300
1301 pRelatedBundle->package.defaultRequested = pRelatedBundle->package.requested;
1302
1303 hr = UserExperienceOnPlanRelatedBundle(pUserExperience, pRelatedBundle->package.sczId, &pRelatedBundle->package.requested);
1304 ExitOnRootFailure(hr, "BA aborted plan related bundle.");
1305
1306 // Log when the BA changed the bundle state so the engine doesn't get blamed for planning the wrong thing.
1307 if (pRelatedBundle->package.requested != pRelatedBundle->package.defaultRequested)
1308 {
1309 LogId(REPORT_STANDARD, MSG_PLANNED_BUNDLE_UX_CHANGED_REQUEST, pRelatedBundle->package.sczId, LoggingRequestStateToString(pRelatedBundle->package.requested), LoggingRequestStateToString(pRelatedBundle->package.defaultRequested));
1310 }
1311
1312 // If uninstalling and the dependent related bundle may be executed, ignore its provider key to allow for downgrades with ref-counting.
1313 if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested)
1314 {
1315 if (0 < pRelatedBundle->package.cDependencyProviders)
1316 {
1317 // Bundles only support a single provider key.
1318 const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders;
1319
1320 hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, pProvider->sczDisplayName);
1321 ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey);
1322 }
1323 }
1324 }
1325
1326LExit:
1327 ReleaseDict(sdAncestors);
1328 ReleaseStrArray(rgsczAncestors, cAncestors);
1329
1330 return hr;
1331}
1332
1333extern "C" HRESULT PlanRelatedBundlesComplete(
1334 __in BURN_REGISTRATION* pRegistration,
1335 __in BURN_PLAN* pPlan,
1336 __in BURN_LOGGING* pLog,
1337 __in BURN_VARIABLES* pVariables,
1338 __in DWORD dwExecuteActionEarlyIndex
1339 )
1340{
1341 HRESULT hr = S_OK;
1342 LPWSTR sczIgnoreDependencies = NULL;
1343 STRINGDICT_HANDLE sdProviderKeys = NULL;
1344
1345 // Get the list of dependencies to ignore to pass to related bundles.
1346 hr = DependencyAllocIgnoreDependencies(pPlan, &sczIgnoreDependencies);
1347 ExitOnFailure(hr, "Failed to get the list of dependencies to ignore.");
1348
1349 hr = DictCreateStringList(&sdProviderKeys, pPlan->cExecuteActions, DICT_FLAG_CASEINSENSITIVE);
1350 ExitOnFailure(hr, "Failed to create dictionary for planned packages.");
1351
1352 BOOL fExecutingAnyPackage = FALSE;
1353
1354 for (DWORD i = 0; i < pPlan->cExecuteActions; ++i)
1355 {
1356 if (BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE == pPlan->rgExecuteActions[i].type && BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].exePackage.action)
1357 {
1358 fExecutingAnyPackage = TRUE;
1359
1360 BURN_PACKAGE* pPackage = pPlan->rgExecuteActions[i].packageProvider.pPackage;
1361 if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol)
1362 {
1363 if (0 < pPackage->cDependencyProviders)
1364 {
1365 // Bundles only support a single provider key.
1366 const BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders;
1367 DictAddKey(sdProviderKeys, pProvider->sczKey);
1368 }
1369 }
1370 }
1371 else
1372 {
1373 switch (pPlan->rgExecuteActions[i].type)
1374 {
1375 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
1376 fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].msiPackage.action);
1377 break;
1378
1379 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
1380 fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].mspTarget.action);
1381 break;
1382
1383 case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
1384 fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].msuPackage.action);
1385 break;
1386 }
1387 }
1388 }
1389
1390 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
1391 {
1392 DWORD *pdwInsertIndex = NULL;
1393 BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
1394
1395 if (!pRelatedBundle->fPlannable)
1396 {
1397 continue;
1398 }
1399
1400 // Do not execute if a major upgrade to the related bundle is an embedded bundle (Provider keys are the same)
1401 if (0 < pRelatedBundle->package.cDependencyProviders)
1402 {
1403 // Bundles only support a single provider key.
1404 const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders;
1405 hr = DictKeyExists(sdProviderKeys, pProvider->sczKey);
1406 if (E_NOTFOUND != hr)
1407 {
1408 ExitOnFailure(hr, "Failed to check the dictionary for a related bundle provider key: \"%ls\".", pProvider->sczKey);
1409 // Key found, so there is an embedded bundle with the same provider key that will be executed. So this related bundle should not be added to the plan
1410 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), pProvider->sczKey);
1411 continue;
1412 }
1413 else
1414 {
1415 hr = S_OK;
1416 }
1417 }
1418
1419 // For an uninstall, there is no need to repair dependent bundles if no packages are executing.
1420 if (!fExecutingAnyPackage && BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pRelatedBundle->package.requested && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
1421 {
1422 pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1423 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DEPENDENT_BUNDLE_REPAIR, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType));
1424 }
1425
1426 if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType)
1427 {
1428 // Addon and patch bundles will be passed a list of dependencies to ignore for planning.
1429 hr = StrAllocString(&pRelatedBundle->package.Exe.sczIgnoreDependencies, sczIgnoreDependencies, 0);
1430 ExitOnFailure(hr, "Failed to copy the list of dependencies to ignore.");
1431
1432 // Uninstall addons and patches early in the chain, before other packages are uninstalled.
1433 if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
1434 {
1435 pdwInsertIndex = &dwExecuteActionEarlyIndex;
1436 }
1437 }
1438
1439 if (BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested)
1440 {
1441 hr = ExeEnginePlanCalculatePackage(&pRelatedBundle->package);
1442 ExitOnFailure(hr, "Failed to calcuate plan for related bundle: %ls", pRelatedBundle->package.sczId);
1443
1444 // Calculate package states based on reference count for addon and patch related bundles.
1445 if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType)
1446 {
1447 hr = DependencyPlanPackageBegin(pRegistration->fPerMachine, &pRelatedBundle->package, pPlan);
1448 ExitOnFailure(hr, "Failed to begin plan dependency actions to package: %ls", pRelatedBundle->package.sczId);
1449
1450 // If uninstalling a related bundle, make sure the bundle is uninstalled after removing registration.
1451 if (pdwInsertIndex && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
1452 {
1453 ++(*pdwInsertIndex);
1454 }
1455 }
1456
1457 hr = ExeEnginePlanAddPackage(pdwInsertIndex, &pRelatedBundle->package, pPlan, pLog, pVariables, NULL);
1458 ExitOnFailure(hr, "Failed to add to plan related bundle: %ls", pRelatedBundle->package.sczId);
1459
1460 // Calculate package states based on reference count for addon and patch related bundles.
1461 if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType)
1462 {
1463 hr = DependencyPlanPackageComplete(&pRelatedBundle->package, pPlan);
1464 ExitOnFailure(hr, "Failed to complete plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId);
1465 }
1466
1467 // If we are going to take any action on this package, add progress for it.
1468 if (BOOTSTRAPPER_ACTION_STATE_NONE != pRelatedBundle->package.execute || BOOTSTRAPPER_ACTION_STATE_NONE != pRelatedBundle->package.rollback)
1469 {
1470 LoggingIncrementPackageSequence();
1471
1472 ++pPlan->cExecutePackagesTotal;
1473 ++pPlan->cOverallProgressTicksTotal;
1474 }
1475
1476 // If package is per-machine and is being executed, flag the plan to be per-machine as well.
1477 if (pRelatedBundle->package.fPerMachine)
1478 {
1479 pPlan->fPerMachine = TRUE;
1480 }
1481 }
1482 else if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType)
1483 {
1484 // Make sure the package is properly ref-counted even if no plan is requested.
1485 hr = DependencyPlanPackageBegin(pRegistration->fPerMachine, &pRelatedBundle->package, pPlan);
1486 ExitOnFailure(hr, "Failed to begin plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId);
1487
1488 hr = DependencyPlanPackage(pdwInsertIndex, &pRelatedBundle->package, pPlan);
1489 ExitOnFailure(hr, "Failed to plan related bundle package provider actions.");
1490
1491 hr = DependencyPlanPackageComplete(&pRelatedBundle->package, pPlan);
1492 ExitOnFailure(hr, "Failed to complete plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId);
1493 }
1494 }
1495
1496LExit:
1497 ReleaseDict(sdProviderKeys);
1498 ReleaseStr(sczIgnoreDependencies);
1499
1500 return hr;
1501}
1502
1503extern "C" HRESULT PlanFinalizeActions(
1504 __in BURN_PLAN* pPlan
1505 )
1506{
1507 HRESULT hr = S_OK;
1508
1509 FinalizePatchActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions);
1510
1511 FinalizePatchActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions);
1512
1513 RemoveUnnecessaryActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions);
1514
1515 RemoveUnnecessaryActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions);
1516
1517 return hr;
1518}
1519
1520extern "C" HRESULT PlanCleanPackage(
1521 __in BURN_PLAN* pPlan,
1522 __in BURN_PACKAGE* pPackage
1523 )
1524{
1525 HRESULT hr = S_OK;
1526 BOOL fPlanCleanPackage = FALSE;
1527 BURN_CLEAN_ACTION* pCleanAction = NULL;
1528
1529 // The following is a complex set of logic that determines when a package should be cleaned from the cache.
1530 if (BOOTSTRAPPER_CACHE_TYPE_FORCE > pPackage->cacheType || BOOTSTRAPPER_ACTION_CACHE > pPlan->action)
1531 {
1532 // The following are all different reasons why the package should be cleaned from the cache.
1533 // The else-ifs are used to make the conditions easier to see (rather than have them combined
1534 // in one huge condition).
1535 if (BOOTSTRAPPER_CACHE_TYPE_KEEP > pPackage->cacheType) // easy, package is not supposed to stay cached.
1536 {
1537 fPlanCleanPackage = TRUE;
1538 }
1539 else if ((BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested ||
1540 BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested) && // requested to be removed and
1541 BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) // actually being removed.
1542 {
1543 fPlanCleanPackage = TRUE;
1544 }
1545 else if ((BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested ||
1546 BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested) && // requested to be removed but
1547 BOOTSTRAPPER_ACTION_STATE_NONE == pPackage->execute && // execute is do nothing and
1548 !pPackage->fDependencyManagerWasHere && // dependency manager didn't change execute and
1549 BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState) // currently not installed.
1550 {
1551 fPlanCleanPackage = TRUE;
1552 }
1553 else if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && // uninstalling and
1554 BOOTSTRAPPER_REQUEST_STATE_NONE == pPackage->requested && // requested do nothing (aka: default) and
1555 BOOTSTRAPPER_ACTION_STATE_NONE == pPackage->execute && // execute is still do nothing and
1556 !pPackage->fDependencyManagerWasHere && // dependency manager didn't change execute and
1557 BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState) // currently not installed.
1558 {
1559 fPlanCleanPackage = TRUE;
1560 }
1561 }
1562
1563 if (fPlanCleanPackage)
1564 {
1565 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgCleanActions), pPlan->cCleanActions + 1, sizeof(BURN_CLEAN_ACTION), 5);
1566 ExitOnFailure(hr, "Failed to grow plan's array of clean actions.");
1567
1568 pCleanAction = pPlan->rgCleanActions + pPlan->cCleanActions;
1569 ++pPlan->cCleanActions;
1570
1571 pCleanAction->pPackage = pPackage;
1572
1573 pPackage->fPlannedUncache = TRUE;
1574
1575 if (pPackage->fCanAffectRegistration)
1576 {
1577 pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
1578 }
1579 }
1580
1581LExit:
1582 return hr;
1583}
1584
1585extern "C" HRESULT PlanExecuteCacheSyncAndRollback(
1586 __in BURN_PLAN* pPlan,
1587 __in BURN_PACKAGE* pPackage,
1588 __in HANDLE hCacheEvent
1589 )
1590{
1591 HRESULT hr = S_OK;
1592 BURN_EXECUTE_ACTION* pAction = NULL;
1593
1594 hr = PlanAppendExecuteAction(pPlan, &pAction);
1595 ExitOnFailure(hr, "Failed to append wait action for caching.");
1596
1597 pAction->type = BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT;
1598 pAction->syncpoint.hEvent = hCacheEvent;
1599
1600 hr = PlanAppendRollbackAction(pPlan, &pAction);
1601 ExitOnFailure(hr, "Failed to append rollback action.");
1602
1603 pAction->type = BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE;
1604 pAction->uncachePackage.pPackage = pPackage;
1605
1606 hr = PlanExecuteCheckpoint(pPlan);
1607 ExitOnFailure(hr, "Failed to append execute checkpoint for cache rollback.");
1608
1609LExit:
1610 return hr;
1611}
1612
1613extern "C" HRESULT PlanExecuteCheckpoint(
1614 __in BURN_PLAN* pPlan
1615 )
1616{
1617 HRESULT hr = S_OK;
1618 BURN_EXECUTE_ACTION* pAction = NULL;
1619 DWORD dwCheckpointId = GetNextCheckpointId(pPlan);
1620
1621 // execute checkpoint
1622 hr = PlanAppendExecuteAction(pPlan, &pAction);
1623 ExitOnFailure(hr, "Failed to append execute action.");
1624
1625 pAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT;
1626 pAction->checkpoint.dwId = dwCheckpointId;
1627 pAction->checkpoint.pActiveRollbackBoundary = pPlan->pActiveRollbackBoundary;
1628
1629 // rollback checkpoint
1630 hr = PlanAppendRollbackAction(pPlan, &pAction);
1631 ExitOnFailure(hr, "Failed to append rollback action.");
1632
1633 pAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT;
1634 pAction->checkpoint.dwId = dwCheckpointId;
1635 pAction->checkpoint.pActiveRollbackBoundary = pPlan->pActiveRollbackBoundary;
1636
1637LExit:
1638 return hr;
1639}
1640
1641extern "C" HRESULT PlanInsertExecuteAction(
1642 __in DWORD dwIndex,
1643 __in BURN_PLAN* pPlan,
1644 __out BURN_EXECUTE_ACTION** ppExecuteAction
1645 )
1646{
1647 HRESULT hr = S_OK;
1648
1649 hr = MemInsertIntoArray((void**)&pPlan->rgExecuteActions, dwIndex, 1, pPlan->cExecuteActions + 1, sizeof(BURN_EXECUTE_ACTION), 5);
1650 ExitOnFailure(hr, "Failed to grow plan's array of execute actions.");
1651
1652 *ppExecuteAction = pPlan->rgExecuteActions + dwIndex;
1653 ++pPlan->cExecuteActions;
1654
1655LExit:
1656 return hr;
1657}
1658
1659extern "C" HRESULT PlanInsertRollbackAction(
1660 __in DWORD dwIndex,
1661 __in BURN_PLAN* pPlan,
1662 __out BURN_EXECUTE_ACTION** ppRollbackAction
1663 )
1664{
1665 HRESULT hr = S_OK;
1666
1667 hr = MemInsertIntoArray((void**)&pPlan->rgRollbackActions, dwIndex, 1, pPlan->cRollbackActions + 1, sizeof(BURN_EXECUTE_ACTION), 5);
1668 ExitOnFailure(hr, "Failed to grow plan's array of rollback actions.");
1669
1670 *ppRollbackAction = pPlan->rgRollbackActions + dwIndex;
1671 ++pPlan->cRollbackActions;
1672
1673LExit:
1674 return hr;
1675}
1676
1677extern "C" HRESULT PlanAppendExecuteAction(
1678 __in BURN_PLAN* pPlan,
1679 __out BURN_EXECUTE_ACTION** ppExecuteAction
1680 )
1681{
1682 HRESULT hr = S_OK;
1683
1684 hr = MemEnsureArraySize((void**)&pPlan->rgExecuteActions, pPlan->cExecuteActions + 1, sizeof(BURN_EXECUTE_ACTION), 5);
1685 ExitOnFailure(hr, "Failed to grow plan's array of execute actions.");
1686
1687 *ppExecuteAction = pPlan->rgExecuteActions + pPlan->cExecuteActions;
1688 ++pPlan->cExecuteActions;
1689
1690LExit:
1691 return hr;
1692}
1693
1694extern "C" HRESULT PlanAppendRollbackAction(
1695 __in BURN_PLAN* pPlan,
1696 __out BURN_EXECUTE_ACTION** ppRollbackAction
1697 )
1698{
1699 HRESULT hr = S_OK;
1700
1701 hr = MemEnsureArraySize((void**)&pPlan->rgRollbackActions, pPlan->cRollbackActions + 1, sizeof(BURN_EXECUTE_ACTION), 5);
1702 ExitOnFailure(hr, "Failed to grow plan's array of rollback actions.");
1703
1704 *ppRollbackAction = pPlan->rgRollbackActions + pPlan->cRollbackActions;
1705 ++pPlan->cRollbackActions;
1706
1707LExit:
1708 return hr;
1709}
1710
1711extern "C" HRESULT PlanRollbackBoundaryBegin(
1712 __in BURN_PLAN* pPlan,
1713 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
1714 )
1715{
1716 HRESULT hr = S_OK;
1717 BURN_EXECUTE_ACTION* pExecuteAction = NULL;
1718
1719 AssertSz(!pPlan->pActiveRollbackBoundary, "PlanRollbackBoundaryBegin called without completing previous RollbackBoundary");
1720 pPlan->pActiveRollbackBoundary = pRollbackBoundary;
1721
1722 // Add begin rollback boundary to execute plan.
1723 hr = PlanAppendExecuteAction(pPlan, &pExecuteAction);
1724 ExitOnFailure(hr, "Failed to append rollback boundary begin action.");
1725
1726 pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY;
1727 pExecuteAction->rollbackBoundary.pRollbackBoundary = pRollbackBoundary;
1728
1729 // Add begin rollback boundary to rollback plan.
1730 hr = PlanAppendRollbackAction(pPlan, &pExecuteAction);
1731 ExitOnFailure(hr, "Failed to append rollback boundary begin action.");
1732
1733 pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY;
1734 pExecuteAction->rollbackBoundary.pRollbackBoundary = pRollbackBoundary;
1735
1736 // Add begin MSI transaction to execute plan.
1737 if (pRollbackBoundary->fTransaction)
1738 {
1739 hr = PlanExecuteCheckpoint(pPlan);
1740 ExitOnFailure(hr, "Failed to append checkpoint before MSI transaction begin action.");
1741
1742 hr = PlanAppendExecuteAction(pPlan, &pExecuteAction);
1743 ExitOnFailure(hr, "Failed to append MSI transaction begin action.");
1744
1745 pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION;
1746 pExecuteAction->msiTransaction.pRollbackBoundary = pRollbackBoundary;
1747 }
1748
1749LExit:
1750 return hr;
1751}
1752
1753extern "C" HRESULT PlanRollbackBoundaryComplete(
1754 __in BURN_PLAN* pPlan
1755 )
1756{
1757 HRESULT hr = S_OK;
1758 BURN_EXECUTE_ACTION* pExecuteAction = NULL;
1759 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = pPlan->pActiveRollbackBoundary;
1760
1761 AssertSz(pRollbackBoundary, "PlanRollbackBoundaryComplete called without an active RollbackBoundary");
1762
1763 if (pRollbackBoundary && pRollbackBoundary->fTransaction)
1764 {
1765 // Add commit MSI transaction to execute plan.
1766 hr = PlanAppendExecuteAction(pPlan, &pExecuteAction);
1767 ExitOnFailure(hr, "Failed to append MSI transaction commit action.");
1768
1769 pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION;
1770 pExecuteAction->msiTransaction.pRollbackBoundary = pRollbackBoundary;
1771 }
1772
1773 pPlan->pActiveRollbackBoundary = NULL;
1774
1775 // Add checkpoints.
1776 hr = PlanExecuteCheckpoint(pPlan);
1777
1778LExit:
1779 return hr;
1780}
1781
1782/*******************************************************************
1783 PlanSetResumeCommand - Initializes resume command string
1784
1785*******************************************************************/
1786extern "C" HRESULT PlanSetResumeCommand(
1787 __in BURN_REGISTRATION* pRegistration,
1788 __in BOOTSTRAPPER_ACTION action,
1789 __in BOOTSTRAPPER_COMMAND* pCommand,
1790 __in BURN_LOGGING* pLog
1791 )
1792{
1793 HRESULT hr = S_OK;
1794
1795 // build the resume command-line.
1796 hr = CoreRecreateCommandLine(&pRegistration->sczResumeCommandLine, action, pCommand->display, pCommand->restart, pCommand->relationType, pCommand->fPassthrough, pRegistration->sczActiveParent, pRegistration->sczAncestors, pLog->sczPath, pCommand->wzCommandLine);
1797 ExitOnFailure(hr, "Failed to recreate resume command-line.");
1798
1799LExit:
1800 return hr;
1801}
1802
1803
1804// internal function definitions
1805
1806static void UninitializeRegistrationAction(
1807 __in BURN_DEPENDENT_REGISTRATION_ACTION* pAction
1808 )
1809{
1810 ReleaseStr(pAction->sczDependentProviderKey);
1811 ReleaseStr(pAction->sczBundleId);
1812 memset(pAction, 0, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION));
1813}
1814
1815static void UninitializeCacheAction(
1816 __in BURN_CACHE_ACTION* pCacheAction
1817 )
1818{
1819 switch (pCacheAction->type)
1820 {
1821 case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT:
1822 ReleaseHandle(pCacheAction->syncpoint.hEvent);
1823 break;
1824
1825 case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE:
1826 ReleaseStr(pCacheAction->bundleLayout.sczExecutableName);
1827 ReleaseStr(pCacheAction->bundleLayout.sczUnverifiedPath);
1828 break;
1829 }
1830}
1831
1832static void ResetPlannedContainerState(
1833 __in BURN_CONTAINER* pContainer
1834 )
1835{
1836 pContainer->fPlanned = FALSE;
1837 pContainer->qwExtractSizeTotal = 0;
1838 pContainer->qwCommittedCacheProgress = 0;
1839 pContainer->qwCommittedExtractProgress = 0;
1840 pContainer->hrExtract = S_OK;
1841}
1842
1843static void ResetPlannedPayloadsState(
1844 __in BURN_PAYLOADS* pPayloads
1845 )
1846{
1847 for (DWORD i = 0; i < pPayloads->cPayloads; ++i)
1848 {
1849 BURN_PAYLOAD* pPayload = pPayloads->rgPayloads + i;
1850
1851 pPayload->cRemainingInstances = 0;
1852 pPayload->state = BURN_PAYLOAD_STATE_NONE;
1853 ReleaseNullStr(pPayload->sczLocalFilePath);
1854 }
1855}
1856
1857static void ResetPlannedPayloadGroupState(
1858 __in BURN_PAYLOAD_GROUP* pPayloadGroup
1859 )
1860{
1861 for (DWORD i = 0; i < pPayloadGroup->cItems; ++i)
1862 {
1863 BURN_PAYLOAD_GROUP_ITEM* pItem = pPayloadGroup->rgItems + i;
1864
1865 pItem->fCached = FALSE;
1866 pItem->qwCommittedCacheProgress = 0;
1867 }
1868}
1869
1870static void ResetPlannedPackageState(
1871 __in BURN_PACKAGE* pPackage
1872 )
1873{
1874 // Reset package state that is a result of planning.
1875 pPackage->cacheType = pPackage->authoredCacheType;
1876 pPackage->defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1877 pPackage->requested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1878 pPackage->fPlannedCache = FALSE;
1879 pPackage->fPlannedUncache = FALSE;
1880 pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
1881 pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
1882 pPackage->providerExecute = BURN_DEPENDENCY_ACTION_NONE;
1883 pPackage->providerRollback = BURN_DEPENDENCY_ACTION_NONE;
1884 pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE;
1885 pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE;
1886 pPackage->fDependencyManagerWasHere = FALSE;
1887 pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
1888 pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
1889
1890 ReleaseNullStr(pPackage->sczCacheFolder);
1891
1892 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
1893 {
1894 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
1895 {
1896 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
1897
1898 pFeature->expectedState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
1899 pFeature->defaultRequested = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
1900 pFeature->requested = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
1901 pFeature->execute = BOOTSTRAPPER_FEATURE_ACTION_NONE;
1902 pFeature->rollback = BOOTSTRAPPER_FEATURE_ACTION_NONE;
1903 }
1904
1905 for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i)
1906 {
1907 BURN_SLIPSTREAM_MSP* pSlipstreamMsp = &pPackage->Msi.rgSlipstreamMsps[i];
1908
1909 pSlipstreamMsp->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
1910 pSlipstreamMsp->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
1911 }
1912 }
1913 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.rgTargetProducts)
1914 {
1915 for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
1916 {
1917 BURN_MSPTARGETPRODUCT* pTargetProduct = &pPackage->Msp.rgTargetProducts[i];
1918
1919 pTargetProduct->defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1920 pTargetProduct->requested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1921 pTargetProduct->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
1922 pTargetProduct->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
1923 pTargetProduct->executeSkip = BURN_PATCH_SKIP_STATE_NONE;
1924 pTargetProduct->rollbackSkip = BURN_PATCH_SKIP_STATE_NONE;
1925 }
1926 }
1927
1928 ResetPlannedPayloadGroupState(&pPackage->payloads);
1929}
1930
1931static void ResetPlannedRollbackBoundaryState(
1932 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
1933 )
1934{
1935 pRollbackBoundary->fActiveTransaction = FALSE;
1936 ReleaseNullStr(pRollbackBoundary->sczLogPath);
1937}
1938
1939static HRESULT GetActionDefaultRequestState(
1940 __in BOOTSTRAPPER_ACTION action,
1941 __in BOOL fPermanent,
1942 __in BOOTSTRAPPER_PACKAGE_STATE currentState,
1943 __out BOOTSTRAPPER_REQUEST_STATE* pRequestState
1944 )
1945{
1946 HRESULT hr = S_OK;
1947
1948 switch (action)
1949 {
1950 case BOOTSTRAPPER_ACTION_CACHE:
1951 switch (currentState)
1952 {
1953 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
1954 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
1955 break;
1956
1957 default:
1958 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE;
1959 break;
1960 }
1961 break;
1962
1963 case BOOTSTRAPPER_ACTION_INSTALL: __fallthrough;
1964 case BOOTSTRAPPER_ACTION_UPDATE_REPLACE: __fallthrough;
1965 case BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED:
1966 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
1967 break;
1968
1969 case BOOTSTRAPPER_ACTION_REPAIR:
1970 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_REPAIR;
1971 break;
1972
1973 case BOOTSTRAPPER_ACTION_UNINSTALL:
1974 *pRequestState = fPermanent ? BOOTSTRAPPER_REQUEST_STATE_NONE : BOOTSTRAPPER_REQUEST_STATE_ABSENT;
1975 break;
1976
1977 case BOOTSTRAPPER_ACTION_MODIFY:
1978 switch (currentState)
1979 {
1980 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
1981 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT;
1982 break;
1983
1984 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
1985 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
1986 break;
1987
1988 default:
1989 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
1990 break;
1991 }
1992 break;
1993
1994 default:
1995 hr = E_INVALIDARG;
1996 ExitOnRootFailure(hr, "Invalid action state.");
1997 }
1998
1999LExit:
2000 return hr;
2001}
2002
2003static HRESULT AddRegistrationAction(
2004 __in BURN_PLAN* pPlan,
2005 __in BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type,
2006 __in_z LPCWSTR wzDependentProviderKey,
2007 __in_z LPCWSTR wzOwnerBundleId
2008 )
2009{
2010 HRESULT hr = S_OK;
2011 BURN_DEPENDENT_REGISTRATION_ACTION_TYPE rollbackType = (BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER == type) ? BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER : BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER;
2012 BURN_DEPENDENT_REGISTRATION_ACTION* pAction = NULL;
2013
2014 // Create forward registration action.
2015 hr = MemEnsureArraySize((void**)&pPlan->rgRegistrationActions, pPlan->cRegistrationActions + 1, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION), 5);
2016 ExitOnFailure(hr, "Failed to grow plan's array of registration actions.");
2017
2018 pAction = pPlan->rgRegistrationActions + pPlan->cRegistrationActions;
2019 ++pPlan->cRegistrationActions;
2020
2021 pAction->type = type;
2022
2023 hr = StrAllocString(&pAction->sczBundleId, wzOwnerBundleId, 0);
2024 ExitOnFailure(hr, "Failed to copy owner bundle to registration action.");
2025
2026 hr = StrAllocString(&pAction->sczDependentProviderKey, wzDependentProviderKey, 0);
2027 ExitOnFailure(hr, "Failed to copy dependent provider key to registration action.");
2028
2029 // Create rollback registration action.
2030 hr = MemEnsureArraySize((void**)&pPlan->rgRollbackRegistrationActions, pPlan->cRollbackRegistrationActions + 1, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION), 5);
2031 ExitOnFailure(hr, "Failed to grow plan's array of rollback registration actions.");
2032
2033 pAction = pPlan->rgRollbackRegistrationActions + pPlan->cRollbackRegistrationActions;
2034 ++pPlan->cRollbackRegistrationActions;
2035
2036 pAction->type = rollbackType;
2037
2038 hr = StrAllocString(&pAction->sczBundleId, wzOwnerBundleId, 0);
2039 ExitOnFailure(hr, "Failed to copy owner bundle to registration action.");
2040
2041 hr = StrAllocString(&pAction->sczDependentProviderKey, wzDependentProviderKey, 0);
2042 ExitOnFailure(hr, "Failed to copy dependent provider key to rollback registration action.");
2043
2044LExit:
2045 return hr;
2046}
2047
2048static HRESULT AddCachePackage(
2049 __in BURN_PLAN* pPlan,
2050 __in BURN_PACKAGE* pPackage,
2051 __out HANDLE* phSyncpointEvent
2052 )
2053{
2054 HRESULT hr = S_OK;
2055
2056 // If this is an MSI package with slipstream MSPs, ensure the MSPs are cached first.
2057 if (BURN_PACKAGE_TYPE_MSI == pPackage->type && 0 < pPackage->Msi.cSlipstreamMspPackages)
2058 {
2059 hr = AddCacheSlipstreamMsps(pPlan, pPackage);
2060 ExitOnFailure(hr, "Failed to plan slipstream patches for package.");
2061 }
2062
2063 hr = AddCachePackageHelper(pPlan, pPackage, phSyncpointEvent);
2064 ExitOnFailure(hr, "Failed to plan cache package.");
2065
2066LExit:
2067 return hr;
2068}
2069
2070static HRESULT AddCachePackageHelper(
2071 __in BURN_PLAN* pPlan,
2072 __in BURN_PACKAGE* pPackage,
2073 __out HANDLE* phSyncpointEvent
2074 )
2075{
2076 AssertSz(pPackage->sczCacheId && *pPackage->sczCacheId, "AddCachePackageHelper() expects the package to have a cache id.");
2077
2078 HRESULT hr = S_OK;
2079 BURN_CACHE_ACTION* pCacheAction = NULL;
2080 DWORD dwCheckpoint = 0;
2081
2082 BOOL fPlanned = AlreadyPlannedCachePackage(pPlan, pPackage->sczId, phSyncpointEvent);
2083 if (fPlanned)
2084 {
2085 ExitFunction();
2086 }
2087
2088 // Cache checkpoints happen before the package is cached because downloading packages'
2089 // payloads will not roll themselves back the way installation packages rollback on
2090 // failure automatically.
2091 dwCheckpoint = GetNextCheckpointId(pPlan);
2092
2093 hr = AppendCacheAction(pPlan, &pCacheAction);
2094 ExitOnFailure(hr, "Failed to append checkpoint before package start action.");
2095
2096 pCacheAction->type = BURN_CACHE_ACTION_TYPE_CHECKPOINT;
2097 pCacheAction->checkpoint.dwId = dwCheckpoint;
2098
2099 // Only plan the cache rollback if the package is also going to be uninstalled;
2100 // otherwise, future operations like repair will not be able to locate the cached package.
2101 BOOL fPlanCacheRollback = (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->rollback);
2102
2103 if (fPlanCacheRollback)
2104 {
2105 hr = AppendRollbackCacheAction(pPlan, &pCacheAction);
2106 ExitOnFailure(hr, "Failed to append rollback cache action.");
2107
2108 pCacheAction->type = BURN_CACHE_ACTION_TYPE_CHECKPOINT;
2109 pCacheAction->checkpoint.dwId = dwCheckpoint;
2110 }
2111
2112 hr = PlanLayoutPackage(pPlan, pPackage);
2113 ExitOnFailure(hr, "Failed to plan cache for package.");
2114
2115 // Create syncpoint action.
2116 hr = AppendCacheAction(pPlan, &pCacheAction);
2117 ExitOnFailure(hr, "Failed to append cache action.");
2118
2119 pCacheAction->type = BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT;
2120 pCacheAction->syncpoint.hEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
2121 ExitOnNullWithLastError(pCacheAction->syncpoint.hEvent, hr, "Failed to create syncpoint event.");
2122
2123 *phSyncpointEvent = pCacheAction->syncpoint.hEvent;
2124
2125 pPackage->fPlannedCache = TRUE;
2126 if (pPackage->fCanAffectRegistration)
2127 {
2128 pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
2129 }
2130
2131LExit:
2132 return hr;
2133}
2134
2135static HRESULT AddCacheSlipstreamMsps(
2136 __in BURN_PLAN* pPlan,
2137 __in BURN_PACKAGE* pPackage
2138 )
2139{
2140 HRESULT hr = S_OK;
2141 HANDLE hIgnored = NULL;
2142
2143 AssertSz(BURN_PACKAGE_TYPE_MSI == pPackage->type, "Only MSI packages can have slipstream patches.");
2144
2145 for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i)
2146 {
2147 BURN_PACKAGE* pMspPackage = pPackage->Msi.rgSlipstreamMsps[i].pMspPackage;
2148 AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches.");
2149
2150 hr = AddCachePackageHelper(pPlan, pMspPackage, &hIgnored);
2151 ExitOnFailure(hr, "Failed to plan slipstream MSP: %ls", pMspPackage->sczId);
2152 }
2153
2154LExit:
2155 return hr;
2156}
2157
2158static BOOL AlreadyPlannedCachePackage(
2159 __in BURN_PLAN* pPlan,
2160 __in_z LPCWSTR wzPackageId,
2161 __out HANDLE* phSyncpointEvent
2162 )
2163{
2164 BOOL fPlanned = FALSE;
2165
2166 for (DWORD iCacheAction = 0; iCacheAction < pPlan->cCacheActions; ++iCacheAction)
2167 {
2168 BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + iCacheAction;
2169
2170 if (BURN_CACHE_ACTION_TYPE_PACKAGE == pCacheAction->type)
2171 {
2172 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pCacheAction->package.pPackage->sczId, -1, wzPackageId, -1))
2173 {
2174 if (iCacheAction + 1 < pPlan->cCacheActions && BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT == pPlan->rgCacheActions[iCacheAction + 1].type)
2175 {
2176 *phSyncpointEvent = pPlan->rgCacheActions[iCacheAction + 1].syncpoint.hEvent;
2177 }
2178
2179 fPlanned = TRUE;
2180 break;
2181 }
2182 }
2183 }
2184
2185 return fPlanned;
2186}
2187
2188static DWORD GetNextCheckpointId(
2189 __in BURN_PLAN* pPlan
2190 )
2191{
2192 return ++pPlan->dwNextCheckpointId;
2193}
2194
2195static HRESULT AppendCacheAction(
2196 __in BURN_PLAN* pPlan,
2197 __out BURN_CACHE_ACTION** ppCacheAction
2198 )
2199{
2200 HRESULT hr = S_OK;
2201
2202 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgCacheActions), pPlan->cCacheActions + 1, sizeof(BURN_CACHE_ACTION), 5);
2203 ExitOnFailure(hr, "Failed to grow plan's array of cache actions.");
2204
2205 *ppCacheAction = pPlan->rgCacheActions + pPlan->cCacheActions;
2206 ++pPlan->cCacheActions;
2207
2208LExit:
2209 return hr;
2210}
2211
2212static HRESULT AppendRollbackCacheAction(
2213 __in BURN_PLAN* pPlan,
2214 __out BURN_CACHE_ACTION** ppCacheAction
2215 )
2216{
2217 HRESULT hr = S_OK;
2218
2219 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgRollbackCacheActions), pPlan->cRollbackCacheActions + 1, sizeof(BURN_CACHE_ACTION), 5);
2220 ExitOnFailure(hr, "Failed to grow plan's array of rollback cache actions.");
2221
2222 *ppCacheAction = pPlan->rgRollbackCacheActions + pPlan->cRollbackCacheActions;
2223 ++pPlan->cRollbackCacheActions;
2224
2225LExit:
2226 return hr;
2227}
2228
2229static HRESULT ProcessPayloadGroup(
2230 __in BURN_PLAN* pPlan,
2231 __in BURN_PAYLOAD_GROUP* pPayloadGroup
2232 )
2233{
2234 HRESULT hr = S_OK;
2235
2236 for (DWORD i = 0; i < pPayloadGroup->cItems; ++i)
2237 {
2238 BURN_PAYLOAD_GROUP_ITEM* pItem = pPayloadGroup->rgItems + i;
2239 BURN_PAYLOAD* pPayload = pItem->pPayload;
2240
2241 pPayload->cRemainingInstances += 1;
2242
2243 if (pPayload->pContainer && !pPayload->pContainer->fPlanned)
2244 {
2245 hr = PlanLayoutContainer(pPlan, pPayload->pContainer);
2246 ExitOnFailure(hr, "Failed to plan container: %ls", pPayload->pContainer->sczId);
2247 }
2248
2249 if (!pPlan->sczLayoutDirectory || !pPayload->pContainer)
2250 {
2251 // Acquire + Verify + Finalize
2252 pPlan->qwCacheSizeTotal += 3 * pPayload->qwFileSize;
2253
2254 if (!pPlan->sczLayoutDirectory)
2255 {
2256 // Staging
2257 pPlan->qwCacheSizeTotal += pPayload->qwFileSize;
2258 }
2259 }
2260
2261 if (!pPlan->sczLayoutDirectory && pPayload->pContainer && 1 == pPayload->cRemainingInstances)
2262 {
2263 // Extract
2264 pPlan->qwCacheSizeTotal += pPayload->qwFileSize;
2265 pPayload->pContainer->qwExtractSizeTotal += pPayload->qwFileSize;
2266 }
2267
2268 if (!pPayload->sczUnverifiedPath)
2269 {
2270 hr = CacheCalculatePayloadWorkingPath(pPlan->wzBundleId, pPayload, &pPayload->sczUnverifiedPath);
2271 ExitOnFailure(hr, "Failed to calculate unverified path for payload.");
2272 }
2273 }
2274
2275LExit:
2276 return hr;
2277}
2278
2279static void RemoveUnnecessaryActions(
2280 __in BOOL fExecute,
2281 __in BURN_EXECUTE_ACTION* rgActions,
2282 __in DWORD cActions
2283 )
2284{
2285 LPCSTR szExecuteOrRollback = fExecute ? "execute" : "rollback";
2286
2287 for (DWORD i = 0; i < cActions; ++i)
2288 {
2289 BURN_EXECUTE_ACTION* pAction = rgActions + i;
2290
2291 if (BURN_EXECUTE_ACTION_TYPE_MSP_TARGET == pAction->type && pAction->mspTarget.pChainedTargetPackage)
2292 {
2293 BURN_MSPTARGETPRODUCT* pFirstTargetProduct = pAction->mspTarget.rgOrderedPatches->pTargetProduct;
2294 BURN_PATCH_SKIP_STATE skipState = fExecute ? pFirstTargetProduct->executeSkip : pFirstTargetProduct->rollbackSkip;
2295 BOOTSTRAPPER_ACTION_STATE chainedTargetPackageAction = fExecute ? pAction->mspTarget.pChainedTargetPackage->execute : pAction->mspTarget.pChainedTargetPackage->rollback;
2296
2297 switch (skipState)
2298 {
2299 case BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL:
2300 pAction->fDeleted = TRUE;
2301 LogId(REPORT_STANDARD, MSG_PLAN_SKIP_PATCH_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback);
2302 break;
2303 case BURN_PATCH_SKIP_STATE_SLIPSTREAM:
2304 pAction->fDeleted = TRUE;
2305 LogId(REPORT_STANDARD, MSG_PLAN_SKIP_SLIPSTREAM_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback);
2306 break;
2307 }
2308 }
2309 }
2310}
2311
2312static void FinalizePatchActions(
2313 __in BOOL fExecute,
2314 __in BURN_EXECUTE_ACTION* rgActions,
2315 __in DWORD cActions
2316 )
2317{
2318 for (DWORD i = 0; i < cActions; ++i)
2319 {
2320 BURN_EXECUTE_ACTION* pAction = rgActions + i;
2321
2322 if (BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE == pAction->type)
2323 {
2324 BURN_PACKAGE* pPackage = pAction->msiPackage.pPackage;
2325 AssertSz(BOOTSTRAPPER_ACTION_STATE_NONE < pAction->msiPackage.action, "Planned execute MSI action to do nothing");
2326
2327 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->msiPackage.action)
2328 {
2329 // If we are uninstalling the MSI, we must skip all the patches.
2330 for (DWORD j = 0; j < pPackage->Msi.cChainedPatches; ++j)
2331 {
2332 BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + j;
2333 BURN_MSPTARGETPRODUCT* pTargetProduct = pChainedPatch->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex;
2334
2335 if (fExecute)
2336 {
2337 pTargetProduct->execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
2338 pTargetProduct->executeSkip = BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL;
2339 }
2340 else
2341 {
2342 pTargetProduct->rollback = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
2343 pTargetProduct->rollbackSkip = BURN_PATCH_SKIP_STATE_TARGET_UNINSTALL;
2344 }
2345 }
2346 }
2347 else
2348 {
2349 // If the slipstream target is being installed or upgraded (not uninstalled or repaired) then we will slipstream so skip
2350 // the patch's standalone action. Also, if the slipstream target is being repaired and the patch is being
2351 // repaired, skip this operation since it will be redundant.
2352 //
2353 // The primary goal here is to ensure that a slipstream patch that is yet not installed is installed even if the MSI
2354 // is already on the machine. The slipstream must be installed standalone if the MSI is being repaired.
2355 for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j)
2356 {
2357 BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + j;
2358 BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + pSlipstreamMsp->dwMsiChainedPatchIndex;
2359 BURN_MSPTARGETPRODUCT* pTargetProduct = pSlipstreamMsp->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex;
2360 BOOTSTRAPPER_ACTION_STATE action = fExecute ? pTargetProduct->execute : pTargetProduct->rollback;
2361 BOOL fSlipstream = BOOTSTRAPPER_ACTION_STATE_UNINSTALL < action &&
2362 (BOOTSTRAPPER_ACTION_STATE_REPAIR != pAction->msiPackage.action || BOOTSTRAPPER_ACTION_STATE_REPAIR == action);
2363
2364 if (fSlipstream)
2365 {
2366 if (fExecute)
2367 {
2368 pSlipstreamMsp->execute = action;
2369 pTargetProduct->executeSkip = BURN_PATCH_SKIP_STATE_SLIPSTREAM;
2370 }
2371 else
2372 {
2373 pSlipstreamMsp->rollback = action;
2374 pTargetProduct->rollbackSkip = BURN_PATCH_SKIP_STATE_SLIPSTREAM;
2375 }
2376 }
2377 }
2378 }
2379 }
2380 }
2381}
2382
2383static void CalculateExpectedRegistrationStates(
2384 __in BURN_PACKAGE* rgPackages,
2385 __in DWORD cPackages
2386 )
2387{
2388 for (DWORD i = 0; i < cPackages; ++i)
2389 {
2390 BURN_PACKAGE* pPackage = rgPackages + i;
2391
2392 // MspPackages can have actions throughout the plan, so the plan needed to be finalized before anything could be calculated.
2393 if (BURN_PACKAGE_TYPE_MSP == pPackage->type && !pPackage->fDependencyManagerWasHere)
2394 {
2395 pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
2396 pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
2397
2398 for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j)
2399 {
2400 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + j;
2401
2402 // The highest aggregate action state found will be used.
2403 if (pPackage->execute < pTargetProduct->execute)
2404 {
2405 pPackage->execute = pTargetProduct->execute;
2406 }
2407
2408 if (pPackage->rollback < pTargetProduct->rollback)
2409 {
2410 pPackage->rollback = pTargetProduct->rollback;
2411 }
2412 }
2413 }
2414
2415 if (pPackage->fCanAffectRegistration)
2416 {
2417 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pPackage->execute)
2418 {
2419 pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
2420 }
2421 else if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute)
2422 {
2423 pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
2424 }
2425
2426 if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute)
2427 {
2428 if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pPackage->expectedCacheRegistrationState)
2429 {
2430 pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
2431 }
2432 if (BURN_PACKAGE_REGISTRATION_STATE_IGNORED == pPackage->expectedInstallRegistrationState)
2433 {
2434 pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
2435 }
2436 }
2437 else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pPackage->dependencyExecute)
2438 {
2439 if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->expectedCacheRegistrationState)
2440 {
2441 pPackage->expectedCacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED;
2442 }
2443 if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->expectedInstallRegistrationState)
2444 {
2445 pPackage->expectedInstallRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED;
2446 }
2447 }
2448 }
2449 }
2450}
2451
2452static HRESULT PlanDependencyActions(
2453 __in BOOL fBundlePerMachine,
2454 __in BURN_PLAN* pPlan,
2455 __in BURN_PACKAGE* pPackage
2456 )
2457{
2458 HRESULT hr = S_OK;
2459
2460 hr = DependencyPlanPackageBegin(fBundlePerMachine, pPackage, pPlan);
2461 ExitOnFailure(hr, "Failed to begin plan dependency actions for package: %ls", pPackage->sczId);
2462
2463 hr = DependencyPlanPackage(NULL, pPackage, pPlan);
2464 ExitOnFailure(hr, "Failed to plan package dependency actions.");
2465
2466 hr = DependencyPlanPackageComplete(pPackage, pPlan);
2467 ExitOnFailure(hr, "Failed to complete plan dependency actions for package: %ls", pPackage->sczId);
2468
2469LExit:
2470 return hr;
2471}
2472
2473static HRESULT CalculateExecuteActions(
2474 __in BURN_PACKAGE* pPackage,
2475 __in_opt BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary
2476 )
2477{
2478 HRESULT hr = S_OK;
2479 BOOL fInsideMsiTransaction = pActiveRollbackBoundary && pActiveRollbackBoundary->fTransaction;
2480
2481 // Calculate execute actions.
2482 switch (pPackage->type)
2483 {
2484 case BURN_PACKAGE_TYPE_EXE:
2485 hr = ExeEnginePlanCalculatePackage(pPackage);
2486 break;
2487
2488 case BURN_PACKAGE_TYPE_MSI:
2489 hr = MsiEnginePlanCalculatePackage(pPackage, fInsideMsiTransaction);
2490 break;
2491
2492 case BURN_PACKAGE_TYPE_MSP:
2493 hr = MspEnginePlanCalculatePackage(pPackage, fInsideMsiTransaction);
2494 break;
2495
2496 case BURN_PACKAGE_TYPE_MSU:
2497 hr = MsuEnginePlanCalculatePackage(pPackage);
2498 break;
2499
2500 default:
2501 hr = E_UNEXPECTED;
2502 ExitOnFailure(hr, "Invalid package type.");
2503 }
2504
2505LExit:
2506 return hr;
2507}
2508
2509static BOOL NeedsCache(
2510 __in BURN_PACKAGE* pPackage,
2511 __in BOOL fExecute
2512 )
2513{
2514 BOOTSTRAPPER_ACTION_STATE action = fExecute ? pPackage->execute : pPackage->rollback;
2515 if (BURN_PACKAGE_TYPE_EXE == pPackage->type) // Exe packages require the package for all operations (even uninstall).
2516 {
2517 return BOOTSTRAPPER_ACTION_STATE_NONE != action;
2518 }
2519 else // The other package types can uninstall without the original package.
2520 {
2521 return BOOTSTRAPPER_ACTION_STATE_UNINSTALL < action;
2522 }
2523}
2524
2525static BOOL ForceCache(
2526 __in BURN_PLAN* pPlan,
2527 __in BURN_PACKAGE* pPackage
2528 )
2529{
2530 // All packages that have cacheType set to force should be cached if the bundle is going to be present.
2531 return BOOTSTRAPPER_CACHE_TYPE_FORCE == pPackage->cacheType && BOOTSTRAPPER_ACTION_UNINSTALL < pPlan->action;
2532}
2533
2534static void CacheActionLog(
2535 __in DWORD iAction,
2536 __in BURN_CACHE_ACTION* pAction,
2537 __in BOOL fRollback
2538 )
2539{
2540 LPCWSTR wzBase = fRollback ? L" Rollback cache" : L" Cache";
2541 switch (pAction->type)
2542 {
2543 case BURN_CACHE_ACTION_TYPE_CHECKPOINT:
2544 LogStringLine(PlanDumpLevel, "%ls action[%u]: CHECKPOINT id: %u", wzBase, iAction, pAction->checkpoint.dwId);
2545 break;
2546
2547 case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE:
2548 LogStringLine(PlanDumpLevel, "%ls action[%u]: LAYOUT_BUNDLE working path: %ls, exe name: %ls", wzBase, iAction, pAction->bundleLayout.sczUnverifiedPath, pAction->bundleLayout.sczExecutableName);
2549 break;
2550
2551 case BURN_CACHE_ACTION_TYPE_CONTAINER:
2552 LogStringLine(PlanDumpLevel, "%ls action[%u]: CONTAINER container id: %ls, working path: %ls", wzBase, iAction, pAction->container.pContainer->sczId, pAction->container.pContainer->sczUnverifiedPath);
2553 break;
2554
2555 case BURN_CACHE_ACTION_TYPE_PACKAGE:
2556 LogStringLine(PlanDumpLevel, "%ls action[%u]: PACKAGE id: %ls", wzBase, iAction, pAction->package.pPackage->sczId);
2557 break;
2558
2559 case BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE:
2560 LogStringLine(PlanDumpLevel, "%ls action[%u]: ROLLBACK_PACKAGE id: %ls", wzBase, iAction, pAction->rollbackPackage.pPackage->sczId);
2561 break;
2562
2563 case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT:
2564 LogStringLine(PlanDumpLevel, "%ls action[%u]: SIGNAL_SYNCPOINT event handle: 0x%p", wzBase, iAction, pAction->syncpoint.hEvent);
2565 break;
2566
2567 default:
2568 AssertSz(FALSE, "Unknown cache action type.");
2569 break;
2570 }
2571}
2572
2573static void ExecuteActionLog(
2574 __in DWORD iAction,
2575 __in BURN_EXECUTE_ACTION* pAction,
2576 __in BOOL fRollback
2577 )
2578{
2579 LPCWSTR wzBase = fRollback ? L" Rollback" : L" Execute";
2580 switch (pAction->type)
2581 {
2582 case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT:
2583 LogStringLine(PlanDumpLevel, "%ls action[%u]: CHECKPOINT id: %u, msi transaction id: %ls", wzBase, iAction, pAction->checkpoint.dwId, pAction->checkpoint.pActiveRollbackBoundary && pAction->checkpoint.pActiveRollbackBoundary->fTransaction ? pAction->checkpoint.pActiveRollbackBoundary->sczId : L"(none)");
2584 break;
2585
2586 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER:
2587 LogStringLine(PlanDumpLevel, "%ls action[%u]: PACKAGE_PROVIDER package id: %ls, action: %hs", wzBase, iAction, pAction->packageProvider.pPackage->sczId, LoggingDependencyActionToString(pAction->packageProvider.action));
2588 break;
2589
2590 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY:
2591 LogStringLine(PlanDumpLevel, "%ls action[%u]: PACKAGE_DEPENDENCY package id: %ls, bundle provider key: %ls, action: %hs", wzBase, iAction, pAction->packageDependency.pPackage->sczId, pAction->packageDependency.sczBundleProviderKey, LoggingDependencyActionToString(pAction->packageDependency.action));
2592 break;
2593
2594 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
2595 LogStringLine(PlanDumpLevel, "%ls action[%u]: EXE_PACKAGE package id: %ls, action: %hs, ignore dependencies: %ls", wzBase, iAction, pAction->exePackage.pPackage->sczId, LoggingActionStateToString(pAction->exePackage.action), pAction->exePackage.sczIgnoreDependencies);
2596 break;
2597
2598 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
2599 LogStringLine(PlanDumpLevel, "%ls action[%u]: MSI_PACKAGE package id: %ls, action: %hs, action msi property: %ls, ui level: %u, disable externaluihandler: %ls, log path: %ls, logging attrib: %u", wzBase, iAction, pAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pAction->msiPackage.action), LoggingBurnMsiPropertyToString(pAction->msiPackage.actionMsiProperty), pAction->msiPackage.uiLevel, pAction->msiPackage.fDisableExternalUiHandler ? L"yes" : L"no", pAction->msiPackage.sczLogPath, pAction->msiPackage.dwLoggingAttributes);
2600 for (DWORD j = 0; j < pAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++j)
2601 {
2602 const BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pAction->msiPackage.pPackage->Msi.rgSlipstreamMsps + j;
2603 LogStringLine(PlanDumpLevel, " Patch[%u]: msp package id: %ls, action: %hs", j, pSlipstreamMsp->pMspPackage->sczId, LoggingActionStateToString(fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute));
2604 }
2605 break;
2606
2607 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
2608 LogStringLine(PlanDumpLevel, "%ls action[%u]: MSP_TARGET package id: %ls, action: %hs, target product code: %ls, target per-machine: %ls, action msi property: %ls, ui level: %u, disable externaluihandler: %ls, log path: %ls", wzBase, iAction, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.sczTargetProductCode, pAction->mspTarget.fPerMachineTarget ? L"yes" : L"no", LoggingBurnMsiPropertyToString(pAction->mspTarget.actionMsiProperty), pAction->mspTarget.uiLevel, pAction->mspTarget.fDisableExternalUiHandler ? L"yes" : L"no", pAction->mspTarget.sczLogPath);
2609 for (DWORD j = 0; j < pAction->mspTarget.cOrderedPatches; ++j)
2610 {
2611 LogStringLine(PlanDumpLevel, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->mspTarget.rgOrderedPatches[j].pTargetProduct->dwOrder, pAction->mspTarget.rgOrderedPatches[j].pPackage->sczId);
2612 }
2613 break;
2614
2615 case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
2616 LogStringLine(PlanDumpLevel, "%ls action[%u]: MSU_PACKAGE package id: %ls, action: %hs, log path: %ls", wzBase, iAction, pAction->msuPackage.pPackage->sczId, LoggingActionStateToString(pAction->msuPackage.action), pAction->msuPackage.sczLogPath);
2617 break;
2618
2619 case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY:
2620 LogStringLine(PlanDumpLevel, "%ls action[%u]: ROLLBACK_BOUNDARY id: %ls, vital: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no");
2621 break;
2622
2623 case BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT:
2624 LogStringLine(PlanDumpLevel, "%ls action[%u]: WAIT_SYNCPOINT event handle: 0x%p", wzBase, iAction, pAction->syncpoint.hEvent);
2625 break;
2626
2627 case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE:
2628 LogStringLine(PlanDumpLevel, "%ls action[%u]: UNCACHE_PACKAGE id: %ls", wzBase, iAction, pAction->uncachePackage.pPackage->sczId);
2629 break;
2630
2631 case BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION:
2632 LogStringLine(PlanDumpLevel, "%ls action[%u]: BEGIN_MSI_TRANSACTION id: %ls", wzBase, iAction, pAction->msiTransaction.pRollbackBoundary->sczId);
2633 break;
2634
2635 case BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION:
2636 LogStringLine(PlanDumpLevel, "%ls action[%u]: COMMIT_MSI_TRANSACTION id: %ls", wzBase, iAction, pAction->msiTransaction.pRollbackBoundary->sczId);
2637 break;
2638
2639 default:
2640 AssertSz(FALSE, "Unknown execute action type.");
2641 break;
2642 }
2643
2644 if (pAction->fDeleted)
2645 {
2646 LogStringLine(PlanDumpLevel, " (deleted action)");
2647 }
2648}
2649
2650extern "C" void PlanDump(
2651 __in BURN_PLAN* pPlan
2652 )
2653{
2654 LogStringLine(PlanDumpLevel, "--- Begin plan dump ---");
2655
2656 LogStringLine(PlanDumpLevel, "Plan action: %hs", LoggingBurnActionToString(pPlan->action));
2657 LogStringLine(PlanDumpLevel, " per-machine: %hs", LoggingTrueFalseToString(pPlan->fPerMachine));
2658 LogStringLine(PlanDumpLevel, " disable-rollback: %hs", LoggingTrueFalseToString(pPlan->fDisableRollback));
2659 LogStringLine(PlanDumpLevel, " estimated size: %llu", pPlan->qwEstimatedSize);
2660 if (pPlan->sczLayoutDirectory)
2661 {
2662 LogStringLine(PlanDumpLevel, " layout directory: %ls", pPlan->sczLayoutDirectory);
2663 }
2664
2665 LogStringLine(PlanDumpLevel, "Plan cache size: %llu", pPlan->qwCacheSizeTotal);
2666 for (DWORD i = 0; i < pPlan->cCacheActions; ++i)
2667 {
2668 CacheActionLog(i, pPlan->rgCacheActions + i, FALSE);
2669 }
2670
2671 for (DWORD i = 0; i < pPlan->cRollbackCacheActions; ++i)
2672 {
2673 CacheActionLog(i, pPlan->rgRollbackCacheActions + i, TRUE);
2674 }
2675
2676 LogStringLine(PlanDumpLevel, "Plan execute package count: %u", pPlan->cExecutePackagesTotal);
2677 LogStringLine(PlanDumpLevel, " overall progress ticks: %u", pPlan->cOverallProgressTicksTotal);
2678 for (DWORD i = 0; i < pPlan->cExecuteActions; ++i)
2679 {
2680 ExecuteActionLog(i, pPlan->rgExecuteActions + i, FALSE);
2681 }
2682
2683 for (DWORD i = 0; i < pPlan->cRollbackActions; ++i)
2684 {
2685 ExecuteActionLog(i, pPlan->rgRollbackActions + i, TRUE);
2686 }
2687
2688 for (DWORD i = 0; i < pPlan->cCleanActions; ++i)
2689 {
2690 LogStringLine(PlanDumpLevel, " Clean action[%u]: CLEAN_PACKAGE package id: %ls", i, pPlan->rgCleanActions[i].pPackage->sczId);
2691 }
2692
2693 for (DWORD i = 0; i < pPlan->cPlannedProviders; ++i)
2694 {
2695 LogStringLine(PlanDumpLevel, " Dependency action[%u]: PLANNED_PROVIDER key: %ls, name: %ls", i, pPlan->rgPlannedProviders[i].sczKey, pPlan->rgPlannedProviders[i].sczName);
2696 }
2697
2698 LogStringLine(PlanDumpLevel, "--- End plan dump ---");
2699}
diff --git a/src/burn/engine/plan.h b/src/burn/engine/plan.h
new file mode 100644
index 00000000..00ab5516
--- /dev/null
+++ b/src/burn/engine/plan.h
@@ -0,0 +1,456 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// constants
11
12const DWORD BURN_PLAN_INVALID_ACTION_INDEX = 0x80000000;
13
14enum BURN_REGISTRATION_ACTION_OPERATIONS
15{
16 BURN_REGISTRATION_ACTION_OPERATIONS_NONE = 0x0,
17 BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE = 0x1,
18 BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION = 0x2,
19 BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE = 0x4,
20};
21
22enum BURN_DEPENDENCY_REGISTRATION_ACTION
23{
24 BURN_DEPENDENCY_REGISTRATION_ACTION_NONE,
25 BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER,
26 BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER,
27};
28
29enum BURN_DEPENDENT_REGISTRATION_ACTION_TYPE
30{
31 BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_NONE,
32 BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER,
33 BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER,
34};
35
36enum BURN_CACHE_ACTION_TYPE
37{
38 BURN_CACHE_ACTION_TYPE_NONE,
39 BURN_CACHE_ACTION_TYPE_CHECKPOINT,
40 BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE,
41 BURN_CACHE_ACTION_TYPE_PACKAGE,
42 BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE,
43 BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT,
44 BURN_CACHE_ACTION_TYPE_CONTAINER,
45};
46
47enum BURN_EXECUTE_ACTION_TYPE
48{
49 BURN_EXECUTE_ACTION_TYPE_NONE,
50 BURN_EXECUTE_ACTION_TYPE_CHECKPOINT,
51 BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT,
52 BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE,
53 BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE,
54 BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE,
55 BURN_EXECUTE_ACTION_TYPE_MSP_TARGET,
56 BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE,
57 BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER,
58 BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY,
59 BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY,
60 BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION,
61 BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION,
62};
63
64enum BURN_CLEAN_ACTION_TYPE
65{
66 BURN_CLEAN_ACTION_TYPE_NONE,
67 BURN_CLEAN_ACTION_TYPE_BUNDLE,
68 BURN_CLEAN_ACTION_TYPE_PACKAGE,
69};
70
71
72// structs
73
74typedef struct _BURN_DEPENDENT_REGISTRATION_ACTION
75{
76 BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type;
77 LPWSTR sczBundleId;
78 LPWSTR sczDependentProviderKey;
79} BURN_DEPENDENT_REGISTRATION_ACTION;
80
81typedef struct _BURN_CACHE_CONTAINER_PROGRESS
82{
83 LPWSTR wzId;
84 DWORD iIndex;
85 BOOL fCachedDuringApply;
86 BURN_CONTAINER* pContainer;
87} BURN_CACHE_CONTAINER_PROGRESS;
88
89typedef struct _BURN_CACHE_PAYLOAD_PROGRESS
90{
91 LPWSTR wzId;
92 DWORD iIndex;
93 BOOL fCachedDuringApply;
94 BURN_PAYLOAD* pPayload;
95} BURN_CACHE_PAYLOAD_PROGRESS;
96
97typedef struct _BURN_CACHE_ACTION
98{
99 BURN_CACHE_ACTION_TYPE type;
100 union
101 {
102 struct
103 {
104 DWORD dwId;
105 } checkpoint;
106 struct
107 {
108 LPWSTR sczExecutableName;
109 LPWSTR sczUnverifiedPath;
110 DWORD64 qwBundleSize;
111 BURN_PAYLOAD_GROUP* pPayloadGroup;
112 } bundleLayout;
113 struct
114 {
115 BURN_PACKAGE* pPackage;
116 } package;
117 struct
118 {
119 BURN_PACKAGE* pPackage;
120 } rollbackPackage;
121 struct
122 {
123 HANDLE hEvent;
124 } syncpoint;
125 struct
126 {
127 BURN_CONTAINER* pContainer;
128 } container;
129 };
130} BURN_CACHE_ACTION;
131
132typedef struct _BURN_ORDERED_PATCHES
133{
134 BURN_PACKAGE* pPackage;
135
136 BURN_MSPTARGETPRODUCT* pTargetProduct; // only valid in the unelevated engine.
137} BURN_ORDERED_PATCHES;
138
139typedef struct _BURN_EXECUTE_ACTION_CHECKPOINT
140{
141 DWORD dwId;
142 BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary;
143} BURN_EXECUTE_ACTION_CHECKPOINT;
144
145typedef struct _BURN_EXECUTE_ACTION
146{
147 BURN_EXECUTE_ACTION_TYPE type;
148 BOOL fDeleted; // used to skip an action after it was planned since deleting actions out of the plan is too hard.
149 union
150 {
151 BURN_EXECUTE_ACTION_CHECKPOINT checkpoint;
152 struct
153 {
154 HANDLE hEvent;
155 } syncpoint;
156 struct
157 {
158 BURN_PACKAGE* pPackage;
159 } uncachePackage;
160 struct
161 {
162 BURN_PACKAGE* pPackage;
163 BOOL fFireAndForget;
164 BOOTSTRAPPER_ACTION_STATE action;
165 LPWSTR sczIgnoreDependencies;
166 LPWSTR sczAncestors;
167 } exePackage;
168 struct
169 {
170 BURN_PACKAGE* pPackage;
171 LPWSTR sczLogPath;
172 DWORD dwLoggingAttributes;
173 BURN_MSI_PROPERTY actionMsiProperty;
174 INSTALLUILEVEL uiLevel;
175 BOOL fDisableExternalUiHandler;
176 BOOTSTRAPPER_ACTION_STATE action;
177
178 BOOTSTRAPPER_FEATURE_ACTION* rgFeatures;
179 } msiPackage;
180 struct
181 {
182 BURN_PACKAGE* pPackage;
183 LPWSTR sczTargetProductCode;
184 BURN_PACKAGE* pChainedTargetPackage;
185 BOOL fSlipstream;
186 BOOL fPerMachineTarget;
187 LPWSTR sczLogPath;
188 BURN_MSI_PROPERTY actionMsiProperty;
189 INSTALLUILEVEL uiLevel;
190 BOOL fDisableExternalUiHandler;
191 BOOTSTRAPPER_ACTION_STATE action;
192
193 BURN_ORDERED_PATCHES* rgOrderedPatches;
194 DWORD cOrderedPatches;
195 } mspTarget;
196 struct
197 {
198 BURN_PACKAGE* pPackage;
199 LPWSTR sczLogPath;
200 BOOTSTRAPPER_ACTION_STATE action;
201 } msuPackage;
202 struct
203 {
204 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary;
205 } rollbackBoundary;
206 struct
207 {
208 BURN_PACKAGE* pPackage;
209 BURN_DEPENDENCY_ACTION action;
210 } packageProvider;
211 struct
212 {
213 BURN_PACKAGE* pPackage;
214 LPWSTR sczBundleProviderKey;
215 BURN_DEPENDENCY_ACTION action;
216 } packageDependency;
217 struct
218 {
219 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary;
220 } msiTransaction;
221 };
222} BURN_EXECUTE_ACTION;
223
224typedef struct _BURN_CLEAN_ACTION
225{
226 BURN_PACKAGE* pPackage;
227} BURN_CLEAN_ACTION;
228
229typedef struct _BURN_PLAN
230{
231 BOOTSTRAPPER_ACTION action;
232 BURN_PAYLOADS* pPayloads; // points directly into parent the ENGINE_STATE.
233 LPWSTR wzBundleId; // points directly into parent the ENGINE_STATE.
234 LPWSTR wzBundleProviderKey; // points directly into parent the ENGINE_STATE.
235 BOOL fPerMachine;
236 BOOL fCanAffectMachineState;
237 DWORD dwRegistrationOperations;
238 BOOL fDisallowRemoval;
239 BOOL fDisableRollback;
240 BOOL fAffectedMachineState;
241 BOOL fIgnoreAllDependents;
242 LPWSTR sczLayoutDirectory;
243
244 DWORD64 qwCacheSizeTotal;
245
246 DWORD64 qwEstimatedSize;
247
248 DWORD cExecutePackagesTotal;
249 DWORD cOverallProgressTicksTotal;
250
251 BOOL fEnabledForwardCompatibleBundle;
252 BURN_PACKAGE forwardCompatibleBundle;
253
254 BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction;
255
256 BURN_DEPENDENT_REGISTRATION_ACTION* rgRegistrationActions;
257 DWORD cRegistrationActions;
258
259 BURN_DEPENDENT_REGISTRATION_ACTION* rgRollbackRegistrationActions;
260 DWORD cRollbackRegistrationActions;
261
262 BURN_CACHE_ACTION* rgCacheActions;
263 DWORD cCacheActions;
264
265 BURN_CACHE_ACTION* rgRollbackCacheActions;
266 DWORD cRollbackCacheActions;
267
268 BURN_EXECUTE_ACTION* rgExecuteActions;
269 DWORD cExecuteActions;
270
271 BURN_EXECUTE_ACTION* rgRollbackActions;
272 DWORD cRollbackActions;
273
274 BURN_CLEAN_ACTION* rgCleanActions;
275 DWORD cCleanActions;
276
277 DEPENDENCY* rgPlannedProviders;
278 UINT cPlannedProviders;
279
280 BURN_CACHE_CONTAINER_PROGRESS* rgContainerProgress;
281 DWORD cContainerProgress;
282 STRINGDICT_HANDLE shContainerProgress;
283
284 BURN_CACHE_PAYLOAD_PROGRESS* rgPayloadProgress;
285 DWORD cPayloadProgress;
286 STRINGDICT_HANDLE shPayloadProgress;
287
288 DWORD dwNextCheckpointId; // for plan internal use
289 BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary; // for plan internal use
290} BURN_PLAN;
291
292
293// functions
294
295void PlanReset(
296 __in BURN_PLAN* pPlan,
297 __in BURN_CONTAINERS* pContainers,
298 __in BURN_PACKAGES* pPackages,
299 __in BURN_PAYLOAD_GROUP* pLayoutPayloads
300 );
301void PlanUninitializeExecuteAction(
302 __in BURN_EXECUTE_ACTION* pExecuteAction
303 );
304HRESULT PlanSetVariables(
305 __in BOOTSTRAPPER_ACTION action,
306 __in BURN_VARIABLES* pVariables
307 );
308HRESULT PlanDefaultPackageRequestState(
309 __in BURN_PACKAGE_TYPE packageType,
310 __in BOOTSTRAPPER_PACKAGE_STATE currentState,
311 __in BOOL fPermanent,
312 __in BOOTSTRAPPER_ACTION action,
313 __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition,
314 __in BOOTSTRAPPER_RELATION_TYPE relationType,
315 __out BOOTSTRAPPER_REQUEST_STATE* pRequestState
316 );
317HRESULT PlanLayoutBundle(
318 __in BURN_PLAN* pPlan,
319 __in_z LPCWSTR wzExecutableName,
320 __in DWORD64 qwBundleSize,
321 __in BURN_VARIABLES* pVariables,
322 __in BURN_PAYLOAD_GROUP* pLayoutPayloads
323 );
324HRESULT PlanForwardCompatibleBundles(
325 __in BURN_USER_EXPERIENCE* pUX,
326 __in BOOTSTRAPPER_COMMAND* pCommand,
327 __in BURN_PLAN* pPlan,
328 __in BURN_REGISTRATION* pRegistration,
329 __in BOOTSTRAPPER_ACTION action
330 );
331HRESULT PlanPackages(
332 __in BURN_USER_EXPERIENCE* pUX,
333 __in BURN_PACKAGES* pPackages,
334 __in BURN_PLAN* pPlan,
335 __in BURN_LOGGING* pLog,
336 __in BURN_VARIABLES* pVariables,
337 __in BOOTSTRAPPER_DISPLAY display,
338 __in BOOTSTRAPPER_RELATION_TYPE relationType
339 );
340HRESULT PlanRegistration(
341 __in BURN_PLAN* pPlan,
342 __in BURN_REGISTRATION* pRegistration,
343 __in BOOTSTRAPPER_RESUME_TYPE resumeType,
344 __in BOOTSTRAPPER_RELATION_TYPE relationType,
345 __inout BOOL* pfContinuePlanning
346 );
347HRESULT PlanPassThroughBundle(
348 __in BURN_USER_EXPERIENCE* pUX,
349 __in BURN_PACKAGE* pPackage,
350 __in BURN_PLAN* pPlan,
351 __in BURN_LOGGING* pLog,
352 __in BURN_VARIABLES* pVariables,
353 __in BOOTSTRAPPER_DISPLAY display,
354 __in BOOTSTRAPPER_RELATION_TYPE relationType
355 );
356HRESULT PlanUpdateBundle(
357 __in BURN_USER_EXPERIENCE* pUX,
358 __in BURN_PACKAGE* pPackage,
359 __in BURN_PLAN* pPlan,
360 __in BURN_LOGGING* pLog,
361 __in BURN_VARIABLES* pVariables,
362 __in BOOTSTRAPPER_DISPLAY display,
363 __in BOOTSTRAPPER_RELATION_TYPE relationType
364 );
365HRESULT PlanLayoutContainer(
366 __in BURN_PLAN* pPlan,
367 __in BURN_CONTAINER* pContainer
368 );
369HRESULT PlanLayoutPackage(
370 __in BURN_PLAN* pPlan,
371 __in BURN_PACKAGE* pPackage
372 );
373HRESULT PlanExecutePackage(
374 __in BOOL fPerMachine,
375 __in BOOTSTRAPPER_DISPLAY display,
376 __in BURN_USER_EXPERIENCE* pUserExperience,
377 __in BURN_PLAN* pPlan,
378 __in BURN_PACKAGE* pPackage,
379 __in BURN_LOGGING* pLog,
380 __in BURN_VARIABLES* pVariables,
381 __inout HANDLE* phSyncpointEvent
382 );
383HRESULT PlanDefaultRelatedBundleRequestState(
384 __in BOOTSTRAPPER_RELATION_TYPE commandRelationType,
385 __in BOOTSTRAPPER_RELATION_TYPE relatedBundleRelationType,
386 __in BOOTSTRAPPER_ACTION action,
387 __in VERUTIL_VERSION* pRegistrationVersion,
388 __in VERUTIL_VERSION* pRelatedBundleVersion,
389 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestState
390 );
391HRESULT PlanRelatedBundlesBegin(
392 __in BURN_USER_EXPERIENCE* pUserExperience,
393 __in BURN_REGISTRATION* pRegistration,
394 __in BOOTSTRAPPER_RELATION_TYPE relationType,
395 __in BURN_PLAN* pPlan
396 );
397HRESULT PlanRelatedBundlesComplete(
398 __in BURN_REGISTRATION* pRegistration,
399 __in BURN_PLAN* pPlan,
400 __in BURN_LOGGING* pLog,
401 __in BURN_VARIABLES* pVariables,
402 __in DWORD dwExecuteActionEarlyIndex
403 );
404HRESULT PlanFinalizeActions(
405 __in BURN_PLAN* pPlan
406 );
407HRESULT PlanCleanPackage(
408 __in BURN_PLAN* pPlan,
409 __in BURN_PACKAGE* pPackage
410 );
411HRESULT PlanExecuteCacheSyncAndRollback(
412 __in BURN_PLAN* pPlan,
413 __in BURN_PACKAGE* pPackage,
414 __in HANDLE hCacheEvent
415 );
416HRESULT PlanExecuteCheckpoint(
417 __in BURN_PLAN* pPlan
418 );
419HRESULT PlanInsertExecuteAction(
420 __in DWORD dwIndex,
421 __in BURN_PLAN* pPlan,
422 __out BURN_EXECUTE_ACTION** ppExecuteAction
423 );
424HRESULT PlanInsertRollbackAction(
425 __in DWORD dwIndex,
426 __in BURN_PLAN* pPlan,
427 __out BURN_EXECUTE_ACTION** ppRollbackAction
428 );
429HRESULT PlanAppendExecuteAction(
430 __in BURN_PLAN* pPlan,
431 __out BURN_EXECUTE_ACTION** ppExecuteAction
432 );
433HRESULT PlanAppendRollbackAction(
434 __in BURN_PLAN* pPlan,
435 __out BURN_EXECUTE_ACTION** ppExecuteAction
436 );
437HRESULT PlanRollbackBoundaryBegin(
438 __in BURN_PLAN* pPlan,
439 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
440 );
441HRESULT PlanRollbackBoundaryComplete(
442 __in BURN_PLAN* pPlan
443 );
444HRESULT PlanSetResumeCommand(
445 __in BURN_REGISTRATION* pRegistration,
446 __in BOOTSTRAPPER_ACTION action,
447 __in BOOTSTRAPPER_COMMAND* pCommand,
448 __in BURN_LOGGING* pLog
449 );
450void PlanDump(
451 __in BURN_PLAN* pPlan
452 );
453
454#if defined(__cplusplus)
455}
456#endif
diff --git a/src/burn/engine/platform.cpp b/src/burn/engine/platform.cpp
new file mode 100644
index 00000000..9469ff49
--- /dev/null
+++ b/src/burn/engine/platform.cpp
@@ -0,0 +1,16 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// variables
7
8PFN_INITIATESYSTEMSHUTDOWNEXW vpfnInitiateSystemShutdownExW;
9
10
11// function definitions
12
13extern "C" void PlatformInitialize()
14{
15 vpfnInitiateSystemShutdownExW = ::InitiateSystemShutdownExW;
16}
diff --git a/src/burn/engine/platform.h b/src/burn/engine/platform.h
new file mode 100644
index 00000000..3681f248
--- /dev/null
+++ b/src/burn/engine/platform.h
@@ -0,0 +1,34 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// typedefs
11
12typedef BOOL (WINAPI *PFN_INITIATESYSTEMSHUTDOWNEXW)(
13 __in_opt LPWSTR lpMachineName,
14 __in_opt LPWSTR lpMessage,
15 __in DWORD dwTimeout,
16 __in BOOL bForceAppsClosed,
17 __in BOOL bRebootAfterShutdown,
18 __in DWORD dwReason
19 );
20
21
22// variable declarations
23
24extern PFN_INITIATESYSTEMSHUTDOWNEXW vpfnInitiateSystemShutdownExW;
25
26
27// function declarations
28
29void PlatformInitialize();
30
31
32#if defined(__cplusplus)
33}
34#endif
diff --git a/src/burn/engine/precomp.cpp b/src/burn/engine/precomp.cpp
new file mode 100644
index 00000000..37664a1c
--- /dev/null
+++ b/src/burn/engine/precomp.cpp
@@ -0,0 +1,3 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
diff --git a/src/burn/engine/precomp.h b/src/burn/engine/precomp.h
new file mode 100644
index 00000000..11b594da
--- /dev/null
+++ b/src/burn/engine/precomp.h
@@ -0,0 +1,102 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#include <windows.h>
6#include <aclapi.h>
7
8#include <math.h>
9#include <msiquery.h>
10#include <sddl.h>
11#include <shlobj.h>
12#include <shlwapi.h>
13#include <softpub.h>
14#include <strsafe.h>
15#include <intsafe.h>
16#include <mscat.h>
17#include <lmcons.h>
18#include <wininet.h>
19#include <stddef.h>
20#include <VersionHelpers.h>
21
22#include <dutilsources.h>
23#include <burnsources.h>
24
25#include <dutil.h>
26#include <verutil.h>
27#include <aclutil.h>
28#include <apputil.h>
29#include <buffutil.h>
30#include <cabutil.h>
31#include <cryputil.h>
32#include <dirutil.h>
33#include <fileutil.h>
34#include <guidutil.h>
35#include <logutil.h>
36#include <memutil.h>
37#include <osutil.h>
38#include <pathutil.h>
39#include <polcutil.h>
40#include <procutil.h>
41#include <regutil.h>
42#include <resrutil.h>
43#include <shelutil.h>
44#include <srputil.h>
45#include <strutil.h>
46#include <svcutil.h>
47#include <userutil.h>
48#include <wiutil.h>
49#include <wuautil.h>
50#include <xmlutil.h>
51#include <dictutil.h>
52#include <deputil.h>
53#include <dlutil.h>
54#include <atomutil.h>
55#include <apuputil.h>
56#include <dpiutil.h>
57
58#include "BootstrapperEngine.h"
59#include "BootstrapperApplication.h"
60#include "BundleExtensionEngine.h"
61#include "BundleExtension.h"
62
63#include "platform.h"
64#include "variant.h"
65#include "variable.h"
66#include "condition.h"
67#include "section.h"
68#include "approvedexe.h"
69#include "container.h"
70#include "payload.h"
71#include "cabextract.h"
72#include "burnextension.h"
73#include "search.h"
74#include "userexperience.h"
75#include "package.h"
76#include "update.h"
77#include "pseudobundle.h"
78#include "registration.h"
79#include "relatedbundle.h"
80#include "detect.h"
81#include "plan.h"
82#include "logging.h"
83#include "pipe.h"
84#include "core.h"
85#include "cache.h"
86#include "apply.h"
87#include "exeengine.h"
88#include "msiengine.h"
89#include "mspengine.h"
90#include "msuengine.h"
91#include "dependency.h"
92#include "elevation.h"
93#include "embedded.h"
94#include "manifest.h"
95#include "splashscreen.h"
96#include "uithread.h"
97#include "netfxchainer.h"
98
99#include "externalengine.h"
100#include "EngineForApplication.h"
101#include "EngineForExtension.h"
102#include "engine.messages.h"
diff --git a/src/burn/engine/pseudobundle.cpp b/src/burn/engine/pseudobundle.cpp
new file mode 100644
index 00000000..180cc621
--- /dev/null
+++ b/src/burn/engine/pseudobundle.cpp
@@ -0,0 +1,241 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6extern "C" HRESULT PseudoBundleInitialize(
7 __in DWORD64 qwEngineVersion,
8 __in BURN_PACKAGE* pPackage,
9 __in BOOL fPerMachine,
10 __in_z LPCWSTR wzId,
11 __in BOOTSTRAPPER_RELATION_TYPE relationType,
12 __in BOOTSTRAPPER_PACKAGE_STATE state,
13 __in BOOL fCached,
14 __in_z LPCWSTR wzFilePath,
15 __in_z LPCWSTR wzLocalSource,
16 __in_z_opt LPCWSTR wzDownloadSource,
17 __in DWORD64 qwSize,
18 __in BOOL fVital,
19 __in_z_opt LPCWSTR wzInstallArguments,
20 __in_z_opt LPCWSTR wzRepairArguments,
21 __in_z_opt LPCWSTR wzUninstallArguments,
22 __in_opt BURN_DEPENDENCY_PROVIDER* pDependencyProvider,
23 __in_opt const BYTE* pbHash,
24 __in const DWORD cbHash
25 )
26{
27 HRESULT hr = S_OK;
28 LPWSTR sczRelationTypeCommandLineSwitch = NULL;
29 BURN_PAYLOAD* pPayload = NULL;
30
31 LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType);
32 if (wzRelationTypeCommandLine)
33 {
34 hr = StrAllocFormatted(&sczRelationTypeCommandLineSwitch, L" -%ls", wzRelationTypeCommandLine);
35 }
36
37 // Initialize the single payload, and fill out all the necessary fields
38 pPackage->payloads.rgItems = (BURN_PAYLOAD_GROUP_ITEM*)MemAlloc(sizeof(BURN_PAYLOAD_GROUP_ITEM), TRUE);
39 ExitOnNull(pPackage->payloads.rgItems, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload group inside of related bundle struct");
40 pPackage->payloads.cItems = 1;
41
42 pPayload = (BURN_PAYLOAD*)MemAlloc(sizeof(BURN_PAYLOAD), TRUE);
43 ExitOnNull(pPayload, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload inside of related bundle struct");
44 pPackage->payloads.rgItems[0].pPayload = pPayload;
45 pPayload->packaging = BURN_PAYLOAD_PACKAGING_EXTERNAL;
46 pPayload->qwFileSize = qwSize;
47
48 hr = StrAllocString(&pPayload->sczKey, wzId, 0);
49 ExitOnFailure(hr, "Failed to copy key for pseudo bundle payload.");
50
51 hr = StrAllocString(&pPayload->sczFilePath, wzFilePath, 0);
52 ExitOnFailure(hr, "Failed to copy filename for pseudo bundle.");
53
54 hr = StrAllocString(&pPayload->sczSourcePath, wzLocalSource, 0);
55 ExitOnFailure(hr, "Failed to copy local source path for pseudo bundle.");
56
57 if (wzDownloadSource && *wzDownloadSource)
58 {
59 hr = StrAllocString(&pPayload->downloadSource.sczUrl, wzDownloadSource, 0);
60 ExitOnFailure(hr, "Failed to copy download source for pseudo bundle.");
61 }
62
63 if (pbHash)
64 {
65 pPayload->pbHash = static_cast<BYTE*>(MemAlloc(cbHash, FALSE));
66 ExitOnNull(pPayload->pbHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for pseudo bundle payload hash.");
67
68 pPayload->cbHash = cbHash;
69 memcpy_s(pPayload->pbHash, pPayload->cbHash, pbHash, cbHash);
70 }
71
72 pPackage->Exe.fPseudoBundle = TRUE;
73
74 pPackage->type = BURN_PACKAGE_TYPE_EXE;
75 pPackage->fPerMachine = fPerMachine;
76 pPackage->currentState = state;
77 pPackage->fCached = fCached;
78 pPackage->qwInstallSize = qwSize;
79 pPackage->qwSize = qwSize;
80 pPackage->fVital = fVital;
81
82 hr = StrAllocString(&pPackage->sczId, wzId, 0);
83 ExitOnFailure(hr, "Failed to copy key for pseudo bundle.");
84
85 hr = StrAllocString(&pPackage->sczCacheId, wzId, 0);
86 ExitOnFailure(hr, "Failed to copy cache id for pseudo bundle.");
87
88 // If we are a self updating bundle, we don't have to have Install arguments.
89 if (wzInstallArguments)
90 {
91 hr = StrAllocString(&pPackage->Exe.sczInstallArguments, wzInstallArguments, 0);
92 ExitOnFailure(hr, "Failed to copy install arguments for related bundle package");
93 }
94
95 if (sczRelationTypeCommandLineSwitch)
96 {
97 hr = StrAllocConcat(&pPackage->Exe.sczInstallArguments, sczRelationTypeCommandLineSwitch, 0);
98 ExitOnFailure(hr, "Failed to append relation type to install arguments for related bundle package");
99 }
100
101 if (wzRepairArguments)
102 {
103 hr = StrAllocString(&pPackage->Exe.sczRepairArguments, wzRepairArguments, 0);
104 ExitOnFailure(hr, "Failed to copy repair arguments for related bundle package");
105
106 if (sczRelationTypeCommandLineSwitch)
107 {
108 hr = StrAllocConcat(&pPackage->Exe.sczRepairArguments, sczRelationTypeCommandLineSwitch, 0);
109 ExitOnFailure(hr, "Failed to append relation type to repair arguments for related bundle package");
110 }
111
112 pPackage->Exe.fRepairable = TRUE;
113 }
114
115 if (wzUninstallArguments)
116 {
117 hr = StrAllocString(&pPackage->Exe.sczUninstallArguments, wzUninstallArguments, 0);
118 ExitOnFailure(hr, "Failed to copy uninstall arguments for related bundle package");
119
120 if (sczRelationTypeCommandLineSwitch)
121 {
122 hr = StrAllocConcat(&pPackage->Exe.sczUninstallArguments, sczRelationTypeCommandLineSwitch, 0);
123 ExitOnFailure(hr, "Failed to append relation type to uninstall arguments for related bundle package");
124 }
125
126 pPackage->fUninstallable = TRUE;
127 }
128
129 // Only support progress from engines that are compatible (aka: version greater than or equal to last protocol breaking change *and* versions that are older or the same as this engine).
130 pPackage->Exe.protocol = (FILEMAKEVERSION(3, 6, 2221, 0) <= qwEngineVersion && qwEngineVersion <= FILEMAKEVERSION(rmj, rmm, rup, rpr)) ? BURN_EXE_PROTOCOL_TYPE_BURN : BURN_EXE_PROTOCOL_TYPE_NONE;
131
132 // All versions of Burn past v3.9 RTM support suppressing ancestors.
133 pPackage->Exe.fSupportsAncestors = FILEMAKEVERSION(3, 9, 1006, 0) <= qwEngineVersion;
134
135 if (pDependencyProvider)
136 {
137 pPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER), TRUE);
138 ExitOnNull(pPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers.");
139 pPackage->cDependencyProviders = 1;
140
141 pPackage->rgDependencyProviders[0].fImported = pDependencyProvider->fImported;
142
143 hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczKey, pDependencyProvider->sczKey, 0);
144 ExitOnFailure(hr, "Failed to copy key for pseudo bundle.");
145
146 hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczVersion, pDependencyProvider->sczVersion, 0);
147 ExitOnFailure(hr, "Failed to copy version for pseudo bundle.");
148
149 hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczDisplayName, pDependencyProvider->sczDisplayName, 0);
150 ExitOnFailure(hr, "Failed to copy display name for pseudo bundle.");
151 }
152
153LExit:
154 ReleaseStr(sczRelationTypeCommandLineSwitch);
155
156 return hr;
157}
158
159extern "C" HRESULT PseudoBundleInitializePassthrough(
160 __in BURN_PACKAGE* pPassthroughPackage,
161 __in BOOTSTRAPPER_COMMAND* pCommand,
162 __in_z_opt LPCWSTR wzAppendLogPath,
163 __in_z_opt LPCWSTR wzActiveParent,
164 __in_z_opt LPCWSTR wzAncestors,
165 __in BURN_PACKAGE* pPackage
166 )
167{
168 Assert(BURN_PACKAGE_TYPE_EXE == pPackage->type);
169
170 HRESULT hr = S_OK;
171 LPWSTR sczArguments = NULL;
172
173 // Initialize the payloads, and copy the necessary fields.
174 pPassthroughPackage->payloads.rgItems = (BURN_PAYLOAD_GROUP_ITEM*)MemAlloc(sizeof(BURN_PAYLOAD_GROUP_ITEM) * pPackage->payloads.cItems, TRUE);
175 ExitOnNull(pPassthroughPackage->payloads.rgItems, hr, E_OUTOFMEMORY, "Failed to allocate space for burn package payload inside of passthrough bundle.");
176 pPassthroughPackage->payloads.cItems = pPackage->payloads.cItems;
177
178 for (DWORD iPayload = 0; iPayload < pPackage->payloads.cItems; ++iPayload)
179 {
180 pPassthroughPackage->payloads.rgItems[iPayload].pPayload = pPackage->payloads.rgItems[iPayload].pPayload;
181 }
182
183 pPassthroughPackage->Exe.fPseudoBundle = TRUE;
184
185 pPassthroughPackage->fPerMachine = FALSE; // passthrough bundles are always launched per-user.
186 pPassthroughPackage->type = pPackage->type;
187 pPassthroughPackage->currentState = pPackage->currentState;
188 pPassthroughPackage->fCached = pPackage->fCached;
189 pPassthroughPackage->qwInstallSize = pPackage->qwInstallSize;
190 pPassthroughPackage->qwSize = pPackage->qwSize;
191 pPassthroughPackage->fVital = pPackage->fVital;
192
193 hr = StrAllocString(&pPassthroughPackage->sczId, pPackage->sczId, 0);
194 ExitOnFailure(hr, "Failed to copy key for passthrough pseudo bundle.");
195
196 hr = StrAllocString(&pPassthroughPackage->sczCacheId, pPackage->sczCacheId, 0);
197 ExitOnFailure(hr, "Failed to copy cache id for passthrough pseudo bundle.");
198
199 pPassthroughPackage->Exe.protocol = pPackage->Exe.protocol;
200
201 // No matter the operation, we're passing the same command-line. That's what makes
202 // this a passthrough bundle.
203 hr = CoreRecreateCommandLine(&sczArguments, pCommand->action, pCommand->display, pCommand->restart, pCommand->relationType, TRUE, wzActiveParent, wzAncestors, wzAppendLogPath, pCommand->wzCommandLine);
204 ExitOnFailure(hr, "Failed to recreate command-line arguments.");
205
206 hr = StrAllocString(&pPassthroughPackage->Exe.sczInstallArguments, sczArguments, 0);
207 ExitOnFailure(hr, "Failed to copy install arguments for passthrough bundle package");
208
209 hr = StrAllocString(&pPassthroughPackage->Exe.sczRepairArguments, sczArguments, 0);
210 ExitOnFailure(hr, "Failed to copy related arguments for passthrough bundle package");
211
212 pPassthroughPackage->Exe.fRepairable = TRUE;
213
214 hr = StrAllocString(&pPassthroughPackage->Exe.sczUninstallArguments, sczArguments, 0);
215 ExitOnFailure(hr, "Failed to copy uninstall arguments for passthrough bundle package");
216
217 pPassthroughPackage->fUninstallable = TRUE;
218
219 // TODO: consider bringing this back in the near future.
220 //if (pDependencyProvider)
221 //{
222 // pPassthroughPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER), TRUE);
223 // ExitOnNull(pPassthroughPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers.");
224 // pPassthroughPackage->cDependencyProviders = 1;
225
226 // pPassthroughPackage->rgDependencyProviders[0].fImported = pDependencyProvider->fImported;
227
228 // hr = StrAllocString(&pPassthroughPackage->rgDependencyProviders[0].sczKey, pDependencyProvider->sczKey, 0);
229 // ExitOnFailure(hr, "Failed to copy key for pseudo bundle.");
230
231 // hr = StrAllocString(&pPassthroughPackage->rgDependencyProviders[0].sczVersion, pDependencyProvider->sczVersion, 0);
232 // ExitOnFailure(hr, "Failed to copy version for pseudo bundle.");
233
234 // hr = StrAllocString(&pPassthroughPackage->rgDependencyProviders[0].sczDisplayName, pDependencyProvider->sczDisplayName, 0);
235 // ExitOnFailure(hr, "Failed to copy display name for pseudo bundle.");
236 //}
237
238LExit:
239 ReleaseStr(sczArguments);
240 return hr;
241}
diff --git a/src/burn/engine/pseudobundle.h b/src/burn/engine/pseudobundle.h
new file mode 100644
index 00000000..9fb530aa
--- /dev/null
+++ b/src/burn/engine/pseudobundle.h
@@ -0,0 +1,40 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9HRESULT PseudoBundleInitialize(
10 __in DWORD64 qwEngineVersion,
11 __in BURN_PACKAGE* pPackage,
12 __in BOOL fPerMachine,
13 __in_z LPCWSTR wzId,
14 __in BOOTSTRAPPER_RELATION_TYPE relationType,
15 __in BOOTSTRAPPER_PACKAGE_STATE state,
16 __in BOOL fCached,
17 __in_z LPCWSTR wzFilePath,
18 __in_z LPCWSTR wzLocalSource,
19 __in_z_opt LPCWSTR wzDownloadSource,
20 __in DWORD64 qwSize,
21 __in BOOL fVital,
22 __in_z_opt LPCWSTR wzInstallArguments,
23 __in_z_opt LPCWSTR wzRepairArguments,
24 __in_z_opt LPCWSTR wzUninstallArguments,
25 __in_opt BURN_DEPENDENCY_PROVIDER* pDependencyProvider,
26 __in_opt const BYTE* pbHash,
27 __in const DWORD cbHash
28 );
29HRESULT PseudoBundleInitializePassthrough(
30 __in BURN_PACKAGE* pPassthroughPackage,
31 __in BOOTSTRAPPER_COMMAND* pCommand,
32 __in_z_opt LPCWSTR wzAppendLogPath,
33 __in_z_opt LPCWSTR wzActiveParent,
34 __in_z_opt LPCWSTR wzAncestors,
35 __in BURN_PACKAGE* pPackage
36 );
37
38#if defined(__cplusplus)
39}
40#endif
diff --git a/src/burn/engine/registration.cpp b/src/burn/engine/registration.cpp
new file mode 100644
index 00000000..19da543c
--- /dev/null
+++ b/src/burn/engine/registration.cpp
@@ -0,0 +1,1702 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// constants
7
8const LPCWSTR REGISTRY_RUN_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
9const LPCWSTR REGISTRY_RUN_ONCE_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce";
10const LPCWSTR REGISTRY_REBOOT_PENDING_FORMAT = L"%ls.RebootRequired";
11const LPCWSTR REGISTRY_BUNDLE_INSTALLED = L"Installed";
12const LPCWSTR REGISTRY_BUNDLE_DISPLAY_ICON = L"DisplayIcon";
13const LPCWSTR REGISTRY_BUNDLE_DISPLAY_VERSION = L"DisplayVersion";
14const LPCWSTR REGISTRY_BUNDLE_ESTIMATED_SIZE = L"EstimatedSize";
15const LPCWSTR REGISTRY_BUNDLE_PUBLISHER = L"Publisher";
16const LPCWSTR REGISTRY_BUNDLE_HELP_LINK = L"HelpLink";
17const LPCWSTR REGISTRY_BUNDLE_HELP_TELEPHONE = L"HelpTelephone";
18const LPCWSTR REGISTRY_BUNDLE_URL_INFO_ABOUT = L"URLInfoAbout";
19const LPCWSTR REGISTRY_BUNDLE_URL_UPDATE_INFO = L"URLUpdateInfo";
20const LPCWSTR REGISTRY_BUNDLE_PARENT_DISPLAY_NAME = L"ParentDisplayName";
21const LPCWSTR REGISTRY_BUNDLE_PARENT_KEY_NAME = L"ParentKeyName";
22const LPCWSTR REGISTRY_BUNDLE_COMMENTS = L"Comments";
23const LPCWSTR REGISTRY_BUNDLE_CONTACT = L"Contact";
24const LPCWSTR REGISTRY_BUNDLE_NO_MODIFY = L"NoModify";
25const LPCWSTR REGISTRY_BUNDLE_MODIFY_PATH = L"ModifyPath";
26const LPCWSTR REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY = L"NoElevateOnModify";
27const LPCWSTR REGISTRY_BUNDLE_NO_REMOVE = L"NoRemove";
28const LPCWSTR REGISTRY_BUNDLE_SYSTEM_COMPONENT = L"SystemComponent";
29const LPCWSTR REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING = L"QuietUninstallString";
30const LPCWSTR REGISTRY_BUNDLE_UNINSTALL_STRING = L"UninstallString";
31const LPCWSTR REGISTRY_BUNDLE_RESUME_COMMAND_LINE = L"BundleResumeCommandLine";
32const LPCWSTR REGISTRY_BUNDLE_VERSION_MAJOR = L"VersionMajor";
33const LPCWSTR REGISTRY_BUNDLE_VERSION_MINOR = L"VersionMinor";
34const LPCWSTR SWIDTAG_FOLDER = L"swidtag";
35
36// internal function declarations
37
38static HRESULT ParseSoftwareTagsFromXml(
39 __in IXMLDOMNode* pixnRegistrationNode,
40 __out BURN_SOFTWARE_TAG** prgSoftwareTags,
41 __out DWORD* pcSoftwareTags
42 );
43static HRESULT SetPaths(
44 __in BURN_REGISTRATION* pRegistration
45 );
46static HRESULT GetBundleManufacturer(
47 __in BURN_REGISTRATION* pRegistration,
48 __in BURN_VARIABLES* pVariables,
49 __out LPWSTR* psczBundleManufacturer
50 );
51static HRESULT GetBundleName(
52 __in BURN_REGISTRATION* pRegistration,
53 __in BURN_VARIABLES* pVariables,
54 __out LPWSTR* psczBundleName
55 );
56static HRESULT UpdateResumeMode(
57 __in BURN_REGISTRATION* pRegistration,
58 __in HKEY hkRegistration,
59 __in BURN_RESUME_MODE resumeMode,
60 __in BOOL fRestartInitiated
61 );
62static HRESULT ParseRelatedCodes(
63 __in BURN_REGISTRATION* pRegistration,
64 __in IXMLDOMNode* pixnBundle
65 );
66static HRESULT FormatUpdateRegistrationKey(
67 __in BURN_REGISTRATION* pRegistration,
68 __out_z LPWSTR* psczKey
69 );
70static HRESULT WriteSoftwareTags(
71 __in BURN_VARIABLES* pVariables,
72 __in BURN_SOFTWARE_TAGS* pSoftwareTags
73 );
74static HRESULT RemoveSoftwareTags(
75 __in BURN_VARIABLES* pVariables,
76 __in BURN_SOFTWARE_TAGS* pSoftwareTags
77 );
78static HRESULT WriteUpdateRegistration(
79 __in BURN_REGISTRATION* pRegistration,
80 __in BURN_VARIABLES* pVariables
81 );
82static HRESULT RemoveUpdateRegistration(
83 __in BURN_REGISTRATION* pRegistration
84 );
85static HRESULT RegWriteStringVariable(
86 __in HKEY hkKey,
87 __in BURN_VARIABLES* pVariables,
88 __in LPCWSTR wzVariable,
89 __in LPCWSTR wzName
90 );
91static HRESULT UpdateBundleNameRegistration(
92 __in BURN_REGISTRATION* pRegistration,
93 __in BURN_VARIABLES* pVariables,
94 __in HKEY hkRegistration
95 );
96static BOOL IsWuRebootPending();
97static BOOL IsBundleRebootPending(
98 __in BURN_REGISTRATION* pRegistration
99);
100static BOOL IsRegistryRebootPending();
101
102// function definitions
103
104/*******************************************************************
105 RegistrationParseFromXml - Parses registration information from manifest.
106
107*******************************************************************/
108extern "C" HRESULT RegistrationParseFromXml(
109 __in BURN_REGISTRATION* pRegistration,
110 __in IXMLDOMNode* pixnBundle
111 )
112{
113 HRESULT hr = S_OK;
114 IXMLDOMNode* pixnRegistrationNode = NULL;
115 IXMLDOMNode* pixnArpNode = NULL;
116 IXMLDOMNode* pixnUpdateNode = NULL;
117 LPWSTR scz = NULL;
118
119 // select registration node
120 hr = XmlSelectSingleNode(pixnBundle, L"Registration", &pixnRegistrationNode);
121 if (S_FALSE == hr)
122 {
123 hr = E_NOTFOUND;
124 }
125 ExitOnFailure(hr, "Failed to select registration node.");
126
127 // @Id
128 hr = XmlGetAttributeEx(pixnRegistrationNode, L"Id", &pRegistration->sczId);
129 ExitOnFailure(hr, "Failed to get @Id.");
130
131 // @Tag
132 hr = XmlGetAttributeEx(pixnRegistrationNode, L"Tag", &pRegistration->sczTag);
133 ExitOnFailure(hr, "Failed to get @Tag.");
134
135 hr = ParseRelatedCodes(pRegistration, pixnBundle);
136 ExitOnFailure(hr, "Failed to parse related bundles");
137
138 // @Version
139 hr = XmlGetAttributeEx(pixnRegistrationNode, L"Version", &scz);
140 ExitOnFailure(hr, "Failed to get @Version.");
141
142 hr = VerParseVersion(scz, 0, FALSE, &pRegistration->pVersion);
143 ExitOnFailure(hr, "Failed to parse @Version: %ls", scz);
144
145 if (pRegistration->pVersion->fInvalid)
146 {
147 LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz);
148 }
149
150 // @ProviderKey
151 hr = XmlGetAttributeEx(pixnRegistrationNode, L"ProviderKey", &pRegistration->sczProviderKey);
152 ExitOnFailure(hr, "Failed to get @ProviderKey.");
153
154 // @ExecutableName
155 hr = XmlGetAttributeEx(pixnRegistrationNode, L"ExecutableName", &pRegistration->sczExecutableName);
156 ExitOnFailure(hr, "Failed to get @ExecutableName.");
157
158 // @PerMachine
159 hr = XmlGetYesNoAttribute(pixnRegistrationNode, L"PerMachine", &pRegistration->fPerMachine);
160 ExitOnFailure(hr, "Failed to get @PerMachine.");
161
162 // select ARP node
163 hr = XmlSelectSingleNode(pixnRegistrationNode, L"Arp", &pixnArpNode);
164 if (S_FALSE != hr)
165 {
166 ExitOnFailure(hr, "Failed to select ARP node.");
167
168 // @Register
169 hr = XmlGetYesNoAttribute(pixnArpNode, L"Register", &pRegistration->fRegisterArp);
170 ExitOnFailure(hr, "Failed to get @Register.");
171
172 // @DisplayName
173 hr = XmlGetAttributeEx(pixnArpNode, L"DisplayName", &pRegistration->sczDisplayName);
174 if (E_NOTFOUND != hr)
175 {
176 ExitOnFailure(hr, "Failed to get @DisplayName.");
177 }
178
179 // @DisplayVersion
180 hr = XmlGetAttributeEx(pixnArpNode, L"DisplayVersion", &pRegistration->sczDisplayVersion);
181 if (E_NOTFOUND != hr)
182 {
183 ExitOnFailure(hr, "Failed to get @DisplayVersion.");
184 }
185
186 // @Publisher
187 hr = XmlGetAttributeEx(pixnArpNode, L"Publisher", &pRegistration->sczPublisher);
188 if (E_NOTFOUND != hr)
189 {
190 ExitOnFailure(hr, "Failed to get @Publisher.");
191 }
192
193 // @HelpLink
194 hr = XmlGetAttributeEx(pixnArpNode, L"HelpLink", &pRegistration->sczHelpLink);
195 if (E_NOTFOUND != hr)
196 {
197 ExitOnFailure(hr, "Failed to get @HelpLink.");
198 }
199
200 // @HelpTelephone
201 hr = XmlGetAttributeEx(pixnArpNode, L"HelpTelephone", &pRegistration->sczHelpTelephone);
202 if (E_NOTFOUND != hr)
203 {
204 ExitOnFailure(hr, "Failed to get @HelpTelephone.");
205 }
206
207 // @AboutUrl
208 hr = XmlGetAttributeEx(pixnArpNode, L"AboutUrl", &pRegistration->sczAboutUrl);
209 if (E_NOTFOUND != hr)
210 {
211 ExitOnFailure(hr, "Failed to get @AboutUrl.");
212 }
213
214 // @UpdateUrl
215 hr = XmlGetAttributeEx(pixnArpNode, L"UpdateUrl", &pRegistration->sczUpdateUrl);
216 if (E_NOTFOUND != hr)
217 {
218 ExitOnFailure(hr, "Failed to get @UpdateUrl.");
219 }
220
221 // @ParentDisplayName
222 hr = XmlGetAttributeEx(pixnArpNode, L"ParentDisplayName", &pRegistration->sczParentDisplayName);
223 if (E_NOTFOUND != hr)
224 {
225 ExitOnFailure(hr, "Failed to get @ParentDisplayName.");
226 }
227
228 // @Comments
229 hr = XmlGetAttributeEx(pixnArpNode, L"Comments", &pRegistration->sczComments);
230 if (E_NOTFOUND != hr)
231 {
232 ExitOnFailure(hr, "Failed to get @Comments.");
233 }
234
235 // @Contact
236 hr = XmlGetAttributeEx(pixnArpNode, L"Contact", &pRegistration->sczContact);
237 if (E_NOTFOUND != hr)
238 {
239 ExitOnFailure(hr, "Failed to get @Contact.");
240 }
241
242 // @DisableModify
243 hr = XmlGetAttributeEx(pixnArpNode, L"DisableModify", &scz);
244 if (SUCCEEDED(hr))
245 {
246 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"button", -1))
247 {
248 pRegistration->modify = BURN_REGISTRATION_MODIFY_DISABLE_BUTTON;
249 }
250 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"yes", -1))
251 {
252 pRegistration->modify = BURN_REGISTRATION_MODIFY_DISABLE;
253 }
254 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"no", -1))
255 {
256 pRegistration->modify = BURN_REGISTRATION_MODIFY_ENABLED;
257 }
258 else
259 {
260 hr = E_UNEXPECTED;
261 ExitOnRootFailure(hr, "Invalid modify disabled type: %ls", scz);
262 }
263 }
264 else if (E_NOTFOUND == hr)
265 {
266 pRegistration->modify = BURN_REGISTRATION_MODIFY_ENABLED;
267 hr = S_OK;
268 }
269 ExitOnFailure(hr, "Failed to get @DisableModify.");
270
271 // @DisableRemove
272 hr = XmlGetYesNoAttribute(pixnArpNode, L"DisableRemove", &pRegistration->fNoRemove);
273 if (E_NOTFOUND != hr)
274 {
275 ExitOnFailure(hr, "Failed to get @DisableRemove.");
276 pRegistration->fNoRemoveDefined = TRUE;
277 }
278 }
279
280 hr = ParseSoftwareTagsFromXml(pixnRegistrationNode, &pRegistration->softwareTags.rgSoftwareTags, &pRegistration->softwareTags.cSoftwareTags);
281 ExitOnFailure(hr, "Failed to parse software tag.");
282
283 // select Update node
284 hr = XmlSelectSingleNode(pixnRegistrationNode, L"Update", &pixnUpdateNode);
285 if (S_FALSE != hr)
286 {
287 ExitOnFailure(hr, "Failed to select Update node.");
288
289 pRegistration->update.fRegisterUpdate = TRUE;
290
291 // @Manufacturer
292 hr = XmlGetAttributeEx(pixnUpdateNode, L"Manufacturer", &pRegistration->update.sczManufacturer);
293 ExitOnFailure(hr, "Failed to get @Manufacturer.");
294
295 // @Department
296 hr = XmlGetAttributeEx(pixnUpdateNode, L"Department", &pRegistration->update.sczDepartment);
297 if (E_NOTFOUND != hr)
298 {
299 ExitOnFailure(hr, "Failed to get @Department.");
300 }
301
302 // @ProductFamily
303 hr = XmlGetAttributeEx(pixnUpdateNode, L"ProductFamily", &pRegistration->update.sczProductFamily);
304 if (E_NOTFOUND != hr)
305 {
306 ExitOnFailure(hr, "Failed to get @ProductFamily.");
307 }
308
309 // @Name
310 hr = XmlGetAttributeEx(pixnUpdateNode, L"Name", &pRegistration->update.sczName);
311 ExitOnFailure(hr, "Failed to get @Name.");
312
313 // @Classification
314 hr = XmlGetAttributeEx(pixnUpdateNode, L"Classification", &pRegistration->update.sczClassification);
315 ExitOnFailure(hr, "Failed to get @Classification.");
316 }
317
318 hr = SetPaths(pRegistration);
319 ExitOnFailure(hr, "Failed to set registration paths.");
320
321LExit:
322 ReleaseObject(pixnRegistrationNode);
323 ReleaseObject(pixnArpNode);
324 ReleaseObject(pixnUpdateNode);
325 ReleaseStr(scz);
326
327 return hr;
328}
329
330/*******************************************************************
331 RegistrationUninitialize -
332
333*******************************************************************/
334extern "C" void RegistrationUninitialize(
335 __in BURN_REGISTRATION* pRegistration
336 )
337{
338 ReleaseStr(pRegistration->sczId);
339 ReleaseStr(pRegistration->sczTag);
340
341 for (DWORD i = 0; i < pRegistration->cDetectCodes; ++i)
342 {
343 ReleaseStr(pRegistration->rgsczDetectCodes[i]);
344 }
345 ReleaseMem(pRegistration->rgsczDetectCodes);
346
347 for (DWORD i = 0; i < pRegistration->cUpgradeCodes; ++i)
348 {
349 ReleaseStr(pRegistration->rgsczUpgradeCodes[i]);
350 }
351 ReleaseMem(pRegistration->rgsczUpgradeCodes);
352
353 for (DWORD i = 0; i < pRegistration->cAddonCodes; ++i)
354 {
355 ReleaseStr(pRegistration->rgsczAddonCodes[i]);
356 }
357 ReleaseMem(pRegistration->rgsczAddonCodes);
358
359 for (DWORD i = 0; i < pRegistration->cPatchCodes; ++i)
360 {
361 ReleaseStr(pRegistration->rgsczPatchCodes[i]);
362 }
363 ReleaseMem(pRegistration->rgsczPatchCodes);
364
365 ReleaseStr(pRegistration->sczProviderKey);
366 ReleaseStr(pRegistration->sczActiveParent);
367 ReleaseStr(pRegistration->sczExecutableName);
368
369 ReleaseStr(pRegistration->sczRegistrationKey);
370 ReleaseStr(pRegistration->sczCacheExecutablePath);
371 ReleaseStr(pRegistration->sczResumeCommandLine);
372 ReleaseStr(pRegistration->sczStateFile);
373
374 ReleaseStr(pRegistration->sczDisplayName);
375 ReleaseStr(pRegistration->sczDisplayVersion);
376 ReleaseStr(pRegistration->sczPublisher);
377 ReleaseStr(pRegistration->sczHelpLink);
378 ReleaseStr(pRegistration->sczHelpTelephone);
379 ReleaseStr(pRegistration->sczAboutUrl);
380 ReleaseStr(pRegistration->sczUpdateUrl);
381 ReleaseStr(pRegistration->sczParentDisplayName);
382 ReleaseStr(pRegistration->sczComments);
383 ReleaseStr(pRegistration->sczContact);
384
385 ReleaseStr(pRegistration->update.sczManufacturer);
386 ReleaseStr(pRegistration->update.sczDepartment);
387 ReleaseStr(pRegistration->update.sczProductFamily);
388 ReleaseStr(pRegistration->update.sczName);
389 ReleaseStr(pRegistration->update.sczClassification);
390
391 if (pRegistration->softwareTags.rgSoftwareTags)
392 {
393 for (DWORD i = 0; i < pRegistration->softwareTags.cSoftwareTags; ++i)
394 {
395 ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczFilename);
396 ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczRegid);
397 ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczPath);
398 ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczTag);
399 }
400
401 MemFree(pRegistration->softwareTags.rgSoftwareTags);
402 }
403
404 ReleaseStr(pRegistration->sczDetectedProviderKeyBundleId);
405 ReleaseStr(pRegistration->sczAncestors);
406 ReleaseStr(pRegistration->sczBundlePackageAncestors);
407 RelatedBundlesUninitialize(&pRegistration->relatedBundles);
408
409 // clear struct
410 memset(pRegistration, 0, sizeof(BURN_REGISTRATION));
411}
412
413/*******************************************************************
414 RegistrationSetVariables - Initializes bundle variables that map to
415 registration entities.
416
417*******************************************************************/
418extern "C" HRESULT RegistrationSetVariables(
419 __in BURN_REGISTRATION* pRegistration,
420 __in BURN_VARIABLES* pVariables
421 )
422{
423 HRESULT hr = S_OK;
424 LPWSTR sczBundleManufacturer = NULL;
425 LPWSTR sczBundleName = NULL;
426
427 if (pRegistration->fInstalled)
428 {
429 hr = VariableSetNumeric(pVariables, BURN_BUNDLE_INSTALLED, 1, TRUE);
430 ExitOnFailure(hr, "Failed to set the bundle installed built-in variable.");
431 }
432
433 // Ensure the registration bundle name is updated.
434 hr = GetBundleName(pRegistration, pVariables, &sczBundleName);
435 ExitOnFailure(hr, "Failed to initialize bundle name.");
436
437 hr = GetBundleManufacturer(pRegistration, pVariables, &sczBundleName);
438 ExitOnFailure(hr, "Failed to initialize bundle manufacturer.");
439
440 if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent)
441 {
442 hr = VariableSetString(pVariables, BURN_BUNDLE_ACTIVE_PARENT, pRegistration->sczActiveParent, TRUE, FALSE);
443 ExitOnFailure(hr, "Failed to overwrite the bundle active parent built-in variable.");
444 }
445
446 hr = VariableSetString(pVariables, BURN_BUNDLE_PROVIDER_KEY, pRegistration->sczProviderKey, TRUE, FALSE);
447 ExitOnFailure(hr, "Failed to overwrite the bundle provider key built-in variable.");
448
449 hr = VariableSetString(pVariables, BURN_BUNDLE_TAG, pRegistration->sczTag, TRUE, FALSE);
450 ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable.");
451
452 hr = VariableSetVersion(pVariables, BURN_BUNDLE_VERSION, pRegistration->pVersion, TRUE);
453 ExitOnFailure(hr, "Failed to overwrite the bundle version built-in variable.");
454
455 hr = VariableSetNumeric(pVariables, BURN_REBOOT_PENDING, IsBundleRebootPending(pRegistration) || IsWuRebootPending() || IsRegistryRebootPending(), TRUE);
456 ExitOnFailure(hr, "Failed to overwrite the bundle reboot-pending built-in variable.");
457
458LExit:
459 ReleaseStr(sczBundleManufacturer);
460 ReleaseStr(sczBundleName);
461
462 return hr;
463}
464
465extern "C" HRESULT RegistrationDetectInstalled(
466 __in BURN_REGISTRATION* pRegistration
467 )
468{
469 HRESULT hr = S_OK;
470 HKEY hkRegistration = NULL;
471 DWORD dwInstalled = 0;
472
473 pRegistration->fCached = FileExistsEx(pRegistration->sczCacheExecutablePath, NULL);
474
475 // open registration key
476 hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration);
477 if (SUCCEEDED(hr))
478 {
479 hr = RegReadNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, &dwInstalled);
480 }
481
482 // Not finding the key or value is okay.
483 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
484 {
485 hr = S_OK;
486 }
487
488 pRegistration->fInstalled = (1 == dwInstalled);
489
490 ReleaseRegKey(hkRegistration);
491 return hr;
492}
493
494/*******************************************************************
495 RegistrationDetectResumeMode - Detects registration information on the system
496 to determine if a resume is taking place.
497
498*******************************************************************/
499extern "C" HRESULT RegistrationDetectResumeType(
500 __in BURN_REGISTRATION* pRegistration,
501 __out BOOTSTRAPPER_RESUME_TYPE* pResumeType
502 )
503{
504 HRESULT hr = S_OK;
505 HKEY hkRegistration = NULL;
506 DWORD dwResume = 0;
507
508 if (IsBundleRebootPending(pRegistration))
509 {
510 LogId(REPORT_STANDARD, MSG_PENDING_REBOOT_DETECTED, pRegistration->sczRegistrationKey);
511
512 *pResumeType = BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING;
513 ExitFunction1(hr = S_OK);
514 }
515
516 // open registration key
517 hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration);
518 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
519 {
520 *pResumeType = BOOTSTRAPPER_RESUME_TYPE_NONE;
521 ExitFunction1(hr = S_OK);
522 }
523 ExitOnFailure(hr, "Failed to open registration key.");
524
525 // read Resume value
526 hr = RegReadNumber(hkRegistration, L"Resume", &dwResume);
527 if (E_FILENOTFOUND == hr)
528 {
529 *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INVALID;
530 ExitFunction1(hr = S_OK);
531 }
532 ExitOnFailure(hr, "Failed to read Resume value.");
533
534 switch (dwResume)
535 {
536 case BURN_RESUME_MODE_ACTIVE:
537 // a previous run was interrupted
538 *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INTERRUPTED;
539 break;
540
541 case BURN_RESUME_MODE_SUSPEND:
542 *pResumeType = BOOTSTRAPPER_RESUME_TYPE_SUSPEND;
543 break;
544
545 case BURN_RESUME_MODE_ARP:
546 *pResumeType = BOOTSTRAPPER_RESUME_TYPE_ARP;
547 break;
548
549 case BURN_RESUME_MODE_REBOOT_PENDING:
550 // The volatile pending registry doesn't exist (checked above) which means
551 // the system was successfully restarted.
552 *pResumeType = BOOTSTRAPPER_RESUME_TYPE_REBOOT;
553 break;
554
555 default:
556 // the value stored in the registry is not valid
557 *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INVALID;
558 break;
559 }
560
561LExit:
562 ReleaseRegKey(hkRegistration);
563
564 return hr;
565}
566
567/*******************************************************************
568 RegistrationDetectRelatedBundles - finds the bundles with same
569 upgrade/detect/addon/patch codes.
570
571*******************************************************************/
572extern "C" HRESULT RegistrationDetectRelatedBundles(
573 __in BURN_REGISTRATION* pRegistration
574 )
575{
576 HRESULT hr = S_OK;
577
578 hr = RelatedBundlesInitializeForScope(TRUE, pRegistration, &pRegistration->relatedBundles);
579 ExitOnFailure(hr, "Failed to initialize per-machine related bundles.");
580
581 hr = RelatedBundlesInitializeForScope(FALSE, pRegistration, &pRegistration->relatedBundles);
582 ExitOnFailure(hr, "Failed to initialize per-user related bundles.");
583
584LExit:
585 return hr;
586}
587
588/*******************************************************************
589 RegistrationSessionBegin - Registers a run session on the system.
590
591*******************************************************************/
592extern "C" HRESULT RegistrationSessionBegin(
593 __in_z LPCWSTR wzEngineWorkingPath,
594 __in BURN_REGISTRATION* pRegistration,
595 __in BURN_VARIABLES* pVariables,
596 __in DWORD dwRegistrationOptions,
597 __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction,
598 __in DWORD64 qwEstimatedSize
599 )
600{
601 HRESULT hr = S_OK;
602 DWORD dwSize = 0;
603 HKEY hkRegistration = NULL;
604 LPWSTR sczPublisher = NULL;
605
606 LogId(REPORT_VERBOSE, MSG_SESSION_BEGIN, pRegistration->sczRegistrationKey, dwRegistrationOptions, LoggingBoolToString(pRegistration->fDisableResume));
607
608 // Cache bundle executable.
609 if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE)
610 {
611 hr = CacheCompleteBundle(pRegistration->fPerMachine, pRegistration->sczExecutableName, pRegistration->sczId, wzEngineWorkingPath
612#ifdef DEBUG
613 , pRegistration->sczCacheExecutablePath
614#endif
615 );
616 ExitOnFailure(hr, "Failed to cache bundle from path: %ls", wzEngineWorkingPath);
617 }
618
619 // create registration key
620 hr = RegCreate(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration);
621 ExitOnFailure(hr, "Failed to create registration key.");
622
623 // Write any ARP values and software tags.
624 if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION)
625 {
626 // Upgrade information
627 hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, pRegistration->sczCacheExecutablePath);
628 ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH);
629
630 hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, pRegistration->rgsczUpgradeCodes, pRegistration->cUpgradeCodes);
631 ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE);
632
633 hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, pRegistration->rgsczAddonCodes, pRegistration->cAddonCodes);
634 ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE);
635
636 hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, pRegistration->rgsczDetectCodes, pRegistration->cDetectCodes);
637 ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE);
638
639 hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, pRegistration->rgsczPatchCodes, pRegistration->cPatchCodes);
640 ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE);
641
642 hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, pRegistration->pVersion->sczVersion);
643 ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION);
644
645 hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_VERSION_MAJOR, pRegistration->pVersion->dwMajor);
646 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_VERSION_MAJOR);
647
648 hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_VERSION_MINOR, pRegistration->pVersion->dwMinor);
649 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_VERSION_MINOR);
650
651 if (pRegistration->sczProviderKey)
652 {
653 hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY, pRegistration->sczProviderKey);
654 ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY);
655 }
656
657 if (pRegistration->sczTag)
658 {
659 hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_TAG, pRegistration->sczTag);
660 ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_TAG);
661 }
662
663 hr = RegWriteStringFormatted(hkRegistration, BURN_REGISTRATION_REGISTRY_ENGINE_VERSION, L"%hs", szVerMajorMinorBuild);
664 ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_ENGINE_VERSION);
665
666 // DisplayIcon: [path to exe] and ",0" to refer to the first icon in the executable.
667 hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_DISPLAY_ICON, L"%s,0", pRegistration->sczCacheExecutablePath);
668 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_DISPLAY_ICON);
669
670 // update display name
671 hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration);
672 ExitOnFailure(hr, "Failed to update name and publisher.");
673
674 // DisplayVersion: provided by UI
675 if (pRegistration->sczDisplayVersion)
676 {
677 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_DISPLAY_VERSION, pRegistration->sczDisplayVersion);
678 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_DISPLAY_VERSION);
679 }
680
681 // Publisher: provided by UI
682 hr = GetBundleManufacturer(pRegistration, pVariables, &sczPublisher);
683 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PUBLISHER, SUCCEEDED(hr) ? sczPublisher : pRegistration->sczPublisher);
684 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PUBLISHER);
685
686 // HelpLink: provided by UI
687 if (pRegistration->sczHelpLink)
688 {
689 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_HELP_LINK, pRegistration->sczHelpLink);
690 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_HELP_LINK);
691 }
692
693 // HelpTelephone: provided by UI
694 if (pRegistration->sczHelpTelephone)
695 {
696 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_HELP_TELEPHONE, pRegistration->sczHelpTelephone);
697 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_HELP_TELEPHONE);
698 }
699
700 // URLInfoAbout, provided by UI
701 if (pRegistration->sczAboutUrl)
702 {
703 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_URL_INFO_ABOUT, pRegistration->sczAboutUrl);
704 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_URL_INFO_ABOUT);
705 }
706
707 // URLUpdateInfo, provided by UI
708 if (pRegistration->sczUpdateUrl)
709 {
710 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_URL_UPDATE_INFO, pRegistration->sczUpdateUrl);
711 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_URL_UPDATE_INFO);
712 }
713
714 // ParentDisplayName
715 if (pRegistration->sczParentDisplayName)
716 {
717 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PARENT_DISPLAY_NAME, pRegistration->sczParentDisplayName);
718 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PARENT_DISPLAY_NAME);
719
720 // Need to write the ParentKeyName but can be set to anything.
721 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PARENT_KEY_NAME, pRegistration->sczParentDisplayName);
722 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PARENT_KEY_NAME);
723 }
724
725 // Comments, provided by UI
726 if (pRegistration->sczComments)
727 {
728 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_COMMENTS, pRegistration->sczComments);
729 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_COMMENTS);
730 }
731
732 // Contact, provided by UI
733 if (pRegistration->sczContact)
734 {
735 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_CONTACT, pRegistration->sczContact);
736 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_CONTACT);
737 }
738
739 // InstallLocation: provided by UI
740 // TODO: need to figure out what "InstallLocation" means in a chainer. <smile/>
741
742 // NoModify
743 if (BURN_REGISTRATION_MODIFY_DISABLE == pRegistration->modify)
744 {
745 hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_MODIFY, 1);
746 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_MODIFY);
747 }
748 else if (BURN_REGISTRATION_MODIFY_DISABLE_BUTTON != pRegistration->modify) // if support modify (aka: did not disable anything)
749 {
750 // ModifyPath: [path to exe] /modify
751 hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_MODIFY_PATH, L"\"%ls\" /modify", pRegistration->sczCacheExecutablePath);
752 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_MODIFY_PATH);
753
754 // NoElevateOnModify: 1
755 hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY, 1);
756 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY);
757 }
758
759 // NoRemove: should this be allowed?
760 if (pRegistration->fNoRemoveDefined)
761 {
762 hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_REMOVE, (DWORD)pRegistration->fNoRemove);
763 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_REMOVE);
764 }
765
766 // Conditionally hide the ARP entry.
767 if (!pRegistration->fRegisterArp)
768 {
769 hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_SYSTEM_COMPONENT, 1);
770 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_SYSTEM_COMPONENT);
771 }
772
773 // QuietUninstallString: [path to exe] /uninstall /quiet
774 hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING, L"\"%ls\" /uninstall /quiet", pRegistration->sczCacheExecutablePath);
775 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING);
776
777 // UninstallString, [path to exe]
778 // If the modify button is to be disabled, we'll add "/modify" to the uninstall string because the button is "Uninstall/Change". Otherwise,
779 // it's just the "Uninstall" button so we add "/uninstall" to make the program just go away.
780 LPCWSTR wzUninstallParameters = (BURN_REGISTRATION_MODIFY_DISABLE_BUTTON == pRegistration->modify) ? L"/modify" : L" /uninstall";
781 hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_UNINSTALL_STRING, L"\"%ls\" %ls", pRegistration->sczCacheExecutablePath, wzUninstallParameters);
782 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_UNINSTALL_STRING);
783
784 if (pRegistration->softwareTags.cSoftwareTags)
785 {
786 hr = WriteSoftwareTags(pVariables, &pRegistration->softwareTags);
787 ExitOnFailure(hr, "Failed to write software tags.");
788 }
789
790 // Update registration.
791 if (pRegistration->update.fRegisterUpdate)
792 {
793 hr = WriteUpdateRegistration(pRegistration, pVariables);
794 ExitOnFailure(hr, "Failed to write update registration.");
795 }
796 }
797
798 // Update estimated size.
799 if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE)
800 {
801 qwEstimatedSize /= 1024; // Convert bytes to KB
802 if (0 < qwEstimatedSize)
803 {
804 if (DWORD_MAX < qwEstimatedSize)
805 {
806 // ARP doesn't support QWORDs here
807 dwSize = DWORD_MAX;
808 }
809 else
810 {
811 dwSize = static_cast<DWORD>(qwEstimatedSize);
812 }
813
814 hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_ESTIMATED_SIZE, dwSize);
815 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_ESTIMATED_SIZE);
816 }
817 }
818
819 // Register the bundle dependency key.
820 if (BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER == dependencyRegistrationAction)
821 {
822 hr = DependencyRegisterBundle(pRegistration);
823 ExitOnFailure(hr, "Failed to register the bundle dependency key.");
824 }
825
826 // update resume mode
827 hr = UpdateResumeMode(pRegistration, hkRegistration, BURN_RESUME_MODE_ACTIVE, FALSE);
828 ExitOnFailure(hr, "Failed to update resume mode.");
829
830LExit:
831 ReleaseStr(sczPublisher);
832 ReleaseRegKey(hkRegistration);
833
834 return hr;
835}
836
837
838/*******************************************************************
839 RegistrationSessionResume - Resumes a previous run session.
840
841*******************************************************************/
842extern "C" HRESULT RegistrationSessionResume(
843 __in BURN_REGISTRATION* pRegistration,
844 __in BURN_VARIABLES* pVariables
845 )
846{
847 HRESULT hr = S_OK;
848 HKEY hkRegistration = NULL;
849
850 // open registration key
851 hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration);
852 ExitOnFailure(hr, "Failed to open registration key.");
853
854 // update resume mode
855 hr = UpdateResumeMode(pRegistration, hkRegistration, BURN_RESUME_MODE_ACTIVE, FALSE);
856 ExitOnFailure(hr, "Failed to update resume mode.");
857
858 // update display name
859 hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration);
860 ExitOnFailure(hr, "Failed to update name and publisher.");
861
862LExit:
863 ReleaseRegKey(hkRegistration);
864
865 return hr;
866}
867
868
869/*******************************************************************
870 RegistrationSessionEnd - Unregisters a run session from the system.
871
872 *******************************************************************/
873extern "C" HRESULT RegistrationSessionEnd(
874 __in BURN_REGISTRATION* pRegistration,
875 __in BURN_VARIABLES* pVariables,
876 __in BURN_PACKAGES* pPackages,
877 __in BURN_RESUME_MODE resumeMode,
878 __in BOOTSTRAPPER_APPLY_RESTART restart,
879 __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction
880 )
881{
882 HRESULT hr = S_OK;
883 LPWSTR sczRebootRequiredKey = NULL;
884 HKEY hkRebootRequired = NULL;
885 HKEY hkRegistration = NULL;
886
887 LogId(REPORT_STANDARD, MSG_SESSION_END, pRegistration->sczRegistrationKey, LoggingResumeModeToString(resumeMode), LoggingRestartToString(restart), LoggingBoolToString(pRegistration->fDisableResume));
888
889 // If a restart is required for any reason, write a volatile registry key to track of
890 // of that fact until the reboot has taken place.
891 if (BOOTSTRAPPER_APPLY_RESTART_NONE != restart)
892 {
893 // We'll write the volatile registry key right next to the bundle ARP registry key
894 // because that's easy. This is all best effort since the worst case just means in
895 // the rare case the user launches the same install again before taking the restart
896 // the BA won't know a restart was still required.
897 hr = StrAllocFormatted(&sczRebootRequiredKey, REGISTRY_REBOOT_PENDING_FORMAT, pRegistration->sczRegistrationKey);
898 if (SUCCEEDED(hr))
899 {
900 hr = RegCreateEx(pRegistration->hkRoot, sczRebootRequiredKey, KEY_WRITE, TRUE, NULL, &hkRebootRequired, NULL);
901 }
902
903 if (FAILED(hr))
904 {
905 ExitTraceSource(DUTIL_SOURCE_DEFAULT, hr, "Failed to write volatile reboot required registry key.");
906 hr = S_OK;
907 }
908 }
909
910 // If no resume mode, then remove the bundle registration.
911 if (BURN_RESUME_MODE_NONE == resumeMode)
912 {
913 // If we just registered the bundle dependency but something went wrong and caused us to not
914 // keep the bundle registration (like rollback) or we are supposed to unregister the bundle
915 // dependency when unregistering the bundle, do so.
916 if (BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER == dependencyRegistrationAction ||
917 BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER == dependencyRegistrationAction)
918 {
919 // Remove the bundle dependency key.
920 DependencyUnregisterBundle(pRegistration, pPackages);
921 }
922
923 // Delete update registration key.
924 if (pRegistration->update.fRegisterUpdate)
925 {
926 RemoveUpdateRegistration(pRegistration);
927 }
928
929 RemoveSoftwareTags(pVariables, &pRegistration->softwareTags);
930
931 // Delete registration key.
932 hr = RegDelete(pRegistration->hkRoot, pRegistration->sczRegistrationKey, REG_KEY_DEFAULT, FALSE);
933 if (E_FILENOTFOUND != hr)
934 {
935 ExitOnFailure(hr, "Failed to delete registration key: %ls", pRegistration->sczRegistrationKey);
936 }
937
938 CacheRemoveBundle(pRegistration->fPerMachine, pRegistration->sczId);
939 }
940 else // the mode needs to be updated so open the registration key.
941 {
942 // Open registration key.
943 hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration);
944 ExitOnFailure(hr, "Failed to open registration key.");
945 }
946
947 // Update resume mode.
948 hr = UpdateResumeMode(pRegistration, hkRegistration, resumeMode, BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart);
949 ExitOnFailure(hr, "Failed to update resume mode.");
950
951LExit:
952 ReleaseRegKey(hkRegistration);
953 ReleaseRegKey(hkRebootRequired);
954 ReleaseStr(sczRebootRequiredKey);
955
956 return hr;
957}
958
959/*******************************************************************
960 RegistrationSaveState - Saves an engine state BLOB for retreval after a resume.
961
962*******************************************************************/
963extern "C" HRESULT RegistrationSaveState(
964 __in BURN_REGISTRATION* pRegistration,
965 __in_bcount(cbBuffer) BYTE* pbBuffer,
966 __in SIZE_T cbBuffer
967 )
968{
969 HRESULT hr = S_OK;
970
971 // write data to file
972 hr = FileWrite(pRegistration->sczStateFile, FILE_ATTRIBUTE_NORMAL, pbBuffer, cbBuffer, NULL);
973 if (E_PATHNOTFOUND == hr)
974 {
975 // TODO: should we log that the bundle's cache folder was not present so the state file wasn't created either?
976 hr = S_OK;
977 }
978 ExitOnFailure(hr, "Failed to write state to file: %ls", pRegistration->sczStateFile);
979
980LExit:
981 return hr;
982}
983
984/*******************************************************************
985 RegistrationLoadState - Loads a previously stored engine state BLOB.
986
987*******************************************************************/
988extern "C" HRESULT RegistrationLoadState(
989 __in BURN_REGISTRATION* pRegistration,
990 __out_bcount(*pcbBuffer) BYTE** ppbBuffer,
991 __out SIZE_T* pcbBuffer
992 )
993{
994 // read data from file
995 HRESULT hr = FileRead(ppbBuffer, pcbBuffer, pRegistration->sczStateFile);
996 return hr;
997}
998
999/*******************************************************************
1000RegistrationGetResumeCommandLine - Gets the resume command line from the registry
1001
1002*******************************************************************/
1003extern "C" HRESULT RegistrationGetResumeCommandLine(
1004 __in const BURN_REGISTRATION* pRegistration,
1005 __deref_out_z LPWSTR* psczResumeCommandLine
1006 )
1007{
1008 HRESULT hr = S_OK;
1009 HKEY hkRegistration = NULL;
1010
1011 // open registration key
1012 hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration);
1013 if (SUCCEEDED(hr))
1014 {
1015 hr = RegReadString(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE, psczResumeCommandLine);
1016 }
1017
1018 // Not finding the key or value is okay.
1019 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
1020 {
1021 hr = S_OK;
1022 }
1023
1024 ReleaseRegKey(hkRegistration);
1025
1026 return hr;
1027}
1028
1029
1030// internal helper functions
1031
1032static HRESULT ParseSoftwareTagsFromXml(
1033 __in IXMLDOMNode* pixnRegistrationNode,
1034 __out BURN_SOFTWARE_TAG** prgSoftwareTags,
1035 __out DWORD* pcSoftwareTags
1036 )
1037{
1038 HRESULT hr = S_OK;
1039 IXMLDOMNodeList* pixnNodes = NULL;
1040 IXMLDOMNode* pixnNode = NULL;
1041 DWORD cNodes = 0;
1042
1043 BURN_SOFTWARE_TAG* pSoftwareTags = NULL;
1044 BSTR bstrTagXml = NULL;
1045
1046 // select tag nodes
1047 hr = XmlSelectNodes(pixnRegistrationNode, L"SoftwareTag", &pixnNodes);
1048 ExitOnFailure(hr, "Failed to select software tag nodes.");
1049
1050 // get tag node count
1051 hr = pixnNodes->get_length((long*)&cNodes);
1052 ExitOnFailure(hr, "Failed to get software tag count.");
1053
1054 if (cNodes)
1055 {
1056 pSoftwareTags = (BURN_SOFTWARE_TAG*)MemAlloc(sizeof(BURN_SOFTWARE_TAG) * cNodes, TRUE);
1057 ExitOnNull(pSoftwareTags, hr, E_OUTOFMEMORY, "Failed to allocate memory for software tag structs.");
1058
1059 for (DWORD i = 0; i < cNodes; ++i)
1060 {
1061 BURN_SOFTWARE_TAG* pSoftwareTag = &pSoftwareTags[i];
1062
1063 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
1064 ExitOnFailure(hr, "Failed to get next node.");
1065
1066 hr = XmlGetAttributeEx(pixnNode, L"Filename", &pSoftwareTag->sczFilename);
1067 ExitOnFailure(hr, "Failed to get @Filename.");
1068
1069 hr = XmlGetAttributeEx(pixnNode, L"Regid", &pSoftwareTag->sczRegid);
1070 ExitOnFailure(hr, "Failed to get @Regid.");
1071
1072 hr = XmlGetAttributeEx(pixnNode, L"Path", &pSoftwareTag->sczPath);
1073 ExitOnFailure(hr, "Failed to get @Path.");
1074
1075 hr = XmlGetText(pixnNode, &bstrTagXml);
1076 ExitOnFailure(hr, "Failed to get SoftwareTag text.");
1077
1078 hr = StrAnsiAllocString(&pSoftwareTag->sczTag, bstrTagXml, 0, CP_UTF8);
1079 ExitOnFailure(hr, "Failed to convert SoftwareTag text to UTF-8");
1080
1081 // prepare next iteration
1082 ReleaseNullBSTR(bstrTagXml);
1083 ReleaseNullObject(pixnNode);
1084 }
1085 }
1086
1087 *pcSoftwareTags = cNodes;
1088 *prgSoftwareTags = pSoftwareTags;
1089 pSoftwareTags = NULL;
1090
1091 hr = S_OK;
1092
1093LExit:
1094 ReleaseBSTR(bstrTagXml);
1095 ReleaseObject(pixnNode);
1096 ReleaseObject(pixnNodes);
1097 ReleaseMem(pSoftwareTags);
1098
1099 return hr;
1100}
1101
1102static HRESULT SetPaths(
1103 __in BURN_REGISTRATION* pRegistration
1104 )
1105{
1106 HRESULT hr = S_OK;
1107 LPWSTR sczCacheDirectory = NULL;
1108
1109 // save registration key root
1110 pRegistration->hkRoot = pRegistration->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1111
1112 // build uninstall registry key path
1113 hr = StrAllocFormatted(&pRegistration->sczRegistrationKey, L"%s\\%s", BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, pRegistration->sczId);
1114 ExitOnFailure(hr, "Failed to build uninstall registry key path.");
1115
1116 // build cache directory
1117 hr = CacheGetCompletedPath(pRegistration->fPerMachine, pRegistration->sczId, &sczCacheDirectory);
1118 ExitOnFailure(hr, "Failed to build cache directory.");
1119
1120 // build cached executable path
1121 hr = PathConcat(sczCacheDirectory, pRegistration->sczExecutableName, &pRegistration->sczCacheExecutablePath);
1122 ExitOnFailure(hr, "Failed to build cached executable path.");
1123
1124 // build state file path
1125 hr = StrAllocFormatted(&pRegistration->sczStateFile, L"%s\\state.rsm", sczCacheDirectory);
1126 ExitOnFailure(hr, "Failed to build state file path.");
1127
1128LExit:
1129 ReleaseStr(sczCacheDirectory);
1130 return hr;
1131}
1132
1133static HRESULT GetBundleManufacturer(
1134 __in BURN_REGISTRATION* pRegistration,
1135 __in BURN_VARIABLES* pVariables,
1136 __out LPWSTR* psczBundleManufacturer
1137 )
1138{
1139 HRESULT hr = S_OK;
1140
1141 hr = VariableGetString(pVariables, BURN_BUNDLE_MANUFACTURER, psczBundleManufacturer);
1142 if (E_NOTFOUND == hr)
1143 {
1144 hr = VariableSetString(pVariables, BURN_BUNDLE_MANUFACTURER, pRegistration->sczPublisher, FALSE, FALSE);
1145 ExitOnFailure(hr, "Failed to set bundle manufacturer.");
1146
1147 hr = StrAllocString(psczBundleManufacturer, pRegistration->sczPublisher, 0);
1148 }
1149 ExitOnFailure(hr, "Failed to get bundle manufacturer.");
1150
1151LExit:
1152 return hr;
1153}
1154
1155static HRESULT GetBundleName(
1156 __in BURN_REGISTRATION* pRegistration,
1157 __in BURN_VARIABLES* pVariables,
1158 __out LPWSTR* psczBundleName
1159 )
1160{
1161 HRESULT hr = S_OK;
1162
1163 hr = VariableGetString(pVariables, BURN_BUNDLE_NAME, psczBundleName);
1164 if (E_NOTFOUND == hr)
1165 {
1166 hr = VariableSetString(pVariables, BURN_BUNDLE_NAME, pRegistration->sczDisplayName, FALSE, FALSE);
1167 ExitOnFailure(hr, "Failed to set bundle name.");
1168
1169 hr = StrAllocString(psczBundleName, pRegistration->sczDisplayName, 0);
1170 }
1171 ExitOnFailure(hr, "Failed to get bundle name.");
1172
1173LExit:
1174 return hr;
1175}
1176
1177static HRESULT UpdateResumeMode(
1178 __in BURN_REGISTRATION* pRegistration,
1179 __in HKEY hkRegistration,
1180 __in BURN_RESUME_MODE resumeMode,
1181 __in BOOL fRestartInitiated
1182 )
1183{
1184 HRESULT hr = S_OK;
1185 DWORD er = ERROR_SUCCESS;
1186 HKEY hkRebootRequired = NULL;
1187 HKEY hkRun = NULL;
1188 LPWSTR sczResumeCommandLine = NULL;
1189 LPCWSTR sczResumeKey = REGISTRY_RUN_ONCE_KEY;
1190
1191 LogId(REPORT_STANDARD, MSG_SESSION_UPDATE, pRegistration->sczRegistrationKey, LoggingResumeModeToString(resumeMode), LoggingBoolToString(fRestartInitiated), LoggingBoolToString(pRegistration->fDisableResume));
1192
1193 // write resume information
1194 if (hkRegistration)
1195 {
1196 // write Resume value
1197 hr = RegWriteNumber(hkRegistration, L"Resume", (DWORD)resumeMode);
1198 ExitOnFailure(hr, "Failed to write Resume value.");
1199
1200 // Write the Installed value *only* when the mode is ARP. This will tell us
1201 // that the bundle considers itself "installed" on the machine. Note that we
1202 // never change the value to "0" after that. The bundle will be considered
1203 // "uninstalled" when all of the registration is removed.
1204 if (BURN_RESUME_MODE_ARP == resumeMode)
1205 {
1206 // Write Installed value.
1207 hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, 1);
1208 ExitOnFailure(hr, "Failed to write Installed value.");
1209 }
1210 }
1211
1212 // If the engine is active write the run key so we resume if there is an unexpected
1213 // power loss. Also, if a restart was initiated in the middle of the chain then
1214 // ensure the run key exists (it should since going active would have written it).
1215 // Do not write the run key when embedded since the containing bundle
1216 // is expected to detect for and restart the embedded bundle.
1217 if ((BURN_RESUME_MODE_ACTIVE == resumeMode || fRestartInitiated) && !pRegistration->fDisableResume)
1218 {
1219 // append RunOnce switch
1220 hr = StrAllocFormatted(&sczResumeCommandLine, L"\"%ls\" /%ls", pRegistration->sczCacheExecutablePath, BURN_COMMANDLINE_SWITCH_RUNONCE);
1221 ExitOnFailure(hr, "Failed to format resume command line for RunOnce.");
1222
1223 // write run key
1224 hr = RegCreate(pRegistration->hkRoot, sczResumeKey, KEY_WRITE, &hkRun);
1225 ExitOnFailure(hr, "Failed to create run key.");
1226
1227 hr = RegWriteString(hkRun, pRegistration->sczId, sczResumeCommandLine);
1228 ExitOnFailure(hr, "Failed to write run key value.");
1229
1230 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE, pRegistration->sczResumeCommandLine);
1231 ExitOnFailure(hr, "Failed to write resume command line value.");
1232 }
1233 else // delete run key value
1234 {
1235 hr = RegOpen(pRegistration->hkRoot, sczResumeKey, KEY_WRITE, &hkRun);
1236 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
1237 {
1238 hr = S_OK;
1239 }
1240 else
1241 {
1242 ExitOnWin32Error(er, hr, "Failed to open run key.");
1243
1244 er = ::RegDeleteValueW(hkRun, pRegistration->sczId);
1245 if (ERROR_FILE_NOT_FOUND == er)
1246 {
1247 er = ERROR_SUCCESS;
1248 }
1249 ExitOnWin32Error(er, hr, "Failed to delete run key value.");
1250 }
1251
1252 if (hkRegistration)
1253 {
1254 er = ::RegDeleteValueW(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE);
1255 if (ERROR_FILE_NOT_FOUND == er)
1256 {
1257 er = ERROR_SUCCESS;
1258 }
1259 ExitOnWin32Error(er, hr, "Failed to delete resume command line value.");
1260 }
1261 }
1262
1263LExit:
1264 ReleaseStr(sczResumeCommandLine);
1265 ReleaseRegKey(hkRebootRequired);
1266 ReleaseRegKey(hkRun);
1267
1268 return hr;
1269}
1270
1271static HRESULT ParseRelatedCodes(
1272 __in BURN_REGISTRATION* pRegistration,
1273 __in IXMLDOMNode* pixnBundle
1274 )
1275{
1276 HRESULT hr = S_OK;
1277 IXMLDOMNodeList* pixnNodes = NULL;
1278 IXMLDOMNode* pixnElement = NULL;
1279 LPWSTR sczAction = NULL;
1280 LPWSTR sczId = NULL;
1281 DWORD cElements = 0;
1282
1283 hr = XmlSelectNodes(pixnBundle, L"RelatedBundle", &pixnNodes);
1284 ExitOnFailure(hr, "Failed to get RelatedBundle nodes");
1285
1286 hr = pixnNodes->get_length((long*)&cElements);
1287 ExitOnFailure(hr, "Failed to get RelatedBundle element count.");
1288
1289 for (DWORD i = 0; i < cElements; ++i)
1290 {
1291 hr = XmlNextElement(pixnNodes, &pixnElement, NULL);
1292 ExitOnFailure(hr, "Failed to get next RelatedBundle element.");
1293
1294 hr = XmlGetAttributeEx(pixnElement, L"Action", &sczAction);
1295 ExitOnFailure(hr, "Failed to get @Action.");
1296
1297 hr = XmlGetAttributeEx(pixnElement, L"Id", &sczId);
1298 ExitOnFailure(hr, "Failed to get @Id.");
1299
1300 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Detect", -1))
1301 {
1302 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes + 1, sizeof(LPWSTR), 5);
1303 ExitOnFailure(hr, "Failed to resize Detect code array in registration");
1304
1305 pRegistration->rgsczDetectCodes[pRegistration->cDetectCodes] = sczId;
1306 sczId = NULL;
1307 ++pRegistration->cDetectCodes;
1308 }
1309 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Upgrade", -1))
1310 {
1311 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes + 1, sizeof(LPWSTR), 5);
1312 ExitOnFailure(hr, "Failed to resize Upgrade code array in registration");
1313
1314 pRegistration->rgsczUpgradeCodes[pRegistration->cUpgradeCodes] = sczId;
1315 sczId = NULL;
1316 ++pRegistration->cUpgradeCodes;
1317 }
1318 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Addon", -1))
1319 {
1320 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes + 1, sizeof(LPWSTR), 5);
1321 ExitOnFailure(hr, "Failed to resize Addon code array in registration");
1322
1323 pRegistration->rgsczAddonCodes[pRegistration->cAddonCodes] = sczId;
1324 sczId = NULL;
1325 ++pRegistration->cAddonCodes;
1326 }
1327 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Patch", -1))
1328 {
1329 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes + 1, sizeof(LPWSTR), 5);
1330 ExitOnFailure(hr, "Failed to resize Patch code array in registration");
1331
1332 pRegistration->rgsczPatchCodes[pRegistration->cPatchCodes] = sczId;
1333 sczId = NULL;
1334 ++pRegistration->cPatchCodes;
1335 }
1336 else
1337 {
1338 hr = E_INVALIDARG;
1339 ExitOnFailure(hr, "Invalid value for @Action: %ls", sczAction);
1340 }
1341 }
1342
1343LExit:
1344 ReleaseObject(pixnNodes);
1345 ReleaseObject(pixnElement);
1346 ReleaseStr(sczAction);
1347 ReleaseStr(sczId);
1348
1349 return hr;
1350}
1351
1352static HRESULT FormatUpdateRegistrationKey(
1353 __in BURN_REGISTRATION* pRegistration,
1354 __out_z LPWSTR* psczKey
1355 )
1356{
1357 HRESULT hr = S_OK;
1358 LPWSTR sczKey = NULL;
1359
1360 hr = StrAllocFormatted(&sczKey, L"SOFTWARE\\%ls\\Updates\\", pRegistration->update.sczManufacturer);
1361 ExitOnFailure(hr, "Failed to format the key path for update registration.");
1362
1363 if (pRegistration->update.sczProductFamily)
1364 {
1365 hr = StrAllocFormatted(&sczKey, L"%ls%ls\\", sczKey, pRegistration->update.sczProductFamily);
1366 ExitOnFailure(hr, "Failed to format the key path for update registration.");
1367 }
1368
1369 hr = StrAllocConcat(&sczKey, pRegistration->update.sczName, 0);
1370 ExitOnFailure(hr, "Failed to format the key path for update registration.");
1371
1372 *psczKey = sczKey;
1373 sczKey = NULL;
1374
1375LExit:
1376 ReleaseStr(sczKey);
1377
1378 return hr;
1379}
1380
1381static HRESULT WriteSoftwareTags(
1382 __in BURN_VARIABLES* pVariables,
1383 __in BURN_SOFTWARE_TAGS* pSoftwareTags
1384 )
1385{
1386 HRESULT hr = S_OK;
1387 LPWSTR sczRootFolder = NULL;
1388 LPWSTR sczTagFolder = NULL;
1389 LPWSTR sczPath = NULL;
1390
1391 for (DWORD iTag = 0; iTag < pSoftwareTags->cSoftwareTags; ++iTag)
1392 {
1393 BURN_SOFTWARE_TAG* pSoftwareTag = pSoftwareTags->rgSoftwareTags + iTag;
1394
1395 hr = VariableFormatString(pVariables, pSoftwareTag->sczPath, &sczRootFolder, NULL);
1396 ExitOnFailure(hr, "Failed to format tag folder path.");
1397
1398 hr = PathConcat(sczRootFolder, SWIDTAG_FOLDER, &sczTagFolder);
1399 ExitOnFailure(hr, "Failed to allocate regid folder path.");
1400
1401 hr = PathConcat(sczTagFolder, pSoftwareTag->sczFilename, &sczPath);
1402 ExitOnFailure(hr, "Failed to allocate regid file path.");
1403
1404 hr = DirEnsureExists(sczTagFolder, NULL);
1405 ExitOnFailure(hr, "Failed to create regid folder: %ls", sczTagFolder);
1406
1407 hr = FileWrite(sczPath, FILE_ATTRIBUTE_NORMAL, reinterpret_cast<LPBYTE>(pSoftwareTag->sczTag), lstrlenA(pSoftwareTag->sczTag), NULL);
1408 ExitOnFailure(hr, "Failed to write tag xml to file: %ls", sczPath);
1409 }
1410
1411LExit:
1412 ReleaseStr(sczPath);
1413 ReleaseStr(sczTagFolder);
1414 ReleaseStr(sczRootFolder);
1415
1416 return hr;
1417}
1418
1419static HRESULT RemoveSoftwareTags(
1420 __in BURN_VARIABLES* pVariables,
1421 __in BURN_SOFTWARE_TAGS* pSoftwareTags
1422 )
1423{
1424 HRESULT hr = S_OK;
1425 LPWSTR sczRootFolder = NULL;
1426 LPWSTR sczTagFolder = NULL;
1427 LPWSTR sczPath = NULL;
1428
1429 for (DWORD iTag = 0; iTag < pSoftwareTags->cSoftwareTags; ++iTag)
1430 {
1431 BURN_SOFTWARE_TAG* pSoftwareTag = pSoftwareTags->rgSoftwareTags + iTag;
1432
1433 hr = VariableFormatString(pVariables, pSoftwareTag->sczPath, &sczRootFolder, NULL);
1434 ExitOnFailure(hr, "Failed to format tag folder path.");
1435
1436 hr = PathConcat(sczRootFolder, SWIDTAG_FOLDER, &sczTagFolder);
1437 ExitOnFailure(hr, "Failed to allocate regid folder path.");
1438
1439 hr = PathConcat(sczTagFolder, pSoftwareTag->sczFilename, &sczPath);
1440 ExitOnFailure(hr, "Failed to allocate regid file path.");
1441
1442 // Best effort to delete the software tag file and the regid folder.
1443 FileEnsureDelete(sczPath);
1444
1445 DirDeleteEmptyDirectoriesToRoot(sczTagFolder, 0);
1446 }
1447
1448LExit:
1449 ReleaseStr(sczPath);
1450 ReleaseStr(sczTagFolder);
1451 ReleaseStr(sczRootFolder);
1452
1453 return hr;
1454}
1455
1456static HRESULT WriteUpdateRegistration(
1457 __in BURN_REGISTRATION* pRegistration,
1458 __in BURN_VARIABLES* pVariables
1459 )
1460{
1461 HRESULT hr = S_OK;
1462 LPWSTR sczKey = NULL;
1463 HKEY hkKey = NULL;
1464
1465 hr = FormatUpdateRegistrationKey(pRegistration, &sczKey);
1466 ExitOnFailure(hr, "Failed to get the formatted key path for update registration.");
1467
1468 hr = RegCreate(pRegistration->hkRoot, sczKey, KEY_WRITE, &hkKey);
1469 ExitOnFailure(hr, "Failed to create the key for update registration.");
1470
1471 hr = RegWriteString(hkKey, L"ThisVersionInstalled", L"Y");
1472 ExitOnFailure(hr, "Failed to write %ls value.", L"ThisVersionInstalled");
1473
1474 hr = RegWriteString(hkKey, L"PackageName", pRegistration->sczDisplayName);
1475 ExitOnFailure(hr, "Failed to write %ls value.", L"PackageName");
1476
1477 hr = RegWriteString(hkKey, L"PackageVersion", pRegistration->sczDisplayVersion);
1478 ExitOnFailure(hr, "Failed to write %ls value.", L"PackageVersion");
1479
1480 hr = RegWriteString(hkKey, L"Publisher", pRegistration->sczPublisher);
1481 ExitOnFailure(hr, "Failed to write %ls value.", L"Publisher");
1482
1483 if (pRegistration->update.sczDepartment)
1484 {
1485 hr = RegWriteString(hkKey, L"PublishingGroup", pRegistration->update.sczDepartment);
1486 ExitOnFailure(hr, "Failed to write %ls value.", L"PublishingGroup");
1487 }
1488
1489 hr = RegWriteString(hkKey, L"ReleaseType", pRegistration->update.sczClassification);
1490 ExitOnFailure(hr, "Failed to write %ls value.", L"ReleaseType");
1491
1492 hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_LOGONUSER, L"InstalledBy");
1493 ExitOnFailure(hr, "Failed to write %ls value.", L"InstalledBy");
1494
1495 hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_DATE, L"InstalledDate");
1496 ExitOnFailure(hr, "Failed to write %ls value.", L"InstalledDate");
1497
1498 hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_INSTALLERNAME, L"InstallerName");
1499 ExitOnFailure(hr, "Failed to write %ls value.", L"InstallerName");
1500
1501 hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_INSTALLERVERSION, L"InstallerVersion");
1502 ExitOnFailure(hr, "Failed to write %ls value.", L"InstallerVersion");
1503
1504LExit:
1505 ReleaseRegKey(hkKey);
1506 ReleaseStr(sczKey);
1507
1508 return hr;
1509}
1510
1511static HRESULT RemoveUpdateRegistration(
1512 __in BURN_REGISTRATION* pRegistration
1513 )
1514{
1515 HRESULT hr = S_OK;
1516 LPWSTR sczKey = NULL;
1517 LPWSTR sczPackageVersion = NULL;
1518 HKEY hkKey = NULL;
1519 BOOL fDeleteRegKey = TRUE;
1520
1521 hr = FormatUpdateRegistrationKey(pRegistration, &sczKey);
1522 ExitOnFailure(hr, "Failed to format key for update registration.");
1523
1524 // Only delete if the uninstalling bundle's PackageVersion is the same as the
1525 // PackageVersion in the update registration key.
1526 // This is to support build to build upgrades
1527 hr = RegOpen(pRegistration->hkRoot, sczKey, KEY_QUERY_VALUE, &hkKey);
1528 if (SUCCEEDED(hr))
1529 {
1530 hr = RegReadString(hkKey, L"PackageVersion", &sczPackageVersion);
1531 if (SUCCEEDED(hr))
1532 {
1533 if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczPackageVersion, -1, pRegistration->sczDisplayVersion, -1))
1534 {
1535 fDeleteRegKey = FALSE;
1536 }
1537 }
1538 ReleaseRegKey(hkKey);
1539 }
1540
1541 // Unable to open the key or read the value is okay.
1542 hr = S_OK;
1543
1544 if (fDeleteRegKey)
1545 {
1546 hr = RegDelete(pRegistration->hkRoot, sczKey, REG_KEY_DEFAULT, FALSE);
1547 if (E_FILENOTFOUND != hr)
1548 {
1549 ExitOnFailure(hr, "Failed to remove update registration key: %ls", sczKey);
1550 }
1551 }
1552
1553LExit:
1554 ReleaseStr(sczPackageVersion);
1555 ReleaseStr(sczKey);
1556
1557 return hr;
1558}
1559
1560static HRESULT RegWriteStringVariable(
1561 __in HKEY hk,
1562 __in BURN_VARIABLES* pVariables,
1563 __in LPCWSTR wzVariable,
1564 __in LPCWSTR wzName
1565 )
1566{
1567 HRESULT hr = S_OK;
1568 LPWSTR sczValue = NULL;
1569
1570 hr = VariableGetString(pVariables, wzVariable, &sczValue);
1571 ExitOnFailure(hr, "Failed to get the %ls variable.", wzVariable);
1572
1573 hr = RegWriteString(hk, wzName, sczValue);
1574 ExitOnFailure(hr, "Failed to write %ls value.", wzName);
1575
1576LExit:
1577 StrSecureZeroFreeString(sczValue);
1578
1579 return hr;
1580}
1581
1582static HRESULT UpdateBundleNameRegistration(
1583 __in BURN_REGISTRATION* pRegistration,
1584 __in BURN_VARIABLES* pVariables,
1585 __in HKEY hkRegistration
1586 )
1587{
1588 HRESULT hr = S_OK;
1589 LPWSTR sczDisplayName = NULL;
1590
1591 // DisplayName: provided by UI
1592 hr = GetBundleName(pRegistration, pVariables, &sczDisplayName);
1593 hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME, SUCCEEDED(hr) ? sczDisplayName : pRegistration->sczDisplayName);
1594 ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME);
1595
1596LExit:
1597 ReleaseStr(sczDisplayName);
1598
1599 return hr;
1600}
1601
1602static BOOL IsWuRebootPending()
1603{
1604 HRESULT hr = S_OK;
1605 BOOL fRebootPending = FALSE;
1606
1607 // Do a best effort to ask WU if a reboot is required. If anything goes
1608 // wrong then let's pretend a reboot is not required.
1609 hr = ::CoInitialize(NULL);
1610 if (SUCCEEDED(hr) || RPC_E_CHANGED_MODE == hr)
1611 {
1612 hr = WuaRestartRequired(&fRebootPending);
1613 if (FAILED(hr))
1614 {
1615 fRebootPending = FALSE;
1616 }
1617
1618 ::CoUninitialize();
1619 }
1620
1621 return fRebootPending;
1622}
1623
1624static BOOL IsBundleRebootPending(BURN_REGISTRATION* pRegistration)
1625{
1626 HRESULT hr = S_OK;
1627 LPWSTR sczRebootRequiredKey = NULL;
1628 HKEY hkRebootRequired = NULL;
1629 BOOL fBundleRebootPending = FALSE;
1630
1631 // Check to see if a restart is pending for this bundle.
1632 hr = StrAllocFormatted(&sczRebootRequiredKey, REGISTRY_REBOOT_PENDING_FORMAT, pRegistration->sczRegistrationKey);
1633 ExitOnFailure(hr, "Failed to format pending restart registry key to read.");
1634
1635 hr = RegOpen(pRegistration->hkRoot, sczRebootRequiredKey, KEY_QUERY_VALUE, &hkRebootRequired);
1636 fBundleRebootPending = SUCCEEDED(hr);
1637
1638LExit:
1639 ReleaseStr(sczRebootRequiredKey);
1640 ReleaseRegKey(hkRebootRequired);
1641
1642 return fBundleRebootPending;
1643}
1644
1645static BOOL IsRegistryRebootPending()
1646{
1647 HRESULT hr = S_OK;
1648 DWORD dwValue;
1649 HKEY hk = NULL;
1650 BOOL fRebootPending = FALSE;
1651
1652 hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\ServerManager", L"CurrentRebootAttempts", TRUE, &dwValue);
1653 fRebootPending = SUCCEEDED(hr) && 0 < dwValue;
1654
1655 if (!fRebootPending)
1656 {
1657 hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Updates", L"UpdateExeVolatile", TRUE, &dwValue);
1658 fRebootPending = SUCCEEDED(hr) && 0 < dwValue;
1659
1660 if (!fRebootPending)
1661 {
1662 fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootPending", NULL, TRUE);
1663
1664 if (!fRebootPending)
1665 {
1666 fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootInProgress", NULL, TRUE);
1667
1668 if (!fRebootPending)
1669 {
1670 hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update", L"AUState", TRUE, &dwValue);
1671 fRebootPending = SUCCEEDED(hr) && 8 == dwValue;
1672
1673 if (!fRebootPending)
1674 {
1675 fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager", L"PendingFileRenameOperations", TRUE);
1676
1677 if (!fRebootPending)
1678 {
1679 fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager", L"PendingFileRenameOperations2", TRUE);
1680
1681 if (!fRebootPending)
1682 {
1683 hr = RegOpen(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\FileRenameOperations", KEY_READ | KEY_WOW64_64KEY, &hk);
1684 if (SUCCEEDED(hr))
1685 {
1686 DWORD cSubKeys = 0;
1687 DWORD cValues = 0;
1688 hr = RegQueryKey(hk, &cSubKeys, &cValues);
1689 fRebootPending = SUCCEEDED(hr) && (0 < cSubKeys || 0 < cValues);
1690 }
1691 }
1692 }
1693 }
1694 }
1695 }
1696 }
1697 }
1698
1699 ReleaseRegKey(hk);
1700
1701 return fRebootPending;
1702}
diff --git a/src/burn/engine/registration.h b/src/burn/engine/registration.h
new file mode 100644
index 00000000..6d8a6d2a
--- /dev/null
+++ b/src/burn/engine/registration.h
@@ -0,0 +1,225 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10enum BURN_MODE;
11enum BURN_DEPENDENCY_REGISTRATION_ACTION;
12struct _BURN_LOGGING;
13typedef _BURN_LOGGING BURN_LOGGING;
14
15// constants
16
17const LPCWSTR BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
18const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH = L"BundleCachePath";
19const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE = L"BundleAddonCode";
20const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE = L"BundleDetectCode";
21const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE = L"BundlePatchCode";
22const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode";
23const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME = L"DisplayName";
24const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION = L"BundleVersion";
25const LPCWSTR BURN_REGISTRATION_REGISTRY_ENGINE_VERSION = L"EngineVersion";
26const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey";
27const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_TAG = L"BundleTag";
28
29enum BURN_RESUME_MODE
30{
31 BURN_RESUME_MODE_NONE,
32 BURN_RESUME_MODE_ACTIVE,
33 BURN_RESUME_MODE_SUSPEND,
34 BURN_RESUME_MODE_ARP,
35 BURN_RESUME_MODE_REBOOT_PENDING,
36};
37
38enum BURN_REGISTRATION_MODIFY_TYPE
39{
40 BURN_REGISTRATION_MODIFY_ENABLED,
41 BURN_REGISTRATION_MODIFY_DISABLE,
42 BURN_REGISTRATION_MODIFY_DISABLE_BUTTON,
43};
44
45
46// structs
47
48typedef struct _BURN_UPDATE_REGISTRATION
49{
50 BOOL fRegisterUpdate;
51 LPWSTR sczManufacturer;
52 LPWSTR sczDepartment;
53 LPWSTR sczProductFamily;
54 LPWSTR sczName;
55 LPWSTR sczClassification;
56} BURN_UPDATE_REGISTRATION;
57
58typedef struct _BURN_RELATED_BUNDLE
59{
60 BOOTSTRAPPER_RELATION_TYPE relationType;
61 BOOL fForwardCompatible;
62
63 VERUTIL_VERSION* pVersion;
64 LPWSTR sczTag;
65 BOOL fPlannable;
66
67 BURN_PACKAGE package;
68} BURN_RELATED_BUNDLE;
69
70typedef struct _BURN_RELATED_BUNDLES
71{
72 BURN_RELATED_BUNDLE* rgRelatedBundles;
73 DWORD cRelatedBundles;
74} BURN_RELATED_BUNDLES;
75
76typedef struct _BURN_SOFTWARE_TAG
77{
78 LPWSTR sczFilename;
79 LPWSTR sczRegid;
80 LPWSTR sczPath;
81 LPSTR sczTag;
82} BURN_SOFTWARE_TAG;
83
84typedef struct _BURN_SOFTWARE_TAGS
85{
86 BURN_SOFTWARE_TAG* rgSoftwareTags;
87 DWORD cSoftwareTags;
88} BURN_SOFTWARE_TAGS;
89
90typedef struct _BURN_REGISTRATION
91{
92 BOOL fPerMachine;
93 BOOL fRegisterArp;
94 BOOL fDisableResume;
95 BOOL fCached;
96 BOOL fInstalled;
97 LPWSTR sczId;
98 LPWSTR sczTag;
99
100 LPWSTR *rgsczDetectCodes;
101 DWORD cDetectCodes;
102
103 LPWSTR *rgsczUpgradeCodes;
104 DWORD cUpgradeCodes;
105
106 LPWSTR *rgsczAddonCodes;
107 DWORD cAddonCodes;
108
109 LPWSTR *rgsczPatchCodes;
110 DWORD cPatchCodes;
111
112 VERUTIL_VERSION* pVersion;
113 LPWSTR sczActiveParent;
114 LPWSTR sczProviderKey;
115 LPWSTR sczExecutableName;
116
117 // paths
118 HKEY hkRoot;
119 LPWSTR sczRegistrationKey;
120 LPWSTR sczCacheExecutablePath;
121 LPWSTR sczResumeCommandLine;
122 LPWSTR sczStateFile;
123
124 // ARP registration
125 LPWSTR sczDisplayName;
126 LPWSTR sczDisplayVersion;
127 LPWSTR sczPublisher;
128 LPWSTR sczHelpLink;
129 LPWSTR sczHelpTelephone;
130 LPWSTR sczAboutUrl;
131 LPWSTR sczUpdateUrl;
132 LPWSTR sczParentDisplayName;
133 LPWSTR sczComments;
134 //LPWSTR sczReadme; // TODO: this would be a file path
135 LPWSTR sczContact;
136 //DWORD64 qwEstimatedSize; // TODO: size should come from disk cost calculation
137 BURN_REGISTRATION_MODIFY_TYPE modify;
138 BOOL fNoRemoveDefined;
139 BOOL fNoRemove;
140
141 BURN_SOFTWARE_TAGS softwareTags;
142
143 // Update registration
144 BURN_UPDATE_REGISTRATION update;
145
146 BURN_RELATED_BUNDLES relatedBundles; // Only valid after detect.
147 DEPENDENCY* rgIgnoredDependencies; // Only valid after detect.
148 UINT cIgnoredDependencies; // Only valid after detect.
149 DEPENDENCY* rgDependents; // Only valid after detect.
150 UINT cDependents; // Only valid after detect.
151 BOOL fIgnoreAllDependents; // Only valid after detect.
152 LPCWSTR wzSelfDependent; // Only valid after detect.
153 BOOL fSelfRegisteredAsDependent; // Only valid after detect.
154 BOOL fParentRegisteredAsDependent; // Only valid after detect.
155 BOOL fForwardCompatibleBundleExists; // Only valid after detect.
156 BOOL fEligibleForCleanup; // Only valid after detect.
157
158 LPWSTR sczDetectedProviderKeyBundleId;
159 LPWSTR sczAncestors;
160 LPWSTR sczBundlePackageAncestors;
161} BURN_REGISTRATION;
162
163
164// functions
165
166HRESULT RegistrationParseFromXml(
167 __in BURN_REGISTRATION* pRegistration,
168 __in IXMLDOMNode* pixnBundle
169 );
170void RegistrationUninitialize(
171 __in BURN_REGISTRATION* pRegistration
172 );
173HRESULT RegistrationSetVariables(
174 __in BURN_REGISTRATION* pRegistration,
175 __in BURN_VARIABLES* pVariables
176 );
177HRESULT RegistrationDetectInstalled(
178 __in BURN_REGISTRATION* pRegistration
179 );
180HRESULT RegistrationDetectResumeType(
181 __in BURN_REGISTRATION* pRegistration,
182 __out BOOTSTRAPPER_RESUME_TYPE* pResumeType
183 );
184HRESULT RegistrationDetectRelatedBundles(
185 __in BURN_REGISTRATION* pRegistration
186 );
187HRESULT RegistrationSessionBegin(
188 __in_z LPCWSTR wzEngineWorkingPath,
189 __in BURN_REGISTRATION* pRegistration,
190 __in BURN_VARIABLES* pVariables,
191 __in DWORD dwRegistrationOptions,
192 __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction,
193 __in DWORD64 qwEstimatedSize
194 );
195HRESULT RegistrationSessionResume(
196 __in BURN_REGISTRATION* pRegistration,
197 __in BURN_VARIABLES* pVariables
198 );
199HRESULT RegistrationSessionEnd(
200 __in BURN_REGISTRATION* pRegistration,
201 __in BURN_VARIABLES* pVariables,
202 __in BURN_PACKAGES* pPackages,
203 __in BURN_RESUME_MODE resumeMode,
204 __in BOOTSTRAPPER_APPLY_RESTART restart,
205 __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction
206 );
207HRESULT RegistrationSaveState(
208 __in BURN_REGISTRATION* pRegistration,
209 __in_bcount_opt(cbBuffer) BYTE* pbBuffer,
210 __in_opt SIZE_T cbBuffer
211 );
212HRESULT RegistrationLoadState(
213 __in BURN_REGISTRATION* pRegistration,
214 __out_bcount(*pcbBuffer) BYTE** ppbBuffer,
215 __out SIZE_T* pcbBuffer
216 );
217HRESULT RegistrationGetResumeCommandLine(
218 __in const BURN_REGISTRATION* pRegistration,
219 __deref_out_z LPWSTR* psczResumeCommandLine
220 );
221
222
223#if defined(__cplusplus)
224}
225#endif
diff --git a/src/burn/engine/relatedbundle.cpp b/src/burn/engine/relatedbundle.cpp
new file mode 100644
index 00000000..d3c856a6
--- /dev/null
+++ b/src/burn/engine/relatedbundle.cpp
@@ -0,0 +1,483 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5// internal function declarations
6
7static HRESULT LoadIfRelatedBundle(
8 __in BOOL fPerMachine,
9 __in HKEY hkUninstallKey,
10 __in_z LPCWSTR sczRelatedBundleId,
11 __in BURN_REGISTRATION* pRegistration,
12 __in BURN_RELATED_BUNDLES* pRelatedBundles
13 );
14static HRESULT DetermineRelationType(
15 __in HKEY hkBundleId,
16 __in BURN_REGISTRATION* pRegistration,
17 __out BOOTSTRAPPER_RELATION_TYPE* pRelationType
18 );
19static HRESULT LoadRelatedBundleFromKey(
20 __in_z LPCWSTR wzRelatedBundleId,
21 __in HKEY hkBundleId,
22 __in BOOL fPerMachine,
23 __in BOOTSTRAPPER_RELATION_TYPE relationType,
24 __inout BURN_RELATED_BUNDLE *pRelatedBundle
25 );
26
27
28// function definitions
29
30extern "C" HRESULT RelatedBundlesInitializeForScope(
31 __in BOOL fPerMachine,
32 __in BURN_REGISTRATION* pRegistration,
33 __in BURN_RELATED_BUNDLES* pRelatedBundles
34 )
35{
36 HRESULT hr = S_OK;
37 HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
38 HKEY hkUninstallKey = NULL;
39 LPWSTR sczRelatedBundleId = NULL;
40
41 hr = RegOpen(hkRoot, BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, &hkUninstallKey);
42 if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
43 {
44 ExitFunction1(hr = S_OK);
45 }
46 ExitOnFailure(hr, "Failed to open uninstall registry key.");
47
48 for (DWORD dwIndex = 0; /* exit via break below */; ++dwIndex)
49 {
50 hr = RegKeyEnum(hkUninstallKey, dwIndex, &sczRelatedBundleId);
51 if (E_NOMOREITEMS == hr)
52 {
53 hr = S_OK;
54 break;
55 }
56 ExitOnFailure(hr, "Failed to enumerate uninstall key for related bundles.");
57
58 // If we did not find our bundle id, try to load the subkey as a related bundle.
59 if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczRelatedBundleId, -1, pRegistration->sczId, -1))
60 {
61 // Ignore failures here since we'll often find products that aren't actually
62 // related bundles (or even bundles at all).
63 HRESULT hrRelatedBundle = LoadIfRelatedBundle(fPerMachine, hkUninstallKey, sczRelatedBundleId, pRegistration, pRelatedBundles);
64 UNREFERENCED_PARAMETER(hrRelatedBundle);
65 }
66 }
67
68LExit:
69 ReleaseStr(sczRelatedBundleId);
70 ReleaseRegKey(hkUninstallKey);
71
72 return hr;
73}
74
75extern "C" void RelatedBundlesUninitialize(
76 __in BURN_RELATED_BUNDLES* pRelatedBundles
77 )
78{
79 if (pRelatedBundles->rgRelatedBundles)
80 {
81 for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i)
82 {
83 BURN_PACKAGE* pPackage = &pRelatedBundles->rgRelatedBundles[i].package;
84
85 for (DWORD j = 0; j < pPackage->payloads.cItems; ++j)
86 {
87 PayloadUninitialize(pPackage->payloads.rgItems[j].pPayload);
88 }
89
90 PackageUninitialize(pPackage);
91 ReleaseStr(pRelatedBundles->rgRelatedBundles[i].sczTag);
92 }
93
94 MemFree(pRelatedBundles->rgRelatedBundles);
95 }
96
97 memset(pRelatedBundles, 0, sizeof(BURN_RELATED_BUNDLES));
98}
99
100
101// internal helper functions
102
103static HRESULT LoadIfRelatedBundle(
104 __in BOOL fPerMachine,
105 __in HKEY hkUninstallKey,
106 __in_z LPCWSTR sczRelatedBundleId,
107 __in BURN_REGISTRATION* pRegistration,
108 __in BURN_RELATED_BUNDLES* pRelatedBundles
109 )
110{
111 HRESULT hr = S_OK;
112 HKEY hkBundleId = NULL;
113 BOOTSTRAPPER_RELATION_TYPE relationType = BOOTSTRAPPER_RELATION_NONE;
114
115 hr = RegOpen(hkUninstallKey, sczRelatedBundleId, KEY_READ, &hkBundleId);
116 ExitOnFailure(hr, "Failed to open uninstall key for potential related bundle: %ls", sczRelatedBundleId);
117
118 hr = DetermineRelationType(hkBundleId, pRegistration, &relationType);
119 if (FAILED(hr) || BOOTSTRAPPER_RELATION_NONE == relationType)
120 {
121 // Must not be a related bundle.
122 hr = E_NOTFOUND;
123 }
124 else // load the related bundle.
125 {
126 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5);
127 ExitOnFailure(hr, "Failed to ensure there is space for related bundles.");
128
129 BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles;
130
131 hr = LoadRelatedBundleFromKey(sczRelatedBundleId, hkBundleId, fPerMachine, relationType, pRelatedBundle);
132 ExitOnFailure(hr, "Failed to initialize package from related bundle id: %ls", sczRelatedBundleId);
133
134 ++pRelatedBundles->cRelatedBundles;
135 }
136
137LExit:
138 ReleaseRegKey(hkBundleId);
139
140 return hr;
141}
142
143static HRESULT DetermineRelationType(
144 __in HKEY hkBundleId,
145 __in BURN_REGISTRATION* pRegistration,
146 __out BOOTSTRAPPER_RELATION_TYPE* pRelationType
147 )
148{
149 HRESULT hr = S_OK;
150 LPWSTR* rgsczUpgradeCodes = NULL;
151 DWORD cUpgradeCodes = 0;
152 STRINGDICT_HANDLE sdUpgradeCodes = NULL;
153 LPWSTR* rgsczAddonCodes = NULL;
154 DWORD cAddonCodes = 0;
155 STRINGDICT_HANDLE sdAddonCodes = NULL;
156 LPWSTR* rgsczDetectCodes = NULL;
157 DWORD cDetectCodes = 0;
158 STRINGDICT_HANDLE sdDetectCodes = NULL;
159 LPWSTR* rgsczPatchCodes = NULL;
160 DWORD cPatchCodes = 0;
161 STRINGDICT_HANDLE sdPatchCodes = NULL;
162
163 *pRelationType = BOOTSTRAPPER_RELATION_NONE;
164
165 // All remaining operations should treat all related bundles as non-vital.
166 hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes, &cUpgradeCodes);
167 if (HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE) == hr)
168 {
169 TraceError(hr, "Failed to read upgrade codes as REG_MULTI_SZ. Trying again as REG_SZ in case of older bundles.");
170
171 rgsczUpgradeCodes = reinterpret_cast<LPWSTR*>(MemAlloc(sizeof(LPWSTR), TRUE));
172 ExitOnNull(rgsczUpgradeCodes, hr, E_OUTOFMEMORY, "Failed to allocate list for a single upgrade code from older bundle.");
173
174 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes[0]);
175 if (SUCCEEDED(hr))
176 {
177 cUpgradeCodes = 1;
178 }
179 }
180
181 // Compare upgrade codes.
182 if (SUCCEEDED(hr))
183 {
184 hr = DictCreateStringListFromArray(&sdUpgradeCodes, rgsczUpgradeCodes, cUpgradeCodes, DICT_FLAG_CASEINSENSITIVE);
185 ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "upgrade codes");
186
187 // Upgrade relationship: when their upgrade codes match our upgrade codes.
188 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes);
189 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
190 {
191 hr = S_OK;
192 }
193 else
194 {
195 ExitOnFailure(hr, "Failed to do array search for upgrade code match.");
196
197 *pRelationType = BOOTSTRAPPER_RELATION_UPGRADE;
198 ExitFunction();
199 }
200
201 // Detect relationship: when their upgrade codes match our detect codes.
202 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
203 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
204 {
205 hr = S_OK;
206 }
207 else
208 {
209 ExitOnFailure(hr, "Failed to do array search for detect code match.");
210
211 *pRelationType = BOOTSTRAPPER_RELATION_DETECT;
212 ExitFunction();
213 }
214
215 // Dependent relationship: when their upgrade codes match our addon codes.
216 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes);
217 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
218 {
219 hr = S_OK;
220 }
221 else
222 {
223 ExitOnFailure(hr, "Failed to do array search for addon code match.");
224
225 *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
226 ExitFunction();
227 }
228
229 // Dependent relationship: when their upgrade codes match our patch codes.
230 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes);
231 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
232 {
233 hr = S_OK;
234 }
235 else
236 {
237 ExitOnFailure(hr, "Failed to do array search for addon code match.");
238
239 *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
240 ExitFunction();
241 }
242
243 ReleaseNullDict(sdUpgradeCodes);
244 ReleaseNullStrArray(rgsczUpgradeCodes, cUpgradeCodes);
245 }
246
247 // Compare addon codes.
248 hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, &rgsczAddonCodes, &cAddonCodes);
249 if (SUCCEEDED(hr))
250 {
251 hr = DictCreateStringListFromArray(&sdAddonCodes, rgsczAddonCodes, cAddonCodes, DICT_FLAG_CASEINSENSITIVE);
252 ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "addon codes");
253
254 // Addon relationship: when their addon codes match our detect codes.
255 hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
256 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
257 {
258 hr = S_OK;
259 }
260 else
261 {
262 ExitOnFailure(hr, "Failed to do array search for addon code match.");
263
264 *pRelationType = BOOTSTRAPPER_RELATION_ADDON;
265 ExitFunction();
266 }
267
268 // Addon relationship: when their addon codes match our upgrade codes.
269 hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes);
270 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
271 {
272 hr = S_OK;
273 }
274 else
275 {
276 ExitOnFailure(hr, "Failed to do array search for addon code match.");
277
278 *pRelationType = BOOTSTRAPPER_RELATION_ADDON;
279 ExitFunction();
280 }
281
282 ReleaseNullDict(sdAddonCodes);
283 ReleaseNullStrArray(rgsczAddonCodes, cAddonCodes);
284 }
285
286 // Compare patch codes.
287 hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, &rgsczPatchCodes, &cPatchCodes);
288 if (SUCCEEDED(hr))
289 {
290 hr = DictCreateStringListFromArray(&sdPatchCodes, rgsczPatchCodes, cPatchCodes, DICT_FLAG_CASEINSENSITIVE);
291 ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "patch codes");
292
293 // Patch relationship: when their patch codes match our detect codes.
294 hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
295 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
296 {
297 hr = S_OK;
298 }
299 else
300 {
301 ExitOnFailure(hr, "Failed to do array search for patch code match.");
302
303 *pRelationType = BOOTSTRAPPER_RELATION_PATCH;
304 ExitFunction();
305 }
306
307 // Patch relationship: when their patch codes match our upgrade codes.
308 hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes);
309 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
310 {
311 hr = S_OK;
312 }
313 else
314 {
315 ExitOnFailure(hr, "Failed to do array search for patch code match.");
316
317 *pRelationType = BOOTSTRAPPER_RELATION_PATCH;
318 ExitFunction();
319 }
320
321 ReleaseNullDict(sdPatchCodes);
322 ReleaseNullStrArray(rgsczPatchCodes, cPatchCodes);
323 }
324
325 // Compare detect codes.
326 hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, &rgsczDetectCodes, &cDetectCodes);
327 if (SUCCEEDED(hr))
328 {
329 hr = DictCreateStringListFromArray(&sdDetectCodes, rgsczDetectCodes, cDetectCodes, DICT_FLAG_CASEINSENSITIVE);
330 ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "detect codes");
331
332 // Detect relationship: when their detect codes match our detect codes.
333 hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
334 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
335 {
336 hr = S_OK;
337 }
338 else
339 {
340 ExitOnFailure(hr, "Failed to do array search for detect code match.");
341
342 *pRelationType = BOOTSTRAPPER_RELATION_DETECT;
343 ExitFunction();
344 }
345
346 // Dependent relationship: when their detect codes match our addon codes.
347 hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes);
348 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
349 {
350 hr = S_OK;
351 }
352 else
353 {
354 ExitOnFailure(hr, "Failed to do array search for addon code match.");
355
356 *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
357 ExitFunction();
358 }
359
360 // Dependent relationship: when their detect codes match our patch codes.
361 hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes);
362 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
363 {
364 hr = S_OK;
365 }
366 else
367 {
368 ExitOnFailure(hr, "Failed to do array search for addon code match.");
369
370 *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
371 ExitFunction();
372 }
373
374 ReleaseNullDict(sdDetectCodes);
375 ReleaseNullStrArray(rgsczDetectCodes, cDetectCodes);
376 }
377
378LExit:
379 if (SUCCEEDED(hr) && BOOTSTRAPPER_RELATION_NONE == *pRelationType)
380 {
381 hr = E_NOTFOUND;
382 }
383
384 ReleaseDict(sdUpgradeCodes);
385 ReleaseStrArray(rgsczUpgradeCodes, cUpgradeCodes);
386 ReleaseDict(sdAddonCodes);
387 ReleaseStrArray(rgsczAddonCodes, cAddonCodes);
388 ReleaseDict(sdDetectCodes);
389 ReleaseStrArray(rgsczDetectCodes, cDetectCodes);
390 ReleaseDict(sdPatchCodes);
391 ReleaseStrArray(rgsczPatchCodes, cPatchCodes);
392
393 return hr;
394}
395
396static HRESULT LoadRelatedBundleFromKey(
397 __in_z LPCWSTR wzRelatedBundleId,
398 __in HKEY hkBundleId,
399 __in BOOL fPerMachine,
400 __in BOOTSTRAPPER_RELATION_TYPE relationType,
401 __inout BURN_RELATED_BUNDLE* pRelatedBundle
402 )
403{
404 HRESULT hr = S_OK;
405 DWORD64 qwEngineVersion = 0;
406 LPWSTR sczBundleVersion = NULL;
407 LPWSTR sczCachePath = NULL;
408 BOOL fCached = FALSE;
409 DWORD64 qwFileSize = 0;
410 BURN_DEPENDENCY_PROVIDER dependencyProvider = { };
411
412 hr = RegReadVersion(hkBundleId, BURN_REGISTRATION_REGISTRY_ENGINE_VERSION, &qwEngineVersion);
413 if (FAILED(hr))
414 {
415 qwEngineVersion = 0;
416 hr = S_OK;
417 }
418
419 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, &sczBundleVersion);
420 ExitOnFailure(hr, "Failed to read version from registry for bundle: %ls", wzRelatedBundleId);
421
422 hr = VerParseVersion(sczBundleVersion, 0, FALSE, &pRelatedBundle->pVersion);
423 ExitOnFailure(hr, "Failed to parse pseudo bundle version: %ls", sczBundleVersion);
424
425 if (pRelatedBundle->pVersion->fInvalid)
426 {
427 LogId(REPORT_WARNING, MSG_RELATED_PACKAGE_INVALID_VERSION, wzRelatedBundleId, sczBundleVersion);
428 }
429
430 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, &sczCachePath);
431 ExitOnFailure(hr, "Failed to read cache path from registry for bundle: %ls", wzRelatedBundleId);
432
433 if (FileExistsEx(sczCachePath, NULL))
434 {
435 fCached = TRUE;
436 }
437 else
438 {
439 LogId(REPORT_STANDARD, MSG_DETECT_RELATED_BUNDLE_NOT_CACHED, wzRelatedBundleId, sczCachePath);
440 }
441
442 pRelatedBundle->fPlannable = fCached;
443
444 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY, &dependencyProvider.sczKey);
445 if (E_FILENOTFOUND != hr)
446 {
447 ExitOnFailure(hr, "Failed to read provider key from registry for bundle: %ls", wzRelatedBundleId);
448
449 dependencyProvider.fImported = TRUE;
450
451 hr = StrAllocString(&dependencyProvider.sczVersion, pRelatedBundle->pVersion->sczVersion, 0);
452 ExitOnFailure(hr, "Failed to copy version for bundle: %ls", wzRelatedBundleId);
453
454 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME, &dependencyProvider.sczDisplayName);
455 if (E_FILENOTFOUND != hr)
456 {
457 ExitOnFailure(hr, "Failed to copy display name for bundle: %ls", wzRelatedBundleId);
458 }
459 }
460
461 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_TAG, &pRelatedBundle->sczTag);
462 if (E_FILENOTFOUND == hr)
463 {
464 hr = S_OK;
465 }
466 ExitOnFailure(hr, "Failed to read tag from registry for bundle: %ls", wzRelatedBundleId);
467
468 pRelatedBundle->relationType = relationType;
469
470 hr = PseudoBundleInitialize(qwEngineVersion, &pRelatedBundle->package, fPerMachine, wzRelatedBundleId, pRelatedBundle->relationType,
471 BOOTSTRAPPER_PACKAGE_STATE_PRESENT, fCached, sczCachePath, sczCachePath, NULL, qwFileSize, FALSE,
472 L"-quiet", L"-repair -quiet", L"-uninstall -quiet",
473 (dependencyProvider.sczKey && *dependencyProvider.sczKey) ? &dependencyProvider : NULL,
474 NULL, 0);
475 ExitOnFailure(hr, "Failed to initialize related bundle to represent bundle: %ls", wzRelatedBundleId);
476
477LExit:
478 DependencyUninitializeProvider(&dependencyProvider);
479 ReleaseStr(sczCachePath);
480 ReleaseStr(sczBundleVersion);
481
482 return hr;
483}
diff --git a/src/burn/engine/relatedbundle.h b/src/burn/engine/relatedbundle.h
new file mode 100644
index 00000000..01691c25
--- /dev/null
+++ b/src/burn/engine/relatedbundle.h
@@ -0,0 +1,20 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9HRESULT RelatedBundlesInitializeForScope(
10 __in BOOL fPerMachine,
11 __in BURN_REGISTRATION* pRegistration,
12 __in BURN_RELATED_BUNDLES* pRelatedBundles
13 );
14void RelatedBundlesUninitialize(
15 __in BURN_RELATED_BUNDLES* pRelatedBundles
16 );
17
18#if defined(__cplusplus)
19}
20#endif
diff --git a/src/burn/engine/search.cpp b/src/burn/engine/search.cpp
new file mode 100644
index 00000000..6d5f8d49
--- /dev/null
+++ b/src/burn/engine/search.cpp
@@ -0,0 +1,1303 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// internal function declarations
7
8static HRESULT DirectorySearchExists(
9 __in BURN_SEARCH* pSearch,
10 __in BURN_VARIABLES* pVariables
11 );
12static HRESULT DirectorySearchPath(
13 __in BURN_SEARCH* pSearch,
14 __in BURN_VARIABLES* pVariables
15 );
16static HRESULT FileSearchExists(
17 __in BURN_SEARCH* pSearch,
18 __in BURN_VARIABLES* pVariables
19 );
20static HRESULT FileSearchVersion(
21 __in BURN_SEARCH* pSearch,
22 __in BURN_VARIABLES* pVariables
23 );
24static HRESULT FileSearchPath(
25 __in BURN_SEARCH* pSearch,
26 __in BURN_VARIABLES* pVariables
27 );
28static HRESULT RegistrySearchExists(
29 __in BURN_SEARCH* pSearch,
30 __in BURN_VARIABLES* pVariables
31 );
32static HRESULT RegistrySearchValue(
33 __in BURN_SEARCH* pSearch,
34 __in BURN_VARIABLES* pVariables
35 );
36static HRESULT MsiComponentSearch(
37 __in BURN_SEARCH* pSearch,
38 __in BURN_VARIABLES* pVariables
39 );
40static HRESULT MsiProductSearch(
41 __in BURN_SEARCH* pSearch,
42 __in BURN_VARIABLES* pVariables
43 );
44static HRESULT MsiFeatureSearch(
45 __in BURN_SEARCH* pSearch,
46 __in BURN_VARIABLES* pVariables
47 );
48static HRESULT PerformExtensionSearch(
49 __in BURN_SEARCH* pSearch
50 );
51static HRESULT PerformSetVariable(
52 __in BURN_SEARCH* pSearch,
53 __in BURN_VARIABLES* pVariables
54);
55
56
57// function definitions
58
59extern "C" HRESULT SearchesParseFromXml(
60 __in BURN_SEARCHES* pSearches,
61 __in BURN_EXTENSIONS* pBurnExtensions,
62 __in IXMLDOMNode* pixnBundle
63 )
64{
65 HRESULT hr = S_OK;
66 IXMLDOMNodeList* pixnNodes = NULL;
67 IXMLDOMNode* pixnNode = NULL;
68 DWORD cNodes = 0;
69 BSTR bstrNodeName = NULL;
70 LPWSTR scz = NULL;
71 BURN_VARIANT_TYPE valueType = BURN_VARIANT_TYPE_NONE;
72
73 // select search nodes
74 hr = XmlSelectNodes(pixnBundle, L"DirectorySearch|FileSearch|RegistrySearch|MsiComponentSearch|MsiProductSearch|MsiFeatureSearch|ExtensionSearch|SetVariable", &pixnNodes);
75 ExitOnFailure(hr, "Failed to select search nodes.");
76
77 // get search node count
78 hr = pixnNodes->get_length((long*)&cNodes);
79 ExitOnFailure(hr, "Failed to get search node count.");
80
81 if (!cNodes)
82 {
83 ExitFunction();
84 }
85
86 // allocate memory for searches
87 pSearches->rgSearches = (BURN_SEARCH*)MemAlloc(sizeof(BURN_SEARCH) * cNodes, TRUE);
88 ExitOnNull(pSearches->rgSearches, hr, E_OUTOFMEMORY, "Failed to allocate memory for search structs.");
89
90 pSearches->cSearches = cNodes;
91
92 // parse search elements
93 for (DWORD i = 0; i < cNodes; ++i)
94 {
95 BURN_SEARCH* pSearch = &pSearches->rgSearches[i];
96
97 hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName);
98 ExitOnFailure(hr, "Failed to get next node.");
99
100 // @Id
101 hr = XmlGetAttributeEx(pixnNode, L"Id", &pSearch->sczKey);
102 ExitOnFailure(hr, "Failed to get @Id.");
103
104 // @Variable
105 hr = XmlGetAttributeEx(pixnNode, L"Variable", &pSearch->sczVariable);
106 ExitOnFailure(hr, "Failed to get @Variable.");
107
108 // @Condition
109 hr = XmlGetAttributeEx(pixnNode, L"Condition", &pSearch->sczCondition);
110 if (E_NOTFOUND != hr)
111 {
112 ExitOnFailure(hr, "Failed to get @Condition.");
113 }
114
115 // read type specific attributes
116 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"DirectorySearch", -1))
117 {
118 pSearch->Type = BURN_SEARCH_TYPE_DIRECTORY;
119
120 // @Path
121 hr = XmlGetAttributeEx(pixnNode, L"Path", &pSearch->DirectorySearch.sczPath);
122 ExitOnFailure(hr, "Failed to get @Path.");
123
124 // @Type
125 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
126 ExitOnFailure(hr, "Failed to get @Type.");
127
128 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1))
129 {
130 pSearch->DirectorySearch.Type = BURN_DIRECTORY_SEARCH_TYPE_EXISTS;
131 }
132 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"path", -1))
133 {
134 pSearch->DirectorySearch.Type = BURN_DIRECTORY_SEARCH_TYPE_PATH;
135 }
136 else
137 {
138 hr = E_INVALIDARG;
139 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
140 }
141 }
142 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"FileSearch", -1))
143 {
144 pSearch->Type = BURN_SEARCH_TYPE_FILE;
145
146 // @Path
147 hr = XmlGetAttributeEx(pixnNode, L"Path", &pSearch->FileSearch.sczPath);
148 ExitOnFailure(hr, "Failed to get @Path.");
149
150 // @Type
151 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
152 ExitOnFailure(hr, "Failed to get @Type.");
153
154 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1))
155 {
156 pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_EXISTS;
157 }
158 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1))
159 {
160 pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_VERSION;
161 }
162 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"path", -1))
163 {
164 pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_PATH;
165 }
166 else
167 {
168 hr = E_INVALIDARG;
169 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
170 }
171 }
172 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"RegistrySearch", -1))
173 {
174 pSearch->Type = BURN_SEARCH_TYPE_REGISTRY;
175
176 // @Root
177 hr = XmlGetAttributeEx(pixnNode, L"Root", &scz);
178 ExitOnFailure(hr, "Failed to get @Root.");
179
180 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKCR", -1))
181 {
182 pSearch->RegistrySearch.hRoot = HKEY_CLASSES_ROOT;
183 }
184 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKCU", -1))
185 {
186 pSearch->RegistrySearch.hRoot = HKEY_CURRENT_USER;
187 }
188 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKLM", -1))
189 {
190 pSearch->RegistrySearch.hRoot = HKEY_LOCAL_MACHINE;
191 }
192 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKU", -1))
193 {
194 pSearch->RegistrySearch.hRoot = HKEY_USERS;
195 }
196 else
197 {
198 hr = E_INVALIDARG;
199 ExitOnFailure(hr, "Invalid value for @Root: %ls", scz);
200 }
201
202 // @Key
203 hr = XmlGetAttributeEx(pixnNode, L"Key", &pSearch->RegistrySearch.sczKey);
204 ExitOnFailure(hr, "Failed to get Key attribute.");
205
206 // @Value
207 hr = XmlGetAttributeEx(pixnNode, L"Value", &pSearch->RegistrySearch.sczValue);
208 if (E_NOTFOUND != hr)
209 {
210 ExitOnFailure(hr, "Failed to get Value attribute.");
211 }
212
213 // @Type
214 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
215 ExitOnFailure(hr, "Failed to get @Type.");
216
217 hr = XmlGetYesNoAttribute(pixnNode, L"Win64", &pSearch->RegistrySearch.fWin64);
218 if (E_NOTFOUND != hr)
219 {
220 ExitOnFailure(hr, "Failed to get Win64 attribute.");
221 }
222
223 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1))
224 {
225 pSearch->RegistrySearch.Type = BURN_REGISTRY_SEARCH_TYPE_EXISTS;
226 }
227 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"value", -1))
228 {
229 pSearch->RegistrySearch.Type = BURN_REGISTRY_SEARCH_TYPE_VALUE;
230
231 // @ExpandEnvironment
232 hr = XmlGetYesNoAttribute(pixnNode, L"ExpandEnvironment", &pSearch->RegistrySearch.fExpandEnvironment);
233 if (E_NOTFOUND != hr)
234 {
235 ExitOnFailure(hr, "Failed to get @ExpandEnvironment.");
236 }
237
238 // @VariableType
239 hr = XmlGetAttributeEx(pixnNode, L"VariableType", &scz);
240 ExitOnFailure(hr, "Failed to get @VariableType.");
241
242 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"formatted", -1))
243 {
244 pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_FORMATTED;
245 }
246 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1))
247 {
248 pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_NUMERIC;
249 }
250 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1))
251 {
252 pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_STRING;
253 }
254 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1))
255 {
256 pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_VERSION;
257 }
258 else
259 {
260 hr = E_INVALIDARG;
261 ExitOnFailure(hr, "Invalid value for @VariableType: %ls", scz);
262 }
263 }
264 else
265 {
266 hr = E_INVALIDARG;
267 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
268 }
269 }
270 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiComponentSearch", -1))
271 {
272 pSearch->Type = BURN_SEARCH_TYPE_MSI_COMPONENT;
273
274 // @ProductCode
275 hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiComponentSearch.sczProductCode);
276 if (E_NOTFOUND != hr)
277 {
278 ExitOnFailure(hr, "Failed to get @ProductCode.");
279 }
280
281 // @ComponentId
282 hr = XmlGetAttributeEx(pixnNode, L"ComponentId", &pSearch->MsiComponentSearch.sczComponentId);
283 ExitOnFailure(hr, "Failed to get @ComponentId.");
284
285 // @Type
286 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
287 ExitOnFailure(hr, "Failed to get @Type.");
288
289 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"keyPath", -1))
290 {
291 pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH;
292 }
293 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1))
294 {
295 pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_STATE;
296 }
297 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"directory", -1))
298 {
299 pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY;
300 }
301 else
302 {
303 hr = E_INVALIDARG;
304 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
305 }
306 }
307 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiProductSearch", -1))
308 {
309 pSearch->Type = BURN_SEARCH_TYPE_MSI_PRODUCT;
310 pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE;
311
312 // @ProductCode (if we don't find a product code then look for an upgrade code)
313 hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiProductSearch.sczGuid);
314 if (E_NOTFOUND != hr)
315 {
316 ExitOnFailure(hr, "Failed to get @ProductCode.");
317 pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_PRODUCTCODE;
318 }
319 else
320 {
321 // @UpgradeCode
322 hr = XmlGetAttributeEx(pixnNode, L"UpgradeCode", &pSearch->MsiProductSearch.sczGuid);
323 if (E_NOTFOUND != hr)
324 {
325 ExitOnFailure(hr, "Failed to get @UpgradeCode.");
326 pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE;
327 }
328 }
329
330 // make sure we found either a product or upgrade code
331 if (BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE == pSearch->MsiProductSearch.GuidType)
332 {
333 hr = E_NOTFOUND;
334 ExitOnFailure(hr, "Failed to get @ProductCode or @UpgradeCode.");
335 }
336
337 // @Type
338 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
339 ExitOnFailure(hr, "Failed to get @Type.");
340
341 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1))
342 {
343 pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION;
344 }
345 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"language", -1))
346 {
347 pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE;
348 }
349 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1))
350 {
351 pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_STATE;
352 }
353 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"assignment", -1))
354 {
355 pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT;
356 }
357 else
358 {
359 hr = E_INVALIDARG;
360 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
361 }
362 }
363 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiFeatureSearch", -1))
364 {
365 pSearch->Type = BURN_SEARCH_TYPE_MSI_FEATURE;
366
367 // @ProductCode
368 hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiFeatureSearch.sczProductCode);
369 ExitOnFailure(hr, "Failed to get @ProductCode.");
370
371 // @FeatureId
372 hr = XmlGetAttributeEx(pixnNode, L"FeatureId", &pSearch->MsiFeatureSearch.sczFeatureId);
373 ExitOnFailure(hr, "Failed to get @FeatureId.");
374
375 // @Type
376 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
377 ExitOnFailure(hr, "Failed to get @Type.");
378
379 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1))
380 {
381 pSearch->MsiFeatureSearch.Type = BURN_MSI_FEATURE_SEARCH_TYPE_STATE;
382 }
383 else
384 {
385 hr = E_INVALIDARG;
386 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
387 }
388 }
389 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"ExtensionSearch", -1))
390 {
391 pSearch->Type = BURN_SEARCH_TYPE_EXTENSION;
392
393 // @ExtensionId
394 hr = XmlGetAttributeEx(pixnNode, L"ExtensionId", &scz);
395 ExitOnFailure(hr, "Failed to get @ExtensionId.");
396
397 hr = BurnExtensionFindById(pBurnExtensions, scz, &pSearch->ExtensionSearch.pExtension);
398 ExitOnFailure(hr, "Failed to find extension '%ls' for search '%ls'", scz, pSearch->sczKey);
399 }
400 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"SetVariable", -1))
401 {
402 pSearch->Type = BURN_SEARCH_TYPE_SET_VARIABLE;
403
404 // @Value
405 hr = XmlGetAttributeEx(pixnNode, L"Value", &scz);
406 if (E_NOTFOUND != hr)
407 {
408 ExitOnFailure(hr, "Failed to get @Value.");
409
410 hr = BVariantSetString(&pSearch->SetVariable.value, scz, 0, FALSE);
411 ExitOnFailure(hr, "Failed to set variant value.");
412
413 // @Type
414 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
415 ExitOnFailure(hr, "Failed to get @Type.");
416
417 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"formatted", -1))
418 {
419 valueType = BURN_VARIANT_TYPE_FORMATTED;
420 }
421 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1))
422 {
423 valueType = BURN_VARIANT_TYPE_NUMERIC;
424 }
425 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1))
426 {
427 valueType = BURN_VARIANT_TYPE_STRING;
428 }
429 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1))
430 {
431 valueType = BURN_VARIANT_TYPE_VERSION;
432 }
433 else
434 {
435 hr = E_INVALIDARG;
436 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
437 }
438 }
439 else
440 {
441 valueType = BURN_VARIANT_TYPE_NONE;
442 }
443
444 // change value variant to correct type
445 hr = BVariantChangeType(&pSearch->SetVariable.value, valueType);
446 ExitOnFailure(hr, "Failed to change variant type.");
447 }
448 else
449 {
450 hr = E_UNEXPECTED;
451 ExitOnFailure(hr, "Unexpected element name: %ls", bstrNodeName);
452 }
453
454 // prepare next iteration
455 ReleaseNullObject(pixnNode);
456 ReleaseNullBSTR(bstrNodeName);
457 }
458
459 hr = S_OK;
460
461LExit:
462 ReleaseObject(pixnNodes);
463 ReleaseObject(pixnNode);
464 ReleaseBSTR(bstrNodeName);
465 ReleaseStr(scz);
466 return hr;
467}
468
469extern "C" HRESULT SearchesExecute(
470 __in BURN_SEARCHES* pSearches,
471 __in BURN_VARIABLES* pVariables
472 )
473{
474 HRESULT hr = S_OK;
475 BOOL f = FALSE;
476
477 for (DWORD i = 0; i < pSearches->cSearches; ++i)
478 {
479 BURN_SEARCH* pSearch = &pSearches->rgSearches[i];
480
481 // evaluate condition
482 if (pSearch->sczCondition && *pSearch->sczCondition)
483 {
484 hr = ConditionEvaluate(pVariables, pSearch->sczCondition, &f);
485 if (E_INVALIDDATA == hr)
486 {
487 TraceError(hr, "Failed to parse search condition. Id = '%ls', Condition = '%ls'", pSearch->sczKey, pSearch->sczCondition);
488 hr = S_OK;
489 continue;
490 }
491 ExitOnFailure(hr, "Failed to evaluate search condition. Id = '%ls', Condition = '%ls'", pSearch->sczKey, pSearch->sczCondition);
492
493 if (!f)
494 {
495 continue; // condition evaluated to false, skip
496 }
497 }
498
499 switch (pSearch->Type)
500 {
501 case BURN_SEARCH_TYPE_DIRECTORY:
502 switch (pSearch->DirectorySearch.Type)
503 {
504 case BURN_DIRECTORY_SEARCH_TYPE_EXISTS:
505 hr = DirectorySearchExists(pSearch, pVariables);
506 break;
507 case BURN_DIRECTORY_SEARCH_TYPE_PATH:
508 hr = DirectorySearchPath(pSearch, pVariables);
509 break;
510 default:
511 hr = E_UNEXPECTED;
512 }
513 break;
514 case BURN_SEARCH_TYPE_FILE:
515 switch (pSearch->FileSearch.Type)
516 {
517 case BURN_FILE_SEARCH_TYPE_EXISTS:
518 hr = FileSearchExists(pSearch, pVariables);
519 break;
520 case BURN_FILE_SEARCH_TYPE_VERSION:
521 hr = FileSearchVersion(pSearch, pVariables);
522 break;
523 case BURN_FILE_SEARCH_TYPE_PATH:
524 hr = FileSearchPath(pSearch, pVariables);
525 break;
526 default:
527 hr = E_UNEXPECTED;
528 }
529 break;
530 case BURN_SEARCH_TYPE_REGISTRY:
531 switch (pSearch->RegistrySearch.Type)
532 {
533 case BURN_REGISTRY_SEARCH_TYPE_EXISTS:
534 hr = RegistrySearchExists(pSearch, pVariables);
535 break;
536 case BURN_REGISTRY_SEARCH_TYPE_VALUE:
537 hr = RegistrySearchValue(pSearch, pVariables);
538 break;
539 default:
540 hr = E_UNEXPECTED;
541 }
542 break;
543 case BURN_SEARCH_TYPE_MSI_COMPONENT:
544 hr = MsiComponentSearch(pSearch, pVariables);
545 break;
546 case BURN_SEARCH_TYPE_MSI_PRODUCT:
547 hr = MsiProductSearch(pSearch, pVariables);
548 break;
549 case BURN_SEARCH_TYPE_MSI_FEATURE:
550 hr = MsiFeatureSearch(pSearch, pVariables);
551 break;
552 case BURN_SEARCH_TYPE_EXTENSION:
553 hr = PerformExtensionSearch(pSearch);
554 break;
555 case BURN_SEARCH_TYPE_SET_VARIABLE:
556 hr = PerformSetVariable(pSearch, pVariables);
557 break;
558 default:
559 hr = E_UNEXPECTED;
560 }
561
562 if (FAILED(hr))
563 {
564 TraceError(hr, "Search failed. Id = '%ls'", pSearch->sczKey);
565 continue;
566 }
567 }
568
569 hr = S_OK;
570
571LExit:
572 return hr;
573}
574
575extern "C" void SearchesUninitialize(
576 __in BURN_SEARCHES* pSearches
577 )
578{
579 if (pSearches->rgSearches)
580 {
581 for (DWORD i = 0; i < pSearches->cSearches; ++i)
582 {
583 BURN_SEARCH* pSearch = &pSearches->rgSearches[i];
584
585 ReleaseStr(pSearch->sczKey);
586 ReleaseStr(pSearch->sczVariable);
587 ReleaseStr(pSearch->sczCondition);
588
589 switch (pSearch->Type)
590 {
591 case BURN_SEARCH_TYPE_DIRECTORY:
592 ReleaseStr(pSearch->DirectorySearch.sczPath);
593 break;
594 case BURN_SEARCH_TYPE_FILE:
595 ReleaseStr(pSearch->FileSearch.sczPath);
596 break;
597 case BURN_SEARCH_TYPE_REGISTRY:
598 ReleaseStr(pSearch->RegistrySearch.sczKey);
599 ReleaseStr(pSearch->RegistrySearch.sczValue);
600 break;
601 case BURN_SEARCH_TYPE_MSI_COMPONENT:
602 ReleaseStr(pSearch->MsiComponentSearch.sczProductCode);
603 ReleaseStr(pSearch->MsiComponentSearch.sczComponentId);
604 break;
605 case BURN_SEARCH_TYPE_MSI_PRODUCT:
606 ReleaseStr(pSearch->MsiProductSearch.sczGuid);
607 break;
608 case BURN_SEARCH_TYPE_MSI_FEATURE:
609 ReleaseStr(pSearch->MsiFeatureSearch.sczProductCode);
610 ReleaseStr(pSearch->MsiFeatureSearch.sczFeatureId);
611 break;
612 case BURN_SEARCH_TYPE_SET_VARIABLE:
613 BVariantUninitialize(&pSearch->SetVariable.value);
614 break;
615 }
616 }
617 MemFree(pSearches->rgSearches);
618 }
619}
620
621
622// internal function definitions
623
624static HRESULT DirectorySearchExists(
625 __in BURN_SEARCH* pSearch,
626 __in BURN_VARIABLES* pVariables
627 )
628{
629 HRESULT hr = S_OK;
630 LPWSTR sczPath = NULL;
631 BOOL fExists = FALSE;
632
633 // format path
634 hr = VariableFormatString(pVariables, pSearch->DirectorySearch.sczPath, &sczPath, NULL);
635 ExitOnFailure(hr, "Failed to format variable string.");
636
637 DWORD dwAttributes = ::GetFileAttributesW(sczPath);
638 if (INVALID_FILE_ATTRIBUTES == dwAttributes)
639 {
640 hr = HRESULT_FROM_WIN32(::GetLastError());
641 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
642 {
643 hr = S_OK; // didn't find file, fExists still is false.
644 }
645 }
646 else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
647 {
648 fExists = TRUE;
649 }
650
651 // else must have found a file.
652 // What if there is a hidden variable in sczPath?
653 ExitOnFailure(hr, "Failed while searching directory search: %ls, for path: %ls", pSearch->sczKey, sczPath);
654
655 // set variable
656 hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE);
657 ExitOnFailure(hr, "Failed to set variable.");
658
659LExit:
660 StrSecureZeroFreeString(sczPath);
661
662 return hr;
663}
664
665static HRESULT DirectorySearchPath(
666 __in BURN_SEARCH* pSearch,
667 __in BURN_VARIABLES* pVariables
668 )
669{
670 HRESULT hr = S_OK;
671 LPWSTR sczPath = NULL;
672
673 // format path
674 hr = VariableFormatString(pVariables, pSearch->DirectorySearch.sczPath, &sczPath, NULL);
675 ExitOnFailure(hr, "Failed to format variable string.");
676
677 DWORD dwAttributes = ::GetFileAttributesW(sczPath);
678 if (INVALID_FILE_ATTRIBUTES == dwAttributes)
679 {
680 hr = HRESULT_FROM_WIN32(::GetLastError());
681 }
682 else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
683 {
684 hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE);
685 ExitOnFailure(hr, "Failed to set directory search path variable.");
686 }
687 else // must have found a file.
688 {
689 hr = E_PATHNOTFOUND;
690 }
691
692 // What if there is a hidden variable in sczPath?
693 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
694 {
695 LogStringLine(REPORT_STANDARD, "Directory search: %ls, did not find path: %ls, reason: 0x%x", pSearch->sczKey, sczPath, hr);
696 ExitFunction1(hr = S_OK);
697 }
698 ExitOnFailure(hr, "Failed while searching directory search: %ls, for path: %ls", pSearch->sczKey, sczPath);
699
700LExit:
701 StrSecureZeroFreeString(sczPath);
702
703 return hr;
704}
705
706static HRESULT FileSearchExists(
707 __in BURN_SEARCH* pSearch,
708 __in BURN_VARIABLES* pVariables
709 )
710{
711 HRESULT hr = S_OK;
712 DWORD er = ERROR_SUCCESS;
713 LPWSTR sczPath = NULL;
714 BOOL fExists = FALSE;
715
716 // format path
717 hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL);
718 ExitOnFailure(hr, "Failed to format variable string.");
719
720 // find file
721 DWORD dwAttributes = ::GetFileAttributesW(sczPath);
722 if (INVALID_FILE_ATTRIBUTES == dwAttributes)
723 {
724 er = ::GetLastError();
725 if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er)
726 {
727 // What if there is a hidden variable in sczPath?
728 LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath);
729 }
730 else
731 {
732 ExitOnWin32Error(er, hr, "Failed get to file attributes. '%ls'", pSearch->DirectorySearch.sczPath);
733 }
734 }
735 else if (FILE_ATTRIBUTE_DIRECTORY != (dwAttributes & FILE_ATTRIBUTE_DIRECTORY))
736 {
737 fExists = TRUE;
738 }
739
740 // set variable
741 hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE);
742 ExitOnFailure(hr, "Failed to set variable.");
743
744LExit:
745 StrSecureZeroFreeString(sczPath);
746 return hr;
747}
748
749static HRESULT FileSearchVersion(
750 __in BURN_SEARCH* pSearch,
751 __in BURN_VARIABLES* pVariables
752 )
753{
754 HRESULT hr = S_OK;
755 ULARGE_INTEGER uliVersion = { };
756 LPWSTR sczPath = NULL;
757 VERUTIL_VERSION* pVersion = NULL;
758
759 // format path
760 hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL);
761 ExitOnFailure(hr, "Failed to format path string.");
762
763 // get file version
764 hr = FileVersion(sczPath, &uliVersion.HighPart, &uliVersion.LowPart);
765 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
766 {
767 // What if there is a hidden variable in sczPath?
768 LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath);
769 ExitFunction1(hr = S_OK);
770 }
771 ExitOnFailure(hr, "Failed to get file version.");
772
773 hr = VerVersionFromQword(uliVersion.QuadPart, &pVersion);
774 ExitOnFailure(hr, "Failed to create version from file version.");
775
776 // set variable
777 hr = VariableSetVersion(pVariables, pSearch->sczVariable, pVersion, FALSE);
778 ExitOnFailure(hr, "Failed to set variable.");
779
780LExit:
781 StrSecureZeroFreeString(sczPath);
782 ReleaseVerutilVersion(pVersion);
783 return hr;
784}
785
786static HRESULT FileSearchPath(
787 __in BURN_SEARCH* pSearch,
788 __in BURN_VARIABLES* pVariables
789 )
790{
791 HRESULT hr = S_OK;
792 LPWSTR sczPath = NULL;
793
794 // format path
795 hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL);
796 ExitOnFailure(hr, "Failed to format variable string.");
797
798 DWORD dwAttributes = ::GetFileAttributesW(sczPath);
799 if (INVALID_FILE_ATTRIBUTES == dwAttributes)
800 {
801 hr = HRESULT_FROM_WIN32(::GetLastError());
802 }
803 else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) // found a directory.
804 {
805 hr = E_FILENOTFOUND;
806 }
807 else // found our file.
808 {
809 hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE);
810 ExitOnFailure(hr, "Failed to set variable to file search path.");
811 }
812
813 // What if there is a hidden variable in sczPath?
814 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
815 {
816 LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath);
817 ExitFunction1(hr = S_OK);
818 }
819 ExitOnFailure(hr, "Failed while searching file search: %ls, for path: %ls", pSearch->sczKey, sczPath);
820
821LExit:
822 StrSecureZeroFreeString(sczPath);
823
824 return hr;
825}
826
827static HRESULT RegistrySearchExists(
828 __in BURN_SEARCH* pSearch,
829 __in BURN_VARIABLES* pVariables
830 )
831{
832 HRESULT hr = S_OK;
833 DWORD er = ERROR_SUCCESS;
834 LPWSTR sczKey = NULL;
835 LPWSTR sczValue = NULL;
836 HKEY hKey = NULL;
837 DWORD dwType = 0;
838 BOOL fExists = FALSE;
839 REGSAM samDesired = KEY_QUERY_VALUE;
840
841 if (pSearch->RegistrySearch.fWin64)
842 {
843 samDesired = samDesired | KEY_WOW64_64KEY;
844 }
845
846 // format key string
847 hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczKey, &sczKey, NULL);
848 ExitOnFailure(hr, "Failed to format key string.");
849
850 // open key
851 hr = RegOpen(pSearch->RegistrySearch.hRoot, sczKey, samDesired, &hKey);
852 if (SUCCEEDED(hr))
853 {
854 fExists = TRUE;
855 }
856 else if (E_FILENOTFOUND == hr)
857 {
858 // What if there is a hidden variable in sczKey?
859 LogStringLine(REPORT_STANDARD, "Registry key not found. Key = '%ls'", sczKey);
860 fExists = FALSE;
861 hr = S_OK;
862 }
863 else
864 {
865 // What if there is a hidden variable in sczKey?
866 ExitOnFailure(hr, "Failed to open registry key. Key = '%ls'", sczKey);
867 }
868
869 if (fExists && pSearch->RegistrySearch.sczValue)
870 {
871 // format value string
872 hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczValue, &sczValue, NULL);
873 ExitOnFailure(hr, "Failed to format value string.");
874
875 // query value
876 er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, NULL, NULL);
877 switch (er)
878 {
879 case ERROR_SUCCESS:
880 fExists = TRUE;
881 break;
882 case ERROR_FILE_NOT_FOUND:
883 // What if there is a hidden variable in sczKey or sczValue?
884 LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue);
885 fExists = FALSE;
886 break;
887 default:
888 ExitOnWin32Error(er, hr, "Failed to query registry key value.");
889 }
890 }
891
892 // set variable
893 hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE);
894 ExitOnFailure(hr, "Failed to set variable.");
895
896LExit:
897 if (FAILED(hr))
898 {
899 // What if there is a hidden variable in sczKey?
900 LogStringLine(REPORT_STANDARD, "RegistrySearchExists failed: ID '%ls', HRESULT 0x%x", sczKey, hr);
901 }
902
903 StrSecureZeroFreeString(sczKey);
904 StrSecureZeroFreeString(sczValue);
905 ReleaseRegKey(hKey);
906
907 return hr;
908}
909
910static HRESULT RegistrySearchValue(
911 __in BURN_SEARCH* pSearch,
912 __in BURN_VARIABLES* pVariables
913 )
914{
915 HRESULT hr = S_OK;
916 DWORD er = ERROR_SUCCESS;
917 LPWSTR sczKey = NULL;
918 LPWSTR sczValue = NULL;
919 HKEY hKey = NULL;
920 DWORD dwType = 0;
921 DWORD cbData = 0;
922 LPBYTE pData = NULL;
923 DWORD cch = 0;
924 BURN_VARIANT value = { };
925 REGSAM samDesired = KEY_QUERY_VALUE;
926
927 if (pSearch->RegistrySearch.fWin64)
928 {
929 samDesired = samDesired | KEY_WOW64_64KEY;
930 }
931
932 // format key string
933 hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczKey, &sczKey, NULL);
934 ExitOnFailure(hr, "Failed to format key string.");
935
936 // format value string
937 if (pSearch->RegistrySearch.sczValue)
938 {
939 hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczValue, &sczValue, NULL);
940 ExitOnFailure(hr, "Failed to format value string.");
941 }
942
943 // open key
944 hr = RegOpen(pSearch->RegistrySearch.hRoot, sczKey, samDesired, &hKey);
945 if (E_FILENOTFOUND == hr)
946 {
947 // What if there is a hidden variable in sczKey?
948 LogStringLine(REPORT_STANDARD, "Registry key not found. Key = '%ls'", sczKey);
949
950 ExitFunction1(hr = S_OK);
951 }
952 ExitOnFailure(hr, "Failed to open registry key.");
953
954 // get value
955 er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, NULL, &cbData);
956 if (ERROR_FILE_NOT_FOUND == er)
957 {
958 // What if there is a hidden variable in sczKey or sczValue?
959 LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue);
960
961 ExitFunction1(hr = S_OK);
962 }
963 ExitOnWin32Error(er, hr, "Failed to query registry key value size.");
964
965 pData = (LPBYTE)MemAlloc(cbData + sizeof(WCHAR), TRUE); // + sizeof(WCHAR) here to ensure that we always have a null terminator for REG_SZ
966 ExitOnNull(pData, hr, E_OUTOFMEMORY, "Failed to allocate memory registry value.");
967
968 er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, pData, &cbData);
969 ExitOnWin32Error(er, hr, "Failed to query registry key value.");
970
971 switch (dwType)
972 {
973 case REG_DWORD:
974 if (sizeof(LONG) != cbData)
975 {
976 ExitFunction1(hr = E_UNEXPECTED);
977 }
978 hr = BVariantSetNumeric(&value, *((LONG*)pData));
979 break;
980 case REG_QWORD:
981 if (sizeof(LONGLONG) != cbData)
982 {
983 ExitFunction1(hr = E_UNEXPECTED);
984 }
985 hr = BVariantSetNumeric(&value, *((LONGLONG*)pData));
986 break;
987 case REG_EXPAND_SZ:
988 if (pSearch->RegistrySearch.fExpandEnvironment)
989 {
990 hr = StrAlloc(&value.sczValue, cbData);
991 ExitOnFailure(hr, "Failed to allocate string buffer.");
992 value.Type = BURN_VARIANT_TYPE_STRING;
993
994 cch = ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cbData);
995 if (cch > cbData)
996 {
997 hr = StrAlloc(&value.sczValue, cch);
998 ExitOnFailure(hr, "Failed to allocate string buffer.");
999
1000 if (cch != ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cch))
1001 {
1002 ExitWithLastError(hr, "Failed to get expand environment string.");
1003 }
1004 }
1005 break;
1006 }
1007 __fallthrough;
1008 case REG_SZ:
1009 hr = BVariantSetString(&value, (LPCWSTR)pData, 0, FALSE);
1010 break;
1011 default:
1012 ExitOnFailure(hr = E_NOTIMPL, "Unsupported registry key value type. Type = '%u'", dwType);
1013 }
1014 ExitOnFailure(hr, "Failed to read registry value.");
1015
1016 // change value to requested type
1017 hr = BVariantChangeType(&value, pSearch->RegistrySearch.VariableType);
1018 ExitOnFailure(hr, "Failed to change value type.");
1019
1020 // Set variable.
1021 hr = VariableSetVariant(pVariables, pSearch->sczVariable, &value);
1022 ExitOnFailure(hr, "Failed to set variable.");
1023
1024LExit:
1025 if (FAILED(hr))
1026 {
1027 // What if there is a hidden variable in sczKey?
1028 LogStringLine(REPORT_STANDARD, "RegistrySearchValue failed: ID '%ls', HRESULT 0x%x", sczKey, hr);
1029 }
1030
1031 StrSecureZeroFreeString(sczKey);
1032 StrSecureZeroFreeString(sczValue);
1033 ReleaseRegKey(hKey);
1034 ReleaseMem(pData);
1035 BVariantUninitialize(&value);
1036
1037 return hr;
1038}
1039
1040static HRESULT MsiComponentSearch(
1041 __in BURN_SEARCH* pSearch,
1042 __in BURN_VARIABLES* pVariables
1043 )
1044{
1045 HRESULT hr = S_OK;
1046 INSTALLSTATE is = INSTALLSTATE_BROKEN;
1047 LPWSTR sczComponentId = NULL;
1048 LPWSTR sczProductCode = NULL;
1049 LPWSTR sczPath = NULL;
1050
1051 // format component id string
1052 hr = VariableFormatString(pVariables, pSearch->MsiComponentSearch.sczComponentId, &sczComponentId, NULL);
1053 ExitOnFailure(hr, "Failed to format component id string.");
1054
1055 if (pSearch->MsiComponentSearch.sczProductCode)
1056 {
1057 // format product code string
1058 hr = VariableFormatString(pVariables, pSearch->MsiComponentSearch.sczProductCode, &sczProductCode, NULL);
1059 ExitOnFailure(hr, "Failed to format product code string.");
1060 }
1061
1062 if (sczProductCode)
1063 {
1064 hr = WiuGetComponentPath(sczProductCode, sczComponentId, &is, &sczPath);
1065 }
1066 else
1067 {
1068 hr = WiuLocateComponent(sczComponentId, &is, &sczPath);
1069 }
1070
1071 if (INSTALLSTATE_SOURCEABSENT == is)
1072 {
1073 is = INSTALLSTATE_SOURCE;
1074 }
1075 else if (INSTALLSTATE_UNKNOWN == is || INSTALLSTATE_NOTUSED == is)
1076 {
1077 is = INSTALLSTATE_ABSENT;
1078 }
1079 else if (INSTALLSTATE_ABSENT != is && INSTALLSTATE_LOCAL != is && INSTALLSTATE_SOURCE != is)
1080 {
1081 hr = E_INVALIDARG;
1082 ExitOnFailure(hr, "Failed to get component path: %d", is);
1083 }
1084
1085 // set variable
1086 switch (pSearch->MsiComponentSearch.Type)
1087 {
1088 case BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH:
1089 if (INSTALLSTATE_ABSENT == is || INSTALLSTATE_LOCAL == is || INSTALLSTATE_SOURCE == is)
1090 {
1091 hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE);
1092 }
1093 break;
1094 case BURN_MSI_COMPONENT_SEARCH_TYPE_STATE:
1095 hr = VariableSetNumeric(pVariables, pSearch->sczVariable, is, FALSE);
1096 break;
1097 case BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY:
1098 if (INSTALLSTATE_ABSENT == is || INSTALLSTATE_LOCAL == is || INSTALLSTATE_SOURCE == is)
1099 {
1100 // remove file part from path, if any
1101 LPWSTR wz = wcsrchr(sczPath, L'\\');
1102 if (wz)
1103 {
1104 wz[1] = L'\0';
1105 }
1106
1107 hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE);
1108 }
1109 break;
1110 }
1111 ExitOnFailure(hr, "Failed to set variable.");
1112
1113LExit:
1114 if (FAILED(hr))
1115 {
1116 LogStringLine(REPORT_STANDARD, "MsiComponentSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr);
1117 }
1118
1119 StrSecureZeroFreeString(sczComponentId);
1120 StrSecureZeroFreeString(sczProductCode);
1121 ReleaseStr(sczPath);
1122 return hr;
1123}
1124
1125static HRESULT MsiProductSearch(
1126 __in BURN_SEARCH* pSearch,
1127 __in BURN_VARIABLES* pVariables
1128 )
1129{
1130 HRESULT hr = S_OK;
1131 LPWSTR sczGuid = NULL;
1132 LPCWSTR wzProperty = NULL;
1133 LPWSTR *rgsczRelatedProductCodes = NULL;
1134 DWORD dwRelatedProducts = 0;
1135 BURN_VARIANT_TYPE type = BURN_VARIANT_TYPE_NONE;
1136 BURN_VARIANT value = { };
1137
1138 switch (pSearch->MsiProductSearch.Type)
1139 {
1140 case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION:
1141 wzProperty = INSTALLPROPERTY_VERSIONSTRING;
1142 break;
1143 case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE:
1144 wzProperty = INSTALLPROPERTY_LANGUAGE;
1145 break;
1146 case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE:
1147 wzProperty = INSTALLPROPERTY_PRODUCTSTATE;
1148 break;
1149 case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT:
1150 wzProperty = INSTALLPROPERTY_ASSIGNMENTTYPE;
1151 break;
1152 default:
1153 ExitOnFailure(hr = E_NOTIMPL, "Unsupported product search type: %u", pSearch->MsiProductSearch.Type);
1154 }
1155
1156 // format guid string
1157 hr = VariableFormatString(pVariables, pSearch->MsiProductSearch.sczGuid, &sczGuid, NULL);
1158 ExitOnFailure(hr, "Failed to format GUID string.");
1159
1160 // get product info
1161 value.Type = BURN_VARIANT_TYPE_STRING;
1162
1163 // if this is an upgrade code then get the product code of the highest versioned related product
1164 if (BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE == pSearch->MsiProductSearch.GuidType)
1165 {
1166 // WiuEnumRelatedProductCodes will log sczGuid on errors, what if there's a hidden variable in there?
1167 hr = WiuEnumRelatedProductCodes(sczGuid, &rgsczRelatedProductCodes, &dwRelatedProducts, TRUE);
1168 ExitOnFailure(hr, "Failed to enumerate related products for upgrade code.");
1169
1170 // if we actually found a related product then use its upgrade code for the rest of the search
1171 if (1 == dwRelatedProducts)
1172 {
1173 hr = StrAllocStringSecure(&sczGuid, rgsczRelatedProductCodes[0], 0);
1174 ExitOnFailure(hr, "Failed to copy upgrade code.");
1175 }
1176 else
1177 {
1178 // set this here so we have a way of knowing that we don't need to bother
1179 // querying for the product information below
1180 hr = HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT);
1181 }
1182 }
1183
1184 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr)
1185 {
1186 hr = WiuGetProductInfo(sczGuid, wzProperty, &value.sczValue);
1187 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr)
1188 {
1189 // product state is available only through MsiGetProductInfoEx
1190 // What if there is a hidden variable in sczGuid?
1191 LogStringLine(REPORT_VERBOSE, "Trying per-machine extended info for property '%ls' for product: %ls", wzProperty, sczGuid);
1192 hr = WiuGetProductInfoEx(sczGuid, NULL, MSIINSTALLCONTEXT_MACHINE, wzProperty, &value.sczValue);
1193
1194 // if not in per-machine context, try per-user (unmanaged)
1195 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr)
1196 {
1197 // What if there is a hidden variable in sczGuid?
1198 LogStringLine(REPORT_STANDARD, "Trying per-user extended info for property '%ls' for product: %ls", wzProperty, sczGuid);
1199 hr = WiuGetProductInfoEx(sczGuid, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, wzProperty, &value.sczValue);
1200 }
1201 }
1202 }
1203
1204 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr)
1205 {
1206 // What if there is a hidden variable in sczGuid?
1207 LogStringLine(REPORT_STANDARD, "Product or related product not found: %ls", sczGuid);
1208
1209 // set value to indicate absent
1210 switch (pSearch->MsiProductSearch.Type)
1211 {
1212 case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT: __fallthrough;
1213 case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION:
1214 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1215 value.llValue = 0;
1216 break;
1217 case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE:
1218 // is supposed to remain empty
1219 break;
1220 case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE:
1221 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1222 value.llValue = INSTALLSTATE_ABSENT;
1223 break;
1224 }
1225
1226 hr = S_OK;
1227 }
1228 ExitOnFailure(hr, "Failed to get product info.");
1229
1230 // change value type
1231 switch (pSearch->MsiProductSearch.Type)
1232 {
1233 case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION:
1234 type = BURN_VARIANT_TYPE_VERSION;
1235 break;
1236 case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE:
1237 type = BURN_VARIANT_TYPE_STRING;
1238 break;
1239 case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE: __fallthrough;
1240 case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT:
1241 type = BURN_VARIANT_TYPE_NUMERIC;
1242 break;
1243 }
1244 hr = BVariantChangeType(&value, type);
1245 ExitOnFailure(hr, "Failed to change value type.");
1246
1247 // Set variable.
1248 hr = VariableSetVariant(pVariables, pSearch->sczVariable, &value);
1249 ExitOnFailure(hr, "Failed to set variable.");
1250
1251LExit:
1252 if (FAILED(hr))
1253 {
1254 LogStringLine(REPORT_STANDARD, "MsiProductSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr);
1255 }
1256
1257 StrSecureZeroFreeString(sczGuid);
1258 ReleaseStrArray(rgsczRelatedProductCodes, dwRelatedProducts);
1259 BVariantUninitialize(&value);
1260
1261 return hr;
1262}
1263
1264static HRESULT MsiFeatureSearch(
1265 __in BURN_SEARCH* pSearch,
1266 __in BURN_VARIABLES* /*pVariables*/
1267 )
1268{
1269 HRESULT hr = E_NOTIMPL;
1270
1271//LExit:
1272 if (FAILED(hr))
1273 {
1274 LogStringLine(REPORT_STANDARD, "MsiFeatureSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr);
1275 }
1276
1277 return hr;
1278}
1279
1280static HRESULT PerformExtensionSearch(
1281 __in BURN_SEARCH* pSearch
1282 )
1283{
1284 HRESULT hr = S_OK;
1285
1286 hr = BurnExtensionPerformSearch(pSearch->ExtensionSearch.pExtension, pSearch->sczKey, pSearch->sczVariable);
1287
1288 return hr;
1289}
1290
1291static HRESULT PerformSetVariable(
1292 __in BURN_SEARCH* pSearch,
1293 __in BURN_VARIABLES* pVariables
1294 )
1295{
1296 HRESULT hr = S_OK;
1297
1298 hr = VariableSetVariant(pVariables, pSearch->sczVariable, &pSearch->SetVariable.value);
1299 ExitOnFailure(hr, "Failed to set variable: %ls", pSearch->sczVariable);
1300
1301LExit:
1302 return hr;
1303}
diff --git a/src/burn/engine/search.h b/src/burn/engine/search.h
new file mode 100644
index 00000000..c699c97c
--- /dev/null
+++ b/src/burn/engine/search.h
@@ -0,0 +1,163 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// constants
11
12enum BURN_SEARCH_TYPE
13{
14 BURN_SEARCH_TYPE_NONE,
15 BURN_SEARCH_TYPE_DIRECTORY,
16 BURN_SEARCH_TYPE_FILE,
17 BURN_SEARCH_TYPE_REGISTRY,
18 BURN_SEARCH_TYPE_MSI_COMPONENT,
19 BURN_SEARCH_TYPE_MSI_PRODUCT,
20 BURN_SEARCH_TYPE_MSI_FEATURE,
21 BURN_SEARCH_TYPE_EXTENSION,
22 BURN_SEARCH_TYPE_SET_VARIABLE,
23};
24
25enum BURN_DIRECTORY_SEARCH_TYPE
26{
27 BURN_DIRECTORY_SEARCH_TYPE_NONE,
28 BURN_DIRECTORY_SEARCH_TYPE_EXISTS,
29 BURN_DIRECTORY_SEARCH_TYPE_PATH,
30};
31
32enum BURN_FILE_SEARCH_TYPE
33{
34 BURN_FILE_SEARCH_TYPE_NONE,
35 BURN_FILE_SEARCH_TYPE_EXISTS,
36 BURN_FILE_SEARCH_TYPE_VERSION,
37 BURN_FILE_SEARCH_TYPE_PATH,
38};
39
40enum BURN_REGISTRY_SEARCH_TYPE
41{
42 BURN_REGISTRY_SEARCH_TYPE_NONE,
43 BURN_REGISTRY_SEARCH_TYPE_EXISTS,
44 BURN_REGISTRY_SEARCH_TYPE_VALUE,
45};
46
47enum BURN_MSI_COMPONENT_SEARCH_TYPE
48{
49 BURN_MSI_COMPONENT_SEARCH_TYPE_NONE,
50 BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH,
51 BURN_MSI_COMPONENT_SEARCH_TYPE_STATE,
52 BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY,
53};
54
55enum BURN_MSI_PRODUCT_SEARCH_TYPE
56{
57 BURN_MSI_PRODUCT_SEARCH_TYPE_NONE,
58 BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION,
59 BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE,
60 BURN_MSI_PRODUCT_SEARCH_TYPE_STATE,
61 BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT,
62};
63
64enum BURN_MSI_PRODUCT_SEARCH_GUID_TYPE
65{
66 BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE,
67 BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_PRODUCTCODE,
68 BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE
69};
70
71enum BURN_MSI_FEATURE_SEARCH_TYPE
72{
73 BURN_MSI_FEATURE_SEARCH_TYPE_NONE,
74 BURN_MSI_FEATURE_SEARCH_TYPE_STATE,
75};
76
77
78// structs
79
80typedef struct _BURN_SEARCH
81{
82 LPWSTR sczKey;
83 LPWSTR sczVariable;
84 LPWSTR sczCondition;
85
86 BURN_SEARCH_TYPE Type;
87 union
88 {
89 struct
90 {
91 BURN_DIRECTORY_SEARCH_TYPE Type;
92 LPWSTR sczPath;
93 } DirectorySearch;
94 struct
95 {
96 BURN_FILE_SEARCH_TYPE Type;
97 LPWSTR sczPath;
98 } FileSearch;
99 struct
100 {
101 BURN_REGISTRY_SEARCH_TYPE Type;
102 BURN_VARIANT_TYPE VariableType;
103 HKEY hRoot;
104 LPWSTR sczKey;
105 LPWSTR sczValue;
106 BOOL fWin64;
107 BOOL fExpandEnvironment;
108 } RegistrySearch;
109 struct
110 {
111 BURN_MSI_COMPONENT_SEARCH_TYPE Type;
112 LPWSTR sczProductCode;
113 LPWSTR sczComponentId;
114 } MsiComponentSearch;
115 struct
116 {
117 BURN_MSI_PRODUCT_SEARCH_TYPE Type;
118 BURN_MSI_PRODUCT_SEARCH_GUID_TYPE GuidType;
119 LPWSTR sczGuid;
120 } MsiProductSearch;
121 struct
122 {
123 BURN_MSI_FEATURE_SEARCH_TYPE Type;
124 LPWSTR sczProductCode;
125 LPWSTR sczFeatureId;
126 } MsiFeatureSearch;
127 struct
128 {
129 BURN_EXTENSION* pExtension;
130 } ExtensionSearch;
131 struct
132 {
133 BURN_VARIANT value;
134 } SetVariable;
135 };
136} BURN_SEARCH;
137
138typedef struct _BURN_SEARCHES
139{
140 BURN_SEARCH* rgSearches;
141 DWORD cSearches;
142} BURN_SEARCHES;
143
144
145// function declarations
146
147HRESULT SearchesParseFromXml(
148 __in BURN_SEARCHES* pSearches,
149 __in BURN_EXTENSIONS* pBurnExtensions,
150 __in IXMLDOMNode* pixnBundle
151 );
152HRESULT SearchesExecute(
153 __in BURN_SEARCHES* pSearches,
154 __in BURN_VARIABLES* pVariables
155 );
156void SearchesUninitialize(
157 __in BURN_SEARCHES* pSearches
158 );
159
160
161#if defined(__cplusplus)
162}
163#endif
diff --git a/src/burn/engine/section.cpp b/src/burn/engine/section.cpp
new file mode 100644
index 00000000..3720155c
--- /dev/null
+++ b/src/burn/engine/section.cpp
@@ -0,0 +1,399 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// constants
7
8// If these defaults ever change, be sure to update constants in burn\stub\StubSection.cpp as well.
9#define BURN_SECTION_NAME ".wixburn"
10#define BURN_SECTION_MAGIC 0x00f14300
11#define BURN_SECTION_VERSION 0x00000002
12#define MANIFEST_CABINET_TOKEN L"0"
13
14// structs
15typedef struct _BURN_SECTION_HEADER
16{
17 DWORD dwMagic;
18 DWORD dwVersion;
19
20 GUID guidBundleId;
21
22 DWORD dwStubSize;
23 DWORD dwOriginalChecksum;
24 DWORD dwOriginalSignatureOffset;
25 DWORD dwOriginalSignatureSize;
26
27 DWORD dwFormat;
28 DWORD cContainers;
29 DWORD rgcbContainers[1];
30} BURN_SECTION_HEADER;
31
32static HRESULT VerifySectionMatchesMemoryPEHeader(
33 __in REFGUID pSection
34 );
35
36
37extern "C" HRESULT SectionInitialize(
38 __in BURN_SECTION* pSection,
39 __in HANDLE hEngineFile,
40 __in HANDLE hSourceEngineFile
41 )
42{
43 HRESULT hr = S_OK;
44 DWORD cbRead = 0;
45 LARGE_INTEGER li = { };
46 LONGLONG llSize = 0;
47 IMAGE_DOS_HEADER dosHeader = { };
48 IMAGE_NT_HEADERS ntHeader = { };
49 DWORD dwChecksumOffset = 0;
50 DWORD dwCertificateTableOffset = 0;
51 DWORD dwSignatureOffset = 0;
52 DWORD cbSignature = 0;
53 IMAGE_SECTION_HEADER sectionHeader = { };
54 DWORD_PTR dwOriginalChecksumAndSignatureOffset = 0;
55 BURN_SECTION_HEADER* pBurnSectionHeader = NULL;
56
57 pSection->hEngineFile = hEngineFile;
58 ExitOnInvalidHandleWithLastError(pSection->hEngineFile, hr, "Failed to open handle to engine process path.");
59
60 pSection->hSourceEngineFile = INVALID_HANDLE_VALUE == hSourceEngineFile ? hEngineFile : hSourceEngineFile;
61
62 //
63 // First, make sure we have a valid DOS signature.
64 //
65 if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN))
66 {
67 ExitWithLastError(hr, "Failed to seek to start of file.");
68 }
69
70 // read DOS header
71 if (!::ReadFile(pSection->hEngineFile, &dosHeader, sizeof(IMAGE_DOS_HEADER), &cbRead, NULL))
72 {
73 ExitWithLastError(hr, "Failed to read DOS header.");
74 }
75 else if (sizeof(IMAGE_DOS_HEADER) > cbRead || IMAGE_DOS_SIGNATURE != dosHeader.e_magic)
76 {
77 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
78 ExitOnRootFailure(hr, "Failed to find valid DOS image header in buffer.");
79 }
80
81 //
82 // Now, make sure we have a valid NT signature.
83 //
84
85 // seek to new header
86 li.QuadPart = dosHeader.e_lfanew;
87 if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN))
88 {
89 ExitWithLastError(hr, "Failed to seek to NT header.");
90 }
91
92 // read NT header
93 if (!::ReadFile(pSection->hEngineFile, &ntHeader, sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER), &cbRead, NULL))
94 {
95 ExitWithLastError(hr, "Failed to read NT header.");
96 }
97 else if ((sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER)) > cbRead || IMAGE_NT_SIGNATURE != ntHeader.Signature)
98 {
99 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
100 ExitOnRootFailure(hr, "Failed to find valid NT image header in buffer.");
101 }
102
103 // Get the table offsets.
104 dwChecksumOffset = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + (sizeof(DWORD) * 16);
105 dwCertificateTableOffset = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - (sizeof(IMAGE_DATA_DIRECTORY) * (IMAGE_NUMBEROF_DIRECTORY_ENTRIES - IMAGE_DIRECTORY_ENTRY_SECURITY));
106
107 // Seek into the certificate table to get the signature size.
108 li.QuadPart = dwCertificateTableOffset;
109 if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN))
110 {
111 ExitWithLastError(hr, "Failed to seek to section info.");
112 }
113
114 if (!::ReadFile(pSection->hEngineFile, &dwSignatureOffset, sizeof(dwSignatureOffset), &cbRead, NULL))
115 {
116 ExitWithLastError(hr, "Failed to read signature offset.");
117 }
118
119 if (!::ReadFile(pSection->hEngineFile, &cbSignature, sizeof(cbSignature), &cbRead, NULL))
120 {
121 ExitWithLastError(hr, "Failed to read signature size.");
122 }
123
124 //
125 // Finally, get into the section table and look for the Burn section info.
126 //
127
128 // seek past optional headers
129 li.QuadPart = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + ntHeader.FileHeader.SizeOfOptionalHeader;
130 if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN))
131 {
132 ExitWithLastError(hr, "Failed to seek past optional headers.");
133 }
134
135 // read sections one by one until we find our section
136 for (DWORD i = 0; ; ++i)
137 {
138 // read section
139 if (!::ReadFile(pSection->hEngineFile, &sectionHeader, sizeof(IMAGE_SECTION_HEADER), &cbRead, NULL))
140 {
141 ExitWithLastError(hr, "Failed to read image section header, index: %u", i);
142 }
143 if (sizeof(IMAGE_SECTION_HEADER) > cbRead)
144 {
145 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
146 ExitOnRootFailure(hr, "Failed to read complete image section header, index: %u", i);
147 }
148
149 // compare header name
150 C_ASSERT(sizeof(sectionHeader.Name) == sizeof(BURN_SECTION_NAME) - 1);
151 if (0 == memcmp(sectionHeader.Name, BURN_SECTION_NAME, sizeof(sectionHeader.Name)))
152 {
153 break;
154 }
155
156 // fail if we hit the end
157 if (i + 1 >= ntHeader.FileHeader.NumberOfSections)
158 {
159 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
160 ExitOnRootFailure(hr, "Failed to find Burn section.");
161 }
162 }
163
164 //
165 // We've arrived at the section info.
166 //
167
168 // check size of section
169 if (sizeof(BURN_SECTION_HEADER) > sectionHeader.SizeOfRawData)
170 {
171 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
172 ExitOnRootFailure(hr, "Failed to read section info, data to short: %u", sectionHeader.SizeOfRawData);
173 }
174
175 // allocate buffer for section info
176 pBurnSectionHeader = (BURN_SECTION_HEADER*)MemAlloc(sectionHeader.SizeOfRawData, TRUE);
177 ExitOnNull(pBurnSectionHeader, hr, E_OUTOFMEMORY, "Failed to allocate buffer for section info.");
178
179 // seek to section info
180 li.QuadPart = sectionHeader.PointerToRawData;
181 if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN))
182 {
183 ExitWithLastError(hr, "Failed to seek to section info.");
184 }
185
186 // Note the location of original checksum and signature information in the burn section header.
187 dwOriginalChecksumAndSignatureOffset = sectionHeader.PointerToRawData + (reinterpret_cast<LPBYTE>(&pBurnSectionHeader->dwOriginalChecksum) - reinterpret_cast<LPBYTE>(pBurnSectionHeader));
188
189 // read section info
190 if (!::ReadFile(pSection->hEngineFile, pBurnSectionHeader, sectionHeader.SizeOfRawData, &cbRead, NULL))
191 {
192 ExitWithLastError(hr, "Failed to read section info.");
193 }
194 else if (sectionHeader.SizeOfRawData > cbRead)
195 {
196 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
197 ExitOnRootFailure(hr, "Failed to read complete section info.");
198 }
199
200 // validate version of section info
201 if (BURN_SECTION_VERSION != pBurnSectionHeader->dwVersion)
202 {
203 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
204 ExitOnRootFailure(hr, "Failed to read section info, unsupported version: %08x", pBurnSectionHeader->dwVersion);
205 }
206
207 hr = FileSizeByHandle(pSection->hSourceEngineFile, &llSize);
208 ExitOnFailure(hr, "Failed to get total size of bundle.");
209
210 pSection->cbStub = pBurnSectionHeader->dwStubSize;
211
212 // If there is an original signature use that to determine the engine size.
213 if (pBurnSectionHeader->dwOriginalSignatureOffset)
214 {
215 pSection->cbEngineSize = pBurnSectionHeader->dwOriginalSignatureOffset + pBurnSectionHeader->dwOriginalSignatureSize;
216 }
217 else if (dwSignatureOffset) // if there is a signature, use it.
218 {
219 pSection->cbEngineSize = dwSignatureOffset + cbSignature;
220 }
221 else // just use the stub and UX container as the size of the engine.
222 {
223 pSection->cbEngineSize = pSection->cbStub + pBurnSectionHeader->rgcbContainers[0];
224 }
225
226 pSection->qwBundleSize = static_cast<DWORD64>(llSize);
227
228 pSection->dwChecksumOffset = dwChecksumOffset;
229 pSection->dwCertificateTableOffset = dwCertificateTableOffset;
230 pSection->dwOriginalChecksumAndSignatureOffset = dwOriginalChecksumAndSignatureOffset;
231
232 pSection->dwOriginalChecksum = pBurnSectionHeader->dwOriginalChecksum;
233 pSection->dwOriginalSignatureOffset = pBurnSectionHeader->dwOriginalSignatureOffset;
234 pSection->dwOriginalSignatureSize = pBurnSectionHeader->dwOriginalSignatureSize;
235
236 pSection->dwFormat = pBurnSectionHeader->dwFormat;
237 pSection->cContainers = pBurnSectionHeader->cContainers;
238 pSection->rgcbContainers = (DWORD*)MemAlloc(sizeof(DWORD) * pSection->cContainers, TRUE);
239 ExitOnNull(pSection->rgcbContainers, hr, E_OUTOFMEMORY, "Failed to allocate memory for container sizes.");
240
241 memcpy(pSection->rgcbContainers, pBurnSectionHeader->rgcbContainers, sizeof(DWORD) * pSection->cContainers);
242
243 // TODO: verify more than just the GUID.
244 hr = VerifySectionMatchesMemoryPEHeader(pBurnSectionHeader->guidBundleId);
245 ExitOnRootFailure(hr, "PE Header from file didn't match PE Header in memory.");
246
247LExit:
248 ReleaseMem(pBurnSectionHeader);
249
250 return hr;
251}
252
253extern "C" void SectionUninitialize(
254 __out BURN_SECTION* pSection
255 )
256{
257 ReleaseMem(pSection->rgcbContainers);
258 memset(pSection, 0, sizeof(BURN_SECTION));
259}
260
261extern "C" HRESULT SectionGetAttachedContainerInfo(
262 __in BURN_SECTION* pSection,
263 __in DWORD iContainerIndex,
264 __in DWORD dwExpectedType,
265 __out DWORD64* pqwOffset,
266 __out DWORD64* pqwSize,
267 __out BOOL* pfPresent
268 )
269{
270 HRESULT hr = S_OK;
271
272 // validate container info
273 if (iContainerIndex >= pSection->cContainers)
274 {
275 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
276 ExitOnRootFailure(hr, "Failed to find container info, too few elements: %u", pSection->cContainers);
277 }
278 else if (dwExpectedType != pSection->dwFormat)
279 {
280 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
281 ExitOnRootFailure(hr, "Unexpected container format.");
282 }
283
284 // If we are asking for the UX container, find it right after the stub.
285 if (0 == iContainerIndex)
286 {
287 *pqwOffset = pSection->cbStub;
288 }
289 else // attached containers start after the whole engine.
290 {
291 *pqwOffset = pSection->cbEngineSize;
292 for (DWORD i = 1; i < iContainerIndex; ++i)
293 {
294 *pqwOffset += pSection->rgcbContainers[i];
295 }
296 }
297
298 *pqwSize = pSection->rgcbContainers[iContainerIndex];
299 *pfPresent = (*pqwOffset + *pqwSize) <= pSection->qwBundleSize;
300
301 AssertSz(*pfPresent || pSection->qwBundleSize <= *pqwOffset, "An attached container should either be present or completely absent from the bundle. Found a case where the attached container is partially present which is wrong.");
302
303LExit:
304 return hr;
305}
306
307HRESULT VerifySectionMatchesMemoryPEHeader(
308 __in REFGUID pBundleId
309 )
310{
311 HRESULT hr = S_OK;
312 BYTE* pbPEHeader = NULL;
313 PIMAGE_DOS_HEADER pDosHeader = NULL;
314 PIMAGE_NT_HEADERS pNtHeader = NULL;
315 PIMAGE_SECTION_HEADER pSections = NULL;
316 PIMAGE_SECTION_HEADER pSectionHeader = NULL;
317 BURN_SECTION_HEADER* pBurnSectionHeader = NULL;
318
319 pbPEHeader = reinterpret_cast<BYTE*>(::GetModuleHandleW(NULL));
320 ExitOnNullWithLastError(pbPEHeader, hr, "Failed to get module handle to process.");
321
322 //
323 // First, make sure we have a valid DOS signature.
324 //
325
326 pDosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(pbPEHeader);
327 if (IMAGE_DOS_SIGNATURE != pDosHeader->e_magic)
328 {
329 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
330 ExitOnRootFailure(hr, "Failed to find valid DOS image header in buffer.");
331 }
332
333 //
334 // Now, make sure we have a valid NT signature.
335 //
336
337 pNtHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(pbPEHeader + pDosHeader->e_lfanew);
338 if (IMAGE_NT_SIGNATURE != pNtHeader->Signature)
339 {
340 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
341 ExitOnRootFailure(hr, "Failed to find valid NT image header in buffer.");
342 }
343
344 //
345 // Finally, get into the section table and look for the Burn section info.
346 //
347
348 pSections = reinterpret_cast<PIMAGE_SECTION_HEADER>(pbPEHeader + pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + pNtHeader->FileHeader.SizeOfOptionalHeader);
349
350 // Read sections one by one until we find our section.
351 for (DWORD i = 0; ; ++i)
352 {
353 pSectionHeader = pSections + i;
354
355 // Compare header name.
356 C_ASSERT(sizeof(pSectionHeader->Name) == sizeof(BURN_SECTION_NAME) - 1);
357 if (0 == memcmp(pSectionHeader->Name, BURN_SECTION_NAME, sizeof(pSectionHeader->Name)))
358 {
359 break;
360 }
361
362 // Fail if we hit the end.
363 if (i + 1 >= pNtHeader->FileHeader.NumberOfSections)
364 {
365 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
366 ExitOnRootFailure(hr, "Failed to find Burn section.");
367 }
368 }
369
370 //
371 // We've arrived at the section info.
372 //
373
374 // Check size of section.
375 if (sizeof(BURN_SECTION_HEADER) > pSectionHeader->SizeOfRawData)
376 {
377 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
378 ExitOnRootFailure(hr, "Failed to read section info, data to short: %u", pSectionHeader->SizeOfRawData);
379 }
380
381 // Get Burn section info.
382 pBurnSectionHeader = reinterpret_cast<BURN_SECTION_HEADER*>(pbPEHeader + pSectionHeader->VirtualAddress);
383
384 // Validate version of section info.
385 if (BURN_SECTION_VERSION != pBurnSectionHeader->dwVersion)
386 {
387 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
388 ExitOnRootFailure(hr, "Failed to read section info, unsupported version: %08x", pBurnSectionHeader->dwVersion);
389 }
390
391 if (!::IsEqualGUID(pBundleId, pBurnSectionHeader->guidBundleId))
392 {
393 hr = E_INVALIDDATA;
394 ExitOnRootFailure(hr, "Bundle guid didn't match the guid in the PE Header in memory.");
395 }
396
397LExit:
398 return hr;
399}
diff --git a/src/burn/engine/section.h b/src/burn/engine/section.h
new file mode 100644
index 00000000..6c62ba44
--- /dev/null
+++ b/src/burn/engine/section.h
@@ -0,0 +1,54 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// structs
11
12typedef struct _BURN_SECTION
13{
14 HANDLE hEngineFile;
15 HANDLE hSourceEngineFile;
16
17 DWORD cbStub;
18 DWORD cbEngineSize; // stub + UX container + original certficiate
19 DWORD64 qwBundleSize; // stub + UX container + original certificate [+ attached containers* + final certificate]
20
21 DWORD dwChecksumOffset;
22 DWORD dwCertificateTableOffset;
23 DWORD_PTR dwOriginalChecksumAndSignatureOffset;
24
25 DWORD dwOriginalChecksum;
26 DWORD dwOriginalSignatureOffset;
27 DWORD dwOriginalSignatureSize;
28
29 DWORD dwFormat;
30 DWORD cContainers;
31 DWORD* rgcbContainers;
32} BURN_SECTION;
33
34
35HRESULT SectionInitialize(
36 __in BURN_SECTION* pSection,
37 __in HANDLE hEngineFile,
38 __in HANDLE hSourceEngineFile
39 );
40void SectionUninitialize(
41 __in BURN_SECTION* pSection
42 );
43HRESULT SectionGetAttachedContainerInfo(
44 __in BURN_SECTION* pSection,
45 __in DWORD iContainerIndex,
46 __in DWORD dwExpectedType,
47 __out DWORD64* pqwOffset,
48 __out DWORD64* pqwSize,
49 __out BOOL* pfPresent
50 );
51
52#if defined(__cplusplus)
53}
54#endif
diff --git a/src/burn/engine/splashscreen.cpp b/src/burn/engine/splashscreen.cpp
new file mode 100644
index 00000000..90bd5203
--- /dev/null
+++ b/src/burn/engine/splashscreen.cpp
@@ -0,0 +1,355 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5#define BURN_SPLASHSCREEN_CLASS_WINDOW L"WixBurnSplashScreen"
6#define IDB_SPLASHSCREEN 1
7
8// struct
9
10struct SPLASHSCREEN_INFO
11{
12 HBITMAP hBitmap;
13 SIZE defaultDpiSize;
14 SIZE size;
15 UINT nDpi;
16 HWND hWnd;
17};
18
19struct SPLASHSCREEN_CONTEXT
20{
21 HANDLE hInitializedEvent;
22 HINSTANCE hInstance;
23 LPCWSTR wzCaption;
24
25 HWND* pHwnd;
26};
27
28// internal function definitions
29
30static DWORD WINAPI ThreadProc(
31 __in LPVOID pvContext
32 );
33static LRESULT CALLBACK WndProc(
34 __in HWND hWnd,
35 __in UINT uMsg,
36 __in WPARAM wParam,
37 __in LPARAM lParam
38 );
39static HRESULT LoadSplashScreen(
40 __in SPLASHSCREEN_CONTEXT* pContext,
41 __in SPLASHSCREEN_INFO* pSplashScreen
42 );
43static BOOL OnDpiChanged(
44 __in SPLASHSCREEN_INFO* pSplashScreen,
45 __in WPARAM wParam,
46 __in LPARAM lParam
47 );
48static void OnEraseBkgnd(
49 __in SPLASHSCREEN_INFO* pSplashScreen,
50 __in WPARAM wParam
51 );
52static void OnNcCreate(
53 __in HWND hWnd,
54 __in LPARAM lParam
55 );
56static void ScaleSplashScreen(
57 __in SPLASHSCREEN_INFO* pSplashScreen,
58 __in UINT nDpi,
59 __in int x,
60 __in int y
61 );
62
63
64// function definitions
65
66extern "C" void SplashScreenCreate(
67 __in HINSTANCE hInstance,
68 __in_z_opt LPCWSTR wzCaption,
69 __out HWND* pHwnd
70 )
71{
72 HRESULT hr = S_OK;
73 SPLASHSCREEN_CONTEXT context = { };
74 HANDLE rgSplashScreenEvents[2] = { };
75 DWORD dwSplashScreenThreadId = 0;
76
77 rgSplashScreenEvents[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL);
78 ExitOnNullWithLastError(rgSplashScreenEvents[0], hr, "Failed to create modal event.");
79
80 // create splash screen thread.
81 context.hInitializedEvent = rgSplashScreenEvents[0];
82 context.hInstance = hInstance;
83 context.wzCaption = wzCaption;
84 context.pHwnd = pHwnd;
85
86 rgSplashScreenEvents[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, &dwSplashScreenThreadId);
87 ExitOnNullWithLastError(rgSplashScreenEvents[1], hr, "Failed to create UI thread.");
88
89 // It doesn't really matter if the thread gets initialized (WAIT_OBJECT_0) or fails and exits
90 // prematurely (WAIT_OBJECT_0 + 1), we just want to wait long enough for one of those two
91 // events to happen.
92 ::WaitForMultipleObjects(countof(rgSplashScreenEvents), rgSplashScreenEvents, FALSE, INFINITE);
93
94LExit:
95 ReleaseHandle(rgSplashScreenEvents[1]);
96 ReleaseHandle(rgSplashScreenEvents[0]);
97}
98
99extern "C" HRESULT SplashScreenDisplayError(
100 __in BOOTSTRAPPER_DISPLAY display,
101 __in_z LPCWSTR wzBundleName,
102 __in HRESULT hrError
103 )
104{
105 HRESULT hr = S_OK;
106 LPWSTR sczDisplayString = NULL;
107
108 hr = StrAllocFromError(&sczDisplayString, hrError, NULL);
109 ExitOnFailure(hr, "Failed to allocate string to display error message");
110
111 Trace(REPORT_STANDARD, "Error message displayed because: %ls", sczDisplayString);
112
113 if (BOOTSTRAPPER_DISPLAY_NONE == display || BOOTSTRAPPER_DISPLAY_PASSIVE == display || BOOTSTRAPPER_DISPLAY_EMBEDDED == display)
114 {
115 // Don't display the error dialog in these modes
116 ExitFunction1(hr = S_OK);
117 }
118
119 ::MessageBoxW(NULL, sczDisplayString, wzBundleName, MB_OK | MB_ICONERROR | MB_SYSTEMMODAL);
120
121LExit:
122 ReleaseStr(sczDisplayString);
123
124 return hr;
125}
126
127
128static DWORD WINAPI ThreadProc(
129 __in LPVOID pvContext
130 )
131{
132 HRESULT hr = S_OK;
133 SPLASHSCREEN_CONTEXT* pContext = static_cast<SPLASHSCREEN_CONTEXT*>(pvContext);
134
135 SPLASHSCREEN_INFO splashScreenInfo = { };
136
137 WNDCLASSW wc = { };
138 BOOL fRegistered = TRUE;
139
140 BOOL fRet = FALSE;
141 MSG msg = { };
142
143 // Register the window class.
144 wc.lpfnWndProc = WndProc;
145 wc.hInstance = pContext->hInstance;
146 wc.hCursor = ::LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
147 wc.lpszClassName = BURN_SPLASHSCREEN_CLASS_WINDOW;
148 if (!::RegisterClassW(&wc))
149 {
150 ExitWithLastError(hr, "Failed to register window.");
151 }
152
153 fRegistered = TRUE;
154
155 hr = LoadSplashScreen(pContext, &splashScreenInfo);
156 ExitOnFailure(hr, "Failed to load splash screen.");
157
158 // Return the splash screen window and free the main thread waiting for us to be initialized.
159 *pContext->pHwnd = splashScreenInfo.hWnd;
160 ::SetEvent(pContext->hInitializedEvent);
161
162 // Pump messages until the bootstrapper application destroys the window.
163 while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
164 {
165 if (-1 == fRet)
166 {
167 hr = E_UNEXPECTED;
168 ExitOnFailure(hr, "Unexpected return value from message pump.");
169 }
170 else if (!::IsDialogMessageW(splashScreenInfo.hWnd, &msg))
171 {
172 ::TranslateMessage(&msg);
173 ::DispatchMessageW(&msg);
174 }
175 }
176
177LExit:
178 if (fRegistered)
179 {
180 ::UnregisterClassW(BURN_SPLASHSCREEN_CLASS_WINDOW, pContext->hInstance);
181 }
182
183 if (splashScreenInfo.hBitmap)
184 {
185 ::DeleteObject(splashScreenInfo.hBitmap);
186 }
187
188 return hr;
189}
190
191static LRESULT CALLBACK WndProc(
192 __in HWND hWnd,
193 __in UINT uMsg,
194 __in WPARAM wParam,
195 __in LPARAM lParam
196 )
197{
198 LRESULT lres = 0;
199 SPLASHSCREEN_INFO* pSplashScreen = reinterpret_cast<SPLASHSCREEN_INFO*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
200
201 switch (uMsg)
202 {
203 case WM_NCCREATE:
204 OnNcCreate(hWnd, lParam);
205 break;
206
207 case WM_NCDESTROY:
208 lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
209 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
210 ::PostQuitMessage(0);
211 return lres;
212
213 case WM_NCHITTEST:
214 return HTCAPTION; // allow window to be moved by grabbing any pixel.
215
216 case WM_DPICHANGED:
217 if (OnDpiChanged(pSplashScreen, wParam, lParam))
218 {
219 return 0;
220 }
221 break;
222
223 case WM_ERASEBKGND:
224 OnEraseBkgnd(pSplashScreen, wParam);
225 return 1;
226 }
227
228 return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
229}
230
231static HRESULT LoadSplashScreen(
232 __in SPLASHSCREEN_CONTEXT* pContext,
233 __in SPLASHSCREEN_INFO* pSplashScreen
234 )
235{
236 HRESULT hr = S_OK;
237 BITMAP bmp = { };
238 POINT pt = { };
239 int x = 0;
240 int y = 0;
241 DPIU_MONITOR_CONTEXT* pMonitorContext = NULL;
242 RECT* pMonitorRect = NULL;
243
244 pSplashScreen->nDpi = USER_DEFAULT_SCREEN_DPI;
245 pSplashScreen->hBitmap = ::LoadBitmapW(pContext->hInstance, MAKEINTRESOURCEW(IDB_SPLASHSCREEN));
246 ExitOnNullWithLastError(pSplashScreen->hBitmap, hr, "Failed to load splash screen bitmap.");
247
248 ::GetObject(pSplashScreen->hBitmap, sizeof(bmp), static_cast<void*>(&bmp));
249 pSplashScreen->defaultDpiSize.cx = pSplashScreen->size.cx = bmp.bmWidth;
250 pSplashScreen->defaultDpiSize.cy = pSplashScreen->size.cy = bmp.bmHeight;
251
252 // Try to default to the monitor with the mouse, otherwise default to the primary monitor.
253 if (!::GetCursorPos(&pt))
254 {
255 pt.x = 0;
256 pt.y = 0;
257 }
258
259 // Try to center the window on the chosen monitor.
260 hr = DpiuGetMonitorContextFromPoint(&pt, &pMonitorContext);
261 if (SUCCEEDED(hr))
262 {
263 pMonitorRect = &pMonitorContext->mi.rcWork;
264 if (pMonitorContext->nDpi != pSplashScreen->nDpi)
265 {
266 ScaleSplashScreen(pSplashScreen, pMonitorContext->nDpi, pMonitorRect->left, pMonitorRect->top);
267 }
268
269 x = pMonitorRect->left + (pMonitorRect->right - pMonitorRect->left - pSplashScreen->size.cx) / 2;
270 y = pMonitorRect->top + (pMonitorRect->bottom - pMonitorRect->top - pSplashScreen->size.cy) / 2;
271 }
272 else
273 {
274 hr = S_OK;
275 x = CW_USEDEFAULT;
276 y = CW_USEDEFAULT;
277 }
278
279 pSplashScreen->hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, BURN_SPLASHSCREEN_CLASS_WINDOW, pContext->wzCaption, WS_POPUP | WS_VISIBLE, x, y, pSplashScreen->size.cx, pSplashScreen->size.cy, HWND_DESKTOP, NULL, pContext->hInstance, pSplashScreen);
280 ExitOnNullWithLastError(pSplashScreen->hWnd, hr, "Failed to create window.");
281
282LExit:
283 MemFree(pMonitorContext);
284
285 return hr;
286}
287
288static BOOL OnDpiChanged(
289 __in SPLASHSCREEN_INFO* pSplashScreen,
290 __in WPARAM wParam,
291 __in LPARAM lParam
292 )
293{
294 UINT nDpi = HIWORD(wParam);
295 RECT* pRect = reinterpret_cast<RECT*>(lParam);
296 BOOL fDpiChanged = pSplashScreen->nDpi != nDpi;
297
298 if (fDpiChanged)
299 {
300 ScaleSplashScreen(pSplashScreen, nDpi, pRect->left, pRect->top);
301 }
302
303 return fDpiChanged;
304}
305
306static void OnEraseBkgnd(
307 __in SPLASHSCREEN_INFO* pSplashScreen,
308 __in WPARAM wParam
309 )
310{
311 HDC hdc = reinterpret_cast<HDC>(wParam);
312 HDC hdcMem = ::CreateCompatibleDC(hdc);
313 HBITMAP hDefaultBitmap = static_cast<HBITMAP>(::SelectObject(hdcMem, pSplashScreen->hBitmap));
314 ::StretchBlt(hdc, 0, 0, pSplashScreen->size.cx, pSplashScreen->size.cy, hdcMem, 0, 0, pSplashScreen->defaultDpiSize.cx, pSplashScreen->defaultDpiSize.cy, SRCCOPY);
315 ::SelectObject(hdcMem, hDefaultBitmap);
316 ::DeleteDC(hdcMem);
317}
318
319static void OnNcCreate(
320 __in HWND hWnd,
321 __in LPARAM lParam
322 )
323{
324 DPIU_WINDOW_CONTEXT windowContext = { };
325 CREATESTRUCTW* pCreateStruct = reinterpret_cast<CREATESTRUCTW*>(lParam);
326 SPLASHSCREEN_INFO* pSplashScreen = reinterpret_cast<SPLASHSCREEN_INFO*>(pCreateStruct->lpCreateParams);
327
328 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pSplashScreen));
329 pSplashScreen->hWnd = hWnd;
330
331 DpiuGetWindowContext(pSplashScreen->hWnd, &windowContext);
332
333 if (windowContext.nDpi != pSplashScreen->nDpi)
334 {
335 ScaleSplashScreen(pSplashScreen, windowContext.nDpi, pCreateStruct->x, pCreateStruct->y);
336 }
337}
338
339static void ScaleSplashScreen(
340 __in SPLASHSCREEN_INFO* pSplashScreen,
341 __in UINT nDpi,
342 __in int x,
343 __in int y
344 )
345{
346 pSplashScreen->nDpi = nDpi;
347
348 pSplashScreen->size.cx = DpiuScaleValue(pSplashScreen->defaultDpiSize.cx, pSplashScreen->nDpi);
349 pSplashScreen->size.cy = DpiuScaleValue(pSplashScreen->defaultDpiSize.cy, pSplashScreen->nDpi);
350
351 if (pSplashScreen->hWnd)
352 {
353 ::SetWindowPos(pSplashScreen->hWnd, NULL, x, y, pSplashScreen->size.cx, pSplashScreen->size.cy, SWP_NOACTIVATE | SWP_NOZORDER);
354 }
355}
diff --git a/src/burn/engine/splashscreen.h b/src/burn/engine/splashscreen.h
new file mode 100644
index 00000000..8f8817c7
--- /dev/null
+++ b/src/burn/engine/splashscreen.h
@@ -0,0 +1,31 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// constants
11
12
13// structs
14
15
16// functions
17
18void SplashScreenCreate(
19 __in HINSTANCE hInstance,
20 __in_z_opt LPCWSTR wzCaption,
21 __out HWND* pHwnd
22 );
23HRESULT SplashScreenDisplayError(
24 __in BOOTSTRAPPER_DISPLAY display,
25 __in_z LPCWSTR wzBundleName,
26 __in HRESULT hrError
27 );
28
29#if defined(__cplusplus)
30}
31#endif
diff --git a/src/burn/engine/uithread.cpp b/src/burn/engine/uithread.cpp
new file mode 100644
index 00000000..433cb171
--- /dev/null
+++ b/src/burn/engine/uithread.cpp
@@ -0,0 +1,222 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5#define BURN_UITHREAD_CLASS_WINDOW L"WixBurnMessageWindow"
6
7
8// structs
9
10struct UITHREAD_CONTEXT
11{
12 HANDLE hInitializedEvent;
13 HINSTANCE hInstance;
14 BURN_ENGINE_STATE* pEngineState;
15};
16
17struct UITHREAD_INFO
18{
19 BOOL fElevated;
20 BURN_USER_EXPERIENCE* pUserExperience;
21};
22
23
24// internal function declarations
25
26static DWORD WINAPI ThreadProc(
27 __in LPVOID pvContext
28 );
29
30static LRESULT CALLBACK WndProc(
31 __in HWND hWnd,
32 __in UINT uMsg,
33 __in WPARAM wParam,
34 __in LPARAM lParam
35 );
36
37
38// function definitions
39
40HRESULT UiCreateMessageWindow(
41 __in HINSTANCE hInstance,
42 __in BURN_ENGINE_STATE* pEngineState
43 )
44{
45 HRESULT hr = S_OK;
46 HANDLE rgWaitHandles[2] = { };
47 UITHREAD_CONTEXT context = { };
48
49 // Create event to signal after the UI thread / window is initialized.
50 rgWaitHandles[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL);
51 ExitOnNullWithLastError(rgWaitHandles[0], hr, "Failed to create initialization event.");
52
53 // Pass necessary information to create the window.
54 context.hInitializedEvent = rgWaitHandles[0];
55 context.hInstance = hInstance;
56 context.pEngineState = pEngineState;
57
58 // Create our separate UI thread.
59 rgWaitHandles[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, NULL);
60 ExitOnNullWithLastError(rgWaitHandles[1], hr, "Failed to create the UI thread.");
61
62 // Wait for either the thread to be initialized or the window to exit / fail prematurely.
63 ::WaitForMultipleObjects(countof(rgWaitHandles), rgWaitHandles, FALSE, INFINITE);
64
65 pEngineState->hMessageWindowThread = rgWaitHandles[1];
66 rgWaitHandles[1] = NULL;
67
68LExit:
69 ReleaseHandle(rgWaitHandles[1]);
70 ReleaseHandle(rgWaitHandles[0]);
71
72 return hr;
73}
74
75void UiCloseMessageWindow(
76 __in BURN_ENGINE_STATE* pEngineState
77 )
78{
79 if (::IsWindow(pEngineState->hMessageWindow))
80 {
81 ::PostMessageW(pEngineState->hMessageWindow, WM_CLOSE, 0, 0);
82
83 // Give the window 15 seconds to close because if it stays open it can prevent
84 // the engine from starting a reboot (should a reboot actually be necessary).
85 ::WaitForSingleObject(pEngineState->hMessageWindowThread, 15 * 1000);
86 }
87}
88
89
90// internal function definitions
91
92static DWORD WINAPI ThreadProc(
93 __in LPVOID pvContext
94 )
95{
96 HRESULT hr = S_OK;
97 UITHREAD_CONTEXT* pContext = static_cast<UITHREAD_CONTEXT*>(pvContext);
98 UITHREAD_INFO info = { };
99
100 WNDCLASSW wc = { };
101 BOOL fRegistered = TRUE;
102 HWND hWnd = NULL;
103
104 BOOL fRet = FALSE;
105 MSG msg = { };
106
107 BURN_ENGINE_STATE* pEngineState = pContext->pEngineState;
108 BOOL fElevated = BURN_MODE_ELEVATED == pContext->pEngineState->mode;
109
110 // If elevated, set up the thread local storage to store the correct pipe to communicate logging.
111 if (fElevated)
112 {
113 Assert(TLS_OUT_OF_INDEXES != pEngineState->dwElevatedLoggingTlsId);
114
115 if (!::TlsSetValue(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe))
116 {
117 // If the function failed we cannot write to the pipe so just terminate.
118 ExitFunction1(hr = E_INVALIDSTATE);
119 }
120 }
121
122 wc.lpfnWndProc = WndProc;
123 wc.hInstance = pContext->hInstance;
124 wc.lpszClassName = BURN_UITHREAD_CLASS_WINDOW;
125
126 if (!::RegisterClassW(&wc))
127 {
128 ExitWithLastError(hr, "Failed to register window.");
129 }
130
131 fRegistered = TRUE;
132
133 info.fElevated = fElevated;
134 info.pUserExperience = &pEngineState->userExperience;
135
136 // Create the window to handle reboots without activating it.
137 hWnd = ::CreateWindowExW(WS_EX_NOACTIVATE, wc.lpszClassName, NULL, WS_POPUP, 0, 0, 0, 0, HWND_DESKTOP, NULL, pContext->hInstance, &info);
138 ExitOnNullWithLastError(hWnd, hr, "Failed to create window.");
139
140 ::ShowWindow(hWnd, SW_SHOWNA);
141
142 // Persist the window handle and let the caller know we've initialized.
143 pEngineState->hMessageWindow = hWnd;
144 ::SetEvent(pContext->hInitializedEvent);
145
146 // Pump messages until the window is closed.
147 while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
148 {
149 if (-1 == fRet)
150 {
151 hr = E_UNEXPECTED;
152 ExitOnFailure(hr, "Unexpected return value from message pump.");
153 }
154 else if (!::IsDialogMessageW(msg.hwnd, &msg))
155 {
156 ::TranslateMessage(&msg);
157 ::DispatchMessageW(&msg);
158 }
159 }
160
161LExit:
162 if (fRegistered)
163 {
164 ::UnregisterClassW(BURN_UITHREAD_CLASS_WINDOW, pContext->hInstance);
165 }
166
167 return hr;
168}
169
170static LRESULT CALLBACK WndProc(
171 __in HWND hWnd,
172 __in UINT uMsg,
173 __in WPARAM wParam,
174 __in LPARAM lParam
175 )
176{
177 switch (uMsg)
178 {
179 case WM_NCCREATE:
180 {
181 LPCREATESTRUCTW lpcs = reinterpret_cast<LPCREATESTRUCTW>(lParam);
182 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(lpcs->lpCreateParams));
183 break;
184 }
185
186 case WM_NCDESTROY:
187 {
188 LRESULT lRes = ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
189 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
190 return lRes;
191 }
192
193 case WM_QUERYENDSESSION:
194 {
195 DWORD dwEndSession = static_cast<DWORD>(lParam);
196 BOOL fCritical = ENDSESSION_CRITICAL & dwEndSession;
197 BOOL fCancel = TRUE;
198 BOOL fRet = FALSE;
199
200 // Always block shutdown in the elevated process, but ask the BA in the non-elevated.
201 UITHREAD_INFO* pInfo = reinterpret_cast<UITHREAD_INFO*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
202 if (!pInfo->fElevated)
203 {
204 // TODO: instead of recommending canceling all non-critical shutdowns, maybe we should only recommend cancel
205 // when the engine is doing work?
206 fCancel = !fCritical;
207 // TODO: There's a race condition here where the BA may not have been loaded, or already was unloaded.
208 UserExperienceOnSystemShutdown(pInfo->pUserExperience, dwEndSession, &fCancel);
209 }
210
211 fRet = !fCancel;
212 LogId(REPORT_STANDARD, MSG_SYSTEM_SHUTDOWN, LoggingBoolToString(fCritical), LoggingBoolToString(pInfo->fElevated), LoggingBoolToString(fRet));
213 return fRet;
214 }
215
216 case WM_DESTROY:
217 ::PostQuitMessage(0);
218 return 0;
219 }
220
221 return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
222}
diff --git a/src/burn/engine/uithread.h b/src/burn/engine/uithread.h
new file mode 100644
index 00000000..41168d52
--- /dev/null
+++ b/src/burn/engine/uithread.h
@@ -0,0 +1,23 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// functions
11
12HRESULT UiCreateMessageWindow(
13 __in HINSTANCE hInstance,
14 __in BURN_ENGINE_STATE* pEngineState
15 );
16
17void UiCloseMessageWindow(
18 __in BURN_ENGINE_STATE* pEngineState
19 );
20
21#if defined(__cplusplus)
22}
23#endif
diff --git a/src/burn/engine/update.cpp b/src/burn/engine/update.cpp
new file mode 100644
index 00000000..b04fa9a4
--- /dev/null
+++ b/src/burn/engine/update.cpp
@@ -0,0 +1,44 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// internal function declarations
7
8
9// function definitions
10
11extern "C" HRESULT UpdateParseFromXml(
12 __in BURN_UPDATE* pUpdate,
13 __in IXMLDOMNode* pixnBundle
14 )
15{
16 HRESULT hr = S_OK;
17 IXMLDOMNode* pixnUpdateNode = NULL;
18
19 hr = XmlSelectSingleNode(pixnBundle, L"Update", &pixnUpdateNode);
20 if (S_FALSE == hr)
21 {
22 ExitFunction1(hr = S_OK);
23 }
24 ExitOnFailure(hr, "Failed to select Bundle/Update node.");
25
26 // @Location
27 hr = XmlGetAttributeEx(pixnUpdateNode, L"Location", &pUpdate->sczUpdateSource);
28 ExitOnFailure(hr, "Failed to get Update@Location.");
29
30LExit:
31 ReleaseObject(pixnUpdateNode);
32
33 return hr;
34}
35
36extern "C" void UpdateUninitialize(
37 __in BURN_UPDATE* pUpdate
38 )
39{
40 PackageUninitialize(&pUpdate->package);
41
42 ReleaseStr(pUpdate->sczUpdateSource);
43 memset(pUpdate, 0, sizeof(BURN_UPDATE));
44}
diff --git a/src/burn/engine/update.h b/src/burn/engine/update.h
new file mode 100644
index 00000000..67d40481
--- /dev/null
+++ b/src/burn/engine/update.h
@@ -0,0 +1,33 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// structs
11
12typedef struct _BURN_UPDATE
13{
14 BOOL fUpdateAvailable;
15 LPWSTR sczUpdateSource;
16
17 BURN_PACKAGE package;
18} BURN_UPDATE;
19
20
21// function declarations
22
23HRESULT UpdateParseFromXml(
24 __in BURN_UPDATE* pUpdate,
25 __in IXMLDOMNode* pixnBundle
26 );
27void UpdateUninitialize(
28 __in BURN_UPDATE* pUpdate
29 );
30
31#if defined(__cplusplus)
32}
33#endif
diff --git a/src/burn/engine/userexperience.cpp b/src/burn/engine/userexperience.cpp
new file mode 100644
index 00000000..2215a070
--- /dev/null
+++ b/src/burn/engine/userexperience.cpp
@@ -0,0 +1,2653 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5// internal function declarations
6
7static int FilterResult(
8 __in DWORD dwAllowedResults,
9 __in int nResult
10 );
11
12static HRESULT FilterExecuteResult(
13 __in BURN_USER_EXPERIENCE* pUserExperience,
14 __in HRESULT hrStatus,
15 __in BOOL fRollback,
16 __in BOOL fCancel,
17 __in LPCWSTR sczEventName
18 );
19
20static HRESULT SendBAMessage(
21 __in BURN_USER_EXPERIENCE* pUserExperience,
22 __in BOOTSTRAPPER_APPLICATION_MESSAGE message,
23 __in const LPVOID pvArgs,
24 __inout LPVOID pvResults
25 );
26
27static HRESULT SendBAMessageFromInactiveEngine(
28 __in BURN_USER_EXPERIENCE* pUserExperience,
29 __in BOOTSTRAPPER_APPLICATION_MESSAGE message,
30 __in const LPVOID pvArgs,
31 __inout LPVOID pvResults
32 );
33
34
35// function definitions
36
37/*******************************************************************
38 UserExperienceParseFromXml -
39
40*******************************************************************/
41extern "C" HRESULT UserExperienceParseFromXml(
42 __in BURN_USER_EXPERIENCE* pUserExperience,
43 __in IXMLDOMNode* pixnBundle
44 )
45{
46 HRESULT hr = S_OK;
47 IXMLDOMNode* pixnUserExperienceNode = NULL;
48
49 // select UX node
50 hr = XmlSelectSingleNode(pixnBundle, L"UX", &pixnUserExperienceNode);
51 if (S_FALSE == hr)
52 {
53 hr = E_NOTFOUND;
54 }
55 ExitOnFailure(hr, "Failed to select user experience node.");
56
57 // parse splash screen
58 hr = XmlGetYesNoAttribute(pixnUserExperienceNode, L"SplashScreen", &pUserExperience->fSplashScreen);
59 if (E_NOTFOUND != hr)
60 {
61 ExitOnFailure(hr, "Failed to to get UX/@SplashScreen");
62 }
63
64 // parse payloads
65 hr = PayloadsParseFromXml(&pUserExperience->payloads, NULL, NULL, pixnUserExperienceNode);
66 ExitOnFailure(hr, "Failed to parse user experience payloads.");
67
68 // make sure we have at least one payload
69 if (0 == pUserExperience->payloads.cPayloads)
70 {
71 hr = E_UNEXPECTED;
72 ExitOnFailure(hr, "Too few UX payloads.");
73 }
74
75LExit:
76 ReleaseObject(pixnUserExperienceNode);
77
78 return hr;
79}
80
81/*******************************************************************
82 UserExperienceUninitialize -
83
84*******************************************************************/
85extern "C" void UserExperienceUninitialize(
86 __in BURN_USER_EXPERIENCE* pUserExperience
87 )
88{
89 ReleaseStr(pUserExperience->sczTempDirectory);
90 PayloadsUninitialize(&pUserExperience->payloads);
91
92 // clear struct
93 memset(pUserExperience, 0, sizeof(BURN_USER_EXPERIENCE));
94}
95
96/*******************************************************************
97 UserExperienceLoad -
98
99*******************************************************************/
100extern "C" HRESULT UserExperienceLoad(
101 __in BURN_USER_EXPERIENCE* pUserExperience,
102 __in BOOTSTRAPPER_ENGINE_CONTEXT* pEngineContext,
103 __in BOOTSTRAPPER_COMMAND* pCommand
104 )
105{
106 HRESULT hr = S_OK;
107 BOOTSTRAPPER_CREATE_ARGS args = { };
108 BOOTSTRAPPER_CREATE_RESULTS results = { };
109
110 args.cbSize = sizeof(BOOTSTRAPPER_CREATE_ARGS);
111 args.pCommand = pCommand;
112 args.pfnBootstrapperEngineProc = EngineForApplicationProc;
113 args.pvBootstrapperEngineProcContext = pEngineContext;
114 args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 4, 27, 0);
115
116 results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS);
117
118 // Load BA DLL.
119 pUserExperience->hUXModule = ::LoadLibraryExW(pUserExperience->payloads.rgPayloads[0].sczLocalFilePath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
120 ExitOnNullWithLastError(pUserExperience->hUXModule, hr, "Failed to load BA DLL.");
121
122 // Get BootstrapperApplicationCreate entry-point.
123 PFN_BOOTSTRAPPER_APPLICATION_CREATE pfnCreate = (PFN_BOOTSTRAPPER_APPLICATION_CREATE)::GetProcAddress(pUserExperience->hUXModule, "BootstrapperApplicationCreate");
124 ExitOnNullWithLastError(pfnCreate, hr, "Failed to get BootstrapperApplicationCreate entry-point");
125
126 // Create BA.
127 hr = pfnCreate(&args, &results);
128 ExitOnFailure(hr, "Failed to create BA.");
129
130 pUserExperience->pfnBAProc = results.pfnBootstrapperApplicationProc;
131 pUserExperience->pvBAProcContext = results.pvBootstrapperApplicationProcContext;
132 pUserExperience->fDisableUnloading = results.fDisableUnloading;
133
134LExit:
135 return hr;
136}
137
138/*******************************************************************
139 UserExperienceUnload -
140
141*******************************************************************/
142extern "C" HRESULT UserExperienceUnload(
143 __in BURN_USER_EXPERIENCE* pUserExperience
144 )
145{
146 HRESULT hr = S_OK;
147
148 if (pUserExperience->hUXModule)
149 {
150 // Get BootstrapperApplicationDestroy entry-point and call it if it exists.
151 PFN_BOOTSTRAPPER_APPLICATION_DESTROY pfnDestroy = (PFN_BOOTSTRAPPER_APPLICATION_DESTROY)::GetProcAddress(pUserExperience->hUXModule, "BootstrapperApplicationDestroy");
152 if (pfnDestroy)
153 {
154 pfnDestroy();
155 }
156
157 // Free BA DLL if it supports it.
158 if (!pUserExperience->fDisableUnloading && !::FreeLibrary(pUserExperience->hUXModule))
159 {
160 hr = HRESULT_FROM_WIN32(::GetLastError());
161 TraceError(hr, "Failed to unload BA DLL.");
162 }
163 pUserExperience->hUXModule = NULL;
164 }
165
166//LExit:
167 return hr;
168}
169
170extern "C" HRESULT UserExperienceEnsureWorkingFolder(
171 __in LPCWSTR wzBundleId,
172 __deref_out_z LPWSTR* psczUserExperienceWorkingFolder
173 )
174{
175 HRESULT hr = S_OK;
176 LPWSTR sczWorkingFolder = NULL;
177
178 hr = CacheEnsureWorkingFolder(wzBundleId, &sczWorkingFolder);
179 ExitOnFailure(hr, "Failed to create working folder.");
180
181 hr = StrAllocFormatted(psczUserExperienceWorkingFolder, L"%ls%ls\\", sczWorkingFolder, L".ba");
182 ExitOnFailure(hr, "Failed to calculate the bootstrapper application working path.");
183
184 hr = DirEnsureExists(*psczUserExperienceWorkingFolder, NULL);
185 ExitOnFailure(hr, "Failed create bootstrapper application working folder.");
186
187LExit:
188 ReleaseStr(sczWorkingFolder);
189
190 return hr;
191}
192
193
194extern "C" HRESULT UserExperienceRemove(
195 __in BURN_USER_EXPERIENCE* pUserExperience
196 )
197{
198 HRESULT hr = S_OK;
199
200 // Remove temporary UX directory
201 if (pUserExperience->sczTempDirectory)
202 {
203 hr = DirEnsureDeleteEx(pUserExperience->sczTempDirectory, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE);
204 TraceError(hr, "Could not delete bootstrapper application folder. Some files will be left in the temp folder.");
205 }
206
207//LExit:
208 return hr;
209}
210
211extern "C" int UserExperienceSendError(
212 __in BURN_USER_EXPERIENCE* pUserExperience,
213 __in BOOTSTRAPPER_ERROR_TYPE errorType,
214 __in_z_opt LPCWSTR wzPackageId,
215 __in HRESULT hrCode,
216 __in_z_opt LPCWSTR wzError,
217 __in DWORD uiFlags,
218 __in int nRecommendation
219 )
220{
221 int nResult = nRecommendation;
222 DWORD dwCode = HRESULT_CODE(hrCode);
223 LPWSTR sczError = NULL;
224
225 // If no error string was provided, try to get the error string from the HRESULT.
226 if (!wzError)
227 {
228 if (SUCCEEDED(StrAllocFromError(&sczError, hrCode, NULL)))
229 {
230 wzError = sczError;
231 }
232 }
233
234 UserExperienceOnError(pUserExperience, errorType, wzPackageId, dwCode, wzError, uiFlags, 0, NULL, &nResult); // ignore return value.
235
236 ReleaseStr(sczError);
237 return nResult;
238}
239
240extern "C" void UserExperienceActivateEngine(
241 __in BURN_USER_EXPERIENCE* pUserExperience
242 )
243{
244 ::EnterCriticalSection(&pUserExperience->csEngineActive);
245 AssertSz(!pUserExperience->fEngineActive, "Engine should have been deactivated before activating it.");
246 pUserExperience->fEngineActive = TRUE;
247 ::LeaveCriticalSection(&pUserExperience->csEngineActive);
248}
249
250extern "C" void UserExperienceDeactivateEngine(
251 __in BURN_USER_EXPERIENCE* pUserExperience
252 )
253{
254 ::EnterCriticalSection(&pUserExperience->csEngineActive);
255 AssertSz(pUserExperience->fEngineActive, "Engine should have been active before deactivating it.");
256 pUserExperience->fEngineActive = FALSE;
257 ::LeaveCriticalSection(&pUserExperience->csEngineActive);
258}
259
260extern "C" HRESULT UserExperienceEnsureEngineInactive(
261 __in BURN_USER_EXPERIENCE* pUserExperience
262 )
263{
264 // Make a slight optimization here by ignoring the critical section, because all callers should have needed to enter it for their operation anyway.
265 HRESULT hr = pUserExperience->fEngineActive ? HRESULT_FROM_WIN32(ERROR_BUSY) : S_OK;
266 ExitOnRootFailure(hr, "Engine is active, cannot proceed.");
267
268LExit:
269 return hr;
270}
271
272extern "C" void UserExperienceExecuteReset(
273 __in BURN_USER_EXPERIENCE* pUserExperience
274 )
275{
276 pUserExperience->hrApplyError = S_OK;
277 pUserExperience->hwndApply = NULL;
278}
279
280extern "C" void UserExperienceExecutePhaseComplete(
281 __in BURN_USER_EXPERIENCE* pUserExperience,
282 __in HRESULT hrResult
283 )
284{
285 if (FAILED(hrResult))
286 {
287 pUserExperience->hrApplyError = hrResult;
288 }
289}
290
291EXTERN_C BAAPI UserExperienceOnApplyBegin(
292 __in BURN_USER_EXPERIENCE* pUserExperience,
293 __in DWORD dwPhaseCount
294 )
295{
296 HRESULT hr = S_OK;
297 BA_ONAPPLYBEGIN_ARGS args = { };
298 BA_ONAPPLYBEGIN_RESULTS results = { };
299
300 args.cbSize = sizeof(args);
301 args.dwPhaseCount = dwPhaseCount;
302
303 results.cbSize = sizeof(results);
304
305 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYBEGIN, &args, &results);
306 ExitOnFailure(hr, "BA OnApplyBegin failed.");
307
308 if (results.fCancel)
309 {
310 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
311 }
312
313LExit:
314 return hr;
315}
316
317EXTERN_C BAAPI UserExperienceOnApplyComplete(
318 __in BURN_USER_EXPERIENCE* pUserExperience,
319 __in HRESULT hrStatus,
320 __in BOOTSTRAPPER_APPLY_RESTART restart,
321 __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction
322 )
323{
324 HRESULT hr = S_OK;
325 BA_ONAPPLYCOMPLETE_ARGS args = { };
326 BA_ONAPPLYCOMPLETE_RESULTS results = { };
327
328 args.cbSize = sizeof(args);
329 args.hrStatus = hrStatus;
330 args.restart = restart;
331 args.recommendation = *pAction;
332
333 results.cbSize = sizeof(results);
334 results.action = *pAction;
335
336 hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYCOMPLETE, &args, &results);
337 ExitOnFailure(hr, "BA OnApplyComplete failed.");
338
339 *pAction = results.action;
340
341LExit:
342 return hr;
343}
344
345EXTERN_C BAAPI UserExperienceOnBeginMsiTransactionBegin(
346 __in BURN_USER_EXPERIENCE* pUserExperience,
347 __in LPCWSTR wzTransactionId
348 )
349{
350 HRESULT hr = S_OK;
351 BA_ONBEGINMSITRANSACTIONBEGIN_ARGS args = { };
352 BA_ONBEGINMSITRANSACTIONBEGIN_RESULTS results = { };
353
354 args.cbSize = sizeof(args);
355 args.wzTransactionId = wzTransactionId;
356
357 results.cbSize = sizeof(results);
358
359 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONBEGINMSITRANSACTIONBEGIN, &args, &results);
360 ExitOnFailure(hr, "BA OnBeginMsiTransactionBegin failed.");
361
362 if (results.fCancel)
363 {
364 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
365 }
366
367LExit:
368 return hr;
369}
370
371EXTERN_C BAAPI UserExperienceOnBeginMsiTransactionComplete(
372 __in BURN_USER_EXPERIENCE* pUserExperience,
373 __in LPCWSTR wzTransactionId,
374 __in HRESULT hrStatus
375 )
376{
377 HRESULT hr = S_OK;
378 BA_ONBEGINMSITRANSACTIONCOMPLETE_ARGS args = { };
379 BA_ONBEGINMSITRANSACTIONCOMPLETE_RESULTS results = { };
380
381 args.cbSize = sizeof(args);
382 args.wzTransactionId = wzTransactionId;
383 args.hrStatus = hrStatus;
384
385 results.cbSize = sizeof(results);
386
387 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONBEGINMSITRANSACTIONCOMPLETE, &args, &results);
388 ExitOnFailure(hr, "BA OnBeginMsiTransactionComplete failed.");
389
390LExit:
391 return hr;
392}
393
394EXTERN_C BAAPI UserExperienceOnCacheAcquireBegin(
395 __in BURN_USER_EXPERIENCE* pUserExperience,
396 __in_z_opt LPCWSTR wzPackageOrContainerId,
397 __in_z_opt LPCWSTR wzPayloadId,
398 __in_z LPWSTR* pwzSource,
399 __in_z LPWSTR* pwzDownloadUrl,
400 __in_z_opt LPCWSTR wzPayloadContainerId,
401 __out BOOTSTRAPPER_CACHE_OPERATION* pCacheOperation
402 )
403{
404 HRESULT hr = S_OK;
405 BA_ONCACHEACQUIREBEGIN_ARGS args = { };
406 BA_ONCACHEACQUIREBEGIN_RESULTS results = { };
407 *pCacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE;
408
409 args.cbSize = sizeof(args);
410 args.wzPackageOrContainerId = wzPackageOrContainerId;
411 args.wzPayloadId = wzPayloadId;
412 args.wzSource = *pwzSource;
413 args.wzDownloadUrl = *pwzDownloadUrl;
414 args.wzPayloadContainerId = wzPayloadContainerId;
415 args.recommendation = *pCacheOperation;
416
417 results.cbSize = sizeof(results);
418 results.action = *pCacheOperation;
419
420 hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREBEGIN, &args, &results);
421 ExitOnFailure(hr, "BA OnCacheAcquireBegin failed.");
422
423 if (results.fCancel)
424 {
425 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
426 }
427 else
428 {
429 // Verify the BA requested an action that is possible.
430 if (BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD == results.action && *pwzDownloadUrl && **pwzDownloadUrl ||
431 BOOTSTRAPPER_CACHE_OPERATION_EXTRACT == results.action && wzPayloadContainerId ||
432 BOOTSTRAPPER_CACHE_OPERATION_COPY == results.action ||
433 BOOTSTRAPPER_CACHE_OPERATION_NONE == results.action)
434 {
435 *pCacheOperation = results.action;
436 }
437 }
438
439LExit:
440 return hr;
441}
442
443EXTERN_C BAAPI UserExperienceOnCacheAcquireComplete(
444 __in BURN_USER_EXPERIENCE* pUserExperience,
445 __in_z_opt LPCWSTR wzPackageOrContainerId,
446 __in_z_opt LPCWSTR wzPayloadId,
447 __in HRESULT hrStatus,
448 __inout BOOL* pfRetry
449 )
450{
451 HRESULT hr = S_OK;
452 BA_ONCACHEACQUIRECOMPLETE_ARGS args = { };
453 BA_ONCACHEACQUIRECOMPLETE_RESULTS results = { };
454
455 args.cbSize = sizeof(args);
456 args.wzPackageOrContainerId = wzPackageOrContainerId;
457 args.wzPayloadId = wzPayloadId;
458 args.hrStatus = hrStatus;
459 args.recommendation = *pfRetry ? BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_RETRY : BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_NONE;
460
461 results.cbSize = sizeof(results);
462 results.action = args.recommendation;
463
464 hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRECOMPLETE, &args, &results);
465 ExitOnFailure(hr, "BA OnCacheAcquireComplete failed.");
466
467 if (FAILED(hrStatus))
468 {
469 *pfRetry = BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_RETRY == results.action;
470 }
471
472LExit:
473 return hr;
474}
475
476EXTERN_C BAAPI UserExperienceOnCacheAcquireProgress(
477 __in BURN_USER_EXPERIENCE* pUserExperience,
478 __in_z_opt LPCWSTR wzPackageOrContainerId,
479 __in_z_opt LPCWSTR wzPayloadId,
480 __in DWORD64 dw64Progress,
481 __in DWORD64 dw64Total,
482 __in DWORD dwOverallPercentage
483 )
484{
485 HRESULT hr = S_OK;
486 BA_ONCACHEACQUIREPROGRESS_ARGS args = { };
487 BA_ONCACHEACQUIREPROGRESS_RESULTS results = { };
488
489 args.cbSize = sizeof(args);
490 args.wzPackageOrContainerId = wzPackageOrContainerId;
491 args.wzPayloadId = wzPayloadId;
492 args.dw64Progress = dw64Progress;
493 args.dw64Total = dw64Total;
494 args.dwOverallPercentage = dwOverallPercentage;
495
496 results.cbSize = sizeof(results);
497
498 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREPROGRESS, &args, &results);
499 ExitOnFailure(hr, "BA OnCacheAcquireProgress failed.");
500
501 if (results.fCancel)
502 {
503 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
504 }
505
506LExit:
507 return hr;
508}
509
510EXTERN_C BAAPI UserExperienceOnCacheAcquireResolving(
511 __in BURN_USER_EXPERIENCE* pUserExperience,
512 __in_z_opt LPCWSTR wzPackageOrContainerId,
513 __in_z_opt LPCWSTR wzPayloadId,
514 __in_z LPWSTR* rgSearchPaths,
515 __in DWORD cSearchPaths,
516 __in BOOL fFoundLocal,
517 __in DWORD* pdwChosenSearchPath,
518 __in_z_opt LPWSTR* pwzDownloadUrl,
519 __in_z_opt LPCWSTR wzPayloadContainerId,
520 __inout BOOTSTRAPPER_CACHE_RESOLVE_OPERATION* pCacheOperation
521 )
522{
523 HRESULT hr = S_OK;
524 BA_ONCACHEACQUIRERESOLVING_ARGS args = { };
525 BA_ONCACHEACQUIRERESOLVING_RESULTS results = { };
526
527 args.cbSize = sizeof(args);
528 args.wzPackageOrContainerId = wzPackageOrContainerId;
529 args.wzPayloadId = wzPayloadId;
530 args.rgSearchPaths = const_cast<LPCWSTR*>(rgSearchPaths);
531 args.cSearchPaths = cSearchPaths;
532 args.fFoundLocal = fFoundLocal;
533 args.dwRecommendedSearchPath = *pdwChosenSearchPath;
534 args.wzDownloadUrl = *pwzDownloadUrl;
535 args.recommendation = *pCacheOperation;
536
537 results.cbSize = sizeof(results);
538 results.dwChosenSearchPath = *pdwChosenSearchPath;
539 results.action = *pCacheOperation;
540
541 hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRERESOLVING, &args, &results);
542 ExitOnFailure(hr, "BA OnCacheAcquireResolving failed.");
543
544 if (results.fCancel)
545 {
546 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
547 }
548 else
549 {
550 // Verify the BA requested an action that is possible.
551 if (BOOTSTRAPPER_CACHE_RESOLVE_DOWNLOAD == results.action && *pwzDownloadUrl && **pwzDownloadUrl ||
552 BOOTSTRAPPER_CACHE_RESOLVE_CONTAINER == results.action && wzPayloadContainerId ||
553 BOOTSTRAPPER_CACHE_RESOLVE_RETRY == results.action ||
554 BOOTSTRAPPER_CACHE_RESOLVE_NONE == results.action)
555 {
556 *pCacheOperation = results.action;
557 }
558 else if (BOOTSTRAPPER_CACHE_RESOLVE_LOCAL == results.action && results.dwChosenSearchPath < cSearchPaths)
559 {
560 *pdwChosenSearchPath = results.dwChosenSearchPath;
561 *pCacheOperation = results.action;
562 }
563 }
564
565LExit:
566 return hr;
567}
568
569EXTERN_C BAAPI UserExperienceOnCacheBegin(
570 __in BURN_USER_EXPERIENCE* pUserExperience
571 )
572{
573 HRESULT hr = S_OK;
574 BA_ONCACHEBEGIN_ARGS args = { };
575 BA_ONCACHEBEGIN_RESULTS results = { };
576
577 args.cbSize = sizeof(args);
578
579 results.cbSize = sizeof(results);
580
581 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEBEGIN, &args, &results);
582 ExitOnFailure(hr, "BA OnCacheBegin failed.");
583
584 if (results.fCancel)
585 {
586 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
587 }
588
589LExit:
590 return hr;
591}
592
593EXTERN_C BAAPI UserExperienceOnCacheComplete(
594 __in BURN_USER_EXPERIENCE* pUserExperience,
595 __in HRESULT hrStatus
596 )
597{
598 HRESULT hr = S_OK;
599 BA_ONCACHECOMPLETE_ARGS args = { };
600 BA_ONCACHECOMPLETE_RESULTS results = { };
601
602 args.cbSize = sizeof(args);
603 args.hrStatus = hrStatus;
604
605 results.cbSize = sizeof(results);
606
607 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECOMPLETE, &args, &results);
608 ExitOnFailure(hr, "BA OnCacheComplete failed.");
609
610LExit:
611 return hr;
612}
613
614EXTERN_C BAAPI UserExperienceOnCacheContainerOrPayloadVerifyBegin(
615 __in BURN_USER_EXPERIENCE* pUserExperience,
616 __in_z_opt LPCWSTR wzPackageOrContainerId,
617 __in_z_opt LPCWSTR wzPayloadId
618 )
619{
620 HRESULT hr = S_OK;
621 BA_ONCACHECONTAINERORPAYLOADVERIFYBEGIN_ARGS args = { };
622 BA_ONCACHECONTAINERORPAYLOADVERIFYBEGIN_RESULTS results = { };
623
624 args.cbSize = sizeof(args);
625 args.wzPackageOrContainerId = wzPackageOrContainerId;
626 args.wzPayloadId = wzPayloadId;
627
628 results.cbSize = sizeof(results);
629
630 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECONTAINERORPAYLOADVERIFYBEGIN, &args, &results);
631 ExitOnFailure(hr, "BA OnCacheContainerOrPayloadVerifyBegin failed.");
632
633 if (results.fCancel)
634 {
635 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
636 }
637
638LExit:
639 return hr;
640}
641
642EXTERN_C BAAPI UserExperienceOnCacheContainerOrPayloadVerifyComplete(
643 __in BURN_USER_EXPERIENCE* pUserExperience,
644 __in_z_opt LPCWSTR wzPackageOrContainerId,
645 __in_z_opt LPCWSTR wzPayloadId,
646 __in HRESULT hrStatus
647 )
648{
649 HRESULT hr = S_OK;
650 BA_ONCACHECONTAINERORPAYLOADVERIFYCOMPLETE_ARGS args = { };
651 BA_ONCACHECONTAINERORPAYLOADVERIFYCOMPLETE_RESULTS results = { };
652
653 args.cbSize = sizeof(args);
654 args.wzPackageOrContainerId = wzPackageOrContainerId;
655 args.wzPayloadId = wzPayloadId;
656 args.hrStatus = hrStatus;
657
658 results.cbSize = sizeof(results);
659
660 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECONTAINERORPAYLOADVERIFYCOMPLETE, &args, &results);
661 ExitOnFailure(hr, "BA OnCacheContainerOrPayloadVerifyComplete failed.");
662
663LExit:
664 return hr;
665}
666
667EXTERN_C BAAPI UserExperienceOnCacheContainerOrPayloadVerifyProgress(
668 __in BURN_USER_EXPERIENCE* pUserExperience,
669 __in_z_opt LPCWSTR wzPackageOrContainerId,
670 __in_z_opt LPCWSTR wzPayloadId,
671 __in DWORD64 dw64Progress,
672 __in DWORD64 dw64Total,
673 __in DWORD dwOverallPercentage
674 )
675{
676 HRESULT hr = S_OK;
677 BA_ONCACHECONTAINERORPAYLOADVERIFYPROGRESS_ARGS args = { };
678 BA_ONCACHECONTAINERORPAYLOADVERIFYPROGRESS_RESULTS results = { };
679
680 args.cbSize = sizeof(args);
681 args.wzPackageOrContainerId = wzPackageOrContainerId;
682 args.wzPayloadId = wzPayloadId;
683 args.dw64Progress = dw64Progress;
684 args.dw64Total = dw64Total;
685 args.dwOverallPercentage = dwOverallPercentage;
686
687 results.cbSize = sizeof(results);
688
689 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECONTAINERORPAYLOADVERIFYPROGRESS, &args, &results);
690 ExitOnFailure(hr, "BA OnCacheContainerOrPayloadVerifyProgress failed.");
691
692 if (results.fCancel)
693 {
694 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
695 }
696
697LExit:
698 return hr;
699}
700
701EXTERN_C BAAPI UserExperienceOnCachePackageBegin(
702 __in BURN_USER_EXPERIENCE* pUserExperience,
703 __in_z LPCWSTR wzPackageId,
704 __in DWORD cCachePayloads,
705 __in DWORD64 dw64PackageCacheSize
706 )
707{
708 HRESULT hr = S_OK;
709 BA_ONCACHEPACKAGEBEGIN_ARGS args = { };
710 BA_ONCACHEPACKAGEBEGIN_RESULTS results = { };
711
712 args.cbSize = sizeof(args);
713 args.wzPackageId = wzPackageId;
714 args.cCachePayloads = cCachePayloads;
715 args.dw64PackageCacheSize = dw64PackageCacheSize;
716
717 results.cbSize = sizeof(results);
718
719 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGEBEGIN, &args, &results);
720 ExitOnFailure(hr, "BA OnCachePackageBegin failed.");
721
722 if (results.fCancel)
723 {
724 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
725 }
726
727LExit:
728 return hr;
729}
730
731EXTERN_C BAAPI UserExperienceOnCachePackageComplete(
732 __in BURN_USER_EXPERIENCE* pUserExperience,
733 __in_z LPCWSTR wzPackageId,
734 __in HRESULT hrStatus,
735 __inout BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION* pAction
736 )
737{
738 HRESULT hr = S_OK;
739 BA_ONCACHEPACKAGECOMPLETE_ARGS args = { };
740 BA_ONCACHEPACKAGECOMPLETE_RESULTS results = { };
741
742 args.cbSize = sizeof(args);
743 args.wzPackageId = wzPackageId;
744 args.hrStatus = hrStatus;
745 args.recommendation = *pAction;
746
747 results.cbSize = sizeof(results);
748 results.action = *pAction;
749
750 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGECOMPLETE, &args, &results);
751 ExitOnFailure(hr, "BA OnCachePackageComplete failed.");
752
753 if (FAILED(hrStatus))
754 {
755 *pAction = results.action;
756 }
757
758LExit:
759 return hr;
760}
761
762EXTERN_C BAAPI UserExperienceOnCachePayloadExtractBegin(
763 __in BURN_USER_EXPERIENCE* pUserExperience,
764 __in_z_opt LPCWSTR wzContainerId,
765 __in_z_opt LPCWSTR wzPayloadId
766 )
767{
768 HRESULT hr = S_OK;
769 BA_ONCACHEPAYLOADEXTRACTBEGIN_ARGS args = { };
770 BA_ONCACHEPAYLOADEXTRACTBEGIN_RESULTS results = { };
771
772 args.cbSize = sizeof(args);
773 args.wzContainerId = wzContainerId;
774 args.wzPayloadId = wzPayloadId;
775
776 results.cbSize = sizeof(results);
777
778 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPAYLOADEXTRACTBEGIN, &args, &results);
779 ExitOnFailure(hr, "BA OnCachePayloadExtractBegin failed.");
780
781 if (results.fCancel)
782 {
783 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
784 }
785
786LExit:
787 return hr;
788}
789
790EXTERN_C BAAPI UserExperienceOnCachePayloadExtractComplete(
791 __in BURN_USER_EXPERIENCE* pUserExperience,
792 __in_z_opt LPCWSTR wzContainerId,
793 __in_z_opt LPCWSTR wzPayloadId,
794 __in HRESULT hrStatus
795 )
796{
797 HRESULT hr = S_OK;
798 BA_ONCACHEPAYLOADEXTRACTCOMPLETE_ARGS args = { };
799 BA_ONCACHEPAYLOADEXTRACTCOMPLETE_RESULTS results = { };
800
801 args.cbSize = sizeof(args);
802 args.wzContainerId = wzContainerId;
803 args.wzPayloadId = wzPayloadId;
804 args.hrStatus = hrStatus;
805
806 results.cbSize = sizeof(results);
807
808 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPAYLOADEXTRACTCOMPLETE, &args, &results);
809 ExitOnFailure(hr, "BA OnCachePayloadExtractComplete failed.");
810
811LExit:
812 return hr;
813}
814
815EXTERN_C BAAPI UserExperienceOnCachePayloadExtractProgress(
816 __in BURN_USER_EXPERIENCE* pUserExperience,
817 __in_z_opt LPCWSTR wzContainerId,
818 __in_z_opt LPCWSTR wzPayloadId,
819 __in DWORD64 dw64Progress,
820 __in DWORD64 dw64Total,
821 __in DWORD dwOverallPercentage
822 )
823{
824 HRESULT hr = S_OK;
825 BA_ONCACHEPAYLOADEXTRACTPROGRESS_ARGS args = { };
826 BA_ONCACHEPAYLOADEXTRACTPROGRESS_RESULTS results = { };
827
828 args.cbSize = sizeof(args);
829 args.wzContainerId = wzContainerId;
830 args.wzPayloadId = wzPayloadId;
831 args.dw64Progress = dw64Progress;
832 args.dw64Total = dw64Total;
833 args.dwOverallPercentage = dwOverallPercentage;
834
835 results.cbSize = sizeof(results);
836
837 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPAYLOADEXTRACTPROGRESS, &args, &results);
838 ExitOnFailure(hr, "BA OnCachePayloadExtractProgress failed.");
839
840 if (results.fCancel)
841 {
842 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
843 }
844
845LExit:
846 return hr;
847}
848
849EXTERN_C BAAPI UserExperienceOnCacheVerifyBegin(
850 __in BURN_USER_EXPERIENCE* pUserExperience,
851 __in_z_opt LPCWSTR wzPackageOrContainerId,
852 __in_z_opt LPCWSTR wzPayloadId
853 )
854{
855 HRESULT hr = S_OK;
856 BA_ONCACHEVERIFYBEGIN_ARGS args = { };
857 BA_ONCACHEVERIFYBEGIN_RESULTS results = { };
858
859 args.cbSize = sizeof(args);
860 args.wzPackageOrContainerId = wzPackageOrContainerId;
861 args.wzPayloadId = wzPayloadId;
862
863 results.cbSize = sizeof(results);
864
865 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYBEGIN, &args, &results);
866 ExitOnFailure(hr, "BA OnCacheVerifyBegin failed.");
867
868 if (results.fCancel)
869 {
870 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
871 }
872
873LExit:
874 return hr;
875}
876
877EXTERN_C BAAPI UserExperienceOnCacheVerifyComplete(
878 __in BURN_USER_EXPERIENCE* pUserExperience,
879 __in_z_opt LPCWSTR wzPackageOrContainerId,
880 __in_z_opt LPCWSTR wzPayloadId,
881 __in HRESULT hrStatus,
882 __inout BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION* pAction
883 )
884{
885 HRESULT hr = S_OK;
886 BA_ONCACHEVERIFYCOMPLETE_ARGS args = { };
887 BA_ONCACHEVERIFYCOMPLETE_RESULTS results = { };
888
889 args.cbSize = sizeof(args);
890 args.wzPackageOrContainerId = wzPackageOrContainerId;
891 args.wzPayloadId = wzPayloadId;
892 args.hrStatus = hrStatus;
893 args.recommendation = *pAction;
894
895 results.cbSize = sizeof(results);
896 results.action = *pAction;
897
898 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYCOMPLETE, &args, &results);
899 ExitOnFailure(hr, "BA OnCacheVerifyComplete failed.");
900
901 if (FAILED(hrStatus))
902 {
903 *pAction = results.action;
904 }
905
906LExit:
907 return hr;
908}
909
910EXTERN_C BAAPI UserExperienceOnCacheVerifyProgress(
911 __in BURN_USER_EXPERIENCE* pUserExperience,
912 __in_z_opt LPCWSTR wzPackageOrContainerId,
913 __in_z_opt LPCWSTR wzPayloadId,
914 __in DWORD64 dw64Progress,
915 __in DWORD64 dw64Total,
916 __in DWORD dwOverallPercentage,
917 __in BOOTSTRAPPER_CACHE_VERIFY_STEP verifyStep
918 )
919{
920 HRESULT hr = S_OK;
921 BA_ONCACHEVERIFYPROGRESS_ARGS args = { };
922 BA_ONCACHEVERIFYPROGRESS_RESULTS results = { };
923
924 args.cbSize = sizeof(args);
925 args.wzPackageOrContainerId = wzPackageOrContainerId;
926 args.wzPayloadId = wzPayloadId;
927 args.dw64Progress = dw64Progress;
928 args.dw64Total = dw64Total;
929 args.dwOverallPercentage = dwOverallPercentage;
930 args.verifyStep = verifyStep;
931
932 results.cbSize = sizeof(results);
933
934 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYPROGRESS, &args, &results);
935 ExitOnFailure(hr, "BA OnCacheVerifyProgress failed.");
936
937 if (results.fCancel)
938 {
939 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
940 }
941
942LExit:
943 return hr;
944}
945
946EXTERN_C BAAPI UserExperienceOnCommitMsiTransactionBegin(
947 __in BURN_USER_EXPERIENCE* pUserExperience,
948 __in LPCWSTR wzTransactionId
949 )
950{
951 HRESULT hr = S_OK;
952 BA_ONCOMMITMSITRANSACTIONBEGIN_ARGS args = { };
953 BA_ONCOMMITMSITRANSACTIONBEGIN_RESULTS results = { };
954
955 args.cbSize = sizeof(args);
956 args.wzTransactionId = wzTransactionId;
957
958 results.cbSize = sizeof(results);
959
960 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCOMMITMSITRANSACTIONBEGIN, &args, &results);
961 ExitOnFailure(hr, "BA OnCommitMsiTransactionBegin failed.");
962
963 if (results.fCancel)
964 {
965 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
966 }
967
968LExit:
969 return hr;
970}
971
972EXTERN_C BAAPI UserExperienceOnCommitMsiTransactionComplete(
973 __in BURN_USER_EXPERIENCE* pUserExperience,
974 __in LPCWSTR wzTransactionId,
975 __in HRESULT hrStatus
976 )
977{
978 HRESULT hr = S_OK;
979 BA_ONCOMMITMSITRANSACTIONCOMPLETE_ARGS args = { };
980 BA_ONCOMMITMSITRANSACTIONCOMPLETE_RESULTS results = { };
981
982 args.cbSize = sizeof(args);
983 args.wzTransactionId = wzTransactionId;
984 args.hrStatus = hrStatus;
985
986 results.cbSize = sizeof(results);
987
988 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONCOMMITMSITRANSACTIONCOMPLETE, &args, &results);
989 ExitOnFailure(hr, "BA OnCommitMsiTransactionComplete failed.");
990
991LExit:
992 return hr;
993}
994
995EXTERN_C BAAPI UserExperienceOnDetectBegin(
996 __in BURN_USER_EXPERIENCE* pUserExperience,
997 __in BOOL fCached,
998 __in BOOL fInstalled,
999 __in DWORD cPackages
1000 )
1001{
1002 HRESULT hr = S_OK;
1003 BA_ONDETECTBEGIN_ARGS args = { };
1004 BA_ONDETECTBEGIN_RESULTS results = { };
1005
1006 args.cbSize = sizeof(args);
1007 args.cPackages = cPackages;
1008 args.fInstalled = fInstalled;
1009 args.fCached = fCached;
1010
1011 results.cbSize = sizeof(results);
1012
1013 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTBEGIN, &args, &results);
1014 ExitOnFailure(hr, "BA OnDetectBegin failed.");
1015
1016 if (results.fCancel)
1017 {
1018 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1019 }
1020
1021LExit:
1022 return hr;
1023}
1024
1025EXTERN_C BAAPI UserExperienceOnDetectComplete(
1026 __in BURN_USER_EXPERIENCE* pUserExperience,
1027 __in HRESULT hrStatus,
1028 __in BOOL fEligibleForCleanup
1029 )
1030{
1031 HRESULT hr = S_OK;
1032 BA_ONDETECTCOMPLETE_ARGS args = { };
1033 BA_ONDETECTCOMPLETE_RESULTS results = { };
1034
1035 args.cbSize = sizeof(args);
1036 args.hrStatus = hrStatus;
1037 args.fEligibleForCleanup = fEligibleForCleanup;
1038
1039 results.cbSize = sizeof(results);
1040
1041 hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPLETE, &args, &results);
1042 ExitOnFailure(hr, "BA OnDetectComplete failed.");
1043
1044LExit:
1045 return hr;
1046}
1047
1048EXTERN_C BAAPI UserExperienceOnDetectForwardCompatibleBundle(
1049 __in BURN_USER_EXPERIENCE* pUserExperience,
1050 __in_z LPCWSTR wzBundleId,
1051 __in BOOTSTRAPPER_RELATION_TYPE relationType,
1052 __in_z LPCWSTR wzBundleTag,
1053 __in BOOL fPerMachine,
1054 __in VERUTIL_VERSION* pVersion,
1055 __in BOOL fMissingFromCache
1056 )
1057{
1058 HRESULT hr = S_OK;
1059 BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_ARGS args = { };
1060 BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_RESULTS results = { };
1061
1062 args.cbSize = sizeof(args);
1063 args.wzBundleId = wzBundleId;
1064 args.relationType = relationType;
1065 args.wzBundleTag = wzBundleTag;
1066 args.fPerMachine = fPerMachine;
1067 args.wzVersion = pVersion->sczVersion;
1068 args.fMissingFromCache = fMissingFromCache;
1069
1070 results.cbSize = sizeof(results);
1071
1072 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTFORWARDCOMPATIBLEBUNDLE, &args, &results);
1073 ExitOnFailure(hr, "BA OnDetectForwardCompatibleBundle failed.");
1074
1075 if (results.fCancel)
1076 {
1077 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1078 }
1079
1080LExit:
1081 return hr;
1082}
1083
1084EXTERN_C BAAPI UserExperienceOnDetectMsiFeature(
1085 __in BURN_USER_EXPERIENCE* pUserExperience,
1086 __in_z LPCWSTR wzPackageId,
1087 __in_z LPCWSTR wzFeatureId,
1088 __in BOOTSTRAPPER_FEATURE_STATE state
1089 )
1090{
1091 HRESULT hr = S_OK;
1092 BA_ONDETECTMSIFEATURE_ARGS args = { };
1093 BA_ONDETECTMSIFEATURE_RESULTS results = { };
1094
1095 args.cbSize = sizeof(args);
1096 args.wzPackageId = wzPackageId;
1097 args.wzFeatureId = wzFeatureId;
1098 args.state = state;
1099
1100 results.cbSize = sizeof(results);
1101
1102 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTMSIFEATURE, &args, &results);
1103 ExitOnFailure(hr, "BA OnDetectMsiFeature failed.");
1104
1105 if (results.fCancel)
1106 {
1107 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1108 }
1109
1110LExit:
1111 return hr;
1112}
1113
1114EXTERN_C BAAPI UserExperienceOnDetectPackageBegin(
1115 __in BURN_USER_EXPERIENCE* pUserExperience,
1116 __in_z LPCWSTR wzPackageId
1117 )
1118{
1119 HRESULT hr = S_OK;
1120 BA_ONDETECTPACKAGEBEGIN_ARGS args = { };
1121 BA_ONDETECTPACKAGEBEGIN_RESULTS results = { };
1122
1123 args.cbSize = sizeof(args);
1124 args.wzPackageId = wzPackageId;
1125
1126 results.cbSize = sizeof(results);
1127
1128 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGEBEGIN, &args, &results);
1129 ExitOnFailure(hr, "BA OnDetectPackageBegin failed.");
1130
1131 if (results.fCancel)
1132 {
1133 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1134 }
1135
1136LExit:
1137 return hr;
1138}
1139
1140EXTERN_C BAAPI UserExperienceOnDetectPackageComplete(
1141 __in BURN_USER_EXPERIENCE* pUserExperience,
1142 __in_z LPCWSTR wzPackageId,
1143 __in HRESULT hrStatus,
1144 __in BOOTSTRAPPER_PACKAGE_STATE state,
1145 __in BOOL fCached
1146 )
1147{
1148 HRESULT hr = S_OK;
1149 BA_ONDETECTPACKAGECOMPLETE_ARGS args = { };
1150 BA_ONDETECTPACKAGECOMPLETE_RESULTS results = { };
1151
1152 args.cbSize = sizeof(args);
1153 args.wzPackageId = wzPackageId;
1154 args.hrStatus = hrStatus;
1155 args.state = state;
1156 args.fCached = fCached;
1157
1158 results.cbSize = sizeof(results);
1159
1160 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGECOMPLETE, &args, &results);
1161 ExitOnFailure(hr, "BA OnDetectPackageComplete failed.");
1162
1163LExit:
1164 return hr;
1165}
1166
1167EXTERN_C BAAPI UserExperienceOnDetectRelatedBundle(
1168 __in BURN_USER_EXPERIENCE* pUserExperience,
1169 __in_z LPCWSTR wzBundleId,
1170 __in BOOTSTRAPPER_RELATION_TYPE relationType,
1171 __in_z LPCWSTR wzBundleTag,
1172 __in BOOL fPerMachine,
1173 __in VERUTIL_VERSION* pVersion,
1174 __in BOOTSTRAPPER_RELATED_OPERATION operation,
1175 __in BOOL fMissingFromCache
1176 )
1177{
1178 HRESULT hr = S_OK;
1179 BA_ONDETECTRELATEDBUNDLE_ARGS args = { };
1180 BA_ONDETECTRELATEDBUNDLE_RESULTS results = { };
1181
1182 args.cbSize = sizeof(args);
1183 args.wzBundleId = wzBundleId;
1184 args.relationType = relationType;
1185 args.wzBundleTag = wzBundleTag;
1186 args.fPerMachine = fPerMachine;
1187 args.wzVersion = pVersion->sczVersion;
1188 args.operation = operation;
1189 args.fMissingFromCache = fMissingFromCache;
1190
1191 results.cbSize = sizeof(results);
1192
1193 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLE, &args, &results);
1194 ExitOnFailure(hr, "BA OnDetectRelatedBundle failed.");
1195
1196 if (results.fCancel)
1197 {
1198 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1199 }
1200
1201LExit:
1202 return hr;
1203}
1204
1205EXTERN_C BAAPI UserExperienceOnDetectRelatedMsiPackage(
1206 __in BURN_USER_EXPERIENCE* pUserExperience,
1207 __in_z LPCWSTR wzPackageId,
1208 __in_z LPCWSTR wzUpgradeCode,
1209 __in_z LPCWSTR wzProductCode,
1210 __in BOOL fPerMachine,
1211 __in VERUTIL_VERSION* pVersion,
1212 __in BOOTSTRAPPER_RELATED_OPERATION operation
1213 )
1214{
1215 HRESULT hr = S_OK;
1216 BA_ONDETECTRELATEDMSIPACKAGE_ARGS args = { };
1217 BA_ONDETECTRELATEDMSIPACKAGE_RESULTS results = { };
1218
1219 args.cbSize = sizeof(args);
1220 args.wzPackageId = wzPackageId;
1221 args.wzUpgradeCode = wzUpgradeCode;
1222 args.wzProductCode = wzProductCode;
1223 args.fPerMachine = fPerMachine;
1224 args.wzVersion = pVersion->sczVersion;
1225 args.operation = operation;
1226
1227 results.cbSize = sizeof(results);
1228
1229 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDMSIPACKAGE, &args, &results);
1230 ExitOnFailure(hr, "BA OnDetectRelatedMsiPackage failed.");
1231
1232 if (results.fCancel)
1233 {
1234 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1235 }
1236
1237LExit:
1238 return hr;
1239}
1240
1241EXTERN_C BAAPI UserExperienceOnDetectPatchTarget(
1242 __in BURN_USER_EXPERIENCE* pUserExperience,
1243 __in_z LPCWSTR wzPackageId,
1244 __in_z LPCWSTR wzProductCode,
1245 __in BOOTSTRAPPER_PACKAGE_STATE patchState
1246 )
1247{
1248 HRESULT hr = S_OK;
1249 BA_ONDETECTPATCHTARGET_ARGS args = { };
1250 BA_ONDETECTPATCHTARGET_RESULTS results = { };
1251
1252 args.cbSize = sizeof(args);
1253 args.wzPackageId = wzPackageId;
1254 args.wzProductCode = wzProductCode;
1255 args.patchState = patchState;
1256
1257 results.cbSize = sizeof(results);
1258
1259 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPATCHTARGET, &args, &results);
1260 ExitOnFailure(hr, "BA OnDetectPatchTarget failed.");
1261
1262 if (results.fCancel)
1263 {
1264 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1265 }
1266
1267LExit:
1268 return hr;
1269}
1270
1271EXTERN_C BAAPI UserExperienceOnDetectUpdate(
1272 __in BURN_USER_EXPERIENCE* pUserExperience,
1273 __in_z_opt LPCWSTR wzUpdateLocation,
1274 __in DWORD64 dw64Size,
1275 __in VERUTIL_VERSION* pVersion,
1276 __in_z_opt LPCWSTR wzTitle,
1277 __in_z_opt LPCWSTR wzSummary,
1278 __in_z_opt LPCWSTR wzContentType,
1279 __in_z_opt LPCWSTR wzContent,
1280 __inout BOOL* pfStopProcessingUpdates
1281 )
1282{
1283 HRESULT hr = S_OK;
1284 BA_ONDETECTUPDATE_ARGS args = { };
1285 BA_ONDETECTUPDATE_RESULTS results = { };
1286
1287 args.cbSize = sizeof(args);
1288 args.wzUpdateLocation = wzUpdateLocation;
1289 args.dw64Size = dw64Size;
1290 args.wzVersion = pVersion->sczVersion;
1291 args.wzTitle = wzTitle;
1292 args.wzSummary = wzSummary;
1293 args.wzContentType = wzContentType;
1294 args.wzContent = wzContent;
1295
1296 results.cbSize = sizeof(results);
1297 results.fStopProcessingUpdates = *pfStopProcessingUpdates;
1298
1299 hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATE, &args, &results);
1300 ExitOnFailure(hr, "BA OnDetectUpdate failed.");
1301
1302 if (results.fCancel)
1303 {
1304 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1305 }
1306 *pfStopProcessingUpdates = results.fStopProcessingUpdates;
1307
1308LExit:
1309 return hr;
1310}
1311
1312EXTERN_C BAAPI UserExperienceOnDetectUpdateBegin(
1313 __in BURN_USER_EXPERIENCE* pUserExperience,
1314 __in_z LPCWSTR wzUpdateLocation,
1315 __inout BOOL* pfSkip
1316 )
1317{
1318 HRESULT hr = S_OK;
1319 BA_ONDETECTUPDATEBEGIN_ARGS args = { };
1320 BA_ONDETECTUPDATEBEGIN_RESULTS results = { };
1321
1322 args.cbSize = sizeof(args);
1323 args.wzUpdateLocation = wzUpdateLocation;
1324
1325 results.cbSize = sizeof(results);
1326 results.fSkip = *pfSkip;
1327
1328 hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATEBEGIN, &args, &results);
1329 ExitOnFailure(hr, "BA OnDetectUpdateBegin failed.");
1330
1331 if (results.fCancel)
1332 {
1333 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1334 }
1335 *pfSkip = results.fSkip;
1336
1337LExit:
1338 return hr;
1339}
1340
1341EXTERN_C BAAPI UserExperienceOnDetectUpdateComplete(
1342 __in BURN_USER_EXPERIENCE* pUserExperience,
1343 __in HRESULT hrStatus,
1344 __inout BOOL* pfIgnoreError
1345 )
1346{
1347 HRESULT hr = S_OK;
1348 BA_ONDETECTUPDATECOMPLETE_ARGS args = { };
1349 BA_ONDETECTUPDATECOMPLETE_RESULTS results = { };
1350
1351 args.cbSize = sizeof(args);
1352 args.hrStatus = hrStatus;
1353
1354 results.cbSize = sizeof(results);
1355 results.fIgnoreError = *pfIgnoreError;
1356
1357 hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATECOMPLETE, &args, &results);
1358 ExitOnFailure(hr, "BA OnDetectUpdateComplete failed.");
1359
1360 if (FAILED(hrStatus))
1361 {
1362 *pfIgnoreError = results.fIgnoreError;
1363 }
1364
1365LExit:
1366 return hr;
1367}
1368
1369EXTERN_C BAAPI UserExperienceOnElevateBegin(
1370 __in BURN_USER_EXPERIENCE* pUserExperience
1371 )
1372{
1373 HRESULT hr = S_OK;
1374 BA_ONELEVATEBEGIN_ARGS args = { };
1375 BA_ONELEVATEBEGIN_RESULTS results = { };
1376
1377 args.cbSize = sizeof(args);
1378
1379 results.cbSize = sizeof(results);
1380
1381 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATEBEGIN, &args, &results);
1382 ExitOnFailure(hr, "BA OnElevateBegin failed.");
1383
1384 if (results.fCancel)
1385 {
1386 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1387 }
1388
1389LExit:
1390 return hr;
1391}
1392
1393EXTERN_C BAAPI UserExperienceOnElevateComplete(
1394 __in BURN_USER_EXPERIENCE* pUserExperience,
1395 __in HRESULT hrStatus
1396 )
1397{
1398 HRESULT hr = S_OK;
1399 BA_ONELEVATECOMPLETE_ARGS args = { };
1400 BA_ONELEVATECOMPLETE_RESULTS results = { };
1401
1402 args.cbSize = sizeof(args);
1403 args.hrStatus = hrStatus;
1404
1405 results.cbSize = sizeof(results);
1406
1407 hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATECOMPLETE, &args, &results);
1408 ExitOnFailure(hr, "BA OnElevateComplete failed.");
1409
1410LExit:
1411 return hr;
1412}
1413
1414EXTERN_C BAAPI UserExperienceOnError(
1415 __in BURN_USER_EXPERIENCE* pUserExperience,
1416 __in BOOTSTRAPPER_ERROR_TYPE errorType,
1417 __in_z_opt LPCWSTR wzPackageId,
1418 __in DWORD dwCode,
1419 __in_z_opt LPCWSTR wzError,
1420 __in DWORD dwUIHint,
1421 __in DWORD cData,
1422 __in_ecount_z_opt(cData) LPCWSTR* rgwzData,
1423 __inout int* pnResult
1424 )
1425{
1426 HRESULT hr = S_OK;
1427 BA_ONERROR_ARGS args = { };
1428 BA_ONERROR_RESULTS results = { };
1429
1430 args.cbSize = sizeof(args);
1431 args.errorType = errorType;
1432 args.wzPackageId = wzPackageId;
1433 args.dwCode = dwCode;
1434 args.wzError = wzError;
1435 args.dwUIHint = dwUIHint;
1436 args.cData = cData;
1437 args.rgwzData = rgwzData;
1438 args.nRecommendation = *pnResult;
1439
1440 results.cbSize = sizeof(results);
1441 results.nResult = *pnResult;
1442
1443 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONERROR, &args, &results);
1444 ExitOnFailure(hr, "BA OnError failed.");
1445
1446 *pnResult = results.nResult;
1447
1448LExit:
1449 return hr;
1450}
1451
1452EXTERN_C BAAPI UserExperienceOnExecuteBegin(
1453 __in BURN_USER_EXPERIENCE* pUserExperience,
1454 __in DWORD cExecutingPackages
1455 )
1456{
1457 HRESULT hr = S_OK;
1458 BA_ONEXECUTEBEGIN_ARGS args = { };
1459 BA_ONEXECUTEBEGIN_RESULTS results = { };
1460
1461 args.cbSize = sizeof(args);
1462 args.cExecutingPackages = cExecutingPackages;
1463
1464 results.cbSize = sizeof(results);
1465
1466 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEBEGIN, &args, &results);
1467 ExitOnFailure(hr, "BA OnExecuteBegin failed.");
1468
1469 if (results.fCancel)
1470 {
1471 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1472 }
1473
1474LExit:
1475 return hr;
1476}
1477
1478EXTERN_C BAAPI UserExperienceOnExecuteComplete(
1479 __in BURN_USER_EXPERIENCE* pUserExperience,
1480 __in HRESULT hrStatus
1481 )
1482{
1483 HRESULT hr = S_OK;
1484 BA_ONEXECUTECOMPLETE_ARGS args = { };
1485 BA_ONEXECUTECOMPLETE_RESULTS results = { };
1486
1487 args.cbSize = sizeof(args);
1488 args.hrStatus = hrStatus;
1489
1490 results.cbSize = sizeof(results);
1491
1492 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTECOMPLETE, &args, &results);
1493 ExitOnFailure(hr, "BA OnExecuteComplete failed.");
1494
1495LExit:
1496 return hr;
1497}
1498
1499EXTERN_C BAAPI UserExperienceOnExecuteFilesInUse(
1500 __in BURN_USER_EXPERIENCE* pUserExperience,
1501 __in_z LPCWSTR wzPackageId,
1502 __in DWORD cFiles,
1503 __in_ecount_z_opt(cFiles) LPCWSTR* rgwzFiles,
1504 __inout int* pnResult
1505 )
1506{
1507 HRESULT hr = S_OK;
1508 BA_ONEXECUTEFILESINUSE_ARGS args = { };
1509 BA_ONEXECUTEFILESINUSE_RESULTS results = { };
1510
1511 args.cbSize = sizeof(args);
1512 args.wzPackageId = wzPackageId;
1513 args.cFiles = cFiles;
1514 args.rgwzFiles = rgwzFiles;
1515 args.nRecommendation = *pnResult;
1516
1517 results.cbSize = sizeof(results);
1518 results.nResult = *pnResult;
1519
1520 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEFILESINUSE, &args, &results);
1521 ExitOnFailure(hr, "BA OnExecuteFilesInUse failed.");
1522
1523 *pnResult = results.nResult;
1524
1525LExit:
1526 return hr;
1527}
1528
1529EXTERN_C BAAPI UserExperienceOnExecuteMsiMessage(
1530 __in BURN_USER_EXPERIENCE* pUserExperience,
1531 __in_z LPCWSTR wzPackageId,
1532 __in INSTALLMESSAGE messageType,
1533 __in DWORD dwUIHint,
1534 __in_z LPCWSTR wzMessage,
1535 __in DWORD cData,
1536 __in_ecount_z_opt(cData) LPCWSTR* rgwzData,
1537 __inout int* pnResult
1538 )
1539{
1540 HRESULT hr = S_OK;
1541 BA_ONEXECUTEMSIMESSAGE_ARGS args = { };
1542 BA_ONEXECUTEMSIMESSAGE_RESULTS results = { };
1543
1544 args.cbSize = sizeof(args);
1545 args.wzPackageId = wzPackageId;
1546 args.messageType = messageType;
1547 args.dwUIHint = dwUIHint;
1548 args.wzMessage = wzMessage;
1549 args.cData = cData;
1550 args.rgwzData = rgwzData;
1551 args.nRecommendation = *pnResult;
1552
1553 results.cbSize = sizeof(results);
1554 results.nResult = *pnResult;
1555
1556 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEMSIMESSAGE, &args, &results);
1557 ExitOnFailure(hr, "BA OnExecuteMsiMessage failed.");
1558
1559 *pnResult = results.nResult;
1560
1561LExit:
1562 return hr;
1563}
1564
1565EXTERN_C BAAPI UserExperienceOnExecutePackageBegin(
1566 __in BURN_USER_EXPERIENCE* pUserExperience,
1567 __in_z LPCWSTR wzPackageId,
1568 __in BOOL fExecute,
1569 __in BOOTSTRAPPER_ACTION_STATE action,
1570 __in INSTALLUILEVEL uiLevel,
1571 __in BOOL fDisableExternalUiHandler
1572 )
1573{
1574 HRESULT hr = S_OK;
1575 BA_ONEXECUTEPACKAGEBEGIN_ARGS args = { };
1576 BA_ONEXECUTEPACKAGEBEGIN_RESULTS results = { };
1577
1578 args.cbSize = sizeof(args);
1579 args.wzPackageId = wzPackageId;
1580 args.fExecute = fExecute;
1581 args.action = action;
1582 args.uiLevel = uiLevel;
1583 args.fDisableExternalUiHandler = fDisableExternalUiHandler;
1584
1585 results.cbSize = sizeof(results);
1586
1587 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGEBEGIN, &args, &results);
1588 ExitOnFailure(hr, "BA OnExecutePackageBegin failed.");
1589
1590 if (results.fCancel)
1591 {
1592 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1593 }
1594
1595LExit:
1596 return hr;
1597}
1598
1599EXTERN_C BAAPI UserExperienceOnExecutePackageComplete(
1600 __in BURN_USER_EXPERIENCE* pUserExperience,
1601 __in_z LPCWSTR wzPackageId,
1602 __in HRESULT hrStatus,
1603 __in BOOTSTRAPPER_APPLY_RESTART restart,
1604 __inout BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION* pAction
1605 )
1606{
1607 HRESULT hr = S_OK;
1608 BA_ONEXECUTEPACKAGECOMPLETE_ARGS args = { };
1609 BA_ONEXECUTEPACKAGECOMPLETE_RESULTS results = { };
1610
1611 args.cbSize = sizeof(args);
1612 args.wzPackageId = wzPackageId;
1613 args.hrStatus = hrStatus;
1614 args.restart = restart;
1615 args.recommendation = *pAction;
1616
1617 results.cbSize = sizeof(results);
1618 results.action = *pAction;
1619
1620 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGECOMPLETE, &args, &results);
1621 ExitOnFailure(hr, "BA OnExecutePackageComplete failed.");
1622
1623 *pAction = results.action;
1624
1625LExit:
1626 return hr;
1627}
1628
1629EXTERN_C BAAPI UserExperienceOnExecutePatchTarget(
1630 __in BURN_USER_EXPERIENCE* pUserExperience,
1631 __in_z LPCWSTR wzPackageId,
1632 __in_z LPCWSTR wzTargetProductCode
1633 )
1634{
1635 HRESULT hr = S_OK;
1636 BA_ONEXECUTEPATCHTARGET_ARGS args = { };
1637 BA_ONEXECUTEPATCHTARGET_RESULTS results = { };
1638
1639 args.cbSize = sizeof(args);
1640 args.wzPackageId = wzPackageId;
1641 args.wzTargetProductCode = wzTargetProductCode;
1642
1643 results.cbSize = sizeof(results);
1644
1645 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPATCHTARGET, &args, &results);
1646 ExitOnFailure(hr, "BA OnExecutePatchTarget failed.");
1647
1648 if (results.fCancel)
1649 {
1650 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1651 }
1652
1653LExit:
1654 return hr;
1655}
1656
1657EXTERN_C BAAPI UserExperienceOnExecuteProgress(
1658 __in BURN_USER_EXPERIENCE* pUserExperience,
1659 __in_z LPCWSTR wzPackageId,
1660 __in DWORD dwProgressPercentage,
1661 __in DWORD dwOverallPercentage,
1662 __out int* pnResult
1663 )
1664{
1665 HRESULT hr = S_OK;
1666 BA_ONEXECUTEPROGRESS_ARGS args = { };
1667 BA_ONEXECUTEPROGRESS_RESULTS results = { };
1668
1669 args.cbSize = sizeof(args);
1670 args.wzPackageId = wzPackageId;
1671 args.dwProgressPercentage = dwProgressPercentage;
1672 args.dwOverallPercentage = dwOverallPercentage;
1673
1674 results.cbSize = sizeof(results);
1675
1676 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROGRESS, &args, &results);
1677 ExitOnFailure(hr, "BA OnExecuteProgress failed.");
1678
1679LExit:
1680 if (FAILED(hr))
1681 {
1682 *pnResult = IDERROR;
1683 }
1684 else if (results.fCancel)
1685 {
1686 *pnResult = IDCANCEL;
1687 }
1688 else
1689 {
1690 *pnResult = IDNOACTION;
1691 }
1692 return hr;
1693}
1694
1695EXTERN_C BAAPI UserExperienceOnLaunchApprovedExeBegin(
1696 __in BURN_USER_EXPERIENCE* pUserExperience
1697 )
1698{
1699 HRESULT hr = S_OK;
1700 BA_ONLAUNCHAPPROVEDEXEBEGIN_ARGS args = { };
1701 BA_ONLAUNCHAPPROVEDEXEBEGIN_RESULTS results = { };
1702
1703 args.cbSize = sizeof(args);
1704
1705 results.cbSize = sizeof(results);
1706
1707 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXEBEGIN, &args, &results);
1708 ExitOnFailure(hr, "BA OnLaunchApprovedExeBegin failed.");
1709
1710 if (results.fCancel)
1711 {
1712 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1713 }
1714
1715LExit:
1716 return hr;
1717}
1718
1719EXTERN_C BAAPI UserExperienceOnLaunchApprovedExeComplete(
1720 __in BURN_USER_EXPERIENCE* pUserExperience,
1721 __in HRESULT hrStatus,
1722 __in DWORD dwProcessId
1723 )
1724{
1725 HRESULT hr = S_OK;
1726 BA_ONLAUNCHAPPROVEDEXECOMPLETE_ARGS args = { };
1727 BA_ONLAUNCHAPPROVEDEXECOMPLETE_RESULTS results = { };
1728
1729 args.cbSize = sizeof(args);
1730 args.hrStatus = hrStatus;
1731 args.dwProcessId = dwProcessId;
1732
1733 results.cbSize = sizeof(results);
1734
1735 hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXECOMPLETE, &args, &results);
1736 ExitOnFailure(hr, "BA OnLaunchApprovedExeComplete failed.");
1737
1738LExit:
1739 return hr;
1740}
1741
1742EXTERN_C BAAPI UserExperienceOnPauseAUBegin(
1743 __in BURN_USER_EXPERIENCE* pUserExperience
1744 )
1745{
1746 HRESULT hr = S_OK;
1747 BA_ONPAUSEAUTOMATICUPDATESBEGIN_ARGS args = { };
1748 BA_ONPAUSEAUTOMATICUPDATESBEGIN_RESULTS results = { };
1749
1750 args.cbSize = sizeof(args);
1751
1752 results.cbSize = sizeof(results);
1753
1754 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPAUSEAUTOMATICUPDATESBEGIN, &args, &results);
1755 ExitOnFailure(hr, "BA OnPauseAUBegin failed.");
1756
1757LExit:
1758 return hr;
1759}
1760
1761EXTERN_C BAAPI UserExperienceOnPauseAUComplete(
1762 __in BURN_USER_EXPERIENCE* pUserExperience,
1763 __in HRESULT hrStatus
1764 )
1765{
1766 HRESULT hr = S_OK;
1767 BA_ONPAUSEAUTOMATICUPDATESCOMPLETE_ARGS args = { };
1768 BA_ONPAUSEAUTOMATICUPDATESCOMPLETE_RESULTS results = { };
1769
1770 args.cbSize = sizeof(args);
1771 args.hrStatus = hrStatus;
1772
1773 results.cbSize = sizeof(results);
1774
1775 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPAUSEAUTOMATICUPDATESCOMPLETE, &args, &results);
1776 ExitOnFailure(hr, "BA OnPauseAUComplete failed.");
1777
1778LExit:
1779 return hr;
1780}
1781
1782EXTERN_C BAAPI UserExperienceOnPlanBegin(
1783 __in BURN_USER_EXPERIENCE* pUserExperience,
1784 __in DWORD cPackages
1785 )
1786{
1787 HRESULT hr = S_OK;
1788 BA_ONPLANBEGIN_ARGS args = { };
1789 BA_ONPLANBEGIN_RESULTS results = { };
1790
1791 args.cbSize = sizeof(args);
1792 args.cPackages = cPackages;
1793
1794 results.cbSize = sizeof(results);
1795
1796 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANBEGIN, &args, &results);
1797 ExitOnFailure(hr, "BA OnPlanBegin failed.");
1798
1799 if (results.fCancel)
1800 {
1801 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1802 }
1803
1804LExit:
1805 return hr;
1806}
1807
1808EXTERN_C BAAPI UserExperienceOnPlanMsiFeature(
1809 __in BURN_USER_EXPERIENCE* pUserExperience,
1810 __in_z LPCWSTR wzPackageId,
1811 __in_z LPCWSTR wzFeatureId,
1812 __inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState
1813 )
1814{
1815 HRESULT hr = S_OK;
1816 BA_ONPLANMSIFEATURE_ARGS args = { };
1817 BA_ONPLANMSIFEATURE_RESULTS results = { };
1818
1819 args.cbSize = sizeof(args);
1820 args.wzPackageId = wzPackageId;
1821 args.wzFeatureId = wzFeatureId;
1822 args.recommendedState = *pRequestedState;
1823
1824 results.cbSize = sizeof(results);
1825 results.requestedState = *pRequestedState;
1826
1827 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIFEATURE, &args, &results);
1828 ExitOnFailure(hr, "BA OnPlanMsiFeature failed.");
1829
1830 if (results.fCancel)
1831 {
1832 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1833 }
1834 *pRequestedState = results.requestedState;
1835
1836LExit:
1837 return hr;
1838}
1839
1840EXTERN_C BAAPI UserExperienceOnPlanComplete(
1841 __in BURN_USER_EXPERIENCE* pUserExperience,
1842 __in HRESULT hrStatus
1843 )
1844{
1845 HRESULT hr = S_OK;
1846 BA_ONPLANCOMPLETE_ARGS args = { };
1847 BA_ONPLANCOMPLETE_RESULTS results = { };
1848
1849 args.cbSize = sizeof(args);
1850 args.hrStatus = hrStatus;
1851
1852 results.cbSize = sizeof(results);
1853
1854 hr = SendBAMessageFromInactiveEngine(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPLETE, &args, &results);
1855 ExitOnFailure(hr, "BA OnPlanComplete failed.");
1856
1857LExit:
1858 return hr;
1859}
1860
1861EXTERN_C BAAPI UserExperienceOnPlanForwardCompatibleBundle(
1862 __in BURN_USER_EXPERIENCE* pUserExperience,
1863 __in_z LPCWSTR wzBundleId,
1864 __in BOOTSTRAPPER_RELATION_TYPE relationType,
1865 __in_z LPCWSTR wzBundleTag,
1866 __in BOOL fPerMachine,
1867 __in VERUTIL_VERSION* pVersion,
1868 __inout BOOL* pfIgnoreBundle
1869 )
1870{
1871 HRESULT hr = S_OK;
1872 BA_ONPLANFORWARDCOMPATIBLEBUNDLE_ARGS args = { };
1873 BA_ONPLANFORWARDCOMPATIBLEBUNDLE_RESULTS results = { };
1874
1875 args.cbSize = sizeof(args);
1876 args.wzBundleId = wzBundleId;
1877 args.relationType = relationType;
1878 args.wzBundleTag = wzBundleTag;
1879 args.fPerMachine = fPerMachine;
1880 args.wzVersion = pVersion->sczVersion;
1881 args.fRecommendedIgnoreBundle = *pfIgnoreBundle;
1882
1883 results.cbSize = sizeof(results);
1884 results.fIgnoreBundle = *pfIgnoreBundle;
1885
1886 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANFORWARDCOMPATIBLEBUNDLE, &args, &results);
1887 ExitOnFailure(hr, "BA OnPlanForwardCompatibleBundle failed.");
1888
1889 if (results.fCancel)
1890 {
1891 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1892 }
1893 *pfIgnoreBundle = results.fIgnoreBundle;
1894
1895LExit:
1896 return hr;
1897}
1898
1899EXTERN_C BAAPI UserExperienceOnPlanMsiPackage(
1900 __in BURN_USER_EXPERIENCE* pUserExperience,
1901 __in_z LPCWSTR wzPackageId,
1902 __in BOOL fExecute,
1903 __in BOOTSTRAPPER_ACTION_STATE action,
1904 __inout BURN_MSI_PROPERTY* pActionMsiProperty,
1905 __inout INSTALLUILEVEL* pUiLevel,
1906 __inout BOOL* pfDisableExternalUiHandler
1907 )
1908{
1909 HRESULT hr = S_OK;
1910 BA_ONPLANMSIPACKAGE_ARGS args = { };
1911 BA_ONPLANMSIPACKAGE_RESULTS results = { };
1912
1913 args.cbSize = sizeof(args);
1914 args.wzPackageId = wzPackageId;
1915 args.fExecute = fExecute;
1916 args.action = action;
1917
1918 results.cbSize = sizeof(results);
1919 results.actionMsiProperty = *pActionMsiProperty;
1920 results.uiLevel = *pUiLevel;
1921 results.fDisableExternalUiHandler = *pfDisableExternalUiHandler;
1922
1923 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIPACKAGE, &args, &results);
1924 ExitOnFailure(hr, "BA OnPlanMsiPackage failed.");
1925
1926 if (results.fCancel)
1927 {
1928 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1929 }
1930 *pActionMsiProperty = results.actionMsiProperty;
1931 *pUiLevel = results.uiLevel;
1932 *pfDisableExternalUiHandler = results.fDisableExternalUiHandler;
1933
1934LExit:
1935 return hr;
1936}
1937
1938EXTERN_C BAAPI UserExperienceOnPlannedPackage(
1939 __in BURN_USER_EXPERIENCE* pUserExperience,
1940 __in_z LPCWSTR wzPackageId,
1941 __in BOOTSTRAPPER_ACTION_STATE execute,
1942 __in BOOTSTRAPPER_ACTION_STATE rollback,
1943 __in BOOL fPlannedCache,
1944 __in BOOL fPlannedUncache
1945 )
1946{
1947 HRESULT hr = S_OK;
1948 BA_ONPLANNEDPACKAGE_ARGS args = { };
1949 BA_ONPLANNEDPACKAGE_RESULTS results = { };
1950
1951 args.cbSize = sizeof(args);
1952 args.wzPackageId = wzPackageId;
1953 args.execute = execute;
1954 args.rollback = rollback;
1955 args.fPlannedCache = fPlannedCache;
1956 args.fPlannedUncache = fPlannedUncache;
1957
1958 results.cbSize = sizeof(results);
1959
1960 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDPACKAGE, &args, &results);
1961 ExitOnFailure(hr, "BA OnPlannedPackage failed.");
1962
1963LExit:
1964 return hr;
1965}
1966
1967EXTERN_C BAAPI UserExperienceOnPlanPackageBegin(
1968 __in BURN_USER_EXPERIENCE* pUserExperience,
1969 __in_z LPCWSTR wzPackageId,
1970 __in BOOTSTRAPPER_PACKAGE_STATE state,
1971 __in BOOL fCached,
1972 __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition,
1973 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState,
1974 __inout BOOTSTRAPPER_CACHE_TYPE* pRequestedCacheType
1975 )
1976{
1977 HRESULT hr = S_OK;
1978 BA_ONPLANPACKAGEBEGIN_ARGS args = { };
1979 BA_ONPLANPACKAGEBEGIN_RESULTS results = { };
1980
1981 args.cbSize = sizeof(args);
1982 args.wzPackageId = wzPackageId;
1983 args.state = state;
1984 args.fCached = fCached;
1985 args.installCondition = installCondition;
1986 args.recommendedState = *pRequestedState;
1987 args.recommendedCacheType = *pRequestedCacheType;
1988
1989 results.cbSize = sizeof(results);
1990 results.requestedState = *pRequestedState;
1991 results.requestedCacheType = *pRequestedCacheType;
1992
1993 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGEBEGIN, &args, &results);
1994 ExitOnFailure(hr, "BA OnPlanPackageBegin failed.");
1995
1996 if (results.fCancel)
1997 {
1998 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1999 }
2000 *pRequestedState = results.requestedState;
2001 *pRequestedCacheType = results.requestedCacheType;
2002
2003LExit:
2004 return hr;
2005}
2006
2007EXTERN_C BAAPI UserExperienceOnPlanPackageComplete(
2008 __in BURN_USER_EXPERIENCE* pUserExperience,
2009 __in_z LPCWSTR wzPackageId,
2010 __in HRESULT hrStatus,
2011 __in BOOTSTRAPPER_REQUEST_STATE requested
2012 )
2013{
2014 HRESULT hr = S_OK;
2015 BA_ONPLANPACKAGECOMPLETE_ARGS args = { };
2016 BA_ONPLANPACKAGECOMPLETE_RESULTS results = { };
2017
2018 args.cbSize = sizeof(args);
2019 args.wzPackageId = wzPackageId;
2020 args.hrStatus = hrStatus;
2021 args.requested = requested;
2022
2023 results.cbSize = sizeof(results);
2024
2025 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGECOMPLETE, &args, &results);
2026 ExitOnFailure(hr, "BA OnPlanPackageComplete failed.");
2027
2028LExit:
2029 return hr;
2030}
2031
2032EXTERN_C BAAPI UserExperienceOnPlanRelatedBundle(
2033 __in BURN_USER_EXPERIENCE* pUserExperience,
2034 __in_z LPCWSTR wzBundleId,
2035 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
2036 )
2037{
2038 HRESULT hr = S_OK;
2039 BA_ONPLANRELATEDBUNDLE_ARGS args = { };
2040 BA_ONPLANRELATEDBUNDLE_RESULTS results = { };
2041
2042 args.cbSize = sizeof(args);
2043 args.wzBundleId = wzBundleId;
2044 args.recommendedState = *pRequestedState;
2045
2046 results.cbSize = sizeof(results);
2047 results.requestedState = *pRequestedState;
2048
2049 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLE, &args, &results);
2050 ExitOnFailure(hr, "BA OnPlanRelatedBundle failed.");
2051
2052 if (results.fCancel)
2053 {
2054 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
2055 }
2056 *pRequestedState = results.requestedState;
2057
2058LExit:
2059 return hr;
2060}
2061
2062EXTERN_C BAAPI UserExperienceOnPlanPatchTarget(
2063 __in BURN_USER_EXPERIENCE* pUserExperience,
2064 __in_z LPCWSTR wzPackageId,
2065 __in_z LPCWSTR wzProductCode,
2066 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
2067 )
2068{
2069 HRESULT hr = S_OK;
2070 BA_ONPLANPATCHTARGET_ARGS args = { };
2071 BA_ONPLANPATCHTARGET_RESULTS results = { };
2072
2073 args.cbSize = sizeof(args);
2074 args.wzPackageId = wzPackageId;
2075 args.wzProductCode = wzProductCode;
2076 args.recommendedState = *pRequestedState;
2077
2078 results.cbSize = sizeof(results);
2079 results.requestedState = *pRequestedState;
2080
2081 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPATCHTARGET, &args, &results);
2082 ExitOnFailure(hr, "BA OnPlanPatchTarget failed.");
2083
2084 if (results.fCancel)
2085 {
2086 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
2087 }
2088 *pRequestedState = results.requestedState;
2089
2090LExit:
2091 return hr;
2092}
2093
2094EXTERN_C BAAPI UserExperienceOnProgress(
2095 __in BURN_USER_EXPERIENCE* pUserExperience,
2096 __in BOOL fRollback,
2097 __in DWORD dwProgressPercentage,
2098 __in DWORD dwOverallPercentage
2099 )
2100{
2101 HRESULT hr = S_OK;
2102 BA_ONPROGRESS_ARGS args = { };
2103 BA_ONPROGRESS_RESULTS results = { };
2104
2105 args.cbSize = sizeof(args);
2106 args.dwProgressPercentage = dwProgressPercentage;
2107 args.dwOverallPercentage = dwOverallPercentage;
2108
2109 results.cbSize = sizeof(results);
2110
2111 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPROGRESS, &args, &results);
2112 hr = FilterExecuteResult(pUserExperience, hr, fRollback, results.fCancel, L"OnProgress");
2113
2114 return hr;
2115}
2116
2117EXTERN_C BAAPI UserExperienceOnRegisterBegin(
2118 __in BURN_USER_EXPERIENCE* pUserExperience
2119 )
2120{
2121 HRESULT hr = S_OK;
2122 BA_ONREGISTERBEGIN_ARGS args = { };
2123 BA_ONREGISTERBEGIN_RESULTS results = { };
2124
2125 args.cbSize = sizeof(args);
2126
2127 results.cbSize = sizeof(results);
2128
2129 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERBEGIN, &args, &results);
2130 ExitOnFailure(hr, "BA OnRegisterBegin failed.");
2131
2132 if (results.fCancel)
2133 {
2134 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
2135 }
2136
2137LExit:
2138 return hr;
2139}
2140
2141EXTERN_C BAAPI UserExperienceOnRegisterComplete(
2142 __in BURN_USER_EXPERIENCE* pUserExperience,
2143 __in HRESULT hrStatus
2144 )
2145{
2146 HRESULT hr = S_OK;
2147 BA_ONREGISTERCOMPLETE_ARGS args = { };
2148 BA_ONREGISTERCOMPLETE_RESULTS results = { };
2149
2150 args.cbSize = sizeof(args);
2151 args.hrStatus = hrStatus;
2152
2153 results.cbSize = sizeof(results);
2154
2155 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERCOMPLETE, &args, &results);
2156 ExitOnFailure(hr, "BA OnRegisterComplete failed.");
2157
2158LExit:
2159 return hr;
2160}
2161
2162EXTERN_C BAAPI UserExperienceOnRollbackMsiTransactionBegin(
2163 __in BURN_USER_EXPERIENCE* pUserExperience,
2164 __in LPCWSTR wzTransactionId
2165 )
2166{
2167 HRESULT hr = S_OK;
2168 BA_ONROLLBACKMSITRANSACTIONBEGIN_ARGS args = { };
2169 BA_ONROLLBACKMSITRANSACTIONBEGIN_RESULTS results = { };
2170
2171 args.cbSize = sizeof(args);
2172 args.wzTransactionId = wzTransactionId;
2173
2174 results.cbSize = sizeof(results);
2175
2176 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONROLLBACKMSITRANSACTIONBEGIN, &args, &results);
2177 ExitOnFailure(hr, "BA OnRollbackMsiTransactionBegin failed.");
2178
2179LExit:
2180 return hr;
2181}
2182
2183EXTERN_C BAAPI UserExperienceOnRollbackMsiTransactionComplete(
2184 __in BURN_USER_EXPERIENCE* pUserExperience,
2185 __in LPCWSTR wzTransactionId,
2186 __in HRESULT hrStatus
2187 )
2188{
2189 HRESULT hr = S_OK;
2190 BA_ONROLLBACKMSITRANSACTIONCOMPLETE_ARGS args = { };
2191 BA_ONROLLBACKMSITRANSACTIONCOMPLETE_RESULTS results = { };
2192
2193 args.cbSize = sizeof(args);
2194 args.wzTransactionId = wzTransactionId;
2195 args.hrStatus = hrStatus;
2196
2197 results.cbSize = sizeof(results);
2198
2199 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONROLLBACKMSITRANSACTIONCOMPLETE, &args, &results);
2200 ExitOnFailure(hr, "BA OnRollbackMsiTransactionComplete failed.");
2201
2202LExit:
2203 return hr;
2204}
2205
2206EXTERN_C BAAPI UserExperienceOnShutdown(
2207 __in BURN_USER_EXPERIENCE* pUserExperience,
2208 __inout BOOTSTRAPPER_SHUTDOWN_ACTION* pAction
2209 )
2210{
2211 HRESULT hr = S_OK;
2212 BA_ONSHUTDOWN_ARGS args = { };
2213 BA_ONSHUTDOWN_RESULTS results = { };
2214
2215 args.cbSize = sizeof(args);
2216
2217 results.cbSize = sizeof(results);
2218 results.action = *pAction;
2219
2220 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSHUTDOWN, &args, &results);
2221 ExitOnFailure(hr, "BA OnShutdown failed.");
2222
2223 *pAction = results.action;
2224
2225LExit:
2226 return hr;
2227}
2228
2229EXTERN_C BAAPI UserExperienceOnStartup(
2230 __in BURN_USER_EXPERIENCE* pUserExperience
2231 )
2232{
2233 HRESULT hr = S_OK;
2234 BA_ONSTARTUP_ARGS args = { };
2235 BA_ONSTARTUP_RESULTS results = { };
2236
2237 args.cbSize = sizeof(args);
2238
2239 results.cbSize = sizeof(results);
2240
2241 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSTARTUP, &args, &results);
2242 ExitOnFailure(hr, "BA OnStartup failed.");
2243
2244LExit:
2245 return hr;
2246}
2247
2248EXTERN_C BAAPI UserExperienceOnSystemRestorePointBegin(
2249 __in BURN_USER_EXPERIENCE* pUserExperience
2250 )
2251{
2252 HRESULT hr = S_OK;
2253 BA_ONSYSTEMRESTOREPOINTBEGIN_ARGS args = { };
2254 BA_ONSYSTEMRESTOREPOINTBEGIN_RESULTS results = { };
2255
2256 args.cbSize = sizeof(args);
2257
2258 results.cbSize = sizeof(results);
2259
2260 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMRESTOREPOINTBEGIN, &args, &results);
2261 ExitOnFailure(hr, "BA OnSystemRestorePointBegin failed.");
2262
2263LExit:
2264 return hr;
2265}
2266
2267EXTERN_C BAAPI UserExperienceOnSystemRestorePointComplete(
2268 __in BURN_USER_EXPERIENCE* pUserExperience,
2269 __in HRESULT hrStatus
2270 )
2271{
2272 HRESULT hr = S_OK;
2273 BA_ONSYSTEMRESTOREPOINTCOMPLETE_ARGS args = { };
2274 BA_ONSYSTEMRESTOREPOINTCOMPLETE_RESULTS results = { };
2275
2276 args.cbSize = sizeof(args);
2277 args.hrStatus = hrStatus;
2278
2279 results.cbSize = sizeof(results);
2280
2281 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMRESTOREPOINTCOMPLETE, &args, &results);
2282 ExitOnFailure(hr, "BA OnSystemRestorePointComplete failed.");
2283
2284LExit:
2285 return hr;
2286}
2287
2288EXTERN_C BAAPI UserExperienceOnSystemShutdown(
2289 __in BURN_USER_EXPERIENCE* pUserExperience,
2290 __in DWORD dwEndSession,
2291 __inout BOOL* pfCancel
2292 )
2293{
2294 HRESULT hr = S_OK;
2295 BA_ONSYSTEMSHUTDOWN_ARGS args = { };
2296 BA_ONSYSTEMSHUTDOWN_RESULTS results = { };
2297
2298 args.cbSize = sizeof(args);
2299 args.dwEndSession = dwEndSession;
2300
2301 results.cbSize = sizeof(results);
2302 results.fCancel = *pfCancel;
2303
2304 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMSHUTDOWN, &args, &results);
2305 ExitOnFailure(hr, "BA OnSystemShutdown failed.");
2306
2307 *pfCancel = results.fCancel;
2308
2309LExit:
2310 return hr;
2311}
2312
2313EXTERN_C BAAPI UserExperienceOnUnregisterBegin(
2314 __in BURN_USER_EXPERIENCE* pUserExperience,
2315 __inout BOOL* pfKeepRegistration
2316 )
2317{
2318 HRESULT hr = S_OK;
2319 BA_ONUNREGISTERBEGIN_ARGS args = { };
2320 BA_ONUNREGISTERBEGIN_RESULTS results = { };
2321
2322 args.cbSize = sizeof(args);
2323 args.fKeepRegistration = *pfKeepRegistration;
2324
2325 results.cbSize = sizeof(results);
2326
2327 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERBEGIN, &args, &results);
2328 ExitOnFailure(hr, "BA OnUnregisterBegin failed.");
2329
2330 if (!args.fKeepRegistration && results.fForceKeepRegistration)
2331 {
2332 *pfKeepRegistration = TRUE;
2333 }
2334
2335LExit:
2336 return hr;
2337}
2338
2339EXTERN_C BAAPI UserExperienceOnUnregisterComplete(
2340 __in BURN_USER_EXPERIENCE* pUserExperience,
2341 __in HRESULT hrStatus
2342 )
2343{
2344 HRESULT hr = S_OK;
2345 BA_ONUNREGISTERCOMPLETE_ARGS args = { };
2346 BA_ONUNREGISTERCOMPLETE_RESULTS results = { };
2347
2348 args.cbSize = sizeof(args);
2349 args.hrStatus = hrStatus;
2350
2351 results.cbSize = sizeof(results);
2352
2353 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERCOMPLETE, &args, &results);
2354 ExitOnFailure(hr, "BA OnUnregisterComplete failed.");
2355
2356LExit:
2357 return hr;
2358}
2359
2360extern "C" int UserExperienceCheckExecuteResult(
2361 __in BURN_USER_EXPERIENCE* pUserExperience,
2362 __in BOOL fRollback,
2363 __in DWORD dwAllowedResults,
2364 __in int nResult
2365 )
2366{
2367 // Do not allow canceling while rolling back.
2368 if (fRollback && (IDCANCEL == nResult || IDABORT == nResult))
2369 {
2370 nResult = IDNOACTION;
2371 }
2372 else if (FAILED(pUserExperience->hrApplyError) && !fRollback) // if we failed cancel except not during rollback.
2373 {
2374 nResult = IDCANCEL;
2375 }
2376
2377 nResult = FilterResult(dwAllowedResults, nResult);
2378 return nResult;
2379}
2380
2381extern "C" HRESULT UserExperienceInterpretExecuteResult(
2382 __in BURN_USER_EXPERIENCE* pUserExperience,
2383 __in BOOL fRollback,
2384 __in DWORD dwAllowedResults,
2385 __in int nResult
2386 )
2387{
2388 HRESULT hr = S_OK;
2389
2390 // If we failed return that error unless this is rollback which should roll on.
2391 if (FAILED(pUserExperience->hrApplyError) && !fRollback)
2392 {
2393 hr = pUserExperience->hrApplyError;
2394 }
2395 else
2396 {
2397 int nCheckedResult = UserExperienceCheckExecuteResult(pUserExperience, fRollback, dwAllowedResults, nResult);
2398 hr = IDOK == nCheckedResult || IDNOACTION == nCheckedResult ? S_OK : IDCANCEL == nCheckedResult || IDABORT == nCheckedResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
2399 }
2400
2401 return hr;
2402}
2403
2404
2405// internal functions
2406
2407static int FilterResult(
2408 __in DWORD dwAllowedResults,
2409 __in int nResult
2410 )
2411{
2412 DWORD dwFilteredAllowedResults = dwAllowedResults & MB_TYPEMASK;
2413 if (IDNOACTION == nResult || IDERROR == nResult) // do nothing and errors pass through.
2414 {
2415 }
2416 else
2417 {
2418 switch (dwFilteredAllowedResults)
2419 {
2420 case MB_OK:
2421 nResult = IDOK;
2422 break;
2423
2424 case MB_OKCANCEL:
2425 if (IDOK == nResult || IDYES == nResult)
2426 {
2427 nResult = IDOK;
2428 }
2429 else if (IDCANCEL == nResult || IDABORT == nResult || IDNO == nResult)
2430 {
2431 nResult = IDCANCEL;
2432 }
2433 else
2434 {
2435 nResult = IDNOACTION;
2436 }
2437 break;
2438
2439 case MB_ABORTRETRYIGNORE:
2440 if (IDCANCEL == nResult || IDABORT == nResult)
2441 {
2442 nResult = IDABORT;
2443 }
2444 else if (IDRETRY == nResult || IDTRYAGAIN == nResult)
2445 {
2446 nResult = IDRETRY;
2447 }
2448 else if (IDIGNORE == nResult)
2449 {
2450 nResult = IDIGNORE;
2451 }
2452 else
2453 {
2454 nResult = IDNOACTION;
2455 }
2456 break;
2457
2458 case MB_YESNO:
2459 if (IDOK == nResult || IDYES == nResult)
2460 {
2461 nResult = IDYES;
2462 }
2463 else if (IDCANCEL == nResult || IDABORT == nResult || IDNO == nResult)
2464 {
2465 nResult = IDNO;
2466 }
2467 else
2468 {
2469 nResult = IDNOACTION;
2470 }
2471 break;
2472
2473 case MB_YESNOCANCEL:
2474 if (IDOK == nResult || IDYES == nResult)
2475 {
2476 nResult = IDYES;
2477 }
2478 else if (IDNO == nResult)
2479 {
2480 nResult = IDNO;
2481 }
2482 else if (IDCANCEL == nResult || IDABORT == nResult)
2483 {
2484 nResult = IDCANCEL;
2485 }
2486 else
2487 {
2488 nResult = IDNOACTION;
2489 }
2490 break;
2491
2492 case MB_RETRYCANCEL:
2493 if (IDRETRY == nResult || IDTRYAGAIN == nResult)
2494 {
2495 nResult = IDRETRY;
2496 }
2497 else if (IDCANCEL == nResult || IDABORT == nResult)
2498 {
2499 nResult = IDABORT;
2500 }
2501 else
2502 {
2503 nResult = IDNOACTION;
2504 }
2505 break;
2506
2507 case MB_CANCELTRYCONTINUE:
2508 if (IDCANCEL == nResult || IDABORT == nResult)
2509 {
2510 nResult = IDABORT;
2511 }
2512 else if (IDRETRY == nResult || IDTRYAGAIN == nResult)
2513 {
2514 nResult = IDRETRY;
2515 }
2516 else if (IDCONTINUE == nResult || IDIGNORE == nResult)
2517 {
2518 nResult = IDCONTINUE;
2519 }
2520 else
2521 {
2522 nResult = IDNOACTION;
2523 }
2524 break;
2525
2526 case WIU_MB_OKIGNORECANCELRETRY: // custom Windows Installer utility return code.
2527 if (IDOK == nResult || IDYES == nResult)
2528 {
2529 nResult = IDOK;
2530 }
2531 else if (IDCONTINUE == nResult || IDIGNORE == nResult)
2532 {
2533 nResult = IDIGNORE;
2534 }
2535 else if (IDCANCEL == nResult || IDABORT == nResult)
2536 {
2537 nResult = IDCANCEL;
2538 }
2539 else if (IDRETRY == nResult || IDTRYAGAIN == nResult || IDNO == nResult)
2540 {
2541 nResult = IDRETRY;
2542 }
2543 else
2544 {
2545 nResult = IDNOACTION;
2546 }
2547 break;
2548
2549 case MB_RETRYTRYAGAIN: // custom return code.
2550 if (IDRETRY != nResult && IDTRYAGAIN != nResult)
2551 {
2552 nResult = IDNOACTION;
2553 }
2554 break;
2555
2556 default:
2557 AssertSz(FALSE, "Unknown allowed results.");
2558 break;
2559 }
2560 }
2561
2562 return nResult;
2563}
2564
2565// This filters the BA's responses to events during apply.
2566// If an apply thread failed, then return its error so this thread will bail out.
2567// During rollback, the BA can't cancel.
2568static HRESULT FilterExecuteResult(
2569 __in BURN_USER_EXPERIENCE* pUserExperience,
2570 __in HRESULT hrStatus,
2571 __in BOOL fRollback,
2572 __in BOOL fCancel,
2573 __in LPCWSTR sczEventName
2574 )
2575{
2576 HRESULT hr = hrStatus;
2577 HRESULT hrApplyError = pUserExperience->hrApplyError; // make sure to use the same value for the whole method, since it can be changed in other threads.
2578
2579 // If we failed return that error unless this is rollback which should roll on.
2580 if (FAILED(hrApplyError) && !fRollback)
2581 {
2582 hr = hrApplyError;
2583 }
2584 else if (fRollback)
2585 {
2586 if (fCancel)
2587 {
2588 LogId(REPORT_STANDARD, MSG_APPLY_CANCEL_IGNORED_DURING_ROLLBACK, sczEventName);
2589 }
2590 // TODO: since cancel isn't allowed, should the BA's HRESULT be ignored as well?
2591 // In the previous code, they could still alter rollback by returning IDERROR.
2592 }
2593 else
2594 {
2595 ExitOnFailure(hr, "BA %ls failed.", sczEventName);
2596
2597 if (fCancel)
2598 {
2599 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
2600 }
2601 }
2602
2603LExit:
2604 return hr;
2605}
2606
2607static HRESULT SendBAMessage(
2608 __in BURN_USER_EXPERIENCE* pUserExperience,
2609 __in BOOTSTRAPPER_APPLICATION_MESSAGE message,
2610 __in const LPVOID pvArgs,
2611 __inout LPVOID pvResults
2612 )
2613{
2614 HRESULT hr = S_OK;
2615
2616 if (!pUserExperience->hUXModule)
2617 {
2618 ExitFunction();
2619 }
2620
2621 hr = pUserExperience->pfnBAProc(message, pvArgs, pvResults, pUserExperience->pvBAProcContext);
2622 if (hr == E_NOTIMPL)
2623 {
2624 hr = S_OK;
2625 }
2626
2627LExit:
2628 return hr;
2629}
2630
2631static HRESULT SendBAMessageFromInactiveEngine(
2632 __in BURN_USER_EXPERIENCE* pUserExperience,
2633 __in BOOTSTRAPPER_APPLICATION_MESSAGE message,
2634 __in const LPVOID pvArgs,
2635 __inout LPVOID pvResults
2636 )
2637{
2638 HRESULT hr = S_OK;
2639
2640 if (!pUserExperience->hUXModule)
2641 {
2642 ExitFunction();
2643 }
2644
2645 UserExperienceDeactivateEngine(pUserExperience);
2646
2647 hr = SendBAMessage(pUserExperience, message, pvArgs, pvResults);
2648
2649 UserExperienceActivateEngine(pUserExperience);
2650
2651LExit:
2652 return hr;
2653}
diff --git a/src/burn/engine/userexperience.h b/src/burn/engine/userexperience.h
new file mode 100644
index 00000000..f2453dca
--- /dev/null
+++ b/src/burn/engine/userexperience.h
@@ -0,0 +1,545 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4#define BAAPI HRESULT __stdcall
5
6#if defined(__cplusplus)
7extern "C" {
8#endif
9
10
11// constants
12
13const DWORD MB_RETRYTRYAGAIN = 0xF;
14
15
16// structs
17
18typedef struct _BOOTSTRAPPER_ENGINE_CONTEXT BOOTSTRAPPER_ENGINE_CONTEXT;
19
20typedef struct _BURN_USER_EXPERIENCE
21{
22 BOOL fSplashScreen;
23 BURN_PAYLOADS payloads;
24
25 HMODULE hUXModule;
26 PFN_BOOTSTRAPPER_APPLICATION_PROC pfnBAProc;
27 LPVOID pvBAProcContext;
28 BOOL fDisableUnloading;
29 LPWSTR sczTempDirectory;
30
31 CRITICAL_SECTION csEngineActive; // Changing the engine active state in the user experience must be
32 // syncronized through this critical section.
33 // Note: The engine must never do a UX callback while in this critical section.
34
35 BOOL fEngineActive; // Indicates that the engine is currently active with one of the execution
36 // steps (detect, plan, apply), and cannot accept requests from the UX.
37 // This flag should be cleared by the engine prior to UX callbacks that
38 // allows altering of the engine state.
39
40 HRESULT hrApplyError; // Tracks is an error occurs during apply that requires the cache or
41 // execute threads to bail.
42
43 HWND hwndApply; // The window handle provided at the beginning of Apply(). Only valid
44 // during apply.
45
46 HWND hwndDetect; // The window handle provided at the beginning of Detect(). Only valid
47 // during Detect.
48
49 DWORD dwExitCode; // Exit code returned by the user experience for the engine overall.
50} BURN_USER_EXPERIENCE;
51
52// functions
53
54HRESULT UserExperienceParseFromXml(
55 __in BURN_USER_EXPERIENCE* pUserExperience,
56 __in IXMLDOMNode* pixnBundle
57 );
58void UserExperienceUninitialize(
59 __in BURN_USER_EXPERIENCE* pUserExperience
60 );
61HRESULT UserExperienceLoad(
62 __in BURN_USER_EXPERIENCE* pUserExperience,
63 __in BOOTSTRAPPER_ENGINE_CONTEXT* pEngineContext,
64 __in BOOTSTRAPPER_COMMAND* pCommand
65 );
66HRESULT UserExperienceUnload(
67 __in BURN_USER_EXPERIENCE* pUserExperience
68 );
69HRESULT UserExperienceEnsureWorkingFolder(
70 __in LPCWSTR wzBundleId,
71 __deref_out_z LPWSTR* psczUserExperienceWorkingFolder
72 );
73HRESULT UserExperienceRemove(
74 __in BURN_USER_EXPERIENCE* pUserExperience
75 );
76int UserExperienceSendError(
77 __in BURN_USER_EXPERIENCE* pUserExperience,
78 __in BOOTSTRAPPER_ERROR_TYPE errorType,
79 __in_z_opt LPCWSTR wzPackageId,
80 __in HRESULT hrCode,
81 __in_z_opt LPCWSTR wzError,
82 __in DWORD uiFlags,
83 __in int nRecommendation
84 );
85void UserExperienceActivateEngine(
86 __in BURN_USER_EXPERIENCE* pUserExperience
87 );
88void UserExperienceDeactivateEngine(
89 __in BURN_USER_EXPERIENCE* pUserExperience
90 );
91/********************************************************************
92 UserExperienceEnsureEngineInactive - Verifies the engine is inactive.
93 The caller MUST enter the csActive critical section before calling.
94
95*********************************************************************/
96HRESULT UserExperienceEnsureEngineInactive(
97 __in BURN_USER_EXPERIENCE* pUserExperience
98 );
99void UserExperienceExecuteReset(
100 __in BURN_USER_EXPERIENCE* pUserExperience
101 );
102void UserExperienceExecutePhaseComplete(
103 __in BURN_USER_EXPERIENCE* pUserExperience,
104 __in HRESULT hrResult
105 );
106BAAPI UserExperienceOnApplyBegin(
107 __in BURN_USER_EXPERIENCE* pUserExperience,
108 __in DWORD dwPhaseCount
109 );
110BAAPI UserExperienceOnApplyComplete(
111 __in BURN_USER_EXPERIENCE* pUserExperience,
112 __in HRESULT hrStatus,
113 __in BOOTSTRAPPER_APPLY_RESTART restart,
114 __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction
115 );
116BAAPI UserExperienceOnBeginMsiTransactionBegin(
117 __in BURN_USER_EXPERIENCE* pUserExperience,
118 __in LPCWSTR wzTransactionId
119 );
120BAAPI UserExperienceOnBeginMsiTransactionComplete(
121 __in BURN_USER_EXPERIENCE* pUserExperience,
122 __in LPCWSTR wzTransactionId,
123 __in HRESULT hrStatus
124 );
125BAAPI UserExperienceOnCacheAcquireBegin(
126 __in BURN_USER_EXPERIENCE* pUserExperience,
127 __in_z_opt LPCWSTR wzPackageOrContainerId,
128 __in_z_opt LPCWSTR wzPayloadId,
129 __in_z LPWSTR* pwzSource,
130 __in_z LPWSTR* pwzDownloadUrl,
131 __in_z_opt LPCWSTR wzPayloadContainerId,
132 __out BOOTSTRAPPER_CACHE_OPERATION* pCacheOperation
133 );
134BAAPI UserExperienceOnCacheAcquireComplete(
135 __in BURN_USER_EXPERIENCE* pUserExperience,
136 __in_z_opt LPCWSTR wzPackageOrContainerId,
137 __in_z_opt LPCWSTR wzPayloadId,
138 __in HRESULT hrStatus,
139 __inout BOOL* pfRetry
140 );
141BAAPI UserExperienceOnCacheAcquireProgress(
142 __in BURN_USER_EXPERIENCE* pUserExperience,
143 __in_z_opt LPCWSTR wzPackageOrContainerId,
144 __in_z_opt LPCWSTR wzPayloadId,
145 __in DWORD64 dw64Progress,
146 __in DWORD64 dw64Total,
147 __in DWORD dwOverallPercentage
148 );
149BAAPI UserExperienceOnCacheAcquireResolving(
150 __in BURN_USER_EXPERIENCE* pUserExperience,
151 __in_z_opt LPCWSTR wzPackageOrContainerId,
152 __in_z_opt LPCWSTR wzPayloadId,
153 __in_z LPWSTR* rgSearchPaths,
154 __in DWORD cSearchPaths,
155 __in BOOL fFoundLocal,
156 __in DWORD* pdwChosenSearchPath,
157 __in_z_opt LPWSTR* pwzDownloadUrl,
158 __in_z_opt LPCWSTR wzPayloadContainerId,
159 __inout BOOTSTRAPPER_CACHE_RESOLVE_OPERATION* pCacheOperation
160 );
161BAAPI UserExperienceOnCacheBegin(
162 __in BURN_USER_EXPERIENCE* pUserExperience
163 );
164BAAPI UserExperienceOnCacheComplete(
165 __in BURN_USER_EXPERIENCE* pUserExperience,
166 __in HRESULT hrStatus
167 );
168BAAPI UserExperienceOnCacheContainerOrPayloadVerifyBegin(
169 __in BURN_USER_EXPERIENCE* pUserExperience,
170 __in_z_opt LPCWSTR wzPackageOrContainerId,
171 __in_z_opt LPCWSTR wzPayloadId
172 );
173BAAPI UserExperienceOnCacheContainerOrPayloadVerifyComplete(
174 __in BURN_USER_EXPERIENCE* pUserExperience,
175 __in_z_opt LPCWSTR wzPackageOrContainerId,
176 __in_z_opt LPCWSTR wzPayloadId,
177 __in HRESULT hrStatus
178 );
179BAAPI UserExperienceOnCacheContainerOrPayloadVerifyProgress(
180 __in BURN_USER_EXPERIENCE* pUserExperience,
181 __in_z_opt LPCWSTR wzPackageOrContainerId,
182 __in_z_opt LPCWSTR wzPayloadId,
183 __in DWORD64 dw64Progress,
184 __in DWORD64 dw64Total,
185 __in DWORD dwOverallPercentage
186 );
187BAAPI UserExperienceOnCachePackageBegin(
188 __in BURN_USER_EXPERIENCE* pUserExperience,
189 __in_z LPCWSTR wzPackageId,
190 __in DWORD cCachePayloads,
191 __in DWORD64 dw64PackageCacheSize
192 );
193BAAPI UserExperienceOnCachePackageComplete(
194 __in BURN_USER_EXPERIENCE* pUserExperience,
195 __in_z LPCWSTR wzPackageId,
196 __in HRESULT hrStatus,
197 __inout BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION* pAction
198 );
199BAAPI UserExperienceOnCachePayloadExtractBegin(
200 __in BURN_USER_EXPERIENCE* pUserExperience,
201 __in_z_opt LPCWSTR wzContainerId,
202 __in_z_opt LPCWSTR wzPayloadId
203 );
204BAAPI UserExperienceOnCachePayloadExtractComplete(
205 __in BURN_USER_EXPERIENCE* pUserExperience,
206 __in_z_opt LPCWSTR wzContainerId,
207 __in_z_opt LPCWSTR wzPayloadId,
208 __in HRESULT hrStatus
209 );
210BAAPI UserExperienceOnCachePayloadExtractProgress(
211 __in BURN_USER_EXPERIENCE* pUserExperience,
212 __in_z_opt LPCWSTR wzContainerId,
213 __in_z_opt LPCWSTR wzPayloadId,
214 __in DWORD64 dw64Progress,
215 __in DWORD64 dw64Total,
216 __in DWORD dwOverallPercentage
217 );
218BAAPI UserExperienceOnCacheVerifyBegin(
219 __in BURN_USER_EXPERIENCE* pUserExperience,
220 __in_z_opt LPCWSTR wzPackageOrContainerId,
221 __in_z_opt LPCWSTR wzPayloadId
222 );
223BAAPI UserExperienceOnCacheVerifyComplete(
224 __in BURN_USER_EXPERIENCE* pUserExperience,
225 __in_z_opt LPCWSTR wzPackageOrContainerId,
226 __in_z_opt LPCWSTR wzPayloadId,
227 __in HRESULT hrStatus,
228 __inout BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION* pAction
229 );
230BAAPI UserExperienceOnCacheVerifyProgress(
231 __in BURN_USER_EXPERIENCE* pUserExperience,
232 __in_z_opt LPCWSTR wzPackageOrContainerId,
233 __in_z_opt LPCWSTR wzPayloadId,
234 __in DWORD64 dw64Progress,
235 __in DWORD64 dw64Total,
236 __in DWORD dwOverallPercentage,
237 __in BOOTSTRAPPER_CACHE_VERIFY_STEP verifyStep
238 );
239BAAPI UserExperienceOnCommitMsiTransactionBegin(
240 __in BURN_USER_EXPERIENCE* pUserExperience,
241 __in LPCWSTR wzTransactionId
242 );
243BAAPI UserExperienceOnCommitMsiTransactionComplete(
244 __in BURN_USER_EXPERIENCE* pUserExperience,
245 __in LPCWSTR wzTransactionId,
246 __in HRESULT hrStatus
247 );
248BAAPI UserExperienceOnDetectBegin(
249 __in BURN_USER_EXPERIENCE* pUserExperience,
250 __in BOOL fCached,
251 __in BOOL fInstalled,
252 __in DWORD cPackages
253 );
254BAAPI UserExperienceOnDetectComplete(
255 __in BURN_USER_EXPERIENCE* pUserExperience,
256 __in HRESULT hrStatus,
257 __in BOOL fEligibleForCleanup
258 );
259BAAPI UserExperienceOnDetectForwardCompatibleBundle(
260 __in BURN_USER_EXPERIENCE* pUserExperience,
261 __in_z LPCWSTR wzBundleId,
262 __in BOOTSTRAPPER_RELATION_TYPE relationType,
263 __in_z LPCWSTR wzBundleTag,
264 __in BOOL fPerMachine,
265 __in VERUTIL_VERSION* pVersion,
266 __in BOOL fMissingFromCache
267 );
268BAAPI UserExperienceOnDetectMsiFeature(
269 __in BURN_USER_EXPERIENCE* pUserExperience,
270 __in_z LPCWSTR wzPackageId,
271 __in_z LPCWSTR wzFeatureId,
272 __in BOOTSTRAPPER_FEATURE_STATE state
273 );
274BAAPI UserExperienceOnDetectPackageBegin(
275 __in BURN_USER_EXPERIENCE* pUserExperience,
276 __in_z LPCWSTR wzPackageId
277 );
278BAAPI UserExperienceOnDetectPackageComplete(
279 __in BURN_USER_EXPERIENCE* pUserExperience,
280 __in_z LPCWSTR wzPackageId,
281 __in HRESULT hrStatus,
282 __in BOOTSTRAPPER_PACKAGE_STATE state,
283 __in BOOL fCached
284 );
285BAAPI UserExperienceOnDetectRelatedBundle(
286 __in BURN_USER_EXPERIENCE* pUserExperience,
287 __in_z LPCWSTR wzBundleId,
288 __in BOOTSTRAPPER_RELATION_TYPE relationType,
289 __in_z LPCWSTR wzBundleTag,
290 __in BOOL fPerMachine,
291 __in VERUTIL_VERSION* pVersion,
292 __in BOOTSTRAPPER_RELATED_OPERATION operation,
293 __in BOOL fMissingFromCache
294 );
295BAAPI UserExperienceOnDetectRelatedMsiPackage(
296 __in BURN_USER_EXPERIENCE* pUserExperience,
297 __in_z LPCWSTR wzPackageId,
298 __in_z LPCWSTR wzUpgradeCode,
299 __in_z LPCWSTR wzProductCode,
300 __in BOOL fPerMachine,
301 __in VERUTIL_VERSION* pVersion,
302 __in BOOTSTRAPPER_RELATED_OPERATION operation
303 );
304BAAPI UserExperienceOnDetectPatchTarget(
305 __in BURN_USER_EXPERIENCE* pUserExperience,
306 __in_z LPCWSTR wzPackageId,
307 __in_z LPCWSTR wzProductCode,
308 __in BOOTSTRAPPER_PACKAGE_STATE patchState
309 );
310BAAPI UserExperienceOnDetectUpdate(
311 __in BURN_USER_EXPERIENCE* pUserExperience,
312 __in_z_opt LPCWSTR wzUpdateLocation,
313 __in DWORD64 dw64Size,
314 __in VERUTIL_VERSION* pVersion,
315 __in_z_opt LPCWSTR wzTitle,
316 __in_z_opt LPCWSTR wzSummary,
317 __in_z_opt LPCWSTR wzContentType,
318 __in_z_opt LPCWSTR wzContent,
319 __inout BOOL* pfStopProcessingUpdates
320 );
321BAAPI UserExperienceOnDetectUpdateBegin(
322 __in BURN_USER_EXPERIENCE* pUserExperience,
323 __in_z LPCWSTR wzUpdateLocation,
324 __inout BOOL* pfSkip
325 );
326BAAPI UserExperienceOnDetectUpdateComplete(
327 __in BURN_USER_EXPERIENCE* pUserExperience,
328 __in HRESULT hrStatus,
329 __inout BOOL* pfIgnoreError
330 );
331BAAPI UserExperienceOnElevateBegin(
332 __in BURN_USER_EXPERIENCE* pUserExperience
333 );
334BAAPI UserExperienceOnElevateComplete(
335 __in BURN_USER_EXPERIENCE* pUserExperience,
336 __in HRESULT hrStatus
337 );
338BAAPI UserExperienceOnError(
339 __in BURN_USER_EXPERIENCE* pUserExperience,
340 __in BOOTSTRAPPER_ERROR_TYPE errorType,
341 __in_z_opt LPCWSTR wzPackageId,
342 __in DWORD dwCode,
343 __in_z_opt LPCWSTR wzError,
344 __in DWORD dwUIHint,
345 __in DWORD cData,
346 __in_ecount_z_opt(cData) LPCWSTR* rgwzData,
347 __inout int* pnResult
348 );
349BAAPI UserExperienceOnExecuteBegin(
350 __in BURN_USER_EXPERIENCE* pUserExperience,
351 __in DWORD cExecutingPackages
352 );
353BAAPI UserExperienceOnExecuteComplete(
354 __in BURN_USER_EXPERIENCE* pUserExperience,
355 __in HRESULT hrStatus
356 );
357BAAPI UserExperienceOnExecuteFilesInUse(
358 __in BURN_USER_EXPERIENCE* pUserExperience,
359 __in_z LPCWSTR wzPackageId,
360 __in DWORD cFiles,
361 __in_ecount_z_opt(cFiles) LPCWSTR* rgwzFiles,
362 __inout int* pnResult
363 );
364BAAPI UserExperienceOnExecuteMsiMessage(
365 __in BURN_USER_EXPERIENCE* pUserExperience,
366 __in_z LPCWSTR wzPackageId,
367 __in INSTALLMESSAGE messageType,
368 __in DWORD dwUIHint,
369 __in_z LPCWSTR wzMessage,
370 __in DWORD cData,
371 __in_ecount_z_opt(cData) LPCWSTR* rgwzData,
372 __inout int* pnResult
373 );
374BAAPI UserExperienceOnExecutePackageBegin(
375 __in BURN_USER_EXPERIENCE* pUserExperience,
376 __in_z LPCWSTR wzPackageId,
377 __in BOOL fExecute,
378 __in BOOTSTRAPPER_ACTION_STATE action,
379 __in INSTALLUILEVEL uiLevel,
380 __in BOOL fDisableExternalUiHandler
381 );
382BAAPI UserExperienceOnExecutePackageComplete(
383 __in BURN_USER_EXPERIENCE* pUserExperience,
384 __in_z LPCWSTR wzPackageId,
385 __in HRESULT hrStatus,
386 __in BOOTSTRAPPER_APPLY_RESTART restart,
387 __inout BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION* pAction
388 );
389BAAPI UserExperienceOnExecutePatchTarget(
390 __in BURN_USER_EXPERIENCE* pUserExperience,
391 __in_z LPCWSTR wzPackageId,
392 __in_z LPCWSTR wzTargetProductCode
393 );
394BAAPI UserExperienceOnExecuteProgress(
395 __in BURN_USER_EXPERIENCE* pUserExperience,
396 __in_z LPCWSTR wzPackageId,
397 __in DWORD dwProgressPercentage,
398 __in DWORD dwOverallPercentage,
399 __out int* pnResult
400 );
401BAAPI UserExperienceOnLaunchApprovedExeBegin(
402 __in BURN_USER_EXPERIENCE* pUserExperience
403 );
404BAAPI UserExperienceOnLaunchApprovedExeComplete(
405 __in BURN_USER_EXPERIENCE* pUserExperience,
406 __in HRESULT hrStatus,
407 __in DWORD dwProcessId
408 );
409BAAPI UserExperienceOnPauseAUBegin(
410 __in BURN_USER_EXPERIENCE* pUserExperience
411 );
412BAAPI UserExperienceOnPauseAUComplete(
413 __in BURN_USER_EXPERIENCE* pUserExperience,
414 __in HRESULT hrStatus
415 );
416BAAPI UserExperienceOnPlanBegin(
417 __in BURN_USER_EXPERIENCE* pUserExperience,
418 __in DWORD cPackages
419 );
420BAAPI UserExperienceOnPlanComplete(
421 __in BURN_USER_EXPERIENCE* pUserExperience,
422 __in HRESULT hrStatus
423 );
424BAAPI UserExperienceOnPlanForwardCompatibleBundle(
425 __in BURN_USER_EXPERIENCE* pUserExperience,
426 __in_z LPCWSTR wzBundleId,
427 __in BOOTSTRAPPER_RELATION_TYPE relationType,
428 __in_z LPCWSTR wzBundleTag,
429 __in BOOL fPerMachine,
430 __in VERUTIL_VERSION* pVersion,
431 __inout BOOL* pfIgnoreBundle
432 );
433BAAPI UserExperienceOnPlanMsiFeature(
434 __in BURN_USER_EXPERIENCE* pUserExperience,
435 __in_z LPCWSTR wzPackageId,
436 __in_z LPCWSTR wzFeatureId,
437 __inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState
438 );
439BAAPI UserExperienceOnPlanMsiPackage(
440 __in BURN_USER_EXPERIENCE* pUserExperience,
441 __in_z LPCWSTR wzPackageId,
442 __in BOOL fExecute,
443 __in BOOTSTRAPPER_ACTION_STATE action,
444 __inout BURN_MSI_PROPERTY* pActionMsiProperty,
445 __inout INSTALLUILEVEL* pUiLevel,
446 __inout BOOL* pfDisableExternalUiHandler
447 );
448BAAPI UserExperienceOnPlannedPackage(
449 __in BURN_USER_EXPERIENCE* pUserExperience,
450 __in_z LPCWSTR wzPackageId,
451 __in BOOTSTRAPPER_ACTION_STATE execute,
452 __in BOOTSTRAPPER_ACTION_STATE rollback,
453 __in BOOL fPlannedCache,
454 __in BOOL fPlannedUncache
455 );
456BAAPI UserExperienceOnPlanPackageBegin(
457 __in BURN_USER_EXPERIENCE* pUserExperience,
458 __in_z LPCWSTR wzPackageId,
459 __in BOOTSTRAPPER_PACKAGE_STATE state,
460 __in BOOL fCached,
461 __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition,
462 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState,
463 __inout BOOTSTRAPPER_CACHE_TYPE* pRequestedCacheType
464 );
465BAAPI UserExperienceOnPlanPackageComplete(
466 __in BURN_USER_EXPERIENCE* pUserExperience,
467 __in_z LPCWSTR wzPackageId,
468 __in HRESULT hrStatus,
469 __in BOOTSTRAPPER_REQUEST_STATE requested
470 );
471BAAPI UserExperienceOnPlanRelatedBundle(
472 __in BURN_USER_EXPERIENCE* pUserExperience,
473 __in_z LPCWSTR wzBundleId,
474 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
475 );
476BAAPI UserExperienceOnPlanPatchTarget(
477 __in BURN_USER_EXPERIENCE* pUserExperience,
478 __in_z LPCWSTR wzPackageId,
479 __in_z LPCWSTR wzProductCode,
480 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
481 );
482BAAPI UserExperienceOnProgress(
483 __in BURN_USER_EXPERIENCE* pUserExperience,
484 __in BOOL fRollback,
485 __in DWORD dwProgressPercentage,
486 __in DWORD dwOverallPercentage
487 );
488BAAPI UserExperienceOnRegisterBegin(
489 __in BURN_USER_EXPERIENCE* pUserExperience
490 );
491BAAPI UserExperienceOnRegisterComplete(
492 __in BURN_USER_EXPERIENCE* pUserExperience,
493 __in HRESULT hrStatus
494 );
495BAAPI UserExperienceOnRollbackMsiTransactionBegin(
496 __in BURN_USER_EXPERIENCE* pUserExperience,
497 __in LPCWSTR wzTransactionId
498 );
499BAAPI UserExperienceOnRollbackMsiTransactionComplete(
500 __in BURN_USER_EXPERIENCE* pUserExperience,
501 __in LPCWSTR wzTransactionId,
502 __in HRESULT hrStatus
503 );
504BAAPI UserExperienceOnShutdown(
505 __in BURN_USER_EXPERIENCE* pUserExperience,
506 __inout BOOTSTRAPPER_SHUTDOWN_ACTION* pAction
507 );
508BAAPI UserExperienceOnStartup(
509 __in BURN_USER_EXPERIENCE* pUserExperience
510 );
511BAAPI UserExperienceOnSystemRestorePointBegin(
512 __in BURN_USER_EXPERIENCE* pUserExperience
513 );
514BAAPI UserExperienceOnSystemRestorePointComplete(
515 __in BURN_USER_EXPERIENCE* pUserExperience,
516 __in HRESULT hrStatus
517 );
518BAAPI UserExperienceOnSystemShutdown(
519 __in BURN_USER_EXPERIENCE* pUserExperience,
520 __in DWORD dwEndSession,
521 __inout BOOL* pfCancel
522 );
523BAAPI UserExperienceOnUnregisterBegin(
524 __in BURN_USER_EXPERIENCE* pUserExperience,
525 __inout BOOL* pfKeepRegistration
526 );
527BAAPI UserExperienceOnUnregisterComplete(
528 __in BURN_USER_EXPERIENCE* pUserExperience,
529 __in HRESULT hrStatus
530 );
531int UserExperienceCheckExecuteResult(
532 __in BURN_USER_EXPERIENCE* pUserExperience,
533 __in BOOL fRollback,
534 __in DWORD dwAllowedResults,
535 __in int nResult
536 );
537HRESULT UserExperienceInterpretExecuteResult(
538 __in BURN_USER_EXPERIENCE* pUserExperience,
539 __in BOOL fRollback,
540 __in DWORD dwAllowedResults,
541 __in int nResult
542 );
543#if defined(__cplusplus)
544}
545#endif
diff --git a/src/burn/engine/variable.cpp b/src/burn/engine/variable.cpp
new file mode 100644
index 00000000..6f818ff3
--- /dev/null
+++ b/src/burn/engine/variable.cpp
@@ -0,0 +1,2323 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// structs
7
8typedef const struct _BUILT_IN_VARIABLE_DECLARATION
9{
10 LPCWSTR wzVariable;
11 PFN_INITIALIZEVARIABLE pfnInitialize;
12 DWORD_PTR dwpInitializeData;
13 BOOL fPersist;
14 BOOL fOverridable;
15} BUILT_IN_VARIABLE_DECLARATION;
16
17
18// constants
19
20const DWORD GROW_VARIABLE_ARRAY = 3;
21
22enum OS_INFO_VARIABLE
23{
24 OS_INFO_VARIABLE_NONE,
25 OS_INFO_VARIABLE_VersionNT,
26 OS_INFO_VARIABLE_VersionNT64,
27 OS_INFO_VARIABLE_ServicePackLevel,
28 OS_INFO_VARIABLE_NTProductType,
29 OS_INFO_VARIABLE_NTSuiteBackOffice,
30 OS_INFO_VARIABLE_NTSuiteDataCenter,
31 OS_INFO_VARIABLE_NTSuiteEnterprise,
32 OS_INFO_VARIABLE_NTSuitePersonal,
33 OS_INFO_VARIABLE_NTSuiteSmallBusiness,
34 OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted,
35 OS_INFO_VARIABLE_NTSuiteWebServer,
36 OS_INFO_VARIABLE_CompatibilityMode,
37 OS_INFO_VARIABLE_TerminalServer,
38 OS_INFO_VARIABLE_ProcessorArchitecture,
39 OS_INFO_VARIABLE_WindowsBuildNumber,
40};
41
42enum SET_VARIABLE
43{
44 SET_VARIABLE_NOT_BUILTIN,
45 SET_VARIABLE_OVERRIDE_BUILTIN,
46 SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS,
47 SET_VARIABLE_ANY,
48};
49
50// internal function declarations
51
52static HRESULT FormatString(
53 __in BURN_VARIABLES* pVariables,
54 __in_z LPCWSTR wzIn,
55 __out_z_opt LPWSTR* psczOut,
56 __out_opt SIZE_T* pcchOut,
57 __in BOOL fObfuscateHiddenVariables,
58 __out BOOL* pfContainsHiddenVariable
59 );
60static HRESULT GetFormatted(
61 __in BURN_VARIABLES* pVariables,
62 __in_z LPCWSTR wzVariable,
63 __out_z LPWSTR* psczValue,
64 __out BOOL* pfContainsHiddenVariable
65 );
66static HRESULT AddBuiltInVariable(
67 __in BURN_VARIABLES* pVariables,
68 __in LPCWSTR wzVariable,
69 __in PFN_INITIALIZEVARIABLE pfnInitialize,
70 __in DWORD_PTR dwpInitializeData,
71 __in BOOL fPersist,
72 __in BOOL fOverridable
73 );
74static HRESULT GetVariable(
75 __in BURN_VARIABLES* pVariables,
76 __in_z LPCWSTR wzVariable,
77 __out BURN_VARIABLE** ppVariable
78 );
79static HRESULT FindVariableIndexByName(
80 __in BURN_VARIABLES* pVariables,
81 __in_z LPCWSTR wzVariable,
82 __out DWORD* piVariable
83 );
84static HRESULT InsertVariable(
85 __in BURN_VARIABLES* pVariables,
86 __in_z LPCWSTR wzVariable,
87 __in DWORD iPosition
88 );
89static HRESULT SetVariableValue(
90 __in BURN_VARIABLES* pVariables,
91 __in_z LPCWSTR wzVariable,
92 __in BURN_VARIANT* pVariant,
93 __in SET_VARIABLE setBuiltin,
94 __in BOOL fLog
95 );
96static HRESULT InitializeVariableVersionNT(
97 __in DWORD_PTR dwpData,
98 __inout BURN_VARIANT* pValue
99 );
100static HRESULT InitializeVariableOsInfo(
101 __in DWORD_PTR dwpData,
102 __inout BURN_VARIANT* pValue
103 );
104static HRESULT InitializeVariableSystemInfo(
105 __in DWORD_PTR dwpData,
106 __inout BURN_VARIANT* pValue
107 );
108static HRESULT InitializeVariableComputerName(
109 __in DWORD_PTR dwpData,
110 __inout BURN_VARIANT* pValue
111 );
112static HRESULT InitializeVariableVersionMsi(
113 __in DWORD_PTR dwpData,
114 __inout BURN_VARIANT* pValue
115 );
116static HRESULT InitializeVariableCsidlFolder(
117 __in DWORD_PTR dwpData,
118 __inout BURN_VARIANT* pValue
119 );
120static HRESULT InitializeVariableWindowsVolumeFolder(
121 __in DWORD_PTR dwpData,
122 __inout BURN_VARIANT* pValue
123 );
124static HRESULT InitializeVariableTempFolder(
125 __in DWORD_PTR dwpData,
126 __inout BURN_VARIANT* pValue
127 );
128static HRESULT InitializeVariableSystemFolder(
129 __in DWORD_PTR dwpData,
130 __inout BURN_VARIANT* pValue
131 );
132static HRESULT InitializeVariablePrivileged(
133 __in DWORD_PTR dwpData,
134 __inout BURN_VARIANT* pValue
135 );
136static HRESULT InitializeSystemLanguageID(
137 __in DWORD_PTR dwpData,
138 __inout BURN_VARIANT* pValue
139 );
140static HRESULT InitializeUserUILanguageID(
141 __in DWORD_PTR dwpData,
142 __inout BURN_VARIANT* pValue
143 );
144static HRESULT InitializeUserLanguageID(
145 __in DWORD_PTR dwpData,
146 __inout BURN_VARIANT* pValue
147 );
148static HRESULT InitializeVariableString(
149 __in DWORD_PTR dwpData,
150 __inout BURN_VARIANT* pValue
151 );
152static HRESULT InitializeVariableNumeric(
153 __in DWORD_PTR dwpData,
154 __inout BURN_VARIANT* pValue
155 );
156static HRESULT InitializeVariable6432Folder(
157 __in DWORD_PTR dwpData,
158 __inout BURN_VARIANT* pValue
159 );
160static HRESULT InitializeVariableDate(
161 __in DWORD_PTR dwpData,
162 __inout BURN_VARIANT* pValue
163 );
164static HRESULT InitializeVariableInstallerName(
165 __in DWORD_PTR dwpData,
166 __inout BURN_VARIANT* pValue
167 );
168static HRESULT InitializeVariableInstallerVersion(
169 __in DWORD_PTR dwpData,
170 __inout BURN_VARIANT* pValue
171 );
172static HRESULT InitializeVariableVersion(
173 __in DWORD_PTR dwpData,
174 __inout BURN_VARIANT* pValue
175 );
176static HRESULT InitializeVariableLogonUser(
177 __in DWORD_PTR dwpData,
178 __inout BURN_VARIANT* pValue
179 );
180static HRESULT Get64bitFolderFromRegistry(
181 __in int nFolder,
182 __deref_out_z LPWSTR* psczPath
183 );
184
185#if !defined(_WIN64)
186static HRESULT InitializeVariableRegistryFolder(
187 __in DWORD_PTR dwpData,
188 __inout BURN_VARIANT* pValue
189 );
190#endif
191
192
193// function definitions
194
195extern "C" HRESULT VariableInitialize(
196 __in BURN_VARIABLES* pVariables
197 )
198{
199 HRESULT hr = S_OK;
200
201 ::InitializeCriticalSection(&pVariables->csAccess);
202
203 const BUILT_IN_VARIABLE_DECLARATION vrgBuiltInVariables[] = {
204 {L"AdminToolsFolder", InitializeVariableCsidlFolder, CSIDL_ADMINTOOLS},
205 {L"AppDataFolder", InitializeVariableCsidlFolder, CSIDL_APPDATA},
206 {L"CommonAppDataFolder", InitializeVariableCsidlFolder, CSIDL_COMMON_APPDATA},
207#if defined(_WIN64)
208 {L"CommonFiles64Folder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMON},
209 {L"CommonFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMONX86},
210#else
211 {L"CommonFiles64Folder", InitializeVariableRegistryFolder, CSIDL_PROGRAM_FILES_COMMON},
212 {L"CommonFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMON},
213#endif
214 {L"CommonFiles6432Folder", InitializeVariable6432Folder, CSIDL_PROGRAM_FILES_COMMON},
215 {L"CompatibilityMode", InitializeVariableOsInfo, OS_INFO_VARIABLE_CompatibilityMode},
216 {VARIABLE_DATE, InitializeVariableDate, 0},
217 {L"ComputerName", InitializeVariableComputerName, 0},
218 {L"DesktopFolder", InitializeVariableCsidlFolder, CSIDL_DESKTOP},
219 {L"FavoritesFolder", InitializeVariableCsidlFolder, CSIDL_FAVORITES},
220 {L"FontsFolder", InitializeVariableCsidlFolder, CSIDL_FONTS},
221 {VARIABLE_INSTALLERNAME, InitializeVariableInstallerName, 0},
222 {VARIABLE_INSTALLERVERSION, InitializeVariableInstallerVersion, 0},
223 {L"LocalAppDataFolder", InitializeVariableCsidlFolder, CSIDL_LOCAL_APPDATA},
224 {VARIABLE_LOGONUSER, InitializeVariableLogonUser, 0},
225 {L"MyPicturesFolder", InitializeVariableCsidlFolder, CSIDL_MYPICTURES},
226 {L"NTProductType", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTProductType},
227 {L"NTSuiteBackOffice", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteBackOffice},
228 {L"NTSuiteDataCenter", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteDataCenter},
229 {L"NTSuiteEnterprise", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteEnterprise},
230 {L"NTSuitePersonal", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuitePersonal},
231 {L"NTSuiteSmallBusiness", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteSmallBusiness},
232 {L"NTSuiteSmallBusinessRestricted", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted},
233 {L"NTSuiteWebServer", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteWebServer},
234 {L"PersonalFolder", InitializeVariableCsidlFolder, CSIDL_PERSONAL},
235 {L"Privileged", InitializeVariablePrivileged, 0},
236 {L"ProcessorArchitecture", InitializeVariableSystemInfo, OS_INFO_VARIABLE_ProcessorArchitecture},
237#if defined(_WIN64)
238 {L"ProgramFiles64Folder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES},
239 {L"ProgramFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILESX86},
240#else
241 {L"ProgramFiles64Folder", InitializeVariableRegistryFolder, CSIDL_PROGRAM_FILES},
242 {L"ProgramFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES},
243#endif
244 {L"ProgramFiles6432Folder", InitializeVariable6432Folder, CSIDL_PROGRAM_FILES},
245 {L"ProgramMenuFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAMS},
246 {L"SendToFolder", InitializeVariableCsidlFolder, CSIDL_SENDTO},
247 {L"ServicePackLevel", InitializeVariableVersionNT, OS_INFO_VARIABLE_ServicePackLevel},
248 {L"StartMenuFolder", InitializeVariableCsidlFolder, CSIDL_STARTMENU},
249 {L"StartupFolder", InitializeVariableCsidlFolder, CSIDL_STARTUP},
250 {L"SystemFolder", InitializeVariableSystemFolder, FALSE},
251 {L"System64Folder", InitializeVariableSystemFolder, TRUE},
252 {L"SystemLanguageID", InitializeSystemLanguageID, 0},
253 {L"TempFolder", InitializeVariableTempFolder, 0},
254 {L"TemplateFolder", InitializeVariableCsidlFolder, CSIDL_TEMPLATES},
255 {L"TerminalServer", InitializeVariableOsInfo, OS_INFO_VARIABLE_TerminalServer},
256 {L"UserUILanguageID", InitializeUserUILanguageID, 0},
257 {L"UserLanguageID", InitializeUserLanguageID, 0},
258 {L"VersionMsi", InitializeVariableVersionMsi, 0},
259 {L"VersionNT", InitializeVariableVersionNT, OS_INFO_VARIABLE_VersionNT},
260 {L"VersionNT64", InitializeVariableVersionNT, OS_INFO_VARIABLE_VersionNT64},
261 {L"WindowsBuildNumber", InitializeVariableVersionNT, OS_INFO_VARIABLE_WindowsBuildNumber},
262 {L"WindowsFolder", InitializeVariableCsidlFolder, CSIDL_WINDOWS},
263 {L"WindowsVolume", InitializeVariableWindowsVolumeFolder, 0},
264 {BURN_BUNDLE_ACTION, InitializeVariableNumeric, 0, FALSE, TRUE},
265 {BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, InitializeVariableString, NULL, FALSE, TRUE},
266 {BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, InitializeVariableString, NULL, FALSE, TRUE},
267 {BURN_BUNDLE_FORCED_RESTART_PACKAGE, InitializeVariableString, NULL, TRUE, TRUE},
268 {BURN_BUNDLE_INSTALLED, InitializeVariableNumeric, 0, FALSE, TRUE},
269 {BURN_BUNDLE_ELEVATED, InitializeVariableNumeric, 0, FALSE, TRUE},
270 {BURN_BUNDLE_ACTIVE_PARENT, InitializeVariableString, NULL, FALSE, TRUE},
271 {BURN_BUNDLE_PROVIDER_KEY, InitializeVariableString, (DWORD_PTR)L"", FALSE, TRUE},
272 {BURN_BUNDLE_SOURCE_PROCESS_PATH, InitializeVariableString, NULL, FALSE, TRUE},
273 {BURN_BUNDLE_SOURCE_PROCESS_FOLDER, InitializeVariableString, NULL, FALSE, TRUE},
274 {BURN_BUNDLE_TAG, InitializeVariableString, (DWORD_PTR)L"", FALSE, TRUE},
275 {BURN_BUNDLE_UILEVEL, InitializeVariableNumeric, 0, FALSE, TRUE},
276 {BURN_BUNDLE_VERSION, InitializeVariableVersion, (DWORD_PTR)L"0", FALSE, TRUE},
277 };
278
279 for (DWORD i = 0; i < countof(vrgBuiltInVariables); ++i)
280 {
281 BUILT_IN_VARIABLE_DECLARATION* pBuiltInVariable = &vrgBuiltInVariables[i];
282
283 hr = AddBuiltInVariable(pVariables, pBuiltInVariable->wzVariable, pBuiltInVariable->pfnInitialize, pBuiltInVariable->dwpInitializeData, pBuiltInVariable->fPersist, pBuiltInVariable->fOverridable);
284 ExitOnFailure(hr, "Failed to add built-in variable: %ls.", pBuiltInVariable->wzVariable);
285 }
286
287LExit:
288 return hr;
289}
290
291extern "C" HRESULT VariablesParseFromXml(
292 __in BURN_VARIABLES* pVariables,
293 __in IXMLDOMNode* pixnBundle
294 )
295{
296 HRESULT hr = S_OK;
297 IXMLDOMNodeList* pixnNodes = NULL;
298 IXMLDOMNode* pixnNode = NULL;
299 DWORD cNodes = 0;
300 LPWSTR sczId = NULL;
301 LPWSTR scz = NULL;
302 BURN_VARIANT value = { };
303 BURN_VARIANT_TYPE valueType = BURN_VARIANT_TYPE_NONE;
304 BOOL fHidden = FALSE;
305 BOOL fPersisted = FALSE;
306 DWORD iVariable = 0;
307
308 ::EnterCriticalSection(&pVariables->csAccess);
309
310 // select variable nodes
311 hr = XmlSelectNodes(pixnBundle, L"Variable", &pixnNodes);
312 ExitOnFailure(hr, "Failed to select variable nodes.");
313
314 // get variable node count
315 hr = pixnNodes->get_length((long*)&cNodes);
316 ExitOnFailure(hr, "Failed to get variable node count.");
317
318 // parse variable elements
319 for (DWORD i = 0; i < cNodes; ++i)
320 {
321 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
322 ExitOnFailure(hr, "Failed to get next node.");
323
324 // @Id
325 hr = XmlGetAttributeEx(pixnNode, L"Id", &sczId);
326 ExitOnFailure(hr, "Failed to get @Id.");
327
328 // @Hidden
329 hr = XmlGetYesNoAttribute(pixnNode, L"Hidden", &fHidden);
330 ExitOnFailure(hr, "Failed to get @Hidden.");
331
332 // @Persisted
333 hr = XmlGetYesNoAttribute(pixnNode, L"Persisted", &fPersisted);
334 ExitOnFailure(hr, "Failed to get @Persisted.");
335
336 // @Value
337 hr = XmlGetAttributeEx(pixnNode, L"Value", &scz);
338 if (E_NOTFOUND != hr)
339 {
340 ExitOnFailure(hr, "Failed to get @Value.");
341
342 hr = BVariantSetString(&value, scz, 0, FALSE);
343 ExitOnFailure(hr, "Failed to set variant value.");
344
345 // @Type
346 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
347 ExitOnFailure(hr, "Failed to get @Type.");
348
349 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"formatted", -1))
350 {
351 if (!fHidden)
352 {
353 LogStringLine(REPORT_STANDARD, "Initializing formatted variable '%ls' to value '%ls'", sczId, value.sczValue);
354 }
355 valueType = BURN_VARIANT_TYPE_FORMATTED;
356 }
357 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1))
358 {
359 if (!fHidden)
360 {
361 LogStringLine(REPORT_STANDARD, "Initializing numeric variable '%ls' to value '%ls'", sczId, value.sczValue);
362 }
363 valueType = BURN_VARIANT_TYPE_NUMERIC;
364 }
365 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1))
366 {
367 if (!fHidden)
368 {
369 LogStringLine(REPORT_STANDARD, "Initializing string variable '%ls' to value '%ls'", sczId, value.sczValue);
370 }
371 valueType = BURN_VARIANT_TYPE_STRING;
372 }
373 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1))
374 {
375 if (!fHidden)
376 {
377 LogStringLine(REPORT_STANDARD, "Initializing version variable '%ls' to value '%ls'", sczId, value.sczValue);
378 }
379 valueType = BURN_VARIANT_TYPE_VERSION;
380 }
381 else
382 {
383 hr = E_INVALIDARG;
384 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
385 }
386 }
387 else
388 {
389 valueType = BURN_VARIANT_TYPE_NONE;
390 }
391
392 if (fHidden)
393 {
394 LogStringLine(REPORT_STANDARD, "Initializing hidden variable '%ls'", sczId);
395 }
396
397 // change value variant to correct type
398 hr = BVariantChangeType(&value, valueType);
399 ExitOnFailure(hr, "Failed to change variant type.");
400
401 if (BURN_VARIANT_TYPE_VERSION == valueType && value.pValue->fInvalid)
402 {
403 LogId(REPORT_WARNING, MSG_VARIABLE_INVALID_VERSION, sczId);
404 }
405
406 // find existing variable
407 hr = FindVariableIndexByName(pVariables, sczId, &iVariable);
408 ExitOnFailure(hr, "Failed to find variable value '%ls'.", sczId);
409
410 // insert element if not found
411 if (S_FALSE == hr)
412 {
413 hr = InsertVariable(pVariables, sczId, iVariable);
414 ExitOnFailure(hr, "Failed to insert variable '%ls'.", sczId);
415 }
416 else if (BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariables->rgVariables[iVariable].internalType)
417 {
418 hr = E_INVALIDARG;
419 ExitOnRootFailure(hr, "Attempt to set built-in variable value: %ls", sczId);
420 }
421 pVariables->rgVariables[iVariable].fHidden = fHidden;
422 pVariables->rgVariables[iVariable].fPersisted = fPersisted;
423
424 // update variable value
425 hr = BVariantSetValue(&pVariables->rgVariables[iVariable].Value, &value);
426 ExitOnFailure(hr, "Failed to set value of variable: %ls", sczId);
427
428 // prepare next iteration
429 ReleaseNullObject(pixnNode);
430 BVariantUninitialize(&value);
431 ReleaseNullStrSecure(scz);
432 }
433
434LExit:
435 ::LeaveCriticalSection(&pVariables->csAccess);
436
437 ReleaseObject(pixnNodes);
438 ReleaseObject(pixnNode);
439 ReleaseStr(scz);
440 ReleaseStr(sczId);
441 BVariantUninitialize(&value);
442
443 return hr;
444}
445
446extern "C" void VariablesUninitialize(
447 __in BURN_VARIABLES* pVariables
448 )
449{
450 ::DeleteCriticalSection(&pVariables->csAccess);
451
452 if (pVariables->rgVariables)
453 {
454 for (DWORD i = 0; i < pVariables->cVariables; ++i)
455 {
456 BURN_VARIABLE* pVariable = &pVariables->rgVariables[i];
457 if (pVariable)
458 {
459 ReleaseStr(pVariable->sczName);
460 BVariantUninitialize(&pVariable->Value);
461 }
462 }
463 MemFree(pVariables->rgVariables);
464 }
465}
466
467extern "C" void VariablesDump(
468 __in BURN_VARIABLES* pVariables
469 )
470{
471 HRESULT hr = S_OK;
472 LPWSTR sczValue = NULL;
473
474 for (DWORD i = 0; i < pVariables->cVariables; ++i)
475 {
476 BURN_VARIABLE* pVariable = &pVariables->rgVariables[i];
477 if (pVariable && BURN_VARIANT_TYPE_NONE != pVariable->Value.Type)
478 {
479 hr = StrAllocFormatted(&sczValue, L"%ls = [%ls]", pVariable->sczName, pVariable->sczName);
480 if (SUCCEEDED(hr))
481 {
482 if (pVariable->fHidden)
483 {
484 hr = VariableFormatStringObfuscated(pVariables, sczValue, &sczValue, NULL);
485 }
486 else
487 {
488 hr = VariableFormatString(pVariables, sczValue, &sczValue, NULL);
489 }
490 }
491
492 if (FAILED(hr))
493 {
494 // already logged; best-effort to dump the rest on our way out the door
495 continue;
496 }
497
498 LogId(REPORT_VERBOSE, MSG_VARIABLE_DUMP, sczValue);
499
500 ReleaseNullStrSecure(sczValue);
501 }
502 }
503
504 StrSecureZeroFreeString(sczValue);
505}
506
507extern "C" HRESULT VariableGetNumeric(
508 __in BURN_VARIABLES* pVariables,
509 __in_z LPCWSTR wzVariable,
510 __out LONGLONG* pllValue
511 )
512{
513 HRESULT hr = S_OK;
514 BURN_VARIABLE* pVariable = NULL;
515
516 ::EnterCriticalSection(&pVariables->csAccess);
517
518 hr = GetVariable(pVariables, wzVariable, &pVariable);
519 if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type)
520 {
521 ExitFunction1(hr = E_NOTFOUND);
522 }
523 else if (E_NOTFOUND == hr)
524 {
525 ExitFunction();
526 }
527 ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable);
528
529 hr = BVariantGetNumeric(&pVariable->Value, pllValue);
530 ExitOnFailure(hr, "Failed to get value as numeric for variable: %ls", wzVariable);
531
532LExit:
533 ::LeaveCriticalSection(&pVariables->csAccess);
534
535 return hr;
536}
537
538extern "C" HRESULT VariableGetString(
539 __in BURN_VARIABLES* pVariables,
540 __in_z LPCWSTR wzVariable,
541 __out_z LPWSTR* psczValue
542 )
543{
544 HRESULT hr = S_OK;
545 BURN_VARIABLE* pVariable = NULL;
546
547 ::EnterCriticalSection(&pVariables->csAccess);
548
549 hr = GetVariable(pVariables, wzVariable, &pVariable);
550 if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type)
551 {
552 ExitFunction1(hr = E_NOTFOUND);
553 }
554 else if (E_NOTFOUND == hr)
555 {
556 ExitFunction();
557 }
558 ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable);
559
560 hr = BVariantGetString(&pVariable->Value, psczValue);
561 ExitOnFailure(hr, "Failed to get value as string for variable: %ls", wzVariable);
562
563LExit:
564 ::LeaveCriticalSection(&pVariables->csAccess);
565
566 return hr;
567}
568
569extern "C" HRESULT VariableGetVersion(
570 __in BURN_VARIABLES* pVariables,
571 __in_z LPCWSTR wzVariable,
572 __in VERUTIL_VERSION** ppValue
573 )
574{
575 HRESULT hr = S_OK;
576 BURN_VARIABLE* pVariable = NULL;
577
578 ::EnterCriticalSection(&pVariables->csAccess);
579
580 hr = GetVariable(pVariables, wzVariable, &pVariable);
581 if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type)
582 {
583 ExitFunction1(hr = E_NOTFOUND);
584 }
585 else if (E_NOTFOUND == hr)
586 {
587 ExitFunction();
588 }
589 ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable);
590
591 hr = BVariantGetVersionHidden(&pVariable->Value, pVariable->fHidden, ppValue);
592 ExitOnFailure(hr, "Failed to get value as version for variable: %ls", wzVariable);
593
594LExit:
595 ::LeaveCriticalSection(&pVariables->csAccess);
596
597 return hr;
598}
599
600extern "C" HRESULT VariableGetVariant(
601 __in BURN_VARIABLES* pVariables,
602 __in_z LPCWSTR wzVariable,
603 __in BURN_VARIANT* pValue
604 )
605{
606 HRESULT hr = S_OK;
607 BURN_VARIABLE* pVariable = NULL;
608
609 ::EnterCriticalSection(&pVariables->csAccess);
610
611 hr = GetVariable(pVariables, wzVariable, &pVariable);
612 if (E_NOTFOUND == hr)
613 {
614 ExitFunction();
615 }
616 ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable);
617
618 hr = BVariantCopy(&pVariable->Value, pValue);
619 ExitOnFailure(hr, "Failed to copy value of variable: %ls", wzVariable);
620
621LExit:
622 ::LeaveCriticalSection(&pVariables->csAccess);
623
624 return hr;
625}
626
627extern "C" HRESULT VariableGetFormatted(
628 __in BURN_VARIABLES* pVariables,
629 __in_z LPCWSTR wzVariable,
630 __out_z LPWSTR* psczValue,
631 __out BOOL* pfContainsHiddenVariable
632 )
633{
634 HRESULT hr = S_OK;
635
636 if (pfContainsHiddenVariable)
637 {
638 *pfContainsHiddenVariable = FALSE;
639 }
640
641 hr = GetFormatted(pVariables, wzVariable, psczValue, pfContainsHiddenVariable);
642
643 return hr;
644}
645
646extern "C" HRESULT VariableSetNumeric(
647 __in BURN_VARIABLES* pVariables,
648 __in_z LPCWSTR wzVariable,
649 __in LONGLONG llValue,
650 __in BOOL fOverwriteBuiltIn
651 )
652{
653 BURN_VARIANT variant = { };
654
655 variant.llValue = llValue;
656 variant.Type = BURN_VARIANT_TYPE_NUMERIC;
657
658 return SetVariableValue(pVariables, wzVariable, &variant, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE);
659}
660
661extern "C" HRESULT VariableSetString(
662 __in BURN_VARIABLES* pVariables,
663 __in_z LPCWSTR wzVariable,
664 __in_z_opt LPCWSTR wzValue,
665 __in BOOL fOverwriteBuiltIn,
666 __in BOOL fFormatted
667 )
668{
669 BURN_VARIANT variant = { };
670
671 variant.sczValue = (LPWSTR)wzValue;
672 variant.Type = fFormatted ? BURN_VARIANT_TYPE_FORMATTED : BURN_VARIANT_TYPE_STRING;
673
674 return SetVariableValue(pVariables, wzVariable, &variant, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE);
675}
676
677extern "C" HRESULT VariableSetVersion(
678 __in BURN_VARIABLES* pVariables,
679 __in_z LPCWSTR wzVariable,
680 __in VERUTIL_VERSION* pValue,
681 __in BOOL fOverwriteBuiltIn
682 )
683{
684 BURN_VARIANT variant = { };
685
686 variant.pValue = pValue;
687 variant.Type = BURN_VARIANT_TYPE_VERSION;
688
689 return SetVariableValue(pVariables, wzVariable, &variant, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE);
690}
691
692extern "C" HRESULT VariableSetVariant(
693 __in BURN_VARIABLES * pVariables,
694 __in_z LPCWSTR wzVariable,
695 __in BURN_VARIANT * pVariant
696 )
697{
698 return SetVariableValue(pVariables, wzVariable, pVariant, SET_VARIABLE_NOT_BUILTIN, TRUE);
699}
700
701extern "C" HRESULT VariableFormatString(
702 __in BURN_VARIABLES* pVariables,
703 __in_z LPCWSTR wzIn,
704 __out_z_opt LPWSTR* psczOut,
705 __out_opt SIZE_T* pcchOut
706 )
707{
708 return FormatString(pVariables, wzIn, psczOut, pcchOut, FALSE, NULL);
709}
710
711extern "C" HRESULT VariableFormatStringObfuscated(
712 __in BURN_VARIABLES* pVariables,
713 __in_z LPCWSTR wzIn,
714 __out_z_opt LPWSTR* psczOut,
715 __out_opt SIZE_T* pcchOut
716 )
717{
718 return FormatString(pVariables, wzIn, psczOut, pcchOut, TRUE, NULL);
719}
720
721extern "C" HRESULT VariableEscapeString(
722 __in_z LPCWSTR wzIn,
723 __out_z LPWSTR* psczOut
724 )
725{
726 HRESULT hr = S_OK;
727 LPCWSTR wzRead = NULL;
728 LPWSTR pwzEscaped = NULL;
729 LPWSTR pwz = NULL;
730 SIZE_T i = 0;
731
732 // allocate buffer for escaped string
733 hr = StrAlloc(&pwzEscaped, lstrlenW(wzIn) + 1);
734 ExitOnFailure(hr, "Failed to allocate buffer for escaped string.");
735
736 // read through string and move characters, inserting escapes as needed
737 wzRead = wzIn;
738 for (;;)
739 {
740 // find next character needing escaping
741 i = wcscspn(wzRead, L"[]{}");
742
743 // copy skipped characters
744 if (0 < i)
745 {
746 hr = StrAllocConcat(&pwzEscaped, wzRead, i);
747 ExitOnFailure(hr, "Failed to append characters.");
748 }
749
750 if (L'\0' == wzRead[i])
751 {
752 break; // end reached
753 }
754
755 // escape character
756 hr = StrAllocFormatted(&pwz, L"[\\%c]", wzRead[i]);
757 ExitOnFailure(hr, "Failed to format escape sequence.");
758
759 hr = StrAllocConcat(&pwzEscaped, pwz, 0);
760 ExitOnFailure(hr, "Failed to append escape sequence.");
761
762 // update read pointer
763 wzRead += i + 1;
764 }
765
766 // return value
767 hr = StrAllocString(psczOut, pwzEscaped, 0);
768 ExitOnFailure(hr, "Failed to copy string.");
769
770LExit:
771 ReleaseStr(pwzEscaped);
772 ReleaseStr(pwz);
773 return hr;
774}
775
776extern "C" HRESULT VariableSerialize(
777 __in BURN_VARIABLES* pVariables,
778 __in BOOL fPersisting,
779 __inout BYTE** ppbBuffer,
780 __inout SIZE_T* piBuffer
781 )
782{
783 HRESULT hr = S_OK;
784 BOOL fIncluded = FALSE;
785 LONGLONG ll = 0;
786 LPWSTR scz = NULL;
787
788 ::EnterCriticalSection(&pVariables->csAccess);
789
790 // Write variable count.
791 hr = BuffWriteNumber(ppbBuffer, piBuffer, pVariables->cVariables);
792 ExitOnFailure(hr, "Failed to write variable count.");
793
794 // Write variables.
795 for (DWORD i = 0; i < pVariables->cVariables; ++i)
796 {
797 BURN_VARIABLE* pVariable = &pVariables->rgVariables[i];
798
799 // If we aren't persisting, include only variables that aren't rejected by the elevated process.
800 // If we are persisting, include only variables that should be persisted.
801 fIncluded = (!fPersisting && BURN_VARIABLE_INTERNAL_TYPE_BUILTIN != pVariable->internalType) ||
802 (fPersisting && pVariable->fPersisted);
803
804 // Write included flag.
805 hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)fIncluded);
806 ExitOnFailure(hr, "Failed to write included flag.");
807
808 if (!fIncluded)
809 {
810 continue;
811 }
812
813 // Write variable name.
814 hr = BuffWriteString(ppbBuffer, piBuffer, pVariable->sczName);
815 ExitOnFailure(hr, "Failed to write variable name.");
816
817 // Write variable value type.
818 hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)pVariable->Value.Type);
819 ExitOnFailure(hr, "Failed to write variable value type.");
820
821 // Write variable value.
822 switch (pVariable->Value.Type)
823 {
824 case BURN_VARIANT_TYPE_NONE:
825 break;
826 case BURN_VARIANT_TYPE_NUMERIC:
827 hr = BVariantGetNumeric(&pVariable->Value, &ll);
828 ExitOnFailure(hr, "Failed to get numeric.");
829
830 hr = BuffWriteNumber64(ppbBuffer, piBuffer, static_cast<DWORD64>(ll));
831 ExitOnFailure(hr, "Failed to write variable value as number.");
832
833 SecureZeroMemory(&ll, sizeof(ll));
834 break;
835 case BURN_VARIANT_TYPE_VERSION: __fallthrough;
836 case BURN_VARIANT_TYPE_FORMATTED: __fallthrough;
837 case BURN_VARIANT_TYPE_STRING:
838 hr = BVariantGetString(&pVariable->Value, &scz);
839 ExitOnFailure(hr, "Failed to get string.");
840
841 hr = BuffWriteString(ppbBuffer, piBuffer, scz);
842 ExitOnFailure(hr, "Failed to write variable value as string.");
843
844 ReleaseNullStrSecure(scz);
845 break;
846 default:
847 hr = E_INVALIDARG;
848 ExitOnFailure(hr, "Unsupported variable type.");
849 }
850 }
851
852LExit:
853 ::LeaveCriticalSection(&pVariables->csAccess);
854 SecureZeroMemory(&ll, sizeof(ll));
855 StrSecureZeroFreeString(scz);
856
857 return hr;
858}
859
860extern "C" HRESULT VariableDeserialize(
861 __in BURN_VARIABLES* pVariables,
862 __in BOOL fWasPersisted,
863 __in_bcount(cbBuffer) BYTE* pbBuffer,
864 __in SIZE_T cbBuffer,
865 __inout SIZE_T* piBuffer
866 )
867{
868 HRESULT hr = S_OK;
869 DWORD cVariables = 0;
870 LPWSTR sczName = NULL;
871 BOOL fIncluded = FALSE;
872 BURN_VARIANT value = { };
873 LPWSTR scz = NULL;
874 DWORD64 qw = 0;
875 VERUTIL_VERSION* pVersion = NULL;
876
877 ::EnterCriticalSection(&pVariables->csAccess);
878
879 // Read variable count.
880 hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, &cVariables);
881 ExitOnFailure(hr, "Failed to read variable count.");
882
883 // Read variables.
884 for (DWORD i = 0; i < cVariables; ++i)
885 {
886 // Read variable included flag.
887 hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&fIncluded);
888 ExitOnFailure(hr, "Failed to read variable included flag.");
889
890 if (!fIncluded)
891 {
892 continue; // if variable is not included, skip.
893 }
894
895 // Read variable name.
896 hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &sczName);
897 ExitOnFailure(hr, "Failed to read variable name.");
898
899 // Read variable value type.
900 hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&value.Type);
901 ExitOnFailure(hr, "Failed to read variable value type.");
902
903 // Read variable value.
904 switch (value.Type)
905 {
906 case BURN_VARIANT_TYPE_NONE:
907 break;
908 case BURN_VARIANT_TYPE_NUMERIC:
909 hr = BuffReadNumber64(pbBuffer, cbBuffer, piBuffer, &qw);
910 ExitOnFailure(hr, "Failed to read variable value as number.");
911
912 hr = BVariantSetNumeric(&value, static_cast<LONGLONG>(qw));
913 ExitOnFailure(hr, "Failed to set variable value.");
914
915 SecureZeroMemory(&qw, sizeof(qw));
916 break;
917 case BURN_VARIANT_TYPE_VERSION:
918 hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &scz);
919 ExitOnFailure(hr, "Failed to read variable value as string.");
920
921 hr = VerParseVersion(scz, 0, FALSE, &pVersion);
922 ExitOnFailure(hr, "Failed to parse variable value as version.");
923
924 hr = BVariantSetVersion(&value, pVersion);
925 ExitOnFailure(hr, "Failed to set variable value.");
926
927 SecureZeroMemory(&qw, sizeof(qw));
928 break;
929 case BURN_VARIANT_TYPE_FORMATTED: __fallthrough;
930 case BURN_VARIANT_TYPE_STRING:
931 hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &scz);
932 ExitOnFailure(hr, "Failed to read variable value as string.");
933
934 hr = BVariantSetString(&value, scz, NULL, BURN_VARIANT_TYPE_FORMATTED == value.Type);
935 ExitOnFailure(hr, "Failed to set variable value.");
936
937 ReleaseNullStrSecure(scz);
938 break;
939 default:
940 hr = E_INVALIDARG;
941 ExitOnFailure(hr, "Unsupported variable type.");
942 }
943
944 // Set variable.
945 hr = SetVariableValue(pVariables, sczName, &value, fWasPersisted ? SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS : SET_VARIABLE_ANY, FALSE);
946 ExitOnFailure(hr, "Failed to set variable.");
947
948 // Clean up.
949 BVariantUninitialize(&value);
950 }
951
952LExit:
953 ::LeaveCriticalSection(&pVariables->csAccess);
954
955 ReleaseVerutilVersion(pVersion);
956 ReleaseStr(sczName);
957 BVariantUninitialize(&value);
958 SecureZeroMemory(&qw, sizeof(qw));
959 StrSecureZeroFreeString(scz);
960
961 return hr;
962}
963
964extern "C" HRESULT VariableStrAlloc(
965 __in BOOL fZeroOnRealloc,
966 __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
967 __in DWORD_PTR cch
968 )
969{
970 HRESULT hr = S_OK;
971
972 if (fZeroOnRealloc)
973 {
974 hr = StrAllocSecure(ppwz, cch);
975 }
976 else
977 {
978 hr = StrAlloc(ppwz, cch);
979 }
980
981 return hr;
982}
983
984extern "C" HRESULT VariableStrAllocString(
985 __in BOOL fZeroOnRealloc,
986 __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz,
987 __in_z LPCWSTR wzSource,
988 __in DWORD_PTR cchSource
989 )
990{
991 HRESULT hr = S_OK;
992
993 if (fZeroOnRealloc)
994 {
995 hr = StrAllocStringSecure(ppwz, wzSource, cchSource);
996 }
997 else
998 {
999 hr = StrAllocString(ppwz, wzSource, cchSource);
1000 }
1001
1002 return hr;
1003}
1004
1005extern "C" HRESULT VariableStrAllocConcat(
1006 __in BOOL fZeroOnRealloc,
1007 __deref_out_z LPWSTR* ppwz,
1008 __in_z LPCWSTR wzSource,
1009 __in DWORD_PTR cchSource
1010 )
1011{
1012 HRESULT hr = S_OK;
1013
1014 if (fZeroOnRealloc)
1015 {
1016 hr = StrAllocConcatSecure(ppwz, wzSource, cchSource);
1017 }
1018 else
1019 {
1020 hr = StrAllocConcat(ppwz, wzSource, cchSource);
1021 }
1022
1023 return hr;
1024}
1025
1026extern "C" HRESULT __cdecl VariableStrAllocFormatted(
1027 __in BOOL fZeroOnRealloc,
1028 __deref_out_z LPWSTR* ppwz,
1029 __in __format_string LPCWSTR wzFormat,
1030 ...
1031 )
1032{
1033 HRESULT hr = S_OK;
1034 va_list args;
1035
1036 va_start(args, wzFormat);
1037 if (fZeroOnRealloc)
1038 {
1039 hr = StrAllocFormattedArgsSecure(ppwz, wzFormat, args);
1040 }
1041 else
1042 {
1043 hr = StrAllocFormattedArgs(ppwz, wzFormat, args);
1044 }
1045 va_end(args);
1046
1047 return hr;
1048}
1049
1050extern "C" HRESULT VariableIsHidden(
1051 __in BURN_VARIABLES* pVariables,
1052 __in_z LPCWSTR wzVariable,
1053 __out BOOL* pfHidden
1054 )
1055{
1056 HRESULT hr = S_OK;
1057 BURN_VARIABLE* pVariable = NULL;
1058
1059 ::EnterCriticalSection(&pVariables->csAccess);
1060
1061 hr = GetVariable(pVariables, wzVariable, &pVariable);
1062 if (E_NOTFOUND == hr)
1063 {
1064 // A missing variable does not need its data hidden.
1065 *pfHidden = FALSE;
1066
1067 ExitFunction1(hr = S_OK);
1068 }
1069 ExitOnFailure(hr, "Failed to get visibility of variable: %ls", wzVariable);
1070
1071 *pfHidden = pVariable->fHidden;
1072
1073LExit:
1074 ::LeaveCriticalSection(&pVariables->csAccess);
1075
1076 return hr;
1077}
1078
1079
1080// internal function definitions
1081
1082static HRESULT FormatString(
1083 __in BURN_VARIABLES* pVariables,
1084 __in_z LPCWSTR wzIn,
1085 __out_z_opt LPWSTR* psczOut,
1086 __out_opt SIZE_T* pcchOut,
1087 __in BOOL fObfuscateHiddenVariables,
1088 __out BOOL* pfContainsHiddenVariable
1089 )
1090{
1091 HRESULT hr = S_OK;
1092 DWORD er = ERROR_SUCCESS;
1093 LPWSTR sczUnformatted = NULL;
1094 LPWSTR sczFormat = NULL;
1095 LPCWSTR wzRead = NULL;
1096 LPCWSTR wzOpen = NULL;
1097 LPCWSTR wzClose = NULL;
1098 LPWSTR scz = NULL;
1099 LPWSTR* rgVariables = NULL;
1100 DWORD cVariables = 0;
1101 DWORD cch = 0;
1102 size_t cchIn = 0;
1103 BOOL fHidden = FALSE;
1104 MSIHANDLE hRecord = NULL;
1105
1106 ::EnterCriticalSection(&pVariables->csAccess);
1107
1108 // allocate buffer for format string
1109 hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_LENGTH, &cchIn);
1110 ExitOnFailure(hr, "Failed to length of format string.");
1111
1112 hr = StrAlloc(&sczFormat, cchIn + 1);
1113 ExitOnFailure(hr, "Failed to allocate buffer for format string.");
1114
1115 // read out variables from the unformatted string and build a format string
1116 wzRead = wzIn;
1117 for (;;)
1118 {
1119 // scan for opening '['
1120 wzOpen = wcschr(wzRead, L'[');
1121 if (!wzOpen)
1122 {
1123 // end reached, append the remainder of the string and end loop
1124 hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, 0);
1125 ExitOnFailure(hr, "Failed to append string.");
1126 break;
1127 }
1128
1129 // scan for closing ']'
1130 wzClose = wcschr(wzOpen + 1, L']');
1131 if (!wzClose)
1132 {
1133 // end reached, treat unterminated expander as literal
1134 hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, 0);
1135 ExitOnFailure(hr, "Failed to append string.");
1136 break;
1137 }
1138 cch = (DWORD)(wzClose - wzOpen - 1);
1139
1140 if (0 == cch)
1141 {
1142 // blank, copy all text including the terminator
1143 hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, (DWORD_PTR)(wzClose - wzRead) + 1);
1144 ExitOnFailure(hr, "Failed to append string.");
1145 }
1146 else
1147 {
1148 // append text preceding expander
1149 if (wzOpen > wzRead)
1150 {
1151 hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, (DWORD_PTR)(wzOpen - wzRead));
1152 ExitOnFailure(hr, "Failed to append string.");
1153 }
1154
1155 // get variable name
1156 hr = VariableStrAllocString(!fObfuscateHiddenVariables, &scz, wzOpen + 1, cch);
1157 ExitOnFailure(hr, "Failed to get variable name.");
1158
1159 // allocate space in variable array
1160 if (rgVariables)
1161 {
1162 LPVOID pv = MemReAlloc(rgVariables, sizeof(LPWSTR) * (cVariables + 1), TRUE);
1163 ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate variable array.");
1164 rgVariables = (LPWSTR*)pv;
1165 }
1166 else
1167 {
1168 rgVariables = (LPWSTR*)MemAlloc(sizeof(LPWSTR) * (cVariables + 1), TRUE);
1169 ExitOnNull(rgVariables, hr, E_OUTOFMEMORY, "Failed to allocate variable array.");
1170 }
1171
1172 // set variable value
1173 if (2 <= cch && L'\\' == wzOpen[1])
1174 {
1175 // escape sequence, copy character
1176 hr = VariableStrAllocString(!fObfuscateHiddenVariables, &rgVariables[cVariables], &wzOpen[2], 1);
1177 }
1178 else
1179 {
1180 hr = VariableIsHidden(pVariables, scz, &fHidden);
1181 ExitOnFailure(hr, "Failed to determine variable visibility: '%ls'.", scz);
1182
1183 if (pfContainsHiddenVariable)
1184 {
1185 *pfContainsHiddenVariable |= fHidden;
1186 }
1187
1188 if (fObfuscateHiddenVariables && fHidden)
1189 {
1190 hr = StrAllocString(&rgVariables[cVariables], L"*****", 0);
1191 }
1192 else
1193 {
1194 // get formatted variable value
1195 hr = GetFormatted(pVariables, scz, &rgVariables[cVariables], pfContainsHiddenVariable);
1196 if (E_NOTFOUND == hr) // variable not found
1197 {
1198 hr = StrAllocStringSecure(&rgVariables[cVariables], L"", 0);
1199 }
1200 }
1201 }
1202 ExitOnFailure(hr, "Failed to set variable value.");
1203 ++cVariables;
1204
1205 // append placeholder to format string
1206 hr = VariableStrAllocFormatted(!fObfuscateHiddenVariables, &scz, L"[%d]", cVariables);
1207 ExitOnFailure(hr, "Failed to format placeholder string.");
1208
1209 hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, scz, 0);
1210 ExitOnFailure(hr, "Failed to append placeholder.");
1211 }
1212
1213 // update read pointer
1214 wzRead = wzClose + 1;
1215 }
1216
1217 // create record
1218 hRecord = ::MsiCreateRecord(cVariables);
1219 ExitOnNull(hRecord, hr, E_OUTOFMEMORY, "Failed to allocate record.");
1220
1221 // set format string
1222 er = ::MsiRecordSetStringW(hRecord, 0, sczFormat);
1223 ExitOnWin32Error(er, hr, "Failed to set record format string.");
1224
1225 // copy record fields
1226 for (DWORD i = 0; i < cVariables; ++i)
1227 {
1228 if (*rgVariables[i]) // not setting if blank
1229 {
1230 er = ::MsiRecordSetStringW(hRecord, i + 1, rgVariables[i]);
1231 ExitOnWin32Error(er, hr, "Failed to set record string.");
1232 }
1233 }
1234
1235 // get formatted character count
1236 cch = 0;
1237#pragma prefast(push)
1238#pragma prefast(disable:6298)
1239 er = ::MsiFormatRecordW(NULL, hRecord, L"", &cch);
1240#pragma prefast(pop)
1241 if (ERROR_MORE_DATA != er)
1242 {
1243 ExitOnWin32Error(er, hr, "Failed to get formatted length.");
1244 }
1245
1246 // return formatted string
1247 if (psczOut)
1248 {
1249 hr = VariableStrAlloc(!fObfuscateHiddenVariables, &scz, ++cch);
1250 ExitOnFailure(hr, "Failed to allocate string.");
1251
1252 er = ::MsiFormatRecordW(NULL, hRecord, scz, &cch);
1253 ExitOnWin32Error(er, hr, "Failed to format record.");
1254
1255 hr = VariableStrAllocString(!fObfuscateHiddenVariables, psczOut, scz, 0);
1256 ExitOnFailure(hr, "Failed to copy string.");
1257 }
1258
1259 // return character count
1260 if (pcchOut)
1261 {
1262 *pcchOut = cch;
1263 }
1264
1265LExit:
1266 ::LeaveCriticalSection(&pVariables->csAccess);
1267
1268 if (rgVariables)
1269 {
1270 for (DWORD i = 0; i < cVariables; ++i)
1271 {
1272 if (fObfuscateHiddenVariables)
1273 {
1274 ReleaseStr(rgVariables[i]);
1275 }
1276 else
1277 {
1278 StrSecureZeroFreeString(rgVariables[i]);
1279 }
1280 }
1281 MemFree(rgVariables);
1282 }
1283
1284 if (hRecord)
1285 {
1286 ::MsiCloseHandle(hRecord);
1287 }
1288
1289 if (fObfuscateHiddenVariables)
1290 {
1291 ReleaseStr(sczUnformatted);
1292 ReleaseStr(sczFormat);
1293 ReleaseStr(scz);
1294 }
1295 else
1296 {
1297 StrSecureZeroFreeString(sczUnformatted);
1298 StrSecureZeroFreeString(sczFormat);
1299 StrSecureZeroFreeString(scz);
1300 }
1301
1302 return hr;
1303}
1304
1305static HRESULT GetFormatted(
1306 __in BURN_VARIABLES* pVariables,
1307 __in_z LPCWSTR wzVariable,
1308 __out_z LPWSTR* psczValue,
1309 __out BOOL* pfContainsHiddenVariable
1310 )
1311{
1312 HRESULT hr = S_OK;
1313 BURN_VARIABLE* pVariable = NULL;
1314 LPWSTR scz = NULL;
1315
1316 ::EnterCriticalSection(&pVariables->csAccess);
1317
1318 hr = GetVariable(pVariables, wzVariable, &pVariable);
1319 if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type)
1320 {
1321 ExitFunction1(hr = E_NOTFOUND);
1322 }
1323 else if (E_NOTFOUND == hr)
1324 {
1325 ExitFunction();
1326 }
1327 ExitOnFailure(hr, "Failed to get variable: %ls", wzVariable);
1328
1329 if (pfContainsHiddenVariable)
1330 {
1331 *pfContainsHiddenVariable |= pVariable->fHidden;
1332 }
1333
1334 if (BURN_VARIANT_TYPE_FORMATTED == pVariable->Value.Type)
1335 {
1336 hr = BVariantGetString(&pVariable->Value, &scz);
1337 ExitOnFailure(hr, "Failed to get unformatted string.");
1338
1339 hr = FormatString(pVariables, scz, psczValue, NULL, FALSE, pfContainsHiddenVariable);
1340 ExitOnFailure(hr, "Failed to format value '%ls' of variable: %ls", pVariable->fHidden ? L"*****" : pVariable->Value.sczValue, wzVariable);
1341 }
1342 else
1343 {
1344 hr = BVariantGetString(&pVariable->Value, psczValue);
1345 ExitOnFailure(hr, "Failed to get value as string for variable: %ls", wzVariable);
1346 }
1347
1348LExit:
1349 ::LeaveCriticalSection(&pVariables->csAccess);
1350 StrSecureZeroFreeString(scz);
1351
1352 return hr;
1353}
1354
1355static HRESULT AddBuiltInVariable(
1356 __in BURN_VARIABLES* pVariables,
1357 __in LPCWSTR wzVariable,
1358 __in PFN_INITIALIZEVARIABLE pfnInitialize,
1359 __in DWORD_PTR dwpInitializeData,
1360 __in BOOL fPersist,
1361 __in BOOL fOverridable
1362 )
1363{
1364 HRESULT hr = S_OK;
1365 DWORD iVariable = 0;
1366 BURN_VARIABLE* pVariable = NULL;
1367
1368 hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable);
1369 ExitOnFailure(hr, "Failed to find variable value.");
1370
1371 // insert element if not found
1372 if (S_FALSE == hr)
1373 {
1374 hr = InsertVariable(pVariables, wzVariable, iVariable);
1375 ExitOnFailure(hr, "Failed to insert variable.");
1376 }
1377
1378 // set variable values
1379 pVariable = &pVariables->rgVariables[iVariable];
1380 pVariable->fPersisted = fPersist;
1381 pVariable->internalType = fOverridable ? BURN_VARIABLE_INTERNAL_TYPE_OVERRIDABLE_BUILTIN : BURN_VARIABLE_INTERNAL_TYPE_BUILTIN;
1382 pVariable->pfnInitialize = pfnInitialize;
1383 pVariable->dwpInitializeData = dwpInitializeData;
1384
1385LExit:
1386 return hr;
1387}
1388
1389static HRESULT GetVariable(
1390 __in BURN_VARIABLES* pVariables,
1391 __in_z LPCWSTR wzVariable,
1392 __out BURN_VARIABLE** ppVariable
1393 )
1394{
1395 HRESULT hr = S_OK;
1396 DWORD iVariable = 0;
1397 BURN_VARIABLE* pVariable = NULL;
1398
1399 hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable);
1400 ExitOnFailure(hr, "Failed to find variable value '%ls'.", wzVariable);
1401
1402 if (S_FALSE == hr)
1403 {
1404 ExitFunction1(hr = E_NOTFOUND);
1405 }
1406
1407 pVariable = &pVariables->rgVariables[iVariable];
1408
1409 // initialize built-in variable
1410 if (BURN_VARIANT_TYPE_NONE == pVariable->Value.Type && BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariable->internalType)
1411 {
1412 hr = pVariable->pfnInitialize(pVariable->dwpInitializeData, &pVariable->Value);
1413 ExitOnFailure(hr, "Failed to initialize built-in variable value '%ls'.", wzVariable);
1414 }
1415
1416 *ppVariable = pVariable;
1417
1418LExit:
1419 return hr;
1420}
1421
1422static HRESULT FindVariableIndexByName(
1423 __in BURN_VARIABLES* pVariables,
1424 __in_z LPCWSTR wzVariable,
1425 __out DWORD* piVariable
1426 )
1427{
1428 HRESULT hr = S_OK;
1429 DWORD iRangeFirst = 0;
1430 DWORD cRangeLength = pVariables->cVariables;
1431
1432 while (cRangeLength)
1433 {
1434 // get variable in middle of range
1435 DWORD iPosition = cRangeLength / 2;
1436 BURN_VARIABLE* pVariable = &pVariables->rgVariables[iRangeFirst + iPosition];
1437
1438 switch (::CompareStringW(LOCALE_INVARIANT, SORT_STRINGSORT, wzVariable, -1, pVariable->sczName, -1))
1439 {
1440 case CSTR_LESS_THAN:
1441 // restrict range to elements before the current
1442 cRangeLength = iPosition;
1443 break;
1444 case CSTR_EQUAL:
1445 // variable found
1446 *piVariable = iRangeFirst + iPosition;
1447 ExitFunction1(hr = S_OK);
1448 case CSTR_GREATER_THAN:
1449 // restrict range to elements after the current
1450 iRangeFirst += iPosition + 1;
1451 cRangeLength -= iPosition + 1;
1452 break;
1453 default:
1454 ExitWithLastError(hr, "Failed to compare strings.");
1455 }
1456 }
1457
1458 *piVariable = iRangeFirst;
1459 hr = S_FALSE; // variable not found
1460
1461LExit:
1462 return hr;
1463}
1464
1465static HRESULT InsertVariable(
1466 __in BURN_VARIABLES* pVariables,
1467 __in_z LPCWSTR wzVariable,
1468 __in DWORD iPosition
1469 )
1470{
1471 HRESULT hr = S_OK;
1472 size_t cbAllocSize = 0;
1473
1474 // ensure there is room in the variable array
1475 if (pVariables->cVariables == pVariables->dwMaxVariables)
1476 {
1477 hr = ::DWordAdd(pVariables->dwMaxVariables, GROW_VARIABLE_ARRAY, &(pVariables->dwMaxVariables));
1478 ExitOnRootFailure(hr, "Overflow while growing variable array size");
1479
1480 if (pVariables->rgVariables)
1481 {
1482 hr = ::SizeTMult(sizeof(BURN_VARIABLE), pVariables->dwMaxVariables, &cbAllocSize);
1483 ExitOnRootFailure(hr, "Overflow while calculating size of variable array buffer");
1484
1485 LPVOID pv = MemReAlloc(pVariables->rgVariables, cbAllocSize, FALSE);
1486 ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate room for more variables.");
1487
1488 // Prefast claims it's possible to hit this. Putting the check in just in case.
1489 if (pVariables->dwMaxVariables < pVariables->cVariables)
1490 {
1491 hr = INTSAFE_E_ARITHMETIC_OVERFLOW;
1492 ExitOnRootFailure(hr, "Overflow while dealing with variable array buffer allocation");
1493 }
1494
1495 pVariables->rgVariables = (BURN_VARIABLE*)pv;
1496 memset(&pVariables->rgVariables[pVariables->cVariables], 0, sizeof(BURN_VARIABLE) * (pVariables->dwMaxVariables - pVariables->cVariables));
1497 }
1498 else
1499 {
1500 pVariables->rgVariables = (BURN_VARIABLE*)MemAlloc(sizeof(BURN_VARIABLE) * pVariables->dwMaxVariables, TRUE);
1501 ExitOnNull(pVariables->rgVariables, hr, E_OUTOFMEMORY, "Failed to allocate room for variables.");
1502 }
1503 }
1504
1505 // move variables
1506 if (0 < pVariables->cVariables - iPosition)
1507 {
1508 memmove(&pVariables->rgVariables[iPosition + 1], &pVariables->rgVariables[iPosition], sizeof(BURN_VARIABLE) * (pVariables->cVariables - iPosition));
1509 memset(&pVariables->rgVariables[iPosition], 0, sizeof(BURN_VARIABLE));
1510 }
1511
1512 ++pVariables->cVariables;
1513
1514 // allocate name
1515 hr = StrAllocString(&pVariables->rgVariables[iPosition].sczName, wzVariable, 0);
1516 ExitOnFailure(hr, "Failed to copy variable name.");
1517
1518LExit:
1519 return hr;
1520}
1521
1522static HRESULT SetVariableValue(
1523 __in BURN_VARIABLES* pVariables,
1524 __in_z LPCWSTR wzVariable,
1525 __in BURN_VARIANT* pVariant,
1526 __in SET_VARIABLE setBuiltin,
1527 __in BOOL fLog
1528 )
1529{
1530 HRESULT hr = S_OK;
1531 DWORD iVariable = 0;
1532
1533 ::EnterCriticalSection(&pVariables->csAccess);
1534
1535 hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable);
1536 ExitOnFailure(hr, "Failed to find variable value '%ls'.", wzVariable);
1537
1538 // Insert element if not found.
1539 if (S_FALSE == hr)
1540 {
1541 hr = InsertVariable(pVariables, wzVariable, iVariable);
1542 ExitOnFailure(hr, "Failed to insert variable '%ls'.", wzVariable);
1543 }
1544 else if (BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariables->rgVariables[iVariable].internalType) // built-in variables must be overridden.
1545 {
1546 if (SET_VARIABLE_OVERRIDE_BUILTIN == setBuiltin ||
1547 (SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS == setBuiltin && pVariables->rgVariables[iVariable].fPersisted) ||
1548 SET_VARIABLE_ANY == setBuiltin && BURN_VARIABLE_INTERNAL_TYPE_BUILTIN != pVariables->rgVariables[iVariable].internalType)
1549 {
1550 hr = S_OK;
1551 }
1552 else
1553 {
1554 hr = E_INVALIDARG;
1555 ExitOnRootFailure(hr, "Attempt to set built-in variable value: %ls", wzVariable);
1556 }
1557 }
1558 else // must *not* be a built-in variable so caller should not have tried to override it as a built-in.
1559 {
1560 // Not possible from external callers so just assert.
1561 AssertSz(SET_VARIABLE_OVERRIDE_BUILTIN != setBuiltin, "Intent to overwrite non-built-in variable.");
1562 }
1563
1564 // Log value when not overwriting a built-in variable.
1565 if (fLog && BURN_VARIABLE_INTERNAL_TYPE_NORMAL == pVariables->rgVariables[iVariable].internalType)
1566 {
1567 if (pVariables->rgVariables[iVariable].fHidden)
1568 {
1569 LogStringLine(REPORT_STANDARD, "Setting hidden variable '%ls'", wzVariable);
1570 }
1571 else
1572 {
1573 switch (pVariant->Type)
1574 {
1575 case BURN_VARIANT_TYPE_NONE:
1576 if (BURN_VARIANT_TYPE_NONE != pVariables->rgVariables[iVariable].Value.Type)
1577 {
1578 LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable);
1579 }
1580 break;
1581
1582 case BURN_VARIANT_TYPE_NUMERIC:
1583 LogStringLine(REPORT_STANDARD, "Setting numeric variable '%ls' to value %lld", wzVariable, pVariant->llValue);
1584 break;
1585
1586 case BURN_VARIANT_TYPE_FORMATTED: __fallthrough;
1587 case BURN_VARIANT_TYPE_STRING:
1588 if (!pVariant->sczValue)
1589 {
1590 LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable);
1591 }
1592 else
1593 {
1594 LogStringLine(REPORT_STANDARD, "Setting %ls variable '%ls' to value '%ls'", BURN_VARIANT_TYPE_FORMATTED == pVariant->Type ? L"formatted" : L"string", wzVariable, pVariant->sczValue);
1595 }
1596 break;
1597
1598 case BURN_VARIANT_TYPE_VERSION:
1599 if (!pVariant->pValue)
1600 {
1601 LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable);
1602 }
1603 else
1604 {
1605 LogStringLine(REPORT_STANDARD, "Setting version variable '%ls' to value '%ls'", wzVariable, pVariant->pValue->sczVersion);
1606 }
1607 break;
1608
1609 default:
1610 AssertSz(FALSE, "Unknown variant type.");
1611 break;
1612 }
1613 }
1614
1615 if (BURN_VARIANT_TYPE_VERSION == pVariant->Type && pVariant->pValue && pVariant->pValue->fInvalid)
1616 {
1617 LogId(REPORT_WARNING, MSG_VARIABLE_INVALID_VERSION, wzVariable);
1618 }
1619 }
1620
1621 // Update variable value.
1622 hr = BVariantSetValue(&pVariables->rgVariables[iVariable].Value, pVariant);
1623 ExitOnFailure(hr, "Failed to set value of variable: %ls", wzVariable);
1624
1625LExit:
1626 ::LeaveCriticalSection(&pVariables->csAccess);
1627
1628 if (FAILED(hr) && fLog)
1629 {
1630 LogStringLine(REPORT_STANDARD, "Setting variable failed: ID '%ls', HRESULT 0x%x", wzVariable, hr);
1631 }
1632
1633 return hr;
1634}
1635
1636static HRESULT InitializeVariableVersionNT(
1637 __in DWORD_PTR dwpData,
1638 __inout BURN_VARIANT* pValue
1639 )
1640{
1641 HRESULT hr = S_OK;
1642 RTL_OSVERSIONINFOEXW ovix = { };
1643 BURN_VARIANT value = { };
1644 VERUTIL_VERSION* pVersion = NULL;
1645
1646 hr = OsRtlGetVersion(&ovix);
1647 ExitOnFailure(hr, "Failed to get OS info.");
1648
1649 switch ((OS_INFO_VARIABLE)dwpData)
1650 {
1651 case OS_INFO_VARIABLE_ServicePackLevel:
1652 if (0 != ovix.wServicePackMajor)
1653 {
1654 value.llValue = static_cast<LONGLONG>(ovix.wServicePackMajor);
1655 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1656 }
1657 break;
1658 case OS_INFO_VARIABLE_VersionNT:
1659 hr = VerVersionFromQword(MAKEQWORDVERSION(ovix.dwMajorVersion, ovix.dwMinorVersion, 0, 0), &pVersion);
1660 ExitOnFailure(hr, "Failed to create VersionNT from QWORD.");
1661
1662 value.pValue = pVersion;
1663 value.Type = BURN_VARIANT_TYPE_VERSION;
1664 break;
1665 case OS_INFO_VARIABLE_VersionNT64:
1666 {
1667#if !defined(_WIN64)
1668 BOOL fIsWow64 = FALSE;
1669
1670 ProcWow64(::GetCurrentProcess(), &fIsWow64);
1671 if (fIsWow64)
1672#endif
1673 {
1674 hr = VerVersionFromQword(MAKEQWORDVERSION(ovix.dwMajorVersion, ovix.dwMinorVersion, 0, 0), &pVersion);
1675 ExitOnFailure(hr, "Failed to create VersionNT64 from QWORD.");
1676
1677 value.pValue = pVersion;
1678 value.Type = BURN_VARIANT_TYPE_VERSION;
1679 }
1680 }
1681 break;
1682 case OS_INFO_VARIABLE_WindowsBuildNumber:
1683 value.llValue = static_cast<LONGLONG>(ovix.dwBuildNumber);
1684 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1685 default:
1686 AssertSz(FALSE, "Unknown OS info type.");
1687 break;
1688 }
1689
1690 hr = BVariantCopy(&value, pValue);
1691 ExitOnFailure(hr, "Failed to set variant value.");
1692
1693LExit:
1694 ReleaseVerutilVersion(pVersion);
1695
1696 return hr;
1697}
1698
1699static HRESULT InitializeVariableOsInfo(
1700 __in DWORD_PTR dwpData,
1701 __inout BURN_VARIANT* pValue
1702 )
1703{
1704 HRESULT hr = S_OK;
1705 RTL_OSVERSIONINFOEXW ovix = { };
1706 BURN_VARIANT value = { };
1707
1708 hr = OsRtlGetVersion(&ovix);
1709 ExitOnFailure(hr, "Failed to get OS info.");
1710
1711 switch ((OS_INFO_VARIABLE)dwpData)
1712 {
1713 case OS_INFO_VARIABLE_NTProductType:
1714 value.llValue = ovix.wProductType;
1715 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1716 break;
1717 case OS_INFO_VARIABLE_NTSuiteBackOffice:
1718 value.llValue = VER_SUITE_BACKOFFICE & ovix.wSuiteMask ? 1 : 0;
1719 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1720 break;
1721 case OS_INFO_VARIABLE_NTSuiteDataCenter:
1722 value.llValue = VER_SUITE_DATACENTER & ovix.wSuiteMask ? 1 : 0;
1723 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1724 break;
1725 case OS_INFO_VARIABLE_NTSuiteEnterprise:
1726 value.llValue = VER_SUITE_ENTERPRISE & ovix.wSuiteMask ? 1 : 0;
1727 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1728 break;
1729 case OS_INFO_VARIABLE_NTSuitePersonal:
1730 value.llValue = VER_SUITE_PERSONAL & ovix.wSuiteMask ? 1 : 0;
1731 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1732 break;
1733 case OS_INFO_VARIABLE_NTSuiteSmallBusiness:
1734 value.llValue = VER_SUITE_SMALLBUSINESS & ovix.wSuiteMask ? 1 : 0;
1735 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1736 break;
1737 case OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted:
1738 value.llValue = VER_SUITE_SMALLBUSINESS_RESTRICTED & ovix.wSuiteMask ? 1 : 0;
1739 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1740 break;
1741 case OS_INFO_VARIABLE_NTSuiteWebServer:
1742 value.llValue = VER_SUITE_BLADE & ovix.wSuiteMask ? 1 : 0;
1743 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1744 break;
1745 case OS_INFO_VARIABLE_CompatibilityMode:
1746 {
1747 DWORDLONG dwlConditionMask = 0;
1748 VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_EQUAL);
1749 VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_EQUAL);
1750 VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMAJOR, VER_EQUAL);
1751 VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMINOR, VER_EQUAL);
1752
1753 value.llValue = ::VerifyVersionInfoW(&ovix, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, dwlConditionMask);
1754 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1755 }
1756 break;
1757 case OS_INFO_VARIABLE_TerminalServer:
1758 value.llValue = (VER_SUITE_TERMINAL == (ovix.wSuiteMask & VER_SUITE_TERMINAL)) && (VER_SUITE_SINGLEUSERTS != (ovix.wSuiteMask & VER_SUITE_SINGLEUSERTS)) ? 1 : 0;
1759 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1760 break;
1761 default:
1762 AssertSz(FALSE, "Unknown OS info type.");
1763 break;
1764 }
1765
1766 hr = BVariantCopy(&value, pValue);
1767 ExitOnFailure(hr, "Failed to set variant value.");
1768
1769LExit:
1770 return hr;
1771}
1772
1773static HRESULT InitializeVariableSystemInfo(
1774 __in DWORD_PTR dwpData,
1775 __inout BURN_VARIANT* pValue
1776 )
1777{
1778 HRESULT hr = S_OK;
1779 SYSTEM_INFO si = { };
1780 BURN_VARIANT value = { };
1781
1782 ::GetNativeSystemInfo(&si);
1783
1784 switch ((OS_INFO_VARIABLE)dwpData)
1785 {
1786 case OS_INFO_VARIABLE_ProcessorArchitecture:
1787 value.llValue = si.wProcessorArchitecture;
1788 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1789 break;
1790 default:
1791 AssertSz(FALSE, "Unknown OS info type.");
1792 break;
1793 }
1794
1795 hr = BVariantCopy(&value, pValue);
1796 ExitOnFailure(hr, "Failed to set variant value.");
1797
1798LExit:
1799 return hr;
1800}
1801
1802static HRESULT InitializeVariableComputerName(
1803 __in DWORD_PTR dwpData,
1804 __inout BURN_VARIANT* pValue
1805 )
1806{
1807 UNREFERENCED_PARAMETER(dwpData);
1808
1809 HRESULT hr = S_OK;
1810 WCHAR wzComputerName[MAX_COMPUTERNAME_LENGTH + 1] = { };
1811 DWORD cchComputerName = countof(wzComputerName);
1812
1813 // get computer name
1814 if (!::GetComputerNameW(wzComputerName, &cchComputerName))
1815 {
1816 ExitWithLastError(hr, "Failed to get computer name.");
1817 }
1818
1819 // set value
1820 hr = BVariantSetString(pValue, wzComputerName, 0, FALSE);
1821 ExitOnFailure(hr, "Failed to set variant value.");
1822
1823LExit:
1824 return hr;
1825}
1826
1827static HRESULT InitializeVariableVersionMsi(
1828 __in DWORD_PTR /*dwpData*/,
1829 __inout BURN_VARIANT* pValue
1830 )
1831{
1832 HRESULT hr = S_OK;
1833 DLLGETVERSIONPROC pfnMsiDllGetVersion = NULL;
1834 DLLVERSIONINFO msiVersionInfo = { };
1835 VERUTIL_VERSION* pVersion = NULL;
1836
1837 // get DllGetVersion proc address
1838 pfnMsiDllGetVersion = (DLLGETVERSIONPROC)::GetProcAddress(::GetModuleHandleW(L"msi"), "DllGetVersion");
1839 ExitOnNullWithLastError(pfnMsiDllGetVersion, hr, "Failed to find DllGetVersion entry point in msi.dll.");
1840
1841 // get msi.dll version info
1842 msiVersionInfo.cbSize = sizeof(DLLVERSIONINFO);
1843 hr = pfnMsiDllGetVersion(&msiVersionInfo);
1844 ExitOnFailure(hr, "Failed to get msi.dll version info.");
1845
1846 hr = VerVersionFromQword(MAKEQWORDVERSION(msiVersionInfo.dwMajorVersion, msiVersionInfo.dwMinorVersion, 0, 0), &pVersion);
1847 ExitOnFailure(hr, "Failed to create msi.dll version from QWORD.");
1848
1849 hr = BVariantSetVersion(pValue, pVersion);
1850 ExitOnFailure(hr, "Failed to set variant value.");
1851
1852LExit:
1853 ReleaseVerutilVersion(pVersion);
1854
1855 return hr;
1856}
1857
1858static HRESULT InitializeVariableCsidlFolder(
1859 __in DWORD_PTR dwpData,
1860 __inout BURN_VARIANT* pValue
1861 )
1862{
1863 HRESULT hr = S_OK;
1864 LPWSTR sczPath = NULL;
1865 int nFolder = (int)dwpData;
1866
1867 // get folder path
1868 hr = ShelGetFolder(&sczPath, nFolder);
1869 ExitOnRootFailure(hr, "Failed to get shell folder.");
1870
1871 // set value
1872 hr = BVariantSetString(pValue, sczPath, 0, FALSE);
1873 ExitOnFailure(hr, "Failed to set variant value.");
1874
1875LExit:
1876 ReleaseStr(sczPath);
1877
1878 return hr;
1879}
1880
1881static HRESULT InitializeVariableTempFolder(
1882 __in DWORD_PTR dwpData,
1883 __inout BURN_VARIANT* pValue
1884 )
1885{
1886 UNREFERENCED_PARAMETER(dwpData);
1887
1888 HRESULT hr = S_OK;
1889 WCHAR wzPath[MAX_PATH] = { };
1890
1891 // get volume path name
1892 if (!::GetTempPathW(MAX_PATH, wzPath))
1893 {
1894 ExitWithLastError(hr, "Failed to get temp path.");
1895 }
1896
1897 // set value
1898 hr = BVariantSetString(pValue, wzPath, 0, FALSE);
1899 ExitOnFailure(hr, "Failed to set variant value.");
1900
1901LExit:
1902 return hr;
1903}
1904
1905static HRESULT InitializeVariableSystemFolder(
1906 __in DWORD_PTR dwpData,
1907 __inout BURN_VARIANT* pValue
1908 )
1909{
1910 HRESULT hr = S_OK;
1911 BOOL f64 = (BOOL)dwpData;
1912 WCHAR wzSystemFolder[MAX_PATH] = { };
1913
1914#if !defined(_WIN64)
1915 BOOL fIsWow64 = FALSE;
1916 ProcWow64(::GetCurrentProcess(), &fIsWow64);
1917
1918 if (fIsWow64)
1919 {
1920 if (f64)
1921 {
1922 if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder)))
1923 {
1924 ExitWithLastError(hr, "Failed to get 64-bit system folder.");
1925 }
1926 }
1927 else
1928 {
1929 if (!::GetSystemWow64DirectoryW(wzSystemFolder, countof(wzSystemFolder)))
1930 {
1931 ExitWithLastError(hr, "Failed to get 32-bit system folder.");
1932 }
1933 }
1934 }
1935 else
1936 {
1937 if (!f64)
1938 {
1939 if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder)))
1940 {
1941 ExitWithLastError(hr, "Failed to get 32-bit system folder.");
1942 }
1943 }
1944 }
1945#else
1946 if (f64)
1947 {
1948 if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder)))
1949 {
1950 ExitWithLastError(hr, "Failed to get 64-bit system folder.");
1951 }
1952 }
1953 else
1954 {
1955 if (!::GetSystemWow64DirectoryW(wzSystemFolder, countof(wzSystemFolder)))
1956 {
1957 ExitWithLastError(hr, "Failed to get 32-bit system folder.");
1958 }
1959 }
1960#endif
1961
1962 if (*wzSystemFolder)
1963 {
1964 hr = PathFixedBackslashTerminate(wzSystemFolder, countof(wzSystemFolder));
1965 ExitOnFailure(hr, "Failed to backslash terminate system folder.");
1966 }
1967
1968 // set value
1969 hr = BVariantSetString(pValue, wzSystemFolder, 0, FALSE);
1970 ExitOnFailure(hr, "Failed to set system folder variant value.");
1971
1972LExit:
1973 return hr;
1974}
1975
1976static HRESULT InitializeVariableWindowsVolumeFolder(
1977 __in DWORD_PTR dwpData,
1978 __inout BURN_VARIANT* pValue
1979 )
1980{
1981 UNREFERENCED_PARAMETER(dwpData);
1982
1983 HRESULT hr = S_OK;
1984 WCHAR wzWindowsPath[MAX_PATH] = { };
1985 WCHAR wzVolumePath[MAX_PATH] = { };
1986
1987 // get windows directory
1988 if (!::GetWindowsDirectoryW(wzWindowsPath, countof(wzWindowsPath)))
1989 {
1990 ExitWithLastError(hr, "Failed to get windows directory.");
1991 }
1992
1993 // get volume path name
1994 if (!::GetVolumePathNameW(wzWindowsPath, wzVolumePath, MAX_PATH))
1995 {
1996 ExitWithLastError(hr, "Failed to get volume path name.");
1997 }
1998
1999 // set value
2000 hr = BVariantSetString(pValue, wzVolumePath, 0, FALSE);
2001 ExitOnFailure(hr, "Failed to set variant value.");
2002
2003LExit:
2004 return hr;
2005}
2006
2007static HRESULT InitializeVariablePrivileged(
2008 __in DWORD_PTR dwpData,
2009 __inout BURN_VARIANT* pValue
2010 )
2011{
2012 UNREFERENCED_PARAMETER(dwpData);
2013
2014 HRESULT hr = S_OK;
2015 BOOL fPrivileged = FALSE;
2016
2017 // check if process could run privileged.
2018 hr = OsCouldRunPrivileged(&fPrivileged);
2019 ExitOnFailure(hr, "Failed to check if process could run privileged.");
2020
2021 // set value
2022 hr = BVariantSetNumeric(pValue, fPrivileged);
2023 ExitOnFailure(hr, "Failed to set variant value.");
2024
2025LExit:
2026 return hr;
2027}
2028
2029static HRESULT InitializeSystemLanguageID(
2030 __in DWORD_PTR dwpData,
2031 __inout BURN_VARIANT* pValue
2032 )
2033{
2034 UNREFERENCED_PARAMETER(dwpData);
2035
2036 HRESULT hr = S_OK;
2037 LANGID langid = ::GetSystemDefaultLangID();
2038
2039 hr = BVariantSetNumeric(pValue, langid);
2040 ExitOnFailure(hr, "Failed to set variant value.");
2041
2042LExit:
2043 return hr;
2044}
2045
2046static HRESULT InitializeUserUILanguageID(
2047 __in DWORD_PTR dwpData,
2048 __inout BURN_VARIANT* pValue
2049 )
2050{
2051 UNREFERENCED_PARAMETER(dwpData);
2052
2053 HRESULT hr = S_OK;
2054 LANGID langid = ::GetUserDefaultUILanguage();
2055
2056 hr = BVariantSetNumeric(pValue, langid);
2057 ExitOnFailure(hr, "Failed to set variant value.");
2058
2059LExit:
2060 return hr;
2061}
2062
2063static HRESULT InitializeUserLanguageID(
2064 __in DWORD_PTR dwpData,
2065 __inout BURN_VARIANT* pValue
2066 )
2067{
2068 UNREFERENCED_PARAMETER(dwpData);
2069
2070 HRESULT hr = S_OK;
2071 LANGID langid = ::GetUserDefaultLangID();
2072
2073 hr = BVariantSetNumeric(pValue, langid);
2074 ExitOnFailure(hr, "Failed to set variant value.");
2075
2076LExit:
2077 return hr;
2078}
2079
2080static HRESULT InitializeVariableString(
2081 __in DWORD_PTR dwpData,
2082 __inout BURN_VARIANT* pValue
2083 )
2084{
2085 HRESULT hr = S_OK;
2086 LPCWSTR wzValue = (LPCWSTR)dwpData;
2087
2088 // set value
2089 hr = BVariantSetString(pValue, wzValue, 0, FALSE);
2090 ExitOnFailure(hr, "Failed to set variant value.");
2091
2092LExit:
2093 return hr;
2094}
2095
2096static HRESULT InitializeVariableNumeric(
2097 __in DWORD_PTR dwpData,
2098 __inout BURN_VARIANT* pValue
2099 )
2100{
2101 HRESULT hr = S_OK;
2102 LONGLONG llValue = (LONGLONG)dwpData;
2103
2104 // set value
2105 hr = BVariantSetNumeric(pValue, llValue);
2106 ExitOnFailure(hr, "Failed to set variant value.");
2107
2108LExit:
2109 return hr;
2110}
2111
2112#if !defined(_WIN64)
2113static HRESULT InitializeVariableRegistryFolder(
2114 __in DWORD_PTR dwpData,
2115 __inout BURN_VARIANT* pValue
2116 )
2117{
2118 HRESULT hr = S_OK;
2119 int nFolder = (int)dwpData;
2120 LPWSTR sczPath = NULL;
2121
2122 BOOL fIsWow64 = FALSE;
2123
2124 ProcWow64(::GetCurrentProcess(), &fIsWow64);
2125 if (!fIsWow64) // on 32-bit machines, variables aren't set
2126 {
2127 ExitFunction();
2128 }
2129
2130 hr = Get64bitFolderFromRegistry(nFolder, &sczPath);
2131 ExitOnFailure(hr, "Failed to get 64-bit folder.");
2132
2133 // set value
2134 hr = BVariantSetString(pValue, sczPath, 0, FALSE);
2135 ExitOnFailure(hr, "Failed to set variant value.");
2136
2137LExit:
2138 ReleaseStr(sczPath);
2139
2140 return hr;
2141}
2142#endif
2143
2144static HRESULT InitializeVariable6432Folder(
2145 __in DWORD_PTR dwpData,
2146 __inout BURN_VARIANT* pValue
2147 )
2148{
2149 HRESULT hr = S_OK;
2150 int nFolder = (int)dwpData;
2151 LPWSTR sczPath = NULL;
2152
2153#if !defined(_WIN64)
2154 BOOL fIsWow64 = FALSE;
2155
2156 // If 32-bit use shell-folder.
2157 ProcWow64(::GetCurrentProcess(), &fIsWow64);
2158 if (!fIsWow64)
2159 {
2160 hr = ShelGetFolder(&sczPath, nFolder);
2161 ExitOnRootFailure(hr, "Failed to get shell folder.");
2162 }
2163 else
2164#endif
2165 {
2166 hr = Get64bitFolderFromRegistry(nFolder, &sczPath);
2167 ExitOnFailure(hr, "Failed to get 64-bit folder.");
2168 }
2169
2170 // set value
2171 hr = BVariantSetString(pValue, sczPath, 0, FALSE);
2172 ExitOnFailure(hr, "Failed to set variant value.");
2173
2174LExit:
2175 ReleaseStr(sczPath);
2176
2177 return hr;
2178}
2179
2180// Get the date in the same format as Windows Installer.
2181static HRESULT InitializeVariableDate(
2182 __in DWORD_PTR /*dwpData*/,
2183 __inout BURN_VARIANT* pValue
2184 )
2185{
2186 HRESULT hr = S_OK;
2187 SYSTEMTIME systime = { };
2188 LPWSTR sczDate = NULL;
2189 int cchDate = 0;
2190
2191 ::GetSystemTime(&systime);
2192
2193 cchDate = ::GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, NULL, NULL, cchDate);
2194 if (!cchDate)
2195 {
2196 ExitOnLastError(hr, "Failed to get the required buffer length for the Date.");
2197 }
2198
2199 hr = StrAlloc(&sczDate, cchDate);
2200 ExitOnFailure(hr, "Failed to allocate the buffer for the Date.");
2201
2202 if (!::GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, NULL, sczDate, cchDate))
2203 {
2204 ExitOnLastError(hr, "Failed to get the Date.");
2205 }
2206
2207 // set value
2208 hr = BVariantSetString(pValue, sczDate, cchDate, FALSE);
2209 ExitOnFailure(hr, "Failed to set variant value.");
2210
2211LExit:
2212 ReleaseStr(sczDate);
2213
2214 return hr;
2215}
2216
2217static HRESULT InitializeVariableInstallerName(
2218 __in DWORD_PTR /*dwpData*/,
2219 __inout BURN_VARIANT* pValue
2220 )
2221{
2222 HRESULT hr = S_OK;
2223
2224 // set value
2225 hr = BVariantSetString(pValue, L"WiX Burn", 0, FALSE);
2226 ExitOnFailure(hr, "Failed to set variant value.");
2227
2228LExit:
2229 return hr;
2230}
2231
2232static HRESULT InitializeVariableInstallerVersion(
2233 __in DWORD_PTR /*dwpData*/,
2234 __inout BURN_VARIANT* pValue
2235 )
2236{
2237 HRESULT hr = S_OK;
2238 LPWSTR sczVersion = NULL;
2239
2240 hr = StrAllocStringAnsi(&sczVersion, szVerMajorMinorBuild, 0, CP_ACP);
2241 ExitOnFailure(hr, "Failed to copy the engine version.");
2242
2243 // set value
2244 hr = BVariantSetString(pValue, sczVersion, 0, FALSE);
2245 ExitOnFailure(hr, "Failed to set variant value.");
2246
2247LExit:
2248 ReleaseStr(sczVersion);
2249
2250 return hr;
2251}
2252
2253static HRESULT InitializeVariableVersion(
2254 __in DWORD_PTR dwpData,
2255 __inout BURN_VARIANT* pValue
2256 )
2257{
2258 HRESULT hr = S_OK;
2259 LPCWSTR wzValue = (LPCWSTR)dwpData;
2260 VERUTIL_VERSION* pVersion = NULL;
2261
2262 hr = VerParseVersion(wzValue, 0, FALSE, &pVersion);
2263 ExitOnFailure(hr, "Failed to initialize version.");
2264
2265 // set value
2266 hr = BVariantSetVersion(pValue, pVersion);
2267 ExitOnFailure(hr, "Failed to set variant value.");
2268
2269LExit:
2270 ReleaseVerutilVersion(pVersion);
2271
2272 return hr;
2273}
2274
2275// Get the current user the same as Windows Installer.
2276static HRESULT InitializeVariableLogonUser(
2277 __in DWORD_PTR /*dwpData*/,
2278 __inout BURN_VARIANT* pValue
2279 )
2280{
2281 HRESULT hr = S_OK;
2282 WCHAR wzUserName[UNLEN + 1];
2283 DWORD cchUserName = countof(wzUserName);
2284
2285 if (!::GetUserNameW(wzUserName, &cchUserName))
2286 {
2287 ExitOnLastError(hr, "Failed to get the user name.");
2288 }
2289
2290 // set value
2291 hr = BVariantSetString(pValue, wzUserName, 0, FALSE);
2292 ExitOnFailure(hr, "Failed to set variant value.");
2293
2294LExit:
2295 return hr;
2296}
2297
2298static HRESULT Get64bitFolderFromRegistry(
2299 __in int nFolder,
2300 __deref_out_z LPWSTR* psczPath
2301 )
2302{
2303 HRESULT hr = S_OK;
2304 HKEY hkFolders = NULL;
2305
2306 AssertSz(CSIDL_PROGRAM_FILES == nFolder || CSIDL_PROGRAM_FILES_COMMON == nFolder, "Unknown folder CSIDL.");
2307 LPCWSTR wzFolderValue = CSIDL_PROGRAM_FILES_COMMON == nFolder ? L"CommonFilesDir" : L"ProgramFilesDir";
2308
2309 hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion", KEY_READ | KEY_WOW64_64KEY, &hkFolders);
2310 ExitOnFailure(hr, "Failed to open Windows folder key.");
2311
2312 hr = RegReadString(hkFolders, wzFolderValue, psczPath);
2313 ExitOnFailure(hr, "Failed to read folder path for '%ls'.", wzFolderValue);
2314
2315 hr = PathBackslashTerminate(psczPath);
2316 ExitOnFailure(hr, "Failed to ensure path was backslash terminated.");
2317
2318LExit:
2319 ReleaseRegKey(hkFolders);
2320
2321 return hr;
2322}
2323
diff --git a/src/burn/engine/variable.h b/src/burn/engine/variable.h
new file mode 100644
index 00000000..a38c9daa
--- /dev/null
+++ b/src/burn/engine/variable.h
@@ -0,0 +1,185 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// constants
11
12const LPCWSTR VARIABLE_DATE = L"Date";
13const LPCWSTR VARIABLE_LOGONUSER = L"LogonUser";
14const LPCWSTR VARIABLE_INSTALLERNAME = L"InstallerName";
15const LPCWSTR VARIABLE_INSTALLERVERSION = L"InstallerVersion";
16
17
18// typedefs
19
20typedef HRESULT (*PFN_INITIALIZEVARIABLE)(
21 __in DWORD_PTR dwpData,
22 __inout BURN_VARIANT* pValue
23 );
24
25
26// constants
27
28enum BURN_VARIABLE_INTERNAL_TYPE
29{
30 BURN_VARIABLE_INTERNAL_TYPE_NORMAL, // the BA can set this variable.
31 BURN_VARIABLE_INTERNAL_TYPE_OVERRIDABLE_BUILTIN, // the BA can't set this variable, but the unelevated process can serialize it to the elevated process.
32 BURN_VARIABLE_INTERNAL_TYPE_BUILTIN, // the BA can't set this variable, and the unelevated process can't serialize it to the elevated process.
33};
34
35
36// structs
37
38typedef struct _BURN_VARIABLE
39{
40 LPWSTR sczName;
41 BURN_VARIANT Value;
42 BOOL fHidden;
43 BOOL fPersisted;
44
45 // used for late initialization of built-in variables
46 BURN_VARIABLE_INTERNAL_TYPE internalType;
47 PFN_INITIALIZEVARIABLE pfnInitialize;
48 DWORD_PTR dwpInitializeData;
49} BURN_VARIABLE;
50
51typedef struct _BURN_VARIABLES
52{
53 CRITICAL_SECTION csAccess;
54 DWORD dwMaxVariables;
55 DWORD cVariables;
56 BURN_VARIABLE* rgVariables;
57} BURN_VARIABLES;
58
59
60// function declarations
61
62HRESULT VariableInitialize(
63 __in BURN_VARIABLES* pVariables
64 );
65HRESULT VariablesParseFromXml(
66 __in BURN_VARIABLES* pVariables,
67 __in IXMLDOMNode* pixnBundle
68 );
69void VariablesUninitialize(
70 __in BURN_VARIABLES* pVariables
71 );
72void VariablesDump(
73 __in BURN_VARIABLES* pVariables
74 );
75HRESULT VariableGetNumeric(
76 __in BURN_VARIABLES* pVariables,
77 __in_z LPCWSTR wzVariable,
78 __out LONGLONG* pllValue
79 );
80HRESULT VariableGetString(
81 __in BURN_VARIABLES* pVariables,
82 __in_z LPCWSTR wzVariable,
83 __out_z LPWSTR* psczValue
84 );
85HRESULT VariableGetVersion(
86 __in BURN_VARIABLES* pVariables,
87 __in_z LPCWSTR wzVariable,
88 __in VERUTIL_VERSION** ppValue
89 );
90HRESULT VariableGetVariant(
91 __in BURN_VARIABLES* pVariables,
92 __in_z LPCWSTR wzVariable,
93 __in BURN_VARIANT* pValue
94 );
95HRESULT VariableGetFormatted(
96 __in BURN_VARIABLES* pVariables,
97 __in_z LPCWSTR wzVariable,
98 __out_z LPWSTR* psczValue,
99 __out BOOL* pfContainsHiddenVariable
100 );
101HRESULT VariableSetNumeric(
102 __in BURN_VARIABLES* pVariables,
103 __in_z LPCWSTR wzVariable,
104 __in LONGLONG llValue,
105 __in BOOL fOverwriteBuiltIn
106 );
107HRESULT VariableSetString(
108 __in BURN_VARIABLES* pVariables,
109 __in_z LPCWSTR wzVariable,
110 __in_z_opt LPCWSTR wzValue,
111 __in BOOL fOverwriteBuiltIn,
112 __in BOOL fFormatted
113 );
114HRESULT VariableSetVersion(
115 __in BURN_VARIABLES* pVariables,
116 __in_z LPCWSTR wzVariable,
117 __in VERUTIL_VERSION* pValue,
118 __in BOOL fOverwriteBuiltIn
119 );
120HRESULT VariableSetVariant(
121 __in BURN_VARIABLES* pVariables,
122 __in_z LPCWSTR wzVariable,
123 __in BURN_VARIANT* pVariant
124 );
125HRESULT VariableFormatString(
126 __in BURN_VARIABLES* pVariables,
127 __in_z LPCWSTR wzIn,
128 __out_z_opt LPWSTR* psczOut,
129 __out_opt SIZE_T* pcchOut
130 );
131HRESULT VariableFormatStringObfuscated(
132 __in BURN_VARIABLES* pVariables,
133 __in_z LPCWSTR wzIn,
134 __out_z_opt LPWSTR* psczOut,
135 __out_opt SIZE_T* pcchOut
136 );
137HRESULT VariableEscapeString(
138 __in_z LPCWSTR wzIn,
139 __out_z LPWSTR* psczOut
140 );
141HRESULT VariableSerialize(
142 __in BURN_VARIABLES* pVariables,
143 __in BOOL fPersisting,
144 __inout BYTE** ppbBuffer,
145 __inout SIZE_T* piBuffer
146 );
147HRESULT VariableDeserialize(
148 __in BURN_VARIABLES* pVariables,
149 __in BOOL fWasPersisted,
150 __in_bcount(cbBuffer) BYTE* pbBuffer,
151 __in SIZE_T cbBuffer,
152 __inout SIZE_T* piBuffer
153 );
154HRESULT VariableStrAlloc(
155 __in BOOL fZeroOnRealloc,
156 __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
157 __in DWORD_PTR cch
158 );
159HRESULT VariableStrAllocString(
160 __in BOOL fZeroOnRealloc,
161 __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz,
162 __in_z LPCWSTR wzSource,
163 __in DWORD_PTR cchSource
164 );
165HRESULT VariableStrAllocConcat(
166 __in BOOL fZeroOnRealloc,
167 __deref_out_z LPWSTR* ppwz,
168 __in_z LPCWSTR wzSource,
169 __in DWORD_PTR cchSource
170 );
171HRESULT __cdecl VariableStrAllocFormatted(
172 __in BOOL fZeroOnRealloc,
173 __deref_out_z LPWSTR* ppwz,
174 __in __format_string LPCWSTR wzFormat,
175 ...
176 );
177HRESULT VariableIsHidden(
178 __in BURN_VARIABLES* pVariables,
179 __in_z LPCWSTR wzVariable,
180 __out BOOL* pfHidden
181 );
182
183#if defined(__cplusplus)
184}
185#endif
diff --git a/src/burn/engine/variant.cpp b/src/burn/engine/variant.cpp
new file mode 100644
index 00000000..2267ee7b
--- /dev/null
+++ b/src/burn/engine/variant.cpp
@@ -0,0 +1,321 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5// internal function declarations
6
7static HRESULT GetVersionInternal(
8 __in BURN_VARIANT* pVariant,
9 __in BOOL fHidden,
10 __in BOOL fSilent,
11 __out VERUTIL_VERSION** ppValue
12 );
13
14// function definitions
15
16extern "C" void BVariantUninitialize(
17 __in BURN_VARIANT* pVariant
18 )
19{
20 if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type ||
21 BURN_VARIANT_TYPE_STRING == pVariant->Type)
22 {
23 StrSecureZeroFreeString(pVariant->sczValue);
24 }
25 SecureZeroMemory(pVariant, sizeof(BURN_VARIANT));
26}
27
28extern "C" HRESULT BVariantGetNumeric(
29 __in BURN_VARIANT* pVariant,
30 __out LONGLONG* pllValue
31 )
32{
33 HRESULT hr = S_OK;
34
35 switch (pVariant->Type)
36 {
37 case BURN_VARIANT_TYPE_NUMERIC:
38 *pllValue = pVariant->llValue;
39 break;
40 case BURN_VARIANT_TYPE_FORMATTED: __fallthrough;
41 case BURN_VARIANT_TYPE_STRING:
42 hr = StrStringToInt64(pVariant->sczValue, 0, pllValue);
43 if (FAILED(hr))
44 {
45 hr = DISP_E_TYPEMISMATCH;
46 }
47 break;
48 case BURN_VARIANT_TYPE_VERSION:
49 hr = StrStringToInt64(pVariant->pValue ? pVariant->pValue->sczVersion : NULL, 0, pllValue);
50 if (FAILED(hr))
51 {
52 hr = DISP_E_TYPEMISMATCH;
53 }
54 break;
55 default:
56 hr = E_INVALIDARG;
57 break;
58 }
59
60 return hr;
61}
62
63extern "C" HRESULT BVariantGetString(
64 __in BURN_VARIANT* pVariant,
65 __out_z LPWSTR* psczValue
66 )
67{
68 HRESULT hr = S_OK;
69
70 switch (pVariant->Type)
71 {
72 case BURN_VARIANT_TYPE_NUMERIC:
73 hr = StrAllocFormattedSecure(psczValue, L"%I64d", pVariant->llValue);
74 ExitOnFailure(hr, "Failed to convert int64 to string.");
75 break;
76 case BURN_VARIANT_TYPE_FORMATTED: __fallthrough;
77 case BURN_VARIANT_TYPE_STRING:
78 hr = StrAllocStringSecure(psczValue, pVariant->sczValue, 0);
79 ExitOnFailure(hr, "Failed to copy string value.");
80 break;
81 case BURN_VARIANT_TYPE_VERSION:
82 hr = StrAllocStringSecure(psczValue, pVariant->pValue ? pVariant->pValue->sczVersion : NULL, 0);
83 ExitOnFailure(hr, "Failed to copy version value.");
84 break;
85 default:
86 hr = E_INVALIDARG;
87 break;
88 }
89
90LExit:
91 return hr;
92}
93
94extern "C" HRESULT BVariantGetVersion(
95 __in BURN_VARIANT* pVariant,
96 __out VERUTIL_VERSION** ppValue
97 )
98{
99 return GetVersionInternal(pVariant, FALSE, FALSE, ppValue);
100}
101
102extern "C" HRESULT BVariantGetVersionHidden(
103 __in BURN_VARIANT* pVariant,
104 __in BOOL fHidden,
105 __out VERUTIL_VERSION** ppValue
106 )
107{
108 return GetVersionInternal(pVariant, fHidden, FALSE, ppValue);
109}
110
111extern "C" HRESULT BVariantGetVersionSilent(
112 __in BURN_VARIANT* pVariant,
113 __in BOOL fSilent,
114 __out VERUTIL_VERSION** ppValue
115 )
116{
117 return GetVersionInternal(pVariant, FALSE, fSilent, ppValue);
118}
119
120static HRESULT GetVersionInternal(
121 __in BURN_VARIANT* pVariant,
122 __in BOOL fHidden,
123 __in BOOL fSilent,
124 __out VERUTIL_VERSION** ppValue
125 )
126{
127 HRESULT hr = S_OK;
128
129 switch (pVariant->Type)
130 {
131 case BURN_VARIANT_TYPE_NUMERIC:
132 hr = VerVersionFromQword(pVariant->llValue, ppValue);
133 break;
134 case BURN_VARIANT_TYPE_FORMATTED: __fallthrough;
135 case BURN_VARIANT_TYPE_STRING:
136 hr = VerParseVersion(pVariant->sczValue, 0, FALSE, ppValue);
137 if (SUCCEEDED(hr) && !fSilent && (*ppValue)->fInvalid)
138 {
139 LogId(REPORT_WARNING, MSG_INVALID_VERSION_COERSION, fHidden ? L"*****" : pVariant->sczValue);
140 }
141 break;
142 case BURN_VARIANT_TYPE_VERSION:
143 if (!pVariant->pValue)
144 {
145 *ppValue = NULL;
146 }
147 else
148 {
149 hr = VerCopyVersion(pVariant->pValue, ppValue);
150 }
151 break;
152 default:
153 hr = E_INVALIDARG;
154 break;
155 }
156
157 return hr;
158}
159
160extern "C" HRESULT BVariantSetNumeric(
161 __in BURN_VARIANT* pVariant,
162 __in LONGLONG llValue
163 )
164{
165 HRESULT hr = S_OK;
166
167 if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type ||
168 BURN_VARIANT_TYPE_STRING == pVariant->Type)
169 {
170 StrSecureZeroFreeString(pVariant->sczValue);
171 }
172 memset(pVariant, 0, sizeof(BURN_VARIANT));
173 pVariant->llValue = llValue;
174 pVariant->Type = BURN_VARIANT_TYPE_NUMERIC;
175
176 return hr;
177}
178
179extern "C" HRESULT BVariantSetString(
180 __in BURN_VARIANT* pVariant,
181 __in_z_opt LPCWSTR wzValue,
182 __in DWORD_PTR cchValue,
183 __in BOOL fFormatted
184 )
185{
186 HRESULT hr = S_OK;
187
188 if (!wzValue) // if we're nulling out the string, make the variable NONE.
189 {
190 BVariantUninitialize(pVariant);
191 }
192 else // assign the value.
193 {
194 if (BURN_VARIANT_TYPE_FORMATTED != pVariant->Type &&
195 BURN_VARIANT_TYPE_STRING != pVariant->Type)
196 {
197 memset(pVariant, 0, sizeof(BURN_VARIANT));
198 }
199
200 hr = StrAllocStringSecure(&pVariant->sczValue, wzValue, cchValue);
201 ExitOnFailure(hr, "Failed to copy string.");
202
203 pVariant->Type = fFormatted ? BURN_VARIANT_TYPE_FORMATTED : BURN_VARIANT_TYPE_STRING;
204 }
205
206LExit:
207 return hr;
208}
209
210extern "C" HRESULT BVariantSetVersion(
211 __in BURN_VARIANT* pVariant,
212 __in VERUTIL_VERSION* pValue
213 )
214{
215 HRESULT hr = S_OK;
216
217 if (!pValue) // if we're nulling out the version, make the variable NONE.
218 {
219 BVariantUninitialize(pVariant);
220 }
221 else // assign the value.
222 {
223 if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type ||
224 BURN_VARIANT_TYPE_STRING == pVariant->Type)
225 {
226 StrSecureZeroFreeString(pVariant->sczValue);
227 }
228 memset(pVariant, 0, sizeof(BURN_VARIANT));
229 hr = VerCopyVersion(pValue, &pVariant->pValue);
230 pVariant->Type = BURN_VARIANT_TYPE_VERSION;
231 }
232
233 return hr;
234}
235
236extern "C" HRESULT BVariantSetValue(
237 __in BURN_VARIANT* pVariant,
238 __in BURN_VARIANT* pValue
239 )
240{
241 HRESULT hr = S_OK;
242
243 switch (pValue->Type)
244 {
245 case BURN_VARIANT_TYPE_NONE:
246 BVariantUninitialize(pVariant);
247 break;
248 case BURN_VARIANT_TYPE_NUMERIC:
249 hr = BVariantSetNumeric(pVariant, pValue->llValue);
250 break;
251 case BURN_VARIANT_TYPE_FORMATTED: __fallthrough;
252 case BURN_VARIANT_TYPE_STRING:
253 hr = BVariantSetString(pVariant, pValue->sczValue, 0, BURN_VARIANT_TYPE_FORMATTED == pValue->Type);
254 break;
255 case BURN_VARIANT_TYPE_VERSION:
256 hr = BVariantSetVersion(pVariant, pValue->pValue);
257 break;
258 default:
259 hr = E_INVALIDARG;
260 }
261 ExitOnFailure(hr, "Failed to copy variant value.");
262
263LExit:
264 return hr;
265}
266
267extern "C" HRESULT BVariantCopy(
268 __in BURN_VARIANT* pSource,
269 __out BURN_VARIANT* pTarget
270 )
271{
272 return BVariantSetValue(pTarget, pSource);
273}
274
275extern "C" HRESULT BVariantChangeType(
276 __in BURN_VARIANT* pVariant,
277 __in BURN_VARIANT_TYPE type
278 )
279{
280 HRESULT hr = S_OK;
281 BURN_VARIANT variant = { };
282
283 if (pVariant->Type == type)
284 {
285 ExitFunction(); // variant already is of the requested type
286 }
287 else if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type && BURN_VARIANT_TYPE_STRING == type ||
288 BURN_VARIANT_TYPE_STRING == pVariant->Type && BURN_VARIANT_TYPE_FORMATTED == type)
289 {
290 pVariant->Type = type;
291 ExitFunction();
292 }
293
294 switch (type)
295 {
296 case BURN_VARIANT_TYPE_NONE:
297 hr = S_OK;
298 break;
299 case BURN_VARIANT_TYPE_NUMERIC:
300 hr = BVariantGetNumeric(pVariant, &variant.llValue);
301 break;
302 case BURN_VARIANT_TYPE_FORMATTED: __fallthrough;
303 case BURN_VARIANT_TYPE_STRING:
304 hr = BVariantGetString(pVariant, &variant.sczValue);
305 break;
306 case BURN_VARIANT_TYPE_VERSION:
307 hr = BVariantGetVersionSilent(pVariant, TRUE, &variant.pValue);
308 break;
309 default:
310 ExitFunction1(hr = E_INVALIDARG);
311 }
312 variant.Type = type;
313 ExitOnFailure(hr, "Failed to copy variant value.");
314
315 BVariantUninitialize(pVariant);
316 memcpy_s(pVariant, sizeof(BURN_VARIANT), &variant, sizeof(BURN_VARIANT));
317 SecureZeroMemory(&variant, sizeof(BURN_VARIANT));
318
319LExit:
320 return hr;
321}
diff --git a/src/burn/engine/variant.h b/src/burn/engine/variant.h
new file mode 100644
index 00000000..e460005b
--- /dev/null
+++ b/src/burn/engine/variant.h
@@ -0,0 +1,100 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// constants
11
12enum BURN_VARIANT_TYPE
13{
14 BURN_VARIANT_TYPE_NONE,
15 BURN_VARIANT_TYPE_FORMATTED,
16 BURN_VARIANT_TYPE_NUMERIC,
17 BURN_VARIANT_TYPE_STRING, // when formatting this value should be used as is (don't continue recursively formatting).
18 BURN_VARIANT_TYPE_VERSION,
19};
20
21
22// struct
23
24typedef struct _BURN_VARIANT
25{
26 union
27 {
28 LONGLONG llValue;
29 VERUTIL_VERSION* pValue;
30 LPWSTR sczValue;
31 };
32 BURN_VARIANT_TYPE Type;
33} BURN_VARIANT;
34
35
36// function declarations
37
38void BVariantUninitialize(
39 __in BURN_VARIANT* pVariant
40 );
41HRESULT BVariantGetNumeric(
42 __in BURN_VARIANT* pVariant,
43 __out LONGLONG* pllValue
44 );
45HRESULT BVariantGetString(
46 __in BURN_VARIANT* pVariant,
47 __out_z LPWSTR* psczValue
48 );
49HRESULT BVariantGetVersion(
50 __in BURN_VARIANT* pVariant,
51 __out VERUTIL_VERSION** ppValue
52 );
53HRESULT BVariantGetVersionHidden(
54 __in BURN_VARIANT* pVariant,
55 __in BOOL fHidden,
56 __out VERUTIL_VERSION** ppValue
57 );
58HRESULT BVariantGetVersionSilent(
59 __in BURN_VARIANT* pVariant,
60 __in BOOL fSilent,
61 __out VERUTIL_VERSION** ppValue
62 );
63HRESULT BVariantSetNumeric(
64 __in BURN_VARIANT* pVariant,
65 __in LONGLONG llValue
66 );
67HRESULT BVariantSetString(
68 __in BURN_VARIANT* pVariant,
69 __in_z_opt LPCWSTR wzValue,
70 __in DWORD_PTR cchValue,
71 __in BOOL fFormatted
72 );
73HRESULT BVariantSetVersion(
74 __in BURN_VARIANT* pVariant,
75 __in VERUTIL_VERSION* pValue
76 );
77/********************************************************************
78BVariantSetValue - Convenience function that calls BVariantUninitialize,
79 BVariantSetNumeric, BVariantSetString, or
80 BVariantSetVersion based on the type of pValue.
81********************************************************************/
82HRESULT BVariantSetValue(
83 __in BURN_VARIANT* pVariant,
84 __in BURN_VARIANT* pValue
85 );
86/********************************************************************
87BVariantCopy - creates a copy of pSource.
88********************************************************************/
89HRESULT BVariantCopy(
90 __in BURN_VARIANT* pSource,
91 __out BURN_VARIANT* pTarget
92 );
93HRESULT BVariantChangeType(
94 __in BURN_VARIANT* pVariant,
95 __in BURN_VARIANT_TYPE type
96 );
97
98#if defined(__cplusplus)
99}
100#endif
diff --git a/src/burn/nuget.config b/src/burn/nuget.config
new file mode 100644
index 00000000..237c522e
--- /dev/null
+++ b/src/burn/nuget.config
@@ -0,0 +1,10 @@
1<?xml version="1.0" encoding="utf-8"?>
2<configuration>
3 <packageSources>
4 <clear />
5 <add key="wixbuildtools" value="https://ci.appveyor.com/nuget/wixbuildtools" />
6 <add key="wixtoolset-dutil" value="https://ci.appveyor.com/nuget/wixtoolset-dutil" />
7 <add key="wixtoolset-balutil" value="https://ci.appveyor.com/nuget/wixtoolset-balutil" />
8 <add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
9 </packageSources>
10</configuration>
diff --git a/src/burn/stub/StubSection.cpp b/src/burn/stub/StubSection.cpp
new file mode 100644
index 00000000..962bb3cf
--- /dev/null
+++ b/src/burn/stub/StubSection.cpp
@@ -0,0 +1,23 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5#pragma section(".wixburn",read)
6
7// If these defaults ever change, be sure to update constants in burn\engine\section.cpp as well.
8#pragma data_seg(push, ".wixburn")
9static DWORD dwMagic = 0x00f14300;
10static DWORD dwVersion = 0x00000002;
11
12static GUID guidBundleId = { };
13
14static DWORD dwStubSize = 0;
15static DWORD dwOriginalChecksum = 0;
16static DWORD dwOriginalSignatureOffset = 0;
17static DWORD dwOriginalSignatureSize = 0;
18
19static DWORD dwContainerFormat = 1;
20static DWORD dwContainerCount = 0;
21static DWORD qwBootstrapperApplicationContainerSize = 0;
22static DWORD qwAttachedContainerSize = 0;
23#pragma data_seg(pop)
diff --git a/src/burn/stub/WixToolset.Burn.props b/src/burn/stub/WixToolset.Burn.props
new file mode 100644
index 00000000..38cd333e
--- /dev/null
+++ b/src/burn/stub/WixToolset.Burn.props
@@ -0,0 +1,13 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
5 <ItemGroup>
6 <BurnExes Include="$(MSBuildThisFileDirectory)..\tools\**\burn.exe" />
7 <None Include="@(BurnExes)">
8 <Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
9 <Visible>False</Visible>
10 <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
11 </None>
12 </ItemGroup>
13</Project>
diff --git a/src/burn/stub/packages.config b/src/burn/stub/packages.config
new file mode 100644
index 00000000..a98c0c8e
--- /dev/null
+++ b/src/burn/stub/packages.config
@@ -0,0 +1,8 @@
1<?xml version="1.0" encoding="utf-8"?>
2<packages>
3 <package id="Microsoft.Build.Tasks.Git" version="1.0.0" targetFramework="native" developmentDependency="true" />
4 <package id="Microsoft.SourceLink.Common" version="1.0.0" targetFramework="native" developmentDependency="true" />
5 <package id="Microsoft.SourceLink.GitHub" version="1.0.0" targetFramework="native" developmentDependency="true" />
6 <package id="Nerdbank.GitVersioning" version="3.3.37" targetFramework="native" developmentDependency="true" />
7 <package id="WixToolset.DUtil" version="4.0.72" targetFramework="native" />
8</packages> \ No newline at end of file
diff --git a/src/burn/stub/precomp.cpp b/src/burn/stub/precomp.cpp
new file mode 100644
index 00000000..37664a1c
--- /dev/null
+++ b/src/burn/stub/precomp.cpp
@@ -0,0 +1,3 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
diff --git a/src/burn/stub/precomp.h b/src/burn/stub/precomp.h
new file mode 100644
index 00000000..bb7ded9c
--- /dev/null
+++ b/src/burn/stub/precomp.h
@@ -0,0 +1,17 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#include <windows.h>
6
7#include <dutilsources.h>
8#include <burnsources.h>
9
10#include <dutil.h>
11#include <apputil.h>
12#include <strutil.h>
13#include <fileutil.h>
14#include <pathutil.h>
15#include <logutil.h>
16
17#include "engine.h"
diff --git a/src/burn/stub/stub.cpp b/src/burn/stub/stub.cpp
new file mode 100644
index 00000000..0cb202e0
--- /dev/null
+++ b/src/burn/stub/stub.cpp
@@ -0,0 +1,106 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6static void CALLBACK BurnTraceError(
7 __in_z LPCSTR szFile,
8 __in int iLine,
9 __in REPORT_LEVEL rl,
10 __in UINT source,
11 __in HRESULT hrError,
12 __in_z __format_string LPCSTR szFormat,
13 __in va_list args
14 );
15
16int WINAPI wWinMain(
17 __in HINSTANCE hInstance,
18 __in_opt HINSTANCE /* hPrevInstance */,
19 __in_z_opt LPWSTR lpCmdLine,
20 __in int nCmdShow
21 )
22{
23 HRESULT hr = S_OK;
24 DWORD dwExitCode = 0;
25 LPWSTR sczPath = NULL;
26 HANDLE hEngineFile = INVALID_HANDLE_VALUE;
27
28 LPCWSTR rgsczSafelyLoadSystemDlls[] =
29 {
30 L"cabinet.dll", // required by Burn.
31 L"msi.dll", // required by Burn.
32 L"version.dll", // required by Burn.
33 L"wininet.dll", // required by Burn.
34
35 L"comres.dll", // required by CLSIDFromProgID() when loading clbcatq.dll.
36 L"clbcatq.dll", // required by CLSIDFromProgID() when loading msxml?.dll.
37
38 L"msasn1.dll", // required by DecryptFile() when loading crypt32.dll.
39 L"crypt32.dll", // required by DecryptFile() when loading feclient.dll.
40 L"feclient.dll", // unsafely loaded by DecryptFile().
41 };
42
43 DutilInitialize(&BurnTraceError);
44
45 // Best effort attempt to get our file handle as soon as possible.
46 hr = PathForCurrentProcess(&sczPath, NULL);
47 if (SUCCEEDED(hr))
48 {
49 hEngineFile = ::CreateFileW(sczPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
50 }
51
52 // If the engine is in the clean room, we'll do the unsafe initialization
53 // because some systems in Windows (namely GDI+) will fail when run in
54 // a process that protects against DLL hijacking. Since we know the clean
55 // room is in a clean folder and not subject to DLL hijacking we won't
56 // make ourselves perfectly secure so that we can load BAs that still
57 // depend on those parts of Windows that are insecure to DLL hijacking.
58 if (EngineInCleanRoom(lpCmdLine))
59 {
60 AppInitializeUnsafe();
61 }
62 else
63 {
64 AppInitialize(rgsczSafelyLoadSystemDlls, countof(rgsczSafelyLoadSystemDlls));
65 }
66
67 // call run
68 hr = EngineRun(hInstance, hEngineFile, lpCmdLine, nCmdShow, &dwExitCode);
69 ExitOnFailure(hr, "Failed to run application.");
70
71LExit:
72 ReleaseFileHandle(hEngineFile);
73 ReleaseStr(sczPath);
74
75 DutilUninitialize();
76
77 return FAILED(hr) ? (int)hr : (int)dwExitCode;
78}
79
80static void CALLBACK BurnTraceError(
81 __in_z LPCSTR /*szFile*/,
82 __in int /*iLine*/,
83 __in REPORT_LEVEL /*rl*/,
84 __in UINT source,
85 __in HRESULT hrError,
86 __in_z __format_string LPCSTR szFormat,
87 __in va_list args
88 )
89{
90 BOOL fLog = FALSE;
91
92 switch (source)
93 {
94 case DUTIL_SOURCE_DEFAULT:
95 fLog = TRUE;
96 break;
97 default:
98 fLog = REPORT_VERBOSE < LogGetLevel();
99 break;
100 }
101
102 if (fLog)
103 {
104 LogErrorStringArgs(hrError, szFormat, args);
105 }
106}
diff --git a/src/burn/stub/stub.ico b/src/burn/stub/stub.ico
new file mode 100644
index 00000000..c2e2717c
--- /dev/null
+++ b/src/burn/stub/stub.ico
Binary files differ
diff --git a/src/burn/stub/stub.nuspec b/src/burn/stub/stub.nuspec
new file mode 100644
index 00000000..968feff3
--- /dev/null
+++ b/src/burn/stub/stub.nuspec
@@ -0,0 +1,25 @@
1<?xml version="1.0"?>
2<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
3 <metadata minClientVersion="4.0">
4 <id>$id$</id>
5 <version>$version$</version>
6 <title>$title$</title>
7 <description>$description$</description>
8 <authors>$authors$</authors>
9 <license type="expression">MS-RL</license>
10 <requireLicenseAcceptance>false</requireLicenseAcceptance>
11 <copyright>$copyright$</copyright>
12 <projectUrl>$projectUrl$</projectUrl>
13 <repository type="$repositorytype$" url="$repositoryurl$" commit="$repositorycommit$" />
14 </metadata>
15
16 <files>
17 <file src="$projectFolder$$id$.props" target="buildTransitive" />
18 <file src="..\..\build\$configuration$\Win32\burn.exe" target="tools\x86" />
19 <file src="..\..\build\$configuration$\Win32\burn.pdb" target="tools\x86" />
20 <file src="..\..\build\$configuration$\x64\burn.exe" target="tools\x64" />
21 <file src="..\..\build\$configuration$\x64\burn.pdb" target="tools\x64" />
22 <file src="..\..\build\$configuration$\arm64\burn.exe" target="tools\arm64" />
23 <file src="..\..\build\$configuration$\arm64\burn.pdb" target="tools\arm64" />
24 </files>
25</package>
diff --git a/src/burn/stub/stub.rc b/src/burn/stub/stub.rc
new file mode 100644
index 00000000..80e1aac4
--- /dev/null
+++ b/src/burn/stub/stub.rc
@@ -0,0 +1,3 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
31 ICON "stub.ico"
diff --git a/src/burn/stub/stub.vcxproj b/src/burn/stub/stub.vcxproj
new file mode 100644
index 00000000..97972848
--- /dev/null
+++ b/src/burn/stub/stub.vcxproj
@@ -0,0 +1,120 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5 <Import Project="..\..\packages\WixToolset.DUtil.4.0.72\build\WixToolset.DUtil.props" Condition="Exists('..\..\packages\WixToolset.DUtil.4.0.72\build\WixToolset.DUtil.props')" />
6 <Import Project="..\..\packages\Microsoft.SourceLink.GitHub.1.0.0\build\Microsoft.SourceLink.GitHub.props" Condition="Exists('..\..\packages\Microsoft.SourceLink.GitHub.1.0.0\build\Microsoft.SourceLink.GitHub.props')" />
7 <Import Project="..\..\packages\Microsoft.SourceLink.Common.1.0.0\build\Microsoft.SourceLink.Common.props" Condition="Exists('..\..\packages\Microsoft.SourceLink.Common.1.0.0\build\Microsoft.SourceLink.Common.props')" />
8 <Import Project="..\..\packages\Microsoft.Build.Tasks.Git.1.0.0\build\Microsoft.Build.Tasks.Git.props" Condition="Exists('..\..\packages\Microsoft.Build.Tasks.Git.1.0.0\build\Microsoft.Build.Tasks.Git.props')" />
9
10 <ItemGroup Label="ProjectConfigurations">
11 <ProjectConfiguration Include="Debug|Win32">
12 <Configuration>Debug</Configuration>
13 <Platform>Win32</Platform>
14 </ProjectConfiguration>
15 <ProjectConfiguration Include="Release|Win32">
16 <Configuration>Release</Configuration>
17 <Platform>Win32</Platform>
18 </ProjectConfiguration>
19 <ProjectConfiguration Include="Debug|x64">
20 <Configuration>Debug</Configuration>
21 <Platform>x64</Platform>
22 </ProjectConfiguration>
23 <ProjectConfiguration Include="Release|x64">
24 <Configuration>Release</Configuration>
25 <Platform>x64</Platform>
26 </ProjectConfiguration>
27 <ProjectConfiguration Include="Debug|ARM64">
28 <Configuration>Debug</Configuration>
29 <Platform>ARM64</Platform>
30 </ProjectConfiguration>
31 <ProjectConfiguration Include="Release|ARM64">
32 <Configuration>Release</Configuration>
33 <Platform>ARM64</Platform>
34 </ProjectConfiguration>
35 </ItemGroup>
36
37 <PropertyGroup Label="Globals">
38 <ProjectGuid>{C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}</ProjectGuid>
39 <ConfigurationType>Application</ConfigurationType>
40 <ProjectSubSystem>Windows</ProjectSubSystem>
41 <TargetName>burn</TargetName>
42 <PlatformToolset>v142</PlatformToolset>
43 <CharacterSet>Unicode</CharacterSet>
44 <GenerateManifest>false</GenerateManifest>
45 <Description>Native component of WixToolset.Burn</Description>
46 <!-- NBGV properties -->
47 <AssemblyLanguage>1033</AssemblyLanguage>
48 <AssemblyProduct>Burn</AssemblyProduct>
49 <PackageId>WixToolset.Burn</PackageId>
50 <SignOutput>false</SignOutput>
51 </PropertyGroup>
52
53 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
54 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
55
56 <ImportGroup Label="ExtensionSettings">
57 </ImportGroup>
58
59 <ImportGroup Label="Shared">
60 <Import Project="..\..\packages\Microsoft.Build.Tasks.Git.1.0.0\build\Microsoft.Build.Tasks.Git.targets" Condition="Exists('..\..\packages\Microsoft.Build.Tasks.Git.1.0.0\build\Microsoft.Build.Tasks.Git.targets')" />
61 <Import Project="..\..\packages\Microsoft.SourceLink.Common.1.0.0\build\Microsoft.SourceLink.Common.targets" Condition="Exists('..\..\packages\Microsoft.SourceLink.Common.1.0.0\build\Microsoft.SourceLink.Common.targets')" />
62 <Import Project="..\..\packages\Microsoft.SourceLink.GitHub.1.0.0\build\Microsoft.SourceLink.GitHub.targets" Condition="Exists('..\..\packages\Microsoft.SourceLink.GitHub.1.0.0\build\Microsoft.SourceLink.GitHub.targets')" />
63 <Import Project="..\..\packages\Nerdbank.GitVersioning.3.3.37\build\Nerdbank.GitVersioning.targets" Condition="Exists('..\..\packages\Nerdbank.GitVersioning.3.3.37\build\Nerdbank.GitVersioning.targets')" />
64 </ImportGroup>
65
66 <PropertyGroup>
67 <ProjectAdditionalIncludeDirectories>$(ProjectDir)..\engine\inc</ProjectAdditionalIncludeDirectories>
68 <ProjectAdditionalLinkLibraries>cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;wuguid.lib;engine.res</ProjectAdditionalLinkLibraries>
69 </PropertyGroup>
70
71 <ItemDefinitionGroup>
72 <Link>
73 <SwapRunFromCD>true</SwapRunFromCD>
74 <SwapRunFromNET>true</SwapRunFromNET>
75 <DelayLoadDLLs>cabinet.dll;crypt32.dll;msi.dll;shlwapi.dll;version.dll;wininet.dll</DelayLoadDLLs>
76 </Link>
77 </ItemDefinitionGroup>
78
79 <ItemGroup>
80 <ClInclude Include="precomp.h" />
81 </ItemGroup>
82 <ItemGroup>
83 <ClCompile Include="precomp.cpp">
84 <PrecompiledHeader>Create</PrecompiledHeader>
85 </ClCompile>
86 <ClCompile Include="stub.cpp" />
87 <ClCompile Include="StubSection.cpp">
88 <!-- Workaround for VS2015 behavior change that omits the .wixburn section. -->
89 <RemoveUnreferencedCodeData>false</RemoveUnreferencedCodeData>
90 </ClCompile>
91 </ItemGroup>
92 <ItemGroup>
93 <ResourceCompile Include="stub.rc" />
94 </ItemGroup>
95
96 <ItemGroup>
97 <ProjectReference Include="..\engine\engine.vcxproj">
98 <Project>{8119537D-E1D9-6591-D51A-49768A2F9C37}</Project>
99 </ProjectReference>
100 </ItemGroup>
101
102 <ItemGroup>
103 <None Include="packages.config" />
104 </ItemGroup>
105
106 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
107 <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
108 <PropertyGroup>
109 <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText>
110 </PropertyGroup>
111 <Error Condition="!Exists('..\..\packages\Microsoft.Build.Tasks.Git.1.0.0\build\Microsoft.Build.Tasks.Git.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Build.Tasks.Git.1.0.0\build\Microsoft.Build.Tasks.Git.props'))" />
112 <Error Condition="!Exists('..\..\packages\Microsoft.Build.Tasks.Git.1.0.0\build\Microsoft.Build.Tasks.Git.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Build.Tasks.Git.1.0.0\build\Microsoft.Build.Tasks.Git.targets'))" />
113 <Error Condition="!Exists('..\..\packages\Microsoft.SourceLink.Common.1.0.0\build\Microsoft.SourceLink.Common.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.SourceLink.Common.1.0.0\build\Microsoft.SourceLink.Common.props'))" />
114 <Error Condition="!Exists('..\..\packages\Microsoft.SourceLink.Common.1.0.0\build\Microsoft.SourceLink.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.SourceLink.Common.1.0.0\build\Microsoft.SourceLink.Common.targets'))" />
115 <Error Condition="!Exists('..\..\packages\Microsoft.SourceLink.GitHub.1.0.0\build\Microsoft.SourceLink.GitHub.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.SourceLink.GitHub.1.0.0\build\Microsoft.SourceLink.GitHub.props'))" />
116 <Error Condition="!Exists('..\..\packages\Microsoft.SourceLink.GitHub.1.0.0\build\Microsoft.SourceLink.GitHub.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.SourceLink.GitHub.1.0.0\build\Microsoft.SourceLink.GitHub.targets'))" />
117 <Error Condition="!Exists('..\..\packages\Nerdbank.GitVersioning.3.3.37\build\Nerdbank.GitVersioning.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Nerdbank.GitVersioning.3.3.37\build\Nerdbank.GitVersioning.targets'))" />
118 <Error Condition="!Exists('..\..\packages\WixToolset.DUtil.4.0.72\build\WixToolset.DUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.DUtil.4.0.72\build\WixToolset.DUtil.props'))" />
119 </Target>
120</Project>
diff --git a/src/burn/test/BurnUnitTest/AssemblyInfo.cpp b/src/burn/test/BurnUnitTest/AssemblyInfo.cpp
new file mode 100644
index 00000000..0282b1b7
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/AssemblyInfo.cpp
@@ -0,0 +1,12 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5using namespace System::Reflection;
6using namespace System::Runtime::CompilerServices;
7using namespace System::Runtime::InteropServices;
8
9[assembly: AssemblyTitleAttribute("Windows Installer XML Burn unit tests")];
10[assembly: AssemblyDescriptionAttribute("Burn unit tests")];
11[assembly: AssemblyCultureAttribute("")];
12[assembly: ComVisible(false)];
diff --git a/src/burn/test/BurnUnitTest/BurnTestException.h b/src/burn/test/BurnUnitTest/BurnTestException.h
new file mode 100644
index 00000000..bd94b4fc
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/BurnTestException.h
@@ -0,0 +1,93 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5namespace Microsoft
6{
7namespace Tools
8{
9namespace WindowsInstallerXml
10{
11namespace Test
12{
13namespace Bootstrapper
14{
15 using namespace System;
16
17 public ref struct BurnTestException : public System::Exception
18 {
19 public:
20 BurnTestException(HRESULT error)
21 {
22 this->HResult = error;
23 }
24
25 BurnTestException(HRESULT error, String^ message)
26 : Exception(message)
27 {
28 this->HResult = error;
29 }
30
31 property Int32 ErrorCode
32 {
33 Int32 get()
34 {
35 return this->HResult;
36 }
37 }
38
39 };
40}
41}
42}
43}
44}
45
46// this class is used by __TestThrowOnFailure_Format() below to deallocate
47// the string created after the function call has returned
48class __TestThrowOnFailure_StringFree
49{
50 LPWSTR m_scz;
51
52public:
53 __TestThrowOnFailure_StringFree(LPWSTR scz)
54 {
55 m_scz = scz;
56 }
57
58 ~__TestThrowOnFailure_StringFree()
59 {
60 ReleaseStr(m_scz);
61 }
62
63 operator LPCWSTR()
64 {
65 return m_scz;
66 }
67};
68
69// used by the TestThrowOnFailure macros to format the error string and
70// return an LPCWSTR that can be used to initialize a System::String
71#pragma warning (push)
72#pragma warning (disable : 4793)
73inline __TestThrowOnFailure_StringFree __TestThrowOnFailure_Format(LPCWSTR wzFormat, ...)
74{
75 Assert(wzFormat && *wzFormat);
76
77 HRESULT hr = S_OK;
78 LPWSTR scz = NULL;
79 va_list args;
80
81 va_start(args, wzFormat);
82 hr = StrAllocFormattedArgs(&scz, wzFormat, args);
83 va_end(args);
84 ExitOnFailure(hr, "Failed to format message string.");
85
86LExit:
87 return scz;
88}
89#pragma warning (pop)
90
91#define TestThrowOnFailure(hr, s) if (FAILED(hr)) { throw gcnew Microsoft::Tools::WindowsInstallerXml::Test::Bootstrapper::BurnTestException(hr, gcnew System::String(s)); }
92#define TestThrowOnFailure1(hr, s, p) if (FAILED(hr)) { throw gcnew Microsoft::Tools::WindowsInstallerXml::Test::Bootstrapper::BurnTestException(hr, gcnew System::String(__TestThrowOnFailure_Format(s, p))); }
93#define TestThrowOnFailure2(hr, s, p1, p2) if (FAILED(hr)) { throw gcnew Microsoft::Tools::WindowsInstallerXml::Test::Bootstrapper::BurnTestException(hr, gcnew System::String(__TestThrowOnFailure_Format(s, p1, p2))); }
diff --git a/src/burn/test/BurnUnitTest/BurnTestFixture.h b/src/burn/test/BurnUnitTest/BurnTestFixture.h
new file mode 100644
index 00000000..103972ef
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/BurnTestFixture.h
@@ -0,0 +1,75 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5namespace Microsoft
6{
7namespace Tools
8{
9namespace WindowsInstallerXml
10{
11namespace Test
12{
13namespace Bootstrapper
14{
15 using namespace System;
16 using namespace WixBuildTools::TestSupport;
17
18 public ref class BurnTestFixture : IDisposable
19 {
20 public:
21 BurnTestFixture()
22 {
23 HRESULT hr = XmlInitialize();
24 TestThrowOnFailure(hr, L"Failed to initialize XML support.");
25
26 hr = RegInitialize();
27 TestThrowOnFailure(hr, L"Failed to initialize Regutil.");
28
29 hr = CrypInitialize();
30 TestThrowOnFailure(hr, L"Failed to initialize Cryputil.");
31
32 PlatformInitialize();
33
34 this->testDirectory = WixBuildTools::TestSupport::TestData::Get();
35
36 LogInitialize(::GetModuleHandleW(NULL));
37
38 LogSetLevel(REPORT_DEBUG, FALSE);
39
40 hr = LogOpen(NULL, L"BurnUnitTest", NULL, L"txt", FALSE, FALSE, NULL);
41 TestThrowOnFailure(hr, L"Failed to open log.");
42 }
43
44 ~BurnTestFixture()
45 {
46 CrypUninitialize();
47 XmlUninitialize();
48 RegUninitialize();
49 LogUninitialize(FALSE);
50 }
51
52 property String^ DataDirectory
53 {
54 String^ get()
55 {
56 return this->testDirectory;
57 }
58 }
59
60 property String^ TestDirectory
61 {
62 String^ get()
63 {
64 return this->testDirectory;
65 }
66 }
67
68 private:
69 String^ testDirectory;
70 };
71}
72}
73}
74}
75}
diff --git a/src/burn/test/BurnUnitTest/BurnUnitTest.h b/src/burn/test/BurnUnitTest/BurnUnitTest.h
new file mode 100644
index 00000000..ed1d2956
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/BurnUnitTest.h
@@ -0,0 +1,48 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5namespace Microsoft
6{
7namespace Tools
8{
9namespace WindowsInstallerXml
10{
11namespace Test
12{
13namespace Bootstrapper
14{
15 using namespace System;
16 using namespace Xunit;
17
18 [CollectionDefinition("Burn")]
19 public ref class BurnCollectionDefinition : ICollectionFixture<BurnTestFixture^>
20 {
21
22 };
23
24 [Collection("Burn")]
25 public ref class BurnUnitTest
26 {
27 public:
28 BurnUnitTest(BurnTestFixture^ fixture)
29 {
30 this->testContext = fixture;
31 }
32
33 property BurnTestFixture^ TestContext
34 {
35 BurnTestFixture^ get()
36 {
37 return this->testContext;
38 }
39 }
40
41 private:
42 BurnTestFixture^ testContext;
43 };
44}
45}
46}
47}
48}
diff --git a/src/burn/test/BurnUnitTest/BurnUnitTest.rc b/src/burn/test/BurnUnitTest/BurnUnitTest.rc
new file mode 100644
index 00000000..3a815db2
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/BurnUnitTest.rc
@@ -0,0 +1,6 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#define VER_APP
4#define VER_ORIGINAL_FILENAME "BurnUnitTest.dll"
5#define VER_INTERNAL_NAME "setup"
6#define VER_FILE_DESCRIPTION "WiX Toolset Bootstrapper unit tests"
diff --git a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj
new file mode 100644
index 00000000..33c8ed6c
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj
@@ -0,0 +1,109 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5 <Import Project="..\..\..\packages\WixToolset.BootstrapperCore.Native.4.0.57\build\WixToolset.BootstrapperCore.Native.props" Condition="Exists('..\..\..\packages\WixToolset.BootstrapperCore.Native.4.0.57\build\WixToolset.BootstrapperCore.Native.props')" />
6 <Import Project="..\..\..\packages\WixToolset.DUtil.4.0.70\build\WixToolset.DUtil.props" Condition="Exists('..\..\..\packages\WixToolset.DUtil.4.0.70\build\WixToolset.DUtil.props')" />
7 <Import Project="..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\build\WixBuildTools.TestSupport.Native.props" Condition="Exists('..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\build\WixBuildTools.TestSupport.Native.props')" />
8 <ItemGroup Label="ProjectConfigurations">
9 <ProjectConfiguration Include="Debug|ARM64">
10 <Configuration>Debug</Configuration>
11 <Platform>ARM64</Platform>
12 </ProjectConfiguration>
13 <ProjectConfiguration Include="Debug|Win32">
14 <Configuration>Debug</Configuration>
15 <Platform>Win32</Platform>
16 </ProjectConfiguration>
17 <ProjectConfiguration Include="Release|ARM64">
18 <Configuration>Release</Configuration>
19 <Platform>ARM64</Platform>
20 </ProjectConfiguration>
21 <ProjectConfiguration Include="Release|Win32">
22 <Configuration>Release</Configuration>
23 <Platform>Win32</Platform>
24 </ProjectConfiguration>
25 </ItemGroup>
26
27 <PropertyGroup Label="Globals">
28 <ProjectTypes>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}</ProjectTypes>
29 <ProjectGuid>{9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}</ProjectGuid>
30 <RootNamespace>UnitTest</RootNamespace>
31 <Keyword>ManagedCProj</Keyword>
32 <ConfigurationType>DynamicLibrary</ConfigurationType>
33 <CharacterSet>Unicode</CharacterSet>
34 <CLRSupport>true</CLRSupport>
35 <SignOutput>false</SignOutput>
36 </PropertyGroup>
37
38 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
39 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
40
41 <PropertyGroup>
42 <ProjectAdditionalIncludeDirectories Condition=" '$(DirectReference)'=='true' ">$(ProjectDir)..\..\..\..\balutil\src\WixToolset.BootstrapperCore.Native\inc</ProjectAdditionalIncludeDirectories>
43 <ProjectAdditionalIncludeDirectories>$(ProjectAdditionalIncludeDirectories);..\..\engine</ProjectAdditionalIncludeDirectories>
44 <ProjectAdditionalLinkLibraries>cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib</ProjectAdditionalLinkLibraries>
45 </PropertyGroup>
46
47 <ItemGroup>
48 <ClCompile Include="AssemblyInfo.cpp" />
49 <ClCompile Include="CacheTest.cpp" />
50 <ClCompile Include="ElevationTest.cpp" />
51 <ClCompile Include="ManifestHelpers.cpp" />
52 <ClCompile Include="ManifestTest.cpp" />
53 <ClCompile Include="PlanTest.cpp" />
54 <ClCompile Include="precomp.cpp">
55 <PrecompiledHeader>Create</PrecompiledHeader>
56 <!-- Warnings from referencing netstandard dlls -->
57 <DisableSpecificWarnings>4564;4691</DisableSpecificWarnings>
58 </ClCompile>
59 <ClCompile Include="RegistrationTest.cpp" />
60 <ClCompile Include="SearchTest.cpp" />
61 <ClCompile Include="VariableHelpers.cpp" />
62 <ClCompile Include="VariableTest.cpp" />
63 <ClCompile Include="VariantTest.cpp" />
64 </ItemGroup>
65 <ItemGroup>
66 <ClInclude Include="BurnTestException.h" />
67 <ClInclude Include="BurnTestFixture.h" />
68 <ClInclude Include="BurnUnitTest.h" />
69 <ClInclude Include="ManifestHelpers.h" />
70 <ClInclude Include="precomp.h" />
71 <ClInclude Include="VariableHelpers.h" />
72 </ItemGroup>
73 <ItemGroup>
74 <None Include="packages.config" />
75 <ResourceCompile Include="BurnUnitTest.rc" />
76 </ItemGroup>
77 <ItemGroup>
78 <None Include="TestData\CacheTest\CacheSignatureTest.File" CopyToOutputDirectory="PreserveNewest" />
79 <None Include="TestData\PlanTest\BasicFunctionality_BundleA_manifest.xml" CopyToOutputDirectory="PreserveNewest" />
80 <None Include="TestData\PlanTest\MsiTransaction_BundleAv1_manifest.xml" CopyToOutputDirectory="PreserveNewest" />
81 <None Include="TestData\PlanTest\Slipstream_BundleA_manifest.xml" CopyToOutputDirectory="PreserveNewest" />
82 </ItemGroup>
83 <ItemGroup>
84 <Reference Include="System" />
85 <Reference Include="System.Core" />
86 <Reference Include="WixBuildTools.TestSupport">
87 <HintPath>..\..\..\packages\WixBuildTools.TestSupport.4.0.50\lib\net472\WixBuildTools.TestSupport.dll</HintPath>
88 </Reference>
89 <Reference Include="WixBuildTools.TestSupport.Native">
90 <HintPath>..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.50\lib\net472\WixBuildTools.TestSupport.Native.dll</HintPath>
91 </Reference>
92 </ItemGroup>
93 <ItemGroup>
94 <ProjectReference Include="..\..\engine\engine.vcxproj">
95 <Project>{8119537D-E1D9-6591-D51A-49770A2F9C37}</Project>
96 </ProjectReference>
97 </ItemGroup>
98 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
99 <Import Project="..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.50\build\WixBuildTools.TestSupport.Native.targets" Condition="Exists('..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.50\build\WixBuildTools.TestSupport.Native.targets')" />
100 <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
101 <PropertyGroup>
102 <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
103 </PropertyGroup>
104 <Error Condition="!Exists('..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\build\WixBuildTools.TestSupport.Native.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\build\WixBuildTools.TestSupport.Native.props'))" />
105 <Error Condition="!Exists('..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\build\WixBuildTools.TestSupport.Native.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\build\WixBuildTools.TestSupport.Native.targets'))" />
106 <Error Condition="!Exists('..\..\..\packages\WixToolset.DUtil.4.0.70\build\WixToolset.DUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\WixToolset.DUtil.4.0.70\build\WixToolset.DUtil.props'))" />
107 <Error Condition="!Exists('..\..\..\packages\WixToolset.BootstrapperCore.Native.4.0.57\build\WixToolset.BootstrapperCore.Native.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\WixToolset.BootstrapperCore.Native.4.0.57\build\WixToolset.BootstrapperCore.Native.props'))" />
108 </Target>
109</Project>
diff --git a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters
new file mode 100644
index 00000000..f9461f53
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters
@@ -0,0 +1,80 @@
1<?xml version="1.0" encoding="utf-8"?>
2<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 <ItemGroup>
4 <Filter Include="Source Files">
5 <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
6 <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
7 </Filter>
8 <Filter Include="Header Files">
9 <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
10 <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
11 </Filter>
12 <Filter Include="Resource Files">
13 <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
14 <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
15 </Filter>
16 </ItemGroup>
17 <ItemGroup>
18 <ClCompile Include="AssemblyInfo.cpp">
19 <Filter>Source Files</Filter>
20 </ClCompile>
21 <ClCompile Include="CacheTest.cpp">
22 <Filter>Source Files</Filter>
23 </ClCompile>
24 <ClCompile Include="ElevationTest.cpp">
25 <Filter>Source Files</Filter>
26 </ClCompile>
27 <ClCompile Include="ManifestHelpers.cpp">
28 <Filter>Source Files</Filter>
29 </ClCompile>
30 <ClCompile Include="ManifestTest.cpp">
31 <Filter>Source Files</Filter>
32 </ClCompile>
33 <ClCompile Include="PlanTest.cpp">
34 <Filter>Source Files</Filter>
35 </ClCompile>
36 <ClCompile Include="precomp.cpp">
37 <Filter>Source Files</Filter>
38 </ClCompile>
39 <ClCompile Include="RegistrationTest.cpp">
40 <Filter>Source Files</Filter>
41 </ClCompile>
42 <ClCompile Include="SearchTest.cpp">
43 <Filter>Source Files</Filter>
44 </ClCompile>
45 <ClCompile Include="VariableHelpers.cpp">
46 <Filter>Source Files</Filter>
47 </ClCompile>
48 <ClCompile Include="VariableTest.cpp">
49 <Filter>Source Files</Filter>
50 </ClCompile>
51 <ClCompile Include="VariantTest.cpp">
52 <Filter>Source Files</Filter>
53 </ClCompile>
54 </ItemGroup>
55 <ItemGroup>
56 <ClInclude Include="BurnTestException.h">
57 <Filter>Header Files</Filter>
58 </ClInclude>
59 <ClInclude Include="ManifestHelpers.h">
60 <Filter>Header Files</Filter>
61 </ClInclude>
62 <ClInclude Include="precomp.h">
63 <Filter>Header Files</Filter>
64 </ClInclude>
65 <ClInclude Include="VariableHelpers.h">
66 <Filter>Header Files</Filter>
67 </ClInclude>
68 <ClInclude Include="BurnUnitTest.h">
69 <Filter>Header Files</Filter>
70 </ClInclude>
71 <ClInclude Include="BurnTestFixture.h">
72 <Filter>Header Files</Filter>
73 </ClInclude>
74 </ItemGroup>
75 <ItemGroup>
76 <ResourceCompile Include="BurnUnitTest.rc">
77 <Filter>Resource Files</Filter>
78 </ResourceCompile>
79 </ItemGroup>
80</Project> \ No newline at end of file
diff --git a/src/burn/test/BurnUnitTest/CacheTest.cpp b/src/burn/test/BurnUnitTest/CacheTest.cpp
new file mode 100644
index 00000000..d0cc237f
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/CacheTest.cpp
@@ -0,0 +1,119 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5static HRESULT CALLBACK CacheTestEventRoutine(
6 __in BURN_CACHE_MESSAGE* pMessage,
7 __in LPVOID pvContext
8 );
9
10static DWORD CALLBACK CacheTestProgressRoutine(
11 __in LARGE_INTEGER TotalFileSize,
12 __in LARGE_INTEGER TotalBytesTransferred,
13 __in LARGE_INTEGER StreamSize,
14 __in LARGE_INTEGER StreamBytesTransferred,
15 __in DWORD dwStreamNumber,
16 __in DWORD dwCallbackReason,
17 __in HANDLE hSourceFile,
18 __in HANDLE hDestinationFile,
19 __in_opt LPVOID lpData
20 );
21
22typedef struct _CACHE_TEST_CONTEXT
23{
24} CACHE_TEST_CONTEXT;
25
26namespace Microsoft
27{
28namespace Tools
29{
30namespace WindowsInstallerXml
31{
32namespace Test
33{
34namespace Bootstrapper
35{
36 using namespace System;
37 using namespace System::IO;
38 using namespace Xunit;
39
40 public ref class CacheTest : BurnUnitTest
41 {
42 public:
43 CacheTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture)
44 {
45 }
46
47 [Fact]
48 void CacheSignatureTest()
49 {
50 HRESULT hr = S_OK;
51 BURN_PACKAGE package = { };
52 BURN_PAYLOAD payload = { };
53 LPWSTR sczPayloadPath = NULL;
54 BYTE* pb = NULL;
55 DWORD cb = NULL;
56 CACHE_TEST_CONTEXT context = { };
57
58 try
59 {
60 pin_ptr<const wchar_t> dataDirectory = PtrToStringChars(this->TestContext->TestDirectory);
61 hr = PathConcat(dataDirectory, L"TestData\\CacheTest\\CacheSignatureTest.File", &sczPayloadPath);
62 Assert::True(S_OK == hr, "Failed to get path to test file.");
63 Assert::True(FileExistsEx(sczPayloadPath, NULL), "Test file does not exist.");
64
65 hr = StrAllocHexDecode(L"25e61cd83485062b70713aebddd3fe4992826cb121466fddc8de3eacb1e42f39d4bdd8455d95eec8c9529ced4c0296ab861931fe2c86df2f2b4e8d259a6d9223", &pb, &cb);
66 Assert::Equal(S_OK, hr);
67
68 package.fPerMachine = FALSE;
69 package.sczCacheId = L"Bootstrapper.CacheTest.CacheSignatureTest";
70 payload.sczKey = L"CacheSignatureTest.PayloadKey";
71 payload.sczFilePath = L"CacheSignatureTest.File";
72 payload.pbHash = pb;
73 payload.cbHash = cb;
74
75 hr = CacheCompletePayload(package.fPerMachine, &payload, package.sczCacheId, sczPayloadPath, FALSE, CacheTestEventRoutine, CacheTestProgressRoutine, &context);
76 Assert::Equal(S_OK, hr);
77 }
78 finally
79 {
80 ReleaseMem(pb);
81 ReleaseStr(sczPayloadPath);
82
83 String^ filePath = Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), "Package Cache\\Bootstrapper.CacheTest.CacheSignatureTest\\CacheSignatureTest.File");
84 if (File::Exists(filePath))
85 {
86 File::SetAttributes(filePath, FileAttributes::Normal);
87 File::Delete(filePath);
88 }
89 }
90 }
91 };
92}
93}
94}
95}
96}
97
98static HRESULT CALLBACK CacheTestEventRoutine(
99 __in BURN_CACHE_MESSAGE* /*pMessage*/,
100 __in LPVOID /*pvContext*/
101 )
102{
103 return S_OK;
104}
105
106static DWORD CALLBACK CacheTestProgressRoutine(
107 __in LARGE_INTEGER /*TotalFileSize*/,
108 __in LARGE_INTEGER /*TotalBytesTransferred*/,
109 __in LARGE_INTEGER /*StreamSize*/,
110 __in LARGE_INTEGER /*StreamBytesTransferred*/,
111 __in DWORD /*dwStreamNumber*/,
112 __in DWORD /*dwCallbackReason*/,
113 __in HANDLE /*hSourceFile*/,
114 __in HANDLE /*hDestinationFile*/,
115 __in_opt LPVOID /*lpData*/
116 )
117{
118 return PROGRESS_QUIET;
119}
diff --git a/src/burn/test/BurnUnitTest/ElevationTest.cpp b/src/burn/test/BurnUnitTest/ElevationTest.cpp
new file mode 100644
index 00000000..3d144128
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/ElevationTest.cpp
@@ -0,0 +1,221 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6const DWORD TEST_CHILD_SENT_MESSAGE_ID = 0xFFFE;
7const DWORD TEST_PARENT_SENT_MESSAGE_ID = 0xFFFF;
8const HRESULT S_TEST_SUCCEEDED = 0x3133;
9const char TEST_MESSAGE_DATA[] = "{94949868-7EAE-4ac5-BEAC-AFCA2821DE01}";
10
11
12static BOOL STDAPICALLTYPE ElevateTest_ShellExecuteExW(
13 __inout LPSHELLEXECUTEINFOW lpExecInfo
14 );
15static DWORD CALLBACK ElevateTest_ThreadProc(
16 __in LPVOID lpThreadParameter
17 );
18static HRESULT ProcessParentMessages(
19 __in BURN_PIPE_MESSAGE* pMsg,
20 __in_opt LPVOID pvContext,
21 __out DWORD* pdwResult
22 );
23static HRESULT ProcessChildMessages(
24 __in BURN_PIPE_MESSAGE* pMsg,
25 __in_opt LPVOID pvContext,
26 __out DWORD* pdwResult
27 );
28
29namespace Microsoft
30{
31namespace Tools
32{
33namespace WindowsInstallerXml
34{
35namespace Test
36{
37namespace Bootstrapper
38{
39 using namespace System;
40 using namespace System::IO;
41 using namespace System::Threading;
42 using namespace Xunit;
43
44 public ref class ElevationTest : BurnUnitTest
45 {
46 public:
47 ElevationTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture)
48 {
49 }
50
51 [Fact]
52 void ElevateTest()
53 {
54 HRESULT hr = S_OK;
55 BURN_PIPE_CONNECTION connection = { };
56 HANDLE hEvent = NULL;
57 DWORD dwResult = S_OK;
58 try
59 {
60 ShelFunctionOverride(ElevateTest_ShellExecuteExW);
61
62 PipeConnectionInitialize(&connection);
63
64 //
65 // per-user side setup
66 //
67 hr = PipeCreateNameAndSecret(&connection.sczName, &connection.sczSecret);
68 TestThrowOnFailure(hr, L"Failed to create connection name and secret.");
69
70 hr = PipeCreatePipes(&connection, TRUE, &hEvent);
71 TestThrowOnFailure(hr, L"Failed to create pipes.");
72
73 hr = PipeLaunchChildProcess(L"tests\\ignore\\this\\path\\to\\burn.exe", &connection, TRUE, NULL);
74 TestThrowOnFailure(hr, L"Failed to create elevated process.");
75
76 hr = PipeWaitForChildConnect(&connection);
77 TestThrowOnFailure(hr, L"Failed to wait for child process to connect.");
78
79 // post execute message
80 hr = PipeSendMessage(connection.hPipe, TEST_PARENT_SENT_MESSAGE_ID, NULL, 0, ProcessParentMessages, NULL, &dwResult);
81 TestThrowOnFailure(hr, "Failed to post execute message to per-machine process.");
82
83 //
84 // initiate termination
85 //
86 hr = PipeTerminateChildProcess(&connection, 666, FALSE);
87 TestThrowOnFailure(hr, L"Failed to terminate elevated process.");
88
89 // check flags
90 Assert::Equal(S_TEST_SUCCEEDED, (HRESULT)dwResult);
91 }
92 finally
93 {
94 PipeConnectionUninitialize(&connection);
95 ReleaseHandle(hEvent);
96 }
97 }
98 };
99}
100}
101}
102}
103}
104
105
106static BOOL STDAPICALLTYPE ElevateTest_ShellExecuteExW(
107 __inout LPSHELLEXECUTEINFOW lpExecInfo
108 )
109{
110 HRESULT hr = S_OK;
111 LPWSTR scz = NULL;
112
113 hr = StrAllocString(&scz, lpExecInfo->lpParameters, 0);
114 ExitOnFailure(hr, "Failed to copy arguments.");
115
116 // Pretend this thread is the elevated process.
117 lpExecInfo->hProcess = ::CreateThread(NULL, 0, ElevateTest_ThreadProc, scz, 0, NULL);
118 ExitOnNullWithLastError(lpExecInfo->hProcess, hr, "Failed to create thread.");
119 scz = NULL;
120
121LExit:
122 ReleaseStr(scz);
123
124 return SUCCEEDED(hr);
125}
126
127static DWORD CALLBACK ElevateTest_ThreadProc(
128 __in LPVOID lpThreadParameter
129 )
130{
131 HRESULT hr = S_OK;
132 LPWSTR sczArguments = (LPWSTR)lpThreadParameter;
133 BURN_PIPE_CONNECTION connection = { };
134 BURN_PIPE_RESULT result = { };
135
136 PipeConnectionInitialize(&connection);
137
138 StrAlloc(&connection.sczName, MAX_PATH);
139 StrAlloc(&connection.sczSecret, MAX_PATH);
140
141 // parse command line arguments
142 if (3 != swscanf_s(sczArguments, L"-q -burn.elevated %s %s %u", connection.sczName, MAX_PATH, connection.sczSecret, MAX_PATH, &connection.dwProcessId))
143 {
144 hr = E_INVALIDARG;
145 ExitOnFailure(hr, "Failed to parse argument string.");
146 }
147
148 // set up connection with per-user process
149 hr = PipeChildConnect(&connection, TRUE);
150 ExitOnFailure(hr, "Failed to connect to per-user process.");
151
152 // pump messages
153 hr = PipePumpMessages(connection.hPipe, ProcessChildMessages, static_cast<LPVOID>(connection.hPipe), &result);
154 ExitOnFailure(hr, "Failed while pumping messages in child 'process'.");
155
156LExit:
157 PipeConnectionUninitialize(&connection);
158 ReleaseStr(sczArguments);
159
160 return FAILED(hr) ? (DWORD)hr : result.dwResult;
161}
162
163static HRESULT ProcessParentMessages(
164 __in BURN_PIPE_MESSAGE* pMsg,
165 __in_opt LPVOID /*pvContext*/,
166 __out DWORD* pdwResult
167 )
168{
169 HRESULT hr = S_OK;
170 HRESULT hrResult = E_INVALIDDATA;
171
172 // Process the message.
173 switch (pMsg->dwMessage)
174 {
175 case TEST_CHILD_SENT_MESSAGE_ID:
176 if (sizeof(TEST_MESSAGE_DATA) == pMsg->cbData && 0 == memcmp(TEST_MESSAGE_DATA, pMsg->pvData, sizeof(TEST_MESSAGE_DATA)))
177 {
178 hrResult = S_TEST_SUCCEEDED;
179 }
180 break;
181
182 default:
183 hr = E_INVALIDARG;
184 ExitOnRootFailure(hr, "Unexpected elevated message sent to parent process, msg: %u", pMsg->dwMessage);
185 }
186
187 *pdwResult = static_cast<DWORD>(hrResult);
188
189LExit:
190 return hr;
191}
192
193static HRESULT ProcessChildMessages(
194 __in BURN_PIPE_MESSAGE* pMsg,
195 __in_opt LPVOID pvContext,
196 __out DWORD* pdwResult
197 )
198{
199 HRESULT hr = S_OK;
200 HANDLE hPipe = static_cast<HANDLE>(pvContext);
201 DWORD dwResult = 0;
202
203 // Process the message.
204 switch (pMsg->dwMessage)
205 {
206 case TEST_PARENT_SENT_MESSAGE_ID:
207 // send test message
208 hr = PipeSendMessage(hPipe, TEST_CHILD_SENT_MESSAGE_ID, (LPVOID)TEST_MESSAGE_DATA, sizeof(TEST_MESSAGE_DATA), NULL, NULL, &dwResult);
209 ExitOnFailure(hr, "Failed to send message to per-machine process.");
210 break;
211
212 default:
213 hr = E_INVALIDARG;
214 ExitOnRootFailure(hr, "Unexpected elevated message sent to child process, msg: %u", pMsg->dwMessage);
215 }
216
217 *pdwResult = dwResult;
218
219LExit:
220 return hr;
221}
diff --git a/src/burn/test/BurnUnitTest/ManifestHelpers.cpp b/src/burn/test/BurnUnitTest/ManifestHelpers.cpp
new file mode 100644
index 00000000..96d5fab4
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/ManifestHelpers.cpp
@@ -0,0 +1,41 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6using namespace System;
7using namespace Xunit;
8
9
10namespace Microsoft
11{
12namespace Tools
13{
14namespace WindowsInstallerXml
15{
16namespace Test
17{
18namespace Bootstrapper
19{
20 void LoadBundleXmlHelper(LPCWSTR wzDocument, IXMLDOMElement** ppixeBundle)
21 {
22 HRESULT hr = S_OK;
23 IXMLDOMDocument* pixdDocument = NULL;
24 try
25 {
26 hr = XmlLoadDocument(wzDocument, &pixdDocument);
27 TestThrowOnFailure(hr, L"Failed to load XML document.");
28
29 hr = pixdDocument->get_documentElement(ppixeBundle);
30 TestThrowOnFailure(hr, L"Failed to get bundle element.");
31 }
32 finally
33 {
34 ReleaseObject(pixdDocument);
35 }
36 }
37}
38}
39}
40}
41}
diff --git a/src/burn/test/BurnUnitTest/ManifestHelpers.h b/src/burn/test/BurnUnitTest/ManifestHelpers.h
new file mode 100644
index 00000000..e3e57555
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/ManifestHelpers.h
@@ -0,0 +1,24 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5namespace Microsoft
6{
7namespace Tools
8{
9namespace WindowsInstallerXml
10{
11namespace Test
12{
13namespace Bootstrapper
14{
15
16
17void LoadBundleXmlHelper(LPCWSTR wzDocument, IXMLDOMElement** ppixeBundle);
18
19
20}
21}
22}
23}
24}
diff --git a/src/burn/test/BurnUnitTest/ManifestTest.cpp b/src/burn/test/BurnUnitTest/ManifestTest.cpp
new file mode 100644
index 00000000..963be156
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/ManifestTest.cpp
@@ -0,0 +1,62 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5namespace Microsoft
6{
7namespace Tools
8{
9namespace WindowsInstallerXml
10{
11namespace Test
12{
13namespace Bootstrapper
14{
15 using namespace System;
16 using namespace Xunit;
17
18 public ref class ManifestTest : BurnUnitTest
19 {
20 public:
21 ManifestTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture)
22 {
23 }
24
25 [Fact]
26 void ManifestLoadXmlTest()
27 {
28 HRESULT hr = S_OK;
29 BURN_ENGINE_STATE engineState = { };
30 try
31 {
32 LPCSTR szDocument =
33 "<Bundle>"
34 " <UX UxDllPayloadId='ux.dll'>"
35 " <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' Hash='000000000000' />"
36 " </UX>"
37 " <Registration Id='{D54F896D-1952-43e6-9C67-B5652240618C}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no' />"
38 " <Variable Id='Variable1' Type='numeric' Value='1' Hidden='no' Persisted='no' />"
39 " <RegistrySearch Id='Search1' Type='exists' Root='HKLM' Key='SOFTWARE\\Microsoft' Variable='Variable1' Condition='0' />"
40 "</Bundle>";
41
42 hr = VariableInitialize(&engineState.variables);
43 TestThrowOnFailure(hr, L"Failed to initialize variables.");
44
45 // load manifest from XML
46 hr = ManifestLoadXmlFromBuffer((BYTE*)szDocument, lstrlenA(szDocument), &engineState);
47 TestThrowOnFailure(hr, L"Failed to parse searches from XML.");
48
49 // check variable values
50 Assert::True(VariableExistsHelper(&engineState.variables, L"Variable1"));
51 }
52 finally
53 {
54 //CoreUninitialize(&engineState);
55 }
56 }
57 };
58}
59}
60}
61}
62}
diff --git a/src/burn/test/BurnUnitTest/PlanTest.cpp b/src/burn/test/BurnUnitTest/PlanTest.cpp
new file mode 100644
index 00000000..a7c1d83c
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/PlanTest.cpp
@@ -0,0 +1,1473 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5static HRESULT WINAPI PlanTestBAProc(
6 __in BOOTSTRAPPER_APPLICATION_MESSAGE message,
7 __in const LPVOID pvArgs,
8 __inout LPVOID pvResults,
9 __in_opt LPVOID pvContext
10 );
11
12static LPCWSTR wzMsiTransactionManifestFileName = L"MsiTransaction_BundleAv1_manifest.xml";
13static LPCWSTR wzSingleMsiManifestFileName = L"BasicFunctionality_BundleA_manifest.xml";
14static LPCWSTR wzSlipstreamManifestFileName = L"Slipstream_BundleA_manifest.xml";
15
16namespace Microsoft
17{
18namespace Tools
19{
20namespace WindowsInstallerXml
21{
22namespace Test
23{
24namespace Bootstrapper
25{
26 using namespace System;
27 using namespace Xunit;
28
29 public ref class PlanTest : BurnUnitTest
30 {
31 public:
32 PlanTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture)
33 {
34 }
35
36 [Fact]
37 void MsiTransactionInstallTest()
38 {
39 HRESULT hr = S_OK;
40 BURN_ENGINE_STATE engineState = { };
41 BURN_ENGINE_STATE* pEngineState = &engineState;
42 BURN_PLAN* pPlan = &engineState.plan;
43
44 InitializeEngineStateForCorePlan(wzMsiTransactionManifestFileName, pEngineState);
45 DetectPackagesAsAbsent(pEngineState);
46 DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"1.0.0.0");
47
48 hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL);
49 NativeAssert::Succeeded(hr, "CorePlan failed");
50
51 Assert::Equal<DWORD>(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action);
52 Assert::Equal<BOOL>(TRUE, pPlan->fPerMachine);
53 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
54
55 BOOL fRollback = FALSE;
56 DWORD dwIndex = 0;
57 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1);
58 ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA");
59 ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++);
60 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 9);
61 ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageB");
62 ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++);
63 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 14);
64 ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageC");
65 ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++);
66 Assert::Equal(dwIndex, pPlan->cCacheActions);
67
68 fRollback = TRUE;
69 dwIndex = 0;
70 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1);
71 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
72
73 Assert::Equal(107082ull, pPlan->qwEstimatedSize);
74 Assert::Equal(506145ull, pPlan->qwCacheSizeTotal);
75
76 fRollback = FALSE;
77 dwIndex = 0;
78 DWORD dwExecuteCheckpointId = 2;
79 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
80 ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent);
81 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
82 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
83 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER);
84 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
85 ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0);
86 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
87 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER);
88 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
89 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
90 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE);
91 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
92 ValidateExecuteBeginMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ");
93 ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[5].syncpoint.hEvent);
94 dwExecuteCheckpointId += 1; // cache checkpoints
95 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
96 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
97 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_REGISTER);
98 ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0);
99 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
100 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER);
101 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
102 ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[8].syncpoint.hEvent);
103 dwExecuteCheckpointId += 1; // cache checkpoints
104 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
105 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
106 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_REGISTER);
107 ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0);
108 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
109 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER);
110 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
111 ValidateExecuteCommitMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ");
112 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
113 ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL);
114 Assert::Equal(dwIndex, pPlan->cExecuteActions);
115
116 fRollback = TRUE;
117 dwIndex = 0;
118 dwExecuteCheckpointId = 2;
119 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
120 ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA");
121 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
122 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER);
123 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
124 ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0);
125 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
126 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER);
127 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
128 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
129 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
130 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE);
131 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
132 ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageB");
133 dwExecuteCheckpointId += 1; // cache checkpoints
134 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
135 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_UNREGISTER);
136 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
137 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER);
138 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
139 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
140 ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageC");
141 dwExecuteCheckpointId += 1; // cache checkpoints
142 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
143 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_UNREGISTER);
144 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
145 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER);
146 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
147 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
148 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
149 ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL);
150 Assert::Equal(dwIndex, pPlan->cRollbackActions);
151
152 Assert::Equal(4ul, pPlan->cExecutePackagesTotal);
153 Assert::Equal(7ul, pPlan->cOverallProgressTicksTotal);
154
155 dwIndex = 0;
156 Assert::Equal(dwIndex, pPlan->cCleanActions);
157
158 UINT uIndex = 0;
159 ValidatePlannedProvider(pPlan, uIndex++, L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", NULL);
160 Assert::Equal(uIndex, pPlan->cPlannedProviders);
161
162 Assert::Equal(3ul, pEngineState->packages.cPackages);
163 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT);
164 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT);
165 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT);
166 }
167
168 [Fact]
169 void MsiTransactionUninstallTest()
170 {
171 HRESULT hr = S_OK;
172 BURN_ENGINE_STATE engineState = { };
173 BURN_ENGINE_STATE* pEngineState = &engineState;
174 BURN_PLAN* pPlan = &engineState.plan;
175
176 InitializeEngineStateForCorePlan(wzMsiTransactionManifestFileName, pEngineState);
177 DetectPackagesAsPresentAndCached(pEngineState);
178
179 hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL);
180 NativeAssert::Succeeded(hr, "CorePlan failed");
181
182 Assert::Equal<DWORD>(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action);
183 Assert::Equal<BOOL>(TRUE, pPlan->fPerMachine);
184 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
185
186 BOOL fRollback = FALSE;
187 DWORD dwIndex = 0;
188 Assert::Equal(dwIndex, pPlan->cCacheActions);
189
190 fRollback = TRUE;
191 dwIndex = 0;
192 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
193
194 Assert::Equal(0ull, pPlan->qwEstimatedSize);
195 Assert::Equal(0ull, pPlan->qwCacheSizeTotal);
196
197 fRollback = FALSE;
198 dwIndex = 0;
199 DWORD dwExecuteCheckpointId = 1;
200 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE);
201 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
202 ValidateExecuteBeginMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ");
203 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
204 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER);
205 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
206 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_UNREGISTER);
207 ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0);
208 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
209 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
210 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER);
211 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
212 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_UNREGISTER);
213 ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0);
214 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
215 ValidateExecuteCommitMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ");
216 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
217 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
218 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
219 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER);
220 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
221 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER);
222 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
223 ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0);
224 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
225 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
226 Assert::Equal(dwIndex, pPlan->cExecuteActions);
227
228 fRollback = TRUE;
229 dwIndex = 0;
230 dwExecuteCheckpointId = 1;
231 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE);
232 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
233 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER);
234 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
235 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_REGISTER);
236 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
237 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
238 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER);
239 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
240 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_REGISTER);
241 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
242 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
243 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
244 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
245 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER);
246 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
247 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER);
248 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
249 ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0);
250 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
251 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
252 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
253 Assert::Equal(dwIndex, pPlan->cRollbackActions);
254
255 Assert::Equal(3ul, pPlan->cExecutePackagesTotal);
256 Assert::Equal(3ul, pPlan->cOverallProgressTicksTotal);
257
258 dwIndex = 0;
259 ValidateCleanAction(pPlan, dwIndex++, L"PackageC");
260 ValidateCleanAction(pPlan, dwIndex++, L"PackageB");
261 ValidateCleanAction(pPlan, dwIndex++, L"PackageA");
262 Assert::Equal(dwIndex, pPlan->cCleanActions);
263
264 UINT uIndex = 0;
265 ValidatePlannedProvider(pPlan, uIndex++, L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", NULL);
266 ValidatePlannedProvider(pPlan, uIndex++, L"{A497C5E5-C78B-4F0B-BF72-B33E1DB1C4B8}", NULL);
267 ValidatePlannedProvider(pPlan, uIndex++, L"{D1D01094-23CE-4AF0-84B6-4A1A133F21D3}", NULL);
268 ValidatePlannedProvider(pPlan, uIndex++, L"{01E6B748-7B95-4BA9-976D-B6F35076CEF4}", NULL);
269 Assert::Equal(uIndex, pPlan->cPlannedProviders);
270
271 Assert::Equal(3ul, pEngineState->packages.cPackages);
272 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT);
273 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT);
274 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT);
275 }
276
277 [Fact]
278 void RelatedBundleMissingFromCacheTest()
279 {
280 HRESULT hr = S_OK;
281 BURN_ENGINE_STATE engineState = { };
282 BURN_ENGINE_STATE* pEngineState = &engineState;
283 BURN_PLAN* pPlan = &engineState.plan;
284
285 InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState);
286 DetectAttachedContainerAsAttached(pEngineState);
287 DetectPackagesAsAbsent(pEngineState);
288 BURN_RELATED_BUNDLE* pRelatedBundle = DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"0.9.0.0");
289 pRelatedBundle->fPlannable = FALSE;
290
291 hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL);
292 NativeAssert::Succeeded(hr, "CorePlan failed");
293
294 Assert::Equal<DWORD>(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action);
295 Assert::Equal<BOOL>(TRUE, pPlan->fPerMachine);
296 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
297
298 BOOL fRollback = FALSE;
299 DWORD dwIndex = 0;
300 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1);
301 ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA");
302 ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++);
303 Assert::Equal(dwIndex, pPlan->cCacheActions);
304
305 fRollback = TRUE;
306 dwIndex = 0;
307 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1);
308 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
309
310 Assert::Equal(35694ull, pPlan->qwEstimatedSize);
311 Assert::Equal(168715ull, pPlan->qwCacheSizeTotal);
312
313 fRollback = FALSE;
314 dwIndex = 0;
315 DWORD dwExecuteCheckpointId = 2;
316 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
317 ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent);
318 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
319 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
320 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER);
321 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
322 ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0);
323 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
324 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER);
325 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
326 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
327 Assert::Equal(dwIndex, pPlan->cExecuteActions);
328
329 fRollback = TRUE;
330 dwIndex = 0;
331 dwExecuteCheckpointId = 2;
332 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
333 ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA");
334 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
335 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER);
336 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
337 ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0);
338 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
339 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER);
340 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
341 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
342 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
343 Assert::Equal(dwIndex, pPlan->cRollbackActions);
344
345 Assert::Equal(1ul, pPlan->cExecutePackagesTotal);
346 Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal);
347
348 dwIndex = 0;
349 Assert::Equal(dwIndex, pPlan->cCleanActions);
350
351 UINT uIndex = 0;
352 ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL);
353 Assert::Equal(uIndex, pPlan->cPlannedProviders);
354
355 Assert::Equal(1ul, pEngineState->packages.cPackages);
356 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT);
357 }
358
359 [Fact]
360 void SingleMsiCacheTest()
361 {
362 HRESULT hr = S_OK;
363 BURN_ENGINE_STATE engineState = { };
364 BURN_ENGINE_STATE* pEngineState = &engineState;
365 BURN_PLAN* pPlan = &engineState.plan;
366
367 InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState);
368 DetectAttachedContainerAsAttached(pEngineState);
369 DetectPackagesAsAbsent(pEngineState);
370 DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"0.9.0.0");
371
372 hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_CACHE);
373 NativeAssert::Succeeded(hr, "CorePlan failed");
374
375 Assert::Equal<DWORD>(BOOTSTRAPPER_ACTION_CACHE, pPlan->action);
376 Assert::Equal<BOOL>(TRUE, pPlan->fPerMachine);
377 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
378
379 BOOL fRollback = FALSE;
380 DWORD dwIndex = 0;
381 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1);
382 ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA");
383 ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++);
384 Assert::Equal(dwIndex, pPlan->cCacheActions);
385
386 fRollback = TRUE;
387 dwIndex = 0;
388 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
389
390 Assert::Equal(33743ull, pPlan->qwEstimatedSize);
391 Assert::Equal(168715ull, pPlan->qwCacheSizeTotal);
392
393 fRollback = FALSE;
394 dwIndex = 0;
395 DWORD dwExecuteCheckpointId = 2;
396 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
397 ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent);
398 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
399 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
400 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
401 Assert::Equal(dwIndex, pPlan->cExecuteActions);
402
403 fRollback = TRUE;
404 dwIndex = 0;
405 dwExecuteCheckpointId = 2;
406 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
407 ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA");
408 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
409 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER);
410 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
411 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
412 Assert::Equal(dwIndex, pPlan->cRollbackActions);
413
414 Assert::Equal(0ul, pPlan->cExecutePackagesTotal);
415 Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal);
416
417 dwIndex = 0;
418 Assert::Equal(dwIndex, pPlan->cCleanActions);
419
420 UINT uIndex = 0;
421 ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL);
422 Assert::Equal(uIndex, pPlan->cPlannedProviders);
423
424 Assert::Equal(1ul, pEngineState->packages.cPackages);
425 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT);
426 }
427
428 [Fact]
429 void SingleMsiInstallTest()
430 {
431 HRESULT hr = S_OK;
432 BURN_ENGINE_STATE engineState = { };
433 BURN_ENGINE_STATE* pEngineState = &engineState;
434 BURN_PLAN* pPlan = &engineState.plan;
435
436 InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState);
437 DetectAttachedContainerAsAttached(pEngineState);
438 DetectPackagesAsAbsent(pEngineState);
439 DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"0.9.0.0");
440
441 hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL);
442 NativeAssert::Succeeded(hr, "CorePlan failed");
443
444 Assert::Equal<DWORD>(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action);
445 Assert::Equal<BOOL>(TRUE, pPlan->fPerMachine);
446 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
447
448 BOOL fRollback = FALSE;
449 DWORD dwIndex = 0;
450 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1);
451 ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA");
452 ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++);
453 Assert::Equal(dwIndex, pPlan->cCacheActions);
454
455 fRollback = TRUE;
456 dwIndex = 0;
457 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1);
458 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
459
460 Assert::Equal(35694ull, pPlan->qwEstimatedSize);
461 Assert::Equal(168715ull, pPlan->qwCacheSizeTotal);
462
463 fRollback = FALSE;
464 dwIndex = 0;
465 DWORD dwExecuteCheckpointId = 2;
466 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
467 ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent);
468 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
469 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
470 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER);
471 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
472 ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0);
473 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
474 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER);
475 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
476 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
477 ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL);
478 Assert::Equal(dwIndex, pPlan->cExecuteActions);
479
480 fRollback = TRUE;
481 dwIndex = 0;
482 dwExecuteCheckpointId = 2;
483 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
484 ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA");
485 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
486 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER);
487 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
488 ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0);
489 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
490 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER);
491 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
492 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
493 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
494 ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL);
495 Assert::Equal(dwIndex, pPlan->cRollbackActions);
496
497 Assert::Equal(2ul, pPlan->cExecutePackagesTotal);
498 Assert::Equal(3ul, pPlan->cOverallProgressTicksTotal);
499
500 dwIndex = 0;
501 Assert::Equal(dwIndex, pPlan->cCleanActions);
502
503 UINT uIndex = 0;
504 ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL);
505 Assert::Equal(uIndex, pPlan->cPlannedProviders);
506
507 Assert::Equal(1ul, pEngineState->packages.cPackages);
508 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT);
509 }
510
511 [Fact]
512 void SingleMsiInstalledWithNoInstalledPackagesModifyTest()
513 {
514 HRESULT hr = S_OK;
515 BURN_ENGINE_STATE engineState = { };
516 BURN_ENGINE_STATE* pEngineState = &engineState;
517 BURN_PLAN* pPlan = &engineState.plan;
518
519 InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState);
520 DetectPackagesAsAbsent(pEngineState);
521
522 pEngineState->registration.fInstalled = TRUE;
523
524 hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_MODIFY);
525 NativeAssert::Succeeded(hr, "CorePlan failed");
526
527 Assert::Equal<DWORD>(BOOTSTRAPPER_ACTION_MODIFY, pPlan->action);
528 Assert::Equal<BOOL>(TRUE, pPlan->fPerMachine);
529 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
530
531 BOOL fRollback = FALSE;
532 DWORD dwIndex = 0;
533 Assert::Equal(dwIndex, pPlan->cCacheActions);
534
535 fRollback = TRUE;
536 dwIndex = 0;
537 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
538
539 Assert::Equal(0ull, pPlan->qwEstimatedSize);
540 Assert::Equal(0ull, pPlan->qwCacheSizeTotal);
541
542 fRollback = FALSE;
543 dwIndex = 0;
544 DWORD dwExecuteCheckpointId = 1;
545 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
546 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
547 Assert::Equal(dwIndex, pPlan->cExecuteActions);
548
549 fRollback = TRUE;
550 dwIndex = 0;
551 dwExecuteCheckpointId = 1;
552 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
553 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
554 Assert::Equal(dwIndex, pPlan->cRollbackActions);
555
556 Assert::Equal(0ul, pPlan->cExecutePackagesTotal);
557 Assert::Equal(0ul, pPlan->cOverallProgressTicksTotal);
558
559 dwIndex = 0;
560 ValidateCleanAction(pPlan, dwIndex++, L"PackageA");
561 Assert::Equal(dwIndex, pPlan->cCleanActions);
562
563 UINT uIndex = 0;
564 ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL);
565 Assert::Equal(uIndex, pPlan->cPlannedProviders);
566
567 Assert::Equal(1ul, pEngineState->packages.cPackages);
568 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT);
569 }
570
571 [Fact]
572 void SingleMsiUninstallTest()
573 {
574 HRESULT hr = S_OK;
575 BURN_ENGINE_STATE engineState = { };
576 BURN_ENGINE_STATE* pEngineState = &engineState;
577 BURN_PLAN* pPlan = &engineState.plan;
578
579 InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState);
580 DetectPackagesAsPresentAndCached(pEngineState);
581
582 hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL);
583 NativeAssert::Succeeded(hr, "CorePlan failed");
584
585 Assert::Equal<DWORD>(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action);
586 Assert::Equal<BOOL>(TRUE, pPlan->fPerMachine);
587 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
588
589 BOOL fRollback = FALSE;
590 DWORD dwIndex = 0;
591 Assert::Equal(dwIndex, pPlan->cCacheActions);
592
593 fRollback = TRUE;
594 dwIndex = 0;
595 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
596
597 Assert::Equal(0ull, pPlan->qwEstimatedSize);
598 Assert::Equal(0ull, pPlan->qwCacheSizeTotal);
599
600 fRollback = FALSE;
601 dwIndex = 0;
602 DWORD dwExecuteCheckpointId = 1;
603 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
604 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
605 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER);
606 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
607 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER);
608 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
609 ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0);
610 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
611 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
612 Assert::Equal(dwIndex, pPlan->cExecuteActions);
613
614 fRollback = TRUE;
615 dwIndex = 0;
616 dwExecuteCheckpointId = 1;
617 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
618 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER);
619 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
620 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER);
621 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
622 ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0);
623 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
624 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
625 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
626 Assert::Equal(dwIndex, pPlan->cRollbackActions);
627
628 Assert::Equal(1ul, pPlan->cExecutePackagesTotal);
629 Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal);
630
631 dwIndex = 0;
632 ValidateCleanAction(pPlan, dwIndex++, L"PackageA");
633 Assert::Equal(dwIndex, pPlan->cCleanActions);
634
635 UINT uIndex = 0;
636 ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL);
637 ValidatePlannedProvider(pPlan, uIndex++, L"{64633047-D172-4BBB-B202-64337D15C952}", NULL);
638 Assert::Equal(uIndex, pPlan->cPlannedProviders);
639
640 Assert::Equal(1ul, pEngineState->packages.cPackages);
641 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT);
642 }
643
644 [Fact]
645 void SingleMsiUninstallTestFromUpgradeBundleWithSameExactPackage()
646 {
647 HRESULT hr = S_OK;
648 BURN_ENGINE_STATE engineState = { };
649 BURN_ENGINE_STATE* pEngineState = &engineState;
650 BURN_PLAN* pPlan = &engineState.plan;
651
652 InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState);
653 DetectAsRelatedUpgradeBundle(&engineState, L"{02940F3E-C83E-452D-BFCF-C943777ACEAE}", L"2.0.0.0");
654
655 hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL);
656 NativeAssert::Succeeded(hr, "CorePlan failed");
657
658 Assert::Equal<DWORD>(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action);
659 Assert::Equal<BOOL>(TRUE, pPlan->fPerMachine);
660 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
661
662 BOOL fRollback = FALSE;
663 DWORD dwIndex = 0;
664 Assert::Equal(dwIndex, pPlan->cCacheActions);
665
666 fRollback = TRUE;
667 dwIndex = 0;
668 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
669
670 Assert::Equal(0ull, pPlan->qwEstimatedSize);
671 Assert::Equal(0ull, pPlan->qwCacheSizeTotal);
672
673 fRollback = FALSE;
674 dwIndex = 0;
675 DWORD dwExecuteCheckpointId = 1;
676 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
677 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
678 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER);
679 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
680 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
681 Assert::Equal(dwIndex, pPlan->cExecuteActions);
682
683 fRollback = TRUE;
684 dwIndex = 0;
685 dwExecuteCheckpointId = 1;
686 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
687 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER);
688 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
689 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
690 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
691 Assert::Equal(dwIndex, pPlan->cRollbackActions);
692
693 Assert::Equal(0ul, pPlan->cExecutePackagesTotal);
694 Assert::Equal(0ul, pPlan->cOverallProgressTicksTotal);
695
696 dwIndex = 0;
697 Assert::Equal(dwIndex, pPlan->cCleanActions);
698
699 UINT uIndex = 0;
700 ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL);
701 Assert::Equal(uIndex, pPlan->cPlannedProviders);
702
703 Assert::Equal(1ul, pEngineState->packages.cPackages);
704 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_IGNORED, BURN_PACKAGE_REGISTRATION_STATE_IGNORED);
705 }
706
707 [Fact]
708 void SlipstreamInstallTest()
709 {
710 HRESULT hr = S_OK;
711 BURN_ENGINE_STATE engineState = { };
712 BURN_ENGINE_STATE* pEngineState = &engineState;
713 BURN_PLAN* pPlan = &engineState.plan;
714
715 InitializeEngineStateForCorePlan(wzSlipstreamManifestFileName, pEngineState);
716 DetectPermanentPackagesAsPresentAndCached(pEngineState);
717 PlanTestDetectPatchInitialize(pEngineState);
718
719 hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL);
720 NativeAssert::Succeeded(hr, "CorePlan failed");
721
722 Assert::Equal<DWORD>(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action);
723 Assert::Equal<BOOL>(TRUE, pPlan->fPerMachine);
724 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
725
726 BOOL fRollback = FALSE;
727 DWORD dwIndex = 0;
728 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1);
729 ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PatchA");
730 ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++);
731 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 2);
732 ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA");
733 ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++);
734 Assert::Equal(dwIndex, pPlan->cCacheActions);
735
736 fRollback = TRUE;
737 dwIndex = 0;
738 ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 2);
739 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
740
741 Assert::Equal(3055111ull, pPlan->qwEstimatedSize);
742 Assert::Equal(212992ull, pPlan->qwCacheSizeTotal);
743
744 fRollback = FALSE;
745 dwIndex = 0;
746 DWORD dwExecuteCheckpointId = 3;
747 BURN_EXECUTE_ACTION* pExecuteAction = NULL;
748 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
749 ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[5].syncpoint.hEvent);
750 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
751 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
752 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER);
753 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
754 ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0);
755 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
756 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER);
757 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
758 ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent);
759 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
760 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
761 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_REGISTER);
762 pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_INSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE);
763 ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA");
764 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
765 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER);
766 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
767 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
768 Assert::Equal(dwIndex, pPlan->cExecuteActions);
769
770 fRollback = TRUE;
771 dwIndex = 0;
772 dwExecuteCheckpointId = 3;
773 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
774 ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA");
775 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
776 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER);
777 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
778 ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0);
779 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
780 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER);
781 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
782 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
783 ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PatchA");
784 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
785 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_UNREGISTER);
786 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
787 pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE);
788 ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA");
789 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER);
790 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
791 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
792 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
793 Assert::Equal(dwIndex, pPlan->cRollbackActions);
794
795 Assert::Equal(2ul, pPlan->cExecutePackagesTotal);
796 Assert::Equal(4ul, pPlan->cOverallProgressTicksTotal);
797
798 dwIndex = 0;
799 Assert::Equal(dwIndex, pPlan->cCleanActions);
800
801 UINT uIndex = 0;
802 ValidatePlannedProvider(pPlan, uIndex++, L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", NULL);
803 Assert::Equal(uIndex, pPlan->cPlannedProviders);
804
805 Assert::Equal(3ul, pEngineState->packages.cPackages);
806 ValidatePermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"NetFx48Web");
807 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT);
808 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PatchA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT);
809 }
810
811 [Fact]
812 void SlipstreamUninstallTest()
813 {
814 HRESULT hr = S_OK;
815 BURN_ENGINE_STATE engineState = { };
816 BURN_ENGINE_STATE* pEngineState = &engineState;
817 BURN_PLAN* pPlan = &engineState.plan;
818
819 InitializeEngineStateForCorePlan(wzSlipstreamManifestFileName, pEngineState);
820 DetectPackagesAsPresentAndCached(pEngineState);
821
822 hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL);
823 NativeAssert::Succeeded(hr, "CorePlan failed");
824
825 Assert::Equal<DWORD>(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action);
826 Assert::Equal<BOOL>(TRUE, pPlan->fPerMachine);
827 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
828
829 BOOL fRollback = FALSE;
830 DWORD dwIndex = 0;
831 Assert::Equal(dwIndex, pPlan->cCacheActions);
832
833 fRollback = TRUE;
834 dwIndex = 0;
835 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
836
837 Assert::Equal(0ull, pPlan->qwEstimatedSize);
838 Assert::Equal(0ull, pPlan->qwCacheSizeTotal);
839
840 fRollback = FALSE;
841 dwIndex = 0;
842 DWORD dwExecuteCheckpointId = 1;
843 BURN_EXECUTE_ACTION* pExecuteAction = NULL;
844 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
845 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
846 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER);
847 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
848 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_UNREGISTER);
849 pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE);
850 ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA");
851 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
852 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
853 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER);
854 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
855 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER);
856 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
857 ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0);
858 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
859 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
860 Assert::Equal(dwIndex, pPlan->cExecuteActions);
861
862 fRollback = TRUE;
863 dwIndex = 0;
864 dwExecuteCheckpointId = 1;
865 ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
866 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER);
867 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
868 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_REGISTER);
869 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
870 pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_INSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE);
871 ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA");
872 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
873 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER);
874 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
875 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER);
876 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
877 ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0);
878 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
879 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
880 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
881 Assert::Equal(dwIndex, pPlan->cRollbackActions);
882
883 Assert::Equal(2ul, pPlan->cExecutePackagesTotal);
884 Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal);
885
886 dwIndex = 0;
887 ValidateCleanAction(pPlan, dwIndex++, L"PatchA");
888 ValidateCleanAction(pPlan, dwIndex++, L"PackageA");
889 Assert::Equal(dwIndex, pPlan->cCleanActions);
890
891 UINT uIndex = 0;
892 ValidatePlannedProvider(pPlan, uIndex++, L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", NULL);
893 ValidatePlannedProvider(pPlan, uIndex++, L"{0A5113E3-06A5-4CE0-8E83-9EB42F6764A6}", NULL);
894 ValidatePlannedProvider(pPlan, uIndex++, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", NULL);
895 Assert::Equal(uIndex, pPlan->cPlannedProviders);
896
897 Assert::Equal(3ul, pEngineState->packages.cPackages);
898 ValidatePermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"NetFx48Web");
899 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT);
900 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PatchA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT);
901 }
902
903 private:
904 // This doesn't initialize everything, just enough for CorePlan to work.
905 void InitializeEngineStateForCorePlan(LPCWSTR wzManifestFileName, BURN_ENGINE_STATE* pEngineState)
906 {
907 HRESULT hr = S_OK;
908 LPWSTR sczFilePath = NULL;
909
910 ::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive);
911
912 hr = VariableInitialize(&pEngineState->variables);
913 NativeAssert::Succeeded(hr, "Failed to initialize variables.");
914
915 try
916 {
917 pin_ptr<const wchar_t> dataDirectory = PtrToStringChars(this->TestContext->TestDirectory);
918 hr = PathConcat(dataDirectory, L"TestData\\PlanTest", &sczFilePath);
919 NativeAssert::Succeeded(hr, "Failed to get path to test file directory.");
920 hr = PathConcat(sczFilePath, wzManifestFileName, &sczFilePath);
921 NativeAssert::Succeeded(hr, "Failed to get path to test file.");
922 Assert::True(FileExistsEx(sczFilePath, NULL), "Test file does not exist.");
923
924 hr = ManifestLoadXmlFromFile(sczFilePath, pEngineState);
925 NativeAssert::Succeeded(hr, "Failed to load manifest.");
926 }
927 finally
928 {
929 ReleaseStr(sczFilePath);
930 }
931
932 hr = CoreInitializeConstants(pEngineState);
933 NativeAssert::Succeeded(hr, "Failed to initialize core constants");
934
935 pEngineState->userExperience.pfnBAProc = PlanTestBAProc;
936 }
937
938 void PlanTestDetect(BURN_ENGINE_STATE* pEngineState)
939 {
940 HRESULT hr = S_OK;
941 BURN_REGISTRATION* pRegistration = &pEngineState->registration;
942
943 DetectReset(pRegistration, &pEngineState->packages);
944 PlanReset(&pEngineState->plan, &pEngineState->containers, &pEngineState->packages, &pEngineState->layoutPayloads);
945
946 hr = DepDependencyArrayAlloc(&pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies, pRegistration->sczProviderKey, NULL);
947 NativeAssert::Succeeded(hr, "Failed to add the bundle provider key to the list of dependencies to ignore.");
948
949 pEngineState->userExperience.fEngineActive = TRUE;
950 pEngineState->fDetected = TRUE;
951 }
952
953 void PlanTestDetectPatchInitialize(BURN_ENGINE_STATE* pEngineState)
954 {
955 HRESULT hr = MsiEngineDetectInitialize(&pEngineState->packages);
956 NativeAssert::Succeeded(hr, "MsiEngineDetectInitialize failed");
957
958 for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i)
959 {
960 BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i;
961
962 if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
963 {
964 for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j)
965 {
966 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + j;
967
968 if (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN == pTargetProduct->patchPackageState)
969 {
970 pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
971 }
972 }
973 }
974 }
975 }
976
977 void DetectAttachedContainerAsAttached(BURN_ENGINE_STATE* pEngineState)
978 {
979 for (DWORD i = 0; i < pEngineState->containers.cContainers; ++i)
980 {
981 BURN_CONTAINER* pContainer = pEngineState->containers.rgContainers + i;
982 if (pContainer->fAttached)
983 {
984 pContainer->fActuallyAttached = TRUE;
985 }
986 }
987 }
988
989 void DetectPackageAsAbsent(BURN_PACKAGE* pPackage)
990 {
991 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
992 if (pPackage->fCanAffectRegistration)
993 {
994 pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
995 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
996 }
997 }
998
999 void DetectPackageAsPresentAndCached(BURN_PACKAGE* pPackage)
1000 {
1001 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT;
1002 pPackage->fCached = TRUE;
1003 if (pPackage->fCanAffectRegistration)
1004 {
1005 pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
1006 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
1007 }
1008 }
1009
1010 void DetectPackageDependent(BURN_PACKAGE* pPackage, LPCWSTR wzId)
1011 {
1012 HRESULT hr = S_OK;
1013
1014 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
1015 {
1016 BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + i;
1017
1018 hr = DepDependencyArrayAlloc(&pProvider->rgDependents, &pProvider->cDependents, wzId, NULL);
1019 NativeAssert::Succeeded(hr, "Failed to add package dependent");
1020 }
1021 }
1022
1023 void DetectPackagesAsAbsent(BURN_ENGINE_STATE* pEngineState)
1024 {
1025 PlanTestDetect(pEngineState);
1026
1027 for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i)
1028 {
1029 BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i;
1030 DetectPackageAsAbsent(pPackage);
1031 }
1032 }
1033
1034 void DetectPackagesAsPresentAndCached(BURN_ENGINE_STATE* pEngineState)
1035 {
1036 PlanTestDetect(pEngineState);
1037
1038 pEngineState->registration.fInstalled = TRUE;
1039
1040 for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i)
1041 {
1042 BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i;
1043 DetectPackageAsPresentAndCached(pPackage);
1044 DetectPackageDependent(pPackage, pEngineState->registration.sczId);
1045
1046 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
1047 {
1048 for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j)
1049 {
1050 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED;
1051
1052 BURN_PACKAGE* pMspPackage = pPackage->Msi.rgSlipstreamMsps[j].pMspPackage;
1053 MspEngineAddDetectedTargetProduct(&pEngineState->packages, pMspPackage, j, pPackage->Msi.sczProductCode, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED);
1054
1055 BURN_MSPTARGETPRODUCT* pTargetProduct = pMspPackage->Msp.rgTargetProducts + (pMspPackage->Msp.cTargetProductCodes - 1);
1056 pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT;
1057 pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
1058 }
1059 }
1060 }
1061 }
1062
1063 void DetectPermanentPackagesAsPresentAndCached(BURN_ENGINE_STATE* pEngineState)
1064 {
1065 PlanTestDetect(pEngineState);
1066
1067 pEngineState->registration.fInstalled = TRUE;
1068
1069 for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i)
1070 {
1071 BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i;
1072 if (pPackage->fUninstallable)
1073 {
1074 DetectPackageAsAbsent(pPackage);
1075 }
1076 else
1077 {
1078 DetectPackageAsPresentAndCached(pPackage);
1079 DetectPackageDependent(pPackage, pEngineState->registration.sczId);
1080 }
1081 }
1082 }
1083
1084 BURN_RELATED_BUNDLE* DetectUpgradeBundle(
1085 __in BURN_ENGINE_STATE* pEngineState,
1086 __in LPCWSTR wzId,
1087 __in LPCWSTR wzVersion
1088 )
1089 {
1090 HRESULT hr = S_OK;
1091 BURN_RELATED_BUNDLES* pRelatedBundles = &pEngineState->registration.relatedBundles;
1092 BURN_DEPENDENCY_PROVIDER dependencyProvider = { };
1093
1094 hr = StrAllocString(&dependencyProvider.sczKey, wzId, 0);
1095 NativeAssert::Succeeded(hr, "Failed to copy provider key");
1096
1097 dependencyProvider.fImported = TRUE;
1098
1099 hr = StrAllocString(&dependencyProvider.sczVersion, wzVersion, 0);
1100 NativeAssert::Succeeded(hr, "Failed to copy version");
1101
1102 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5);
1103 NativeAssert::Succeeded(hr, "Failed to ensure there is space for related bundles.");
1104
1105 BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles;
1106
1107 hr = VerParseVersion(wzVersion, 0, FALSE, &pRelatedBundle->pVersion);
1108 NativeAssert::Succeeded(hr, "Failed to parse pseudo bundle version: %ls", wzVersion);
1109
1110 pRelatedBundle->fPlannable = TRUE;
1111 pRelatedBundle->relationType = BOOTSTRAPPER_RELATION_UPGRADE;
1112
1113 hr = PseudoBundleInitialize(0, &pRelatedBundle->package, TRUE, wzId, pRelatedBundle->relationType, BOOTSTRAPPER_PACKAGE_STATE_PRESENT, TRUE, NULL, NULL, NULL, 0, FALSE, L"-quiet", L"-repair -quiet", L"-uninstall -quiet", &dependencyProvider, NULL, 0);
1114 NativeAssert::Succeeded(hr, "Failed to initialize related bundle to represent bundle: %ls", wzId);
1115
1116 ++pRelatedBundles->cRelatedBundles;
1117
1118 return pRelatedBundle;
1119 }
1120
1121 void DetectAsRelatedUpgradeBundle(
1122 __in BURN_ENGINE_STATE* pEngineState,
1123 __in LPCWSTR wzId,
1124 __in LPCWSTR wzVersion
1125 )
1126 {
1127 HRESULT hr = StrAllocString(&pEngineState->registration.sczAncestors, wzId, 0);
1128 NativeAssert::Succeeded(hr, "Failed to set registration's ancestors");
1129
1130 pEngineState->command.relationType = BOOTSTRAPPER_RELATION_UPGRADE;
1131
1132 DetectPackagesAsPresentAndCached(pEngineState);
1133 DetectUpgradeBundle(pEngineState, wzId, wzVersion);
1134
1135 for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i)
1136 {
1137 BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i;
1138 DetectPackageDependent(pPackage, wzId);
1139 }
1140 }
1141
1142 void ValidateCacheContainer(
1143 __in BURN_PLAN* pPlan,
1144 __in BOOL fRollback,
1145 __in DWORD dwIndex,
1146 __in LPCWSTR wzContainerId
1147 )
1148 {
1149 BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex);
1150 Assert::Equal<DWORD>(BURN_CACHE_ACTION_TYPE_CONTAINER, pAction->type);
1151 NativeAssert::StringEqual(wzContainerId, pAction->container.pContainer->sczId);
1152 }
1153
1154 BURN_CACHE_ACTION* ValidateCacheActionExists(BURN_PLAN* pPlan, BOOL fRollback, DWORD dwIndex)
1155 {
1156 Assert::InRange(dwIndex + 1ul, 1ul, (fRollback ? pPlan->cRollbackCacheActions : pPlan->cCacheActions));
1157 return (fRollback ? pPlan->rgRollbackCacheActions : pPlan->rgCacheActions) + dwIndex;
1158 }
1159
1160 void ValidateCacheCheckpoint(
1161 __in BURN_PLAN* pPlan,
1162 __in BOOL fRollback,
1163 __in DWORD dwIndex,
1164 __in DWORD dwId
1165 )
1166 {
1167 BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex);
1168 Assert::Equal<DWORD>(BURN_CACHE_ACTION_TYPE_CHECKPOINT, pAction->type);
1169 Assert::Equal(dwId, pAction->checkpoint.dwId);
1170 }
1171
1172 DWORD ValidateCachePackage(
1173 __in BURN_PLAN* pPlan,
1174 __in BOOL fRollback,
1175 __in DWORD dwIndex,
1176 __in LPCWSTR wzPackageId
1177 )
1178 {
1179 BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex);
1180 Assert::Equal<DWORD>(BURN_CACHE_ACTION_TYPE_PACKAGE, pAction->type);
1181 NativeAssert::StringEqual(wzPackageId, pAction->package.pPackage->sczId);
1182 return dwIndex + 1;
1183 }
1184
1185 void ValidateCacheRollbackPackage(
1186 __in BURN_PLAN* pPlan,
1187 __in BOOL fRollback,
1188 __in DWORD dwIndex,
1189 __in LPCWSTR wzPackageId
1190 )
1191 {
1192 BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex);
1193 Assert::Equal<DWORD>(BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE, pAction->type);
1194 NativeAssert::StringEqual(wzPackageId, pAction->rollbackPackage.pPackage->sczId);
1195 }
1196
1197 void ValidateCacheSignalSyncpoint(
1198 __in BURN_PLAN* pPlan,
1199 __in BOOL fRollback,
1200 __in DWORD dwIndex
1201 )
1202 {
1203 BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex);
1204 Assert::Equal<DWORD>(BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT, pAction->type);
1205 Assert::NotEqual((DWORD_PTR)NULL, (DWORD_PTR)pAction->syncpoint.hEvent);
1206 }
1207
1208 void ValidateCleanAction(
1209 __in BURN_PLAN* pPlan,
1210 __in DWORD dwIndex,
1211 __in LPCWSTR wzPackageId
1212 )
1213 {
1214 Assert::InRange(dwIndex + 1ul, 1ul, pPlan->cCleanActions);
1215
1216 BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + dwIndex;
1217 Assert::NotEqual((DWORD_PTR)0, (DWORD_PTR)pCleanAction->pPackage);
1218 NativeAssert::StringEqual(wzPackageId, pCleanAction->pPackage->sczId);
1219 }
1220
1221 BURN_EXECUTE_ACTION* ValidateExecuteActionExists(BURN_PLAN* pPlan, BOOL fRollback, DWORD dwIndex)
1222 {
1223 Assert::InRange(dwIndex + 1ul, 1ul, (fRollback ? pPlan->cRollbackActions : pPlan->cExecuteActions));
1224 return (fRollback ? pPlan->rgRollbackActions : pPlan->rgExecuteActions) + dwIndex;
1225 }
1226
1227 void ValidateExecuteBeginMsiTransaction(
1228 __in BURN_PLAN* pPlan,
1229 __in BOOL fRollback,
1230 __in DWORD dwIndex,
1231 __in LPCWSTR wzRollbackBoundaryId
1232 )
1233 {
1234 BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex);
1235 Assert::Equal<DWORD>(BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION, pAction->type);
1236 NativeAssert::StringEqual(wzRollbackBoundaryId, pAction->msiTransaction.pRollbackBoundary->sczId);
1237 Assert::Equal<BOOL>(FALSE, pAction->fDeleted);
1238 }
1239
1240 void ValidateExecuteCheckpoint(
1241 __in BURN_PLAN* pPlan,
1242 __in BOOL fRollback,
1243 __in DWORD dwIndex,
1244 __in DWORD dwId
1245 )
1246 {
1247 BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex);
1248 Assert::Equal<DWORD>(BURN_EXECUTE_ACTION_TYPE_CHECKPOINT, pAction->type);
1249 Assert::Equal(dwId, pAction->checkpoint.dwId);
1250 Assert::Equal<BOOL>(FALSE, pAction->fDeleted);
1251 }
1252
1253 void ValidateExecuteCommitMsiTransaction(
1254 __in BURN_PLAN* pPlan,
1255 __in BOOL fRollback,
1256 __in DWORD dwIndex,
1257 __in LPCWSTR wzRollbackBoundaryId
1258 )
1259 {
1260 BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex);
1261 Assert::Equal<DWORD>(BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION, pAction->type);
1262 NativeAssert::StringEqual(wzRollbackBoundaryId, pAction->msiTransaction.pRollbackBoundary->sczId);
1263 Assert::Equal<BOOL>(FALSE, pAction->fDeleted);
1264 }
1265
1266 void ValidateExecuteExePackage(
1267 __in BURN_PLAN* pPlan,
1268 __in BOOL fRollback,
1269 __in DWORD dwIndex,
1270 __in LPCWSTR wzPackageId,
1271 __in BOOTSTRAPPER_ACTION_STATE action,
1272 __in LPCWSTR wzIgnoreDependencies
1273 )
1274 {
1275 BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex);
1276 Assert::Equal<DWORD>(BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE, pAction->type);
1277 NativeAssert::StringEqual(wzPackageId, pAction->exePackage.pPackage->sczId);
1278 Assert::Equal<DWORD>(action, pAction->exePackage.action);
1279 NativeAssert::StringEqual(wzIgnoreDependencies, pAction->exePackage.sczIgnoreDependencies);
1280 Assert::Equal<BOOL>(FALSE, pAction->fDeleted);
1281 }
1282
1283 void ValidateExecuteMsiPackage(
1284 __in BURN_PLAN* pPlan,
1285 __in BOOL fRollback,
1286 __in DWORD dwIndex,
1287 __in_z LPCWSTR wzPackageId,
1288 __in BOOTSTRAPPER_ACTION_STATE action,
1289 __in BURN_MSI_PROPERTY actionMsiProperty,
1290 __in DWORD uiLevel,
1291 __in BOOL fDisableExternalUiHandler,
1292 __in DWORD dwLoggingAttributes
1293 )
1294 {
1295 BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex);
1296 Assert::Equal<DWORD>(BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE, pAction->type);
1297 NativeAssert::StringEqual(wzPackageId, pAction->msiPackage.pPackage->sczId);
1298 Assert::Equal<DWORD>(action, pAction->msiPackage.action);
1299 Assert::Equal<DWORD>(actionMsiProperty, pAction->msiPackage.actionMsiProperty);
1300 Assert::Equal<DWORD>(uiLevel, pAction->msiPackage.uiLevel);
1301 Assert::Equal<BOOL>(fDisableExternalUiHandler, pAction->msiPackage.fDisableExternalUiHandler);
1302 NativeAssert::NotNull(pAction->msiPackage.sczLogPath);
1303 Assert::Equal<DWORD>(dwLoggingAttributes, pAction->msiPackage.dwLoggingAttributes);
1304 Assert::Equal<BOOL>(FALSE, pAction->fDeleted);
1305 }
1306
1307 BURN_EXECUTE_ACTION* ValidateDeletedExecuteMspTarget(
1308 __in BURN_PLAN* pPlan,
1309 __in BOOL fRollback,
1310 __in DWORD dwIndex,
1311 __in_z LPCWSTR wzPackageId,
1312 __in BOOTSTRAPPER_ACTION_STATE action,
1313 __in_z LPCWSTR wzTargetProductCode,
1314 __in BOOL fPerMachineTarget,
1315 __in BURN_MSI_PROPERTY actionMsiProperty,
1316 __in DWORD uiLevel,
1317 __in BOOL fDisableExternalUiHandler,
1318 __in BOOL fDeleted
1319 )
1320 {
1321 BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex);
1322 Assert::Equal<DWORD>(BURN_EXECUTE_ACTION_TYPE_MSP_TARGET, pAction->type);
1323 NativeAssert::StringEqual(wzPackageId, pAction->mspTarget.pPackage->sczId);
1324 Assert::Equal<DWORD>(action, pAction->mspTarget.action);
1325 NativeAssert::StringEqual(wzTargetProductCode, pAction->mspTarget.sczTargetProductCode);
1326 Assert::Equal<BOOL>(fPerMachineTarget, pAction->mspTarget.fPerMachineTarget);
1327 Assert::Equal<DWORD>(actionMsiProperty, pAction->mspTarget.actionMsiProperty);
1328 Assert::Equal<DWORD>(uiLevel, pAction->mspTarget.uiLevel);
1329 Assert::Equal<BOOL>(fDisableExternalUiHandler, pAction->mspTarget.fDisableExternalUiHandler);
1330 NativeAssert::NotNull(pAction->mspTarget.sczLogPath);
1331 Assert::Equal<BOOL>(fDeleted, pAction->fDeleted);
1332 return pAction;
1333 }
1334
1335 void ValidateExecuteMspTargetPatch(
1336 __in BURN_EXECUTE_ACTION* pAction,
1337 __in DWORD dwIndex,
1338 __in_z LPCWSTR wzPackageId
1339 )
1340 {
1341 Assert::InRange(dwIndex + 1ul, 1ul, pAction->mspTarget.cOrderedPatches);
1342 BURN_ORDERED_PATCHES* pOrderedPatch = pAction->mspTarget.rgOrderedPatches + dwIndex;
1343 NativeAssert::StringEqual(wzPackageId, pOrderedPatch->pPackage->sczId);
1344 }
1345
1346 void ValidateExecutePackageDependency(
1347 __in BURN_PLAN* pPlan,
1348 __in BOOL fRollback,
1349 __in DWORD dwIndex,
1350 __in LPCWSTR wzPackageId,
1351 __in LPCWSTR wzBundleProviderKey,
1352 __in BURN_DEPENDENCY_ACTION action
1353 )
1354 {
1355 BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex);
1356 Assert::Equal<DWORD>(BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY, pAction->type);
1357 NativeAssert::StringEqual(wzPackageId, pAction->packageDependency.pPackage->sczId);
1358 NativeAssert::StringEqual(wzBundleProviderKey, pAction->packageDependency.sczBundleProviderKey);
1359 Assert::Equal<DWORD>(action, pAction->packageDependency.action);
1360 Assert::Equal<BOOL>(FALSE, pAction->fDeleted);
1361 }
1362
1363 void ValidateExecutePackageProvider(
1364 __in BURN_PLAN* pPlan,
1365 __in BOOL fRollback,
1366 __in DWORD dwIndex,
1367 __in LPCWSTR wzPackageId,
1368 __in BURN_DEPENDENCY_ACTION action
1369 )
1370 {
1371 BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex);
1372 Assert::Equal<DWORD>(BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER, pAction->type);
1373 NativeAssert::StringEqual(wzPackageId, pAction->packageProvider.pPackage->sczId);
1374 Assert::Equal<DWORD>(action, pAction->packageProvider.action);
1375 Assert::Equal<BOOL>(FALSE, pAction->fDeleted);
1376 }
1377
1378 void ValidateExecuteRollbackBoundary(
1379 __in BURN_PLAN* pPlan,
1380 __in BOOL fRollback,
1381 __in DWORD dwIndex,
1382 __in LPCWSTR wzId,
1383 __in BOOL fVital,
1384 __in BOOL fTransaction
1385 )
1386 {
1387 BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex);
1388 Assert::Equal<DWORD>(BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY, pAction->type);
1389 NativeAssert::StringEqual(wzId, pAction->rollbackBoundary.pRollbackBoundary->sczId);
1390 Assert::Equal<BOOL>(fVital, pAction->rollbackBoundary.pRollbackBoundary->fVital);
1391 Assert::Equal<BOOL>(fTransaction, pAction->rollbackBoundary.pRollbackBoundary->fTransaction);
1392 Assert::Equal<BOOL>(FALSE, pAction->fDeleted);
1393 }
1394
1395 void ValidateExecuteUncachePackage(
1396 __in BURN_PLAN* pPlan,
1397 __in BOOL fRollback,
1398 __in DWORD dwIndex,
1399 __in LPCWSTR wzPackageId
1400 )
1401 {
1402 BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex);
1403 Assert::Equal<DWORD>(BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE, pAction->type);
1404 NativeAssert::StringEqual(wzPackageId, pAction->uncachePackage.pPackage->sczId);
1405 Assert::Equal<BOOL>(FALSE, pAction->fDeleted);
1406 }
1407
1408 void ValidateExecuteWaitSyncpoint(
1409 __in BURN_PLAN* pPlan,
1410 __in BOOL fRollback,
1411 __in DWORD dwIndex,
1412 __in HANDLE hEvent
1413 )
1414 {
1415 BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex);
1416 Assert::Equal<DWORD>(BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT, pAction->type);
1417 Assert::Equal((DWORD_PTR)hEvent, (DWORD_PTR)pAction->syncpoint.hEvent);
1418 Assert::Equal<BOOL>(FALSE, pAction->fDeleted);
1419 }
1420
1421 void ValidateNonPermanentPackageExpectedStates(
1422 __in BURN_PACKAGE* pPackage,
1423 __in_z LPCWSTR wzPackageId,
1424 __in BURN_PACKAGE_REGISTRATION_STATE expectedCacheState,
1425 __in BURN_PACKAGE_REGISTRATION_STATE expectedInstallState
1426 )
1427 {
1428 NativeAssert::StringEqual(wzPackageId, pPackage->sczId);
1429 Assert::Equal<BOOL>(TRUE, pPackage->fCanAffectRegistration);
1430 Assert::Equal<DWORD>(expectedCacheState, pPackage->expectedCacheRegistrationState);
1431 Assert::Equal<DWORD>(expectedInstallState, pPackage->expectedInstallRegistrationState);
1432 }
1433
1434 void ValidatePermanentPackageExpectedStates(
1435 __in BURN_PACKAGE* pPackage,
1436 __in_z LPCWSTR wzPackageId
1437 )
1438 {
1439 NativeAssert::StringEqual(wzPackageId, pPackage->sczId);
1440 Assert::Equal<BOOL>(FALSE, pPackage->fCanAffectRegistration);
1441 Assert::Equal<DWORD>(BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, pPackage->expectedCacheRegistrationState);
1442 Assert::Equal<DWORD>(BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, pPackage->expectedInstallRegistrationState);
1443 }
1444
1445 void ValidatePlannedProvider(
1446 __in BURN_PLAN* pPlan,
1447 __in UINT uIndex,
1448 __in LPCWSTR wzKey,
1449 __in LPCWSTR wzName
1450 )
1451 {
1452 Assert::InRange(uIndex + 1u, 1u, pPlan->cPlannedProviders);
1453
1454 DEPENDENCY* pProvider = pPlan->rgPlannedProviders + uIndex;
1455 NativeAssert::StringEqual(wzKey, pProvider->sczKey);
1456 NativeAssert::StringEqual(wzName, pProvider->sczName);
1457 }
1458 };
1459}
1460}
1461}
1462}
1463}
1464
1465static HRESULT WINAPI PlanTestBAProc(
1466 __in BOOTSTRAPPER_APPLICATION_MESSAGE /*message*/,
1467 __in const LPVOID /*pvArgs*/,
1468 __inout LPVOID /*pvResults*/,
1469 __in_opt LPVOID /*pvContext*/
1470 )
1471{
1472 return S_OK;
1473}
diff --git a/src/burn/test/BurnUnitTest/RegistrationTest.cpp b/src/burn/test/BurnUnitTest/RegistrationTest.cpp
new file mode 100644
index 00000000..7b126f61
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/RegistrationTest.cpp
@@ -0,0 +1,772 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6#define ROOT_PATH L"SOFTWARE\\WiX_Burn_UnitTest"
7#define HKLM_PATH L"SOFTWARE\\WiX_Burn_UnitTest\\HKLM"
8#define HKCU_PATH L"SOFTWARE\\WiX_Burn_UnitTest\\HKCU"
9#define REGISTRY_UNINSTALL_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
10#define REGISTRY_RUN_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"
11
12#define TEST_UNINSTALL_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_UNINSTALL_KEY L"\\{D54F896D-1952-43e6-9C67-B5652240618C}"
13#define TEST_RUN_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_RUN_KEY
14
15
16static LSTATUS APIENTRY RegistrationTest_RegCreateKeyExW(
17 __in HKEY hKey,
18 __in LPCWSTR lpSubKey,
19 __reserved DWORD Reserved,
20 __in_opt LPWSTR lpClass,
21 __in DWORD dwOptions,
22 __in REGSAM samDesired,
23 __in_opt CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes,
24 __out PHKEY phkResult,
25 __out_opt LPDWORD lpdwDisposition
26 );
27static LSTATUS APIENTRY RegistrationTest_RegOpenKeyExW(
28 __in HKEY hKey,
29 __in_opt LPCWSTR lpSubKey,
30 __reserved DWORD ulOptions,
31 __in REGSAM samDesired,
32 __out PHKEY phkResult
33 );
34static LSTATUS APIENTRY RegistrationTest_RegDeleteKeyExW(
35 __in HKEY hKey,
36 __in LPCWSTR lpSubKey,
37 __in REGSAM samDesired,
38 __reserved DWORD Reserved
39 );
40
41namespace Microsoft
42{
43namespace Tools
44{
45namespace WindowsInstallerXml
46{
47namespace Test
48{
49namespace Bootstrapper
50{
51 using namespace Microsoft::Win32;
52 using namespace System;
53 using namespace System::IO;
54 using namespace Xunit;
55
56 public ref class RegistrationTest : BurnUnitTest
57 {
58 public:
59 RegistrationTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture)
60 {
61 }
62
63 [Fact]
64 void RegisterBasicTest()
65 {
66 HRESULT hr = S_OK;
67 IXMLDOMElement* pixeBundle = NULL;
68 LPWSTR sczCurrentProcess = NULL;
69 BURN_VARIABLES variables = { };
70 BURN_USER_EXPERIENCE userExperience = { };
71 BOOTSTRAPPER_COMMAND command = { };
72 BURN_REGISTRATION registration = { };
73 BURN_LOGGING logging = { };
74 BURN_PACKAGES packages = { };
75 String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"));
76
77 try
78 {
79 // set mock API's
80 RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL);
81
82 Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH));
83
84 logging.sczPath = L"BurnUnitTest.txt";
85
86 LPCWSTR wzDocument =
87 L"<Bundle>"
88 L" <UX>"
89 L" <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' Hash='000000000000' />"
90 L" </UX>"
91 L" <Registration Id='{D54F896D-1952-43e6-9C67-B5652240618C}' UpgradeCode='{D54F896D-1952-43e6-9C67-B5652240618C}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
92 L" <Arp Register='yes' Publisher='WiX Toolset' DisplayName='RegisterBasicTest' DisplayVersion='1.0.0.0' />"
93 L" </Registration>"
94 L"</Bundle>";
95
96 // load XML document
97 LoadBundleXmlHelper(wzDocument, &pixeBundle);
98
99 hr = VariableInitialize(&variables);
100 TestThrowOnFailure(hr, L"Failed to initialize variables.");
101
102 hr = UserExperienceParseFromXml(&userExperience, pixeBundle);
103 TestThrowOnFailure(hr, L"Failed to parse UX from XML.");
104
105 hr = RegistrationParseFromXml(&registration, pixeBundle);
106 TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
107
108 hr = PlanSetResumeCommand(&registration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging);
109 TestThrowOnFailure(hr, L"Failed to set registration resume command.");
110
111 hr = PathForCurrentProcess(&sczCurrentProcess, NULL);
112 TestThrowOnFailure(hr, L"Failed to get current process path.");
113
114 // write registration
115 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE | BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0);
116 TestThrowOnFailure(hr, L"Failed to register bundle.");
117
118 // verify that registration was created
119 Assert::True(Directory::Exists(cacheDirectory));
120 Assert::True(File::Exists(Path::Combine(cacheDirectory, gcnew String(L"setup.exe"))));
121
122 Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
123 Assert::Equal<String^>(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)(Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)));
124
125 // end session
126 hr = RegistrationSessionEnd(&registration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER);
127 TestThrowOnFailure(hr, L"Failed to unregister bundle.");
128
129 // verify that registration was removed
130 Assert::False(Directory::Exists(cacheDirectory));
131
132 Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
133 Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
134 }
135 finally
136 {
137 ReleaseStr(sczCurrentProcess);
138 ReleaseObject(pixeBundle);
139 UserExperienceUninitialize(&userExperience);
140 RegistrationUninitialize(&registration);
141 VariablesUninitialize(&variables);
142
143 Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH));
144 if (Directory::Exists(cacheDirectory))
145 {
146 Directory::Delete(cacheDirectory, true);
147 }
148
149 RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
150 }
151 }
152
153 [Fact]
154 void RegisterArpMinimumTest()
155 {
156 HRESULT hr = S_OK;
157 IXMLDOMElement* pixeBundle = NULL;
158 LPWSTR sczCurrentProcess = NULL;
159 BURN_VARIABLES variables = { };
160 BURN_USER_EXPERIENCE userExperience = { };
161 BOOTSTRAPPER_COMMAND command = { };
162 BURN_REGISTRATION registration = { };
163 BURN_LOGGING logging = { };
164 BURN_PACKAGES packages = { };
165 String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"));
166 try
167 {
168 // set mock API's
169 RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL);
170
171 Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH));
172
173 logging.sczPath = L"BurnUnitTest.txt";
174
175 LPCWSTR wzDocument =
176 L"<Bundle>"
177 L" <UX>"
178 L" <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' Hash='000000000000' />"
179 L" </UX>"
180 L" <Registration Id='{D54F896D-1952-43e6-9C67-B5652240618C}' UpgradeCode='{D54F896D-1952-43e6-9C67-B5652240618C}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
181 L" <Arp Register='yes' Publisher='WiX Toolset' DisplayName='Product1' DisplayVersion='1.0.0.0' />"
182 L" </Registration>"
183 L"</Bundle>";
184
185 // load XML document
186 LoadBundleXmlHelper(wzDocument, &pixeBundle);
187
188 hr = VariableInitialize(&variables);
189 TestThrowOnFailure(hr, L"Failed to initialize variables.");
190
191 hr = UserExperienceParseFromXml(&userExperience, pixeBundle);
192 TestThrowOnFailure(hr, L"Failed to parse UX from XML.");
193
194 hr = RegistrationParseFromXml(&registration, pixeBundle);
195 TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
196
197 hr = PlanSetResumeCommand(&registration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging);
198 TestThrowOnFailure(hr, L"Failed to set registration resume command.");
199
200 hr = PathForCurrentProcess(&sczCurrentProcess, NULL);
201 TestThrowOnFailure(hr, L"Failed to get current process path.");
202
203 //
204 // install
205 //
206
207 // write registration
208 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0);
209 TestThrowOnFailure(hr, L"Failed to register bundle.");
210
211 // verify that registration was created
212 Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
213 Assert::Equal<String^>(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
214
215 // complete registration
216 hr = RegistrationSessionEnd(&registration, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER);
217 TestThrowOnFailure(hr, L"Failed to unregister bundle.");
218
219 // verify that registration was updated
220 Assert::Equal(Int32(BURN_RESUME_MODE_ARP), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
221 Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr));
222 Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
223
224 //
225 // uninstall
226 //
227
228 // write registration
229 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, 0);
230 TestThrowOnFailure(hr, L"Failed to register bundle.");
231
232 // verify that registration was updated
233 Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
234 Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr));
235 Assert::Equal<String^>(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
236
237 // delete registration
238 hr = RegistrationSessionEnd(&registration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER);
239 TestThrowOnFailure(hr, L"Failed to unregister bundle.");
240
241 // verify that registration was removed
242 Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
243 Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr));
244 Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
245 }
246 finally
247 {
248 ReleaseStr(sczCurrentProcess);
249 ReleaseObject(pixeBundle);
250 UserExperienceUninitialize(&userExperience);
251 RegistrationUninitialize(&registration);
252 VariablesUninitialize(&variables);
253
254 Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH));
255 if (Directory::Exists(cacheDirectory))
256 {
257 Directory::Delete(cacheDirectory, true);
258 }
259
260 RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
261 }
262 }
263
264 [Fact]
265 void RegisterVariablesTest()
266 {
267 HRESULT hr = S_OK;
268 IXMLDOMElement* pixeBundle = NULL;
269 LPWSTR sczCurrentProcess = NULL;
270 BURN_VARIABLES variables = { };
271 BURN_USER_EXPERIENCE userExperience = { };
272 BOOTSTRAPPER_COMMAND command = { };
273 BURN_REGISTRATION registration = { };
274 BURN_LOGGING logging = { };
275 BURN_PACKAGES packages = { };
276 String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"));
277 try
278 {
279 // set mock API's
280 RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL);
281
282 Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH));
283
284 logging.sczPath = L"BurnUnitTest.txt";
285
286 LPCWSTR wzDocument =
287 L"<Bundle>"
288 L" <UX>"
289 L" <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' Hash='000000000000' />"
290 L" </UX>"
291 L" <Registration Id='{D54F896D-1952-43e6-9C67-B5652240618C}' UpgradeCode='{D54F896D-1952-43e6-9C67-B5652240618C}' Tag='foo' ProviderKey='bar' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
292 L" <Arp Register='yes' Publisher='WiX Toolset' DisplayName='Product1' DisplayVersion='1.0.0.0' />"
293 L" </Registration>"
294 L"</Bundle>";
295
296 // load XML document
297 LoadBundleXmlHelper(wzDocument, &pixeBundle);
298
299 hr = VariableInitialize(&variables);
300 TestThrowOnFailure(hr, L"Failed to initialize variables.");
301
302 hr = UserExperienceParseFromXml(&userExperience, pixeBundle);
303 TestThrowOnFailure(hr, L"Failed to parse UX from XML.");
304
305 hr = RegistrationParseFromXml(&registration, pixeBundle);
306 TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
307
308 hr = PlanSetResumeCommand(&registration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging);
309 TestThrowOnFailure(hr, L"Failed to set registration resume command.");
310
311 hr = PathForCurrentProcess(&sczCurrentProcess, NULL);
312 TestThrowOnFailure(hr, L"Failed to get current process path.");
313
314 //
315 // install
316 //
317
318 // write registration
319 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0);
320 TestThrowOnFailure(hr, L"Failed to register bundle.");
321
322 // verify that registration was created
323 Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
324 Assert::Equal<String^>(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
325
326 // complete registration
327 hr = RegistrationSessionEnd(&registration, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_REQUIRED, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER);
328 TestThrowOnFailure(hr, L"Failed to unregister bundle.");
329
330 // verify that registration variables were updated
331 registration.fInstalled = TRUE;
332
333 hr = RegistrationSetVariables(&registration, &variables);
334 TestThrowOnFailure(hr, L"Failed to set registration variables.");
335
336 Assert::Equal(1ll, VariableGetNumericHelper(&variables, BURN_BUNDLE_INSTALLED));
337 Assert::Equal(1ll, VariableGetNumericHelper(&variables, BURN_REBOOT_PENDING));
338 Assert::Equal<String^>(gcnew String(L"foo"), VariableGetStringHelper(&variables, BURN_BUNDLE_TAG));
339 Assert::Equal<String^>(gcnew String(L"bar"), VariableGetStringHelper(&variables, BURN_BUNDLE_PROVIDER_KEY));
340 Assert::Equal<String^>(gcnew String(L"1.0.0.0"), VariableGetVersionHelper(&variables, BURN_BUNDLE_VERSION));
341
342 //
343 // uninstall
344 //
345
346 // delete registration
347 hr = RegistrationSessionEnd(&registration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER);
348 TestThrowOnFailure(hr, L"Failed to unregister bundle.");
349
350 // verify that registration was removed
351 Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
352 Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr));
353 Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
354 }
355 finally
356 {
357 ReleaseStr(sczCurrentProcess);
358 ReleaseObject(pixeBundle);
359 UserExperienceUninitialize(&userExperience);
360 RegistrationUninitialize(&registration);
361 VariablesUninitialize(&variables);
362
363 Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH));
364 if (Directory::Exists(cacheDirectory))
365 {
366 Directory::Delete(cacheDirectory, true);
367 }
368
369 RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
370 }
371 }
372
373 [Fact]
374 void RegisterArpFullTest()
375 {
376 HRESULT hr = S_OK;
377 IXMLDOMElement* pixeBundle = NULL;
378 LPWSTR sczCurrentProcess = NULL;
379 BURN_VARIABLES variables = { };
380 BURN_USER_EXPERIENCE userExperience = { };
381 BOOTSTRAPPER_COMMAND command = { };
382 BURN_REGISTRATION registration = { };
383 BURN_LOGGING logging = { };
384 BURN_PACKAGES packages = { };
385 String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"));
386 try
387 {
388 // set mock API's
389 RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL);
390
391 Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH));
392
393 logging.sczPath = L"BurnUnitTest.txt";
394
395 LPCWSTR wzDocument =
396 L"<Bundle>"
397 L" <UX UxDllPayloadId='ux.dll'>"
398 L" <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' Hash='000000000000' />"
399 L" </UX>"
400 L" <Registration Id='{D54F896D-1952-43e6-9C67-B5652240618C}' UpgradeCode='{D54F896D-1952-43e6-9C67-B5652240618C}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
401 L" <Arp Register='yes' DisplayName='DisplayName1' DisplayVersion='1.2.3.4' Publisher='Publisher1' HelpLink='http://www.microsoft.com/help'"
402 L" HelpTelephone='555-555-5555' AboutUrl='http://www.microsoft.com/about' UpdateUrl='http://www.microsoft.com/update'"
403 L" Comments='Comments1' Contact='Contact1' DisableModify='yes' DisableRemove='yes' />"
404 L" </Registration>"
405 L"</Bundle>";
406
407 // load XML document
408 LoadBundleXmlHelper(wzDocument, &pixeBundle);
409
410 hr = VariableInitialize(&variables);
411 TestThrowOnFailure(hr, L"Failed to initialize variables.");
412
413 hr = UserExperienceParseFromXml(&userExperience, pixeBundle);
414 TestThrowOnFailure(hr, L"Failed to parse UX from XML.");
415
416 hr = RegistrationParseFromXml(&registration, pixeBundle);
417 TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
418
419 hr = PlanSetResumeCommand(&registration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging);
420 TestThrowOnFailure(hr, L"Failed to set registration resume command.");
421
422 hr = PathForCurrentProcess(&sczCurrentProcess, NULL);
423 TestThrowOnFailure(hr, L"Failed to get current process path.");
424
425 //
426 // install
427 //
428
429 // write registration
430 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0);
431 TestThrowOnFailure(hr, L"Failed to register bundle.");
432
433 // verify that registration was created
434 Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
435 Assert::Equal<String^>(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
436
437 // finish registration
438 hr = RegistrationSessionEnd(&registration, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER);
439 TestThrowOnFailure(hr, L"Failed to register bundle.");
440
441 // verify that registration was updated
442 Assert::Equal(Int32(BURN_RESUME_MODE_ARP), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
443 Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr));
444 Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
445
446 Assert::Equal<String^>(gcnew String(L"DisplayName1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"DisplayName"), nullptr));
447 Assert::Equal<String^>(gcnew String(L"1.2.3.4"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"DisplayVersion"), nullptr));
448 Assert::Equal<String^>(gcnew String(L"Publisher1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Publisher"), nullptr));
449 Assert::Equal<String^>(gcnew String(L"http://www.microsoft.com/help"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"HelpLink"), nullptr));
450 Assert::Equal<String^>(gcnew String(L"555-555-5555"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"HelpTelephone"), nullptr));
451 Assert::Equal<String^>(gcnew String(L"http://www.microsoft.com/about"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"URLInfoAbout"), nullptr));
452 Assert::Equal<String^>(gcnew String(L"http://www.microsoft.com/update"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"URLUpdateInfo"), nullptr));
453 Assert::Equal<String^>(gcnew String(L"Comments1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Comments"), nullptr));
454 Assert::Equal<String^>(gcnew String(L"Contact1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Contact"), nullptr));
455 Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"NoModify"), nullptr));
456 Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"NoRemove"), nullptr));
457
458 //
459 // uninstall
460 //
461
462 // write registration
463 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, 0);
464 TestThrowOnFailure(hr, L"Failed to register bundle.");
465
466 // verify that registration was updated
467 Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
468 Assert::Equal<String^>(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
469
470 // delete registration
471 hr = RegistrationSessionEnd(&registration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER);
472 TestThrowOnFailure(hr, L"Failed to unregister bundle.");
473
474 // verify that registration was removed
475 Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
476 Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr));
477 Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
478 }
479 finally
480 {
481 ReleaseStr(sczCurrentProcess);
482 ReleaseObject(pixeBundle);
483 UserExperienceUninitialize(&userExperience);
484 RegistrationUninitialize(&registration);
485 VariablesUninitialize(&variables);
486
487 Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH));
488 if (Directory::Exists(cacheDirectory))
489 {
490 Directory::Delete(cacheDirectory, true);
491 }
492
493 RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
494 }
495 }
496
497 [Fact(Skip = "Currently fails")]
498 void ResumeTest()
499 {
500 HRESULT hr = S_OK;
501 IXMLDOMElement* pixeBundle = NULL;
502 LPWSTR sczCurrentProcess = NULL;
503 BURN_VARIABLES variables = { };
504 BURN_USER_EXPERIENCE userExperience = { };
505 BOOTSTRAPPER_COMMAND command = { };
506 BURN_REGISTRATION registration = { };
507 BURN_LOGGING logging = { };
508 BURN_PACKAGES packages = { };
509 BYTE rgbData[256] = { };
510 BOOTSTRAPPER_RESUME_TYPE resumeType = BOOTSTRAPPER_RESUME_TYPE_NONE;
511 BYTE* pbBuffer = NULL;
512 SIZE_T cbBuffer = 0;
513 String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"));
514 try
515 {
516 for (DWORD i = 0; i < 256; ++i)
517 {
518 rgbData[i] = (BYTE)i;
519 }
520
521 // set mock API's
522 RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL);
523
524 Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH));
525
526 logging.sczPath = L"BurnUnitTest.txt";
527
528 LPCWSTR wzDocument =
529 L"<Bundle>"
530 L" <UX>"
531 L" <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' Hash='000000000000' />"
532 L" </UX>"
533 L" <Registration Id='{D54F896D-1952-43e6-9C67-B5652240618C}' UpgradeCode='{D54F896D-1952-43e6-9C67-B5652240618C}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
534 L" <Arp Register='yes' Publisher='WiX Toolset' DisplayName='RegisterBasicTest' DisplayVersion='1.0.0.0' />"
535 L" </Registration>"
536 L"</Bundle>";
537
538 // load XML document
539 LoadBundleXmlHelper(wzDocument, &pixeBundle);
540
541 hr = VariableInitialize(&variables);
542 TestThrowOnFailure(hr, L"Failed to initialize variables.");
543
544 hr = UserExperienceParseFromXml(&userExperience, pixeBundle);
545 TestThrowOnFailure(hr, L"Failed to parse UX from XML.");
546
547 hr = RegistrationParseFromXml(&registration, pixeBundle);
548 TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
549
550 hr = PlanSetResumeCommand(&registration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging);
551 TestThrowOnFailure(hr, L"Failed to set registration resume command.");
552
553 hr = PathForCurrentProcess(&sczCurrentProcess, NULL);
554 TestThrowOnFailure(hr, L"Failed to get current process path.");
555
556 // read resume type before session
557 hr = RegistrationDetectResumeType(&registration, &resumeType);
558 TestThrowOnFailure(hr, L"Failed to read resume type.");
559
560 Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_NONE, (int)resumeType);
561
562 // begin session
563 hr = RegistrationSessionBegin(sczCurrentProcess, &registration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0);
564 TestThrowOnFailure(hr, L"Failed to register bundle.");
565
566 hr = RegistrationSaveState(&registration, rgbData, sizeof(rgbData));
567 TestThrowOnFailure(hr, L"Failed to save state.");
568
569 // read interrupted resume type
570 hr = RegistrationDetectResumeType(&registration, &resumeType);
571 TestThrowOnFailure(hr, L"Failed to read interrupted resume type.");
572
573 Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_INTERRUPTED, (int)resumeType);
574
575 // suspend session
576 hr = RegistrationSessionEnd(&registration, &variables, &packages, BURN_RESUME_MODE_SUSPEND, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER);
577 TestThrowOnFailure(hr, L"Failed to suspend session.");
578
579 // verify that run key was removed
580 Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
581
582 // read suspend resume type
583 hr = RegistrationDetectResumeType(&registration, &resumeType);
584 TestThrowOnFailure(hr, L"Failed to read suspend resume type.");
585
586 Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_SUSPEND, (int)resumeType);
587
588 // read state back
589 hr = RegistrationLoadState(&registration, &pbBuffer, &cbBuffer);
590 TestThrowOnFailure(hr, L"Failed to load state.");
591
592 Assert::Equal((SIZE_T)sizeof(rgbData), cbBuffer);
593 Assert::True(0 == memcmp(pbBuffer, rgbData, sizeof(rgbData)));
594
595 // write active resume mode
596 hr = RegistrationSessionResume(&registration, &variables);
597 TestThrowOnFailure(hr, L"Failed to write active resume mode.");
598
599 // verify that run key was put back
600 Assert::NotEqual((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
601
602 // end session
603 hr = RegistrationSessionEnd(&registration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER);
604 TestThrowOnFailure(hr, L"Failed to unregister bundle.");
605
606 // read resume type after session
607 hr = RegistrationDetectResumeType(&registration, &resumeType);
608 TestThrowOnFailure(hr, L"Failed to read resume type.");
609
610 Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_NONE, (int)resumeType);
611 }
612 finally
613 {
614 ReleaseStr(sczCurrentProcess);
615 ReleaseObject(pixeBundle);
616 UserExperienceUninitialize(&userExperience);
617 RegistrationUninitialize(&registration);
618 VariablesUninitialize(&variables);
619
620 Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH));
621 if (Directory::Exists(cacheDirectory))
622 {
623 Directory::Delete(cacheDirectory, true);
624 }
625
626 RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
627 }
628 }
629
630 //BOOTSTRAPPER_RESUME_TYPE_NONE,
631 //BOOTSTRAPPER_RESUME_TYPE_INVALID, // resume information is present but invalid
632 //BOOTSTRAPPER_RESUME_TYPE_UNEXPECTED, // relaunched after an unexpected interruption
633 //BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING, // reboot has not taken place yet
634 //BOOTSTRAPPER_RESUME_TYPE_REBOOT, // relaunched after reboot
635 //BOOTSTRAPPER_RESUME_TYPE_SUSPEND, // relaunched after suspend
636 //BOOTSTRAPPER_RESUME_TYPE_ARP, // launched from ARP
637 };
638}
639}
640}
641}
642}
643
644
645static LSTATUS APIENTRY RegistrationTest_RegCreateKeyExW(
646 __in HKEY hKey,
647 __in LPCWSTR lpSubKey,
648 __reserved DWORD Reserved,
649 __in_opt LPWSTR lpClass,
650 __in DWORD dwOptions,
651 __in REGSAM samDesired,
652 __in_opt CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes,
653 __out PHKEY phkResult,
654 __out_opt LPDWORD lpdwDisposition
655 )
656{
657 LSTATUS ls = ERROR_SUCCESS;
658 LPCWSTR wzRoot = NULL;
659 HKEY hkRoot = NULL;
660
661 if (HKEY_LOCAL_MACHINE == hKey)
662 {
663 wzRoot = HKLM_PATH;
664 }
665 else if (HKEY_CURRENT_USER == hKey)
666 {
667 wzRoot = HKCU_PATH;
668 }
669 else
670 {
671 hkRoot = hKey;
672 }
673
674 if (wzRoot)
675 {
676 ls = ::RegOpenKeyExW(HKEY_CURRENT_USER, wzRoot, 0, KEY_WRITE, &hkRoot);
677 if (ERROR_SUCCESS != ls)
678 {
679 ExitFunction();
680 }
681 }
682
683 ls = ::RegCreateKeyExW(hkRoot, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition);
684
685LExit:
686 ReleaseRegKey(hkRoot);
687
688 return ls;
689}
690
691static LSTATUS APIENTRY RegistrationTest_RegOpenKeyExW(
692 __in HKEY hKey,
693 __in_opt LPCWSTR lpSubKey,
694 __reserved DWORD ulOptions,
695 __in REGSAM samDesired,
696 __out PHKEY phkResult
697 )
698{
699 LSTATUS ls = ERROR_SUCCESS;
700 LPCWSTR wzRoot = NULL;
701 HKEY hkRoot = NULL;
702
703 if (HKEY_LOCAL_MACHINE == hKey)
704 {
705 wzRoot = HKLM_PATH;
706 }
707 else if (HKEY_CURRENT_USER == hKey)
708 {
709 wzRoot = HKCU_PATH;
710 }
711 else
712 {
713 hkRoot = hKey;
714 }
715
716 if (wzRoot)
717 {
718 ls = ::RegOpenKeyExW(HKEY_CURRENT_USER, wzRoot, 0, KEY_WRITE, &hkRoot);
719 if (ERROR_SUCCESS != ls)
720 {
721 ExitFunction();
722 }
723 }
724
725 ls = ::RegOpenKeyExW(hkRoot, lpSubKey, ulOptions, samDesired, phkResult);
726
727LExit:
728 ReleaseRegKey(hkRoot);
729
730 return ls;
731}
732
733static LSTATUS APIENTRY RegistrationTest_RegDeleteKeyExW(
734 __in HKEY hKey,
735 __in LPCWSTR lpSubKey,
736 __in REGSAM samDesired,
737 __reserved DWORD Reserved
738 )
739{
740 LSTATUS ls = ERROR_SUCCESS;
741 LPCWSTR wzRoot = NULL;
742 HKEY hkRoot = NULL;
743
744 if (HKEY_LOCAL_MACHINE == hKey)
745 {
746 wzRoot = HKLM_PATH;
747 }
748 else if (HKEY_CURRENT_USER == hKey)
749 {
750 wzRoot = HKCU_PATH;
751 }
752 else
753 {
754 hkRoot = hKey;
755 }
756
757 if (wzRoot)
758 {
759 ls = ::RegOpenKeyExW(HKEY_CURRENT_USER, wzRoot, 0, KEY_WRITE | samDesired, &hkRoot);
760 if (ERROR_SUCCESS != ls)
761 {
762 ExitFunction();
763 }
764 }
765
766 ls = ::RegDeleteKeyExW(hkRoot, lpSubKey, samDesired, Reserved);
767
768LExit:
769 ReleaseRegKey(hkRoot);
770
771 return ls;
772}
diff --git a/src/burn/test/BurnUnitTest/SearchTest.cpp b/src/burn/test/BurnUnitTest/SearchTest.cpp
new file mode 100644
index 00000000..eca01f5f
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/SearchTest.cpp
@@ -0,0 +1,815 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiGetComponentPathW(
7 __in LPCWSTR szProduct,
8 __in LPCWSTR szComponent,
9 __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf,
10 __inout_opt LPDWORD pcchBuf
11 );
12static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiLocateComponentW(
13 __in LPCWSTR szComponent,
14 __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf,
15 __inout_opt LPDWORD pcchBuf
16 );
17static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoW(
18 __in LPCWSTR szProductCode,
19 __in LPCWSTR szProperty,
20 __out_ecount_opt(*pcchValue) LPWSTR szValue,
21 __inout_opt LPDWORD pcchValue
22 );
23static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoExW(
24 __in LPCWSTR szProductCode,
25 __in_opt LPCWSTR szUserSid,
26 __in MSIINSTALLCONTEXT dwContext,
27 __in LPCWSTR szProperty,
28 __out_ecount_opt(*pcchValue) LPWSTR szValue,
29 __inout_opt LPDWORD pcchValue
30 );
31
32using namespace System;
33using namespace Xunit;
34using namespace Microsoft::Win32;
35
36namespace Microsoft
37{
38namespace Tools
39{
40namespace WindowsInstallerXml
41{
42namespace Test
43{
44namespace Bootstrapper
45{
46 public ref class SearchTest : BurnUnitTest
47 {
48 public:
49 SearchTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture)
50 {
51 }
52
53 [Fact]
54 void DirectorySearchTest()
55 {
56 HRESULT hr = S_OK;
57 IXMLDOMElement* pixeBundle = NULL;
58 BURN_VARIABLES variables = { };
59 BURN_SEARCHES searches = { };
60 BURN_EXTENSIONS burnExtensions = { };
61 try
62 {
63 hr = VariableInitialize(&variables);
64 TestThrowOnFailure(hr, L"Failed to initialize variables.");
65
66 pin_ptr<const WCHAR> wzDirectory1 = PtrToStringChars(this->TestContext->TestDirectory);
67 pin_ptr<const WCHAR> wzDirectory2 = PtrToStringChars(System::IO::Path::Combine(this->TestContext->TestDirectory, gcnew String(L"none")));
68
69 VariableSetStringHelper(&variables, L"Directory1", wzDirectory1, FALSE);
70 VariableSetStringHelper(&variables, L"Directory2", wzDirectory2, FALSE);
71
72 LPCWSTR wzDocument =
73 L"<Bundle>"
74 L" <DirectorySearch Id='Search1' Type='exists' Path='[Directory1]' Variable='Variable1' />"
75 L" <DirectorySearch Id='Search2' Type='exists' Path='[Directory2]' Variable='Variable2' />"
76 L"</Bundle>";
77
78 // load XML document
79 LoadBundleXmlHelper(wzDocument, &pixeBundle);
80
81 hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle);
82 TestThrowOnFailure(hr, L"Failed to parse searches from XML.");
83
84 // execute searches
85 hr = SearchesExecute(&searches, &variables);
86 TestThrowOnFailure(hr, L"Failed to execute searches.");
87
88 // check variable values
89 Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable1"));
90 Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable2"));
91 }
92 finally
93 {
94 ReleaseObject(pixeBundle);
95 VariablesUninitialize(&variables);
96 SearchesUninitialize(&searches);
97 }
98 }
99
100 [Fact(Skip = "Currently fails")]
101 void FileSearchTest()
102 {
103 HRESULT hr = S_OK;
104 IXMLDOMElement* pixeBundle = NULL;
105 BURN_VARIABLES variables = { };
106 BURN_SEARCHES searches = { };
107 BURN_EXTENSIONS burnExtensions = { };
108 ULARGE_INTEGER uliVersion = { };
109 VERUTIL_VERSION* pVersion = NULL;
110 try
111 {
112 hr = VariableInitialize(&variables);
113 TestThrowOnFailure(hr, L"Failed to initialize variables.");
114
115 pin_ptr<const WCHAR> wzFile1 = PtrToStringChars(System::IO::Path::Combine(this->TestContext->TestDirectory, gcnew String(L"none.txt")));
116 pin_ptr<const WCHAR> wzFile2 = PtrToStringChars(System::Reflection::Assembly::GetExecutingAssembly()->Location);
117
118 hr = FileVersion(wzFile2, &uliVersion.HighPart, &uliVersion.LowPart);
119 TestThrowOnFailure(hr, L"Failed to get DLL version.");
120
121 hr = VerVersionFromQword(uliVersion.QuadPart, &pVersion);
122 NativeAssert::Succeeded(hr, "Failed to create version.");
123
124 VariableSetStringHelper(&variables, L"File1", wzFile1, FALSE);
125 VariableSetStringHelper(&variables, L"File2", wzFile2, FALSE);
126
127 LPCWSTR wzDocument =
128 L"<Bundle>"
129 L" <FileSearch Id='Search1' Type='exists' Path='[File1]' Variable='Variable1' />"
130 L" <FileSearch Id='Search2' Type='exists' Path='[File2]' Variable='Variable2' />"
131 L" <FileSearch Id='Search3' Type='version' Path='[File2]' Variable='Variable3' />"
132 L"</Bundle>";
133
134 // load XML document
135 LoadBundleXmlHelper(wzDocument, &pixeBundle);
136
137 hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle);
138 TestThrowOnFailure(hr, L"Failed to parse searches from XML.");
139
140 // execute searches
141 hr = SearchesExecute(&searches, &variables);
142 TestThrowOnFailure(hr, L"Failed to execute searches.");
143
144 // check variable values
145 Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable1"));
146 Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable2"));
147 Assert::Equal<String^>(gcnew String(pVersion->sczVersion), VariableGetVersionHelper(&variables, L"Variable3"));
148 }
149 finally
150 {
151 ReleaseVerutilVersion(pVersion);
152 ReleaseObject(pixeBundle);
153 VariablesUninitialize(&variables);
154 SearchesUninitialize(&searches);
155 }
156 }
157
158 [Fact]
159 void RegistrySearchTest()
160 {
161 HRESULT hr = S_OK;
162 IXMLDOMElement* pixeBundle = NULL;
163 BURN_VARIABLES variables = { };
164 BURN_SEARCHES searches = { };
165 BURN_EXTENSIONS burnExtensions = { };
166 HKEY hkey32 = NULL;
167 HKEY hkey64 = NULL;
168 BOOL f64bitMachine = (nullptr != Environment::GetEnvironmentVariable("ProgramFiles(x86)"));
169
170 try
171 {
172 hr = VariableInitialize(&variables);
173 TestThrowOnFailure(hr, L"Failed to initialize variables.");
174
175 Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"String"), gcnew String(L"String1 %TEMP%"), RegistryValueKind::String);
176 Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"StringExpand"), gcnew String(L"String1 %TEMP%"), RegistryValueKind::ExpandString);
177 Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"DWord"), 1, RegistryValueKind::DWord);
178 Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"QWord"), 1ll, RegistryValueKind::QWord);
179 Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"VersionString"), gcnew String(L"1.1.1.1"), RegistryValueKind::String);
180 Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"VersionQWord"), MAKEQWORDVERSION(1,1,1,1), RegistryValueKind::QWord);
181 Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\String"), nullptr, gcnew String(L"String1"), RegistryValueKind::String);
182 Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Numeric"), nullptr, 1ll, RegistryValueKind::DWord);
183
184 if (f64bitMachine)
185 {
186 hr = RegCreate(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness\\", KEY_WRITE | KEY_WOW64_32KEY, &hkey32);
187 Assert::True(SUCCEEDED(hr));
188
189 hr = RegCreate(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness\\", KEY_WRITE | KEY_WOW64_64KEY, &hkey64);
190 Assert::True(SUCCEEDED(hr));
191
192 hr = RegWriteString(hkey64, L"TestStringSpecificToBitness", L"64-bit");
193 Assert::True(SUCCEEDED(hr));
194
195 hr = RegWriteString(hkey32, L"TestStringSpecificToBitness", L"32-bit");
196 Assert::True(SUCCEEDED(hr));
197 }
198
199 VariableSetStringHelper(&variables, L"MyKey", L"SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value", FALSE);
200 VariableSetStringHelper(&variables, L"MyValue", L"String", FALSE);
201 VariableSetStringHelper(&variables, L"Variable27", L"Default27", FALSE);
202 VariableSetStringHelper(&variables, L"Variable28", L"Default28", FALSE);
203
204 LPCWSTR wzDocument =
205 L"<Bundle>"
206 L" <RegistrySearch Id='Search1' Type='exists' Root='HKLM' Key='SOFTWARE\\Microsoft' Variable='Variable1' />"
207 L" <RegistrySearch Id='Search2' Type='exists' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\None' Variable='Variable2' />"
208 L" <RegistrySearch Id='Search3' Type='exists' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='None' Variable='Variable3' />"
209 L" <RegistrySearch Id='Search4' Type='exists' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='String' Variable='Variable4' />"
210 L" <RegistrySearch Id='Search5' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='String' Variable='Variable5' VariableType='string' />"
211 L" <RegistrySearch Id='Search6' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='String' Variable='Variable6' VariableType='string' ExpandEnvironment='no' />"
212 L" <RegistrySearch Id='Search7' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='String' Variable='Variable7' VariableType='string' ExpandEnvironment='yes' />"
213 L" <RegistrySearch Id='Search8' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='StringExpand' Variable='Variable8' VariableType='string' />"
214 L" <RegistrySearch Id='Search9' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='StringExpand' Variable='Variable9' VariableType='string' ExpandEnvironment='no' />"
215 L" <RegistrySearch Id='Search10' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='StringExpand' Variable='Variable10' VariableType='string' ExpandEnvironment='yes' />"
216 L" <RegistrySearch Id='Search11' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='DWord' Variable='Variable11' VariableType='numeric' />"
217 L" <RegistrySearch Id='Search12' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='QWord' Variable='Variable12' VariableType='numeric' />"
218 L" <RegistrySearch Id='Search13' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='VersionString' Variable='Variable13' VariableType='version' />"
219 L" <RegistrySearch Id='Search14' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='VersionQWord' Variable='Variable14' VariableType='version' />"
220 L" <RegistrySearch Id='Search15' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\String' Variable='Variable15' VariableType='string' />"
221 L" <RegistrySearch Id='Search16' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Numeric' Variable='Variable16' VariableType='numeric' />"
222 L" <RegistrySearch Id='Search17' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\None' Variable='Variable17' VariableType='numeric' />"
223 L" <RegistrySearch Id='Search18' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Numeric' Value='None' Variable='Variable18' VariableType='numeric' />"
224 L" <RegistrySearch Id='Search19' Type='exists' Root='HKCU' Key='[MyKey]' Value='[MyValue]' Variable='Variable19' />"
225 L" <RegistrySearch Id='Search20' Type='value' Root='HKCU' Key='[MyKey]' Value='[MyValue]' Variable='Variable20' VariableType='string' />"
226 L" <RegistrySearch Id='Search21' Type='value' Root='HKCU' Key='SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness' Value='TestStringSpecificToBitness' Variable='Variable21' VariableType='string' Win64='no' />"
227 L" <RegistrySearch Id='Search22' Type='value' Root='HKCU' Key='SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness' Value='TestStringSpecificToBitness' Variable='Variable22' VariableType='string' Win64='yes' />"
228 L" <RegistrySearch Id='Search23' Type='exists' Root='HKU' Key='.DEFAULT\\Environment' Variable='Variable23' />"
229 L" <RegistrySearch Id='Search24' Type='exists' Root='HKU' Key='.DEFAULT\\System\\NetworkServiceSidSubkeyDoesNotExist' Variable='Variable24' />"
230 L" <RegistrySearch Id='Search25' Type='value' Root='HKCR' Key='.msi' Variable='Variable25' VariableType='string' />"
231 L" <RegistrySearch Id='Search26' Type='value' Root='HKCR' Key='.msi' Variable='Variable26' VariableType='formatted' />"
232 L" <RegistrySearch Id='Search27' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\StringDoesNotExist' Value='String' Variable='Variable27' VariableType='string' />"
233 L" <RegistrySearch Id='Search28' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\String' Value='DoesNotExist' Variable='Variable28' VariableType='string' />"
234 L"</Bundle>";
235
236 // load XML document
237 LoadBundleXmlHelper(wzDocument, &pixeBundle);
238
239 hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle);
240 TestThrowOnFailure(hr, L"Failed to parse searches from XML.");
241
242 // execute searches
243 hr = SearchesExecute(&searches, &variables);
244 TestThrowOnFailure(hr, L"Failed to execute searches.");
245
246 // check variable values
247 Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable1"));
248 Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable2"));
249 Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable3"));
250 Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable4"));
251 Assert::Equal<String^>(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable5"));
252 Assert::Equal<String^>(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable6"));
253 Assert::Equal<String^>(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable7"));
254 Assert::Equal<String^>(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable8"));
255 Assert::Equal<String^>(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable9"));
256 Assert::NotEqual(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable10"));
257 Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable11"));
258 Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable12"));
259 Assert::Equal<String^>(gcnew String(L"1.1.1.1"), VariableGetVersionHelper(&variables, L"Variable13"));
260 Assert::Equal<String^>(gcnew String(L"1.1.1.1"), VariableGetVersionHelper(&variables, L"Variable14"));
261 Assert::Equal<String^>(gcnew String(L"String1"), VariableGetStringHelper(&variables, L"Variable15"));
262 Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable16"));
263 Assert::False(VariableExistsHelper(&variables, L"Variable17"));
264 Assert::False(VariableExistsHelper(&variables, L"Variable18"));
265 Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable19"));
266 Assert::Equal<String^>(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable20"));
267 if (f64bitMachine)
268 {
269 Assert::Equal<String^>(gcnew String(L"32-bit"), VariableGetStringHelper(&variables, L"Variable21"));
270 Assert::Equal<String^>(gcnew String(L"64-bit"), VariableGetStringHelper(&variables, L"Variable22"));
271 }
272
273 Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable23"));
274 Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable24"));
275 Assert::Equal<String^>(gcnew String(L"Msi.Package"), VariableGetStringHelper(&variables, L"Variable25"));
276 Assert::Equal<String^>(gcnew String(L"Msi.Package"), VariableGetStringHelper(&variables, L"Variable26"));
277 Assert::Equal<String^>(gcnew String(L"Default27"), VariableGetStringHelper(&variables, L"Variable27"));
278 Assert::Equal<String^>(gcnew String(L"Default28"), VariableGetStringHelper(&variables, L"Variable28"));
279 }
280 finally
281 {
282 ReleaseRegKey(hkey32);
283 ReleaseRegKey(hkey64);
284 ReleaseObject(pixeBundle);
285 VariablesUninitialize(&variables);
286 SearchesUninitialize(&searches);
287
288 Registry::CurrentUser->DeleteSubKeyTree(gcnew String(L"SOFTWARE\\Microsoft\\WiX_Burn_UnitTest"));
289 if (f64bitMachine)
290 {
291 RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness", REG_KEY_32BIT, FALSE);
292 RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest", REG_KEY_32BIT, FALSE);
293 RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness", REG_KEY_64BIT, FALSE);
294 RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest", REG_KEY_64BIT, FALSE);
295 }
296 }
297 }
298
299 [Fact]
300 void MsiComponentSearchTest()
301 {
302 HRESULT hr = S_OK;
303 IXMLDOMElement* pixeBundle = NULL;
304 BURN_VARIABLES variables = { };
305 BURN_SEARCHES searches = { };
306 BURN_EXTENSIONS burnExtensions = { };
307 try
308 {
309 hr = VariableInitialize(&variables);
310 TestThrowOnFailure(hr, L"Failed to initialize variables.");
311
312 // set mock API's
313 WiuFunctionOverride(NULL, MsiComponentSearchTest_MsiGetComponentPathW, MsiComponentSearchTest_MsiLocateComponentW, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
314
315 LPCWSTR wzDocument =
316 L"<Bundle>"
317 L" <MsiComponentSearch Id='Search1' Type='state' ComponentId='{BAD00000-1000-0000-0000-000000000000}' Variable='Variable1' />"
318 L" <MsiComponentSearch Id='Search2' Type='state' ProductCode='{BAD00000-0000-0000-0000-000000000000}' ComponentId='{BAD00000-1000-0000-0000-000000000000}' Variable='Variable2' />"
319 L" <MsiComponentSearch Id='Search3' Type='state' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{BAD00000-1000-0000-0000-000000000000}' Variable='Variable3' />"
320 L" <MsiComponentSearch Id='Search4' Type='keyPath' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-1000-0000-000000000000}' Variable='Variable4' />"
321 L" <MsiComponentSearch Id='Search5' Type='keyPath' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-2000-0000-000000000000}' Variable='Variable5' />"
322 L" <MsiComponentSearch Id='Search6' Type='keyPath' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-3000-0000-000000000000}' Variable='Variable6' />"
323 L" <MsiComponentSearch Id='Search7' Type='keyPath' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-4000-0000-000000000000}' Variable='Variable7' />"
324 L" <MsiComponentSearch Id='Search8' Type='keyPath' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-5000-0000-000000000000}' Variable='Variable8' />"
325 L" <MsiComponentSearch Id='Search9' Type='keyPath' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-6000-0000-000000000000}' Variable='Variable9' />" // todo: value key path
326 L" <MsiComponentSearch Id='Search10' Type='state' ComponentId='{600D0000-1000-1000-0000-000000000000}' Variable='Variable10' />"
327 L" <MsiComponentSearch Id='Search11' Type='state' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-1000-0000-000000000000}' Variable='Variable11' />"
328 L" <MsiComponentSearch Id='Search12' Type='state' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-2000-0000-000000000000}' Variable='Variable12' />"
329 L" <MsiComponentSearch Id='Search13' Type='state' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-3000-0000-000000000000}' Variable='Variable13' />"
330 L" <MsiComponentSearch Id='Search14' Type='state' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-4000-0000-000000000000}' Variable='Variable14' />"
331 L" <MsiComponentSearch Id='Search15' Type='directory' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-1000-0000-000000000000}' Variable='Variable15' />"
332 L" <MsiComponentSearch Id='Search16' Type='directory' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-2000-0000-000000000000}' Variable='Variable16' />"
333 L" <MsiComponentSearch Id='Search17' Type='directory' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-3000-0000-000000000000}' Variable='Variable17' />"
334 L" <MsiComponentSearch Id='Search18' Type='directory' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-4000-0000-000000000000}' Variable='Variable18' />"
335 L" <MsiComponentSearch Id='Search19' Type='keyPath' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-7000-0000-000000000000}' Variable='Variable19' />"
336 L"</Bundle>";
337
338 // load XML document
339 LoadBundleXmlHelper(wzDocument, &pixeBundle);
340
341 hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle);
342 TestThrowOnFailure(hr, L"Failed to parse searches from XML.");
343
344 // execute searches
345 hr = SearchesExecute(&searches, &variables);
346 TestThrowOnFailure(hr, L"Failed to execute searches.");
347
348 // check variable values
349 Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable1"));
350 Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable2"));
351 Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable3"));
352 Assert::Equal<String^>(gcnew String(L"C:\\directory\\file1.txt"), VariableGetStringHelper(&variables, L"Variable4"));
353 Assert::Equal<String^>(gcnew String(L"C:\\directory\\file2.txt"), VariableGetStringHelper(&variables, L"Variable5"));
354 Assert::Equal<String^>(gcnew String(L"C:\\directory\\file3.txt"), VariableGetStringHelper(&variables, L"Variable6"));
355 Assert::Equal<String^>(gcnew String(L"C:\\directory\\file4.txt"), VariableGetStringHelper(&variables, L"Variable7"));
356 Assert::Equal<String^>(gcnew String(L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\"), VariableGetStringHelper(&variables, L"Variable8"));
357 Assert::Equal<String^>(gcnew String(L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), VariableGetStringHelper(&variables, L"Variable9"));
358 Assert::Equal(3ll, VariableGetNumericHelper(&variables, L"Variable10"));
359 Assert::Equal(3ll, VariableGetNumericHelper(&variables, L"Variable11"));
360 Assert::Equal(4ll, VariableGetNumericHelper(&variables, L"Variable12"));
361 Assert::Equal(4ll, VariableGetNumericHelper(&variables, L"Variable13"));
362 Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable14"));
363 Assert::Equal<String^>(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable15"));
364 Assert::Equal<String^>(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable16"));
365 Assert::Equal<String^>(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable17"));
366 Assert::Equal<String^>(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable18"));
367 Assert::Equal<String^>(gcnew String(L"C:\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\file5.txt"), VariableGetStringHelper(&variables, L"Variable19"));
368 }
369 finally
370 {
371 ReleaseObject(pixeBundle);
372 VariablesUninitialize(&variables);
373 SearchesUninitialize(&searches);
374 }
375 }
376
377 [Fact]
378 void MsiProductSearchTest()
379 {
380 HRESULT hr = S_OK;
381 IXMLDOMElement* pixeBundle = NULL;
382 BURN_VARIABLES variables = { };
383 BURN_SEARCHES searches = { };
384 BURN_EXTENSIONS burnExtensions = { };
385 try
386 {
387 hr = VariableInitialize(&variables);
388 TestThrowOnFailure(hr, L"Failed to initialize variables.");
389
390 // set mock API's
391 WiuFunctionOverride(NULL, NULL, NULL, NULL, MsiProductSearchTest_MsiGetProductInfoW, MsiProductSearchTest_MsiGetProductInfoExW, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
392
393 LPCWSTR wzDocument =
394 L"<Bundle>"
395 L" <MsiProductSearch Id='Search1' Type='state' ProductCode='{BAD00000-0000-0000-0000-000000000000}' Variable='Variable1' />"
396 L" <MsiProductSearch Id='Search2' Type='version' ProductCode='{600D0000-0000-0000-0000-000000000000}' Variable='Variable2' />"
397 L" <MsiProductSearch Id='Search3' Type='language' ProductCode='{600D0000-0000-0000-0000-000000000000}' Variable='Variable3' />"
398 L" <MsiProductSearch Id='Search4' Type='state' ProductCode='{600D0000-0000-0000-0000-000000000000}' Variable='Variable4' />"
399 L" <MsiProductSearch Id='Search5' Type='assignment' ProductCode='{600D0000-0000-0000-0000-000000000000}' Variable='Variable5' />"
400 L" <MsiProductSearch Id='Search6' Type='version' ProductCode='{600D0000-1000-0000-0000-000000000000}' Variable='Variable6' />"
401 L"</Bundle>";
402
403 // load XML document
404 LoadBundleXmlHelper(wzDocument, &pixeBundle);
405
406 hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle);
407 TestThrowOnFailure(hr, L"Failed to parse searches from XML.");
408
409 // execute searches
410 hr = SearchesExecute(&searches, &variables);
411 TestThrowOnFailure(hr, L"Failed to execute searches.");
412
413 // check variable values
414 Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable1"));
415 Assert::Equal<String^>(gcnew String(L"1.0.0.0"), VariableGetVersionHelper(&variables, L"Variable2"));
416 Assert::Equal(1033ll, VariableGetNumericHelper(&variables, L"Variable3"));
417 Assert::Equal(5ll, VariableGetNumericHelper(&variables, L"Variable4"));
418 Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable5"));
419 Assert::Equal<String^>(gcnew String(L"1.0.0.0"), VariableGetVersionHelper(&variables, L"Variable6"));
420 }
421 finally
422 {
423 ReleaseObject(pixeBundle);
424 VariablesUninitialize(&variables);
425 SearchesUninitialize(&searches);
426 }
427 }
428
429 [Fact]
430 void MsiFeatureSearchTest()
431 {
432 HRESULT hr = S_OK;
433 IXMLDOMElement* pixeBundle = NULL;
434 BURN_VARIABLES variables = { };
435 BURN_SEARCHES searches = { };
436 BURN_EXTENSIONS burnExtensions = { };
437 try
438 {
439 LPCWSTR wzDocument =
440 L"<Bundle>"
441 L" <MsiFeatureSearch Id='Search1' Type='state' ProductCode='{BAD00000-0000-0000-0000-000000000000}' FeatureId='' Variable='Variable1' />"
442 L"</Bundle>";
443
444 hr = VariableInitialize(&variables);
445 TestThrowOnFailure(hr, L"Failed to initialize variables.");
446
447 // load XML document
448 LoadBundleXmlHelper(wzDocument, &pixeBundle);
449
450 hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle);
451 TestThrowOnFailure(hr, L"Failed to parse searches from XML.");
452
453 // execute searches
454 hr = SearchesExecute(&searches, &variables);
455 TestThrowOnFailure(hr, L"Failed to execute searches.");
456 }
457 finally
458 {
459 ReleaseObject(pixeBundle);
460 VariablesUninitialize(&variables);
461 SearchesUninitialize(&searches);
462 }
463 }
464
465 [Fact]
466 void ConditionalSearchTest()
467 {
468 HRESULT hr = S_OK;
469 IXMLDOMElement* pixeBundle = NULL;
470 BURN_VARIABLES variables = { };
471 BURN_SEARCHES searches = { };
472 BURN_EXTENSIONS burnExtensions = { };
473 try
474 {
475 LPCWSTR wzDocument =
476 L"<Bundle>"
477 L" <RegistrySearch Id='Search1' Type='exists' Root='HKLM' Key='SOFTWARE\\Microsoft' Variable='Variable1' Condition='0' />"
478 L" <RegistrySearch Id='Search2' Type='exists' Root='HKLM' Key='SOFTWARE\\Microsoft' Variable='Variable2' Condition='1' />"
479 L" <RegistrySearch Id='Search3' Type='exists' Root='HKLM' Key='SOFTWARE\\Microsoft' Variable='Variable3' Condition='=' />"
480 L"</Bundle>";
481
482 hr = VariableInitialize(&variables);
483 TestThrowOnFailure(hr, L"Failed to initialize variables.");
484
485 // load XML document
486 LoadBundleXmlHelper(wzDocument, &pixeBundle);
487
488 hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle);
489 TestThrowOnFailure(hr, L"Failed to parse searches from XML.");
490
491 // execute searches
492 hr = SearchesExecute(&searches, &variables);
493 TestThrowOnFailure(hr, L"Failed to execute searches.");
494
495 // check variable values
496 Assert::False(VariableExistsHelper(&variables, L"Variable1"));
497 Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable2"));
498 Assert::False(VariableExistsHelper(&variables, L"Variable3"));
499 }
500 finally
501 {
502 ReleaseObject(pixeBundle);
503 VariablesUninitialize(&variables);
504 SearchesUninitialize(&searches);
505 }
506 }
507 [Fact]
508 void NoSearchesTest()
509 {
510 HRESULT hr = S_OK;
511 IXMLDOMElement* pixeBundle = NULL;
512 BURN_VARIABLES variables = { };
513 BURN_SEARCHES searches = { };
514 BURN_EXTENSIONS burnExtensions = { };
515 try
516 {
517 LPCWSTR wzDocument =
518 L"<Bundle>"
519 L"</Bundle>";
520
521 hr = VariableInitialize(&variables);
522 TestThrowOnFailure(hr, L"Failed to initialize variables.");
523
524 // load XML document
525 LoadBundleXmlHelper(wzDocument, &pixeBundle);
526
527 hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle);
528 TestThrowOnFailure(hr, L"Failed to parse searches from XML.");
529
530 // execute searches
531 hr = SearchesExecute(&searches, &variables);
532 TestThrowOnFailure(hr, L"Failed to execute searches.");
533 }
534 finally
535 {
536 ReleaseObject(pixeBundle);
537 VariablesUninitialize(&variables);
538 SearchesUninitialize(&searches);
539 }
540 }
541
542 [Fact]
543 void SetVariableSearchTest()
544 {
545 HRESULT hr = S_OK;
546 IXMLDOMElement* pixeBundle = NULL;
547 BURN_VARIABLES variables = { };
548 BURN_SEARCHES searches = { };
549 BURN_EXTENSIONS burnExtensions = { };
550 try
551 {
552 LPCWSTR wzDocument =
553 L"<Bundle>"
554 L" <SetVariable Id='Search1' Type='string' Value='VAL1' Variable='PROP1' />"
555 L" <SetVariable Id='Search2' Type='numeric' Value='2' Variable='PROP2' />"
556 L" <SetVariable Id='Search3' Type='string' Value='VAL3' Variable='PROP3' />"
557 L" <SetVariable Id='Search4' Type='string' Value='VAL4' Variable='PROP4' />"
558 L" <SetVariable Id='Search5' Type='string' Value='VAL5' Variable='PROP5' />"
559 L" <SetVariable Id='Search6' Type='string' Value='VAL6' Variable='PROP6' />"
560 L" <SetVariable Id='Search7' Type='string' Value='7' Variable='PROP7' />"
561 L" <SetVariable Id='Search8' Type='version' Value='1.1.0.0' Variable='PROP8' />"
562 L" <SetVariable Id='Search9' Type='formatted' Value='[VAL9]' Variable='PROP9' />"
563 L" <SetVariable Id='Search10' Type='numeric' Value='42' Variable='OVERWRITTEN_STRING' />"
564 L" <SetVariable Id='Search11' Type='string' Value='NEW' Variable='OVERWRITTEN_NUMBER' />"
565 L" <SetVariable Id='Search12' Variable='REMOVED_NUMBER' />"
566 L"</Bundle>";
567
568 hr = VariableInitialize(&variables);
569 TestThrowOnFailure(hr, L"Failed to initialize variables.");
570
571 // set variables
572 VariableSetStringHelper(&variables, L"OVERWRITTEN_STRING", L"ORIGINAL", FALSE);
573 VariableSetNumericHelper(&variables, L"OVERWRITTEN_NUMBER", 5);
574 VariableSetNumericHelper(&variables, L"REMOVED_NUMBER", 22);
575
576 // load XML document
577 LoadBundleXmlHelper(wzDocument, &pixeBundle);
578
579 hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle);
580 TestThrowOnFailure(hr, L"Failed to parse searches from XML.");
581
582 // execute searches
583 hr = SearchesExecute(&searches, &variables);
584 TestThrowOnFailure(hr, L"Failed to execute searches.");
585
586 // check variable values
587 Assert::Equal<String^>(gcnew String(L"VAL1"), VariableGetStringHelper(&variables, L"PROP1"));
588 Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"PROP2"));
589 Assert::Equal<String^>(gcnew String(L"2"), VariableGetStringHelper(&variables, L"PROP2"));
590 Assert::Equal<String^>(gcnew String(L"VAL3"), VariableGetStringHelper(&variables, L"PROP3"));
591 Assert::Equal<String^>(gcnew String(L"VAL4"), VariableGetStringHelper(&variables, L"PROP4"));
592 Assert::Equal<String^>(gcnew String(L"VAL5"), VariableGetStringHelper(&variables, L"PROP5"));
593 Assert::Equal<String^>(gcnew String(L"VAL6"), VariableGetStringHelper(&variables, L"PROP6"));
594 Assert::Equal(7ll, VariableGetNumericHelper(&variables, L"PROP7"));
595 Assert::Equal<String^>(gcnew String(L"1.1.0.0"), VariableGetVersionHelper(&variables, L"PROP8"));
596 Assert::Equal<String^>(gcnew String(L"1.1.0.0"), VariableGetStringHelper(&variables, L"PROP8"));
597 Assert::Equal<String^>(gcnew String(L"[VAL9]"), VariableGetStringHelper(&variables, L"PROP9"));
598
599 Assert::Equal(42ll, VariableGetNumericHelper(&variables, L"OVERWRITTEN_STRING"));
600 Assert::Equal<String^>(gcnew String(L"NEW"), VariableGetStringHelper(&variables, L"OVERWRITTEN_NUMBER"));
601 Assert::Equal((int)BURN_VARIANT_TYPE_NONE, VariableGetTypeHelper(&variables, L"REMOVED_NUMBER"));
602 }
603 finally
604 {
605 ReleaseObject(pixeBundle);
606 VariablesUninitialize(&variables);
607 SearchesUninitialize(&searches);
608 }
609 }
610 };
611}
612}
613}
614}
615}
616
617
618static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiGetComponentPathW(
619 __in LPCWSTR szProduct,
620 __in LPCWSTR szComponent,
621 __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf,
622 __inout_opt LPDWORD pcchBuf
623 )
624{
625 INSTALLSTATE is = INSTALLSTATE_INVALIDARG;
626 String^ product = gcnew String(szProduct);
627
628 if (String::Equals(product, gcnew String(L"{BAD00000-0000-0000-0000-000000000000}")))
629 {
630 is = INSTALLSTATE_UNKNOWN;
631 }
632 else if (String::Equals(product, gcnew String(L"{600D0000-0000-0000-0000-000000000000}")))
633 {
634 is = MsiComponentSearchTest_MsiLocateComponentW(szComponent, lpPathBuf, pcchBuf);
635 }
636
637 return is;
638}
639
640static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiLocateComponentW(
641 __in LPCWSTR szComponent,
642 __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf,
643 __inout_opt LPDWORD pcchBuf
644 )
645{
646 HRESULT hr = S_OK;
647 INSTALLSTATE is = INSTALLSTATE_INVALIDARG;
648 String^ component = gcnew String(szComponent);
649 LPCWSTR wzValue = NULL;
650
651 if (String::Equals(component, gcnew String(L"{BAD00000-1000-0000-0000-000000000000}")))
652 {
653 is = INSTALLSTATE_UNKNOWN;
654 }
655 else if (String::Equals(component, gcnew String(L"{600D0000-1000-1000-0000-000000000000}")))
656 {
657 wzValue = L"C:\\directory\\file1.txt";
658 is = INSTALLSTATE_LOCAL;
659 }
660 else if (String::Equals(component, gcnew String(L"{600D0000-1000-2000-0000-000000000000}")))
661 {
662 wzValue = L"C:\\directory\\file2.txt";
663 is = INSTALLSTATE_SOURCE;
664 }
665 else if (String::Equals(component, gcnew String(L"{600D0000-1000-3000-0000-000000000000}")))
666 {
667 wzValue = L"C:\\directory\\file3.txt";
668 is = INSTALLSTATE_SOURCEABSENT;
669 }
670 else if (String::Equals(component, gcnew String(L"{600D0000-1000-4000-0000-000000000000}")))
671 {
672 wzValue = L"C:\\directory\\file4.txt";
673 is = INSTALLSTATE_ABSENT;
674 }
675 else if (String::Equals(component, gcnew String(L"{600D0000-1000-5000-0000-000000000000}")))
676 {
677 wzValue = L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\";
678 is = INSTALLSTATE_LOCAL;
679 }
680 else if (String::Equals(component, gcnew String(L"{600D0000-1000-6000-0000-000000000000}")))
681 {
682 wzValue = L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value";
683 is = INSTALLSTATE_LOCAL;
684 }
685 else if (String::Equals(component, gcnew String(L"{600D0000-1000-7000-0000-000000000000}")))
686 {
687 wzValue = L"C:\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\file5.txt";
688 is = INSTALLSTATE_ABSENT;
689 }
690
691 if (wzValue && lpPathBuf)
692 {
693 hr = ::StringCchCopyW(lpPathBuf, *pcchBuf, wzValue);
694 if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
695 {
696 *pcchBuf = lstrlenW(wzValue);
697 is = INSTALLSTATE_MOREDATA;
698 }
699 else if (FAILED(hr))
700 {
701 is = INSTALLSTATE_INVALIDARG;
702 }
703 }
704
705 return is;
706}
707
708static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoW(
709 __in LPCWSTR szProductCode,
710 __in LPCWSTR szProperty,
711 __out_ecount_opt(*pcchValue) LPWSTR szValue,
712 __inout_opt LPDWORD pcchValue
713 )
714{
715 if (String::Equals(gcnew String(szProductCode), gcnew String(L"{600D0000-0000-0000-0000-000000000000}")) &&
716 String::Equals(gcnew String(szProperty), gcnew String(INSTALLPROPERTY_PRODUCTSTATE)))
717 {
718 // force call to WiuGetProductInfoEx
719 return ERROR_UNKNOWN_PROPERTY;
720 }
721
722 UINT er = MsiProductSearchTest_MsiGetProductInfoExW(szProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, szProperty, szValue, pcchValue);
723 return er;
724}
725
726static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoExW(
727 __in LPCWSTR szProductCode,
728 __in_opt LPCWSTR /*szUserSid*/,
729 __in MSIINSTALLCONTEXT dwContext,
730 __in LPCWSTR szProperty,
731 __out_ecount_opt(*pcchValue) LPWSTR szValue,
732 __inout_opt LPDWORD pcchValue
733 )
734{
735 HRESULT hr = S_OK;
736 DWORD er = ERROR_FUNCTION_FAILED;
737 LPCWSTR wzValue = NULL;
738
739 String^ productCode = gcnew String(szProductCode);
740 String^ _property = gcnew String(szProperty);
741 switch (dwContext)
742 {
743 case MSIINSTALLCONTEXT_USERMANAGED:
744 er = ERROR_UNKNOWN_PRODUCT;
745 break;
746 case MSIINSTALLCONTEXT_USERUNMANAGED:
747 if (String::Equals(productCode, gcnew String(L"{600D0000-0000-0000-0000-000000000000}")))
748 {
749 if (String::Equals(_property, gcnew String(INSTALLPROPERTY_PRODUCTSTATE)))
750 {
751 wzValue = L"5";
752 }
753 }
754 break;
755 case MSIINSTALLCONTEXT_MACHINE:
756 if (String::Equals(productCode, gcnew String(L"{BAD00000-0000-0000-0000-000000000000}")))
757 {
758 er = ERROR_UNKNOWN_PRODUCT;
759 }
760 else if (String::Equals(productCode, gcnew String(L"{600D0000-0000-0000-0000-000000000000}")))
761 {
762 if (String::Equals(_property, gcnew String(INSTALLPROPERTY_VERSIONSTRING)))
763 {
764 wzValue = L"1.0.0.0";
765 }
766 else if (String::Equals(_property, gcnew String(INSTALLPROPERTY_LANGUAGE)))
767 {
768 wzValue = L"1033";
769 }
770 else if (String::Equals(_property, gcnew String(INSTALLPROPERTY_ASSIGNMENTTYPE)))
771 {
772 wzValue = L"1";
773 }
774 else if (String::Equals(_property, gcnew String(INSTALLPROPERTY_PRODUCTSTATE)))
775 {
776 // try again in per-user context
777 er = ERROR_UNKNOWN_PRODUCT;
778 }
779 }
780 else if (String::Equals(productCode, gcnew String(L"{600D0000-1000-0000-0000-000000000000}")))
781 {
782 static BOOL fFlipp = FALSE;
783 if (fFlipp)
784 {
785 if (String::Equals(_property, gcnew String(INSTALLPROPERTY_VERSIONSTRING)))
786 {
787 wzValue = L"1.0.0.0";
788 }
789 }
790 else
791 {
792 *pcchValue = MAX_PATH * 2;
793 er = ERROR_MORE_DATA;
794 }
795 fFlipp = !fFlipp;
796 }
797 break;
798 }
799
800 if (wzValue)
801 {
802 hr = ::StringCchCopyW(szValue, *pcchValue, wzValue);
803 if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
804 {
805 *pcchValue = lstrlenW(wzValue);
806 er = ERROR_MORE_DATA;
807 }
808 else if (SUCCEEDED(hr))
809 {
810 er = ERROR_SUCCESS;
811 }
812 }
813
814 return er;
815}
diff --git a/src/burn/test/BurnUnitTest/TestData/CacheTest/CacheSignatureTest.File b/src/burn/test/BurnUnitTest/TestData/CacheTest/CacheSignatureTest.File
new file mode 100644
index 00000000..896ac017
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/TestData/CacheTest/CacheSignatureTest.File
@@ -0,0 +1 @@
This file has a known hash. \ No newline at end of file
diff --git a/src/burn/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml b/src/burn/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml
new file mode 100644
index 00000000..65e3c63d
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml
@@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8"?><BurnManifest xmlns="http://wixtoolset.org/schemas/v4/2008/Burn"><Log PathVariable="WixBundleLog" Prefix="~BasicFunctionalityTests_BundleA" Extension=".log" /><RelatedBundle Id="{8C7E2C47-1EE7-4BBE-99A2-EAB7F3693F48}" Action="Upgrade" /><Variable Id="TestGroupName" Value="BasicFunctionalityTests" Type="string" Hidden="no" Persisted="no" /><Variable Id="WixBundleName" Hidden="no" Persisted="yes" /><Variable Id="WixBundleOriginalSource" Hidden="no" Persisted="yes" /><Variable Id="WixBundleOriginalSourceFolder" Hidden="no" Persisted="yes" /><Variable Id="WixBundleLastUsedSource" Hidden="no" Persisted="yes" /><UX><Payload Id="WixStandardBootstrapperApplication" FilePath="wixstdba.dll" FileSize="245760" Hash="23F0568ADACD69D72B259F876B437707A0D41069" Packaging="embedded" SourcePath="u3" /><Payload Id="pay00kQk8rVqabvZJ20B.w1mpx7GDo" FilePath="thm.xml" FileSize="7980" Hash="7A88582165EEE4CA1D23F1B7DD58F8023552E049" Packaging="embedded" SourcePath="u0" /><Payload Id="payI2_GHsNfx8LnXWC6YRRG.VuyhI4" FilePath="thm.wxl" FileSize="4194" Hash="906294A9515835C5C8F4C5E86A32E179041C90DD" Packaging="embedded" SourcePath="u1" /><Payload Id="payjqSD44latbvJnf4vAQuVMUST73A" FilePath="logo.png" FileSize="852" Hash="239F10674BF6022854C1F1BF7C91955BDE34D3E4" Packaging="embedded" SourcePath="u2" /><Payload Id="uxTxMXPVMXwQrPTMIGa5WGt93w0Ns" FilePath="BootstrapperApplicationData.xml" FileSize="3698" Hash="AADECC6EF50E87D0642A5667CD612EF53E2CFB9A" Packaging="embedded" SourcePath="u4" /><Payload Id="uxYRbgitOs0K878jn5L_z7LdJ21KI" FilePath="BundleExtensionData.xml" FileSize="252" Hash="86688B13D3364ADB90BBA552F544D4D546AFD63D" Packaging="embedded" SourcePath="u5" /></UX><Container Id="WixAttachedContainer" FileSize="6959" Hash="4FC82B3432B5892D2A4EC593264A916DBDA9CE45" FilePath="BundleA.exe" AttachedIndex="1" Attached="yes" Primary="yes" /><Payload Id="PackageA" FilePath="PackageA.msi" FileSize="32768" Hash="89C61F8A105A81B08036401152A1FDE67CDC0158" Packaging="embedded" SourcePath="a0" Container="WixAttachedContainer" /><Payload Id="cab9Ins_fTP3wNwq5Gxo41ch5VUPaQ" FilePath="1a.cab" FileSize="975" Hash="11DE5863C4B2A8762D0EE23FE25B7774CA07676B" Packaging="embedded" SourcePath="a1" Container="WixAttachedContainer" /><RollbackBoundary Id="WixDefaultBoundary" Vital="yes" Transaction="no" /><Registration Id="{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}" ExecutableName="BundleA.exe" PerMachine="yes" Tag="" Version="1.0.0.0" ProviderKey="{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}"><Arp Register="yes" DisplayName="~BasicFunctionalityTests - BundleA" DisplayVersion="1.0.0.0" /></Registration><Chain><MsiPackage Id="PackageA" Cache="keep" CacheId="{64633047-D172-4BBB-B202-64337D15C952}v1.0.0.0" InstallSize="1951" Size="33743" PerMachine="yes" Permanent="no" Vital="yes" RollbackBoundaryForward="WixDefaultBoundary" RollbackBoundaryBackward="WixDefaultBoundary" LogPathVariable="WixBundleLog_PackageA" RollbackLogPathVariable="WixBundleRollbackLog_PackageA" ProductCode="{64633047-D172-4BBB-B202-64337D15C952}" Language="1033" Version="1.0.0.0" UpgradeCode="{7FD50F1B-D134-4365-923C-DFA160F74738}"><MsiProperty Id="ARPSYSTEMCOMPONENT" Value="1" /><MsiProperty Id="MSIFASTINSTALL" Value="7" /><Provides Key="{64633047-D172-4BBB-B202-64337D15C952}" Version="1.0.0.0" DisplayName="~BasicFunctionalityTests - PackageA" /><RelatedPackage Id="{7FD50F1B-D134-4365-923C-DFA160F74738}" MaxVersion="1.0.0.0" MaxInclusive="no" OnlyDetect="no" LangInclusive="no"><Language Id="1033" /></RelatedPackage><RelatedPackage Id="{7FD50F1B-D134-4365-923C-DFA160F74738}" MinVersion="1.0.0.0" MinInclusive="no" OnlyDetect="yes" LangInclusive="no"><Language Id="1033" /></RelatedPackage><PayloadRef Id="PackageA" /><PayloadRef Id="cab9Ins_fTP3wNwq5Gxo41ch5VUPaQ" /></MsiPackage></Chain></BurnManifest> \ No newline at end of file
diff --git a/src/burn/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml b/src/burn/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml
new file mode 100644
index 00000000..cca9a982
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml
@@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8"?><BurnManifest xmlns="http://wixtoolset.org/schemas/v4/2008/Burn"><Log PathVariable="WixBundleLog" Prefix="~MsiTransactionTests_BundleAv1" Extension=".log" /><RelatedBundle Id="{90ED10D5-B187-4470-B498-05D80DAB729A}" Action="Upgrade" /><Variable Id="TestGroupName" Value="MsiTransactionTests" Type="string" Hidden="no" Persisted="no" /><Variable Id="WixBundleName" Hidden="no" Persisted="yes" /><Variable Id="WixBundleOriginalSource" Hidden="no" Persisted="yes" /><Variable Id="WixBundleOriginalSourceFolder" Hidden="no" Persisted="yes" /><Variable Id="WixBundleLastUsedSource" Hidden="no" Persisted="yes" /><UX><Payload Id="WixStandardBootstrapperApplication" FilePath="wixstdba.dll" FileSize="245760" Hash="23F0568ADACD69D72B259F876B437707A0D41069" Packaging="embedded" SourcePath="u3" /><Payload Id="pay00kQk8rVqabvZJ20B.w1mpx7GDo" FilePath="thm.xml" FileSize="7980" Hash="7A88582165EEE4CA1D23F1B7DD58F8023552E049" Packaging="embedded" SourcePath="u0" /><Payload Id="payI2_GHsNfx8LnXWC6YRRG.VuyhI4" FilePath="thm.wxl" FileSize="4194" Hash="906294A9515835C5C8F4C5E86A32E179041C90DD" Packaging="embedded" SourcePath="u1" /><Payload Id="payjqSD44latbvJnf4vAQuVMUST73A" FilePath="logo.png" FileSize="852" Hash="239F10674BF6022854C1F1BF7C91955BDE34D3E4" Packaging="embedded" SourcePath="u2" /><Payload Id="uxTxMXPVMXwQrPTMIGa5WGt93w0Ns" FilePath="BootstrapperApplicationData.xml" FileSize="6742" Hash="E124C9502891F1277A47D1AEC0F1755BA605E6E3" Packaging="embedded" SourcePath="u4" /><Payload Id="uxYRbgitOs0K878jn5L_z7LdJ21KI" FilePath="BundleExtensionData.xml" FileSize="252" Hash="86688B13D3364ADB90BBA552F544D4D546AFD63D" Packaging="embedded" SourcePath="u5" /></UX><Container Id="WixAttachedContainer" FileSize="16403" Hash="AABC770A92954AE4234A322A3621333B3FDDE225" FilePath="BundleAv1.exe" AttachedIndex="1" Attached="yes" Primary="yes" /><Payload Id="PackageA" FilePath="PackageA.msi" FileSize="32768" Hash="C763E00CD117F79643F58442B87F51721554686D" Packaging="embedded" SourcePath="a0" Container="WixAttachedContainer" /><Payload Id="PackageB" FilePath="PackageBv1.msi" FileSize="32768" Hash="25570B420BD65BE187AB56B695A0CC36712A224F" Packaging="embedded" SourcePath="a1" Container="WixAttachedContainer" /><Payload Id="PackageC" FilePath="PackageCv1.msi" FileSize="32768" Hash="23472E6C185E9B3DC9C53F92435D1C4F018C06DB" Packaging="embedded" SourcePath="a2" Container="WixAttachedContainer" /><Payload Id="cab9Ins_fTP3wNwq5Gxo41ch5VUPaQ" FilePath="1a.cab" FileSize="975" Hash="11DE5863C4B2A8762D0EE23FE25B7774CA07676B" Packaging="embedded" SourcePath="a3" Container="WixAttachedContainer" /><Payload Id="cablKtJUKxAbhSMIBwQU6vJ_CDsIkE" FilePath="1bv1.cab" FileSize="975" Hash="11DE5863C4B2A8762D0EE23FE25B7774CA07676B" Packaging="embedded" SourcePath="a4" Container="WixAttachedContainer" /><Payload Id="cab3wekki1le1R8RPDV2B8_g8jcjZc" FilePath="1cv1.cab" FileSize="975" Hash="11DE5863C4B2A8762D0EE23FE25B7774CA07676B" Packaging="embedded" SourcePath="a5" Container="WixAttachedContainer" /><RollbackBoundary Id="WixDefaultBoundary" Vital="yes" Transaction="no" /><RollbackBoundary Id="rbaOCA08D8ky7uBOK71_6FWz1K3TuQ" Vital="yes" Transaction="yes" /><Registration Id="{E6469F05-BDC8-4EB8-B218-67412543EFAA}" ExecutableName="BundleAv1.exe" PerMachine="yes" Tag="" Version="1.0.0.0" ProviderKey="{E6469F05-BDC8-4EB8-B218-67412543EFAA}"><Arp Register="yes" DisplayName="~MsiTransactionTests - BundleAv1" DisplayVersion="1.0.0.0" /></Registration><Chain><MsiPackage Id="PackageA" Cache="keep" CacheId="{01E6B748-7B95-4BA9-976D-B6F35076CEF4}v1.0.0.0" InstallSize="1951" Size="33743" PerMachine="yes" Permanent="no" Vital="yes" RollbackBoundaryForward="WixDefaultBoundary" RollbackBoundaryBackward="WixDefaultBoundary" LogPathVariable="WixBundleLog_PackageA" RollbackLogPathVariable="WixBundleRollbackLog_PackageA" ProductCode="{01E6B748-7B95-4BA9-976D-B6F35076CEF4}" Language="1033" Version="1.0.0.0" UpgradeCode="{7772FCDF-5FDB-497D-B5DF-C6D17D667976}"><MsiProperty Id="ARPSYSTEMCOMPONENT" Value="1" /><MsiProperty Id="MSIFASTINSTALL" Value="7" /><Provides Key="{01E6B748-7B95-4BA9-976D-B6F35076CEF4}" Version="1.0.0.0" DisplayName="~MsiTransactionTests - PackageA" /><RelatedPackage Id="{7772FCDF-5FDB-497D-B5DF-C6D17D667976}" MaxVersion="1.0.0.0" MaxInclusive="no" OnlyDetect="no" LangInclusive="no"><Language Id="1033" /></RelatedPackage><RelatedPackage Id="{7772FCDF-5FDB-497D-B5DF-C6D17D667976}" MinVersion="1.0.0.0" MinInclusive="no" OnlyDetect="yes" LangInclusive="no"><Language Id="1033" /></RelatedPackage><PayloadRef Id="PackageA" /><PayloadRef Id="cab9Ins_fTP3wNwq5Gxo41ch5VUPaQ" /></MsiPackage><MsiPackage Id="PackageB" Cache="keep" CacheId="{D1D01094-23CE-4AF0-84B6-4A1A133F21D3}v1.0.0.0" InstallSize="1951" Size="33743" PerMachine="yes" Permanent="no" Vital="yes" RollbackBoundaryForward="rbaOCA08D8ky7uBOK71_6FWz1K3TuQ" LogPathVariable="WixBundleLog_PackageB" RollbackLogPathVariable="WixBundleRollbackLog_PackageB" ProductCode="{D1D01094-23CE-4AF0-84B6-4A1A133F21D3}" Language="1033" Version="1.0.0.0" UpgradeCode="{EAFC0C6B-626E-415C-8132-536FBD19F49B}"><MsiProperty Id="ARPSYSTEMCOMPONENT" Value="1" /><MsiProperty Id="MSIFASTINSTALL" Value="7" /><Provides Key="{D1D01094-23CE-4AF0-84B6-4A1A133F21D3}" Version="1.0.0.0" DisplayName="~MsiTransactionTests - PackageBv1" /><RelatedPackage Id="{EAFC0C6B-626E-415C-8132-536FBD19F49B}" MaxVersion="1.0.0.0" MaxInclusive="no" OnlyDetect="no" LangInclusive="no"><Language Id="1033" /></RelatedPackage><RelatedPackage Id="{EAFC0C6B-626E-415C-8132-536FBD19F49B}" MinVersion="1.0.0.0" MinInclusive="no" OnlyDetect="yes" LangInclusive="no"><Language Id="1033" /></RelatedPackage><PayloadRef Id="PackageB" /><PayloadRef Id="cablKtJUKxAbhSMIBwQU6vJ_CDsIkE" /></MsiPackage><MsiPackage Id="PackageC" Cache="keep" CacheId="{A497C5E5-C78B-4F0B-BF72-B33E1DB1C4B8}v1.0.0.0" InstallSize="1951" Size="33743" PerMachine="yes" Permanent="no" Vital="yes" RollbackBoundaryBackward="rbaOCA08D8ky7uBOK71_6FWz1K3TuQ" LogPathVariable="WixBundleLog_PackageC" RollbackLogPathVariable="WixBundleRollbackLog_PackageC" ProductCode="{A497C5E5-C78B-4F0B-BF72-B33E1DB1C4B8}" Language="1033" Version="1.0.0.0" UpgradeCode="{A18BDC12-DAEC-43EE-87D1-31B2C2BC6269}"><MsiProperty Id="ARPSYSTEMCOMPONENT" Value="1" /><MsiProperty Id="MSIFASTINSTALL" Value="7" /><Provides Key="{A497C5E5-C78B-4F0B-BF72-B33E1DB1C4B8}" Version="1.0.0.0" DisplayName="~MsiTransactionTests - PackageCv1" /><RelatedPackage Id="{A18BDC12-DAEC-43EE-87D1-31B2C2BC6269}" MaxVersion="1.0.0.0" MaxInclusive="no" OnlyDetect="no" LangInclusive="no"><Language Id="1033" /></RelatedPackage><RelatedPackage Id="{A18BDC12-DAEC-43EE-87D1-31B2C2BC6269}" MinVersion="1.0.0.0" MinInclusive="no" OnlyDetect="yes" LangInclusive="no"><Language Id="1033" /></RelatedPackage><PayloadRef Id="PackageC" /><PayloadRef Id="cab3wekki1le1R8RPDV2B8_g8jcjZc" /></MsiPackage></Chain></BurnManifest> \ No newline at end of file
diff --git a/src/burn/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml b/src/burn/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml
new file mode 100644
index 00000000..996976b2
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml
@@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8"?><BurnManifest xmlns="http://wixtoolset.org/schemas/v4/2008/Burn"><Log PathVariable="WixBundleLog" Prefix="~SlipstreamTests_BundleA" Extension=".log" /><RelatedBundle Id="{62C28DAF-A13E-4F55-ACA1-FB843630789C}" Action="Upgrade" /><Variable Id="TestGroupName" Value="SlipstreamTests" Type="string" Hidden="no" Persisted="no" /><Variable Id="WixBundleName" Hidden="no" Persisted="yes" /><Variable Id="WixBundleOriginalSource" Hidden="no" Persisted="yes" /><Variable Id="WixBundleOriginalSourceFolder" Hidden="no" Persisted="yes" /><Variable Id="WixBundleLastUsedSource" Hidden="no" Persisted="yes" /><RegistrySearch Id="NETFRAMEWORK45" Variable="NETFRAMEWORK45" Root="HKLM" Key="SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" Value="Release" Type="value" VariableType="string" /><UX><Payload Id="WixManagedBootstrapperApplicationHost" FilePath="mbahost.dll" FileSize="140288" Hash="4569C53566B1025E243E0C29A96C608BD4019979" Packaging="embedded" SourcePath="u30" /><Payload Id="payO60IVK4ATGzPpMz3rwVbUWl6DyU" FilePath="WixToolset.Mba.Host.config" FileSize="783" Hash="B5BDD5E7179A94C2C817069913CA8C099DF811B9" Packaging="embedded" SourcePath="u0" /><Payload Id="payxj4zDAKL2NVlz4ohp0GvwFHepyI" FilePath="TestBA.dll" FileSize="25088" Hash="DB12DB6565CDBC4E9705204830E421ACEB710129" Packaging="embedded" SourcePath="u1" /><Payload Id="pay1hOSAUC8_D633cD2TXpIXCL30OU" FilePath="mbanative.dll" FileSize="118272" Hash="3A7A20D97B0546A23A025EE5774BE237C14D2957" Packaging="embedded" SourcePath="u2" /><Payload Id="payujy6Izl_BlUNfHt2eI.ADfjYAv4" FilePath="WixToolset.Mba.Core.dll" FileSize="114688" Hash="56BA3EA94BEBF8EB562C914495E1594E74F05DBE" Packaging="embedded" SourcePath="u3" /><Payload Id="payR4EbR4OTDZpPEycWaSSM_gZRBWM" FilePath="mbapreq.thm" FileSize="3599" Hash="8D9797C1E1A50AECB8B85FFCEA6A2A2EF611BD7F" Packaging="embedded" SourcePath="u4" /><Payload Id="paylVCy2Ecl8pHPdJTCQZryUG4T9us" FilePath="mbapreq.png" FileSize="797" Hash="75AE41181581FD6376CA9CA88147011E48BF9A30" Packaging="embedded" SourcePath="u5" /><Payload Id="payTaG4B_lob1aLcKFaOqSSG3MPMpU" FilePath="mbapreq.wxl" FileSize="2237" Hash="068B3C5E27AECE7987EABAA2802C9EB07B39EAF8" Packaging="embedded" SourcePath="u6" /><Payload Id="payZwIGuiezVTitZOoZKxyh2DdRSGs" FilePath="1028\mbapreq.wxl" FileSize="1998" Hash="A989D9B892F497215D81F903591ECB6CD50CFFFC" Packaging="embedded" SourcePath="u7" /><Payload Id="pay.herBWX.LlOh8jLsx24aWdunV_0" FilePath="1029\mbapreq.wxl" FileSize="2428" Hash="E6B8E4B1AA89430EB6A5A1E997CA3D1D2F968285" Packaging="embedded" SourcePath="u8" /><Payload Id="pay8DkMszYsoxxdgX14huLDMYXylQg" FilePath="1030\mbapreq.wxl" FileSize="2256" Hash="612CD2FD0CF3800639385C0BF4D805B24507D356" Packaging="embedded" SourcePath="u9" /><Payload Id="payPaHpoTeOdkW.TK99IDwktNLhTAg" FilePath="1031\mbapreq.wxl" FileSize="2409" Hash="E59A8F11D95AC17FC70BD718706EE36BFA50EF02" Packaging="embedded" SourcePath="u10" /><Payload Id="pay45AtAzterLTMzZgdxxtuYvaiXwU" FilePath="1032\mbapreq.wxl" FileSize="3368" Hash="154E0A658BA7EE59889224A231423634A9725547" Packaging="embedded" SourcePath="u11" /><Payload Id="payA2VEKIqhePyNIEmr14eyH3JoVLc" FilePath="1035\mbapreq.wxl" FileSize="2205" Hash="6AAE55269E42F99A5D88ADD18C433384DEB9E956" Packaging="embedded" SourcePath="u12" /><Payload Id="payvre23ObscjzhcaFIifUAkXMdPa8" FilePath="1036\mbapreq.wxl" FileSize="2276" Hash="7DC74874357F50AE8C4871D8F4DC06B337CF6352" Packaging="embedded" SourcePath="u13" /><Payload Id="paytxUV3vuBbG2c.a9c.d_sZX2x6wA" FilePath="1038\mbapreq.wxl" FileSize="2362" Hash="B60C34DE38E6E48BA0841E8A962C17179FC1B69A" Packaging="embedded" SourcePath="u14" /><Payload Id="payYvMWRK9xelo5.sQn7jRkJIaBp9A" FilePath="1040\mbapreq.wxl" FileSize="2273" Hash="902D231AD6306087F215DEABB7F2AB2F8072C401" Packaging="embedded" SourcePath="u15" /><Payload Id="pay68KKSApyQimbA25t6kSbqhdeH10" FilePath="1041\mbapreq.wxl" FileSize="2518" Hash="4095A1AFCF18C01F7DA51A1A389C2FBBB1A82A12" Packaging="embedded" SourcePath="u16" /><Payload Id="paypiqxaHpYZqx.9eDVjQrj1igLbRY" FilePath="1042\mbapreq.wxl" FileSize="2209" Hash="99CE8B42300EF656E6BD44F01766DC638CB0496F" Packaging="embedded" SourcePath="u17" /><Payload Id="payTO0YwZzxKpbqdrBVUcVRTu3BFe8" FilePath="1043\mbapreq.wxl" FileSize="2282" Hash="87117EE32E0004E25DDCEB1A7D417F3A02856A50" Packaging="embedded" SourcePath="u18" /><Payload Id="payIXg2ldBJukRzhqWolJVOEbTmF34" FilePath="1044\mbapreq.wxl" FileSize="2141" Hash="5AED841C6A870C3A8BAF8A10D00F887A781D0CF0" Packaging="embedded" SourcePath="u19" /><Payload Id="payOHIZbSkIvrpwKkkXI173tv3u3B4" FilePath="1045\mbapreq.wxl" FileSize="2338" Hash="07E37CBC59298F24A5C8C3B8FEB7A45DADF8CD07" Packaging="embedded" SourcePath="u20" /><Payload Id="payQRQ_UZl_R2UtV0xDXB2yeH2bg3E" FilePath="1046\mbapreq.wxl" FileSize="2118" Hash="AEC0CE51E8E335E9B86F1AC7E39CCD172B896582" Packaging="embedded" SourcePath="u21" /><Payload Id="payhrejLLBfc1i27iN._QPhQ4K337I" FilePath="1049\mbapreq.wxl" FileSize="2851" Hash="9628BADB173B171ED85D902634D9AA5D91FE9721" Packaging="embedded" SourcePath="u22" /><Payload Id="payqEzaDNzxB68vGp29jgDcCos6dvg" FilePath="1051\mbapreq.wxl" FileSize="2304" Hash="B584E8C0D7F9B7A1BB70BC00E42BFD35BED5D81D" Packaging="embedded" SourcePath="u23" /><Payload Id="paydz8Vk8xSTyYohgGXTSIxWGXL5.Q" FilePath="1053\mbapreq.wxl" FileSize="2102" Hash="67E93F555DBFEF8508E79F7CA8CE76B881308760" Packaging="embedded" SourcePath="u24" /><Payload Id="pay0HRUZTlbC3taSOffJBsEj92Br8Y" FilePath="1055\mbapreq.wxl" FileSize="2273" Hash="AEB8C90D66942A5CD73EA52A6F2ADD4F7D518A0D" Packaging="embedded" SourcePath="u25" /><Payload Id="payIvUOkc_EMH7laMFehefNolV8hZo" FilePath="1060\mbapreq.wxl" FileSize="2170" Hash="B1D4B71907B8BD82DD8B047404AF10FDBBE5CBA0" Packaging="embedded" SourcePath="u26" /><Payload Id="payLFhOb.rHuk4sW5CYAPMShG0NjGI" FilePath="2052\mbapreq.wxl" FileSize="1953" Hash="C8FB8982EC71C48D6EA021ADD9AAA7BCB0656281" Packaging="embedded" SourcePath="u27" /><Payload Id="payqIKCmERK7Nhxx_nNXvRxdKqKDbI" FilePath="2070\mbapreq.wxl" FileSize="2182" Hash="825F27A543907ED27E815EC67DFD48AF7BF5831E" Packaging="embedded" SourcePath="u28" /><Payload Id="payqeWUzIVaEqjuRXN0z8ECC3Y4tCc" FilePath="3082\mbapreq.wxl" FileSize="2369" Hash="39C07C31077AAFDC0DD208273AA41654CAD80FDD" Packaging="embedded" SourcePath="u29" /><Payload Id="paylfeHEjJSSTnNzY9QMZM2Ye3Ipy4" FilePath="mbapreq.dll" FileSize="245760" Hash="6499FA21D178131DDE13A4EF44ABEC32E91D65D4" Packaging="embedded" SourcePath="u31" /><Payload Id="payDPxs6uy8nbky.R7zhir2RRAfc.c" FilePath="WixToolset.Mba.Host.dll" FileSize="11264" Hash="9E6452891E401EB211DD41550A09FDF98EC0992F" Packaging="embedded" SourcePath="u32" /><Payload Id="uxTxMXPVMXwQrPTMIGa5WGt93w0Ns" FilePath="BootstrapperApplicationData.xml" FileSize="14292" Hash="CDF09A0723F4F33C13670BBAFCFFA7E660E15DFC" Packaging="embedded" SourcePath="u33" /><Payload Id="uxYRbgitOs0K878jn5L_z7LdJ21KI" FilePath="BundleExtensionData.xml" FileSize="252" Hash="86688B13D3364ADB90BBA552F544D4D546AFD63D" Packaging="embedded" SourcePath="u34" /></UX><Payload Id="NetFx48Web" FilePath="redist\ndp48-web.exe" FileSize="1479400" Hash="5A84A8E612E270E27D0061D58DB6B470153BE1F9" DownloadUrl="https://go.microsoft.com/fwlink/?LinkId=2085155" Packaging="external" SourcePath="redist\ndp48-web.exe" /><Payload Id="PackageA" FilePath="PackageAv1.msi" FileSize="32768" Hash="2369B16B7219B3C834DFBC5D2AF8B2EF8803D43D" Packaging="external" SourcePath="PackageAv1.msi" /><Payload Id="PatchA" FilePath="PatchA.msp" FileSize="20480" Hash="FABC6C18E4A778E127E84CDF67F93A291CAEC8BB" Packaging="external" SourcePath="PatchA.msp" /><RollbackBoundary Id="WixDefaultBoundary" Vital="yes" Transaction="no" /><Registration Id="{22D1DDBA-284D-40A7-BD14-95EA07906F21}" ExecutableName="BundleA.exe" PerMachine="yes" Tag="" Version="1.0.0.0" ProviderKey="{22D1DDBA-284D-40A7-BD14-95EA07906F21}"><Arp Register="yes" DisplayName="~SlipstreamTests - BundleA" DisplayVersion="1.0.0.0" /></Registration><Chain><ExePackage Id="NetFx48Web" Cache="keep" CacheId="5A84A8E612E270E27D0061D58DB6B470153BE1F9" InstallSize="1479400" Size="1479400" PerMachine="yes" Permanent="yes" Vital="yes" RollbackBoundaryForward="WixDefaultBoundary" LogPathVariable="NetFx48WebLog" RollbackLogPathVariable="WixBundleRollbackLog_NetFx48Web" DetectCondition="NETFRAMEWORK45 &gt;= 528040" InstallArguments="/q /norestart /ChainingPackage &quot;[WixBundleName]&quot; /log &quot;[NetFx48WebLog].html&quot;" UninstallArguments="/uninstall /q /norestart /ChainingPackage &quot;[WixBundleName]&quot; /log &quot;[NetFx48WebLog].html&quot;" RepairArguments="/q /norestart /repair /ChainingPackage &quot;[WixBundleName]&quot; /log &quot;[NetFx48WebLog].html&quot;" Repairable="yes" Protocol="netfx4"><PayloadRef Id="NetFx48Web" /></ExePackage><MsiPackage Id="PackageA" Cache="keep" CacheId="{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}v1.0.0.0" InstallSize="2103" Size="32768" PerMachine="yes" Permanent="no" Vital="yes" LogPathVariable="WixBundleLog_PackageA" RollbackLogPathVariable="WixBundleRollbackLog_PackageA" ProductCode="{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}" Language="1033" Version="1.0.0.0" UpgradeCode="{DB87BB66-FE5D-4293-81AC-EE313D3F864B}"><MsiProperty Id="ARPSYSTEMCOMPONENT" Value="1" /><MsiProperty Id="MSIFASTINSTALL" Value="7" /><SlipstreamMsp Id="PatchA" /><Provides Key="{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}" Version="1.0.0.0" DisplayName="~SlipstreamTests - PackageA" /><RelatedPackage Id="{DB87BB66-FE5D-4293-81AC-EE313D3F864B}" MaxVersion="1.0.0.0" MaxInclusive="no" OnlyDetect="no" LangInclusive="no"><Language Id="1033" /></RelatedPackage><RelatedPackage Id="{DB87BB66-FE5D-4293-81AC-EE313D3F864B}" MinVersion="1.0.0.0" MinInclusive="no" OnlyDetect="yes" LangInclusive="no"><Language Id="1033" /></RelatedPackage><PayloadRef Id="PackageA" /></MsiPackage><MspPackage Id="PatchA" Cache="keep" CacheId="{0A5113E3-06A5-4CE0-8E83-9EB42F6764A6}" InstallSize="20480" Size="20480" PerMachine="yes" Permanent="no" Vital="yes" RollbackBoundaryBackward="WixDefaultBoundary" LogPathVariable="WixBundleLog_PatchA" RollbackLogPathVariable="WixBundleRollbackLog_PatchA" PatchCode="{0A5113E3-06A5-4CE0-8E83-9EB42F6764A6}" PatchXml="&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-16&quot;?&gt;&lt;MsiPatch xmlns=&quot;http://www.microsoft.com/msi/patch_applicability.xsd&quot; SchemaVersion=&quot;1.0.0.0&quot; PatchGUID=&quot;{0A5113E3-06A5-4CE0-8E83-9EB42F6764A6}&quot; MinMsiVersion=&quot;5&quot; TargetsRTM=&quot;true&quot;&gt;&lt;TargetProduct MinMsiVersion=&quot;500&quot;&gt;&lt;TargetProductCode Validate=&quot;true&quot;&gt;{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}&lt;/TargetProductCode&gt;&lt;TargetVersion Validate=&quot;true&quot; ComparisonType=&quot;Equal&quot; ComparisonFilter=&quot;MajorMinorUpdate&quot;&gt;1.0.0.0&lt;/TargetVersion&gt;&lt;UpdatedVersion&gt;1.0.1.0&lt;/UpdatedVersion&gt;&lt;TargetLanguage Validate=&quot;false&quot;&gt;1033&lt;/TargetLanguage&gt;&lt;UpdatedLanguages&gt;1033&lt;/UpdatedLanguages&gt;&lt;UpgradeCode Validate=&quot;true&quot;&gt;{DB87BB66-FE5D-4293-81AC-EE313D3F864B}&lt;/UpgradeCode&gt;&lt;/TargetProduct&gt;&lt;TargetProductCode&gt;{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}&lt;/TargetProductCode&gt;&lt;/MsiPatch&gt;"><Provides Key="{0A5113E3-06A5-4CE0-8E83-9EB42F6764A6}" DisplayName="SlipstreamTests - Patch A" /><PayloadRef Id="PatchA" /></MspPackage></Chain><PatchTargetCode TargetCode="{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}" Product="yes" /></BurnManifest> \ No newline at end of file
diff --git a/src/burn/test/BurnUnitTest/VariableHelpers.cpp b/src/burn/test/BurnUnitTest/VariableHelpers.cpp
new file mode 100644
index 00000000..40f958f8
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/VariableHelpers.cpp
@@ -0,0 +1,217 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6using namespace System;
7using namespace Xunit;
8
9
10namespace Microsoft
11{
12namespace Tools
13{
14namespace WindowsInstallerXml
15{
16namespace Test
17{
18namespace Bootstrapper
19{
20 void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue, BOOL fFormatted)
21 {
22 HRESULT hr = S_OK;
23
24 hr = VariableSetString(pVariables, wzVariable, wzValue, FALSE, fFormatted);
25 TestThrowOnFailure2(hr, L"Failed to set %s to: %s", wzVariable, wzValue);
26 }
27
28 void VariableSetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LONGLONG llValue)
29 {
30 HRESULT hr = S_OK;
31
32 hr = VariableSetNumeric(pVariables, wzVariable, llValue, FALSE);
33 TestThrowOnFailure2(hr, L"Failed to set %s to: %I64d", wzVariable, llValue);
34 }
35
36 void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue)
37 {
38 HRESULT hr = S_OK;
39 VERUTIL_VERSION* pVersion = NULL;
40
41 try
42 {
43 hr = VerParseVersion(wzValue, 0, FALSE, &pVersion);
44 TestThrowOnFailure1(hr, L"Failed to parse version '%ls'", wzValue);
45
46 hr = VariableSetVersion(pVariables, wzVariable, pVersion, FALSE);
47 TestThrowOnFailure2(hr, L"Failed to set %s to: '%ls'", wzVariable, wzValue);
48 }
49 finally
50 {
51 ReleaseVerutilVersion(pVersion);
52 }
53 }
54
55 String^ VariableGetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable)
56 {
57 HRESULT hr = S_OK;
58 LPWSTR scz = NULL;
59 try
60 {
61 hr = VariableGetString(pVariables, wzVariable, &scz);
62 TestThrowOnFailure1(hr, L"Failed to get: %s", wzVariable);
63
64 return gcnew String(scz);
65 }
66 finally
67 {
68 ReleaseStr(scz);
69 }
70 }
71
72 __int64 VariableGetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable)
73 {
74 HRESULT hr = S_OK;
75 LONGLONG llValue = 0;
76
77 hr = VariableGetNumeric(pVariables, wzVariable, &llValue);
78 TestThrowOnFailure1(hr, L"Failed to get: %s", wzVariable);
79
80 return llValue;
81 }
82
83 String^ VariableGetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable)
84 {
85 HRESULT hr = S_OK;
86 VERUTIL_VERSION* pValue = NULL;
87
88 try
89 {
90 hr = VariableGetVersion(pVariables, wzVariable, &pValue);
91 TestThrowOnFailure1(hr, L"Failed to get: %s", wzVariable);
92
93 return gcnew String(pValue->sczVersion);
94 }
95 finally
96 {
97 ReleaseVerutilVersion(pValue);
98 }
99 }
100
101 String^ VariableGetFormattedHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, BOOL* pfContainsHiddenVariable)
102 {
103 HRESULT hr = S_OK;
104 LPWSTR scz = NULL;
105 try
106 {
107 hr = VariableGetFormatted(pVariables, wzVariable, &scz, pfContainsHiddenVariable);
108 TestThrowOnFailure1(hr, L"Failed to get formatted: %s", wzVariable);
109
110 return gcnew String(scz);
111 }
112 finally
113 {
114 ReleaseStr(scz);
115 }
116 }
117
118 String^ VariableFormatStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzIn)
119 {
120 HRESULT hr = S_OK;
121 LPWSTR scz = NULL;
122 try
123 {
124 hr = VariableFormatString(pVariables, wzIn, &scz, NULL);
125 TestThrowOnFailure1(hr, L"Failed to format string: '%s'", wzIn);
126
127 return gcnew String(scz);
128 }
129 finally
130 {
131 ReleaseStr(scz);
132 }
133 }
134
135 String^ VariableEscapeStringHelper(LPCWSTR wzIn)
136 {
137 HRESULT hr = S_OK;
138 LPWSTR scz = NULL;
139 try
140 {
141 hr = VariableEscapeString(wzIn, &scz);
142 TestThrowOnFailure1(hr, L"Failed to escape string: '%s'", wzIn);
143
144 return gcnew String(scz);
145 }
146 finally
147 {
148 ReleaseStr(scz);
149 }
150 }
151
152 bool EvaluateConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition)
153 {
154 HRESULT hr = S_OK;
155 BOOL f = FALSE;
156
157 hr = ConditionEvaluate(pVariables, wzCondition, &f);
158 TestThrowOnFailure1(hr, L"Failed to evaluate condition: '%s'", wzCondition);
159
160 return f ? true : false;
161 }
162
163 bool EvaluateFailureConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition)
164 {
165 HRESULT hr = S_OK;
166 BOOL f = FALSE;
167
168 hr = ConditionEvaluate(pVariables, wzCondition, &f);
169 return E_INVALIDDATA == hr ? true : false;
170 }
171
172 bool VariableExistsHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable)
173 {
174 HRESULT hr = S_OK;
175 BURN_VARIANT value = { };
176
177 try
178 {
179 hr = VariableGetVariant(pVariables, wzVariable, &value);
180 if (E_NOTFOUND == hr || value.Type == BURN_VARIANT_TYPE_NONE)
181 {
182 return false;
183 }
184 else
185 {
186 TestThrowOnFailure1(hr, L"Failed to find variable: '%s'", wzVariable);
187 return true;
188 }
189 }
190 finally
191 {
192 BVariantUninitialize(&value);
193 }
194 }
195
196 int VariableGetTypeHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable)
197 {
198 HRESULT hr = S_OK;
199 BURN_VARIANT value = { };
200
201 try
202 {
203 hr = VariableGetVariant(pVariables, wzVariable, &value);
204 TestThrowOnFailure1(hr, L"Failed to find variable: '%s'", wzVariable);
205
206 return (int)value.Type;
207 }
208 finally
209 {
210 BVariantUninitialize(&value);
211 }
212 }
213}
214}
215}
216}
217}
diff --git a/src/burn/test/BurnUnitTest/VariableHelpers.h b/src/burn/test/BurnUnitTest/VariableHelpers.h
new file mode 100644
index 00000000..d460c60f
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/VariableHelpers.h
@@ -0,0 +1,36 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5namespace Microsoft
6{
7namespace Tools
8{
9namespace WindowsInstallerXml
10{
11namespace Test
12{
13namespace Bootstrapper
14{
15
16
17void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue, BOOL fFormatted);
18void VariableSetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LONGLONG llValue);
19void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue);
20System::String^ VariableGetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable);
21__int64 VariableGetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable);
22System::String^ VariableGetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable);
23System::String^ VariableGetFormattedHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, BOOL* pfContainsHiddenVariable);
24System::String^ VariableFormatStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzIn);
25System::String^ VariableEscapeStringHelper(LPCWSTR wzIn);
26bool EvaluateConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition);
27bool EvaluateFailureConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition);
28bool VariableExistsHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable);
29int VariableGetTypeHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable);
30
31
32}
33}
34}
35}
36}
diff --git a/src/burn/test/BurnUnitTest/VariableTest.cpp b/src/burn/test/BurnUnitTest/VariableTest.cpp
new file mode 100644
index 00000000..5c9dce03
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/VariableTest.cpp
@@ -0,0 +1,532 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4#undef GetTempPath
5#undef GetEnvironmentVariable
6
7namespace Microsoft
8{
9namespace Tools
10{
11namespace WindowsInstallerXml
12{
13namespace Test
14{
15namespace Bootstrapper
16{
17 using namespace System;
18 using namespace Xunit;
19
20 public ref class VariableTest : BurnUnitTest
21 {
22 public:
23 VariableTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture)
24 {
25 }
26
27 [Fact]
28 void VariablesBasicTest()
29 {
30 HRESULT hr = S_OK;
31 BURN_VARIABLES variables = { };
32 try
33 {
34 hr = VariableInitialize(&variables);
35 TestThrowOnFailure(hr, L"Failed to initialize variables.");
36
37 // set variables
38 VariableSetStringHelper(&variables, L"PROP1", L"VAL1", FALSE);
39 VariableSetNumericHelper(&variables, L"PROP2", 2);
40 VariableSetStringHelper(&variables, L"PROP5", L"VAL5", FALSE);
41 VariableSetStringHelper(&variables, L"PROP3", L"VAL3", FALSE);
42 VariableSetStringHelper(&variables, L"PROP4", L"VAL4", FALSE);
43 VariableSetStringHelper(&variables, L"PROP6", L"VAL6", FALSE);
44 VariableSetStringHelper(&variables, L"PROP7", L"7", FALSE);
45 VariableSetVersionHelper(&variables, L"PROP8", L"1.1.0.0");
46 VariableSetStringHelper(&variables, L"PROP9", L"[VAL9]", TRUE);
47
48 // set overwritten variables
49 VariableSetStringHelper(&variables, L"OVERWRITTEN_STRING", L"ORIGINAL", FALSE);
50 VariableSetNumericHelper(&variables, L"OVERWRITTEN_STRING", 42);
51
52 VariableSetNumericHelper(&variables, L"OVERWRITTEN_NUMBER", 5);
53 VariableSetStringHelper(&variables, L"OVERWRITTEN_NUMBER", L"NEW", FALSE);
54
55 // get and verify variable values
56 Assert::Equal<String^>(gcnew String(L"VAL1"), VariableGetStringHelper(&variables, L"PROP1"));
57 Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"PROP2"));
58 Assert::Equal<String^>(gcnew String(L"2"), VariableGetStringHelper(&variables, L"PROP2"));
59 Assert::Equal<String^>(gcnew String(L"VAL3"), VariableGetStringHelper(&variables, L"PROP3"));
60 Assert::Equal<String^>(gcnew String(L"VAL4"), VariableGetStringHelper(&variables, L"PROP4"));
61 Assert::Equal<String^>(gcnew String(L"VAL5"), VariableGetStringHelper(&variables, L"PROP5"));
62 Assert::Equal<String^>(gcnew String(L"VAL6"), VariableGetStringHelper(&variables, L"PROP6"));
63 Assert::Equal(7ll, VariableGetNumericHelper(&variables, L"PROP7"));
64 Assert::Equal<String^>(gcnew String(L"1.1.0.0"), VariableGetVersionHelper(&variables, L"PROP8"));
65 Assert::Equal<String^>(gcnew String(L"1.1.0.0"), VariableGetStringHelper(&variables, L"PROP8"));
66 Assert::Equal<String^>(gcnew String(L"[VAL9]"), VariableGetStringHelper(&variables, L"PROP9"));
67
68 Assert::Equal(42ll, VariableGetNumericHelper(&variables, L"OVERWRITTEN_STRING"));
69 Assert::Equal<String^>(gcnew String(L"NEW"), VariableGetStringHelper(&variables, L"OVERWRITTEN_NUMBER"));
70 }
71 finally
72 {
73 VariablesUninitialize(&variables);
74 }
75 }
76
77 [Fact]
78 void VariablesParseXmlTest()
79 {
80 HRESULT hr = S_OK;
81 IXMLDOMElement* pixeBundle = NULL;
82 BURN_VARIABLES variables = { };
83 BOOL fContainsHiddenData = FALSE;
84 try
85 {
86 LPCWSTR wzDocument =
87 L"<Bundle>"
88 L" <Variable Id='Var1' Type='numeric' Value='1' Hidden='no' Persisted='no' />"
89 L" <Variable Id='Var2' Type='string' Value='String value.' Hidden='no' Persisted='no' />"
90 L" <Variable Id='Var3' Type='version' Value='1.2.3.4' Hidden='no' Persisted='no' />"
91 L" <Variable Id='Var4' Hidden='no' Persisted='no' />"
92 L" <Variable Id='Var5' Type='string' Value='' Hidden='no' Persisted='no' />"
93 L" <Variable Id='Var6' Type='formatted' Value='[Formatted]' Hidden='no' Persisted='no' />"
94 L" <Variable Id='Formatted' Type='formatted' Value='supersecret' Hidden='yes' Persisted='no' />"
95 L"</Bundle>";
96
97 hr = VariableInitialize(&variables);
98 TestThrowOnFailure(hr, L"Failed to initialize variables.");
99
100 // load XML document
101 LoadBundleXmlHelper(wzDocument, &pixeBundle);
102
103 hr = VariablesParseFromXml(&variables, pixeBundle);
104 TestThrowOnFailure(hr, L"Failed to parse variables from XML.");
105
106 // get and verify variable values
107 Assert::Equal((int)BURN_VARIANT_TYPE_NUMERIC, VariableGetTypeHelper(&variables, L"Var1"));
108 Assert::Equal((int)BURN_VARIANT_TYPE_STRING, VariableGetTypeHelper(&variables, L"Var2"));
109 Assert::Equal((int)BURN_VARIANT_TYPE_VERSION, VariableGetTypeHelper(&variables, L"Var3"));
110 Assert::Equal((int)BURN_VARIANT_TYPE_NONE, VariableGetTypeHelper(&variables, L"Var4"));
111 Assert::Equal((int)BURN_VARIANT_TYPE_FORMATTED, VariableGetTypeHelper(&variables, L"Var6"));
112
113 Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Var1"));
114 Assert::Equal<String^>(gcnew String(L"String value."), VariableGetStringHelper(&variables, L"Var2"));
115 Assert::Equal<String^>(gcnew String(L"1.2.3.4"), VariableGetVersionHelper(&variables, L"Var3"));
116 Assert::Equal<String^>(gcnew String(L"[Formatted]"), VariableGetStringHelper(&variables, L"Var6"));
117 Assert::Equal<String^>(gcnew String(L"supersecret"), VariableGetFormattedHelper(&variables, L"Formatted", &fContainsHiddenData));
118 Assert::Equal<BOOL>(TRUE, fContainsHiddenData);
119 Assert::Equal<String^>(gcnew String(L"supersecret"), VariableGetFormattedHelper(&variables, L"Var6", &fContainsHiddenData));
120 Assert::Equal<BOOL>(TRUE, fContainsHiddenData);
121 Assert::Equal<String^>(gcnew String(L"String value."), VariableGetFormattedHelper(&variables, L"Var2", &fContainsHiddenData));
122 Assert::Equal<BOOL>(FALSE, fContainsHiddenData);
123 }
124 finally
125 {
126 ReleaseObject(pixeBundle);
127 VariablesUninitialize(&variables);
128 }
129 }
130
131 [Fact]
132 void VariablesFormatTest()
133 {
134 HRESULT hr = S_OK;
135 BURN_VARIABLES variables = { };
136 LPWSTR scz = NULL;
137 SIZE_T cch = 0;
138 BOOL fContainsHiddenData = FALSE;
139 try
140 {
141 hr = VariableInitialize(&variables);
142 TestThrowOnFailure(hr, L"Failed to initialize variables.");
143
144 // set variables
145 VariableSetStringHelper(&variables, L"PROP1", L"VAL1", FALSE);
146 VariableSetStringHelper(&variables, L"PROP2", L"VAL2", FALSE);
147 VariableSetNumericHelper(&variables, L"PROP3", 3);
148 VariableSetStringHelper(&variables, L"PROP4", L"[PROP1]", FALSE);
149 VariableSetStringHelper(&variables, L"PROP5", L"[PROP2]", FALSE);
150 VariableSetStringHelper(&variables, L"PROP6", L"[PROP4]", TRUE);
151 VariableSetStringHelper(&variables, L"PROP7", L"[PROP5]", TRUE);
152
153 // test string formatting
154 Assert::Equal<String^>(gcnew String(L"NOPROP"), VariableFormatStringHelper(&variables, L"NOPROP"));
155 Assert::Equal<String^>(gcnew String(L"VAL1"), VariableFormatStringHelper(&variables, L"[PROP1]"));
156 Assert::Equal<String^>(gcnew String(L" VAL1 "), VariableFormatStringHelper(&variables, L" [PROP1] "));
157 Assert::Equal<String^>(gcnew String(L"PRE VAL1"), VariableFormatStringHelper(&variables, L"PRE [PROP1]"));
158 Assert::Equal<String^>(gcnew String(L"VAL1 POST"), VariableFormatStringHelper(&variables, L"[PROP1] POST"));
159 Assert::Equal<String^>(gcnew String(L"PRE VAL1 POST"), VariableFormatStringHelper(&variables, L"PRE [PROP1] POST"));
160 Assert::Equal<String^>(gcnew String(L"VAL1 MID VAL2"), VariableFormatStringHelper(&variables, L"[PROP1] MID [PROP2]"));
161 Assert::Equal<String^>(gcnew String(L""), VariableFormatStringHelper(&variables, L"[NONE]"));
162 Assert::Equal<String^>(gcnew String(L""), VariableFormatStringHelper(&variables, L"[prop1]"));
163 Assert::Equal<String^>(gcnew String(L"["), VariableFormatStringHelper(&variables, L"[\\[]"));
164 Assert::Equal<String^>(gcnew String(L"]"), VariableFormatStringHelper(&variables, L"[\\]]"));
165 Assert::Equal<String^>(gcnew String(L"[]"), VariableFormatStringHelper(&variables, L"[]"));
166 Assert::Equal<String^>(gcnew String(L"[NONE"), VariableFormatStringHelper(&variables, L"[NONE"));
167 Assert::Equal<String^>(gcnew String(L"VAL2"), VariableGetFormattedHelper(&variables, L"PROP2", &fContainsHiddenData));
168 Assert::Equal<BOOL>(FALSE, fContainsHiddenData);
169 Assert::Equal<String^>(gcnew String(L"3"), VariableGetFormattedHelper(&variables, L"PROP3", &fContainsHiddenData));
170 Assert::Equal<BOOL>(FALSE, fContainsHiddenData);
171 Assert::Equal<String^>(gcnew String(L"[PROP1]"), VariableGetFormattedHelper(&variables, L"PROP4", &fContainsHiddenData));
172 Assert::Equal<BOOL>(FALSE, fContainsHiddenData);
173 Assert::Equal<String^>(gcnew String(L"[PROP2]"), VariableGetFormattedHelper(&variables, L"PROP5", &fContainsHiddenData));
174 Assert::Equal<BOOL>(FALSE, fContainsHiddenData);
175 Assert::Equal<String^>(gcnew String(L"[PROP1]"), VariableGetFormattedHelper(&variables, L"PROP6", &fContainsHiddenData));
176 Assert::Equal<BOOL>(FALSE, fContainsHiddenData);
177 Assert::Equal<String^>(gcnew String(L"[PROP2]"), VariableGetFormattedHelper(&variables, L"PROP7", &fContainsHiddenData));
178 Assert::Equal<BOOL>(FALSE, fContainsHiddenData);
179
180 hr = VariableFormatString(&variables, L"PRE [PROP1] POST", &scz, &cch);
181 TestThrowOnFailure(hr, L"Failed to format string");
182
183 Assert::Equal((SIZE_T)lstrlenW(scz), cch);
184
185 hr = VariableFormatString(&variables, L"PRE [PROP1] POST", NULL, &cch);
186 TestThrowOnFailure(hr, L"Failed to format string");
187
188 Assert::Equal((SIZE_T)lstrlenW(scz), cch);
189 }
190 finally
191 {
192 VariablesUninitialize(&variables);
193 ReleaseStr(scz);
194 }
195 }
196
197 [Fact]
198 void VariablesEscapeTest()
199 {
200 // test string escaping
201 Assert::Equal<String^>(gcnew String(L"[\\[]"), VariableEscapeStringHelper(L"["));
202 Assert::Equal<String^>(gcnew String(L"[\\]]"), VariableEscapeStringHelper(L"]"));
203 Assert::Equal<String^>(gcnew String(L" [\\[]TEXT[\\]] "), VariableEscapeStringHelper(L" [TEXT] "));
204 }
205
206 [Fact]
207 void VariablesConditionTest()
208 {
209 HRESULT hr = S_OK;
210 BURN_VARIABLES variables = { };
211 try
212 {
213 hr = VariableInitialize(&variables);
214 TestThrowOnFailure(hr, L"Failed to initialize variables.");
215
216 // set variables
217 VariableSetStringHelper(&variables, L"PROP1", L"VAL1", FALSE);
218 VariableSetStringHelper(&variables, L"PROP2", L"VAL2", FALSE);
219 VariableSetStringHelper(&variables, L"PROP3", L"VAL3", FALSE);
220 VariableSetStringHelper(&variables, L"PROP4", L"BEGIN MID END", FALSE);
221 VariableSetNumericHelper(&variables, L"PROP5", 5);
222 VariableSetNumericHelper(&variables, L"PROP6", 6);
223 VariableSetStringHelper(&variables, L"PROP7", L"", FALSE);
224 VariableSetNumericHelper(&variables, L"PROP8", 0);
225 VariableSetStringHelper(&variables, L"_PROP9", L"VAL9", FALSE);
226 VariableSetNumericHelper(&variables, L"PROP10", -10);
227 VariableSetNumericHelper(&variables, L"PROP11", 9223372036854775807ll);
228 VariableSetNumericHelper(&variables, L"PROP12", -9223372036854775808ll);
229 VariableSetNumericHelper(&variables, L"PROP13", 0x00010000);
230 VariableSetNumericHelper(&variables, L"PROP14", 0x00000001);
231 VariableSetNumericHelper(&variables, L"PROP15", 0x00010001);
232 VariableSetVersionHelper(&variables, L"PROP16", L"0.0.0.0");
233 VariableSetVersionHelper(&variables, L"PROP17", L"1.0.0.0");
234 VariableSetVersionHelper(&variables, L"PROP18", L"1.1.0.0");
235 VariableSetVersionHelper(&variables, L"PROP19", L"1.1.1.0");
236 VariableSetVersionHelper(&variables, L"PROP20", L"1.1.1.1");
237 VariableSetNumericHelper(&variables, L"vPROP21", 1);
238 VariableSetVersionHelper(&variables, L"PROP22", L"65535.65535.65535.65535");
239 VariableSetStringHelper(&variables, L"PROP23", L"1.1.1", FALSE);
240 VariableSetStringHelper(&variables, L"PROP24", L"[PROP1]", TRUE);
241 VariableSetStringHelper(&variables, L"PROP25", L"[PROP7]", TRUE);
242 VariableSetStringHelper(&variables, L"PROP26", L"[PROP8]", TRUE);
243 VariableSetStringHelper(&variables, L"PROP27", L"[PROP16]", TRUE);
244
245 // test conditions
246 Assert::True(EvaluateConditionHelper(&variables, L"PROP1"));
247 Assert::True(EvaluateConditionHelper(&variables, L"PROP5"));
248 Assert::False(EvaluateConditionHelper(&variables, L"PROP7"));
249 Assert::False(EvaluateConditionHelper(&variables, L"PROP8"));
250 Assert::True(EvaluateConditionHelper(&variables, L"_PROP9"));
251 Assert::True(EvaluateConditionHelper(&variables, L"PROP16"));
252 Assert::True(EvaluateConditionHelper(&variables, L"PROP17"));
253 Assert::True(EvaluateConditionHelper(&variables, L"PROP24=\"VAL1\""));
254 Assert::False(EvaluateConditionHelper(&variables, L"PROP25"));
255 Assert::True(EvaluateConditionHelper(&variables, L"PROP26"));
256 Assert::True(EvaluateConditionHelper(&variables, L"PROP27"));
257
258 Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\""));
259 Assert::False(EvaluateConditionHelper(&variables, L"NONE = \"NOT\""));
260 Assert::False(EvaluateConditionHelper(&variables, L"PROP1 <> \"VAL1\""));
261 Assert::False(EvaluateConditionHelper(&variables, L"PROP1 ~<> \"VAL1\""));
262 Assert::False(EvaluateConditionHelper(&variables, L"PROP1 ~<> \"Val1\""));
263 Assert::True(EvaluateConditionHelper(&variables, L"NONE <> \"NOT\""));
264 Assert::True(EvaluateConditionHelper(&variables, L"NONE ~<> \"NOT\""));
265
266 Assert::True(EvaluateConditionHelper(&variables, L"PROP1 ~= \"val1\""));
267 Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"val1\""));
268 Assert::False(EvaluateConditionHelper(&variables, L"PROP1 ~<> \"val1\""));
269 Assert::True(EvaluateConditionHelper(&variables, L"PROP1 <> \"val1\""));
270
271 Assert::True(EvaluateConditionHelper(&variables, L"PROP5 = 5"));
272 Assert::False(EvaluateConditionHelper(&variables, L"PROP5 = 0"));
273 Assert::False(EvaluateConditionHelper(&variables, L"PROP5 <> 5"));
274 Assert::True(EvaluateConditionHelper(&variables, L"PROP5 <> 0"));
275
276 Assert::True(EvaluateConditionHelper(&variables, L"PROP10 = -10"));
277 Assert::False(EvaluateConditionHelper(&variables, L"PROP10 <> -10"));
278
279 Assert::True(EvaluateConditionHelper(&variables, L"PROP17 = v1"));
280 Assert::False(EvaluateConditionHelper(&variables, L"PROP17 = v0"));
281 Assert::False(EvaluateConditionHelper(&variables, L"PROP17 <> v1"));
282 Assert::True(EvaluateConditionHelper(&variables, L"PROP17 <> v0"));
283
284 Assert::True(EvaluateConditionHelper(&variables, L"PROP16 = v0"));
285 Assert::True(EvaluateConditionHelper(&variables, L"PROP17 = v1"));
286 Assert::True(EvaluateConditionHelper(&variables, L"PROP18 = v1.1"));
287 Assert::True(EvaluateConditionHelper(&variables, L"PROP19 = v1.1.1"));
288 Assert::True(EvaluateConditionHelper(&variables, L"PROP20 = v1.1.1.1"));
289 Assert::True(EvaluateConditionHelper(&variables, L"PROP20 > v1.1.1.1.0"));
290 Assert::True(EvaluateConditionHelper(&variables, L"PROP20 > v1.1.1.1.1"));
291 Assert::True(EvaluateConditionHelper(&variables, L"vPROP21 = 1"));
292 Assert::True(EvaluateConditionHelper(&variables, L"PROP23 = v1.1.1"));
293 Assert::True(EvaluateConditionHelper(&variables, L"v1.1.1 = PROP23"));
294 Assert::False(EvaluateConditionHelper(&variables, L"v1.1.1<>PROP23"));
295 Assert::True(EvaluateConditionHelper(&variables, L"PROP1 <> v1.1.1"));
296 Assert::True(EvaluateConditionHelper(&variables, L"v1.1.1 <> PROP1"));
297
298 Assert::False(EvaluateConditionHelper(&variables, L"PROP11 = 9223372036854775806"));
299 Assert::True(EvaluateConditionHelper(&variables, L"PROP11 = 9223372036854775807"));
300 Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP11 = 9223372036854775808"));
301 Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP11 = 92233720368547758070000"));
302
303 Assert::False(EvaluateConditionHelper(&variables, L"PROP12 = -9223372036854775807"));
304 Assert::True(EvaluateConditionHelper(&variables, L"PROP12 = -9223372036854775808"));
305 Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP12 = -9223372036854775809"));
306 Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP12 = -92233720368547758080000"));
307
308 Assert::True(EvaluateConditionHelper(&variables, L"PROP22 = v65535.65535.65535.65535"));
309 Assert::True(EvaluateConditionHelper(&variables, L"PROP22 < v65536.65535.65535.65535"));
310 Assert::True(EvaluateConditionHelper(&variables, L"PROP22 < v65535.655350000.65535.65535"));
311
312 Assert::True(EvaluateConditionHelper(&variables, L"PROP5 < 6"));
313 Assert::False(EvaluateConditionHelper(&variables, L"PROP5 < 5"));
314 Assert::True(EvaluateConditionHelper(&variables, L"PROP5 > 4"));
315 Assert::False(EvaluateConditionHelper(&variables, L"PROP5 > 5"));
316 Assert::True(EvaluateConditionHelper(&variables, L"PROP5 <= 6"));
317 Assert::True(EvaluateConditionHelper(&variables, L"PROP5 <= 5"));
318 Assert::False(EvaluateConditionHelper(&variables, L"PROP5 <= 4"));
319 Assert::True(EvaluateConditionHelper(&variables, L"PROP5 >= 4"));
320 Assert::True(EvaluateConditionHelper(&variables, L"PROP5 >= 5"));
321 Assert::False(EvaluateConditionHelper(&variables, L"PROP5 >= 6"));
322
323 Assert::True(EvaluateConditionHelper(&variables, L"PROP4 << \"BEGIN\""));
324 Assert::False(EvaluateConditionHelper(&variables, L"PROP4 << \"END\""));
325 Assert::True(EvaluateConditionHelper(&variables, L"PROP4 >> \"END\""));
326 Assert::False(EvaluateConditionHelper(&variables, L"PROP4 >> \"BEGIN\""));
327 Assert::True(EvaluateConditionHelper(&variables, L"PROP4 >< \"MID\""));
328 Assert::False(EvaluateConditionHelper(&variables, L"PROP4 >< \"NONE\""));
329
330 Assert::True(EvaluateConditionHelper(&variables, L"PROP16 < v1.1"));
331 Assert::False(EvaluateConditionHelper(&variables, L"PROP16 < v0"));
332 Assert::True(EvaluateConditionHelper(&variables, L"PROP17 > v0.12"));
333 Assert::False(EvaluateConditionHelper(&variables, L"PROP17 > v1"));
334 Assert::True(EvaluateConditionHelper(&variables, L"PROP18 >= v1.0"));
335 Assert::True(EvaluateConditionHelper(&variables, L"PROP18 >= v1.1"));
336 Assert::False(EvaluateConditionHelper(&variables, L"PROP18 >= v2.1"));
337 Assert::True(EvaluateConditionHelper(&variables, L"PROP19 <= v1.1234.1"));
338 Assert::True(EvaluateConditionHelper(&variables, L"PROP19 <= v1.1.1"));
339 Assert::False(EvaluateConditionHelper(&variables, L"PROP19 <= v1.0.123"));
340
341 Assert::True(EvaluateConditionHelper(&variables, L"PROP6 = \"6\""));
342 Assert::True(EvaluateConditionHelper(&variables, L"\"6\" = PROP6"));
343 Assert::False(EvaluateConditionHelper(&variables, L"PROP6 = \"ABC\""));
344 Assert::False(EvaluateConditionHelper(&variables, L"\"ABC\" = PROP6"));
345 Assert::False(EvaluateConditionHelper(&variables, L"\"ABC\" = PROP6"));
346
347 Assert::True(EvaluateConditionHelper(&variables, L"PROP13 << 1"));
348 Assert::False(EvaluateConditionHelper(&variables, L"PROP13 << 0"));
349 Assert::True(EvaluateConditionHelper(&variables, L"PROP14 >> 1"));
350 Assert::False(EvaluateConditionHelper(&variables, L"PROP14 >> 0"));
351 Assert::True(EvaluateConditionHelper(&variables, L"PROP15 >< 65537"));
352 Assert::False(EvaluateConditionHelper(&variables, L"PROP15 >< 0"));
353
354 Assert::False(EvaluateConditionHelper(&variables, L"NOT PROP1"));
355 Assert::True(EvaluateConditionHelper(&variables, L"NOT (PROP1 <> \"VAL1\")"));
356
357 Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\""));
358 Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\""));
359 Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" AND PROP2 = \"VAL2\""));
360 Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" AND PROP2 = \"NOT\""));
361
362 Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" OR PROP2 = \"VAL2\""));
363 Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" OR PROP2 = \"NOT\""));
364 Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" OR PROP2 = \"VAL2\""));
365 Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" OR PROP2 = \"NOT\""));
366
367 Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\" OR PROP3 = \"NOT\""));
368 Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"VAL3\""));
369 Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"NOT\""));
370 Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND (PROP2 = \"NOT\" OR PROP3 = \"VAL3\")"));
371 Assert::True(EvaluateConditionHelper(&variables, L"(PROP1 = \"VAL1\" AND PROP2 = \"VAL2\") OR PROP3 = \"NOT\""));
372
373 Assert::True(EvaluateConditionHelper(&variables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"VAL2\""));
374 Assert::True(EvaluateConditionHelper(&variables, L"PROP3 = \"VAL3\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\""));
375 Assert::False(EvaluateConditionHelper(&variables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\""));
376 Assert::True(EvaluateConditionHelper(&variables, L"(PROP3 = \"NOT\" OR PROP1 = \"VAL1\") AND PROP2 = \"VAL2\""));
377 Assert::True(EvaluateConditionHelper(&variables, L"PROP3 = \"NOT\" OR (PROP1 = \"VAL1\" AND PROP2 = \"VAL2\")"));
378
379 Assert::True(EvaluateFailureConditionHelper(&variables, L"="));
380 Assert::True(EvaluateFailureConditionHelper(&variables, L"(PROP1"));
381 Assert::True(EvaluateFailureConditionHelper(&variables, L"(PROP1 = \""));
382 Assert::True(EvaluateFailureConditionHelper(&variables, L"1A"));
383 Assert::True(EvaluateFailureConditionHelper(&variables, L"*"));
384
385 Assert::True(EvaluateFailureConditionHelper(&variables, L"1 == 1"));
386 }
387 finally
388 {
389 VariablesUninitialize(&variables);
390 }
391 }
392
393 [Fact]
394 void VariablesSerializationTest()
395 {
396 HRESULT hr = S_OK;
397 BYTE* pbBuffer = NULL;
398 SIZE_T cbBuffer = 0;
399 SIZE_T iBuffer = 0;
400 BURN_VARIABLES variables1 = { };
401 BURN_VARIABLES variables2 = { };
402 try
403 {
404 // serialize
405 hr = VariableInitialize(&variables1);
406 TestThrowOnFailure(hr, L"Failed to initialize variables.");
407
408 VariableSetStringHelper(&variables1, L"PROP1", L"VAL1", FALSE);
409 VariableSetNumericHelper(&variables1, L"PROP2", 2);
410 VariableSetVersionHelper(&variables1, L"PROP3", L"1.1.1.1");
411 VariableSetStringHelper(&variables1, L"PROP4", L"VAL4", FALSE);
412 VariableSetStringHelper(&variables1, L"PROP5", L"[PROP1]", TRUE);
413
414 hr = VariableSerialize(&variables1, FALSE, &pbBuffer, &cbBuffer);
415 TestThrowOnFailure(hr, L"Failed to serialize variables.");
416
417 // deserialize
418 hr = VariableInitialize(&variables2);
419 TestThrowOnFailure(hr, L"Failed to initialize variables.");
420
421 hr = VariableDeserialize(&variables2, FALSE, pbBuffer, cbBuffer, &iBuffer);
422 TestThrowOnFailure(hr, L"Failed to deserialize variables.");
423
424 Assert::Equal<String^>(gcnew String(L"VAL1"), VariableGetStringHelper(&variables2, L"PROP1"));
425 Assert::Equal(2ll, VariableGetNumericHelper(&variables2, L"PROP2"));
426 Assert::Equal<String^>(gcnew String(L"1.1.1.1"), VariableGetVersionHelper(&variables2, L"PROP3"));
427 Assert::Equal<String^>(gcnew String(L"VAL4"), VariableGetStringHelper(&variables2, L"PROP4"));
428 Assert::Equal<String^>(gcnew String(L"[PROP1]"), VariableGetStringHelper(&variables2, L"PROP5"));
429
430 Assert::Equal((int)BURN_VARIANT_TYPE_STRING, VariableGetTypeHelper(&variables2, L"PROP1"));
431 Assert::Equal((int)BURN_VARIANT_TYPE_NUMERIC, VariableGetTypeHelper(&variables2, L"PROP2"));
432 Assert::Equal((int)BURN_VARIANT_TYPE_VERSION, VariableGetTypeHelper(&variables2, L"PROP3"));
433 Assert::Equal((int)BURN_VARIANT_TYPE_STRING, VariableGetTypeHelper(&variables2, L"PROP4"));
434 Assert::Equal((int)BURN_VARIANT_TYPE_FORMATTED, VariableGetTypeHelper(&variables2, L"PROP5"));
435 }
436 finally
437 {
438 ReleaseBuffer(pbBuffer);
439 VariablesUninitialize(&variables1);
440 VariablesUninitialize(&variables2);
441 }
442 }
443
444 [Fact]
445 void VariablesBuiltInTest()
446 {
447 HRESULT hr = S_OK;
448 BURN_VARIABLES variables = { };
449 try
450 {
451 hr = VariableInitialize(&variables);
452 TestThrowOnFailure(hr, L"Failed to initialize variables.");
453
454 // VersionMsi
455 Assert::True(EvaluateConditionHelper(&variables, L"VersionMsi >= v1.1"));
456
457 // VersionNT
458 Assert::True(EvaluateConditionHelper(&variables, L"VersionNT <> v0.0.0.0"));
459
460 // VersionNT64
461 if (nullptr == Environment::GetEnvironmentVariable("ProgramFiles(x86)"))
462 {
463 Assert::False(EvaluateConditionHelper(&variables, L"VersionNT64"));
464 }
465 else
466 {
467 Assert::True(EvaluateConditionHelper(&variables, L"VersionNT64"));
468 }
469
470 // attempt to set a built-in property
471 hr = VariableSetString(&variables, L"VersionNT", L"VAL", FALSE, FALSE);
472 Assert::Equal(E_INVALIDARG, hr);
473 Assert::False(EvaluateConditionHelper(&variables, L"VersionNT = \"VAL\""));
474
475 VariableGetNumericHelper(&variables, L"NTProductType");
476 VariableGetNumericHelper(&variables, L"NTSuiteBackOffice");
477 VariableGetNumericHelper(&variables, L"NTSuiteDataCenter");
478 VariableGetNumericHelper(&variables, L"NTSuiteEnterprise");
479 VariableGetNumericHelper(&variables, L"NTSuitePersonal");
480 VariableGetNumericHelper(&variables, L"NTSuiteSmallBusiness");
481 VariableGetNumericHelper(&variables, L"NTSuiteSmallBusinessRestricted");
482 VariableGetNumericHelper(&variables, L"NTSuiteWebServer");
483 VariableGetNumericHelper(&variables, L"CompatibilityMode");
484 VariableGetNumericHelper(&variables, L"Privileged");
485 VariableGetNumericHelper(&variables, L"SystemLanguageID");
486 VariableGetNumericHelper(&variables, L"TerminalServer");
487 VariableGetNumericHelper(&variables, L"UserUILanguageID");
488 VariableGetNumericHelper(&variables, L"UserLanguageID");
489
490 // known folders
491 Assert::Equal<String^>(Environment::GetFolderPath(Environment::SpecialFolder::ApplicationData) + "\\", VariableGetStringHelper(&variables, L"AppDataFolder"));
492 Assert::Equal<String^>(Environment::GetFolderPath(Environment::SpecialFolder::CommonApplicationData) + "\\", VariableGetStringHelper(&variables, L"CommonAppDataFolder"));
493
494 Assert::Equal<String^>(Environment::GetFolderPath(Environment::SpecialFolder::ProgramFiles) + "\\", VariableGetStringHelper(&variables, L"ProgramFilesFolder"));
495 Assert::Equal<String^>(Environment::GetFolderPath(Environment::SpecialFolder::DesktopDirectory) + "\\", VariableGetStringHelper(&variables, L"DesktopFolder"));
496 Assert::Equal<String^>(Environment::GetFolderPath(Environment::SpecialFolder::Favorites) + "\\", VariableGetStringHelper(&variables, L"FavoritesFolder"));
497 VariableGetStringHelper(&variables, L"FontsFolder");
498 Assert::Equal<String^>(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData) + "\\", VariableGetStringHelper(&variables, L"LocalAppDataFolder"));
499 Assert::Equal<String^>(Environment::GetFolderPath(Environment::SpecialFolder::Personal) + "\\", VariableGetStringHelper(&variables, L"PersonalFolder"));
500 Assert::Equal<String^>(Environment::GetFolderPath(Environment::SpecialFolder::Programs) + "\\", VariableGetStringHelper(&variables, L"ProgramMenuFolder"));
501 Assert::Equal<String^>(Environment::GetFolderPath(Environment::SpecialFolder::SendTo) + "\\", VariableGetStringHelper(&variables, L"SendToFolder"));
502 Assert::Equal<String^>(Environment::GetFolderPath(Environment::SpecialFolder::StartMenu) + "\\", VariableGetStringHelper(&variables, L"StartMenuFolder"));
503 Assert::Equal<String^>(Environment::GetFolderPath(Environment::SpecialFolder::Startup) + "\\", VariableGetStringHelper(&variables, L"StartupFolder"));
504 VariableGetStringHelper(&variables, L"SystemFolder");
505 VariableGetStringHelper(&variables, L"WindowsFolder");
506 VariableGetStringHelper(&variables, L"WindowsVolume");
507
508 Assert::Equal<String^>(System::IO::Path::GetTempPath(), System::IO::Path::GetFullPath(VariableGetStringHelper(&variables, L"TempFolder")));
509
510 VariableGetStringHelper(&variables, L"AdminToolsFolder");
511 Assert::Equal<String^>(Environment::GetFolderPath(Environment::SpecialFolder::CommonProgramFiles) + "\\", VariableGetStringHelper(&variables, L"CommonFilesFolder"));
512 Assert::Equal<String^>(Environment::GetFolderPath(Environment::SpecialFolder::MyPictures) + "\\", VariableGetStringHelper(&variables, L"MyPicturesFolder"));
513 Assert::Equal<String^>(Environment::GetFolderPath(Environment::SpecialFolder::Templates) + "\\", VariableGetStringHelper(&variables, L"TemplateFolder"));
514
515 if (Environment::Is64BitOperatingSystem)
516 {
517 VariableGetStringHelper(&variables, L"ProgramFiles64Folder");
518 VariableGetStringHelper(&variables, L"CommonFiles64Folder");
519 VariableGetStringHelper(&variables, L"System64Folder");
520 }
521 }
522 finally
523 {
524 VariablesUninitialize(&variables);
525 }
526 }
527 };
528}
529}
530}
531}
532}
diff --git a/src/burn/test/BurnUnitTest/VariantTest.cpp b/src/burn/test/BurnUnitTest/VariantTest.cpp
new file mode 100644
index 00000000..43899a2b
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/VariantTest.cpp
@@ -0,0 +1,221 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5namespace Microsoft
6{
7namespace Tools
8{
9namespace WindowsInstallerXml
10{
11namespace Test
12{
13namespace Bootstrapper
14{
15 using namespace System;
16 using namespace Xunit;
17
18 public ref class VariantTest : BurnUnitTest
19 {
20 public:
21 VariantTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture)
22 {
23 }
24
25 [Fact]
26 void VariantBasicTest()
27 {
28 BURN_VARIANT expectedVariants[10];
29 BURN_VARIANT actualVariants[10];
30 for (DWORD i = 0; i < 10; i++)
31 {
32 BVariantUninitialize(expectedVariants + i);
33 BVariantUninitialize(actualVariants + i);
34 }
35
36 try
37 {
38 InitNumericValue(expectedVariants + 0, 2, FALSE, L"PROP1", actualVariants + 0);
39 InitStringValue(expectedVariants + 1, L"VAL2", FALSE, L"PROP2", actualVariants + 1);
40 InitVersionValue(expectedVariants + 2, L"1.1.0.0", FALSE, L"PROP3", actualVariants + 2);
41 InitNoneValue(expectedVariants + 3, FALSE, L"PROP4", actualVariants + 3);
42 InitNoneValue(expectedVariants + 4, TRUE, L"PROP5", actualVariants + 4);
43 InitVersionValue(expectedVariants + 5, L"1.1.1.0", TRUE, L"PROP6", actualVariants + 5);
44 InitStringValue(expectedVariants + 6, L"7", TRUE, L"PROP7", actualVariants + 6);
45 InitNumericValue(expectedVariants + 7, 11, TRUE, L"PROP8", actualVariants + 7);
46 InitFormattedValue(expectedVariants + 8, L"VAL9", FALSE, L"PROP9", actualVariants + 8);
47 InitFormattedValue(expectedVariants + 9, L"VAL10", TRUE, L"PROP10", actualVariants + 9);
48
49 VerifyNumericValue(expectedVariants + 0, actualVariants + 0);
50 VerifyStringValue(expectedVariants + 1, actualVariants + 1);
51 VerifyVersionValue(expectedVariants + 2, actualVariants + 2);
52 VerifyNoneValue(expectedVariants + 3, actualVariants + 3);
53 VerifyNoneValue(expectedVariants + 4, actualVariants + 4);
54 VerifyVersionValue(expectedVariants + 5, actualVariants + 5);
55 VerifyStringValue(expectedVariants + 6, actualVariants + 6);
56 VerifyNumericValue(expectedVariants + 7, actualVariants + 7);
57 VerifyFormattedValue(expectedVariants + 8, actualVariants + 8);
58 VerifyFormattedValue(expectedVariants + 9, actualVariants + 9);
59 }
60 finally
61 {
62 for (DWORD i = 0; i < 10; i++)
63 {
64 BVariantUninitialize(expectedVariants + i);
65 BVariantUninitialize(actualVariants + i);
66 }
67 }
68 }
69
70 private:
71 void InitFormattedValue(BURN_VARIANT* pValue, LPWSTR wzValue, BOOL /*fHidden*/, LPCWSTR wz, BURN_VARIANT* pActualValue)
72 {
73 HRESULT hr = S_OK;
74 pValue->Type = BURN_VARIANT_TYPE_FORMATTED;
75
76 hr = StrAllocString(&pValue->sczValue, wzValue, 0);
77 NativeAssert::Succeeded(hr, "Failed to alloc string: {0}", wzValue);
78
79 hr = BVariantCopy(pValue, pActualValue);
80 NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz);
81 }
82
83 void InitNoneValue(BURN_VARIANT* pValue, BOOL /*fHidden*/, LPCWSTR wz, BURN_VARIANT* pActualValue)
84 {
85 HRESULT hr = S_OK;
86 pValue->Type = BURN_VARIANT_TYPE_NONE;
87
88 hr = BVariantCopy(pValue, pActualValue);
89 NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz);
90 }
91
92 void InitNumericValue(BURN_VARIANT* pValue, LONGLONG llValue, BOOL /*fHidden*/, LPCWSTR wz, BURN_VARIANT* pActualValue)
93 {
94 HRESULT hr = S_OK;
95 pValue->Type = BURN_VARIANT_TYPE_NUMERIC;
96 pValue->llValue = llValue;
97
98 hr = BVariantCopy(pValue, pActualValue);
99 NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz);
100 }
101
102 void InitStringValue(BURN_VARIANT* pValue, LPWSTR wzValue, BOOL /*fHidden*/, LPCWSTR wz, BURN_VARIANT* pActualValue)
103 {
104 HRESULT hr = S_OK;
105 pValue->Type = BURN_VARIANT_TYPE_STRING;
106
107 hr = StrAllocString(&pValue->sczValue, wzValue, 0);
108 NativeAssert::Succeeded(hr, "Failed to alloc string: {0}", wzValue);
109
110 hr = BVariantCopy(pValue, pActualValue);
111 NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz);
112 }
113
114 void InitVersionValue(BURN_VARIANT* pValue, LPCWSTR wzValue, BOOL /*fHidden*/, LPCWSTR wz, BURN_VARIANT* pActualValue)
115 {
116 HRESULT hr = S_OK;
117 VERUTIL_VERSION* pVersion = NULL;
118
119 try
120 {
121 hr = VerParseVersion(wzValue, 0, FALSE, &pVersion);
122 NativeAssert::Succeeded(hr, "Failed to parse version {0}", wzValue);
123
124 pValue->Type = BURN_VARIANT_TYPE_VERSION;
125 pValue->pValue = pVersion;
126 pVersion = NULL;
127
128 hr = BVariantCopy(pValue, pActualValue);
129 NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz);
130 }
131 finally
132 {
133 ReleaseVerutilVersion(pVersion);
134 }
135 }
136
137 void VerifyFormattedValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue)
138 {
139 HRESULT hr = S_OK;
140 LPWSTR sczValue = NULL;
141 NativeAssert::Equal<DWORD>(BURN_VARIANT_TYPE_FORMATTED, pExpectedValue->Type);
142 NativeAssert::Equal<DWORD>(BURN_VARIANT_TYPE_FORMATTED, pActualValue->Type);
143
144 try
145 {
146 hr = BVariantGetString(pActualValue, &sczValue);
147 NativeAssert::Succeeded(hr, "Failed to get string value");
148
149 NativeAssert::StringEqual(pExpectedValue->sczValue, sczValue);
150 }
151 finally
152 {
153 ReleaseStr(sczValue);
154 }
155 }
156
157 void VerifyNumericValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue)
158 {
159 HRESULT hr = S_OK;
160 LONGLONG llValue = 0;
161 NativeAssert::Equal<DWORD>(BURN_VARIANT_TYPE_NUMERIC, pExpectedValue->Type);
162 NativeAssert::Equal<DWORD>(BURN_VARIANT_TYPE_NUMERIC, pActualValue->Type);
163
164 hr = BVariantGetNumeric(pActualValue, &llValue);
165 NativeAssert::Succeeded(hr, "Failed to get numeric value");
166
167 NativeAssert::Equal<LONGLONG>(pExpectedValue->llValue, llValue);
168 }
169
170 void VerifyNoneValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue)
171 {
172 NativeAssert::Equal<DWORD>(BURN_VARIANT_TYPE_NONE, pExpectedValue->Type);
173 NativeAssert::Equal<DWORD>(BURN_VARIANT_TYPE_NONE, pActualValue->Type);
174 NativeAssert::Equal<LONGLONG>(pExpectedValue->llValue, pActualValue->llValue);
175 }
176
177 void VerifyStringValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue)
178 {
179 HRESULT hr = S_OK;
180 LPWSTR sczValue = NULL;
181 NativeAssert::Equal<DWORD>(BURN_VARIANT_TYPE_STRING, pExpectedValue->Type);
182 NativeAssert::Equal<DWORD>(BURN_VARIANT_TYPE_STRING, pActualValue->Type);
183
184 try
185 {
186 hr = BVariantGetString(pActualValue, &sczValue);
187 NativeAssert::Succeeded(hr, "Failed to get string value");
188
189 NativeAssert::StringEqual(pExpectedValue->sczValue, sczValue);
190 }
191 finally
192 {
193 ReleaseStr(sczValue);
194 }
195 }
196
197 void VerifyVersionValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue)
198 {
199 HRESULT hr = S_OK;
200 VERUTIL_VERSION* pValue = NULL;
201 NativeAssert::Equal<DWORD>(BURN_VARIANT_TYPE_VERSION, pExpectedValue->Type);
202 NativeAssert::Equal<DWORD>(BURN_VARIANT_TYPE_VERSION, pActualValue->Type);
203
204 try
205 {
206 hr = BVariantGetVersion(pActualValue, &pValue);
207 NativeAssert::Succeeded(hr, "Failed to get version value");
208
209 NativeAssert::StringEqual(pExpectedValue->pValue->sczVersion, pActualValue->pValue->sczVersion);
210 }
211 finally
212 {
213 ReleaseVerutilVersion(pValue);
214 }
215 }
216 };
217}
218}
219}
220}
221}
diff --git a/src/burn/test/BurnUnitTest/packages.config b/src/burn/test/BurnUnitTest/packages.config
new file mode 100644
index 00000000..1d36c387
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/packages.config
@@ -0,0 +1,15 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3<packages>
4 <package id="WixBuildTools.TestSupport" version="4.0.47" />
5 <package id="WixBuildTools.TestSupport.Native" version="4.0.47" />
6 <package id="WixToolset.DUtil" version="4.0.70" targetFramework="native" />
7 <package id="WixToolset.BootstrapperCore.Native" version="4.0.57" targetFramework="native" />
8 <package id="xunit.abstractions" version="2.0.3" />
9 <package id="xunit.assert" version="2.4.1" />
10 <package id="xunit.core" version="2.4.1" />
11 <package id="xunit.extensibility.core" version="2.4.1" />
12 <package id="xunit.extensibility.execution" version="2.4.1" />
13 <package id="xunit.runner.msbuild" version="2.4.1" />
14 <package id="xunit.runner.visualstudio" version="2.4.1" />
15</packages> \ No newline at end of file
diff --git a/src/burn/test/BurnUnitTest/precomp.cpp b/src/burn/test/BurnUnitTest/precomp.cpp
new file mode 100644
index 00000000..37664a1c
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/precomp.cpp
@@ -0,0 +1,3 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
diff --git a/src/burn/test/BurnUnitTest/precomp.h b/src/burn/test/BurnUnitTest/precomp.h
new file mode 100644
index 00000000..d2b57d61
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/precomp.h
@@ -0,0 +1,79 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#include <windows.h>
6#include <Bits.h>
7#include <msiquery.h>
8#include <objbase.h>
9#include <shlobj.h>
10#include <shlwapi.h>
11#include <stdlib.h>
12#include <strsafe.h>
13#include "wininet.h"
14
15#include <dutil.h>
16#include <verutil.h>
17#include <cryputil.h>
18#include <dlutil.h>
19#include <buffutil.h>
20#include <dirutil.h>
21#include <fileutil.h>
22#include <logutil.h>
23#include <memutil.h>
24#include <pathutil.h>
25#include <regutil.h>
26#include <resrutil.h>
27#include <shelutil.h>
28#include <strutil.h>
29#include <wiutil.h>
30#include <xmlutil.h>
31#include <dictutil.h>
32#include <deputil.h>
33
34#include "BootstrapperEngine.h"
35#include "BootstrapperApplication.h"
36#include "BundleExtensionEngine.h"
37#include "BundleExtension.h"
38
39#include "platform.h"
40#include "variant.h"
41#include "variable.h"
42#include "condition.h"
43#include "section.h"
44#include "approvedexe.h"
45#include "container.h"
46#include "payload.h"
47#include "cabextract.h"
48#include "burnextension.h"
49#include "search.h"
50#include "userexperience.h"
51#include "package.h"
52#include "update.h"
53#include "pseudobundle.h"
54#include "registration.h"
55#include "plan.h"
56#include "pipe.h"
57#include "logging.h"
58#include "core.h"
59#include "cache.h"
60#include "apply.h"
61#include "exeengine.h"
62#include "msiengine.h"
63#include "mspengine.h"
64#include "msuengine.h"
65#include "dependency.h"
66#include "elevation.h"
67#include "embedded.h"
68#include "manifest.h"
69#include "splashscreen.h"
70#include "detect.h"
71
72#pragma managed
73#include <vcclr.h>
74
75#include "BurnTestException.h"
76#include "BurnTestFixture.h"
77#include "BurnUnitTest.h"
78#include "VariableHelpers.h"
79#include "ManifestHelpers.h"
diff --git a/src/samples/burn/ManagedBundleRunner/BundleErrorEventArgs.cs b/src/samples/burn/ManagedBundleRunner/BundleErrorEventArgs.cs
new file mode 100644
index 00000000..2c377326
--- /dev/null
+++ b/src/samples/burn/ManagedBundleRunner/BundleErrorEventArgs.cs
@@ -0,0 +1,33 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace Wix.Samples
4{
5 using System;
6
7 /// <summary>
8 /// Arguments provided when bundle encounters an error.
9 /// </summary>
10 [Serializable]
11 public class BundleErrorEventArgs : EventArgs
12 {
13 /// <summary>
14 /// Gets the error code.
15 /// </summary>
16 public int Code { get; set; }
17
18 /// <summary>
19 /// Gets the error message.
20 /// </summary>
21 public string Message { get; set; }
22
23 /// <summary>
24 /// Gets the recommended display flags for an error dialog.
25 /// </summary>
26 public int UIHint { get; set; }
27
28 /// <summary>
29 /// Gets or sets the <see cref="Result"/> of the operation. This is passed back to the bundle.
30 /// </summary>
31 public BundleResult Result { get; set; }
32 }
33}
diff --git a/src/samples/burn/ManagedBundleRunner/BundleProgressEventArgs.cs b/src/samples/burn/ManagedBundleRunner/BundleProgressEventArgs.cs
new file mode 100644
index 00000000..ed42b5b1
--- /dev/null
+++ b/src/samples/burn/ManagedBundleRunner/BundleProgressEventArgs.cs
@@ -0,0 +1,23 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace Wix.Samples
4{
5 using System;
6
7 /// <summary>
8 /// Arguments provided when bundle progress is updated.
9 /// </summary>
10 [Serializable]
11 public class BundleProgressEventArgs : EventArgs
12 {
13 /// <summary>
14 /// Gets the percentage from 0 to 100 completed for a bundle.
15 /// </summary>
16 public int Progress { get; set; }
17
18 /// <summary>
19 /// Gets or sets the <see cref="Result"/> of the operation. This is passed back to the bundle.
20 /// </summary>
21 public BundleResult Result { get; set; }
22 }
23}
diff --git a/src/samples/burn/ManagedBundleRunner/BundleResult.cs b/src/samples/burn/ManagedBundleRunner/BundleResult.cs
new file mode 100644
index 00000000..c32644f4
--- /dev/null
+++ b/src/samples/burn/ManagedBundleRunner/BundleResult.cs
@@ -0,0 +1,24 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace Wix.Samples
4{
5 /// <summary>
6 /// Result codes.
7 /// </summary>
8 public enum BundleResult
9 {
10 Error = -1,
11 None,
12 Ok,
13 Cancel,
14 Abort,
15 Retry,
16 Ignore,
17 Yes,
18 No,
19 Close,
20 Help,
21 TryAgain,
22 Continue,
23 }
24}
diff --git a/src/samples/burn/ManagedBundleRunner/BundleRunner.cs b/src/samples/burn/ManagedBundleRunner/BundleRunner.cs
new file mode 100644
index 00000000..e2089787
--- /dev/null
+++ b/src/samples/burn/ManagedBundleRunner/BundleRunner.cs
@@ -0,0 +1,212 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace Wix.Samples
4{
5 using System;
6 using System.Diagnostics;
7 using System.IO.Pipes;
8 using System.Text;
9 using System.Threading;
10
11 /// <summary>
12 /// Runs a bundle with provided command-line.
13 /// </summary>
14 public class BundleRunner
15 {
16 /// <summary>
17 /// Creates a runner for the provided bundle.
18 /// </summary>
19 /// <param name="bundle">Path to the bundle to run.</param>
20 public BundleRunner(string bundle)
21 {
22 this.Path = bundle;
23 }
24
25 /// <summary>
26 /// Fired when the bundle encounters an error.
27 /// </summary>
28 public event EventHandler<BundleErrorEventArgs> Error;
29
30 /// <summary>
31 /// Fired when the bundle progress is udpated.
32 /// </summary>
33 public event EventHandler<BundleProgressEventArgs> Progress;
34
35 /// <summary>
36 /// Gets the path to the bundle to run.
37 /// </summary>
38 public string Path { get; private set; }
39
40 /// <summary>
41 /// Runs the bundle with the provided command-line.
42 /// </summary>
43 /// <param name="commandLine">Optional command-line to pass to the bundle.</param>
44 /// <returns>Exit code from the bundle.</returns>
45 public int Run(string commandLine = null)
46 {
47 WaitHandle[] waits = new WaitHandle[] { new ManualResetEvent(false), new ManualResetEvent(false) };
48 int returnCode = 0;
49 int pid = Process.GetCurrentProcess().Id;
50 string pipeName = String.Concat("bpe_", pid);
51 string pipeSecret = Guid.NewGuid().ToString("N");
52
53 using (NamedPipeServerStream pipe = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1))
54 {
55 using (Process bundleProcess = new Process())
56 {
57 bundleProcess.StartInfo.FileName = this.Path;
58 bundleProcess.StartInfo.Arguments = String.Format("{0} -burn.embedded {1} {2} {3}", commandLine ?? String.Empty, pipeName, pipeSecret, pid);
59 bundleProcess.StartInfo.UseShellExecute = false;
60 bundleProcess.StartInfo.CreateNoWindow = true;
61 bundleProcess.Start();
62
63 Connect(pipe, pipeSecret, pid, bundleProcess.Id);
64
65 PumpMessages(pipe);
66
67 bundleProcess.WaitForExit();
68 returnCode = bundleProcess.ExitCode;
69 }
70 }
71
72 return returnCode;
73 }
74
75 /// <summary>
76 /// Called when bundle encounters an error.
77 /// </summary>
78 /// <param name="e">Additional arguments for this event.</param>
79 protected virtual void OnError(BundleErrorEventArgs e)
80 {
81 EventHandler<BundleErrorEventArgs> handler = this.Error;
82 if (handler != null)
83 {
84 handler(this, e);
85 }
86 }
87
88 /// <summary>
89 /// Called when bundle progress is updated.
90 /// </summary>
91 /// <param name="e">Additional arguments for this event.</param>
92 protected virtual void OnProgress(BundleProgressEventArgs e)
93 {
94 EventHandler<BundleProgressEventArgs> handler = this.Progress;
95 if (handler != null)
96 {
97 handler(this, e);
98 }
99 }
100
101 private void Connect(NamedPipeServerStream pipe, string pipeSecret, int pid, int childPid)
102 {
103 pipe.WaitForConnection();
104
105 WriteSecretToPipe(pipe, pipeSecret);
106
107 WriteNumberToPipe(pipe, (uint)pid);
108
109 uint ack = ReadNumberFromPipe(pipe);
110 // This is not true when bundle is run under a debugger
111 //if (ack != childPid)
112 //{
113 // throw new ApplicationException("Incorrect child process.");
114 //}
115 }
116
117 private void PumpMessages(NamedPipeServerStream pipe)
118 {
119 uint messageId;
120 while (TryReadNumberFromPipe(pipe, out messageId))
121 {
122 uint messageSize = ReadNumberFromPipe(pipe);
123
124 BundleResult result = BundleResult.None;
125 switch (messageId)
126 {
127 case 1: //error
128 result = ProcessErrorMessage(pipe);
129 break;
130
131 case 2: // progress
132 result = ProcessProgressMessage(pipe);
133 break;
134
135 default: // unknown message, do nothing.
136 break;
137 }
138
139 CompleteMessage(pipe, result);
140 }
141 }
142
143 private BundleResult ProcessErrorMessage(NamedPipeServerStream pipe)
144 {
145 BundleErrorEventArgs e = new BundleErrorEventArgs();
146 e.Code = (int)ReadNumberFromPipe(pipe);
147 e.Message = ReadStringFromPipe(pipe);
148 e.UIHint = (int)ReadNumberFromPipe(pipe);
149
150 this.OnError(e);
151
152 return e.Result;
153 }
154
155 private BundleResult ProcessProgressMessage(NamedPipeServerStream pipe)
156 {
157 ReadNumberFromPipe(pipe); // eat the first progress number because it is always zero.
158
159 BundleProgressEventArgs e = new BundleProgressEventArgs();
160 e.Progress = (int)ReadNumberFromPipe(pipe);
161
162 this.OnProgress(e);
163
164 return e.Result;
165 }
166
167 private void CompleteMessage(NamedPipeServerStream pipe, BundleResult result)
168 {
169 uint complete = 0xF0000002;
170 WriteNumberToPipe(pipe, complete);
171 WriteNumberToPipe(pipe, 4); // size of message data
172 WriteNumberToPipe(pipe, (uint)result);
173 }
174
175 private uint ReadNumberFromPipe(NamedPipeServerStream pipe)
176 {
177 byte[] buffer = new byte[4];
178 pipe.Read(buffer, 0, buffer.Length);
179 return BitConverter.ToUInt32(buffer, 0);
180 }
181
182 private string ReadStringFromPipe(NamedPipeServerStream pipe)
183 {
184 uint length = ReadNumberFromPipe(pipe);
185
186 byte[] buffer = new byte[length * 2];
187 pipe.Read(buffer, 0, buffer.Length);
188
189 return Encoding.Unicode.GetString(buffer);
190 }
191
192 private bool TryReadNumberFromPipe(NamedPipeServerStream pipe, out uint value)
193 {
194 value = ReadNumberFromPipe(pipe); // reading will not block and return zero if pipe is not connected.
195 return pipe.IsConnected;
196 }
197
198 private void WriteNumberToPipe(NamedPipeServerStream pipe, uint value)
199 {
200 byte[] buffer = BitConverter.GetBytes(value);
201 pipe.Write(buffer, 0, buffer.Length);
202 }
203
204 private void WriteSecretToPipe(NamedPipeServerStream pipe, string secret)
205 {
206 byte[] buffer = Encoding.Unicode.GetBytes(secret);
207
208 WriteNumberToPipe(pipe, (uint)buffer.Length);
209 pipe.Write(buffer, 0, buffer.Length);
210 }
211 }
212}
diff --git a/src/samples/burn/runbundle/AssemblyInfo.cs b/src/samples/burn/runbundle/AssemblyInfo.cs
new file mode 100644
index 00000000..3a66d5e3
--- /dev/null
+++ b/src/samples/burn/runbundle/AssemblyInfo.cs
@@ -0,0 +1,12 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3using System;
4using System.Reflection;
5using System.Runtime.CompilerServices;
6using System.Runtime.InteropServices;
7
8[assembly: AssemblyTitle("Executable to demonstrate Bundle Runner Sample")]
9[assembly: AssemblyDescription("")]
10[assembly: AssemblyCulture("")]
11[assembly: CLSCompliant(true)]
12[assembly: ComVisible(false)]
diff --git a/src/samples/burn/runbundle/Program.cs b/src/samples/burn/runbundle/Program.cs
new file mode 100644
index 00000000..8edca5dc
--- /dev/null
+++ b/src/samples/burn/runbundle/Program.cs
@@ -0,0 +1,47 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace Wix.Samples
4{
5 using System;
6 using System.Linq;
7 using Wix.Samples;
8
9 /// <summary>
10 /// Example executable that installs then immediately uninstalls a bundle showing progress.
11 /// </summary>
12 class Program
13 {
14 static int Main(string[] args)
15 {
16 if (args.Length == 0)
17 {
18 Console.WriteLine("Must provide the path to the bundle to install then uninstall.");
19 return -1;
20 }
21
22 BundleRunner runner = new BundleRunner(args[0]);
23 runner.Error += Program.OnError;
24 runner.Progress += Program.OnProgress;
25
26 Console.WriteLine("Installing: {0}", runner.Path);
27 int exitCode = runner.Run(String.Join(" ", args.Skip(1).ToArray()));
28 if (0 == exitCode)
29 {
30 Console.WriteLine("\r\nUninstalling: {0}", runner.Path);
31 exitCode = runner.Run("-uninstall");
32 }
33
34 return exitCode;
35 }
36
37 static void OnError(object sender, BundleErrorEventArgs e)
38 {
39 Console.WriteLine("error: {0}, uiHint: {1}, message: {2}", e.Code, e.UIHint, e.Message);
40 }
41
42 static void OnProgress(object sender, BundleProgressEventArgs e)
43 {
44 Console.WriteLine("progresss: {0}%", e.Progress);
45 }
46 }
47}