diff options
author | Rob Mensching <rob@firegiant.com> | 2021-05-11 07:42:44 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2021-05-11 07:42:44 -0700 |
commit | afc20ecb53ad3ade524aceefa30d98a3d84fd479 (patch) | |
tree | 4fb762f65dc195ccb104a6c1fcce7c40e7693e98 | |
parent | 8e1207c4c5e451ba1a087f0fa11b63964ac35f3c (diff) | |
parent | af10c45d7b3a44af0b461a557847fe03263dcc10 (diff) | |
download | wix-afc20ecb53ad3ade524aceefa30d98a3d84fd479.tar.gz wix-afc20ecb53ad3ade524aceefa30d98a3d84fd479.tar.bz2 wix-afc20ecb53ad3ade524aceefa30d98a3d84fd479.zip |
Merge burn
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->'%(Identity)')" -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) > 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 | ||
2 | burn.lib - Burn engine | ||
diff --git a/src/burn/appveyor.cmd b/src/burn/appveyor.cmd new file mode 100644 index 00000000..a35e10d5 --- /dev/null +++ b/src/burn/appveyor.cmd | |||
@@ -0,0 +1,17 @@ | |||
1 | @setlocal | ||
2 | @pushd %~dp0 | ||
3 | @set _C=Release | ||
4 | @if /i "%1"=="debug" set _C=Debug | ||
5 | |||
6 | nuget restore || exit /b | ||
7 | |||
8 | msbuild -t:Test -p:Configuration=%_C% src\test\BurnUnitTest || exit /b | ||
9 | |||
10 | msbuild -p:Configuration=%_C%;Platform=x86 || exit /b | ||
11 | msbuild -p:Configuration=%_C%;Platform=x64 || exit /b | ||
12 | msbuild -p:Configuration=%_C%;Platform=arm64 || exit /b | ||
13 | |||
14 | msbuild -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 | |||
6 | branches: | ||
7 | only: | ||
8 | - master | ||
9 | - develop | ||
10 | |||
11 | image: Visual Studio 2019 | ||
12 | |||
13 | version: 0.0.0.{build} | ||
14 | configuration: Release | ||
15 | |||
16 | environment: | ||
17 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true | ||
18 | DOTNET_CLI_TELEMETRY_OPTOUT: 1 | ||
19 | NUGET_XMLDOC_MODE: skip | ||
20 | |||
21 | build_script: | ||
22 | - appveyor.cmd | ||
23 | |||
24 | test: off | ||
25 | |||
26 | pull_requests: | ||
27 | do_not_increment_build_number: true | ||
28 | |||
29 | nuget: | ||
30 | disable_publish_on_pr: true | ||
31 | |||
32 | skip_branch_with_pr: true | ||
33 | skip_tags: true | ||
34 | |||
35 | artifacts: | ||
36 | - path: build\Release\**\*.nupkg | ||
37 | name: nuget | ||
38 | - path: build\Release\**\*.snupkg | ||
39 | name: snupkg | ||
40 | |||
41 | notifications: | ||
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 |  | ||
2 | Microsoft Visual Studio Solution File, Format Version 12.00 | ||
3 | # Visual Studio Version 16 | ||
4 | VisualStudioVersion = 16.0.30711.63 | ||
5 | MinimumVisualStudioVersion = 15.0.26124.0 | ||
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "engine", "src\engine\engine.vcxproj", "{8119537D-E1D9-6591-D51A-49768A2F9C37}" | ||
7 | EndProject | ||
8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stub", "src\stub\stub.vcxproj", "{C38373AA-882F-4F55-B03F-2AAB4BFBE3F1}" | ||
9 | EndProject | ||
10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BurnUnitTest", "src\test\BurnUnitTest\BurnUnitTest.vcxproj", "{9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}" | ||
11 | EndProject | ||
12 | Global | ||
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 | ||
61 | EndGlobal | ||
diff --git a/src/burn/engine/EngineForApplication.cpp b/src/burn/engine/EngineForApplication.cpp new file mode 100644 index 00000000..83d88ba1 --- /dev/null +++ b/src/burn/engine/EngineForApplication.cpp | |||
@@ -0,0 +1,529 @@ | |||
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 | static 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 | |||
18 | LExit: | ||
19 | return hr; | ||
20 | } | ||
21 | |||
22 | static 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 | |||
34 | LExit: | ||
35 | return hr; | ||
36 | } | ||
37 | |||
38 | static 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 | |||
50 | LExit: | ||
51 | return hr; | ||
52 | } | ||
53 | |||
54 | static 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 | |||
66 | LExit: | ||
67 | return hr; | ||
68 | } | ||
69 | |||
70 | static 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 | |||
82 | LExit: | ||
83 | return hr; | ||
84 | } | ||
85 | |||
86 | static 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 | |||
98 | LExit: | ||
99 | return hr; | ||
100 | } | ||
101 | |||
102 | static 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 | |||
114 | LExit: | ||
115 | return hr; | ||
116 | } | ||
117 | |||
118 | static 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 | |||
154 | LExit: | ||
155 | return hr; | ||
156 | } | ||
157 | |||
158 | static 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 | |||
170 | LExit: | ||
171 | return hr; | ||
172 | } | ||
173 | |||
174 | static 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 | |||
186 | LExit: | ||
187 | return hr; | ||
188 | } | ||
189 | |||
190 | static 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 | |||
202 | LExit: | ||
203 | return hr; | ||
204 | } | ||
205 | |||
206 | static 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 | |||
218 | LExit: | ||
219 | return hr; | ||
220 | } | ||
221 | |||
222 | static 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 | |||
234 | LExit: | ||
235 | return hr; | ||
236 | } | ||
237 | |||
238 | static 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 | |||
250 | LExit: | ||
251 | return hr; | ||
252 | } | ||
253 | |||
254 | static 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 | |||
266 | LExit: | ||
267 | return hr; | ||
268 | } | ||
269 | |||
270 | static 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 | |||
282 | LExit: | ||
283 | return hr; | ||
284 | } | ||
285 | |||
286 | static 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 | |||
298 | LExit: | ||
299 | return hr; | ||
300 | } | ||
301 | |||
302 | static 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 | |||
314 | LExit: | ||
315 | return hr; | ||
316 | } | ||
317 | |||
318 | static 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 | |||
330 | LExit: | ||
331 | return hr; | ||
332 | } | ||
333 | |||
334 | static 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 | |||
346 | LExit: | ||
347 | return hr; | ||
348 | } | ||
349 | |||
350 | static 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 | |||
362 | LExit: | ||
363 | return hr; | ||
364 | } | ||
365 | |||
366 | static 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 | |||
378 | LExit: | ||
379 | return hr; | ||
380 | } | ||
381 | |||
382 | static 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 | |||
394 | LExit: | ||
395 | return hr; | ||
396 | } | ||
397 | |||
398 | static 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 | |||
410 | LExit: | ||
411 | return hr; | ||
412 | } | ||
413 | |||
414 | static 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 | |||
426 | LExit: | ||
427 | return hr; | ||
428 | } | ||
429 | |||
430 | HRESULT 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 | |||
527 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | // constants | ||
10 | |||
11 | enum 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 | |||
27 | typedef struct _BOOTSTRAPPER_ENGINE_CONTEXT | ||
28 | { | ||
29 | BURN_ENGINE_STATE* pEngineState; | ||
30 | DWORD dwThreadId; | ||
31 | } BOOTSTRAPPER_ENGINE_CONTEXT; | ||
32 | |||
33 | // function declarations | ||
34 | |||
35 | HRESULT 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 | |||
6 | static 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 | |||
18 | LExit: | ||
19 | return hr; | ||
20 | } | ||
21 | |||
22 | static 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 | |||
34 | LExit: | ||
35 | return hr; | ||
36 | } | ||
37 | |||
38 | static 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 | |||
50 | LExit: | ||
51 | return hr; | ||
52 | } | ||
53 | |||
54 | static 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 | |||
66 | LExit: | ||
67 | return hr; | ||
68 | } | ||
69 | |||
70 | static 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 | |||
82 | LExit: | ||
83 | return hr; | ||
84 | } | ||
85 | |||
86 | static 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 | |||
98 | LExit: | ||
99 | return hr; | ||
100 | } | ||
101 | |||
102 | static 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 | |||
138 | LExit: | ||
139 | return hr; | ||
140 | } | ||
141 | |||
142 | static 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 | |||
154 | LExit: | ||
155 | return hr; | ||
156 | } | ||
157 | |||
158 | static 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 | |||
170 | LExit: | ||
171 | return hr; | ||
172 | } | ||
173 | |||
174 | static 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 | |||
186 | LExit: | ||
187 | return hr; | ||
188 | } | ||
189 | |||
190 | static 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 | |||
202 | LExit: | ||
203 | return hr; | ||
204 | } | ||
205 | |||
206 | HRESULT 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 | |||
261 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | // structs | ||
10 | |||
11 | typedef struct _BURN_EXTENSION_ENGINE_CONTEXT | ||
12 | { | ||
13 | BURN_ENGINE_STATE* pEngineState; | ||
14 | } BURN_EXTENSION_ENGINE_CONTEXT; | ||
15 | |||
16 | // function declarations | ||
17 | |||
18 | HRESULT 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 | |||
12 | const DWORD BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS = 2; | ||
13 | |||
14 | enum 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 | |||
27 | typedef 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 | |||
43 | typedef 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 | |||
56 | typedef 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 | ||
68 | static 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 | |||
76 | static void CalculateKeepRegistration( | ||
77 | __in BURN_ENGINE_STATE* pEngineState, | ||
78 | __inout BOOL* pfKeepRegistration | ||
79 | ); | ||
80 | static 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 | ); | ||
86 | static HRESULT ApplyCachePackage( | ||
87 | __in BURN_CACHE_CONTEXT* pContext, | ||
88 | __in BURN_PACKAGE* pPackage | ||
89 | ); | ||
90 | static HRESULT ApplyExtractContainer( | ||
91 | __in BURN_CACHE_CONTEXT* pContext, | ||
92 | __in BURN_CONTAINER* pContainer | ||
93 | ); | ||
94 | static 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 | ); | ||
101 | static HRESULT ApplyLayoutContainer( | ||
102 | __in BURN_CACHE_CONTEXT* pContext, | ||
103 | __in BURN_CONTAINER* pContainer | ||
104 | ); | ||
105 | static HRESULT ApplyProcessPayload( | ||
106 | __in BURN_CACHE_CONTEXT* pContext, | ||
107 | __in_opt BURN_PACKAGE* pPackage, | ||
108 | __in BURN_PAYLOAD_GROUP_ITEM* pPayloadGroupItem | ||
109 | ); | ||
110 | static 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 | ); | ||
116 | static HRESULT ExtractContainer( | ||
117 | __in BURN_CACHE_CONTEXT* pContext, | ||
118 | __in BURN_CONTAINER* pContainer | ||
119 | ); | ||
120 | static HRESULT LayoutBundle( | ||
121 | __in BURN_CACHE_CONTEXT* pContext, | ||
122 | __in_z LPCWSTR wzExecutableName, | ||
123 | __in_z LPCWSTR wzUnverifiedPath, | ||
124 | __in DWORD64 qwBundleSize | ||
125 | ); | ||
126 | static 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 | ); | ||
132 | static HRESULT AcquireContainerOrPayload( | ||
133 | __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, | ||
134 | __out BOOL* pfRetry | ||
135 | ); | ||
136 | static BOOL IsValidLocalFile( | ||
137 | __in_z LPCWSTR wzFilePath, | ||
138 | __in DWORD64 qwFileSize, | ||
139 | __in BOOL fMinimumFileSize | ||
140 | ); | ||
141 | static 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 | ); | ||
149 | static HRESULT PreparePayloadDestinationPath( | ||
150 | __in_z LPCWSTR wzDestinationPath | ||
151 | ); | ||
152 | static HRESULT CopyPayload( | ||
153 | __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, | ||
154 | __in HANDLE hSourceFile, | ||
155 | __in_z LPCWSTR wzSourcePath, | ||
156 | __in_z LPCWSTR wzDestinationPath | ||
157 | ); | ||
158 | static HRESULT DownloadPayload( | ||
159 | __in BURN_CACHE_PROGRESS_CONTEXT* pProgress, | ||
160 | __in_z LPCWSTR wzDestinationPath | ||
161 | ); | ||
162 | static HRESULT CALLBACK CacheMessageHandler( | ||
163 | __in BURN_CACHE_MESSAGE* pMessage, | ||
164 | __in LPVOID pvContext | ||
165 | ); | ||
166 | static HRESULT CompleteCacheProgress( | ||
167 | __in BURN_CACHE_PROGRESS_CONTEXT* pContext, | ||
168 | __in DWORD64 qwFileSize | ||
169 | ); | ||
170 | static 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 | ); | ||
181 | static void DoRollbackCache( | ||
182 | __in BURN_USER_EXPERIENCE* pUX, | ||
183 | __in BURN_PLAN* pPlan, | ||
184 | __in HANDLE hPipe, | ||
185 | __in DWORD dwCheckpoint | ||
186 | ); | ||
187 | static 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 | ); | ||
197 | static 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 | ); | ||
203 | static 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 | ); | ||
212 | static 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 | ); | ||
222 | static 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 | ); | ||
232 | static 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 | ); | ||
242 | static HRESULT ExecutePackageProviderAction( | ||
243 | __in BURN_ENGINE_STATE* pEngineState, | ||
244 | __in BURN_EXECUTE_ACTION* pAction, | ||
245 | __in BURN_EXECUTE_CONTEXT* pContext | ||
246 | ); | ||
247 | static HRESULT ExecuteDependencyAction( | ||
248 | __in BURN_ENGINE_STATE* pEngineState, | ||
249 | __in BURN_EXECUTE_ACTION* pAction, | ||
250 | __in BURN_EXECUTE_CONTEXT* pContext | ||
251 | ); | ||
252 | static HRESULT ExecuteMsiBeginTransaction( | ||
253 | __in BURN_ENGINE_STATE* pEngineState, | ||
254 | __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, | ||
255 | __in BURN_EXECUTE_CONTEXT* pContext | ||
256 | ); | ||
257 | static HRESULT ExecuteMsiCommitTransaction( | ||
258 | __in BURN_ENGINE_STATE* pEngineState, | ||
259 | __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, | ||
260 | __in BURN_EXECUTE_CONTEXT* pContext | ||
261 | ); | ||
262 | static HRESULT ExecuteMsiRollbackTransaction( | ||
263 | __in BURN_ENGINE_STATE* pEngineState, | ||
264 | __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, | ||
265 | __in BURN_EXECUTE_CONTEXT* pContext | ||
266 | ); | ||
267 | static void ResetTransactionRegistrationState( | ||
268 | __in BURN_ENGINE_STATE* pEngineState, | ||
269 | __in BOOL fCommit | ||
270 | ); | ||
271 | static HRESULT CleanPackage( | ||
272 | __in HANDLE hElevatedPipe, | ||
273 | __in BURN_PACKAGE* pPackage | ||
274 | ); | ||
275 | static int GenericExecuteMessageHandler( | ||
276 | __in GENERIC_EXECUTE_MESSAGE* pMessage, | ||
277 | __in LPVOID pvContext | ||
278 | ); | ||
279 | static int MsiExecuteMessageHandler( | ||
280 | __in WIU_MSI_EXECUTE_MESSAGE* pMessage, | ||
281 | __in_opt LPVOID pvContext | ||
282 | ); | ||
283 | static HRESULT ReportOverallProgressTicks( | ||
284 | __in BURN_USER_EXPERIENCE* pUX, | ||
285 | __in BOOL fRollback, | ||
286 | __in DWORD cOverallProgressTicksTotal, | ||
287 | __in DWORD cOverallProgressTicks | ||
288 | ); | ||
289 | static 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 | |||
304 | extern "C" void ApplyInitialize() | ||
305 | { | ||
306 | // Prevent the system from sleeping. | ||
307 | ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED); | ||
308 | } | ||
309 | |||
310 | extern "C" void ApplyUninitialize() | ||
311 | { | ||
312 | ::SetThreadExecutionState(ES_CONTINUOUS); | ||
313 | } | ||
314 | |||
315 | extern "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 | |||
324 | LExit: | ||
325 | return hr; | ||
326 | } | ||
327 | |||
328 | extern "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 | |||
343 | extern "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 | |||
367 | LExit: | ||
368 | ReleaseHandle(hLock); | ||
369 | #endif | ||
370 | return hr; | ||
371 | } | ||
372 | |||
373 | extern "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 | |||
428 | LExit: | ||
429 | UserExperienceOnRegisterComplete(&pEngineState->userExperience, hr); | ||
430 | ReleaseStr(sczEngineWorkingPath); | ||
431 | |||
432 | return hr; | ||
433 | } | ||
434 | |||
435 | extern "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 | |||
490 | LExit: | ||
491 | UserExperienceOnUnregisterComplete(&pEngineState->userExperience, hr); | ||
492 | |||
493 | return hr; | ||
494 | } | ||
495 | |||
496 | extern "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 | |||
590 | LExit: | ||
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 | |||
616 | extern "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 | |||
724 | LExit: | ||
725 | // Send execute complete to BA. | ||
726 | UserExperienceOnExecuteComplete(&pEngineState->userExperience, hr); | ||
727 | |||
728 | return hr; | ||
729 | } | ||
730 | |||
731 | extern "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 | |||
751 | static 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 | |||
782 | static 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 | |||
807 | LExit: | ||
808 | return hr; | ||
809 | } | ||
810 | |||
811 | static 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 | |||
887 | LExit: | ||
888 | return hr; | ||
889 | } | ||
890 | |||
891 | static 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 | |||
938 | LExit: | ||
939 | ReleaseNullStr(pContext->sczLastUsedFolderCandidate); | ||
940 | |||
941 | return hr; | ||
942 | } | ||
943 | |||
944 | static 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 | |||
965 | LExit: | ||
966 | return hr; | ||
967 | } | ||
968 | |||
969 | static 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 | |||
1015 | LExit: | ||
1016 | ReleaseNullStr(pContext->sczLastUsedFolderCandidate); | ||
1017 | |||
1018 | return hr; | ||
1019 | } | ||
1020 | |||
1021 | static 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 | |||
1074 | LExit: | ||
1075 | ReleaseNullStr(pContext->sczLastUsedFolderCandidate); | ||
1076 | |||
1077 | return hr; | ||
1078 | } | ||
1079 | |||
1080 | static 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 | |||
1113 | static 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 | |||
1192 | LExit: | ||
1193 | ReleaseStr(sczStreamName); | ||
1194 | ContainerClose(&context); | ||
1195 | |||
1196 | return hr; | ||
1197 | } | ||
1198 | |||
1199 | static 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 | |||
1337 | LExit: | ||
1338 | ReleaseStr(sczDestinationPath); | ||
1339 | ReleaseStr(sczBundleDownloadUrl); | ||
1340 | ReleaseStr(sczBundlePath); | ||
1341 | |||
1342 | return hr; | ||
1343 | } | ||
1344 | |||
1345 | static 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 | |||
1377 | LExit: | ||
1378 | return hr; | ||
1379 | } | ||
1380 | |||
1381 | static 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 | |||
1567 | LExit: | ||
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 | |||
1584 | static 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 | |||
1604 | static 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 | |||
1708 | LExit: | ||
1709 | return hr; | ||
1710 | } | ||
1711 | |||
1712 | static 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 | |||
1732 | LExit: | ||
1733 | if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) | ||
1734 | { | ||
1735 | hr = S_OK; | ||
1736 | } | ||
1737 | |||
1738 | return hr; | ||
1739 | } | ||
1740 | |||
1741 | static 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 | |||
1796 | LExit: | ||
1797 | ReleaseFileHandle(hDestinationFile); | ||
1798 | ReleaseFileHandle(hSourceOpenedFile); | ||
1799 | |||
1800 | return hr; | ||
1801 | } | ||
1802 | |||
1803 | static 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 | |||
1836 | LExit: | ||
1837 | return hr; | ||
1838 | } | ||
1839 | |||
1840 | static 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 | |||
1893 | LExit: | ||
1894 | ReleaseStr(sczError); | ||
1895 | |||
1896 | return hr; | ||
1897 | } | ||
1898 | |||
1899 | static 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 | |||
1947 | static 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 | |||
2033 | static 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 | |||
2090 | LExit: | ||
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 | |||
2109 | static 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 | |||
2156 | static 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 | |||
2273 | LExit: | ||
2274 | return hr; | ||
2275 | } | ||
2276 | |||
2277 | static 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 | |||
2380 | LExit: | ||
2381 | return hr; | ||
2382 | } | ||
2383 | |||
2384 | static 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 | |||
2450 | LExit: | ||
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 | |||
2464 | static 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 | |||
2515 | LExit: | ||
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 | |||
2529 | static 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 | |||
2589 | LExit: | ||
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 | |||
2603 | static 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 | |||
2670 | LExit: | ||
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 | |||
2684 | static 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 | |||
2703 | LExit: | ||
2704 | return hr; | ||
2705 | } | ||
2706 | |||
2707 | static 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 | |||
2778 | LExit: | ||
2779 | return hr; | ||
2780 | } | ||
2781 | |||
2782 | static 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 | |||
2817 | LExit: | ||
2818 | if (fBeginCalled) | ||
2819 | { | ||
2820 | UserExperienceOnBeginMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr); | ||
2821 | } | ||
2822 | |||
2823 | return hr; | ||
2824 | } | ||
2825 | |||
2826 | static 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 | |||
2861 | LExit: | ||
2862 | if (fCommitBeginCalled) | ||
2863 | { | ||
2864 | UserExperienceOnCommitMsiTransactionComplete(&pEngineState->userExperience, pRollbackBoundary->sczId, hr); | ||
2865 | } | ||
2866 | |||
2867 | return hr; | ||
2868 | } | ||
2869 | |||
2870 | static 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 | |||
2897 | LExit: | ||
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 | |||
2910 | static 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 | |||
2942 | static 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 | |||
2966 | static 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 | |||
2996 | static 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 | |||
3032 | static 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 | |||
3048 | static 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 | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | enum 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 | |||
18 | typedef struct _APPLY_AUTHENTICATION_REQUIRED_DATA | ||
19 | { | ||
20 | BURN_USER_EXPERIENCE* pUX; | ||
21 | LPCWSTR wzPackageOrContainerId; | ||
22 | LPCWSTR wzPayloadId; | ||
23 | } APPLY_AUTHENTICATION_REQUIRED_DATA; | ||
24 | |||
25 | typedef 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 | |||
50 | typedef int (*PFN_GENERICMESSAGEHANDLER)( | ||
51 | __in GENERIC_EXECUTE_MESSAGE* pMessage, | ||
52 | __in LPVOID pvContext | ||
53 | ); | ||
54 | |||
55 | |||
56 | void ApplyInitialize(); | ||
57 | void ApplyUninitialize(); | ||
58 | HRESULT ApplySetVariables( | ||
59 | __in BURN_VARIABLES* pVariables | ||
60 | ); | ||
61 | void ApplyReset( | ||
62 | __in BURN_USER_EXPERIENCE* pUX, | ||
63 | __in BURN_PACKAGES* pPackages | ||
64 | ); | ||
65 | HRESULT ApplyLock( | ||
66 | __in BOOL fPerMachine, | ||
67 | __out HANDLE* phLock | ||
68 | ); | ||
69 | HRESULT ApplyRegister( | ||
70 | __in BURN_ENGINE_STATE* pEngineState | ||
71 | ); | ||
72 | HRESULT ApplyUnregister( | ||
73 | __in BURN_ENGINE_STATE* pEngineState, | ||
74 | __in BOOL fFailedOrRollback, | ||
75 | __in BOOL fSuspend, | ||
76 | __in BOOTSTRAPPER_APPLY_RESTART restart | ||
77 | ); | ||
78 | HRESULT 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 | ); | ||
87 | HRESULT 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 | ); | ||
95 | void 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 | |||
8 | extern "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 | |||
75 | LExit: | ||
76 | ReleaseObject(pixnNodes); | ||
77 | ReleaseObject(pixnNode); | ||
78 | ReleaseStr(scz); | ||
79 | return hr; | ||
80 | } | ||
81 | |||
82 | extern "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 | |||
100 | extern "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 | |||
113 | extern "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 | |||
135 | LExit: | ||
136 | return hr; | ||
137 | } | ||
138 | |||
139 | extern "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 | |||
200 | LExit: | ||
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 | |||
213 | extern "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 | |||
258 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // structs | ||
11 | |||
12 | typedef struct _BURN_APPROVED_EXE | ||
13 | { | ||
14 | LPWSTR sczId; | ||
15 | LPWSTR sczKey; | ||
16 | LPWSTR sczValueName; | ||
17 | BOOL fWin64; | ||
18 | } BURN_APPROVED_EXE; | ||
19 | |||
20 | typedef struct _BURN_APPROVED_EXES | ||
21 | { | ||
22 | BURN_APPROVED_EXE* rgApprovedExes; | ||
23 | DWORD cApprovedExes; | ||
24 | } BURN_APPROVED_EXES; | ||
25 | |||
26 | typedef 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 | |||
38 | HRESULT ApprovedExesParseFromXml( | ||
39 | __in BURN_APPROVED_EXES* pApprovedExes, | ||
40 | __in IXMLDOMNode* pixnBundle | ||
41 | ); | ||
42 | |||
43 | void ApprovedExesUninitialize( | ||
44 | __in BURN_APPROVED_EXES* pApprovedExes | ||
45 | ); | ||
46 | void ApprovedExesUninitializeLaunch( | ||
47 | __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe | ||
48 | ); | ||
49 | HRESULT ApprovedExesFindById( | ||
50 | __in BURN_APPROVED_EXES* pApprovedExes, | ||
51 | __in_z LPCWSTR wzId, | ||
52 | __out BURN_APPROVED_EXE** ppApprovedExe | ||
53 | ); | ||
54 | HRESULT ApprovedExesLaunch( | ||
55 | __in BURN_VARIABLES* pVariables, | ||
56 | __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe, | ||
57 | __out DWORD* pdwProcessId | ||
58 | ); | ||
59 | HRESULT 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 | |||
6 | static 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 | *******************************************************************/ | ||
19 | EXTERN_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 | |||
74 | LExit: | ||
75 | ReleaseObject(pixnNode); | ||
76 | ReleaseObject(pixnNodes); | ||
77 | |||
78 | return hr; | ||
79 | } | ||
80 | |||
81 | /******************************************************************* | ||
82 | BurnExtensionUninitialize - | ||
83 | |||
84 | *******************************************************************/ | ||
85 | EXTERN_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 | *******************************************************************/ | ||
109 | EXTERN_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 | |||
160 | LExit: | ||
161 | ReleaseStr(sczBundleExtensionDataPath); | ||
162 | |||
163 | return hr; | ||
164 | } | ||
165 | |||
166 | /******************************************************************* | ||
167 | BurnExtensionUnload - | ||
168 | |||
169 | *******************************************************************/ | ||
170 | EXTERN_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 | |||
203 | EXTERN_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 | |||
225 | LExit: | ||
226 | return hr; | ||
227 | } | ||
228 | |||
229 | EXTERN_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 | |||
248 | LExit: | ||
249 | return hr; | ||
250 | } | ||
251 | |||
252 | static 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) | ||
7 | extern "C" { | ||
8 | #endif | ||
9 | |||
10 | // structs | ||
11 | |||
12 | typedef struct _BURN_EXTENSION_ENGINE_CONTEXT BURN_EXTENSION_ENGINE_CONTEXT; | ||
13 | |||
14 | typedef 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 | |||
26 | typedef struct _BURN_EXTENSIONS | ||
27 | { | ||
28 | BURN_EXTENSION* rgExtensions; | ||
29 | DWORD cExtensions; | ||
30 | } BURN_EXTENSIONS; | ||
31 | |||
32 | // functions | ||
33 | |||
34 | HRESULT BurnExtensionParseFromXml( | ||
35 | __in BURN_EXTENSIONS* pBurnExtensions, | ||
36 | __in BURN_PAYLOADS* pBaPayloads, | ||
37 | __in IXMLDOMNode* pixnBundle | ||
38 | ); | ||
39 | void BurnExtensionUninitialize( | ||
40 | __in BURN_EXTENSIONS* pBurnExtensions | ||
41 | ); | ||
42 | HRESULT BurnExtensionLoad( | ||
43 | __in BURN_EXTENSIONS* pBurnExtensions, | ||
44 | __in BURN_EXTENSION_ENGINE_CONTEXT* pEngineContext | ||
45 | ); | ||
46 | void BurnExtensionUnload( | ||
47 | __in BURN_EXTENSIONS* pBurnExtensions | ||
48 | ); | ||
49 | HRESULT BurnExtensionFindById( | ||
50 | __in BURN_EXTENSIONS* pBurnExtensions, | ||
51 | __in_z LPCWSTR wzId, | ||
52 | __out BURN_EXTENSION** ppExtension | ||
53 | ); | ||
54 | BEEAPI 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 | |||
9 | const LPSTR INVALID_CAB_NAME = "<the>.cab"; | ||
10 | |||
11 | // structs | ||
12 | |||
13 | typedef 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 | |||
37 | static HRESULT BeginAndWaitForOperation( | ||
38 | __in BURN_CONTAINER_CONTEXT* pContext | ||
39 | ); | ||
40 | static HRESULT WaitForOperation( | ||
41 | __in BURN_CONTAINER_CONTEXT* pContext | ||
42 | ); | ||
43 | static DWORD WINAPI ExtractThreadProc( | ||
44 | __in LPVOID lpThreadParameter | ||
45 | ); | ||
46 | static INT_PTR DIAMONDAPI CabNotifyCallback( | ||
47 | __in FDINOTIFICATIONTYPE iNotification, | ||
48 | __inout FDINOTIFICATION *pFDINotify | ||
49 | ); | ||
50 | static INT_PTR CopyFileCallback( | ||
51 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
52 | __inout FDINOTIFICATION *pFDINotify | ||
53 | ); | ||
54 | static INT_PTR CloseFileInfoCallback( | ||
55 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
56 | __inout FDINOTIFICATION *pFDINotify | ||
57 | ); | ||
58 | static LPVOID DIAMONDAPI CabAlloc( | ||
59 | __in DWORD dwSize | ||
60 | ); | ||
61 | static void DIAMONDAPI CabFree( | ||
62 | __in LPVOID pvData | ||
63 | ); | ||
64 | static INT_PTR FAR DIAMONDAPI CabOpen( | ||
65 | __in char FAR *pszFile, | ||
66 | __in int /* oflag */, | ||
67 | __in int /* pmode */ | ||
68 | ); | ||
69 | static UINT FAR DIAMONDAPI CabRead( | ||
70 | __in INT_PTR hf, | ||
71 | __out void FAR *pv, | ||
72 | __in UINT cb | ||
73 | ); | ||
74 | static UINT FAR DIAMONDAPI CabWrite( | ||
75 | __in INT_PTR hf, | ||
76 | __in void FAR *pv, | ||
77 | __in UINT cb | ||
78 | ); | ||
79 | static long FAR DIAMONDAPI CabSeek( | ||
80 | __in INT_PTR hf, | ||
81 | __in long dist, | ||
82 | __in int seektype | ||
83 | ); | ||
84 | static int FAR DIAMONDAPI CabClose( | ||
85 | __in INT_PTR hf | ||
86 | ); | ||
87 | static HRESULT AddVirtualFilePointer( | ||
88 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
89 | __in HANDLE hFile, | ||
90 | __in LONGLONG llInitialFilePointer | ||
91 | ); | ||
92 | static HRESULT ReadIfVirtualFilePointer( | ||
93 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
94 | __in HANDLE hFile, | ||
95 | __in DWORD cbRead | ||
96 | ); | ||
97 | static 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 | ); | ||
104 | static HRESULT CloseIfVirturalFilePointer( | ||
105 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
106 | __in HANDLE hFile | ||
107 | ); | ||
108 | static 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 | |||
121 | extern "C" void CabExtractInitialize() | ||
122 | { | ||
123 | } | ||
124 | |||
125 | extern "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 | |||
153 | LExit: | ||
154 | return hr; | ||
155 | } | ||
156 | |||
157 | extern "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 | |||
175 | LExit: | ||
176 | return hr; | ||
177 | } | ||
178 | |||
179 | extern "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 | |||
197 | LExit: | ||
198 | return hr; | ||
199 | } | ||
200 | |||
201 | extern "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 | |||
225 | LExit: | ||
226 | return hr; | ||
227 | } | ||
228 | |||
229 | extern "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 | |||
242 | LExit: | ||
243 | return hr; | ||
244 | } | ||
245 | |||
246 | extern "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 | |||
271 | LExit: | ||
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 | |||
284 | static 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 | |||
299 | LExit: | ||
300 | return hr; | ||
301 | } | ||
302 | |||
303 | static 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 | |||
337 | LExit: | ||
338 | return hr; | ||
339 | } | ||
340 | |||
341 | static 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 | |||
458 | LExit: | ||
459 | if (hfdi) | ||
460 | { | ||
461 | ::FDIDestroy(hfdi); | ||
462 | } | ||
463 | if (fComInitialized) | ||
464 | { | ||
465 | ::CoUninitialize(); | ||
466 | } | ||
467 | |||
468 | return (DWORD)hr; | ||
469 | } | ||
470 | |||
471 | static 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 | |||
503 | static 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 | |||
619 | LExit: | ||
620 | ReleaseStr(pwzPath); | ||
621 | |||
622 | pContext->Cabinet.hrError = hr; | ||
623 | return SUCCEEDED(hr) ? ipResult : -1; | ||
624 | } | ||
625 | |||
626 | static 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 | |||
680 | LExit: | ||
681 | pContext->Cabinet.hrError = hr; | ||
682 | return SUCCEEDED(hr) ? ipResult : -1; | ||
683 | } | ||
684 | |||
685 | static LPVOID DIAMONDAPI CabAlloc( | ||
686 | __in DWORD dwSize | ||
687 | ) | ||
688 | { | ||
689 | return MemAlloc(dwSize, FALSE); | ||
690 | } | ||
691 | |||
692 | static void DIAMONDAPI CabFree( | ||
693 | __in LPVOID pvData | ||
694 | ) | ||
695 | { | ||
696 | MemFree(pvData); | ||
697 | } | ||
698 | |||
699 | static 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 | |||
728 | LExit: | ||
729 | pContext->Cabinet.hrError = hr; | ||
730 | return FAILED(hr) ? -1 : (INT_PTR)hFile; | ||
731 | } | ||
732 | |||
733 | static 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 | |||
751 | LExit: | ||
752 | pContext->Cabinet.hrError = hr; | ||
753 | return FAILED(hr) ? -1 : cbRead; | ||
754 | } | ||
755 | |||
756 | static 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 | |||
789 | LExit: | ||
790 | pContext->Cabinet.hrError = hr; | ||
791 | return FAILED(hr) ? -1 : cbWrite; | ||
792 | } | ||
793 | |||
794 | static 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 | |||
843 | LExit: | ||
844 | pContext->Cabinet.hrError = hr; | ||
845 | return FAILED(hr) ? -1 : liNewPointer.LowPart; | ||
846 | } | ||
847 | |||
848 | static 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 | |||
861 | static 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 | |||
876 | LExit: | ||
877 | return hr; | ||
878 | } | ||
879 | |||
880 | static 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 | |||
901 | LExit: | ||
902 | return hr; | ||
903 | } | ||
904 | |||
905 | static 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 | |||
941 | static 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 | |||
959 | static 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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // function declarations | ||
11 | |||
12 | void CabExtractInitialize(); | ||
13 | HRESULT CabExtractOpen( | ||
14 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
15 | __in LPCWSTR wzFilePath | ||
16 | ); | ||
17 | HRESULT CabExtractNextStream( | ||
18 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
19 | __inout_z LPWSTR* psczStreamName | ||
20 | ); | ||
21 | HRESULT CabExtractStreamToFile( | ||
22 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
23 | __in_z LPCWSTR wzFileName | ||
24 | ); | ||
25 | HRESULT CabExtractStreamToBuffer( | ||
26 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
27 | __out BYTE** ppbBuffer, | ||
28 | __out SIZE_T* pcbBuffer | ||
29 | ); | ||
30 | HRESULT CabExtractSkipStream( | ||
31 | __in BURN_CONTAINER_CONTEXT* pContext | ||
32 | ); | ||
33 | HRESULT 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 | |||
5 | static const LPCWSTR BUNDLE_CLEAN_ROOM_WORKING_FOLDER_NAME = L".cr"; | ||
6 | static const LPCWSTR BUNDLE_WORKING_FOLDER_NAME = L".be"; | ||
7 | static const LPCWSTR UNVERIFIED_CACHE_FOLDER_NAME = L".unverified"; | ||
8 | static const LPCWSTR PACKAGE_CACHE_FOLDER_NAME = L"Package Cache"; | ||
9 | static const DWORD FILE_OPERATION_RETRY_COUNT = 3; | ||
10 | static const DWORD FILE_OPERATION_RETRY_WAIT = 2000; | ||
11 | |||
12 | static BOOL vfInitializedCache = FALSE; | ||
13 | static BOOL vfRunningFromCache = FALSE; | ||
14 | static LPWSTR vsczSourceProcessFolder = NULL; | ||
15 | static LPWSTR vsczWorkingFolder = NULL; | ||
16 | static LPWSTR vsczDefaultUserPackageCache = NULL; | ||
17 | static LPWSTR vsczDefaultMachinePackageCache = NULL; | ||
18 | static LPWSTR vsczCurrentMachinePackageCache = NULL; | ||
19 | |||
20 | static HRESULT CalculateWorkingFolder( | ||
21 | __in_z LPCWSTR wzBundleId, | ||
22 | __deref_out_z LPWSTR* psczWorkingFolder | ||
23 | ); | ||
24 | static HRESULT GetLastUsedSourceFolder( | ||
25 | __in BURN_VARIABLES* pVariables, | ||
26 | __out_z LPWSTR* psczLastSource | ||
27 | ); | ||
28 | static HRESULT CreateCompletedPath( | ||
29 | __in BOOL fPerMachine, | ||
30 | __in LPCWSTR wzCacheId, | ||
31 | __out LPWSTR* psczCacheDirectory | ||
32 | ); | ||
33 | static HRESULT CreateUnverifiedPath( | ||
34 | __in BOOL fPerMachine, | ||
35 | __in_z LPCWSTR wzPayloadId, | ||
36 | __out_z LPWSTR* psczUnverifiedPayloadPath | ||
37 | ); | ||
38 | static HRESULT GetRootPath( | ||
39 | __in BOOL fPerMachine, | ||
40 | __in BOOL fAllowRedirect, | ||
41 | __deref_out_z LPWSTR* psczRootPath | ||
42 | ); | ||
43 | static 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 | ); | ||
52 | static 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 | ); | ||
61 | static 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 | ); | ||
71 | static 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 | ); | ||
80 | static 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 | ); | ||
89 | static HRESULT ResetPathPermissions( | ||
90 | __in BOOL fPerMachine, | ||
91 | __in_z LPCWSTR wzPath | ||
92 | ); | ||
93 | static HRESULT SecurePath( | ||
94 | __in LPCWSTR wzPath | ||
95 | ); | ||
96 | static 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 | ); | ||
103 | static HRESULT CopyEngineWithSignatureFixup( | ||
104 | __in HANDLE hEngineFile, | ||
105 | __in_z LPCWSTR wzEnginePath, | ||
106 | __in_z LPCWSTR wzTargetPath, | ||
107 | __in BURN_SECTION* pSection | ||
108 | ); | ||
109 | static HRESULT RemoveBundleOrPackage( | ||
110 | __in BOOL fBundle, | ||
111 | __in BOOL fPerMachine, | ||
112 | __in_z LPCWSTR wzBundleOrPackageId, | ||
113 | __in_z LPCWSTR wzCacheId | ||
114 | ); | ||
115 | static 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 | ); | ||
126 | static HRESULT SendCacheBeginMessage( | ||
127 | __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, | ||
128 | __in LPVOID pContext, | ||
129 | __in BURN_CACHE_STEP cacheStep | ||
130 | ); | ||
131 | static HRESULT SendCacheSuccessMessage( | ||
132 | __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, | ||
133 | __in LPVOID pContext, | ||
134 | __in DWORD64 qwFileSize | ||
135 | ); | ||
136 | static HRESULT SendCacheCompleteMessage( | ||
137 | __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, | ||
138 | __in LPVOID pContext, | ||
139 | __in HRESULT hrStatus | ||
140 | ); | ||
141 | |||
142 | |||
143 | extern "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 | |||
216 | LExit: | ||
217 | ReleaseStr(sczCurrentPath); | ||
218 | ReleaseStr(sczCompletedFolder); | ||
219 | ReleaseStr(sczCompletedPath); | ||
220 | ReleaseStr(sczOriginalSource); | ||
221 | ReleaseStr(sczOriginalSourceFolder); | ||
222 | |||
223 | return hr; | ||
224 | } | ||
225 | |||
226 | extern "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 | |||
249 | LExit: | ||
250 | ReleaseStr(sczWorkingFolder); | ||
251 | |||
252 | return hr; | ||
253 | } | ||
254 | |||
255 | extern "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 | |||
282 | LExit: | ||
283 | ReleaseStr(sczWorkingFolder); | ||
284 | |||
285 | return hr; | ||
286 | } | ||
287 | |||
288 | extern "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 | |||
302 | LExit: | ||
303 | ReleaseStr(sczWorkingFolder); | ||
304 | |||
305 | return hr; | ||
306 | } | ||
307 | |||
308 | extern "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 | |||
322 | LExit: | ||
323 | return hr; | ||
324 | } | ||
325 | |||
326 | extern "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 | |||
340 | LExit: | ||
341 | return hr; | ||
342 | } | ||
343 | |||
344 | extern "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 | |||
364 | extern "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 | |||
413 | LExit: | ||
414 | ReleaseNullStr(sczDefaultCompletedPath); | ||
415 | ReleaseNullStr(sczCurrentCompletedPath); | ||
416 | ReleaseNullStr(sczRootPath); | ||
417 | |||
418 | return hr; | ||
419 | } | ||
420 | |||
421 | extern "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 | |||
431 | LExit: | ||
432 | return hr; | ||
433 | } | ||
434 | |||
435 | extern "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 | |||
600 | LExit: | ||
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 | |||
612 | extern "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 | |||
664 | LExit: | ||
665 | ReleaseStr(sczLastSourceFolder); | ||
666 | ReleaseStr(sczSourceFolder); | ||
667 | |||
668 | return hr; | ||
669 | } | ||
670 | |||
671 | extern "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 | |||
713 | LExit: | ||
714 | return hr; | ||
715 | } | ||
716 | |||
717 | extern "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 | |||
739 | extern "C" BOOL CacheBundleRunningFromCache() | ||
740 | { | ||
741 | return vfRunningFromCache; | ||
742 | } | ||
743 | |||
744 | extern "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 | |||
761 | LExit: | ||
762 | ReleaseStr(sczSourcePath); | ||
763 | |||
764 | return hr; | ||
765 | } | ||
766 | |||
767 | extern "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 | |||
796 | LExit: | ||
797 | ReleaseStr(sczSourcePath); | ||
798 | |||
799 | return hr; | ||
800 | } | ||
801 | |||
802 | extern "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 | |||
823 | LExit: | ||
824 | ReleaseStr(sczTargetPath); | ||
825 | |||
826 | return hr; | ||
827 | } | ||
828 | |||
829 | extern "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 | |||
880 | LExit: | ||
881 | ReleaseStr(sczPayloadSourcePath); | ||
882 | ReleaseStr(sczSourceDirectory); | ||
883 | ReleaseStr(sczTargetPath); | ||
884 | ReleaseStr(sczTargetDirectory); | ||
885 | |||
886 | return hr; | ||
887 | } | ||
888 | |||
889 | extern "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 | |||
908 | LExit: | ||
909 | ReleaseStr(sczCachedPath); | ||
910 | |||
911 | return hr; | ||
912 | } | ||
913 | |||
914 | extern "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 | |||
933 | LExit: | ||
934 | ReleaseStr(sczCachedPath); | ||
935 | |||
936 | return hr; | ||
937 | } | ||
938 | |||
939 | extern "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 | |||
1007 | LExit: | ||
1008 | ReleaseStr(sczUnverifiedPayloadPath); | ||
1009 | ReleaseStr(sczCachedPath); | ||
1010 | ReleaseStr(sczCachedDirectory); | ||
1011 | |||
1012 | return hr; | ||
1013 | } | ||
1014 | |||
1015 | extern "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 | |||
1031 | LExit: | ||
1032 | ReleaseStr(sczCachedPath); | ||
1033 | |||
1034 | return hr; | ||
1035 | } | ||
1036 | |||
1037 | extern "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 | |||
1053 | LExit: | ||
1054 | ReleaseStr(sczCachedPath); | ||
1055 | |||
1056 | return hr; | ||
1057 | } | ||
1058 | |||
1059 | extern "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 | |||
1076 | LExit: | ||
1077 | ReleaseStr(sczWorkingFolder); | ||
1078 | |||
1079 | return hr; | ||
1080 | } | ||
1081 | |||
1082 | extern "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 | |||
1092 | LExit: | ||
1093 | return hr; | ||
1094 | } | ||
1095 | |||
1096 | extern "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 | |||
1107 | LExit: | ||
1108 | return hr; | ||
1109 | } | ||
1110 | |||
1111 | extern "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 | |||
1178 | extern "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 | |||
1192 | static 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 | |||
1243 | LExit: | ||
1244 | return hr; | ||
1245 | } | ||
1246 | |||
1247 | static 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 | |||
1317 | LExit: | ||
1318 | ReleaseStr(sczAppData); | ||
1319 | |||
1320 | return hr; | ||
1321 | } | ||
1322 | |||
1323 | static 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 | |||
1340 | static 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 | |||
1381 | LExit: | ||
1382 | ReleaseStr(sczCacheDirectory); | ||
1383 | return hr; | ||
1384 | } | ||
1385 | |||
1386 | static 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 | |||
1411 | LExit: | ||
1412 | ReleaseStr(sczUnverifiedCacheFolder); | ||
1413 | |||
1414 | return hr; | ||
1415 | } | ||
1416 | |||
1417 | static 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 | |||
1448 | LExit: | ||
1449 | ReleaseFileHandle(hFile); | ||
1450 | |||
1451 | return hr; | ||
1452 | } | ||
1453 | |||
1454 | static 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 | |||
1484 | LExit: | ||
1485 | ReleaseFileHandle(hFile); | ||
1486 | |||
1487 | return hr; | ||
1488 | } | ||
1489 | |||
1490 | static 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 | |||
1520 | LExit: | ||
1521 | SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr); | ||
1522 | |||
1523 | return hr; | ||
1524 | } | ||
1525 | |||
1526 | static 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 | |||
1563 | LExit: | ||
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 | |||
1579 | static 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 | |||
1616 | LExit: | ||
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 | |||
1632 | static 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 | |||
1652 | LExit: | ||
1653 | ReleaseMem(pAllocSid); | ||
1654 | return hr; | ||
1655 | } | ||
1656 | |||
1657 | |||
1658 | static 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 | |||
1688 | LExit: | ||
1689 | ReleaseMem(pSid); | ||
1690 | return hr; | ||
1691 | } | ||
1692 | |||
1693 | |||
1694 | static 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 | |||
1711 | LExit: | ||
1712 | return hr; | ||
1713 | } | ||
1714 | |||
1715 | |||
1716 | static 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 | |||
1746 | LExit: | ||
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 | |||
1761 | static 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 | |||
1799 | LExit: | ||
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 | |||
1811 | static 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 | |||
1872 | LExit: | ||
1873 | ReleaseFileHandle(hTarget); | ||
1874 | |||
1875 | return hr; | ||
1876 | } | ||
1877 | |||
1878 | |||
1879 | static 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 | |||
1932 | LExit: | ||
1933 | ReleaseStr(sczDirectory); | ||
1934 | ReleaseStr(sczRootCacheDirectory); | ||
1935 | |||
1936 | return hr; | ||
1937 | } | ||
1938 | |||
1939 | static 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 | |||
1994 | LExit: | ||
1995 | SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr); | ||
1996 | |||
1997 | ReleaseStr(pszActual); | ||
1998 | ReleaseStr(pszExpected); | ||
1999 | |||
2000 | return hr; | ||
2001 | } | ||
2002 | |||
2003 | static 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 | |||
2020 | static 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 | |||
2037 | static 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 | ||
7 | extern "C" { | ||
8 | #endif | ||
9 | |||
10 | |||
11 | enum BURN_CACHE_MESSAGE_TYPE | ||
12 | { | ||
13 | BURN_CACHE_MESSAGE_BEGIN, | ||
14 | BURN_CACHE_MESSAGE_SUCCESS, | ||
15 | BURN_CACHE_MESSAGE_COMPLETE, | ||
16 | }; | ||
17 | |||
18 | enum 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 | |||
27 | typedef 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 | |||
48 | typedef HRESULT(CALLBACK* PFN_BURNCACHEMESSAGEHANDLER)( | ||
49 | __in BURN_CACHE_MESSAGE* pMessage, | ||
50 | __in LPVOID pvContext | ||
51 | ); | ||
52 | |||
53 | // functions | ||
54 | |||
55 | HRESULT CacheInitialize( | ||
56 | __in BURN_REGISTRATION* pRegistration, | ||
57 | __in BURN_VARIABLES* pVariables, | ||
58 | __in_z_opt LPCWSTR wzSourceProcessPath | ||
59 | ); | ||
60 | HRESULT CacheEnsureWorkingFolder( | ||
61 | __in_z_opt LPCWSTR wzBundleId, | ||
62 | __deref_out_z_opt LPWSTR* psczWorkingFolder | ||
63 | ); | ||
64 | HRESULT CacheCalculateBundleWorkingPath( | ||
65 | __in_z LPCWSTR wzBundleId, | ||
66 | __in LPCWSTR wzExecutableName, | ||
67 | __deref_out_z LPWSTR* psczWorkingPath | ||
68 | ); | ||
69 | HRESULT CacheCalculateBundleLayoutWorkingPath( | ||
70 | __in_z LPCWSTR wzBundleId, | ||
71 | __deref_out_z LPWSTR* psczWorkingPath | ||
72 | ); | ||
73 | HRESULT CacheCalculatePayloadWorkingPath( | ||
74 | __in_z LPCWSTR wzBundleId, | ||
75 | __in BURN_PAYLOAD* pPayload, | ||
76 | __deref_out_z LPWSTR* psczWorkingPath | ||
77 | ); | ||
78 | HRESULT CacheCalculateContainerWorkingPath( | ||
79 | __in_z LPCWSTR wzBundleId, | ||
80 | __in BURN_CONTAINER* pContainer, | ||
81 | __deref_out_z LPWSTR* psczWorkingPath | ||
82 | ); | ||
83 | HRESULT CacheGetRootCompletedPath( | ||
84 | __in BOOL fPerMachine, | ||
85 | __in BOOL fForceInitialize, | ||
86 | __deref_out_z LPWSTR* psczRootCompletedPath | ||
87 | ); | ||
88 | HRESULT CacheGetCompletedPath( | ||
89 | __in BOOL fPerMachine, | ||
90 | __in_z LPCWSTR wzCacheId, | ||
91 | __deref_out_z LPWSTR* psczCompletedPath | ||
92 | ); | ||
93 | HRESULT CacheGetResumePath( | ||
94 | __in_z LPCWSTR wzPayloadWorkingPath, | ||
95 | __deref_out_z LPWSTR* psczResumePath | ||
96 | ); | ||
97 | HRESULT 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 | ); | ||
108 | HRESULT CacheSetLastUsedSource( | ||
109 | __in BURN_VARIABLES* pVariables, | ||
110 | __in_z LPCWSTR wzSourcePath, | ||
111 | __in_z LPCWSTR wzRelativePath | ||
112 | ); | ||
113 | HRESULT CacheSendProgressCallback( | ||
114 | __in DOWNLOAD_CACHE_CALLBACK* pCallback, | ||
115 | __in DWORD64 dw64Progress, | ||
116 | __in DWORD64 dw64Total, | ||
117 | __in HANDLE hDestinationFile | ||
118 | ); | ||
119 | void CacheSendErrorCallback( | ||
120 | __in DOWNLOAD_CACHE_CALLBACK* pCallback, | ||
121 | __in HRESULT hrError, | ||
122 | __in_z_opt LPCWSTR wzError, | ||
123 | __out_opt BOOL* pfRetry | ||
124 | ); | ||
125 | BOOL CacheBundleRunningFromCache(); | ||
126 | HRESULT CacheBundleToCleanRoom( | ||
127 | __in BURN_SECTION* pSection, | ||
128 | __deref_out_z_opt LPWSTR* psczCleanRoomBundlePath | ||
129 | ); | ||
130 | HRESULT CacheBundleToWorkingDirectory( | ||
131 | __in_z LPCWSTR wzBundleId, | ||
132 | __in_z LPCWSTR wzExecutableName, | ||
133 | __in BURN_SECTION* pSection, | ||
134 | __deref_out_z_opt LPWSTR* psczEngineWorkingPath | ||
135 | ); | ||
136 | HRESULT 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 | ); | ||
145 | HRESULT 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 | ); | ||
154 | HRESULT 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 | ); | ||
163 | HRESULT 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 | ); | ||
172 | HRESULT 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 | ); | ||
182 | HRESULT 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 | ); | ||
189 | HRESULT 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 | ); | ||
196 | HRESULT CacheRemoveWorkingFolder( | ||
197 | __in_z_opt LPCWSTR wzBundleId | ||
198 | ); | ||
199 | HRESULT CacheRemoveBundle( | ||
200 | __in BOOL fPerMachine, | ||
201 | __in_z LPCWSTR wzPackageId | ||
202 | ); | ||
203 | HRESULT CacheRemovePackage( | ||
204 | __in BOOL fPerMachine, | ||
205 | __in_z LPCWSTR wzPackageId, | ||
206 | __in_z LPCWSTR wzCacheId | ||
207 | ); | ||
208 | void CacheCleanup( | ||
209 | __in BOOL fPerMachine, | ||
210 | __in_z LPCWSTR wzBundleId | ||
211 | ); | ||
212 | void 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 | |||
23 | enum 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 | |||
60 | struct BURN_SYMBOL | ||
61 | { | ||
62 | BURN_SYMBOL_TYPE Type; | ||
63 | DWORD iPosition; | ||
64 | BURN_VARIANT Value; | ||
65 | }; | ||
66 | |||
67 | struct BURN_CONDITION_PARSE_CONTEXT | ||
68 | { | ||
69 | BURN_VARIABLES* pVariables; | ||
70 | LPCWSTR wzCondition; | ||
71 | LPCWSTR wzRead; | ||
72 | BURN_SYMBOL NextSymbol; | ||
73 | BOOL fError; | ||
74 | }; | ||
75 | |||
76 | struct BURN_CONDITION_OPERAND | ||
77 | { | ||
78 | BOOL fHidden; | ||
79 | BURN_VARIANT Value; | ||
80 | }; | ||
81 | |||
82 | |||
83 | // internal function declarations | ||
84 | |||
85 | static HRESULT ParseExpression( | ||
86 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
87 | __out BOOL* pf | ||
88 | ); | ||
89 | static HRESULT ParseBooleanTerm( | ||
90 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
91 | __out BOOL* pf | ||
92 | ); | ||
93 | static HRESULT ParseBooleanFactor( | ||
94 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
95 | __out BOOL* pf | ||
96 | ); | ||
97 | static HRESULT ParseTerm( | ||
98 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
99 | __out BOOL* pf | ||
100 | ); | ||
101 | static HRESULT ParseOperand( | ||
102 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
103 | __out BURN_CONDITION_OPERAND* pOperand | ||
104 | ); | ||
105 | static HRESULT Expect( | ||
106 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
107 | __in BURN_SYMBOL_TYPE symbolType | ||
108 | ); | ||
109 | static HRESULT NextSymbol( | ||
110 | __in BURN_CONDITION_PARSE_CONTEXT* pContext | ||
111 | ); | ||
112 | static 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 | ); | ||
118 | static HRESULT CompareStringValues( | ||
119 | __in BURN_SYMBOL_TYPE comparison, | ||
120 | __in_z LPCWSTR wzLeftOperand, | ||
121 | __in_z LPCWSTR wzRightOperand, | ||
122 | __out BOOL* pfResult | ||
123 | ); | ||
124 | static HRESULT CompareIntegerValues( | ||
125 | __in BURN_SYMBOL_TYPE comparison, | ||
126 | __in LONGLONG llLeftOperand, | ||
127 | __in LONGLONG llRightOperand, | ||
128 | __out BOOL* pfResult | ||
129 | ); | ||
130 | static 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 | |||
140 | extern "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 | |||
168 | LExit: | ||
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 | |||
178 | extern "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 | |||
215 | LExit: | ||
216 | return hr; | ||
217 | } | ||
218 | |||
219 | HRESULT 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 | |||
243 | LExit: | ||
244 | ReleaseBSTR(bstrExpression); | ||
245 | ReleaseObject(pixnNode); | ||
246 | |||
247 | return hr; | ||
248 | } | ||
249 | |||
250 | |||
251 | // internal function definitions | ||
252 | |||
253 | static 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 | |||
280 | LExit: | ||
281 | return hr; | ||
282 | } | ||
283 | |||
284 | static 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 | |||
311 | LExit: | ||
312 | return hr; | ||
313 | } | ||
314 | |||
315 | static 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 | |||
337 | LExit: | ||
338 | return hr; | ||
339 | } | ||
340 | |||
341 | static 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 | |||
419 | LExit: | ||
420 | BVariantUninitialize(&firstOperand.Value); | ||
421 | BVariantUninitialize(&secondOperand.Value); | ||
422 | return hr; | ||
423 | } | ||
424 | |||
425 | static 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 | |||
477 | LExit: | ||
478 | StrSecureZeroFreeString(sczFormatted); | ||
479 | |||
480 | return hr; | ||
481 | } | ||
482 | |||
483 | // | ||
484 | // Expect - expects a symbol. | ||
485 | // | ||
486 | static 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 | |||
503 | LExit: | ||
504 | return hr; | ||
505 | } | ||
506 | |||
507 | // | ||
508 | // NextSymbol - finds the next symbol in an expression string. | ||
509 | // | ||
510 | static 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 | |||
778 | LExit: | ||
779 | return hr; | ||
780 | } | ||
781 | |||
782 | // | ||
783 | // CompareOperands - compares two variant values using a given comparison. | ||
784 | // | ||
785 | static 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 | |||
909 | LExit: | ||
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 | // | ||
923 | static 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 | |||
992 | LExit: | ||
993 | return hr; | ||
994 | } | ||
995 | |||
996 | // | ||
997 | // CompareIntegerValues - compares two integer values using a given comparison. | ||
998 | // | ||
999 | static 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 | |||
1023 | LExit: | ||
1024 | return hr; | ||
1025 | } | ||
1026 | |||
1027 | // | ||
1028 | // CompareVersionValues - compares two quad-word version values using a given comparison. | ||
1029 | // | ||
1030 | static 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 | |||
1055 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | typedef 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 | |||
19 | HRESULT ConditionEvaluate( | ||
20 | __in BURN_VARIABLES* pVariables, | ||
21 | __in_z LPCWSTR wzCondition, | ||
22 | __out BOOL* pf | ||
23 | ); | ||
24 | HRESULT 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 | ); | ||
32 | HRESULT 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 | |||
8 | extern "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 | |||
125 | LExit: | ||
126 | ReleaseObject(pixnNodes); | ||
127 | ReleaseObject(pixnNode); | ||
128 | ReleaseStr(scz); | ||
129 | |||
130 | return hr; | ||
131 | } | ||
132 | |||
133 | extern "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 | |||
156 | LExit: | ||
157 | return hr; | ||
158 | } | ||
159 | |||
160 | extern "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 | |||
187 | extern "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 | |||
213 | LExit: | ||
214 | ReleaseStr(sczExecutablePath); | ||
215 | |||
216 | return hr; | ||
217 | } | ||
218 | |||
219 | extern "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 | |||
268 | LExit: | ||
269 | return hr; | ||
270 | } | ||
271 | |||
272 | extern "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 | |||
290 | extern "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 | |||
308 | extern "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 | |||
331 | extern "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 | |||
348 | extern "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 | |||
363 | LExit: | ||
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 | |||
374 | extern "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 | |||
396 | LExit: | ||
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) | ||
6 | extern "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 | |||
38 | enum BURN_CONTAINER_TYPE | ||
39 | { | ||
40 | BURN_CONTAINER_TYPE_NONE, | ||
41 | BURN_CONTAINER_TYPE_CABINET, | ||
42 | BURN_CONTAINER_TYPE_SEVENZIP, | ||
43 | }; | ||
44 | |||
45 | enum 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 | |||
58 | typedef 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 | |||
85 | typedef struct _BURN_CONTAINERS | ||
86 | { | ||
87 | BURN_CONTAINER* rgContainers; | ||
88 | DWORD cContainers; | ||
89 | } BURN_CONTAINERS; | ||
90 | |||
91 | typedef 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 | |||
97 | typedef 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 | |||
119 | typedef 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 | |||
142 | HRESULT ContainersParseFromXml( | ||
143 | __in BURN_CONTAINERS* pContainers, | ||
144 | __in IXMLDOMNode* pixnBundle | ||
145 | ); | ||
146 | HRESULT ContainersInitialize( | ||
147 | __in BURN_CONTAINERS* pContainers, | ||
148 | __in BURN_SECTION* pSection | ||
149 | ); | ||
150 | void ContainersUninitialize( | ||
151 | __in BURN_CONTAINERS* pContainers | ||
152 | ); | ||
153 | HRESULT ContainerOpenUX( | ||
154 | __in BURN_SECTION* pSection, | ||
155 | __in BURN_CONTAINER_CONTEXT* pContext | ||
156 | ); | ||
157 | HRESULT ContainerOpen( | ||
158 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
159 | __in BURN_CONTAINER* pContainer, | ||
160 | __in HANDLE hContainerFile, | ||
161 | __in_z LPCWSTR wzFilePath | ||
162 | ); | ||
163 | HRESULT ContainerNextStream( | ||
164 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
165 | __inout_z LPWSTR* psczStreamName | ||
166 | ); | ||
167 | HRESULT ContainerStreamToFile( | ||
168 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
169 | __in_z LPCWSTR wzFileName | ||
170 | ); | ||
171 | HRESULT ContainerStreamToBuffer( | ||
172 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
173 | __out BYTE** ppbBuffer, | ||
174 | __out SIZE_T* pcbBuffer | ||
175 | ); | ||
176 | HRESULT ContainerSkipStream( | ||
177 | __in BURN_CONTAINER_CONTEXT* pContext | ||
178 | ); | ||
179 | HRESULT ContainerClose( | ||
180 | __in BURN_CONTAINER_CONTEXT* pContext | ||
181 | ); | ||
182 | HRESULT 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 | |||
8 | struct BURN_CACHE_THREAD_CONTEXT | ||
9 | { | ||
10 | BURN_ENGINE_STATE* pEngineState; | ||
11 | DWORD* pcOverallProgressTicks; | ||
12 | BOOL* pfRollback; | ||
13 | }; | ||
14 | |||
15 | |||
16 | // internal function declarations | ||
17 | |||
18 | static 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 | ); | ||
38 | static HRESULT ParsePipeConnection( | ||
39 | __in_ecount(3) LPWSTR* rgArgs, | ||
40 | __in BURN_PIPE_CONNECTION* pConnection | ||
41 | ); | ||
42 | static HRESULT DetectPackage( | ||
43 | __in BURN_ENGINE_STATE* pEngineState, | ||
44 | __in BURN_PACKAGE* pPackage | ||
45 | ); | ||
46 | static HRESULT DetectPackagePayloadsCached( | ||
47 | __in BURN_PACKAGE* pPackage | ||
48 | ); | ||
49 | static DWORD WINAPI CacheThreadProc( | ||
50 | __in LPVOID lpThreadParameter | ||
51 | ); | ||
52 | static HRESULT WaitForCacheThread( | ||
53 | __in HANDLE hCacheThread | ||
54 | ); | ||
55 | static 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 | ); | ||
62 | static void LogRelatedBundles( | ||
63 | __in const BURN_RELATED_BUNDLES* pRelatedBundles, | ||
64 | __in BOOL fReverse | ||
65 | ); | ||
66 | |||
67 | |||
68 | // function definitions | ||
69 | |||
70 | extern "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 | |||
168 | LExit: | ||
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 | |||
180 | extern "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 | |||
213 | LExit: | ||
214 | return hr; | ||
215 | } | ||
216 | |||
217 | extern "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 | |||
228 | LExit: | ||
229 | return hr; | ||
230 | } | ||
231 | |||
232 | extern "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 | |||
268 | LExit: | ||
269 | ReleaseBuffer(pbBuffer); | ||
270 | |||
271 | return hr; | ||
272 | } | ||
273 | |||
274 | extern "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 | |||
411 | LExit: | ||
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 | |||
434 | extern "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 | |||
553 | LExit: | ||
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 | |||
569 | extern "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 | |||
598 | LExit: | ||
599 | return hr; | ||
600 | } | ||
601 | |||
602 | extern "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 | |||
755 | LExit: | ||
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 | |||
793 | extern "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 | |||
813 | LExit: | ||
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 | |||
823 | extern "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 | |||
850 | extern "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 | |||
874 | LExit: | ||
875 | ReleaseBuffer(pbBuffer); | ||
876 | |||
877 | return hr; | ||
878 | } | ||
879 | |||
880 | extern "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 | |||
913 | extern "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 | |||
1031 | LExit: | ||
1032 | ReleaseStr(scz); | ||
1033 | |||
1034 | return hr; | ||
1035 | } | ||
1036 | |||
1037 | extern "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 | |||
1059 | LExit: | ||
1060 | ReleaseFileHandle(hExecutableFile); | ||
1061 | |||
1062 | return hr; | ||
1063 | } | ||
1064 | |||
1065 | extern "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 | |||
1094 | LExit: | ||
1095 | ReleaseFileHandle(hExecutableFile); | ||
1096 | |||
1097 | return hr; | ||
1098 | } | ||
1099 | |||
1100 | extern "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 | |||
1149 | LExit: | ||
1150 | LogId(REPORT_STANDARD, MSG_CLEANUP_COMPLETE, hr); | ||
1151 | } | ||
1152 | |||
1153 | // internal helper functions | ||
1154 | |||
1155 | static 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 | |||
1575 | LExit: | ||
1576 | ReleaseStr(sczVariableName); | ||
1577 | ReleaseStr(sczSanitizedArgument); | ||
1578 | ReleaseStr(sczCommandLine); | ||
1579 | |||
1580 | return hr; | ||
1581 | } | ||
1582 | |||
1583 | static 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 | |||
1599 | LExit: | ||
1600 | return hr; | ||
1601 | } | ||
1602 | |||
1603 | static 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 | |||
1643 | LExit: | ||
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 | |||
1657 | static 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 | |||
1702 | LExit: | ||
1703 | ReleaseStr(sczPayloadCachePath); | ||
1704 | ReleaseStr(sczCachePath); | ||
1705 | return hr; | ||
1706 | } | ||
1707 | |||
1708 | static 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 | |||
1727 | LExit: | ||
1728 | UserExperienceExecutePhaseComplete(&pEngineState->userExperience, hr); // signal that cache completed. | ||
1729 | |||
1730 | if (fComInitialized) | ||
1731 | { | ||
1732 | ::CoUninitialize(); | ||
1733 | } | ||
1734 | |||
1735 | return (DWORD)hr; | ||
1736 | } | ||
1737 | |||
1738 | static 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 | |||
1754 | LExit: | ||
1755 | return hr; | ||
1756 | } | ||
1757 | |||
1758 | static 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 | |||
1837 | static 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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // constants | ||
11 | |||
12 | const LPCWSTR BURN_POLICY_REGISTRY_PATH = L"WiX\\Burn"; | ||
13 | |||
14 | const LPCWSTR BURN_COMMANDLINE_SWITCH_PARENT = L"parent"; | ||
15 | const LPCWSTR BURN_COMMANDLINE_SWITCH_PARENT_NONE = L"parent:none"; | ||
16 | const LPCWSTR BURN_COMMANDLINE_SWITCH_CLEAN_ROOM = L"burn.clean.room"; | ||
17 | const LPCWSTR BURN_COMMANDLINE_SWITCH_ELEVATED = L"burn.elevated"; | ||
18 | const LPCWSTR BURN_COMMANDLINE_SWITCH_EMBEDDED = L"burn.embedded"; | ||
19 | const LPCWSTR BURN_COMMANDLINE_SWITCH_RUNONCE = L"burn.runonce"; | ||
20 | const LPCWSTR BURN_COMMANDLINE_SWITCH_LOG_APPEND = L"burn.log.append"; | ||
21 | const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_DETECT = L"burn.related.detect"; | ||
22 | const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE = L"burn.related.upgrade"; | ||
23 | const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_ADDON = L"burn.related.addon"; | ||
24 | const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_PATCH = L"burn.related.patch"; | ||
25 | const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPDATE = L"burn.related.update"; | ||
26 | const LPCWSTR BURN_COMMANDLINE_SWITCH_PASSTHROUGH = L"burn.passthrough"; | ||
27 | const LPCWSTR BURN_COMMANDLINE_SWITCH_DISABLE_UNELEVATE = L"burn.disable.unelevate"; | ||
28 | const LPCWSTR BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES = L"burn.ignoredependencies"; | ||
29 | const LPCWSTR BURN_COMMANDLINE_SWITCH_ANCESTORS = L"burn.ancestors"; | ||
30 | const LPCWSTR BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED = L"burn.filehandle.attached"; | ||
31 | const LPCWSTR BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF = L"burn.filehandle.self"; | ||
32 | const LPCWSTR BURN_COMMANDLINE_SWITCH_PREFIX = L"burn."; | ||
33 | |||
34 | const LPCWSTR BURN_BUNDLE_LAYOUT_DIRECTORY = L"WixBundleLayoutDirectory"; | ||
35 | const LPCWSTR BURN_BUNDLE_ACTION = L"WixBundleAction"; | ||
36 | const LPCWSTR BURN_BUNDLE_ACTIVE_PARENT = L"WixBundleActiveParent"; | ||
37 | const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER = L"WixBundleExecutePackageCacheFolder"; | ||
38 | const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_ACTION = L"WixBundleExecutePackageAction"; | ||
39 | const LPCWSTR BURN_BUNDLE_FORCED_RESTART_PACKAGE = L"WixBundleForcedRestartPackage"; | ||
40 | const LPCWSTR BURN_BUNDLE_INSTALLED = L"WixBundleInstalled"; | ||
41 | const LPCWSTR BURN_BUNDLE_ELEVATED = L"WixBundleElevated"; | ||
42 | const LPCWSTR BURN_BUNDLE_PROVIDER_KEY = L"WixBundleProviderKey"; | ||
43 | const LPCWSTR BURN_BUNDLE_MANUFACTURER = L"WixBundleManufacturer"; | ||
44 | const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_PATH = L"WixBundleSourceProcessPath"; | ||
45 | const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_FOLDER = L"WixBundleSourceProcessFolder"; | ||
46 | const LPCWSTR BURN_BUNDLE_TAG = L"WixBundleTag"; | ||
47 | const LPCWSTR BURN_BUNDLE_UILEVEL = L"WixBundleUILevel"; | ||
48 | const LPCWSTR BURN_BUNDLE_VERSION = L"WixBundleVersion"; | ||
49 | const LPCWSTR BURN_REBOOT_PENDING = L"RebootPending"; | ||
50 | |||
51 | // The following constants must stay in sync with src\wix\Binder.cs | ||
52 | const LPCWSTR BURN_BUNDLE_NAME = L"WixBundleName"; | ||
53 | const LPCWSTR BURN_BUNDLE_ORIGINAL_SOURCE = L"WixBundleOriginalSource"; | ||
54 | const LPCWSTR BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER = L"WixBundleOriginalSourceFolder"; | ||
55 | const LPCWSTR BURN_BUNDLE_LAST_USED_SOURCE = L"WixBundleLastUsedSource"; | ||
56 | |||
57 | |||
58 | // enums | ||
59 | |||
60 | enum 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 | |||
69 | enum 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 | |||
79 | typedef 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 | |||
140 | HRESULT CoreInitialize( | ||
141 | __in BURN_ENGINE_STATE* pEngineState | ||
142 | ); | ||
143 | HRESULT CoreInitializeConstants( | ||
144 | __in BURN_ENGINE_STATE* pEngineState | ||
145 | ); | ||
146 | HRESULT CoreSerializeEngineState( | ||
147 | __in BURN_ENGINE_STATE* pEngineState, | ||
148 | __inout BYTE** ppbBuffer, | ||
149 | __inout SIZE_T* piBuffer | ||
150 | ); | ||
151 | HRESULT 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 | // ); | ||
159 | HRESULT CoreDetect( | ||
160 | __in BURN_ENGINE_STATE* pEngineState, | ||
161 | __in_opt HWND hwndParent | ||
162 | ); | ||
163 | HRESULT CorePlan( | ||
164 | __in BURN_ENGINE_STATE* pEngineState, | ||
165 | __in BOOTSTRAPPER_ACTION action | ||
166 | ); | ||
167 | HRESULT CoreElevate( | ||
168 | __in BURN_ENGINE_STATE* pEngineState, | ||
169 | __in_opt HWND hwndParent | ||
170 | ); | ||
171 | HRESULT CoreApply( | ||
172 | __in BURN_ENGINE_STATE* pEngineState, | ||
173 | __in_opt HWND hwndParent | ||
174 | ); | ||
175 | HRESULT CoreLaunchApprovedExe( | ||
176 | __in BURN_ENGINE_STATE* pEngineState, | ||
177 | __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe | ||
178 | ); | ||
179 | HRESULT CoreQuit( | ||
180 | __in BURN_ENGINE_STATE* pEngineState, | ||
181 | __in int nExitCode | ||
182 | ); | ||
183 | HRESULT CoreSaveEngineState( | ||
184 | __in BURN_ENGINE_STATE* pEngineState | ||
185 | ); | ||
186 | LPCWSTR CoreRelationTypeToCommandLineString( | ||
187 | __in BOOTSTRAPPER_RELATION_TYPE relationType | ||
188 | ); | ||
189 | HRESULT 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 | ); | ||
201 | HRESULT CoreAppendFileHandleAttachedToCommandLine( | ||
202 | __in HANDLE hFileWithAttachedContainer, | ||
203 | __out HANDLE* phExecutableFile, | ||
204 | __deref_inout_z LPWSTR* psczCommandLine | ||
205 | ); | ||
206 | HRESULT CoreAppendFileHandleSelfToCommandLine( | ||
207 | __in LPCWSTR wzExecutablePath, | ||
208 | __out HANDLE* phExecutableFile, | ||
209 | __deref_inout_z LPWSTR* psczCommandLine, | ||
210 | __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine | ||
211 | ); | ||
212 | void 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 | ||
8 | const LPCWSTR vcszIgnoreDependenciesDelim = L";"; | ||
9 | |||
10 | |||
11 | // internal function declarations | ||
12 | |||
13 | static HRESULT DetectPackageDependents( | ||
14 | __in BURN_PACKAGE* pPackage, | ||
15 | __in STRINGDICT_HANDLE sdIgnoredDependents, | ||
16 | __in const BURN_REGISTRATION* pRegistration | ||
17 | ); | ||
18 | |||
19 | static 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 | |||
26 | static HRESULT JoinIgnoreDependencies( | ||
27 | __out_z LPWSTR* psczIgnoreDependencies, | ||
28 | __in_ecount(cDependencies) const DEPENDENCY* rgDependencies, | ||
29 | __in UINT cDependencies | ||
30 | ); | ||
31 | |||
32 | static HRESULT GetIgnoredDependents( | ||
33 | __in const BURN_PACKAGE* pPackage, | ||
34 | __in const BURN_PLAN* pPlan, | ||
35 | __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents | ||
36 | ); | ||
37 | |||
38 | static BOOL GetProviderExists( | ||
39 | __in HKEY hkRoot, | ||
40 | __in_z LPCWSTR wzProviderKey | ||
41 | ); | ||
42 | |||
43 | static 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 | |||
50 | static 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 | |||
58 | static HRESULT RegisterPackageProvider( | ||
59 | __in const BURN_PACKAGE* pPackage | ||
60 | ); | ||
61 | |||
62 | static void UnregisterPackageProvider( | ||
63 | __in const BURN_PACKAGE* pPackage | ||
64 | ); | ||
65 | |||
66 | static HRESULT RegisterPackageDependency( | ||
67 | __in BOOL fPerMachine, | ||
68 | __in const BURN_PACKAGE* pPackage, | ||
69 | __in_z LPCWSTR wzDependentProviderKey | ||
70 | ); | ||
71 | |||
72 | static void UnregisterPackageDependency( | ||
73 | __in BOOL fPerMachine, | ||
74 | __in const BURN_PACKAGE* pPackage, | ||
75 | __in_z LPCWSTR wzDependentProviderKey | ||
76 | ); | ||
77 | |||
78 | |||
79 | // functions | ||
80 | |||
81 | extern "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 | |||
93 | extern "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 | |||
166 | LExit: | ||
167 | ReleaseObject(pixnNode); | ||
168 | ReleaseObject(pixnNodes); | ||
169 | |||
170 | return hr; | ||
171 | } | ||
172 | |||
173 | extern "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 | |||
202 | LExit: | ||
203 | return hr; | ||
204 | } | ||
205 | |||
206 | extern "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 | |||
226 | LExit: | ||
227 | return hr; | ||
228 | } | ||
229 | |||
230 | extern "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 | |||
293 | LExit: | ||
294 | ReleaseDict(sdIgnoredDependents); | ||
295 | |||
296 | return hr; | ||
297 | } | ||
298 | |||
299 | extern "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 | |||
315 | LExit: | ||
316 | return hr; | ||
317 | } | ||
318 | |||
319 | extern "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 | |||
333 | LExit: | ||
334 | return hr; | ||
335 | } | ||
336 | |||
337 | extern "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 | |||
360 | LExit: | ||
361 | return hr; | ||
362 | } | ||
363 | |||
364 | extern "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 | |||
487 | LExit: | ||
488 | ReleaseDict(sdIgnoredDependents); | ||
489 | |||
490 | return hr; | ||
491 | } | ||
492 | |||
493 | extern "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 | |||
550 | LExit: | ||
551 | return hr; | ||
552 | } | ||
553 | |||
554 | extern "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 | |||
577 | LExit: | ||
578 | return hr; | ||
579 | } | ||
580 | |||
581 | extern "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 | |||
601 | LExit: | ||
602 | if (!pPackage->fVital) | ||
603 | { | ||
604 | hr = S_OK; | ||
605 | } | ||
606 | |||
607 | return hr; | ||
608 | } | ||
609 | |||
610 | extern "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 | |||
631 | LExit: | ||
632 | if (!pPackage->fVital) | ||
633 | { | ||
634 | hr = S_OK; | ||
635 | } | ||
636 | |||
637 | return hr; | ||
638 | } | ||
639 | |||
640 | extern "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 | |||
652 | LExit: | ||
653 | return hr; | ||
654 | } | ||
655 | |||
656 | extern "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 | |||
680 | LExit: | ||
681 | return hr; | ||
682 | } | ||
683 | |||
684 | extern "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 | |||
720 | static 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 | |||
808 | LExit: | ||
809 | return hr; | ||
810 | } | ||
811 | |||
812 | /******************************************************************** | ||
813 | SplitIgnoreDependencies - Splits a semicolon-delimited | ||
814 | string into a list of unique dependencies to ignore. | ||
815 | |||
816 | *********************************************************************/ | ||
817 | static 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 | |||
856 | LExit: | ||
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 | *********************************************************************/ | ||
867 | static 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 | |||
911 | LExit: | ||
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 | *********************************************************************/ | ||
924 | static 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 | |||
963 | LExit: | ||
964 | ReleaseStr(sczIgnoreDependencies); | ||
965 | |||
966 | return hr; | ||
967 | } | ||
968 | |||
969 | /******************************************************************** | ||
970 | GetProviderExists - Gets whether the provider key is registered. | ||
971 | |||
972 | *********************************************************************/ | ||
973 | static 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 | *********************************************************************/ | ||
987 | static 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 | *********************************************************************/ | ||
1089 | static 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 | |||
1146 | LExit: | ||
1147 | return hr; | ||
1148 | } | ||
1149 | |||
1150 | static 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 | |||
1183 | LExit: | ||
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 | *********************************************************************/ | ||
1198 | static 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 | *********************************************************************/ | ||
1232 | static 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 | |||
1269 | LExit: | ||
1270 | return hr; | ||
1271 | } | ||
1272 | |||
1273 | /******************************************************************** | ||
1274 | UnregisterPackageDependency - Unregisters the provider key | ||
1275 | as a dependent of a package. | ||
1276 | |||
1277 | *********************************************************************/ | ||
1278 | static 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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | // constants | ||
10 | |||
11 | const 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 | *********************************************************************/ | ||
21 | void 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 | *********************************************************************/ | ||
30 | HRESULT DependencyParseProvidersFromXml( | ||
31 | __in BURN_PACKAGE* pPackage, | ||
32 | __in IXMLDOMNode* pixnPackage | ||
33 | ); | ||
34 | |||
35 | HRESULT 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 | *********************************************************************/ | ||
46 | HRESULT DependencyDetectProviderKeyBundleId( | ||
47 | __in BURN_REGISTRATION* pRegistration | ||
48 | ); | ||
49 | |||
50 | /******************************************************************** | ||
51 | DependencyDetect - Detects dependency information. | ||
52 | |||
53 | *********************************************************************/ | ||
54 | HRESULT DependencyDetect( | ||
55 | __in BURN_ENGINE_STATE* pEngineState | ||
56 | ); | ||
57 | |||
58 | /******************************************************************** | ||
59 | DependencyPlanInitialize - Initializes the plan. | ||
60 | |||
61 | *********************************************************************/ | ||
62 | HRESULT 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 | *********************************************************************/ | ||
72 | HRESULT 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 | *********************************************************************/ | ||
82 | HRESULT 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 | *********************************************************************/ | ||
92 | HRESULT 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 | *********************************************************************/ | ||
103 | HRESULT 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 | *********************************************************************/ | ||
114 | HRESULT 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 | *********************************************************************/ | ||
124 | HRESULT 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 | *********************************************************************/ | ||
133 | HRESULT DependencyExecutePackageDependencyAction( | ||
134 | __in BOOL fPerMachine, | ||
135 | __in const BURN_EXECUTE_ACTION* pAction | ||
136 | ); | ||
137 | |||
138 | /******************************************************************** | ||
139 | DependencyRegisterBundle - Registers the bundle dependency provider. | ||
140 | |||
141 | *********************************************************************/ | ||
142 | HRESULT 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 | *********************************************************************/ | ||
151 | HRESULT 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 | *********************************************************************/ | ||
161 | void 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 | |||
5 | typedef struct _DETECT_AUTHENTICATION_REQUIRED_DATA | ||
6 | { | ||
7 | BURN_USER_EXPERIENCE* pUX; | ||
8 | LPCWSTR wzPackageOrContainerId; | ||
9 | } DETECT_AUTHENTICATION_REQUIRED_DATA; | ||
10 | |||
11 | // internal function definitions | ||
12 | static 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 | |||
20 | static HRESULT DetectAtomFeedUpdate( | ||
21 | __in_z LPCWSTR wzBundleId, | ||
22 | __in BURN_USER_EXPERIENCE* pUX, | ||
23 | __in BURN_UPDATE* pUpdate | ||
24 | ); | ||
25 | |||
26 | static 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 | |||
35 | extern "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 | |||
118 | extern "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 | |||
156 | LExit: | ||
157 | return hr; | ||
158 | } | ||
159 | |||
160 | extern "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 | |||
242 | LExit: | ||
243 | return hr; | ||
244 | } | ||
245 | |||
246 | extern "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 | |||
278 | LExit: | ||
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 | |||
293 | static 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 | |||
345 | LExit: | ||
346 | ReleaseStr(sczError); | ||
347 | |||
348 | return hr; | ||
349 | } | ||
350 | |||
351 | static 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 | |||
388 | LExit: | ||
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 | |||
408 | static 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 | |||
457 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // constants | ||
11 | |||
12 | |||
13 | // structs | ||
14 | |||
15 | |||
16 | // functions | ||
17 | |||
18 | void DetectReset( | ||
19 | __in BURN_REGISTRATION* pRegistration, | ||
20 | __in BURN_PACKAGES* pPackages | ||
21 | ); | ||
22 | |||
23 | HRESULT DetectForwardCompatibleBundles( | ||
24 | __in BURN_USER_EXPERIENCE* pUX, | ||
25 | __in BURN_REGISTRATION* pRegistration | ||
26 | ); | ||
27 | |||
28 | HRESULT 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 | |||
36 | HRESULT 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 | |||
6 | const DWORD BURN_TIMEOUT = 5 * 60 * 1000; // TODO: is 5 minutes good? | ||
7 | |||
8 | typedef 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 | |||
52 | typedef 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 | |||
59 | typedef 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 | |||
66 | typedef struct _BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT | ||
67 | { | ||
68 | PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler; | ||
69 | LPVOID pvContext; | ||
70 | } BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT; | ||
71 | |||
72 | typedef struct _BURN_ELEVATION_MSI_MESSAGE_CONTEXT | ||
73 | { | ||
74 | PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler; | ||
75 | LPVOID pvContext; | ||
76 | } BURN_ELEVATION_MSI_MESSAGE_CONTEXT; | ||
77 | |||
78 | typedef struct _BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT | ||
79 | { | ||
80 | DWORD dwProcessId; | ||
81 | } BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT; | ||
82 | |||
83 | typedef 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 | |||
101 | static DWORD WINAPI ElevatedChildCacheThreadProc( | ||
102 | __in LPVOID lpThreadParameter | ||
103 | ); | ||
104 | static HRESULT WaitForElevatedChildCacheThread( | ||
105 | __in HANDLE hCacheThread, | ||
106 | __in DWORD dwExpectedExitCode | ||
107 | ); | ||
108 | static HRESULT ProcessApplyInitializeMessages( | ||
109 | __in BURN_PIPE_MESSAGE* pMsg, | ||
110 | __in_opt LPVOID pvContext, | ||
111 | __out DWORD* pdwResult | ||
112 | ); | ||
113 | static HRESULT ProcessBurnCacheMessages( | ||
114 | __in BURN_PIPE_MESSAGE* pMsg, | ||
115 | __in LPVOID pvContext, | ||
116 | __out DWORD* pdwResult | ||
117 | ); | ||
118 | static HRESULT ProcessGenericExecuteMessages( | ||
119 | __in BURN_PIPE_MESSAGE* pMsg, | ||
120 | __in LPVOID pvContext, | ||
121 | __out DWORD* pdwResult | ||
122 | ); | ||
123 | static HRESULT ProcessMsiPackageMessages( | ||
124 | __in BURN_PIPE_MESSAGE* pMsg, | ||
125 | __in_opt LPVOID pvContext, | ||
126 | __out DWORD* pdwResult | ||
127 | ); | ||
128 | static HRESULT ProcessLaunchApprovedExeMessages( | ||
129 | __in BURN_PIPE_MESSAGE* pMsg, | ||
130 | __in_opt LPVOID pvContext, | ||
131 | __out DWORD* pdwResult | ||
132 | ); | ||
133 | static HRESULT ProcessProgressRoutineMessage( | ||
134 | __in BURN_PIPE_MESSAGE* pMsg, | ||
135 | __in LPPROGRESS_ROUTINE pfnProgress, | ||
136 | __in LPVOID pvContext, | ||
137 | __out DWORD* pdwResult | ||
138 | ); | ||
139 | static HRESULT ProcessElevatedChildMessage( | ||
140 | __in BURN_PIPE_MESSAGE* pMsg, | ||
141 | __in_opt LPVOID pvContext, | ||
142 | __out DWORD* pdwResult | ||
143 | ); | ||
144 | static HRESULT ProcessElevatedChildCacheMessage( | ||
145 | __in BURN_PIPE_MESSAGE* pMsg, | ||
146 | __in_opt LPVOID pvContext, | ||
147 | __out DWORD* pdwResult | ||
148 | ); | ||
149 | static HRESULT ProcessResult( | ||
150 | __in DWORD dwResult, | ||
151 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
152 | ); | ||
153 | static 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 | ); | ||
162 | static HRESULT OnApplyUninitialize( | ||
163 | __in HANDLE* phLock | ||
164 | ); | ||
165 | static HRESULT OnSessionBegin( | ||
166 | __in BURN_REGISTRATION* pRegistration, | ||
167 | __in BURN_VARIABLES* pVariables, | ||
168 | __in BYTE* pbData, | ||
169 | __in SIZE_T cbData | ||
170 | ); | ||
171 | static HRESULT OnSessionResume( | ||
172 | __in BURN_REGISTRATION* pRegistration, | ||
173 | __in BURN_VARIABLES* pVariables, | ||
174 | __in BYTE* pbData, | ||
175 | __in SIZE_T cbData | ||
176 | ); | ||
177 | static 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 | ); | ||
184 | static HRESULT OnSaveState( | ||
185 | __in BURN_REGISTRATION* pRegistration, | ||
186 | __in BYTE* pbData, | ||
187 | __in SIZE_T cbData | ||
188 | ); | ||
189 | static 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 | ); | ||
196 | static 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 | ); | ||
203 | static void OnCacheCleanup( | ||
204 | __in_z LPCWSTR wzBundleId | ||
205 | ); | ||
206 | static HRESULT OnProcessDependentRegistration( | ||
207 | __in const BURN_REGISTRATION* pRegistration, | ||
208 | __in BYTE* pbData, | ||
209 | __in SIZE_T cbData | ||
210 | ); | ||
211 | static 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 | ); | ||
219 | static 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 | ); | ||
226 | static 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 | ); | ||
233 | static 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 | ); | ||
240 | static HRESULT OnExecutePackageProviderAction( | ||
241 | __in BURN_PACKAGES* pPackages, | ||
242 | __in BURN_RELATED_BUNDLES* pRelatedBundles, | ||
243 | __in BYTE* pbData, | ||
244 | __in SIZE_T cbData | ||
245 | ); | ||
246 | static HRESULT OnExecutePackageDependencyAction( | ||
247 | __in BURN_PACKAGES* pPackages, | ||
248 | __in BURN_RELATED_BUNDLES* pRelatedBundles, | ||
249 | __in BYTE* pbData, | ||
250 | __in SIZE_T cbData | ||
251 | ); | ||
252 | static HRESULT CALLBACK BurnCacheMessageHandler( | ||
253 | __in BURN_CACHE_MESSAGE* pMessage, | ||
254 | __in LPVOID pvContext | ||
255 | ); | ||
256 | static 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 | ); | ||
267 | static int GenericExecuteMessageHandler( | ||
268 | __in GENERIC_EXECUTE_MESSAGE* pMessage, | ||
269 | __in LPVOID pvContext | ||
270 | ); | ||
271 | static int MsiExecuteMessageHandler( | ||
272 | __in WIU_MSI_EXECUTE_MESSAGE* pMessage, | ||
273 | __in_opt LPVOID pvContext | ||
274 | ); | ||
275 | static HRESULT OnCleanPackage( | ||
276 | __in BURN_PACKAGES* pPackages, | ||
277 | __in BYTE* pbData, | ||
278 | __in SIZE_T cbData | ||
279 | ); | ||
280 | static 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 | ); | ||
287 | static HRESULT OnMsiBeginTransaction( | ||
288 | __in BURN_PACKAGES* pPackages, | ||
289 | __in BYTE* pbData, | ||
290 | __in SIZE_T cbData | ||
291 | ); | ||
292 | static HRESULT OnMsiCommitTransaction( | ||
293 | __in BURN_PACKAGES* pPackages, | ||
294 | __in BYTE* pbData, | ||
295 | __in SIZE_T cbData | ||
296 | ); | ||
297 | static HRESULT OnMsiRollbackTransaction( | ||
298 | __in BURN_PACKAGES* pPackages, | ||
299 | __in BYTE* pbData, | ||
300 | __in SIZE_T cbData | ||
301 | ); | ||
302 | static HRESULT ElevatedOnPauseAUBegin( | ||
303 | __in HANDLE hPipe | ||
304 | ); | ||
305 | static HRESULT ElevatedOnPauseAUComplete( | ||
306 | __in HANDLE hPipe, | ||
307 | __in HRESULT hrStatus | ||
308 | ); | ||
309 | static HRESULT ElevatedOnSystemRestorePointBegin( | ||
310 | __in HANDLE hPipe | ||
311 | ); | ||
312 | static HRESULT ElevatedOnSystemRestorePointComplete( | ||
313 | __in HANDLE hPipe, | ||
314 | __in HRESULT hrStatus | ||
315 | ); | ||
316 | |||
317 | |||
318 | // function definitions | ||
319 | |||
320 | extern "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 | |||
376 | LExit: | ||
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 | |||
389 | extern "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 | |||
435 | LExit: | ||
436 | ReleaseBuffer(pbData); | ||
437 | |||
438 | return hr; | ||
439 | } | ||
440 | |||
441 | extern "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 | |||
456 | LExit: | ||
457 | ReleaseBuffer(pbData); | ||
458 | |||
459 | return hr; | ||
460 | } | ||
461 | |||
462 | /******************************************************************* | ||
463 | ElevationSessionBegin - | ||
464 | |||
465 | *******************************************************************/ | ||
466 | extern "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 | |||
510 | LExit: | ||
511 | ReleaseBuffer(pbData); | ||
512 | |||
513 | return hr; | ||
514 | } | ||
515 | |||
516 | /******************************************************************* | ||
517 | ElevationSessionResume - | ||
518 | |||
519 | *******************************************************************/ | ||
520 | extern "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 | |||
548 | LExit: | ||
549 | ReleaseBuffer(pbData); | ||
550 | |||
551 | return hr; | ||
552 | } | ||
553 | |||
554 | /******************************************************************* | ||
555 | ElevationSessionEnd - | ||
556 | |||
557 | *******************************************************************/ | ||
558 | extern "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 | |||
586 | LExit: | ||
587 | ReleaseBuffer(pbData); | ||
588 | |||
589 | return hr; | ||
590 | } | ||
591 | |||
592 | /******************************************************************* | ||
593 | ElevationSaveState - | ||
594 | |||
595 | *******************************************************************/ | ||
596 | HRESULT 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 | |||
611 | LExit: | ||
612 | return hr; | ||
613 | } | ||
614 | |||
615 | /******************************************************************* | ||
616 | ElevationCacheCompletePayload - | ||
617 | |||
618 | *******************************************************************/ | ||
619 | extern "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 | |||
659 | LExit: | ||
660 | ReleaseBuffer(pbData); | ||
661 | |||
662 | return hr; | ||
663 | } | ||
664 | |||
665 | extern "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 | |||
697 | LExit: | ||
698 | ReleaseBuffer(pbData); | ||
699 | |||
700 | return hr; | ||
701 | } | ||
702 | |||
703 | /******************************************************************* | ||
704 | ElevationCacheCleanup - | ||
705 | |||
706 | *******************************************************************/ | ||
707 | extern "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 | |||
720 | LExit: | ||
721 | return hr; | ||
722 | } | ||
723 | |||
724 | extern "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 | |||
750 | LExit: | ||
751 | ReleaseBuffer(pbData); | ||
752 | |||
753 | return hr; | ||
754 | } | ||
755 | |||
756 | /******************************************************************* | ||
757 | ElevationExecuteExePackage - | ||
758 | |||
759 | *******************************************************************/ | ||
760 | extern "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 | |||
804 | LExit: | ||
805 | ReleaseBuffer(pbData); | ||
806 | |||
807 | return hr; | ||
808 | } | ||
809 | |||
810 | extern "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 | |||
832 | LExit: | ||
833 | ReleaseBuffer(pbData); | ||
834 | |||
835 | return hr; | ||
836 | } | ||
837 | |||
838 | extern "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 | |||
860 | LExit: | ||
861 | ReleaseBuffer(pbData); | ||
862 | |||
863 | return hr; | ||
864 | } | ||
865 | |||
866 | extern "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 | |||
888 | LExit: | ||
889 | ReleaseBuffer(pbData); | ||
890 | |||
891 | return hr; | ||
892 | } | ||
893 | |||
894 | |||
895 | |||
896 | /******************************************************************* | ||
897 | ElevationExecuteMsiPackage - | ||
898 | |||
899 | *******************************************************************/ | ||
900 | extern "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 | |||
971 | LExit: | ||
972 | ReleaseBuffer(pbData); | ||
973 | |||
974 | return hr; | ||
975 | } | ||
976 | |||
977 | /******************************************************************* | ||
978 | ElevationExecuteMspPackage - | ||
979 | |||
980 | *******************************************************************/ | ||
981 | extern "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 | |||
1047 | LExit: | ||
1048 | ReleaseBuffer(pbData); | ||
1049 | |||
1050 | return hr; | ||
1051 | } | ||
1052 | |||
1053 | /******************************************************************* | ||
1054 | ElevationExecuteMsuPackage - | ||
1055 | |||
1056 | *******************************************************************/ | ||
1057 | extern "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 | |||
1098 | LExit: | ||
1099 | ReleaseBuffer(pbData); | ||
1100 | |||
1101 | return hr; | ||
1102 | } | ||
1103 | |||
1104 | extern "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 | |||
1129 | LExit: | ||
1130 | ReleaseBuffer(pbData); | ||
1131 | |||
1132 | return hr; | ||
1133 | } | ||
1134 | |||
1135 | extern "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 | |||
1163 | LExit: | ||
1164 | ReleaseBuffer(pbData); | ||
1165 | |||
1166 | return hr; | ||
1167 | } | ||
1168 | |||
1169 | /******************************************************************* | ||
1170 | ElevationCleanPackage - | ||
1171 | |||
1172 | *******************************************************************/ | ||
1173 | extern "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 | |||
1193 | LExit: | ||
1194 | ReleaseBuffer(pbData); | ||
1195 | |||
1196 | return hr; | ||
1197 | } | ||
1198 | |||
1199 | extern "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 | |||
1228 | LExit: | ||
1229 | ReleaseBuffer(pbData); | ||
1230 | |||
1231 | return hr; | ||
1232 | } | ||
1233 | |||
1234 | /******************************************************************* | ||
1235 | ElevationChildPumpMessages - | ||
1236 | |||
1237 | *******************************************************************/ | ||
1238 | extern "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 | |||
1295 | LExit: | ||
1296 | ReleaseHandle(hCacheThread); | ||
1297 | |||
1298 | return hr; | ||
1299 | } | ||
1300 | |||
1301 | extern "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 | |||
1312 | LExit: | ||
1313 | return hr; | ||
1314 | } | ||
1315 | |||
1316 | // internal function definitions | ||
1317 | |||
1318 | static 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 | |||
1342 | LExit: | ||
1343 | if (fComInitialized) | ||
1344 | { | ||
1345 | ::CoUninitialize(); | ||
1346 | } | ||
1347 | |||
1348 | return (DWORD)hr; | ||
1349 | } | ||
1350 | |||
1351 | static 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 | |||
1373 | LExit: | ||
1374 | return hr; | ||
1375 | } | ||
1376 | |||
1377 | static 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 | |||
1435 | LExit: | ||
1436 | return hr; | ||
1437 | } | ||
1438 | |||
1439 | static 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 | |||
1498 | LExit: | ||
1499 | return hr; | ||
1500 | } | ||
1501 | |||
1502 | static 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 | |||
1572 | LExit: | ||
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 | |||
1586 | static 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 | |||
1672 | LExit: | ||
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 | |||
1688 | static 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 | |||
1717 | LExit: | ||
1718 | return hr; | ||
1719 | } | ||
1720 | |||
1721 | static 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 | |||
1747 | LExit: | ||
1748 | return hr; | ||
1749 | } | ||
1750 | |||
1751 | static 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 | |||
1843 | LExit: | ||
1844 | return hr; | ||
1845 | } | ||
1846 | |||
1847 | static 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 | |||
1883 | LExit: | ||
1884 | return hr; | ||
1885 | } | ||
1886 | |||
1887 | static 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 | |||
1907 | static 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 | |||
2011 | LExit: | ||
2012 | ReleaseStr(sczBundleName); | ||
2013 | return hr; | ||
2014 | } | ||
2015 | |||
2016 | static 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 | |||
2034 | static 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 | |||
2074 | LExit: | ||
2075 | ReleaseStr(sczEngineWorkingPath); | ||
2076 | |||
2077 | return hr; | ||
2078 | } | ||
2079 | |||
2080 | static 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 | |||
2104 | LExit: | ||
2105 | return hr; | ||
2106 | } | ||
2107 | |||
2108 | static 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 | |||
2136 | LExit: | ||
2137 | return hr; | ||
2138 | } | ||
2139 | |||
2140 | static 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 | |||
2152 | LExit: | ||
2153 | return hr; | ||
2154 | } | ||
2155 | |||
2156 | static 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 | |||
2208 | LExit: | ||
2209 | ReleaseStr(sczUnverifiedPath); | ||
2210 | ReleaseStr(scz); | ||
2211 | |||
2212 | return hr; | ||
2213 | } | ||
2214 | |||
2215 | static 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 | |||
2263 | LExit: | ||
2264 | ReleaseStr(sczCacheDirectory); | ||
2265 | ReleaseStr(scz); | ||
2266 | |||
2267 | return hr; | ||
2268 | } | ||
2269 | |||
2270 | static void OnCacheCleanup( | ||
2271 | __in_z LPCWSTR wzBundleId | ||
2272 | ) | ||
2273 | { | ||
2274 | CacheCleanup(TRUE, wzBundleId); | ||
2275 | } | ||
2276 | |||
2277 | static 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 | |||
2301 | LExit: | ||
2302 | // TODO: do the right thing here. | ||
2303 | //DependencyUninitializeRegistrationAction(&action); | ||
2304 | ReleaseStr(action.sczDependentProviderKey); | ||
2305 | ReleaseStr(action.sczBundleId) | ||
2306 | |||
2307 | return hr; | ||
2308 | } | ||
2309 | |||
2310 | static 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 | |||
2374 | LExit: | ||
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 | |||
2395 | static 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 | |||
2473 | LExit: | ||
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 | |||
2492 | static 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 | |||
2568 | LExit: | ||
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 | |||
2587 | static 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 | |||
2628 | LExit: | ||
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 | |||
2647 | static 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 | |||
2680 | LExit: | ||
2681 | ReleaseStr(sczPackage); | ||
2682 | PlanUninitializeExecuteAction(&executeAction); | ||
2683 | |||
2684 | return hr; | ||
2685 | } | ||
2686 | |||
2687 | static 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 | |||
2723 | LExit: | ||
2724 | ReleaseStr(sczPackage); | ||
2725 | PlanUninitializeExecuteAction(&executeAction); | ||
2726 | |||
2727 | return hr; | ||
2728 | } | ||
2729 | |||
2730 | static 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 | |||
2774 | LExit: | ||
2775 | ReleaseBuffer(pbData); | ||
2776 | |||
2777 | return hr; | ||
2778 | } | ||
2779 | |||
2780 | static 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 | |||
2809 | LExit: | ||
2810 | ReleaseBuffer(pbData); | ||
2811 | |||
2812 | return dwResult; | ||
2813 | } | ||
2814 | |||
2815 | static 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 | |||
2869 | LExit: | ||
2870 | ReleaseBuffer(pbData); | ||
2871 | |||
2872 | return nResult; | ||
2873 | } | ||
2874 | |||
2875 | static 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 | |||
2951 | LExit: | ||
2952 | ReleaseBuffer(pbData); | ||
2953 | |||
2954 | return nResult; | ||
2955 | } | ||
2956 | |||
2957 | static 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 | |||
2979 | LExit: | ||
2980 | ReleaseStr(sczPackage); | ||
2981 | return hr; | ||
2982 | } | ||
2983 | |||
2984 | static 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 | |||
3049 | LExit: | ||
3050 | ReleaseBuffer(pbSendData); | ||
3051 | ApprovedExesUninitializeLaunch(pLaunchApprovedExe); | ||
3052 | return hr; | ||
3053 | } | ||
3054 | |||
3055 | static 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 | |||
3081 | LExit: | ||
3082 | ReleaseStr(sczId); | ||
3083 | ReleaseStr(sczLogPath); | ||
3084 | |||
3085 | if (pRollbackBoundary) | ||
3086 | { | ||
3087 | pRollbackBoundary->sczLogPath = NULL; | ||
3088 | } | ||
3089 | |||
3090 | return hr; | ||
3091 | } | ||
3092 | |||
3093 | static 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 | |||
3119 | LExit: | ||
3120 | ReleaseStr(sczId); | ||
3121 | ReleaseStr(sczLogPath); | ||
3122 | |||
3123 | if (pRollbackBoundary) | ||
3124 | { | ||
3125 | pRollbackBoundary->sczLogPath = NULL; | ||
3126 | } | ||
3127 | |||
3128 | return hr; | ||
3129 | } | ||
3130 | |||
3131 | static 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 | |||
3157 | LExit: | ||
3158 | ReleaseStr(sczId); | ||
3159 | ReleaseStr(sczLogPath); | ||
3160 | |||
3161 | if (pRollbackBoundary) | ||
3162 | { | ||
3163 | pRollbackBoundary->sczLogPath = NULL; | ||
3164 | } | ||
3165 | |||
3166 | return hr; | ||
3167 | } | ||
3168 | |||
3169 | static 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 | |||
3179 | LExit: | ||
3180 | return hr; | ||
3181 | } | ||
3182 | |||
3183 | static 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 | |||
3199 | LExit: | ||
3200 | ReleaseBuffer(pbSendData); | ||
3201 | |||
3202 | return hr; | ||
3203 | } | ||
3204 | |||
3205 | static 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 | |||
3215 | LExit: | ||
3216 | return hr; | ||
3217 | } | ||
3218 | |||
3219 | static 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 | |||
3235 | LExit: | ||
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 | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // Parent (per-user process) side functions. | ||
11 | HRESULT ElevationElevate( | ||
12 | __in BURN_ENGINE_STATE* pEngineState, | ||
13 | __in_opt HWND hwndParent | ||
14 | ); | ||
15 | HRESULT 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 | ); | ||
23 | HRESULT ElevationApplyUninitialize( | ||
24 | __in HANDLE hPipe | ||
25 | ); | ||
26 | HRESULT 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 | ); | ||
36 | HRESULT ElevationSessionResume( | ||
37 | __in HANDLE hPipe, | ||
38 | __in_z LPCWSTR wzResumeCommandLine, | ||
39 | __in BOOL fDisableResume, | ||
40 | __in BURN_VARIABLES* pVariables | ||
41 | ); | ||
42 | HRESULT 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 | ); | ||
48 | HRESULT ElevationSaveState( | ||
49 | __in HANDLE hPipe, | ||
50 | __in_bcount(cbBuffer) BYTE* pbBuffer, | ||
51 | __in SIZE_T cbBuffer | ||
52 | ); | ||
53 | HRESULT 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 | ); | ||
63 | HRESULT 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 | ); | ||
71 | HRESULT ElevationCacheCleanup( | ||
72 | __in HANDLE hPipe | ||
73 | ); | ||
74 | HRESULT ElevationProcessDependentRegistration( | ||
75 | __in HANDLE hPipe, | ||
76 | __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction | ||
77 | ); | ||
78 | HRESULT 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 | ); | ||
87 | HRESULT 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 | ); | ||
97 | HRESULT 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 | ); | ||
107 | HRESULT 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 | ); | ||
116 | HRESULT ElevationExecutePackageProviderAction( | ||
117 | __in HANDLE hPipe, | ||
118 | __in BURN_EXECUTE_ACTION* pExecuteAction | ||
119 | ); | ||
120 | HRESULT ElevationExecutePackageDependencyAction( | ||
121 | __in HANDLE hPipe, | ||
122 | __in BURN_EXECUTE_ACTION* pExecuteAction | ||
123 | ); | ||
124 | HRESULT ElevationLaunchElevatedChild( | ||
125 | __in HANDLE hPipe, | ||
126 | __in BURN_PACKAGE* pPackage, | ||
127 | __in LPCWSTR wzPipeName, | ||
128 | __in LPCWSTR wzPipeToken, | ||
129 | __out DWORD* pdwChildPid | ||
130 | ); | ||
131 | HRESULT ElevationCleanPackage( | ||
132 | __in HANDLE hPipe, | ||
133 | __in BURN_PACKAGE* pPackage | ||
134 | ); | ||
135 | HRESULT 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. | ||
142 | HRESULT 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 | ); | ||
158 | HRESULT ElevationChildResumeAutomaticUpdates(); | ||
159 | |||
160 | |||
161 | HRESULT ElevationMsiBeginTransaction( | ||
162 | __in HANDLE hPipe, | ||
163 | __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary | ||
164 | ); | ||
165 | HRESULT ElevationMsiCommitTransaction( | ||
166 | __in HANDLE hPipe, | ||
167 | __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary | ||
168 | ); | ||
169 | HRESULT 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 | |||
8 | struct BURN_EMBEDDED_CALLBACK_CONTEXT | ||
9 | { | ||
10 | PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler; | ||
11 | LPVOID pvContext; | ||
12 | }; | ||
13 | |||
14 | // internal function declarations | ||
15 | |||
16 | static HRESULT ProcessEmbeddedMessages( | ||
17 | __in BURN_PIPE_MESSAGE* pMsg, | ||
18 | __in_opt LPVOID pvContext, | ||
19 | __out DWORD* pdwResult | ||
20 | ); | ||
21 | static 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 | ); | ||
28 | static 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 | *******************************************************************/ | ||
42 | extern "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 | |||
93 | LExit: | ||
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 | |||
107 | static 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 | |||
137 | LExit: | ||
138 | return hr; | ||
139 | } | ||
140 | |||
141 | static 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 | |||
169 | LExit: | ||
170 | ReleaseStr(sczMessage); | ||
171 | |||
172 | return hr; | ||
173 | } | ||
174 | |||
175 | static 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 | |||
195 | LExit: | ||
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 | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | typedef 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 | |||
17 | HRESULT 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 | |||
8 | const DWORD RESTART_RETRIES = 10; | ||
9 | |||
10 | // internal function declarations | ||
11 | |||
12 | static HRESULT InitializeEngineState( | ||
13 | __in BURN_ENGINE_STATE* pEngineState, | ||
14 | __in HANDLE hEngineFile | ||
15 | ); | ||
16 | static void UninitializeEngineState( | ||
17 | __in BURN_ENGINE_STATE* pEngineState | ||
18 | ); | ||
19 | static HRESULT RunUntrusted( | ||
20 | __in LPCWSTR wzCommandLine, | ||
21 | __in BURN_ENGINE_STATE* pEngineState | ||
22 | ); | ||
23 | static HRESULT RunNormal( | ||
24 | __in HINSTANCE hInstance, | ||
25 | __in BURN_ENGINE_STATE* pEngineState | ||
26 | ); | ||
27 | static HRESULT RunElevated( | ||
28 | __in HINSTANCE hInstance, | ||
29 | __in LPCWSTR wzCommandLine, | ||
30 | __in BURN_ENGINE_STATE* pEngineState | ||
31 | ); | ||
32 | static HRESULT RunEmbedded( | ||
33 | __in HINSTANCE hInstance, | ||
34 | __in BURN_ENGINE_STATE* pEngineState | ||
35 | ); | ||
36 | static HRESULT RunRunOnce( | ||
37 | __in const BURN_REGISTRATION* pRegistration, | ||
38 | __in int nCmdShow | ||
39 | ); | ||
40 | static HRESULT RunApplication( | ||
41 | __in BURN_ENGINE_STATE* pEngineState, | ||
42 | __out BOOL* pfReloadApp, | ||
43 | __out BOOL* pfSkipCleanup | ||
44 | ); | ||
45 | static HRESULT ProcessMessage( | ||
46 | __in BURN_ENGINE_STATE* pEngineState, | ||
47 | __in const MSG* pmsg | ||
48 | ); | ||
49 | static HRESULT DAPI RedirectLoggingOverPipe( | ||
50 | __in_z LPCSTR szString, | ||
51 | __in_opt LPVOID pvContext | ||
52 | ); | ||
53 | static HRESULT Restart(); | ||
54 | |||
55 | |||
56 | // function definitions | ||
57 | |||
58 | extern "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 | |||
81 | extern "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 | |||
231 | LExit: | ||
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 | |||
318 | static 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 | |||
371 | LExit: | ||
372 | return hr; | ||
373 | } | ||
374 | |||
375 | static 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 | |||
426 | static 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 | |||
512 | LExit: | ||
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 | |||
525 | static 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 | |||
618 | LExit: | ||
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 | |||
649 | static 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 | |||
689 | LExit: | ||
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 | |||
709 | static 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 | |||
733 | LExit: | ||
734 | return hr; | ||
735 | } | ||
736 | |||
737 | static 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 | |||
757 | LExit: | ||
758 | ReleaseHandle(hProcess); | ||
759 | ReleaseStr(sczNewCommandLine); | ||
760 | ReleaseStr(sczBurnPath); | ||
761 | |||
762 | return hr; | ||
763 | } | ||
764 | |||
765 | static 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 | |||
815 | LExit: | ||
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 | |||
842 | static 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 | |||
884 | LExit: | ||
885 | UserExperienceDeactivateEngine(&pEngineState->userExperience); | ||
886 | |||
887 | return hr; | ||
888 | } | ||
889 | |||
890 | static 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 | |||
934 | LExit: | ||
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 | |||
946 | static 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 | |||
989 | LExit: | ||
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 | |||
4 | MessageIdTypedef=DWORD | ||
5 | |||
6 | LanguageNames=(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 | |||
32 | MessageId=1 | ||
33 | Severity=Success | ||
34 | SymbolicName=MSG_BURN_INFO | ||
35 | Language=English | ||
36 | Burn %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 | |||
39 | MessageId=2 | ||
40 | Severity=Warning | ||
41 | SymbolicName=MSG_BURN_UNKNOWN_PRIVATE_SWITCH | ||
42 | Language=English | ||
43 | Unknown burn internal command-line switch encountered: '%1!ls!'. | ||
44 | . | ||
45 | |||
46 | MessageId=3 | ||
47 | Severity=Success | ||
48 | SymbolicName=MSG_BURN_RUN_BY_RELATED_BUNDLE | ||
49 | Language=English | ||
50 | This bundle is being run by a related bundle as type '%1!hs!'. | ||
51 | . | ||
52 | |||
53 | MessageId=4 | ||
54 | Severity=Success | ||
55 | SymbolicName=MSG_BA_REQUESTED_RESTART | ||
56 | Language=English | ||
57 | Bootstrapper application requested restart at shutdown. Planned to restart already: %1!hs!. | ||
58 | . | ||
59 | |||
60 | MessageId=5 | ||
61 | Severity=Warning | ||
62 | SymbolicName=MSG_RESTARTING | ||
63 | Language=English | ||
64 | Restarting computer... | ||
65 | ======================================= | ||
66 | . | ||
67 | |||
68 | MessageId=6 | ||
69 | Severity=Success | ||
70 | SymbolicName=MSG_BA_REQUESTED_RELOAD | ||
71 | Language=English | ||
72 | Bootstrapper application requested to be reloaded. | ||
73 | . | ||
74 | |||
75 | MessageId=7 | ||
76 | Severity=Success | ||
77 | SymbolicName=MSG_EXITING | ||
78 | Language=English | ||
79 | Exit code: 0x%1!x!, restarting: %2!hs! | ||
80 | . | ||
81 | |||
82 | MessageId=8 | ||
83 | Severity=Warning | ||
84 | SymbolicName=MSG_RESTART_ABORTED | ||
85 | Language=English | ||
86 | Preventing requested restart because bundle is related: '%1!hs!'. Returning restart requested to parent bundle. | ||
87 | . | ||
88 | |||
89 | MessageId=9 | ||
90 | Severity=Success | ||
91 | SymbolicName=MSG_BURN_COMMAND_LINE | ||
92 | Language=English | ||
93 | Command Line: '%1!ls!' | ||
94 | . | ||
95 | |||
96 | MessageId=10 | ||
97 | Severity=Success | ||
98 | SymbolicName=MSG_LAUNCH_ELEVATED_ENGINE_STARTING | ||
99 | Language=English | ||
100 | Launching elevated engine process. | ||
101 | . | ||
102 | |||
103 | MessageId=11 | ||
104 | Severity=Success | ||
105 | SymbolicName=MSG_LAUNCH_ELEVATED_ENGINE_SUCCESS | ||
106 | Language=English | ||
107 | Launched elevated engine process. | ||
108 | . | ||
109 | |||
110 | MessageId=12 | ||
111 | Severity=Success | ||
112 | SymbolicName=MSG_CONNECT_TO_ELEVATED_ENGINE_SUCCESS | ||
113 | Language=English | ||
114 | Connected to elevated engine. | ||
115 | . | ||
116 | |||
117 | MessageId=13 | ||
118 | Severity=Warning | ||
119 | SymbolicName=MSG_MANIFEST_INVALID_VERSION | ||
120 | Language=English | ||
121 | The manifest contains an invalid version string: '%1!ls!' | ||
122 | . | ||
123 | |||
124 | MessageId=14 | ||
125 | Severity=Success | ||
126 | SymbolicName=MSG_BA_REQUESTED_SKIP_CLEANUP | ||
127 | Language=English | ||
128 | Bootstrapper application opted out of any engine behavior to automatically uninstall the bundle during shutdown. | ||
129 | . | ||
130 | |||
131 | MessageId=51 | ||
132 | Severity=Error | ||
133 | SymbolicName=MSG_FAILED_PARSE_CONDITION | ||
134 | Language=English | ||
135 | Error %1!hs!. Failed to parse condition %2!ls!. Unexpected symbol at position %3!hs! | ||
136 | . | ||
137 | |||
138 | MessageId=52 | ||
139 | Severity=Success | ||
140 | SymbolicName=MSG_CONDITION_RESULT | ||
141 | Language=English | ||
142 | Condition '%1!ls!' evaluates to %2!hs!. | ||
143 | . | ||
144 | |||
145 | MessageId=53 | ||
146 | Severity=Error | ||
147 | SymbolicName=MSG_FAILED_CONDITION_CHECK | ||
148 | Language=English | ||
149 | Bundle global condition check didn't succeed - aborting without loading application. | ||
150 | . | ||
151 | |||
152 | MessageId=54 | ||
153 | Severity=Error | ||
154 | SymbolicName=MSG_RESOLVE_SOURCE_FAILED | ||
155 | Language=English | ||
156 | Failed to resolve source for payload: %2!ls!, package: %3!ls!, container: %4!ls!, error: %1!ls!. | ||
157 | . | ||
158 | |||
159 | MessageId=55 | ||
160 | Severity=Warning | ||
161 | SymbolicName=MSG_CANNOT_LOAD_STATE_FILE | ||
162 | Language=English | ||
163 | Could not load or read state file: %2!ls!, error: 0x%1!x!. | ||
164 | . | ||
165 | |||
166 | MessageId=56 | ||
167 | Severity=Error | ||
168 | SymbolicName=MSG_USER_CANCELED | ||
169 | Language=English | ||
170 | Application canceled operation: %2!ls!, error: %1!ls! | ||
171 | . | ||
172 | |||
173 | MessageId=57 | ||
174 | Severity=Warning | ||
175 | SymbolicName=MSG_CONDITION_INVALID_VERSION | ||
176 | Language=English | ||
177 | Condition '%1!ls!' contains invalid version string '%2!ls!'. | ||
178 | . | ||
179 | |||
180 | MessageId=58 | ||
181 | Severity=Warning | ||
182 | SymbolicName=MSG_IGNORE_OPERATION_AFTER_QUIT | ||
183 | Language=English | ||
184 | Bootstrapper application already requested to quit, ignoring request: '%1!hs!'. | ||
185 | . | ||
186 | |||
187 | MessageId=100 | ||
188 | Severity=Success | ||
189 | SymbolicName=MSG_DETECT_BEGIN | ||
190 | Language=English | ||
191 | Detect begin, %1!u! packages | ||
192 | . | ||
193 | |||
194 | MessageId=101 | ||
195 | Severity=Success | ||
196 | SymbolicName=MSG_DETECTED_PACKAGE | ||
197 | Language=English | ||
198 | Detected package: %1!ls!, state: %2!hs!, cached: %3!hs!, install registration state: %4!hs!, cache registration state: %5!hs! | ||
199 | . | ||
200 | |||
201 | MessageId=102 | ||
202 | Severity=Success | ||
203 | SymbolicName=MSG_DETECTED_RELATED_BUNDLE | ||
204 | Language=English | ||
205 | Detected related bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!ls!, operation: %5!hs!, cached: %6!hs! | ||
206 | . | ||
207 | |||
208 | MessageId=103 | ||
209 | Severity=Success | ||
210 | SymbolicName=MSG_DETECTED_RELATED_PACKAGE | ||
211 | Language=English | ||
212 | Detected related package: %1!ls!, scope: %2!hs!, version: %3!ls!, language: %4!u! operation: %5!hs! | ||
213 | . | ||
214 | |||
215 | MessageId=104 | ||
216 | Severity=Success | ||
217 | SymbolicName=MSG_DETECTED_MSI_FEATURE | ||
218 | Language=English | ||
219 | Detected package: %1!ls!, feature: %2!ls!, state: %3!hs! | ||
220 | . | ||
221 | |||
222 | MessageId=105 | ||
223 | Severity=Success | ||
224 | SymbolicName=MSG_DETECTED_MSP_TARGET | ||
225 | Language=English | ||
226 | Detected package: %1!ls! target: %2!ls!, state: %3!hs! | ||
227 | . | ||
228 | |||
229 | MessageId=106 | ||
230 | Severity=Success | ||
231 | SymbolicName=MSG_DETECT_CALCULATE_PATCH_APPLICABILITY | ||
232 | Language=English | ||
233 | Calculating patch applicability for target product code: %1!ls!, context: %2!hs! | ||
234 | . | ||
235 | |||
236 | MessageId=107 | ||
237 | Severity=Success | ||
238 | SymbolicName=MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE | ||
239 | Language=English | ||
240 | Detected forward compatible bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!ls!, cached: %5!hs! | ||
241 | . | ||
242 | |||
243 | MessageId=108 | ||
244 | Severity=Warning | ||
245 | SymbolicName=MSG_DETECT_RELATED_BUNDLE_NOT_CACHED | ||
246 | Language=English | ||
247 | Detected related bundle missing from cache: %1!ls!, cache path: %2!ls! | ||
248 | . | ||
249 | |||
250 | MessageId=120 | ||
251 | Severity=Warning | ||
252 | SymbolicName=MSG_DETECT_PACKAGE_NOT_FULLY_CACHED | ||
253 | Language=English | ||
254 | Detected partially cached package: %1!ls!, missing payload: %2!ls! | ||
255 | . | ||
256 | |||
257 | MessageId=121 | ||
258 | Severity=Warning | ||
259 | SymbolicName=MSG_DETECT_FAILED_CALCULATE_PATCH_APPLICABILITY | ||
260 | Language=English | ||
261 | Could not calculate patch applicability for target product code: %1!ls!, context: %2!hs!, reason: 0x%3!x! | ||
262 | . | ||
263 | |||
264 | MessageId=122 | ||
265 | Severity=Warning | ||
266 | SymbolicName=MSG_RELATED_PACKAGE_INVALID_VERSION | ||
267 | Language=English | ||
268 | Related package: '%1!ls!' has invalid version: %2!ls! | ||
269 | . | ||
270 | |||
271 | MessageId=123 | ||
272 | Severity=Warning | ||
273 | SymbolicName=MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION | ||
274 | Language=English | ||
275 | Detected msi package with invalid version, product code: '%1!ls!', version: '%2!ls!' | ||
276 | . | ||
277 | |||
278 | MessageId=151 | ||
279 | Severity=Error | ||
280 | SymbolicName=MSG_FAILED_DETECT_PACKAGE | ||
281 | Language=English | ||
282 | Detect failed for package: %2!ls!, error: %1!ls! | ||
283 | . | ||
284 | |||
285 | MessageId=152 | ||
286 | Severity=Error | ||
287 | SymbolicName=MSG_FAILED_READ_RELATED_PACKAGE_LANGUAGE | ||
288 | Language=English | ||
289 | Detected related package: %2!ls!, but failed to read language: %3!hs!, error: 0x%1!x! | ||
290 | . | ||
291 | |||
292 | MessageId=170 | ||
293 | Severity=Warning | ||
294 | SymbolicName=MSG_DETECT_BAD_PRODUCT_CONFIGURATION | ||
295 | Language=English | ||
296 | Detected bad configuration for product: %1!ls! | ||
297 | . | ||
298 | |||
299 | MessageId=199 | ||
300 | Severity=Success | ||
301 | SymbolicName=MSG_DETECT_COMPLETE | ||
302 | Language=English | ||
303 | Detect complete, result: 0x%1!x!, installed: %2!hs!, cached: %3!hs!, eligible for cleanup: %4!hs! | ||
304 | . | ||
305 | |||
306 | MessageId=200 | ||
307 | Severity=Success | ||
308 | SymbolicName=MSG_PLAN_BEGIN | ||
309 | Language=English | ||
310 | Plan begin, %1!u! packages, action: %2!hs! | ||
311 | . | ||
312 | |||
313 | MessageId=201 | ||
314 | Severity=Success | ||
315 | SymbolicName=MSG_PLANNED_PACKAGE | ||
316 | Language=English | ||
317 | Planned package: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, cache: %7!hs!, uncache: %8!hs!, dependency: %9!hs!, expected install registration state: %10!hs!, expected cache registration state: %11!hs! | ||
318 | . | ||
319 | |||
320 | MessageId=202 | ||
321 | Severity=Success | ||
322 | SymbolicName=MSG_PLANNED_BUNDLE_UX_CHANGED_REQUEST | ||
323 | Language=English | ||
324 | Planned bundle: %1!ls!, ba requested state: %2!hs! over default: %3!hs! | ||
325 | . | ||
326 | |||
327 | MessageId=203 | ||
328 | Severity=Success | ||
329 | SymbolicName=MSG_PLANNED_MSI_FEATURE | ||
330 | Language=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 | |||
334 | MessageId=204 | ||
335 | Severity=Success | ||
336 | SymbolicName=MSG_PLANNED_MSI_FEATURES | ||
337 | Language=English | ||
338 | Plan %1!u! msi features for package: %2!ls! | ||
339 | . | ||
340 | |||
341 | MessageId=205 | ||
342 | Severity=Warning | ||
343 | SymbolicName=MSG_PLAN_SKIP_PATCH_ACTION | ||
344 | Language=English | ||
345 | Plan %5!hs! skipped patch: %1!ls!, action: %2!hs! because chained target package: %3!ls! being uninstalled | ||
346 | . | ||
347 | |||
348 | MessageId=206 | ||
349 | Severity=Warning | ||
350 | SymbolicName=MSG_PLAN_SKIP_SLIPSTREAM_ACTION | ||
351 | Language=English | ||
352 | Plan %5!hs! skipped patch: %1!ls!, action: %2!hs! because slipstreamed into chained target package: %3!ls!, action: %4!hs! | ||
353 | . | ||
354 | |||
355 | MessageId=207 | ||
356 | Severity=Success | ||
357 | SymbolicName=MSG_PLANNED_RELATED_BUNDLE | ||
358 | Language=English | ||
359 | Planned related bundle: %1!ls!, type: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, dependency: %7!hs! | ||
360 | . | ||
361 | |||
362 | MessageId=208 | ||
363 | Severity=Warning | ||
364 | SymbolicName=MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE | ||
365 | Language=English | ||
366 | Plan disabled rollback due to incomplete cache for package: %1!ls!, original rollback action: %2!hs! | ||
367 | . | ||
368 | |||
369 | MessageId=209 | ||
370 | Severity=Warning | ||
371 | SymbolicName=MSG_PLAN_SKIPPED_PROVIDER_KEY_REMOVAL | ||
372 | Language=English | ||
373 | Plan skipped removal of provider key: %1!ls! because it is registered to a different bundle: %2!ls! | ||
374 | . | ||
375 | |||
376 | MessageId=210 | ||
377 | Severity=Warning | ||
378 | SymbolicName=MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS | ||
379 | Language=English | ||
380 | Plan skipped due to remaining dependents: | ||
381 | . | ||
382 | |||
383 | MessageId=211 | ||
384 | Severity=Success | ||
385 | SymbolicName=MSG_PLANNED_UPGRADE_BUNDLE | ||
386 | Language=English | ||
387 | Planned upgrade bundle: %1!ls!, default requested: %2!hs!, ba requested: %3!hs!, execute: %4!hs!, rollback: %5!hs!, dependency: %6!hs! | ||
388 | . | ||
389 | |||
390 | MessageId=212 | ||
391 | Severity=Success | ||
392 | SymbolicName=MSG_PLANNED_FORWARD_COMPATIBLE_BUNDLE | ||
393 | Language=English | ||
394 | Planned forward compatible bundle: %1!ls!, default requested: %2!hs!, ba requested: %3!hs!, execute: %4!hs!, rollback: %5!hs!, dependency: %6!hs! | ||
395 | . | ||
396 | |||
397 | MessageId=213 | ||
398 | Severity=Success | ||
399 | SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_DEPENDENT | ||
400 | Language=English | ||
401 | Plan 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 | |||
404 | MessageId=214 | ||
405 | Severity=Success | ||
406 | SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_SCHEDULED | ||
407 | Language=English | ||
408 | Plan skipped related bundle: %1!ls!, type: %2!hs!, because it was previously scheduled. | ||
409 | . | ||
410 | |||
411 | MessageId=216 | ||
412 | Severity=Success | ||
413 | SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER | ||
414 | Language=English | ||
415 | Plan skipped related bundle: %1!ls!, type: %2!hs!, provider key: %3!ls!, because an embedded bundle with the same provider key is being installed. | ||
416 | . | ||
417 | |||
418 | MessageId=217 | ||
419 | Severity=Success | ||
420 | SymbolicName=MSG_PLAN_SKIPPED_DEPENDENT_BUNDLE_REPAIR | ||
421 | Language=English | ||
422 | Plan skipped dependent bundle repair: %1!ls!, type: %2!hs!, because no packages are being executed during this uninstall operation. | ||
423 | . | ||
424 | |||
425 | MessageId=218 | ||
426 | Severity=Success | ||
427 | SymbolicName=MSG_PLANNED_MSP_TARGETS | ||
428 | Language=English | ||
429 | Plan %1!u! patch targets for package: %2!ls! | ||
430 | . | ||
431 | |||
432 | MessageId=219 | ||
433 | Severity=Success | ||
434 | SymbolicName=MSG_PLANNED_MSP_TARGET | ||
435 | Language=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 | |||
439 | MessageId=220 | ||
440 | Severity=Success | ||
441 | SymbolicName=MSG_PLANNED_SLIPSTREAMED_MSP_TARGETS | ||
442 | Language=English | ||
443 | Plan %1!u! slipstream patches for package: %2!ls! | ||
444 | . | ||
445 | |||
446 | MessageId=221 | ||
447 | Severity=Success | ||
448 | SymbolicName=MSG_PLANNED_SLIPSTREAMED_MSP_TARGET | ||
449 | Language=English | ||
450 | Planned slipstreamed patch: %1!ls!, execute: %2!hs!, rollback: %3!hs! | ||
451 | . | ||
452 | |||
453 | MessageId=299 | ||
454 | Severity=Success | ||
455 | SymbolicName=MSG_PLAN_COMPLETE | ||
456 | Language=English | ||
457 | Plan complete, result: 0x%1!x! | ||
458 | . | ||
459 | |||
460 | MessageId=300 | ||
461 | Severity=Success | ||
462 | SymbolicName=MSG_APPLY_BEGIN | ||
463 | Language=English | ||
464 | Apply begin | ||
465 | . | ||
466 | |||
467 | MessageId=301 | ||
468 | Severity=Success | ||
469 | SymbolicName=MSG_APPLYING_PACKAGE | ||
470 | Language=English | ||
471 | Applying %1!hs! package: %2!ls!, action: %3!hs!, path: %4!ls!, arguments: '%5!ls!' | ||
472 | . | ||
473 | |||
474 | MessageId=302 | ||
475 | Severity=Success | ||
476 | SymbolicName=MSG_ACQUIRED_PAYLOAD | ||
477 | Language=English | ||
478 | Acquired payload: %1!ls! to working path: %2!ls! from: %4!ls!. | ||
479 | . | ||
480 | |||
481 | MessageId=303 | ||
482 | Severity=Success | ||
483 | SymbolicName=MSG_VERIFIED_EXISTING_CONTAINER | ||
484 | Language=English | ||
485 | Verified existing container: %1!ls! at path: %2!ls!. | ||
486 | . | ||
487 | |||
488 | MessageId=304 | ||
489 | Severity=Success | ||
490 | SymbolicName=MSG_VERIFIED_EXISTING_PAYLOAD | ||
491 | Language=English | ||
492 | Verified existing payload: %1!ls! at path: %2!ls!. | ||
493 | . | ||
494 | |||
495 | MessageId=305 | ||
496 | Severity=Success | ||
497 | SymbolicName=MSG_VERIFIED_ACQUIRED_PAYLOAD | ||
498 | Language=English | ||
499 | Verified acquired payload: %1!ls! at path: %2!ls!, %3!hs! to: %4!ls!. | ||
500 | . | ||
501 | |||
502 | MessageId=306 | ||
503 | Severity=Success | ||
504 | SymbolicName=MSG_APPLYING_PATCH_PACKAGE | ||
505 | Language=English | ||
506 | Applying package: %1!ls!, target: %5!ls!, action: %2!hs!, path: %3!ls!, arguments: '%4!ls!' | ||
507 | . | ||
508 | |||
509 | MessageId=307 | ||
510 | Severity=Warning | ||
511 | SymbolicName=MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE | ||
512 | Language=English | ||
513 | Attempted to uninstall absent package: %1!ls!. Continuing... | ||
514 | . | ||
515 | |||
516 | MessageId=308 | ||
517 | Severity=Warning | ||
518 | SymbolicName=MSG_FAILED_PAUSE_AU | ||
519 | Language=English | ||
520 | Automatic updates could not be paused due to error: 0x%1!x!. Continuing... | ||
521 | . | ||
522 | |||
523 | MessageId=309 | ||
524 | Severity=Warning | ||
525 | SymbolicName=MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE | ||
526 | Language=English | ||
527 | Skipping apply of package: %1!ls! due to cache error: 0x%2!x!. Continuing... | ||
528 | . | ||
529 | |||
530 | MessageId=310 | ||
531 | Severity=Error | ||
532 | SymbolicName=MSG_FAILED_VERIFY_PAYLOAD | ||
533 | Language=English | ||
534 | Failed to verify payload: %2!ls! at path: %3!ls!, error: %1!ls!. Deleting file. | ||
535 | . | ||
536 | |||
537 | MessageId=311 | ||
538 | Severity=Error | ||
539 | SymbolicName=MSG_FAILED_ACQUIRE_CONTAINER | ||
540 | Language=English | ||
541 | Failed to acquire container: %2!ls! to working path: %3!ls!, error: %1!ls!. | ||
542 | . | ||
543 | |||
544 | MessageId=312 | ||
545 | Severity=Error | ||
546 | SymbolicName=MSG_FAILED_EXTRACT_CONTAINER | ||
547 | Language=English | ||
548 | Failed to extract payloads from container: %2!ls! to working path: %3!ls!, error: %1!ls!. | ||
549 | . | ||
550 | |||
551 | MessageId=313 | ||
552 | Severity=Error | ||
553 | SymbolicName=MSG_FAILED_ACQUIRE_PAYLOAD | ||
554 | Language=English | ||
555 | Failed to acquire payload: %2!ls! to working path: %3!ls!, error: %1!ls!. | ||
556 | . | ||
557 | |||
558 | MessageId=314 | ||
559 | Severity=Error | ||
560 | SymbolicName=MSG_FAILED_CACHE_PAYLOAD | ||
561 | Language=English | ||
562 | Failed to cache payload: %2!ls! from working path: %4!ls!, error: %1!ls!. | ||
563 | . | ||
564 | |||
565 | MessageId=315 | ||
566 | Severity=Error | ||
567 | SymbolicName=MSG_FAILED_LAYOUT_BUNDLE | ||
568 | Language=English | ||
569 | Failed to layout bundle: %2!ls! to layout directory: %3!ls!, error: %1!ls!. | ||
570 | . | ||
571 | |||
572 | MessageId=316 | ||
573 | Severity=Error | ||
574 | SymbolicName=MSG_FAILED_LAYOUT_CONTAINER | ||
575 | Language=English | ||
576 | Failed to layout container: %2!ls! to layout directory: %3!ls!, error: %1!ls!. | ||
577 | . | ||
578 | |||
579 | |||
580 | MessageId=317 | ||
581 | Severity=Error | ||
582 | SymbolicName=MSG_FAILED_LAYOUT_PAYLOAD | ||
583 | Language=English | ||
584 | Failed to layout payload: %2!ls! from working path: %4!ls! to layout directory: %3!ls!, error: %1!ls!. | ||
585 | . | ||
586 | |||
587 | MessageId=318 | ||
588 | Severity=Success | ||
589 | SymbolicName=MSG_ROLLBACK_PACKAGE_SKIPPED | ||
590 | Language=English | ||
591 | Skipped rollback of package: %1!ls!, action: %2!hs!, already: %3!hs! | ||
592 | . | ||
593 | |||
594 | MessageId=319 | ||
595 | Severity=Success | ||
596 | SymbolicName=MSG_APPLY_COMPLETED_PACKAGE | ||
597 | Language=English | ||
598 | Applied %1!hs! package: %2!ls!, result: 0x%3!x!, restart: %4!hs! | ||
599 | . | ||
600 | |||
601 | MessageId=320 | ||
602 | Severity=Success | ||
603 | SymbolicName=MSG_DEPENDENCY_BUNDLE_REGISTER | ||
604 | Language=English | ||
605 | Registering bundle dependency provider: %1!ls!, version: %2!ls! | ||
606 | . | ||
607 | |||
608 | MessageId=321 | ||
609 | Severity=Warning | ||
610 | SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_NOPROVIDERS | ||
611 | Language=English | ||
612 | Skipping dependency registration on package with no dependency providers: %1!ls! | ||
613 | . | ||
614 | |||
615 | MessageId=322 | ||
616 | Severity=Warning | ||
617 | SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE | ||
618 | Language=English | ||
619 | Skipping cross-scope dependency registration on package: %1!ls!, bundle scope: %2!hs!, package scope: %3!hs! | ||
620 | . | ||
621 | |||
622 | MessageId=323 | ||
623 | Severity=Success | ||
624 | SymbolicName=MSG_DEPENDENCY_PACKAGE_REGISTER | ||
625 | Language=English | ||
626 | Registering package dependency provider: %1!ls!, version: %2!ls!, package: %3!ls! | ||
627 | . | ||
628 | |||
629 | MessageId=324 | ||
630 | Severity=Warning | ||
631 | SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_MISSING | ||
632 | Language=English | ||
633 | Skipping dependency registration on missing package provider: %1!ls!, package: %2!ls! | ||
634 | . | ||
635 | |||
636 | MessageId=325 | ||
637 | Severity=Success | ||
638 | SymbolicName=MSG_DEPENDENCY_PACKAGE_REGISTER_DEPENDENCY | ||
639 | Language=English | ||
640 | Registering dependency: %1!ls! on package provider: %2!ls!, package: %3!ls! | ||
641 | . | ||
642 | |||
643 | MessageId=326 | ||
644 | Severity=Success | ||
645 | SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY | ||
646 | Language=English | ||
647 | Removed dependency: %1!ls! on package provider: %2!ls!, package %3!ls! | ||
648 | . | ||
649 | |||
650 | MessageId=327 | ||
651 | Severity=Warning | ||
652 | SymbolicName=MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS | ||
653 | Language=English | ||
654 | Will not uninstall package: %1!ls!, found dependents: | ||
655 | . | ||
656 | |||
657 | MessageId=328 | ||
658 | Severity=Warning | ||
659 | SymbolicName=MSG_DEPENDENCY_PACKAGE_DEPENDENT | ||
660 | Language=English | ||
661 | Found dependent: %1!ls!, name: %2!ls! | ||
662 | . | ||
663 | |||
664 | MessageId=329 | ||
665 | Severity=Success | ||
666 | SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED | ||
667 | Language=English | ||
668 | Removed package dependency provider: %1!ls!, package: %2!ls! | ||
669 | . | ||
670 | |||
671 | MessageId=330 | ||
672 | Severity=Success | ||
673 | SymbolicName=MSG_DEPENDENCY_BUNDLE_UNREGISTERED | ||
674 | Language=English | ||
675 | Removed bundle dependency provider: %1!ls! | ||
676 | . | ||
677 | |||
678 | MessageId=331 | ||
679 | Severity=Warning | ||
680 | SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY_FAILED | ||
681 | Language=English | ||
682 | Could not remove dependency: %1!ls! on package provider: %2!ls!, package %3!ls!, error: 0x%4!x! | ||
683 | . | ||
684 | |||
685 | MessageId=332 | ||
686 | Severity=Warning | ||
687 | SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_FAILED | ||
688 | Language=English | ||
689 | Could not remove package dependency provider: %1!ls!, package: %2!ls!, error: 0x%3!x! | ||
690 | . | ||
691 | |||
692 | MessageId=333 | ||
693 | Severity=Warning | ||
694 | SymbolicName=MSG_DEPENDENCY_BUNDLE_UNREGISTERED_FAILED | ||
695 | Language=English | ||
696 | Could not remove bundle dependency provider: %1!ls!, error: 0x%2!x! | ||
697 | . | ||
698 | |||
699 | MessageId=334 | ||
700 | Severity=Warning | ||
701 | SymbolicName=MSG_DEPENDENCY_BUNDLE_DEPENDENT | ||
702 | Language=English | ||
703 | Found dependent: %1!ls!, name: %2!ls! | ||
704 | . | ||
705 | |||
706 | MessageId=335 | ||
707 | Severity=Success | ||
708 | SymbolicName=MSG_ACQUIRE_BUNDLE_PAYLOAD | ||
709 | Language=English | ||
710 | Acquiring bundle payload: %2!ls!, %3!hs! from: %4!ls! | ||
711 | . | ||
712 | |||
713 | MessageId=336 | ||
714 | Severity=Success | ||
715 | SymbolicName=MSG_ACQUIRE_CONTAINER | ||
716 | Language=English | ||
717 | Acquiring container: %1!ls!, %3!hs! from: %4!ls! | ||
718 | . | ||
719 | |||
720 | MessageId=338 | ||
721 | Severity=Success | ||
722 | SymbolicName=MSG_ACQUIRE_PACKAGE_PAYLOAD | ||
723 | Language=English | ||
724 | Acquiring package: %1!ls!, payload: %2!ls!, %3!hs! from: %4!ls! | ||
725 | . | ||
726 | |||
727 | MessageId=339 | ||
728 | Severity=Error | ||
729 | SymbolicName=MSG_FAILED_VERIFY_CONTAINER | ||
730 | Language=English | ||
731 | Failed to verify container: %2!ls! at path: %3!ls!, error: %1!ls!. Deleting file. | ||
732 | . | ||
733 | |||
734 | MessageId=340 | ||
735 | Severity=Warning | ||
736 | SymbolicName=MSG_CACHE_CONTINUING_NONVITAL_PACKAGE | ||
737 | Language=English | ||
738 | Cached non-vital package: %1!ls!, encountered error: 0x%2!x!. Continuing... | ||
739 | . | ||
740 | |||
741 | MessageId=346 | ||
742 | Severity=Warning | ||
743 | SymbolicName=MSG_CACHE_RETRYING_PACKAGE | ||
744 | Language=English | ||
745 | Application requested retry of caching package: %1!ls!, encountered error: 0x%2!x!. Retrying... | ||
746 | . | ||
747 | |||
748 | MessageId=347 | ||
749 | Severity=Warning | ||
750 | SymbolicName=MSG_CACHE_RETRYING_CONTAINER | ||
751 | Language=English | ||
752 | Application requested retry of caching container: %2!ls!, encountered error: %1!ls!. Retrying... | ||
753 | . | ||
754 | |||
755 | MessageId=348 | ||
756 | Severity=Warning | ||
757 | SymbolicName=MSG_APPLY_RETRYING_PACKAGE | ||
758 | Language=English | ||
759 | Application requested retry of executing package: %1!ls!, encountered error: 0x%2!x!. Retrying... | ||
760 | . | ||
761 | |||
762 | MessageId=349 | ||
763 | Severity=Warning | ||
764 | SymbolicName=MSG_CACHE_RETRYING_PAYLOAD | ||
765 | Language=English | ||
766 | Application requested retry of caching payload: %2!ls!, encountered error: %1!ls!. Retrying... | ||
767 | . | ||
768 | |||
769 | MessageId=350 | ||
770 | Severity=Warning | ||
771 | SymbolicName=MSG_APPLY_CONTINUING_NONVITAL_PACKAGE | ||
772 | Language=English | ||
773 | Applied non-vital package: %1!ls!, encountered error: 0x%2!x!. Continuing... | ||
774 | . | ||
775 | |||
776 | MessageId=351 | ||
777 | Severity=Success | ||
778 | SymbolicName=MSG_UNCACHE_PACKAGE | ||
779 | Language=English | ||
780 | Removing cached package: %1!ls!, from path: %2!ls! | ||
781 | . | ||
782 | |||
783 | MessageId=352 | ||
784 | Severity=Success | ||
785 | SymbolicName=MSG_UNCACHE_BUNDLE | ||
786 | Language=English | ||
787 | Removing cached bundle: %1!ls!, from path: %2!ls! | ||
788 | . | ||
789 | |||
790 | MessageId=353 | ||
791 | Severity=Warning | ||
792 | SymbolicName=MSG_UNABLE_UNCACHE_PACKAGE | ||
793 | Language=English | ||
794 | Unable to remove cached package: %1!ls!, from path: %2!ls!, reason: 0x%3!x!. Continuing... | ||
795 | . | ||
796 | |||
797 | MessageId=354 | ||
798 | Severity=Warning | ||
799 | SymbolicName=MSG_UNABLE_UNCACHE_BUNDLE | ||
800 | Language=English | ||
801 | Unable to remove cached bundle: %1!ls!, from path: %2!ls!, reason: 0x%3!x!. Continuing... | ||
802 | . | ||
803 | |||
804 | MessageId=355 | ||
805 | Severity=Warning | ||
806 | SymbolicName=MSG_SOURCELIST_REGISTER | ||
807 | Language=English | ||
808 | Unable to register source directory: %1!ls!, product: %2!ls!, reason: 0x%3!x!. Continuing... | ||
809 | . | ||
810 | |||
811 | MessageId=356 | ||
812 | Severity=Warning | ||
813 | SymbolicName=MSG_APPLY_RETRYING_ACQUIRE_CONTAINER | ||
814 | Language=English | ||
815 | Application requested retry acquire of container: %2!ls!, encountered error: %1!ls!. Retrying... | ||
816 | . | ||
817 | |||
818 | MessageId=357 | ||
819 | Severity=Warning | ||
820 | SymbolicName=MSG_APPLY_RETRYING_ACQUIRE_PAYLOAD | ||
821 | Language=English | ||
822 | Application requested retry acquire of payload: %2!ls!, encountered error: %1!ls!. Retrying... | ||
823 | . | ||
824 | |||
825 | MessageId=358 | ||
826 | Severity=Success | ||
827 | SymbolicName=MSG_PAUSE_AU_STARTING | ||
828 | Language=English | ||
829 | Pausing automatic updates. | ||
830 | . | ||
831 | |||
832 | MessageId=359 | ||
833 | Severity=Success | ||
834 | SymbolicName=MSG_PAUSE_AU_SUCCEEDED | ||
835 | Language=English | ||
836 | Paused automatic updates. | ||
837 | . | ||
838 | |||
839 | MessageId=360 | ||
840 | Severity=Success | ||
841 | SymbolicName=MSG_SYSTEM_RESTORE_POINT_STARTING | ||
842 | Language=English | ||
843 | Creating a system restore point. | ||
844 | . | ||
845 | |||
846 | MessageId=361 | ||
847 | Severity=Success | ||
848 | SymbolicName=MSG_SYSTEM_RESTORE_POINT_SUCCEEDED | ||
849 | Language=English | ||
850 | Created a system restore point. | ||
851 | . | ||
852 | |||
853 | MessageId=362 | ||
854 | Severity=Success | ||
855 | SymbolicName=MSG_SYSTEM_RESTORE_POINT_DISABLED | ||
856 | Language=English | ||
857 | System restore disabled, system restore point not created. | ||
858 | . | ||
859 | |||
860 | MessageId=363 | ||
861 | Severity=Warning | ||
862 | SymbolicName=MSG_SYSTEM_RESTORE_POINT_FAILED | ||
863 | Language=English | ||
864 | Could not create system restore point, error: 0x%1!x!. Continuing... | ||
865 | . | ||
866 | |||
867 | MessageId=370 | ||
868 | Severity=Success | ||
869 | SymbolicName=MSG_SESSION_BEGIN | ||
870 | Language=English | ||
871 | Session begin, registration key: %1!ls!, options: 0x%2!x!, disable resume: %3!hs! | ||
872 | . | ||
873 | |||
874 | MessageId=371 | ||
875 | Severity=Success | ||
876 | SymbolicName=MSG_SESSION_UPDATE | ||
877 | Language=English | ||
878 | Updating session, registration key: %1!ls!, resume: %2!hs!, restart initiated: %3!hs!, disable resume: %4!hs! | ||
879 | . | ||
880 | |||
881 | MessageId=372 | ||
882 | Severity=Success | ||
883 | SymbolicName=MSG_SESSION_END | ||
884 | Language=English | ||
885 | Session end, registration key: %1!ls!, resume: %2!hs!, restart: %3!hs!, disable resume: %4!hs! | ||
886 | . | ||
887 | |||
888 | MessageId=373 | ||
889 | Severity=Success | ||
890 | SymbolicName=MSG_POST_APPLY_CALCULATE_REGISTRATION | ||
891 | Language=English | ||
892 | Calculating whether to keep registration | ||
893 | . | ||
894 | |||
895 | |||
896 | MessageId=374 | ||
897 | Severity=Success | ||
898 | SymbolicName=MSG_POST_APPLY_PACKAGE | ||
899 | Language=English | ||
900 | package: %1!ls!, install registration state: %2!hs!, cache registration state: %3!hs! | ||
901 | . | ||
902 | |||
903 | MessageId=380 | ||
904 | Severity=Warning | ||
905 | SymbolicName=MSG_APPLY_SKIPPED | ||
906 | Language=English | ||
907 | Apply skipped, no planned actions | ||
908 | . | ||
909 | |||
910 | MessageId=381 | ||
911 | Severity=Warning | ||
912 | SymbolicName=MSG_APPLY_CANCEL_IGNORED_DURING_ROLLBACK | ||
913 | Language=English | ||
914 | Ignoring application request to cancel from %1!ls! during rollback. | ||
915 | . | ||
916 | |||
917 | MessageId=382 | ||
918 | Severity=Warning | ||
919 | SymbolicName=MSG_PLAN_ROLLBACK_DISABLED | ||
920 | Language=English | ||
921 | Rollback is disabled for this bundle. | ||
922 | . | ||
923 | |||
924 | MessageId=383 | ||
925 | Severity=Error | ||
926 | SymbolicName=MSG_MSI_TRANSACTIONS_DISABLED | ||
927 | Language=English | ||
928 | Windows Installer rollback is disabled on this computer. It must be enabled for this bundle to proceed. | ||
929 | . | ||
930 | |||
931 | MessageId=384 | ||
932 | Severity=Success | ||
933 | SymbolicName=MSG_MSI_TRANSACTION_BEGIN | ||
934 | Language=English | ||
935 | Starting a new MSI transaction, id: %1!ls! | ||
936 | . | ||
937 | |||
938 | MessageId=385 | ||
939 | Severity=Success | ||
940 | SymbolicName=MSG_MSI_TRANSACTION_COMMIT | ||
941 | Language=English | ||
942 | Committing MSI transaction, id: %1!ls! | ||
943 | . | ||
944 | |||
945 | MessageId=386 | ||
946 | Severity=Warning | ||
947 | SymbolicName=MSG_MSI_TRANSACTION_ROLLBACK | ||
948 | Language=English | ||
949 | Rolling back MSI transaction, id: %1!ls! | ||
950 | . | ||
951 | |||
952 | MessageId=387 | ||
953 | Severity=Error | ||
954 | SymbolicName=MSG_RESTART_REQUEST_DURING_MSI_TRANSACTION | ||
955 | Language=English | ||
956 | Illegal state: Reboot requested within an MSI transaction, id: %1!ls! | ||
957 | . | ||
958 | |||
959 | MessageId=399 | ||
960 | Severity=Success | ||
961 | SymbolicName=MSG_APPLY_COMPLETE | ||
962 | Language=English | ||
963 | Apply complete, result: 0x%1!x!, restart: %2!hs!, ba requested restart: %3!hs! | ||
964 | . | ||
965 | |||
966 | MessageId=400 | ||
967 | Severity=Success | ||
968 | SymbolicName=MSG_SYSTEM_SHUTDOWN | ||
969 | Language=English | ||
970 | Received system request to shut down the process: critical: %1!hs!, elevated: %2!hs!, allowed: %3!hs! | ||
971 | . | ||
972 | |||
973 | MessageId=410 | ||
974 | Severity=Success | ||
975 | SymbolicName=MSG_VARIABLE_DUMP | ||
976 | Language=English | ||
977 | Variable: %1!ls! | ||
978 | . | ||
979 | |||
980 | MessageId=411 | ||
981 | Severity=Warning | ||
982 | SymbolicName=MSG_VARIABLE_INVALID_VERSION | ||
983 | Language=English | ||
984 | The variable '%1!ls!' is being set with an invalid version string. | ||
985 | . | ||
986 | |||
987 | MessageId=412 | ||
988 | Severity=Warning | ||
989 | SymbolicName=MSG_INVALID_VERSION_COERSION | ||
990 | Language=English | ||
991 | The string '%1!ls!' could not be coerced to a valid version. | ||
992 | . | ||
993 | |||
994 | MessageId=420 | ||
995 | Severity=Success | ||
996 | SymbolicName=MSG_RESUME_AU_STARTING | ||
997 | Language=English | ||
998 | Resuming automatic updates. | ||
999 | . | ||
1000 | |||
1001 | MessageId=421 | ||
1002 | Severity=Success | ||
1003 | SymbolicName=MSG_RESUME_AU_SUCCEEDED | ||
1004 | Language=English | ||
1005 | Resumed automatic updates. | ||
1006 | . | ||
1007 | |||
1008 | MessageId=500 | ||
1009 | Severity=Success | ||
1010 | SymbolicName=MSG_QUIT | ||
1011 | Language=English | ||
1012 | Shutting down, exit code: 0x%1!x! | ||
1013 | . | ||
1014 | |||
1015 | MessageId=501 | ||
1016 | Severity=Warning | ||
1017 | SymbolicName=MSG_STATE_NOT_SAVED | ||
1018 | Language=English | ||
1019 | The state file could not be saved, error: %1!ls!. Continuing... | ||
1020 | . | ||
1021 | |||
1022 | MessageId=502 | ||
1023 | Severity=Success | ||
1024 | SymbolicName=MSG_CLEANUP_BEGIN | ||
1025 | Language=English | ||
1026 | Cleanup begin. | ||
1027 | . | ||
1028 | |||
1029 | MessageId=503 | ||
1030 | Severity=Success | ||
1031 | SymbolicName=MSG_CLEANUP_SKIPPED_APPLY | ||
1032 | Language=English | ||
1033 | Cleanup not required due to running Apply. | ||
1034 | . | ||
1035 | |||
1036 | MessageId=504 | ||
1037 | Severity=Success | ||
1038 | SymbolicName=MSG_CLEANUP_SKIPPED_ELEVATION_REQUIRED | ||
1039 | Language=English | ||
1040 | Cleanup check skipped since this per-machine bundle would require elevation. | ||
1041 | . | ||
1042 | |||
1043 | MessageId=599 | ||
1044 | Severity=Success | ||
1045 | SymbolicName=MSG_CLEANUP_COMPLETE | ||
1046 | Language=English | ||
1047 | Cleanup complete, result: 0x%1!x! | ||
1048 | . | ||
1049 | |||
1050 | MessageId=600 | ||
1051 | Severity=Success | ||
1052 | SymbolicName=MSG_LAUNCH_APPROVED_EXE_BEGIN | ||
1053 | Language=English | ||
1054 | LaunchApprovedExe begin, id: %1!ls! | ||
1055 | . | ||
1056 | |||
1057 | MessageId=601 | ||
1058 | Severity=Success | ||
1059 | SymbolicName=MSG_LAUNCH_APPROVED_EXE_SEARCH | ||
1060 | Language=English | ||
1061 | Searching registry for approved exe path, key: %1!ls!, value: '%2!ls!', win64: %3!ls! | ||
1062 | . | ||
1063 | |||
1064 | MessageId=602 | ||
1065 | Severity=Success | ||
1066 | SymbolicName=MSG_LAUNCHING_APPROVED_EXE | ||
1067 | Language=English | ||
1068 | Launching approved exe, path: '%1!ls!', 'command: %2!ls!' | ||
1069 | . | ||
1070 | |||
1071 | MessageId=699 | ||
1072 | Severity=Success | ||
1073 | SymbolicName=MSG_LAUNCH_APPROVED_EXE_COMPLETE | ||
1074 | Language=English | ||
1075 | LaunchApprovedExe complete, result: 0x%1!x!, processId: %2!lu! | ||
1076 | . | ||
1077 | |||
1078 | MessageId=700 | ||
1079 | Severity=Success | ||
1080 | SymbolicName=MSG_MSI_PROPERTY_CONDITION_FAILED | ||
1081 | Language=English | ||
1082 | Skipping MSI property '%1!ls!' because condition '%2!ls!' evaluates to %3!hs!. | ||
1083 | . | ||
1084 | |||
1085 | MessageId=701 | ||
1086 | Severity=Warning | ||
1087 | SymbolicName=MSG_PENDING_REBOOT_DETECTED | ||
1088 | Language=English | ||
1089 | A 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" | ||
155 | rc.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 | |||
8 | static HRESULT HandleExitCode( | ||
9 | __in BURN_PACKAGE* pPackage, | ||
10 | __in DWORD dwExitCode, | ||
11 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
12 | ); | ||
13 | static HRESULT ParseCommandLineArgumentsFromXml( | ||
14 | __in IXMLDOMNode* pixnExePackage, | ||
15 | __in BURN_PACKAGE* pPackage | ||
16 | ); | ||
17 | static HRESULT ParseExitCodesFromXml( | ||
18 | __in IXMLDOMNode* pixnExePackage, | ||
19 | __in BURN_PACKAGE* pPackage | ||
20 | ); | ||
21 | |||
22 | |||
23 | // function definitions | ||
24 | |||
25 | extern "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 | |||
91 | LExit: | ||
92 | ReleaseObject(pixnNodes); | ||
93 | ReleaseObject(pixnNode); | ||
94 | ReleaseStr(scz); | ||
95 | |||
96 | return hr; | ||
97 | } | ||
98 | |||
99 | extern "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 | |||
129 | extern "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 | |||
152 | LExit: | ||
153 | return hr; | ||
154 | } | ||
155 | |||
156 | // | ||
157 | // PlanCalculate - calculates the execute and rollback state for the requested package state. | ||
158 | // | ||
159 | extern "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 | |||
259 | LExit: | ||
260 | return hr; | ||
261 | } | ||
262 | |||
263 | // | ||
264 | // PlanAdd - adds the calculated execute and rollback actions for the package. | ||
265 | // | ||
266 | extern "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 | |||
347 | LExit: | ||
348 | return hr; | ||
349 | } | ||
350 | |||
351 | extern "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 | |||
551 | LExit: | ||
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 | |||
571 | extern "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 | |||
592 | LExit: | ||
593 | return; | ||
594 | } | ||
595 | |||
596 | |||
597 | // internal helper functions | ||
598 | |||
599 | static 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 | |||
659 | LExit: | ||
660 | ReleaseObject(pixnNodes); | ||
661 | ReleaseObject(pixnNode); | ||
662 | ReleaseStr(scz); | ||
663 | |||
664 | return hr; | ||
665 | } | ||
666 | |||
667 | static 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 | |||
724 | LExit: | ||
725 | ReleaseObject(pixnNodes); | ||
726 | ReleaseObject(pixnNode); | ||
727 | ReleaseStr(scz); | ||
728 | |||
729 | return hr; | ||
730 | } | ||
731 | |||
732 | static 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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // function declarations | ||
11 | |||
12 | HRESULT ExeEngineParsePackageFromXml( | ||
13 | __in IXMLDOMNode* pixnExePackage, | ||
14 | __in BURN_PACKAGE* pPackage | ||
15 | ); | ||
16 | void ExeEnginePackageUninitialize( | ||
17 | __in BURN_PACKAGE* pPackage | ||
18 | ); | ||
19 | HRESULT ExeEngineDetectPackage( | ||
20 | __in BURN_PACKAGE* pPackage, | ||
21 | __in BURN_VARIABLES* pVariables | ||
22 | ); | ||
23 | HRESULT ExeEnginePlanCalculatePackage( | ||
24 | __in BURN_PACKAGE* pPackage | ||
25 | ); | ||
26 | HRESULT 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 | ); | ||
34 | HRESULT 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 | ); | ||
42 | void 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 | |||
6 | static HRESULT CopyStringToExternal( | ||
7 | __in_z LPWSTR wzValue, | ||
8 | __in_z_opt LPWSTR wzBuffer, | ||
9 | __inout SIZE_T* pcchBuffer | ||
10 | ); | ||
11 | |||
12 | // function definitions | ||
13 | |||
14 | void ExternalEngineGetPackageCount( | ||
15 | __in BURN_ENGINE_STATE* pEngineState, | ||
16 | __out DWORD* pcPackages | ||
17 | ) | ||
18 | { | ||
19 | *pcPackages = pEngineState->packages.cPackages; | ||
20 | } | ||
21 | |||
22 | HRESULT 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 | |||
43 | HRESULT 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 | |||
71 | HRESULT 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 | |||
99 | HRESULT 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 | |||
127 | HRESULT 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 | |||
154 | HRESULT 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 | |||
175 | HRESULT 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 | |||
187 | HRESULT 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 | |||
220 | LExit: | ||
221 | ReleaseBuffer(pbData); | ||
222 | |||
223 | return hr; | ||
224 | } | ||
225 | |||
226 | HRESULT 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 | |||
255 | LExit: | ||
256 | ReleaseBuffer(pbData); | ||
257 | |||
258 | return hr; | ||
259 | } | ||
260 | |||
261 | HRESULT 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 | |||
327 | LExit: | ||
328 | ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive); | ||
329 | |||
330 | ReleaseStr(sczCommandline); | ||
331 | ReleaseStr(sczFilePath); | ||
332 | |||
333 | return hr; | ||
334 | } | ||
335 | |||
336 | HRESULT 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 | |||
376 | LExit: | ||
377 | ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive); | ||
378 | |||
379 | return hr; | ||
380 | } | ||
381 | |||
382 | HRESULT 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 | |||
453 | LExit: | ||
454 | ::LeaveCriticalSection(&pEngineState->userExperience.csEngineActive); | ||
455 | |||
456 | return hr; | ||
457 | } | ||
458 | |||
459 | HRESULT 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 | |||
478 | LExit: | ||
479 | return hr; | ||
480 | } | ||
481 | |||
482 | HRESULT 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 | |||
502 | LExit: | ||
503 | return hr; | ||
504 | } | ||
505 | |||
506 | HRESULT 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 | |||
532 | LExit: | ||
533 | ReleaseVerutilVersion(pVersion); | ||
534 | |||
535 | return hr; | ||
536 | } | ||
537 | |||
538 | void 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 | |||
549 | HRESULT 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 | |||
562 | HRESULT 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 | |||
574 | LExit: | ||
575 | return hr; | ||
576 | } | ||
577 | |||
578 | HRESULT 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 | |||
595 | LExit: | ||
596 | return hr; | ||
597 | } | ||
598 | |||
599 | HRESULT 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 | |||
616 | LExit: | ||
617 | return hr; | ||
618 | } | ||
619 | |||
620 | HRESULT 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 | |||
638 | LExit: | ||
639 | return hr; | ||
640 | } | ||
641 | |||
642 | HRESULT 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 | |||
654 | LExit: | ||
655 | return hr; | ||
656 | } | ||
657 | |||
658 | HRESULT 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 | |||
706 | LExit: | ||
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 | |||
720 | HRESULT 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 | |||
743 | LExit: | ||
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. | ||
753 | HRESULT 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 | |||
772 | LExit: | ||
773 | return hr; | ||
774 | } | ||
775 | |||
776 | static 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) | ||
11 | extern "C" { | ||
12 | #endif | ||
13 | |||
14 | void ExternalEngineGetPackageCount( | ||
15 | __in BURN_ENGINE_STATE* pEngineState, | ||
16 | __out DWORD* pcPackages | ||
17 | ); | ||
18 | |||
19 | HRESULT ExternalEngineGetVariableNumeric( | ||
20 | __in BURN_ENGINE_STATE* pEngineState, | ||
21 | __in_z LPCWSTR wzVariable, | ||
22 | __out LONGLONG* pllValue | ||
23 | ); | ||
24 | |||
25 | HRESULT 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 | |||
32 | HRESULT 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 | |||
39 | HRESULT 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 | |||
46 | HRESULT ExternalEngineEscapeString( | ||
47 | __in_z LPCWSTR wzIn, | ||
48 | __out_ecount_opt(*pcchOut) LPWSTR wzOut, | ||
49 | __inout SIZE_T* pcchOut | ||
50 | ); | ||
51 | |||
52 | HRESULT ExternalEngineEvaluateCondition( | ||
53 | __in BURN_ENGINE_STATE* pEngineState, | ||
54 | __in_z LPCWSTR wzCondition, | ||
55 | __out BOOL* pf | ||
56 | ); | ||
57 | |||
58 | HRESULT ExternalEngineLog( | ||
59 | __in REPORT_LEVEL rl, | ||
60 | __in_z LPCWSTR wzMessage | ||
61 | ); | ||
62 | |||
63 | HRESULT 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 | |||
71 | HRESULT ExternalEngineSendEmbeddedProgress( | ||
72 | __in BURN_ENGINE_STATE* pEngineState, | ||
73 | __in const DWORD dwProgressPercentage, | ||
74 | __in const DWORD dwOverallProgressPercentage, | ||
75 | __out int* pnResult | ||
76 | ); | ||
77 | |||
78 | HRESULT 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 | |||
88 | HRESULT 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 | |||
95 | HRESULT 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 | |||
104 | HRESULT ExternalEngineSetVariableNumeric( | ||
105 | __in BURN_ENGINE_STATE* pEngineState, | ||
106 | __in_z LPCWSTR wzVariable, | ||
107 | __in const LONGLONG llValue | ||
108 | ); | ||
109 | |||
110 | HRESULT 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 | |||
117 | HRESULT ExternalEngineSetVariableVersion( | ||
118 | __in BURN_ENGINE_STATE* pEngineState, | ||
119 | __in_z LPCWSTR wzVariable, | ||
120 | __in_z_opt LPCWSTR wzValue | ||
121 | ); | ||
122 | |||
123 | void ExternalEngineCloseSplashScreen( | ||
124 | __in BURN_ENGINE_STATE* pEngineState | ||
125 | ); | ||
126 | |||
127 | HRESULT ExternalEngineCompareVersions( | ||
128 | __in_z LPCWSTR wzVersion1, | ||
129 | __in_z LPCWSTR wzVersion2, | ||
130 | __out int* pnResult | ||
131 | ); | ||
132 | |||
133 | HRESULT ExternalEngineDetect( | ||
134 | __in const DWORD dwThreadId, | ||
135 | __in_opt const HWND hwndParent | ||
136 | ); | ||
137 | |||
138 | HRESULT ExternalEnginePlan( | ||
139 | __in const DWORD dwThreadId, | ||
140 | __in const BOOTSTRAPPER_ACTION action | ||
141 | ); | ||
142 | |||
143 | HRESULT ExternalEngineElevate( | ||
144 | __in BURN_ENGINE_STATE* pEngineState, | ||
145 | __in const DWORD dwThreadId, | ||
146 | __in_opt const HWND hwndParent | ||
147 | ); | ||
148 | |||
149 | HRESULT ExternalEngineApply( | ||
150 | __in const DWORD dwThreadId, | ||
151 | __in_opt const HWND hwndParent | ||
152 | ); | ||
153 | |||
154 | HRESULT ExternalEngineQuit( | ||
155 | __in const DWORD dwThreadId, | ||
156 | __in const DWORD dwExitCode | ||
157 | ); | ||
158 | |||
159 | HRESULT 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 | |||
168 | HRESULT ExternalEngineSetUpdateSource( | ||
169 | __in BURN_ENGINE_STATE* pEngineState, | ||
170 | __in_z LPCWSTR wzUrl | ||
171 | ); | ||
172 | |||
173 | HRESULT 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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // function declarations | ||
11 | |||
12 | BOOL EngineInCleanRoom( | ||
13 | __in_z_opt LPCWSTR wzCommandLine | ||
14 | ); | ||
15 | |||
16 | HRESULT 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 | |||
6 | static DWORD vdwPackageSequence = 0; | ||
7 | static const DWORD LOG_OPEN_RETRY_COUNT = 3; | ||
8 | static const DWORD LOG_OPEN_RETRY_WAIT = 2000; | ||
9 | static 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 | |||
17 | static void CheckLoggingPolicy( | ||
18 | __out DWORD *pdwAttributes | ||
19 | ); | ||
20 | static HRESULT GetNonSessionSpecificTempFolder( | ||
21 | __deref_out_z LPWSTR* psczNonSessionTempFolder | ||
22 | ); | ||
23 | |||
24 | |||
25 | // function definitions | ||
26 | |||
27 | extern "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 | |||
177 | LExit: | ||
178 | ReleaseStr(sczLoggingBaseFolder); | ||
179 | StrSecureZeroFreeString(sczPrefixFormatted); | ||
180 | |||
181 | return hr; | ||
182 | } | ||
183 | |||
184 | extern "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 | |||
204 | LExit: | ||
205 | if (hEventLog) | ||
206 | { | ||
207 | ::CloseEventLog(hEventLog); | ||
208 | } | ||
209 | } | ||
210 | |||
211 | extern "C" void LoggingIncrementPackageSequence() | ||
212 | { | ||
213 | ++vdwPackageSequence; | ||
214 | } | ||
215 | |||
216 | extern "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 | |||
255 | LExit: | ||
256 | ReleaseStr(sczLogPath); | ||
257 | |||
258 | return hr; | ||
259 | } | ||
260 | |||
261 | extern "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 | |||
292 | LPCSTR 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 | |||
315 | extern "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 | |||
340 | extern "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 | |||
357 | extern "C" LPCSTR LoggingBoolToString( | ||
358 | __in BOOL f | ||
359 | ) | ||
360 | { | ||
361 | if (f) | ||
362 | { | ||
363 | return "Yes"; | ||
364 | } | ||
365 | |||
366 | return "No"; | ||
367 | } | ||
368 | |||
369 | extern "C" LPCSTR LoggingTrueFalseToString( | ||
370 | __in BOOL f | ||
371 | ) | ||
372 | { | ||
373 | if (f) | ||
374 | { | ||
375 | return "true"; | ||
376 | } | ||
377 | |||
378 | return "false"; | ||
379 | } | ||
380 | |||
381 | extern "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 | |||
402 | extern "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 | |||
427 | extern "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 | |||
448 | extern "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 | |||
473 | extern "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 | |||
496 | extern "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 | |||
517 | extern "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 | |||
535 | extern "C" LPCSTR LoggingPerMachineToString( | ||
536 | __in BOOL fPerMachine | ||
537 | ) | ||
538 | { | ||
539 | if (fPerMachine) | ||
540 | { | ||
541 | return "PerMachine"; | ||
542 | } | ||
543 | |||
544 | return "PerUser"; | ||
545 | } | ||
546 | |||
547 | extern "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 | |||
564 | extern "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 | |||
585 | extern "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 | |||
610 | extern "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 | |||
635 | extern "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 | |||
660 | extern "C" LPCSTR LoggingRollbackOrExecute( | ||
661 | __in BOOL fRollback | ||
662 | ) | ||
663 | { | ||
664 | return fRollback ? "rollback" : "execute"; | ||
665 | } | ||
666 | |||
667 | extern "C" LPWSTR LoggingStringOrUnknownIfNull( | ||
668 | __in LPCWSTR wz | ||
669 | ) | ||
670 | { | ||
671 | return wz ? wz : L"Unknown"; | ||
672 | } | ||
673 | |||
674 | |||
675 | // internal function declarations | ||
676 | |||
677 | static 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 | |||
712 | static 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 | |||
750 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // constants | ||
11 | |||
12 | enum BURN_LOGGING_STATE | ||
13 | { | ||
14 | BURN_LOGGING_STATE_CLOSED, | ||
15 | BURN_LOGGING_STATE_OPEN, | ||
16 | BURN_LOGGING_STATE_DISABLED, | ||
17 | }; | ||
18 | |||
19 | enum 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 | |||
29 | typedef 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 | |||
44 | HRESULT LoggingOpen( | ||
45 | __in BURN_LOGGING* pLog, | ||
46 | __in BURN_VARIABLES* pVariables, | ||
47 | __in BOOTSTRAPPER_DISPLAY display, | ||
48 | __in_z LPCWSTR wzBundleName | ||
49 | ); | ||
50 | |||
51 | void LoggingOpenFailed(); | ||
52 | |||
53 | void LoggingIncrementPackageSequence(); | ||
54 | |||
55 | HRESULT 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 | |||
64 | LPCSTR LoggingBurnActionToString( | ||
65 | __in BOOTSTRAPPER_ACTION action | ||
66 | ); | ||
67 | |||
68 | LPCSTR LoggingBurnMessageToString( | ||
69 | __in UINT message | ||
70 | ); | ||
71 | |||
72 | LPCSTR LoggingActionStateToString( | ||
73 | __in BOOTSTRAPPER_ACTION_STATE actionState | ||
74 | ); | ||
75 | |||
76 | LPCSTR LoggingDependencyActionToString( | ||
77 | BURN_DEPENDENCY_ACTION action | ||
78 | ); | ||
79 | |||
80 | LPCSTR LoggingBoolToString( | ||
81 | __in BOOL f | ||
82 | ); | ||
83 | |||
84 | LPCSTR LoggingTrueFalseToString( | ||
85 | __in BOOL f | ||
86 | ); | ||
87 | |||
88 | LPCSTR LoggingPackageStateToString( | ||
89 | __in BOOTSTRAPPER_PACKAGE_STATE packageState | ||
90 | ); | ||
91 | |||
92 | LPCSTR LoggingPackageRegistrationStateToString( | ||
93 | __in BOOL fCanAffectRegistration, | ||
94 | __in BURN_PACKAGE_REGISTRATION_STATE registrationState | ||
95 | ); | ||
96 | |||
97 | LPCSTR LoggingMsiFeatureStateToString( | ||
98 | __in BOOTSTRAPPER_FEATURE_STATE featureState | ||
99 | ); | ||
100 | |||
101 | LPCSTR LoggingMsiFeatureActionToString( | ||
102 | __in BOOTSTRAPPER_FEATURE_ACTION featureAction | ||
103 | ); | ||
104 | |||
105 | LPCSTR LoggingMsiInstallContext( | ||
106 | __in MSIINSTALLCONTEXT context | ||
107 | ); | ||
108 | |||
109 | LPCWSTR LoggingBurnMsiPropertyToString( | ||
110 | __in BURN_MSI_PROPERTY burnMsiProperty | ||
111 | ); | ||
112 | |||
113 | LPCSTR LoggingMspTargetActionToString( | ||
114 | __in BOOTSTRAPPER_ACTION_STATE action, | ||
115 | __in BURN_PATCH_SKIP_STATE skipState | ||
116 | ); | ||
117 | |||
118 | LPCSTR LoggingPerMachineToString( | ||
119 | __in BOOL fPerMachine | ||
120 | ); | ||
121 | |||
122 | LPCSTR LoggingRestartToString( | ||
123 | __in BOOTSTRAPPER_APPLY_RESTART restart | ||
124 | ); | ||
125 | |||
126 | LPCSTR LoggingResumeModeToString( | ||
127 | __in BURN_RESUME_MODE resumeMode | ||
128 | ); | ||
129 | |||
130 | LPCSTR LoggingRelationTypeToString( | ||
131 | __in BOOTSTRAPPER_RELATION_TYPE type | ||
132 | ); | ||
133 | |||
134 | LPCSTR LoggingRelatedOperationToString( | ||
135 | __in BOOTSTRAPPER_RELATED_OPERATION operation | ||
136 | ); | ||
137 | |||
138 | LPCSTR LoggingRequestStateToString( | ||
139 | __in BOOTSTRAPPER_REQUEST_STATE requestState | ||
140 | ); | ||
141 | |||
142 | LPCSTR LoggingRollbackOrExecute( | ||
143 | __in BOOL fRollback | ||
144 | ); | ||
145 | |||
146 | LPWSTR 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 | |||
6 | static HRESULT ParseFromXml( | ||
7 | __in IXMLDOMDocument* pixdDocument, | ||
8 | __in BURN_ENGINE_STATE* pEngineState | ||
9 | ); | ||
10 | |||
11 | // function definitions | ||
12 | |||
13 | extern "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 | |||
27 | LExit: | ||
28 | ReleaseObject(pixdDocument); | ||
29 | |||
30 | return hr; | ||
31 | } | ||
32 | |||
33 | extern "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 | |||
48 | LExit: | ||
49 | ReleaseObject(pixdDocument); | ||
50 | |||
51 | return hr; | ||
52 | } | ||
53 | |||
54 | static 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 | |||
159 | LExit: | ||
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 | |||
5 | interface IBurnPayload; // forward declare. | ||
6 | |||
7 | #if defined(__cplusplus) | ||
8 | extern "C" { | ||
9 | #endif | ||
10 | |||
11 | |||
12 | // function declarations | ||
13 | |||
14 | HRESULT ManifestLoadXmlFromFile( | ||
15 | __in LPCWSTR wzPath, | ||
16 | __in BURN_ENGINE_STATE* pEngineState | ||
17 | ); | ||
18 | |||
19 | HRESULT 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 | |||
15 | static HRESULT ParseRelatedMsiFromXml( | ||
16 | __in IXMLDOMNode* pixnRelatedMsi, | ||
17 | __in BURN_RELATED_MSI* pRelatedMsi | ||
18 | ); | ||
19 | static 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 | ); | ||
26 | static 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 | ); | ||
33 | static HRESULT EscapePropertyArgumentString( | ||
34 | __in LPCWSTR wzProperty, | ||
35 | __inout_z LPWSTR* psczEscapedValue, | ||
36 | __in BOOL fZeroOnRealloc | ||
37 | ); | ||
38 | static HRESULT ConcatFeatureActionProperties( | ||
39 | __in BURN_PACKAGE* pPackage, | ||
40 | __in BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions, | ||
41 | __inout_z LPWSTR* psczArguments | ||
42 | ); | ||
43 | static HRESULT ConcatPatchProperty( | ||
44 | __in BURN_PACKAGE* pPackage, | ||
45 | __in BOOL fRollback, | ||
46 | __inout_z LPWSTR* psczArguments | ||
47 | ); | ||
48 | static void RegisterSourceDirectory( | ||
49 | __in BURN_PACKAGE* pPackage, | ||
50 | __in_z LPCWSTR wzCacheDirectory | ||
51 | ); | ||
52 | |||
53 | |||
54 | // function definitions | ||
55 | |||
56 | extern "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 | |||
239 | LExit: | ||
240 | ReleaseObject(pixnNodes); | ||
241 | ReleaseObject(pixnNode); | ||
242 | ReleaseStr(scz); | ||
243 | |||
244 | return hr; | ||
245 | } | ||
246 | |||
247 | extern "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 | |||
315 | LExit: | ||
316 | ReleaseNullObject(pixnNodes); | ||
317 | ReleaseMem(pProperties); | ||
318 | |||
319 | return hr; | ||
320 | } | ||
321 | |||
322 | extern "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 | |||
400 | extern "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 | |||
428 | LExit: | ||
429 | return hr; | ||
430 | } | ||
431 | |||
432 | extern "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 | |||
706 | LExit: | ||
707 | ReleaseStr(sczInstalledLanguage); | ||
708 | ReleaseStr(sczInstalledVersion); | ||
709 | ReleaseVerutilVersion(pVersion); | ||
710 | |||
711 | return hr; | ||
712 | } | ||
713 | |||
714 | extern "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 | |||
745 | LExit: | ||
746 | return hr; | ||
747 | } | ||
748 | |||
749 | // | ||
750 | // PlanCalculate - calculates the execute and rollback state for the requested package state. | ||
751 | // | ||
752 | extern "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 | |||
905 | LExit: | ||
906 | return hr; | ||
907 | } | ||
908 | |||
909 | // | ||
910 | // PlanAdd - adds the calculated execute and rollback actions for the package. | ||
911 | // | ||
912 | extern "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 | |||
1002 | LExit: | ||
1003 | ReleaseMem(rgFeatureActions); | ||
1004 | ReleaseMem(rgRollbackFeatureActions); | ||
1005 | |||
1006 | return hr; | ||
1007 | } | ||
1008 | |||
1009 | extern "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 | |||
1028 | LExit: | ||
1029 | ReleaseMsi(hTransactionHandle); | ||
1030 | ReleaseHandle(hChangeOfOwnerEvent); | ||
1031 | |||
1032 | return hr; | ||
1033 | } | ||
1034 | |||
1035 | extern "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 | |||
1046 | LExit: | ||
1047 | |||
1048 | return hr; | ||
1049 | } | ||
1050 | |||
1051 | extern "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 | |||
1062 | LExit: | ||
1063 | |||
1064 | return hr; | ||
1065 | } | ||
1066 | |||
1067 | extern "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 | |||
1261 | LExit: | ||
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 | |||
1292 | extern "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 | |||
1322 | LExit: | ||
1323 | return hr; | ||
1324 | } | ||
1325 | |||
1326 | extern "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 | |||
1381 | LExit: | ||
1382 | StrSecureZeroFreeString(sczValue); | ||
1383 | StrSecureZeroFreeString(sczEscapedValue); | ||
1384 | StrSecureZeroFreeString(sczProperty); | ||
1385 | return hr; | ||
1386 | } | ||
1387 | |||
1388 | extern "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 | |||
1427 | extern "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 | |||
1503 | LExit: | ||
1504 | return; | ||
1505 | } | ||
1506 | |||
1507 | |||
1508 | // internal helper functions | ||
1509 | |||
1510 | static 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 | |||
1610 | LExit: | ||
1611 | ReleaseObject(pixnNodes); | ||
1612 | ReleaseObject(pixnNode); | ||
1613 | ReleaseStr(scz); | ||
1614 | |||
1615 | return hr; | ||
1616 | } | ||
1617 | |||
1618 | static 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 | |||
1675 | LExit: | ||
1676 | return hr; | ||
1677 | } | ||
1678 | |||
1679 | static 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 | |||
1746 | LExit: | ||
1747 | return hr; | ||
1748 | } | ||
1749 | |||
1750 | static 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 | |||
1796 | LExit: | ||
1797 | return hr; | ||
1798 | } | ||
1799 | |||
1800 | static 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 | |||
1938 | LExit: | ||
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 | |||
1950 | static 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 | |||
2005 | LExit: | ||
2006 | ReleaseStr(sczMspPath); | ||
2007 | ReleaseStr(sczCachedDirectory); | ||
2008 | ReleaseStr(sczPatches); | ||
2009 | return hr; | ||
2010 | } | ||
2011 | |||
2012 | static 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 | |||
2031 | LExit: | ||
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) | ||
12 | extern "C" { | ||
13 | #endif | ||
14 | |||
15 | |||
16 | // function declarations | ||
17 | |||
18 | HRESULT MsiEngineParsePackageFromXml( | ||
19 | __in IXMLDOMNode* pixnBundle, | ||
20 | __in BURN_PACKAGE* pPackage | ||
21 | ); | ||
22 | HRESULT MsiEngineParsePropertiesFromXml( | ||
23 | __in IXMLDOMNode* pixnPackage, | ||
24 | __out BURN_MSIPROPERTY** prgProperties, | ||
25 | __out DWORD* pcProperties | ||
26 | ); | ||
27 | void MsiEnginePackageUninitialize( | ||
28 | __in BURN_PACKAGE* pPackage | ||
29 | ); | ||
30 | HRESULT MsiEngineDetectInitialize( | ||
31 | __in BURN_PACKAGES* pPackages | ||
32 | ); | ||
33 | HRESULT MsiEngineDetectPackage( | ||
34 | __in BURN_PACKAGE* pPackage, | ||
35 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
36 | ); | ||
37 | HRESULT MsiEnginePlanInitializePackage( | ||
38 | __in BURN_PACKAGE* pPackage, | ||
39 | __in BURN_VARIABLES* pVariables, | ||
40 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
41 | ); | ||
42 | HRESULT MsiEnginePlanCalculatePackage( | ||
43 | __in BURN_PACKAGE* pPackage, | ||
44 | __in BOOL fInsideMsiTransaction | ||
45 | ); | ||
46 | HRESULT 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 | ); | ||
55 | HRESULT MsiEngineBeginTransaction( | ||
56 | __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary | ||
57 | ); | ||
58 | HRESULT MsiEngineCommitTransaction( | ||
59 | __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary | ||
60 | ); | ||
61 | HRESULT MsiEngineRollbackTransaction( | ||
62 | __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary | ||
63 | ); | ||
64 | HRESULT 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 | ); | ||
73 | HRESULT MsiEngineConcatActionProperty( | ||
74 | __in BURN_MSI_PROPERTY actionMsiProperty, | ||
75 | __deref_out_z LPWSTR* psczProperties | ||
76 | ); | ||
77 | HRESULT 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 | ); | ||
85 | HRESULT 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 | ); | ||
95 | void 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 | |||
11 | struct POSSIBLE_TARGETPRODUCT | ||
12 | { | ||
13 | WCHAR wzProductCode[39]; | ||
14 | LPWSTR pszLocalPackage; | ||
15 | MSIINSTALLCONTEXT context; | ||
16 | }; | ||
17 | |||
18 | // internal function declarations | ||
19 | |||
20 | static HRESULT GetPossibleTargetProductCodes( | ||
21 | __in BURN_PACKAGES* pPackages, | ||
22 | __deref_inout_ecount_opt(*pcPossibleTargetProductCodes) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProductCodes, | ||
23 | __inout DWORD* pcPossibleTargetProductCodes | ||
24 | ); | ||
25 | static 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 | ); | ||
32 | static 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 | ); | ||
39 | static HRESULT AddMsiChainedPatch( | ||
40 | __in BURN_PACKAGE* pPackage, | ||
41 | __in BURN_PACKAGE* pMspPackage, | ||
42 | __in DWORD dwMspTargetProductIndex, | ||
43 | __out DWORD* pdwChainedPatchIndex | ||
44 | ); | ||
45 | static HRESULT DeterminePatchChainedTarget( | ||
46 | __in BURN_PACKAGES* pPackages, | ||
47 | __in BURN_PACKAGE* pMspPackage, | ||
48 | __in LPCWSTR wzTargetProductCode, | ||
49 | __in DWORD dwMspTargetProductIndex | ||
50 | ); | ||
51 | static 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 | |||
67 | extern "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 | |||
86 | LExit: | ||
87 | |||
88 | return hr; | ||
89 | } | ||
90 | |||
91 | extern "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 | |||
119 | extern "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 | |||
191 | LExit: | ||
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 | |||
204 | extern "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 | |||
221 | LExit: | ||
222 | return hr; | ||
223 | } | ||
224 | |||
225 | extern "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 | |||
248 | LExit: | ||
249 | return hr; | ||
250 | } | ||
251 | |||
252 | extern "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 | |||
332 | LExit: | ||
333 | ReleaseStr(sczState); | ||
334 | |||
335 | return hr; | ||
336 | } | ||
337 | |||
338 | extern "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 | |||
362 | LExit: | ||
363 | return hr; | ||
364 | } | ||
365 | |||
366 | // | ||
367 | // PlanCalculate - calculates the execute and rollback state for the requested package state. | ||
368 | // | ||
369 | extern "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 | // | ||
501 | extern "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 | |||
550 | LExit: | ||
551 | |||
552 | return hr; | ||
553 | } | ||
554 | |||
555 | extern "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 | |||
689 | LExit: | ||
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 | |||
720 | extern "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 | |||
783 | LExit: | ||
784 | return; | ||
785 | } | ||
786 | |||
787 | extern "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 | |||
815 | LExit: | ||
816 | return; | ||
817 | } | ||
818 | |||
819 | |||
820 | // internal helper functions | ||
821 | |||
822 | static 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 | |||
922 | LExit: | ||
923 | ReleaseDict(sdUniquePossibleTargetProductCodes); | ||
924 | |||
925 | return hr; | ||
926 | } | ||
927 | |||
928 | static 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 | |||
982 | LExit: | ||
983 | ReleaseStr(pszLocalPackage); | ||
984 | |||
985 | return hr; | ||
986 | } | ||
987 | |||
988 | static 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 | |||
1015 | LExit: | ||
1016 | return hr; | ||
1017 | } | ||
1018 | |||
1019 | static 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; | ||
1037 | LExit: | ||
1038 | return hr; | ||
1039 | } | ||
1040 | |||
1041 | static 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 | |||
1079 | LExit: | ||
1080 | return hr; | ||
1081 | } | ||
1082 | |||
1083 | static 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 | |||
1195 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // constants | ||
11 | |||
12 | |||
13 | // structures | ||
14 | |||
15 | |||
16 | // typedefs | ||
17 | |||
18 | |||
19 | // function declarations | ||
20 | |||
21 | HRESULT MspEngineParsePackageFromXml( | ||
22 | __in IXMLDOMNode* pixnBundle, | ||
23 | __in BURN_PACKAGE* pPackage | ||
24 | ); | ||
25 | void MspEnginePackageUninitialize( | ||
26 | __in BURN_PACKAGE* pPackage | ||
27 | ); | ||
28 | HRESULT MspEngineDetectInitialize( | ||
29 | __in BURN_PACKAGES* pPackages | ||
30 | ); | ||
31 | HRESULT 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 | ); | ||
38 | HRESULT MspEngineAddMissingSlipstreamTarget( | ||
39 | __in BURN_PACKAGE* pMsiPackage, | ||
40 | __in BURN_SLIPSTREAM_MSP* pSlipstreamMsp | ||
41 | ); | ||
42 | HRESULT MspEngineDetectPackage( | ||
43 | __in BURN_PACKAGE* pPackage, | ||
44 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
45 | ); | ||
46 | HRESULT MspEnginePlanInitializePackage( | ||
47 | __in BURN_PACKAGE* pPackage, | ||
48 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
49 | ); | ||
50 | HRESULT MspEnginePlanCalculatePackage( | ||
51 | __in BURN_PACKAGE* pPackage, | ||
52 | __in BOOL fInsideMsiTransaction | ||
53 | ); | ||
54 | HRESULT 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 | ); | ||
63 | HRESULT 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 | ); | ||
72 | void MspEngineUpdateInstallRegistrationState( | ||
73 | __in BURN_EXECUTE_ACTION* pAction, | ||
74 | __in HRESULT hrExecute, | ||
75 | __in BOOL fInsideMsiTransaction | ||
76 | ); | ||
77 | void 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 | ||
13 | static HRESULT EnsureWUServiceEnabled( | ||
14 | __in BOOL fStopWusaService, | ||
15 | __out SC_HANDLE* pschWu, | ||
16 | __out BOOL* pfPreviouslyDisabled | ||
17 | ); | ||
18 | static HRESULT SetServiceStartType( | ||
19 | __in SC_HANDLE sch, | ||
20 | __in DWORD stratType | ||
21 | ); | ||
22 | static HRESULT StopWUService( | ||
23 | __in SC_HANDLE schWu | ||
24 | ); | ||
25 | |||
26 | |||
27 | extern "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 | |||
42 | LExit: | ||
43 | return hr; | ||
44 | } | ||
45 | |||
46 | extern "C" void MsuEnginePackageUninitialize( | ||
47 | __in BURN_PACKAGE* pPackage | ||
48 | ) | ||
49 | { | ||
50 | ReleaseNullStr(pPackage->Msu.sczKB); | ||
51 | ReleaseNullStr(pPackage->Msu.sczDetectCondition); | ||
52 | } | ||
53 | |||
54 | extern "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 | |||
77 | LExit: | ||
78 | return hr; | ||
79 | } | ||
80 | |||
81 | // | ||
82 | // PlanCalculate - calculates the execute and rollback state for the requested package state. | ||
83 | // | ||
84 | extern "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 | |||
184 | LExit: | ||
185 | return hr; | ||
186 | } | ||
187 | |||
188 | // | ||
189 | // PlanAdd - adds the calculated execute and rollback actions for the package. | ||
190 | // | ||
191 | extern "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 | |||
238 | LExit: | ||
239 | return hr; | ||
240 | } | ||
241 | |||
242 | extern "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 | |||
396 | LExit: | ||
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 | |||
418 | extern "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 | |||
439 | LExit: | ||
440 | return; | ||
441 | } | ||
442 | |||
443 | static 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 | |||
491 | LExit: | ||
492 | ReleaseMem(pConfig); | ||
493 | ReleaseServiceHandle(schWu); | ||
494 | ReleaseServiceHandle(schSCM); | ||
495 | |||
496 | return hr; | ||
497 | } | ||
498 | |||
499 | static 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 | |||
511 | LExit: | ||
512 | return hr; | ||
513 | } | ||
514 | |||
515 | static 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 | |||
527 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // function declarations | ||
11 | |||
12 | HRESULT MsuEngineParsePackageFromXml( | ||
13 | __in IXMLDOMNode* pixnMsiPackage, | ||
14 | __in BURN_PACKAGE* pPackage | ||
15 | ); | ||
16 | void MsuEnginePackageUninitialize( | ||
17 | __in BURN_PACKAGE* pPackage | ||
18 | ); | ||
19 | HRESULT MsuEngineDetectPackage( | ||
20 | __in BURN_PACKAGE* pPackage, | ||
21 | __in BURN_VARIABLES* pVariables | ||
22 | ); | ||
23 | HRESULT MsuEnginePlanCalculatePackage( | ||
24 | __in BURN_PACKAGE* pPackage | ||
25 | ); | ||
26 | HRESULT 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 | ); | ||
33 | HRESULT 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 | ); | ||
42 | void 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 | |||
5 | static 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 | |||
25 | static 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 | |||
92 | LExit: | ||
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 | |||
110 | static 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 | |||
124 | static 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 | |||
138 | static 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 | |||
161 | LExit: | ||
162 | ::ReleaseMutex(pChainer->hMutex); | ||
163 | |||
164 | return hr; | ||
165 | } | ||
166 | |||
167 | static 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 | |||
187 | static 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 | |||
213 | static 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 | |||
244 | LExit: | ||
245 | ReleaseMem(rgwzFiles); | ||
246 | |||
247 | return hr; | ||
248 | } | ||
249 | |||
250 | static 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 | |||
274 | static 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 | |||
294 | static 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 | |||
325 | LExit: | ||
326 | ReleaseMem(pBuffer); | ||
327 | |||
328 | return hr; | ||
329 | } | ||
330 | |||
331 | extern "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 | |||
409 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | struct 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 | |||
31 | struct 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 | |||
77 | struct NetFxApplication | ||
78 | { | ||
79 | WCHAR szName[MAX_PATH]; | ||
80 | DWORD dwPid; | ||
81 | }; | ||
82 | |||
83 | struct NetFxCloseApplications | ||
84 | { | ||
85 | DWORD dwApplicationsSize; | ||
86 | NetFxApplication applications[1]; | ||
87 | }; | ||
88 | |||
89 | HRESULT 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 | |||
8 | static HRESULT ParsePayloadRefsFromXml( | ||
9 | __in BURN_PACKAGE* pPackage, | ||
10 | __in BURN_PAYLOADS* pPayloads, | ||
11 | __in IXMLDOMNode* pixnPackage | ||
12 | ); | ||
13 | static HRESULT ParsePatchTargetCode( | ||
14 | __in BURN_PACKAGES* pPackages, | ||
15 | __in IXMLDOMNode* pixnBundle | ||
16 | ); | ||
17 | static 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 | |||
26 | extern "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 | |||
324 | LExit: | ||
325 | ReleaseObject(pixnNodes); | ||
326 | ReleaseObject(pixnNode); | ||
327 | ReleaseBSTR(bstrNodeName); | ||
328 | ReleaseStr(scz); | ||
329 | |||
330 | return hr; | ||
331 | } | ||
332 | |||
333 | extern "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 | |||
371 | extern "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 | |||
410 | extern "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 | |||
432 | LExit: | ||
433 | return hr; | ||
434 | } | ||
435 | |||
436 | |||
437 | extern "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 | |||
459 | LExit: | ||
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 | *********************************************************************/ | ||
473 | extern "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 | |||
511 | LExit: | ||
512 | return hr; | ||
513 | } | ||
514 | |||
515 | extern "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 | |||
537 | LExit: | ||
538 | return hr; | ||
539 | } | ||
540 | |||
541 | |||
542 | // internal function declarations | ||
543 | |||
544 | static 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 | |||
599 | LExit: | ||
600 | ReleaseObject(pixnNodes); | ||
601 | ReleaseObject(pixnNode); | ||
602 | ReleaseStr(sczId); | ||
603 | |||
604 | return hr; | ||
605 | } | ||
606 | |||
607 | static 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 | |||
660 | LExit: | ||
661 | ReleaseBSTR(bstrNodeText); | ||
662 | ReleaseObject(pixnNode); | ||
663 | ReleaseObject(pixnNodes); | ||
664 | |||
665 | return hr; | ||
666 | } | ||
667 | |||
668 | static 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 | |||
690 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | struct _BURN_RELATED_BUNDLES; | ||
10 | typedef _BURN_RELATED_BUNDLES BURN_RELATED_BUNDLES; | ||
11 | |||
12 | struct _BURN_PACKAGE; | ||
13 | typedef _BURN_PACKAGE BURN_PACKAGE; | ||
14 | |||
15 | // constants | ||
16 | |||
17 | const DWORD BURN_PACKAGE_INVALID_PATCH_INDEX = 0x80000000; | ||
18 | |||
19 | enum 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 | |||
28 | enum 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 | |||
35 | enum 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 | |||
44 | enum BURN_DEPENDENCY_ACTION | ||
45 | { | ||
46 | BURN_DEPENDENCY_ACTION_NONE, | ||
47 | BURN_DEPENDENCY_ACTION_REGISTER, | ||
48 | BURN_DEPENDENCY_ACTION_UNREGISTER, | ||
49 | }; | ||
50 | |||
51 | enum 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 | |||
58 | enum 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 | |||
69 | enum 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 | |||
77 | enum 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 | |||
86 | typedef 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 | |||
93 | typedef 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 | |||
101 | typedef 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 | |||
123 | typedef 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 | |||
131 | typedef 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 | |||
149 | typedef 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 | |||
165 | typedef struct _BURN_CHAINED_PATCH | ||
166 | { | ||
167 | BURN_PACKAGE* pMspPackage; | ||
168 | DWORD dwMspTargetProductIndex; // index into the Msp.rgTargetProducts | ||
169 | } BURN_CHAINED_PATCH; | ||
170 | |||
171 | typedef 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 | |||
180 | typedef 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 | |||
191 | typedef 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 | |||
200 | typedef struct _BURN_PATCH_TARGETCODE | ||
201 | { | ||
202 | LPWSTR sczTargetCode; | ||
203 | BURN_PATCH_TARGETCODE_TYPE type; | ||
204 | } BURN_PATCH_TARGETCODE; | ||
205 | |||
206 | typedef 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 | |||
325 | typedef 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 | |||
345 | HRESULT PackagesParseFromXml( | ||
346 | __in BURN_PACKAGES* pPackages, | ||
347 | __in BURN_PAYLOADS* pPayloads, | ||
348 | __in IXMLDOMNode* pixnBundle | ||
349 | ); | ||
350 | void PackageUninitialize( | ||
351 | __in BURN_PACKAGE* pPackage | ||
352 | ); | ||
353 | void PackagesUninitialize( | ||
354 | __in BURN_PACKAGES* pPackages | ||
355 | ); | ||
356 | HRESULT PackageFindById( | ||
357 | __in BURN_PACKAGES* pPackages, | ||
358 | __in_z LPCWSTR wzId, | ||
359 | __out BURN_PACKAGE** ppPackage | ||
360 | ); | ||
361 | HRESULT PackageFindRelatedById( | ||
362 | __in BURN_RELATED_BUNDLES* pRelatedBundles, | ||
363 | __in_z LPCWSTR wzId, | ||
364 | __out BURN_PACKAGE** ppPackage | ||
365 | ); | ||
366 | HRESULT PackageGetProperty( | ||
367 | __in const BURN_PACKAGE* pPackage, | ||
368 | __in_z LPCWSTR wzProperty, | ||
369 | __out_z_opt LPWSTR* psczValue | ||
370 | ); | ||
371 | HRESULT 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 | |||
8 | static 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 | |||
18 | extern "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 | |||
150 | LExit: | ||
151 | ReleaseObject(pixnNodes); | ||
152 | ReleaseObject(pixnNode); | ||
153 | ReleaseStr(scz); | ||
154 | |||
155 | return hr; | ||
156 | } | ||
157 | |||
158 | extern "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 | |||
176 | extern "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 | |||
193 | extern "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 | |||
251 | LExit: | ||
252 | ReleaseStr(sczStreamName); | ||
253 | ReleaseStr(sczDirectory); | ||
254 | |||
255 | return hr; | ||
256 | } | ||
257 | |||
258 | extern "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 | |||
280 | LExit: | ||
281 | return hr; | ||
282 | } | ||
283 | |||
284 | extern "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 | |||
309 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // constants | ||
11 | |||
12 | enum BURN_PAYLOAD_PACKAGING | ||
13 | { | ||
14 | BURN_PAYLOAD_PACKAGING_NONE, | ||
15 | BURN_PAYLOAD_PACKAGING_EMBEDDED, | ||
16 | BURN_PAYLOAD_PACKAGING_EXTERNAL, | ||
17 | }; | ||
18 | |||
19 | enum 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 | |||
29 | typedef 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 | |||
52 | typedef struct _BURN_PAYLOADS | ||
53 | { | ||
54 | BURN_PAYLOAD* rgPayloads; | ||
55 | DWORD cPayloads; | ||
56 | } BURN_PAYLOADS; | ||
57 | |||
58 | typedef 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 | |||
67 | typedef 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 | |||
76 | HRESULT PayloadsParseFromXml( | ||
77 | __in BURN_PAYLOADS* pPayloads, | ||
78 | __in_opt BURN_CONTAINERS* pContainers, | ||
79 | __in_opt BURN_PAYLOAD_GROUP* pLayoutPayloads, | ||
80 | __in IXMLDOMNode* pixnBundle | ||
81 | ); | ||
82 | void PayloadUninitialize( | ||
83 | __in BURN_PAYLOAD* pPayload | ||
84 | ); | ||
85 | void PayloadsUninitialize( | ||
86 | __in BURN_PAYLOADS* pPayloads | ||
87 | ); | ||
88 | HRESULT PayloadExtractUXContainer( | ||
89 | __in BURN_PAYLOADS* pPayloads, | ||
90 | __in BURN_CONTAINER_CONTEXT* pContainerContext, | ||
91 | __in_z LPCWSTR wzTargetDir | ||
92 | ); | ||
93 | HRESULT PayloadFindById( | ||
94 | __in BURN_PAYLOADS* pPayloads, | ||
95 | __in_z LPCWSTR wzId, | ||
96 | __out BURN_PAYLOAD** ppPayload | ||
97 | ); | ||
98 | HRESULT 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 | |||
5 | static const DWORD PIPE_64KB = 64 * 1024; | ||
6 | static const DWORD PIPE_WAIT_FOR_CONNECTION = 100; // wait a 10th of a second, | ||
7 | static const DWORD PIPE_RETRY_FOR_CONNECTION = 1800; // for up to 3 minutes. | ||
8 | |||
9 | static const LPCWSTR PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls"; | ||
10 | static const LPCWSTR CACHE_PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls.Cache"; | ||
11 | |||
12 | static 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 | ); | ||
19 | static void FreePipeMessage( | ||
20 | __in BURN_PIPE_MESSAGE *pMsg | ||
21 | ); | ||
22 | static HRESULT WritePipeMessage( | ||
23 | __in HANDLE hPipe, | ||
24 | __in DWORD dwMessage, | ||
25 | __in_bcount_opt(cbData) LPVOID pvData, | ||
26 | __in SIZE_T cbData | ||
27 | ); | ||
28 | static HRESULT GetPipeMessage( | ||
29 | __in HANDLE hPipe, | ||
30 | __in BURN_PIPE_MESSAGE* pMsg | ||
31 | ); | ||
32 | static 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 | *******************************************************************/ | ||
44 | void 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 | *******************************************************************/ | ||
57 | void 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 | *******************************************************************/ | ||
76 | extern "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 | |||
97 | LExit: | ||
98 | return hr; | ||
99 | } | ||
100 | |||
101 | /******************************************************************* | ||
102 | PipePumpMessages - | ||
103 | |||
104 | *******************************************************************/ | ||
105 | extern "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 | |||
185 | LExit: | ||
186 | ReleaseStr(sczMessage); | ||
187 | FreePipeMessage(&msg); | ||
188 | |||
189 | return hr; | ||
190 | } | ||
191 | |||
192 | /******************************************************************* | ||
193 | PipeCreateNameAndSecret - | ||
194 | |||
195 | *******************************************************************/ | ||
196 | extern "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 | |||
225 | LExit: | ||
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 | *******************************************************************/ | ||
236 | extern "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 | |||
303 | LExit: | ||
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 | *******************************************************************/ | ||
322 | const LPCWSTR BURN_COMMANDLINE_SWITCH_UNELEVATED = L"burn.unelevated"; | ||
323 | HRESULT 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 | |||
370 | LExit: | ||
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 | *******************************************************************/ | ||
384 | extern "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 | |||
411 | LExit: | ||
412 | ReleaseHandle(hProcess); | ||
413 | ReleaseStr(sczParameters); | ||
414 | |||
415 | return hr; | ||
416 | } | ||
417 | |||
418 | /******************************************************************* | ||
419 | PipeWaitForChildConnect - | ||
420 | |||
421 | *******************************************************************/ | ||
422 | extern "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 | |||
510 | LExit: | ||
511 | return hr; | ||
512 | } | ||
513 | |||
514 | /******************************************************************* | ||
515 | PipeTerminateChildProcess - | ||
516 | |||
517 | *******************************************************************/ | ||
518 | extern "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 | |||
574 | LExit: | ||
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 | *******************************************************************/ | ||
583 | extern "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 | |||
646 | LExit: | ||
647 | ReleaseStr(sczPipeName); | ||
648 | |||
649 | return hr; | ||
650 | } | ||
651 | |||
652 | |||
653 | static 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 | |||
687 | LExit: | ||
688 | ReleaseMem(pv); | ||
689 | return hr; | ||
690 | } | ||
691 | |||
692 | static 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 | |||
703 | static 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 | |||
721 | LExit: | ||
722 | ReleaseMem(pv); | ||
723 | return hr; | ||
724 | } | ||
725 | |||
726 | static 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 | |||
755 | LExit: | ||
756 | if (!pMsg->fAllocatedData && pMsg->pvData) | ||
757 | { | ||
758 | MemFree(pMsg->pvData); | ||
759 | } | ||
760 | |||
761 | return hr; | ||
762 | } | ||
763 | |||
764 | static 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 | |||
818 | LExit: | ||
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 | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | typedef 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 | |||
20 | typedef 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 | |||
27 | typedef struct _BURN_PIPE_MESSAGE | ||
28 | { | ||
29 | DWORD dwMessage; | ||
30 | SIZE_T cbData; | ||
31 | |||
32 | BOOL fAllocatedData; | ||
33 | LPVOID pvData; | ||
34 | } BURN_PIPE_MESSAGE; | ||
35 | |||
36 | typedef struct _BURN_PIPE_RESULT | ||
37 | { | ||
38 | DWORD dwResult; | ||
39 | BOOL fRestart; | ||
40 | } BURN_PIPE_RESULT; | ||
41 | |||
42 | |||
43 | typedef 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. | ||
51 | void PipeConnectionInitialize( | ||
52 | __in BURN_PIPE_CONNECTION* pConnection | ||
53 | ); | ||
54 | void PipeConnectionUninitialize( | ||
55 | __in BURN_PIPE_CONNECTION* pConnection | ||
56 | ); | ||
57 | HRESULT 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 | ); | ||
66 | HRESULT 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. | ||
74 | HRESULT PipeCreateNameAndSecret( | ||
75 | __out_z LPWSTR *psczConnectionName, | ||
76 | __out_z LPWSTR *psczSecret | ||
77 | ); | ||
78 | HRESULT PipeCreatePipes( | ||
79 | __in BURN_PIPE_CONNECTION* pConnection, | ||
80 | __in BOOL fCreateCachePipe, | ||
81 | __out HANDLE* phEvent | ||
82 | ); | ||
83 | HRESULT PipeLaunchParentProcess( | ||
84 | __in LPCWSTR wzCommandLine, | ||
85 | __in int nCmdShow, | ||
86 | __in_z LPWSTR sczConnectionName, | ||
87 | __in_z LPWSTR sczSecret, | ||
88 | __in BOOL fDisableUnelevate | ||
89 | ); | ||
90 | HRESULT PipeLaunchChildProcess( | ||
91 | __in_z LPCWSTR wzExecutablePath, | ||
92 | __in BURN_PIPE_CONNECTION* pConnection, | ||
93 | __in BOOL fElevate, | ||
94 | __in_opt HWND hwndParent | ||
95 | ); | ||
96 | HRESULT PipeWaitForChildConnect( | ||
97 | __in BURN_PIPE_CONNECTION* pConnection | ||
98 | ); | ||
99 | HRESULT PipeTerminateChildProcess( | ||
100 | __in BURN_PIPE_CONNECTION* pConnection, | ||
101 | __in DWORD dwParentExitCode, | ||
102 | __in BOOL fRestart | ||
103 | ); | ||
104 | |||
105 | // Child functions. | ||
106 | HRESULT 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 | |||
12 | static void UninitializeRegistrationAction( | ||
13 | __in BURN_DEPENDENT_REGISTRATION_ACTION* pAction | ||
14 | ); | ||
15 | static void UninitializeCacheAction( | ||
16 | __in BURN_CACHE_ACTION* pCacheAction | ||
17 | ); | ||
18 | static void ResetPlannedContainerState( | ||
19 | __in BURN_CONTAINER* pContainer | ||
20 | ); | ||
21 | static void ResetPlannedPayloadsState( | ||
22 | __in BURN_PAYLOADS* pPayloads | ||
23 | ); | ||
24 | static void ResetPlannedPayloadGroupState( | ||
25 | __in BURN_PAYLOAD_GROUP* pPayloadGroup | ||
26 | ); | ||
27 | static void ResetPlannedPackageState( | ||
28 | __in BURN_PACKAGE* pPackage | ||
29 | ); | ||
30 | static void ResetPlannedRollbackBoundaryState( | ||
31 | __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary | ||
32 | ); | ||
33 | static 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 | ); | ||
44 | static 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 | ); | ||
51 | static 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 | ); | ||
62 | static HRESULT ProcessPackageRollbackBoundary( | ||
63 | __in BURN_PLAN* pPlan, | ||
64 | __in_opt BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary, | ||
65 | __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary | ||
66 | ); | ||
67 | static HRESULT GetActionDefaultRequestState( | ||
68 | __in BOOTSTRAPPER_ACTION action, | ||
69 | __in BOOL fPermanent, | ||
70 | __in BOOTSTRAPPER_PACKAGE_STATE currentState, | ||
71 | __out BOOTSTRAPPER_REQUEST_STATE* pRequestState | ||
72 | ); | ||
73 | static 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 | ); | ||
79 | static HRESULT AddCachePackage( | ||
80 | __in BURN_PLAN* pPlan, | ||
81 | __in BURN_PACKAGE* pPackage, | ||
82 | __out HANDLE* phSyncpointEvent | ||
83 | ); | ||
84 | static HRESULT AddCachePackageHelper( | ||
85 | __in BURN_PLAN* pPlan, | ||
86 | __in BURN_PACKAGE* pPackage, | ||
87 | __out HANDLE* phSyncpointEvent | ||
88 | ); | ||
89 | static HRESULT AddCacheSlipstreamMsps( | ||
90 | __in BURN_PLAN* pPlan, | ||
91 | __in BURN_PACKAGE* pPackage | ||
92 | ); | ||
93 | static BOOL AlreadyPlannedCachePackage( | ||
94 | __in BURN_PLAN* pPlan, | ||
95 | __in_z LPCWSTR wzPackageId, | ||
96 | __out HANDLE* phSyncpointEvent | ||
97 | ); | ||
98 | static DWORD GetNextCheckpointId( | ||
99 | __in BURN_PLAN* pPlan | ||
100 | ); | ||
101 | static HRESULT AppendCacheAction( | ||
102 | __in BURN_PLAN* pPlan, | ||
103 | __out BURN_CACHE_ACTION** ppCacheAction | ||
104 | ); | ||
105 | static HRESULT AppendRollbackCacheAction( | ||
106 | __in BURN_PLAN* pPlan, | ||
107 | __out BURN_CACHE_ACTION** ppCacheAction | ||
108 | ); | ||
109 | static HRESULT ProcessPayloadGroup( | ||
110 | __in BURN_PLAN* pPlan, | ||
111 | __in BURN_PAYLOAD_GROUP* pPayloadGroup | ||
112 | ); | ||
113 | static void RemoveUnnecessaryActions( | ||
114 | __in BOOL fExecute, | ||
115 | __in BURN_EXECUTE_ACTION* rgActions, | ||
116 | __in DWORD cActions | ||
117 | ); | ||
118 | static void FinalizePatchActions( | ||
119 | __in BOOL fExecute, | ||
120 | __in BURN_EXECUTE_ACTION* rgActions, | ||
121 | __in DWORD cActions | ||
122 | ); | ||
123 | static void CalculateExpectedRegistrationStates( | ||
124 | __in BURN_PACKAGE* rgPackages, | ||
125 | __in DWORD cPackages | ||
126 | ); | ||
127 | static HRESULT PlanDependencyActions( | ||
128 | __in BOOL fBundlePerMachine, | ||
129 | __in BURN_PLAN* pPlan, | ||
130 | __in BURN_PACKAGE* pPackage | ||
131 | ); | ||
132 | static HRESULT CalculateExecuteActions( | ||
133 | __in BURN_PACKAGE* pPackage, | ||
134 | __in_opt BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary | ||
135 | ); | ||
136 | static BOOL NeedsCache( | ||
137 | __in BURN_PACKAGE* pPackage, | ||
138 | __in BOOL fExecute | ||
139 | ); | ||
140 | static BOOL ForceCache( | ||
141 | __in BURN_PLAN* pPlan, | ||
142 | __in BURN_PACKAGE* pPackage | ||
143 | ); | ||
144 | |||
145 | // function definitions | ||
146 | |||
147 | extern "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 | |||
269 | extern "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 | |||
301 | extern "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 | |||
311 | LExit: | ||
312 | return hr; | ||
313 | } | ||
314 | |||
315 | extern "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 | |||
374 | LExit: | ||
375 | return hr; | ||
376 | } | ||
377 | |||
378 | extern "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 | |||
432 | LExit: | ||
433 | ReleaseStr(sczExecutablePath); | ||
434 | |||
435 | return hr; | ||
436 | } | ||
437 | |||
438 | extern "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 | |||
500 | LExit: | ||
501 | return hr; | ||
502 | } | ||
503 | |||
504 | extern "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 | |||
521 | extern "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 | |||
708 | LExit: | ||
709 | ReleaseDict(sdBundleDependents); | ||
710 | ReleaseDict(sdIgnoreDependents); | ||
711 | |||
712 | return hr; | ||
713 | } | ||
714 | |||
715 | extern "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 | |||
733 | LExit: | ||
734 | return hr; | ||
735 | } | ||
736 | |||
737 | extern "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 | |||
753 | LExit: | ||
754 | return hr; | ||
755 | } | ||
756 | |||
757 | static 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 | |||
844 | LExit: | ||
845 | return hr; | ||
846 | } | ||
847 | |||
848 | static 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 | |||
891 | LExit: | ||
892 | if (fBeginCalled) | ||
893 | { | ||
894 | UserExperienceOnPlanPackageComplete(pUX, pPackage->sczId, hr, pPackage->requested); | ||
895 | } | ||
896 | |||
897 | return hr; | ||
898 | } | ||
899 | |||
900 | static 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 | |||
961 | LExit: | ||
962 | return hr; | ||
963 | } | ||
964 | |||
965 | static 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 | |||
990 | LExit: | ||
991 | return hr; | ||
992 | } | ||
993 | |||
994 | extern "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 | |||
1042 | LExit: | ||
1043 | return hr; | ||
1044 | } | ||
1045 | |||
1046 | extern "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 | |||
1065 | LExit: | ||
1066 | return hr; | ||
1067 | } | ||
1068 | |||
1069 | extern "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 | |||
1172 | LExit: | ||
1173 | return hr; | ||
1174 | } | ||
1175 | |||
1176 | extern "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 | |||
1237 | LExit: | ||
1238 | return hr; | ||
1239 | } | ||
1240 | |||
1241 | extern "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 | |||
1326 | LExit: | ||
1327 | ReleaseDict(sdAncestors); | ||
1328 | ReleaseStrArray(rgsczAncestors, cAncestors); | ||
1329 | |||
1330 | return hr; | ||
1331 | } | ||
1332 | |||
1333 | extern "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 | |||
1496 | LExit: | ||
1497 | ReleaseDict(sdProviderKeys); | ||
1498 | ReleaseStr(sczIgnoreDependencies); | ||
1499 | |||
1500 | return hr; | ||
1501 | } | ||
1502 | |||
1503 | extern "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 | |||
1520 | extern "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 | |||
1581 | LExit: | ||
1582 | return hr; | ||
1583 | } | ||
1584 | |||
1585 | extern "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 | |||
1609 | LExit: | ||
1610 | return hr; | ||
1611 | } | ||
1612 | |||
1613 | extern "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 | |||
1637 | LExit: | ||
1638 | return hr; | ||
1639 | } | ||
1640 | |||
1641 | extern "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 | |||
1655 | LExit: | ||
1656 | return hr; | ||
1657 | } | ||
1658 | |||
1659 | extern "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 | |||
1673 | LExit: | ||
1674 | return hr; | ||
1675 | } | ||
1676 | |||
1677 | extern "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 | |||
1690 | LExit: | ||
1691 | return hr; | ||
1692 | } | ||
1693 | |||
1694 | extern "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 | |||
1707 | LExit: | ||
1708 | return hr; | ||
1709 | } | ||
1710 | |||
1711 | extern "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 | |||
1749 | LExit: | ||
1750 | return hr; | ||
1751 | } | ||
1752 | |||
1753 | extern "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 | |||
1778 | LExit: | ||
1779 | return hr; | ||
1780 | } | ||
1781 | |||
1782 | /******************************************************************* | ||
1783 | PlanSetResumeCommand - Initializes resume command string | ||
1784 | |||
1785 | *******************************************************************/ | ||
1786 | extern "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 | |||
1799 | LExit: | ||
1800 | return hr; | ||
1801 | } | ||
1802 | |||
1803 | |||
1804 | // internal function definitions | ||
1805 | |||
1806 | static 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 | |||
1815 | static 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 | |||
1832 | static 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 | |||
1843 | static 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 | |||
1857 | static 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 | |||
1870 | static 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 | |||
1931 | static void ResetPlannedRollbackBoundaryState( | ||
1932 | __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary | ||
1933 | ) | ||
1934 | { | ||
1935 | pRollbackBoundary->fActiveTransaction = FALSE; | ||
1936 | ReleaseNullStr(pRollbackBoundary->sczLogPath); | ||
1937 | } | ||
1938 | |||
1939 | static 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 | |||
1999 | LExit: | ||
2000 | return hr; | ||
2001 | } | ||
2002 | |||
2003 | static 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 | |||
2044 | LExit: | ||
2045 | return hr; | ||
2046 | } | ||
2047 | |||
2048 | static 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 | |||
2066 | LExit: | ||
2067 | return hr; | ||
2068 | } | ||
2069 | |||
2070 | static 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 | |||
2131 | LExit: | ||
2132 | return hr; | ||
2133 | } | ||
2134 | |||
2135 | static 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 | |||
2154 | LExit: | ||
2155 | return hr; | ||
2156 | } | ||
2157 | |||
2158 | static 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 | |||
2188 | static DWORD GetNextCheckpointId( | ||
2189 | __in BURN_PLAN* pPlan | ||
2190 | ) | ||
2191 | { | ||
2192 | return ++pPlan->dwNextCheckpointId; | ||
2193 | } | ||
2194 | |||
2195 | static 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 | |||
2208 | LExit: | ||
2209 | return hr; | ||
2210 | } | ||
2211 | |||
2212 | static 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 | |||
2225 | LExit: | ||
2226 | return hr; | ||
2227 | } | ||
2228 | |||
2229 | static 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 | |||
2275 | LExit: | ||
2276 | return hr; | ||
2277 | } | ||
2278 | |||
2279 | static 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 | |||
2312 | static 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 | |||
2383 | static 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 | |||
2452 | static 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 | |||
2469 | LExit: | ||
2470 | return hr; | ||
2471 | } | ||
2472 | |||
2473 | static 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 | |||
2505 | LExit: | ||
2506 | return hr; | ||
2507 | } | ||
2508 | |||
2509 | static 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 | |||
2525 | static 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 | |||
2534 | static 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 | |||
2573 | static 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 | |||
2650 | extern "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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // constants | ||
11 | |||
12 | const DWORD BURN_PLAN_INVALID_ACTION_INDEX = 0x80000000; | ||
13 | |||
14 | enum 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 | |||
22 | enum 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 | |||
29 | enum 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 | |||
36 | enum 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 | |||
47 | enum 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 | |||
64 | enum 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 | |||
74 | typedef 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 | |||
81 | typedef 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 | |||
89 | typedef 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 | |||
97 | typedef 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 | |||
132 | typedef 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 | |||
139 | typedef struct _BURN_EXECUTE_ACTION_CHECKPOINT | ||
140 | { | ||
141 | DWORD dwId; | ||
142 | BURN_ROLLBACK_BOUNDARY* pActiveRollbackBoundary; | ||
143 | } BURN_EXECUTE_ACTION_CHECKPOINT; | ||
144 | |||
145 | typedef 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 | |||
224 | typedef struct _BURN_CLEAN_ACTION | ||
225 | { | ||
226 | BURN_PACKAGE* pPackage; | ||
227 | } BURN_CLEAN_ACTION; | ||
228 | |||
229 | typedef 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 | |||
295 | void PlanReset( | ||
296 | __in BURN_PLAN* pPlan, | ||
297 | __in BURN_CONTAINERS* pContainers, | ||
298 | __in BURN_PACKAGES* pPackages, | ||
299 | __in BURN_PAYLOAD_GROUP* pLayoutPayloads | ||
300 | ); | ||
301 | void PlanUninitializeExecuteAction( | ||
302 | __in BURN_EXECUTE_ACTION* pExecuteAction | ||
303 | ); | ||
304 | HRESULT PlanSetVariables( | ||
305 | __in BOOTSTRAPPER_ACTION action, | ||
306 | __in BURN_VARIABLES* pVariables | ||
307 | ); | ||
308 | HRESULT 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 | ); | ||
317 | HRESULT 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 | ); | ||
324 | HRESULT 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 | ); | ||
331 | HRESULT 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 | ); | ||
340 | HRESULT 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 | ); | ||
347 | HRESULT 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 | ); | ||
356 | HRESULT 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 | ); | ||
365 | HRESULT PlanLayoutContainer( | ||
366 | __in BURN_PLAN* pPlan, | ||
367 | __in BURN_CONTAINER* pContainer | ||
368 | ); | ||
369 | HRESULT PlanLayoutPackage( | ||
370 | __in BURN_PLAN* pPlan, | ||
371 | __in BURN_PACKAGE* pPackage | ||
372 | ); | ||
373 | HRESULT 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 | ); | ||
383 | HRESULT 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 | ); | ||
391 | HRESULT PlanRelatedBundlesBegin( | ||
392 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
393 | __in BURN_REGISTRATION* pRegistration, | ||
394 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
395 | __in BURN_PLAN* pPlan | ||
396 | ); | ||
397 | HRESULT 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 | ); | ||
404 | HRESULT PlanFinalizeActions( | ||
405 | __in BURN_PLAN* pPlan | ||
406 | ); | ||
407 | HRESULT PlanCleanPackage( | ||
408 | __in BURN_PLAN* pPlan, | ||
409 | __in BURN_PACKAGE* pPackage | ||
410 | ); | ||
411 | HRESULT PlanExecuteCacheSyncAndRollback( | ||
412 | __in BURN_PLAN* pPlan, | ||
413 | __in BURN_PACKAGE* pPackage, | ||
414 | __in HANDLE hCacheEvent | ||
415 | ); | ||
416 | HRESULT PlanExecuteCheckpoint( | ||
417 | __in BURN_PLAN* pPlan | ||
418 | ); | ||
419 | HRESULT PlanInsertExecuteAction( | ||
420 | __in DWORD dwIndex, | ||
421 | __in BURN_PLAN* pPlan, | ||
422 | __out BURN_EXECUTE_ACTION** ppExecuteAction | ||
423 | ); | ||
424 | HRESULT PlanInsertRollbackAction( | ||
425 | __in DWORD dwIndex, | ||
426 | __in BURN_PLAN* pPlan, | ||
427 | __out BURN_EXECUTE_ACTION** ppRollbackAction | ||
428 | ); | ||
429 | HRESULT PlanAppendExecuteAction( | ||
430 | __in BURN_PLAN* pPlan, | ||
431 | __out BURN_EXECUTE_ACTION** ppExecuteAction | ||
432 | ); | ||
433 | HRESULT PlanAppendRollbackAction( | ||
434 | __in BURN_PLAN* pPlan, | ||
435 | __out BURN_EXECUTE_ACTION** ppExecuteAction | ||
436 | ); | ||
437 | HRESULT PlanRollbackBoundaryBegin( | ||
438 | __in BURN_PLAN* pPlan, | ||
439 | __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary | ||
440 | ); | ||
441 | HRESULT PlanRollbackBoundaryComplete( | ||
442 | __in BURN_PLAN* pPlan | ||
443 | ); | ||
444 | HRESULT PlanSetResumeCommand( | ||
445 | __in BURN_REGISTRATION* pRegistration, | ||
446 | __in BOOTSTRAPPER_ACTION action, | ||
447 | __in BOOTSTRAPPER_COMMAND* pCommand, | ||
448 | __in BURN_LOGGING* pLog | ||
449 | ); | ||
450 | void 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 | |||
8 | PFN_INITIATESYSTEMSHUTDOWNEXW vpfnInitiateSystemShutdownExW; | ||
9 | |||
10 | |||
11 | // function definitions | ||
12 | |||
13 | extern "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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // typedefs | ||
11 | |||
12 | typedef 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 | |||
24 | extern PFN_INITIATESYSTEMSHUTDOWNEXW vpfnInitiateSystemShutdownExW; | ||
25 | |||
26 | |||
27 | // function declarations | ||
28 | |||
29 | void 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 | |||
6 | extern "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 | |||
153 | LExit: | ||
154 | ReleaseStr(sczRelationTypeCommandLineSwitch); | ||
155 | |||
156 | return hr; | ||
157 | } | ||
158 | |||
159 | extern "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 | |||
238 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | HRESULT 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 | ); | ||
29 | HRESULT 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 | |||
8 | const LPCWSTR REGISTRY_RUN_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"; | ||
9 | const LPCWSTR REGISTRY_RUN_ONCE_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"; | ||
10 | const LPCWSTR REGISTRY_REBOOT_PENDING_FORMAT = L"%ls.RebootRequired"; | ||
11 | const LPCWSTR REGISTRY_BUNDLE_INSTALLED = L"Installed"; | ||
12 | const LPCWSTR REGISTRY_BUNDLE_DISPLAY_ICON = L"DisplayIcon"; | ||
13 | const LPCWSTR REGISTRY_BUNDLE_DISPLAY_VERSION = L"DisplayVersion"; | ||
14 | const LPCWSTR REGISTRY_BUNDLE_ESTIMATED_SIZE = L"EstimatedSize"; | ||
15 | const LPCWSTR REGISTRY_BUNDLE_PUBLISHER = L"Publisher"; | ||
16 | const LPCWSTR REGISTRY_BUNDLE_HELP_LINK = L"HelpLink"; | ||
17 | const LPCWSTR REGISTRY_BUNDLE_HELP_TELEPHONE = L"HelpTelephone"; | ||
18 | const LPCWSTR REGISTRY_BUNDLE_URL_INFO_ABOUT = L"URLInfoAbout"; | ||
19 | const LPCWSTR REGISTRY_BUNDLE_URL_UPDATE_INFO = L"URLUpdateInfo"; | ||
20 | const LPCWSTR REGISTRY_BUNDLE_PARENT_DISPLAY_NAME = L"ParentDisplayName"; | ||
21 | const LPCWSTR REGISTRY_BUNDLE_PARENT_KEY_NAME = L"ParentKeyName"; | ||
22 | const LPCWSTR REGISTRY_BUNDLE_COMMENTS = L"Comments"; | ||
23 | const LPCWSTR REGISTRY_BUNDLE_CONTACT = L"Contact"; | ||
24 | const LPCWSTR REGISTRY_BUNDLE_NO_MODIFY = L"NoModify"; | ||
25 | const LPCWSTR REGISTRY_BUNDLE_MODIFY_PATH = L"ModifyPath"; | ||
26 | const LPCWSTR REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY = L"NoElevateOnModify"; | ||
27 | const LPCWSTR REGISTRY_BUNDLE_NO_REMOVE = L"NoRemove"; | ||
28 | const LPCWSTR REGISTRY_BUNDLE_SYSTEM_COMPONENT = L"SystemComponent"; | ||
29 | const LPCWSTR REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING = L"QuietUninstallString"; | ||
30 | const LPCWSTR REGISTRY_BUNDLE_UNINSTALL_STRING = L"UninstallString"; | ||
31 | const LPCWSTR REGISTRY_BUNDLE_RESUME_COMMAND_LINE = L"BundleResumeCommandLine"; | ||
32 | const LPCWSTR REGISTRY_BUNDLE_VERSION_MAJOR = L"VersionMajor"; | ||
33 | const LPCWSTR REGISTRY_BUNDLE_VERSION_MINOR = L"VersionMinor"; | ||
34 | const LPCWSTR SWIDTAG_FOLDER = L"swidtag"; | ||
35 | |||
36 | // internal function declarations | ||
37 | |||
38 | static HRESULT ParseSoftwareTagsFromXml( | ||
39 | __in IXMLDOMNode* pixnRegistrationNode, | ||
40 | __out BURN_SOFTWARE_TAG** prgSoftwareTags, | ||
41 | __out DWORD* pcSoftwareTags | ||
42 | ); | ||
43 | static HRESULT SetPaths( | ||
44 | __in BURN_REGISTRATION* pRegistration | ||
45 | ); | ||
46 | static HRESULT GetBundleManufacturer( | ||
47 | __in BURN_REGISTRATION* pRegistration, | ||
48 | __in BURN_VARIABLES* pVariables, | ||
49 | __out LPWSTR* psczBundleManufacturer | ||
50 | ); | ||
51 | static HRESULT GetBundleName( | ||
52 | __in BURN_REGISTRATION* pRegistration, | ||
53 | __in BURN_VARIABLES* pVariables, | ||
54 | __out LPWSTR* psczBundleName | ||
55 | ); | ||
56 | static HRESULT UpdateResumeMode( | ||
57 | __in BURN_REGISTRATION* pRegistration, | ||
58 | __in HKEY hkRegistration, | ||
59 | __in BURN_RESUME_MODE resumeMode, | ||
60 | __in BOOL fRestartInitiated | ||
61 | ); | ||
62 | static HRESULT ParseRelatedCodes( | ||
63 | __in BURN_REGISTRATION* pRegistration, | ||
64 | __in IXMLDOMNode* pixnBundle | ||
65 | ); | ||
66 | static HRESULT FormatUpdateRegistrationKey( | ||
67 | __in BURN_REGISTRATION* pRegistration, | ||
68 | __out_z LPWSTR* psczKey | ||
69 | ); | ||
70 | static HRESULT WriteSoftwareTags( | ||
71 | __in BURN_VARIABLES* pVariables, | ||
72 | __in BURN_SOFTWARE_TAGS* pSoftwareTags | ||
73 | ); | ||
74 | static HRESULT RemoveSoftwareTags( | ||
75 | __in BURN_VARIABLES* pVariables, | ||
76 | __in BURN_SOFTWARE_TAGS* pSoftwareTags | ||
77 | ); | ||
78 | static HRESULT WriteUpdateRegistration( | ||
79 | __in BURN_REGISTRATION* pRegistration, | ||
80 | __in BURN_VARIABLES* pVariables | ||
81 | ); | ||
82 | static HRESULT RemoveUpdateRegistration( | ||
83 | __in BURN_REGISTRATION* pRegistration | ||
84 | ); | ||
85 | static HRESULT RegWriteStringVariable( | ||
86 | __in HKEY hkKey, | ||
87 | __in BURN_VARIABLES* pVariables, | ||
88 | __in LPCWSTR wzVariable, | ||
89 | __in LPCWSTR wzName | ||
90 | ); | ||
91 | static HRESULT UpdateBundleNameRegistration( | ||
92 | __in BURN_REGISTRATION* pRegistration, | ||
93 | __in BURN_VARIABLES* pVariables, | ||
94 | __in HKEY hkRegistration | ||
95 | ); | ||
96 | static BOOL IsWuRebootPending(); | ||
97 | static BOOL IsBundleRebootPending( | ||
98 | __in BURN_REGISTRATION* pRegistration | ||
99 | ); | ||
100 | static BOOL IsRegistryRebootPending(); | ||
101 | |||
102 | // function definitions | ||
103 | |||
104 | /******************************************************************* | ||
105 | RegistrationParseFromXml - Parses registration information from manifest. | ||
106 | |||
107 | *******************************************************************/ | ||
108 | extern "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 | |||
321 | LExit: | ||
322 | ReleaseObject(pixnRegistrationNode); | ||
323 | ReleaseObject(pixnArpNode); | ||
324 | ReleaseObject(pixnUpdateNode); | ||
325 | ReleaseStr(scz); | ||
326 | |||
327 | return hr; | ||
328 | } | ||
329 | |||
330 | /******************************************************************* | ||
331 | RegistrationUninitialize - | ||
332 | |||
333 | *******************************************************************/ | ||
334 | extern "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 | *******************************************************************/ | ||
418 | extern "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 | |||
458 | LExit: | ||
459 | ReleaseStr(sczBundleManufacturer); | ||
460 | ReleaseStr(sczBundleName); | ||
461 | |||
462 | return hr; | ||
463 | } | ||
464 | |||
465 | extern "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 | *******************************************************************/ | ||
499 | extern "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 | |||
561 | LExit: | ||
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 | *******************************************************************/ | ||
572 | extern "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 | |||
584 | LExit: | ||
585 | return hr; | ||
586 | } | ||
587 | |||
588 | /******************************************************************* | ||
589 | RegistrationSessionBegin - Registers a run session on the system. | ||
590 | |||
591 | *******************************************************************/ | ||
592 | extern "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 | |||
830 | LExit: | ||
831 | ReleaseStr(sczPublisher); | ||
832 | ReleaseRegKey(hkRegistration); | ||
833 | |||
834 | return hr; | ||
835 | } | ||
836 | |||
837 | |||
838 | /******************************************************************* | ||
839 | RegistrationSessionResume - Resumes a previous run session. | ||
840 | |||
841 | *******************************************************************/ | ||
842 | extern "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 | |||
862 | LExit: | ||
863 | ReleaseRegKey(hkRegistration); | ||
864 | |||
865 | return hr; | ||
866 | } | ||
867 | |||
868 | |||
869 | /******************************************************************* | ||
870 | RegistrationSessionEnd - Unregisters a run session from the system. | ||
871 | |||
872 | *******************************************************************/ | ||
873 | extern "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 | |||
951 | LExit: | ||
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 | *******************************************************************/ | ||
963 | extern "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 | |||
980 | LExit: | ||
981 | return hr; | ||
982 | } | ||
983 | |||
984 | /******************************************************************* | ||
985 | RegistrationLoadState - Loads a previously stored engine state BLOB. | ||
986 | |||
987 | *******************************************************************/ | ||
988 | extern "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 | /******************************************************************* | ||
1000 | RegistrationGetResumeCommandLine - Gets the resume command line from the registry | ||
1001 | |||
1002 | *******************************************************************/ | ||
1003 | extern "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 | |||
1032 | static 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 | |||
1093 | LExit: | ||
1094 | ReleaseBSTR(bstrTagXml); | ||
1095 | ReleaseObject(pixnNode); | ||
1096 | ReleaseObject(pixnNodes); | ||
1097 | ReleaseMem(pSoftwareTags); | ||
1098 | |||
1099 | return hr; | ||
1100 | } | ||
1101 | |||
1102 | static 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 | |||
1128 | LExit: | ||
1129 | ReleaseStr(sczCacheDirectory); | ||
1130 | return hr; | ||
1131 | } | ||
1132 | |||
1133 | static 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 | |||
1151 | LExit: | ||
1152 | return hr; | ||
1153 | } | ||
1154 | |||
1155 | static 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 | |||
1173 | LExit: | ||
1174 | return hr; | ||
1175 | } | ||
1176 | |||
1177 | static 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 | |||
1263 | LExit: | ||
1264 | ReleaseStr(sczResumeCommandLine); | ||
1265 | ReleaseRegKey(hkRebootRequired); | ||
1266 | ReleaseRegKey(hkRun); | ||
1267 | |||
1268 | return hr; | ||
1269 | } | ||
1270 | |||
1271 | static 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 | |||
1343 | LExit: | ||
1344 | ReleaseObject(pixnNodes); | ||
1345 | ReleaseObject(pixnElement); | ||
1346 | ReleaseStr(sczAction); | ||
1347 | ReleaseStr(sczId); | ||
1348 | |||
1349 | return hr; | ||
1350 | } | ||
1351 | |||
1352 | static 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 | |||
1375 | LExit: | ||
1376 | ReleaseStr(sczKey); | ||
1377 | |||
1378 | return hr; | ||
1379 | } | ||
1380 | |||
1381 | static 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 | |||
1411 | LExit: | ||
1412 | ReleaseStr(sczPath); | ||
1413 | ReleaseStr(sczTagFolder); | ||
1414 | ReleaseStr(sczRootFolder); | ||
1415 | |||
1416 | return hr; | ||
1417 | } | ||
1418 | |||
1419 | static 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 | |||
1448 | LExit: | ||
1449 | ReleaseStr(sczPath); | ||
1450 | ReleaseStr(sczTagFolder); | ||
1451 | ReleaseStr(sczRootFolder); | ||
1452 | |||
1453 | return hr; | ||
1454 | } | ||
1455 | |||
1456 | static 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 | |||
1504 | LExit: | ||
1505 | ReleaseRegKey(hkKey); | ||
1506 | ReleaseStr(sczKey); | ||
1507 | |||
1508 | return hr; | ||
1509 | } | ||
1510 | |||
1511 | static 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 | |||
1553 | LExit: | ||
1554 | ReleaseStr(sczPackageVersion); | ||
1555 | ReleaseStr(sczKey); | ||
1556 | |||
1557 | return hr; | ||
1558 | } | ||
1559 | |||
1560 | static 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 | |||
1576 | LExit: | ||
1577 | StrSecureZeroFreeString(sczValue); | ||
1578 | |||
1579 | return hr; | ||
1580 | } | ||
1581 | |||
1582 | static 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 | |||
1596 | LExit: | ||
1597 | ReleaseStr(sczDisplayName); | ||
1598 | |||
1599 | return hr; | ||
1600 | } | ||
1601 | |||
1602 | static 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 | |||
1624 | static 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 | |||
1638 | LExit: | ||
1639 | ReleaseStr(sczRebootRequiredKey); | ||
1640 | ReleaseRegKey(hkRebootRequired); | ||
1641 | |||
1642 | return fBundleRebootPending; | ||
1643 | } | ||
1644 | |||
1645 | static 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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | enum BURN_MODE; | ||
11 | enum BURN_DEPENDENCY_REGISTRATION_ACTION; | ||
12 | struct _BURN_LOGGING; | ||
13 | typedef _BURN_LOGGING BURN_LOGGING; | ||
14 | |||
15 | // constants | ||
16 | |||
17 | const LPCWSTR BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; | ||
18 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH = L"BundleCachePath"; | ||
19 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE = L"BundleAddonCode"; | ||
20 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE = L"BundleDetectCode"; | ||
21 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE = L"BundlePatchCode"; | ||
22 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode"; | ||
23 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME = L"DisplayName"; | ||
24 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION = L"BundleVersion"; | ||
25 | const LPCWSTR BURN_REGISTRATION_REGISTRY_ENGINE_VERSION = L"EngineVersion"; | ||
26 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey"; | ||
27 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_TAG = L"BundleTag"; | ||
28 | |||
29 | enum 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 | |||
38 | enum 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 | |||
48 | typedef 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 | |||
58 | typedef 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 | |||
70 | typedef struct _BURN_RELATED_BUNDLES | ||
71 | { | ||
72 | BURN_RELATED_BUNDLE* rgRelatedBundles; | ||
73 | DWORD cRelatedBundles; | ||
74 | } BURN_RELATED_BUNDLES; | ||
75 | |||
76 | typedef struct _BURN_SOFTWARE_TAG | ||
77 | { | ||
78 | LPWSTR sczFilename; | ||
79 | LPWSTR sczRegid; | ||
80 | LPWSTR sczPath; | ||
81 | LPSTR sczTag; | ||
82 | } BURN_SOFTWARE_TAG; | ||
83 | |||
84 | typedef struct _BURN_SOFTWARE_TAGS | ||
85 | { | ||
86 | BURN_SOFTWARE_TAG* rgSoftwareTags; | ||
87 | DWORD cSoftwareTags; | ||
88 | } BURN_SOFTWARE_TAGS; | ||
89 | |||
90 | typedef 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 | |||
166 | HRESULT RegistrationParseFromXml( | ||
167 | __in BURN_REGISTRATION* pRegistration, | ||
168 | __in IXMLDOMNode* pixnBundle | ||
169 | ); | ||
170 | void RegistrationUninitialize( | ||
171 | __in BURN_REGISTRATION* pRegistration | ||
172 | ); | ||
173 | HRESULT RegistrationSetVariables( | ||
174 | __in BURN_REGISTRATION* pRegistration, | ||
175 | __in BURN_VARIABLES* pVariables | ||
176 | ); | ||
177 | HRESULT RegistrationDetectInstalled( | ||
178 | __in BURN_REGISTRATION* pRegistration | ||
179 | ); | ||
180 | HRESULT RegistrationDetectResumeType( | ||
181 | __in BURN_REGISTRATION* pRegistration, | ||
182 | __out BOOTSTRAPPER_RESUME_TYPE* pResumeType | ||
183 | ); | ||
184 | HRESULT RegistrationDetectRelatedBundles( | ||
185 | __in BURN_REGISTRATION* pRegistration | ||
186 | ); | ||
187 | HRESULT 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 | ); | ||
195 | HRESULT RegistrationSessionResume( | ||
196 | __in BURN_REGISTRATION* pRegistration, | ||
197 | __in BURN_VARIABLES* pVariables | ||
198 | ); | ||
199 | HRESULT 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 | ); | ||
207 | HRESULT RegistrationSaveState( | ||
208 | __in BURN_REGISTRATION* pRegistration, | ||
209 | __in_bcount_opt(cbBuffer) BYTE* pbBuffer, | ||
210 | __in_opt SIZE_T cbBuffer | ||
211 | ); | ||
212 | HRESULT RegistrationLoadState( | ||
213 | __in BURN_REGISTRATION* pRegistration, | ||
214 | __out_bcount(*pcbBuffer) BYTE** ppbBuffer, | ||
215 | __out SIZE_T* pcbBuffer | ||
216 | ); | ||
217 | HRESULT 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 | |||
7 | static 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 | ); | ||
14 | static HRESULT DetermineRelationType( | ||
15 | __in HKEY hkBundleId, | ||
16 | __in BURN_REGISTRATION* pRegistration, | ||
17 | __out BOOTSTRAPPER_RELATION_TYPE* pRelationType | ||
18 | ); | ||
19 | static 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 | |||
30 | extern "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 | |||
68 | LExit: | ||
69 | ReleaseStr(sczRelatedBundleId); | ||
70 | ReleaseRegKey(hkUninstallKey); | ||
71 | |||
72 | return hr; | ||
73 | } | ||
74 | |||
75 | extern "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 | |||
103 | static 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 | |||
137 | LExit: | ||
138 | ReleaseRegKey(hkBundleId); | ||
139 | |||
140 | return hr; | ||
141 | } | ||
142 | |||
143 | static 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 | |||
378 | LExit: | ||
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 | |||
396 | static 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 | |||
477 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | HRESULT RelatedBundlesInitializeForScope( | ||
10 | __in BOOL fPerMachine, | ||
11 | __in BURN_REGISTRATION* pRegistration, | ||
12 | __in BURN_RELATED_BUNDLES* pRelatedBundles | ||
13 | ); | ||
14 | void 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 | |||
8 | static HRESULT DirectorySearchExists( | ||
9 | __in BURN_SEARCH* pSearch, | ||
10 | __in BURN_VARIABLES* pVariables | ||
11 | ); | ||
12 | static HRESULT DirectorySearchPath( | ||
13 | __in BURN_SEARCH* pSearch, | ||
14 | __in BURN_VARIABLES* pVariables | ||
15 | ); | ||
16 | static HRESULT FileSearchExists( | ||
17 | __in BURN_SEARCH* pSearch, | ||
18 | __in BURN_VARIABLES* pVariables | ||
19 | ); | ||
20 | static HRESULT FileSearchVersion( | ||
21 | __in BURN_SEARCH* pSearch, | ||
22 | __in BURN_VARIABLES* pVariables | ||
23 | ); | ||
24 | static HRESULT FileSearchPath( | ||
25 | __in BURN_SEARCH* pSearch, | ||
26 | __in BURN_VARIABLES* pVariables | ||
27 | ); | ||
28 | static HRESULT RegistrySearchExists( | ||
29 | __in BURN_SEARCH* pSearch, | ||
30 | __in BURN_VARIABLES* pVariables | ||
31 | ); | ||
32 | static HRESULT RegistrySearchValue( | ||
33 | __in BURN_SEARCH* pSearch, | ||
34 | __in BURN_VARIABLES* pVariables | ||
35 | ); | ||
36 | static HRESULT MsiComponentSearch( | ||
37 | __in BURN_SEARCH* pSearch, | ||
38 | __in BURN_VARIABLES* pVariables | ||
39 | ); | ||
40 | static HRESULT MsiProductSearch( | ||
41 | __in BURN_SEARCH* pSearch, | ||
42 | __in BURN_VARIABLES* pVariables | ||
43 | ); | ||
44 | static HRESULT MsiFeatureSearch( | ||
45 | __in BURN_SEARCH* pSearch, | ||
46 | __in BURN_VARIABLES* pVariables | ||
47 | ); | ||
48 | static HRESULT PerformExtensionSearch( | ||
49 | __in BURN_SEARCH* pSearch | ||
50 | ); | ||
51 | static HRESULT PerformSetVariable( | ||
52 | __in BURN_SEARCH* pSearch, | ||
53 | __in BURN_VARIABLES* pVariables | ||
54 | ); | ||
55 | |||
56 | |||
57 | // function definitions | ||
58 | |||
59 | extern "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 | |||
461 | LExit: | ||
462 | ReleaseObject(pixnNodes); | ||
463 | ReleaseObject(pixnNode); | ||
464 | ReleaseBSTR(bstrNodeName); | ||
465 | ReleaseStr(scz); | ||
466 | return hr; | ||
467 | } | ||
468 | |||
469 | extern "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 | |||
571 | LExit: | ||
572 | return hr; | ||
573 | } | ||
574 | |||
575 | extern "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 | |||
624 | static 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 | |||
659 | LExit: | ||
660 | StrSecureZeroFreeString(sczPath); | ||
661 | |||
662 | return hr; | ||
663 | } | ||
664 | |||
665 | static 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 | |||
700 | LExit: | ||
701 | StrSecureZeroFreeString(sczPath); | ||
702 | |||
703 | return hr; | ||
704 | } | ||
705 | |||
706 | static 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 | |||
744 | LExit: | ||
745 | StrSecureZeroFreeString(sczPath); | ||
746 | return hr; | ||
747 | } | ||
748 | |||
749 | static 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 | |||
780 | LExit: | ||
781 | StrSecureZeroFreeString(sczPath); | ||
782 | ReleaseVerutilVersion(pVersion); | ||
783 | return hr; | ||
784 | } | ||
785 | |||
786 | static 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 | |||
821 | LExit: | ||
822 | StrSecureZeroFreeString(sczPath); | ||
823 | |||
824 | return hr; | ||
825 | } | ||
826 | |||
827 | static 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 | |||
896 | LExit: | ||
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 | |||
910 | static 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 | |||
1024 | LExit: | ||
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 | |||
1040 | static 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 | |||
1113 | LExit: | ||
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 | |||
1125 | static 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 | |||
1251 | LExit: | ||
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 | |||
1264 | static 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 | |||
1280 | static 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 | |||
1291 | static 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 | |||
1301 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // constants | ||
11 | |||
12 | enum 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 | |||
25 | enum 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 | |||
32 | enum 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 | |||
40 | enum 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 | |||
47 | enum 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 | |||
55 | enum 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 | |||
64 | enum 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 | |||
71 | enum 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 | |||
80 | typedef 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 | |||
138 | typedef struct _BURN_SEARCHES | ||
139 | { | ||
140 | BURN_SEARCH* rgSearches; | ||
141 | DWORD cSearches; | ||
142 | } BURN_SEARCHES; | ||
143 | |||
144 | |||
145 | // function declarations | ||
146 | |||
147 | HRESULT SearchesParseFromXml( | ||
148 | __in BURN_SEARCHES* pSearches, | ||
149 | __in BURN_EXTENSIONS* pBurnExtensions, | ||
150 | __in IXMLDOMNode* pixnBundle | ||
151 | ); | ||
152 | HRESULT SearchesExecute( | ||
153 | __in BURN_SEARCHES* pSearches, | ||
154 | __in BURN_VARIABLES* pVariables | ||
155 | ); | ||
156 | void 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 | ||
15 | typedef 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 | |||
32 | static HRESULT VerifySectionMatchesMemoryPEHeader( | ||
33 | __in REFGUID pSection | ||
34 | ); | ||
35 | |||
36 | |||
37 | extern "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, §ionHeader, 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 | |||
247 | LExit: | ||
248 | ReleaseMem(pBurnSectionHeader); | ||
249 | |||
250 | return hr; | ||
251 | } | ||
252 | |||
253 | extern "C" void SectionUninitialize( | ||
254 | __out BURN_SECTION* pSection | ||
255 | ) | ||
256 | { | ||
257 | ReleaseMem(pSection->rgcbContainers); | ||
258 | memset(pSection, 0, sizeof(BURN_SECTION)); | ||
259 | } | ||
260 | |||
261 | extern "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 | |||
303 | LExit: | ||
304 | return hr; | ||
305 | } | ||
306 | |||
307 | HRESULT 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 | |||
397 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // structs | ||
11 | |||
12 | typedef 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 | |||
35 | HRESULT SectionInitialize( | ||
36 | __in BURN_SECTION* pSection, | ||
37 | __in HANDLE hEngineFile, | ||
38 | __in HANDLE hSourceEngineFile | ||
39 | ); | ||
40 | void SectionUninitialize( | ||
41 | __in BURN_SECTION* pSection | ||
42 | ); | ||
43 | HRESULT 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 | |||
10 | struct SPLASHSCREEN_INFO | ||
11 | { | ||
12 | HBITMAP hBitmap; | ||
13 | SIZE defaultDpiSize; | ||
14 | SIZE size; | ||
15 | UINT nDpi; | ||
16 | HWND hWnd; | ||
17 | }; | ||
18 | |||
19 | struct SPLASHSCREEN_CONTEXT | ||
20 | { | ||
21 | HANDLE hInitializedEvent; | ||
22 | HINSTANCE hInstance; | ||
23 | LPCWSTR wzCaption; | ||
24 | |||
25 | HWND* pHwnd; | ||
26 | }; | ||
27 | |||
28 | // internal function definitions | ||
29 | |||
30 | static DWORD WINAPI ThreadProc( | ||
31 | __in LPVOID pvContext | ||
32 | ); | ||
33 | static LRESULT CALLBACK WndProc( | ||
34 | __in HWND hWnd, | ||
35 | __in UINT uMsg, | ||
36 | __in WPARAM wParam, | ||
37 | __in LPARAM lParam | ||
38 | ); | ||
39 | static HRESULT LoadSplashScreen( | ||
40 | __in SPLASHSCREEN_CONTEXT* pContext, | ||
41 | __in SPLASHSCREEN_INFO* pSplashScreen | ||
42 | ); | ||
43 | static BOOL OnDpiChanged( | ||
44 | __in SPLASHSCREEN_INFO* pSplashScreen, | ||
45 | __in WPARAM wParam, | ||
46 | __in LPARAM lParam | ||
47 | ); | ||
48 | static void OnEraseBkgnd( | ||
49 | __in SPLASHSCREEN_INFO* pSplashScreen, | ||
50 | __in WPARAM wParam | ||
51 | ); | ||
52 | static void OnNcCreate( | ||
53 | __in HWND hWnd, | ||
54 | __in LPARAM lParam | ||
55 | ); | ||
56 | static 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 | |||
66 | extern "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 | |||
94 | LExit: | ||
95 | ReleaseHandle(rgSplashScreenEvents[1]); | ||
96 | ReleaseHandle(rgSplashScreenEvents[0]); | ||
97 | } | ||
98 | |||
99 | extern "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 | |||
121 | LExit: | ||
122 | ReleaseStr(sczDisplayString); | ||
123 | |||
124 | return hr; | ||
125 | } | ||
126 | |||
127 | |||
128 | static 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 | |||
177 | LExit: | ||
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 | |||
191 | static 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 | |||
231 | static 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 | |||
282 | LExit: | ||
283 | MemFree(pMonitorContext); | ||
284 | |||
285 | return hr; | ||
286 | } | ||
287 | |||
288 | static 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 | |||
306 | static 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 | |||
319 | static 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 | |||
339 | static 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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // constants | ||
11 | |||
12 | |||
13 | // structs | ||
14 | |||
15 | |||
16 | // functions | ||
17 | |||
18 | void SplashScreenCreate( | ||
19 | __in HINSTANCE hInstance, | ||
20 | __in_z_opt LPCWSTR wzCaption, | ||
21 | __out HWND* pHwnd | ||
22 | ); | ||
23 | HRESULT 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 | |||
10 | struct UITHREAD_CONTEXT | ||
11 | { | ||
12 | HANDLE hInitializedEvent; | ||
13 | HINSTANCE hInstance; | ||
14 | BURN_ENGINE_STATE* pEngineState; | ||
15 | }; | ||
16 | |||
17 | struct UITHREAD_INFO | ||
18 | { | ||
19 | BOOL fElevated; | ||
20 | BURN_USER_EXPERIENCE* pUserExperience; | ||
21 | }; | ||
22 | |||
23 | |||
24 | // internal function declarations | ||
25 | |||
26 | static DWORD WINAPI ThreadProc( | ||
27 | __in LPVOID pvContext | ||
28 | ); | ||
29 | |||
30 | static 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 | |||
40 | HRESULT 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 | |||
68 | LExit: | ||
69 | ReleaseHandle(rgWaitHandles[1]); | ||
70 | ReleaseHandle(rgWaitHandles[0]); | ||
71 | |||
72 | return hr; | ||
73 | } | ||
74 | |||
75 | void 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 | |||
92 | static 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 | |||
161 | LExit: | ||
162 | if (fRegistered) | ||
163 | { | ||
164 | ::UnregisterClassW(BURN_UITHREAD_CLASS_WINDOW, pContext->hInstance); | ||
165 | } | ||
166 | |||
167 | return hr; | ||
168 | } | ||
169 | |||
170 | static 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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // functions | ||
11 | |||
12 | HRESULT UiCreateMessageWindow( | ||
13 | __in HINSTANCE hInstance, | ||
14 | __in BURN_ENGINE_STATE* pEngineState | ||
15 | ); | ||
16 | |||
17 | void 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 | |||
11 | extern "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 | |||
30 | LExit: | ||
31 | ReleaseObject(pixnUpdateNode); | ||
32 | |||
33 | return hr; | ||
34 | } | ||
35 | |||
36 | extern "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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // structs | ||
11 | |||
12 | typedef struct _BURN_UPDATE | ||
13 | { | ||
14 | BOOL fUpdateAvailable; | ||
15 | LPWSTR sczUpdateSource; | ||
16 | |||
17 | BURN_PACKAGE package; | ||
18 | } BURN_UPDATE; | ||
19 | |||
20 | |||
21 | // function declarations | ||
22 | |||
23 | HRESULT UpdateParseFromXml( | ||
24 | __in BURN_UPDATE* pUpdate, | ||
25 | __in IXMLDOMNode* pixnBundle | ||
26 | ); | ||
27 | void 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 | |||
7 | static int FilterResult( | ||
8 | __in DWORD dwAllowedResults, | ||
9 | __in int nResult | ||
10 | ); | ||
11 | |||
12 | static 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 | |||
20 | static 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 | |||
27 | static 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 | *******************************************************************/ | ||
41 | extern "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 | |||
75 | LExit: | ||
76 | ReleaseObject(pixnUserExperienceNode); | ||
77 | |||
78 | return hr; | ||
79 | } | ||
80 | |||
81 | /******************************************************************* | ||
82 | UserExperienceUninitialize - | ||
83 | |||
84 | *******************************************************************/ | ||
85 | extern "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 | *******************************************************************/ | ||
100 | extern "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 | |||
134 | LExit: | ||
135 | return hr; | ||
136 | } | ||
137 | |||
138 | /******************************************************************* | ||
139 | UserExperienceUnload - | ||
140 | |||
141 | *******************************************************************/ | ||
142 | extern "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 | |||
170 | extern "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 | |||
187 | LExit: | ||
188 | ReleaseStr(sczWorkingFolder); | ||
189 | |||
190 | return hr; | ||
191 | } | ||
192 | |||
193 | |||
194 | extern "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 | |||
211 | extern "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 | |||
240 | extern "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 | |||
250 | extern "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 | |||
260 | extern "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 | |||
268 | LExit: | ||
269 | return hr; | ||
270 | } | ||
271 | |||
272 | extern "C" void UserExperienceExecuteReset( | ||
273 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
274 | ) | ||
275 | { | ||
276 | pUserExperience->hrApplyError = S_OK; | ||
277 | pUserExperience->hwndApply = NULL; | ||
278 | } | ||
279 | |||
280 | extern "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 | |||
291 | EXTERN_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 | |||
313 | LExit: | ||
314 | return hr; | ||
315 | } | ||
316 | |||
317 | EXTERN_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 | |||
341 | LExit: | ||
342 | return hr; | ||
343 | } | ||
344 | |||
345 | EXTERN_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 | |||
367 | LExit: | ||
368 | return hr; | ||
369 | } | ||
370 | |||
371 | EXTERN_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 | |||
390 | LExit: | ||
391 | return hr; | ||
392 | } | ||
393 | |||
394 | EXTERN_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 | |||
439 | LExit: | ||
440 | return hr; | ||
441 | } | ||
442 | |||
443 | EXTERN_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 | |||
472 | LExit: | ||
473 | return hr; | ||
474 | } | ||
475 | |||
476 | EXTERN_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 | |||
506 | LExit: | ||
507 | return hr; | ||
508 | } | ||
509 | |||
510 | EXTERN_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 | |||
565 | LExit: | ||
566 | return hr; | ||
567 | } | ||
568 | |||
569 | EXTERN_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 | |||
589 | LExit: | ||
590 | return hr; | ||
591 | } | ||
592 | |||
593 | EXTERN_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 | |||
610 | LExit: | ||
611 | return hr; | ||
612 | } | ||
613 | |||
614 | EXTERN_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 | |||
638 | LExit: | ||
639 | return hr; | ||
640 | } | ||
641 | |||
642 | EXTERN_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 | |||
663 | LExit: | ||
664 | return hr; | ||
665 | } | ||
666 | |||
667 | EXTERN_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 | |||
697 | LExit: | ||
698 | return hr; | ||
699 | } | ||
700 | |||
701 | EXTERN_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 | |||
727 | LExit: | ||
728 | return hr; | ||
729 | } | ||
730 | |||
731 | EXTERN_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 | |||
758 | LExit: | ||
759 | return hr; | ||
760 | } | ||
761 | |||
762 | EXTERN_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 | |||
786 | LExit: | ||
787 | return hr; | ||
788 | } | ||
789 | |||
790 | EXTERN_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 | |||
811 | LExit: | ||
812 | return hr; | ||
813 | } | ||
814 | |||
815 | EXTERN_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 | |||
845 | LExit: | ||
846 | return hr; | ||
847 | } | ||
848 | |||
849 | EXTERN_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 | |||
873 | LExit: | ||
874 | return hr; | ||
875 | } | ||
876 | |||
877 | EXTERN_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 | |||
906 | LExit: | ||
907 | return hr; | ||
908 | } | ||
909 | |||
910 | EXTERN_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 | |||
942 | LExit: | ||
943 | return hr; | ||
944 | } | ||
945 | |||
946 | EXTERN_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 | |||
968 | LExit: | ||
969 | return hr; | ||
970 | } | ||
971 | |||
972 | EXTERN_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 | |||
991 | LExit: | ||
992 | return hr; | ||
993 | } | ||
994 | |||
995 | EXTERN_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 | |||
1021 | LExit: | ||
1022 | return hr; | ||
1023 | } | ||
1024 | |||
1025 | EXTERN_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 | |||
1044 | LExit: | ||
1045 | return hr; | ||
1046 | } | ||
1047 | |||
1048 | EXTERN_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 | |||
1080 | LExit: | ||
1081 | return hr; | ||
1082 | } | ||
1083 | |||
1084 | EXTERN_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 | |||
1110 | LExit: | ||
1111 | return hr; | ||
1112 | } | ||
1113 | |||
1114 | EXTERN_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 | |||
1136 | LExit: | ||
1137 | return hr; | ||
1138 | } | ||
1139 | |||
1140 | EXTERN_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 | |||
1163 | LExit: | ||
1164 | return hr; | ||
1165 | } | ||
1166 | |||
1167 | EXTERN_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 | |||
1201 | LExit: | ||
1202 | return hr; | ||
1203 | } | ||
1204 | |||
1205 | EXTERN_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 | |||
1237 | LExit: | ||
1238 | return hr; | ||
1239 | } | ||
1240 | |||
1241 | EXTERN_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 | |||
1267 | LExit: | ||
1268 | return hr; | ||
1269 | } | ||
1270 | |||
1271 | EXTERN_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 | |||
1308 | LExit: | ||
1309 | return hr; | ||
1310 | } | ||
1311 | |||
1312 | EXTERN_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 | |||
1337 | LExit: | ||
1338 | return hr; | ||
1339 | } | ||
1340 | |||
1341 | EXTERN_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 | |||
1365 | LExit: | ||
1366 | return hr; | ||
1367 | } | ||
1368 | |||
1369 | EXTERN_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 | |||
1389 | LExit: | ||
1390 | return hr; | ||
1391 | } | ||
1392 | |||
1393 | EXTERN_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 | |||
1410 | LExit: | ||
1411 | return hr; | ||
1412 | } | ||
1413 | |||
1414 | EXTERN_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 | |||
1448 | LExit: | ||
1449 | return hr; | ||
1450 | } | ||
1451 | |||
1452 | EXTERN_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 | |||
1474 | LExit: | ||
1475 | return hr; | ||
1476 | } | ||
1477 | |||
1478 | EXTERN_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 | |||
1495 | LExit: | ||
1496 | return hr; | ||
1497 | } | ||
1498 | |||
1499 | EXTERN_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 | |||
1525 | LExit: | ||
1526 | return hr; | ||
1527 | } | ||
1528 | |||
1529 | EXTERN_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 | |||
1561 | LExit: | ||
1562 | return hr; | ||
1563 | } | ||
1564 | |||
1565 | EXTERN_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 | |||
1595 | LExit: | ||
1596 | return hr; | ||
1597 | } | ||
1598 | |||
1599 | EXTERN_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 | |||
1625 | LExit: | ||
1626 | return hr; | ||
1627 | } | ||
1628 | |||
1629 | EXTERN_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 | |||
1653 | LExit: | ||
1654 | return hr; | ||
1655 | } | ||
1656 | |||
1657 | EXTERN_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 | |||
1679 | LExit: | ||
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 | |||
1695 | EXTERN_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 | |||
1715 | LExit: | ||
1716 | return hr; | ||
1717 | } | ||
1718 | |||
1719 | EXTERN_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 | |||
1738 | LExit: | ||
1739 | return hr; | ||
1740 | } | ||
1741 | |||
1742 | EXTERN_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 | |||
1757 | LExit: | ||
1758 | return hr; | ||
1759 | } | ||
1760 | |||
1761 | EXTERN_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 | |||
1778 | LExit: | ||
1779 | return hr; | ||
1780 | } | ||
1781 | |||
1782 | EXTERN_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 | |||
1804 | LExit: | ||
1805 | return hr; | ||
1806 | } | ||
1807 | |||
1808 | EXTERN_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 | |||
1836 | LExit: | ||
1837 | return hr; | ||
1838 | } | ||
1839 | |||
1840 | EXTERN_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 | |||
1857 | LExit: | ||
1858 | return hr; | ||
1859 | } | ||
1860 | |||
1861 | EXTERN_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 | |||
1895 | LExit: | ||
1896 | return hr; | ||
1897 | } | ||
1898 | |||
1899 | EXTERN_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 | |||
1934 | LExit: | ||
1935 | return hr; | ||
1936 | } | ||
1937 | |||
1938 | EXTERN_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 | |||
1963 | LExit: | ||
1964 | return hr; | ||
1965 | } | ||
1966 | |||
1967 | EXTERN_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 | |||
2003 | LExit: | ||
2004 | return hr; | ||
2005 | } | ||
2006 | |||
2007 | EXTERN_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 | |||
2028 | LExit: | ||
2029 | return hr; | ||
2030 | } | ||
2031 | |||
2032 | EXTERN_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 | |||
2058 | LExit: | ||
2059 | return hr; | ||
2060 | } | ||
2061 | |||
2062 | EXTERN_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 | |||
2090 | LExit: | ||
2091 | return hr; | ||
2092 | } | ||
2093 | |||
2094 | EXTERN_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 | |||
2117 | EXTERN_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 | |||
2137 | LExit: | ||
2138 | return hr; | ||
2139 | } | ||
2140 | |||
2141 | EXTERN_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 | |||
2158 | LExit: | ||
2159 | return hr; | ||
2160 | } | ||
2161 | |||
2162 | EXTERN_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 | |||
2179 | LExit: | ||
2180 | return hr; | ||
2181 | } | ||
2182 | |||
2183 | EXTERN_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 | |||
2202 | LExit: | ||
2203 | return hr; | ||
2204 | } | ||
2205 | |||
2206 | EXTERN_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 | |||
2225 | LExit: | ||
2226 | return hr; | ||
2227 | } | ||
2228 | |||
2229 | EXTERN_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 | |||
2244 | LExit: | ||
2245 | return hr; | ||
2246 | } | ||
2247 | |||
2248 | EXTERN_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 | |||
2263 | LExit: | ||
2264 | return hr; | ||
2265 | } | ||
2266 | |||
2267 | EXTERN_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 | |||
2284 | LExit: | ||
2285 | return hr; | ||
2286 | } | ||
2287 | |||
2288 | EXTERN_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 | |||
2309 | LExit: | ||
2310 | return hr; | ||
2311 | } | ||
2312 | |||
2313 | EXTERN_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 | |||
2335 | LExit: | ||
2336 | return hr; | ||
2337 | } | ||
2338 | |||
2339 | EXTERN_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 | |||
2356 | LExit: | ||
2357 | return hr; | ||
2358 | } | ||
2359 | |||
2360 | extern "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 | |||
2381 | extern "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 | |||
2407 | static 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. | ||
2568 | static 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 | |||
2603 | LExit: | ||
2604 | return hr; | ||
2605 | } | ||
2606 | |||
2607 | static 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 | |||
2627 | LExit: | ||
2628 | return hr; | ||
2629 | } | ||
2630 | |||
2631 | static 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 | |||
2651 | LExit: | ||
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) | ||
7 | extern "C" { | ||
8 | #endif | ||
9 | |||
10 | |||
11 | // constants | ||
12 | |||
13 | const DWORD MB_RETRYTRYAGAIN = 0xF; | ||
14 | |||
15 | |||
16 | // structs | ||
17 | |||
18 | typedef struct _BOOTSTRAPPER_ENGINE_CONTEXT BOOTSTRAPPER_ENGINE_CONTEXT; | ||
19 | |||
20 | typedef 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 | |||
54 | HRESULT UserExperienceParseFromXml( | ||
55 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
56 | __in IXMLDOMNode* pixnBundle | ||
57 | ); | ||
58 | void UserExperienceUninitialize( | ||
59 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
60 | ); | ||
61 | HRESULT UserExperienceLoad( | ||
62 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
63 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pEngineContext, | ||
64 | __in BOOTSTRAPPER_COMMAND* pCommand | ||
65 | ); | ||
66 | HRESULT UserExperienceUnload( | ||
67 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
68 | ); | ||
69 | HRESULT UserExperienceEnsureWorkingFolder( | ||
70 | __in LPCWSTR wzBundleId, | ||
71 | __deref_out_z LPWSTR* psczUserExperienceWorkingFolder | ||
72 | ); | ||
73 | HRESULT UserExperienceRemove( | ||
74 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
75 | ); | ||
76 | int 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 | ); | ||
85 | void UserExperienceActivateEngine( | ||
86 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
87 | ); | ||
88 | void 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 | *********************************************************************/ | ||
96 | HRESULT UserExperienceEnsureEngineInactive( | ||
97 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
98 | ); | ||
99 | void UserExperienceExecuteReset( | ||
100 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
101 | ); | ||
102 | void UserExperienceExecutePhaseComplete( | ||
103 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
104 | __in HRESULT hrResult | ||
105 | ); | ||
106 | BAAPI UserExperienceOnApplyBegin( | ||
107 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
108 | __in DWORD dwPhaseCount | ||
109 | ); | ||
110 | BAAPI UserExperienceOnApplyComplete( | ||
111 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
112 | __in HRESULT hrStatus, | ||
113 | __in BOOTSTRAPPER_APPLY_RESTART restart, | ||
114 | __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction | ||
115 | ); | ||
116 | BAAPI UserExperienceOnBeginMsiTransactionBegin( | ||
117 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
118 | __in LPCWSTR wzTransactionId | ||
119 | ); | ||
120 | BAAPI UserExperienceOnBeginMsiTransactionComplete( | ||
121 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
122 | __in LPCWSTR wzTransactionId, | ||
123 | __in HRESULT hrStatus | ||
124 | ); | ||
125 | BAAPI 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 | ); | ||
134 | BAAPI 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 | ); | ||
141 | BAAPI 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 | ); | ||
149 | BAAPI 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 | ); | ||
161 | BAAPI UserExperienceOnCacheBegin( | ||
162 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
163 | ); | ||
164 | BAAPI UserExperienceOnCacheComplete( | ||
165 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
166 | __in HRESULT hrStatus | ||
167 | ); | ||
168 | BAAPI UserExperienceOnCacheContainerOrPayloadVerifyBegin( | ||
169 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
170 | __in_z_opt LPCWSTR wzPackageOrContainerId, | ||
171 | __in_z_opt LPCWSTR wzPayloadId | ||
172 | ); | ||
173 | BAAPI UserExperienceOnCacheContainerOrPayloadVerifyComplete( | ||
174 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
175 | __in_z_opt LPCWSTR wzPackageOrContainerId, | ||
176 | __in_z_opt LPCWSTR wzPayloadId, | ||
177 | __in HRESULT hrStatus | ||
178 | ); | ||
179 | BAAPI 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 | ); | ||
187 | BAAPI UserExperienceOnCachePackageBegin( | ||
188 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
189 | __in_z LPCWSTR wzPackageId, | ||
190 | __in DWORD cCachePayloads, | ||
191 | __in DWORD64 dw64PackageCacheSize | ||
192 | ); | ||
193 | BAAPI UserExperienceOnCachePackageComplete( | ||
194 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
195 | __in_z LPCWSTR wzPackageId, | ||
196 | __in HRESULT hrStatus, | ||
197 | __inout BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION* pAction | ||
198 | ); | ||
199 | BAAPI UserExperienceOnCachePayloadExtractBegin( | ||
200 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
201 | __in_z_opt LPCWSTR wzContainerId, | ||
202 | __in_z_opt LPCWSTR wzPayloadId | ||
203 | ); | ||
204 | BAAPI UserExperienceOnCachePayloadExtractComplete( | ||
205 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
206 | __in_z_opt LPCWSTR wzContainerId, | ||
207 | __in_z_opt LPCWSTR wzPayloadId, | ||
208 | __in HRESULT hrStatus | ||
209 | ); | ||
210 | BAAPI 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 | ); | ||
218 | BAAPI UserExperienceOnCacheVerifyBegin( | ||
219 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
220 | __in_z_opt LPCWSTR wzPackageOrContainerId, | ||
221 | __in_z_opt LPCWSTR wzPayloadId | ||
222 | ); | ||
223 | BAAPI 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 | ); | ||
230 | BAAPI 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 | ); | ||
239 | BAAPI UserExperienceOnCommitMsiTransactionBegin( | ||
240 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
241 | __in LPCWSTR wzTransactionId | ||
242 | ); | ||
243 | BAAPI UserExperienceOnCommitMsiTransactionComplete( | ||
244 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
245 | __in LPCWSTR wzTransactionId, | ||
246 | __in HRESULT hrStatus | ||
247 | ); | ||
248 | BAAPI UserExperienceOnDetectBegin( | ||
249 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
250 | __in BOOL fCached, | ||
251 | __in BOOL fInstalled, | ||
252 | __in DWORD cPackages | ||
253 | ); | ||
254 | BAAPI UserExperienceOnDetectComplete( | ||
255 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
256 | __in HRESULT hrStatus, | ||
257 | __in BOOL fEligibleForCleanup | ||
258 | ); | ||
259 | BAAPI 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 | ); | ||
268 | BAAPI UserExperienceOnDetectMsiFeature( | ||
269 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
270 | __in_z LPCWSTR wzPackageId, | ||
271 | __in_z LPCWSTR wzFeatureId, | ||
272 | __in BOOTSTRAPPER_FEATURE_STATE state | ||
273 | ); | ||
274 | BAAPI UserExperienceOnDetectPackageBegin( | ||
275 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
276 | __in_z LPCWSTR wzPackageId | ||
277 | ); | ||
278 | BAAPI 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 | ); | ||
285 | BAAPI 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 | ); | ||
295 | BAAPI 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 | ); | ||
304 | BAAPI UserExperienceOnDetectPatchTarget( | ||
305 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
306 | __in_z LPCWSTR wzPackageId, | ||
307 | __in_z LPCWSTR wzProductCode, | ||
308 | __in BOOTSTRAPPER_PACKAGE_STATE patchState | ||
309 | ); | ||
310 | BAAPI 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 | ); | ||
321 | BAAPI UserExperienceOnDetectUpdateBegin( | ||
322 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
323 | __in_z LPCWSTR wzUpdateLocation, | ||
324 | __inout BOOL* pfSkip | ||
325 | ); | ||
326 | BAAPI UserExperienceOnDetectUpdateComplete( | ||
327 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
328 | __in HRESULT hrStatus, | ||
329 | __inout BOOL* pfIgnoreError | ||
330 | ); | ||
331 | BAAPI UserExperienceOnElevateBegin( | ||
332 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
333 | ); | ||
334 | BAAPI UserExperienceOnElevateComplete( | ||
335 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
336 | __in HRESULT hrStatus | ||
337 | ); | ||
338 | BAAPI 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 | ); | ||
349 | BAAPI UserExperienceOnExecuteBegin( | ||
350 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
351 | __in DWORD cExecutingPackages | ||
352 | ); | ||
353 | BAAPI UserExperienceOnExecuteComplete( | ||
354 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
355 | __in HRESULT hrStatus | ||
356 | ); | ||
357 | BAAPI 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 | ); | ||
364 | BAAPI 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 | ); | ||
374 | BAAPI 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 | ); | ||
382 | BAAPI 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 | ); | ||
389 | BAAPI UserExperienceOnExecutePatchTarget( | ||
390 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
391 | __in_z LPCWSTR wzPackageId, | ||
392 | __in_z LPCWSTR wzTargetProductCode | ||
393 | ); | ||
394 | BAAPI 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 | ); | ||
401 | BAAPI UserExperienceOnLaunchApprovedExeBegin( | ||
402 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
403 | ); | ||
404 | BAAPI UserExperienceOnLaunchApprovedExeComplete( | ||
405 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
406 | __in HRESULT hrStatus, | ||
407 | __in DWORD dwProcessId | ||
408 | ); | ||
409 | BAAPI UserExperienceOnPauseAUBegin( | ||
410 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
411 | ); | ||
412 | BAAPI UserExperienceOnPauseAUComplete( | ||
413 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
414 | __in HRESULT hrStatus | ||
415 | ); | ||
416 | BAAPI UserExperienceOnPlanBegin( | ||
417 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
418 | __in DWORD cPackages | ||
419 | ); | ||
420 | BAAPI UserExperienceOnPlanComplete( | ||
421 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
422 | __in HRESULT hrStatus | ||
423 | ); | ||
424 | BAAPI 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 | ); | ||
433 | BAAPI UserExperienceOnPlanMsiFeature( | ||
434 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
435 | __in_z LPCWSTR wzPackageId, | ||
436 | __in_z LPCWSTR wzFeatureId, | ||
437 | __inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState | ||
438 | ); | ||
439 | BAAPI 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 | ); | ||
448 | BAAPI 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 | ); | ||
456 | BAAPI 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 | ); | ||
465 | BAAPI UserExperienceOnPlanPackageComplete( | ||
466 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
467 | __in_z LPCWSTR wzPackageId, | ||
468 | __in HRESULT hrStatus, | ||
469 | __in BOOTSTRAPPER_REQUEST_STATE requested | ||
470 | ); | ||
471 | BAAPI UserExperienceOnPlanRelatedBundle( | ||
472 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
473 | __in_z LPCWSTR wzBundleId, | ||
474 | __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState | ||
475 | ); | ||
476 | BAAPI UserExperienceOnPlanPatchTarget( | ||
477 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
478 | __in_z LPCWSTR wzPackageId, | ||
479 | __in_z LPCWSTR wzProductCode, | ||
480 | __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState | ||
481 | ); | ||
482 | BAAPI UserExperienceOnProgress( | ||
483 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
484 | __in BOOL fRollback, | ||
485 | __in DWORD dwProgressPercentage, | ||
486 | __in DWORD dwOverallPercentage | ||
487 | ); | ||
488 | BAAPI UserExperienceOnRegisterBegin( | ||
489 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
490 | ); | ||
491 | BAAPI UserExperienceOnRegisterComplete( | ||
492 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
493 | __in HRESULT hrStatus | ||
494 | ); | ||
495 | BAAPI UserExperienceOnRollbackMsiTransactionBegin( | ||
496 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
497 | __in LPCWSTR wzTransactionId | ||
498 | ); | ||
499 | BAAPI UserExperienceOnRollbackMsiTransactionComplete( | ||
500 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
501 | __in LPCWSTR wzTransactionId, | ||
502 | __in HRESULT hrStatus | ||
503 | ); | ||
504 | BAAPI UserExperienceOnShutdown( | ||
505 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
506 | __inout BOOTSTRAPPER_SHUTDOWN_ACTION* pAction | ||
507 | ); | ||
508 | BAAPI UserExperienceOnStartup( | ||
509 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
510 | ); | ||
511 | BAAPI UserExperienceOnSystemRestorePointBegin( | ||
512 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
513 | ); | ||
514 | BAAPI UserExperienceOnSystemRestorePointComplete( | ||
515 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
516 | __in HRESULT hrStatus | ||
517 | ); | ||
518 | BAAPI UserExperienceOnSystemShutdown( | ||
519 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
520 | __in DWORD dwEndSession, | ||
521 | __inout BOOL* pfCancel | ||
522 | ); | ||
523 | BAAPI UserExperienceOnUnregisterBegin( | ||
524 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
525 | __inout BOOL* pfKeepRegistration | ||
526 | ); | ||
527 | BAAPI UserExperienceOnUnregisterComplete( | ||
528 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
529 | __in HRESULT hrStatus | ||
530 | ); | ||
531 | int UserExperienceCheckExecuteResult( | ||
532 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
533 | __in BOOL fRollback, | ||
534 | __in DWORD dwAllowedResults, | ||
535 | __in int nResult | ||
536 | ); | ||
537 | HRESULT 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 | |||
8 | typedef 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 | |||
20 | const DWORD GROW_VARIABLE_ARRAY = 3; | ||
21 | |||
22 | enum 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 | |||
42 | enum 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 | |||
52 | static 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 | ); | ||
60 | static HRESULT GetFormatted( | ||
61 | __in BURN_VARIABLES* pVariables, | ||
62 | __in_z LPCWSTR wzVariable, | ||
63 | __out_z LPWSTR* psczValue, | ||
64 | __out BOOL* pfContainsHiddenVariable | ||
65 | ); | ||
66 | static 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 | ); | ||
74 | static HRESULT GetVariable( | ||
75 | __in BURN_VARIABLES* pVariables, | ||
76 | __in_z LPCWSTR wzVariable, | ||
77 | __out BURN_VARIABLE** ppVariable | ||
78 | ); | ||
79 | static HRESULT FindVariableIndexByName( | ||
80 | __in BURN_VARIABLES* pVariables, | ||
81 | __in_z LPCWSTR wzVariable, | ||
82 | __out DWORD* piVariable | ||
83 | ); | ||
84 | static HRESULT InsertVariable( | ||
85 | __in BURN_VARIABLES* pVariables, | ||
86 | __in_z LPCWSTR wzVariable, | ||
87 | __in DWORD iPosition | ||
88 | ); | ||
89 | static 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 | ); | ||
96 | static HRESULT InitializeVariableVersionNT( | ||
97 | __in DWORD_PTR dwpData, | ||
98 | __inout BURN_VARIANT* pValue | ||
99 | ); | ||
100 | static HRESULT InitializeVariableOsInfo( | ||
101 | __in DWORD_PTR dwpData, | ||
102 | __inout BURN_VARIANT* pValue | ||
103 | ); | ||
104 | static HRESULT InitializeVariableSystemInfo( | ||
105 | __in DWORD_PTR dwpData, | ||
106 | __inout BURN_VARIANT* pValue | ||
107 | ); | ||
108 | static HRESULT InitializeVariableComputerName( | ||
109 | __in DWORD_PTR dwpData, | ||
110 | __inout BURN_VARIANT* pValue | ||
111 | ); | ||
112 | static HRESULT InitializeVariableVersionMsi( | ||
113 | __in DWORD_PTR dwpData, | ||
114 | __inout BURN_VARIANT* pValue | ||
115 | ); | ||
116 | static HRESULT InitializeVariableCsidlFolder( | ||
117 | __in DWORD_PTR dwpData, | ||
118 | __inout BURN_VARIANT* pValue | ||
119 | ); | ||
120 | static HRESULT InitializeVariableWindowsVolumeFolder( | ||
121 | __in DWORD_PTR dwpData, | ||
122 | __inout BURN_VARIANT* pValue | ||
123 | ); | ||
124 | static HRESULT InitializeVariableTempFolder( | ||
125 | __in DWORD_PTR dwpData, | ||
126 | __inout BURN_VARIANT* pValue | ||
127 | ); | ||
128 | static HRESULT InitializeVariableSystemFolder( | ||
129 | __in DWORD_PTR dwpData, | ||
130 | __inout BURN_VARIANT* pValue | ||
131 | ); | ||
132 | static HRESULT InitializeVariablePrivileged( | ||
133 | __in DWORD_PTR dwpData, | ||
134 | __inout BURN_VARIANT* pValue | ||
135 | ); | ||
136 | static HRESULT InitializeSystemLanguageID( | ||
137 | __in DWORD_PTR dwpData, | ||
138 | __inout BURN_VARIANT* pValue | ||
139 | ); | ||
140 | static HRESULT InitializeUserUILanguageID( | ||
141 | __in DWORD_PTR dwpData, | ||
142 | __inout BURN_VARIANT* pValue | ||
143 | ); | ||
144 | static HRESULT InitializeUserLanguageID( | ||
145 | __in DWORD_PTR dwpData, | ||
146 | __inout BURN_VARIANT* pValue | ||
147 | ); | ||
148 | static HRESULT InitializeVariableString( | ||
149 | __in DWORD_PTR dwpData, | ||
150 | __inout BURN_VARIANT* pValue | ||
151 | ); | ||
152 | static HRESULT InitializeVariableNumeric( | ||
153 | __in DWORD_PTR dwpData, | ||
154 | __inout BURN_VARIANT* pValue | ||
155 | ); | ||
156 | static HRESULT InitializeVariable6432Folder( | ||
157 | __in DWORD_PTR dwpData, | ||
158 | __inout BURN_VARIANT* pValue | ||
159 | ); | ||
160 | static HRESULT InitializeVariableDate( | ||
161 | __in DWORD_PTR dwpData, | ||
162 | __inout BURN_VARIANT* pValue | ||
163 | ); | ||
164 | static HRESULT InitializeVariableInstallerName( | ||
165 | __in DWORD_PTR dwpData, | ||
166 | __inout BURN_VARIANT* pValue | ||
167 | ); | ||
168 | static HRESULT InitializeVariableInstallerVersion( | ||
169 | __in DWORD_PTR dwpData, | ||
170 | __inout BURN_VARIANT* pValue | ||
171 | ); | ||
172 | static HRESULT InitializeVariableVersion( | ||
173 | __in DWORD_PTR dwpData, | ||
174 | __inout BURN_VARIANT* pValue | ||
175 | ); | ||
176 | static HRESULT InitializeVariableLogonUser( | ||
177 | __in DWORD_PTR dwpData, | ||
178 | __inout BURN_VARIANT* pValue | ||
179 | ); | ||
180 | static HRESULT Get64bitFolderFromRegistry( | ||
181 | __in int nFolder, | ||
182 | __deref_out_z LPWSTR* psczPath | ||
183 | ); | ||
184 | |||
185 | #if !defined(_WIN64) | ||
186 | static HRESULT InitializeVariableRegistryFolder( | ||
187 | __in DWORD_PTR dwpData, | ||
188 | __inout BURN_VARIANT* pValue | ||
189 | ); | ||
190 | #endif | ||
191 | |||
192 | |||
193 | // function definitions | ||
194 | |||
195 | extern "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 | |||
287 | LExit: | ||
288 | return hr; | ||
289 | } | ||
290 | |||
291 | extern "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 | |||
434 | LExit: | ||
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 | |||
446 | extern "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 | |||
467 | extern "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 | |||
507 | extern "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 | |||
532 | LExit: | ||
533 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
534 | |||
535 | return hr; | ||
536 | } | ||
537 | |||
538 | extern "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 | |||
563 | LExit: | ||
564 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
565 | |||
566 | return hr; | ||
567 | } | ||
568 | |||
569 | extern "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 | |||
594 | LExit: | ||
595 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
596 | |||
597 | return hr; | ||
598 | } | ||
599 | |||
600 | extern "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 | |||
621 | LExit: | ||
622 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
623 | |||
624 | return hr; | ||
625 | } | ||
626 | |||
627 | extern "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 | |||
646 | extern "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 | |||
661 | extern "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 | |||
677 | extern "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 | |||
692 | extern "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 | |||
701 | extern "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 | |||
711 | extern "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 | |||
721 | extern "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 | |||
770 | LExit: | ||
771 | ReleaseStr(pwzEscaped); | ||
772 | ReleaseStr(pwz); | ||
773 | return hr; | ||
774 | } | ||
775 | |||
776 | extern "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 | |||
852 | LExit: | ||
853 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
854 | SecureZeroMemory(&ll, sizeof(ll)); | ||
855 | StrSecureZeroFreeString(scz); | ||
856 | |||
857 | return hr; | ||
858 | } | ||
859 | |||
860 | extern "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 | |||
952 | LExit: | ||
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 | |||
964 | extern "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 | |||
984 | extern "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 | |||
1005 | extern "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 | |||
1026 | extern "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 | |||
1050 | extern "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 | |||
1073 | LExit: | ||
1074 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
1075 | |||
1076 | return hr; | ||
1077 | } | ||
1078 | |||
1079 | |||
1080 | // internal function definitions | ||
1081 | |||
1082 | static 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 | |||
1265 | LExit: | ||
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 | |||
1305 | static 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 | |||
1348 | LExit: | ||
1349 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
1350 | StrSecureZeroFreeString(scz); | ||
1351 | |||
1352 | return hr; | ||
1353 | } | ||
1354 | |||
1355 | static 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 | |||
1385 | LExit: | ||
1386 | return hr; | ||
1387 | } | ||
1388 | |||
1389 | static 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 | |||
1418 | LExit: | ||
1419 | return hr; | ||
1420 | } | ||
1421 | |||
1422 | static 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 | |||
1461 | LExit: | ||
1462 | return hr; | ||
1463 | } | ||
1464 | |||
1465 | static 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 | |||
1518 | LExit: | ||
1519 | return hr; | ||
1520 | } | ||
1521 | |||
1522 | static 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 | |||
1625 | LExit: | ||
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 | |||
1636 | static 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 | |||
1693 | LExit: | ||
1694 | ReleaseVerutilVersion(pVersion); | ||
1695 | |||
1696 | return hr; | ||
1697 | } | ||
1698 | |||
1699 | static 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 | |||
1769 | LExit: | ||
1770 | return hr; | ||
1771 | } | ||
1772 | |||
1773 | static 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 | |||
1798 | LExit: | ||
1799 | return hr; | ||
1800 | } | ||
1801 | |||
1802 | static 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 | |||
1823 | LExit: | ||
1824 | return hr; | ||
1825 | } | ||
1826 | |||
1827 | static 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 | |||
1852 | LExit: | ||
1853 | ReleaseVerutilVersion(pVersion); | ||
1854 | |||
1855 | return hr; | ||
1856 | } | ||
1857 | |||
1858 | static 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 | |||
1875 | LExit: | ||
1876 | ReleaseStr(sczPath); | ||
1877 | |||
1878 | return hr; | ||
1879 | } | ||
1880 | |||
1881 | static 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 | |||
1901 | LExit: | ||
1902 | return hr; | ||
1903 | } | ||
1904 | |||
1905 | static 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 | |||
1972 | LExit: | ||
1973 | return hr; | ||
1974 | } | ||
1975 | |||
1976 | static 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 | |||
2003 | LExit: | ||
2004 | return hr; | ||
2005 | } | ||
2006 | |||
2007 | static 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 | |||
2025 | LExit: | ||
2026 | return hr; | ||
2027 | } | ||
2028 | |||
2029 | static 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 | |||
2042 | LExit: | ||
2043 | return hr; | ||
2044 | } | ||
2045 | |||
2046 | static 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 | |||
2059 | LExit: | ||
2060 | return hr; | ||
2061 | } | ||
2062 | |||
2063 | static 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 | |||
2076 | LExit: | ||
2077 | return hr; | ||
2078 | } | ||
2079 | |||
2080 | static 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 | |||
2092 | LExit: | ||
2093 | return hr; | ||
2094 | } | ||
2095 | |||
2096 | static 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 | |||
2108 | LExit: | ||
2109 | return hr; | ||
2110 | } | ||
2111 | |||
2112 | #if !defined(_WIN64) | ||
2113 | static 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 | |||
2137 | LExit: | ||
2138 | ReleaseStr(sczPath); | ||
2139 | |||
2140 | return hr; | ||
2141 | } | ||
2142 | #endif | ||
2143 | |||
2144 | static 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 | |||
2174 | LExit: | ||
2175 | ReleaseStr(sczPath); | ||
2176 | |||
2177 | return hr; | ||
2178 | } | ||
2179 | |||
2180 | // Get the date in the same format as Windows Installer. | ||
2181 | static 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 | |||
2211 | LExit: | ||
2212 | ReleaseStr(sczDate); | ||
2213 | |||
2214 | return hr; | ||
2215 | } | ||
2216 | |||
2217 | static 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 | |||
2228 | LExit: | ||
2229 | return hr; | ||
2230 | } | ||
2231 | |||
2232 | static 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 | |||
2247 | LExit: | ||
2248 | ReleaseStr(sczVersion); | ||
2249 | |||
2250 | return hr; | ||
2251 | } | ||
2252 | |||
2253 | static 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 | |||
2269 | LExit: | ||
2270 | ReleaseVerutilVersion(pVersion); | ||
2271 | |||
2272 | return hr; | ||
2273 | } | ||
2274 | |||
2275 | // Get the current user the same as Windows Installer. | ||
2276 | static 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 | |||
2294 | LExit: | ||
2295 | return hr; | ||
2296 | } | ||
2297 | |||
2298 | static 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 | |||
2318 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // constants | ||
11 | |||
12 | const LPCWSTR VARIABLE_DATE = L"Date"; | ||
13 | const LPCWSTR VARIABLE_LOGONUSER = L"LogonUser"; | ||
14 | const LPCWSTR VARIABLE_INSTALLERNAME = L"InstallerName"; | ||
15 | const LPCWSTR VARIABLE_INSTALLERVERSION = L"InstallerVersion"; | ||
16 | |||
17 | |||
18 | // typedefs | ||
19 | |||
20 | typedef HRESULT (*PFN_INITIALIZEVARIABLE)( | ||
21 | __in DWORD_PTR dwpData, | ||
22 | __inout BURN_VARIANT* pValue | ||
23 | ); | ||
24 | |||
25 | |||
26 | // constants | ||
27 | |||
28 | enum 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 | |||
38 | typedef 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 | |||
51 | typedef 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 | |||
62 | HRESULT VariableInitialize( | ||
63 | __in BURN_VARIABLES* pVariables | ||
64 | ); | ||
65 | HRESULT VariablesParseFromXml( | ||
66 | __in BURN_VARIABLES* pVariables, | ||
67 | __in IXMLDOMNode* pixnBundle | ||
68 | ); | ||
69 | void VariablesUninitialize( | ||
70 | __in BURN_VARIABLES* pVariables | ||
71 | ); | ||
72 | void VariablesDump( | ||
73 | __in BURN_VARIABLES* pVariables | ||
74 | ); | ||
75 | HRESULT VariableGetNumeric( | ||
76 | __in BURN_VARIABLES* pVariables, | ||
77 | __in_z LPCWSTR wzVariable, | ||
78 | __out LONGLONG* pllValue | ||
79 | ); | ||
80 | HRESULT VariableGetString( | ||
81 | __in BURN_VARIABLES* pVariables, | ||
82 | __in_z LPCWSTR wzVariable, | ||
83 | __out_z LPWSTR* psczValue | ||
84 | ); | ||
85 | HRESULT VariableGetVersion( | ||
86 | __in BURN_VARIABLES* pVariables, | ||
87 | __in_z LPCWSTR wzVariable, | ||
88 | __in VERUTIL_VERSION** ppValue | ||
89 | ); | ||
90 | HRESULT VariableGetVariant( | ||
91 | __in BURN_VARIABLES* pVariables, | ||
92 | __in_z LPCWSTR wzVariable, | ||
93 | __in BURN_VARIANT* pValue | ||
94 | ); | ||
95 | HRESULT VariableGetFormatted( | ||
96 | __in BURN_VARIABLES* pVariables, | ||
97 | __in_z LPCWSTR wzVariable, | ||
98 | __out_z LPWSTR* psczValue, | ||
99 | __out BOOL* pfContainsHiddenVariable | ||
100 | ); | ||
101 | HRESULT VariableSetNumeric( | ||
102 | __in BURN_VARIABLES* pVariables, | ||
103 | __in_z LPCWSTR wzVariable, | ||
104 | __in LONGLONG llValue, | ||
105 | __in BOOL fOverwriteBuiltIn | ||
106 | ); | ||
107 | HRESULT 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 | ); | ||
114 | HRESULT VariableSetVersion( | ||
115 | __in BURN_VARIABLES* pVariables, | ||
116 | __in_z LPCWSTR wzVariable, | ||
117 | __in VERUTIL_VERSION* pValue, | ||
118 | __in BOOL fOverwriteBuiltIn | ||
119 | ); | ||
120 | HRESULT VariableSetVariant( | ||
121 | __in BURN_VARIABLES* pVariables, | ||
122 | __in_z LPCWSTR wzVariable, | ||
123 | __in BURN_VARIANT* pVariant | ||
124 | ); | ||
125 | HRESULT VariableFormatString( | ||
126 | __in BURN_VARIABLES* pVariables, | ||
127 | __in_z LPCWSTR wzIn, | ||
128 | __out_z_opt LPWSTR* psczOut, | ||
129 | __out_opt SIZE_T* pcchOut | ||
130 | ); | ||
131 | HRESULT VariableFormatStringObfuscated( | ||
132 | __in BURN_VARIABLES* pVariables, | ||
133 | __in_z LPCWSTR wzIn, | ||
134 | __out_z_opt LPWSTR* psczOut, | ||
135 | __out_opt SIZE_T* pcchOut | ||
136 | ); | ||
137 | HRESULT VariableEscapeString( | ||
138 | __in_z LPCWSTR wzIn, | ||
139 | __out_z LPWSTR* psczOut | ||
140 | ); | ||
141 | HRESULT VariableSerialize( | ||
142 | __in BURN_VARIABLES* pVariables, | ||
143 | __in BOOL fPersisting, | ||
144 | __inout BYTE** ppbBuffer, | ||
145 | __inout SIZE_T* piBuffer | ||
146 | ); | ||
147 | HRESULT 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 | ); | ||
154 | HRESULT VariableStrAlloc( | ||
155 | __in BOOL fZeroOnRealloc, | ||
156 | __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, | ||
157 | __in DWORD_PTR cch | ||
158 | ); | ||
159 | HRESULT 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 | ); | ||
165 | HRESULT VariableStrAllocConcat( | ||
166 | __in BOOL fZeroOnRealloc, | ||
167 | __deref_out_z LPWSTR* ppwz, | ||
168 | __in_z LPCWSTR wzSource, | ||
169 | __in DWORD_PTR cchSource | ||
170 | ); | ||
171 | HRESULT __cdecl VariableStrAllocFormatted( | ||
172 | __in BOOL fZeroOnRealloc, | ||
173 | __deref_out_z LPWSTR* ppwz, | ||
174 | __in __format_string LPCWSTR wzFormat, | ||
175 | ... | ||
176 | ); | ||
177 | HRESULT 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 | |||
7 | static 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 | |||
16 | extern "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 | |||
28 | extern "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 | |||
63 | extern "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 | |||
90 | LExit: | ||
91 | return hr; | ||
92 | } | ||
93 | |||
94 | extern "C" HRESULT BVariantGetVersion( | ||
95 | __in BURN_VARIANT* pVariant, | ||
96 | __out VERUTIL_VERSION** ppValue | ||
97 | ) | ||
98 | { | ||
99 | return GetVersionInternal(pVariant, FALSE, FALSE, ppValue); | ||
100 | } | ||
101 | |||
102 | extern "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 | |||
111 | extern "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 | |||
120 | static 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 | |||
160 | extern "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 | |||
179 | extern "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 | |||
206 | LExit: | ||
207 | return hr; | ||
208 | } | ||
209 | |||
210 | extern "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 | |||
236 | extern "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 | |||
263 | LExit: | ||
264 | return hr; | ||
265 | } | ||
266 | |||
267 | extern "C" HRESULT BVariantCopy( | ||
268 | __in BURN_VARIANT* pSource, | ||
269 | __out BURN_VARIANT* pTarget | ||
270 | ) | ||
271 | { | ||
272 | return BVariantSetValue(pTarget, pSource); | ||
273 | } | ||
274 | |||
275 | extern "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 | |||
319 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // constants | ||
11 | |||
12 | enum 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 | |||
24 | typedef 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 | |||
38 | void BVariantUninitialize( | ||
39 | __in BURN_VARIANT* pVariant | ||
40 | ); | ||
41 | HRESULT BVariantGetNumeric( | ||
42 | __in BURN_VARIANT* pVariant, | ||
43 | __out LONGLONG* pllValue | ||
44 | ); | ||
45 | HRESULT BVariantGetString( | ||
46 | __in BURN_VARIANT* pVariant, | ||
47 | __out_z LPWSTR* psczValue | ||
48 | ); | ||
49 | HRESULT BVariantGetVersion( | ||
50 | __in BURN_VARIANT* pVariant, | ||
51 | __out VERUTIL_VERSION** ppValue | ||
52 | ); | ||
53 | HRESULT BVariantGetVersionHidden( | ||
54 | __in BURN_VARIANT* pVariant, | ||
55 | __in BOOL fHidden, | ||
56 | __out VERUTIL_VERSION** ppValue | ||
57 | ); | ||
58 | HRESULT BVariantGetVersionSilent( | ||
59 | __in BURN_VARIANT* pVariant, | ||
60 | __in BOOL fSilent, | ||
61 | __out VERUTIL_VERSION** ppValue | ||
62 | ); | ||
63 | HRESULT BVariantSetNumeric( | ||
64 | __in BURN_VARIANT* pVariant, | ||
65 | __in LONGLONG llValue | ||
66 | ); | ||
67 | HRESULT BVariantSetString( | ||
68 | __in BURN_VARIANT* pVariant, | ||
69 | __in_z_opt LPCWSTR wzValue, | ||
70 | __in DWORD_PTR cchValue, | ||
71 | __in BOOL fFormatted | ||
72 | ); | ||
73 | HRESULT BVariantSetVersion( | ||
74 | __in BURN_VARIANT* pVariant, | ||
75 | __in VERUTIL_VERSION* pValue | ||
76 | ); | ||
77 | /******************************************************************** | ||
78 | BVariantSetValue - Convenience function that calls BVariantUninitialize, | ||
79 | BVariantSetNumeric, BVariantSetString, or | ||
80 | BVariantSetVersion based on the type of pValue. | ||
81 | ********************************************************************/ | ||
82 | HRESULT BVariantSetValue( | ||
83 | __in BURN_VARIANT* pVariant, | ||
84 | __in BURN_VARIANT* pValue | ||
85 | ); | ||
86 | /******************************************************************** | ||
87 | BVariantCopy - creates a copy of pSource. | ||
88 | ********************************************************************/ | ||
89 | HRESULT BVariantCopy( | ||
90 | __in BURN_VARIANT* pSource, | ||
91 | __out BURN_VARIANT* pTarget | ||
92 | ); | ||
93 | HRESULT 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") | ||
9 | static DWORD dwMagic = 0x00f14300; | ||
10 | static DWORD dwVersion = 0x00000002; | ||
11 | |||
12 | static GUID guidBundleId = { }; | ||
13 | |||
14 | static DWORD dwStubSize = 0; | ||
15 | static DWORD dwOriginalChecksum = 0; | ||
16 | static DWORD dwOriginalSignatureOffset = 0; | ||
17 | static DWORD dwOriginalSignatureSize = 0; | ||
18 | |||
19 | static DWORD dwContainerFormat = 1; | ||
20 | static DWORD dwContainerCount = 0; | ||
21 | static DWORD qwBootstrapperApplicationContainerSize = 0; | ||
22 | static 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 | |||
6 | static 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 | |||
16 | int 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 | |||
71 | LExit: | ||
72 | ReleaseFileHandle(hEngineFile); | ||
73 | ReleaseStr(sczPath); | ||
74 | |||
75 | DutilUninitialize(); | ||
76 | |||
77 | return FAILED(hr) ? (int)hr : (int)dwExitCode; | ||
78 | } | ||
79 | |||
80 | static 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 | |||
3 | 1 ICON "stub.ico" | ||
diff --git a/src/burn/stub/stub.vcxproj b/src/burn/stub/stub.vcxproj new file mode 100644 index 00000000..97972848 --- /dev/null +++ b/src/burn/stub/stub.vcxproj | |||
@@ -0,0 +1,120 @@ | |||
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 | |||
5 | using namespace System::Reflection; | ||
6 | using namespace System::Runtime::CompilerServices; | ||
7 | using 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 | |||
5 | namespace Microsoft | ||
6 | { | ||
7 | namespace Tools | ||
8 | { | ||
9 | namespace WindowsInstallerXml | ||
10 | { | ||
11 | namespace Test | ||
12 | { | ||
13 | namespace 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 | ||
48 | class __TestThrowOnFailure_StringFree | ||
49 | { | ||
50 | LPWSTR m_scz; | ||
51 | |||
52 | public: | ||
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) | ||
73 | inline __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 | |||
86 | LExit: | ||
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 | |||
5 | namespace Microsoft | ||
6 | { | ||
7 | namespace Tools | ||
8 | { | ||
9 | namespace WindowsInstallerXml | ||
10 | { | ||
11 | namespace Test | ||
12 | { | ||
13 | namespace 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 | |||
5 | namespace Microsoft | ||
6 | { | ||
7 | namespace Tools | ||
8 | { | ||
9 | namespace WindowsInstallerXml | ||
10 | { | ||
11 | namespace Test | ||
12 | { | ||
13 | namespace 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 | |||
5 | static HRESULT CALLBACK CacheTestEventRoutine( | ||
6 | __in BURN_CACHE_MESSAGE* pMessage, | ||
7 | __in LPVOID pvContext | ||
8 | ); | ||
9 | |||
10 | static 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 | |||
22 | typedef struct _CACHE_TEST_CONTEXT | ||
23 | { | ||
24 | } CACHE_TEST_CONTEXT; | ||
25 | |||
26 | namespace Microsoft | ||
27 | { | ||
28 | namespace Tools | ||
29 | { | ||
30 | namespace WindowsInstallerXml | ||
31 | { | ||
32 | namespace Test | ||
33 | { | ||
34 | namespace 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 | |||
98 | static HRESULT CALLBACK CacheTestEventRoutine( | ||
99 | __in BURN_CACHE_MESSAGE* /*pMessage*/, | ||
100 | __in LPVOID /*pvContext*/ | ||
101 | ) | ||
102 | { | ||
103 | return S_OK; | ||
104 | } | ||
105 | |||
106 | static 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 | |||
6 | const DWORD TEST_CHILD_SENT_MESSAGE_ID = 0xFFFE; | ||
7 | const DWORD TEST_PARENT_SENT_MESSAGE_ID = 0xFFFF; | ||
8 | const HRESULT S_TEST_SUCCEEDED = 0x3133; | ||
9 | const char TEST_MESSAGE_DATA[] = "{94949868-7EAE-4ac5-BEAC-AFCA2821DE01}"; | ||
10 | |||
11 | |||
12 | static BOOL STDAPICALLTYPE ElevateTest_ShellExecuteExW( | ||
13 | __inout LPSHELLEXECUTEINFOW lpExecInfo | ||
14 | ); | ||
15 | static DWORD CALLBACK ElevateTest_ThreadProc( | ||
16 | __in LPVOID lpThreadParameter | ||
17 | ); | ||
18 | static HRESULT ProcessParentMessages( | ||
19 | __in BURN_PIPE_MESSAGE* pMsg, | ||
20 | __in_opt LPVOID pvContext, | ||
21 | __out DWORD* pdwResult | ||
22 | ); | ||
23 | static HRESULT ProcessChildMessages( | ||
24 | __in BURN_PIPE_MESSAGE* pMsg, | ||
25 | __in_opt LPVOID pvContext, | ||
26 | __out DWORD* pdwResult | ||
27 | ); | ||
28 | |||
29 | namespace Microsoft | ||
30 | { | ||
31 | namespace Tools | ||
32 | { | ||
33 | namespace WindowsInstallerXml | ||
34 | { | ||
35 | namespace Test | ||
36 | { | ||
37 | namespace 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 | |||
106 | static 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 | |||
121 | LExit: | ||
122 | ReleaseStr(scz); | ||
123 | |||
124 | return SUCCEEDED(hr); | ||
125 | } | ||
126 | |||
127 | static 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 | |||
156 | LExit: | ||
157 | PipeConnectionUninitialize(&connection); | ||
158 | ReleaseStr(sczArguments); | ||
159 | |||
160 | return FAILED(hr) ? (DWORD)hr : result.dwResult; | ||
161 | } | ||
162 | |||
163 | static 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 | |||
189 | LExit: | ||
190 | return hr; | ||
191 | } | ||
192 | |||
193 | static 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 | |||
219 | LExit: | ||
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 | |||
6 | using namespace System; | ||
7 | using namespace Xunit; | ||
8 | |||
9 | |||
10 | namespace Microsoft | ||
11 | { | ||
12 | namespace Tools | ||
13 | { | ||
14 | namespace WindowsInstallerXml | ||
15 | { | ||
16 | namespace Test | ||
17 | { | ||
18 | namespace 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 | |||
5 | namespace Microsoft | ||
6 | { | ||
7 | namespace Tools | ||
8 | { | ||
9 | namespace WindowsInstallerXml | ||
10 | { | ||
11 | namespace Test | ||
12 | { | ||
13 | namespace Bootstrapper | ||
14 | { | ||
15 | |||
16 | |||
17 | void 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 | |||
5 | namespace Microsoft | ||
6 | { | ||
7 | namespace Tools | ||
8 | { | ||
9 | namespace WindowsInstallerXml | ||
10 | { | ||
11 | namespace Test | ||
12 | { | ||
13 | namespace 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 | |||
5 | static 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 | |||
12 | static LPCWSTR wzMsiTransactionManifestFileName = L"MsiTransaction_BundleAv1_manifest.xml"; | ||
13 | static LPCWSTR wzSingleMsiManifestFileName = L"BasicFunctionality_BundleA_manifest.xml"; | ||
14 | static LPCWSTR wzSlipstreamManifestFileName = L"Slipstream_BundleA_manifest.xml"; | ||
15 | |||
16 | namespace Microsoft | ||
17 | { | ||
18 | namespace Tools | ||
19 | { | ||
20 | namespace WindowsInstallerXml | ||
21 | { | ||
22 | namespace Test | ||
23 | { | ||
24 | namespace 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 | |||
1465 | static 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 | |||
16 | static 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 | ); | ||
27 | static 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 | ); | ||
34 | static LSTATUS APIENTRY RegistrationTest_RegDeleteKeyExW( | ||
35 | __in HKEY hKey, | ||
36 | __in LPCWSTR lpSubKey, | ||
37 | __in REGSAM samDesired, | ||
38 | __reserved DWORD Reserved | ||
39 | ); | ||
40 | |||
41 | namespace Microsoft | ||
42 | { | ||
43 | namespace Tools | ||
44 | { | ||
45 | namespace WindowsInstallerXml | ||
46 | { | ||
47 | namespace Test | ||
48 | { | ||
49 | namespace 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(®istration, pixeBundle); | ||
106 | TestThrowOnFailure(hr, L"Failed to parse registration from XML."); | ||
107 | |||
108 | hr = PlanSetResumeCommand(®istration, 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, ®istration, &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(®istration, &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(®istration); | ||
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(®istration, pixeBundle); | ||
195 | TestThrowOnFailure(hr, L"Failed to parse registration from XML."); | ||
196 | |||
197 | hr = PlanSetResumeCommand(®istration, 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, ®istration, &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(®istration, &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, ®istration, &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(®istration, &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(®istration); | ||
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(®istration, pixeBundle); | ||
306 | TestThrowOnFailure(hr, L"Failed to parse registration from XML."); | ||
307 | |||
308 | hr = PlanSetResumeCommand(®istration, 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, ®istration, &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(®istration, &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(®istration, &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(®istration, &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(®istration); | ||
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(®istration, pixeBundle); | ||
417 | TestThrowOnFailure(hr, L"Failed to parse registration from XML."); | ||
418 | |||
419 | hr = PlanSetResumeCommand(®istration, 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, ®istration, &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(®istration, &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, ®istration, &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(®istration, &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(®istration); | ||
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(®istration, pixeBundle); | ||
548 | TestThrowOnFailure(hr, L"Failed to parse registration from XML."); | ||
549 | |||
550 | hr = PlanSetResumeCommand(®istration, 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(®istration, &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, ®istration, &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(®istration, rgbData, sizeof(rgbData)); | ||
567 | TestThrowOnFailure(hr, L"Failed to save state."); | ||
568 | |||
569 | // read interrupted resume type | ||
570 | hr = RegistrationDetectResumeType(®istration, &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(®istration, &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(®istration, &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(®istration, &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(®istration, &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(®istration, &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(®istration, &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(®istration); | ||
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 | |||
645 | static 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 | |||
685 | LExit: | ||
686 | ReleaseRegKey(hkRoot); | ||
687 | |||
688 | return ls; | ||
689 | } | ||
690 | |||
691 | static 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 | |||
727 | LExit: | ||
728 | ReleaseRegKey(hkRoot); | ||
729 | |||
730 | return ls; | ||
731 | } | ||
732 | |||
733 | static 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 | |||
768 | LExit: | ||
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 | |||
6 | static 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 | ); | ||
12 | static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiLocateComponentW( | ||
13 | __in LPCWSTR szComponent, | ||
14 | __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, | ||
15 | __inout_opt LPDWORD pcchBuf | ||
16 | ); | ||
17 | static 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 | ); | ||
23 | static 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 | |||
32 | using namespace System; | ||
33 | using namespace Xunit; | ||
34 | using namespace Microsoft::Win32; | ||
35 | |||
36 | namespace Microsoft | ||
37 | { | ||
38 | namespace Tools | ||
39 | { | ||
40 | namespace WindowsInstallerXml | ||
41 | { | ||
42 | namespace Test | ||
43 | { | ||
44 | namespace 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 | |||
618 | static 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 | |||
640 | static 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 | |||
708 | static 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 | |||
726 | static 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 >= 528040" InstallArguments="/q /norestart /ChainingPackage "[WixBundleName]" /log "[NetFx48WebLog].html"" UninstallArguments="/uninstall /q /norestart /ChainingPackage "[WixBundleName]" /log "[NetFx48WebLog].html"" RepairArguments="/q /norestart /repair /ChainingPackage "[WixBundleName]" /log "[NetFx48WebLog].html"" 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="<?xml version="1.0" encoding="utf-16"?><MsiPatch xmlns="http://www.microsoft.com/msi/patch_applicability.xsd" SchemaVersion="1.0.0.0" PatchGUID="{0A5113E3-06A5-4CE0-8E83-9EB42F6764A6}" MinMsiVersion="5" TargetsRTM="true"><TargetProduct MinMsiVersion="500"><TargetProductCode Validate="true">{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}</TargetProductCode><TargetVersion Validate="true" ComparisonType="Equal" ComparisonFilter="MajorMinorUpdate">1.0.0.0</TargetVersion><UpdatedVersion>1.0.1.0</UpdatedVersion><TargetLanguage Validate="false">1033</TargetLanguage><UpdatedLanguages>1033</UpdatedLanguages><UpgradeCode Validate="true">{DB87BB66-FE5D-4293-81AC-EE313D3F864B}</UpgradeCode></TargetProduct><TargetProductCode>{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}</TargetProductCode></MsiPatch>"><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 | |||
6 | using namespace System; | ||
7 | using namespace Xunit; | ||
8 | |||
9 | |||
10 | namespace Microsoft | ||
11 | { | ||
12 | namespace Tools | ||
13 | { | ||
14 | namespace WindowsInstallerXml | ||
15 | { | ||
16 | namespace Test | ||
17 | { | ||
18 | namespace 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 | |||
5 | namespace Microsoft | ||
6 | { | ||
7 | namespace Tools | ||
8 | { | ||
9 | namespace WindowsInstallerXml | ||
10 | { | ||
11 | namespace Test | ||
12 | { | ||
13 | namespace Bootstrapper | ||
14 | { | ||
15 | |||
16 | |||
17 | void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue, BOOL fFormatted); | ||
18 | void VariableSetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LONGLONG llValue); | ||
19 | void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue); | ||
20 | System::String^ VariableGetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); | ||
21 | __int64 VariableGetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); | ||
22 | System::String^ VariableGetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); | ||
23 | System::String^ VariableGetFormattedHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, BOOL* pfContainsHiddenVariable); | ||
24 | System::String^ VariableFormatStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzIn); | ||
25 | System::String^ VariableEscapeStringHelper(LPCWSTR wzIn); | ||
26 | bool EvaluateConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition); | ||
27 | bool EvaluateFailureConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition); | ||
28 | bool VariableExistsHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); | ||
29 | int 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 | |||
7 | namespace Microsoft | ||
8 | { | ||
9 | namespace Tools | ||
10 | { | ||
11 | namespace WindowsInstallerXml | ||
12 | { | ||
13 | namespace Test | ||
14 | { | ||
15 | namespace 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 | |||
5 | namespace Microsoft | ||
6 | { | ||
7 | namespace Tools | ||
8 | { | ||
9 | namespace WindowsInstallerXml | ||
10 | { | ||
11 | namespace Test | ||
12 | { | ||
13 | namespace 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 | |||
3 | namespace 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 | |||
3 | namespace 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 | |||
3 | namespace 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 | |||
3 | namespace 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 | |||
3 | using System; | ||
4 | using System.Reflection; | ||
5 | using System.Runtime.CompilerServices; | ||
6 | using 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 | |||
3 | namespace 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 | } | ||